5 min read

Breakout, Tuned by a Bot That Played 10,000 Games

Breakout, Tuned by a Bot That Played 10,000 Games

The Snake-in-Godot post ended with a claim: the 9 MB engine download is prepayment for a game with actual scope. Time to collect. Breakout is the next step up the arcade family tree from Pong, and it is the first game in this series with real moving parts: ball physics with angle control, bricks that fight back, falling power-ups, four levels, particles, screen shake, and sound.

It also comes with a harder question. The first three posts proved Claude Code can build games that work. This one asks whether it can build a game that feels fair. Difficulty is not a correctness property. No unit test can assert "level three is hard but reasonable." So we made the harness answer it instead, with a bot that played over ten thousand headless games while the levels were being tuned. As usual, Claude wrote everything: the GDScript, the 34 test assertions, the bot, and the embed below. My contribution was the direction and, eventually, a high score.

mouse or drag steers · click / tap / space launches · it plays itself until you do

The bot that plays for a living

The Pong post introduced the idea of a simulation harness. Breakout is where it grew teeth. The game rules live in a node-free script, same pattern as always, which means a bot can step the exact shipping logic with no renderer at about 35 full games per second.

The bot is deliberately human-shaped. It only re-reads the ball every so often (its reaction time), and its prediction of where the ball will land carries gaussian error (its aim). Three presets: a novice with 250 ms reactions and sloppy aim, a decent player at 140 ms, and a sharp one at 60 ms with near-perfect aim. A perfect bot would clear everything and tell you nothing.

Every candidate change to the game ran against all three, a few hundred games per level per skill. Here is the final validation run, 6,000 games in three minutes:

level novice decent sharp
1 Warmup 34% 100% 97%
2 Checker 35% 100% 98%
3 Scramble 15% 100% 90%
4 Fortress 9% 99% 83%

That is the shape we wanted: beginners feel a ramp, decent players clear the game but sweat the castle, and nobody hits a wall that the data cannot justify.

The perfect player is bad at Breakout

The first baseline run produced the strangest number of the whole project: the sharp bot lost to the decent bot. On the fortress level the decent player cleared 100 percent of games and the sharp player cleared 73.

The failure replays told the story. The sharp bot's aim is so good that it catches every ball dead center on the paddle. Dead center means a near-vertical bounce. Near-vertical means the ball rides a slow elevator up and down the same column of the board, clearing one brick a trip, until the game runs out the clock. Its losses were games with one or two bricks left after four minutes of tedium. The decent bot's sloppy aim, catching balls off-center, was accidentally spraying the ball at useful angles across the whole board.

Read that again, because I had to: the noise was load-bearing. Imperfection is what makes Breakout work.

The fix is old arcade wisdom rediscovered by a robot: add two degrees of random jitter to every paddle bounce. Invisible to a human hand, fatal to the elevator pattern. Sharp's fortress clear rate went from 73 to 83 percent and its average clear time dropped by twenty seconds, and the change shipped with a comment in the code explaining exactly why it exists.

Tuning by measurement instead of vibes

The first thing everyone reaches for when a game is too easy is speed. We measured it: raising ball speeds about 12 percent left the decent bot untouched at 99 to 100 percent and crushed the novice from 8 percent to 4 on the fortress. Speed was the wrong lever. It punishes weak players and does not challenge strong ones, because a competent player tracks a faster ball just fine.

The right lever was the architecture. The original fortress was a sealed ring of two-hit bricks, and the data showed everyone's games dragging: even good bots needed over two minutes because the ball could not get inside. Opening two gaps in the walls tripled the novice clear rate and cut fifteen seconds off everyone's games without making the level feel easier, just less tedious. The data also caught the level order being wrong: the fortress measured harder than the level after it, so the levels swapped places and the castle became the finale it deserved to be.

None of these calls were guesses. Every one is a before-and-after table in the commit history.

What the engine bought this time

Snake used Godot as an expensive rectangle renderer. Breakout finally spends the engine money. Brick explosions are one-shot particle emitters, five lines each. Screen shake is a decaying offset on the root node. And every sound in the game, the paddle blip, the brick shatter, the power-up chime, the descending game-over slide, is synthesized in GDScript at load time. No audio files, no downloads. The entire sound design is about sixty lines of frequencies you can read.

The economics stay lopsided: the whole game, four levels and all, compiles to a 20 KB pack file riding the same 9 MB engine. But this time the engine is earning it. The particles, the audio pipeline, the input handling across mouse, touch, and keyboard: all of it was a node or a built-in away. The browser Snake would have needed a subsystem written by hand for each.

Attract mode got an upgrade too: the demo pilot on the title screen is the same prediction bot from the tuning harness, dialed to decent. The thing playing when you arrive is the thing that balanced the game you are about to play.

Where this leaves the series

Four games now: Pong in an engine, Snake with none, Snake in the engine, and Breakout using the engine properly, tuned by simulation. The claim from post one has held up the whole way: the hard part of letting an AI build games was never the code, it was giving it a way to see the consequences. This post's version of seeing is a table of clear rates from ten thousand games nobody watched.