Skip to the content
W01NotesEngines from nothing

Gambit, a chess engine written from nothing

Gambit is a chess engine writ­ten from an empty file: the board, every legal move, and a search that picks one. It has no de­pen­den­cies and thinks in a worker in your browser, so the page stays smooth while it does. Play it below. Its move gen­er­a­tor is held against the pub­lished perft counts for six stan­dard po­si­tions, and it gives the same count in every one.

PostedKindNotesSeriesEngines from nothing

Drag a piece, or click it and then a square. The panel shows what Gambit is weigh­ing while it thinks: how many moves ahead it has looked, the score in pawns from white’s side, and the line it ex­pects both sides to play. The game is kept in this browser when you leave.

Held against the published counts

A move gen­er­a­tor is easy to get almost right. Castling through an at­tacked square, an en pas­sant cap­ture that leaves the king open, a pawn that takes and pro­motes in one move: each is a rule that a few po­si­tions in a mil­lion depend on, and a game can go on for hours with­out meet­ing one.

Perft is the stan­dard test. From a po­si­tion it counts every se­quence of legal moves to a given depth, and the totals for a hand­ful of well-known po­si­tions are pub­lished. One miss­ing or extra move any­where in the tree changes a total. Press Count to run it here, with the same code the board above plays with.

Six positions, from the opening to a crowded middlegame with every special move on the board.

The page stops where a count would take more than a few sec­onds. The com­mand runs every depth that is pub­lished up to a few hun­dred mil­lion, and this is its output:

$ node web/notes/gambit/perft.mjs --deep
same  Start       depth 1           20  published          20  1 ms
same  Start       depth 2          400  published         400  1 ms
same  Start       depth 3         8902  published        8902  26 ms
same  Start       depth 4       197281  published      197281  63 ms
same  Start       depth 5      4865609  published     4865609  849 ms
same  Start       depth 6    119060324  published   119060324  19736 ms
same  Kiwipete    depth 1           48  published          48  0 ms
same  Kiwipete    depth 2         2039  published        2039  3 ms
same  Kiwipete    depth 3        97862  published       97862  28 ms
same  Kiwipete    depth 4      4085603  published     4085603  720 ms
same  Kiwipete    depth 5    193690690  published   193690690  33022 ms
same  Position 3  depth 1           14  published          14  0 ms
same  Position 3  depth 2          191  published         191  0 ms
same  Position 3  depth 3         2812  published        2812  1 ms
same  Position 3  depth 4        43238  published       43238  10 ms
same  Position 3  depth 5       674624  published      674624  136 ms
same  Position 3  depth 6     11030083  published    11030083  2401 ms
same  Position 4  depth 1            6  published           6  0 ms
same  Position 4  depth 2          264  published         264  0 ms
same  Position 4  depth 3         9467  published        9467  1 ms
same  Position 4  depth 4       422333  published      422333  77 ms
same  Position 4  depth 5     15833292  published    15833292  2610 ms
same  Position 5  depth 1           44  published          44  0 ms
same  Position 5  depth 2         1486  published        1486  0 ms
same  Position 5  depth 3        62379  published       62379  11 ms
same  Position 5  depth 4      2103487  published     2103487  352 ms
same  Position 5  depth 5     89941194  published    89941194  16449 ms
same  Position 6  depth 1           46  published          46  0 ms
same  Position 6  depth 2         2079  published        2079  0 ms
same  Position 6  depth 3        89890  published       89890  14 ms
same  Position 6  depth 4      3894594  published     3894594  1021 ms
same  Position 6  depth 5    164075551  published   164075551  24439 ms
every count is the published one; 610,195,852 positions in 102.0 s

The board has twice as many squares as it shows

The board is an array of six­teen squares per rank, where only the left eight of each rank are real. A square’s index is its rank times six­teen plus its file. Stepping off the edge of the board gives an index where index & 0x88 is not zero, so the edge test is a single AND, the same for every piece and every di­rec­tion. The format is called 0x88 after that mask.

Point at a square to see its index and where a knight on it can go. A move that would leave the board is caught by the mask and drawn in red.

Here is the knight’s part of that as a script you can run. It is the same test the engine makes, writ­ten out in a few lines. Step walks through it the way the engine reads it, one part of an ex­pres­sion at a time, with every vari­able beside the code. Change the square or the steps and run it again.

const KNIGHT = [33, 31, 18, 14, -33, -31, -18, -14];
const name = (s) => 'abcdefgh'[s & 7] + '87654321'[s >> 4];

function knightMoves(from) {
  const moves = [];
  for (const step of KNIGHT) {
    const to = from + step;
    if (to < 0 || to & 0x88) continue; // off the board
    moves.push(name(to));
  }
  return moves;
}

console.log(knightMoves(0x00)); // a8, in the corner
console.log(knightMoves(0x44)); // e4, in the middle
The script runs in Spark, a JavaScript engine written from nothing like Gambit. It loads when you press a button.

What a position is worth

The score is the ma­te­r­ial on the board plus where each piece stands. Every piece has two tables of bonuses, one for the mid­dlegame and one for the endgame, and Gambit blends them by how much ma­te­r­ial is left. A knight is worth most in the center. A king scores best behind its pawns while the queens are on, and in the middle once they are gone. Choose a piece to see its table.

Middlegame
Endgame
The table is drawn from white’s side. Violet adds to the score, gray takes from it, in hundredths of a pawn.

What it does not have

Gambit has no open­ing book and no endgame tables, and it thinks on one thread. Its strength has not been mea­sured against a rated engine, so this page gives it no rating. What is mea­sured is the move gen­er­a­tor, above, and the tests: mates in one and two, a hang­ing queen taken, no­ta­tion, the ends of a game, and two copies play­ing a full game where every move is checked for le­gal­ity.

The source is in the port­fo­lio’s repos­i­tory under web/notes/gambit/: engine.js for the board and the search, perft.mjs for the counts, engine.test.mjs for the tests.