5 Commits

9 changed files with 235 additions and 213 deletions

View File

@@ -11,8 +11,8 @@
# #
SHELL := /bin/bash SHELL := /bin/bash
#CC := gcc CC := gcc
CC := gcc-13 #CC := gcc-13
#CC := clang #CC := clang
BEAR := bear BEAR := bear
TOUCH := touch TOUCH := touch
@@ -69,8 +69,9 @@ CPPFLAGS += -DBUG_ON # brlib bug.h
# fen.c # fen.c
#CPPFLAGS += -DDEBUG_FEN # FEN decoding #CPPFLAGS += -DDEBUG_FEN # FEN decoding
# hash.c # hash / TT
CPPFLAGS += -DZOBRIST_VERIFY # chk zobrist consistency #CPPFLAGS += -DZOBRIST_VERIFY # double chk zobrist
#CPPFLAGS += -DPERFT_MOVE_HISTORY # perft, keep prev moves
# attack.c # attack.c
#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers #CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers

View File

@@ -111,7 +111,6 @@ hkey_t zobrist_calc(pos_t *pos)
bool zobrist_verify(pos_t *pos) bool zobrist_verify(pos_t *pos)
{ {
hkey_t diff, key = zobrist_calc(pos); hkey_t diff, key = zobrist_calc(pos);
if (pos->key == key) if (pos->key == key)
return true; return true;
@@ -251,9 +250,11 @@ void tt_clear()
*/ */
void tt_delete() void tt_delete()
{ {
if (hash_tt.keys) if (hash_tt.keys) {
safe_free(hash_tt.keys); safe_free(hash_tt.keys);
memset(&hash_tt, 0, sizeof(hash_tt)); hash_tt.keys = NULL;
}
tt_clear();
} }
/** /**
@@ -304,13 +305,17 @@ hentry_t *tt_probe_perft(const hkey_t key, const u16 depth)
entry = bucket->entry + i; entry = bucket->entry + i;
if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) { if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) {
hash_tt.hits++; hash_tt.hits++;
//printf("tt hit: key=%lx bucket=%lu entry=%d!\n", /*
// key, bucket - hash_tt.keys, i); * printf("tt hit: key=%lx depth=%d bucket=%lu entry=%d!\n",
* key, depth, bucket - hash_tt.keys, i);
*/
return entry; return entry;
} }
} }
//printf("tt miss: key=%lx bucket=%lu\n", /*
// key, bucket - hash_tt.keys); * printf("tt miss: key=%lx depth=%d ucket=%lu\n",
* key, depth, bucket - hash_tt.keys);
*/
hash_tt.misses++; hash_tt.misses++;
return TT_MISS; return TT_MISS;
} }
@@ -326,70 +331,93 @@ hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes)
{ {
bucket_t *bucket; bucket_t *bucket;
hentry_t *entry; hentry_t *entry;
int replace = -1, newkey = 0; int replace = -1;
uint mindepth = 1024; uint mindepth = 1024;
u64 data = HASH_PERFT(depth, nodes); u64 data = HASH_PERFT(depth, nodes);
//printf("tt_store: key=%lx data=%lx depth=%d=%d nodes=%lu=%lu\n", //printf("tt_store: key=%lx data=%lx depth=%d=%d nodes=%lu=%lu\n",
// key, data, depth, HASH_PERFT_DEPTH(data), nodes, HASH_PERFT_VAL(data)); // key, data, depth, HASH_PERFT_DEPTH(data), nodes, HASH_PERFT_VAL(data));
printf("tt_store: key=%lx depth=%d nodes=%lu ", /*
key, depth, nodes); * printf("tt_store: key=%lx depth=%d nodes=%lu ",
* key, depth, nodes);
*/
bug_on(!hash_tt.keys); bug_on(!hash_tt.keys);
bucket = hash_tt.keys + (key & hash_tt.mask); bucket = hash_tt.keys + (key & hash_tt.mask);
/* find key in buckets */ /* find key in buckets */
for (int i = 0; i < ENTRIES_PER_BUCKET; ++i) { for (int i = 0; i < ENTRIES_PER_BUCKET; ++i) {
entry = bucket->entry + i; entry = bucket->entry + i;
if (key == entry->key && HASH_PERFT_DEPTH(entry->data)) { //if (!entry->key) {
printf("tt_store: sup key/depth, this should not happen!\n"); // replace = i;
//hash_tt.used_keys++;
// break;
//}
if (key == entry->key) {
if (depth == HASH_PERFT_DEPTH(entry->data)) {
printf("tt_store: dup key=%lx depth=%d, this should not happen!\n",
key, depth);
return NULL; return NULL;
} }
if (!entry->key) {
replace = i;
break;
} }
/* we replace hash if we are higher in tree */ /* always keep higher nodes */
if (HASH_PERFT_DEPTH(entry->data) < mindepth) { if (HASH_PERFT_DEPTH(entry->data) < mindepth) {
mindepth = HASH_PERFT_DEPTH(entry->data); mindepth = HASH_PERFT_DEPTH(entry->data);
replace = i; replace = i;
} }
/*
* else {
*
* if (key == entry->key && HASH_PERFT_DEPTH(entry->data) > mindepth) {
* mindepth = HASH_PERFT_DEPTH(entry->data);
* replace = i;
* }
* }
*/
} }
if (replace >= 0) { if (replace >= 0) {
entry = bucket->entry + replace; entry = bucket->entry + replace;
if (HASH_PERFT_VAL(entry->data)) {
printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n", hash_tt.used_keys += entry->key == 0;
bucket - hash_tt.keys, replace, hash_tt.collisions += entry->key && (key != entry->key);
entry->key, key, /*
HASH_PERFT_VAL(entry->data), nodes); * if (HASH_PERFT_VAL(entry->data)) {
} else { * printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n",
printf("NEW entry=%lu[%d] key=%lx val=%lu\n", * bucket - hash_tt.keys, replace,
bucket - hash_tt.keys, replace, * entry->key, key,
entry->key, nodes); * HASH_PERFT_VAL(entry->data), nodes);
} * } else {
* printf("NEW entry=%lu[%d] key=%lx val=%lu\n",
* bucket - hash_tt.keys, replace,
* entry->key, nodes);
* }
*/
entry->key = key; entry->key = key;
entry->data = data; entry->data = data;
return entry; return entry;
} else { } else {
printf("TT full, skip\n"); //printf("TT full, skip\n");
} }
return NULL; return NULL;
} }
/**
* tt_info() - print hash-table information.
*/
void tt_info()
{
if (hash_tt.keys) {
printf("TT: Mb:%d buckets:%'lu (bits:%u mask:%#x) entries:%'lu\n",
hash_tt.mb, hash_tt.nbuckets, hash_tt.nbits,
hash_tt.mask, hash_tt.nkeys);
} else {
printf("TT: not set.\n");
}
}
/**
* tt_stats() - print hash-table usage.
*/
void tt_stats() void tt_stats()
{ {
printf("TT: sz=%u bits=%u bcks=%'lu entries=%'lu mask=%10x" if (hash_tt.keys) {
"used=%lu hits/miss=%'lu/%'lu\n", float percent = 100.0 * hash_tt.used_keys / hash_tt.nkeys;
hash_tt.mb, hash_tt.nbits, hash_tt.nbuckets, hash_tt.nkeys, hash_tt.mask, printf("hash: used:%'lu/%'lu (%.2f%%) hit:%'lu miss:%'lu coll:%'lu\n",
hash_tt.used_keys, hash_tt.hits, hash_tt.misses); hash_tt.used_keys, hash_tt.nkeys, percent,
//printf("\tused=%lu hits/miss=%lu/%lu\n", hash_tt.hits, hash_tt.misses,
// hash_tt.used_keys, hash_tt.hits, hash_tt.misses); hash_tt.collisions);
} else {
printf("hash: not set.\n");
}
} }

View File

@@ -25,6 +25,8 @@
#define HASH_SIZE_MAX 32768 /* 32Gb */ #define HASH_SIZE_MAX 32768 /* 32Gb */
#define TT_MISS NULL #define TT_MISS NULL
#define TT_DUP (void *) U64(0x01)
#define TT_OK(p) ((p) > (void *)U64(0xF))
typedef u64 hkey_t; /* cannot use typedef for key_t */ typedef u64 hkey_t; /* cannot use typedef for key_t */
@@ -93,7 +95,7 @@ typedef struct {
* we use the formula: * we use the formula:
* idx = ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep); * idx = ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep);
*/ */
#define EP_ZOBRIST_IDX(ep) ( ( (ep) >> 3 ) | sq_file(ep) ) #define EP_ZOBRIST_IDX(ep) ( ( ( ep & SQUARE_NONE ) >> 3 ) | sq_file(ep) )
extern hkey_t zobrist_pieces[16][64]; extern hkey_t zobrist_pieces[16][64];
extern hkey_t zobrist_castling[4 * 4 + 1]; extern hkey_t zobrist_castling[4 * 4 + 1];
@@ -130,6 +132,7 @@ void tt_delete(void);
hentry_t *tt_probe(hkey_t key); hentry_t *tt_probe(hkey_t key);
hentry_t *tt_probe_perft(const hkey_t key, const u16 depth); hentry_t *tt_probe_perft(const hkey_t key, const u16 depth);
hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes); hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes);
void tt_info(void);
void tt_stats(void); void tt_stats(void);
#endif /* HASH_H */ #endif /* HASH_H */

View File

@@ -101,7 +101,7 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state)
} else if (ptype == PAWN) { /* pawn non capture or e.p. */ } else if (ptype == PAWN) { /* pawn non capture or e.p. */
pos->clock_50 = 0; pos->clock_50 = 0;
if (from + up + up == to) { /* if pawn double push, set e.p. */ if (from + up + up == to) { /* if pawn double push, set e.p. */
square_t ep = from + up;; square_t ep = from + up;
if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) {
pos->en_passant = ep; pos->en_passant = ep;
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
@@ -215,7 +215,7 @@ pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state)
/** /**
* move_{do,undo}_alt - alternative move_do/move_undo (to experiment) * move_{do,undo}_alt - alternative move_do/move_undo (to experiment)
*/ */
pos_t *move_do_alt(pos_t *pos, const move_t move) //, state_t *state) pos_t *move_do_alt(pos_t *pos, const move_t move, state_t *state)
{ {
color_t us = pos->turn, them = OPPONENT(us); color_t us = pos->turn, them = OPPONENT(us);
square_t from = move_from(move), to = move_to(move); square_t from = move_from(move), to = move_to(move);
@@ -226,6 +226,8 @@ pos_t *move_do_alt(pos_t *pos, const move_t move) //, state_t *state)
int up = sq_up(us); int up = sq_up(us);
hkey_t key = pos->key; hkey_t key = pos->key;
*state = pos->state; /* save irreversible changes */
/* update key: switch turn, reset castling and ep */ /* update key: switch turn, reset castling and ep */
key ^= zobrist_turn; key ^= zobrist_turn;
key ^= zobrist_castling[pos->castle]; key ^= zobrist_castling[pos->castle];
@@ -267,14 +269,15 @@ pos_t *move_do_alt(pos_t *pos, const move_t move) //, state_t *state)
} else if (ptype == PAWN) { /* pawn non capture or e.p. */ } else if (ptype == PAWN) { /* pawn non capture or e.p. */
pos->clock_50 = 0; pos->clock_50 = 0;
if (from + up + up == to) { /* if pawn double push, set e.p. */ if (from + up + up == to) { /* if pawn double push, set e.p. */
square_t ep = from + up;; square_t ep = from + up;
if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) {
pos->en_passant = ep; pos->en_passant = ep;
key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)];
} }
} else if (is_enpassant(move)) { /* clear grabbed pawn */ } else if (is_enpassant(move)) { /* clear grabbed pawn */
square_t grabbed = to - up; square_t grabbed = to - up;
key ^= zobrist_pieces[pos->board[grabbed]][grabbed]; piece_t pc = pos->board[grabbed];
key ^= zobrist_pieces[pc][grabbed];
pos_clr_sq(pos, grabbed); pos_clr_sq(pos, grabbed);
} }
} }
@@ -321,7 +324,7 @@ pos_t *move_do_alt(pos_t *pos, const move_t move) //, state_t *state)
return pos; return pos;
} }
pos_t *move_undo_alt(pos_t *pos, const move_t move) pos_t *move_undo_alt(pos_t *pos, const move_t move, const state_t *state)
{ {
color_t them = pos->turn, us = OPPONENT(them); color_t them = pos->turn, us = OPPONENT(them);
square_t from = move_from(move), to = move_to(move); square_t from = move_from(move), to = move_to(move);
@@ -355,7 +358,7 @@ pos_t *move_undo_alt(pos_t *pos, const move_t move)
pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them)); pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them));
} }
//pos->state = *state; /* restore irreversible changes */ pos->state = *state; /* restore irreversible changes */
pos->turn = us; pos->turn = us;
return pos; return pos;
} }

View File

@@ -19,7 +19,8 @@
pos_t *move_do(pos_t *pos, const move_t move, state_t *state); pos_t *move_do(pos_t *pos, const move_t move, state_t *state);
pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state); pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state);
pos_t *move_do_alt(pos_t *pos, const move_t move);//, state_t *state); /* new version testing */
pos_t *move_undo_alt(pos_t *pos, const move_t move);//, const state_t *state); pos_t *move_do_alt(pos_t *pos, const move_t move, state_t *state);
pos_t *move_undo_alt(pos_t *pos, const move_t move, const state_t *state);
#endif /* MOVE_DO_H */ #endif /* MOVE_DO_H */

View File

@@ -353,19 +353,12 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
bitboard_t from_bb, to_bb; bitboard_t from_bb, to_bb;
bitboard_t tmp_bb; bitboard_t tmp_bb;
move_t *moves = movelist->move; move_t *moves = movelist->move;
//int *nmoves = &movelist->nmoves;
square_t from, to; square_t from, to;
square_t king = pos->king[us]; square_t king = pos->king[us];
//*nmoves = 0;
/* king - MUST BE FIRST */ /* king - MUST BE FIRST */
to_bb = bb_king_moves(dest_squares, king); to_bb = bb_king_moves(dest_squares, king);
moves = moves_gen(moves, king, to_bb); moves = moves_gen(moves, king, to_bb);
//while(to_bb) {
// to = bb_next(&to_bb);
// *moves++ = move_make(king, to);
//}
if (bb_multiple(pos->checkers)) /* double check, we stop here */ if (bb_multiple(pos->checkers)) /* double check, we stop here */
goto finish; goto finish;
@@ -382,8 +375,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
* Attention ! Castling flags are assumed correct * Attention ! Castling flags are assumed correct
*/ */
bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us); bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us);
//square_t from_square[2] = { E1, E8 }; /* verify king is on E1/E8 */
//bug_on(can_castle(pos->castle, us) && from != from_square[us]);
/* For castle, we check the opponent attacks on squares between from and to. /* For castle, we check the opponent attacks on squares between from and to.
* To square attack check will be done in gen_is_legal. * To square attack check will be done in gen_is_legal.
*/ */
@@ -400,26 +391,19 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
} }
} }
} }
/* sliding pieces */ /* sliding pieces */
from_bb = pos->bb[us][BISHOP] | pos->bb[us][QUEEN]; from_bb = pos->bb[us][BISHOP] | pos->bb[us][QUEEN];
while (from_bb) { while (from_bb) {
from = bb_next(&from_bb); from = bb_next(&from_bb);
to_bb = hyperbola_bishop_moves(occ, from) & dest_squares; to_bb = hyperbola_bishop_moves(occ, from) & dest_squares;
moves = moves_gen(moves, from, to_bb); moves = moves_gen(moves, from, to_bb);
//while(to_bb) {
// to = bb_next(&to_bb);
// *moves++ = move_make(from, to);
//}
} }
from_bb = pos->bb[us][ROOK] | pos->bb[us][QUEEN]; from_bb = pos->bb[us][ROOK] | pos->bb[us][QUEEN];
while (from_bb) { while (from_bb) {
from = bb_next(&from_bb); from = bb_next(&from_bb);
to_bb = hyperbola_rook_moves(occ, from) & dest_squares; to_bb = hyperbola_rook_moves(occ, from) & dest_squares;
moves = moves_gen(moves, from, to_bb); moves = moves_gen(moves, from, to_bb);
//while(to_bb) {
// to = bb_next(&to_bb);
// *moves++ = move_make(from, to);
//}
} }
/* knight */ /* knight */
@@ -428,10 +412,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
from = bb_next(&from_bb); from = bb_next(&from_bb);
to_bb = bb_knight_moves(dest_squares, from); to_bb = bb_knight_moves(dest_squares, from);
moves = moves_gen(moves, from, to_bb); moves = moves_gen(moves, from, to_bb);
//while(to_bb) {
// to = bb_next(&to_bb);
// *moves++ = move_make(from, to);
//}
} }
/* pawn: relative rank and files */ /* pawn: relative rank and files */
@@ -460,13 +440,11 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist)
while(to_bb) { while(to_bb) {
to = bb_next(&to_bb); to = bb_next(&to_bb);
from = to - shift - shift; from = to - shift - shift;
//*moves++ = move_make_flags(from, to, M_DPUSH);
*moves++ = move_make(from, to); *moves++ = move_make(from, to);
} }
/* pawn: captures */ /* pawn: captures */
tmp_bb = bb_pawns_attacks(pos->bb[us][PAWN], shift) & enemy_pieces; tmp_bb = bb_pawns_attacks(pos->bb[us][PAWN], shift) & enemy_pieces;
//bb_print("FAIL", tmp_bb);
to_bb = tmp_bb & ~rel_rank8; to_bb = tmp_bb & ~rel_rank8;
while (to_bb) { while (to_bb) {
to = bb_next(&to_bb); to = bb_next(&to_bb);

View File

@@ -25,7 +25,8 @@
* perft() - Perform perft on position * perft() - Perform perft on position
* @pos: &position to search * @pos: &position to search
* @depth: Wanted depth. * @depth: Wanted depth.
* @ply: perft depth level. * @ply: current perft depth level (root = 1)
* @output: output total for 1st level moves.
* *
* Run perft on a position. This function displays the available moves at @depth * Run perft on a position. This function displays the available moves at @depth
* level for each possible first move, and the total of moves. * level for each possible first move, and the total of moves.
@@ -43,15 +44,15 @@
*/ */
u64 perft(pos_t *pos, int depth, int ply, bool output) u64 perft(pos_t *pos, int depth, int ply, bool output)
{ {
static movelist_t stack;
//int subnodes;
u64 subnodes, nodes = 0; u64 subnodes, nodes = 0;
movelist_t movelist; movelist_t movelist;
move_t *move, *last; move_t *move, *last;
state_t state; state_t state;
# ifdef PERFT_MOVE_HISTORY
static movelist_t stack;
if (ply == 1) if (ply == 1)
stack.nmoves = 0; stack.nmoves = 0;
# endif
pos_set_checkers_pinners_blockers(pos); pos_set_checkers_pinners_blockers(pos);
@@ -62,31 +63,17 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
nodes++; nodes++;
} else { } else {
move_do(pos, *move, &state); move_do(pos, *move, &state);
# ifdef PERFT_MOVE_HISTORY
stack.move[stack.nmoves++] = *move; stack.move[stack.nmoves++] = *move;
if (ply == 2 && # endif
//move_from(*move) == F7 &&
//move_to(*move) == F5 &&
move_from(stack.move[stack.nmoves-2]) == B2 &&
move_to(stack.move[stack.nmoves-2]) == B4 &&
move_from(stack.move[stack.nmoves-1]) == F7 &&
move_to(stack.move[stack.nmoves-1]) == F5
) {
//&& pos->board[F5] == B_PAWN) {
moves_print(&stack, 0);
pos_print(pos);
}
if (depth == 2) { if (depth == 2) {
movelist_t movelist2; movelist_t movelist2;
pos_set_checkers_pinners_blockers(pos); pos_set_checkers_pinners_blockers(pos);
subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves; subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves;
} else { } else if (ply >= 4) {
hentry_t *entry; hentry_t *entry = tt_probe_perft(pos->key, depth);
//if (ply >= 4 && ply <= 8) { if (entry != TT_MISS) {
if (ply == 4) {
if ((entry = tt_probe_perft(pos->key, depth))) {
subnodes = HASH_PERFT_VAL(entry->data); subnodes = HASH_PERFT_VAL(entry->data);
printf("tt hit key=%lx ply=%d depth=%d nodes=%lu\n",
pos->key, ply, depth, subnodes);
} else { } else {
subnodes = perft(pos, depth - 1, ply + 1, output); subnodes = perft(pos, depth - 1, ply + 1, output);
tt_store_perft(pos->key, depth, subnodes); tt_store_perft(pos->key, depth, subnodes);
@@ -94,14 +81,15 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
} else { } else {
subnodes = perft(pos, depth - 1, ply + 1, output); subnodes = perft(pos, depth - 1, ply + 1, output);
} }
}
if (output && ply == 1) { if (output && ply == 1) {
char movestr[8]; char movestr[8];
printf("%s: %lu\n", move_to_str(movestr, *move, 0), subnodes); printf("%s: %lu\n", move_to_str(movestr, *move, 0), subnodes);
} }
nodes += subnodes; nodes += subnodes;
move_undo(pos, *move, &state); move_undo(pos, *move, &state);
# ifdef PERFT_MOVE_HISTORY
stack.nmoves--; stack.nmoves--;
# endif
} }
} }
@@ -114,7 +102,8 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
* perft_alt() - Perform perft on position, experimental version. * perft_alt() - Perform perft on position, experimental version.
* @pos: &position to search * @pos: &position to search
* @depth: Wanted depth. * @depth: Wanted depth.
* @ply: perft depth level. * @ply: current perft depth level (root = 1)
* @output: output total for 1st level moves.
* *
* Run perft on a position. This function displays the available moves at @depth * Run perft on a position. This function displays the available moves at @depth
* level for each possible first move, and the total of moves. * level for each possible first move, and the total of moves.
@@ -123,15 +112,12 @@ u64 perft(pos_t *pos, int depth, int ply, bool output)
*/ */
u64 perft_alt(pos_t *pos, int depth, int ply, bool output) u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
{ {
int subnodes; u64 subnodes, nodes = 0;
u64 nodes = 0;
movelist_t movelist; movelist_t movelist;
move_t *move, *last; move_t *move, *last;
state_t state; state_t state;
//movelist.nmoves = 0;
pos_set_checkers_pinners_blockers(pos); pos_set_checkers_pinners_blockers(pos);
state = pos->state;
pos_legal(pos, pos_gen_pseudo(pos, &movelist)); pos_legal(pos, pos_gen_pseudo(pos, &movelist));
last = movelist.move + movelist.nmoves; last = movelist.move + movelist.nmoves;
@@ -139,7 +125,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
if (depth == 1) { if (depth == 1) {
nodes++; nodes++;
} else { } else {
move_do_alt(pos, *move); move_do_alt(pos, *move, &state);
if (depth == 2) { if (depth == 2) {
movelist_t movelist2; movelist_t movelist2;
pos_set_checkers_pinners_blockers(pos); pos_set_checkers_pinners_blockers(pos);
@@ -149,11 +135,10 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output)
} }
if (output && ply == 1) { if (output && ply == 1) {
char movestr[8]; char movestr[8];
printf("%s: %d\n", move_to_str(movestr, *move, 0), subnodes); printf("%s: %lu\n", move_to_str(movestr, *move, 0), subnodes);
} }
nodes += subnodes; nodes += subnodes;
move_undo_alt(pos, *move); move_undo_alt(pos, *move, &state);
pos->state = state;
} }
} }

View File

@@ -28,29 +28,25 @@ struct fentest {
char *comment; char *comment;
char *fen; char *fen;
} fentest[] = { } fentest[] = {
/******************* TEMP TESTS BELOW *******************/ /************************************************************
* TEMP TESTS BELOW - only run them (till sentinel below) *
************************************************************/
/* /*
* { __LINE__, MOVEGEN | MOVEDO | PERFT, * { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
* "bug perft TT après 1.b4 f5", * "startpos + 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4",
* "1nbqkbn1/ppp1p1pp/8/r1rpPpK1/1P6/8/P1PP1PPP/RNBQ1BNR w - f6 0 2" * "r1bqkbnr/1ppp1ppp/p1n5/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 1 4"
* },
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
* "",
* "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"
* }, * },
*/ */
/* /******************************************************************
* { __LINE__, MOVEGEN | MOVEDO | PERFT, * DO NOT DELETE NEXT LINE - sentinel entry for temp tests above. *
* "bug perft TT après 1.b4", * ignored if first array entry. *
* "1nbqkbn1/ppp1pppp/8/r1rpP1K1/1P6/8/P1PP1PPP/RNBQ1BNR b - - 0 1", ******************************************************************/
* },
*/
{ __LINE__, MOVEGEN | MOVEDO | PERFT,
"bug perft TT",
"1nbqkbn1/ppp1pppp/8/r1rpP1K1/8/8/PPPP1PPP/RNBQ1BNR w - d6 0 1",
},
/* ***************** END of TEMP TESTS ******************/
/* below line ignored if first test */
{ __LINE__, 0, NULL, NULL }, { __LINE__, 0, NULL, NULL },
{ __LINE__, MOVEGEN | MOVEDO | PERFT, { __LINE__, MOVEGEN | MOVEDO | PERFT,
@@ -154,40 +150,45 @@ struct fentest {
"checker: h4", "checker: h4",
"4k3/8/8/8/7b/8/8/4K3 w - - 0 1" "4k3/8/8/8/7b/8/8/4K3 w - - 0 1"
}, },
/*
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
* "1.e3 - perft bug",
* "rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
* },
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
* "1.e3 Nc6 - perft bug",
* "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPP1PPP/RNBQKBNR w KQkq - 1 2"
* },
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
* "1.e3 Nc6 2.Ke2 - perft bug",
* "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPPKPPP/RNBQ1BNR b kq - 2 2"
* },
* { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
* "1.e3 Nc6 2.Ke2 Nd4+ - perft bug",
* "r1bqkbnr/pppppppp/8/8/3n4/4P3/PPPPKPPP/RNBQ1BNR w kq - 3 3"
* },
*/
// First game moves // First game moves
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"startpos", "startpos",
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
}, },
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
// "1.e3 - perft bug",
// "rnbqkbnr/pppppppp/8/8/8/4P3/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
//},
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
// "1.e3 Nc6 - perft bug",
// "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPP1PPP/RNBQKBNR w KQkq - 1 2"
//},
//{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT | PERFT,
// "1.e3 Nc6 2.Ke2 - perft bug",
// "r1bqkbnr/pppppppp/2n5/8/8/4P3/PPPPKPPP/RNBQ1BNR b kq - 2 2"
//},
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"1.e3 Nc6 2.Ke2 Nd4+ - perft bug", "startpos + 1.e4",
"r1bqkbnr/pppppppp/8/8/3n4/4P3/PPPPKPPP/RNBQ1BNR w kq - 3 3"
},
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"1.e4",
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1" "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
}, },
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"1.Nh3", "startpos + 1.e4 e5 2.Nf3",
"rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1" "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2"
}, },
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"1.e4 e5 2.Nf3 Nc6", "startpos + 1.e4 e5 2.Nf3 Nc6",
"r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1" "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3"
},
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,
"startpos + 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4",
"r1bqkbnr/1ppp1ppp/p1n5/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 1 4"
}, },
// castling test // castling test
// both can castle queen only // both can castle queen only
{ __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT,

View File

@@ -255,21 +255,29 @@ static __unused void compare_moves(movelist_t *fish, movelist_t *me)
static int usage(char *prg) static int usage(char *prg)
{ {
fprintf(stderr, "Usage: %s [-cmv][-d depth] [-p perft-version] \n", prg); fprintf(stderr, "Usage: %s [-cms][-d depth] [-p version] [-t size:\n", prg);
fprintf(stderr, "\t-c/m: print comments/moves, -n: no SF check, -d: depth, -p: 1-3, \n"); fprintf(stderr, "\t-c: do *not* print FEN comments\n");
fprintf(stderr, "\t-d depth: perft depth (default: 6)");
fprintf(stderr, "\t-m: print moves details\n");
fprintf(stderr, "\t-s: use Stockfish to validate perft result\n");
fprintf(stderr, "\t-t size: Transposition Table size (Mb). Default: 32\n");
fprintf(stderr,
"\t-p flavor: perft flavor, 1:perft, 2:perft_alt 3:both, default:1\n");
return 1; return 1;
} }
int main(int ac, char**av) int main(int ac, char**av)
{ {
int test_line; int curtest = 0;
u64 sf_count = 0, my_count; u64 sf_count = 0, my_count;
bool comment = false; bool comment = true, sf_run = false, moves_output = false;
char *fen; char *fen;
pos_t *pos = NULL, *fenpos; pos_t *pos = NULL, *fenpos;
pos_t *fishpos = pos_new(); pos_t *fishpos = pos_new();
movelist_t fishmoves; movelist_t fishmoves;
FILE *outfd = NULL; FILE *outfd = NULL;
s64 ms, lps;
int opt, depth = 6, run = 3, tt = 32, newtt = 32;
struct { struct {
s64 count, ms; s64 count, ms;
s64 minlps, maxlps; s64 minlps, maxlps;
@@ -279,54 +287,73 @@ int main(int ac, char**av)
{ .minlps=LONG_MAX }, { .minlps=LONG_MAX },
{ .minlps=LONG_MAX }, { .minlps=LONG_MAX },
}; };
s64 ms, lps;
int opt, depth = 6, run = 3; while ((opt = getopt(ac, av, "cd:mp:st:")) != -1) {
bool sf_run = true, perft_output = false;
while ((opt = getopt(ac, av, "cmnd:p:")) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
comment = true; comment = false;
break; break;
case 'd': case 'd':
depth = atoi(optarg); depth = atoi(optarg);
break; if (depth <= 0)
case 'p': /* 1 or 2 or 3 for both */ depth = 6;
run = atoi(optarg);
break;
case 'n':
sf_run = false;
break; break;
case 'm': case 'm':
perft_output = true; moves_output = false;
break;
case 'p':
run = atoi(optarg);
break;
case 's':
sf_run = true;
break;
case 't':
newtt = atoi(optarg);
break; break;
default: default:
return usage(*av); return usage(*av);
} }
} }
printf("perft: depth = %d run = %x stockfish = %s\n", if (!run) {
depth, run, sf_run? "true": "false"); printf("Nothing to do, exiting\n");
exit(0);
}
init_all(); init_all();
if (newtt != 32 && newtt > 1) {
printf("changing TT size from %d to %d\n", tt, newtt);
tt_create(newtt);
tt = newtt;
}
printf("%s: depth:%d tt_size:%d run:%x SF:%s\n",
*av,
depth, newtt, run,
sf_run? "yes": "no");
if (!run) tt_info();
exit(0); printf("\n");
printf("move_t size:%lu\n", sizeof(move_t));
if (sf_run) if (sf_run)
outfd = open_stockfish(); outfd = open_stockfish();
CLOCK_DEFINE(clock, CLOCK_MONOTONIC); CLOCK_DEFINE(clock, CLOCK_MONOTONIC);
while ((fen = next_fen(PERFT | MOVEDO))) { while ((fen = next_fen(PERFT | MOVEDO))) {
if (comment)
printf("%s\n", *cur_comment()? cur_comment(): "<FIXME>");
test_line = cur_line();
tt_clear();
if (!(fenpos = fen2pos(pos, fen))) { if (!(fenpos = fen2pos(pos, fen))) {
printf("wrong fen line = %d: [%s]\n", test_line, fen); printf("wrong fen line:%d fen:%s\n\n", cur_line(), fen);
continue; continue;
} }
curtest++;
printf("test:%d line:%d", curtest, cur_line());
if (comment)
printf(" comment:%s\n",
*cur_comment()? cur_comment(): "no test desc");
printf("\t%s\n", fen);
tt_clear();
pos = fenpos; pos = fenpos;
if (sf_run) { if (sf_run) {
stockfish_fen(outfd, fen); stockfish_fen(outfd, fen);
@@ -345,15 +372,13 @@ int main(int ac, char**av)
if (lps < res[2].minlps) if (lps < res[2].minlps)
res[2].minlps = lps; res[2].minlps = lps;
} }
printf("SF : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", printf("Stockfish : perft:%'lu ms:%'ld lps:%'lu\n",
test_line, sf_count, ms, sf_count, ms, lps);
lps,
fen);
} }
if (run & 1) { if (run & 1) {
clock_start(&clock); clock_start(&clock);
my_count = perft(pos, depth, 1, perft_output); my_count = perft(pos, depth, 1, moves_output);
ms = clock_elapsed_ms(&clock); ms = clock_elapsed_ms(&clock);
if (!ms) { if (!ms) {
res[0].skipped++; res[0].skipped++;
@@ -369,20 +394,17 @@ int main(int ac, char**av)
} }
if (!sf_run || sf_count == my_count) { if (!sf_run || sf_count == my_count) {
printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", printf("perft : perft:%'lu ms:%'ld lps:%'lu ",
test_line, my_count, ms, my_count, ms, lps);
lps,
fen);
} else {
printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n",
test_line, sf_count, my_count, fen);
}
tt_stats(); tt_stats();
} else {
printf("perft : perft:%'lu ***ERROR***\n", my_count);
}
} }
if (run & 2) { if (run & 2) {
clock_start(&clock); clock_start(&clock);
my_count = perft_alt(pos, depth, 1, perft_output); my_count = perft_alt(pos, depth, 1, moves_output);
ms = clock_elapsed_ms(&clock); ms = clock_elapsed_ms(&clock);
if (!ms) { if (!ms) {
res[1].skipped++; res[1].skipped++;
@@ -398,13 +420,10 @@ int main(int ac, char**av)
} }
if (!sf_run || sf_count == my_count) { if (!sf_run || sf_count == my_count) {
printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", printf("perft_alt : perft:%'lu ms:%'ld lps:%'lu\n",
test_line, my_count, ms, my_count, ms, lps);
lps,
fen);
} else { } else {
printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", printf("perft_alt : perft:%'lu ***ERROR***\n", my_count);
test_line, sf_count, my_count, fen);
} }
} }
printf("\n"); printf("\n");
@@ -413,29 +432,32 @@ int main(int ac, char**av)
if (sf_run) { if (sf_run) {
if (!res[2].ms) if (!res[2].ms)
res[2].ms = 1; res[2].ms = 1;
printf("total SF %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", printf("total Stockfish : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
"(skipped %d/%d)\n",
res[2].count, res[2].ms, res[2].count, res[2].ms,
res[2].count * 1000l / res[2].ms, res[2].count * 1000l / res[2].ms,
res[2].minlps, res[2].maxlps, res[2].minlps, res[2].maxlps,
res[2].skipped); res[0].skipped, curtest);
} }
if (run & 1) { if (run & 1) {
if (!res[0].ms) if (!res[0].ms)
res[0].ms = 1; res[0].ms = 1;
printf("total perft %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", printf("total perft : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
"(skipped %d/%d)\n",
res[0].count, res[0].ms, res[0].count, res[0].ms,
res[0].count * 1000l / res[0].ms, res[0].count * 1000l / res[0].ms,
res[0].minlps, res[0].maxlps, res[0].minlps, res[0].maxlps,
res[0].skipped); res[0].skipped, curtest);
} }
if (run & 2) { if (run & 2) {
if (!res[1].ms) if (!res[1].ms)
res[1].ms = 1; res[1].ms = 1;
printf("total perft2 %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", printf("total perft_alt : perft:%'lums ms:%'lums lps:%'lu min:%'lu max:%'lu "
"(skipped %d/%d)\n",
res[1].count, res[1].ms, res[1].count, res[1].ms,
res[1].count * 1000l / res[1].ms, res[1].count * 1000l / res[1].ms,
res[1].minlps, res[1].maxlps, res[1].minlps, res[1].maxlps,
res[1].skipped); res[0].skipped, curtest);
} }
return 0; return 0;
} }