From e1d1684c23c51205257e78ddf147849245cd9ae5 Mon Sep 17 00:00:00 2001 From: Ole Morud Date: Thu, 8 Jan 2026 18:44:07 +0100 Subject: [PATCH] Change evaluation score to a centi-pawn based integer type --- engine-evaluations.h | 66 +++++----- engine-move-generation.h | 25 ++-- engine-tt.h | 14 ++- engine-types.h | 26 ++-- engine.h | 253 +++++++++++++++++++++++---------------- tests.c | 4 +- 6 files changed, 218 insertions(+), 170 deletions(-) diff --git a/engine-evaluations.h b/engine-evaluations.h index 41c4b46..02f19c1 100644 --- a/engine-evaluations.h +++ b/engine-evaluations.h @@ -20,12 +20,12 @@ 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_KING] = 0, + [PIECE_PAWN] = 1, [PIECE_BISHOP] = 3, [PIECE_KNIGHT] = 3, - [PIECE_ROOK] = 5, - [PIECE_QUEEN] = 9, + [PIECE_ROOK] = 5, + [PIECE_QUEEN] = 9, }; int npm = 0; /* non-pawn material */ @@ -314,25 +314,25 @@ BITBOARD( \ #define EARLY_POSITIONAL_BONUS_0 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.02f, BOARD_CENTER_4X4) \ - X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_4X4) \ - X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \ - X(PIECE_KING, 0.15f, REL_KING_CASTLE_KINGSIDE) \ - X(PIECE_QUEEN, -0.15f, RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6) \ - X(PIECE_ROOK, 0.10f, FILE_MASK_D | FILE_MASK_E) \ + X(PIECE_PAWN, 2, BOARD_CENTER_4X4) \ + X(PIECE_KNIGHT, 5, BOARD_CENTER_4X4) \ + X(PIECE_BISHOP, 5, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \ + X(PIECE_KING, 15, REL_KING_CASTLE_KINGSIDE) \ + X(PIECE_QUEEN, -15, RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6) \ + X(PIECE_ROOK, 10, FILE_MASK_D | FILE_MASK_E) \ /**/ #define EARLY_POSITIONAL_BONUS_1 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.02f, BOARD_CENTER_2X2) \ - X(PIECE_BISHOP, 0.05f, REL_BISHOP_KING_ATTACK) \ + X(PIECE_PAWN, 2, BOARD_CENTER_2X2) \ + X(PIECE_BISHOP, 5, REL_BISHOP_KING_ATTACK) \ /**/ #define EARLY_POSITIONAL_BONUS_2 \ /* piece bonus area*/ \ - X(PIECE_PAWN, -0.18f, ~REL_EARLY_PAWN_STRUCTURE) \ - X(PIECE_KNIGHT, -0.10f, REL_UNDEVELOPED_KNIGHTS) \ - X(PIECE_BISHOP, -0.10f, REL_UNDEVELOPED_BISHOPS) \ + X(PIECE_PAWN, -18, ~REL_EARLY_PAWN_STRUCTURE) \ + X(PIECE_KNIGHT, -10, REL_UNDEVELOPED_KNIGHTS) \ + X(PIECE_BISHOP, -10, REL_UNDEVELOPED_BISHOPS) \ /**/ #define EARLY_POSITIONAL_BONUS_3 \ @@ -345,58 +345,58 @@ BITBOARD( \ #define MIDDLE_POSITIONAL_BONUS_0 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.02f, BOARD_CENTER_4X4) \ - X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_4X4) \ - X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \ - X(PIECE_KING, 0.15f, REL_KING_CASTLE_KINGSIDE) \ + X(PIECE_PAWN, 2, BOARD_CENTER_4X4) \ + X(PIECE_KNIGHT, 5, BOARD_CENTER_4X4) \ + X(PIECE_BISHOP, 5, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \ + X(PIECE_KING, 15, REL_KING_CASTLE_KINGSIDE) \ /**/ #define MIDDLE_POSITIONAL_BONUS_1 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.02f, BOARD_CENTER_2X2) \ - X(PIECE_BISHOP, 0.07f, REL_BISHOP_KING_ATTACK) \ - X(PIECE_QUEEN, 0.07f, REL_BISHOP_KING_ATTACK) \ + X(PIECE_PAWN, 2, BOARD_CENTER_2X2) \ + X(PIECE_BISHOP, 7, REL_BISHOP_KING_ATTACK) \ + X(PIECE_QUEEN, 7, REL_BISHOP_KING_ATTACK) \ /**/ #define MIDDLE_POSITIONAL_BONUS_2 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.02f, REL_PAWN_KINGSIDE) \ + X(PIECE_PAWN, 2, REL_PAWN_KINGSIDE) \ /**/ #define MIDDLE_POSITIONAL_BONUS_3 \ /* piece bonus area*/ \ - X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6) \ - X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_6X6) \ + X(PIECE_BISHOP, 5, BOARD_CENTER_6X6) \ + X(PIECE_KNIGHT, 5, BOARD_CENTER_6X6) \ /**/ /* ------------------------------- end game -------------------------------- */ #define LATE_POSITIONAL_BONUS_0 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \ - X(PIECE_KING, 0.10f, BOARD_CENTER_6X6) \ + X(PIECE_PAWN, 30, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \ + X(PIECE_KING, 10, BOARD_CENTER_6X6) \ /**/ #define LATE_POSITIONAL_BONUS_1 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6) \ - X(PIECE_KING, 0.10f, BOARD_CENTER_4X4) \ + X(PIECE_PAWN, 30, REL_RANK_7 | REL_RANK_6) \ + X(PIECE_KING, 10, BOARD_CENTER_4X4) \ /**/ #define LATE_POSITIONAL_BONUS_2 \ /* piece bonus area*/ \ - X(PIECE_PAWN, 0.70f, REL_RANK_7) \ - X(PIECE_KING, 0.10f, BOARD_CENTER_2X2) \ + X(PIECE_PAWN, 70, REL_RANK_7) \ + X(PIECE_KING, 10, BOARD_CENTER_2X2) \ /**/ #define LATE_POSITIONAL_BONUS_3 \ /* piece bonus area*/ \ - X(PIECE_KING, -0.50f, ~BOARD_CENTER_6X6) \ + X(PIECE_KING, -50, ~BOARD_CENTER_6X6) \ /**/ struct posmod { Bb64 const area; - float const val; + Score16 const val; }; static inline struct posmod positional_modifier(Side8 pl, enum game_progress st, size_t layer, Piece8 pz) diff --git a/engine-move-generation.h b/engine-move-generation.h index c29dff8..ac1ee36 100644 --- a/engine-move-generation.h +++ b/engine-move-generation.h @@ -1,5 +1,7 @@ #pragma once +#define MOVE_MAX 128 + #define MOVE_CASTLE_KINGSIDE_WHITE (struct move) \ {.from = SQ_E1, .to = SQ_G1} @@ -13,19 +15,10 @@ {.from = SQ_E8, .to = SQ_C8} static inline -struct move move_make(struct pos const* restrict pos, - Piece8 piece, - Index8 from, - Index8 to, - uint8_t add_attr) +struct move move_make(Index8 from, Index8 to) { - (void)piece; - (void)pos; - (void)add_attr; - - return (struct move){.from = from, .to = to, .attr = add_attr}; + return (struct move){.from = from, .to = to}; } -#define MOVE_MAX 128 enum move_gen_type { MG_ALL, @@ -64,7 +57,7 @@ static void all_pseudolegal_from_piece(struct pos const* restrict pos, while (move_mask) { Sq8 const to = bitboard_pop_lsb(&move_mask); assuming(*out_count < MOVE_MAX); \ - out[(*out_count)++] = move_make(pos, piece, from, to, 0); + out[(*out_count)++] = move_make(from, to); } } } @@ -88,7 +81,7 @@ static void all_pseudolegal_pawn_moves_##side(struct pos const* restrict pos,\ while (sp) {\ Sq8 const to = bitboard_pop_lsb(&sp);\ Sq8 const from = SQ_SHIFT_##inverse_direction(to, 1);\ - out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\ + out[(*out_count)++] = move_make(from, to);\ }\ \ Bb64 dp = pawn_double_push_##side(piece_mask & pawn_rank, ~all_occ)\ @@ -97,7 +90,7 @@ static void all_pseudolegal_pawn_moves_##side(struct pos const* restrict pos,\ while (dp) {\ Sq8 const to = bitboard_pop_lsb(&dp);\ Sq8 const from = SQ_SHIFT_##inverse_direction(to, 2);\ - out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\ + out[(*out_count)++] = move_make(from, to);\ }\ } DEFINE_ALL_PSEUDOLEGAL_PAWN_MOVES(white, black, SIDE_WHITE, SOUTH, RANK_MASK_2) @@ -125,7 +118,7 @@ static void all_pseudolegal_pawn_attacks_##side(struct pos const* restrict pos,\ Sq8 const to = bitboard_pop_lsb(&ratk);\ Sq8 const from = SQ_SHIFT_WEST(SQ_SHIFT_##inverse_direction(to, 1), 1);\ assuming(*out_count < MOVE_MAX); \ - out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\ + out[(*out_count)++] = move_make(from, to);\ }\ \ Bb64 latk = pawn_attacks_left_##side(piece_mask)\ @@ -136,7 +129,7 @@ static void all_pseudolegal_pawn_attacks_##side(struct pos const* restrict pos,\ Sq8 const to = bitboard_pop_lsb(&latk);\ Sq8 const from = SQ_SHIFT_EAST(SQ_SHIFT_##inverse_direction(to, 1), 1);\ assuming(*out_count < MOVE_MAX); \ - out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\ + out[(*out_count)++] = move_make(from, to);\ }\ } DEFINE_ALL_PSEUDOLEGAL_PAWN_ATTACKS(white, black, SIDE_WHITE, SOUTH) diff --git a/engine-tt.h b/engine-tt.h index 2af92b5..67789f0 100644 --- a/engine-tt.h +++ b/engine-tt.h @@ -1,12 +1,14 @@ #pragma once +#include "engine-types.h" + struct search_option { - uint64_t hash; // + 64 (64 / 8) - float score; // + 32 (96 / 12) - struct move move; // + 32 (128 / 16) - int depth : 4; // + 4 (132 / 17) - int init : 1; // + 1 (133 / 17) - enum tt_flag {TT_EXACT, TT_LOWER, TT_UPPER} flag : 3; // +3 (136 / 17) + uint64_t hash; + Score16 score; + struct move move; + int depth : 4; + int init : 1; + enum tt_flag {TT_EXACT, TT_LOWER, TT_UPPER} flag : 3; }; #define TT_ADDRESS_BITS 24 diff --git a/engine-types.h b/engine-types.h index a55b9b6..40f2ff3 100644 --- a/engine-types.h +++ b/engine-types.h @@ -1,5 +1,14 @@ #pragma once +/* --- Score16 (centipawns) ---- */ + +typedef int16_t Score16; + +enum { + SCORE_INF = 9999, + SCORE_CHECKMATE = 999, +}; + /* ----------- Index8 ----------- */ typedef uint8_t Index8; @@ -195,13 +204,13 @@ static char const* side_str[SIDE_COUNT] = { /* https://en.wikipedia.org/wiki/X_macro */ /* enum value white char white unicode black char black unicode */ #define PIECES \ - X(PIECE_EMPTY, 0.0f, ' ', ' ', ' ', ' ') \ - X(PIECE_PAWN, 1.0f, 'P', 0x2659, 'p', 0x265F) \ - X(PIECE_KNIGHT, 3.1f, 'N', 0x2658, 'n', 0x265E) \ - X(PIECE_BISHOP, 3.2f, 'B', 0x2657, 'b', 0x265D) \ - X(PIECE_ROOK, 5.0f, 'R', 0x2656, 'r', 0x265C) \ - X(PIECE_QUEEN, 9.0f, 'Q', 0x2655, 'q', 0x265B) \ - X(PIECE_KING, 16.0f, 'K', 0x2654, 'k', 0x265A) \ + X(PIECE_EMPTY, 0, ' ', ' ', ' ', ' ') \ + X(PIECE_PAWN, 100, 'P', 0x2659, 'p', 0x265F) \ + X(PIECE_KNIGHT, 301, 'N', 0x2658, 'n', 0x265E) \ + X(PIECE_BISHOP, 302, 'B', 0x2657, 'b', 0x265D) \ + X(PIECE_ROOK, 500, 'R', 0x2656, 'r', 0x265C) \ + X(PIECE_QUEEN, 900, 'Q', 0x2655, 'q', 0x265B) \ + X(PIECE_KING, 1600, 'K', 0x2654, 'k', 0x265A) \ /**/ typedef enum piece : uint8_t { @@ -213,7 +222,7 @@ typedef enum piece : uint8_t { #undef X } Piece8; -static float piece_value[PIECE_COUNT] = { +static Score16 piece_value[PIECE_COUNT] = { #define X(e, v, wc, wu, bc, bu) [e] = v, PIECES #undef X @@ -259,7 +268,6 @@ static int const piece_unicode[SIDE_COUNT][PIECE_COUNT] = { } }; - /* ----------- moves ----------- */ enum { diff --git a/engine.h b/engine.h index 67fe67e..229e5cd 100644 --- a/engine.h +++ b/engine.h @@ -11,6 +11,8 @@ /* temp performance counter */ static uint64_t g_ab_node_volume = 0; +static uint64_t g_pvs_re_search = 0; +static uint64_t g_pvs_probes = 0; #include "engine-macros.h" #include "engine-types.h" @@ -23,9 +25,6 @@ static uint64_t g_ab_node_volume = 0; /* --------------------------- MOVE SEARCH --------------------------------- */ -#define SCORE_INF 1e10f -#define SCORE_CHECKMATE 999.0f - /* for initial ordering of moves in alphabeta search */ static void move_compute_appeal(struct move* restrict m, struct pos const* restrict pos, @@ -43,91 +42,84 @@ static void move_compute_appeal(struct move* restrict m, /* TODO: remove branch */ if (n) { - m->appeal = (uint8_t)(16.0f*(float)n - piece_value[atk]); + m->appeal = (uint8_t)(16*n - piece_value[atk]); } else { m->appeal = 0; } } -static float board_score_heuristic(struct pos const* pos) +static Score16 board_score_heuristic(struct pos const* pos) { /* this function always evaluates from white's perspective before eventually flipping the sign based on `pos` */ - float score = 0.0f; + Score16 score = 0; Bb64 const occw = ~pos->pieces[SIDE_WHITE][PIECE_EMPTY]; Bb64 const occb = ~pos->pieces[SIDE_BLACK][PIECE_EMPTY]; enum game_progress const gp = endgameness(pos); - if (pos->pieces[SIDE_WHITE][PIECE_KING] == 0) { - score -= SCORE_CHECKMATE; - goto end; - } - if (pos->pieces[SIDE_BLACK][PIECE_KING] == 0) { + if (pos->pieces[SIDE_WHITE][PIECE_KING]) { score += SCORE_CHECKMATE; - goto end; + } + if (pos->pieces[SIDE_BLACK][PIECE_KING]) { + score -= SCORE_CHECKMATE; } for (Piece8 p = PIECE_BEGIN; p < PIECE_COUNT; ++p) { /* raw material value */ score += piece_value[p] * - ((float)bitboard_popcount(pos->pieces[SIDE_WHITE][p]) - - (float)bitboard_popcount(pos->pieces[SIDE_BLACK][p])); - - /* very minor advantage for threat projection to break tied moves */ - //if (p != PIECE_PAWN) { - // score += 0.001f * ( - // (float)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_WHITE][p], occall)) - // - (float)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_BLACK][p], occall))); - //} + ((Score16)bitboard_popcount(pos->pieces[SIDE_WHITE][p]) - + (Score16)bitboard_popcount(pos->pieces[SIDE_BLACK][p])); /* positional bonus, see evaluations.h */ for (size_t i = 0; i < POSITIONAL_MODIFIER_COUNT; ++i) { score += positional_modifier(SIDE_WHITE, gp, i, p).val * ( - (float)bitboard_popcount( + (Score16)bitboard_popcount( pos->pieces[SIDE_WHITE][p] & positional_modifier(SIDE_WHITE, gp, i, p).area) - - (float)bitboard_popcount( + - (Score16)bitboard_popcount( pos->pieces[SIDE_BLACK][p] & positional_modifier(SIDE_BLACK, gp, i, p).area) ); } } - score += 0.10f * (float)bitboard_more_than_one(pos->pieces[SIDE_WHITE][PIECE_BISHOP]); - score -= 0.10f * (float)bitboard_more_than_one(pos->pieces[SIDE_BLACK][PIECE_BISHOP]); + /* bishop pair */ + score += 10 * (Score16)bitboard_more_than_one(pos->pieces[SIDE_WHITE][PIECE_BISHOP]); + score -= 10 * (Score16)bitboard_more_than_one(pos->pieces[SIDE_BLACK][PIECE_BISHOP]); /* pawns defending pieces are desired */ - score += 0.03f * ( - (float)bitboard_popcount( + score += 3 * ( + (Score16)bitboard_popcount( pawn_attacks_white(pos->pieces[SIDE_WHITE][PIECE_PAWN]) & occw ) - - (float)bitboard_popcount( + - (Score16)bitboard_popcount( pawn_attacks_black(pos->pieces[SIDE_BLACK][PIECE_PAWN]) & occb ) ); /* stacked pawns are bad */ - const float k = 0.30f; + /* + const Score16 k = 30; for (enum file_index fi = FILE_INDEX_BEGIN; fi < FILE_INDEX_COUNT; ++fi) { uint64_t wstk = bitboard_popcount(pos->pieces[SIDE_WHITE][PIECE_PAWN] & FILE_MASK(fi)); uint64_t bstk = bitboard_popcount(pos->pieces[SIDE_BLACK][PIECE_PAWN] & FILE_MASK(fi)); - score -= k * (float)(wstk - (wstk == 1)); - score += k * (float)(bstk - (bstk == 1)); + score -= k * (Score16)(wstk - (wstk == 1)); + score += k * (Score16)(bstk - (bstk == 1)); } + */ -end: - float sign = (pos->moving_side == SIDE_WHITE) ? 1.0f : -1.0f; + Score16 sign = (pos->moving_side == SIDE_WHITE) ? 1 : -1; return sign*score; } static struct move moves_linear_search(struct move moves[restrict static MOVE_MAX], - size_t* restrict move_count) + size_t* restrict move_count) { size_t best = 0; assuming(*move_count > 0); @@ -146,21 +138,21 @@ struct move moves_linear_search(struct move moves[restrict static MOVE_MAX] /* quiescence is a deep search that only considers captures */ static -float quiesce(struct pos const* pos, - Piece8 mailbox[restrict static SQ_COUNT], - Side8 us, - float alpha, - float beta, - int8_t depth) +Score16 quiesce(struct pos const* pos, + Piece8 mailbox[restrict static SQ_COUNT], + Side8 us, + Score16 alpha, + Score16 beta, + int8_t depth) { if (pos->pieces[us][PIECE_KING] == 0) { - return -(SCORE_CHECKMATE + (float)depth); + return -(SCORE_CHECKMATE + depth); } Side8 const them = other_side(us); - float score = board_score_heuristic(pos); - float highscore = score; + Score16 score = board_score_heuristic(pos); + Score16 highscore = score; if (highscore >= beta) { return highscore; @@ -196,7 +188,7 @@ float quiesce(struct pos const* pos, hist.length = 0; (void)move_piece(&poscpy, us, &hist, mailbox_cpy, m); - score = -quiesce(&poscpy, mailbox_cpy, them, -beta, -alpha, depth - 1); + score = (Score16)-quiesce(&poscpy, mailbox_cpy, them, (Score16)(-beta), (Score16)(-alpha), depth - 1); if (score >= beta) { highscore = score; @@ -222,8 +214,8 @@ struct search_option alphabeta_search(struct pos const* pos, Piece8 mailbox[restrict static SQ_COUNT], Side8 us, int8_t depth, - float alpha, - float beta + Score16 alpha, + Score16 beta #ifdef FEATURE_STOPPABLE_SEARCH , struct searching_flag const* restrict searching @@ -241,7 +233,7 @@ struct search_option alphabeta_search(struct pos const* pos, if (pos->pieces[us][PIECE_KING] == 0) { return (struct search_option) { - .score = -(SCORE_CHECKMATE + (float)depth), + .score = -(SCORE_CHECKMATE + depth), .move = (struct move){0}, .depth = 0, .hash = pos->hash, @@ -251,7 +243,7 @@ struct search_option alphabeta_search(struct pos const* pos, } if (depth <= 0) { - float sc = quiesce(pos, mailbox, us, alpha, beta, 0); + Score16 sc = quiesce(pos, mailbox, us, alpha, beta, 0); return (struct search_option){ .score = sc, .move = (struct move){0}, @@ -262,12 +254,12 @@ struct search_option alphabeta_search(struct pos const* pos, }; } - float const alpha_orig = alpha; + Score16 const alpha_orig = alpha; struct move moves[MOVE_MAX]; size_t move_count = 0; - float best_score = -SCORE_INF; + Score16 best_score = -SCORE_INF; struct move best_move = MOVE_NULL; bool has_principal_move = false; @@ -295,6 +287,8 @@ struct search_option alphabeta_search(struct pos const* pos, has_principal_move = true; } + bool first = true; + enum move_gen_type const types[] = {MG_CAPTURES, MG_CHECKS, MG_QUIETS}; for (size_t i = 0; i < sizeof types / sizeof *types; ++i) { all_pseudolegal_moves(pos, types[i], us, &move_count, moves); @@ -318,30 +312,77 @@ struct search_option alphabeta_search(struct pos const* pos, enum move_result r = move_piece(&pos_cpy, us, hist, mailbox_cpy, m); - float score = 0.0; + Score16 score; if (r == MR_STALEMATE || r == MR_REPEATS) { - score = 0.0; + score = 0; } else { - struct search_option so = alphabeta_search(&pos_cpy, - hist, - tt, - mailbox_cpy, - other_side(us), - depth - 1, - -beta, - -alpha - #ifdef FEATURE_STOPPABLE_SEARCH - , - searching - #endif - ); - if (!so.init) { - hist->length = old_hist_len; - return so; + if (first) { + struct search_option so = alphabeta_search(&pos_cpy, + hist, + tt, + mailbox_cpy, + other_side(us), + depth - 1, + (Score16)(-beta), + (Score16)(-alpha) + #ifdef FEATURE_STOPPABLE_SEARCH + , + searching + #endif + ); + if (!so.init) { + hist->length = old_hist_len; + return so; + } + score = (Score16)-so.score; + first = false; + } else { + struct search_option so; + assuming(alpha < beta); + g_pvs_probes += 1; + so = alphabeta_search(&pos_cpy, + hist, + tt, + mailbox_cpy, + other_side(us), + depth - 1, + (Score16)(-alpha - 1), + (Score16)(-alpha) + #ifdef FEATURE_STOPPABLE_SEARCH + , + searching + #endif + ); + if (!so.init) { + hist->length = old_hist_len; + return so; + } + if ((Score16)-so.score > alpha) { + g_pvs_re_search += 1; + + so = alphabeta_search(&pos_cpy, + hist, + tt, + mailbox_cpy, + other_side(us), + depth - 1, + (Score16)(-beta), + (Score16)(-alpha) + #ifdef FEATURE_STOPPABLE_SEARCH + , + searching + #endif + ); + if (!so.init) { + hist->length = old_hist_len; + return so; + } + } + score = (Score16)-so.score; } - score = -so.score; } + hist->length = old_hist_len; if (score > best_score) { @@ -360,7 +401,7 @@ struct search_option alphabeta_search(struct pos const* pos, finish_search: if (IS_MOVE_NULL(best_move)) { - return (struct search_option){ .init = true, .move = MOVE_NULL, .score = -(SCORE_CHECKMATE + (float)depth) }; + return (struct search_option){ .init = true, .move = MOVE_NULL, .score = -(SCORE_CHECKMATE + depth) }; } enum tt_flag flag = TT_EXACT; @@ -387,8 +428,8 @@ finish_search: } static -struct search_result {struct move move; float score;} - search( +struct search_result {struct move move; Score16 score;} +search( struct board* restrict b, Side8 us, int8_t max_depth @@ -401,12 +442,6 @@ struct search_result {struct move move; float score;} struct move moves[MOVE_MAX]; size_t move_count = 0; -#ifndef NDEBUG - for (size_t i = 0; i < MOVE_MAX; ++i) { - moves[i] = MOVE_POISONED; - } -#endif - all_pseudolegal_moves(&b->pos, MG_ALL, us, &move_count, moves); assuming(move_count); @@ -414,8 +449,10 @@ struct search_result {struct move move; float score;} struct move best_move = moves[0]; g_ab_node_volume = 0; + g_pvs_re_search = 0; + g_pvs_probes = 0; - float score = 0.0; + Score16 score = 0; for (int8_t d = 1; d <= max_depth @@ -425,11 +462,27 @@ struct search_result {struct move move; float score;} ; ++d) { - float window = 2*SCORE_INF; /* temp debug solution */ - float alpha = score - window; - float beta = score + window; + Score16 window; + + if (d == 1) { + window = SCORE_INF; + } else { + window = 20 + 8*d; + } +#ifdef FEATURE_USE_PRINTF + fprintf(stderr, "depth: %hhd - window %hd\n", d, window); +#endif while (1) { + int al = (int)score - (int)window; + int bt = (int)score + (int)window; + + if (al < -SCORE_INF) al = -SCORE_INF; + if (bt > SCORE_INF) bt = SCORE_INF; + + Score16 alpha = (Score16)al; + Score16 beta = (Score16)bt; + struct search_option so = alphabeta_search(&b->pos, &b->hist, &b->tt, b->mailbox, us, d, alpha, beta #ifdef FEATURE_STOPPABLE_SEARCH @@ -438,42 +491,34 @@ struct search_result {struct move move; float score;} ); #ifdef FEATURE_STOPPABLE_SEARCH - if (!searching_still(searching)) { - goto breakbreak; - } + if (!searching_still(searching)) goto stop_search; #endif - if (IS_MOVE_NULL(so.move)) { - goto breakbreak; - } + if (IS_MOVE_NULL(so.move)) goto stop_search; - if (so.score > alpha && so.score < beta) { + if (so.score >= alpha && so.score <= beta) { score = so.score; best_move = so.move; break; } - window *= 2; - - if (so.score <= alpha) { - alpha = -SCORE_INF; - beta = score + window; - } else { - alpha = score - window; - beta = SCORE_INF; - } - - } + if (window < SCORE_INF/2) { #ifdef FEATURE_USE_PRINTF - fprintf(stderr, "depth: %hhd\n", d); + fprintf(stderr, "depth: %hhd - expanding window to %hd\n", d, window); #endif + window *= 2; + } else { + window = SCORE_INF; + } + } } -breakbreak: -#undef SCORE_INF +stop_search: #ifdef FEATURE_USE_PRINTF - fprintf(stderr, "nodes searched: %'llu\n", g_ab_node_volume); + fprintf(stderr, "nodes searched: %'llu\n", g_ab_node_volume); + fprintf(stderr, "pvs re-searches: %'llu\n", g_pvs_re_search); + fprintf(stderr, "pvs probes: %'llu\n", g_pvs_probes); #endif return (struct search_result){.move = best_move, .score = score}; diff --git a/tests.c b/tests.c index 0363008..0834399 100644 --- a/tests.c +++ b/tests.c @@ -470,12 +470,12 @@ int main() struct search_result sr = search(b, b->pos.moving_side, 25, &searching); struct move move = sr.move; - double const score = sr.score; + Score16 const score = sr.score; printf("move %d: {\n" " .from = %s, (%s)\n" " .to = %s,\n" - " .score = %lf,\n" + " .score = %d,\n" " .mask = " "", turn,