Skip to main content

Retro Speedlab Core

CI Python 3.12 Last commit License: GPL-3.0 Ruff codecov

Tool-assisted speedrun search for classic video games. The engine searches the fastest movie of buttons to press on every frame from power-on, verifies that it replays, and streams the run to Retro Speedlab. Game packages supply the game knowledge (RAM map, states, rewards, actions); this repository turns it into a speedrun.

Highlights

  • Power-on start like a real speedrun: every movie boots the game at its title screen; levels are states of one full-game run
  • Go-Explore segment search on many emulator processes, split at the state machine's transitions
  • Every segment is verified: the whole movie replays from power-on before a new segment counts
  • Route memory from earlier runs guides the search towards a state's exit
  • .bk2 movie and MP4 video of the verified run
  • Live relay of the newest verified segment, the run's time, level splits and the lab's status to the Retro Speedlab backend
  • Lab runs: when the search is stuck or stalls, Claude Code improves the game package on its own branch

Architecture

flowchart TD
    Game["Game package<br/>RAM · States · Rewards · Actions"]
    Env["Stable Retro emulator<br/>StateMachineGymWrapper"]
    Search["Segment search<br/>one worker per process"]
    Movie["Verified movie<br/>progress.msgpack · tas.bk2 · screen.mp4"]
    Relay["Live relay<br/>snapshot · whole movie"]
    Lab["Lab run<br/>agent/daily.sh"]

    Game --> Env
    Env --> Search
    Search --> Movie
    Movie --> Relay
    Movie -- "stuck or stalled" --> Lab
    Lab --> Game

The app's default workflow is speedrun: it writes the fastest movie from power-on for any game package and improves it forever:

python -m datenwissenschaften.tas.run search <wrapper> <config> <until-state>
python -m datenwissenschaften.tas.run improve <wrapper> <config>
python -m datenwissenschaften.tas.run verify <wrapper> <config> <progress>

The run is split at the state machine's transitions. Each segment is searched Go-Explore style on speedrun.workers emulator processes: an archive keeps the fastest savestate per cell (state, 8 px position, height, airborne and RamInfo.dynamics(), the game's coarse view of whatever moves nearby) and branches from rarely visited cells with short random input bursts. Half the time it branches from the tenth of cells that earned the most of the segment's reward, and it favours cells the route memory knows to be few decisions from the exit; a full archive drops its least promising cell. Workers step the emulator core without rendering. A crossing into a later state ends a branch and counts only when the new state holds; deaths and backward transitions drop it. Of the three fastest distinct crossings, the one whose next segment is also fastest is kept, and frames it does not need are shaved off. A segment gets speedrun.segment_seconds per round and twice as long with each retry. Random numbers live in the savestates, so the search plans against the real RNG and can wait frames to change it. A segment without a way out is marked stuck in cache/tas/stuck.json and the way into it is searched again.

After every segment the whole movie is replayed from power-on; segments that do not reproduce are searched again. cache/tas/progress.msgpack lets a run resume, cache/tas/tas.bk2 is the verified movie, screen.mp4 its video and search.log the search's log. metadata.speedrun tells the stream the run's time from power-on and from Start, its segments, the level splits (from GameWrapper.levels, with the part before the first level split under the first state's name, opening), each state's goal, the game and its record sources, the engine's events, milestones and time history (journal.json) and the segment being searched. While a segment is searched every worker writes its cells and frames to live/<seed>.msgpack every 2 s and its latest frame at half size to live/<seed>.jpg once a second; the main process folds the reports into search every 5 s and the relay puts a mosaic of the first nine workers' frames (seed order, row by row, seeds in X-Seeds) to /live/attempts.jpg once a second. Whenever the verified movie changes a background thread renders it from power-on to an H.264 MP4 at twice the NES resolution, puts it to /live/movie.mp4 in parts, puts the game memory sampled every 6 frames of the power-on replay to /live/movie-ram.json and publishes speedrun.movie: its version (the first 12 hex digits of its SHA-256), frames, seconds, frame rate and each segment's state, the state it reaches and its start frame, so the stream plays the whole run in sync with its splits. When improve finds a faster segment, the old and the new piece are rendered side by side from the same savestate, put to /live/compare.mp4 and published as speedrun.compare with the state, both frame counts and the time. improve searches every segment again, bounded by its current frames; a faster crossing replaces it and the rest is searched anew. verify fails unless a progress file still replays with a game package.

Route memory

paths.models/<game>/<state>/route.json lists, per 8 px cell, how many decisions the fastest known run needed from there to the state's exit. The search weights cells on the route by how close they are to the exit. A state without the file is searched without guidance.

Workflows and the lab

Flyte runs the work as tasks (datenwissenschaften.workflow): speedrun relays the stream, publishes the lab's status and searches and improves the movie forever. lab first starts the critic (agent/critic.sh) once five new reports landed in agent/reports/ since its last review; the stream shows its lessons as metadata.critic. Then it starts a lab run (agent/daily.sh of the game package) at once when the search is stuck, or once the movie has not improved for agent.stall_minutes minutes. Claude Code works for agent.minutes minutes on its own git worktree of the game package, and its branch ships only when its checks pass and the current movie still replays with it (tas.run verify). The lead session delegates to the subagents defined in the game package's .claude/agents/; the stream shows each member's role, state and latest step and every hand-off with its short task, the lead's latest actions (only the kind of action and the source files it touches), its commit subjects with URLs and secret-like words hidden, and the lines it changed. Every task runs locally with flyte run --local.

Live relay

With ui.enable: true and an upload key, the engine relays to the Retro Speedlab backend (upload.url) every few seconds the snapshot (the published metadata and the release) and the search's newest frame; the whole verified movie goes up whenever it changes. The website's /stream page, opened with the stream key (?api_key=), plays them for OBS. The engine itself serves nothing.

Installation

Retro Speedlab Core requires Python 3.12.

git clone https://github.com/datenwissenschaften/retro-speedlab-core.git
cd retro-speedlab-core
uv sync
cp config.example.yaml config.yaml

The published package and import path are datenwissenschaften.

Minimal usage

This repository does not ship a playable game; a game package supplies the RAM layout, states, rewards and actions:

import numpy as np

from datenwissenschaften.environment.wrapper import StateMachineGymWrapper


class AirstrikerWrapper(StateMachineGymWrapper[AirstrikerRam]):
    start_state_cls = SurviveAndScore
    state_classes = (SurviveAndScore,)
    ram_info_cls = AirstrikerRam
    action_table = np.array([[[1] + [0] * 11, [0] * 12, [0] * 12, [0] * 12]], dtype=np.int8)
    levels = ()
python -m datenwissenschaften.tas.run search airstriker:AirstrikerWrapper config.yaml SurviveAndScore

Every state sets a one-line goal. The ROM is imported through Stable Retro from paths.roms; the engine's own test suite needs no ROM.

Configuration

Every key in config.example.yaml is required; missing or malformed values raise an error when the configuration is loaded.

Key Meaning
paths.roms, paths.integrations ROMs and custom Stable Retro integrations
paths.models Route memory per state
paths.cache The search's movie, progress, stuck marker, journal, live reports and log under tas/
paths.lab_run Marker file of a running lab run
training.game Stable Retro game id
upload.url, upload.api_key Retro Speedlab API; null disables the relay
ui.enable, ui.release Live relay on or off and the release label shown on stream
twitch.enabled Whether the stream page goes live on Twitch
agent.stall_minutes Minutes without a faster movie before the lab task starts a lab run
speedrun.workers, speedrun.segment_seconds Search processes and seconds per segment round
speedrun.id, speedrun.title, speedrun.platform The game as the stream names it
speedrun.category, speedrun.speedrun_com, speedrun.tasvideos The category run and where its records are read: speedrun.com game, category and variables, TASVideos publication
log_level Loguru log level

Testing

uv run pytest --cov=datenwissenschaften --cov-fail-under=95

Tests run on a scripted emulator, so they need no ROM.

Development

uv run ruff check .
uv run ruff format --check .

Relationship to Retro Speedlab

  • Retro Speedlab Core (this repository): the engine: segment search, verified movies, the live relay and the lab workflow.
  • Game packages: one repository per game with its RAM map, states, rewards, actions and Dokku deployment, improved by lab runs.
  • retrospeedlab.com: the lab's website, showing the stream and the speedrun.

License

Copyright © datenwissenschaften contributors.

Distributed under the GNU General Public License v3.0.

Metadata

Release files for datenwissenschaften 3.0.64

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for datenwissenschaften 3.0.64
File Size Uploaded Provenance Actions
datenwissenschaften-3.0.64.tar.gz 50.8 kB Without provenance Details for datenwissenschaften-3.0.64.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for datenwissenschaften 3.0.64
File Interpreter ABI Platform Actions
datenwissenschaften-3.0.64-py3-none-any.whl Python 3 none any Details for datenwissenschaften-3.0.64-py3-none-any.whl

Total release size: 116.8 kB

Release files / datenwissenschaften-3.0.64.tar.gz

Download URL datenwissenschaften-3.0.64.tar.gz
Size 50.8 kB
Tags Source
SHA-256 checksum
How to use checksums
28414604d307b0700ea6f8cca0741ef2abb56d61549c83c33ed8cf553e99d02a
BLAKE2b-256 checksum
How to use checksums
605459855dd472439ceba5a312c22874a0c8258d51de871ba140291894b3edbb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.5.1 CPython/3.12.15 Linux/7.0.0-1017-azure

Release files / datenwissenschaften-3.0.64-py3-none-any.whl

Download URL datenwissenschaften-3.0.64-py3-none-any.whl
Size 66.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c188f52bb552a8687bcd95a2c9a677f02349850bc86eef0dadea0d84592f380
BLAKE2b-256 checksum
How to use checksums
f32cfeb462f38b4fc2a5c9151a86f224f4461152fe08ba68abfcdc5d5935f7f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.5.1 CPython/3.12.15 Linux/7.0.0-1017-azure

Security for datenwissenschaften 3.0.64

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

3.0.64 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page