code
An agent that builds a bot to play a game
A coding agent wrote a bot for a browser game and got it close to a perfect score in a day. Then I moved the camera.
In the UIB simulation, a browser game, missiles and drone swarms fly at three bases. You defend them by clicking weapons: cannons stop missiles, drone launchers stop swarms. A clicked weapon fires at the nearest threat.
What the bot gets each cycle: one screenshot. Red cones are cannons, green crosses drone launchers, yellow streaks missiles.
I wanted a coding agent to write a bot for it, then improve the bot by watching it lose. No machine learning: the bot is plain Python that Claude Code wrote. It gets a screenshot and can only click.
What I wanted to learn
- How to try different models on one problem, using git worktrees and OpenCode Go.
- Whether a coding agent on a strong model can redesign a live agent (here, the bot) from replays and traces of its games.
How it worked
I wrote a playbook.md with the goal and rules as I understood them. Claude Code read it, studied screenshots and wrote the bot, without looking at the game’s code.
A harness played each version on the same three 150-second games. When something went wrong, it saved the screenshots just before, with the bot’s clicks drawn on. Claude Code worked out why the bot failed and wrote the next version, which was kept only if it beat the best score so far.
Changing the score
v1 found missiles and swarms by colour (yellow and purple) and clicked the nearest weapon of the right type. It never lost a base, so by the game’s score, 150 points per kill, it was nearly perfect.
But it clicked about 10 times a second, mostly at swarms still too far away. So I changed the score: points for kills and for staying alive, minus points for every click that didn’t kill (300 for the wrong weapon, 50 for an empty one, 25 for out of range). v1 dropped to 13,717. Then:
- v2 stopped shooting at ammo bars, which turn yellow when low and looked like missiles to v1.
- v3 learned each weapon’s real reach. Claude Code matched every old click to its result and mapped where clicks kill: an oval, not a circle, because the camera views the field at an angle.
- v4 noticed its failed clicks. A killed target is gone in the next screenshot; after a failed shot it’s still there, so the bot rested that weapon for a moment.
- v5 took long cannon shots only with no swarm nearby, and scored 38,717. A perfect game is about 40,000.
Green is a kill, red the wrong weapon, grey out of range. In these 15 seconds v1 clicks out of range 150 times, v3 just 4.
v3 uses the wrong weapon 5 times, v5 once.
My rules were wrong
My playbook said a weapon fires at the nearest threat it can fight. When a cannon click got ERROR … Required: DRONE-X, Claude Code worked out that a weapon fires at the nearest threat of any type and fails if it’s the wrong one. It also found that the game’s help text is wrong (one drone click kills a swarm, not four) and that wrong-weapon shots use up ammo. It logged each with the screenshots that showed it. I hadn’t known any of them.
Then I moved the camera
So far the camera never moved, and the bot knew where each weapon was on screen. For v6, the harness turned, tilted and zoomed the camera every 15 to 25 seconds. v5 lost every base within 34 seconds.
v6 located the camera from where the bases and weapons appeared, then ran v5’s logic. It scored 21,258 but lost every base in two of three games.
The screenshots suggested bad choices: missiles reached a base while no cannon fired. In fact 59% to 75% of v6’s clicks did nothing at all. Clicking a weapon always does something, even an error, so these clicks had missed the weapon. Drone launchers are thin and sit slightly above the ground, and zoomed out, a weapon is a few pixels wide.
v4’s rule made it worse. A miss also leaves the target on screen, so the bot took every miss for a failed shot. It benched a good weapon and passed the threat to the other kind, which then fired at the wrong target. In one game it gave up on three weapons in a fifth of a second, one of which had made the kill. The base fell a moment later. The screenshots couldn’t show this: a click that hits nothing leaves nothing to see.
Same plan, four models
To find this, I paused after each camera move and clicked every spot the bot aimed at, which the bot can’t do. Then I wrote a plan for v7: count how many clicks land, read the screen to see if a click worked, retry after a miss instead of benching the weapon, and remember wrong-weapon mistakes by target, not weapon. I gave it to four models, each in its own git worktree.
| v7 built by | Score | Clicks that landed |
|---|---|---|
| v6 (start) | 21,258 | 25–41% |
| Opus 5.5 | 34,408 | 91–100% |
| DeepSeek 4.1 Flash | 35,100 | 86% |
| Grok 4.7 | 36,483 (second try) | 91% |
| Kimi K3 | stopped before a full test | – |
Average of three 150-second games per version.
All used the same clue: when a click works, the status line changes and a new line appears in the event log; if nothing changes, it missed. Each read that clue its own way. Opus measured how far old log lines jumped down. DeepSeek compared the kill counter column by column, because it slides sideways when the status message changes length. Kimi matched the screen against saved pictures of every status message, and found that the weapon-to-target beam the plan suggested doesn’t exist.
Grok’s first try benched a weapon after a single miss. Most clicks landed, but one base had no working cannon, and it scored 19,768, below v6. Its second try took its own route: after two misses in a row, it threw away its camera estimate and rebuilt it from the bases.
The three finished scores are within about 2,000 points and the games don’t repeat exactly, so I wouldn’t rank the models.
What I learned
- The same prompt to several models gives different answers. The plan said what to fix but not how, and each model, in its own worktree, found its own approach. More ideas, same effort.
- A coding agent can redesign a live agent from its replays. Today’s models can reason about a game like this and write the bot; none of its skill came from training. Claude Code studied screenshots and logs, guessed at causes, tested them and wrote the next version. Writing the playbook and score formula felt like building an RL environment: rules, a reward, and a player that improves by trying. Except what improved was a coding agent rewriting the bot, not a model being trained.