Six small games.
A game is an honest demo — it either plays correctly or it visibly does not. Each of these was picked for one problem that is easy to get subtly wrong, and the note under each says what it is.
Click to reveal. Right-click, long-press, or focus a square and press
F to flag it.
The problemA board you never have to guess on
Two things separate a fair minefield from an irritating one, and most implementations do neither.
The first click is never a mine. Mines are laid after you click, with the clicked square and its whole neighbourhood excluded — so the opening move always opens the board up instead of ending the game on a coin flip.
The board is checked for solvability. Random placement regularly produces an endgame where two squares are genuinely indistinguishable and you simply have to pick one. So the generator lays a board, runs a deduction solver over it, and re-lays it if the solver gets stuck — up to a bounded number of attempts, because a generator that loops until it succeeds is a generator that can hang the tab. When it succeeds it says so under the grid.
The solver is deliberately incomplete: it applies only the two obvious rules and not the pairwise subset rule. That makes it conservative — a board it can finish is definitely guess-free, and it may reject some a good player could manage. For a generator that is the right way round.
Arrow keys or WASD. Swipe on a phone. Ctrl+Z undoes.
The problemA tile may only merge once per move
Push [2, 2, 4] to the left and the answer is
[4, 4] — not [8]. The obvious
implementation, walking the row and combining equal neighbours as it
goes, produces the 8: it merges the two 2s into a 4 and then merges
that 4 with the one already sitting there.
The bug is nasty because it is intermittent. It needs a specific arrangement to show up, so it survives casual testing and then hands the player a 2048 tile they did not earn. The fix is to compact the row first and then merge in a single pass with an index that steps past the tile it just consumed, so nothing can be eaten twice.
Undo comes free. A move builds a new board rather than
editing the old one, so keeping the last forty is a
push and nothing else — no diffing, no reverse
operations, no library. The same argument the task board demo makes,
in about four lines.
The problemScoring a letter that appears twice
Guess ERROR when the answer is OTHER. The answer holds one R; the guess holds three. Exactly one of those three may be coloured — and it has to be the right one:
That row is rendered by calling the game's own scoring function, not by typing the colours in, so it cannot drift away from what the game actually does.
The one-pass version — green if it matches, amber if the answer contains it — colours all three Rs, and practically every fresh implementation ships that bug. The correct algorithm needs two passes: mark the exact positional hits first while counting what is left of each letter, and only then walk the remainder, colouring amber only while that letter's remaining count is above zero.
The keyboard follows the same rule in one direction only — a letter already shown green never goes back to amber on a later guess, because information you have been given should not be taken away.
Next
The bag
Arrows or WASD to move and turn. Space drops.
The strip on the right is the current bag — faded pieces are already dealt.
The problemRandom is not the same as fair
Pick each piece with Math.random() and the game is
perfectly fair over ten thousand pieces and miserable over ten. With
seven shapes, the chance of going a dozen pieces without a straight one
is around fifteen per cent — and players do not experience that as
randomness. They experience it as the game cheating, and they report it
as a bug.
Every modern implementation uses a bag: take all seven pieces, shuffle them, deal them out, refill when empty. You can never wait more than twelve pieces for any given shape, and you can never be handed three of a kind in a row. It is four lines of code, and it is the difference between a game that feels fair and one that feels rigged.
The bag is drawn on screen because it is the whole point — watch it empty and refill as you play.
One other thing in here is worth naming: a piece against a wall that simply refuses to turn reads as broken controls rather than as a rule, so rotation tries a small sideways nudge before giving up.
Click a square and type a digit, or use the pad. Wrong entries turn red.
The problemA puzzle with two answers is not a puzzle
Generating a grid that looks like a Sudoku is easy. Generating one with exactly one solution is the actual job, and it is the step most tutorials skip — which is how you ship a puzzle where the player fills in a perfectly valid grid, is told they are wrong, and is right to be annoyed.
The recipe is three steps. Build a complete valid grid by randomised backtracking. Remove clues one at a time. After each removal, run a second solver that counts solutions and stops as soon as it finds two — if the answer is not exactly one, put the clue back and try a different square.
That counting solver is the whole difference between a puzzle and a guessing game. It is also why the harder settings take a moment: every rejected removal is a full search, and the number under the board is how many clues actually survived the process.
Click a tile next to the gap, or use the arrow keys. Green tiles are already home.
The problemHalf of all shuffles are impossible
This is the most famous trap in the set. Take the fifteen tiles, shuffle the array, and there is a fifty per cent chance the puzzle cannot be solved at all — no sequence of legal moves reaches the finished position. The board looks completely ordinary. The player spends twenty minutes on it with no way of knowing.
The reason is parity. A legal move swaps the gap with one neighbour, and that changes the permutation’s parity in lockstep with the gap’s row. Only half of the arrangements sit on the reachable side of it.
There are two honest fixes: shuffle by making legal moves, so every state you can produce is reachable by construction; or shuffle freely, measure the parity, and swap one pair to correct it. This uses the first — four hundred random legal moves — and prints the parity of whatever it produced, so the claim is checkable rather than asserted.
Press Shuffle the array for the broken version. About half the time it hands you a board that cannot be finished, and says so.