Chess Reasoner 0.8B

A small 0.8B parameter language model that finds chess moves by using tools.

The model calls tools to gather facts about the board: what pieces attack a square, which pieces are hanging, what happens after Nxe5. Each tool call requires a sentence justifying its purpose. After the model has checked candidate moves, it answers with its chosen move.

The base model is Qwen3.5-0.8B, fine-tuned with supervised learning followed by reinforcement learning (GRPO).

Installation

Please see the Github repo for installation instructions: https://github.com/lukesalamone/chess_reasoner_demo

The agent loop

The model makes one tool call at a time. Example:

<tool>attackers(e5) | why: Is the pawn on e5 defended? If not, Nxe5 wins it.</tool>

The harness then runs the call on a scratch board and sends back the result:

<result>e5 (black pawn): attacked by Nf3; defended by Nc6.</result>

The model then either makes another tool call or gives a final answer. The harness limits the model to 24 tool calls, and requires a justification for making the tool call itself to prevent the reasoning trace from devolving into a list of bare tool calls. There is some basic validation on the justifications to prevent malformed, short, long, or repeated justifications.

Tool Call Description
piece_on(square) What piece is on a square
pieces() Every piece on the board and where it is (both sides)
material() Material count for both sides in pawn units
attackers(square) Which pieces (any side) attack a square. If occupied, distinguishes attackers from defenders
targets(square) What enemy pieces the piece on square attacks, and what friendly pieces it defends
hanging(color) What pieces of this color are hanging
pins(color) What pins against the king for color
forcing_moves() Legal checks, captures, and promotions are available in this position
legal_moves() Every legal move in this position, plus whether the side to move is in check
try(move) Push a move onto the stack to evaluate one ply deeper (up to 4 deep)
undo() Pop off the stack to back out of a search
quiet_moves() Legal moves that are not in forcing_moves()
root() Pop the whole stack to return to the starting position
threats() What the opponent could do if it were their move: mates, checks, captures that win material
exchange(square) Result of the capture sequence on a square, starting with the best first capture

Prompts and sampling

The system prompt and user prompt are in the demo repo (system_prompt.txt, prompt.py). The model was trained with them and expects them unchanged. The chat template is plain ChatML (chat_template.jinja). Each assistant turn is one tool call with its reason. After its calls, the model ends with <answer>MOVE</answer>, where MOVE is in SAN.

Evaluation

The test set is 775 held-out positions that are still contested (Stockfish evaluation within 1000 centipawns, no forced mate). None of these positions was used in training.

A move counts as a "hit" when it is within 50 centipawns of Stockfish's best move at depth 20. Four answers were sampled per position.

hit rate
a random legal move 25.2%
chess-reasoner-0.8b 57.1%

It answers with a legal move 99.9% of the time, after 22 tool calls on average.

Training details

  1. Supervised fine-tuning on 34k reasoning transcripts from a teacher model. Transcripts were kept only if their final move was within 50 centipawns of the best move.
    • LoRA rank 64, 1 epoch, LR 1e-4, BS 16, max sequence length 6,144 tokens
  2. GRPO (group relative policy optimization) for 150 steps:
    • 16 positions per step, 16 samples per position, temperature 1.0, LR 1e-5
    • The reward was exp(โˆ’cpl / 100) for a legal answer and 0 otherwise, where cpl is the answer's centipawn loss against Stockfish at depth 20.

The LoRA weights are merged into the bf16 model. The training positions and test positions come from the ChessBench dataset.

Limitations

  • It needs the tool loop. Without tools it has no reliable view of the board.
  • It only picks moves. It was not trained to discuss chess in general.
  • On test positions, hit rate is only about 57%.

More information

You can find more information in my blog post: https://blog.lukesalamone.com/posts/chess-reasoning-llm/

License

Apache 2.0

Downloads last month
372
Safetensors
Model size
0.9B params
Tensor type
BF16
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for lukesalamone/chess-reasoner-0.8b

Finetuned
(689)
this model