API Reference

All classes below are importable directly from retro_gamer.

Game description

class retro_gamer.GameMetadata(actions: list[str], reward: str, character_set: list[str] | None = None, spatial: bool = False, board: bool = True, board_size: tuple[int, int] | None = None, observation_function: str | None = None, factory: str | None = None, extras_size: int = 0)

Describes a retro game for training purposes.

Required fields: actions, reward. Optional fields: character_set, spatial, observation_function, factory. Discovered fields: board_size (from game.board_size), extras_size (measured by DQNTrainer from one sampled observation — never declared).

observation_function, if set, is a “module:attr” string naming a function f(game) -> Any that fully replaces the built-in board/observe_state encoding. It is mutually exclusive with the [preprocessing] observe_state option. See GameEnvironment for how the two paths are selected.

factory, if set, is a “module:attr” string naming the create_game function. Read from [tool.retro-gamer].factory in pyproject.toml. When absent, the loader falls back to looking for create_game on the game module.

resolve_factory() Callable | None

Import and return the factory function named by the factory field, or None if unset.

resolve_observation_function() Callable | None

Import and return the function named by observation_function, or None if unset.

Raises ValueError with an actionable message if the string isn’t “module:attr”, the module can’t be imported, or it has no such attribute.

classmethod from_pyproject(module_name: str) GameMetadata

Load metadata from the [tool.retro-gamer] section of the game’s pyproject.toml.

Training

class retro_gamer.DQNTrainer(game_factory: Callable, metadata: GameMetadata, run_dir: str | Path, preprocessing: dict | None = None, **hyperparams)

Trains a deep Q-network agent to play a retro game.

Automatically selects the best available training device: Apple Silicon GPU (MPS), NVIDIA GPU (CUDA), or CPU. The chosen device is recorded in training.log.

On initialization, the trainer:

  1. Discovers the character set if not already specified in metadata.

  2. Builds the Q-network and logs its full architecture with rationale.

  3. Writes config.toml and initializes training.log in run_dir.

Hyperparameters can be passed as keyword arguments; see the Hyperparameters reference for all options. Values not supplied fall back to sensible defaults.

Call train() to run all episodes. Checkpoints are saved every 100 episodes and training can be stopped (Ctrl+C) and resumed at any time.

Example:

from retro_gamer import GameMetadata, DQNTrainer
from retro.examples.snake import create_game

metadata = GameMetadata.from_pyproject("retro.examples.snake")
trainer = DQNTrainer(create_game, metadata, "runs/snake/")
trainer.train()
train(on_checkpoint=None, on_episode=None)

Run all training episodes and save checkpoints.

on_checkpoint, if provided, is called after each checkpoint with a dict containing episode, avg_reward, avg_steps, avg_loss, and epsilon. on_episode, if provided, is called after every episode. When either callback is supplied, the built-in tqdm progress bar is suppressed (the caller is expected to show its own progress UI).

load_checkpoint(path: str | Path)

Load a checkpoint to resume training.

Checkpoints are PyTorch state dicts stored under run_dir/checkpoints/. Each contains model weights, optimizer state, current epsilon, and total step count.

Raises ValueError if the checkpoint was trained with a different character set, board size, action space, or network architecture. The error message names each changed field and explains why it is incompatible.

The CLI invokes this automatically; call directly only when driving training from Python.

Environment

class retro_gamer.GameEnvironment(game_factory: Callable, metadata: GameMetadata, observe_state: list[str] | None = None, board: bool = True, observe_state_sizes: dict[str, int] | None = None)

Gym-style wrapper around a retro game for RL training.

The observation returned by reset()/step() comes from one of two mutually exclusive paths: metadata.observation_function, if set, fully replaces the built-in board/observe_state encoding — see GameMetadata for its contract. Otherwise the built-in encoder (encode_observation) is used, configured by observe_state/board/observe_state_sizes below.

reset()

Create a fresh game episode and return the initial observation.

The observation’s type depends on metadata.observation_function: a numpy array when using the built-in encoder (or a custom function built for DQN training), but it can be anything a custom function returns — e.g. a plain tuple for tabular use.

step(action: str | None) tuple

Advance one turn. Returns (observation, reward, done).

Using a trained model

class retro_gamer.TrainedPolicy(run_dir: str | Path, checkpoint: str | None = None)

A trained retro-gamer model that can observe a game and choose actions.

Load from a training run directory, then call get_action(game) from inside any agent’s play_turn to get the model’s recommended key.

Example:

from retro_gamer import TrainedPolicy

_ai = TrainedPolicy("runs/enemy/")

class EnemyAgent:
    def play_turn(self, game):
        key = _ai.get_action(game)
        if key == 'KEY_RIGHT': self.direction = (1, 0)
        ...
get_action(game) str | None

Return the key the model recommends this turn, or None for no-op.

class retro_gamer.PolicyInput(model: TrainedPolicy, game)

An InputSource that drives the game with a TrainedPolicy instead of the keyboard.

Pass it as input_source to game.play() and everything else works exactly as usual.

Example:

from retro_gamer import TrainedPolicy, PolicyInput
ai = TrainedPolicy("runs/snake/")
game = create_game()
game.play(input_source=PolicyInput(ai, game))