1# Chapter 11: zbanks (headless), the bot with the lights off 2 3The window (chapter 12) is for watching. This is for finding out. It is the 4zbanks/alttp C bot playing our console with no window, three to four times 5faster than a real SNES, writing down what it did: the proof that it plays, 6and the place to reproduce anything it does. 7 8```bash 9mkdir -p run/zbanks-c/run1 run/zbanks-c/states 10BIN=$(nix develop -c buck2 build //apps/zbanks:zbanks --show-simple-output 2>/dev/null) 11$BIN "roms/Legend of Zelda, The - A Link to the Past (USA).sfc" 30000 run/zbanks-c/run1 \ 12 --states run/zbanks-c/states > run/zbanks-c/run1/bot.log 2> run/zbanks-c/run1/host.log 13``` 14 15A run boots the patched cartridge (chapter 4's `rando`), restores a start 16state if asked, starts the bot, and plays the number of frames given. Every 17600 frames it writes the picture as a PNG and one line of `progress.tsv`: 18mode, room, area, Link's position, hearts, sword, where the bot thinks Link 19is, its info line and current task, pendants, crystals, bow, book, boots, 20gloves, and goals given up and put back. The summary at the end (stderr) 21lists the places seen, the traps and the goal list. 22 23**The first run needs a home.** The bot's own `ap_init` loads a state called 24`home` from the states directory: Link standing in his house at the start of 25an open-mode game. Without one it starts at power-on, where it presses 26nothing. Make it once per states directory: 27 28```bash 29$BIN "roms/Legend of Zelda, The - A Link to the Past (USA).sfc" 0 run/zbanks-c/home \ 30 --states run/zbanks-c/states --make-home home 31``` 32 33`--make-home` runs no bot: it creates save file 1 from power-on, applies the 34Randomizer's open-mode preset to it, boots again and keeps Link in his house 35as the named state. 36 37> **Aside: how the map grows.** Upstream's video ran from a map the bot had 38> built over many runs. Every run here ends by writing the bot's own export 39> of what it learned, `map_export.txt`, and `--import-map FILE` hands one 40> run's export to the next as `map.19.txt`, which the bot imports on its 41> first tick. Run on run, the known explore goals went 155, 200, 287, 367 42> (2026-09-21). 43 44> **Try it.** Make a home, then play 30,000 frames (about two and a half 45> minutes; run it in the background) and read `run/zbanks-c/run1/progress.tsv`. 46> Add `--jev` under `op-env-run -- sh -c '…'` to let Jev choose the goals, 47> and compare. 48 49## For the people who maintain it 50 51`zbanks <rom.sfc> <frames> <out-dir> [--states DIR] [--start NAME] [--every N] [--import-map FILE] [--make-home NAME] [--jev] [--jev-dollars USD] [--no-retry] [--save-at FRAME]...` 52 53| Option | What | 54| --- | --- | 55| `--states DIR` | Where named states live (default `<rom>.states/` beside the ROM). It must already exist. The bot's `ap_init` loads `home` and `hpegs` from there. | 56| `--start NAME` | Restore that state before the bot starts. The bot's own `ap_init` then loads `home` over it whenever the directory has one, so to start somewhere else, make that state the `home` of a states directory of its own. | 57| `--save-at FRAME` | Repeatable: keep the machine after that frame as `<out-dir>/at-FRAME.state`. Copy it into a states directory as `home` to start another run exactly there (with Jev off, a run replays identically). | 58| `--every N` | A PNG (`frame-NNNNNN.png`) and a `progress.tsv` line every N frames (default 600). | 59| `--import-map FILE` | Copies FILE to `<out-dir>/map.19.txt`, which the bot imports on its first tick. | 60| `--make-home NAME` | Make the open-mode start state, as above, and run no bot. | 61| `--no-retry` | Goals the bot gives up on stay given up, as upstream's. By default the host puts them back when Link's possessions change; the summary counts both. | 62| `--jev` (or `ZB_JEV=1`) | Jev makes the goal choice (chapter 5). Needs the key. Every choice is a line of `<out-dir>/jev.jsonl`, and the summary gives questions and dollars per game-minute. | 63| `--jev-dollars USD` | Stop asking once this run has spent that much. | 64| `ZB_HUMAN=first-last:PAD` | Hold PAD (hex, Snes9x layout: `0x0400` down, `0x0040` X) as the human's pad on those frames. With X in it the bot steps aside, which tests what Link can physically do from a state. | 65| `ZB_TRACE=first-last` | Print the pad and a few bytes for each of those frames to stderr. | 66 67A run does not stop the moment the bot gives up (`ap_manual_mode`: no goals 68left). It runs the same stall detector and bounded recovery as the window 69(chapter 4): every given-up goal is retried, with growing backoff and a cap 70of tries without progress, and the run stops a minute after recovery itself 71gives up, saying so. 72 73The bot's stdout is its own log, redirect it. Its debug files (`goals.txt`, 74`map`, `map.pbm`, `full_map.dot`, ...) land in `<out-dir>`. By convention runs 75go under `run/zbanks-c/`. What the bot is and why the host is as it is: 76[research/zbanks-alttp.md](../../research/zbanks-alttp.md). 77 78### In this folder 79 80| Path | What | 81| --- | --- | 82| [src/main.rs](src/main.rs) | The whole program: options, the play loop, PNGs and `progress.tsv`, `--make-home`, Jev, stall and recovery, the summary. | 83| [BUCK](BUCK) | The `rust_binary`, over `console`, `alttp`, `zbanks`, `decisions`, `jev-http` and `png`. | 84 85← Previous: [Chapter 10, headless/](../headless/) · Up: [apps](../) · Next: [Chapter 12, native/](../native/) →