Compare commits

..

3 Commits

Author SHA1 Message Date
546343ab99 Fix web layer 2026-01-07 21:09:00 +01:00
7cb7e4ddd9 refactor: (bb64)occupied -> ~(Bb64)PIECE_EMPTY 2026-01-07 20:27:14 +01:00
a62d423035 Performance improvements 2026-01-07 15:15:18 +01:00
9 changed files with 429 additions and 235 deletions

View File

@@ -6,7 +6,7 @@ CFLAGS.gcc := -std=c23 -Wall -Wextra -Wconversion -Wno-unused-function
CFLAGS.gcc.release := -Ofast -march=native -DNDEBUG
CFLAGS.gcc.debug := -ggdb -O1 -fsanitize=address
CFLAGS.clang := -std=c23 -g -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion -Wno-macro-redefined -Wno-initializer-overrides
CFLAGS.clang := -std=c23 -g -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion
CFLAGS.clang.release := -O3 -ffast-math -march=native -DNDEBUG -DNSTATS
CFLAGS.clang.debug := -g3 -O1 -fsanitize=address,undefined
CFLAGS.clang.wasm := \
@@ -22,7 +22,7 @@ all: tests
wasm: chess.wasm
codegen: codegen.c
$(CC) -D_DEFAULT_SOURCE -o $@ $(CFLAGS) $^
$(CC) -o $@ $(CFLAGS) $^
chess.wasm: wasm-compat.c mbb_rook.h mbb_bishop.h engine.h
$(CC) -DWASM -o $@ wasm-compat.c $(CFLAGS.$(CC)) $(CFLAGS.$(CC).wasm)
@@ -34,4 +34,4 @@ mbb_bishop.h: codegen
./codegen
tests: tests.c mbb_rook.h mbb_bishop.h engine.h
$(CC) -D_DEFAULT_SOURCE -o $@ $(CFLAGS) tests.c
$(CC) -o $@ $(CFLAGS) tests.c

View File

@@ -22,7 +22,7 @@ struct board {
/* used for repeated board state detection only */
struct history {
struct pos items[50];
uint64_t hashes[50]; /* array of zobrist hashes */
size_t length;
} hist;
@@ -380,23 +380,38 @@ static enum move_result move_piece(struct pos* restrict pos,
pos->fullmoves += (pos->moving_side == SIDE_BLACK);
pos->halfmoves += 1;
assuming(hist->length < sizeof hist->items / sizeof *hist->items);
assuming(hist->length < sizeof hist->hashes / sizeof *hist->hashes);
/* check for repeated moves */
/* TODO: add move_do and move_undo to create proper repeat checks */
assuming(hist->length < 64);
for (size_t i = 0; i < hist->length; ++i) {
_Static_assert(sizeof *pos == sizeof hist->items[i]);
if (!my_memcmp(&hist->items[i].pieces, &pos->pieces, sizeof pos->pieces)
&& !my_memcmp(&hist->items[i].castling_illegal, &pos->castling_illegal, sizeof pos->castling_illegal)
&& hist->items[i].moving_side == pos->moving_side
&& hist->items[i].ep_targets == pos->ep_targets)
{
size_t i;
for (i = 0; i < hist->length / 8; ++i) {
uint64_t vec_a[8];
uint64_t vec_b[8];
bool match = false;
for (size_t vec_i = 0; vec_i < 8; ++vec_i) {
vec_a[i] = pos->hash;
}
for (size_t vec_i = 0; vec_i < 8; ++vec_i) {
vec_b[i] = hist->hashes[8*i + vec_i];
}
for (size_t vec_i = 0; vec_i < 8; ++vec_i) {
if (vec_a[vec_i] == vec_b[vec_i]) {
match = true;
}
}
if (match) {
return MR_STALEMATE;
}
}
for (; i < hist->length; ++i) {
if (hist->hashes[i] == pos->hash) {
return MR_STALEMATE;
}
}
hist->hashes[hist->length++] = pos->hash;
}
if (pos->halfmoves > 50) {
return MR_STALEMATE;

View File

@@ -314,25 +314,25 @@ BITBOARD( \
#define EARLY_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
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) \
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) \
/**/
#define EARLY_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 2, BOARD_CENTER_2X2) \
X(PIECE_BISHOP, 5, REL_BISHOP_KING_ATTACK) \
X(PIECE_PAWN, 0.02f, BOARD_CENTER_2X2) \
X(PIECE_BISHOP, 0.05f, REL_BISHOP_KING_ATTACK) \
/**/
#define EARLY_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, -18, ~REL_EARLY_PAWN_STRUCTURE) \
X(PIECE_KNIGHT, -10, REL_UNDEVELOPED_KNIGHTS) \
X(PIECE_BISHOP, -10, REL_UNDEVELOPED_BISHOPS) \
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) \
/**/
#define EARLY_POSITIONAL_BONUS_3 \
@@ -345,58 +345,58 @@ BITBOARD( \
#define MIDDLE_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
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_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) \
/**/
#define MIDDLE_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 2, BOARD_CENTER_2X2) \
X(PIECE_BISHOP, 7, REL_BISHOP_KING_ATTACK) \
X(PIECE_QUEEN, 7, REL_BISHOP_KING_ATTACK) \
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) \
/**/
#define MIDDLE_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, 2, REL_PAWN_KINGSIDE) \
X(PIECE_PAWN, 0.02f, REL_PAWN_KINGSIDE) \
/**/
#define MIDDLE_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \
X(PIECE_BISHOP, 5, BOARD_CENTER_6X6) \
X(PIECE_KNIGHT, 5, BOARD_CENTER_6X6) \
X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6) \
X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_6X6) \
/**/
/* ------------------------------- end game -------------------------------- */
#define LATE_POSITIONAL_BONUS_0 \
/* piece bonus area*/ \
X(PIECE_PAWN, 30, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \
X(PIECE_KING, 10, BOARD_CENTER_6X6) \
X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \
X(PIECE_KING, 0.10f, BOARD_CENTER_6X6) \
/**/
#define LATE_POSITIONAL_BONUS_1 \
/* piece bonus area*/ \
X(PIECE_PAWN, 30, REL_RANK_7 | REL_RANK_6) \
X(PIECE_KING, 10, BOARD_CENTER_4X4) \
X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6) \
X(PIECE_KING, 0.10f, BOARD_CENTER_4X4) \
/**/
#define LATE_POSITIONAL_BONUS_2 \
/* piece bonus area*/ \
X(PIECE_PAWN, 70, REL_RANK_7) \
X(PIECE_KING, 10, BOARD_CENTER_2X2) \
X(PIECE_PAWN, 0.70f, REL_RANK_7) \
X(PIECE_KING, 0.10f, BOARD_CENTER_2X2) \
/**/
#define LATE_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \
X(PIECE_KING, -50, ~BOARD_CENTER_6X6) \
X(PIECE_KING, -0.50f, ~BOARD_CENTER_6X6) \
/**/
struct posmod {
Bb64 const area;
Score16 const val;
float const val;
};
static inline struct posmod positional_modifier(Side8 pl, enum game_progress st, size_t layer, Piece8 pz)

View File

@@ -1,7 +1,5 @@
#pragma once
#define MOVE_MAX 128
#define MOVE_CASTLE_KINGSIDE_WHITE (struct move) \
{.from = SQ_E1, .to = SQ_G1}
@@ -15,10 +13,19 @@
{.from = SQ_E8, .to = SQ_C8}
static inline
struct move move_make(Index8 from, Index8 to)
struct move move_make(struct pos const* restrict pos,
Piece8 piece,
Index8 from,
Index8 to,
uint8_t add_attr)
{
return (struct move){.from = from, .to = to};
(void)piece;
(void)pos;
(void)add_attr;
return (struct move){.from = from, .to = to, .attr = add_attr};
}
#define MOVE_MAX 128
enum move_gen_type {
MG_ALL,
@@ -57,7 +64,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(from, to);
out[(*out_count)++] = move_make(pos, piece, from, to, 0);
}
}
}
@@ -81,7 +88,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(from, to);\
out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\
}\
\
Bb64 dp = pawn_double_push_##side(piece_mask & pawn_rank, ~all_occ)\
@@ -90,7 +97,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(from, to);\
out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\
}\
}
DEFINE_ALL_PSEUDOLEGAL_PAWN_MOVES(white, black, SIDE_WHITE, SOUTH, RANK_MASK_2)
@@ -118,7 +125,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(from, to);\
out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\
}\
\
Bb64 latk = pawn_attacks_left_##side(piece_mask)\
@@ -129,7 +136,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(from, to);\
out[(*out_count)++] = move_make(pos, PIECE_PAWN, from, to, 0);\
}\
}
DEFINE_ALL_PSEUDOLEGAL_PAWN_ATTACKS(white, black, SIDE_WHITE, SOUTH)

View File

@@ -1,14 +1,12 @@
#pragma once
#include "engine-types.h"
struct search_option {
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;
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)
};
#define TT_ADDRESS_BITS 24

View File

@@ -1,14 +1,5 @@
#pragma once
/* --- Score16 (centipawns) ---- */
typedef int16_t Score16;
enum {
SCORE_INF = 9999,
SCORE_CHECKMATE = 999,
};
/* ----------- Index8 ----------- */
typedef uint8_t Index8;
@@ -204,13 +195,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, ' ', ' ', ' ', ' ') \
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) \
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) \
/**/
typedef enum piece : uint8_t {
@@ -222,7 +213,7 @@ typedef enum piece : uint8_t {
#undef X
} Piece8;
static Score16 piece_value[PIECE_COUNT] = {
static float piece_value[PIECE_COUNT] = {
#define X(e, v, wc, wu, bc, bu) [e] = v,
PIECES
#undef X
@@ -268,6 +259,7 @@ static int const piece_unicode[SIDE_COUNT][PIECE_COUNT] = {
}
};
/* ----------- moves ----------- */
enum {

215
engine.h
View File

@@ -11,8 +11,6 @@
/* 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"
@@ -25,6 +23,9 @@ static uint64_t g_pvs_probes = 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,
@@ -42,77 +43,84 @@ static void move_compute_appeal(struct move* restrict m,
/* TODO: remove branch */
if (n) {
m->appeal = (uint8_t)(16*n - piece_value[atk]);
m->appeal = (uint8_t)(16.0f*(float)n - piece_value[atk]);
} else {
m->appeal = 0;
}
}
static Score16 board_score_heuristic(struct pos const* pos)
static float board_score_heuristic(struct pos const* pos)
{
/* this function always evaluates from white's perspective before
eventually flipping the sign based on `pos` */
Score16 score = 0;
float score = 0.0f;
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]) {
score += SCORE_CHECKMATE;
}
if (pos->pieces[SIDE_BLACK][PIECE_KING]) {
if (pos->pieces[SIDE_WHITE][PIECE_KING] == 0) {
score -= SCORE_CHECKMATE;
goto end;
}
if (pos->pieces[SIDE_BLACK][PIECE_KING] == 0) {
score += SCORE_CHECKMATE;
goto end;
}
for (Piece8 p = PIECE_BEGIN; p < PIECE_COUNT; ++p) {
/* raw material value */
score += piece_value[p] *
((Score16)bitboard_popcount(pos->pieces[SIDE_WHITE][p]) -
(Score16)bitboard_popcount(pos->pieces[SIDE_BLACK][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)));
//}
/* positional bonus, see evaluations.h */
for (size_t i = 0; i < POSITIONAL_MODIFIER_COUNT; ++i) {
score += positional_modifier(SIDE_WHITE, gp, i, p).val *
(
(Score16)bitboard_popcount(
(float)bitboard_popcount(
pos->pieces[SIDE_WHITE][p]
& positional_modifier(SIDE_WHITE, gp, i, p).area)
- (Score16)bitboard_popcount(
- (float)bitboard_popcount(
pos->pieces[SIDE_BLACK][p]
& positional_modifier(SIDE_BLACK, gp, i, p).area)
);
}
}
/* 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]);
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]);
/* pawns defending pieces are desired */
score += 3 * (
(Score16)bitboard_popcount(
score += 0.03f * (
(float)bitboard_popcount(
pawn_attacks_white(pos->pieces[SIDE_WHITE][PIECE_PAWN]) & occw
)
- (Score16)bitboard_popcount(
- (float)bitboard_popcount(
pawn_attacks_black(pos->pieces[SIDE_BLACK][PIECE_PAWN]) & occb
)
);
/* stacked pawns are bad */
/*
const Score16 k = 30;
const float k = 0.30f;
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 * (Score16)(wstk - (wstk == 1));
score += k * (Score16)(bstk - (bstk == 1));
score -= k * (float)(wstk - (wstk == 1));
score += k * (float)(bstk - (bstk == 1));
}
*/
Score16 sign = (pos->moving_side == SIDE_WHITE) ? 1 : -1;
end:
float sign = (pos->moving_side == SIDE_WHITE) ? 1.0f : -1.0f;
return sign*score;
}
@@ -138,21 +146,21 @@ struct move moves_linear_search(struct move moves[restrict static MOVE_MAX]
/* quiescence is a deep search that only considers captures */
static
Score16 quiesce(struct pos const* pos,
float quiesce(struct pos const* pos,
Piece8 mailbox[restrict static SQ_COUNT],
Side8 us,
Score16 alpha,
Score16 beta,
float alpha,
float beta,
int8_t depth)
{
if (pos->pieces[us][PIECE_KING] == 0) {
return -(SCORE_CHECKMATE + depth);
return -(SCORE_CHECKMATE + (float)depth);
}
Side8 const them = other_side(us);
Score16 score = board_score_heuristic(pos);
Score16 highscore = score;
float score = board_score_heuristic(pos);
float highscore = score;
if (highscore >= beta) {
return highscore;
@@ -188,7 +196,7 @@ Score16 quiesce(struct pos const* pos,
hist.length = 0;
(void)move_piece(&poscpy, us, &hist, mailbox_cpy, m);
score = (Score16)-quiesce(&poscpy, mailbox_cpy, them, (Score16)(-beta), (Score16)(-alpha), depth - 1);
score = -quiesce(&poscpy, mailbox_cpy, them, -beta, -alpha, depth - 1);
if (score >= beta) {
highscore = score;
@@ -214,8 +222,8 @@ struct search_option alphabeta_search(struct pos const* pos,
Piece8 mailbox[restrict static SQ_COUNT],
Side8 us,
int8_t depth,
Score16 alpha,
Score16 beta
float alpha,
float beta
#ifdef FEATURE_STOPPABLE_SEARCH
,
struct searching_flag const* restrict searching
@@ -233,7 +241,7 @@ struct search_option alphabeta_search(struct pos const* pos,
if (pos->pieces[us][PIECE_KING] == 0) {
return (struct search_option) {
.score = -(SCORE_CHECKMATE + depth),
.score = -(SCORE_CHECKMATE + (float)depth),
.move = (struct move){0},
.depth = 0,
.hash = pos->hash,
@@ -243,7 +251,7 @@ struct search_option alphabeta_search(struct pos const* pos,
}
if (depth <= 0) {
Score16 sc = quiesce(pos, mailbox, us, alpha, beta, 0);
float sc = quiesce(pos, mailbox, us, alpha, beta, 0);
return (struct search_option){
.score = sc,
.move = (struct move){0},
@@ -254,12 +262,12 @@ struct search_option alphabeta_search(struct pos const* pos,
};
}
Score16 const alpha_orig = alpha;
float const alpha_orig = alpha;
struct move moves[MOVE_MAX];
size_t move_count = 0;
Score16 best_score = -SCORE_INF;
float best_score = -SCORE_INF;
struct move best_move = MOVE_NULL;
bool has_principal_move = false;
@@ -287,8 +295,6 @@ 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);
@@ -312,20 +318,19 @@ struct search_option alphabeta_search(struct pos const* pos,
enum move_result r = move_piece(&pos_cpy, us, hist, mailbox_cpy, m);
Score16 score;
float score = 0.0;
if (r == MR_STALEMATE || r == MR_REPEATS) {
score = 0;
score = 0.0;
} else {
if (first) {
struct search_option so = alphabeta_search(&pos_cpy,
hist,
tt,
mailbox_cpy,
other_side(us),
depth - 1,
(Score16)(-beta),
(Score16)(-alpha)
-beta,
-alpha
#ifdef FEATURE_STOPPABLE_SEARCH
,
searching
@@ -335,54 +340,8 @@ struct search_option alphabeta_search(struct pos const* pos,
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;
score = -so.score;
}
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;
}
}
hist->length = old_hist_len;
if (score > best_score) {
@@ -401,7 +360,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 + depth) };
return (struct search_option){ .init = true, .move = MOVE_NULL, .score = -(SCORE_CHECKMATE + (float)depth) };
}
enum tt_flag flag = TT_EXACT;
@@ -428,8 +387,8 @@ finish_search:
}
static
struct search_result {struct move move; Score16 score;}
search(
struct search_result {struct move move; float score;}
search(
struct board* restrict b,
Side8 us,
int8_t max_depth
@@ -442,6 +401,12 @@ search(
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);
@@ -449,10 +414,8 @@ search(
struct move best_move = moves[0];
g_ab_node_volume = 0;
g_pvs_re_search = 0;
g_pvs_probes = 0;
Score16 score = 0;
float score = 0.0;
for (int8_t d = 1;
d <= max_depth
@@ -462,27 +425,11 @@ search(
;
++d)
{
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
float window = 2*SCORE_INF; /* temp debug solution */
float alpha = score - window;
float beta = score + window;
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
@@ -491,34 +438,42 @@ search(
);
#ifdef FEATURE_STOPPABLE_SEARCH
if (!searching_still(searching)) goto stop_search;
if (!searching_still(searching)) {
goto breakbreak;
}
#endif
if (IS_MOVE_NULL(so.move)) goto stop_search;
if (IS_MOVE_NULL(so.move)) {
goto breakbreak;
}
if (so.score >= alpha && so.score <= beta) {
if (so.score > alpha && so.score < beta) {
score = so.score;
best_move = so.move;
break;
}
if (window < SCORE_INF/2) {
#ifdef FEATURE_USE_PRINTF
fprintf(stderr, "depth: %hhd - expanding window to %hd\n", d, window);
#endif
window *= 2;
if (so.score <= alpha) {
alpha = -SCORE_INF;
beta = score + window;
} else {
window = SCORE_INF;
}
}
alpha = score - window;
beta = SCORE_INF;
}
stop_search:
}
#ifdef FEATURE_USE_PRINTF
fprintf(stderr, "depth: %hhd\n", d);
#endif
}
breakbreak:
#undef SCORE_INF
#ifdef FEATURE_USE_PRINTF
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};

View File

@@ -470,12 +470,12 @@ int main()
struct search_result sr = search(b, b->pos.moving_side, 25, &searching);
struct move move = sr.move;
Score16 const score = sr.score;
double const score = sr.score;
printf("move %d: {\n"
" .from = %s, (%s)\n"
" .to = %s,\n"
" .score = %d,\n"
" .score = %lf,\n"
" .mask = "
"",
turn,

View File

@@ -0,0 +1,227 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {
/* Begin PBXGroup section */
7FF73D842F0D52CE009C9579 = {
isa = PBXGroup;
children = (
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXLegacyTarget section */
7FF73D892F0D52CE009C9579 /* chess-engine */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "$(ACTION)";
buildConfigurationList = 7FF73D8C2F0D52CE009C9579 /* Build configuration list for PBXLegacyTarget "chess-engine" */;
buildPhases = (
);
buildToolPath = /usr/bin/make;
dependencies = (
);
name = "chess-engine";
packageProductDependencies = (
);
passBuildSettingsInEnvironment = 1;
productName = "chess-engine";
};
/* End PBXLegacyTarget section */
/* Begin PBXProject section */
7FF73D852F0D52CE009C9579 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 2610;
TargetAttributes = {
7FF73D892F0D52CE009C9579 = {
CreatedOnToolsVersion = 26.1;
};
};
};
buildConfigurationList = 7FF73D882F0D52CE009C9579 /* Build configuration list for PBXProject "chess-engine" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 7FF73D842F0D52CE009C9579;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
projectDirPath = "";
projectRoot = "";
targets = (
7FF73D892F0D52CE009C9579 /* chess-engine */,
);
};
/* End PBXProject section */
/* Begin XCBuildConfiguration section */
7FF73D8A2F0D52CE009C9579 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
};
name = Debug;
};
7FF73D8B2F0D52CE009C9579 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
};
name = Release;
};
7FF73D8D2F0D52CE009C9579 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEBUGGING_SYMBOLS = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
7FF73D8E2F0D52CE009C9579 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7FF73D882F0D52CE009C9579 /* Build configuration list for PBXProject "chess-engine" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7FF73D8A2F0D52CE009C9579 /* Debug */,
7FF73D8B2F0D52CE009C9579 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7FF73D8C2F0D52CE009C9579 /* Build configuration list for PBXLegacyTarget "chess-engine" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7FF73D8D2F0D52CE009C9579 /* Debug */,
7FF73D8E2F0D52CE009C9579 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7FF73D852F0D52CE009C9579 /* Project object */;
}