// The mutable local-game board: a 15x15 row-major grid of placed tiles (alphabet-index letter // plus a blank flag). It satisfies the read view the validator and generator need (GenBoard, // which extends validate.ts's Board) and adds set() so the engine can apply a play. The board is // alphabet-agnostic — a cell's letter is a variant index, meaningful with the variant's ruleset. import { BOARD_SIZE } from '../premiums'; import type { Cell } from '../dict/validate'; import type { GenBoard } from '../dict/generate'; /** LocalBoard is the engine's in-memory board; it reads as a GenBoard and applies plays via set. */ export class LocalBoard implements GenBoard { readonly rows = BOARD_SIZE; readonly cols = BOARD_SIZE; private readonly grid: (Cell | null)[]; private count = 0; constructor() { this.grid = new Array(BOARD_SIZE * BOARD_SIZE).fill(null); } inBounds(row: number, col: number): boolean { return row >= 0 && row < this.rows && col >= 0 && col < this.cols; } filled(row: number, col: number): boolean { return this.inBounds(row, col) && this.grid[row * this.cols + col] !== null; } cellAt(row: number, col: number): Cell { return this.grid[row * this.cols + col]!; } isEmpty(): boolean { return this.count === 0; } /** set places a tile at (row, col). It assumes the square was empty (a play only lays tiles * on empty squares), keeping the filled count exact for isEmpty. */ set(row: number, col: number, letter: number, blank: boolean): void { const i = row * this.cols + col; if (this.grid[i] === null) this.count++; this.grid[i] = { letter, blank }; } }