YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Charles the Chess Bot

This is Charles, a bot designed by me for EnderChef's chess model competition. While not the best player on earth, Charles moves quickly and doesn't really worry about the future, instead opting to choose whichever move feels right given the current board.

I like to describe Charles' architecture "0 shot" as after hearing about the competition, I, without looking at any other model architectures, came up with the whole thing while walking home (around 20 mins). Also during training there were no iterations or changes, making it truly a 0 shot design. Because of this Charles has some clear design flaws which are evident in the fact that it only reaches around 2000 elo and many mistakes became obvious to me while training.

Architecture

Charles contains 32,537,088 parameters and consists of two primary parts: the Board Encoder and the Move Decider. The data flow is:

  1. The Board Encoder encodes the current board state.
  2. The Board Encoder encodes the board state after every legal move.
  3. Move embeddings are created by subtracting the current board embedding from the corresponding successor-board embedding. This produces one transition embedding per legal move, typically around 20.
  4. The original current-board embedding is preserved unchanged as the fixed scoring query.
  5. A copy of the current-board embedding is prepended to the move embeddings and passed through the Move Decider.
  6. The Move Decider contextualizes the copied board token and all move tokens using bidirectional attention.
  7. The contextualized move embeddings are projected through a learned, bias-free key matrix.
  8. The original, unaltered current-board embedding is projected through a learned, bias-free query matrix.
  9. Query and key vectors are RMS-normalized and compared with scaled dot products to produce one logit per legal move.
  10. The highest-scoring legal move is selected greedily.

Board Encoder

Component Details
Input Tokenized board [64]
Piece vocabulary 13 IDs: empty, mover pieces, opponent pieces
Piece embedding 13 x 384
Square embedding Learned absolute 64 x 384 embeddings
Board tokens Piece embedding + square embedding
Summary token Learned 384-dimensional token prepended
Transformer 6 bidirectional transformer layers
Attention 6 heads, 64 dimensions per head
Feed-forward SwiGLU, hidden size 1,040
Output One 384-dimensional board embedding
Causal masking None

Move Decider

Component Details
Current board Encoded once by the Board Encoder
Successor boards Encoded once per legal move
Move embeddings successor_embedding - current_embedding
Candidate count Typically around 20 legal moves
Candidate sequence Current board copy + all move embeddings
Type embeddings Separate board-token and move-token embeddings
Candidate positions No candidate position embeddings
Transformer 12 bidirectional transformer layers
Attention 6 heads, 64 dimensions per head
Feed-forward SwiGLU, hidden size 1,040
Query source Original, unaltered current-board embedding
Query projection Bias-free 384 x 384 linear layer
Key source Contextualized move embeddings
Key projection Bias-free 384 x 384 linear layer
Normalization Parameter-free RMSNorm on queries and keys
Scoring Scaled query-key dot product
Output One logit per legal move
Padding Invalid candidates masked to -inf

Training

The model was trained in three phases on tournament chess positions. Legal moves and successor boards were generated with python-chess.

Phase Data and process Objective and details Approx. Elo
1. Supervised policy Human tournament positions; the recorded human move is the target Cross-entropy over all legal moves. AdamW; learning rate 4e-4 -> 3e-5; 2,000-step warmup; batch size 32; gradient accumulation 4; BF16 autocast; 0.0 dropout 1300-1400
2. Stockfish distillation The same positions annotated with Stockfish scores for every legal move KL distillation against the full Stockfish move distribution. Stockfish depth 12; teacher temperature 30; student temperature 1.0; fine-tuned from Phase 1 1700-1800
3. On-policy training The model generates fresh games while Stockfish plays the opponent; completed rollouts are used once For the move selected by the model, minimize (model_probability - Stockfish_probability) ** 2. Unchosen moves receive no direct loss. Muon for matrix weights plus AdamW for other parameters; Opponent depth 8; Teacher depth 10; 10,000-step schedule ~2000

All phases use fully bidirectional scaled-dot-product attention. Phase 3 keeps the model's own actions rather than replacing them with Stockfish's best move, so the rollout distribution remains on-policy.

Changes

Changes id make if I had the time to iterate include:

  • Static + RoPE Square embeddings: As squares both have absolute identities and relative identities
  • Replace move subtraction: I reckon a linear subtraction is a rich enough transform for this task
  • More Capacity: Since this Charles relies on alot of emergent properties such as next move planning and overall generalization, more capacity would almost certainly help
  • Self Play: This was originally planned but ran out of time
Downloads last month
-
Safetensors
Model size
32.5M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Space using Datdanboi25/Charles-the-Chess-Bot 1