jevsnes.git / apps / zbanks / README.md
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/) →