chess
Back in April, I came up with a scheme for move flags in one of my chess engines. This scheme has since been adopted by other engines, such as Clockwork, Heimdall and Cherry. I am writing this blog post to document my rationale for the design of this scheme.
We want moves in a chess engine to have a small representation. One reason for this is because we want to minimize the size of entries in the transposition table; this entry size is 10 bytes in most chess engines. A common size for a move representation is 16 bits.
There is some amount of minimum required information we need to put into those 16 bits. These are:
Squares are generally represented by an integer from 0 to 63 (6 bits). There are four possible pieces a pawn can promote to (knight, bishop, rook, queen), and having an additional value for "not a promotion" would mean this field requires 3 bits. So, a simple implementation would use 15 bits for the move representation.
There is some remaining space we can use for additional information. Some additional information we would want to look up about a move would include:
By encoding this information in the move, we avoid the need to have to look up this information.
By switching on move flags with more information, we also reduces the amount of branching we need to do in our makemove function.
If we use the obvious representation of 12 bits for source/destination squares, You can use less than 12 bits for encoding this, but encoding/decoding gets expensive. this leaves us 4 bits for move flags. This is enough to encode most of the above except for the piece type.
| Capture | Promo | ||
| 0 | 0 | 00 | Normal quiet move |
| 01 | Double pawn push | ||
| 10 | Queen-side castle | ||
| 11 | King-side castle | ||
| 1 | 00 | Promote to knight (quiet) | |
| 01 | Promote to bishop (quiet) | ||
| 10 | Promote to rook (quiet) | ||
| 11 | Promote to queen (quiet) | ||
| 1 | 0 | 00 | Normal capture |
| 01 | En passant | ||
| 10 | Unused | ||
| 11 | Unused | ||
| 1 | 00 | Capture and promote to knight | |
| 01 | Capture and promote to bishop | ||
| 10 | Capture and promote to rook | ||
| 11 | Capture and promote to queen |
“Make common operations fast”. Detecting capture or promotion is a bit test. Piece types were ordered NBRQ because this is the common existing relative ordering for piece types.
Example implementation of utility functions:
bool is_capture() const { return value & 0x8000; }
bool is_promo() const { return value & 0x4000; }
PieceType promo() const { return ((value & 0x3000) >> 12) + PieceType::knight; }
bool is_en_passant() const { return (value & 0xF000) == MoveFlags::en_passant; }
bool is_castle() const { return (value & 0xE000) == 0x2000; }