Performance improvements
This commit is contained in:
9
Makefile
9
Makefile
@@ -4,13 +4,14 @@ CC := clang
|
|||||||
|
|
||||||
CFLAGS.gcc := -std=c23 -Wall -Wextra -Wconversion -Wno-unused-function
|
CFLAGS.gcc := -std=c23 -Wall -Wextra -Wconversion -Wno-unused-function
|
||||||
CFLAGS.gcc.release := -Ofast -march=native -DNDEBUG
|
CFLAGS.gcc.release := -Ofast -march=native -DNDEBUG
|
||||||
CFLAGS.gcc.debug := -ggdb -O0 -fsanitize=address
|
CFLAGS.gcc.debug := -ggdb -O1 -fsanitize=address
|
||||||
|
|
||||||
CFLAGS.clang := -std=c23 -g -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion
|
CFLAGS.clang := -std=c23 -g -Wall -Wextra -Wconversion -Wno-unused-function -Wimplicit-int-conversion
|
||||||
CFLAGS.clang.release := -O3 -ffast-math -march=native # -DNDEBUG
|
CFLAGS.clang.release := -O3 -ffast-math -march=native -DNDEBUG -DNSTATS
|
||||||
CFLAGS.clang.debug := -g3 -O0 -fsanitize=address
|
CFLAGS.clang.debug := -g3 -O1 -fsanitize=address,undefined
|
||||||
CFLAGS.clang.wasm := \
|
CFLAGS.clang.wasm := \
|
||||||
--target=wasm32-unknown-unknown -nostdlib -g \
|
--target=wasm32-unknown-unknown -nostdlib -g \
|
||||||
|
-DNSTATS \
|
||||||
-Wl,--export-all \
|
-Wl,--export-all \
|
||||||
-Wl,--no-entry
|
-Wl,--no-entry
|
||||||
|
|
||||||
@@ -33,4 +34,4 @@ mbb_bishop.h: codegen
|
|||||||
./codegen
|
./codegen
|
||||||
|
|
||||||
tests: tests.c mbb_rook.h mbb_bishop.h engine.h
|
tests: tests.c mbb_rook.h mbb_bishop.h engine.h
|
||||||
$(CC) -o $@ $(CFLAGS) -DUSE_PRINTF tests.c
|
$(CC) -o $@ $(CFLAGS) tests.c
|
||||||
|
|||||||
@@ -38,13 +38,12 @@ static void bitboard_print(Bb64 b, FILE* out)
|
|||||||
|
|
||||||
static void tt_print_stats(struct tt* tt, FILE* out)
|
static void tt_print_stats(struct tt* tt, FILE* out)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
|
||||||
double sat = 0;
|
double sat = 0;
|
||||||
for (size_t i = 0; i < TT_ENTRIES; ++i) {
|
for (size_t i = 0; i < TT_ENTRIES; ++i) {
|
||||||
if (tt->entries[i].init)
|
if (tt->entries[i].init)
|
||||||
sat += 1.0;
|
sat += 1.0;
|
||||||
}
|
}
|
||||||
double const pct = sat/TT_ENTRIES;
|
double const pct = sat/((double)tt->mask+1.0);
|
||||||
|
|
||||||
fprintf(out, "---- Stats ---\n");
|
fprintf(out, "---- Stats ---\n");
|
||||||
fprintf(out, "tt collisions: %"PRIu64"\n", tt->collisions);
|
fprintf(out, "tt collisions: %"PRIu64"\n", tt->collisions);
|
||||||
@@ -52,10 +51,6 @@ static void tt_print_stats(struct tt* tt, FILE* out)
|
|||||||
fprintf(out, "tt probes: %"PRIu64"\n", tt->probes);
|
fprintf(out, "tt probes: %"PRIu64"\n", tt->probes);
|
||||||
fprintf(out, "tt insertions: %"PRIu64"\n", tt->insertions);
|
fprintf(out, "tt insertions: %"PRIu64"\n", tt->insertions);
|
||||||
fprintf(out, "saturation: %.02lf\n", pct);
|
fprintf(out, "saturation: %.02lf\n", pct);
|
||||||
#else
|
|
||||||
(void)tt;
|
|
||||||
fprintf(out, "stats not available with NDEBUG\n");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void board_print_fen(struct pos const* pos, FILE* out)
|
static void board_print_fen(struct pos const* pos, FILE* out)
|
||||||
|
|||||||
@@ -15,6 +15,16 @@ static Bb64 cardinals_from_index(Sq8 p)
|
|||||||
return (FILE_MASK(sq_to_file(p)) | RANK_MASK(sq_to_rank(p)));
|
return (FILE_MASK(sq_to_file(p)) | RANK_MASK(sq_to_rank(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Bb64 cardinals(Bb64 p)
|
||||||
|
{
|
||||||
|
Bb64 b = 0ULL;
|
||||||
|
while (p) {
|
||||||
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
||||||
|
b |= cardinals_from_index(lsb);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
static Bb64 diagonals_from_index(Sq8 sq)
|
static Bb64 diagonals_from_index(Sq8 sq)
|
||||||
{
|
{
|
||||||
#ifdef CODEGEN
|
#ifdef CODEGEN
|
||||||
@@ -66,6 +76,15 @@ static Bb64 diagonals_from_index(Sq8 sq)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Bb64 diagonals(Bb64 p)
|
||||||
|
{
|
||||||
|
Bb64 b = 0ULL;
|
||||||
|
while (p) {
|
||||||
|
Sq8 const lsb = bitboard_pop_lsb(&p);
|
||||||
|
b |= diagonals_from_index(lsb);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* PIECE ATTACKS
|
/* PIECE ATTACKS
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct board {
|
|||||||
|
|
||||||
/* used for repeated board state detection only */
|
/* used for repeated board state detection only */
|
||||||
struct history {
|
struct history {
|
||||||
struct pos items[64];
|
uint64_t hashes[50]; /* array of zobrist hashes */
|
||||||
size_t length;
|
size_t length;
|
||||||
} hist;
|
} hist;
|
||||||
|
|
||||||
@@ -36,6 +36,9 @@ struct board {
|
|||||||
Piece8 mailbox[SQ_COUNT];
|
Piece8 mailbox[SQ_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* attack-sets depends on the definition of struct pos, whoops ¯\_(ツ)_/¯ */
|
||||||
|
#include "engine-attack-sets.h"
|
||||||
|
|
||||||
#define BOARD_INIT (struct board) { \
|
#define BOARD_INIT (struct board) { \
|
||||||
.pos = { \
|
.pos = { \
|
||||||
.fullmoves = 1, \
|
.fullmoves = 1, \
|
||||||
@@ -112,9 +115,12 @@ void board_init(struct board* b)
|
|||||||
*b = BOARD_INIT;
|
*b = BOARD_INIT;
|
||||||
}
|
}
|
||||||
if (b->tt.entries == NULL) {
|
if (b->tt.entries == NULL) {
|
||||||
b->tt.entries = sys_mmap_anon_shared(TT_ENTRIES * sizeof b->tt.entries[0],
|
size_t const entries = 1<<26;
|
||||||
|
size_t const mask = entries-1;
|
||||||
|
b->tt.entries = sys_mmap_anon_shared(entries * sizeof b->tt.entries[0],
|
||||||
SYS_PROT_READ | SYS_PROT_WRITE,
|
SYS_PROT_READ | SYS_PROT_WRITE,
|
||||||
SYS_MADV_RANDOM);
|
SYS_MADV_RANDOM);
|
||||||
|
b->tt.mask = mask;
|
||||||
if (b->tt.entries == NULL) {
|
if (b->tt.entries == NULL) {
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
@@ -232,13 +238,23 @@ enum move_result {
|
|||||||
MR_CHECKMATE,
|
MR_CHECKMATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct move_undo {
|
||||||
|
Piece8 captured_piece;
|
||||||
|
Piece8 moved_piece;
|
||||||
|
Sq8 captured_square;
|
||||||
|
Sq8 moved_square;
|
||||||
|
};
|
||||||
|
|
||||||
/* does not check validity */
|
/* does not check validity */
|
||||||
static enum move_result move_piece(struct pos* restrict pos,
|
static enum move_result move_piece(struct pos* restrict pos,
|
||||||
|
Side8 us,
|
||||||
struct history* restrict hist,
|
struct history* restrict hist,
|
||||||
Piece8 mailbox[restrict static SQ_COUNT],
|
Piece8 mailbox[restrict static SQ_COUNT],
|
||||||
struct move move)
|
struct move move)
|
||||||
{
|
{
|
||||||
Side8 const us = pos->moving_side;
|
struct move_undo undo;
|
||||||
|
|
||||||
|
//Side8 const us = pos->moving_side;
|
||||||
Side8 const them = other_side(us);
|
Side8 const them = other_side(us);
|
||||||
|
|
||||||
Piece8 const from_piece = mailbox[move.from];
|
Piece8 const from_piece = mailbox[move.from];
|
||||||
@@ -375,36 +391,42 @@ static enum move_result move_piece(struct pos* restrict pos,
|
|||||||
pos->fullmoves += (pos->moving_side == SIDE_BLACK);
|
pos->fullmoves += (pos->moving_side == SIDE_BLACK);
|
||||||
pos->halfmoves += 1;
|
pos->halfmoves += 1;
|
||||||
|
|
||||||
assuming(hist->length < 64);
|
assuming(hist->length < sizeof hist->hashes / sizeof *hist->hashes);
|
||||||
int repetitions = 0;
|
|
||||||
for (size_t i = 0; i < hist->length; ++i) {
|
/* check for repeated moves */
|
||||||
_Static_assert(sizeof *pos == sizeof hist->items[i]);
|
/* TODO: add move_do and move_undo to create proper repeat checks */
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
repetitions += 1;
|
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) {
|
||||||
hist->items[hist->length++] = *pos;
|
|
||||||
|
|
||||||
if (repetitions >= 3 || pos->halfmoves > 50) {
|
|
||||||
return MR_STALEMATE;
|
return MR_STALEMATE;
|
||||||
}
|
}
|
||||||
else if (repetitions > 0) {
|
|
||||||
return MR_REPEATS;
|
|
||||||
}
|
}
|
||||||
else if (pos->halfmoves > 50) {
|
for (; i < hist->length; ++i) {
|
||||||
|
if (hist->hashes[i] == pos->hash) {
|
||||||
return MR_STALEMATE;
|
return MR_STALEMATE;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
else if (attacks_to(pos, pos->pieces[them][PIECE_KING], 0ULL, 0ULL)
|
|
||||||
& ~pos->occupied[them]) {
|
|
||||||
return MR_CHECK;
|
|
||||||
}
|
}
|
||||||
#endif
|
hist->hashes[hist->length++] = pos->hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos->halfmoves > 50) {
|
||||||
|
return MR_STALEMATE;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return MR_NORMAL;
|
return MR_NORMAL;
|
||||||
}
|
}
|
||||||
@@ -417,7 +439,25 @@ static enum move_result move_piece(struct pos* restrict pos,
|
|||||||
static enum move_result board_move(struct board* b, struct move move)
|
static enum move_result board_move(struct board* b, struct move move)
|
||||||
{
|
{
|
||||||
return move_piece(&b->pos,
|
return move_piece(&b->pos,
|
||||||
|
b->pos.moving_side,
|
||||||
&b->hist,
|
&b->hist,
|
||||||
b->mailbox,
|
b->mailbox,
|
||||||
move);
|
move);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool pos_is_legal(struct pos const* restrict pos)
|
||||||
|
{
|
||||||
|
Side8 const mside = pos->moving_side;
|
||||||
|
Side8 const oside = other_side(mside);
|
||||||
|
|
||||||
|
if (pos->pieces[oside][PIECE_KING] & all_threats_from_side(pos, mside)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool board_is_legal(struct board* b)
|
||||||
|
{
|
||||||
|
return pos_is_legal(&b->pos);
|
||||||
|
}
|
||||||
|
|||||||
@@ -314,25 +314,25 @@ BITBOARD( \
|
|||||||
|
|
||||||
#define EARLY_POSITIONAL_BONUS_0 \
|
#define EARLY_POSITIONAL_BONUS_0 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
|
X(PIECE_PAWN, 0.02f, BOARD_CENTER_4X4) \
|
||||||
X(PIECE_KNIGHT, 0.05, BOARD_CENTER_4X4) \
|
X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_4X4) \
|
||||||
X(PIECE_BISHOP, 0.05, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \
|
X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \
|
||||||
X(PIECE_KING, 0.15, REL_KING_CASTLE_KINGSIDE) \
|
X(PIECE_KING, 0.15f, REL_KING_CASTLE_KINGSIDE) \
|
||||||
X(PIECE_QUEEN, -0.15, RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6) \
|
X(PIECE_QUEEN, -0.15f, RANK_MASK_3 | RANK_MASK_4 | RANK_MASK_5 | RANK_MASK_6) \
|
||||||
X(PIECE_ROOK, 0.10, FILE_MASK_D | FILE_MASK_E) \
|
X(PIECE_ROOK, 0.10f, FILE_MASK_D | FILE_MASK_E) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define EARLY_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.02f, BOARD_CENTER_2X2) \
|
||||||
X(PIECE_BISHOP, 0.05, REL_BISHOP_KING_ATTACK) \
|
X(PIECE_BISHOP, 0.05f, REL_BISHOP_KING_ATTACK) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define EARLY_POSITIONAL_BONUS_2 \
|
#define EARLY_POSITIONAL_BONUS_2 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, -0.18, ~REL_EARLY_PAWN_STRUCTURE) \
|
X(PIECE_PAWN, -0.18f, ~REL_EARLY_PAWN_STRUCTURE) \
|
||||||
X(PIECE_KNIGHT, -0.10, REL_UNDEVELOPED_KNIGHTS) \
|
X(PIECE_KNIGHT, -0.10f, REL_UNDEVELOPED_KNIGHTS) \
|
||||||
X(PIECE_BISHOP, -0.10, REL_UNDEVELOPED_BISHOPS) \
|
X(PIECE_BISHOP, -0.10f, REL_UNDEVELOPED_BISHOPS) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define EARLY_POSITIONAL_BONUS_3 \
|
#define EARLY_POSITIONAL_BONUS_3 \
|
||||||
@@ -345,58 +345,58 @@ BITBOARD( \
|
|||||||
|
|
||||||
#define MIDDLE_POSITIONAL_BONUS_0 \
|
#define MIDDLE_POSITIONAL_BONUS_0 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.02, BOARD_CENTER_4X4) \
|
X(PIECE_PAWN, 0.02f, BOARD_CENTER_4X4) \
|
||||||
X(PIECE_KNIGHT, 0.05, BOARD_CENTER_4X4) \
|
X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_4X4) \
|
||||||
X(PIECE_BISHOP, 0.05, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \
|
X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6 & (DIAGONAL_A1_H8 | DIAGONAL_A8_H1)) \
|
||||||
X(PIECE_KING, 0.15, REL_KING_CASTLE_KINGSIDE) \
|
X(PIECE_KING, 0.15f, REL_KING_CASTLE_KINGSIDE) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define MIDDLE_POSITIONAL_BONUS_1 \
|
#define MIDDLE_POSITIONAL_BONUS_1 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.02, BOARD_CENTER_2X2) \
|
X(PIECE_PAWN, 0.02f, BOARD_CENTER_2X2) \
|
||||||
X(PIECE_BISHOP, 0.07, REL_BISHOP_KING_ATTACK) \
|
X(PIECE_BISHOP, 0.07f, REL_BISHOP_KING_ATTACK) \
|
||||||
X(PIECE_QUEEN, 0.07, REL_BISHOP_KING_ATTACK) \
|
X(PIECE_QUEEN, 0.07f, REL_BISHOP_KING_ATTACK) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define MIDDLE_POSITIONAL_BONUS_2 \
|
#define MIDDLE_POSITIONAL_BONUS_2 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.02, REL_PAWN_KINGSIDE) \
|
X(PIECE_PAWN, 0.02f, REL_PAWN_KINGSIDE) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define MIDDLE_POSITIONAL_BONUS_3 \
|
#define MIDDLE_POSITIONAL_BONUS_3 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_BISHOP, 0.05, BOARD_CENTER_6X6) \
|
X(PIECE_BISHOP, 0.05f, BOARD_CENTER_6X6) \
|
||||||
X(PIECE_KNIGHT, 0.05, BOARD_CENTER_6X6) \
|
X(PIECE_KNIGHT, 0.05f, BOARD_CENTER_6X6) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* ------------------------------- end game -------------------------------- */
|
/* ------------------------------- end game -------------------------------- */
|
||||||
|
|
||||||
#define LATE_POSITIONAL_BONUS_0 \
|
#define LATE_POSITIONAL_BONUS_0 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.30, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \
|
X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6 | REL_RANK_5) \
|
||||||
X(PIECE_KING, 0.10, BOARD_CENTER_6X6) \
|
X(PIECE_KING, 0.10f, BOARD_CENTER_6X6) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define LATE_POSITIONAL_BONUS_1 \
|
#define LATE_POSITIONAL_BONUS_1 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.30, REL_RANK_7 | REL_RANK_6) \
|
X(PIECE_PAWN, 0.30f, REL_RANK_7 | REL_RANK_6) \
|
||||||
X(PIECE_KING, 0.10, BOARD_CENTER_4X4) \
|
X(PIECE_KING, 0.10f, BOARD_CENTER_4X4) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define LATE_POSITIONAL_BONUS_2 \
|
#define LATE_POSITIONAL_BONUS_2 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_PAWN, 0.70, REL_RANK_7) \
|
X(PIECE_PAWN, 0.70f, REL_RANK_7) \
|
||||||
X(PIECE_KING, 0.10, BOARD_CENTER_2X2) \
|
X(PIECE_KING, 0.10f, BOARD_CENTER_2X2) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
#define LATE_POSITIONAL_BONUS_3 \
|
#define LATE_POSITIONAL_BONUS_3 \
|
||||||
/* piece bonus area*/ \
|
/* piece bonus area*/ \
|
||||||
X(PIECE_KING, -0.50, ~BOARD_CENTER_6X6) \
|
X(PIECE_KING, -0.50f, ~BOARD_CENTER_6X6) \
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
struct posmod {
|
struct posmod {
|
||||||
Bb64 const area;
|
Bb64 const area;
|
||||||
double const val;
|
float const val;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct posmod positional_modifier(Side8 pl, enum game_progress st, size_t layer, Piece8 pz)
|
static inline struct posmod positional_modifier(Side8 pl, enum game_progress st, size_t layer, Piece8 pz)
|
||||||
|
|||||||
@@ -11,3 +11,8 @@
|
|||||||
#else
|
#else
|
||||||
#define assuming(expr) ((expr) ? 0 : (__builtin_unreachable(), 0))
|
#define assuming(expr) ((expr) ? 0 : (__builtin_unreachable(), 0))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define CACHE_LINE_SIZE 64
|
||||||
|
|
||||||
|
#define LIKELY(expr) __builtin_expect((expr), 1)
|
||||||
|
#define UNLIKELY(expr) __builtin_expect((expr), 0)
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ static void all_pseudolegal_moves(struct pos const* restrict pos,
|
|||||||
Bb64 const their_threats = all_threats_from_side(pos, them);
|
Bb64 const their_threats = all_threats_from_side(pos, them);
|
||||||
|
|
||||||
Bb64 const their_occ = pos->occupied[them];
|
Bb64 const their_occ = pos->occupied[them];
|
||||||
Bb64 const all_occ = pos->occupied[SIDE_WHITE] | pos->occupied[SIDE_BLACK];
|
|
||||||
|
|
||||||
Bb64 allowed;
|
Bb64 allowed;
|
||||||
if (type == MG_CAPTURES) {
|
if (type == MG_CAPTURES) {
|
||||||
|
|||||||
29
engine-tt.h
29
engine-tt.h
@@ -1,28 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
struct search_option {
|
struct search_option {
|
||||||
/* TODO: optimize order of fields and size */
|
uint64_t hash; // + 64 (64 / 8)
|
||||||
double score;
|
float score; // + 32 (96 / 12)
|
||||||
struct move move;
|
struct move move; // + 32 (128 / 16)
|
||||||
uint64_t hash;
|
int depth : 4; // + 4 (132 / 17)
|
||||||
uint8_t init : 1;
|
int init : 1; // + 1 (133 / 17)
|
||||||
enum tt_flag {TT_EXACT, TT_LOWER, TT_UPPER} flag : 3;
|
enum tt_flag {TT_EXACT, TT_LOWER, TT_UPPER} flag : 3; // +3 (136 / 17)
|
||||||
int8_t depth : 4;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TT_ADDRESS_BITS 24
|
#define TT_ADDRESS_BITS 24
|
||||||
#define TT_ENTRIES (1ULL<<TT_ADDRESS_BITS)
|
#define TT_ENTRIES (1ULL<<TT_ADDRESS_BITS)
|
||||||
|
#define TT_MASK (TT_ENTRIES-1)
|
||||||
struct tt {
|
struct tt {
|
||||||
struct search_option* entries/*[TT_ENTRIES]*/; /* must be initialized somewhere */
|
struct search_option* entries/*[TT_ENTRIES]*/; /* must be initialized somewhere */
|
||||||
|
size_t mask;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
/* stats */
|
/* stats */
|
||||||
uint64_t collisions;
|
uint64_t collisions;
|
||||||
uint64_t hits;
|
uint64_t hits;
|
||||||
uint64_t probes;
|
uint64_t probes;
|
||||||
uint64_t overwritten;
|
uint64_t overwritten;
|
||||||
uint64_t insertions;
|
uint64_t insertions;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct zobrist {
|
struct zobrist {
|
||||||
@@ -92,10 +91,10 @@ static inline uint64_t tt_hash_switch_side(uint64_t hash)
|
|||||||
|
|
||||||
static inline struct search_option tt_get(struct tt* tt, uint64_t hash)
|
static inline struct search_option tt_get(struct tt* tt, uint64_t hash)
|
||||||
{
|
{
|
||||||
uint64_t const addr = hash % TT_ENTRIES;
|
uint64_t const addr = hash & tt->mask;
|
||||||
struct search_option tte = tt->entries[addr];
|
struct search_option tte = tt->entries[addr];
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NSTATS
|
||||||
tt->probes += 1;
|
tt->probes += 1;
|
||||||
if (tte.init && tte.hash == hash) {
|
if (tte.init && tte.hash == hash) {
|
||||||
tt->hits += 1;
|
tt->hits += 1;
|
||||||
@@ -108,10 +107,10 @@ static inline struct search_option tt_get(struct tt* tt, uint64_t hash)
|
|||||||
|
|
||||||
static inline void tt_insert(struct tt* tt, uint64_t hash, struct search_option so)
|
static inline void tt_insert(struct tt* tt, uint64_t hash, struct search_option so)
|
||||||
{
|
{
|
||||||
uint64_t const addr = hash % TT_ENTRIES;
|
uint64_t const addr = hash & tt->mask;
|
||||||
so.init = true;
|
so.init = true;
|
||||||
tt->entries[addr] = so;
|
tt->entries[addr] = so;
|
||||||
#ifndef NDEBUG
|
#ifndef NSTATS
|
||||||
tt->insertions += 1;
|
tt->insertions += 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,7 @@ static inline void tt_insert(struct tt* tt, uint64_t hash, struct search_option
|
|||||||
*/
|
*/
|
||||||
static inline void tt_insert_maybe(struct tt* tt, uint64_t hash, struct search_option so)
|
static inline void tt_insert_maybe(struct tt* tt, uint64_t hash, struct search_option so)
|
||||||
{
|
{
|
||||||
uint64_t const addr = hash % TT_ENTRIES;
|
uint64_t const addr = hash & tt->mask;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
struct search_option* tte = &tt->entries[addr];
|
struct search_option* tte = &tt->entries[addr];
|
||||||
@@ -134,7 +133,7 @@ static inline void tt_insert_maybe(struct tt* tt, uint64_t hash, struct search_o
|
|||||||
|
|
||||||
so.init = true;
|
so.init = true;
|
||||||
tt->entries[addr] = so;
|
tt->entries[addr] = so;
|
||||||
#ifndef NDEBUG
|
#ifndef NSTATS
|
||||||
tt->insertions += 1;
|
tt->insertions += 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ typedef enum sq8 : uint8_t {
|
|||||||
#define X(file, rank) SQ_##file##rank = SQ_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
#define X(file, rank) SQ_##file##rank = SQ_FROM_RF(RANK_INDEX_##rank, FILE_INDEX_##file),
|
||||||
SQUARES_LIST
|
SQUARES_LIST
|
||||||
SQ_COUNT,
|
SQ_COUNT,
|
||||||
|
SQ_POISONED,
|
||||||
#undef X
|
#undef X
|
||||||
/* define iterator begin enum */
|
/* define iterator begin enum */
|
||||||
#define X(file, rank) SQ_BEGIN = SQ_##file##rank,
|
#define X(file, rank) SQ_BEGIN = SQ_##file##rank,
|
||||||
@@ -194,22 +195,24 @@ static char const* side_str[SIDE_COUNT] = {
|
|||||||
/* https://en.wikipedia.org/wiki/X_macro */
|
/* https://en.wikipedia.org/wiki/X_macro */
|
||||||
/* enum value white char white unicode black char black unicode */
|
/* enum value white char white unicode black char black unicode */
|
||||||
#define PIECES \
|
#define PIECES \
|
||||||
X(PIECE_PAWN, 1.0, 'P', 0x2659, 'p', 0x265F) \
|
X(PIECE_PAWN, 1.0f, 'P', 0x2659, 'p', 0x265F) \
|
||||||
X(PIECE_KING, 0.0, 'K', 0x2654, 'k', 0x265A) \
|
X(PIECE_KNIGHT, 3.1f, 'N', 0x2658, 'n', 0x265E) \
|
||||||
X(PIECE_QUEEN, 9.0, 'Q', 0x2655, 'q', 0x265B) \
|
X(PIECE_BISHOP, 3.2f, 'B', 0x2657, 'b', 0x265D) \
|
||||||
X(PIECE_BISHOP, 3.0, 'B', 0x2657, 'b', 0x265D) \
|
X(PIECE_ROOK, 5.0f, 'R', 0x2656, 'r', 0x265C) \
|
||||||
X(PIECE_ROOK, 5.0, 'R', 0x2656, 'r', 0x265C) \
|
X(PIECE_QUEEN, 9.0f, 'Q', 0x2655, 'q', 0x265B) \
|
||||||
X(PIECE_KNIGHT, 3.0, 'N', 0x2658, 'n', 0x265E)
|
X(PIECE_KING, 16.0f, 'K', 0x2654, 'k', 0x265A) \
|
||||||
|
/**/
|
||||||
|
|
||||||
typedef enum piece : uint8_t {
|
typedef enum piece : uint8_t {
|
||||||
#define X(e, v, wc, wu, bc, bu) e,
|
#define X(e, v, wc, wu, bc, bu) e,
|
||||||
PIECES
|
PIECES
|
||||||
PIECE_COUNT,
|
PIECE_COUNT,
|
||||||
PIECE_BEGIN = 0,
|
PIECE_BEGIN = 0,
|
||||||
|
PIECE_POISONED, /* used as default undefined value in debug builds */
|
||||||
#undef X
|
#undef X
|
||||||
} Piece8;
|
} Piece8;
|
||||||
|
|
||||||
static double piece_value[PIECE_COUNT] = {
|
static float piece_value[PIECE_COUNT] = {
|
||||||
#define X(e, v, wc, wu, bc, bu) [e] = v,
|
#define X(e, v, wc, wu, bc, bu) [e] = v,
|
||||||
PIECES
|
PIECES
|
||||||
#undef X
|
#undef X
|
||||||
@@ -260,6 +263,7 @@ static int const piece_unicode[SIDE_COUNT][PIECE_COUNT] = {
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
MATTR_PROMOTE = 1<<0,
|
MATTR_PROMOTE = 1<<0,
|
||||||
|
MATTR_POISONED = 1<<1,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct move {
|
struct move {
|
||||||
@@ -272,9 +276,11 @@ struct move {
|
|||||||
_Static_assert(sizeof(struct move) == 4,
|
_Static_assert(sizeof(struct move) == 4,
|
||||||
"this static assuming is here to check when sizeof(move) changes");
|
"this static assuming is here to check when sizeof(move) changes");
|
||||||
|
|
||||||
#define NULL_MOVE (struct move){0}
|
#define MOVE_NULL (struct move){0}
|
||||||
|
|
||||||
#define IS_NULL_MOVE(m) ((m).from == (m).to)
|
#define MOVE_POISONED (struct move) {.from = SQ_POISONED, .to = SQ_POISONED, .attr = MATTR_POISONED }
|
||||||
|
|
||||||
|
#define IS_MOVE_NULL(m) ((m).from == (m).to)
|
||||||
|
|
||||||
/* ----------- castle_direction ----------- */
|
/* ----------- castle_direction ----------- */
|
||||||
enum castle_direction {
|
enum castle_direction {
|
||||||
@@ -283,3 +289,32 @@ enum castle_direction {
|
|||||||
CASTLE_QUEENSIDE,
|
CASTLE_QUEENSIDE,
|
||||||
CASTLE_COUNT,
|
CASTLE_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------- stop flag --------------- */
|
||||||
|
|
||||||
|
/* occupy an entire cache line to avoid invalidation from neighboring writes */
|
||||||
|
struct searching_flag {
|
||||||
|
alignas(CACHE_LINE_SIZE) atomic_uint_fast8_t v;
|
||||||
|
uint8_t pad[CACHE_LINE_SIZE - sizeof(atomic_uint_fast8_t)];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void searching_init(struct searching_flag* restrict sf)
|
||||||
|
{
|
||||||
|
atomic_init(&sf->v, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool searching_still(struct searching_flag const* restrict sf)
|
||||||
|
{
|
||||||
|
return atomic_load_explicit(&sf->v, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void searching_start(struct searching_flag* restrict sf)
|
||||||
|
{
|
||||||
|
atomic_store_explicit(&sf->v, 1, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void searching_stop(struct searching_flag* restrict sf)
|
||||||
|
{
|
||||||
|
atomic_store_explicit(&sf->v, 0, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|||||||
190
engine.h
190
engine.h
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/* WIP switches */
|
||||||
|
#define NEW_SEARCHING
|
||||||
|
|
||||||
#include "libc-lite.h"
|
#include "libc-lite.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
|
|
||||||
@@ -20,8 +23,8 @@ static uint64_t g_ab_node_volume = 0;
|
|||||||
|
|
||||||
/* --------------------------- MOVE SEARCH --------------------------------- */
|
/* --------------------------- MOVE SEARCH --------------------------------- */
|
||||||
|
|
||||||
#define SCORE_INF 1e30
|
#define SCORE_INF 1e10f
|
||||||
#define SCORE_CHECKMATE 999.0
|
#define SCORE_CHECKMATE 999.0f
|
||||||
|
|
||||||
/* for initial ordering of moves in alphabeta search */
|
/* for initial ordering of moves in alphabeta search */
|
||||||
static void move_compute_appeal(struct move* restrict m,
|
static void move_compute_appeal(struct move* restrict m,
|
||||||
@@ -38,16 +41,16 @@ static void move_compute_appeal(struct move* restrict m,
|
|||||||
n += (uint8_t)piece_value[mailbox[m->to]];
|
n += (uint8_t)piece_value[mailbox[m->to]];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t mmv_lva_bonus = 16*n - (uint8_t)piece_value[atk];
|
uint8_t mmv_lva_bonus = (uint8_t)(16.0f*(float)n - piece_value[atk]);
|
||||||
|
|
||||||
m->appeal = mmv_lva_bonus;
|
m->appeal = mmv_lva_bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double 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
|
/* this function always evaluates from white's perspective before
|
||||||
eventually flipping the sign based on `pos` */
|
eventually flipping the sign based on `pos` */
|
||||||
double score = 0.0;
|
float score = 0.0f;
|
||||||
|
|
||||||
Bb64 const occw = pos->occupied[SIDE_WHITE];
|
Bb64 const occw = pos->occupied[SIDE_WHITE];
|
||||||
Bb64 const occb = pos->occupied[SIDE_BLACK];
|
Bb64 const occb = pos->occupied[SIDE_BLACK];
|
||||||
@@ -55,57 +58,67 @@ static double board_score_heuristic(struct pos const* pos)
|
|||||||
|
|
||||||
enum game_progress const gp = endgameness(pos);
|
enum game_progress const gp = endgameness(pos);
|
||||||
|
|
||||||
if (pos->pieces[SIDE_WHITE][PIECE_KING] == 0) score -= (double)SCORE_CHECKMATE;
|
if (pos->pieces[SIDE_WHITE][PIECE_KING] == 0) {
|
||||||
if (pos->pieces[SIDE_BLACK][PIECE_KING] == 0) score += (double)SCORE_CHECKMATE;
|
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) {
|
for (Piece8 p = PIECE_BEGIN; p < PIECE_COUNT; ++p) {
|
||||||
/* raw material value */
|
/* raw material value */
|
||||||
score += piece_value[p] *
|
score += piece_value[p] *
|
||||||
((double)bitboard_popcount(pos->pieces[SIDE_WHITE][p]) -
|
((float)bitboard_popcount(pos->pieces[SIDE_WHITE][p]) -
|
||||||
(double)bitboard_popcount(pos->pieces[SIDE_BLACK][p]));
|
(float)bitboard_popcount(pos->pieces[SIDE_BLACK][p]));
|
||||||
|
|
||||||
/* very minor advantage for threat projection to break tied moves */
|
/* very minor advantage for threat projection to break tied moves */
|
||||||
if (p != PIECE_PAWN) {
|
//if (p != PIECE_PAWN) {
|
||||||
score += 0.001 * (
|
// score += 0.001f * (
|
||||||
(double)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_WHITE][p], occall))
|
// (float)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_WHITE][p], occall))
|
||||||
- (double)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_BLACK][p], occall)));
|
// - (float)bitboard_popcount(non_pawn_piece_attacks(p, pos->pieces[SIDE_BLACK][p], occall)));
|
||||||
}
|
//}
|
||||||
|
|
||||||
/* 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(SIDE_WHITE, gp, i, p).val *
|
score += positional_modifier(SIDE_WHITE, gp, i, p).val *
|
||||||
(
|
(
|
||||||
(double)bitboard_popcount(
|
(float)bitboard_popcount(
|
||||||
pos->pieces[SIDE_WHITE][p]
|
pos->pieces[SIDE_WHITE][p]
|
||||||
& positional_modifier(SIDE_WHITE, gp, i, p).area)
|
& positional_modifier(SIDE_WHITE, gp, i, p).area)
|
||||||
- (double)bitboard_popcount(
|
- (float)bitboard_popcount(
|
||||||
pos->pieces[SIDE_BLACK][p]
|
pos->pieces[SIDE_BLACK][p]
|
||||||
& positional_modifier(SIDE_BLACK, gp, i, p).area)
|
& 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]);
|
||||||
|
|
||||||
/* pawns defending pieces are desired */
|
/* pawns defending pieces are desired */
|
||||||
score += 0.03 * (
|
score += 0.03f * (
|
||||||
(double)bitboard_popcount(
|
(float)bitboard_popcount(
|
||||||
pawn_attacks_white(pos->pieces[SIDE_WHITE][PIECE_PAWN]) & occw
|
pawn_attacks_white(pos->pieces[SIDE_WHITE][PIECE_PAWN]) & occw
|
||||||
)
|
)
|
||||||
- (double)bitboard_popcount(
|
- (float)bitboard_popcount(
|
||||||
pawn_attacks_black(pos->pieces[SIDE_BLACK][PIECE_PAWN]) & occb
|
pawn_attacks_black(pos->pieces[SIDE_BLACK][PIECE_PAWN]) & occb
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
/* stacked pawns are bad */
|
/* stacked pawns are bad */
|
||||||
const double k = 0.30;
|
const float k = 0.30f;
|
||||||
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) {
|
||||||
uint64_t wstk = bitboard_popcount(pos->pieces[SIDE_WHITE][PIECE_PAWN] & FILE_MASK(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));
|
uint64_t bstk = bitboard_popcount(pos->pieces[SIDE_BLACK][PIECE_PAWN] & FILE_MASK(fi));
|
||||||
|
|
||||||
score -= k * (double)(wstk - (wstk == 1));
|
score -= k * (float)(wstk - (wstk == 1));
|
||||||
score += k * (double)(bstk - (bstk == 1));
|
score += k * (float)(bstk - (bstk == 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
double sign = (pos->moving_side == SIDE_WHITE) ? 1.0 : -1.0;
|
end:
|
||||||
|
float sign = (pos->moving_side == SIDE_WHITE) ? 1.0f : -1.0f;
|
||||||
|
|
||||||
return sign*score;
|
return sign*score;
|
||||||
}
|
}
|
||||||
@@ -131,21 +144,21 @@ struct move moves_linear_search(struct move moves[restrict static MOVE_MAX]
|
|||||||
|
|
||||||
/* quiescence is a deep search that only considers captures */
|
/* quiescence is a deep search that only considers captures */
|
||||||
static
|
static
|
||||||
double quiesce(struct pos const* pos,
|
float quiesce(struct pos const* pos,
|
||||||
Piece8 mailbox[restrict static SQ_COUNT],
|
Piece8 mailbox[restrict static SQ_COUNT],
|
||||||
Side8 us,
|
Side8 us,
|
||||||
double alpha,
|
float alpha,
|
||||||
double beta,
|
float beta,
|
||||||
int8_t depth)
|
int8_t depth)
|
||||||
{
|
{
|
||||||
if (pos->pieces[us][PIECE_KING] == 0) {
|
if (pos->pieces[us][PIECE_KING] == 0) {
|
||||||
return -SCORE_CHECKMATE;
|
return -(SCORE_CHECKMATE + (float)depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
Side8 const them = other_side(us);
|
Side8 const them = other_side(us);
|
||||||
|
|
||||||
double score = board_score_heuristic(pos);
|
float score = board_score_heuristic(pos);
|
||||||
double highscore = score;
|
float highscore = score;
|
||||||
|
|
||||||
if (highscore >= beta) {
|
if (highscore >= beta) {
|
||||||
return highscore;
|
return highscore;
|
||||||
@@ -155,13 +168,11 @@ double quiesce(struct pos const* pos,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t move_count = 0;
|
size_t move_count = 0;
|
||||||
|
|
||||||
struct move moves[MOVE_MAX];
|
struct move moves[MOVE_MAX];
|
||||||
|
|
||||||
all_pseudolegal_moves(pos, MG_CAPTURES, us, &move_count, moves);
|
all_pseudolegal_moves(pos, MG_CAPTURES, us, &move_count, moves);
|
||||||
if (move_count == 0) {
|
if (move_count == 0) {
|
||||||
/* TODO: detect stalemate */
|
return -(SCORE_CHECKMATE + (float)depth);
|
||||||
return -(999.0 + (double)depth);
|
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < move_count; ++i) {
|
for (size_t i = 0; i < move_count; ++i) {
|
||||||
move_compute_appeal(&moves[i], pos, us, mailbox);
|
move_compute_appeal(&moves[i], pos, us, mailbox);
|
||||||
@@ -180,7 +191,7 @@ double quiesce(struct pos const* pos,
|
|||||||
/* history is irrelevant when all moves are captures */
|
/* history is irrelevant when all moves are captures */
|
||||||
static struct history hist;
|
static struct history hist;
|
||||||
hist.length = 0;
|
hist.length = 0;
|
||||||
(void)move_piece(&poscpy, &hist, mailbox_cpy, m);
|
(void)move_piece(&poscpy, us, &hist, mailbox_cpy, m);
|
||||||
|
|
||||||
score = -quiesce(&poscpy, mailbox_cpy, them, -beta, -alpha, depth - 1);
|
score = -quiesce(&poscpy, mailbox_cpy, them, -beta, -alpha, depth - 1);
|
||||||
|
|
||||||
@@ -208,15 +219,26 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
Piece8 mailbox[restrict static SQ_COUNT],
|
Piece8 mailbox[restrict static SQ_COUNT],
|
||||||
Side8 us,
|
Side8 us,
|
||||||
int8_t depth,
|
int8_t depth,
|
||||||
double alpha,
|
float alpha,
|
||||||
double beta,
|
float beta
|
||||||
atomic_bool* searching)
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
,
|
||||||
|
struct searching_flag const* restrict searching
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
g_ab_node_volume += 1;
|
g_ab_node_volume += 1;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
if (!searching_still(searching)) {
|
||||||
|
return (struct search_option) { .init = false, .move = MOVE_NULL };
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (pos->pieces[us][PIECE_KING] == 0) {
|
if (pos->pieces[us][PIECE_KING] == 0) {
|
||||||
return (struct search_option) {
|
return (struct search_option) {
|
||||||
.score = -(SCORE_CHECKMATE + (double)depth),
|
.score = -(SCORE_CHECKMATE + (float)depth),
|
||||||
.move = (struct move){0},
|
.move = (struct move){0},
|
||||||
.depth = 0,
|
.depth = 0,
|
||||||
.hash = pos->hash,
|
.hash = pos->hash,
|
||||||
@@ -226,7 +248,7 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (depth <= 0) {
|
if (depth <= 0) {
|
||||||
double sc = quiesce(pos, mailbox, us, alpha, beta, 0);
|
float sc = quiesce(pos, mailbox, us, alpha, beta, 0);
|
||||||
return (struct search_option){
|
return (struct search_option){
|
||||||
.score = sc,
|
.score = sc,
|
||||||
.move = (struct move){0},
|
.move = (struct move){0},
|
||||||
@@ -237,15 +259,20 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
double const alpha_orig = alpha;
|
float const alpha_orig = alpha;
|
||||||
|
|
||||||
struct move moves[MOVE_MAX];
|
struct move moves[MOVE_MAX];
|
||||||
size_t move_count = 0;
|
size_t move_count = 0;
|
||||||
|
|
||||||
|
float best_score = -SCORE_INF;
|
||||||
|
struct move best_move = MOVE_NULL;
|
||||||
|
bool has_principal_move = false;
|
||||||
|
|
||||||
struct search_option tte = tt_get(tt, pos->hash);
|
struct search_option tte = tt_get(tt, pos->hash);
|
||||||
if (tte.init && tte.hash == pos->hash) {
|
if (tte.init && tte.hash == pos->hash) {
|
||||||
if (tte.depth >= depth) {
|
if (tte.depth >= depth) {
|
||||||
if (tte.flag == TT_EXACT) {
|
if (tte.flag == TT_EXACT) {
|
||||||
|
assuming(tte.init);
|
||||||
return tte;
|
return tte;
|
||||||
} else if (tte.flag == TT_LOWER) {
|
} else if (tte.flag == TT_LOWER) {
|
||||||
if (tte.score > alpha) alpha = tte.score;
|
if (tte.score > alpha) alpha = tte.score;
|
||||||
@@ -254,6 +281,7 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (alpha >= beta) {
|
if (alpha >= beta) {
|
||||||
|
assuming(tte.init);
|
||||||
return tte;
|
return tte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,23 +289,23 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
moves[move_count] = tte.move;
|
moves[move_count] = tte.move;
|
||||||
moves[move_count].appeal = APPEAL_MAX;
|
moves[move_count].appeal = APPEAL_MAX;
|
||||||
++move_count;
|
++move_count;
|
||||||
|
has_principal_move = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double best_score = -SCORE_INF;
|
|
||||||
struct move best_move = NULL_MOVE;
|
|
||||||
|
|
||||||
enum move_gen_type const types[] = {MG_CAPTURES, MG_CHECKS, MG_QUIETS};
|
enum move_gen_type const types[] = {MG_CAPTURES, MG_CHECKS, MG_QUIETS};
|
||||||
for (size_t i = 0; i < sizeof types / sizeof *types; ++i) {
|
for (size_t i = 0; i < sizeof types / sizeof *types; ++i) {
|
||||||
all_pseudolegal_moves(pos, types[i], us, &move_count, moves);
|
all_pseudolegal_moves(pos, types[i], us, &move_count, moves);
|
||||||
|
|
||||||
for (size_t i = 0; i < move_count; ++i) {
|
for (size_t i = (size_t)has_principal_move; i < move_count; ++i) {
|
||||||
move_compute_appeal(&moves[i], pos, us, mailbox);
|
move_compute_appeal(&moves[i], pos, us, mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (move_count > 0) {
|
while (move_count > 0) {
|
||||||
if (!atomic_load_explicit(searching, memory_order_relaxed)) {
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
if (!searching_still(searching)) {
|
||||||
return (struct search_option) { .init = false };
|
return (struct search_option) { .init = false };
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
struct move m = moves_linear_search(moves, &move_count);
|
struct move m = moves_linear_search(moves, &move_count);
|
||||||
|
|
||||||
size_t const old_hist_len = hist->length;
|
size_t const old_hist_len = hist->length;
|
||||||
@@ -285,9 +313,9 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
Piece8 mailbox_cpy[SQ_COUNT];
|
Piece8 mailbox_cpy[SQ_COUNT];
|
||||||
my_memcpy(mailbox_cpy, mailbox, sizeof mailbox_cpy);
|
my_memcpy(mailbox_cpy, mailbox, sizeof mailbox_cpy);
|
||||||
|
|
||||||
enum move_result r = move_piece(&pos_cpy, hist, mailbox_cpy, m);
|
enum move_result r = move_piece(&pos_cpy, us, hist, mailbox_cpy, m);
|
||||||
|
|
||||||
double score;
|
float score = 0.0;
|
||||||
|
|
||||||
if (r == MR_STALEMATE || r == MR_REPEATS) {
|
if (r == MR_STALEMATE || r == MR_REPEATS) {
|
||||||
score = 0.0;
|
score = 0.0;
|
||||||
@@ -299,15 +327,18 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
other_side(us),
|
other_side(us),
|
||||||
depth - 1,
|
depth - 1,
|
||||||
-beta,
|
-beta,
|
||||||
-alpha,
|
-alpha
|
||||||
searching);
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
,
|
||||||
|
searching
|
||||||
|
#endif
|
||||||
|
);
|
||||||
if (!so.init) {
|
if (!so.init) {
|
||||||
hist->length = old_hist_len;
|
hist->length = old_hist_len;
|
||||||
return (struct search_option){ .init = false, .move = NULL_MOVE };
|
return so;
|
||||||
}
|
}
|
||||||
score = -so.score;
|
score = -so.score;
|
||||||
}
|
}
|
||||||
|
|
||||||
hist->length = old_hist_len;
|
hist->length = old_hist_len;
|
||||||
|
|
||||||
if (score > best_score) {
|
if (score > best_score) {
|
||||||
@@ -325,8 +356,8 @@ struct search_option alphabeta_search(struct pos const* pos,
|
|||||||
|
|
||||||
finish_search:
|
finish_search:
|
||||||
|
|
||||||
if (IS_NULL_MOVE(best_move)) {
|
if (IS_MOVE_NULL(best_move)) {
|
||||||
return (struct search_option){ .init = false, .move = NULL_MOVE };
|
return (struct search_option){ .init = true, .move = MOVE_NULL, .score = -(SCORE_CHECKMATE + (float)depth) };
|
||||||
}
|
}
|
||||||
|
|
||||||
enum tt_flag flag = TT_EXACT;
|
enum tt_flag flag = TT_EXACT;
|
||||||
@@ -348,15 +379,31 @@ finish_search:
|
|||||||
|
|
||||||
tt_insert(tt, pos->hash, out);
|
tt_insert(tt, pos->hash, out);
|
||||||
|
|
||||||
|
assuming(out.init);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
struct search_result {struct move move; double score;}
|
struct search_result {struct move move; float score;}
|
||||||
search(struct board* b, Side8 us, int8_t max_depth, atomic_bool* searching)
|
search(
|
||||||
|
struct board* restrict b,
|
||||||
|
Side8 us,
|
||||||
|
int8_t max_depth
|
||||||
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
,
|
||||||
|
struct searching_flag const* restrict searching
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
struct move moves[MOVE_MAX];
|
struct move moves[MOVE_MAX];
|
||||||
size_t move_count = 0;
|
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);
|
all_pseudolegal_moves(&b->pos, MG_ALL, us, &move_count, moves);
|
||||||
|
|
||||||
assuming(move_count);
|
assuming(move_count);
|
||||||
@@ -365,26 +412,36 @@ struct search_result {struct move move; double score;}
|
|||||||
|
|
||||||
g_ab_node_volume = 0;
|
g_ab_node_volume = 0;
|
||||||
|
|
||||||
double score = 0.0;
|
float score = 0.0;
|
||||||
|
|
||||||
for (int8_t d = 1;
|
for (int8_t d = 1;
|
||||||
d <= max_depth && atomic_load_explicit(searching, memory_order_relaxed);
|
d <= max_depth
|
||||||
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
&& searching_still(searching)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
++d)
|
++d)
|
||||||
{
|
{
|
||||||
double window = SCORE_INF; /* temp debug solution */
|
float window = 2*SCORE_INF; /* temp debug solution */
|
||||||
double alpha = score - window;
|
float alpha = score - window;
|
||||||
double beta = score + window;
|
float beta = score + window;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct search_option so =
|
struct search_option so =
|
||||||
alphabeta_search(&b->pos, &b->hist, &b->tt, b->mailbox, us, d, alpha, beta, searching);
|
alphabeta_search(&b->pos, &b->hist, &b->tt, b->mailbox, us, d, alpha, beta
|
||||||
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
|
, searching
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
if (atomic_load_explicit(searching, memory_order_relaxed) == false) {
|
#ifdef FEATURE_STOPPABLE_SEARCH
|
||||||
break;
|
if (!searching_still(searching)) {
|
||||||
|
goto breakbreak;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (IS_NULL_MOVE(so.move)) {
|
if (IS_MOVE_NULL(so.move)) {
|
||||||
break;
|
goto breakbreak;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (so.score > alpha && so.score < beta) {
|
if (so.score > alpha && so.score < beta) {
|
||||||
@@ -404,14 +461,15 @@ struct search_result {struct move move; double score;}
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#ifdef USE_PRINTF
|
#ifdef FEATURE_USE_PRINTF
|
||||||
fprintf(stderr, "depth: %hhd\n", d);
|
fprintf(stderr, "depth: %hhd\n", d);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
breakbreak:
|
||||||
|
|
||||||
#undef SCORE_INF
|
#undef SCORE_INF
|
||||||
|
|
||||||
#ifdef USE_PRINTF
|
#ifdef FEATURE_USE_PRINTF
|
||||||
fprintf(stderr, "nodes searched: %'llu\n", g_ab_node_volume);
|
fprintf(stderr, "nodes searched: %'llu\n", g_ab_node_volume);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
39
tests.c
39
tests.c
@@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
#define USE_PRINTF
|
#define FEATURE_STOPPABLE_SEARCH
|
||||||
|
#define FEATURE_USE_PRINTF
|
||||||
|
#undef NSTATS
|
||||||
|
|
||||||
#define _XOPEN_SOURCE 500
|
#define _XOPEN_SOURCE 500
|
||||||
#include <unistd.h> /* usleep */
|
#include <unistd.h> /* usleep */
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@@ -390,15 +393,15 @@ static void test_bishops(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct timeout_params {
|
struct timeout_params {
|
||||||
atomic_bool* x;
|
struct searching_flag* x;
|
||||||
bool v;
|
uint32_t v;
|
||||||
useconds_t us;
|
useconds_t us;
|
||||||
};
|
};
|
||||||
void* set_after_timeout(void* x)
|
void* set_after_timeout(void* x)
|
||||||
{
|
{
|
||||||
struct timeout_params* p = x;
|
struct timeout_params* p = x;
|
||||||
usleep(p->us);
|
usleep(p->us);
|
||||||
*p->x = p->v;
|
searching_stop(p->x);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +412,8 @@ 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));
|
||||||
printf("sizeof board: %zu\n", sizeof (struct board));
|
printf("sizeof board: %zu\n", sizeof (struct board));
|
||||||
|
printf("sizeof search_option: %zu\n", sizeof (struct search_option));
|
||||||
|
printf("sizeof all tt entries: %zu\n", (1<<26) * sizeof (struct search_option));
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
test_rooks();
|
test_rooks();
|
||||||
@@ -431,6 +436,7 @@ int main()
|
|||||||
//board_load_fen_unsafe(b, "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(b, "8/8/2kr4/6R1/4K3/6P1/8/8 b - - 0 1");
|
//board_load_fen_unsafe(b, "8/8/2kr4/6R1/4K3/6P1/8/8 b - - 0 1");
|
||||||
//board_load_fen_unsafe(b, "8/8/5R2/8/2K3PP/1B2k3/8/8 b - - 1 4");
|
//board_load_fen_unsafe(b, "8/8/5R2/8/2K3PP/1B2k3/8/8 b - - 1 4");
|
||||||
|
//board_load_fen_unsafe(b, "4r1k1/b1P1B3/P5pK/1p4P1/7q/8/4p3/1R6 w - - 1 54");
|
||||||
//board_print_fen(b->pos, stdout);
|
//board_print_fen(b->pos, stdout);
|
||||||
board_print(&b->pos, NULL, stdout, print_threats);
|
board_print(&b->pos, NULL, stdout, print_threats);
|
||||||
|
|
||||||
@@ -438,6 +444,7 @@ int main()
|
|||||||
size_t move_count;
|
size_t move_count;
|
||||||
|
|
||||||
for (int turn = 0; turn < 200; ++turn) {
|
for (int turn = 0; turn < 200; ++turn) {
|
||||||
|
/*
|
||||||
move_count = 0;
|
move_count = 0;
|
||||||
all_pseudolegal_moves(&b->pos, MG_ALL, b->pos.moving_side, &move_count, moves);
|
all_pseudolegal_moves(&b->pos, MG_ALL, b->pos.moving_side, &move_count, moves);
|
||||||
|
|
||||||
@@ -448,10 +455,12 @@ int main()
|
|||||||
board_print(&b->pos, NULL, stdout, print_threats);
|
board_print(&b->pos, NULL, stdout, print_threats);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pthread_t timer;
|
pthread_t timer;
|
||||||
atomic_bool searching;
|
struct searching_flag searching;
|
||||||
atomic_init(&searching, true);
|
searching_start(&searching);
|
||||||
|
//atomic_init(&searching, 1);
|
||||||
#if 1
|
#if 1
|
||||||
struct timeout_params timer_params = {
|
struct timeout_params timer_params = {
|
||||||
.x = &searching,
|
.x = &searching,
|
||||||
@@ -482,11 +491,18 @@ int main()
|
|||||||
|
|
||||||
enum move_result const r = board_move(b, move);
|
enum move_result const r = board_move(b, move);
|
||||||
|
|
||||||
|
/* illegal board state from an engine move (i.e. hanging king) means checkmate */
|
||||||
|
if (!board_is_legal(b)) {
|
||||||
|
printf("checkmate!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
board_print_fen(&b->pos, stdout);
|
board_print_fen(&b->pos, stdout);
|
||||||
tt_print_stats(&b->tt, stdout);
|
tt_print_stats(&b->tt, stdout);
|
||||||
board_print(&b->pos, &move, stdout, print_threats);
|
board_print(&b->pos, &move, stdout, print_threats);
|
||||||
fprintf(stderr, "board hist len: %zu\n", b->hist.length);
|
fprintf(stderr, "board hist len: %zu\n", b->hist.length);
|
||||||
|
fprintf(stderr, "\n------------------------\n\n\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (r == MR_STALEMATE) {
|
if (r == MR_STALEMATE) {
|
||||||
@@ -496,14 +512,13 @@ int main()
|
|||||||
|
|
||||||
if (b->pos.pieces[SIDE_WHITE][PIECE_KING] == 0ULL) {
|
if (b->pos.pieces[SIDE_WHITE][PIECE_KING] == 0ULL) {
|
||||||
printf("white king gone!!\n");
|
printf("white king gone!!\n");
|
||||||
exit(1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
|
||||||
if (b->pos.pieces[SIDE_BLACK][PIECE_KING] == 0ULL) {
|
|
||||||
printf("black king gone!!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//usleep(1000000);
|
if (b->pos.pieces[SIDE_BLACK][PIECE_KING] == 0ULL) {
|
||||||
|
printf("black king gone!!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|||||||
Reference in New Issue
Block a user