25 KiB
Room escape: choosing the direction hold
Measured 2026-09-16 for docs/design/room-escape.md section 1, which asked for a hold value
between 600 and 1000 ms chosen by measurement rather than by argument.
Superseded in part by the v0.1.1 section below, which reversed the fatigueGain half of this
result and lowered the hysteresis: the random walker used here cannot build an incumbency, so it
cannot see what a low fatigue gain does to a real network. The 800 ms hold survived.
Chosen: holdMs = decisionMs = 800, fatigueGain = 0.04. Baked into
packages/brain/src/readout/presets/gameboy.ts and its Rust twin
flybrain-core::decoder::gameboy, noted in docs/readout.md.
What was run
services/flysim/crates/flysim/examples/room_escape.rs, on the pinned Pokémon Red cartridge
(SHA-256 0e85…219f), unthrottled, 15 worker threads on the WSL development box. The real
PopulationDecoder with the real Game Boy preset, the real PokemonRedReward adapter, and seeded
uniform random rates in place of a network — the random_walker.rs pattern, which is how the
readout gets measured without 139,255 neurons of confound.
Two starting rooms, both reached by the harness itself rather than from an archive:
- Red's bedroom, map 38 (
REDS_HOUSE_2F). Booted from a cold cartridge: Start and A on a slow alternation through the intro and the naming screens, then B until the adapter reports a stable, unscripted, dialogue-free overworld sample. One warp, the stairs at (7, 1); the fly spawns at (3, 6) with (3, 5) blocked. - The ground floor, map 37 (
REDS_HOUSE_1F). One run walked down the stairs and its state was cached. Captured 15 brain seconds after arrival, not on arrival: the first safe sample after a warp is standing on the staircase, and "leave the map" from the staircase is one step back up. Three warps — the two front-door tiles at (2, 7) and (3, 7), and the stairs at (7, 1) — and a table between them.
Each cell is 60 seeded runs of 10 brain minutes, run to the full budget rather than stopped at the first map change, so the coverage column is uncensored.
The design's twenty seeds
Twenty is what the design asked for. It is not enough to choose on: every candidate leaves both rooms in every one of twenty runs, so the leave fraction saturates at 20/20 everywhere, and the median time to leave moved by more between neighbouring hold values than it did across the whole range (bedroom, fatigue 0.04: 38.1 s at 400, 26.8 s at 600, 29.2 s at 800, 43.7 s at 1000 — not monotone in either direction). The tables below are therefore 60 seeds, of which those twenty are the first.
From Red's bedroom, map 38
60 seeded runs per row.
| hold = decision | fatigue gain | left the map | median time to leave | mean time to leave | median tiles | mean tiles | median maps |
|---|---|---|---|---|---|---|---|
| 400 ms | 0.04 | 60/60 | 34.3 s | 52.4 s | 173.0 | 168.9 | 4.0 |
| 400 ms | 0.08 | 60/60 | 37.5 s | 56.6 s | 179.0 | 173.0 | 3.0 |
| 600 ms | 0.04 | 60/60 | 31.3 s | 39.6 s | 174.5 | 173.2 | 4.0 |
| 600 ms | 0.08 | 60/60 | 28.7 s | 36.0 s | 174.0 | 178.9 | 4.0 |
| 800 ms | 0.04 | 60/60 | 22.6 s | 27.0 s | 188.0 | 181.6 | 4.0 |
| 800 ms | 0.08 | 60/60 | 23.8 s | 31.6 s | 172.5 | 171.1 | 4.0 |
| 1000 ms | 0.04 | 60/60 | 30.2 s | 35.9 s | 174.0 | 173.8 | 4.0 |
| 1000 ms | 0.08 | 60/60 | 24.0 s | 31.0 s | 182.0 | 174.3 | 4.0 |
Maps reached across all 480 runs in this table: Pallet Town (0) in 470, the ground floor (37) in 480, the bedroom again (38) in 384, Oak's Lab (40) in 323 and Blue's house (39) in 73. No run reached route 1 (12) in either table: the game blocks the north exit from Pallet Town until the player has a starter, so a walker with no plan circles the four maps it can open.
From the ground floor, map 37
60 seeded runs per row.
| hold = decision | fatigue gain | left the map | median time to leave | mean time to leave | median tiles | mean tiles | median maps |
|---|---|---|---|---|---|---|---|
| 400 ms | 0.04 | 60/60 | 17.3 s | 24.6 s | 158.0 | 151.6 | 3.0 |
| 400 ms | 0.08 | 60/60 | 16.2 s | 19.6 s | 159.5 | 152.7 | 3.0 |
| 600 ms | 0.04 | 60/60 | 15.0 s | 21.2 s | 166.0 | 162.5 | 3.0 |
| 600 ms | 0.08 | 60/60 | 19.4 s | 24.0 s | 148.0 | 152.7 | 3.0 |
| 800 ms | 0.04 | 60/60 | 20.0 s | 28.5 s | 171.0 | 164.7 | 3.0 |
| 800 ms | 0.08 | 60/60 | 18.9 s | 25.8 s | 162.5 | 163.8 | 3.0 |
| 1000 ms | 0.04 | 60/60 | 24.1 s | 28.1 s | 165.0 | 162.6 | 3.0 |
| 1000 ms | 0.08 | 60/60 | 19.6 s | 25.9 s | 178.0 | 171.7 | 4.0 |
Maps reached across all 480 runs in this table: Pallet Town (0) in 480, the ground floor again (37) in 369, the bedroom (38) in 275, Oak's Lab (40) in 350 and Blue's house (39) in 91.
Confirmation with the real brain
FLY_ESCAPE_BRAIN=data/fafb-v783: NeuralAgent on the FlyWire connectome, 139,255 neurons, four
sweep threads, warm-up included, the adapter's payouts reinforcing as they do on the stream, from
the same bedroom state. Five runs each, and the runs differ only in warm-up length — the network
and binjgb are both deterministic, so five runs from one state with one warm-up are one run
reported five times. (That was the first attempt: all five left the bedroom at the same
millisecond.)
| hold / fatigue | run 1 | run 2 | run 3 | run 4 | run 5 | left | median | mean |
|---|---|---|---|---|---|---|---|---|
| 400 ms / 0.08 (before) | 38.3 s | 22.7 s | 129.1 s | 52.5 s | 294.2 s | 5/5 | 52.5 s | 107.4 s |
| 800 ms / 0.04 (chosen) | 18.6 s | 28.6 s | 68.3 s | 30.3 s | 68.8 s | 5/5 | 30.3 s | 42.9 s |
The brain is slower than the random walker at both settings and moves the same way between them: the median more than halves and the mean falls by 60%. So the choice is not a random-walker artefact. Every run left through the stairs to map 37; none of the ten went anywhere else first.
Why 800 and 0.04 (the 0.04 half was wrong)
- Leave fraction cannot decide it. Every candidate escapes both rooms in every run at 60 seeds, which is worth recording as a result in itself: the design's premise, that the fly cannot get out of a room, is not reproducible by a random walker in these two rooms at any of the four hold values. What the old setting cost was time, not success.
- 800 ms is the best row in the bedroom on every column. Fastest median (22.6 s), fastest mean (27.0 s), most ground covered (188 median tiles). The bedroom is the tight room — 8x8 tiles with furniture and one exit — and it is the case the design was written about.
- 1000 ms is worse, and consistently. Slower than 800 in both rooms at both fatigue gains. A commitment long enough to cross the whole room means running into a wall and staying there until the next decision.
- 0.04 beats 0.08 at 800 ms, on the bedroom mean (27.0 s against 31.6 s) and on coverage in both rooms. Elsewhere in the table the two fatigue gains are inside the seed-to-seed spread, so this is a weak result on its own; it agrees with the mechanism the design gives, which is that a lower gain is what lets a committed direction survive a re-decision, and it is the value the design proposed. This was the wrong call, and the weakness of the evidence was the warning. A uniform random source re-rolls a fresh direction at every decision, so it never builds the long incumbency the gain governs; on a real network at 0.04 one direction won 4.1 consecutive holds, twelve tiles of commitment in an eight-tile room. See the v0.1.1 section.
- The ground floor costs a few seconds. 800/0.04 is 20.0 s against 15.0 s for the best row (600/0.04), inside the spread of that room's eight rows (15.0 to 24.1 s) and paid back in coverage (171 against 166 median tiles). The ground floor has three warps including two adjacent front-door tiles, so it is easy at every setting and is the weaker of the two signals.
Honesty
This measures the readout. A random walker is not a brain, and a configuration that leaves a room faster is not learning anything — the whole reason the walker is the instrument here is that it removes the brain from the loop so the readout can be seen on its own. The five-run brain confirmation is five runs, on one cartridge, from one room; it says the number is not an artefact of the random source, and it is not a benchmark of the fly.
Reproducing
FLY_ROM="$HOME/fly-plays-pokemon/Pokemon Red (U) [S][BF].gb" \
FLY_ESCAPE_SEEDS=60 FLY_ESCAPE_JOBS=15 \
cargo run --release -p flysim --example room_escape
About 17 minutes of wall time for the 960 runs. Then, for the confirmation rows:
FLY_ROM=... FLY_ESCAPE_SEEDS=1 FLY_ESCAPE_HOLDS=800 FLY_ESCAPE_FATIGUES=0.04 \
FLY_ESCAPE_BRAIN=data/fafb-v783 FLY_ESCAPE_HOLD=800 FLY_ESCAPE_FATIGUE=0.04 \
cargo run --release -p flysim --example room_escape
binjgb prints the cartridge header to stdout on every boot, so the report is easier to read
through grep -v. The ROM is read from the path in FLY_ROM and is never copied into the
repository.
v0.1.1: why the live fly stayed on the ground floor
Measured 2026-09-16 for docs/design/room-escape.md section 3, after the release box (the release container,
twitch.tv/<twitch-channel>, v0.1.0) spent forty minutes on rung 2.
Chosen: hysteresis 1.15 -> 1.05, fatigueGain 0.04 -> 0.08, and the blocked-direction cooldown
on at blockedFatigue 0.35 / blockedMs 800. holdMs and decisionMs stay at 800. Baked into
packages/brain/src/readout/presets/gameboy.ts and its Rust twin
flybrain-core::decoder::gameboy, dated in docs/readout.md.
The state this was measured from
Generation 525 of the release run, pct pulled read-only off the release container and never written back:
SHA-256 a899997a36ab1706…fd3d49, 2,667,148 bytes, brain clock 2,589,850 ms (43.2 minutes),
emulator frame 154,537, rank 2 since 95,740 ms, ratchet best 2 with 0 recoveries and 0 attempts,
tileCounts 37:32 and 38:39, all eight boundary payouts spent, lifetime reward 3.20.
The fly was standing at map 37 (7, 1) — on the staircase tile, which only warps on the step onto
it — holding up into the wall above it, with stable at 248 samples.
Its four direction scores at that instant:
| channel | rate | baseline | score | fatigue | adjusted |
|---|---|---|---|---|---|
| up | 50.744 | 50.661 | 1.0016 | 0.1446 | 0.8751 |
| down | 69.265 | 71.920 | 0.9636 | 0.0000 | 0.9636 |
| left | 62.733 | 59.897 | 1.0466 | 0.0512 | 0.9956 |
| right | 65.500 | 66.633 | 0.9833 | 0.0939 | 0.8988 |
The spread from best to worst is 8.6%, well under the 15% a challenger needed to beat an incumbent:
no direction could ever win on score alone. Only fatigue could move the winner, and at gain 0.04
that took a run of four wins — so the readout was a slow four-step rotation through a fixed
preference order, and down, the only direction that opens the front door, was last in it.
Where the preference order comes from
calibrate() runs once, at warm-up, and stores each role's rate as its baseline. At that instant
every score is exactly 1 by construction and the four directions are exactly tied. The whole spread
above is drift of the four command_* roles since that calibration, 43 brain minutes earlier,
frozen into the score. It is not a claim about what the fly wants now; it is the sign of each role's
slow drift, and it holds its shape for as long as the run does. That is why habituation was the only
thing that could reorder the directions, and why fatigueGain and hysteresis — two numbers
section 1 treated as secondary — turned out to decide whether the fly could leave a room.
The room
survey in the harness walks map 37 with real presses on throwaway emulators. 48 tiles are
reachable and exactly six presses leave the map:
| from | press | to |
|---|---|---|
| (6, 1) | right | 38, the bedroom |
| (7, 2) | up | 38, the bedroom |
| (2, 6) | down | 0, Pallet Town |
| (3, 6) | down | 0, Pallet Town |
| (2, 7) | down | 0, Pallet Town |
| (3, 7) | down | 0, Pallet Town |
The live ledger held 32 of the 48; the 16 it had never stood on were (1,4), (1,5), (1,6), (2,1), (2,4), (2,5), (2,6), (2,7), (3,6), (3,7), (4,6), (5,5), (5,6), (6,4), (6,5) and (6,6) — the southern interior, and with it three of the four tiles the front door needs. So "71 unique locations (the whole floor)" was wrong in two ways: 71 is both floors, and neither floor was covered.
The two doormats are the exception that proves the mechanism. boundary:37:7:2:on and
boundary:37:7:3:on were both paid, 0.10 each, at 355 s — the fly did stand on both mats. It
walked onto them sideways along row 7, which does nothing, and the next direction it committed to
was not down. (They are missing from the tile ledger because the main sample gate rejects
wMovementFlags & 0xc7, which includes BIT_STANDING_ON_DOOR; the boundary rule keeps its own
gate at 0xc0 precisely so the on-exit half can fire. Both behaviours are correct and unchanged.)
How the runs were made independent
Fifteen runs of ten brain minutes per candidate, unthrottled, the real NeuralAgent on
data/fafb-v783 (139,255 neurons), the real PokemonRedReward with the checkpoint's own ledger
imported, and the real Ratchet with the checkpoint's own budget — one thread each, fifteen at a
time, on the 16-core WSL development box, about nine minutes of wall time per candidate.
A restore has no warm-up length to vary, because simloop.rs deliberately skips the warm-up for a
checkpoint that already carries a settled network and a calibrated readout. So the runs differ only
in LifState::rng, the xorshift state behind the per-tick noise kicks, displaced by the harness's
own seed list. Everything else — membranes, refractory counters, learned gains, eligibility traces,
the brain clock, the decoder's fatigue, holds and baseline, the emulator, the reward ledger — is the
live fly's to the byte. Fifteen noise streams from one state is the same kind of variation fifteen
warm-ups are, and on a restored run it is the only arbitrary quantity left.
Runs stop when the fly leaves the house. Leaving the map is not leaving the house: two of the six exits are the staircase, and the bedroom is a room the fly has also exhausted.
The baseline: what v0.1.0 does from here
Fifteen runs, ten brain minutes each, at holdMs 800 / fatigueGain 0.04 / hysteresis 1.15.
Columns after the first four are the diagnosis: the share of decisions each direction won, the mean
length of a same-direction run in decisions, the share of decisions where the fatigue-adjusted
argmax lost to the incumbent under hysteresis, the share of holds over which the position never
changed at all, the longest single motionless stretch, and the decisions taken while standing on a
tile one press from leaving (over how many such decisions there were).
| run | left the map | out of the house | new tiles | decisions | up | down | left | right | mean run | hysteresis | blocked holds | longest still | exit decisions | recoveries | reward |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 28.7 s | no | 0 | 747 | 29% | 17% | 34% | 20% | 4.15 | 72% | 481 (64%) | 23.5 s | 13/25 | 0 | 0.00 |
| 2 | 20.0 s | no | 0 | 747 | 31% | 15% | 33% | 21% | 4.22 | 73% | 460 (62%) | 21.7 s | 14/41 | 0 | 0.00 |
| 3 | 6.0 s | no | 6 | 747 | 30% | 18% | 33% | 20% | 4.10 | 72% | 475 (64%) | 23.6 s | 7/20 | 0 | 0.00 |
| 4 | 6.5 s | no | 0 | 747 | 30% | 14% | 33% | 24% | 4.17 | 73% | 466 (62%) | 8.2 s | 14/14 | 0 | 0.00 |
| 5 | 22.7 s | no | 0 | 747 | 29% | 17% | 32% | 22% | 4.08 | 71% | 462 (62%) | 11.6 s | 10/14 | 0 | 0.00 |
| 6 | 33.5 s | no | 8 | 747 | 30% | 16% | 32% | 22% | 4.13 | 72% | 474 (63%) | 34.5 s | 11/28 | 0 | 0.05 |
| 7 | 13.0 s | no | 0 | 747 | 31% | 16% | 33% | 21% | 4.08 | 72% | 473 (63%) | 27.1 s | 10/27 | 0 | 0.00 |
| 8 | 62.4 s | no | 0 | 747 | 29% | 16% | 32% | 22% | 4.20 | 73% | 472 (63%) | 7.2 s | 8/21 | 0 | 0.00 |
| 9 | 48.1 s | no | 0 | 747 | 29% | 16% | 34% | 22% | 4.10 | 73% | 470 (63%) | 26.5 s | 5/21 | 0 | 0.00 |
| 10 | 13.3 s | no | 4 | 747 | 30% | 15% | 32% | 23% | 4.10 | 73% | 442 (59%) | 11.7 s | 11/39 | 0 | 0.00 |
| 11 | 46.5 s | no | 0 | 747 | 30% | 15% | 32% | 23% | 4.17 | 73% | 483 (65%) | 31.3 s | 9/17 | 0 | 0.00 |
| 12 | 11.8 s | no | 0 | 747 | 30% | 16% | 33% | 21% | 4.17 | 72% | 460 (62%) | 8.0 s | 13/30 | 0 | 0.00 |
| 13 | 11.7 s | no | 0 | 747 | 30% | 15% | 33% | 21% | 4.17 | 74% | 469 (63%) | 9.6 s | 10/27 | 0 | 0.00 |
| 14 | 172.7 s | no | 4 | 747 | 31% | 14% | 32% | 23% | 4.20 | 73% | 452 (61%) | 17.2 s | 9/27 | 0 | 0.00 |
| 15 | 6.9 s | 573.5 s | 5 | 714 | 30% | 16% | 32% | 23% | 4.13 | 73% | 432 (61%) | 9.1 s | 12/23 | 0 | 0.00 |
1/15 out of the house, 0/15 inside five minutes. Every run left the map within three minutes and, except for run 15, every one of those exits was the staircase. Ten of the fifteen discovered not one new tile in ten minutes, and fourteen of the fifteen earned no reward at all. Frames spent on an exit tile, summed over the fifteen runs: the two staircase tiles 5,524 and 8,710, the two doormats 1,441 and 1,427, and the two tiles above the doormats 68 and 55 — 0.02% of the run.
59 to 65% of holds moved the player not one tile, for up to 34.5 seconds at a stretch.
The mean same-direction run was 4.1 decisions. At holdMs 800 that is 3.3 seconds of committed
travel, and one overworld step is about 268 ms, so a run commits to about 12 tiles of straight
walking. Red's ground floor is 8 tiles wide. This is the arithmetic section 1 got wrong: it doubled
the hold from 400 to 800 ms, which was right, and halved fatigueGain from 0.08 to 0.04 at the same
time, which doubled the run length in holds as well — so the committed distance went from about 6
tiles to about 12, and every run began ending in a wall. The random walker could not see it, because
a uniform random source re-rolls a fresh direction at every decision and so builds no incumbency.
71 to 74% of decisions were settled by hysteresis, not by score, which is the 8.6% spread against the 15% margin, measured rather than argued.
The candidates
Fifteen runs of ten brain minutes each, all from generation 525, all measured the same way.
| candidate | holdMs |
fatigueGain |
hysteresis |
blockedFatigue |
out of the house | inside 5 min | median | mean run | hysteresis share | blocked holds |
|---|---|---|---|---|---|---|---|---|---|---|
| v0.1.0 | 800 | 0.04 | 1.15 | off | 1/15 | 0/15 | 573.5 s | 4.1 | 71-74% | 59-65% |
| fatigue only | 800 | 0.08 | 1.15 | off | 11/15 | 4/15 | 314.6 s | 2.5 | 57-61% | 25-45% |
| fatigue + cooldown | 800 | 0.08 | 1.15 | 0.35 | 7/15 | 4/15 | 282.7 s | 2.0 | 44-50% | 30-39% |
| no commitment bonus | 800 | 0.08 | 1.00 | off | 8/15 | 3/15 | 330.2 s | 1.0 | 0% | 31-54% |
| shorter hold | 600 | 0.08 | 1.05 | off | 8/15 | 4/15 | 276.7 s | 1.2 | 12-19% | 41-55% |
| hysteresis only | 800 | 0.08 | 1.05 | off | 13/15 | 6/15 | 312.3 s | 1.2 | 12-20% | 30-62% |
| chosen | 800 | 0.08 | 1.05 | 0.35 | 14/15 | 13/15 | 130.3 s | 1.2 | 11-21% | 27-57% |
The blocked-direction cooldown alone, at v0.1.0's fatigue and hysteresis (800 / 0.04 / 1.15 / 0.35), left the house in 0 of 5 runs and is not in the table for that reason.
Three things are worth reading off it.
- The hysteresis is the lock, and 1.05 is the right amount to unlock. 1.00, no commitment bonus at all, is worse than 1.05 on every column (8/15 against 13/15): the hold is a commitment device and dropping the last of the margin wastes it. The mean run at 1.00 is exactly 1.0 — the incumbent never once survived a decision.
- The shorter hold is not the answer. 600 ms at the same fatigue and hysteresis gets 8/15. The three-tile commitment an 800 ms hold buys is worth keeping; section 1 was right about that.
- The cooldown's sign depends on the hysteresis. At 1.15 it makes things worse (7/15 against 11/15 without it) because a fly pushed off walls faster orbits the perimeter faster, and the staircase is on the perimeter. At 1.05 the fly turns inward instead and the cooldown stops each crossing being spent on a wall. Time on the two staircase tiles over the fifteen runs: 16,268 frames at 1.15 with the cooldown, 3,642 at 1.05 with it. Time on the two tiles above the doormats: 55 and 68 frames under v0.1.0, 482 and 367 under the chosen config.
The chosen config also gets the fly paid again — 0.05 to 0.10 per run against v0.1.0's 0.00 — and back to discovering 5 to 19 new tiles per run against 0.
From a fresh boot
Five runs each, real brain, cold warm-up, the same harness without FLY_ESCAPE_CHECKPOINT.
| room | out of the house | inside 5 min | median | left the starting map |
|---|---|---|---|---|
| ground floor (map 37) | 5/5 | 4/5 | 53.2 s | 5/5, median 16.6 s |
| bedroom (map 38) | 5/5 | 4/5 | 83.8 s | 5/5, median 20.3 s |
The bedroom row is the check against section 1's confirmation table, which measured leaving the bedroom and nothing else: 5/5 at a median 30.3 s under v0.1.0, 5/5 at a median 20.3 s here. No regression, and the fly now also leaves the building. A fresh fly earns 1.75 to 2.30 of reward in these runs, against the live fly's 0.00, because a cold ledger still has the operator's payouts in it.
The random walker, for scale
Twenty seeded walker runs per cell, both rooms, ten brain minutes, no brain. The walker cannot exercise the blocked-direction cooldown — nothing tells it whether it moved — so this covers the hold, the fatigue gain and the hysteresis only, and it is the bedroom-regression check section 1's tables were for.
| room | fatigueGain |
hysteresis |
left the map | median | out of the house | median |
|---|---|---|---|---|---|---|
| bedroom | 0.04 | 1.15 | 20/20 | 29.2 s | 20/20 | 162.1 s |
| bedroom | 0.08 | 1.05 | 20/20 | 36.0 s | 20/20 | 122.2 s |
| ground floor | 0.04 | 1.15 | 20/20 | 18.1 s | 20/20 | 24.8 s |
| ground floor | 0.08 | 1.05 | 20/20 | 12.4 s | 20/20 | 25.8 s |
Two readings. The bedroom does not regress: still 20/20, the median time to leave it moves by 7 seconds inside a table whose v0.1.0 spread was 22.6 to 43.7 seconds, and the time to get outside from the bedroom improves by a quarter. And the scale is worth stating plainly: a uniform random walker leaves the ground floor for Pallet Town in a median 25 seconds at either setting. The fly's best measured median is 130 seconds. The readout is no longer the thing stopping it, but it is still five times slower than choosing directions at random, and nothing here claims otherwise.
Reproducing
The checkpoint is not in the repository and never will be. Pull one read-only off the release box —
read manifest.json first for the generation to ask for:
ssh the host 'pct exec <release-ctid> -- cat /srv/fly/state/manifest.json'
ssh the host 'pct pull <release-ctid> /srv/fly/state/<latest>.checkpoint /tmp/fly.checkpoint'
scp the host:/tmp/fly.checkpoint ./
ssh the host 'rm -f /tmp/fly.checkpoint'
Then, from services/flysim:
FLY_ROM="$HOME/fly-plays-pokemon/Pokemon Red (U) [S][BF].gb" \
FLY_ESCAPE_CHECKPOINT=./fly.checkpoint FLY_ESCAPE_BRAIN=../../data/fafb-v783 \
FLY_ESCAPE_BRAIN_RUNS=15 FLY_ESCAPE_MINUTES=10 \
FLY_ESCAPE_BRAIN_THREADS=1 FLY_ESCAPE_BRAIN_PARALLEL=15 \
FLY_ESCAPE_CANDIDATES=800/0.08/0.35/1.05 \
cargo run --release -p flysim --example room_escape
About nine minutes of wall time per candidate on a 16-core box. FLY_ESCAPE_CANDIDATES takes names
from the harness's own list or bare hold/fatigue/blocked[/hysteresis] tuples, so a follow-up point
needs no recompile. The fresh-boot half is the same binary without FLY_ESCAPE_CHECKPOINT:
FLY_ROM=... FLY_ESCAPE_SKIP_SWEEP=1 FLY_ESCAPE_BRAIN=../../data/fafb-v783 \
FLY_ESCAPE_BRAIN_ROOM=1F FLY_ESCAPE_BRAIN_RUNS=5 FLY_ESCAPE_MINUTES=10 \
FLY_ESCAPE_BRAIN_THREADS=15 FLY_ESCAPE_CANDIDATES=800/0.08/0.35/1.05 \
cargo run --release -p flysim --example room_escape
which boots the cartridge, walks one random-walker run down the stairs, caches that state in
room-escape-1f.state (gitignored) and runs the brain from it. The walker table is the same binary
with neither variable and FLY_ESCAPE_HYSTERESES set. binjgb prints the cartridge header on every
boot, so read the report through grep -v.
Honesty
This measures the readout, from one state, on one cartridge, with one connectome. Fifteen noise streams from a single checkpoint are fifteen samples of one initial condition, not fifteen independent flies, and a configuration that leaves a house faster has not learned anything — the whole reason to measure from the live state was to see the readout as the stream actually runs it, after the drift and the spent ledger a fresh boot does not have. The one thing here that is evidence about the fly is the last row of the walker table, and it says the fly is still slower than chance.