Minesweeper
A grid, some hidden mines, and one reveal operation that has to cascade outward on its own. The class model is small; the trap is writing that cascade recursively and finding out on a 30x30 board that the call stack disagrees.
Requirements
Functional
- An
R x Cboard is seeded withKmines at random positions before the first move. - Revealing a cell that holds a mine ends the game as a loss.
- Revealing a cell with zero adjacent mines automatically reveals its neighbors, and theirs, and so on, stopping at the first ring of cells that do have an adjacent mine.
- Revealing every non-mine cell wins the game. Cells can be flagged and unflagged, and a flagged cell cannot be revealed until unflagged.
Non-functional
- The cascade in requirement three must not recurse - a board with thousands of empty cells chained together should not risk a stack overflow.
- Checking "has the player won yet" should not re-scan the whole board on every single reveal once the board is large.
Design
Cell is deliberately dumb - a bag of four booleans/counters (isMine, revealed,
flagged, adjacentMines) with no idea what a neighbor is. All the graph-shaped work -
computing adjacency counts, walking the flood-fill frontier - lives in Board, using an
explicit queue instead of recursion so the non-functional requirement is structural, not a
matter of remembering to be careful.
- 1The player only ever calls reveal on the game facade.
- 2Game forwards the coordinates without inspecting board internals itself.
- 3If this one cell is a mine, the board can return LOSE immediately.
- 4Otherwise the board walks an explicit queue of zero-adjacency cells outward.
- 5Each cell touched by the flood fill flips its own revealed flag - it does not know why it was reached.
- 6After the reveal settles, the game asks whether every non-mine cell is now revealed.
reveal returns a result rather than mutating some shared "game over" flag - the caller
(Game) decides what a LOSE or WIN means for the UI; Board just reports what happened.
Class diagram
Code
Design decisions
- Flood fill is an explicit BFS queue, not recursion. A recursive
revealNeighborsreads cleaner, but its call depth is bounded by however many empty cells happen to chain together- on a large board that's an unbounded, data-dependent stack depth. A queue keeps memory on the heap where it's supposed to be.
Cellhas zero neighbor-awareness. It would be tempting to give a cell a list of neighbor references so it can compute its ownadjacentMines. That couples every cell to the board's dimensions and makesCellimpossible to unit-test in isolation.Boardcomputing offsets and counting is one method, done once, in one place.revealreturns aRevealResultenum instead of throwing or setting a flag. Loss and win are ordinary outcomes of this operation, not exceptional ones, and a caller checking a return value doesn't need to know aboutGame's internal state to react correctly.- What's missing for a real system: a running "cells remaining" counter maintained incrementally (instead of a linear win-check scan) is the obvious optimization once boards get large, and a real client also wants first-click safety - guaranteeing the very first reveal is never a mine, which means deferring mine placement until after that first click instead of seeding the board up front.