Reinforcement learning · Settlers of Catan

Caratan

Teaching a small, open LLM to play 1v1 Settlers of Catan with reinforcement learning — discover its weaknesses, build verifiable environments, and GRPO-train it to play measurably better.

Qwen3.5 8B · thinking off

Headline · head-to-head win rate

The trained model beats the baseline 88% of the time

100 games, trained vs. the untrained baseline — a coin-flip baseline is 50%.

88%
trained model wins
vs
50%
baseline (parity)
Head-to-head
88%
Placement-trained
best checkpoint
opening-placement RL alone — the strongest model
70%
Full chain
placement + maritime + build
later envs sharpened isolated decisions but slightly hurt end-to-end play

Trained = the placement-RL checkpoint (our best). A coin-flip baseline wins ~50% by definition. Full-chain (placement+maritime+build) lands ~70% — the later envs sharpened isolated decisions but slightly hurt end-to-end play, so the placement-only checkpoint is the one we ship. (300-turn cap.)

Watch the games

Pick a game to replay

Step through any game on a rendered board — the trained model beating the base, or the base model playing itself. Click a game to open its replay.

Loading games…

Fully autonomous · recursive self-improvement

An infinite self-improvement loop

No human in the loop. The system plays, finds its own weaknesses, builds the reward that fixes them, trains, and verifies on unseen boards — then starts over on a stronger model. Each pass compounds: recursive self-improvement that just keeps running.

1

Play games

Run 1v1 self-play with the base open model to generate full game transcripts.

2

Discover weaknesses

Mine the transcripts for systematic mistakes: poor openings, hoarding, aimless trades.

3

Build a verifiable env

Turn each weakness into a small RL env with a deterministic, programmatic reward.

4

GRPO-train

Group-relative policy optimization on Qwen3.5 8B (thinking off) via HUD / Tinker, our graders as reward.

5

Measure improvement

Score before vs after on held-out, unseen boards to confirm real generalization.

↻ repeats autonomously — every cycle makes the next one smarter

We left it running

12 autonomous rounds in ~2 hours

50%
fresh model vs base
56%
after ~2h, fully autonomous

Left the fully-autonomous loop running ~2 hours (12 rounds, no human in the loop). It played, mined its own weaknesses, wrote new reward envs, and kept only checkpoints that beat the last — lifting win-rate vs base from a coin flip to 56%, with held-out decision quality climbing too.

Headline results

Before → after on held-out boards

Each environment evaluated on a disjoint set of unseen boards. The base model vs. the GRPO-trained checkpoint, both forced to answer directly so we measure pick quality, not formatting.

Opening placementtop-3 hit
41%78%
top-1 29%→49%, regret 0.125→0.044
Build vs hoardbuild-rate
78%97%
mean reward 0.93→1.38
Maritime tradestrade-rate
20%3%
learns to stop aimless trading; reward −0.03→+0.004

Held-out = scored on boards the model never trained on. Placement and build generalize cleanly; maritime learned to stop over-trading (lower trade-rate is the intended direction here).

Training

Reward over optimization steps

Per-step reward on the training boards for each environment during the final GRPO run. All three show a clear climb.

placementtop-3 hit rate
start 0.68030 stepsend 1.000
maritimemean reward
start -0.46125 stepsend 0.103
buildmean reward
start 0.63725 stepsend 1.464

Session

What it took

The whole overnight GRPO session across all three environments.

104
optim steps
12,979
rollouts
123 min
training time
~$4.6
inference cost

Explore

Look under the hood

Interactive static viewers — open in a new tab.

Runs locally

Play 1v1 against the trained model

Live play isn't deployed on this site — it needs the inference gateway and a local game server. To play against the trained model yourself, run it locally from the repo:

# 1. start the local play server python scripts/play_server.py --model "fireworks:accounts/brickedup25/deployments/qhzroqz3" # 2. then open the play UI in your browser http://localhost:8000/viewer/play.html