RDTChess β€” a wireframe chessboard, 128x8 + recurrent depth x2 + gated memory + SE, ~11M games, me vs my AI

β™ŸοΈ RDTChess

A 11.05M-parameter residual network β€” recurrent depth Γ—2, gated memory, Squeeze-and-Excitation β€” that plays chess in one forward pass β€” no search, no MCTS, no opening book. Trained from scratch on a single GTX 1080 Ti by supervised imitation of β‰ˆ10.99M human games from Lichess. This is RDTChess2.pt, currently the model live on the Lichess bot.

play on lichess license params games positions search framework hardware

⚠️ One seed, one run. Every number in the Identity and Evaluation sections just below comes from a single training run on a single seed (123). Directions are consistent across three of the four instruments used there; magnitudes are not multi-seed validated. Read this as a lab notebook, not a benchmark.

This repository ships two checkpoints. RDTChess2.pt (this card's main subject, live on Lichess) is recurrent depth Γ—2 + gated + SE on top of a 128Γ—8 tower, trained on β‰ˆ11M games β€” compared against the plain architecture just below. RDTChess.pt is that plain architecture on its own: the original, single-seed, 5.5M-game run, described with its own numbers in πŸ“Ž RDTChess (original) near the end of this card. Smaller and differently-shaped siblings were trained alongside both β€” narrower, shallower, other variants β€” but none of them are released here, and none of the numbers on this card come from them.


🧩 Identity

Architecture Residual tower, 128 channels Γ— 8 blocks, recurrence Γ—2, gated, SE, two heads
Parameters 11.05 M
Input 19 planes of 8Γ—8, canonical frame
Policy output 4096 raw logits (from-square Γ— 64 + to-square)
Value output One scalar in [βˆ’1, 1], tanh
Search None β€” one forward pass per move
Training data β‰ˆ10.99 M Lichess games, both players β‰₯ 1800 Elo (8 sequential slices, one seed carried through with --resume)
Positions seen 801,295,726, summed across the 8 slices
Training time 68.6 h total on a GTX 1080 Ti, across the 8 sequential runs

This is RDTChess2.pt. RDTChess.pt β€” the plain architecture on its own, no recurrence, no gate, no SE β€” has its own Identity/Evaluation/Data-scaling numbers in the πŸ“Ž RDTChess (original) section near the end of this card.

The encoding, and why it is shaped this way

Everything lives in a canonical frame: the side to move is always "White, playing up the board". When Black is to move, every square is mirrored (square ^ 56) and the piece colours swap.

Two consequences, both deliberate:

  • the value head always answers "how good is this for the player to move", which is a well-defined question β€” unlike an absolute encoding, which does not even tell the network whose turn it is;
  • a position and its colour-reversed twin share one representation, so every game trains both colours at once.

The 19 planes: 12 for pieces (6 ours, 6 theirs), 4 castling rights, 1 en passant, 1 fifty-move counter, 1 repetition flag.

The policy head emits logits, not a softmax. That is what lets the caller mask illegal moves before normalising. Softmaxing over all 4096 actions and then zeroing ~99% of them spends most of the model's capacity learning that illegal moves are illegal β€” capacity this size cannot spare.


πŸ”¨ How it was made

Architecture. Same 128Γ—8 residual tower as RDTChess.pt, plus three additions applied together:

  • Recurrent depth Γ—2 β€” the tower is applied twice per move, sharing the same convolution weights both times (effective depth Γ—2 at no extra parameter cost for the convolutions themselves).
  • Gated shortcut β€” a learned per-channel, per-square gate decides how much of the previous pass to keep versus overwrite (z = sigmoid(W[candidate; previous]), initialised at sigmoid(-2) β‰ˆ 0.12 so ~88 % of the previous state survives each pass by default).
  • Squeeze-and-Excitation in every residual block β€” a per-channel scale and bias computed from the whole board at once, buying long-range interaction (e.g. a1–h8) without extra convolutional depth.

Data. The Lichess July 2026 standard-game dump, split into 8 sequential slices of β‰ˆ1.37M games each (both players rated β‰₯ 1800). One seed (123) trained on the first slice, then carried through the other 7 with --resume, so the final checkpoint has seen all β‰ˆ10.99M games and 801,295,726 positions, one epoch each.

Objective. Plain behaviour cloning. The policy is cross-entropy against the move the human actually played, computed over the legal moves only. The value head regresses the final game result, expressed from the side to move.

Recipe. AdamW, learning rate 1e-3, weight decay 1e-4, betas (0.9, 0.999), batch size 512, gradient clipping 1.0, one epoch per slice, no learning-rate schedule.

That is the whole method. No self-play, no MCTS, no distillation from an engine, no opening book, no endgame tablebase.


πŸ“Š Evaluation

Compared against the plain (non-recurrent, non-gated, non-SE) 128Γ—8 architecture, same data, same seed, one run each time β€” puzzles, Maia and minimax across all 8 slices; the round-robin across the first 7, run before the 8th finished training:

Yardstick Result
Puzzles, first move (McNemar, 23,845 puzzles)
single forward pass, no search
ahead 8 / 8 β€” never inverted, always significant
Maia-1100 / 1500 / 1900, no search either side
300 games per slice per level
ahead 23 / 24 β€” one isolated reversal, recovered next slice
Full round-robin, 14 checkpoints, 91 pairs
27,300 games, no search either side, aggregate score vs the other 13
ahead 7 / 7 β€” never inverted, the most stable instrument of all four
Minimax depth 2 (n=300)
an adversary that searches, unlike the three yardsticks above
ahead 4 / 8 β€” no stable direction, each swing confirmed on a second independent draw

🚫 No absolute Elo is claimed. This model has never been played against a rated pool. Every figure here is a difference measured inside one family of models, on one training seed.

Actual scores against Maia (CSSLab's human-imitating engine, nodes=1, no search either side), on the final checkpoint β€” one training seed (123):

Opponent RDTChess2.pt Plain architecture
Maia-1100 78.5 % 77.3 %
Maia-1500 69.7 % 68.8 %
Maia-1900 60.5 % 55.8 %

Read this precisely, not optimistically. Puzzles, Maia, and the round-robin all judge in one pass, no search β€” the same regime this model is actually deployed in β€” and all three are stable and one-sided across every slice tested. Minimax is a stress test against an opponent that searches for a refutation; the mechanism never collapses against it, but the signal is too noisy at n=300 to claim it wins there either. On the 5 hand-picked positions where the production model actually lost a real game, both architectures bounce between fixing and re-breaking the same handful of positions slice to slice β€” until, on the very last slice, both converged on choosing the exact identical move on all 6 tested, the first time that happened.

This was confirmed on one seed (123) only, extended sequentially across 8 slices of the same data with --resume. The 1.38M-game starting point was separately confirmed on three independent seeds (24, 123, 454) before this single-seed extension began; the extension itself has not been repeated on another seed. Full protocol, every intermediate number, and the honest instability of the minimax signal are in the project's own lab notebook, not reproduced here in full.

What it is good at, and where it breaks

Measured on the same 23,845 Lichess puzzles as the comparison above, on the final checkpoint (all 8 slices). One move per position, no search.

First move of the solution 61.5 %
Entire line 42.5 %
Random baseline
28.0 legal moves on average
3.6 %

The entry move is the hard one

The network is asked at every move of the line β€” even after it has already erred, so that no step is measured on a pre-filtered sample.

Length Accuracy, first move β†’ last
2 moves 54.5 β†’ 78.3
3 moves 56.9 β†’ 71.1 β†’ 87.0
4 moves 64.1 β†’ 72.7 β†’ 77.7 β†’ 89.6
5 moves 68.7 β†’ 75.5 β†’ 77.6 β†’ 83.0 β†’ 91.7

The entry is the hardest move of every line here too, by 10 to 22 points β€” a smaller gap than RDTChess.pt's 20 to 35, but not closed. The last move of the line sits around 90 %, same as the original architecture.

And it is the quiet entry that breaks, same as the original

First move found, split by what kind of move it is.

Entry move < 1200 1200-1599 1600-1999 2000-2399 2400+
Forcing β€” visible in the position
Check 79.7 65.2 52.6 42.0 55.3
Capture 92.2 77.4 63.8 60.7 59.9
Non-forcing β€” justified only by what comes later
Quiet 95.1 80.3 59.1 42.0 33.3

The quiet move is still the hardest category at high rating β€” 33.3 % at 2400+, essentially unchanged from RDTChess.pt's 32.8 %. Every band improved a little (quiet at 1600-1999: 59.1 vs 54.7; at 2000-2399: 42.0 vs 39.3), but the shape β€” quiet collapsing from easiest to hardest as rating rises β€” is identical. Recurrent depth, gating and SE bought accuracy; they did not change what kind of move the network struggles to initiate.


πŸš€ Usage

Play it right now, nothing to install

The model runs live as a Lichess bot: lichess.org/@/RDTChessBot

Challenge it in bullet, blitz, rapid or classical, casual or rated. Correspondence is declined on purpose β€” the bot answers in about 7 ms, so a game played over days would tie up a slot for weeks and tell you nothing.

It plays exactly the weights in this repository, through uci.py, with FinishPlies: 1 and two CPU threads. No search, no opening book, no tablebase: every move you see is one forward pass. The bot currently runs RDTChess2.pt β€” swapping uci.py's default is the only thing that changes when the deployed checkpoint changes, so check this line if you're reproducing a game and the move doesn't match what you'd expect from RDTChess.pt.

Install locally

pip install -r requirements.txt

Pick a move

python predict.py                                        # initial position
python predict.py "6k1/5ppp/8/8/8/8/5PPP/R5K1 w - - 0 1" # any FEN
top 5 moves
  Ra8#      64.3 %
  Ra7       11.4 %
  Re1        5.6 %

Play it in your browser

python play.py              # you are White, opens http://localhost:8000
python play.py --black
python play.py --port 8080 --depth 3

Three modes in the page β€” play the model, play the minimax, or watch the two of them β€” plus a depth slider. Standard-library HTTP server, no build step, no CDN.

Use it in your own code
import chess, numpy as np, torch
from encoding import board_to_planes, legal_move_indices
from network import ChessNetwork

model = ChessNetwork.from_checkpoint("RDTChess2.pt", device="cpu")  # or "RDTChess.pt"
board = chess.Board()

moves, indices = legal_move_indices(board)          # legal moves + their indices
planes = torch.from_numpy(board_to_planes(board)).unsqueeze(0)

with torch.no_grad():
    logits, value = model(planes)

legal = logits[0, torch.as_tensor(indices.astype(np.int64))]
probs = torch.softmax(legal.float(), dim=0).numpy()  # over legal moves ONLY
print(board.san(moves[int(probs.argmax())]), float(value))

⚠️ The one mistake to avoid: reading argmax over the full 4096 logits. About 99 % of them are illegal in any given position. Always gather at legal_move_indices first.

ChessNetwork.from_checkpoint returns the model already in eval() mode. Keep it there β€” the tower is BatchNorm, and train-mode batch statistics will corrupt single-position inference.

What is in this repository
file role
RDTChess2.pt 128Γ—8 + recurrence Γ—2 + gated + SE, weights + architecture config (44 MB)
RDTChess.pt plain 128Γ—8, weights + architecture config (44 MB)
network.py the architecture β€” loads either checkpoint, config comes from the file
encoding.py board ↔ tensor, move ↔ index
player.py the network as a player, get_move(engine)
engine.py board wrapper, canonical encoding, hand-written evaluation
algo_player.py alpha-beta minimax, the opponent used in evaluation
gui.py, play.py the browser board
predict.py one-position example

Inference only. The training code is not part of this release; the recipe above is complete enough to reproduce it. Both released checkpoints carry weights and architecture config, with the optimiser state stripped β€” 44 MB each instead of ~131 MB.


⚠️ Limits β€” read these before quoting any number

  • It imitates, it does not solve. The objective is agreement with 1800+ humans. Where those humans are systematically wrong, so is the model, by construction. It is not trying to find the best move; it is trying to find the likely move.
  • No search, and it shows. One forward pass. The puzzle breakdown in the RDTChess (original) section locates the cost precisely: 90 % on forced continuations, 32.8 % on quiet entries in hard positions β€” the same weakness this architecture inherits, since neither addition changes what the model is asked to do.
  • One seed, one run. No variance control on the extension past 1.38M games (the 1.38M starting point itself was 3-seed confirmed).
  • Under-promotions cannot be represented. The action space collapses from/to pairs and always decodes promotions to a queen. This costs well under 0.1 % of moves in normal play, but it makes a handful of knight-promotion puzzles unsolvable by construction.
  • No absolute rating. Never played against a rated pool. Do not read a win-rate against minimax as an Elo.
  • The value head is weak supervision. It regresses the final result of the game, which is a very noisy label for a position at move 12.
  • Distribution. Trained on rated blitz/rapid from one month of one server, at one rating floor. Behaviour outside that distribution β€” correspondence, odds games, composed positions β€” is untested.
  • The released weights are the raw supervised checkpoint. No self-play fine-tuning was applied to it.
  • The minimax comparison is noisy at n=300. Read the round-robin (91 pairs, 27,300 games) as the more direct instrument; minimax swings by double digits between two independent draws.

πŸ“Ž RDTChess (original)

Everything in this section is about RDTChess.pt specifically: the plain 128Γ—8 tower, no recurrence, no gate, no SE β€” the original single-seed, 5.5M-game run. None of the numbers below apply to RDTChess2.pt above; its own comparison against this architecture is in the Evaluation section near the top of this card.

Architecture Residual tower, 128 channels Γ— 8 blocks, two heads
Parameters 10.92 M
Input 19 planes of 8Γ—8, canonical frame
Policy output 4096 raw logits (from-square Γ— 64 + to-square)
Value output One scalar in [βˆ’1, 1], tanh
Search None β€” one forward pass per move
Training data 5,497,103 Lichess games, both players β‰₯ 1800 Elo
Positions seen 400,581,531, one epoch
Training time 12 h 54 on a GTX 1080 Ti

How it was made

Data. The Lichess July 2026 standard-game dump. A game is kept when both players are rated β‰₯ 1800 (the filter takes min(WhiteElo, BlackElo)), which leaves 5,497,103 games in the 5 GB archive β€” all of them used. Every position of every kept game becomes one training sample, from the first ply: 400,581,531 positions, seen once.

Objective. Plain behaviour cloning. The policy is cross-entropy against the move the human actually played, computed over the legal moves only. The value head regresses the final game result, expressed from the side to move.

Recipe. AdamW, learning rate 1e-3, weight decay 1e-4, betas (0.9, 0.999), batch size 512, gradient clipping 1.0, one epoch, no learning-rate schedule.

That is the whole method. No self-play, no MCTS, no distillation from an engine, no opening book, no endgame tablebase.

Evaluation

Three independent yardsticks, because they disagree β€” and the disagreement is itself a finding.

Yardstick Result
Top-1 on held-out human moves
agreement with what the human actually played
51.7 %
vs minimax depth 1
300 games Β· +249 =34 βˆ’17
88.7 %
vs minimax depth 2
300 games Β· +144 =69 βˆ’87
59.5 %
vs the same net trained on 2M games
600 games Β· +35 Elo Β· p = 0.0034
55.1 %

A fixed opponent measures how well you exploit that opponent, not general strength: the depth-1 and depth-2 figures move by 29 points for the same model. The head-to-head duel is the most informative of the three.

🚫 No absolute Elo is claimed. This model has never been played against a rated pool. Every Elo figure here is a difference measured inside one family of models.

Data scaling

Games Net Decade Gain vs d1 Rate
150 k β†’ 1 M 96Γ—4 0.82 +21.2 pts 25.7 / decade
1 M β†’ 2 M 96Γ—4 0.30 +6.3 pts 20.9 / decade
2 M β†’ 5.5 M 128Γ—8 0.44 +8.0 pts 18.2 / decade

The rate bends steadily β€” 25.7, 20.9, 18.2 β€” without collapsing. The +8.0 was predicted at +9 before the run from the two earlier points.

The last row is a wider net than the first two, so the three rates are not a single controlled curve: read the bend as a trend, not as a measurement. The architecture comparison was run separately, at fixed data, and found depth to be the lever that pays and width almost nothing on its own β€” until the two are combined.

What it is good at, and where it breaks

Measured on 23,845 Lichess puzzles, sampled by cell (solution length Γ— rating band) so that puzzle difficulty is not confounded with solution length. One move per position, no search.

First move of the solution 57.8 %
Entire line 38.3 %
Random baseline
28.0 legal moves on average
3.6 %

The entry move is the hard one

The network is asked at every move of the line β€” even after it has already erred, so that no step is measured on a pre-filtered sample.

Length Accuracy, first move β†’ last
2 moves 51.9 β†’ 76.1
3 moves 53.6 β†’ 67.5 β†’ 85.1
4 moves 60.4 β†’ 69.6 β†’ 75.2 β†’ 88.0
5 moves 65.5 β†’ 72.5 β†’ 75.5 β†’ 80.3 β†’ 90.6

The entry is the hardest move of every line, by 20 to 35 points. The last is around 90 %, whatever the depth. Once inside a forcing sequence β€” recaptures, checks, the mate β€” the network barely errs. Starting one is the problem.

And it is the quiet entry that breaks

First move found, split by what kind of move it is.

Entry move < 1200 1200-1599 1600-1999 2000-2399 2400+
Forcing β€” visible in the position
Check 74.4 59.5 47.5 39.5 51.4
Capture 91.6 73.8 60.7 57.7 56.1
Non-forcing β€” justified only by what comes later
Quiet 94.6 75.6 54.7 39.3 32.8

The quiet move goes from the easiest category to the hardest β€” 23 points below captures in the same band β€” as difficulty rises. Captures and checks are visible in the position; a quiet preparatory move is justified only by what comes three plies later.

πŸ’‘ In one sentence: this network executes tactics, it does not initiate them.


πŸ“„ License

Apache-2.0.

πŸ“š Citation

@misc{charlet2026rdtchess,
  author = {ThΓ©o CHARLET},
  title  = {RDTChess: 128x8 chess policy and value networks trained on human Lichess games},
  year   = {2026},
  publisher = {Hugging Face},
  howpublished = {\url{https://huggingface.co/RDTvlokip/RDTChess}}
}

ThΓ©o CHARLET

TSSR Graduate (IT Systems & Networks Technician) β€” AI/ML Specialization
Creator of AG-BPE (Attention-Guided Byte-Pair Encoding)

LinkedIn Website Search

πŸš€ Seeking internship opportunities

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Collection including RDTvlokip/RDTChess

Evaluation results

  • First move of the solution on Lichess puzzle database, 23845 puzzles sampled by solution length x rating band, one seed (123)
    self-reported
    61.500
  • Entire solution line on Lichess puzzle database, 23845 puzzles sampled by solution length x rating band, one seed (123)
    self-reported
    42.500
  • Score vs minimax depth 2 on 300 games, seed 4242, 300-move cap, one training seed (123)
    self-reported
    60.800
  • Stages ahead of the plain architecture (aggregate score) on 91 pairs, 27300 games, one training seed (123)
    self-reported
    7.000
  • Top-1 move agreement on Lichess July 2026 standard games, both players 1800+ Elo, held-out split
    self-reported
    51.700
  • First move of the solution on Lichess puzzle database, 23845 puzzles sampled by solution length x rating band
    self-reported
    57.800
  • Entire solution line on Lichess puzzle database, 23845 puzzles sampled by solution length x rating band
    self-reported
    38.300
  • Quiet entry move, rating 2400+ on Lichess puzzle database, 23845 puzzles sampled by solution length x rating band
    self-reported
    32.800