A homemade UCI chess engine written from scratch in C++.
Challenge DeepBlunder to a game on Lichess!
Most of the inspiration and knowledge of how to build a chess engine came from BlueFever Software's series of videos on YouTube: Chess engine in C. The basic structure of this engine follows the structure of his engine Vice.
Additionally:
Pre-built binaries for windows and linux can be found on the releases page.
On windows, you can open DeepBlunder.sln with Visual Studio.
Alternatively, this engine can be built with the Makefile:
Linux:
git clone https://github.com/boettcherb/DeepBlunder-Chess-Engine.git
cd DeepBlunder-Chess-Engine/src
make releaseWindows:
git clone https://github.com/boettcherb/DeepBlunder-Chess-Engine.git
cd DeepBlunder-Chess-Engine/src
mingw32-make releaseExample starting a search from the initial chess position after running the engine:
./deepblunder
uci
isready
position startpos
go depth 8This engine uses the UCI Protocol to communicate with users and GUIs. The UCI protocol involves a back-and-forth communication through standard input and output.
- First, the user sends
ucito the engine to let the engine know to use the UCI protocol. The engine respondsuciok. - Next, the user asks
isreadyand the engine respondsreadyokonce it is fully initialized. - Next, the user sends a board position to the engine so the engine knows which position to search:
position startpos [moves ...]. For example, if the user is white and they play pawn to e4, they will sendposition startpos moves e2e4to the engine. The engine will then search that position (after receiving agocommand) and return a move as black (say, pawn to e5). Then the user will make another move (say, knight to f3), and they will send this position to the engine withposition startpos moves e2e4 e7e5 g1f3. Most GUIs use this method for the entire game. Alternatively, the user can send a specific position to the engine withposition fen <FEN string> [moves ...]. The engine will not print anything in response to the position command. - After sending a position to the engine, the user must tell the engine to start searching that position with the
gocommand. There are many options for this command, but the simplest arego depth <max depth>to search to a specific depth, andgo movetime <time in ms>to search for a specific amount of time. For GUIs, it is most common to send the time remaining and the increment for both sides. For example, if both sides have 3 minutes on the clock and there is a 2 second increment:go wtime 180000 btime 180000 winc 2000 binc 2000. The engine will respond withbestmove <move>once it is done searching. - Repeat steps 3 and 4 for the entire game.
Instead of using the console and having to send the position every move, you can instead download a GUI to automate the process (and so you can actually see the board).
Here are some of the most popular (free) ones:
Currently, the UCI protocol is the only protocol this engine knows. If the first command given after running the program is not uci, the engine will default to running its perft tests. Perft tests (performance tests) are used to debug the engine's move generation by counting the number of positions reachable from a starting position (up to a certain depth). To run the perft tests, simply enter an integer for the max depth to search to. 3 is recommended for the debug build and 5 is recommended for the release build.
The strength of chess players and engines is measured in ELO.
| Version | Bullet (lichess) | Blitz (lichess) | Rapid (lichess) |
|---|---|---|---|
| v1.1.8 | 1991 | 1869 | 2009 |
| v1.0.5 | 1965 | 1879 | 1982 |
- Magic bitboards
- Zobrist Hashing
- Alpha-Beta pruning (Negamax algorithm)
- Iterative Deepening
- Quiescence Search
- Transposition Table
- Move Ordering:
- Hand-crafted evaluation function: Material counts, Piece-Square tables, pawn structure, king safety, piece activity, piece mobility, center control, etc.
My plans to improve this engine in the future:
- Add more pruning methods. There are many more pruning techniques than just Alpha-Beta pruning, such as null move pruning, late move reduction (LMR), aspiration windows, futility pruning, etc.
- Add an Opening Book (such as PolyGlot) to save clock time at the beginning of the game and to ensure a decent position.
- Add an Endgame Database/Tablebase (such as Syzygy). Currently, DeepBlunder cannot find a win in a King+Queen vs King game, and instead draws by the 50 move rule. An endgame database will ensure best play in endgames where a checkmate is too deep in the search tree to find with a normal search.
- Test out different Transposition table replacement strategies. Currently the engine uses the 'always replace' strategy because it is the simplest.
- Implement multithreading to speed up the search function (Lazy SMP).
- Improve time management. Possible Ideas:
- If there is only one move that doesn't cause a massive disadvantage, then play it instantly.
- If there is no more time to complete another depth in iterative deepening, then stop the search.
- Add UCI more options, such as number of threads and pondering.
- Move from hand-crafted evaluation to NNUE.
