- lab_api.py: import and register card_art router before static mount - lab_api.py: mount /static/card-images BEFORE the legacy UI wildcard mount - requirements.txt: add tqdm>=4.65.0, aiofiles>=23.0.0
Commander AI Lab
A full-stack MTG Commander AI research platform. Simulate hundreds of games overnight, train ML models on the results, and let simulation data automatically shape the AI deck builder — all from a web UI or CLI.
What It Does
| Layer | What it is |
|---|---|
| Monte Carlo Engine | Pure-Python headless sim — runs thousands of games in minutes, no JVM needed |
| Forge Engine | Java/Forge rules-complete sim for ground-truth validation |
| Overnight Runner | Unattended batch runner — runs sims, updates sim weights, trains neural net |
| ML Pipeline | Outcome-weighted RL nudges card scoring weights; neural net trains on decision logs |
| Deck Builder | 7-step LLM pipeline (EDHrec → Scryfall → Ollama) now injected with sim-learned insights |
| AI Coach | LLM-powered coaching sessions (Anthropic/Ollama) with streaming support |
| Web UI | React + FastAPI dashboard for decks, sims, training, and deck building |
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Web UI (React/TypeScript — frontend/) │
│ Deck Builder · Sim Dashboard · Training · Coach │
└────────────────────────┬────────────────────────────────────────┘
│ HTTP/REST + WebSocket
┌────────────────────────┴────────────────────────────────────────┐
│ FastAPI Server (lab_api.py / routes/ — port 8080) │
│ /api/lab/* /api/ml/* /api/coach/* /api/deck-builder/* │
└────────┬──────────────────┬──────────────────┬──────────────────┘
│ │ │
Java subprocess Python sim engine Ollama (local LLM)
(Forge rules) (Monte Carlo) qwen2.5:7b + others
│ │
┌────────┴───────┐ ┌───────┴────────────────────────────────────┐
│ Forge Engine │ │ src/commander_ai_lab/ │
│ (rules- │ │ sim/ — engine, models, rules │
│ complete) │ │ deck_builder/ — 7-step LLM pipeline │
└────────────────┘ │ coach/ — streaming LLM coach │
└────────────────────────────────────────────┘
Quick Start (Windows)
Prerequisites: Java 17+, Python 3.10+, Maven 3.8+, Forge built at D:\ForgeCommander\forge-repo
# 1. Build Forge (one-time)
cd D:\ForgeCommander\forge-repo
mvn package -pl forge-gui-desktop "-DskipTests" "-Drevision=2.0.12-SNAPSHOT" -am
# 2. Build Commander AI Lab
cd D:\ForgeCommander\commander-ai-lab
mvn package "-DskipTests"
# 3. Install Python deps
pip install -e .
# 4. Launch
.\start-lab.bat
# → open http://localhost:8080
Overnight AFK Runner
Runs batch sims unattended, then updates card weights and trains the neural net:
python overnight-run.py # default: 11.5h DeepSeek sims
python overnight-run.py --batches 20 --games 50 # fixed batch count
python overnight-run.py --no-update-weights # skip weight update
python overnight-run.py --no-train # skip neural net training
What happens after sims complete:
update_weights.py— nudgeslearned_weights.jsonfrom decision logs (feeds deck builder)/api/ml/train— trains the neural net on the full decision dataset
Sim → Deck Builder Feedback Loop
Simulation outcomes automatically improve deck recommendations:
Overnight sims → decisions_*.jsonl
↓
update_weights.py → learned_weights.json
↓
sim_insights.py → natural-language insight block
↓
Ollama prompts (suggest_cards + enforce_deck_ratios)
↓
Better card recommendations
When active, BuildResult.sources_consulted includes "sim_weights". No-op until real sim data exists.
Deck Builder Pipeline
The POST /api/deck-builder/build endpoint runs 7 steps:
- Resolve commander via Scryfall
- Fetch EDHrec recommendations
- Fetch Scryfall candidates by category (ramp, removal, draw, lands)
- Ollama suggests synergy/protection/wincon cards ← sim insights injected here
- Filter by color identity, collection, ban list
- Ollama enforces ratios (trim/expand to 99) ← sim insights injected here
- Assemble final deck
ML Pipeline
# Update sim card weights from decision logs (runs automatically in overnight-run.py)
python -m ml.scripts.update_weights --min-games 10 --lr 0.05 --dry-run
# Reset weights to defaults
python -m ml.scripts.update_weights --reset
# Generate synthetic training data
python ml/scripts/generate_synthetic_data.py
Learned weights are clamped to [-20.0, 20.0] and stored in src/commander_ai_lab/sim/learned_weights.json.
Python Simulator API
from commander_ai_lab.lab.experiments import build_deck, run_simulation
deck = build_deck(["Sol Ring", "Cultivate", "Lightning Bolt"] + ["Forest"] * 36)
stats = run_simulation(deck, num_games=100, name_a="My Deck")
print(f"Win rate: {stats['win_rate']:.1%}, Avg turns: {stats['avg_turns']:.1f}")
The Monte Carlo engine models: combat keywords (flying, trample, deathtouch, lifelink, menace, reach, haste), summoning sickness, tapped/untap state, commander zone, multiplayer attack targeting, and board wipes. It does not model the stack, instants, or continuous type-changing effects.
API Reference
| Endpoint | Method | Description |
|---|---|---|
/api/lab/decks |
GET | List available Commander decks |
/api/lab/start |
POST | Start Forge batch (returns batchId) |
/api/lab/start-deepseek |
POST | Start Python/DeepSeek batch |
/api/lab/status?batchId=X |
GET | Poll batch progress |
/api/lab/result?batchId=X |
GET | Get completed batch JSON |
/api/lab/history |
GET | List past batch runs |
/api/ml/train |
POST | Start neural net training |
/api/ml/train/status |
GET | Poll training progress |
/api/coach/chat |
POST | LLM coaching session (streaming) |
/api/deck-builder/build |
POST | Build a 99-card Commander deck |
Project Structure
commander-ai-lab/
├── overnight-run.py # AFK batch runner
├── start-lab.bat # Windows one-click launcher
├── lab_api.py # FastAPI server entry point
├── frontend/ # React + TypeScript web UI
├── ui/ # Legacy static HTML UI
├── src/commander_ai_lab/
│ ├── sim/ # Monte Carlo engine (engine, models, rules)
│ ├── deck_builder/ # LLM deck builder pipeline
│ │ ├── api/ # Scryfall, EDHrec, Ollama, sim_insights
│ │ ├── core/ # Models, rules engine, assembler
│ │ └── pipeline/ # build_deck.py (7-step orchestrator)
│ └── coach/ # LLM coaching (Anthropic + Ollama)
├── ml/
│ ├── scripts/ # update_weights.py, ml_cli.py
│ ├── training/ # Neural net trainer
│ ├── encoder/ # Feature encoding
│ └── models/ # Saved model checkpoints
├── scanner/ # Card OCR pipeline
├── logs/decisions/ # ML decision logs (gitignored)
└── src/main/java/commanderailab/ # Java/Forge batch runner
Known Limitations
- Monte Carlo engine — no stack, no instants, no continuous type changes (animated lands don't attack)
- Forge engine — each game spawns a new JVM (~2-3s overhead), ~0.04 sims/sec single-threaded
- Forge winner life total not captured (quiet mode only reports losers)
- Deck builder — Ollama suggestions are only as good as the local model;
qwen2.5:7bworks well