A logic puzzle with two solutions is not a logic puzzle. If a player deduces their way to a grid that satisfies every clue and the game says no, the game is wrong, not the player. So a generator for Star Grid — Star Battle, the puzzle where each row, column and region holds exactly one star — has to guarantee the board it deals has exactly one answer.

The obvious way is rejection sampling: make a board, count its solutions, throw it away unless the count is 1, repeat.

The numbers that killed rejection sampling

11% of 7x7 boards were unique. 0.5% of 10x10 boards were.

At 0.5% you are counting solutions on two hundred boards to keep one, in a browser, while somebody waits. It is not a tuning problem. Larger boards have more room for a second answer to hide, so the approach gets worse exactly where you need it most.

Repair instead

Here is the move. Do not throw the board away. Ask the solver for the second solution — the specific rival — and then make one targeted edit that kills that rival and cannot touch the answer.

For Star Battle, the edit is: take a cell the rival crowns and the answer does not, and move it into a neighbouring region. The rival now holds two stars in that region, which is illegal, so it dies. The answer is untouched, because the answer's star for that region is somewhere else entirely.

That is not a heuristic. It is a proof, and it is a short one: the edit is constructed from the difference between the two solutions, so it is guaranteed to bite exactly one of them.

Yield went to 100% at every size, 2.9 to 29.8 milliseconds a board — roughly a 40x improvement.

The question worth carrying to any generator that rejects: is there an edit that kills this specific rival and cannot kill the answer? Often there is, and it is much cheaper than rolling the dice again.

Verify the counter before you trust anything it says

While building the same batch, Daily Loop — Slitherlink — shipped every board with a number in all thirty-six cells. Not a subtle imbalance. Every clue, every board, no carving at all.

The cause: the solution counter returned 0 on a board with exactly one answer. Carving works by removing a clue and checking the board still has one solution; a counter that says 0 means "removing this broke it", so nothing was ever removed. Nothing threw. The generator did exactly what it was told.

Two separate bugs produced it, both invisible to reading the code:

  • Cells off the edge of the grid are outside, and known to be outside. We treated them as undecided, which meant no clue could ever fire and every board rated unsolvable.
  • The reachability rule counted two outside areas on opposite edges of the grid as disconnected — when in fact they meet around the outer face. It needed a virtual outer-face node in the union-find.

A 25-line brute-force scan on a 5x5 found both in one run, and it is now a permanent check in the suite. Verify what the counter says about a known board before you believe what it says about your generator.

Follow the grain of the puzzle

We wanted a difficulty tier that meant the same thing across sizes. It does not.

For Twos — Binairo — hunting a 10x10 solvable without contradiction cost 590 milliseconds a deal, against 36 milliseconds for the tier that size produces naturally. We were fighting the puzzle.

So the bands now pair each size with the tier it deals happily, and the copy says the honest thing: Expert is a bigger Hard, not a harder one.

The difficulty knob is rarely the obvious one

Three games, three different levers, none of which we would have guessed:

  • Star Grid: the one-cell region — a star given away free. We tried forbidding it, on the grounds that it looks like a defect. That produced zero Easy boards at every size and tripled generation cost. The apparent defect was the only source of a beginner tier.
  • Daily Loop: carve depth. Minimal boards nearly always need proof-by-contradiction. Leaving clues on is what makes Easy easy.
  • Twos: board size, as above.

And one that beat us

Kakuro resisted every technique here and did not ship in that batch. Fill-then-test is 0% unique — 12 of 12 boards had four or more answers, because a 2x2 diagonal swap preserves every run sum. Repair-by-blocking hits 1%, because blocking leaves illegal length-one runs. Mean run length was already 2.7 at every density, so density is not a lever either.

It needed a construction that forces uniqueness, built outward from the run-combination table, rather than any amount of tuning. It got one later, and that is its own story.


Play them: Star Grid · Twos · Daily Loop

Related reading: we deleted our puzzle bank · cap the generator, not the solver