Press A, Enter or click the box to continue.

Claude and Jev play Pokémon

Pokémon Red, in an emulator, played by a bot with no strategy of its own. It lists what it could do next and asks Jev, a decision model, to pick. Every five minutes a Claude Code session reads the run logs and rewrites the bot. On 28 September 2026, 29 hours and 53 minutes after a fresh start, the bot walked into the Hall of Fame.

The code is on GitHub. Everything below is measured from the run logs.

Thirty hours in one line

The game keeps 261 story flags, from picking a starter to the Hall of Fame. The line is how many were set at the end of each five-minute run. Red ticks are badges. Shaded stretches are where the bot was stuck. The wobble at the end is the Elite Four resetting its flags every time the bot lost.

Eight badges

Seven of the eight needed a deep session first: a longer Claude session told to diagnose before coding. Each one opened with the same finding. The bot was not choosing badly, it was seeing the map wrong. The pictures are the last frame of the run each badge was won in.

  1. Route 3, a trainer talking to Red

    Brock Pewter City, 1 h 36 m

    The first stall was a loop between Viridian City and its Mart. Three sessions blamed Jev. The first deep session found the walk-to-edge routine stopped one row short of the map seam, the south exit was marked failed after eight ticks whether or not the bot had moved, and the map data lagged a moment behind every warp. Brock fell to a Lv15 Bulbasaur.

  2. A battle screen: Ivysaur, nicknamed AAAAAAAAAA, at 37 of 75 HP

    Misty Cerulean City, 3 h 32 m

    Mt Moon took two hours and four blackouts. Money went from ¥1,929 to ¥120 in twenty minutes, the poorest the bot ever was. Every ladder on the bottom floor leads to the floor above, so the bot's door-merging left Jev exactly one option: the ladder it had just come down. A Zubat finished Ivysaur at 11% HP because the heal option claimed a wild fight costs 8%.

  3. Red on Route 11 beside tall grass

    Lt. Surge Vermilion City, 5 h 42 m

    With Cut in the bag the bot wandered Route 11 and Diglett's Cave for fifty minutes. One run asked Jev the same destination question 1,522 times. After cutting the tree in front of the gym it gave up on the door instead of walking in, and the tree grew back.

  4. Celadon Gym, Red among the trainers and trees

    Erika Celadon City, 7 h 47 m

    Rock Tunnel gave the bot Onix and Geodude, its first captures since the starter, and a twenty-minute freeze. In trainer battles it answered yes to "Will RED change POKéMON?" and then re-selected the Venusaur that was already out, forever.

  5. Route 18, Red walking between trees

    Koga Fuchsia City, 13 h 0 m

    The longest stretch, three different blocks. The Rocket Hideout's arrow tiles look like plain floor to the bot, so it slid over the same one for half an hour. At the Route 12 gate it pressed the Poké Flute's menu buttons before the menu opened, and one run made zero Jev calls in nineteen minutes. On Route 14 it always crossed at the row that lands in a four-tile pocket behind a Bird Keeper.

  6. Saffron Gym, Red between two teleport pads

    Sabrina Saffron City, 17 h 44 m

    Silph Co took 2 h 43 m and five deep sessions. The Card Key's corridor cannot be reached on foot at all; the only way in is a teleport pad two floors up. Locked doors open when you face them and press A, not when you walk into them. Sabrina's room has walls on every side and is reached by a chain of pads. The bot could only walk, until a session read every pad's landing square from the ROM.

  7. Cinnabar Island, Red beside the water

    Blaine Cinnabar Island, 22 h 15 m

    Lapras knew Surf and the bot had the badge for it, but water was never walkable. Once it was, the bot went down through Seafoam Islands and looped between the lower floors, then sat in a sealed pocket of sea west of the islands pressing left into rock. It gave up on the sea and walked to Cinnabar the long way, back through Lavender, Pewter and Pallet Town.

  8. Viridian Gym, dialog reads RED as a warm up for

    Giovanni Viridian City, 22 h 30 m

    The fastest badge: fifteen minutes, three sessions, $1.04. The session before noticed Jev was still being told to get badge 7 from Blaine, and wrote goals for badges 7 and 8.

Twelve tries at the Elite Four

Five fights in a row with no healing between rooms, and a blackout halves your money. Each column is one attempt, from Lorelei at the bottom to the Champion at the top. A filled room was beaten. A red room is where the attempt ended.

Room 123456789101112
Battle screen: Exeggutor Lv61 against a Haunter nicknamed AAAAAAAAAA, Lv36, 43 of 84 HP, moves Lick, Confuse Ray, Night Shade, Hypnosis
Attempt 6, the first time in the Champion's room. Venusaur fainted early and the bot spent fifteen minutes switching two Haunters against Exeggutor, neither with a move that could hurt it.

What the seventeen deep sessions on the League found, in order: the bag was full so the shop sold nothing and the bot arrived at Agatha with no potions; the two Haunters had the same level and HP so the bot read the wrong one's PP; Solarbeam and Mega Drain were spent on the wrong rooms; Max Potions went to Haunters while Venusaur, revived to half, was never offered one; the shop bought only Max Potions, which cannot revive; nothing could cure paralysis; damage estimates had no type bonus, so a Jynx hit estimated at 28 was really 112. The winning code was applied at 00:52. The last three sessions tested it, found it won about half its passes, and shipped nothing.

The live pass is a coin flip.

Session summary, 01:27 UTC, seven minutes before the win

Who decided what

Jev picked

The bot never decided anything itself. It collected the options that existed right now, attached facts to each, and asked. Before the win it asked 17,673 times, always the same shape of question, at about a third of a second and $0.000037 each. All of it cost $0.65.

Where should the player go next on this map?10,292
Which move this turn?3,780
What next: progress, heal, train or shop?2,263
Wild battle: fight, switch or run?593
Keep the current Pokémon or switch?497
Everything else: elevators, shops, TMs, boulders, the starter248

A fifth of its answers were near-certain and a fifth were below 30% confidence. The starter was Bulbasaur at 26%. This is the question from Route 22, where the rival's Charizard had just beaten the bot three times running:

CHARIZARD Lv53, Fire and Flying, 145 of 154 HP, against VENUSAUR Lv64, Grass and Poison, 125 of 204 HP. Venusaur has no damaging move with PP that can hit Charizard.

Cut
Normal, power 50, PP 26 of 30, neutral against Fire and Flying, expected-damage score 48
Growth
Raises Special; after a few uses shows Nothing happened!
Leech Seed
Drains an eighth of its max HP every turn, useful when our damaging moves are resisted
Mega Drain
A quarter damage against Fire and Flying, PP 4 of 10, score 15

Jev: Leech Seed, 75%.

Claude fixed

304 sessions. 257 of them were about a minute long and cost $0.31: relabel the exits for the next leg, add a fact, fix one menu. When three sessions in a row made no progress on badges, story flags, Pokémon owned or maps visited, the next one became a deep session: thirty minutes, diagnose before coding, prove the fix in a test that gets past the block. There were 47 of those. They were 15% of the sessions and 54% of the $176.52.

Every long stall was a mechanics bug. The deep sessions were told to check the notes' diagnoses against evidence, and nearly every one opened by ruling Jev out.

What was actually wrongFor example
The bot's picture of the map was incompleteArrow tiles in the Rocket Hideout. Teleport pads to the Card Key and to Sabrina. Locked doors need A, not walking. A rock read as water while surfing.
Stale reads and cachesMap data from before the warp. A cached destination re-run without pressing anything. Failures remembered per map name on a map split in two by a gate.
Menu timingThe Poké Flute pressed before its menu opened. "Will RED change POKéMON?" answered yes forever. The lobby clerk not responding after a loss.
Missing mechanicsSurfing. Pushing boulders. Healing between Elite Four rooms. Curing a status problem. Throwing something away when the bag is full.
Wrong facts given to Jev"The boulder switch stays pressed." "A wild fight costs 8% HP." Damage estimates with no type bonus. Goal text still naming a badge already won.
Resource strategyWhich room gets Solarbeam. Revive against Max Potion. A Lv86 carry with a Lv22 to Lv40 bench.

What the bot became

The host runs one file, bots/bot.js, inside a V8 isolate with no Node, no network and a 128 MB cap. It hands the bot a set of synchronous calls and hot-reloads the file whenever it changes. That is the whole contract Claude had to write against.

// Passed in, and also available as globals. Every call blocks until it is done.
export default function play({
  press, hold, wait, frame,        // buttons, a frame at a time: A B START SELECT UP DOWN LEFT RIGHT
  state, party, box, bag, battle,  // read straight from the game's memory, never written
  screen, menu, sprites, event,    // text rows, dialog, the ▼ prompt, the cursor, story flags
  grid,                            // this map: walls, grass, water, ledges, doors, NPCs, blocked moves
  read, sym, rom,                  // bytes by pokered symbol; species, moves, items, maps, type chart
  jev,                             // jev(state, questions) → { answers, usage }, billed to the host
  save, load, snapshot, log,
}) { /* ... */ }

The first thing the bot ever saw of a map came from grid(), as text. This is the legend it had to read the world through.

PALLET_TOWN 20x18
 ##########,,########      .  walkable            ,  tall grass
 #..........@.......#      #  wall or obstacle    ~  water (needs SURF)
 #...####....####...#      T  cuttable tree       C  counter (talk across it)
 #..##D##...##D##...#      v < > ^  ledge, jump over it that way
 #..N...............#      D  door, stairs, warp  N  NPC or object   @  you

The starting point

A fresh start puts a 40-line bot in place. It presses through Oak's intro, takes the preset names RED and BLUE, and stops in Red's bedroom. Everything the finished bot does, it learned from here.

export default function play({ press, wait, screen, state, log, snapshot }) {
  if (state().playerName === 'RED') { log('already past the intro: nothing to do'); return; }
  for (let i = 0; i < 400; i++) {
    const s = screen(), st = state();
    if (st.playerName === 'RED' && st.map === 'REDS_HOUSE_2F' && s.nonMapTiles === 0) break;
    if (isNamePrompt(s)) { press('DOWN'); press('A', 4, 20); continue; }
    press(s.hasTextBox || s.cursor ? 'A' : 'START', 4, 20);
  }
  wait(60);
  snapshot('bedroom');
}

The loop it ended with

Thirty hours later the file is 3,699 lines, plus a 35-line helper. It is still one function with one loop. Each tick reads the state, deals with whatever the game is showing, and if nothing is in the way, asks Jev what to do next and walks there. Condensed to its skeleton. The names are the real ones, except the menu line and the heal line, which each stand in for a couple of hundred lines of special cases.

export default function play() {
  if (gstate().playerName !== 'RED') intro();
  for (let tick = 0; tick < 100000; tick++) {
    const st = gstate(), s = screen(), p = party();
    if (moveLearnStep()) continue;               // "wants to learn X": Jev picks which move to forget
    if (st.inBattle) { battleStep(); continue; }  // pickMove, askSwitch, catch or RUN
    if (st.map !== loopMap) { settleMap(st.map); continue; }   // grid() lags a warp by a few frames
    if (openMenuWithNoQuestion(s)) { press('B'); continue; }   // the flute bug, never again
    if (s.hasTextBox || s.waitingForA || s.cursor) { press('A'); continue; }
    if (maybeLobbyShop(st) || maybeFieldHeal(st) || maybeTeachTM()) continue;
    const gl = goal(st, p);                       // Jev: progress, heal, train or shop
    if (gl === 'heal') { walkToNearestCenter(st); continue; }
    if (solveVRBoulder(st) || elevatorStep(st)) continue;
    goDest(st);                                   // Jev picks a door or a map edge; code walks there
  }
}

Under that loop sit the pieces the deep sessions added, most of them a few dozen lines each: a breadth-first pathfinder over grid() that treats ledges and Rocket Hideout arrow tiles as jumps, a table of Saffron Gym teleport pads read from the ROM, a surfing mode that makes water walkable, a boulder-push planner for Victory Road, a damage estimator for every move Jev is offered, and a PP budget across the five League rooms. The file carries 204 comments that begin with a session id, one for every fix, and 12 of them are marked as a root cause.

Every question, one shape

The helper file has three functions, and the bot asks Jev through this one at 21 places. The context is a plain object, the options are names mapped to facts, and the fallback is what to do if Jev is off or refuses.

export function askChoice(context, instructions, options, fallback) {
  try {
    const { answers } = jev(context, { pick: { type: 'choice', instructions, criteria: options } });
    const a = answers.pick;
    log(`jev: ${a.choice} (conf ${a.confidence})`);
    return a.choice in options ? a.choice : fallback;
  } catch (e) { log(`jev failed: ${e}`); return fallback; }
}

What those places ask, in the order they appear in the file: the Cinnabar Gym quiz, the starter, which move this turn, whether to switch after a faint, whether to switch when a trainer sends out something new, what to do next, which item to throw away, what to buy in the Indigo lobby, whether to use a healing item before the next Elite Four room, which floor in the Silph Co elevator, which floor in the Rocket Hideout lift, where to go next on this map, which move to forget (two places), whether to swap a boxed Pokémon in, which TM to teach, which member gets it, which boulder goes on which switch (two places), whether Lapras learns Surf, and who learns Strength.

How it grew

Lines of bot code at each milestone. Nearly all of it was added in the deep sessions, and nearly none of it was ever removed.

First run40
Badge 1, Brock467
Badge 2, Misty649
Badge 3, Lt. Surge992
Badge 4, Erika1,166
Badge 5, Koga1,678
Badge 6, Sabrina2,291
Badge 7, Blaine2,812
Badge 8, Giovanni2,833
Entering the Elite Four3,303
The winning run3,734

Alongside the code, the sessions kept bots/NOTES.md, one section per session, 3,334 lines by the end. Each session had to edit a copy of the bot, run it against the real game from the live save state, and get at least one answer from Jev in that test, or its changes were thrown away. Eleven were.

Every blackout halves your money

The bot earned ¥432,138 in prize money and finished with ¥32,234. Each drop is a loss. The steepest are Mt Moon, the rival on Route 22 three times in half an hour, and the Elite Four.

Trainer card

The bot mashed A through the nickname prompt for Bulbasaur and for every capture after it. So the player is RED, the rival is BLUE, and all six Pokémon are called AAAAAAAAAA. The game announced "RED got on AAAAAAAAAA!" 94 times, once per surf.

TRAINER CARDRED
TIME
29:53
MONEY
¥32234
BADGES
8
POKéDEX
10 OWN 119 SEEN
A PRESSES
532316
BATTLES
1161
RAN AWAY
504
ZUBAT MET
176
CUT USED
618
THUNDERBOLT
3
  • AAAAAAAAAAVENUSAUR L89
  • AAAAAAAAAAGEODUDE L22
  • AAAAAAAAAAHAUNTER L40
  • AAAAAAAAAAHAUNTER L38
  • AAAAAAAAAAHAUNTER L33
  • AAAAAAAAAALAPRAS L18

Nobody stopped the bot after the credits. It walked back to the League and beat the Champion a second time at 02:57, then spent five hours in Victory Road, where Venusaur reached Lv99.