Devlog

A move budget is what turned placement into a decision

4 min read

Sunny Sort had no decisions in it. Not a shortage of them — none: across levels 4-20, every legal move at every turn still led to a win, so nothing the player chose could matter. Giving each level a budget of moves is what fixed that, and the measurement it took to be sure the fix was real is the interesting part.

The problem, stated as a number

Probing the shipped curve produced a uniform result: strategic depth of 1 in every level, decision width of 100% in every level, zero forced turns, and full recovery after any non-optimal move.

Decision width is the share of legal moves that keep the win alive. At 100%, placement is decoration. The board never filled far enough for the losing condition to be reachable, so the player could not be wrong.

The hypothesis was that a decision only exists when different choices lead to different outcomes — and that if every move cost something, wasted moves would acquire a price and setting up a cascade would stop being optional.

The experiment

pressure ran levels 8-15 with the budget set to the solver's own line plus a margin.

Budget Mean decision width Greedy wins Solvable
optimal+6 100% 8/8 8/8
optimal+4 100% 7/8 8/8
optimal+2 97% 6/8 8/8
optimal+0 61% 3/8 8/8

The shape of that table is the finding. Width does not decline gradually with the budget — it sits at 100% through +6, +4 and +2, and then falls off a cliff at +0. Two spare moves are enough to make almost every mistake recoverable.

The last column matters as much as the first: at every margin, all eight levels stayed solvable. Pressure was not making levels impossible, it was making them answerable in fewer ways.

Calibrating per level, not globally

A single global margin would have been wrong, because the levels differ. The generator calibrates each one:

  1. binary search for the smallest budget that still wins;
  2. add the stage's margin;
  3. widen until a deliberately weak searcher also wins.

Step three is a fairness check. A human is not a strong search, and a budget only a beam solver can meet is not a budget.

Two rules sit on top. Victory is checked before the budget, so the move that hits the target wins even if it spends the last one. And both tutorial stages have no limit at all — teaching does not combine with pressure.

The result

Across levels 7-20:

Metric No budget With budget
Mean decision width 100% 62%
Levels won with one-move lookahead 14 of 14 7 of 14
Forced turns 0% up to 33%
Recovery after a mistake 100% 61%

The target set before the experiment was a decision width between 30% and 70%. It landed at 62% with a margin of zero plus the fairness check. With a margin of 3 the width sat at 98%, which would not have been worth the change — a useful thing to have measured, because a margin of 3 is exactly what intuition would have picked as "a bit of breathing room".

Recovery falling from 100% to 61% is the number to watch, and it is the one that later forced a companion change: a level that cannot be recovered at all is a level demanding one perfect line, which turned out to be true of five of them and had to be fixed with a minimum-slack floor.

What the player sees

None of this is exposed as a number the player has to reason about. The MOVES chip counts down when a budget exists and warms up as it runs out — amber at five or fewer, red at two.

The one visible piece is that losing now distinguishes OUT OF MOVES from OUT OF SPACE. They are different mistakes and they need different corrections: one means you wasted placements, the other means you scattered them. Telling a player only that they lost would waste the entire measurement.

All devlog posts