Commit graph

176 commits

Author SHA1 Message Date
acamilo
78fb86336e Merge feat/map-aware-walks: whole-map walkability grid, A* over the map for every walk 2026-09-22 05:16:57 +00:00
claude
4b93f9dc7f docs: the trap hunt for section 15, before and after
286 distinct tiles become 489 over twenty brain minutes from the rung-9
checkpoint, the median flagged window holds 55 tiles instead of 16, GO FRONTIER
runs 64 walks worth up to twelve net tiles instead of four worth one, and one
walk spends its cap where six did.

The cost is named rather than buried: flagged windows go 61 to 70 and windows
under four tiles 5 to 14, and every one of those is a window spent inside a
battle -- a fly that covers more ground walks into more grass, 48,756 battle
frames becoming 50,413 with the longest battle 9,549 frames becoming 13,411.
This is the first measurement in that file where more ground and fewer flags do
not both hold, and it is stated as such.
2026-09-22 05:15:30 +00:00
claude
ba581e44d3 flysim: the trap hunt says which refusal a frame with no grid is 2026-09-22 04:50:24 +00:00
acamilo
9479681b9f Merge branch 'main' into feat/map-aware-walks 2026-09-22 04:21:54 +00:00
claude
b9e01dea5b docs: macros.md section 15 and the WRAM table behind it
Section 15 is the contract for map-aware walks: what is decoded, the one ROM
read and why it is a read of the cartridge image, what the window fallback is
for and how it says so, and the proof. macros-wram.md section 9 is the bytes --
four addresses with their encodings, the bank-aware read on the seam, the corner
the cartridge settled, the two gates against a plausible-but-wrong decode, and
the survey of Pallet Town and Viridian Forest.
2026-09-22 04:21:54 +00:00
claude
c8de2e42e9 docs(gb): the module header names the corner the cartridge settled 2026-09-22 04:16:48 +00:00
claude
abd3e563a8 gb: refuse a map header that does not fit wOverworldMap
The buffer is ds 1300 and every real map plus its three-block border fits in it.
A header that says otherwise is one read mid-load, and decoding it would read
past the buffer into somebody else s WRAM, so it is a refusal rather than a
clamp.
2026-09-22 04:15:29 +00:00
acamilo
592c2640a3 docs: v0.4.2 status
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 04:12:59 +00:00
acamilo
e2082e3699 Merge fix/loop-20260922T0254: between-turns pad without a listless BACK, THROW BALL skips held species 2026-09-22 04:12:58 +00:00
claude
44a7011299 tests: the grid against the cartridge on two maps
ROM-gated and checkpoint-gated, skipped cleanly without either. The decode
against the window predicate on every tile the window can answer for; the decode
against a survey of real presses, where every refused press has to be a wall, a
directed wall or a sprite in the way, and every step the cartridge made has to be
one the grid would have planned; GO FRONTIER aiming at ground outside the window
and walking there; and every way out of the map reachable in one plan with no
guessed tile in it.

Pallet Town: 221 walkable, 207 reachable, 90 window tiles, 120 surveyed, 58
refused presses. Viridian Forest: 719 walkable, all reachable, 90 window tiles,
120 surveyed, 74 refused presses, a 215-frame frontier walk out of the window and
ways out 27 to 149 steps away.
2026-09-22 04:12:19 +00:00
claude
246e0c7d4c flysim: the map grid in the probes
scene_probe prints the grid s size, its walkable count, the count reachable from
where the fly stands and the count never stood on, draws the ground with the
reading it actually used, and names the refusal when there is no grid. trap_hunt
carries the same line into every trace line and into the summary, so a stalled
walk can be read at a glance: a fly with 719 walkable tiles and 4 reachable ones
is fenced in and no re-plan will help it.
2026-09-22 04:12:19 +00:00
claude
bcae388d99 gb: re-check a cached grid against the screen when it is served
A warp writes wCurMap before the map header and the block data: on Oak s lab
doormat wCurMap already reads PALLET_TOWN while the header still reads the lab s
ten-by-twelve. The decode agrees with the screen on such a frame -- both are the
old map -- so only the id is wrong, and a grid filed under it would stay wrong
for as long as it was cached. One byte answers it: does the cached grid still
agree with the screen about the tile the fly is standing on.
2026-09-22 04:10:28 +00:00
claude
1b06e6a6cb gb: the collision tile is the lower left of a map tile s quadrant
Measured on the cartridge, not derived: a map tile is 2x2 screen tiles and
CheckTilePassable matches one id, and the screen agrees with the lower-left tile
of the quadrant. Viridian Forest s (4, 32) reads 3, the second row of its
block, where the first row holds bash4. On open ground most quadrants hold one id
four times over, so the upper-left guess reads correctly on a town and falls
apart in a forest -- which is why the decode is cross-checked against the screen
before it is trusted.
2026-09-22 04:10:28 +00:00
acamilo
18e16819ca docs: section 12.9, and rows 38 to 41 of the trap audit
The rung-9 forest review: what was live, the mechanism of all three
observations, the audit rows, and the trap hunt before and after from the
release container's own checkpoint.

The hunt is brain-driven rather than stubbed, which is legitimate here and
was not for sections 13 and 14: this branch adds no macro type, so no
population is re-dealt and the two arms differ in the macro code alone.
Distinct tiles 216 to 286, windows flagged 70 of 73 to 61, windows under
four tiles 21 to 5, BACK starts with no list open 175 to 0, MOVE n starts
36 to 68.

Four residuals are named rather than buried: BACK is still 359 starts of
830 over open lists, which is row 34's contract; a NEXT/BACK two-cycle
closes the run inside one battle, from a move list whose cursor the seam
cannot place on its opening frames; a window spent in a battle is flagged
by the tile rule whatever happens in it; and the THROW BALL precondition
is inert in this run because the save carries a party of one.

Row 41 is the loop behind this one: in the before arm the fly answered the
nurse's box YES 474 times on one tile of a Pokemon Center for the last
eight brain minutes of the run. The after arm never enters it, so it is
neither reproduced nor fixed here. It is the next thing to measure.
2026-09-22 04:09:05 +00:00
acamilo
75375fa799 docs(design): index the flybus communications bus 2026-09-22 03:47:44 +00:00
acamilo
093db147eb feat(flybus): add standalone IPC bus and conformance tests
(cherry picked from commit 95ac8d1bdbc866a7c49fad3ddba468d945267650)
2026-09-22 03:46:27 +00:00
claude
f7fa39afa7 macros: plan every walk over the map grid
route and frontier ask for the grid once per plan and fall back to the window
when there is none. With it: one plan crosses a whole map, GO WARP / GO OUT /
GO ROUTE route to their warp or connection tile rather than to the nearest tile
of an edge that reads Unknown, a tile-pair wall is planned around instead of
being learned by walking into it, and the frontier is the nearest unstood
walkable tile anywhere on the map instead of the nearest one on screen.
2026-09-22 03:32:35 +00:00
claude
68228ef2a1 gb: decode the whole current map into a walkability grid
The walkable predicate answered for the ten-by-nine screen window and Unknown
everywhere else. MapGrid is the same rule over every tile of the loaded map:
block ids out of wOverworldMap, a blocks-to-tiles read of the tileset header s
blockset through the new bank-aware ROM read, the tileset s collision list as
before, and the TilePairCollisionsLand values as directed walls both ways.

The reader checks itself before it answers: the decode is compared against the
window predicate over the player s own neighbourhood, and a frame where the
window can answer for none of it -- a battle, a text box, a frame mid-warp -- is
refused, because wOverworldMap shares its bytes with the picture buffer. Every
refusal is named (GridRefusal) and leaves the window predicate in charge.

Cached per map id and size, so a map is decoded once on arrival rather than once
per question, and owned beside the session ledgers: never checkpointed.
2026-09-22 03:32:35 +00:00
claude
d75b7320ea seam: a bank-aware ROM read beside the bus read
MemoryReader::read8 reads the CPU bus, where banks 1 and up are whichever bank
the cartridge last switched to, so a table in another bank could only be read by
writing the mapper register -- and the joypad is the only write this workspace
makes into a running game. read_rom takes a bank and a CPU address and reads the
cartridge image the process already holds instead. Defaulted to None, so every
reader without a cartridge behind it narrows rather than guesses.
2026-09-22 03:32:19 +00:00
acamilo
7d5c788876 tests: the rung-9 forest checkpoint, on the cartridge
A ROM-gated run from the checkpoint the stream stalled on, gated on its
own variable because the two checkpoint tests beside it assert the map
their envelope is on and one envelope cannot be both.

What it holds: the objective is Pewter City, the road there is on the pad,
BACK is never on a battle pad with no list open, no ball is thrown at a
species the party holds, the fly gets turns, a move or a ball is chosen,
and a battle ends. Which move and which way it walks are the fly's, so
they are reported rather than asserted -- including the forest's north
gate, which this run does not reach.
2026-09-22 03:28:28 +00:00
acamilo
ce02a89922 macros: BACK belongs to a list, and no ball at a species the party holds
Two traps from the rung-9 forest checkpoint, both section 12.2's rule: a
macro that completes without moving because its precondition is already
satisfied where the fly stands.

BACK was dealt by the between-turns row, which section 13.1 gave NEXT and
BACK for the sake of the battle bag -- the bag reads as nobody's turn, so
it lands there -- and which is also every frame of battle text. With
nothing open there is nothing to back out of, so the press completes in
fifteen frames on the tile it started on: 163 of 959 macro starts in
twenty brain minutes from the checkpoint, plus twelve more on a move list
whose cursor the seam cannot place. The row is now the sub-state's: BACK
with the bag open, NEXT alone otherwise. The move list and the party list
keep it, and the top-level menu never had it.

THROW BALL gains a fourth fact: the species on the other side is not one
the party already holds. The party is the caught set, because it is the
cartridge's own lifetime record in the same internal numbering the enemy
is read in; wPokedexOwned is by Pokedex number and the table that converts
one to the other is in a ROM bank this crate cannot read. An enemy the
seam cannot place leaves the button where it was.

RUN is unchanged. Its blocked outcomes are the script and not the
cartridge: a cursor macro started as the menu closes waits out CURSOR_WAIT
pressing nothing (mean 184 frames over ten starts), which is audit row 19.

The decoder, the reward catalog, the adapter version and the compatibility
string are untouched: --print-compatibility is byte-identical at 648 bytes.
2026-09-22 03:28:20 +00:00
claude
320b05ecee symbols: the four WRAM names the whole-map grid reads
resolve_wram.py is a second reading of the disassembly beside gen_symbols.py:
it walks ram/wram.asm with a cursor that is only ever live while anchored on an
address symbols.rs already pins, re-derives 40 of the 63 it carries with no
disagreement, and emits an address only when a pinned one after it agrees too.
wOverworldMap, wCurMapTileset, wTilesetBank and wTilesetBlocksPtr come out of
that pass; no address is hand-written and nothing else in the table moves.
2026-09-22 03:17:20 +00:00
acamilo
097a11196b trap hunt: report the battle sub-state, its pad and the starts on it
A scene histogram says "battle" and a battle deals five different pads
(docs/design/macros.md 12.6, 13.1), so a loop inside one of them is
invisible in the tables the hunt already prints. Three more: frames per
sub-state, every macro channel ever bound in each one, and the macro
starts by the sub-state they started on.
2026-09-22 03:13:27 +00:00
acamilo
1a3cfb714a docs: v0.4.1 release record
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 02:23:36 +00:00
acamilo
faec84b42e stage(describe): the public repo URL on the card
Some checks are pending
ci / node 22 (test + typecheck) (push) Waiting to run
ci / rust stable (cargo test --workspace --release) (push) Waiting to run
ci / infra/tests/lint.sh (push) Waiting to run
ci / playwright apps/stage (allowed to fail) (push) Waiting to run
2026-09-22 02:08:31 +00:00
acamilo
660c3cf00d flybrain v0.4.0: public tree (history retained privately)
Some checks failed
ci / node 22 (test + typecheck) (push) Has been cancelled
ci / rust stable (cargo test --workspace --release) (push) Has been cancelled
ci / infra/tests/lint.sh (push) Has been cancelled
ci / playwright apps/stage (allowed to fail) (push) Has been cancelled
2026-09-21 15:09:46 +00:00