Watch cube agents learn soccer (striker vs goalie) in real time, and steer what they learn with live reward sliders. Godot 4 renders and simulates 16 pitches at once; a Python PPO trainer is the brain. No presets — behavior emerges from rewards only.
# Terminal 1 — the brain
uv run python -m trainer.main
# Terminal 2 — the lab (or open godot/ in the Godot editor and press F5)
"C:\Users\omamm\Tools\Godot\Godot_v4.7.1-stable_win64.exe" --path godot
Adjust the Godot path if yours differs.
The game connects to the trainer over a local TCP socket (127.0.0.1:9878); it retries every 2 s and pauses the pitches until connected, so start order doesn't matter.
Controls: right-mouse drag = look, WASD/QE = fly, wheel = camera speed. Panel (right side): a visible round timer counting down the hero pitch's (pitches[0]) round; reward sliders (touch_ball, goal_scored, goal_conceded, save, fall_out, touch_wall) apply instantly mid-training, 0-4x each; time scale 1-10x; pause/resume training; "Reset rounds + timer" to reset every pitch's round clock; save/load policy (trainer/checkpoints/{striker,goalie}.pt, autosaved every 60 s); "Load best brains" to load the all-time-best checkpoints (trainer/checkpoints/{striker,goalie}_best.pt); an "Auto-restore" toggle (default OFF) that, when enabled, has the trainer auto-restore from the best brains whenever performance regresses; live rounds/goals/saves stats plus mean round length; a mean-reward chart (red = striker, blue = goalie). In the world itself: a record scoreboard standing behind the hero pitch's goal showing the best score ever recorded, and a green/red reward label floating over each cube that shows its live per-tick reward.
Reset the brain: stop the trainer and delete the whole trainer/checkpoints/ directory — it starts fresh (untrained) on the next connection. (Deleting only the *.pt files leaves best.json behind, so a fresh brain would inherit a stale unbeatable record.)
TensorBoard: uv run tensorboard --logdir runs
The brain lives in trainer/checkpoints/ (autosaved every 60 s and on shutdown) and reloads automatically whenever the trainer starts. Updates that keep the observation/action sizes unchanged (like this one) resume training seamlessly. The best brains ever recorded are kept separately as striker_best.pt / goalie_best.pt — load them anytime with the panel's "Load best brains" button. To start completely fresh, stop the trainer and delete trainer/checkpoints/.
uv run pytest -q # python suite: protocol, PPO, rollout,
# session, random-brain, end-to-end (full suite)
bash scripts/soak_test.sh # 60s soak: random brain + headless game
# godot unit tests (headless):
GODOT="/c/Users/omamm/Tools/Godot/Godot_v4.7.1-stable_win64_console.exe"
"$GODOT" --headless --path godot --script res://tests/test_cube_controller.gd
"$GODOT" --headless --path godot --script res://tests/test_pitch.gd
"$GODOT" --headless --path godot --script res://tests/test_push.gd
"$GODOT" --headless --path godot --script res://tests/test_ui_panel.gd
Two standalone fake-brain/fake-game tools back the tests above and are also
runnable on their own: uv run python -m tools.random_brain (accepts the
Godot handshake, replies with random actions — no learning) and
uv run python -m tools.echo_game (a fake Godot client with a trivial
learnable task; needs a trainer already listening on 9878).
Spec: docs/superpowers/specs/2026-08-10-cube-soccer-lab-design.md Plan: docs/superpowers/plans/2026-08-10-cube-soccer-lab.md