Data Structures
This guide describes the core data structures used within the FollyChess implementation. It is intended for developers interested in the engine’s internals. We assume familiarity with C++.
Bitboards
A key requirement for any chess engine is an efficient way to track square occupancy. FollyChess uses bitboards: unsigned 64-bit integers where each bit maps to one of the 64 chess squares.
FollyChess maps bits to squares by following a top-to-bottom, left-to-right
progression, which matches the typical visual representation of a chessboard.
The A8 square corresponds to the least
significant bit (LSB), and H1 to the most significant bit (MSB).
8: 0 1 2 3 4 5 6 7
7: 8 9 10 11 12 13 14 15
6: 16 17 18 19 20 21 22 23
5: 24 25 26 27 28 29 30 31
4: 32 33 34 35 36 37 38 39
3: 40 41 42 43 44 45 46 47
2: 48 49 50 51 52 53 54 55
1: 56 57 58 59 60 61 62 63
a b c d e f g hExamples
1ULL: Represents only theA8square.3ULL(0b11): Represents theA8andB8squares.1ULL << 63: Represents only theH1square.
The Bitboard Class
The Bitboard
class encapsulates the underlying unsigned 64-bit integer and provides a clean
API for bitwise manipulation: By using constexpr, we ensure many board
calculations are inlined by the compiler.
class Bitboard {
public:
constexpr explicit Bitboard(std::uint64_t data) : data_(data) {}
constexpr Bitboard operator&(const Bitboard &other) const {
return Bitboard(data_ & other.data_);
}
constexpr Bitboard operator&(Square square) const {
return Bitboard(data_ & (1ULL << square));
}
constexpr Bitboard operator<<(int bits) const {
return Bitboard(data_ << bits);
}
constexpr Bitboard operator>>(int bits) const {
return Bitboard(data_ >> bits);
}
// ...
private:
std::uint64_t data_;
};The Square Enum
The Square enum provides a named constant for every square, making the code
more readable:
enum Square : std::uint8_t {
A8, B8, C8, D8, E8, F8, G8, H8,
A7, B7, C7, D7, E7, F7, G7, H7,
A6, B6, C6, D6, E6, F6, G6, H6,
A5, B5, C5, D5, E5, F5, G5, H5,
A4, B4, C4, D4, E4, F4, G4, H4,
A3, B3, C3, D3, E3, F3, G3, H3,
A2, B2, C2, D2, E2, F2, G2, H2,
A1, B1, C1, D1, E1, F1, G1, H1,
};The Bitboard class provides facilities for working with squares:
class Bitboard {
public:
// Initializes a bitboard with a single square set.
constexpr explicit Bitboard(Square square) : data_(1ULL << square) {}
// Gets the square of the least significant bit (LSB).
//
// Precondition: The Bitboard must not be empty.
[[nodiscard]] constexpr Square LeastSignificantBit() const {
if !consteval {
DCHECK(data_ != 0ULL);
}
return static_cast<Square>(std::countr_zero(data_));
}
// Finds the least significant bit (LSB), clears it from the board,
// and returns its corresponding square.
//
// Precondition: The Bitboard must not be empty.
constexpr Square PopLeastSignificantBit() {
Square square = LeastSignificantBit();
data_ &= data_ - 1;
return square;
}
// ...
};Example:
Bitboard pieces = ...
while (pieces) {
Square from = pieces.PopLeastSignificantBit();
Bitboard attacks =
GenerateAttacks<Piece>(from, position.GetPieces()) & targets;
// ...
}The Position Class
TODO!