Calvin is a chess engine written in Java.
It features a a traditional alpha-beta search algorithm paired with an NNUE evaluation function.
The NNUE neural network was trained using bullet on a dataset of 250 million positions taken from the Leela Chess Zero dataset, that I re-scored using Calvin's own search and HCE. The network architecture is (768->256)x2->1.
Calvin is rated roughly 3000 elo (~150th place) on the Computer Chess Rating Lists leaderboards, and is currently playing on Lichess.
My aim with this project was to combine my passion (playing mediocre chess) with my profession (writing mediocre code). My secondary goal was to learn about chess programming. I have certainly learned a great deal, and I hope that my code is well-documented so that first-time readers can learn too. If you find some information is missing or poorly explained, don't hesitate to let me know!
Like most engines, Calvin does not implement its own user interface. Instead, it communicates using the UCI protocol, meaning it can either be used directly from the command line, or via any popular chess GUI, such as Arena Chess, Banksia or Cute Chess.
To run Calvin locally, you will need Java (minimum Java 17) installed on your machine. The binary calvin.jar can be downloaded from the Releases section. Start up Calvin by executing the command:
java --add-modules jdk.incubator.vector -jar calvin-chess-engine-4.0.1.jar
Please note the '--add-modules jdk.incubator.vector' - Calvin uses the incubator Vector API for SIMD operations during NNUE inference, and this module needs to enabled explicitly.
From there, use the "help" option or refer to UCI documentation for further information on available commands.
The table below tracks the strength of previous Calvin releases, both on the CCRL leaderboards and on Lichess.
Version | Release date | Lichess | CCRL Blitz | CCRL Rapid |
---|---|---|---|---|
4.0.0 | 2024-05-19 | ~2700 | 3011 | 3029 |
3.4.0 | 2024-05-19 | ~2580 | - | 2492 |
3.3.0 | 2024-05-10 | ~2550 | 2453 | - |
3.2.0 | 2023-12-09 | ~2400 | 2233 | - |
3.1.0 | 2023-12-05 | ~2390 | - | - |
3.0.0 | 2023-12-02 | ~2380 | - | - |
2.6.2 | 2023-11-12 | ~2300 | 2173 | - |
- Bitboards - Calvin uses bitboards to keep track of the state of the board. Bitboards are up-to-64 bit integers which represent different features of the board, such as the positions of the pieces for each colour, legal moves, attacked/defended squares, and so on.
- Move generation - Move generation algorithms can be divided into roughly two camps: legal and pseudo-legal. Pseudo-legal move generators generate all moves regardless of whether they leave the king in check, and then only later check for legality. Legal move generators take into account the position of the king - and pieces pinned to the king - and don't generate any illegal moves. Calvin uses the latter approach.
- Sliding pieces - Generating moves for the sliding pieces (bishops, rooks & queens) is notoriously the most computationally-expensive part of any move generation algorithm. Calvin uses Magic Bitboards for sliding piece move generation.
- Alpha-Beta - Calvin uses a classical alpha-beta minimax search algorithm to traverse the game tree. This is enhanced by Principal Variation Search, combined with an Iterative Deepening depth-first approach to managing time, and finally a Quiescence Search at the tips of the tree to filter out noisy/tactical positions.
- Transposition table - A transposition table is an in-memory hashtable recording information of all the previously visited positions in the search, which helps drastically cut down on the search space, since the searcher will encounter the same positions from multiple different move orders. Zobrist hashing is used to create the hash index.
- Parallel Search - Lazy SMP is implemented for multi-threaded parallel search.
- Pruning - Calvin uses multiple pruning techniques to cut down on the search space, including Null-Move Pruning, Futility Pruning, Reverse Futility Pruning, Late Move Pruning and Delta Pruning
- Search Extensions - Calvin uses the popular Check Extension to extend the search when in check, as well as an extension when trading into a pawn endgame (to avoid potentially trading into a drawn/lost ending).
- Search Reductions - Calvin features Late Move Reductions for reducing search depth for moves ordered late in the list.
- Captures are ordered using the MVV-LVA (Most-Valuable-Victim, Least-Valuable-Attacker) heuristic.
- Non-captures are ordered using the Killer move and History heuristics.
- Simple opening book loaded from a .txt file on startup. Can be disabled using the 'OwnBook' UCI option.
- Calvin can probe the Lichess Tablebase API for endgames of 7 men or fewer. Can be disabled using the 'OwnTablebase' UCI option.
- Calvin communicates using the Universal Chess Interface (UCI) protocol.
- Pondering, where the engine thinks on the opponent's move. Can be disabled using the 'Ponder' UCI option.
- Hash size and number of Lazy SMP threads are also configurable via the UCI.
- Calvin is connected to Lichess where he plays regularly in the engine pool: https://lichess.org/@/Calvin_Bot
- The Chess Programming Wiki - A brilliant resource for all chess engine programmers, this wiki has been my go-to reference for every new topic.
- The kind folks in the Engine Programming Discord server, who were very helpful for answering my various questions related to NNUE implementation.
- The TalkChess forums - The home for chess engine geeks to talk about geeky chess engine stuff.
- Other engines - I have drawn inspiration from countless others' engines, including but not limited to: Chess Coding Adventure (whose Youtube video inspired me to write my own engine); Stockfish (the queen of all engines); Leorik (whose author keeps an excellent devlog on the TalkChess forum); Lynx (my frequent Lichess rival); Rustic, Simbelyne and Mantissa (who taught me that Rust is Cool); and many others.
If you would like to contribute, or just talk about chess/chess programming, get in touch!