A generative horror adventure running entirely on the Nintendo Game Boy.
Moon Below contains a small transformer that generates narration token by token from the current game state. The world, puzzles, threat, and endings are deterministic. The wording is generated locally inside the cartridge.
No server. No API. No runtime sentence table.
Download the release candidate · How it works · Build it · What proof mode proves
You wake beneath a ruined observatory during an eclipse. Before morning, reach the Moon Gate and decide what comes through—or what remains below.
The observatory has 14 locations, ten important objects, three materially different routes, and six deterministic endings. Its Warden hunts sound rather than following a fixed room script: loud actions change its target and movement, while darkness, flooding, fuel, corruption, mechanisms, and inventory interact with the route you take.
Play slowly. Listen before committing to noisy machinery. Light helps you read the observatory, but it is limited. Replays expose different routes, discoveries, wording, and consequences without changing the underlying world rules.
- D-pad — move through the two-column action grid.
- A — choose an action or continue generated prologue/ending text.
- SELECT — open readable inventory, condition, and clue status.
- START — open ML context proof from gameplay.
- B — return from status/proof, or return gameplay to the title.
The ROM is Game Boy-compatible. Current performance and screenshots are measured under PyBoy DMG timing; original-hardware validation is still pending.
The game engine owns truth: rooms, items, puzzles, Warden movement, resources, and ending predicates. After a valid action it encodes the resulting state as a nine-token structured prompt. A one-layer quantized transformer consumes that prompt and generates up to ten visible tokens plus EOS autoregressively.
Semantic masks remove impossible vocabulary for the current state. They do not contain ordered phrases or completed sentences. Model logits still rank the allowed next tokens, and a persistent cartridge PRNG samples among model-scored candidates. A different seed can therefore change valid wording for the same state; a changed state changes the contextual candidate surface.
Game state → 9 prompt tokens → transformer → semantic mask
→ seeded sampling → generated words → Game Boy tiles
| Property | Production value |
|---|---|
| Parameters | 15,840 |
| Vocabulary | 232 tokens |
| Context | 20 tokens |
| Structured prompt | 9 tokens |
| Visible generation | up to 10 tokens plus EOS |
| Average inference | 1.784 s/token |
| Worst inference | 1.892 s/token |
| Static WRAM | 4,487 bytes |
| Bank 0 headroom | 1,671 bytes |
| ROM size | 131,072 bytes |
| Model fingerprint | E4757CDAC4FD154C |
| ROM SHA-256 | 9606843EF6F5E19C0985D6D7332FFE3845C095CADD159107890400204F448B30 |
The 2 KiB Bank 0 preference is advisory; the release passes its 1 KiB hard gate. See benchmarks for timing and memory methodology.
No complete runtime narration sentences are stored in the ROM.
- The transformer computes logits for every generated position.
- Seeded sampling selects among model-scored valid tokens.
- Masks remove words that would contradict the deterministic state.
- Masks are unordered token sets plus grammatical constraints, not sentence templates or ordered phrase tables.
- Exact Python/native/Game Boy traces verify the same logits, attention, and sampled tokens.
- The ROM audit scans for all reference narrations and known sentence-table symbols and fails if it finds them.
Examples captured by the release test:
Same state, different seeds:
soft sound remains under the cellar .
in the cellar your breath answers water .
Changed state:
iron steps answer in the cellar .
Press START during gameplay to see the exact challenge state, model fingerprint, seed, active mask size, candidates, selected token, and attention checksum used by production inference.
Requirements are pinned and documented in reproducibility.md.
With GBDK-2020 4.5.0 available through GBDK_HOME or PATH:
make rom
make test
make verifyOn Windows use mingw32-make and, if needed, PYTHON=python.
make rom regenerates world tables, the dataset, model headers, banked model
storage, prefix projections, and semantic masks before linking
gb/moon-below.gb. make verify starts from deleted generated C artifacts,
links twice to prove byte reproducibility, executes all game/ML/ROM checks,
captures media, scans public files, and builds the release archive.
The committed 96-epoch model is a versioned release artifact. Reproducing its 92-epoch training plus four-epoch fine-tune is documented separately and is not silently performed by a normal ROM build.
adventure/game/— deterministic engine, dataset, training, model, masks.gb/src/— game-owned Game Boy runtime snapshot, engine, UI, generated data.native/— desktop integer reference used for exact parity.train/— shared integer graph/export helpers inherited from DMGFormer.tests/— world, route, engine, narration, native, and model gates.tools/— clean build, audits, emulator verification, media, and packaging.docs/— player guide, architecture, model card, proof, and limitations.releases/— generated release archive and checksums.
Moon Below builds on DMGFormer, the reusable transformer runtime and research project. The runtime code needed by the game is included here, so a clean checkout never needs the original repository.
@software{perez2026moonbelow,
author = {Pedro Perez},
title = {Moon Below: A Generative Transformer Adventure for Game Boy},
year = {2026},
version = {1.0.0-rc1},
url = {https://github.com/pjperez/moon-below}
}Project-authored code is MIT licensed. The model, dataset, and visual assets
have explicit MIT statements under LICENSES/. Toolchains are not vendored;
their notices are in THIRD_PARTY_NOTICES.md.
