Prompt
Implement the core logic functions for Minesweeper.
Write three pure functions:
createBoard()— returns an 8x8 board with 10 bombs placed randomly. Each cell: { bomb: boolean, adjacent: number, revealed: boolean }revealCell(board, r, c)— returns a NEW board with cell (r,c) revealed. If adjacent=0, auto-reveal neighbors (flood fill). Bomb cells are never revealed by this function — the UI detects the bomb tap and handles game over.checkWin(board)— returns true if all non-bomb cells are revealed.
Solution
Minesweeper is the strongest test of quarantining impurity. Math.random() lives only in createBoard, a factory called once per game; revealCell and checkWin are deterministic board-in/board-out functions. That split is what makes the game logic unit-testable and what makes React happy: the component holds the board in state and every tap is setBoard(revealCell(board, r, c)).
The subtle parts:
- Deep-copy before flood fill.
revealCellclones every row and every cell object up front, then lets the recursiverevealmutate the clone freely. Local mutation of a fresh copy is a legitimate performance pattern — purity is about the function's contract (inputs untouched, new output), not about never using assignment internally. - Flood-fill termination: the recursion stops at bounds, at already-revealed cells, and at bombs. The revealed-check is the visited-set — without it, two adjacent zero cells recurse into each other forever.
- Bounds come from
board.length, not the constant, so the function works on any board size — which is also what makes it testable with tiny hand-built boards. checkWinis a derived predicate —flat().every(revealed || bomb)— never a flag you maintain by hand.
Red flag: mutating the board held in state and calling setBoard(board). Same reference — React skips the re-render and taps "stop working." Every reveal must return a new board.
Say it: "Randomness is quarantined in the factory; reveal is board-in, new-board-out with the revealed flag doubling as the flood-fill visited set."