docs: macros.md 12.24 and the row 59 audit

This commit is contained in:
acamilo 2026-09-23 22:33:25 +00:00
parent 437dfa84a9
commit 9ccc0592b1
3 changed files with 187 additions and 2 deletions

View file

@ -1539,6 +1539,75 @@ Nothing is ranked, weighted or pressed for the fly: a button leaves the pad whil
anything and comes back when it can (a new battle resets the stages). The decoder, the reward anything and comes back when it can (a new battle resets the stages). The decoder, the reward
catalog, the adapter version, the roles and the compatibility string are untouched. catalog, the adapter version, the roles and the compatibility string are untouched.
### 12.24 The map graph is the disassembly's, piece by piece (2026-09-23, row 59)
Opened pre-emptively: the row-58 review carried the route survey past the Boulder Badge and the
fly walked Pewter City (39, 17) to Route 3 (0, 9) and back from about frame 68,000, `GO OBJECTIVE`
done on Route 3 538 times and `GO ROUTE` done on Pewter 537. Reproduced from a rank-11 checkpoint
the survey writes (`FLY_PROBE_SAVE_RANK=11`): 509 and 508, never on Route 4. The live fly got
through Route 3 anyway and met the next half on v0.6.0 at 22:20 UTC: rank 12, MT. MOON, the
objective Cerulean, on Route 4 per ten minutes `GO ROUTE` 215, `GO OBJECTIVE` 113, `GO OUT` 103,
two new tiles, in and out of the Pokécenter and the cave mouth. Route 4 was one node with Cerulean
off its east edge, which the mountain cuts off from the cave mouth's side. From the live
checkpoint the route survey on `main` walks it 930 times in 72,000 frames.
**The geography table disagreed with the headers.** Checked row by row against
`data/maps/headers/*.asm` and `data/maps/objects/*.asm` at the pinned commit:
- Route 4 is **north** of Route 3, not east (`Route3.asm`: `connection north, Route4`); Route 3's
top edge is the road to Mt. Moon's Pokécenter. Mt. Moon's doors are both **on Route 4**:
(18, 5) into the first floor and (24, 5) into B1F, whose (27, 3) is the way out. Route 3 has no
warps. So Route 3's north edge named no map, and nothing on Route 3 was the way to the rung.
- Route 14 / 15 and Route 24 / 25 had the right neighbour in the wrong column (west and east,
not south and north). Route 24's east edge is Nugget Bridge's far end, rung 16's road.
**Four maps are pieces the player cannot walk between** (12.7's rule, measured by flooding every
tile of every map on the graph from the blocks, blockset, collision list, tile-pair walls and
ledges): Route 2 as before; **Route 4**, cut by the mountain into the cave mouth's side and
Cerulean's side; **Mt. Moon B1F**, four chambers of two ladders each; **B2F**, one large piece and
two small ones. The one road through is 1F (5, 5), B1F (21, 17), B2F (5, 7), B1F (27, 3); the
other two ladders on 1F lead to dead ends.
A split row is now any number of pieces, each with its doors (the warp index and tile) and what is
one step from it, a whole map or another map's piece. Three rules keep it honest:
- **Which piece the fly is in** is what its walk can reach on the decoded grid (section 15): the
piece whose doors it reaches, when exactly one piece's are. The grid has no ledges, so where it
reaches none (Route 4 below the ledges) the nearest door answers. Flooded over every tile of
the four maps' ground in the disassembly, the rule names the right piece for all of them.
- **Which piece a door lands in** is the cartridge's own answer: a warp names the destination
warp it arrives at (`wWarpEntries` byte 2), and each piece lists its warps. An edge lands in
the piece that lists the map it is stepped off.
- **The hop is a piece**, and an exit is toward the objective only if it lands in that piece. On
1F three ladders go down to B1F and one of them is the road.
**A connection nobody can walk across is not a road.** Four header connections have no tile where
both sides are land: Pallet Town / Route 21, Cinnabar / Route 20, Route 20 / 19 (sea) and Route 22 /
23 (the League's fence). They keep their name and offer no exit and no hop. Without this the road
from Pallet Town to Cerulean was by sea, and a fly that whited out in Mt. Moon, which the survey's
did, walked into Pallet's shore every two seconds.
**One seam frame, found on the way.** Route 3's first trainer closes his challenge onto five frames
of plain overworld before `StartTrainerBattle` decides the battle (`home/trainers.asm`: it runs
after `DisplayTextID`'s close-down). Row 58's pending push-back was decided on the first of them
and walled (11, 6), the one gap between Route 3's west end and the rest of the road, for the
session. A push-back is now a refusal only once the overworld has been the fly's for thirty frames
running; a battle inside them drops it.
From the badge, the route survey reaches Route 4 at frame 70,356 and Mt. Moon at 70,707 (rung 12),
no pushed tile; after whiting out in the cave it walks the land road back from Pallet Town. The
ROM test on the stub rotation reaches Mt. Moon in 56.7 brain minutes with 4 Pewter / Route 3
crossings; the base makes 3,391 in 80.4 and never stands on Route 4. From the live 2026-09-22
rank-11 checkpoint the branch reaches Mt. Moon too, where the base ends fenced on Route 3. From
the live Route 4 checkpoint the branch is in the cave on frame 279 and stays on the road; on the
stub rotation Route 4's west doors are crossed 13 times in twenty brain minutes (whiteouts and
walks back included) against 56 on `main`.
Nothing is ranked and nothing presses for the fly: a table of maps says what the headers say, a
split map has the pieces its ground has, and a frame that was the cartridge's is not read as the
fly's. The decoder, the reward catalog, the adapter version, the roles and the compatibility
string are untouched.
## 13. Shops and Pokémon Centers (the operator, 2026-09-17: "refactor the shop macros. make it a ## 13. Shops and Pokémon Centers (the operator, 2026-09-17: "refactor the shop macros. make it a
## priority to visit the shop at least once per area; make shop macros item purchases. same ## priority to visit the shop at least once per area; make shop macros item purchases. same
## for the Pokécenter. heal should be a macro.") ## for the Pokécenter. heal should be a macro.")

View file

@ -2851,3 +2851,119 @@ of the brain, which is the deviation.
648 bytes, sha256 `4929f340...9ebd9` on v0.5.5 (`7784a9d`), and 648 bytes, `8ce67b97...a8f68` 648 bytes, sha256 `4929f340...9ebd9` on v0.5.5 (`7784a9d`), and 648 bytes, `8ce67b97...a8f68`
on `main` after the engagement rewards (adapter v7). Decoder, reward catalog, adapter version on `main` after the engagement rewards (adapter v7). Decoder, reward catalog, adapter version
and roles untouched. and roles untouched.
- `flysim --print-compatibility`: **648 bytes, sha256 `4929f340...9ebd9`**, byte-identical to the
base. Decoder, reward catalog, adapter version and roles untouched.
## 2026-09-23, row 59: the road to Mt. Moon, and Route 4 in two
### What was coming, and what came
Opened pre-emptively. The row-58 review carried the route survey past the Boulder Badge (v0.5.5's fix):
from about frame 68,000, Pewter City (39, 17) and Route 3 (0, 9) in a ring, `GO OBJECTIVE` done
on Route 3 538 times, `GO ROUTE` done on Pewter 537. Route 3's pad was `GO ROUTE` alone, because
`GO OBJECTIVE` had nothing on Route 3 to aim at. The live fly was due there with the badge.
It got through Route 3 and met the other half on v0.6.0 at 22:20 UTC: rank 12 (MT. MOON), map 15,
per ten minutes `GO ROUTE` 215, `GO OBJECTIVE` 113, `GO OUT` 103, five distinct macros, two new
tiles; the coordinator restarted flysim. The live checkpoint from inside the ring is v7. From it the
route survey on `main` (`4d82f7d`, 72,000 frames) walks between Route 4 and the Pokécenter (map
68): 930 map changes, `GO OBJECTIVE` done on Route 4 465 and `GO ROUTE` done on the Pokécenter
462 with `GO OBJECTIVE` preferred; with a uniform choice 706 changes, `GO ROUTE` 350,
`GO OBJECTIVE` 183, `GO OUT` 170, the live shape. That is 59c.
### Reproduction
`examples/scene_probe.rs` gains `FLY_PROBE_SAVE_RANK`: the route survey writes the first safe
overworld frame at a rung as a checkpoint. From the row-58 checkpoint, `FLY_PROBE_CATCH=route
FLY_PROBE_PREFER="GO OBJECTIVE,TALK" FLY_PROBE_FRAMES=120000`, the survey is byte-identical to the
review's and writes rank 11 on frame 19,620, in the gym beside BROCK
(`.local/checkpoints/survey-rank11-row59.checkpoint`, untracked). From that checkpoint, with empty
ledgers, the base walks the ring again: `GO OBJECTIVE` done on Route 3 509, `GO ROUTE` done on
Pewter 508, never on Route 4.
### The audit: every row of the map graph against the disassembly
`geography.rs` against `data/maps/headers/*.asm` and `data/maps/objects/*.asm` at `0cd19d3`, all
outdoor rows and every `LINKS` pair; then every map on the graph flooded tile by tile (blocks,
blockset, collision list, `TilePairCollisionsLand`, `LedgeTiles`, lower-left quadrant as section
15 reads it) for pieces, and every connection for a strip that lands on land.
| # | trap | trigger | test | fix, or why it is left |
| --- | --- | --- | --- | --- |
| 59 | Route 3 / Route 4 in the wrong columns (`[-,-,PEWTER,ROUTE_4]` / `[-,-,ROUTE_3,CERULEAN]`; the headers say Route 4 is **north** of Route 3), and Mt. Moon 1F linked to **Route 3**, which has no warps; B1F's exit to Route 4 missing | any objective past Pewter: Route 3's north edge named no map, so it was nobody's hop | `the_rows_the_disassembly_corrected_say_what_its_headers_say`, `the_road_to_mt_moon_is_not_a_ring_at_the_pewter_end_from_the_badge_checkpoint` (ROM) | **fixed**: rows as `Route3.asm` / `Route4.asm`; `(MT_MOON_1F, ROUTE_4)`, `(MT_MOON_B1F, ROUTE_4)` per `Route4.asm`'s warps (18, 5) and (24, 5) and B1F's (27, 3) |
| 59b | Route 14 / 15 and Route 24 / 25 in the wrong columns (south/north for west/east) | Nugget Bridge's far end (rung 16) named no map; Route 14's west edge likewise | the same unit test | **fixed**: `Route14.asm`, `Route15.asm`, `Route24.asm`, `Route25.asm` |
| 59c | Route 4 is one node, though Mt. Moon cuts it in two: the cave mouth's side (Pokécenter (11, 5), 1F (18, 5), Route 3) and Cerulean's side (B1F's exit (24, 5), Cerulean) | every hop across Route 4 | `route_4s_sides_are_told_apart_by_the_doors_where_the_ground_cannot`, `route_4s_doors_and_sides_are_the_cartridges_from_the_badge_checkpoint` (ROM) | **fixed**: a `SPLIT` row |
| 59d | Mt. Moon B1F and B2F are one node each, though B1F is four chambers of two ladders and B2F three pieces; 1F has three ladders to B1F and only (5, 5) is the road, so a map-level hop sends the fly up dead ends and back | rung 13 (Cerulean) from the first floor | `the_road_from_pewter_to_cerulean_is_through_mt_moon_one_chamber_at_a_time`, `a_door_or_an_edge_lands_in_the_piece_it_opens_onto` | **fixed**: `SPLIT` rows; a piece lists its warps by index, a warp lands in the piece its destination warp is in (`wWarpEntries` byte 2), and the hop is a piece. The fly's own piece is what its walk reaches on the decoded grid, the nearest door where the grid (no ledges) reaches none; flooded over all four maps' ground, exact for every tile |
| 59e | four header connections have no tile where both sides are land -- Pallet / Route 21, Cinnabar / Route 20, Route 20 / 19 (sea), Route 22 / 23 (the League's fence) -- and the graph routed along them: from Pallet the road to Cerulean was by sea | whiting out in Mt. Moon, which the survey's fly did: `GO ROUTE` walked into Pallet's shore every 108 frames | `a_connection_nobody_can_walk_across_is_named_and_is_not_a_road` | **fixed**: `NO_CROSSING`: named, no exit, no hop. Route 22 / 23 go back into the table from the same list |
| 59f | a trainer's challenge closes onto five frames of plain overworld before `StartTrainerBattle` decides the battle; row 58's pending push-back was written on the first of them | Route 3's first trainer: (11, 6), the one gap between the road's west end and the rest of it, walled for the session | `a_challenge_closing_onto_a_few_frames_of_overworld_is_still_a_challenge` | **fixed**: a push-back is a refusal only after thirty frames running of the fly's overworld; a battle inside them drops it |
Measured and not the same kind, so left: Cerulean's south and east (Route 5, Route 9, the trashed
house's back door) are reached from the town only through the trashed house, which is off the
graph, and back over a ledge -- rung 17's road, with the Saffron gates and the Underground Path
behind it. Routes 5-8, 10-12, 15, 16, 18 and 23 are pieces joined by gate buildings off the graph,
and Rock Tunnel 1F is four pieces (rung 20). And many edges have walkable tiles past the strip
that lands on land: a walk aimed at
one is blocked and rests the whole edge for the window. On the road through rung 16 these are
Route 3's (62, 0) and (63, 0), Route 4's (6, 17) and Cerulean's (0, 13) and (25, 0), all beside the
landing tiles a walk from the road reaches first.
### Before and after
Route survey from the rank-11 checkpoint, 120,000 frames, `GO OBJECTIVE` and `TALK` preferred:
| measure | base `7784a9d` | branch |
| --- | ---: | ---: |
| `GO OBJECTIVE` done on Route 3 | **509** | 9 |
| `GO ROUTE` done on Pewter | **508** | 0 |
| Route 4 / Mt. Moon first | never / never | frame 70,356 / 70,707 |
| rung at the end | 11 | **12, MT. MOON** |
| pushed tiles at the end | (11, 6), (14, 6), (14, 9) on Route 3 | none |
From the live Route 4 checkpoint, 72,000 frames, rebased on `main` `4d82f7d` (v7):
| measure | base `4d82f7d` | branch |
| --- | ---: | ---: |
| map changes, `GO OBJECTIVE` preferred / uniform | **930 / 706** | 29 / 50 |
| `GO OBJECTIVE` done on Route 4, preferred / uniform | **465 / 183** | 3 / 2 |
| `GO ROUTE` done on the Pokécenter, preferred / uniform | **462 / 350** | 0 / 0 |
| into Mt. Moon, preferred / uniform | frame 541 / 826, and back out | frame 279 / 1,910 |
| rung at the end | 12 | 12 |
`the_fly_goes_into_mt_moon_from_the_live_route_4_checkpoint`, twenty brain minutes on the stub
rotation: the base crosses Route 4's west doors 56 times, 18 through the Pokécenter's and 38
through the cave's (fails); the branch 13, over three whiteouts and walks back (passes), in the
cave on frame 278.
The same survey from the live rank-11 checkpoint of 2026-09-22 (Pewter, the badge won):
the base stays on Route 3 with five pushed tiles fencing it, rank 11; the branch reaches Route 4 at
frame 118,621 and Mt. Moon at 118,972, rank 12, nothing pushed.
The ROM-gated run, `the_road_to_mt_moon_is_not_a_ring_at_the_pewter_end_from_the_badge_checkpoint`,
on the stub rotation: the base crosses between Pewter City and Route 3 **3,391** times in 80.4 brain
minutes and never stands on Route 4 (fails); the branch crosses 4 times, whites out twice on the
way, and is on Route 4 at frame 202,734 and at Mt. Moon's door at 203,232, 56.7 brain minutes
(passes). `route_4s_doors_and_sides_are_the_cartridges_from_the_badge_checkpoint` reads Route 4's
warp table off the cartridge -- (11, 5) to `$44`, (18, 5) to `$3b`, (24, 5) to `$3c`, the table's
doors in its order -- and the decoded grid puts the fly, arrived from Route 3 at (9, 17), on the cave
mouth's side.
**No trap hunt numbers.** Two 30-brain-minute stub arms were started from the badge checkpoint on
v0.5.5 with the box at load 35-40, had written nothing after two hours, and were stopped when the
live fly reached Route 4 and this row became the live priority; FND-01 has since changed the
hunt's frame, so hunts across the rebase would not compare (`9301e39`). The proof is the route
survey and the three ROM tests, which is a deviation from the ethos check's letter, recorded.
### Gates
- `cargo test --workspace --no-fail-fast` in release with `FLY_ROM`, `FLY_DATASET` and
`FLY_BADGE_CHECKPOINT`: 1,285 passed, 1 failed --
`flysim::integration::the_service_streams_takes_sugar_checkpoints_and_resumes_after_being_killed`,
the known load-sensitive test (feed at 11.28 Hz with the box at load 35-40); the base fails it
too at the same load (the boot-time `total` assertion).
- `cargo clippy --workspace --all-targets`: **0 warnings**.
- `npm test` 663 passed; `npm run typecheck` clean.
- `infra/tests/lint.sh`: ALL CHECKS PASSED, the de-PII guard included.
- `flysim --print-compatibility`: byte-identical to the base on both bases this branch has had:
648 bytes, `4929f340...9ebd9` on v0.5.5 (`7784a9d`), and 648 bytes, `8ce67b97...a8f68` on
`4d82f7d` (v7). Decoder, reward catalog, adapter version and roles untouched.

View file

@ -1440,7 +1440,7 @@ pub fn toward_objective(state: &mut dyn MacroState, candidates: &[Exit]) -> Vec<
} }
/// The piece of ground the fly is standing in: its map, and on a map whose ground is in pieces /// The piece of ground the fly is standing in: its map, and on a map whose ground is in pieces
/// the piece its walk can reach the doors of (`docs/design/macros.md` sections 12.7 and 12.23). /// the piece its walk can reach the doors of (`docs/design/macros.md` sections 12.7 and 12.24).
pub fn region_here(state: &mut dyn MacroState) -> Option<geography::Region> { pub fn region_here(state: &mut dyn MacroState) -> Option<geography::Region> {
let player = state.player()?; let player = state.player()?;
let grid = state.map_grid(); let grid = state.map_grid();
@ -1449,7 +1449,7 @@ pub fn region_here(state: &mut dyn MacroState) -> Option<geography::Region> {
/// Whether `exit` takes the fly onto `hop`: the map on the other side, and on a map whose ground /// Whether `exit` takes the fly onto `hop`: the map on the other side, and on a map whose ground
/// is in pieces, the piece it lands in. A warp names the destination's warp it arrives at, which /// is in pieces, the piece it lands in. A warp names the destination's warp it arrives at, which
/// is what tells Mt. Moon's three ladders down to B1F apart (section 12.23); an edge lands in the /// is what tells Mt. Moon's three ladders down to B1F apart (section 12.24); an edge lands in the
/// piece that lists the map it is stepped off. A landing the table cannot name is not a match. /// piece that lists the map it is stepped off. A landing the table cannot name is not a match.
fn leads_to(state: &mut dyn MacroState, exit: &Exit, here: u8, hop: geography::Region) -> bool { fn leads_to(state: &mut dyn MacroState, exit: &Exit, here: u8, hop: geography::Region) -> bool {
if exit.destination(here) != Some(hop.map) { if exit.destination(here) != Some(hop.map) {