From cc70bcdf08974e79c7d319008d85ebe528b32f94 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 30 Aug 2025 15:46:50 -0400 Subject: [PATCH] Add castling bonus --- engine/src/ai/negamax.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/engine/src/ai/negamax.c b/engine/src/ai/negamax.c index 87fbe99..8ccb653 100644 --- a/engine/src/ai/negamax.c +++ b/engine/src/ai/negamax.c @@ -6,6 +6,16 @@ #define INF 30000 #define MATE 29000 +#define CASTLE_BONUS 100 +#define UNCASTLED_PENALTY 30 +#define PAWN_SHIELD_BONUS 15 + +const uint64_t W_CASTLED_MASK = BB(G1) | BB(C1); +const uint64_t B_CASTLED_MASK = BB(G8) | BB(C8); +const uint64_t W_KSIDE_SHIELD = BB(13) | BB(14) | BB(15); // f2 g2 h2 +const uint64_t W_QSIDE_SHIELD = BB(8) | BB(9) | BB(10); // a2 b2 c2 +const uint64_t B_KSIDE_SHIELD = BB(53) | BB(54) | BB(55); // f7 g7 h7 +const uint64_t B_QSIDE_SHIELD = BB(48) | BB(49) | BB(50); // a7 b7 c7 // P N B R Q K int VAL[6] = {100, 300, 300, 500, 900, 0}; @@ -20,7 +30,7 @@ int popcount64(uint64_t bits) { return count; } -static int eval(struct Board *board) { +int eval(struct Board *board) { // White minus Black material (centipawns) // Ignore the King's material. int score = 0; @@ -30,11 +40,34 @@ static int eval(struct Board *board) { score += VAL[3] * (popcount64(board->pieces[R]) - popcount64(board->pieces[r])); score += VAL[4] * (popcount64(board->pieces[Q]) - popcount64(board->pieces[q])); + // Castling Bonus + uint64_t white_king = board->pieces[K]; + uint64_t black_king = board->pieces[k]; + uint64_t white_pawns = board->pieces[P]; + uint64_t black_pawns = board->pieces[p]; + + if (white_king & W_CASTLED_MASK) { + score += CASTLE_BONUS; + uint64_t shield = (white_king & BB(G1)) ? W_KSIDE_SHIELD : W_QSIDE_SHIELD; + score += PAWN_SHIELD_BONUS * popcount64(white_pawns & shield); + } else if (white_king & BB(E1)) { + score -= UNCASTLED_PENALTY; + } + + // Black (subtract because score is White minus Black) + if (black_king & B_CASTLED_MASK) { + score -= CASTLE_BONUS; + uint64_t shield = (black_king & BB(G8)) ? B_KSIDE_SHIELD : B_QSIDE_SHIELD; + score -= PAWN_SHIELD_BONUS * popcount64(black_pawns & shield); + } else if (black_king & BB(E8)) { + score += UNCASTLED_PENALTY; + } + // Negamax convention: score from side-to-move POV return (board->side_to_move == WHITE) ? score : -score; } -static int search(struct Board *b, int depth, int alpha, int beta, int ply) { +int search(struct Board *b, int depth, int alpha, int beta, int ply) { if (depth == 0) return eval(b); struct Move moves[256];