Summer of Code · Queen's Gambit

Strategos — AI Chess Engine

A C++ chess engine that plays full games through a CLI. It pulls from a Polyglot opening book for the first ~25 moves, then hands off to an alpha-beta search with a transposition table and phase-aware evaluation for the middlegame and endgame.

Timeline
Jun – Jul 2025 · Summer of Code (Queen's Gambit track)
Type
Self project
Language
C++17
Search
Iterative deepening · alpha-beta · transposition table · MVV-LVA
Built on
disservin/chess-library v0.6.51 — bitboards, magic-bitboard sliders, FEN, movegen
Repo
github.com/Y-Harsha-Vardhan/AI-Chess-Bot

What I built, what I built on

Move generation, magic-bitboard sliders, FEN parsing, and the board representation come from the disservin/chess-library (v0.6.51, ~4.4k LOC of header-only C++). That left the part I actually wanted to write: the search, the evaluation, the opening-book reader, and the move-ordering heuristics — the parts that determine how the engine plays, not how it represents a board.

The project also opens with a week of game-theory groundwork — small C++ and Python solvers for Nim (Sprague–Grundy / Nim-sum), Chomp, Pawnscape, and a "greedy or not" puzzle. None of that code ships with the engine, but the framing — minimax, zero-sum, value of a position — carries directly into what comes next.

How it picks a move

The engine runs iterative deepening with alpha-beta pruning, escalating from depth 1 up to a hard cap of 32. Time budget is 15 seconds for the first move out of the opening book and 8 seconds thereafter — so the engine always returns a legal move even if the deeper search gets cut off.

Move ordering is the single biggest determinant of how much pruning the alpha-beta actually does. The engine orders candidate moves in tiers:

Evaluation

The evaluator combines weighted material (Pawn 100, Knight 300, Bishop 320, Rook 500, Queen 900) with piece-square tables that reward good squares per piece type. The interesting part is the king: two separate tables, one for the middlegame (defensive, tucked away from the centre) and one for the endgame (aggressive, marching to the centre). The endgame detection switches between them based on remaining material — a small detail that changes how the engine plays late in a game.

Transposition table

Positions are hashed with Zobrist keys. Each TT entry stores the best move, score, search depth, and a flag — exact, lower-bound, or upper-bound — that records what kind of cutoff produced it. Subsequent searches that reach the same position re-use the entry to prune entire subtrees and to seed move ordering with the hash move.

Opening book

For the first few dozen moves the engine reads from Titans.bin, a Polyglot binary book — 12 bytes per entry: Zobrist key, encoded move, weight, learn data. The engine decodes the key, binary-searches the book, and plays the move if a match exists. This avoids spending search time on positions that already have a well-known theoretical answer.

Draws and dead positions

Repetition (board.isRepetition(2)), the 50-move rule (board.isHalfMoveDraw()), and insufficient-material detection all return a score of 0, nudging the engine away from positions that are technically drawn — which matters because evaluating a "drawn but ahead in material" position naively would otherwise look like a win.

What I'd carry forward

The biggest takeaway was how much engineering effort goes into not searching — opening books, transposition reuse, move ordering, draw detection. The actual alpha-beta core is short; everything around it is what makes it playable in real time.

Source & build instructions Engine, opening book, and FEN support
View on GitHub