Chessbook

Spending 10 months building the most human-like chess bot

· Marcus Buffett

Chessbook-1, Maia-3 and Allie 2.0 at a glanceCapability scorecard: a 0-100 grade per measurement, and whether the model has the capability at all. Predicting human moves Matching strength Time scramble play Predicting think time Predicting who wins Chessbook-1 199M parameters 96 / 100 81 / 100 84 / 100 98 / 100 97 / 100 Maia-3 79M parameters 85 / 100 56 / 100 57 / 100 No 80 / 100 Allie 2.0 5.6B parameters 99 / 100 48 / 100 99 / 100 73 / 100 99 / 100

Everyone knows playing against an engine like Stockfish is nothing like playing a human. The existing human-like models (Allie 2.0, Maia-3) weren't quite right for my use case, and I became a bit obsessed with making the best one.

I wanted a sparring partner that plays just like a random opponent on Lichess, at any rating and any time control. So over the last ten months I built one. It's called Chessbook-1, and it's the bot behind the training games on Chessbook.

The best models available right now for this are Allie 2.0 and Maia-3. Maia-3 doesn't match playing strength well, plays too strongly at low ratings, isn't trained on low-time situations or non-blitz time controls, and it doesn't predict how long a human will take to play a move.

Allie 2.0 is by a good margin the best predictor overall, but predicts time usage independently of the move, and its strength drifts at low-Elo and high-Elo play.

So I made my own, and here's how it compares to Allie 2.0 and Maia-3.

TL;DR: Chessbook-1 predicts human moves well, is the only model calibrated to play at the right strength at every rating, and it errs in time scrambles like a human would. It predicts time usage well, and is aware of the time it uses.

How we measured it

Two sets of positions, both from Lichess games played in September 2026, a month after every model's training data ends (Chessbook-1 trained on games through July 2026, Maia-3 through July 2025, Allie 2.0 through August 2026):

  • Any game at all. 30,000 positions drawn at random from every rated game that month: every time control from ultrabullet to classical, every rating, no clock floor. A third of them are bullet.
  • Blitz, Maia-3's home field. 28,000 positions from rated blitz games, 4,000 per rating band from 600 to 2600+. Maia-3 was trained on blitz only, so this is the comparison on its own terms.

Every legal move in every position was scored by Stockfish 18. Each model gets the position, the players' ratings and any other features it accepts (Chessbook-1 and Allie read the clocks and the move history; Maia-3 reads the history but not the clock), and outputs a probability for every legal move. Then we compare that distribution with what the human played.

Does it predict what a human would play?

The easiest measure is top-1 accuracy: how often the model's top prediction is the move the human actually played. The other number worth watching is how much probability the model put on the move the human chose, averaged over positions: a model that spreads its guesses sensibly scores well here even when its favourite move is wrong.

Start with a random position from a random game: any time control, any clock, any rating.

Random positions from every rated game, any time control, by rating bandTwo panels, top-1 accuracy and probability on the human's move, on 30,000 random positions from every rated Lichess game in September 2026. Top-1 accuracy 40% 50% 60% 70% 600 1000 1400 1800 2200 2400+ Player rating (Lichess) Maia-3, 600-1000: 45.7% Maia-3, 1000-1400: 51.3% Maia-3, 1400-1800: 54.6% Maia-3, 1800-2200: 56.7% Maia-3, 2200-2400: 56.4% Maia-3, 2400+: 58.6% Allie 2.0, 600-1000: 45.6% Allie 2.0, 1000-1400: 52.1% Allie 2.0, 1400-1800: 55.5% Allie 2.0, 1800-2200: 58.1% Allie 2.0, 2200-2400: 60.1% Allie 2.0, 2400+: 61.6% Chessbook-1, 600-1000: 46.7% Chessbook-1, 1000-1400: 51.6% Chessbook-1, 1400-1800: 54.3% Chessbook-1, 1800-2200: 57.6% Chessbook-1, 2200-2400: 57.3% Chessbook-1, 2400+: 61.6% Chessbook-1 Maia-3 Allie 2.0 Probability on the human's move 15% 20% 25% 30% 35% 40% 600 1000 1400 1800 2200 2400+ Player rating (Lichess) Maia-3, 600-1000: 19.1% Maia-3, 1000-1400: 22.3% Maia-3, 1400-1800: 25.1% Maia-3, 1800-2200: 26.1% Maia-3, 2200-2400: 25.7% Maia-3, 2400+: 27.8% Allie 2.0, 600-1000: 19.4% Allie 2.0, 1000-1400: 23.1% Allie 2.0, 1400-1800: 26.1% Allie 2.0, 1800-2200: 28.1% Allie 2.0, 2200-2400: 29.4% Allie 2.0, 2400+: 32.1% Chessbook-1, 600-1000: 19.0% Chessbook-1, 1000-1400: 22.1% Chessbook-1, 1400-1800: 25.2% Chessbook-1, 1800-2200: 27.4% Chessbook-1, 2200-2400: 28.2% Chessbook-1, 2400+: 31.8%
Higher is better. 30,000 random positions from every rated September 2026 game, by rating band; everyone above 2400 is one band (657 positions, mostly bullet players).

Allie 2.0, at 28 times the size and reading the whole game, is about a point ahead of both in every band but the lowest and the highest. Between Chessbook-1 and Maia-3, the two are level below 1800 and Chessbook-1 pulls ahead from 1800 up; pooled over all 30,000 positions it leads Maia-3 on both measures by a small but real margin, and the gap is widest in bullet games and in the last seconds on the clock, where Maia-3's training data runs out.

This isn't really a fair comparison with Maia-3 though. That model is trained only on blitz games, and every position after a player's clock first drops under 30 seconds is thrown out of its training data, so a random Lichess position is often out-of-distribution. So how does Chessbook-1 do on Maia-3's own terms: blitz games, with the time scrambles filtered out?

Top-1 move prediction accuracy by rating bandShare of positions where the model's most likely move is the one the human played. 28,000 Lichess blitz positions from September 2026. 40% 50% 60% 70% 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Chessbook-1, 600-1000: 46.9 46.9 Chessbook-1, 1000-1400: 53.3 53.3 Chessbook-1, 1400-1800: 54.9 54.9 Chessbook-1, 1800-2200: 59.1 59.1 Chessbook-1, 2200-2400: 61.3 61.3 Chessbook-1, 2400-2600: 61.6 61.6 Chessbook-1, 2600+: 63.5 63.5 Maia-3, 600-1000: 48.1 48.1 Maia-3, 1000-1400: 53.6 53.6 Maia-3, 1400-1800: 55.5 55.5 Maia-3, 1800-2200: 59.2 59.2 Maia-3, 2200-2400: 60.6 60.6 Maia-3, 2400-2600: 61.6 61.6 Maia-3, 2600+: 62.6 62.6 Allie 2.0, 600-1000: 48.5 48.5 Allie 2.0, 1000-1400: 53.4 53.4 Allie 2.0, 1400-1800: 55.3 55.3 Allie 2.0, 1800-2200: 59.8 59.8 Allie 2.0, 2200-2400: 60.8 60.8 Allie 2.0, 2400-2600: 61.7 61.7 Allie 2.0, 2600+: 62.7 62.7 Chessbook-1 Maia-3 Allie 2.0
Higher is better. Top-1 move prediction by rating band on the blitz set.
Average probability assigned to the human's actual moveGeometric mean of the probability each model gave to the move the human played (exp of minus the negative log-likelihood). 15% 20% 25% 30% 35% 40% 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Chessbook-1, 600-1000: 18.9 18.9 Chessbook-1, 1000-1400: 23.6 23.6 Chessbook-1, 1400-1800: 25.2 25.2 Chessbook-1, 1800-2200: 28.8 28.8 Chessbook-1, 2200-2400: 31.7 31.7 Chessbook-1, 2400-2600: 33.3 33.3 Chessbook-1, 2600+: 32.6 32.6 Maia-3, 600-1000: 19.4 19.4 Maia-3, 1000-1400: 24.3 24.3 Maia-3, 1400-1800: 25.6 25.6 Maia-3, 1800-2200: 28.7 28.7 Maia-3, 2200-2400: 31.5 31.5 Maia-3, 2400-2600: 33.5 33.5 Maia-3, 2600+: 33.8 33.8 Allie 2.0, 600-1000: 19.8 19.8 Allie 2.0, 1000-1400: 24.6 24.6 Allie 2.0, 1400-1800: 26.0 26.0 Allie 2.0, 1800-2200: 29.3 29.3 Allie 2.0, 2200-2400: 31.9 31.9 Allie 2.0, 2400-2600: 33.3 33.3 Allie 2.0, 2600+: 33.5 33.5 Chessbook-1 Maia-3 Allie 2.0
Higher is better. Geometric mean of the probability each model assigned to the human's actual move, blitz set; a model that put 100% on every human move would score 100%.

A three-way statistical tie. Over all 28,000 positions Allie 2.0 gets 57.5% top-1, Maia-3 57.3% and Chessbook-1 57.3% (27.9%, 27.6% and 27.3% probability on the human's move); the paired differences are within ±0.3 points. Band by band everything is within noise except 600–1000, where Maia-3 and Allie are about a point ahead on top-1 (the per-band confidence intervals are ±0.8 to ±1.0 points). For scale, Maia-2 is at 50% pooled, and a Stockfish limited to the player's rating agrees with the human only 24–40% of the time, which is the whole problem with engine-based bots.

So our model matches Maia-3 on its own strengths, and beats it in the areas it's not quite trained for. It falls short of Allie 2.0 on raw prediction, which is expected given the 28x size difference in the models, and it makes up for it by matching strength better and with better time usage.

Does it play at the right strength?

A move predictor is not automatically a good opponent. To give an extreme example, if you put 95% probability on the right move every time, but put 5% probability on weird blunders, then you'll never play a competitive game against an opponent who knows what they're doing, because you'll be blundering twice in a 40-move game and it only takes one blunder to lose the game. Maia-3 gets around this a bit by not sampling from its distribution, taking the top move instead. But this causes it to over-perform at low ratings, and it still struggles at high ratings. Chessbook-1 can sample from its distribution at every rating, and play almost exactly at that level, until 2600+.

To measure playing strength, we take positions where the evaluation is within two pawns of equal. Here is each bot's average centipawn loss relative to the humans who actually played those positions, band by band. Zero means it loses exactly as much as they do; below zero means it plays stronger than they do:

Average centipawn loss relative to humans of the same rating, contested positionsBot ACPL minus human ACPL per rating band. Zero means the bot loses exactly as much as the humans; below zero means it plays stronger than they do. -10 -5 0 +5 +10 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Chessbook-1, 600-1000: +1.0 cp Chessbook-1, 1000-1400: -1.0 cp Chessbook-1, 1400-1800: +0.8 cp Chessbook-1, 1800-2200: -0.6 cp Chessbook-1, 2200-2400: -0.8 cp Chessbook-1, 2400-2600: +0.0 cp Chessbook-1, 2600+: +0.9 cp Maia-3, 600-1000: +1.1 cp Maia-3, 1000-1400: -2.6 cp Maia-3, 1400-1800: +0.4 cp Maia-3, 1800-2200: +0.8 cp Maia-3, 2200-2400: +1.9 cp Maia-3, 2400-2600: +4.3 cp Maia-3, 2600+: +6.8 cp Allie 2.0, 600-1000: -8.3 cp Allie 2.0, 1000-1400: -5.9 cp Allie 2.0, 1400-1800: -1.6 cp Allie 2.0, 1800-2200: +0.7 cp Allie 2.0, 2200-2400: +2.1 cp Allie 2.0, 2400-2600: +5.2 cp Allie 2.0, 2600+: +8.2 cp Chessbook-1 Maia-3 Allie 2.0
Closer to zero is better. Bot ACPL minus human ACPL in contested positions (|eval| ≤ 200 cp), per rating band. Bots sample from their move distribution; Maia-3 from its raw probabilities.

And the same for the blunder rate, which is maybe a better representation because it's hard to feel a 10 centipawn loss average difference. But 2 blunders per game vs 3 feels very different over time:

Blunder rate relative to humans of the same rating, contested positionsBot blunder rate minus human blunder rate, in percentage points, per rating band. Below zero means the bot blunders less often than the humans. -2 -1 0 +1 +2 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Chessbook-1, 600-1000: +0.6 pts Chessbook-1, 1000-1400: -0.0 pts Chessbook-1, 1400-1800: +0.2 pts Chessbook-1, 1800-2200: -0.3 pts Chessbook-1, 2200-2400: -0.2 pts Chessbook-1, 2400-2600: -0.3 pts Chessbook-1, 2600+: +0.4 pts Maia-3, 600-1000: +0.5 pts Maia-3, 1000-1400: -0.3 pts Maia-3, 1400-1800: +0.3 pts Maia-3, 1800-2200: +0.1 pts Maia-3, 2200-2400: +0.3 pts Maia-3, 2400-2600: +0.5 pts Maia-3, 2600+: +1.4 pts Allie 2.0, 600-1000: -1.4 pts Allie 2.0, 1000-1400: -0.9 pts Allie 2.0, 1400-1800: -0.1 pts Allie 2.0, 1800-2200: +0.1 pts Allie 2.0, 2200-2400: +0.4 pts Allie 2.0, 2400-2600: +0.7 pts Allie 2.0, 2600+: +1.8 pts Chessbook-1 Maia-3 Allie 2.0
Closer to zero is better. Bot blunder rate minus human blunder rate, in percentage points, contested positions. A blunder loses at least 15 points of win probability; below zero means the bot blunders less often than the humans.

Chessbook-1 tracks the humans in every band on both measures; its bars barely leave zero. Maia-3 and Allie 2.0 match the humans up to about 2200 and then drift too weak as the rating climbs, until at 2600+ both blunder nearly twice as often as the players they are imitating. Allie also runs noticeably too strong at the bottom of the scale. (A rating-limited Stockfish goes the other way: it blunders about half as often as a human of the same rating, which is why it feels like playing a strong player who occasionally hangs a piece.)

Another way to see the same thing: take the model's probability mass on moves that are mistakes and compare it with how often humans at that rating actually make mistakes. A perfectly calibrated opponent errs exactly as often as they do.

How much more often each model errs than humans of the same ratingExpected mistake rate of the model's move distribution relative to the humans' actual mistake rate, per rating band. Zero means the model makes mistakes exactly as often as the humans; +50% means half again as often. -20% 0 +20% +40% +60% +80% 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Chessbook-1, 600-1000: +9% +9% Chessbook-1, 1000-1400: +7% +7% Chessbook-1, 1400-1800: +6% +6% Chessbook-1, 1800-2200: +5% +5% Chessbook-1, 2200-2400: -1% -1% Chessbook-1, 2400-2600: +6% +6% Chessbook-1, 2600+: +18% +18% Maia-3, 600-1000: +6% +6% Maia-3, 1000-1400: +1% +1% Maia-3, 1400-1800: +4% +4% Maia-3, 1800-2200: +8% +8% Maia-3, 2200-2400: +10% +10% Maia-3, 2400-2600: +32% +32% Maia-3, 2600+: +63% +63% Allie 2.0, 600-1000: -3% -3% Allie 2.0, 1000-1400: -5% -5% Allie 2.0, 1400-1800: +0% +0% Allie 2.0, 1800-2200: +8% +8% Allie 2.0, 2200-2400: +13% +13% Allie 2.0, 2400-2600: +41% +41% Allie 2.0, 2600+: +75% +75% Chessbook-1 Maia-3 Allie 2.0
Closer to zero is better. How much more often each model's move distribution errs than the humans of that rating. Zero is a perfect match.

Chessbook-1 stays within 9% of the humans in every band, 18% at 2600+. Maia-3 and Allie 2.0 are fine up to 1800 and then come apart: 63% and 75% more mistakes than a 2600+ player. The stronger the player you imitate, the more these models over-represent the rare mistakes, and nothing in those models corrects for it.

Time scrambles

Humans play differently with limited time remaining on the clock, and a bot that does not know the clock won't play as if it's under any pressure at all. Maia-3 has no clock input. Chessbook-1 trains without any filter for clock, so it sees both players' clocks, the base time and the increment.

On the 2,600 positions of the random set where the player had under 20 seconds left:

Move prediction when the player has under 20 seconds leftThe 2,600 positions of the random set where the mover had under 20 seconds on the clock, by rating band; everyone above 2400 is one band. Top-1 accuracy, under 20 s 40% 50% 60% 70% 600 1000 1400 1800 2200 2400+ Player rating (Lichess) Maia-3, 600-1000: 48.0% Maia-3, 1000-1400: 50.4% Maia-3, 1400-1800: 52.3% Maia-3, 1800-2200: 51.3% Maia-3, 2200-2400: 52.7% Maia-3, 2400+: 59.0% Allie 2.0, 600-1000: 52.0% Allie 2.0, 1000-1400: 53.9% Allie 2.0, 1400-1800: 57.4% Allie 2.0, 1800-2200: 56.6% Allie 2.0, 2200-2400: 61.3% Allie 2.0, 2400+: 62.3% Chessbook-1, 600-1000: 50.7% Chessbook-1, 1000-1400: 50.4% Chessbook-1, 1400-1800: 55.6% Chessbook-1, 1800-2200: 54.7% Chessbook-1, 2200-2400: 56.4% Chessbook-1, 2400+: 63.1% Chessbook-1 Maia-3 Allie 2.0 Probability on the human's move, under 20 s 15% 20% 25% 30% 35% 40% 600 1000 1400 1800 2200 2400+ Player rating (Lichess) Maia-3, 600-1000: 17.9% Maia-3, 1000-1400: 20.1% Maia-3, 1400-1800: 22.3% Maia-3, 1800-2200: 20.3% Maia-3, 2200-2400: 24.6% Maia-3, 2400+: 23.0% Allie 2.0, 600-1000: 19.6% Allie 2.0, 1000-1400: 23.6% Allie 2.0, 1400-1800: 26.6% Allie 2.0, 1800-2200: 27.2% Allie 2.0, 2200-2400: 31.1% Allie 2.0, 2400+: 33.5% Chessbook-1, 600-1000: 19.2% Chessbook-1, 1000-1400: 21.6% Chessbook-1, 1400-1800: 25.4% Chessbook-1, 1800-2200: 26.1% Chessbook-1, 2200-2400: 29.1% Chessbook-1, 2400+: 31.9%
Higher is better. Positions with under 20 seconds on the mover's clock, from the random set; everyone above 2400 is one band (122 positions).

Pooled over all bands Chessbook-1 predicts the human's move 54.8% of the time against Maia-3's 51.9%, and gives the human's move 25.4% probability on average against 21.3%: a gap of three to four points, against a tie on the full set. A model without a clock input plays exactly the same move distribution at 2 seconds as at 2 minutes; Chessbook-1 knows that people in time trouble play faster and worse, and does the same. Allie 2.0 reads the clock too, and keeps its usual two points over Chessbook-1 here.

It knows how long a human would think

Chessbook-1 does not just pick a move; for every candidate move it also predicts how much of the remaining clock a human would spend on it, as a distribution. The bot samples a think time from that distribution for the move it chose, waits, and then plays. Easy recaptures come instantly. A surprising, hard decision takes a while. Allie 2.0 predicts think time too, one expected time per position rather than per candidate move. This means it may use time unpredictably, like playing a complex tactical move with the speed of an instant recapture, or play a trivial recapture in 20 seconds.

Think time: humans vs each model's predicted think time for the move playedMean share of the remaining clock spent on a move, by rating band and by how surprising the move was, on the 30,000 random positions. Share of remaining clock spent per move 0% 2% 4% 6% 8% 600 1000 1400 1800 2200 2400+ Player rating (Lichess) Humans, 600-1000: 4.8% Humans, 1000-1400: 5.1% Humans, 1400-1800: 5.2% Humans, 1800-2200: 5.7% Humans, 2200-2400: 7.0% Humans, 2400+: 6.2% Chessbook-1, 600-1000: 5.0% Chessbook-1, 1000-1400: 5.1% Chessbook-1, 1400-1800: 5.2% Chessbook-1, 1800-2200: 5.8% Chessbook-1, 2200-2400: 6.9% Chessbook-1, 2400+: 6.4% Allie 2.0, 600-1000: 4.8% Allie 2.0, 1000-1400: 4.9% Allie 2.0, 1400-1800: 4.8% Allie 2.0, 1800-2200: 5.1% Allie 2.0, 2200-2400: 5.6% Allie 2.0, 2400+: 4.9% Humans Chessbook-1 Allie 2.0 ...by how surprising the chosen move is 0% 2% 4% 6% 8% >=50% 20-50% 5-20% 1-5% <1% How surprising the move was (model probability) Humans, >=50%: 4.4% Humans, 20-50%: 5.4% Humans, 5-20%: 6.4% Humans, 1-5%: 6.9% Humans, <1%: 7.3% Chessbook-1, >=50%: 4.4% Chessbook-1, 20-50%: 5.6% Chessbook-1, 5-20%: 6.4% Chessbook-1, 1-5%: 6.8% Chessbook-1, <1%: 7.1% Allie 2.0, >=50%: 4.2% Allie 2.0, 20-50%: 5.3% Allie 2.0, 5-20%: 5.5% Allie 2.0, 1-5%: 5.3% Allie 2.0, <1%: 5.2%
Closer to the humans is better. Left: mean share of the remaining clock spent per move on the 30,000 random positions, humans vs each model's predicted think time for the move the human played. Right: the same, grouped by how surprising the chosen move was according to the model (less likely moves take longer to find).

Chessbook-1's predicted think time matches the humans to within two tenths of a percent of the clock in every rating band (5.4% pooled, humans 5.4%), and it rises with the surprisingness of the move exactly as the humans' does: about 4% of the clock for an obvious move, 7% for a move the model gave under 1%. Allie 2.0 runs short: 4.9% pooled, 5% to 6% where 2200+ players spend 6% to 7%, and it barely reacts to surprise (5.2% on the moves it gave under 1%, where humans spend 7.3%). In time trouble the same: with under 30 seconds left humans spend 17% of what remains on a move, Chessbook-1 predicts 17%, Allie 14%. Move by move, Chessbook-1 is off by 2.4 points of the clock on average, Allie by 2.6.

It knows who is winning

Chessbook-1 also predicts the result of the game, as win/draw/loss probabilities for the side to move, conditioned on both players' ratings and the clocks. Maia-3 has a value head too; a rating-blind Stockfish evaluation is the obvious baseline.

The simplest test: in games that ended decisively, how often did the side the model favoured actually win?

How often the side each model favoured went on to winShare of decisive games in which the side the model gave the better chances actually won, by rating band. Higher is better. 55% 60% 65% 70% 75% 600 1000 1400 1800 2200 2400 2600+ Player rating (Lichess) Maia-3, 600-1000: 66.9% Maia-3, 1000-1400: 66.6% Maia-3, 1400-1800: 65.2% Maia-3, 1800-2200: 66.9% Maia-3, 2200-2400: 65.5% Maia-3, 2400-2600: 66.6% Maia-3, 2600+: 68.8% Allie 2.0, 600-1000: 69.9% Allie 2.0, 1000-1400: 68.2% Allie 2.0, 1400-1800: 66.9% Allie 2.0, 1800-2200: 69.1% Allie 2.0, 2200-2400: 68.0% Allie 2.0, 2400-2600: 68.4% Allie 2.0, 2600+: 70.3% Stockfish eval (rating-blind), 600-1000: 64.2% Stockfish eval (rating-blind), 1000-1400: 64.0% Stockfish eval (rating-blind), 1400-1800: 64.1% Stockfish eval (rating-blind), 1800-2200: 63.1% Stockfish eval (rating-blind), 2200-2400: 63.5% Stockfish eval (rating-blind), 2400-2600: 64.1% Stockfish eval (rating-blind), 2600+: 66.4% Chessbook-1, 600-1000: 68.9% Chessbook-1, 1000-1400: 68.6% Chessbook-1, 1400-1800: 66.5% Chessbook-1, 1800-2200: 68.9% Chessbook-1, 2200-2400: 67.3% Chessbook-1, 2400-2600: 68.0% Chessbook-1, 2600+: 70.5% 70.5% 70.3% 68.8% 66.4% Chessbook-1 Maia-3 Allie 2.0 Stockfish eval (rating-blind)
Higher is better. Share of decisive games in which the side the model gave the better chances went on to win, by the rating of the player to move. Stockfish's evaluation picks whoever is ahead on the engine's score.

Chessbook-1 and Allie 2.0 pick the eventual winner 66–71% of the time, Maia-3 65–69%, and Stockfish's evaluation 63–66%, in every rating band. The engine is the worst of the four because a +1.5 pawn advantage means something very different for a 900 than for a 2500, and the models know it. Scored properly (the Brier score and log-loss of the win/draw/loss probabilities against the result), Chessbook-1 and Allie are level with each other, Allie a hair ahead in most bands (it reads the whole game), and both are well ahead of Maia-3 everywhere.

This makes for some very cool applications for game review, which I've been exploring.

The model and the data: stats for nerds

199M
parameters: 18 transformer layers, 768 wide, 12 heads
12 months
of Lichess games, August 2025 to July 2026
345M
training positions in the main run, plus a 33M-position fine-tune
24 h
on 8×H100 for the main run, then 8 h on 8×A100
4,864
possible moves it chooses between (from-square × to-square and promotion)
3
predictions from one forward pass: the move distribution, the result, and a think-time distribution for every candidate move
Chessbook-1: one forward pass, three predictions 199M parameters. Solid arrows carry gradients back into the trunk; the ⊘ marks a stop-gradient. Dashed outlines are training-only heads. Board: 64 square tokens × 142 features 12 piece one-hots · 24 tactical (attackers by role, static exchange, pins, x-rays, control) 17 positional (mobility, pawn structure, weak squares, files) · 1 misc · 4 last-move marks 7 × 12 history planes: piece one-hots of the previous seven positions (most recent first, 5% dropout so the model also works from a bare position) computed with shakmaty at encode time; the network does not rediscover attack maps Global vector g: who is playing, and the clocks both ratings as smooth Gaussian bins over 600–2800 (2 × 23) move number, material, phase and other scalars (13) per side: 13 log-time bins (1 s to 4096 s) · log base, log increment, inc ÷ remaining time spent on this move so far (11; zero at inference) · 10% of examples see no clock feeds every block through FiLM, and every head, never the tokens directly Token embedding MLP 142 → 256 → 768 + learned square embedding · RmsNorm Trunk: 18 × pre-norm transformer block, 768 wide, 12 heads one block FiLM-RmsNorm γ = 1 + Wγ·g, β = Wβ·g (rating and clock reshape every layer) Smolgen attention: a small network reads the tokens and g and emits a 64 × 64 additive bias per head, so where attention looks depends on the position + residual FiLM-RmsNorm → SwiGLU MLP + residual final RmsNorm after block 18 → trunk tokens, 64 × 768 g → FiLM in all 18 blocks, the policy block, the value block and every head trunk tokens Policy block one more FiLM transformer block Factorised policy head from-square × to-square (+ promotion piece) 4,864 logits, FiLM on g, masked to legal moves cross-entropy on the human's move Human move distribution the bot samples from it, sharpened per rating Future-move head training only predicts the next four plies after the human's move, one fused factorised map per ply (weights 0.5, 0.25, 0.125, 0.0625) shapes the trunk; never feeds the policy logits also in the code, off in Chessbook-1: Lc0 policy head, Stockfish value head (engine supervision made it less human) stop-gradient: trunk.detach() Value block one FiLM transformer block on detached tokens (Smolgen weights frozen here) Result head attention pooling over 64 squares ⊕ g → MLP → 3 logits cross-entropy on the game result Win / draw / loss for the side to move, at these ratings Think-time head (move-aware) pooling ⊕ g ⊕ the move's from/to tokens ⊕ the policy's surprise at it → MLP → mean and concentration Beta(α, β) over clock share one distribution per candidate move the policy's surprise at each candidate move, −log p(move), is an input to its think time Why the split The policy is a model of human moves and learns only from them. The future-move head pushes chess knowledge into the shared trunk without touching the policy logits. The result and think-time heads learn from noisy game outcomes and clock annotations, so they sit behind a stop-gradient: they read the trunk but never shape it. Mean-pooled trunk tokens, ZCA-whitened, double as position embeddings for the rest of Chessbook (served by a 192×6 "mini" of the same design). Source: src/model.rs, relative_position_transformer.rs, smolgen.rs, factorized_policy.rs, aux_heads.rs, config.rs.
The whole model. Solid arrows carry gradients back into the trunk; the result and think-time heads sit behind a stop-gradient, and the future-move head exists only in training.

The data is every rated Lichess game from those twelve months with both players rated 600–3000 and within 400 points of each other, and no bot accounts. The main run used games with a base time of at least two minutes; the fine-tune added bullet and ultrabullet. Positions are sampled so that every rating band contributes equally, the opening is thinned (we do not need a million copies of 1.e4 e5), and the evaluation month is never touched.

Everything here is our own code: the model, the training loop and the serving path are written in Rust on top of the Burn framework, with a forked CUDA backend we spent a chunk of the ten months making fast and correct for multi-GPU training.

Inputs. The board is 64 square tokens. Each square carries piece one-hots, attacker/defender and static-exchange features, mobility, rank and file, castling rights, and decayed from/to marks of the last seven plies per side, plus Maia-style history planes of the previous positions (with 5% dropout so the model also works from a bare position). A global vector carries both ratings as smooth Gaussian bins over 600–2800, the move number, material, and a clock block: 13 log-time bins per clock from 1 s to 4096 s, log base time, log increment, and increment over remaining time. Clocks and history can be missing; a flag says so.

Trunk. 18 pre-norm transformer blocks, 768 wide, 12 heads, SwiGLU MLPs. Two things make it chess-shaped. Every RmsNorm is FiLM-conditioned on the global vector (γ = 1 + Wγ·g, β = Wβ·g), so rating and clock reshape every layer rather than being appended once. And attention uses a Smolgen-style dynamic bias, as in Leela Chess Zero: a small generator compresses the tokens and the globals and emits a 64×64 additive bias per head, so where attention looks depends on the position and on who is playing.

Heads. The policy is a factorised from-square × to-square head (plus promotion piece) over 4,864 moves, through its own transformer block, masked to legal moves and trained with cross-entropy on the human move. A fused auxiliary head predicts the next four plies after the human move (weighted 0.5, 0.25, 0.125, …), which in ablations was worth 0.05 nats at 2200+ for nothing. The value head (W/D/L) and the time head sit on a separate "value block" that reads the trunk detached, so they never push gradients into the policy. The time head is move-aware: it reads the tokens on the move's from and to squares plus the policy's surprise for that move and emits the α, β of a Beta distribution over the share of the clock spent. One forward pass returns the policy, the result distribution and a think-time distribution for every candidate move.

Training. Global batch 8,192 (two micro-batches of 512 per GPU on 8×H100), Muon/Aurora for the matrices and AdamW for the rest, a warmup–stable–decay schedule on a 24-hour wall-clock budget (2% warmup, 30% linear decay), EMA weights kept alongside the live ones, 3× oversampling of 2000+ games. ~345M positions. The production run was followed by a 33M-position, 8-hour fine-tune on 8×A100 that trained the value head from ply 1 (the main run ramped it in from ply 10, which made the opening win/draw/loss numbers unreliable) and added bullet and ultrabullet games, which the main run had skipped. Before the production run we did a 14-arm ablation at 512×8 to pick the recipe: history planes, the future-move head, keeping the hand-crafted tactical input features, dropping the engine-supervised heads (they made the model less human on mistakes), batch and learning-rate scaling.

Serving. The bot runs the full model in f16 with CUDA graphs on a single T4 in GKE and answers a prediction in well under a second, then the client waits out the sampled think time before showing the move. A 192×6 "mini" trained on the same architecture provides position embeddings for other Chessbook features.

Benchmark details. Positions are scored with Stockfish 18 MultiPV over every legal move at 8k nodes; a mistake is a move that loses at least 10 points of win probability, a blunder 15, an inaccuracy 5, with evaluations clamped at ±1000 cp for ACPL. Maia-3 is the 79M checkpoint with 7 history positions; Allie 2.0 is the 5.6B MoE run over the full game; Stockfish uses UCI_LimitStrength with UCI_Elo at the mover's rating, 8 plays per position for a sampled distribution. Noctie, the other commercial human-like bot, has no public weights or API, so it is not in the comparison. The benchmark scripts, the eval sets and the raw per-position outputs for every engine are available on request.

Try it out

Play against it. Pick a rating from 600 to 2800 and a time control, and Chessbook-1 will play like a person at that level: the same moves, the same mistakes, the same amount of thinking time, and the same panic when the clock runs low. Play a game →

Build a repertoire for the games you actually play

Chessbook shows you the moves opponents play at your rating and drills your answers with spaced repetition.

Try Chessbook for free

No signup required