The pack
reveal
function reveal(): Pack<{ reveal: RevealApi }>
The reveal pack. Pure logic, so it needs no backend and runs headless.
Helpers
createMinefield
function createMinefield(cfg: MinefieldConfig): Minefield
Creates a Minesweeper board from the config.
Classes
Minefield
class Minefield
A Minesweeper board with hidden state. Create one with createMinefield().
readonly cols: number— The number of columns on the board.readonly rows: number— The number of rows on the board.readonly mines: number— The number of mines on the board.state: 'playing' | 'won' | 'lost'— The game state. 'playing' until a win or a loss.constructor(cfg: MinefieldConfig)cell(x: number, y: number): CellView— The player-visible view of a cell.get minesLeft(): number— Mines minus flags placed (may go negative).reveal(x: number, y: number): RevealResult— Reveal a cell. First reveal places mines (excluding the safe zone).flag(x: number, y: number): void— Flag → question → hidden cycle. Flags block reveal.chord(x: number, y: number): RevealResult— Chord: on a revealed number whose adjacent flags equal it, reveal the rest. A wrong flag here detonates.hint(): { safe: XY[]; mines: XY[] }— The covered cells the open numbers prove safe, and those they prove are mines, as{ safe, mines }, in board order; both empty before the first reveal. Every cell not yet revealed is covered, flagged and question-marked ones too: a flag is the player's guess, so the hint ignores it and proves from the numbers alone, and a flag the numbers prove wrong comes back insafe. Two rules, applied until neither proves more: a number whose proved mines equal it has its other covered neighbours safe; a number whose proved mines and unproved covered neighbours together equal it has those all mines.step(): boolean— Apply onehint()pass: flag every proved mine and open every proved-safe cell, leaving the player's flags and question marks as they are (a mark blocks a reveal, as it blocks a click). Before the first reveal it opens the centre cell. Returns false when it could not act or the game is over.debugGrid(): string— Returns the board as text: '.' hidden, 'F' flag, '*' mine, or the number. For tests.
Types
CellState
type CellState = 'hidden' | 'flagged' | 'question' | 'revealed'
The player-visible state of one cell.
CellView
interface CellView
What the player can see of one cell. Returned by cell().
state: CellState— The player-visible state of the cell.number: number— Adjacent mine count (0–8). Meaningful once revealed.mine?: boolean— True only after game end (loss reveals all).
MinefieldConfig
interface MinefieldConfig
Configuration for createMinefield().
cols: number— The number of columns on the board.rows: number— The number of rows on the board.mines: number— The number of mines. Must be less than cols × rows.seed?: number— The seed for mine placement. The same seed gives the same board. Default 1.firstClickSafe?: 'cell' | 'area'—cell= first click is never a mine;area= its 3×3 is mine-free (modern default).
RevealApi
type RevealApi = typeof kit
game.reveal — every function and type this pack carries, as one namespace.
RevealResult
interface RevealResult
A distance-ordered flood: waves[0]! is the clicked cell, waves[n]! are cells
n steps away — the presenter animates them as an expanding ripple.
waves: { cells: { at: XY; number: number }[] }[]— The revealed cells, grouped by distance from the click.detonated: XY | null— The mine cell the click set off, or null.state: 'playing' | 'won' | 'lost'— The game state after the move.reveal?: { mines: XY[]; wrongFlags: XY[] }— All mines + wrong flags, populated on loss for the reveal-everything render.
XY
interface XY
A grid cell position. x is the column and y is the row, from the top left.
x: number— The column, from the left.y: number— The row, from the top.


