Major rework: add search timer, evaluation improvements, more

ADDITIONAL CHANGES

Alpha-Beta search
* Change back to recursive alphabeta with implicit stack
* Add atomic_bool parameter that stops search when set to false
* Update tests accordingly

Evaluation
* Fix bug where black pawns are using white's positional modifier bonus

Makefile
* Add -march=native to clang release builds
This commit is contained in:
2025-12-26 16:18:09 +01:00
parent e5b6886f40
commit 160ea82549
5 changed files with 600 additions and 428 deletions

View File

@@ -2,6 +2,52 @@
#pragma once
enum game_progress {
GP_OPENING,
GP_MIDDLE,
GP_END,
GP_COUNT,
};
static char const* game_progress_str[GP_COUNT] = {
[GP_OPENING] = "GP_OPENING",
[GP_MIDDLE] = "GP_MIDDLE",
[GP_END] = "GP_END",
};
static enum game_progress endgameness(struct pos const* pos)
{
/* piece_value is already defined similarly elsewhere, but this one should
* remain independent of minor tweaks in the global table */
static int const piece_value[PIECE_COUNT] = {
[PIECE_KING] = 0,
[PIECE_PAWN] = 1,
[PIECE_BISHOP] = 3,
[PIECE_KNIGHT] = 3,
[PIECE_ROOK] = 5,
[PIECE_QUEEN] = 9,
};
int npm = 0; /* non-pawn material */
for (enum player pl = PLAYER_BEGIN; pl < PLAYER_COUNT; ++pl) {
npm += piece_value[PIECE_QUEEN] * bitboard_popcount(pos->pieces[pl][PIECE_QUEEN]);
npm += piece_value[PIECE_BISHOP] * bitboard_popcount(pos->pieces[pl][PIECE_BISHOP]);
npm += piece_value[PIECE_KNIGHT] * bitboard_popcount(pos->pieces[pl][PIECE_KNIGHT]);
npm += piece_value[PIECE_ROOK] * bitboard_popcount(pos->pieces[pl][PIECE_ROOK]);
}
/**/ if (npm > 24) {
return GP_OPENING;
}
else if (npm > 14) {
return GP_MIDDLE;
}
else {
return GP_END;
}
}
#define BITBOARD_WHITE( \
a8,b8,c8,d8,e8,f8,g8,h8, \
a7,b7,c7,d7,e7,f7,g7,h7, \
@@ -44,8 +90,97 @@ h7##g7##f7##e7##d7##c7##b7##a7##\
h8##g8##f8##e8##d8##c8##b8##a8##\
ULL
#define RELATIVE_DIAGONAL_A1_H8 \
RELATIVE_BITBOARD( \
#define REL_RANK_1 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1)
#define REL_RANK_2 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0)
#define REL_RANK_3 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_RANK_4 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_RANK_5 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_RANK_6 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_RANK_7 \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_RANK_8 \
REL_BITBOARD( \
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0)
#define REL_DIAGONAL_A1_H8 \
REL_BITBOARD( \
0,0,0,0,0,0,0,1, \
0,0,0,0,0,0,1,0, \
0,0,0,0,0,1,0,0, \
@@ -55,8 +190,8 @@ ULL
0,1,0,0,0,0,0,0, \
1,0,0,0,0,0,0,0)
#define RELATIVE_DIAGONAL_A8_H1 \
RELATIVE_BITBOARD( \
#define REL_DIAGONAL_A8_H1 \
REL_BITBOARD( \
1,0,0,0,0,0,0,0, \
0,1,0,0,0,0,0,0, \
0,0,1,0,0,0,0,0, \
@@ -66,8 +201,8 @@ ULL
0,0,0,0,0,0,1,0, \
0,0,0,0,0,0,0,1)
#define RELATIVE_BISHOP_KING_ATTACK \
RELATIVE_BITBOARD( \
#define REL_BISHOP_KING_ATTACK \
REL_BITBOARD( \
0,0,0,0,0,0,1,1, \
0,0,0,0,0,1,1,0, \
0,0,0,0,1,1,0,0, \
@@ -77,8 +212,8 @@ ULL
1,1,0,0,0,0,0,0, \
1,0,0,0,0,0,0,0)
#define RELATIVE_BISHOP_QUEEN_ATTACK \
RELATIVE_BITBOARD( \
#define REL_BISHOP_QUEEN_ATTACK \
REL_BITBOARD( \
1,1,0,0,0,0,0,0, \
0,1,1,0,0,0,0,0, \
0,0,1,1,0,0,0,0, \
@@ -88,8 +223,8 @@ ULL
0,0,0,0,0,0,1,1, \
0,0,0,0,0,0,0,1)
#define RELATIVE_KING_CASTLE_KINGSIDE \
RELATIVE_BITBOARD( \
#define REL_KING_CASTLE_KINGSIDE \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
@@ -99,8 +234,8 @@ ULL
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,1,0)
#define RELATIVE_KING_CASTLE_QUEENSIDE \
RELATIVE_BITBOARD( \
#define REL_KING_CASTLE_QUEENSIDE \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
@@ -121,8 +256,8 @@ ULL
0,0,0,0,0,0,0,0, \
1,0,0,0,0,0,0,1)
#define RELATIVE_PAWN_SAFE_ZONE \
BITBOARD( \
#define REL_EARLY_PAWN_STRUCTURE \
REL_BITBOARD( \
0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0, \
1,0,0,0,0,0,0,0, \
@@ -132,6 +267,10 @@ ULL
1,1,1,1,1,1,1,1, \
0,0,0,0,0,0,0,0)
#define BOARD_CENTER_6X6 \
((FILE_MASK_B | FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F | FILE_MASK_G) \
& (RANK_MASK_2 | RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6 | RANK_MASK_7))
#define BOARD_CENTER_4X4 \
((FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F) \
& (RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6))
@@ -140,63 +279,163 @@ ULL
((FILE_MASK_D | FILE_MASK_E) \
& (RANK_MASK_4 | RANK_MASK_5))
#define POSITIONAL_MODIFIER_COUNT 4
#define POSITIONAL_BONUS_0 \
/* ------------------------------ early game ------------------------------- */
#define EARLY_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
X(PIECE_KNIGHT, 0.05, BOARD_CENTER_4X4) \
X(PIECE_BISHOP, 0.05, RELATIVE_DIAGONAL_A1_H8 | RELATIVE_DIAGONAL_A8_H1) \
X(PIECE_KING, 0.15, RELATIVE_KING_CASTLE_KINGSIDE) \
X(PIECE_BISHOP, 0.05, REL_DIAGONAL_A1_H8 | REL_DIAGONAL_A8_H1) \
X(PIECE_KING, 0.15, REL_KING_CASTLE_KINGSIDE) \
X(PIECE_QUEEN, -0.10, RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6) \
/**/
#define POSITIONAL_BONUS_1 \
#define EARLY_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_2X2) \
X(PIECE_BISHOP, 0.05, RELATIVE_BISHOP_KING_ATTACK) \
X(PIECE_BISHOP, 0.05, REL_BISHOP_KING_ATTACK) \
/**/
#define POSITIONAL_BONUS_2 \
#define EARLY_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, -0.10, ~RELATIVE_PAWN_SAFE_ZONE) \
X(PIECE_PAWN, -0.10, ~REL_EARLY_PAWN_STRUCTURE) \
X(PIECE_BISHOP, 0.05, CORNERS) \
/**/
#define POSITIONAL_BONUS_3 \
#define EARLY_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \
X(PIECE_BISHOP, 0.02, RELATIVE_BISHOP_QUEEN_ATTACK)
X(PIECE_BISHOP, 0.02, REL_BISHOP_QUEEN_ATTACK)
#define POSITIONAL_MODIFIER_COUNT 4
static struct {bitboard const area; double const val;} const
positional_modifier[PLAYER_COUNT][POSITIONAL_MODIFIER_COUNT][PIECE_COUNT] = {
#define X(p, b, a) [p] = {.area = (a), .val = b},
#define RELATIVE_BITBOARD BITBOARD_WHITE
[PLAYER_WHITE] = {
{POSITIONAL_BONUS_0},
{POSITIONAL_BONUS_1},
{POSITIONAL_BONUS_2},
{POSITIONAL_BONUS_3},
},
[PLAYER_BLACK] = {
#undef RELATIVE_BITBOARD
#define RELATIVE_BITBOARD BITBOARD_BLACK
{POSITIONAL_BONUS_0},
{POSITIONAL_BONUS_1},
{POSITIONAL_BONUS_2},
{POSITIONAL_BONUS_3},
}
#undef X
/* ------------------------------ middle game ------------------------------ */
/* (almost same as opening for now, but queen is not punished for moving) */
#define MIDDLE_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
X(PIECE_KNIGHT, 0.05, BOARD_CENTER_4X4) \
X(PIECE_BISHOP, 0.05, REL_DIAGONAL_A1_H8 | REL_DIAGONAL_A8_H1) \
X(PIECE_KING, 0.15, REL_KING_CASTLE_KINGSIDE) \
/**/
#define MIDDLE_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_2X2) \
X(PIECE_BISHOP, 0.05, REL_BISHOP_KING_ATTACK) \
/**/
#define MIDDLE_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, -0.10, ~REL_EARLY_PAWN_STRUCTURE) \
X(PIECE_BISHOP, 0.05, CORNERS) \
/**/
#define MIDDLE_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \
X(PIECE_BISHOP, 0.02, REL_BISHOP_QUEEN_ATTACK) \
/**/
/* ------------------------------- end game -------------------------------- */
#define LATE_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.30, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \
X(PIECE_KING, 0.10, BOARD_CENTER_6X6) \
/**/
#define LATE_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.30, REL_RANK_7 | REL_RANK_6) \
X(PIECE_KING, 0.10, BOARD_CENTER_4X4) \
/**/
#define LATE_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, 0.70, REL_RANK_7) \
X(PIECE_KING, 0.10, BOARD_CENTER_2X2) \
/**/
#define LATE_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \
X(PIECE_KING, -0.50, ~BOARD_CENTER_6X6) \
/**/
struct posmod {
bitboard const area;
double const val;
};
static inline struct posmod positional_modifier(enum player pl, enum game_progress st, size_t layer, enum piece pz)
{
static struct posmod const
lookup[PLAYER_COUNT][GP_COUNT][POSITIONAL_MODIFIER_COUNT][PIECE_COUNT] = {
#define X(p, b, a) [p] = {.area = (a), .val = b},
#define REL_BITBOARD BITBOARD_WHITE
[PLAYER_WHITE] = {
[GP_OPENING] = {
{EARLY_POSITIONAL_BONUS_0},
{EARLY_POSITIONAL_BONUS_1},
{EARLY_POSITIONAL_BONUS_2},
{EARLY_POSITIONAL_BONUS_3},
},
[GP_MIDDLE] = {
{MIDDLE_POSITIONAL_BONUS_0},
{MIDDLE_POSITIONAL_BONUS_1},
{MIDDLE_POSITIONAL_BONUS_2},
{MIDDLE_POSITIONAL_BONUS_3},
},
[GP_END] = {
{LATE_POSITIONAL_BONUS_0},
{LATE_POSITIONAL_BONUS_1},
{LATE_POSITIONAL_BONUS_2},
{LATE_POSITIONAL_BONUS_3},
},
},
#undef REL_BITBOARD
#define REL_BITBOARD BITBOARD_BLACK
[PLAYER_BLACK] = {
[GP_OPENING] = {
{EARLY_POSITIONAL_BONUS_0},
{EARLY_POSITIONAL_BONUS_1},
{EARLY_POSITIONAL_BONUS_2},
{EARLY_POSITIONAL_BONUS_3},
},
[GP_MIDDLE] = {
{MIDDLE_POSITIONAL_BONUS_0},
{MIDDLE_POSITIONAL_BONUS_1},
{MIDDLE_POSITIONAL_BONUS_2},
{MIDDLE_POSITIONAL_BONUS_3},
},
[GP_END] = {
{LATE_POSITIONAL_BONUS_0},
{LATE_POSITIONAL_BONUS_1},
{LATE_POSITIONAL_BONUS_2},
{LATE_POSITIONAL_BONUS_3},
},
}
#undef REL_BITBOARD
};
return lookup[pl][st][layer][pz];
}
/* ------------------------------------------------------------------------- */
#undef POSITIONAL_BONUS_0
#undef POSITIONAL_BONUS_1
#undef POSITIONAL_BONUS_2
#undef POSITIONAL_BONUS_3
#undef CORNERS
#undef BOARD_CENTER_4X4
#undef BOARD_CENTER_2X2
#undef BITBOARD_WHITE
#undef BITBOARD_BLACK
#undef RELATIVE_DIAGONAL_A1_H8
#undef RELATIVE_DIAGONAL_A8_H1
#undef RELATIVE_BISHOP_KING_ATTACK
#undef RELATIVE_BISHOP_QUEEN_ATTACK
#undef RELATIVE_KING_CASTLE_KINGSIDE
#undef RELATIVE_KING_CASTLE_QUEENSIDE
#undef REL_DIAGONAL_A1_H8
#undef REL_DIAGONAL_A8_H1
#undef REL_BISHOP_KING_ATTACK
#undef REL_BISHOP_QUEEN_ATTACK
#undef REL_KING_CASTLE_KINGSIDE
#undef REL_KING_CASTLE_QUEENSIDE