Every game we ship with difficulty bands needs a reference line — some mechanical way to say "this board is harder than that one" that does not come down to a person's opinion on a Tuesday. For Bubble Shooter that meant a solver that plays a board and reports how many shots it took. Call that par, band the levels by it, done.
The first version reported par 7 for boards that take a real player thirty-odd shots to clear.
Why it was so wrong
The solver could place any colour still on the board at any reachable cell.
That is a perfectly reasonable thing for a solver to be able to do, and it is also not the game. In Bubble Shooter you do not choose your colour. The cannon deals you one, and the entire skill is deciding where to put the bubble you were given — often the wrong one, which is why you end up firing into a corner to get rid of it.
By handing the solver the choice, we had deleted most of the game and then measured what was left. It was not solving our levels. It was solving a strictly easier puzzle that happened to use the same board.
And the consequence was not academic: we were about to set the shot budget at
par x 1.2. Built on par 7, that is a budget of 8 for a board needing thirty.
Every level would have been unwinnable, and the levels would have looked fine in
the editor.
The fix is one sentence
Deal the solver the same random queue the cannon deals the player — uniform over the colours still on the board — and let it choose only where.
Par moved into a believable range immediately. No tuning, no fudge factor, no multiplier tacked on to make the numbers look right.
Model the constraint the player actually plays under, not just the rules of the game. An unconstrained solver rates the board. You need something that rates the game.
This trap is waiting anywhere a reference line is used to grade difficulty. If your solver has information the player does not, or a choice the player does not get, its numbers are about a game nobody is playing.
Three more things the sweep found
None of these are visible in a single level. All three came from generating 120 levels and printing the mean of each measure per difficulty band — which is the actual lesson of this section, ahead of any individual finding.
A silhouette that narrows at the ceiling makes par collapse to 1. We shaped levels by carving a silhouette. Some silhouettes taper towards the top, which means the entire wall hangs from two or three cells — and one well-placed sever drops the lot. Par 1. The ceiling row now has to be solid, and shapes vary the body hanging beneath it.
Clustering can paint the ceiling a single colour, which is the same failure arriving by a different road: one pop takes the whole ceiling and everything under it. Same-colour runs along the top row are now capped at two.
A board that is too easy is structurally too easy, not chromatically. Our instinct was to recolour boards that rated low. Recolouring does essentially nothing. What raises par is filling holes in the upper rows, because that re-anchors the wall so it cannot be dropped in one piece.
Assert the ramp, not the level
The thing worth asserting is not any individual number, it is that the numbers go the right way.
Bands 0 to 4 landed at par 9.1 rising to 13.6, fill 25% rising to 54%, and slack over par 5.4 falling to 3.0. Rising difficulty, rising density, shrinking margin for error — that is what a ramp looks like in numbers.
An earlier version had par falling across the bands while every level we opened by hand looked completely reasonable. You cannot see a monotonicity failure one level at a time. You can only see it in the means.
Play it: Bubble Shooter · how to play
Related reading: rate a puzzle by what breaks without it · five wrong ways to prove a racing game is fair