CHSM8: A Chess Transformer that Follows Natural-Language Style Prompts
Abstract
We present CHSM8, a 210M-parameter chess transformer that plays moves conditioned on a free-text description of a playing style, e.g. "go for the Sicilian Defense", "solid, safe, risk-free chess" or "You are Magnus Carlsen in his prime". The model is pretrained without search on 1.07B filtered Lichess games, using a factorized move representation and legal-move masking. It reaches 1837 Elo against a strength-limited Stockfish and, in 1,000-game matches, beat every other public model we tested that was likewise trained without preference tuning, rewards or search, including one with 13 times as many parameters. We then fine-tune it to follow text on a synthetic dataset: Gemma-4-31B describes the playing style of 3M game sides, a 1B Gemma-3 model distilled from the 2.4M of those descriptions that contradict nothing on the board labels the rest, and every one of 30M selected game sides gets five descriptions. Supervised fine-tuning attaches a frozen text encoder through gated cross-attention, and direct preference optimization (DPO) uses the same moves under a mismatched description as the rejected sample. After DPO, style adherence rises on every prompt we test (for example, queens traded early 53% -> 67%, pawn moves 29% -> 32% of all moves, queenside castling 97.3% -> 99.7%), and strength without a prompt stays within noise of SFT (1792 vs. 1797 Elo). Design choices at every stage were made by fixed-budget automated search loops on top of standard architectures and training recipes. We document the full pipeline and the decisions behind it, report the ablations that failed, and evaluate the models across many style prompts and kinds of games, with 1,000 games per setting. The work is fully open source and reproducible: weights, datasets with all training labels, prompts, configurations, inference code and playable demos are public and free to build on.
Figures & tables
| tier | conversion | output | process time | wall-clock time |
| 1 | download, 162 monthly .pgn.zst | 2.51 TB | 6.5 h | 6.5 h |
| 2a | PGN 16-bit move ids + metadata (cheap filters) | 784 GB | 170 h | 22 h (8 months at a time, 16 workers each); largest month 1.9 h |
| 2b | strength, time forfeits, clock cut | 156 GB | 4.7 h | 4.7 h (one month at a time); largest month 7 min |
| 3 | move ids factorized tokens + legal masks | 1.39 TB | 76 h | about 1.2 h (96 workers); largest month 1.2 h |
| 3p | greedy packing into 512-position rows | 1.4 TB | — | 12–13 min (162 workers) |
| stage | data | order |
| pretraining chsm8-pt | 1.07B games, 78.32B positions, one pass chsm8-pre-training | 5% uniform, then 2/3 shuffled mixed with 1/3 newest-to-oldest; from 60% to the end globally reshuffled |
| games-only chsm8-player-base | 26.0M games of the post-training selection (train split), no text, one pass chsm8-post-training | uniformly shuffled |
| player fine-tunes chsm8-player-* | one player’s own games, own moves only, 2 passes chsm8-top-players | uniformly shuffled |
| SFT with text chsm8-sft | 27.08M described sides, all five bands, one pass; top-player games with “play like { player } ” prompts in about 6% of batches (3 passes over that corpus) chsm8-post-training , chsm8-top-players | uniformly shuffled across time; length-bucketed batches; 10% of descriptions dropped |
| DPO chsm8 | the same 27.08M sides as label-swap pairs, one random band per game; the main model saw 0.56M, the full-pass model all chsm8-post-training | uniformly shuffled; length-bucketed batches |
| round 1 (30 min, 47.7M model) | round 2 (30 min, final architecture) | ||
| setting | val loss | soft-penalty weight | val loss |
| no legality term | 3.3389 | 0.5 | 3.3038 |
| hard mask | 6.4273 | 1.0 | 3.3128 |
| soft penalty 0.1 | 3.3379 | 2.0 | 3.3258 |
| soft penalty 0.5 | 3.3352 | 4.0 | 3.3333 |
| configuration (55 min each) | params | trained | positions seen | throughput | val loss |
| custom factorized decoder | |||||
| 4L 256 | 3.4M | 3.4M | 586M | 178k | 4.959 |
| 8L 512 | 25.6M | 25.6M | 219M | 66k | 4.417 |
| 8L 512, lr | 25.6M | 25.6M | 244M | 74k | 4.065 |
| + no dropout | 25.6M | 25.6M | 259M | 78k | 4.026 |
| + lr | 25.6M | 25.6M | 409M | 124k | 3.682 |
| architecture | params (total / trainable) | val loss | positions/s | MFU | full pass |
| 20L 768, SwiGLU 2560, QK-norm | 212.7M / 165.5M | 3.0485 | 305.5k | 9.8% | 71 h |
| 12L 960, SwiGLU 3520, QK-norm | 210.5M / 166.2M | 3.0598 | 541.8k | 17.1% | 40 h |
| 20L 768, no QK-norm | 212.7M / 165.5M | 3.0660 | 392.3k | 12.5% | 55 h |
| 12L 960, no QK-norm | 210.5M / 166.2M | 3.0710 | 501.4k | 15.8% | 43 h |
| 20L 768, plain FF 3840 | 213.3M / 166.0M | 3.1094 | 463.9k | 14.8% | 47 h |
| 12L 960, plain FF 4800 | 200.1M / 155.8M | 3.1142 | 633.1k | 19.0% | 34 h |
| opponent | type | parameters | W / D / L | score | Elo advantage (95% CI) |
| Maia-2 blitz, rating input 2000 ( Tang et al., 2024 ) | board policy | 23M | 102 / 52 / 46 | 0.640 | +100 (+59, +144) |
| Chess-GPT, 16 layers ( Karvonen, 2024 ) | char-level PGN | 51M | 123 / 43 / 34 | 0.723 | +166 (+122, +216) |
| chess-gpt2-uci-12x12x768 | UCI-text GPT-2 | 86M | 151 / 37 / 12 | 0.848 | +298 (+249, +359) |
| ChessGPT-base-v1 ( Feng et al., 2023 ) | LM on chess text | 2.8B | 158 / 32 / 10 | 0.870 | +330 (+278, +397) |
| ChessFormer (Transcendence) ( Zhang et al., 2024 ) , 1500 data | char-level PGN | 51M | 163 / 30 / 7 | 0.890 | +363 (+309, +434) |
| teacher | fitness | distinctiveness | retrieval R@1 | parsed |
| MiniMax-M2.5 | 0.598 | 0.515 | 0.67 | 100% |
| Gemma-4-31B | 0.579 | 0.387 | 0.53 | 100% |
| Grok-4.5 | 0.568 | 0.335 | 0.48 | 100% |
| GPT-5.2 | 0.546 | 0.301 | 0.45 | 100% |
| GPT-5-mini | 0.533 | 0.311 | 0.46 | 92% |
| Apertus-70B-Instruct-2509 (v1) | 0.441 | 0.357 | — | 100% |
| model | labels contradicted | claim grounding | distillation | vLLM games/s |
| Gemma-4-31B teacher | 4.7% | 88.3% | — | — |
| Gemma-4-31B, second sample (ceiling) | — | 87.9% | 100% | — |
| Gemma-3-1B, full fine-tune | 4.7% | 88.2% | 94% | 154 |
| Gemma-3-270M, full fine-tune | 5.4% | 88.3% | 90% | 193 |
| T5Gemma 2B-2B, LoRA | 5.5% | 87.4% | 94% | 23 (HF) |
| Qwen3-8B, LoRA | 4.0% | 87.9% | 92% | 40 |
| trained on / tested on | Gemma-4-31B | Apertus-v1.5-70B | distilled Gemma-3-1B |
| Gemma-4-31B | |||
| Apertus-v1.5-70B | |||
| distilled Gemma-3-1B |
| model, prompt | CE | move match |
| chsm8-sft , right name | 2.850 | 56.2% |
| chsm8-sft , another player’s name | 2.962 | 55.0% |
| chsm8-sft , no prompt | 2.984 | 54.7% |
| chsm8-dpo , right name | 2.879 | 55.5% |
| chsm8-dpo , another player’s name | 3.047 | 53.9% |
| chsm8-dpo , no prompt | 2.984 | 54.6% |
| prompt | behavior | no prompt |
| “leave your king in the center and go for an open fight” | castles in 45% of games | 94% |
| “castle queenside and storm the enemy king with your pawns” | 39% of castling queenside | 10% |
| “trade queens as early as possible” | queens off by ply 40 in 40% | 25% |
| “push your pawns aggressively up the board and grab space” | 0.289 pawn moves per move | 0.248 |
| strong-play prompts (8) / weak-play prompts (8) | 1765–1836 / 1778–1839 Elo | 1828 Elo |
| encoder and conditioning | LLM-label CE | mean gap | opening-template gap | relative time |
| Qwen3-Emb-0.6B, pooled (early recipe) | 2.9137 | 1.00 | ||
| Qwen3-Emb-0.6B, pooled + instruction | 2.9109–2.9125 | 1.01 | ||
| EmbeddingGemma-300m, pooled | 2.9114 | 1.10 | ||
| Qwen3-Emb-4B, pooled + instruction | 2.9116 | 1.06 | ||
| Qwen3-Emb-0.6B, per-token | 2.8904 | 1.65 | ||
| Qwen3-Emb-0.6B, per-token, 3 conditioned layers | 2.8983 | 1.57 |
| encoder, pooling | bare | mixed |
| Qwen3-Embedding-0.6B, last token (early recipe) | 98% | 53% |
| Qwen3-Embedding-0.6B, mean | 99% | 4.5% |
| Qwen3-Embedding-0.6B, last token, instruction | 100% | 99% |
| Qwen3-Embedding-8B, last token | 98% | 57% |
| EmbeddingGemma-300m, mean | 93% | 79% |
| Qwen3-0.6B language model, mean | 85% | 6% |
| bands | granularity | queens (%) | pawns | queenside (%) | Elo, none | attack gap |
| SFT | — | 51.3 | .292 | 97.1 | 1789 | |
| one random | game | 65.5 | .317 | 99.1 | 1788 | |
| long only | game | 67.4 | .321 | 99.1 | 1811 | |
| all five | game | 65.2 | .315 | 99.2 | 1793 | |
| all five, label + game pairs | game | 65.3 | .315 | 99.4 | 1806 | |
| one random | move | 56.9 | .298 | 99.1 | 1792 |
| run | GPUs games | games seen | queens (%) | pawns | queenside (%) | Elo, none | attack gap | , decorr. | |
| main | 1 128 | 0.56M | 66.7 | .318 | 99.7 | 1817 | |||
| C | 4 256 | 2.53M | 61.6 | .318 | 99.4 | 1809 | |||
| A, new seed | 1 128 | 0.59M | 64.8 | .315 | 98.1 | 1810 | |||
| D, learning rate 2.8 | 4 256 | 2.50M | 61.0 | .317 | 99.8 | 1796 | |||
| B | 4 32 | 0.59M | 63.1 | .316 | 99.5 | 1795 | |||
| full pass | 8 256 | 27.08M | 57.1 | .319 | 99.6 | 1807 |
| style adherence | strength | |||||
| model | queens (%) | pawns | queenside (%) | Elo, none | attack gap | |
| SFT (start) | 53.0 | .290 | 97.3 | 1792 | 21.0 | |
| DPO, 20-min run (main) | 66.5 | .319 | 99.7 | 1797 | +2.8 | |
| DPO, full pass | 58.7 | .317 | 99.6 | 1812 | 1.4 | |
| DPO, long band only, 25% | 64.2 | .318 | 99.1 | 1790 | 1.1 | |
| DPO, long band only, 100% | 60.9 | .308 | 99.6 | 1798 | 5.6 | |
| model | right | wrong | none | pair accuracy |
| SFT | 2.806 | 3.094 | 2.928 | 0.803 |
| DPO, 20-min run (main) | 2.846 | 3.600 | 2.932 | 0.893 |
| DPO, full pass | 2.851 | 4.077 | 2.933 | 0.925 |
| prompt metric | model | no prompt | right prompt | other prompts |
| “trade queens as early as possible” queens traded by ply 40 | SFT | |||
| DPO | ||||
| “push your pawns aggressively up the board and grab space” pawn moves per own move | SFT | |||
| DPO | ||||
| “castle queenside and storm the enemy king with your pawns” queenside castling, all games | SFT | |||
| DPO |
| opponent | params | Elo (95% CI) | ms/move | chsm8-pt | player-base | player-carlsen |
| public models trained by supervised prediction | ||||||
| Maia-2 blitz | 23M | 1657 (1626–1684) | 3 | 65.3% | 71.8% | 71.7% |
| ChessLFM | 231M | 1687 (1659–1713) | 24 | 91.5% | 93.3% | 92.8% |
| ChessGPT-base | 2.8B | 1731 (1708–1752) | 67 | 88.0% | 93.7% | 94.3% |
| ChessFormer (Transcendence) 1 1 1 A dataset repository holding the checkpoints; we used 50M-High-Elo-1500-No-Elo-Conditioning/2024-04-30---03-07-10_50M-High-Elo-1500-No-Elo-Conditioning/ckpt_1030000.pt . | 51M | 1733 (1710–1754) | 39 | 86.5% | 90.0% | 93.9% |
| chess-gpt2-uci | 86M | 1784 (1763–1803) | 24 | 82.2% | 88.8% | 91.0% |
Appendix figures & tables6 assets
Supplementary material from the paper’s appendix.
Appendix
| pretraining | SFT | DPO (main) | DPO (full pass) | |
| initialization | random | chsm8-pt | chsm8-sft | chsm8-sft |
| text encoder | none | EmbeddingGemma-300m (frozen) | EmbeddingGemma-300m (frozen) | EmbeddingGemma-300m (frozen) |
| conditioned layers | — | top 6 of 12 | top 6 of 12 | top 6 of 12 |
| learning rate, decoder | ||||
| learning rate, text cross-attention | — | |||
| warmup, then cosine to | 5%, 10% | 5%, 0 | 5%, 0 | 5%, 0 |
| games-only continuation ( chsm8-player-base ) | player fine-tunes (14 models) | |
| initialization | chsm8-pt , cross-attention replaced by a per-layer bias | chsm8-player-base |
| trained parameters | all 166M | all 166M |
| learning rate | ||
| warmup, then cosine to | 5%, 0 | 5%, 0 |
| batch per step | 128 rows of 512 positions (32 per GPU, 4 GPUs) | 32 rows of 512 positions (1 GPU) |
| steps | 29,706 | by player, 2 passes |
| move match (%) | CE (nats per move) | Elo | ||||||||||
| player | FIDE rank | rating | games | positions | base | FT | base | FT | % | FT | ||
| Magnus Carlsen | 1 | 2823 | 17,561 | 1.74M | 55.4 | 56.9 | +1.5 | 2.969 | 2.915 | 1.8 | 1903 | +13 |
| Hikaru Nakamura | 2 | 2792 | 19,997 | 1.97M | 54.0 | 57.2 | +3.2 | 3.039 | 2.862 | 5.8 | 1850 | 40 |
| Fabiano Caruana | 3 | 2784 | 5,513 | 584K | 54.5 | 55.6 | +1.1 | 2.960 | 2.938 | 0.7 | 1902 | +12 |
| Javokhir Sindarov | 4 | 2782 | 1,371 | 135K | 55.8 | 56.7 | +0.9 | 2.959 | 2.898 | 2.1 | 1897 | +7 |
| Nodirbek Abdusattorov | 5 | 2780 | 5,356 | 579K | 54.4 | 54.9 | +0.5 | 2.967 | 2.995 | +0.9 | 1906 | +16 |
| band | Gemma-4-31B (teacher) | Gemma-3-1B (distilled, clean) | Apertus-v1.5-70B |
| terse | give up a pawn to blitz the king | give up a pawn for a fast attack | trade pieces and keep pushing pawns |
| terse | hunt the queen then clean up the board | hunt the king with everything | attack, sacrifice, don’t hold back |
| short | ignore early material losses to create an overwhelming attack against the exposed king. | play recklessly by offering material to gain a devastating lead in development and initiative. | sacrifice calmly and salt kingside with pawns while trading everything else |
| medium | play with reckless aggression by trading pawns for rapid development and using the knights to dismantle the opponent’s position quickly. | ignore the early loss of a central pawn to launch a chaotic and bloody offensive that keeps the enemy king running. | play fast and loose with material, keep queens on the board, and pile pawns on the king after sacrificing pieces |
| long | sacrifice an early pawn for a lead in development and then launch a chaotic offensive that captures almost everything in sight until the king is trapped in the center of the board. | be completely fearless with material and prioritize speed over safety so you can swarm the opponent with pieces while they are still struggling to coordinate their defense in a total collapse. | ignore safe positions and keep sacrificing pieces to crash the kingside with pawns, trade relentlessly, and never stop pressing forward—this blitz pace rewards aggression over caution |
Chess
Chess research develops and evaluates engines, human-like play models, strategic reasoning systems, and tools for analyzing boards and games.
See all 31 papers