Vs MuZero
MuZero ditches the rules engine and learns the model; we kept the rules
The Comparison
| MuZero (DeepMind 2019) | this project | |
|---|---|---|
| Approach | learn a world model from observations + self-play RL | use known rules + supervised distillation from Stockfish/KataGo |
| Domain | board games and Atari, RT-pixels-in, no rules engine | board games only, rules engine is canonical |
| Networks | three: representation h (obs → latent) + dynamics g (latent + action → next latent + reward) + prediction f (latent → policy + value) | one: AlphaZero-family ResNet (state → policy + value) |
| MCTS | runs over the learned dynamics function, not the true environment | runs over python-chess / GoBoard: the real, deterministic env |
| Training compute (board games) | similar order to AlphaZero: thousands of TPUs × days | 1 GPU per training run, ~16 GPU-hours |
| Peak Elo (chess) | ~3,500 (matches AlphaZero) | 2,301 wide-CI / 2,153 tight-CI |
| Rules knowledge | none: discovers legality, capture, checkmate from data | full python-chess legality + termination |
What MuZero Adds Over AlphaZero
MuZero is AlphaZero plus a learned world model. AlphaZero needed the rules engine (chess.Board, GoBoard) at every MCTS node; every simulated child is a real legal position. MuZero removes that requirement by training three networks that together simulate the environment:
real observation o_t
│
▼
h_θ(o_t) = s⁰_t latent state at the root
│
▼ ┌─────────────────────────────────────────────────────┐
MCTS │ recursive descent through the LEARNED dynamics g_θ │
│ g_θ(s_t^k, a_{t+1}) = (s_t^{k+1}, r_t^{k+1}) │
│ prediction f_θ(s_t^k) = (π_t^k, v_t^k) │
└─────────────────────────────────────────────────────┘
│
▼
visit-count π over root actions → move played
The dynamics function g_θ is trained to predict what happens only
on the trajectories MCTS visits, not the full state space. That means
MuZero never has to learn “what’s a legal move in chess” in general,
only enough to evaluate the moves its own search considers. That’s
much less than learning chess rules ab initio, but it’s enough to
match AlphaZero’s strength on chess + shogi + Go and also play
Atari (where rules are unknown).
What This Project Has That MuZero Doesn’t Care About
We assume the rules. wm_chess.board.encode_board reads
python-chess’s legality directly, MCTS only descends legal moves,
game termination is determined by board.is_game_over(). That’s a
massive assumption MuZero deliberately drops, but for our problem
(can a small ResNet distilled from Stockfish play strong chess?) it’s
the right assumption. Re-learning chess rules from scratch would be
~10⁶ extra positions of training signal that we don’t have, on top of
the actual hard problem (learning to play well).
A cleaner way to say it: the world-model-learning question is genuinely orthogonal to the question this project asks. AlphaZero and MuZero hit the same Elo on chess; the difference is what they needed to see to get there.
Why Our Network Is “AlphaZero” Not “MuZero”
Three structural reasons:
- We feed in real board state, not pixel observations. The
19-plane encoding (
encode_board) is a hand-crafted representation of piece positions, castling rights, en-passant, and the side to move. MuZero’s representation networkh_θis learned from raw inputs (e.g. screen pixels for Atari). For board games MuZero also uses the same hand-crafted planes, so on chess specifically MuZero’s representation network is doing little work. - MCTS calls the real env. Every descent in our MCTS calls
board.push(move)on a python-chess Board. MuZero would callg_θ(s, a)on the latent. We benefit from the real env being exact and free; MuZero benefits from generalizing past known-env problems. - We have a teacher. Stockfish labels positions for us. MuZero has no teacher; it learns from MCTS visit counts on its own self-play games (same as AlphaZero). The teacher means we don’t need MuZero’s world model to deliver a strong move-distribution target; Stockfish already gives us one for free.
Where MuZero Would Help (and we ignored it)
MuZero’s auxiliary losses anticipated a lot of what KataGo later
formalized. The reward-prediction head in g_θ is morally the same
shape as KataGo’s score head. The recurrent latent unrolling forces
the network to keep enough internal state to plan multi-step, which
helps even with known-env board games because it forces the encoder
to make planning-useful features explicit.
We don’t have any of that. Our network outputs a per-position policy and a single scalar value; that’s it. KataGo’s paper documented ~9× efficiency gains over vanilla AlphaZero from adding aux heads of exactly this flavor (see vs-Lc0 page). We’ve plumbed the architecture for KataGo-style aux heads on the Go side but haven’t enabled them in a real run yet.
Is “Distill from Stockfish” Actually MuZero-Adjacent?
Sort of. Both AlphaZero/MuZero ultimately train the value head on
game outcomes (the z target) and the policy head on improved
distributions (π from MCTS). When we use Stockfish multipv as the
policy target, we’re using a much stronger π than MCTS would
produce at the same network strength; Stockfish at depth 15 sees
much further than 800 MCTS sims of our smallish ResNet. So our policy
training signal is structurally similar but bandwidth-rich.
What we don’t get from Stockfish-distillation: experience with the network’s own search distribution. MCTS’s blind spots (where priors disagree with deep MCTS) are exactly the positions where self-play would help most, and exactly the positions Stockfish’s labels can’t directly inform. That’s the residual gap between distill ceiling (2,300-ish) and Lc0’s 3,600+. The self-play attempts documented on the what’s next page target that gap.
In One Sentence
MuZero kept AlphaZero’s MCTS-at-inference and learned the rules engine; we kept AlphaZero’s MCTS-at-inference and used the rules engine plus a Stockfish teacher. Both are valid moves; ours is the right one for “single GPU + want a strong chess net fast.”