Arcade games
StableTested and in active use. Breaking changes will be called out.
Three arcade games reimplemented as native GPU engines — not emulation. Pure game physics in Mojo, batched on device, with either a compact state vector or an Atari-shaped pixel stack.
| Game | Clean obs | Actions | Pixel obs |
|---|---|---|---|
| Pong | 6 | 3 | 4×84×84 |
| Breakout | 7 | 4 | 4×84×84 |
| Space Invaders | 8 | 4 | 4×84×84 |
Two observation modes
Section titled “Two observation modes”Set by a compile-time parameter:
OBS_MODE=0 — clean state. A short float vector of exactly the quantities
that matter: paddle position, ball position and velocity, brick or alien counts.
Six to eight dimensions.
OBS_MODE=1 — pixels. A 4×84×84 grayscale frame stack, matching the
standard Atari preprocessing, for convolutional agents.
Having both on identical dynamics is the point. You can measure what a representation costs — the same algorithm on the same game, learning from the state directly versus learning to see it. Pixel training typically needs orders of magnitude more steps for the same policy.
Why native rather than emulated
Section titled “Why native rather than emulated”The Atari 2600 emulator exists in this repo and runs these same games from original ROMs. The native engines are a deliberate parallel implementation, for two reasons.
Speed. The emulator is CPU-only — 6502 opcode dispatch diverges too heavily for the Metal compiler to handle. The native engines are GPU kernels, batched across hundreds of environments, so they are orders of magnitude faster to train against.
Control. A native engine can expose clean state. A ROM cannot: the emulator gives you a framebuffer and 128 bytes of RAM whose meaning you would have to reverse-engineer per game.
The emulator earns its keep as a correctness reference — the native engines are validated against it, which is a stronger check than “the training curve looks plausible”.
Training
Section titled “Training”Pong is the standard target. Rainbow on clean observations is the fastest path to a working policy; the pixel variants are the real test:
pixi run -e nvidia mojo run -I . examples/arcade_games/rainbow_pong_training_gpu.mojopixi run -e nvidia mojo run -I . examples/arcade_games/rainbow_pong_pixel_training_gpu.mojopixi run -e nvidia mojo run -I . examples/arcade_games/muzero_pong_pixel_training_gpu.mojoPlay them yourself with the SDL3 renderer:
pixi run mojo run -I . examples/arcade_games/pong_playable.mojopixi run mojo run -I . examples/arcade_games/breakout_playable.mojopixi run mojo run -I . examples/arcade_games/space_invaders_playable.mojoBatched GPU use
Section titled “Batched GPU use”All three implement GPUDiscreteEnv:
from noeira.deep_agents.training.batched_env import BatchedGpuDiscreteEnvfrom noeira.envs.arcade_games.pong import Pong, PongConfig
comptime N_ENVS = 64comptime EnvT = BatchedGpuDiscreteEnv[Pong[DT], N_ENVS, PongConfig.OBS_DIM, 1]- Atari 2600 — the emulator these are validated against
- 2D physics — CarRacing also offers pixel observations
- CPU and GPU paths — batched environment mechanics