import { defineComponent } from "../../src/component"; import type { World } from "../../src/index"; import { randomPiece, collides, lockPiece, clearLines, scoreForLines, BOARD_W, } from "./game"; // ── Component definitions ──────────────────────────── /** The playfield grid (20 rows × 10 cols). 0 = empty, non-zero = color index. */ export const Board = defineComponent("board", { grid: Array.from({ length: 20 }, () => new Uint8Array(10)) as Uint8Array[], }); // ── Active piece ───────────────────────────────────── export const Piece = defineComponent("piece", { shape: [] as number[][], color: 1, x: 3, y: 0, }); // ── Score / state ──────────────────────────────────── export const Score = defineComponent("score", { points: 0, lines: 0, level: 1, }); export const GameOver = defineComponent("gameOver", {}); export const Paused = defineComponent("paused", {}); // ── Timing ─────────────────────────────────────────── export const TickTimer = defineComponent("tickTimer", { accumulator: 0, interval: 800, // ms between gravity ticks }); // ── Piece helpers ──────────────────────────────────── export function createPieceHelpers(world: World) { return { spawnPiece(): void { const p = randomPiece(); world.addSingleton(Piece, { shape: p.shape, color: p.color, x: Math.floor((BOARD_W - p.shape[0].length) / 2), y: 0, }); }, lockAndSpawn(): void { const piece = world.getSingleton(Piece); const board = world.getSingleton(Board); lockPiece(board.grid, piece.shape, piece.color, piece.x, piece.y); world.removeSingleton(Piece); const cleared = clearLines(board.grid); if (cleared > 0) { const score = world.getSingleton(Score); score.lines += cleared; score.points += scoreForLines(cleared, score.level); score.level = Math.floor(score.lines / 10) + 1; const timer = world.getSingleton(TickTimer); timer.interval = Math.max(100, 800 - (score.level - 1) * 70); } this.spawnPiece(); const newPiece = world.getSingleton(Piece); if (collides(board.grid, newPiece.shape, newPiece.x, newPiece.y)) { world.removeSingleton(Piece); world.addSingleton(GameOver); } }, }; }