Architecture¶
Overview¶
Kirin uses a bitboard-based architecture optimized for modern 64-bit processors. The engine is structured around several core components: board representation, move generation, position evaluation, and tree search.
Board Representation¶
Bitboard Data Structures¶
The board is represented using 12 bitboards (one per piece type and color) plus 3 occupancy bitboards:
// Define Bitboard Type
#define U64 unsigned long long
// Piece bitboards: P, N, B, R, Q, K, p, n, b, r, q, k
U64 bitboards[12];
// Occupancy bitboards: white, black, both
U64 occupancy[3];
// Game state
int side; // Side to move (white=0, black=1)
int enpassant; // En passant target square
int castle; // Castling rights (4 bits)
U64 hashKey; // Zobrist hash of position
Square Encoding¶
Squares are encoded as integers 0-63, with a8=0 and h1=63:
enum {
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, no_sq
};
Piece Encoding¶
Pieces are encoded as integers 0-11:
enum { P, N, B, R, Q, K, p, n, b, r, q, k };
Castling Rights¶
Castling rights are stored as a 4-bit value:
// Binary encoding:
// 0001 (1) = white king-side
// 0010 (2) = white queen-side
// 0100 (4) = black king-side
// 1000 (8) = black queen-side
enum { wk = 1, wq = 2, bk = 4, bq = 8 };
Bit Manipulation Macros¶
#define setBit(bitboard, square) ((bitboard) |= (1ULL << (square)))
#define getBit(bitboard, square) ((bitboard) & (1ULL << (square)))
#define popBit(bitboard, square) ((bitboard) &= ~(1ULL << (square)))
Chess Position Example¶
Here is an example starting position:
After 1.e4 e5 2.Nf3:
Move Generation¶
Move Encoding¶
Moves are encoded as 24-bit integers containing all move information:
// Binary move representation:
// 0000 0000 0000 0000 0011 1111 source square 0x3f
// 0000 0000 0000 1111 1100 0000 target square 0xfc0
// 0000 0000 1111 0000 0000 0000 piece 0xf000
// 0000 1111 0000 0000 0000 0000 promoted piece 0xf0000
// 0001 0000 0000 0000 0000 0000 capture flag 0x100000
// 0010 0000 0000 0000 0000 0000 double pawn push 0x200000
// 0100 0000 0000 0000 0000 0000 en passant flag 0x400000
// 1000 0000 0000 0000 0000 0000 castling flag 0x800000
#define encodeMove(source, target, piece, promoted, capture, dpp, enpassant, castle) \
(source) | (target << 6) | (piece << 12) | (promoted << 16) | \
(capture << 20) | (dpp << 21) | (enpassant << 22) | (castle << 23)
Attack Tables¶
Leaper pieces (pawns, knights, kings) use pre-computed attack tables:
U64 pawn_attacks[2][64]; // [side][square]
U64 knightAttacks[64];
U64 kingAttacks[64];
Magic Bitboards¶
Sliding pieces (bishops, rooks, queens) use magic bitboard technique for O(1) attack generation:
U64 bishopMasks[64];
U64 rookMasks[64];
U64 bishopAttacks[64][512]; // 32KB
U64 rookAttacks[64][4096]; // 256KB
U64 bishopMagicNums[64];
U64 rookMagicNums[64];
static inline U64 getBishopAttacks(int square, U64 occupancy) {
occupancy &= bishopMasks[square];
occupancy *= bishopMagicNums[square];
occupancy >>= 64 - bishopRelevantBits[square];
return bishopAttacks[square][occupancy];
}
Legal Move Generation¶
moves $\gets$ empty list bitboard $\gets$ pieces of this type square $\gets$ get least significant bit index Generate pseudo-legal moves for piece at square Make move on board Add move to list Restore board state Remove bit from bitboard moves