Merge branch 'hash'
This commit is contained in:
15
Makefile
15
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))
|
||||
|
||||
|
2
brlib
2
brlib
Submodule brlib updated: 8ff163dcf5...7bedfddfba
46
scripts/fetch-all.sh
Executable file
46
scripts/fetch-all.sh
Executable file
@@ -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
|
@@ -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 */
|
||||
|
@@ -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");
|
||||
|
240
src/hash.c
Normal file
240
src/hash.c
Normal file
@@ -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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <brlib.h>
|
||||
#include <bitops.h>
|
||||
|
||||
#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));
|
||||
}
|
104
src/hash.h
Normal file
104
src/hash.h
Normal file
@@ -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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#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 */
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 <bug.h>
|
||||
|
||||
/* pawns on 1st ot 8th rank */
|
||||
error += warn_on((pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) &
|
||||
|
@@ -16,11 +16,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "brlib.h"
|
||||
#include "bitops.h"
|
||||
#include "struct-group.h"
|
||||
#include <brlib.h>
|
||||
#include <bitops.h>
|
||||
#include <struct-group.h>
|
||||
#include <bug.h>
|
||||
|
||||
#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);
|
||||
|
16
src/search.c
16
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);
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user