diff --git a/Makefile b/Makefile index bae6a13..11a0c41 100644 --- a/Makefile +++ b/Makefile @@ -55,8 +55,9 @@ CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i) ##################################### pre-processor flags CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR) -CPPFLAGS += -DNDEBUG # assert +CPPFLAGS += -DNDEBUG # assert (unused) CPPFLAGS += -DWARN_ON # brlib bug.h +CPPFLAGS += -DBUG_ON # brlib bug.h #CPPFLAGS += -DDEBUG # global - unused #CPPFLAGS += -DDEBUG_DEBUG # enable log() functions @@ -68,6 +69,9 @@ CPPFLAGS += -DWARN_ON # brlib bug.h # fen.c #CPPFLAGS += -DDEBUG_FEN # FEN decoding +# hash.c +#CPPFLAGS += -HASH_VERIFY # chk zobrist consistency + # attack.c #CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers #CPPFLAGS += -DDEBUG_ATTACK_PINNERS # sq_pinners details @@ -78,9 +82,6 @@ CPPFLAGS += -DWARN_ON # brlib bug.h CPPFLAGS += -DDIAGRAM_SYM # UTF8 symbols in diagrams -# Never comment this one ! -CPPFLAGS += -DBUG_ON # brlib bug.h - # remove extraneous spaces (due to spaces before comments) CPPFLAGS := $(strip $(CPPFLAGS)) @@ -342,12 +343,12 @@ TEST += movedo-test perft-test PIECE_OBJS := piece.o FEN_OBJS := $(PIECE_OBJS) fen.o position.o bitboard.o board.o \ - hyperbola-quintessence.o attack.o + hyperbola-quintessence.o attack.o hash.o init.o BB_OBJS := $(FEN_OBJS) MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o ATTACK_OBJS := $(MOVEGEN_OBJS) -MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o -PERFT_OBJS := $(MOVEDO_OBJS) search.o misc.o +MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o misc.o +PERFT_OBJS := $(MOVEDO_OBJS) search.o TEST := $(addprefix $(BINDIR)/,$(TEST)) diff --git a/brlib b/brlib index 8ff163d..7bedfdd 160000 --- a/brlib +++ b/brlib @@ -1 +1 @@ -Subproject commit 8ff163dcf569105bbdec28860eff100280e32898 +Subproject commit 7bedfddfba09e9f93615fee2623d44a9d0e1a605 diff --git a/scripts/fetch-all.sh b/scripts/fetch-all.sh new file mode 100755 index 0000000..df04b1c --- /dev/null +++ b/scripts/fetch-all.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +origin=origin + +declare -a remotes local + +readarray -t remotes < <(git remote) +declare -A aremotes alocal_b + +# fetch all remotes +for remote in "${remotes[@]}"; do + aremotes["$remote"]=1 + echo doing git fetch -a "$remote" +done + +# get local branches +readarray -t local_b < <(git for-each-ref --format='%(refname:short)' refs/heads/) + +# build local ref array +for ref in "${local_b[@]}"; do + alocal_b[$ref]=1 +done + + +readarray -t orig_b < <(git for-each-ref --format='%(refname:short)' \ + refs/remotes/"$origin"/) + +declare -p remotes +#declare -p aremotes +declare -p local_b orig_b + +# find-out missing local branches +for remote_b in "${orig_b[@]}"; do + short=${remote_b#"$origin"/}; + echo "$remote_b -> $short ${alocal_b[$short]}" + if ! [[ -v alocal_b[$short] ]]; then + echo git switch -t "$remote_b" + fi +done + + + + + + +#git remote | xargs -n 1 git fetch -a diff --git a/src/chessdefs.h b/src/chessdefs.h index 59f2837..75e9665 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -178,4 +178,6 @@ double clock_elapsed_sec(mclock_t *clock); void rand_init(u64 seed); u64 rand64(void); +void init_all(void); + #endif /* _CHESSDEFS_H */ diff --git a/src/fen.c b/src/fen.c index 2818000..bf7a3f0 100644 --- a/src/fen.c +++ b/src/fen.c @@ -184,6 +184,8 @@ pos_t *fen2pos(pos_t *pos, const char *fen) piece, PIECE(piece), COLOR(piece)); # endif pos_set_sq(&tmppos, sq_make(file, rank), piece); + if (PIECE(piece) == KING) + tmppos.king[COLOR(piece)] = sq_make(file, rank); file++; } else { /* error */ err_line = __LINE__, err_char = *cur, err_pos = cur - fen; @@ -250,8 +252,10 @@ end: } if (fen_check(&tmppos) < 0) return NULL; + tmppos.key = zobrist_calc(&tmppos); if (!pos) - pos = pos_dup(&tmppos); + pos = pos_new(); + pos_copy(&tmppos, pos); //puts("prout 1"); //pos_print_raw(&tmppos, 1); //puts("prout 2"); diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..f9b4112 --- /dev/null +++ b/src/hash.c @@ -0,0 +1,240 @@ +/* hash.c - hash management. + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include + +#include +#include + +#include "chessdefs.h" +#include "util.h" +#include "position.h" +#include "piece.h" +#include "hash.h" + +u64 zobrist_pieces[16][64]; +u64 zobrist_castling[4 * 4 + 1]; +u64 zobrist_turn; /* for black, XOR each ply */ +u64 zobrist_ep[9]; /* 0-7: ep file, 8: SQUARE_NONE */ + +hasht_t hash_tt; /* main transposition table */ + +/** + * zobrist_init() - initialize zobrist tables. + * + * Initialize all zobrist random bitmasks. Must be called before any other + * zobrist function, and can be called once only (further calls will be ignored). + */ +void zobrist_init(void) +{ + static bool called = false; + if (!called) { + called = true; + for (color_t c = WHITE; c <= BLACK; ++c) { + for (piece_type_t p = PAWN; p <= KING; ++p) + for (square_t sq = A1; sq <= H8; ++sq) + zobrist_pieces[MAKE_PIECE(p, c)][sq] = rand64(); + } + for (castle_rights_t c = CASTLE_NONE; c <= CASTLE_ALL; ++c) + zobrist_castling[c] = rand64(); + for (file_t f = FILE_A; f <= FILE_H; ++f) + zobrist_ep[f] = rand64(); + zobrist_ep[8] = 0; /* see EP_ZOBRIST_IDX macro */ + zobrist_turn = rand64(); + } +} + +/** + * zobrist_calc() - calculate a position zobrist hash. + * @pos: &position + * + * Normally, Zobrist keys are incrementally calculated when doing or + * undoing a move. + * This function should normally only be called: + * - When starting a new position + * - To verify incremental Zobrist calculation is correct + * + * @return: @pos Zobrist key + */ +key_t zobrist_calc(pos_t *pos) +{ + key_t key = 0; + + if (pos->turn == BLACK) + key ^= zobrist_turn; + + for (color_t c = WHITE; c <= BLACK; ++c) { + for (piece_type_t pt = PAWN; pt <= KING; ++pt) { + piece_t piece = MAKE_PIECE(pt, c); + bitboard_t bb = pos->bb[c][pt]; + while (bb) { + square_t sq = bb_next(&bb); + key ^= zobrist_pieces[piece][sq]; + } + } + } + key ^= zobrist_castling[pos->castle]; + key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; + + return key; +} + +/** + * zobrist_verify() - verify current position Zobrist key. + * @pos: &position + * + * Verify that position Zobrist key matches a full Zobrist calculation. + * This function cannot be called if ZOBRIST_VERIFY is not set. + * + * @return: True if Zobrist key is OK. + */ +#ifdef ZOBRIST_VERIFY +bool zobrist_verify(pos_t *pos) +{ + key_t diff, key = zobrist_calc(pos); + + if (pos->key == key) + return true; + + printf("key verify: cur=%#lx != %#lx\n", pos->key, key); + + /* try to find-out the key in different zobrist tables */ + diff = pos->key ^ key; + + for (color_t c = WHITE; c <= BLACK; ++c) { + for (piece_type_t p = PAWN; p <= KING; ++p) + for (square_t sq = A1; sq <= H8; ++sq) + if (diff == zobrist_pieces[MAKE_PIECE(p, c)][sq]) { + warn(true, "zobrist difference is piece:[%s][%s]\n", + piece_to_fen(MAKE_PIECE(p, c)), sq_to_string(sq)); + goto end; + } + } + for (castle_rights_t c = CASTLE_NONE; c <= CASTLE_ALL; ++c) + if (diff == zobrist_castling[c]) { + warn(true, "zobrist difference is castling:[%d]\n", c); + goto end; + } + + for (file_t f = FILE_A; f <= FILE_H; ++f) + if (diff == zobrist_ep[f]) { + warn(true, "zobrist difference is ep:[%d]\n", f); + goto end; + } + if (diff == zobrist_turn) { + warn(true, "zobrist difference is turn\n"); + goto end; + } + warn(true, "zobrist diff %lx is unknown\n", diff); +end: + bug_on(false); +} +#endif + +/** + * hash_create() - hashtable creation. + * @sizemb: s32 size of hash table in Mb + * + * Create a hash table of max @sizemb (or HASH_SIZE_MBif @sizemb <= 0) Mb size. + * This function must be called at startup. + * + * The number of bucket_t entries fitting in @sizemb is calculated, and rounded + * (down) to a power of 2. + * This means the actual size could be lower than @sizemb (nearly halved in + * worst case). + * + * If transposition hashtable already exists and new size would not change, + * the old one is cleared. + * If transposition hashtable already exists and new size is different, + * the old one is destroyed first (old data is not preserved). + * + * TODO: + * - Rebuild old hashtable data ? + * + * @return: hash table size in Mb. If memory allocation fails, the function does + * not return. + */ +int hash_create(s32 sizemb) +{ + size_t bytes, target_nbuckets; + u32 nbits; + + static_assert(sizeof(hentry_t) == 16, "fatal: hentry_t size != 16"); + + //printf("mb = %'7u ", sizemb); + /* adjust tt size */ + if (sizemb <= 0) + sizemb = HASH_SIZE_DEFAULT; + sizemb = clamp(sizemb, HASH_SIZE_MIN, HASH_SIZE_MAX); + //printf("-> %'6d ", sizemb); + + bytes = sizemb * 1024ull * 1024ull; /* bytes wanted */ + target_nbuckets = bytes / sizeof(bucket_t); /* target buckets */ + + nbits = msb64(target_nbuckets); /* adjust to power of 2 */ + + if (hash_tt.nbits != nbits) { + if (hash_tt.nbits) + hash_delete(); + + hash_tt.nbits = nbits; + + hash_tt.nbuckets = BIT(hash_tt.nbits); + hash_tt.nkeys = hash_tt.nbuckets * NBUCKETS; + + hash_tt.bytes = hash_tt.nbuckets * sizeof(bucket_t); + hash_tt.mb = hash_tt.bytes / 1024 / 1024; + + hash_tt.mask = -1ull >> (64 - nbits); + + hash_tt.keys = safe_malloc(hash_tt.bytes); + + //printf("bits=%2d size=%'15lu/%'6d Mb/%'14lu buckets ", + // hash_tt.nbits, hash_tt.bytes, hash_tt.mb, hash_tt.nbuckets); + //printf("mask=%9x\n", hash_tt.mask); + } + //else { + // printf("unchanged (cleared)\n"); + //} + /* attention - may fail ! */ + hash_clear(); + + return hash_tt.nbits; +} + +/** + * hash_clear() - clear hashtable data. + * + * Reset hashtable entries (if available) and statistic information. + */ +void hash_clear() +{ + if (hash_tt.keys) + memset(hash_tt.keys, 0, hash_tt.bytes); + + hash_tt.used_buckets = 0; + hash_tt.used_keys = 0; + hash_tt.collisions = 0; +} + +/** + * hash_delete() - delete hashtable data. + * + * free hashtable data. + */ +void hash_delete() +{ + if (hash_tt.keys) + safe_free(hash_tt.keys); + memset(&hash_tt, 0, sizeof(hash_tt)); +} diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000..438ffee --- /dev/null +++ b/src/hash.h @@ -0,0 +1,104 @@ +/* hash.h - hash management. + * + * Copyright (C) 2024 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef HASH_H +#define HASH_H + +#include + +#include "chessdefs.h" + +#define NBUCKETS 4 /* buckets per hash table entry */ + +#define HASH_SIZE_DEFAULT 32 /* default: 32Mb */ +#define HASH_SIZE_MIN 4 +#define HASH_SIZE_MAX 32768 /* 32Gb */ + +#define key_t u64 /* cannot use typedef for key_t */ + +/** + * hentry_t: hashtable bucket. + * + * Size should be exactly 16 bytes. If impossible to fit necessary data in + * 16 bytes in future, it should be updated to be exactly 32 bytes. + */ +typedef struct { + key_t key; /* zobrist */ + union { + u64 data; + struct { + u16 depth; /* ply in search */ + s16 eval; + u16 move; + u8 flags; /* maybe for locking, etc... */ + u8 filler; + }; + }; +} hentry_t; + +typedef struct { + hentry_t buckets[NBUCKETS]; +} bucket_t; + +typedef struct { + bucket_t *keys; /* &hashtable entries */ + + /* memory size in bytes/mb */ + size_t bytes; + u32 mb; + + /* size in buckets/keys */ + size_t nbuckets; + size_t nkeys; /* nbuckets * NBUCKETS */ + + /* internal representation */ + u32 nbits; /* #buckets in bits, power of 2 */ + u32 mask; /* nbuckets - 1, key mask */ + + /* stats - unsure about usage */ + size_t used_buckets; + size_t used_keys; + u64 collisions; +} hasht_t; + +/* hack: + * ep zobrist key index is 0-7 for each en-passant file, 8 for SQUARE_NONE. + * To transform : + * - ep == 64 (SQUARE_NONE) to id = 8 + * - ep == 0~63 to idx = sq_file(ep), i.e. (ep & 7) + * we use the formula: + * idx = ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep); + */ +#define EP_ZOBRIST_IDX(ep) ( ( (ep) >> 3 ) | sq_file(ep) ) + +extern key_t zobrist_pieces[16][64]; +extern key_t zobrist_castling[4 * 4 + 1]; +extern key_t zobrist_turn; /* for black, XOR each ply */ +extern key_t zobrist_ep[9]; /* 0-7: ep file, 8: SQUARE_NONE */ + +extern hasht_t hash_tt; /* main transposition table */ + +void zobrist_init(void); +key_t zobrist_calc(pos_t *pos); + +#ifdef ZOBRIST_VERIFY +bool zobrist_verify(pos_t *pos); +#else +#define zobrist_verify(p) true +#endif + +int hash_create(int Mb); +void hash_clear(void); +void hash_delete(void); + +#endif /* HASH_H */ diff --git a/src/init.c b/src/init.c index a12957a..175594e 100644 --- a/src/init.c +++ b/src/init.c @@ -21,7 +21,7 @@ #include "hash.h" -void init_all() +void init_all(void) { /* for printf() numeric thousands separator */ setlocale(LC_NUMERIC, ""); @@ -37,6 +37,6 @@ void init_all() /* zobrist tables & default tt hashtable */ zobrist_init(); - hash_create(HASH_DEFAULT); + hash_create(HASH_SIZE_DEFAULT); } diff --git a/src/move-do.c b/src/move-do.c index fe62ea2..8fbf9e4 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -23,6 +23,7 @@ #include "move.h" #include "position.h" #include "move-do.h" +#include "hash.h" /** * move_do() - do move. @@ -39,8 +40,12 @@ * - castling * - en-passant * - captured piece (excl. en-passant) + * - tt hash values are updated for: + * - side-to-move + * - en-passant + * - castling rights. * - * @return: pos. + * @return: updated pos. */ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) { @@ -56,6 +61,12 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) piece_type_t ptype = PIECE(piece); piece_t new_piece = piece; int up = sq_up(us); + key_t key = pos->key; + + /* update key: switch turn, reset castling and ep */ + key ^= zobrist_turn; + key ^= zobrist_castling[pos->castle]; + key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; ++pos->clock_50; ++pos->plycount; @@ -74,6 +85,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) pos->clock_50 = 0; //pos->captured = pos->board[to]; /* save capture info */ bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them); + key ^= zobrist_pieces[captured][to]; pos_clr_sq(pos, to); /* clear square */ } else if (is_castle(move)) { /* handle rook move */ square_t rookfrom, rookto; @@ -84,20 +96,26 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) rookfrom = sq_rel(A1, us); rookto = sq_rel(D1, us); } + key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^ + zobrist_pieces[pos->board[rookfrom]][rookfrom]; pos_set_sq(pos, rookto, pos->board[rookfrom]); pos_clr_sq(pos, rookfrom); - pos->castle = clr_castle(pos->castle, us); + //pos->castle = clr_castle(pos->castle, us); } else if (ptype == PAWN) { /* pawn non capture or e.p. */ pos->clock_50 = 0; - if (is_dpush(move)) /* if pawn double push, set e.p. */ + if (is_dpush(move)) { /* if pawn double push, set e.p. */ pos->en_passant = from + up; - else if (is_enpassant(move)) { /* clear grabbed pawn */ + /* update ep key */ + key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; + } else if (is_enpassant(move)) { /* clear grabbed pawn */ square_t grabbed = to - up; + key ^= zobrist_pieces[pos->board[grabbed]][grabbed]; pos_clr_sq(pos, grabbed); } } - pos_clr_sq(pos, from); /* always clear "from" and set "to" */ + key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to]; + pos_clr_sq(pos, from); /* clear "from" and set "to" */ pos_set_sq(pos, to, new_piece); if (ptype == KING) @@ -119,7 +137,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) else if (from == rel_h1) pos->castle = clr_oo(pos->castle, us); } - if (can_castle(pos->castle, them)) { /* do we save time with this test ? */ + if (can_castle(pos->castle, them)) { square_t rel_a8 = sq_rel(A8, us); square_t rel_h8 = sq_rel(H8, us); if (to == rel_a8) @@ -128,6 +146,13 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) pos->castle = clr_oo(pos->castle, them); } + /* update castle key */ + key ^= zobrist_castling[pos->castle]; + + pos->key = key; + + bug_on(zobrist_verify(pos) == false); + return pos; } @@ -143,9 +168,15 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) piece_type_t ptype = PIECE(piece); piece_t new_piece = piece; int up = sq_up(us); + key_t key = pos->key; *state = pos->state; /* save irreversible changes */ + /* update key: switch turn, reset castling and ep */ + key ^= zobrist_turn; + key ^= zobrist_castling[pos->castle]; + key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; + ++pos->clock_50; ++pos->plycount; pos->en_passant = SQUARE_NONE; @@ -163,6 +194,7 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) pos->clock_50 = 0; //pos->captured = pos->board[to]; /* save capture info */ bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them); + key ^= zobrist_pieces[captured][to]; pos_clr_sq(pos, to); /* clear square */ } else if (is_castle(move)) { /* handle rook move */ square_t rookfrom, rookto; @@ -173,20 +205,27 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) rookfrom = sq_rel(A1, us); rookto = sq_rel(D1, us); } + key ^= zobrist_pieces[pos->board[rookfrom]][rookto]; + key ^= zobrist_pieces[pos->board[rookfrom]][rookfrom]; pos_set_sq(pos, rookto, pos->board[rookfrom]); pos_clr_sq(pos, rookfrom); pos->castle = clr_castle(pos->castle, us); } else if (ptype == PAWN) { /* pawn non capture or e.p. */ pos->clock_50 = 0; - if (is_dpush(move)) /* if pawn double push, set e.p. */ + if (is_dpush(move)) { /* if pawn double push, set e.p. */ pos->en_passant = from + up; - else if (is_enpassant(move)) { /* clear grabbed pawn */ + /* update key */ + key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; + } else if (is_enpassant(move)) { /* clear grabbed pawn */ square_t grabbed = to - up; + key ^= zobrist_pieces[pos->board[grabbed]][grabbed]; pos_clr_sq(pos, grabbed); } } - pos_clr_sq(pos, from); /* always clear "from" and set "to" */ + key ^= zobrist_pieces[piece][from]; + key ^= zobrist_pieces[new_piece][to]; + pos_clr_sq(pos, from); /* clear "from" and set "to" */ pos_set_sq(pos, to, new_piece); if (ptype == KING) @@ -208,7 +247,7 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) else if (from == rel_h1) pos->castle = clr_oo(pos->castle, us); } - if (can_castle(pos->castle, them)) { /* do we save time with this test ? */ + if (can_castle(pos->castle, them)) { square_t rel_a8 = sq_rel(A8, us); square_t rel_h8 = sq_rel(H8, us); if (to == rel_a8) @@ -217,6 +256,13 @@ pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) pos->castle = clr_oo(pos->castle, them); } + /* update castle key */ + key ^= zobrist_castling[pos->castle]; + + pos->key = key; + + bug_on(zobrist_verify(pos) == false); + return pos; } diff --git a/src/position.c b/src/position.c index ab277ec..08cb05c 100644 --- a/src/position.c +++ b/src/position.c @@ -62,6 +62,21 @@ pos_t *pos_dup(const pos_t *pos) return newpos; } +/** + * pos_copy() - copy a position into another one. + * @from: &position to duplicate. + * @to: &destination position. + * + * Return a copy of @from into @to. + * + * @Return: @to. + */ +pos_t *pos_copy(const pos_t *from, pos_t *to) +{ + *to = *from; + return to; +} + /** * pos_del() - delete a position. * @pos: &position. @@ -87,6 +102,7 @@ pos_t *pos_clear(pos_t *pos) pos->turn = WHITE; /* move_do/undo position state */ + pos->key = 0; pos->en_passant = SQUARE_NONE; pos->castle = 0; pos->clock_50 = 0; @@ -341,6 +357,7 @@ bool pos_ok(const pos_t *pos, const bool strict) # define BUG_ON # undef WARN_ON # define WARN_ON +# include /* pawns on 1st ot 8th rank */ error += warn_on((pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & diff --git a/src/position.h b/src/position.h index c859188..b99bb30 100644 --- a/src/position.h +++ b/src/position.h @@ -16,11 +16,13 @@ #include -#include "brlib.h" -#include "bitops.h" -#include "struct-group.h" +#include +#include +#include +#include #include "chessdefs.h" +#include "hash.h" #include "bitboard.h" #include "piece.h" #include "move.h" @@ -41,6 +43,7 @@ typedef struct __pos_s { * This allows a memcpy on this data (to save/restore position state). */ struct_group_tagged(state_s, state, + key_t key; square_t en_passant; castle_rights_t castle; int clock_50; @@ -72,11 +75,16 @@ static __always_inline void pos_set_sq(pos_t *pos, square_t square, piece_t piec { color_t color = COLOR(piece); piece_type_t type = PIECE(piece); + + bug_on(pos->board[square] != EMPTY); + pos->board[square] = piece; pos->bb[color][type] |= BIT(square); pos->bb[color][ALL_PIECES] |= BIT(square); - if (type == KING) - pos->king[color] = square; + //if (type == KING) + // pos->king[color] = square; + + //pos->key ^= zobrist_pieces[piece][square]; } /** @@ -91,11 +99,16 @@ static __always_inline void pos_clr_sq(pos_t *pos, square_t square) piece_t piece = pos->board[square]; piece_type_t type = PIECE(piece); color_t color = COLOR(piece); + + bug_on(pos->board[square] == EMPTY); + + //pos->key ^= zobrist_pieces[piece][square]; + pos->board[square] = EMPTY; pos->bb[color][type] &= ~BIT(square); pos->bb[color][ALL_PIECES] &= ~BIT(square); - if (type == KING) - pos->king[color] = SQUARE_NONE; + //if (type == KING) + // pos->king[color] = SQUARE_NONE; } /** @@ -156,6 +169,7 @@ static __always_inline int pos_between_count(const pos_t *pos, pos_t *pos_new(); pos_t *pos_dup(const pos_t *pos); +pos_t *pos_copy(const pos_t *from, pos_t *to); void pos_del(pos_t *pos); pos_t *pos_clear(pos_t *pos); bool pos_cmp(const pos_t *pos1, const pos_t *pos2); diff --git a/src/search.c b/src/search.c index 1fbef4b..9fd90cf 100644 --- a/src/search.c +++ b/src/search.c @@ -60,7 +60,13 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) nodes++; } else { move_do(pos, *move); - subnodes = perft(pos, depth - 1, ply + 1, output); + if (depth == 2) { + movelist_t movelist2; + pos_set_checkers_pinners_blockers(pos); + subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves; + } else { + subnodes = perft(pos, depth - 1, ply + 1, output); + } if (output && ply == 1) { char movestr[8]; printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); @@ -105,7 +111,13 @@ u64 perft_test(pos_t *pos, int depth, int ply, bool output) nodes++; } else { move_do2(pos, *move, &state); - subnodes = perft(pos, depth - 1, ply + 1, output); + if (depth == 2) { + movelist_t movelist2; + pos_set_checkers_pinners_blockers(pos); + subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves; + } else { + subnodes = perft_test(pos, depth - 1, ply + 1, output); + } if (output && ply == 1) { char movestr[8]; printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); diff --git a/test/movedo-test.c b/test/movedo-test.c index 1415c29..bc534ff 100644 --- a/test/movedo-test.c +++ b/test/movedo-test.c @@ -32,10 +32,7 @@ int main(int __unused ac, __unused char**av) movelist_t pseudo; move_t move; - setlinebuf(stdout); /* line-buffered stdout */ - - bitboard_init(); - hyperbola_init(); + init_all(); while ((fen = next_fen(MOVEDO))) { test_line = cur_line(); diff --git a/test/perft-test.c b/test/perft-test.c index 872e8e3..63d3cb4 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -235,16 +235,16 @@ int main(int __unused ac, __unused char**av) int i = 0, test_line; u64 sf_count = 0, my_count; char *fen; - pos_t *pos; + pos_t *pos = NULL, *fenpos; pos_t *fishpos = pos_new(); movelist_t fishmoves; - //move_t move; FILE *outfd = NULL; struct { s64 count, ms; s64 minlps, maxlps; int skipped; - } res[2] = { + } res[3] = { + { .minlps=LONG_MAX }, { .minlps=LONG_MAX }, { .minlps=LONG_MAX }, }; @@ -271,34 +271,47 @@ int main(int __unused ac, __unused char**av) return usage(*av); } } - //if (ac > 1) - // depth = atoi(av[1]); - //if (ac > 2) - // run = atoi(av[2]) & 3; - printf("depth = %d run = %x sf = %s\n", depth, run, sf_run? "true": "false"); + + printf("perft: depth = %d run = %x stockfish = %s\n", + depth, run, sf_run? "true": "false"); + + init_all(); if (!run) exit(0); - setlocale(LC_NUMERIC, ""); - setlinebuf(stdout); /* line-buffered stdout */ if (sf_run) outfd = open_stockfish(); - bitboard_init(); - hyperbola_init(); - - CLOCK_DEFINE(clock, CLOCK_PROCESS); + CLOCK_DEFINE(clock, CLOCK_MONOTONIC); while ((fen = next_fen(PERFT | MOVEDO))) { test_line = cur_line(); - if (!(pos = fen2pos(NULL, fen))) { + if (!(fenpos = fen2pos(pos, fen))) { printf("wrong fen %d: [%s]\n", i, fen); continue; } - if (sf_run) + pos = fenpos; + if (sf_run) { + clock_start(&clock); sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth); - - // savepos = pos_dup(pos); + ms = clock_elapsed_ms(&clock); + if (!ms) { + res[2].skipped++; + lps = 0; + } else { + lps = sf_count * 1000l / ms; + res[2].ms += ms; + res[2].count += sf_count; + if (lps > res[2].maxlps) + res[2].maxlps = lps; + if (lps < res[2].minlps) + res[2].minlps = lps; + } + printf("SF : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", + test_line, sf_count, ms, + lps, + fen); + } if (run & 1) { clock_start(&clock); @@ -357,11 +370,20 @@ int main(int __unused ac, __unused char**av) } printf("\n"); // pos_del(savepos); - pos_del(pos); i++; /* to run first test only */ // exit(0); } + pos_del(pos); + if (sf_run) { + if (!res[2].ms) + res[2].ms = 1; + printf("total SF %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", + res[2].count, res[2].ms, + res[2].count * 1000l / res[2].ms, + res[2].minlps, res[2].maxlps, + res[2].skipped); + } if (run & 1) { if (!res[0].ms) res[0].ms = 1;