Rook
Rook is my chess engine and service — a backend for playing chess against another person or a built-in AI, and for building a searchable archive of games as they’re played.
I wanted a chess service I controlled end to end: game state, move legality, and an opponent, all on my own infrastructure rather than an embedded third-party widget. The rules are dependency-free and well-tested — legal move generation, checkmate and stalemate, the fifty-move rule, threefold repetition, PGN parsing and SAN. The AI is a small embedded search engine (Sunfish, ported to Go) rather than a call to a heavyweight external one, tunable per game. Finished games are archived automatically as PGN, and imported corpora are indexed by position so openings and historical stats can be looked up by FEN.
Rook is my chess game engine and service — a backend for playing chess against another person or against a built-in AI opponent, and for building up a searchable archive of games as they’re played.
Why I Built It
I wanted a chess service I actually controlled end to end: game state, move legality, and an AI opponent, all running on my own infrastructure instead of embedding someone else’s chess widget. It’s also a good excuse to work through the parts of chess that are more fiddly than they look — legal move generation, checkmate and stalemate detection, the fifty-move rule, threefold repetition, PGN parsing and SAN generation — and to keep that logic dependency-free and well-tested rather than reaching for an existing chess library.
The AI opponent is intentionally simple: a small embedded search engine (Sunfish, ported to Go) rather than a call out to a heavyweight external engine. It’s enough to give a human player a real opponent while keeping the whole service self-contained.
What It Does
- Manages chess games — create a game between two human players, a human and an AI, or (in principle) two AI players, and track it through to a result.
- Records moves as they’re played, validating nothing on the way in but evaluating the resulting position after each move to detect checkmate, stalemate, the fifty-move rule, and threefold repetition, and updating the game’s status accordingly.
- Runs an embedded chess engine (a Go port of Sunfish) to compute the next move for an AI-controlled side, tunable per game via a difficulty/max-search-nodes setting.
- Archives finished live games automatically as PGN-backed historical games, so they’re replayable later without ever having gone through PGN import.
- Imports external PGN corpora, indexing every position reached along the way so openings and historical statistics can be looked up by FEN.
- Looks up openings (ECO code, name, variation) for a given position, and reports move statistics (games played, results) seen from that position across the archive.
- Searches the historical game archive by player, ECO code, result, or year, and replays any archived game move by move.
Tech Stack
- Backend: Go, ConnectRPC, PostgreSQL
- Chess engine: an embedded, dependency-free legal-move/check/checkmate evaluator, plus a Go port of the Sunfish search engine for AI opponents
- Migrations: goose
For a deeper look at how the pieces fit together, see Architecture.