export type FallingGridCell = readonly [number, number]; export interface FallingGridConfig { cols: number; rows: number; /** Milliseconds a grounded piece may sit before `lockExpired` fires. Defaults to 500. */ lockDelayMs?: number; } /** Elapsed grounded time for the lock-delay state machine. `null` means airborne / not timing. */ export interface LockState { elapsedMs: number; } export interface FallingGridSnapshot { cols: number; rows: number; cells: readonly (TCell | null)[]; } export interface FallingGrid { readonly cols: number; readonly rows: number; occupied(col: number, row: number): boolean; cellAt(col: number, row: number): TCell | null; setCell(col: number, row: number, value: TCell): void; clearCell(col: number, row: number): void; /** Bounds + collision check for a footprint (`cells`, relative to `offset`). Rows above the grid (`row < 0`) are allowed through, mirroring a piece spawning above the visible board; only columns are bounds-checked there. */ canPlace(cells: readonly FallingGridCell[], offset: FallingGridCell): boolean; /** Writes `value` into every in-bounds cell of the footprint if `canPlace` allows it. Cells above the grid are silently skipped. Returns whether the placement happened. */ place(cells: readonly FallingGridCell[], offset: FallingGridCell, value: TCell): boolean; /** Row indices that are fully occupied, in ascending order. */ fullRows(): readonly number[]; /** Removes the given rows and shifts every row above them down, backfilling the top with empty rows. Returns the number of rows cleared. */ clearRows(rows: readonly number[]): number; /** Column-gravity collapse: every occupied cell in each column falls to the lowest free row below it, preserving relative order. Returns the number of cells that moved. */ settle(): number; /** Advances (or starts) a lock-delay timer while `grounded`, and clears it the instant the piece is airborne. */ advanceLock(state: LockState | null, grounded: boolean, dtMs: number): LockState | null; /** Whether a lock-delay timer has run out `lockDelayMs`. */ lockExpired(state: LockState | null): boolean; snapshot(): FallingGridSnapshot; /** Clears every cell back to empty. */ reset(): void; } export interface GravityIntervalConfig { /** Interval at level 0, in milliseconds. Defaults to 800. */ baseMs?: number; /** Milliseconds shaved off per level. Defaults to 70. */ perLevel?: number; /** Floor the interval never drops below, in milliseconds. Defaults to 50. */ minMs?: number; } /** * Gravity tick interval for a given level: linear speed-up with a floor, generalizing * a falling-block `gravityInterval` (`max(0.05, 0.8 - level * 0.07)` seconds) to milliseconds * with configurable base/step/floor. * @internal */ export declare function gravityIntervalMs(level: number, config?: GravityIntervalConfig): number; /** * A game-agnostic cell grid for gravity-driven falling-block mechanics: bounded occupancy, * footprint placement/collision, line-clear collapse, column-gravity settle (for match-3/sand * style cascades), and a pure lock-delay helper. Extracted from falling-block tetris * logic so any falling-block game can adopt it instead of reimplementing the grid. * @internal */ export declare function createFallingGrid(config: FallingGridConfig): FallingGrid;