Quack-Mate – SwingBit – Swinging Bits, and a bit of Swing
2026-05-20
Quack-Mate
Pushing the Boundaries of Pure SQL Chess
Image generated with Google Gemini
I know what you’re thinking: SQL is a terrible language for a chess engine. And you’re right. It is inherently designed for set-based data retrieval, not for the highly branching, depth-first search of chess engines. My intention with Quack-Mate wasn’t to dethrone Stockfish, but to explore a single, slightly mad question: just how far can we push a modern analytical database to play chess?
TL;DR: Is it possible to build a playable chess engine in pure SQL? Though it’s not trivial, the answer is “yes, with conditions”. Quack-Mate explores the inevitable collision between the set-based execution models of database engines and the sequential, depth-first reasoning required for efficient chess.
A playable compromise between these two opposing forces requires trading off both the algorithmic superiority of traditional engines and the raw data-crunching throughput of modern analytical databases. The result is a functional engine that proves an ‘unsuitable’ paradigm can be stretched to perform—and a clear-eyed look at exactly why the divide between sets and trees runs so deep.
Here is the Quack-Mate user interface in action. You can play the WebAssembly (WASM) version online in your browser (not from your phone, sorry), or run the interface locally using the native Node.js DuckDB driver for a significant performance boost. As the engine thinks, the interface exposes the exact SQL queries executing under the hood in real-time, alongside checkboxes to toggle specific move ordering or lossy pruning heuristics and a complete breakdown of detailed search statistics:
Play Quack-Mate Online
GitHub Repository
If you have played a few games and explored the source code, you might wonder why anyone would choose to build a chess engine this way. For me, it began with a simple realisation: it seemed nobody had done it before—at least, not like this. While there have been a few attempts to implement chess in databases, they typically rely heavily on procedural extensions like Oracle’s PL/SQL or PostgreSQL’s PL/pgSQL (with explicit loops and variables), or they are written as C extensions. Implementing a fully functioning chess engine purely through relational algebra and standard SQL queries on a modern analytical engine (like the brilliant DuckDB) felt like uncharted territory.
Modern analytical engines like DuckDB are absolute beasts at crunching numbers in high volumes. Exploring the immense tree of chess possibilities immediately brings to mind joining a table of ‘boards’ with a table of ‘all possible moves’ to create a new generation of boards. If a pure SQL formulation is possible, you get the tremendous benefits of advanced database engines for free: brutal query optimisation and vectorised parallelisation over millions of rows.
But why is raw efficiency so heavily emphasised in chess programming? It comes down to a computational challenge known as combinatorial explosion. From the starting position, White has 20 possible moves, and Black has 20 responses, meaning there are 400 possible games after just one full turn. After three full turns, there are over 119 million possible games. After just four full turns (8 plies), the game tree explodes to roughly 85 billion possible games! To play well, an engine must look as far ahead into these exponentially growing branches as possible. Therefore, in chess engines, speed directly translates to search depth, and depth translates directly into playing strength.
Anatomy of a Chess Engine (and How SQL Handles It)
Before diving into the complex queries, it helps to understand the basic components of a traditional chess engine and how Quack-Mate translates them into the language of relational databases.
Board Representation & Bitboards
The absolute foundation of any engine is how it “sees” the game. A highly optimised state representation is critical because the engine will need to store, copy, and evaluate millions of these states per second during a deep search.
Before we talk about moving pieces, we have to talk about how the board is stored. A chess board has 64 squares. By a happy coincidence of computing history, modern CPU registers are standardly 64 bits wide.
Modern chess engines use Bitboards : 64-bit integers where each bit represents a square on the board (1 if occupied, 0 if empty). Instead of keeping one big array that says “Square E4 houses a White Pawn”, engines maintain separate bitboards for each piece type. You need 12 in total: White Pawns, White Knights, Black Pawns… all the way to Black Kings.
Using Bitboards means answering chess questions becomes highly efficient bitwise math.
All the White Pawns are identified by a specific bitboard:
wP_bb
Where are all the white pieces?
w_bb = wP_bb | wN_bb | ... | wK_bb
Is that square empty?
((w_bb | b_bb) & square_mask) == 0
Want to flip...