diff --git a/src/common/Position.ts b/src/common/TPosition.ts similarity index 88% rename from src/common/Position.ts rename to src/common/TPosition.ts index aed192d5..06c7de1d 100644 --- a/src/common/Position.ts +++ b/src/common/TPosition.ts @@ -15,4 +15,7 @@ * along with this program. If not, see https://www.gnu.org/licenses/ . */ -export type Position = readonly [number, number] +/** + * Represents a position with [X, Y]. + */ +export type TPosition = readonly [number, number] diff --git a/src/common/ISize.ts b/src/common/TSize.ts similarity index 91% rename from src/common/ISize.ts rename to src/common/TSize.ts index 77f4c21d..2cb75628 100644 --- a/src/common/ISize.ts +++ b/src/common/TSize.ts @@ -18,9 +18,7 @@ /** * Represents a size (width and height). */ -interface ISize { - width: number - height: number +export type TSize = { + readonly width: number + readonly height: number } - -export type { ISize } diff --git a/src/common/index.ts b/src/common/index.ts index b1a1203d..e69bbc67 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -28,8 +28,8 @@ import { isNil } from "./isNil" import { rearrange } from "./rearrange" import { waitForEvent } from "./waitForEvent" -import type { ISize } from "./ISize" -import type { Position } from "./Position" +import type { TPosition } from "./TPosition" +import type { TSize } from "./TSize" export { DefaultGameUpdateIntervalMilliseconds, @@ -43,5 +43,4 @@ export { isNil, formatDuration, } - -export type { ISize, Position } +export type { TSize, TPosition } diff --git a/src/customization/map/IMap.ts b/src/customization/map/IMap.ts index 2932d1cd..acae3981 100644 --- a/src/customization/map/IMap.ts +++ b/src/customization/map/IMap.ts @@ -15,7 +15,7 @@ * along with this program. If not, see https://www.gnu.org/licenses/ . */ -import type { ISize } from "../../common" +import type { TSize } from "../../common" export interface IMapCell { readonly atomicNumber: number @@ -27,5 +27,5 @@ export type TMapRow = IMapCell[] export interface IMap { readonly map: TMapRow[] readonly totalAvailableBlocksCount: number - readonly playAreaSize: ISize + readonly playAreaSize: TSize } diff --git a/src/model/Block.ts b/src/model/Block.ts index ce7b3d3c..d683ad37 100644 --- a/src/model/Block.ts +++ b/src/model/Block.ts @@ -17,7 +17,7 @@ import { TetriminoKind } from "./TetriminoKind" -import type { Position } from "../common" +import type { TPosition } from "../common" /** * The block, the most basic unit of the game. @@ -25,7 +25,7 @@ import type { Position } from "../common" export class Block { public constructor( public readonly filledBy: TetriminoKind, - public readonly position: Position, + public readonly position: TPosition, public /* readonly */ atomicNumber = 0, public readonly id = 0 ) {} diff --git a/src/model/EventArgs.ts b/src/model/EventArgs.ts index fc3fd948..79a7c4ba 100644 --- a/src/model/EventArgs.ts +++ b/src/model/EventArgs.ts @@ -17,9 +17,7 @@ import { Block } from "./Block" -export class BlocksChangedEventArgs { - public constructor( - public readonly blocks: Block[], - public readonly disappeared: boolean - ) {} +export type TBlocksChangedEventArgs = { + readonly blocks: Block[] + readonly disappeared: boolean } diff --git a/src/model/GameModel.ts b/src/model/GameModel.ts index eebc350f..2c87d8fc 100644 --- a/src/model/GameModel.ts +++ b/src/model/GameModel.ts @@ -23,7 +23,6 @@ import { isNil, waitForEvent } from "../common" import { customizationFacade } from "../customization" import { Block } from "./Block" import { MoveDirection, RotationDirection } from "./Direction" -import { BlocksChangedEventArgs } from "./EventArgs" import { GameState } from "./GameState" import { getPlayablePattern, MessageType } from "./generation" import { repairBrokenTetriminos, Tetrimino } from "./Tetrimino" @@ -76,7 +75,7 @@ export class GameModel extends EventEmitter { public get gameState(): GameState { return this._gameState } - private set gameState(v: GameState) { + private set gameState(v) { this._gameState = v this.onGameStateChanged() } @@ -345,7 +344,7 @@ export class GameModel extends EventEmitter { * @param disappeared Whether the block disappeared. */ private onBlocksChanged(blocks: Block[], disappeared: boolean): void { - this.emit("blockschanged", new BlocksChangedEventArgs(blocks, disappeared)) + this.emit("blockschanged", { blocks, disappeared }) } /** diff --git a/src/model/Tetrimino.ts b/src/model/Tetrimino.ts index 03f627e9..42f0ee7a 100644 --- a/src/model/Tetrimino.ts +++ b/src/model/Tetrimino.ts @@ -22,14 +22,14 @@ import { Direction, MoveDirection, RotationDirection } from "./Direction" import { createOffsetedBlocks, mapAtomicNumberInto } from "./TetriminoHelper" import { TetriminoKind } from "./TetriminoKind" -import type { Position } from "../common" +import type { TPosition } from "../common" /** * The type for block collision checker. * * @returns 'false' if no collision found. Otherwise 'true'. */ -export type TBlockCollisionChecker = (block: Block) => boolean +export type TCollisionChecker = (block: Block) => boolean /** * The tetrimino. @@ -46,7 +46,7 @@ export class Tetrimino { */ public tryMove( direction: MoveDirection, - collisionChecker: TBlockCollisionChecker + collisionChecker: TCollisionChecker ): boolean { const deltaY = direction === MoveDirection.Down ? 1 : 0 const deltaX = @@ -54,10 +54,8 @@ export class Tetrimino { (direction === MoveDirection.Right ? 1 : -1) const newBlocks = map(this.blocks, (b) => ({ - filledBy: b.filledBy, + ...b, position: [b.position[0] + deltaX, b.position[1] + deltaY] as const, - atomicNumber: b.atomicNumber, - id: b.id, })) if (newBlocks.some(collisionChecker)) { @@ -80,7 +78,7 @@ export class Tetrimino { */ public tryRotate( rotationDirection: RotationDirection, - collisionChecker: TBlockCollisionChecker + collisionChecker: TCollisionChecker ): boolean { // Find the final direction const count = Object.keys(Direction).length / 2 @@ -118,7 +116,7 @@ export class Tetrimino { */ public constructor( public kind: TetriminoKind, - public position: Position, + public position: TPosition, public facingDirection: Direction ) { this.blocks = createOffsetedBlocks(kind, position, facingDirection) @@ -135,11 +133,7 @@ export class Tetrimino { export function repairBrokenTetriminos( brokenTetriminos: Tetrimino[] ): Tetrimino[] { - const repairedTetriminos = map(brokenTetriminos, (brokenTetrimino) => - Object.create( - Tetrimino.prototype, - Object.getOwnPropertyDescriptors(brokenTetrimino) - ) + return map(brokenTetriminos, (t) => + Object.create(Tetrimino.prototype, Object.getOwnPropertyDescriptors(t)) ) - return repairedTetriminos } diff --git a/src/model/TetriminoHelper.ts b/src/model/TetriminoHelper.ts index bb723512..067b56df 100644 --- a/src/model/TetriminoHelper.ts +++ b/src/model/TetriminoHelper.ts @@ -19,7 +19,7 @@ import { Block } from "./Block" import { Direction } from "./Direction" import { TetriminoKind } from "./TetriminoKind" -import type { ISize, Position } from "../common" +import type { TPosition, TSize } from "../common" const CubicDownMask: number[][] = [ [3, 4], @@ -396,10 +396,10 @@ export function getFirstBlockCoordByType( * @returns The transformed position. */ export function getPositionByFirstBlock( - position: Position, + position: TPosition, kind: TetriminoKind, facingDirection: Direction -): Position { +): TPosition { const firstBlockCoord = getFirstBlockCoordByType(kind, facingDirection) const firstBlockRow = firstBlockCoord.row const firstBlockCol = firstBlockCoord.col @@ -408,8 +408,8 @@ export function getPositionByFirstBlock( export function getInitialPositionByKind( kind: TetriminoKind, - playAreaSize: ISize -): Position { + playAreaSize: TSize +): TPosition { let length: number switch (kind) { case TetriminoKind.Linear: @@ -433,7 +433,7 @@ export function getInitialPositionByKind( export function createOffsetedBlocks( kind: TetriminoKind, - offset: Position, + offset: TPosition, direction: Direction = Direction.Up ): Block[] { const mask = getBlocksMask(kind, direction) diff --git a/src/model/generation/internal/PatternGenerator.ts b/src/model/generation/internal/PatternGenerator.ts index 09fce7ea..fa9038d8 100644 --- a/src/model/generation/internal/PatternGenerator.ts +++ b/src/model/generation/internal/PatternGenerator.ts @@ -20,7 +20,7 @@ import { map, shuffle } from "lodash" import { isNil } from "../../../common" import { Block } from "../../Block" import { Direction, RotationDirection } from "../../Direction" -import { repairBrokenTetriminos, Tetrimino } from "../../Tetrimino" +import { Tetrimino, repairBrokenTetriminos } from "../../Tetrimino" import { getInitialPositionByKind, getPositionByFirstBlock, @@ -28,7 +28,7 @@ import { import { TetriminoKind } from "../../TetriminoKind" import { sort } from "./TetriminoSorter" -import type { ISize, Position } from "../../../common" +import type { TPosition, TSize } from "../../../common" import type { IMap } from "../../../customization" function fastRandom(startInc: number, endExc: number): number { @@ -60,9 +60,7 @@ export async function getPlayablePattern(gameMap: IMap): Promise { ) const fixedTetriminos = repairBrokenTetriminos(ordered) - primeTetriminos(fixedTetriminos, gameMap.playAreaSize) - return fixedTetriminos } @@ -196,7 +194,7 @@ function collisionChecker( function getFirstAvailableBlockCoord( occupationMap: TetriminoKind[][] -): Position { +): TPosition { for (let nRow = occupationMap.length - 1; nRow >= 0; nRow--) { const col = occupationMap[nRow] for (let nCol = col.length - 1; nCol >= 0; nCol--) { @@ -235,7 +233,7 @@ class KindDirectionsPair { * @param tetriminos The tetriminos to prime. * @param playAreaSize Size of play area. */ -function primeTetriminos(tetriminos: Tetrimino[], playAreaSize: ISize) { +function primeTetriminos(tetriminos: Tetrimino[], playAreaSize: TSize) { // Move to initial position and rotate randomly tetriminos.forEach((tetrimino) => { const originalPos = tetrimino.position diff --git a/src/model/generation/internal/TetriminoSorter.ts b/src/model/generation/internal/TetriminoSorter.ts index 792d8430..aeb1a80f 100644 --- a/src/model/generation/internal/TetriminoSorter.ts +++ b/src/model/generation/internal/TetriminoSorter.ts @@ -21,11 +21,11 @@ import toposort from "toposort" import { isNil, rearrange } from "../../../common" import { Tetrimino } from "../../Tetrimino" -import type { ISize } from "../../../common" +import type { TSize } from "../../../common" function getEdges( tetriminos: Tetrimino[], - playAreaSize: ISize + playAreaSize: TSize ): [number, number][] { // Create owners map const owners: number[][] = new Array(playAreaSize.height) @@ -43,16 +43,14 @@ function getEdges( return uniq( flatten( - map(tetriminos, (tetrimino, index) => { - const singleTetriminoDeps: [number, number][] = [] - for (let i = 0; i < tetrimino.blocks.length; i++) { - const block = tetrimino.blocks[i] - const dependedBlockRow = block.position[1] + 1 - const dependedBlockCol = block.position[0] - const result = tryGetOccupiedTetriminoNode( + map(tetriminos, (t, index) => { + const deps: [number, number][] = [] + for (let i = 0; i < t.blocks.length; i++) { + const block = t.blocks[i] + const result = tryGetOccupant( owners, - dependedBlockRow, - dependedBlockCol, + block.position[1] + 1 /* Lower row... */, + block.position[0] /* ...and same column */, playAreaSize ) if (isNil(result) || result === index) { @@ -60,19 +58,19 @@ function getEdges( continue } // Found a result - singleTetriminoDeps.push([result, index]) + deps.push([result, index]) } - return singleTetriminoDeps + return deps }) ) ) } -function tryGetOccupiedTetriminoNode( +function tryGetOccupant( map: number[][], row: number, col: number, - playAreaSize: ISize + playAreaSize: TSize ): number | null { if ( row < 0 || @@ -83,21 +81,14 @@ function tryGetOccupiedTetriminoNode( return null } - const cell = map[row][col] - if (isNil(cell)) { - return null - } - - return cell + return map[row][col] ?? null } export function sort( tetriminos: Tetrimino[], - playAreaSize: ISize + playAreaSize: TSize ): Tetrimino[] { const edges = getEdges(tetriminos, playAreaSize) const sortedIndices = toposort(edges) - const sortedTetriminos = rearrange(tetriminos, sortedIndices) - - return sortedTetriminos + return rearrange(tetriminos, sortedIndices) } diff --git a/src/model/index.ts b/src/model/index.ts index d73099d2..e12f4907 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -17,15 +17,15 @@ import { Block } from "./Block" import { Direction, MoveDirection, RotationDirection } from "./Direction" -import { BlocksChangedEventArgs } from "./EventArgs" import { GameModel } from "./GameModel" import { GameState } from "./GameState" import { Tetrimino } from "./Tetrimino" import { TetriminoKind } from "./TetriminoKind" +import type { TBlocksChangedEventArgs } from "./EventArgs" + export { Block, - BlocksChangedEventArgs, Direction, RotationDirection, MoveDirection, @@ -34,3 +34,5 @@ export { Tetrimino, TetriminoKind, } + +export type { TBlocksChangedEventArgs } diff --git a/src/viewmodel/GameViewModel.ts b/src/viewmodel/GameViewModel.ts index 72629214..2efa3e21 100644 --- a/src/viewmodel/GameViewModel.ts +++ b/src/viewmodel/GameViewModel.ts @@ -32,7 +32,6 @@ import { } from "../components/timerDisplay/timerDisplaySlice" import { customizationFacade } from "../customization" import { - BlocksChangedEventArgs, GameModel, GameState, MoveDirection, @@ -40,7 +39,8 @@ import { } from "../model" import { appStore } from "./appStore" -import type { Position } from "../common" +import type { TPosition } from "../common" +import type { TBlocksChangedEventArgs } from "../model" import type { IBlockSprite } from "./IBlockSprite" const Hammer: HammerStatic = isBrowser ? require("hammerjs") : null @@ -68,7 +68,7 @@ export class GameViewModel { private readonly _model: GameModel = new GameModel() - private readonly _blocksByPosition: Map = new Map() + private readonly _blocksByPosition: Map = new Map() private _gameIntervalTimerHandle: number | null = null @@ -198,7 +198,7 @@ export class GameViewModel { } private modelBlocksChangedEventHandler( - eventArgs: BlocksChangedEventArgs + eventArgs: TBlocksChangedEventArgs ): void { const blocks = eventArgs.blocks if (!eventArgs.disappeared) {