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

@@ -7,7 +7,7 @@ CFLAGS.gcc.release := -Ofast
CFLAGS.gcc.debug := -ggdb -O0 -fsanitize=address CFLAGS.gcc.debug := -ggdb -O0 -fsanitize=address
CFLAGS.clang := -std=c23 -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion CFLAGS.clang := -std=c23 -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion
CFLAGS.clang.release := -Ofast CFLAGS.clang.release := -Ofast -march=native
CFLAGS.clang.debug := -ggdb -O0 -fsanitize=address CFLAGS.clang.debug := -ggdb -O0 -fsanitize=address
CFLAGS.clang.wasm := \ CFLAGS.clang.wasm := \
--target=wasm32-unknown-unknown -O3 -nostdlib \ --target=wasm32-unknown-unknown -O3 -nostdlib \

View File

@@ -199,6 +199,7 @@ static void board_print_fen(struct pos const* pos, FILE* out)
static void board_print(const struct pos* pos, struct move* move, FILE* out) static void board_print(const struct pos* pos, struct move* move, FILE* out)
{ {
int buf[8][8] = {0}; int buf[8][8] = {0};
int color[8][8] = {0};
for (enum player player = PLAYER_BEGIN; player < PLAYER_COUNT; ++player) { for (enum player player = PLAYER_BEGIN; player < PLAYER_COUNT; ++player) {
for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) { for (enum piece piece = PIECE_BEGIN; piece < PIECE_COUNT; ++piece) {
@@ -207,7 +208,10 @@ static void board_print(const struct pos* pos, struct move* move, FILE* out)
for (index i = 7; i < 8; i--) { for (index i = 7; i < 8; i--) {
for (index j = 0; j < 8; ++j) { for (index j = 0; j < 8; ++j) {
if (x & (1ULL<<(i*8+j))) { if (x & (1ULL<<(i*8+j))) {
buf[i][j] = piece_unicode[player][piece]; buf[i][j] = piece_unicode[PLAYER_BLACK][piece];
color[i][j] = (player == PLAYER_WHITE)
? 1
: 2;
} }
} }
} }
@@ -221,19 +225,36 @@ static void board_print(const struct pos* pos, struct move* move, FILE* out)
fprintf(out, "%"INDEX_FMT" ", i+1); fprintf(out, "%"INDEX_FMT" ", i+1);
for (index j = 0; j < 8; ++j) { for (index j = 0; j < 8; ++j) {
index const n = INDEX_FROM_RF(i,j); index const n = INDEX_FROM_RF(i,j);
if (move && n == move->from) {
fprintf(out, "\033[%d;%dm", 30, 104); /* 44: blue*/ int bg, fg;
} else if (move && n == move->to) {
fprintf(out, "\033[%d;%dm", 30, 44); /* 104: bright blue*/ /**/ if (color[i][j] == 1) {
} else { fg = 97; /* bright white */
/* 45: magenta, 47: white */
fprintf(out, "\033[%d;%dm", 30, (i+j) % 2 ? 45 : 47);
} }
else if (color[i][j] == 2) {
fg = 30; /* black */
}
else {
fg = 35; /* magenta (should not happen) */
}
if (move && n == move->from) {
bg = 104; /* blue */
} else if (move && n == move->to) {
bg = 44; /* bright blue */
} else {
/* 45: magenta,
* 47: white */
bg = (i+j) % 2 ? 45 : 47;
}
fprintf(out, "\033[%d;%dm", fg, bg);
if (buf[i][j]) { if (buf[i][j]) {
fprintf(out, "%lc ", buf[i][j]); fprintf(out, "%lc ", buf[i][j]);
} else { } else {
fprintf(out, " "); /* idk why this hack is needed but "%lc " fprintf(out, " "); /* idk why this hack is needed but "%lc "
is not working when buf[i][j] = ' ' */ is not sufficient when buf[i][j] = ' ' */
} }
} }
fprintf(out, "\033[0m"); /* reset background color */ fprintf(out, "\033[0m"); /* reset background color */

488
engine.h
View File

@@ -4,6 +4,7 @@
#include "sys.h" #include "sys.h"
#include <stdint.h> #include <stdint.h>
#include <stdatomic.h>
#ifndef NDEBUG #ifndef NDEBUG
#define assert(expr) \ #define assert(expr) \
@@ -444,19 +445,17 @@ static inline enum rank_index index_to_rank(enum square_index p)
static bitboard cardinals_from_index(enum square_index p) static bitboard cardinals_from_index(enum square_index p)
{ {
/* might benefit from a lookup table */ return (FILE_MASK(index_to_file(p)) | RANK_MASK(index_to_rank(p)));
return (FILE_MASK(index_to_file(p)) | RANK_MASK(index_to_rank(p)))
& ~SQ_MASK_FROM_INDEX(p);
} }
static bitboard diagonals_from_index(enum square_index sq) static bitboard diagonals_from_index(enum square_index sq)
{ {
#ifdef CODEGEN
enum rank_index const rank = index_to_rank(sq); enum rank_index const rank = index_to_rank(sq);
enum file_index const file = index_to_file(sq); enum file_index const file = index_to_file(sq);
bitboard mask = 0ULL; bitboard mask = 0ULL;
/* Ensure signed-underflow detection rules match your style */
_Static_assert(((enum rank_index)-1) > 0, "rank_index must be unsigned"); _Static_assert(((enum rank_index)-1) > 0, "rank_index must be unsigned");
_Static_assert(((enum file_index)-1) > 0, "file_index must be unsigned"); _Static_assert(((enum file_index)-1) > 0, "file_index must be unsigned");
@@ -469,7 +468,6 @@ static bitboard diagonals_from_index(enum square_index sq)
mask |= SQ_MASK_FROM_RF(r, f); mask |= SQ_MASK_FROM_RF(r, f);
} }
/* NW (rank+1, file-1) */
for (r = rank+1, f = file-1; for (r = rank+1, f = file-1;
r <= RANK_INDEX_8 && f <= FILE_INDEX_H; r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
++r, --f) ++r, --f)
@@ -477,7 +475,6 @@ static bitboard diagonals_from_index(enum square_index sq)
mask |= SQ_MASK_FROM_RF(r, f); mask |= SQ_MASK_FROM_RF(r, f);
} }
/* SE (rank-1, file+1) */
for (r = rank-1, f = file+1; for (r = rank-1, f = file+1;
r <= RANK_INDEX_8 && f <= FILE_INDEX_H; r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
--r, ++f) --r, ++f)
@@ -485,15 +482,21 @@ static bitboard diagonals_from_index(enum square_index sq)
mask |= SQ_MASK_FROM_RF(r, f); mask |= SQ_MASK_FROM_RF(r, f);
} }
/* SW (rank-1, file-1) */
for (r = rank-1, f = file-1; for (r = rank-1, f = file-1;
r <= RANK_INDEX_8 && f <= FILE_INDEX_H; r <= RANK_INDEX_8 && f <= FILE_INDEX_H;
--r, --f) --r, --f)
{ {
mask |= SQ_MASK_FROM_RF(r, f); mask |= SQ_MASK_FROM_RF(r, f);
} }
return mask; return mask;
#else
#if ! __has_include("diagonals.h")
#error "compile with -DCODEGEN and run once to generate required header files"
#endif
#include "diagonals.h" /* defines static bitboard diagonals[SQ_INDEX_COUNT]; */
return diagonals[sq];
#endif
} }
@@ -914,13 +917,13 @@ struct board {
enum piece mailbox[SQ_INDEX_COUNT]; enum piece mailbox[SQ_INDEX_COUNT];
}; };
static void move_compute_appeal(struct move* m, static void move_compute_appeal(struct move* restrict m,
struct pos const* pos, struct pos const* restrict pos,
enum player us, enum player us,
enum piece mailbox[restrict static SQ_INDEX_COUNT]) enum piece mailbox[restrict static SQ_INDEX_COUNT])
{ {
enum player them = opposite_player(us);
/* MVV-LVA: https://www.chessprogramming.org/MVV-LVA */ /* MVV-LVA: https://www.chessprogramming.org/MVV-LVA */
enum player them = opposite_player(us);
enum piece const atk = mailbox[m->from]; enum piece const atk = mailbox[m->from];
uint8_t n = 1; uint8_t n = 1;
@@ -928,7 +931,10 @@ static void move_compute_appeal(struct move* m,
n += (uint8_t)piece_value[mailbox[m->to]]; n += (uint8_t)piece_value[mailbox[m->to]];
} }
m->appeal = 16*n - (uint8_t)piece_value[atk]; uint8_t mmv_lva_bonus = 16*n - (uint8_t)piece_value[atk];
assert((uint8_t)16*n > (uint8_t)piece_value[atk]);
m->appeal = mmv_lva_bonus;
} }
#define BOARD_INIT (struct board) { \ #define BOARD_INIT (struct board) { \
@@ -1001,6 +1007,21 @@ static void move_compute_appeal(struct move* m,
.hist = {0}, \ .hist = {0}, \
} }
void board_init(struct board* b)
{
if (b->pos.fullmoves == 0 && b->pos.hash == 0) {
*b = BOARD_INIT;
}
if (b->tt.entries == NULL) {
b->tt.entries = sys_mmap_anon_shared(TT_ENTRIES * sizeof b->tt.entries[0],
SYS_PROT_READ | SYS_PROT_WRITE,
SYS_MADV_RANDOM);
if (b->tt.entries == NULL) {
__builtin_trap();
}
}
}
static bool board_load_fen_unsafe(struct board* b, char const* fen_str) static bool board_load_fen_unsafe(struct board* b, char const* fen_str)
{ {
/* TODO: this function is not tested for malicious/corrupted inputs */ /* TODO: this function is not tested for malicious/corrupted inputs */
@@ -1517,6 +1538,29 @@ static inline void tt_insert(struct tt* tt, uint64_t hash, struct search_option
#endif #endif
} }
/* tt_insert_maybe inserts only if heuristics say it's a good idea. There are
* two considerations:
* - higher depth saves more work per probe hit
* - entries closer to the leaves are more likely to be searched multiple time
*/
static inline void tt_insert_maybe(struct tt* tt, uint64_t hash, struct search_option so)
{
uint64_t const addr = hash % TT_ENTRIES;
#if 0
struct search_option* tte = &tt->entries[addr];
if (so.depth < tte->depth) {
*tte = so;
}
#endif
so.init = true;
tt->entries[addr] = so;
#ifndef NDEBUG
tt->insertions += 1;
#endif
}
enum move_result { enum move_result {
MR_NORMAL, MR_NORMAL,
MR_CHECK, MR_CHECK,
@@ -1708,10 +1752,10 @@ static enum move_result board_move_2(struct board* b, struct move move)
move); move);
} }
/*
* Placeholder heuristic /* --------------------------- MOVE SEARCH --------------------------------- */
* #include "evaluations.h"
* */
static double board_score_heuristic(struct pos const* pos) static double board_score_heuristic(struct pos const* pos)
{ {
/* this function always evaluates from white's perspective before /* this function always evaluates from white's perspective before
@@ -1723,7 +1767,11 @@ static double board_score_heuristic(struct pos const* pos)
- static struct {bitboard const area; double const val} const - static struct {bitboard const area; double const val} const
positional_modifier[PLAYER_COUNT][POSITIONAL_MODIFIER_COUNT][PIECE_COUNT]; positional_modifier[PLAYER_COUNT][POSITIONAL_MODIFIER_COUNT][PIECE_COUNT];
* */ * */
#include "evaluations.h" bitboard const occw = pos->occupied[PLAYER_WHITE];
bitboard const occb = pos->occupied[PLAYER_BLACK];
bitboard const occall = occw | occb;
enum game_progress const gp = endgameness(pos);
for (enum piece p = PIECE_BEGIN; p < PIECE_COUNT; ++p) { for (enum piece p = PIECE_BEGIN; p < PIECE_COUNT; ++p) {
/* raw material value */ /* raw material value */
@@ -1731,34 +1779,35 @@ static double board_score_heuristic(struct pos const* pos)
((double)bitboard_popcount(pos->pieces[PLAYER_WHITE][p]) - ((double)bitboard_popcount(pos->pieces[PLAYER_WHITE][p]) -
(double)bitboard_popcount(pos->pieces[PLAYER_BLACK][p])); (double)bitboard_popcount(pos->pieces[PLAYER_BLACK][p]));
/* pawns defending pieces are desired */ /* very minor advantage for threat projection to break tied moves */
score += 0.05 * ( score += 0.001 * (
(double)bitboard_popcount( (double)bitboard_popcount(piece_attacks(p, PLAYER_WHITE, 0, p, occall, 0))
pawn_attacks_white(pos->pieces[PLAYER_WHITE][PIECE_PAWN]) - (double)bitboard_popcount(piece_attacks(p, PLAYER_BLACK, 0, p, occall, 0)));
& pos->pieces[PLAYER_WHITE][p]
)
- (double)bitboard_popcount(
pawn_attacks_black(pos->pieces[PLAYER_WHITE][PIECE_PAWN])
& pos->pieces[PLAYER_WHITE][p]
)
);
/* positional bonus, see evaluations.h */ /* positional bonus, see evaluations.h */
for (size_t i = 0; i < POSITIONAL_MODIFIER_COUNT; ++i) { for (size_t i = 0; i < POSITIONAL_MODIFIER_COUNT; ++i) {
score += positional_modifier[PLAYER_WHITE][i][p].val * score += positional_modifier(PLAYER_WHITE, gp, i, p).val *
( (
(double)bitboard_popcount( (double)bitboard_popcount(
pos->pieces[PLAYER_WHITE][p] pos->pieces[PLAYER_WHITE][p]
& positional_modifier[PLAYER_WHITE][i][p].area & positional_modifier(PLAYER_WHITE, gp, i, p).area)
)
- (double)bitboard_popcount( - (double)bitboard_popcount(
pos->pieces[PLAYER_BLACK][p] pos->pieces[PLAYER_BLACK][p]
& positional_modifier[PLAYER_BLACK][i][p].area & positional_modifier(PLAYER_BLACK, gp, i, p).area)
)
); );
} }
} }
/* pawns defending pieces are desired */
score += 0.05 * (
(double)bitboard_popcount(
pawn_attacks_white(pos->pieces[PLAYER_WHITE][PIECE_PAWN]) & occw
)
- (double)bitboard_popcount(
pawn_attacks_black(pos->pieces[PLAYER_BLACK][PIECE_PAWN]) & occb
)
);
/* stacked pawns are bad */ /* stacked pawns are bad */
const double k = 0.30; const double k = 0.30;
for (enum file_index fi = FILE_INDEX_BEGIN; fi < FILE_INDEX_COUNT; ++fi) { for (enum file_index fi = FILE_INDEX_BEGIN; fi < FILE_INDEX_COUNT; ++fi) {
@@ -1859,68 +1908,6 @@ double quiesce(struct pos const* pos,
return highscore; return highscore;
} }
struct ab_frame {
enum search_stage {
ST_INIT = 0,
ST_LOOP = 1,
ST_WAIT_CHILD = 2,
} stage;
struct pos pos;
enum piece mailbox[SQ_INDEX_COUNT];
enum player us;
int8_t depth;
uint64_t mattr_filter;
double alpha;
double beta;
double alpha_orig;
struct search_option tte;
struct move moves[MOVE_MAX];
size_t move_count;
double best_score;
struct move best_move;
size_t old_hist_length;
struct move pending_move;
struct search_option result;
};
static inline void ab_update_parent_after_score(struct ab_frame *parent,
struct tt *tt,
struct move m,
double score)
{
if (score > parent->best_score) {
parent->best_score = score;
parent->best_move = m;
}
if (score > parent->alpha) {
parent->alpha = score;
}
if (parent->alpha >= parent->beta) {
struct search_option out = {
.score = parent->alpha,
.move = parent->best_move,
.depth = parent->depth,
.hash = parent->pos.hash,
.init = true,
.flag = TT_LOWER,
};
tt_insert(tt, parent->pos.hash, out);
parent->result = out;
/* mark parent as complete so it will be popped in st_wait_child. */
parent->stage = ST_WAIT_CHILD;
}
}
static static
struct search_option alphabeta_search(struct pos const* pos, struct search_option alphabeta_search(struct pos const* pos,
struct history* hist, struct history* hist,
@@ -1928,265 +1915,167 @@ struct search_option alphabeta_search(struct pos const* pos,
enum piece mailbox[restrict static SQ_INDEX_COUNT], enum piece mailbox[restrict static SQ_INDEX_COUNT],
enum player us, enum player us,
int8_t depth, int8_t depth,
uint64_t mattr_filter,
double alpha, double alpha,
double beta) double beta,
atomic_bool* searching)
{ {
struct { if (depth <= 0) {
struct ab_frame stack[50]; double sc = quiesce(pos, mailbox, us, alpha, beta, 0);
size_t len; return (struct search_option){
} ab_stack = {0}; .score = sc,
#define STACK_PUSH(s, x) \
do { \
assert(s.len < sizeof s.stack / sizeof s.stack[0]); \
(s.stack)[(s.len)++] = (x); \
} while (0)
#define STACK_TOP(s) &(s.stack)[(s.len) - 1]
#define STACK_POP(s) (assert(s.len > 0), s.stack[--(s.len)]);
#define STACK_EMPTY(s) (s.len == 0)
struct ab_frame root = {
.stage = ST_INIT,
.pos = *pos,
.us = us,
.depth = depth,
.mattr_filter = mattr_filter,
.alpha = alpha,
.beta = beta
};
my_memcpy(root.mailbox, mailbox, sizeof root.mailbox);
STACK_PUSH(ab_stack, root);
while (1) {
struct ab_frame *fr = STACK_TOP(ab_stack);
switch (fr->stage) {
case ST_INIT: {
if (fr->depth <= 0) {
fr->result = (struct search_option) {
.score = quiesce(&fr->pos, fr->mailbox, fr->us,
fr->alpha, fr->beta, 0),
.move = (struct move){0}, .move = (struct move){0},
.depth = 0, .depth = 0,
.hash = fr->pos.hash, .hash = pos->hash,
.init = true, .init = true,
.flag = TT_EXACT, .flag = TT_EXACT,
}; };
fr->stage = ST_WAIT_CHILD;
break;
} }
fr->alpha_orig = fr->alpha; double const alpha_orig = alpha;
fr->tte = tt_get(tt, fr->pos.hash); struct search_option tte = tt_get(tt, pos->hash);
if (fr->tte.init && fr->tte.hash == fr->pos.hash && fr->tte.depth >= fr->depth) { if (tte.init && tte.hash == pos->hash && tte.depth >= depth) {
if (fr->tte.flag == TT_EXACT) { if (tte.flag == TT_EXACT) {
fr->result = fr->tte; return tte;
fr->stage = ST_WAIT_CHILD; } else if (tte.flag == TT_LOWER) {
break; if (tte.score > alpha) alpha = tte.score;
} else if (fr->tte.flag == TT_LOWER) { } else if (tte.flag == TT_UPPER) {
if (fr->tte.score > fr->alpha) { if (tte.score < beta) beta = tte.score;
fr->alpha = fr->tte.score;
} }
} else if (fr->tte.flag == TT_UPPER) {
if (fr->tte.score < fr->beta) { if (alpha >= beta) {
fr->beta = fr->tte.score; return tte;
} }
} }
if (fr->alpha >= fr->beta) { struct move moves[MOVE_MAX];
fr->result = fr->tte; size_t move_count = 0;
fr->stage = ST_WAIT_CHILD; all_moves(pos, us, &move_count, moves);
break;
}
}
fr->move_count = 0;
all_moves(&fr->pos, fr->us, &fr->move_count, fr->moves);
/* checkmate or stalemate */ /* checkmate or stalemate */
if (fr->move_count == 0) { if (move_count == 0) {
double score = 0.0; double score = 0.0;
if (attacks_to(&fr->pos, if (attacks_to(pos,
fr->pos.pieces[fr->us][PIECE_KING], pos->pieces[us][PIECE_KING],
0ULL, 0ULL) != 0ULL) { 0ULL, 0ULL) != 0ULL) {
score = -(999.0 + (double)fr->depth); score = -(999.0 + (double)depth);
} }
fr->result = (struct search_option) { return (struct search_option){
.score = score, .score = score,
.move = (struct move){0}, .move = (struct move){0},
.depth = fr->depth, .depth = depth,
.hash = fr->pos.hash, .hash = pos->hash,
.init = true, .init = true,
.flag = TT_EXACT, .flag = TT_EXACT,
}; };
fr->stage = ST_WAIT_CHILD;
break;
} }
for (size_t i = 0; i < fr->move_count; ++i) { for (size_t i = 0; i < move_count; ++i) {
move_compute_appeal(&fr->moves[i], &fr->pos, fr->us, fr->mailbox); move_compute_appeal(&moves[i], pos, us, mailbox);
} }
/* put existing TT entry first */ /* put existing TT move first */
if (fr->tte.init && fr->tte.hash == fr->pos.hash) { if (tte.init && tte.hash == pos->hash) {
for (size_t i = 0; i < fr->move_count; ++i) { for (size_t i = 0; i < move_count; ++i) {
if (fr->moves[i].from == fr->tte.move.from && if (moves[i].from == tte.move.from &&
fr->moves[i].to == fr->tte.move.to) { moves[i].to == tte.move.to) {
fr->moves[i].appeal = APPEAL_MAX; moves[i].appeal = APPEAL_MAX;
break; break;
} }
} }
} }
fr->best_score = -1e300; double best_score = -1e300;
fr->best_move = fr->moves[0]; struct move best_move = moves[0];
fr->stage = ST_LOOP;
} break;
case ST_LOOP: { while (move_count > 0 && atomic_load_explicit(searching, memory_order_relaxed)) {
if (fr->result.init) { struct move m = moves_linear_search(moves, &move_count);
fr->stage = ST_WAIT_CHILD;
break; size_t const old_hist_len = hist->length;
struct pos pos_cpy = *pos;
enum piece mailbox_cpy[SQ_INDEX_COUNT];
my_memcpy(mailbox_cpy, mailbox, sizeof mailbox_cpy);
enum move_result r = board_move(&pos_cpy, hist, mailbox_cpy, m);
double score;
if (r == MR_STALEMATE || r == MR_REPEATS) {
score = 0.0;
} else {
score = -alphabeta_search(&pos_cpy,
hist,
tt,
mailbox_cpy,
opposite_player(us),
depth - 1,
-beta,
-alpha,
searching).score;
}
hist->length = old_hist_len;
if (score > best_score) {
best_score = score;
best_move = m;
}
if (score > alpha) {
alpha = score;
}
if (alpha >= beta) {
break;
}
} }
if (fr->move_count == 0) {
enum tt_flag flag = TT_EXACT; enum tt_flag flag = TT_EXACT;
if (fr->best_score <= fr->alpha_orig) flag = TT_UPPER; if (best_score <= alpha_orig) {
flag = TT_UPPER;
}
else if (best_score >= beta) {
flag = TT_LOWER;
}
fr->result = (struct search_option) { struct search_option out = (struct search_option){
.score = fr->best_score, .score = best_score,
.move = fr->best_move, .move = best_move,
.depth = fr->depth, .depth = depth,
.hash = fr->pos.hash, .hash = pos->hash,
.init = true, .init = true,
.flag = flag, .flag = flag,
}; };
tt_insert(tt, fr->pos.hash, fr->result);
fr->stage = ST_WAIT_CHILD;
break;
}
struct move m = moves_linear_search(fr->moves, &fr->move_count); tt_insert(tt, pos->hash, out);
if (fr->mattr_filter && !(m.attr & fr->mattr_filter)) {
break;
}
fr->old_hist_length = hist->length;
struct pos child_pos = fr->pos;
enum piece child_mailbox[SQ_INDEX_COUNT];
my_memcpy(child_mailbox, fr->mailbox, sizeof child_mailbox);
enum move_result const r = board_move(&child_pos, hist, child_mailbox, m);
if (r == MR_STALEMATE || r == MR_REPEATS) {
hist->length = fr->old_hist_length;
ab_update_parent_after_score(fr, tt, m, 0.0);
break;
}
if (fr->depth - 1 <= 0) {
double score = -quiesce(&child_pos,
child_mailbox,
opposite_player(fr->us),
-fr->beta,
-fr->alpha,
0);
hist->length = fr->old_hist_length;
ab_update_parent_after_score(fr, tt, m, score);
break;
}
fr->pending_move = m;
fr->stage = ST_WAIT_CHILD;
struct ab_frame child = {0};
child.stage = ST_INIT;
child.pos = child_pos;
my_memcpy(child.mailbox, child_mailbox, sizeof child.mailbox);
child.us = opposite_player(fr->us);
child.depth = fr->depth - 1;
child.mattr_filter = fr->mattr_filter;
child.alpha = -fr->beta;
child.beta = -fr->alpha;
STACK_PUSH(ab_stack, child);
} break;
case ST_WAIT_CHILD: {
struct search_option out = fr->result;
STACK_POP(ab_stack);
if (STACK_EMPTY(ab_stack)) {
return out; return out;
} }
struct ab_frame *parent = STACK_TOP(ab_stack);
if (parent->stage == ST_WAIT_CHILD) {
/* parent is waiting on a child (this frame), propagate */
double score = -out.score;
struct move m = parent->pending_move;
hist->length = parent->old_hist_length;
/* resume parent move loop. */
parent->stage = ST_LOOP;
ab_update_parent_after_score(parent, tt, m, score);
} else {
/* parent wasn't waiting: treat as a completed frame result being
propagated through a completion chain. */
parent->result = out;
parent->stage = ST_WAIT_CHILD;
}
} break;
default: {
/* unreachable */
assert(0);
__builtin_unreachable();
} break;
}
}
}
static struct search_result {struct move move; double score;} static struct search_result {struct move move; double score;}
search(struct board* b, enum player us, int8_t max_depth) search(struct board* b, enum player us, int8_t max_depth, atomic_bool* searching)
{ {
#if 1 struct move moves[MOVE_MAX];
if (b->tt.entries == NULL) { size_t move_count = 0;
b->tt.entries = sys_mmap_anon_shared(TT_ENTRIES * sizeof b->tt.entries[0], all_moves(&b->pos, us, &move_count, moves);
SYS_PROT_READ | SYS_PROT_WRITE,
SYS_MADV_RANDOM);
if (b->tt.entries == NULL) {
__builtin_trap();
}
}
#endif
struct move best_move = {0}; assert(move_count);
struct move best_move = moves[0];
#define SCORE_INF 1e300
double score = 0.0; double score = 0.0;
#define SCORE_INF 1e80 for (int8_t d = 1;
d <= max_depth && atomic_load_explicit(searching, memory_order_relaxed);
for (int8_t d = 1; d <= max_depth; ++d) { ++d)
double window = 2.0; {
double window = 1000; /* temp debug solution */
double alpha = score - window; double alpha = score - window;
double beta = score + window; double beta = score + window;
while (true) { while (atomic_load_explicit(searching, memory_order_relaxed)) {
struct search_option so = struct search_option so =
alphabeta_search(&b->pos, &b->hist, &b->tt, b->mailbox, us, d, 0, alpha, beta); alphabeta_search(&b->pos, &b->hist, &b->tt, b->mailbox, us, d, alpha, beta, searching);
if (so.score > alpha && so.score < beta) { if (so.score > alpha && so.score < beta) {
score = so.score; score = so.score;
@@ -2196,12 +2085,11 @@ search(struct board* b, enum player us, int8_t max_depth)
window *= 2; window *= 2;
alpha = score - window;
beta = score + window;
if (so.score <= alpha) { if (so.score <= alpha) {
alpha = -SCORE_INF; alpha = -SCORE_INF;
beta = score + window;
} else { } else {
alpha = score - window;
beta = SCORE_INF; beta = SCORE_INF;
} }

View File

@@ -2,6 +2,52 @@
#pragma once #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( \ #define BITBOARD_WHITE( \
a8,b8,c8,d8,e8,f8,g8,h8, \ a8,b8,c8,d8,e8,f8,g8,h8, \
a7,b7,c7,d7,e7,f7,g7,h7, \ 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##\ h8##g8##f8##e8##d8##c8##b8##a8##\
ULL ULL
#define RELATIVE_DIAGONAL_A1_H8 \ #define REL_RANK_1 \
RELATIVE_BITBOARD( \ 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,0,1, \
0,0,0,0,0,0,1,0, \ 0,0,0,0,0,0,1,0, \
0,0,0,0,0,1,0,0, \ 0,0,0,0,0,1,0,0, \
@@ -55,8 +190,8 @@ ULL
0,1,0,0,0,0,0,0, \ 0,1,0,0,0,0,0,0, \
1,0,0,0,0,0,0,0) 1,0,0,0,0,0,0,0)
#define RELATIVE_DIAGONAL_A8_H1 \ #define REL_DIAGONAL_A8_H1 \
RELATIVE_BITBOARD( \ REL_BITBOARD( \
1,0,0,0,0,0,0,0, \ 1,0,0,0,0,0,0,0, \
0,1,0,0,0,0,0,0, \ 0,1,0,0,0,0,0,0, \
0,0,1,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,1,0, \
0,0,0,0,0,0,0,1) 0,0,0,0,0,0,0,1)
#define RELATIVE_BISHOP_KING_ATTACK \ #define REL_BISHOP_KING_ATTACK \
RELATIVE_BITBOARD( \ REL_BITBOARD( \
0,0,0,0,0,0,1,1, \ 0,0,0,0,0,0,1,1, \
0,0,0,0,0,1,1,0, \ 0,0,0,0,0,1,1,0, \
0,0,0,0,1,1,0,0, \ 0,0,0,0,1,1,0,0, \
@@ -77,8 +212,8 @@ ULL
1,1,0,0,0,0,0,0, \ 1,1,0,0,0,0,0,0, \
1,0,0,0,0,0,0,0) 1,0,0,0,0,0,0,0)
#define RELATIVE_BISHOP_QUEEN_ATTACK \ #define REL_BISHOP_QUEEN_ATTACK \
RELATIVE_BITBOARD( \ REL_BITBOARD( \
1,1,0,0,0,0,0,0, \ 1,1,0,0,0,0,0,0, \
0,1,1,0,0,0,0,0, \ 0,1,1,0,0,0,0,0, \
0,0,1,1,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,1,1, \
0,0,0,0,0,0,0,1) 0,0,0,0,0,0,0,1)
#define RELATIVE_KING_CASTLE_KINGSIDE \ #define REL_KING_CASTLE_KINGSIDE \
RELATIVE_BITBOARD( \ 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, \
@@ -99,8 +234,8 @@ ULL
0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,1,0) 0,0,0,0,0,0,1,0)
#define RELATIVE_KING_CASTLE_QUEENSIDE \ #define REL_KING_CASTLE_QUEENSIDE \
RELATIVE_BITBOARD( \ 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, \
@@ -121,8 +256,8 @@ ULL
0,0,0,0,0,0,0,0, \ 0,0,0,0,0,0,0,0, \
1,0,0,0,0,0,0,1) 1,0,0,0,0,0,0,1)
#define RELATIVE_PAWN_SAFE_ZONE \ #define REL_EARLY_PAWN_STRUCTURE \
BITBOARD( \ 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,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, \ 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 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 \ #define BOARD_CENTER_4X4 \
((FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F) \ ((FILE_MASK_C | FILE_MASK_D | FILE_MASK_E | FILE_MASK_F) \
& (RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6)) & (RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6))
@@ -140,63 +279,163 @@ ULL
((FILE_MASK_D | FILE_MASK_E) \ ((FILE_MASK_D | FILE_MASK_E) \
& (RANK_MASK_4 | RANK_MASK_5)) & (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*/ \ /* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \ X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
X(PIECE_KNIGHT, 0.05, 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_BISHOP, 0.05, REL_DIAGONAL_A1_H8 | REL_DIAGONAL_A8_H1) \
X(PIECE_KING, 0.15, RELATIVE_KING_CASTLE_KINGSIDE) \ 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) \ 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*/ \ /* piece bonus area*/ \
X(PIECE_PAWN, 0.02, BOARD_CENTER_2X2) \ 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*/ \ /* 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) \ X(PIECE_BISHOP, 0.05, CORNERS) \
/**/ /**/
#define POSITIONAL_BONUS_3 \ #define EARLY_POSITIONAL_BONUS_3 \
/* piece bonus area*/ \ /* 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 /* ------------------------------ middle game ------------------------------ */
positional_modifier[PLAYER_COUNT][POSITIONAL_MODIFIER_COUNT][PIECE_COUNT] = { /* (almost same as opening for now, but queen is not punished for moving) */
#define X(p, b, a) [p] = {.area = (a), .val = b},
#define RELATIVE_BITBOARD BITBOARD_WHITE #define MIDDLE_POSITIONAL_BONUS_0 \
[PLAYER_WHITE] = { /* piece bonus area*/ \
{POSITIONAL_BONUS_0}, X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
{POSITIONAL_BONUS_1}, X(PIECE_KNIGHT, 0.05, BOARD_CENTER_4X4) \
{POSITIONAL_BONUS_2}, X(PIECE_BISHOP, 0.05, REL_DIAGONAL_A1_H8 | REL_DIAGONAL_A8_H1) \
{POSITIONAL_BONUS_3}, X(PIECE_KING, 0.15, REL_KING_CASTLE_KINGSIDE) \
}, /**/
[PLAYER_BLACK] = {
#undef RELATIVE_BITBOARD #define MIDDLE_POSITIONAL_BONUS_1 \
#define RELATIVE_BITBOARD BITBOARD_BLACK /* piece bonus area*/ \
{POSITIONAL_BONUS_0}, X(PIECE_PAWN, 0.02, BOARD_CENTER_2X2) \
{POSITIONAL_BONUS_1}, X(PIECE_BISHOP, 0.05, REL_BISHOP_KING_ATTACK) \
{POSITIONAL_BONUS_2}, /**/
{POSITIONAL_BONUS_3},
} #define MIDDLE_POSITIONAL_BONUS_2 \
#undef X /* 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 CORNERS
#undef BOARD_CENTER_4X4 #undef BOARD_CENTER_4X4
#undef BOARD_CENTER_2X2 #undef BOARD_CENTER_2X2
#undef BITBOARD_WHITE #undef BITBOARD_WHITE
#undef BITBOARD_BLACK #undef BITBOARD_BLACK
#undef RELATIVE_DIAGONAL_A1_H8 #undef REL_DIAGONAL_A1_H8
#undef RELATIVE_DIAGONAL_A8_H1 #undef REL_DIAGONAL_A8_H1
#undef RELATIVE_BISHOP_KING_ATTACK #undef REL_BISHOP_KING_ATTACK
#undef RELATIVE_BISHOP_QUEEN_ATTACK #undef REL_BISHOP_QUEEN_ATTACK
#undef RELATIVE_KING_CASTLE_KINGSIDE #undef REL_KING_CASTLE_KINGSIDE
#undef RELATIVE_KING_CASTLE_QUEENSIDE #undef REL_KING_CASTLE_QUEENSIDE

38
tests.c
View File

@@ -1,6 +1,7 @@
#define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE 500
#include <unistd.h> /* usleep */ #include <unistd.h> /* usleep */
#include <pthread.h>
#include "engine.h" #include "engine.h"
#include "board_print.h" #include "board_print.h"
@@ -387,9 +388,21 @@ static void test_bishops(void)
printf("\nAll bishop_attacks_from_index tests passed.\n"); printf("\nAll bishop_attacks_from_index tests passed.\n");
} }
struct timeout_params {
atomic_bool* x;
bool v;
useconds_t us;
};
void* set_after_timeout(void* x)
{
struct timeout_params* p = x;
usleep(p->us);
*p->x = p->v;
return NULL;
}
int main() int main()
{ {
printf("sizeof pos: %zu\n", sizeof (struct pos)); printf("sizeof pos: %zu\n", sizeof (struct pos));
printf("sizeof tt: %zu\n", sizeof (struct tt)); printf("sizeof tt: %zu\n", sizeof (struct tt));
@@ -403,16 +416,18 @@ int main()
} }
fprintf(stdout, "\033[0m\n"); /* reset background color */ fprintf(stdout, "\033[0m\n"); /* reset background color */
/* board is too big for the stack */ /* board could be too big for the stack */
struct board* b = malloc(sizeof *b); struct board* b = malloc(sizeof *b);
if (!b) { if (!b) {
abort(); abort();
} }
*b = BOARD_INIT; *b = BOARD_INIT;
board_init(b);
//board_load_fen_unsafe(&board, "1n1q1rk1/r1p2P2/1p1pp2p/pB2P3/2P5/PPN5/6b1/3QK1NR b - - 0 1"); //board_load_fen_unsafe(b, "1n1q1rk1/r1p2P2/1p1pp2p/pB2P3/2P5/PPN5/6b1/3QK1NR b - - 0 1");
//board_load_fen_unsafe(&board, "5R2/7k/P7/6pp/3B4/1PPK2bP/4r3/8 b - - 3 57"); //board_load_fen_unsafe(b, "8/8/2kr4/6R1/4K3/6P1/8/8 b - - 0 1");
board_print_fen(&b->pos, stdout); //board_load_fen_unsafe(b, "5R2/7k/P7/6pp/3B4/1PPK2bP/4r3/8 b - - 3 57");
//board_print_fen(b->pos, stdout);
board_print(&b->pos, NULL, stdout); board_print(&b->pos, NULL, stdout);
struct move moves[MOVE_MAX]; struct move moves[MOVE_MAX];
@@ -429,8 +444,17 @@ int main()
break; break;
} }
//struct move move = moves[0]; pthread_t timer;
struct search_result sr = search(b, b->pos.player, 7); atomic_bool searching;
atomic_init(&searching, true);
struct timeout_params timer_params = {
.x = &searching,
.v = false,
.us = 3*1000*1000,
};
pthread_create(&timer, NULL, set_after_timeout, &timer_params);
struct search_result sr = search(b, b->pos.player, 5, &searching);
struct move move = sr.move; struct move move = sr.move;
double const score = sr.score; double const score = sr.score;