jevsnes.git / packages / zbanks / README.md
README.mdpreviewREADME.mdsource101 lines · 6.0 KB · raw
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/) →