Minesweeper: deferring mine placement until the first click
A minesweeper where the first click can lose is an unfinished minesweeper. Deferring placement and excluding the surrounding 3 by 3 is the single most important design decision here.
What separates a real implementation from a quick one
The lazy approach places the mines up front and then waits for clicks. That gives the first click better than a one in ten chance of losing on the beginner board (ten mines among 81 cells), and the player has no line of play against it.
The right approach defers placement until the first click: build an empty board, and only after the player clicks does the random placement run, with the clicked cell and the surrounding 3 by 3 excluded from the candidates. The first click is therefore always safe, and it opens onto a region that can immediately be reasoned about.
The cost is slightly biased distribution, since the exclusion zone compresses the field and raises effective density near the edges. That is a trade between feel and statistical purity, and I take the feel.
Flood fill
Revealing a cell with zero neighbouring mines should reveal its neighbours too, and any neighbour that is also zero continues the spread. Two details matter in the implementation.
First, use an explicit stack or queue rather than recursion. On the hard board, a 30 by 16 expanse of empty space is enough to blow the call stack, and that crash only appears on specific layouts, which makes it hard to reproduce.
Second, the spread condition is zero neighbouring mines, not merely no mine. The latter also reveals numbered cells, and the game stops requiring deduction.
Reproducible randomness
Randomness comes from a seeded generator such as mulberry32 rather than Math.random(). That buys two things: the test suite can pin a seed and assert the exact placement for the Nth mine, and debugging can reproduce the same board.
Worth noting, a nearly identical copy of that RNG also exists in the 2048 game. That is not a missed extraction, it is the zero-import constraint on pure logic: a shared module would need a relative import, and the test suite would die with a module-not-found error. Here, duplication beats coupling on purpose.
Pure functions and difficulty
Board creation, revealing, flagging and resolution are all pure functions over plain data structures with no DOM. That is why the rules of all five games can be executed directly by node in the test suite. The three difficulties use the classic ratios: 9 by 9 with ten mines, 16 by 16 with forty, and 30 by 16 with ninety-nine.

Comments
…