YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
Melee RL β Fox vs Jigglypuff via Dolphin + OpenEnv
A reinforcement learning system that trains a Fox AI agent to play Super Smash Bros. Melee in real-time. The agent connects to Slippi Dolphin via libmelee and learns through self-play using PPO, served over HTTP with Meta's OpenEnv framework.
How It Works
Training Script (PPO) OpenEnv Server Slippi Dolphin
ββββββββββββββββββββ HTTP/WS ββββββββββββββββ libmelee ββββββββββββββββ
β dolphin_train.py β ββSmashActionββ> β FastAPI app β ββcontrollerβββ> β Melee game β
β β <βSmashObsβββββ β (port 8000) β <βgamestateββββ β (60 FPS) β
β CompetitiveMeleeβ β EmulatorEnv β β Fox vs Puff β
β Reward shaping β β Server β β on FD β
ββββββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
- OpenEnv server launches Dolphin, navigates menus, and exposes
reset()/step()over HTTP - Training client sends controller inputs (stick, buttons) each frame and receives game state back
- Reward shaping (
CompetitiveMeleeReward) computes per-frame reward on the client side - PPO updates the policy every 2048 frames
Training Pipeline
Phase 1 β Physics Simulator (fast, offline)
Train base policies in a custom Melee physics engine β no emulator needed.
pip install -r requirements.txt
# Train Puff (defensive, spacing-based)
python3 train.py --total-frames 1500000 --device cpu
# Train Fox/Mango (aggressive, pressure-based)
python3 mango_trainer.py --total-frames 500000 --device cpu
Produces checkpoints/puff_final.pt and checkpoints/mango_final.pt.
Phase 2 β Dolphin Fine-Tuning (real game)
Fine-tune the sim-trained Fox against real Melee running in Dolphin.
# Terminal 1: Start the OpenEnv server (launches Dolphin)
cd emulator_env && uv run --project . server
# Terminal 2: Train Fox via PPO against CPU or Puff model
cd emulator_env && uv run python dolphin_train.py --agent mango \
--checkpoint ../checkpoints/mango_final.pt --total-frames 500000
Configuration
All environment config is in emulator_env/.env:
DOLPHIN_PATH=~/Library/Application Support/Slippi Launcher/netplay
ISO_PATH=~/Downloads/Super Smash Bros. Melee (USA) (En,Ja) (v1.02).iso
P1_CHARACTER=FOX
P2_CHARACTER=JIGGLYPUFF
CPU_LEVEL=7 # 0 = model-driven P2, 1-9 = Dolphin CPU AI
TRAINING_MODE=NORMAL # NORMAL or RECOVERY
# P2_CHECKPOINT_PATH=../checkpoints/puff_final.pt # uncomment when CPU_LEVEL=0
| Setting | Options | Description |
|---|---|---|
CPU_LEVEL |
0 |
P2 controlled by a trained model (P2_CHECKPOINT_PATH) |
CPU_LEVEL |
1-9 |
P2 controlled by Dolphin's built-in CPU AI |
TRAINING_MODE |
NORMAL |
Standard matches |
TRAINING_MODE |
RECOVERY |
20% of resets spawn P1 off-stage for recovery training |
Model Architecture
ActorCriticMLP β shared backbone with separate actor and critic heads.
| Component | Details |
|---|---|
| Backbone | 3-layer MLP, 256 hidden units, Tanh activation |
| Actor head | Linear(256, 17) β MultiDiscrete [5, 4, 2, 2, 2, 2] |
| Critic head | Linear(256, 1) β value estimate |
| Observation | 26-dim vector (13 per player) |
| Action space | 320 discrete actions (stick Γ buttons) |
Observation Vector (26-dim)
Per player (13 dims): position (x, y), velocities (x, y), damage (normalized), stocks (normalized), on_ground, facing, action_state, hitstun.
Action Space
MultiDiscrete([5, 4, 2, 2, 2, 2]) mapping to GameCube controller:
| Index | Bins | Control |
|---|---|---|
| 0 | 5 | Stick X: [-1.0, -0.6, 0.0, 0.6, 1.0] |
| 1 | 4 | Stick Y: [-1.0, 0.0, 0.5, 1.0] |
| 2 | 2 | X button (jump) |
| 3 | 2 | A button (attack) |
| 4 | 2 | Z button (grab) |
| 5 | 2 | B button (special) |
Reward System
CompetitiveMeleeReward (rewards/competitive.py) β aggressive Fox / Mango style:
| Signal | Value | Purpose |
|---|---|---|
| Damage dealt | +0.05 per % | Reward hitting opponent |
| Damage taken | -0.005 per % | Light penalty for getting hit |
| Stock taken | +1.0 | Reward kills |
| Stock lost | -5.0 | Heavy anti-SD penalty |
| Win / Loss | +2.0 / -1.0 | Terminal bonus |
| Approach | +0.008 per unit closed | Incentivize charging toward opponent |
| Proximity | +0.003/frame within 25 units | Reward staying close |
| Combo | +0.06 per hit in hitstun | Reward follow-up hits |
| Edgeguard | +0.08 per hit while opponent off-stage | Reward edgeguarding |
| Velocity | +0.02 Γ speed within 50 units | Reward active movement near opponent |
| Existence | +0.002/frame on-stage | Survival incentive |
| Off-stage | -0.015/frame | Penalize being past the ledge |
| Blastzone | -0.0005 Γ (excess)Β² | Exponential wall near blastzones |
| Shield pressure | +0.05 Γ shield drain | Reward pressuring shield |
| Recovery | +0.03 Γ edge_dist_closed | Reward recovering to stage |
PPO Hyperparameters
| Parameter | Value |
|---|---|
| Batch size | 2048 |
| Mini-batch size | 64 |
| PPO epochs | 4 |
| Learning rate | 1e-4 |
| Gamma | 0.99 |
| Lambda (GAE) | 0.95 |
| Clip epsilon | 0.2 |
| Entropy coef | 0.01 β 0.001 (linear decay over 500k frames) |
| Max grad norm | 1.0 |
Tech Stack
| Tool | Role |
|---|---|
| OpenEnv (Meta) | HTTP/WebSocket server framework for RL environments |
| libmelee | Python API for reading game state and sending inputs to Dolphin |
| Slippi Dolphin | Modified GameCube emulator for Melee |
| PyTorch | Neural network + model inference |
| TorchRL | PPO implementation for sim training |
| Gymnasium | Standard RL environment interface (sim) |
| FastAPI | HTTP server (via OpenEnv) |
| uv | Python package manager |
Project Structure
βββ physics/ # Custom Melee physics simulator
β βββ state.py # Game state dataclasses
β βββ constants.py # Character + stage constants
β βββ melee_physics.py # Knockback, hitstun, gravity
β βββ simulator.py # Deterministic step function
β
βββ envs/ # RL environments
β βββ melee_sim_env.py # Gymnasium env (physics sim)
β βββ melee_torchrl_env.py # TorchRL env (alternative)
β
βββ rewards/ # Reward shaping
β βββ competitive.py # CompetitiveMeleeReward (Fox/Mango)
β βββ puff.py # PuffReward (Jigglypuff)
β
βββ emulator_env/ # Dolphin integration (OpenEnv)
β βββ server/
β β βββ app.py # FastAPI server (OpenEnv create_app)
β β βββ emulator_env_environment.py # Dolphin wrapper (reset/step)
β βββ client.py # EmulatorEnv WebSocket client
β βββ dolphin_train.py # PPO fine-tuning loop
β βββ policy_runner.py # ActorCriticMLP + obs/action conversion
β βββ models.py # SmashAction / SmashObservation (Pydantic)
β βββ menu_nav.py # Automated menu navigation
β βββ melee_constants.py # Stage geometry + velocity limits
β
βββ train.py # Puff PPO trainer (sim)
βββ mango_trainer.py # Fox/Mango PPO trainer (sim)
βββ opponents.py # Load frozen opponent checkpoints
β
βββ checkpoints/ # Trained models
βββ mango_final.pt # Sim-trained Fox
βββ puff_final.pt # Sim-trained Puff
βββ dolphin_fox_final_*.pt # Dolphin fine-tuned Fox
Checkpoints
| File | Description | Training |
|---|---|---|
mango_final.pt |
Base Fox policy | Physics sim, 500k+ frames |
puff_final.pt |
Base Puff policy | Physics sim, 1.5M frames |
dolphin_fox_final_501760.pt |
Fine-tuned Fox | Dolphin, 500k frames vs CPU/Puff |
Checkpoint format: {"model_state_dict": ..., "optim_state_dict": ..., "total_frames": int}