All work

Sigma-Go Chess Engine

ResNet plus tree search, trained on human games

Year
2025–26
Kind
Models
Status
Finished

4.3x faster batched tree search

A chess engine built from scratch: a residual network suggests moves and judges positions, and Monte Carlo tree search looks 800 simulations ahead. It learns from human games, not self-play.

The board is encoded as 14 planes and fed to a 10-block residual network with two heads: a policy over 4,544 possible moves and a value for who is winning. Monte Carlo tree search runs 800 simulations per move, guided by both, and evaluates leaves eight at a time with virtual loss, which made it 4.3x faster. The network learns from PGN game records, streamed from disk in 10,000-position chunks. A local Flask app serves a browser board to play it on.

The problem

A policy network on its own plays whichever move looks most natural, and can miss what happens a few moves later. DeepMind's AlphaZero pairs a network with a tree search, so the network's hunches get checked by looking ahead. Sigma-Go builds that pairing from scratch in PyTorch, with one big difference: it learns from recorded human games rather than from self-play. You play it on a board in the browser, served by a small local Flask app.

How it works

  1. 01

    Encode the board as 14 planes

    Each position becomes a 14 x 8 x 8 tensor. Twelve planes mark where each piece type of each colour stands, and the last two say whose turn it is. Moves are indexed as UCI strings, every from-square to every to-square plus promotions, 4,544 in all.

  2. 02

    One network, two answers

    A convolution widens the planes to 256 channels, and 10 residual blocks of 3x3 convolutions follow. The policy head scores all 4,544 moves. The value head squeezes the position through tanh into one number, from -1 if Black is winning to +1 if White is.

  3. 03

    Learn from recorded games

    preprocess.py walks PGN files and stores, for every position, the move that was played and the game's final result, in compressed chunks of 10,000 positions. Training streams the chunks in shuffled order and minimizes cross-entropy on the move plus half the squared error on the result.

  4. 04

    Look ahead with tree search

    Each move gets 800 Monte Carlo tree search simulations. A simulation walks down the tree by PUCT, which adds a move's average value so far to a bonus for moves the network likes but the search has rarely tried. It asks the network about the new leaf and backs the value up, flipping its sign at each ply. Leaves are evaluated eight at a time: a virtual loss, a temporary penalty on paths already being explored, spreads the eight across the tree, and the network scores them in one call, so a move takes 101 network calls instead of 801. Illegal moves are masked out and the remaining priors renormalized.

  5. 05

    Play the most-visited move

    In the opening the engine samples its move in proportion to visit counts raised to 1/T, a temperature, so games don't repeat. After that it plays the move it visited most, not the one with the highest prior, and reports that move's value as its evaluation. The browser board takes drag-and-drop moves, shows legal-move dots, lets you play either side, undo and flip, and draws the evaluation as a bar.

Results

4.3x
faster search from batching 8 leaves per network call: 1,963 vs 461 simulations a second on a laptop RTX 5060
101
network calls per move instead of 801, with the same moves chosen on every test position

Decisions

  • Stream training data from disk

    A position is 14 x 8 x 8 float32 values, about 3.5 KB, so a million positions take about 3.5 GB. Positions go to disk in 10,000-position chunks, and an IterableDataset reads one chunk at a time, shuffling chunk order and positions within each chunk.

  • A fixed move list, masked at search time

    A fixed list of 4,544 moves lets one linear layer score every move in any position. The search keeps only the legal ones and renormalizes their priors, so an illegal move never gets a visit.

  • Noise at the root

    Before each search the root's priors are mixed 75/25 with Dirichlet noise, as in AlphaZero, so the search also explores moves the network ranks low.