1# Chapter 4: zbanks, a home for somebody else's bot 2 3In 2020, Zach Banks wrote [a bot](https://github.com/zbanks/alttp) that 4plays the *A Link to the Past* Randomizer the way a person would: from what 5is on screen, read out of SNES memory, never peeking inside a chest before 6opening it. It is C, written (its own README says) for rapid iteration, and 7in its author's video it plays for 24 minutes and clears the Eastern Palace 8(with infinite health, bombs and arrows, which its README owns up to; those 9cheats are why the bot writes work RAM every frame). 10 11It was written to live inside a patched Snes9x emulator, and Snes9x gave it 12exactly two things: 13 14- **`base(addr)`**: a pointer to the byte at a SNES address. The bot calls it 15 once per address in `ap_init`, keeps the pointers for ever, and reads *and 16 writes* the game through them. 17- **`ap_tick(frame, *joypad)`**: called once per frame, before the frame 18 runs, with the pad the human is holding. Whatever the bot leaves in 19 `*joypad` is the pad for that frame. 20 21This crate is those two things over chapter 2's `Console`. It is the 22patched Snes9x, rewritten as a Rust host, so that upstream's C (chapter 17, 23with the patches of chapter 18) plays our emulator. 24 25```mermaid 26sequenceDiagram 27 participant H as Host (Bot::tick) 28 participant M as WRAM mirror 29 participant C as C bot 30 participant S as Console 31 H->>M: copy work RAM in 32 H->>C: ap_tick(frame, human's pad) 33 C->>M: reads and writes through base() 34 C-->>H: the pad to hold 35 H->>S: copy work RAM back out 36 Note over H,S: the caller holds the pad (apply_pad) and runs the frame 37``` 38 39> **Aside: why a mirror?** The bot keeps raw pointers for its whole life, 40> so the memory they point at must never move. `Bot::start` leaks a 128 KiB 41> buffer (and a copy of the ROM) on purpose, and `tick` copies the console's 42> RAM into it, lets the bot think, and copies it back. Rust gets to keep its 43> console movable; the C gets memory that stays put. 44 45**One bot per process.** The bot's whole state is C globals, so a second 46`Bot::start` panics. That single fact shapes a lot of chapter 12: rewinding 47the bot to an earlier frame means starting a new process. 48 49**Vanilla is not the Randomizer.** The bot was written for a Randomizer seed, 50which is built on the *Japanese* ROM and starts in open mode. We play the USA 51cartridge. So the host answers the bot's Japanese ROM addresses from the USA 52ROM (`JP_TO_US`), and `rando` patches the image the way the Randomizer 53differs where the bot can tell (no item-get text boxes, uncle and the 54Kakariko informant quiet) and presets a save in open mode. Patching the image 55changes its CRC, so every snapshot for the bot is made from the patched image. 56 57**The host helps where it can, from outside.** Everything here that is not 58in the C is a host decision, each with its evidence in 59[research/zbanks-alttp.md](../../research/zbanks-alttp.md): 60 61- *Goals given up on come back.* Upstream gives up a goal after its fourth 62 failed attempt and never retries it, so a goal tried too early (the 63 Eastern Palace big chest, before the big key) is lost for good. The host 64 puts every given-up goal back whenever Link's possessions change 65 (`POSSESSIONS`: items, keys, pendants, crystals, progress; not rupees or 66 health). `retry_given_up = false` is upstream's behaviour. 67- *Goals that cannot or need not be done are not made.* The shim declines 68 uncle once the save says he is gone, a one-time NPC whose reward is 69 already held, and the room `$0123` shopkeeper whose gift a talk can never 70 detect. 71- *A stalled bot is noticed and nudged.* `stall` decides whether the bot has 72 stopped playing (manual mode, no task, or Link not moving, each for five 73 minutes of play); `recovery` then puts every given-up goal back regardless 74 of possessions, with growing backoff, and gives up for good after five 75 tries that make no progress. 76- *Outcomes outlive the process.* Each goal that leaves the list, completed 77 or given up, is appended to `goal_outcomes.jsonl` beside the bot's files 78 and read back by the next bot, which otherwise remembers nothing. 79 80> **Try it.** `nix develop -c buck2 test //packages/zbanks:test` runs the 81> stall detector's, the recovery's and the outcome log's tests: pure state 82> machines, no bot started. To watch the real bot play, see chapter 11. 83 84## For the people who maintain it 85 86### In this folder 87 88| Path | What | 89| --- | --- | 90| [src/lib.rs](src/lib.rs) | `Bot`: `start` (upstream's `ap_init` against our console), `tick` (one `ap_tick`), `report` (info line, tasks, goals with scores, traps, stray reads), `export_map`, `set_goal_chooser`, `retry_all_given_up`. The `GoalChooser` trait and the `Situation`/`GoalOption` it is handed; the pad word (`PAD_BITS`, `apply_pad`); `JP_TO_US`; the `States` trait; `POSSESSIONS`; `rando` (the ROM `PATCHES`, `patch_rom`, the open-mode save `PRESET`). | 91| [src/stall.rs](src/stall.rs) | `Detector`: whether the bot has stopped playing, against `STALL_FRAMES` (18,000, five minutes). Shared by the window and headless, so there is one definition of "stalled". | 92| [src/recovery.rs](src/recovery.rs) | `Recovery`: retries every given-up goal on a stall, with `BACKOFF` between tries and a `CAP` of five tries without progress. | 93| [src/outcomes.rs](src/outcomes.rs) | `Outcomes`: each goal's latest outcome by identity (`kind\|node\|screen`), appended to `goal_outcomes.jsonl` and reloaded at start. | 94| [shim/host.c](shim/host.c) | The C side of the host: a SIGTRAP handler that is `assert_bp`'s gdb `continue`; `fopen`/`rename` sent to the host's directory; readers of the goal and task lists; the goal-choice hook's host side; declining goals the save says are done; the given-up watch and the outcome ring. | 95| [BUCK](BUCK) | The shim as a `cxx_library`, the crate, and `//packages/zbanks:test`. | 96 97Who uses it: `apps/native` (the window, chapter 12), `apps/zbanks` 98(headless, chapter 11), `apps/replay-probe` (chapter 14), and 99`packages/decisions` and `packages/replay`. 100 101← Previous: [Chapter 3, alttp/](../alttp/) · Up: [packages](../) · Next: [Chapter 5, decisions/](../decisions/) →