An AI that learns to play Game Boy / Game Boy Color games by watching human gameplay, then plays autonomously using brute-force search over discovered objective functions.
Ported from LearnfunPlayfunAtari2600 to the Game Boy via the gambatte-libretro core — a TAS-grade, deterministic Game Boy emulator.
Based on Tom Murphy VII's original learnfun & playfun — the paper is a great read if you want to understand the theory.
- Record a human playing a Game Boy game (
recordfun) - Learn objective functions from the recording by finding lexicographic orderings on RAM bytes that correlate with "progress" (
learnfun) - Play the game autonomously by searching over possible input sequences and scoring them against the learned objectives (
playfun) - Replay the AI's best run visually with sound (
replayfun)
The approach is entirely game-agnostic — it knows nothing about any specific game. It discovers what "progress" means by observing which RAM values tend to increase when a human plays well.
| Executable | Purpose |
|---|---|
recordfun |
Play the game with keyboard and save inputs as .gbinp |
learnfun |
Analyze a recording to discover objective functions |
playfun |
AI plays the game using learned objectives (master/helper distributed search) |
replayfun |
Watch a .gbinp replay with video and audio |
scopefun |
Visualize RAM activity during a replay |
pinviz |
Visualize input pin activity |
tasbot_gui.pyw |
Python/tkinter GUI for the full workflow |
- Visual Studio 2022 (MSVC v143 toolset)
- CMake 3.20+
- vcpkg (with
VCPKG_ROOTenvironment variable set, or installed atC:\vcpkg) - SDL2 (via vcpkg:
vcpkg install sdl2:x64-windows) - Other deps installed automatically via vcpkg: protobuf, zlib, libpng, lz4
git clone https://github.com/AtlasRedux/LearnfunPlayfunGameBoy.git
cd LearnfunPlayfunGameBoy
git submodule update --init --recursivecd tasbot
cmake --preset default
cmake --build --preset releaseOr use the included build.bat from the project root.
Binaries are output to tasbot/build/Release/.
The fastest way is the included GUI:
cd tasbot\build\Release
pythonw tasbot_gui.pyw
Drop a .gb or .gbc file into that folder (or use the Browse… button), then click through the four buttons in order:
- 🎮 Record (play game) — opens an SDL window; play the game with the keyboard, ESC saves the input log as
<game>.gbinp. - ▶ Pretrain (learnfun) — analyses the recording and writes
<game>.objectivesand<game>.motifs. - ▶ Run (playfun) — spawns one helper process per CPU core (configurable) plus a master. The AI plays the game and saves its best run so far to
<game>-playfun-futures-progress.gbinpperiodically. The Resume checkbox continues from an existing progress file. - 🎬 Watch Replay — opens
replayfun.exeon the latest progress file, with audio and per-frame controls.
The GUI auto-detects ROMs and input logs in its directory, manages the helper/master lifecycle, and streams the master's coloured progress bars to its log pane.
Keyboard controls in recordfun:
- Arrow keys: D-pad
- Z: A button
- X: B button
- Enter: Start
- Right Shift (or Tab): Select
- ESC: quit and save
If you'd rather skip the GUI, all tools read a config.txt file in the working directory:
game mario
rom mario.gb
movie mario.gbinp
game is the base name (used for output filenames). rom is optional — if omitted, the tools look for <game>.gb, then <game>.gbc.
recordfun.exe # Step 1 — record human play, writes <game>.gbinp
learnfun.exe # Step 2 — write <game>.objectives and <game>.motifs
# Step 3 — start one helper per core, then the master:
playfun.exe --helper 29000
playfun.exe --helper 29001
playfun.exe --master 29000 29001 ...
replayfun.exe # Step 4 — watch the latest progress file
replayfun.exe controls: Space (pause), Right (step), Up/Down (speed), R (restart), M (mute), ESC (quit).
- Emulator wrapper (
emulator.cc): Abstracts the gambatte libretro core behind a clean C++ API — initialize, step, save/load state, read RAM, get video/audio. - Objective discovery (
objective.cc,weighted-objectives.cc): Finds lexicographic orderings on RAM that correlate with human gameplay progress. Works on arbitrary byte arrays — fully generic. - MARIONET protocol (
marionet.proto,netutil.cc): Protobuf-based TCP protocol for distributed master/helper search. Sends opaque byte blobs (emulator states, input sequences). - Input format (
.gbinp): Simple text format — one line per frame, 8 charactersRLDUTSBAwhere.means not pressed. Human-readable and diffable.
| Atari 2600 (previous port) | Game Boy (this port) | |
|---|---|---|
| Emulator | stella2023 (libretro) | gambatte (libretro) |
| Resolution | 160 × 210 (variable) | 160 × 144 |
| RAM exposed | 128 bytes | 8 KB (DMG) / 32 KB (CGB) |
| Inputs | 5 (UDLRF) + 2 console | 8 (RLDUTSBA) |
| State size | ~1.2 KB | varies (~10–60 KB depending on cart) |
| Input format | .a26inp |
.gbinp |
| Refresh rate | ~60 Hz NTSC | ~59.7 Hz |
The larger RAM gives learnfun more candidates to find meaningful progress signals — most Game Boy games keep position, score, lives, and timers in WRAM where the algorithm can latch onto them.
- Tom Murphy VII — original learnfun/playfun concept, NES implementation, and the brilliant paper
- AtlasRedux — NES revival, Atari 2600 port, and Game Boy port
- gambatte — accuracy-focused Game Boy / Game Boy Color emulator by Sindre Aamås and contributors
- cc-lib — Tom Murphy VII's utility library
Same terms as the original learnfun/playfun. Gambatte is GPL v2 — see gambatte-libretro/COPYING. See individual source files for details.