Skip to content

FRLG AutoStory 00.28-00.29: through Rock Tunnel to Lavender Town - #293

Draft
dayfine wants to merge 29 commits into
frlg/checkpoint-replayfrom
frlg/stage8-rocktunnel
Draft

dayfine wants to merge 29 commits into
frlg/checkpoint-replayfrom
frlg/stage8-rocktunnel

Conversation

@dayfine

@dayfine dayfine commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Depends on #258 (base frlg/checkpoint-replay; the diff shows only this change).

The table stopped at Route 10's Pokemon Center (00.27). 00.28 cuts the tree (10, 20), goes through Rock Tunnel without Flash, fights the nine trainers the way can't go round (Cooper by the walker on a leg to a wall, as Mt. Moon's Grunt) and saves on Route 10 by the south mouth. 00.29 goes down Route 10's east side past Carol, Clark, Trent and Herman, hops the ledge (11, 74) and walks to Lavender Town's nurse. Tunnel legs end on walls, ladders or people, and open legs are followed only by legs that a tile of error can't send astray (the 00.27 lesson). No row uses a Repel, because the table has no step for it. The map resource lock moves to PokemonSimulation 6c2f5c8 (dayfine/PokemonSimulation#80), which adds RockTunnel_1F/B1F, LavenderTown and its Center from the decomp. The only change there to an existing map is Route10's links to the new maps (no tile, elevation or object changes).

Testing: there is a new AutoStoryLavender sim suite. It runs each row whole, starts the rest of the row a tile short or long after every open leg, and adds a wild battle mid-leg in the tunnel. The FRLG AutoStory filter passes except the 12 known FileException image tests, StoryReplay passes 18/18, the step-table lint finds no unbound step, and code health has 0 failures. Nothing was checked on a console. The detectors are unmeasured under Rock Tunnel's darkness, and the plan for measuring them is in ~/FRLGLab/notes/2026-11-09-0028-dark-measure.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss

dayfine and others added 2 commits November 9, 2026 10:12
…nter

00.28 goes from Route 10's Pokemon Center through the tree (10, 20) into
Rock Tunnel, fights the nine trainers the way can't go round (Cooper by
the walker on a leg to a wall), and leaves by the south mouth onto
Route 10, where it saves. 00.29 goes down Route 10's east side, hops the
ledge (11, 74) and walks into Lavender Town to its nurse. Tunnel legs end
on walls, ladders or people; open legs are followed only by legs a tile
of error can't send astray.

Maps: the FRLG map resource lock moves to PokemonSimulation 6c2f5c8
(dayfine/PokemonSimulation#80), which adds RockTunnel_1F/B1F,
LavenderTown and its Center, decomp only. Lavender Town is a save card
place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
PokemonFRLG_Tests.cpp is at the 1000-line limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 4 commits November 9, 2026 10:56
…ock Tunnel

Console B, 2026-11-09: B1F row 37 met a wild battle 10 to 14 tiles into
every try and the walker gave up after 9 stops. Red: both fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…est tiles

Rock Tunnel B1F row 37 (console B, 2026-11-09): a wild battle 10 to 14
tiles into every try of "west 16". The only single hold that found the
player went east to the wall (45, 37), behind the leg's start, and the
way back walked all 32 tiles again, until the route gave up. The
reanchor search now also tries each first hold with the fewest holds
after it, and plan_reanchor() takes the way whose holds and way back
walk the fewest tiles together: there, south to row 38 and west to the
wall (1, 38), then 13 tiles back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
The walk waited for a warp's fade only once the hold onto the warp had
ended, so a fade that was over by then went unseen and the walk stopped on
"no fade in and out after walking onto the warp" though the player had
warped (00.27, console B, 2026-11-09 01:41, the warp to CeruleanCity: the
watch started about 1.3 s into the hold). The walk and the recovery walk
now show the hold's frames to the same BlackScreenOverWatcher
(Pokemon::WatchAlong) and wait on with it only if it has not fired. The
watcher and its thresholds are unchanged, so a dim textured floor is still
no fade.

The overworld sim's walker watches for the fade from where the console
does (SimWalkerRules::watch_fade_along_hold), and fails on a warp whose fade
it missed, as the console does. OverworldWarpFade walks out of Viridian
Mart with a 200 ms fade that is over before the hold ends: seen when
watched from the start of the hold, missed from its end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 8 commits November 9, 2026 11:19
… player

Rock Tunnel 00.28 with a wild battle every 7 to 25 steps (sim) still lost
the open legs' progress after the first fix:
- Walk on: after an open leg stops, if the route's next holds bring every
  tile the player may be on to one tile (and none of those ways crosses a
  trainer's sight off the planned way), the walker walks on the rest of
  the leg from the middle candidate instead of finding the player at
  walls first. The player still faces the leg's way, so no turn tap.
  A stop on the walked-on leg counts from every tile it may start on.
- A leg onto a warp stopped by a wild battle, where no trainer may see the
  player, holds on to the warp as off the grass: below Rock Tunnel 1F's
  ladder (20, 13) the holds that found the player went down a column
  Hiker Lucas looks over.
- plan_reanchor() weighs open-leg tiles 4x over held ones, and puts ways
  whose holds cross a trainer's sight last.
- The sim looks for trainers before rolling for a wild battle, as the
  game does (pokefirered ProcessPlayerFieldInput()).

OverworldReanchor rules and two OverworldSim rule tests follow the new
choices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
… walk on through open legs

The 00.28 sim with a wild battle every 7 to 25 steps now passes at all 12
seeds (before these changes: 0 of 12).
- A step into a trainer's line that sets off a wild battle took its
  battle screen for the trainer's, and the battle step after it found no
  message. The step now watches 5 s for a wild battle, runs from one seen
  with no message before it, and steps on (program and sim). The game
  looks for trainers before it rolls for a wild battle, so the trainer
  didn't see the player on that step.
- Walking on after a stop may go through open legs of the plan before the
  hold that finds the player (Rock Tunnel 1F row 31: west, then south 1,
  then the hold west to (12, 32)); a stop on any of them counts from every
  tile it may start on.
- A hold to a wall where wild battles start, stopped right away after
  another interrupt (Cooper, then a wild battle), is walked again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
SimScript::encounter_rates rolls each step onto a wild tile as pokefirered
TryStandardWildEncounter() does (8 - rate / 10 cooldown steps at 5%, then
rate * 16 / 1600 plus a buff that grows with each roll lost; a battle
clears both), seeded per run. Rock Tunnel 1F/B1F use rate 7, Route 10 21
(wild_encounters.json). SimState counts wild battles and their time.

New test: 00.28 at those odds from 30 seeds must all pass, and logs the
runs' numbers. At the same seeds, before this PR's walker fixes 13/30
passed; now 30/30. Over passing runs, before -> after: time 1037 -> 912 s
mean (p90 1190 -> 1004), tiles 535 -> 406 (p90 679 -> 437), tiles walked
again 158 -> 3 (p90 302 -> 34), wild battles 22.6 -> 17.7, time lost per
battle 4.5 -> 0.3 s (p90 6.6 -> 2.0).

With trainers checked before wild battles in the sim, Cooper is always
fought: the 7-to-25-step test no longer allows him unfought.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
Console B run 2 of 00.28 lost the one-tile step north onto B1F row 34
after a wild battle on row 38; the walk east "to the wall (17, 34)" then
ran along row 35 to the rock (20, 35) and martha_line found no trainer.
below_martha_line now ends at (16, 35) and martha_line walks up column 16
(or 17, a tile short) into her line; below_allen_line starts with the
walk east to the wall (17, 33). Sim test replays the console's battle and
the short step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…f missed

Console B lost the one-tile step north onto Rock Tunnel B1F row 34 in
00.28 run 2: the game missed the 40 ms turn tap, the leg's short hold then
only turned the player (pokefirered PlayerTurnInPlace(), 8 frames), and
the leg was taken as walked. OverworldWalkSteps::turn_to() taps, reads the
facing (read_player_facing()) and taps again while it shows another way,
up to 3 taps; an unreadable facing is taken as turned. Both walkers,
step_once() and face_wall() turn with it.

Sim: SimScript drops turn taps by number or at a rate; sim_turn_to()
mirrors turn_to(), switched by SimWalkerRules::confirm_turn. New tests:
a dropped tap before a one-tile leg (red without the rule), and 00.28 at
the game's odds with 1 in 10 turn taps dropped, 30 seeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…thod

Code health: OverworldSim kept at 7 public methods (sim_turn_to() reads the
state) and OverworldSim_Tests under 1000 lines (the turn tap cases moved to
OverworldWalk_Tests, as a table).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 4 commits November 9, 2026 13:36
A lab deployment without PokemonFRLG/PlayerSprites/red_normal.png failed
00.28 with a FileException; no test checked the files the readers load.
autostory_resource_paths() lists the files AutoStory and its readers and
detectors load, and the test fails naming each one missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
Move the walker's pace, tap and release times into
PokemonFRLG_MovementMechanics.h with the tap rules measured on console B
(2026-11-09, Route 10 Center 1F): a tap from a standstill only turns a
player facing another way and steps once for one facing its way, a step
tap within 620 ms of the last one's start is ignored, and taps with no
release between them are one hold. OverworldSim now moves its player by
these rules; the random turn-tap drop is gone (dropped_turn_taps stays as
fault injection for the turn check).

Red: sim_step_once() still taps at the turn tap's pace (340 ms), so three
step_once() lose a step under the lockout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
step_once() tapped 120 ms and released 500 ms, 620 ms from tap to tap:
inside the band where the console's lockout ends (a step tap 566 ms after
the last is ignored, 666 ms works). It now taps STEP_TAP (66 ms) and
releases STEP_TAP_RELEASE (700 ms), 766 ms apart, and sim_step_once()
does the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

FRLG movement mechanics are now one table, Overworld/PokemonFRLG_MovementMechanics.h. It holds the pace, the turn and step taps, the releases, and the tap rules measured on console B on 2026-11-09. The walker, step_once() and OverworldSim all read it.

  • Sim: a tap from a standstill only turns a player who faces another way. It takes one step if they already face that way. A step tap that starts within 620 ms of the last step tap's start is ignored. Taps with no release between them count as one hold. The random 1-in-10 turn drop and its MC case are gone. dropped_turn_taps is kept only as fault injection for the turn_to() facing check.
  • Walker spacing: step_once() tapped 120 ms and released 500 ms, so its taps started 620 ms apart. That sits in the unmeasured band between 566 ms (ignored) and 666 ms (worked). It now taps 66 ms and releases 700 ms, 766 ms apart. One-tile legs come at least 666 ms apart (run) or 733 ms apart (walk) because of the 500 ms after-leg watch, so they are unchanged. hold_to_warp() can start its long hold inside the lockout, but the hold has two tiles of slack.
  • Tests: OverworldWalk "tap:" cases replay the console measurements, plus three step_once() calls in a row. That case was red with the sim's old 340 ms tap and is green now.
  • 00.28 MC, seeds 1–30, game odds: before 30/30 passed, time mean 920.7 s / p90 1008.2 s, tiles walked again mean 5.8 / p90 52.0. After 30/30 passed, mean 938.0 s / p90 1026.8 s, tiles walked again 5.8 / 52.0, and 0 step taps ignored. The added time is the step tap spacing, now 766 ms where the sim used to tap every 340 ms.

dayfine and others added 4 commits November 9, 2026 14:01
…s over every row

Each row with a start runs on the overworld sim clean, then with the
classes of failure console B showed injected everywhere they can happen:
a scene that moved the player ending on each open tile next to the
table's pose; wild battles at pokefirered's land encounter rates on every
map the story walks, 30 seeds, logging passed, time, tiles walked again
and time lost per battle; and every warp's fade 1.5 s late. Steps the sim
does not play put the player on their pose_after; scene triggers and
leaders' battles it has no script for pass where the clean run let them.
00.06 is known failing: two seeds give up on Route 1 after 9 stops.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
… 35)

Rock Tunnel without Flash is black but for a lit circle on the player.
The battle text box and black screen checks read it as a battle on its
way, so the check before each step ran a 15 s battle loop, and an open
leg's stop was placed 0 to 3 tiles in when the battle came at its end
(console B, 2026-11-09 13:35). The walker then "found" the player at the
rock (20, 35) while they stood against the wall (25, 35).

- dark_cave_screen(): black corners, lit middle. The observer reports
  nothing on it, and the check before or after a step sees no battle.
- A stop on an open leg that started in a dark cave may be anywhere on
  the leg (whole_leg()), whatever the timing says.
- below_martha_line: west 16, north 2, west 2 to the corner (11, 35),
  east 5 to (16, 35). The corner finds the player from any of
  (11..14, 35) and (11..16, 35) after a battle.
- Sim: SimScript::dark_maps, with walker rules dark_cave_not_battle and
  whole_leg_in_dark; 00.28's game-odds runs walk Rock Tunnel dark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

00.28 steps 14/15 (console B 13:35 failure): fixed in f6080f0 + 79bfa97.

  • Rock Tunnel's dark overworld (black corners, lit middle; dark_cave_screen()) is no longer read as a battle on its way, before or after a step, or by the screen observer. The thresholds are not measured on console frames yet.
  • An open leg that starts in a dark cave counts a stop as anywhere on the leg (stop_tiles() → whole_leg()). This replaces the timing, which placed the battle at the end of east 12 at 0–3 tiles in. plan_reanchor() already says "found" only when every candidate ends on the same tile, so the 5-tile miss came from the dishonest candidate set.
  • below_martha_line is now west 16, north 2, west 2 to the corner (11, 35), east 5. After a battle, the corner finds the player from any of (11..16, 35).
  • Sim: SimScript::dark_maps, walker rules dark_cave_not_battle and whole_leg_in_dark. New tests: "B1F dark, the 13:35 legs" (red without the rules on 13 of 53 battle steps, green with them) and "B1F dark, a wild battle on any step to Martha's line" (steps 1–40, on time and late). The game-odds MC now walks the tunnel dark.
  • MC 00.28, seeds 1–30. Before (old code, sim without darkness): 30/30, time mean 938.0 s / p90 1026.8, tiles walked again mean 5.8 / p90 52. After (dark modelled): 30/30, mean 1023.4 s / p90 1177.1, tiles walked again mean 61.7 / p90 118.

Not done: the measured north tap. The corner clip covers row 35. Step 15 accepting a bump into Martha is also not done.

dayfine and others added 3 commits November 9, 2026 15:20
Console B, 2026-11-09, 00.28 hiker_lenny in Rock Tunnel without Flash:
the battle was won, and the dark overworld after it read as the battle's
text box with no HP box. The loop held B for an evolution three times
for 20 s, then failed with "no battle screen and no START menu for 60 s
after the battle".

The sim now plays the battle loop's end after each trainer's battle
(battle_end_on_sim(), on EvolutionHold and BattleEndWatch), with the
dark cave's overworld when the map is dark. New: a decisions test, an
00.28 dark sim test, and a DARK_CAVE fault sweep. All fail until the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
After a battle won in Rock Tunnel without Flash, the dark overworld read
as the battle's text box with no HP box, which EvolutionHold took for
"is evolving!". The loop now reads a dark cave's screen
(dark_cave_screen()) as no battle screen and holds nothing on it, so
START looks for the overworld within seconds, as after any battle. An
evolution's scene keeps its text box in the bottom corners, which a
dark cave's are not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

db14a25: fix for 00.28 hiker_lenny (lab-B 2026-11-09 15:11). After a win in Rock Tunnel without Flash, the dark overworld read as the battle text box with no HP box, so the loop held B for an "evolution" 3 x 20 s and then gave up (no START menu for 60 s). Now a dark_cave_screen() frame reads as no battle screen and EvolutionHold never holds on it, so START finds the overworld within seconds.

Red first (2b8b70e): a decisions test, an 00.28 dark sim test, and a new DARK_CAVE fault sweep (Rock Tunnel dark, with no wild battles and with 10 seeds at the game's odds). The sim now plays the battle loop's end after every trainer battle, including the walker's. Tests: PokemonFRLG::AutoStory 363 passed / 13 failed, the same 13 as the baseline (11 AutoStoryBattle images, eng_chansey, Resources). Code health: 0 failures.

🤖 Generated with Claude Code

https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss

@dayfine

dayfine commented Nov 9, 2026

Copy link
Copy Markdown
Owner Author

5e00efa on #213: FRLG's look between steps (ConsoleScreenLook) now goes through Pokemon::SwitchHomeObserver, so a step that starts on the HOME menu fails as game_not_running before any input (the same as BDSP, from #122). Merged down to here (a22824f). Local headless runner: PokemonFRLG::AutoStory 363/13 (the known image cases and AutoStoryResources), Pokemon:: 210/0.

…iner's line

Console B, 2026-11-09 15:50, 00.28 winston_line: a wild battle on the
second of three taps into Winston's line was run from, and the third tap,
sent as the battle's black ended, was lost; the step failed no_trainer.

After a wild battle run from mid-line (and in the capture pacing), the
next tap now waits until the overworld has shown nothing for
AFTER_BATTLE_QUIET (wait_for_quiet_overworld() over the shared
ScreenLook/ScreenSettle). The sim ignores taps for BATTLE_EXIT_LOCKOUT
after a battle, and the fault sweep gains LOST_TAP: a wild battle on each
tile of each trainer's line before its last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
@dayfine

dayfine commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

413f1cb: 00.28 winston_line lost its 3rd tap after a wild battle on the 2nd (console B 15:50). Taps after a battle run from mid-line now wait until the overworld has shown nothing for 1 s (wait_out_battle_exit() over the shared ScreenLook/ScreenSettle), as does capture pacing. The sim now ignores taps for 0.5 s after a battle. Added the red regression test "00.28 B1F dark, winston_line: a wild battle on the 2nd tap" and a LOST_TAP fault sweep (a battle on each tile of each trainer line before its last). Not built or run locally: disk was below 32 GB. Code health and a syntax-only compile of the 7 changed .cpp files pass. Mac and ubuntu-clang CI dispatched.

@dayfine

dayfine commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Built and tested 413f1cb locally (headless SerialProgramsTests, upstream/headless-tests merged in a scratch tree only). Red check: with sim_wait_out_battle_exit commented out, winston_line fails ("D-pad held west ... in the lockout after a battle"); restored, it passes. --filter PokemonFRLG::AutoStory: 399 passed / 13 failed, the 13 being the known local set (11 AutoStoryBattle images, WildEncounterReader eng_chansey.jpg, AutoStoryResources). Both LOST_TAP sweep cases (00.28, 00.29) pass. Code health: 0 failures. No code changes needed.

dayfine added a commit that referenced this pull request Nov 10, 2026
… aren't rows

#293's 00.29 (Route 10 to Lavender's nurse) passed on the console, so the
draft that reused the id is dropped. The README says what blocks 00.30 and
00.31 from becoming table rows: no Celadon or Rocket Hideout maps in the
locked runtime pack, no row to Celadon's Center, no spinner or lift step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant