From ebf01bc7db362de4f523c3f1e82545fb6ca78c9c Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Sun, 9 Jun 2024 22:15:20 +0200 Subject: [PATCH 01/10] perft_test -> perft, perf -> perft_alt move2 -> move, move -> move_alt --- src/board.c | 3 +- src/move-do.c | 352 ++++++++++++++++++++++----------------------- src/move-do.h | 8 +- src/search.c | 18 +-- src/search.h | 2 +- test/movedo-test.c | 32 ++--- test/perft-test.c | 2 +- 7 files changed, 208 insertions(+), 209 deletions(-) diff --git a/src/board.c b/src/board.c index 8acfc4d..96b5c8a 100644 --- a/src/board.c +++ b/src/board.c @@ -14,7 +14,8 @@ #include #include -#include "brlib.h" +#include + #include "board.h" #include "bitboard.h" diff --git a/src/move-do.c b/src/move-do.c index cc4c333..53a16f6 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -47,13 +47,175 @@ * * @return: updated pos. */ -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) { -//# ifdef DEBUG_MOVE_DO -// move_print(move, M_PR_NL | M_PR_LONG); -//# endif - //*state = pos->state; /* save irreversible changes */ + color_t us = pos->turn, them = OPPONENT(us); + square_t from = move_from(move), to = move_to(move); + piece_t piece = pos->board[from]; + piece_t captured = pos->board[to]; + piece_type_t ptype = PIECE(piece); + piece_t new_piece = piece; + int up = sq_up(us); + hkey_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; + pos->turn = them; + pos->captured = captured; + + bug_on(COLOR(piece) != us); + + if (is_promotion(move)) { + bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us)); + new_piece = MAKE_PIECE(move_promoted(move), us); + } + + if (captured != EMPTY) { + 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; + if (is_castle_K(move)) { + rookfrom = sq_rel(H1, us); + rookto = sq_rel(F1, us); + } else { + 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); + } 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. */ + square_t ep = from + up;; + if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { + pos->en_passant = ep; + 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); + } + } + + 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) + pos->king[us] = to; + + /* update castling flags + * As we always consider flags are valid, we : + * - adjust our flags if relative from is "E1", "A1", H1" + * - adjust opp flags if relative to if "A8", H8" + */ + if (can_castle(pos->castle, us)) { /* do we save time with this test ? */ + square_t rel_e1 = sq_rel(E1, us); + square_t rel_a1 = sq_rel(A1, us); + square_t rel_h1 = sq_rel(H1, us); + if (from == rel_e1) + pos->castle = clr_castle(pos->castle, us); + else if (from == rel_a1) + pos->castle = clr_ooo(pos->castle, us); + else if (from == rel_h1) + pos->castle = clr_oo(pos->castle, us); + } + 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) + pos->castle = clr_ooo(pos->castle, them); + else if (to == rel_h8) + pos->castle = clr_oo(pos->castle, them); + } + + /* update castling rights key */ + key ^= zobrist_castling[pos->castle]; + + pos->key = key; + + zobrist_verify(pos); + + return pos; +} + +/** + * move_undo() - undo move. + * @pos: &pos_t position + * @move: move to undo + * @state: &state_t address where irreversible changes were saved + * + * @move is applied to @pos: + * - bitboards and board are updated + * - previous information is restored: + * - castling + * - en-passant + * - captured piece (excl. en-passant) + * - move count + * - 50-moves rule count + * + * @return: pos. + */ +pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state) +{ + color_t them = pos->turn, us = OPPONENT(them); + square_t from = move_from(move), to = move_to(move); + piece_t piece = pos->board[to]; + int up = sq_up(them); + + if (is_promotion(move)) + piece = MAKE_PIECE(PAWN, us); + + pos_clr_sq(pos, to); /* always clear "to" ... */ + pos_set_sq(pos, from, piece); /* ... and set "from" */ + + if (PIECE(piece) == KING) + pos->king[us] = from; + + if (pos->captured != EMPTY) { + pos_set_sq(pos, to, pos->captured); /* restore captured piece */ + } else if (is_castle(move)) { /* make reverse rook move */ + square_t rookfrom, rookto; + if (is_castle_K(move)) { + rookfrom = sq_rel(F1, us); + rookto = sq_rel(H1, us); + } else { + rookfrom = sq_rel(D1, us); + rookto = sq_rel(A1, us); + } + pos_set_sq(pos, rookto, pos->board[rookfrom]); + pos_clr_sq(pos, rookfrom); + } else if (is_enpassant(move)) { /* restore grabbed pawn */ + square_t grabbed = to + up; + pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them)); + } + + pos->state = *state; /* restore irreversible changes */ + pos->turn = us; + return pos; +} + +/** + * 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) +{ color_t us = pos->turn, them = OPPONENT(us); square_t from = move_from(move), to = move_to(move); piece_t piece = pos->board[from]; @@ -100,13 +262,15 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) 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. */ - pos->en_passant = from + up; - /* update ep key */ - key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; + square_t ep = from + up;; + if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { + pos->en_passant = ep; + 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]; @@ -151,139 +315,12 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) pos->key = key; - bug_on(zobrist_verify(pos) == false); + zobrist_verify(pos); return pos; } -pos_t *move_do2(pos_t *pos, const move_t move, state_t *state) -{ -//# ifdef DEBUG_MOVE_DO -// move_print(move, M_PR_NL | M_PR_LONG); -//# endif - color_t us = pos->turn, them = OPPONENT(us); - square_t from = move_from(move), to = move_to(move); - piece_t piece = pos->board[from]; - piece_t captured = pos->board[to]; - piece_type_t ptype = PIECE(piece); - piece_t new_piece = piece; - int up = sq_up(us); - hkey_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; - pos->turn = them; - pos->captured = captured; - - bug_on(COLOR(piece) != us); - - if (is_promotion(move)) { - bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us)); - new_piece = MAKE_PIECE(move_promoted(move), us); - } - - if (captured != EMPTY) { - 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; - if (is_castle_K(move)) { - rookfrom = sq_rel(H1, us); - rookto = sq_rel(F1, us); - } else { - 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. */ - pos->en_passant = from + up; - /* 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); - } - } - - 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) - pos->king[us] = to; - - /* update castling flags - * As we always consider flags are valid, we : - * - adjust our flags if relative from is "E1", "A1", H1" - * - adjust opp flags if relative to if "A8", H8" - */ - if (can_castle(pos->castle, us)) { /* do we save time with this test ? */ - square_t rel_e1 = sq_rel(E1, us); - square_t rel_a1 = sq_rel(A1, us); - square_t rel_h1 = sq_rel(H1, us); - if (from == rel_e1) - pos->castle = clr_castle(pos->castle, us); - else if (from == rel_a1) - pos->castle = clr_ooo(pos->castle, us); - else if (from == rel_h1) - pos->castle = clr_oo(pos->castle, us); - } - 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) - pos->castle = clr_ooo(pos->castle, them); - else if (to == rel_h8) - 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; -} - -/** - * move_undo() - undo move. - * @pos: &pos_t position - * @move: move to undo - * @state: &state_t address where irreversible changes were saved - * - * @move is applied to @pos: - * - bitboards and board are updated - * - previous information is restored: - * - castling - * - en-passant - * - captured piece (excl. en-passant) - * - move count - * - 50-moves rule count - * - * @return: pos. - */ -pos_t *move_undo(pos_t *pos, const move_t move) +pos_t *move_undo_alt(pos_t *pos, const move_t move) { color_t them = pos->turn, us = OPPONENT(them); square_t from = move_from(move), to = move_to(move); @@ -321,42 +358,3 @@ pos_t *move_undo(pos_t *pos, const move_t move) pos->turn = us; return pos; } - -pos_t *move_undo2(pos_t *pos, const move_t move, const state_t *state) -{ - color_t them = pos->turn, us = OPPONENT(them); - square_t from = move_from(move), to = move_to(move); - piece_t piece = pos->board[to]; - int up = sq_up(them); - - if (is_promotion(move)) - piece = MAKE_PIECE(PAWN, us); - - pos_clr_sq(pos, to); /* always clear "to" ... */ - pos_set_sq(pos, from, piece); /* ... and set "from" */ - - if (PIECE(piece) == KING) - pos->king[us] = from; - - if (pos->captured != EMPTY) { - pos_set_sq(pos, to, pos->captured); /* restore captured piece */ - } else if (is_castle(move)) { /* make reverse rook move */ - square_t rookfrom, rookto; - if (is_castle_K(move)) { - rookfrom = sq_rel(F1, us); - rookto = sq_rel(H1, us); - } else { - rookfrom = sq_rel(D1, us); - rookto = sq_rel(A1, us); - } - pos_set_sq(pos, rookto, pos->board[rookfrom]); - pos_clr_sq(pos, rookfrom); - } else if (is_enpassant(move)) { /* restore grabbed pawn */ - square_t grabbed = to + up; - pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them)); - } - - pos->state = *state; /* restore irreversible changes */ - pos->turn = us; - return pos; -} diff --git a/src/move-do.h b/src/move-do.h index fb2dbce..1bfe032 100644 --- a/src/move-do.h +++ b/src/move-do.h @@ -16,10 +16,10 @@ #include "position.h" -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_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_do2(pos_t *pos, const move_t move, state_t *state); -pos_t *move_undo2(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 */ diff --git a/src/search.c b/src/search.c index 9fd90cf..9cdd0f7 100644 --- a/src/search.c +++ b/src/search.c @@ -51,7 +51,6 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) movelist.nmoves = 0; pos_set_checkers_pinners_blockers(pos); - state = pos->state; pos_legal(pos, pos_gen_pseudo(pos, &movelist)); last = movelist.move + movelist.nmoves; @@ -59,7 +58,7 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) if (depth == 1) { nodes++; } else { - move_do(pos, *move); + move_do(pos, *move, &state); if (depth == 2) { movelist_t movelist2; pos_set_checkers_pinners_blockers(pos); @@ -72,8 +71,7 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); } nodes += subnodes; - move_undo(pos, *move); - pos->state = state; + move_undo(pos, *move, &state); } } @@ -83,7 +81,7 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) } /** - * perft_test() - Perform perft on position, experiment version. + * perft_alt() - Perform perft on position, experimental version. * @pos: &position to search * @depth: Wanted depth. * @ply: perft depth level. @@ -93,7 +91,7 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) * * @return: total moves found at @depth level. */ -u64 perft_test(pos_t *pos, int depth, int ply, bool output) +u64 perft_alt(pos_t *pos, int depth, int ply, bool output) { int subnodes; u64 nodes = 0; @@ -103,6 +101,7 @@ u64 perft_test(pos_t *pos, int depth, int ply, bool output) movelist.nmoves = 0; pos_set_checkers_pinners_blockers(pos); + state = pos->state; pos_legal(pos, pos_gen_pseudo(pos, &movelist)); last = movelist.move + movelist.nmoves; @@ -110,20 +109,21 @@ u64 perft_test(pos_t *pos, int depth, int ply, bool output) if (depth == 1) { nodes++; } else { - move_do2(pos, *move, &state); + move_do_alt(pos, *move); 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); + subnodes = perft_alt(pos, depth - 1, ply + 1, output); } if (output && ply == 1) { char movestr[8]; printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); } nodes += subnodes; - move_undo2(pos, *move, &state); + move_undo_alt(pos, *move); + pos->state = state; } } diff --git a/src/search.h b/src/search.h index a2d1531..e6019e4 100644 --- a/src/search.h +++ b/src/search.h @@ -20,6 +20,6 @@ //eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color); u64 perft(pos_t *pos, int depth, int ply, bool output); -u64 perft_test(pos_t *pos, int depth, int ply, bool output); +u64 perft_alt(pos_t *pos, int depth, int ply, bool output); #endif /* SEARCH_H */ diff --git a/test/movedo-test.c b/test/movedo-test.c index bc534ff..25fa11a 100644 --- a/test/movedo-test.c +++ b/test/movedo-test.c @@ -29,8 +29,8 @@ int main(int __unused ac, __unused char**av) int i = 0, test_line; char *fen, movebuf[8];; pos_t *pos, *savepos; - movelist_t pseudo; - move_t move; + movelist_t movelist; + move_t *move, *last; init_all(); @@ -41,39 +41,39 @@ int main(int __unused ac, __unused char**av) continue; } - pos->checkers = pos_checkers(pos, pos->turn); - pos_set_pinners_blockers(pos); - - pos_gen_pseudo(pos, &pseudo); + movelist.nmoves = 0; + pos_set_checkers_pinners_blockers(pos); + pos_legal(pos, pos_gen_pseudo(pos, &movelist)); + last = movelist.move + movelist.nmoves; savepos = pos_dup(pos); state_t state = pos->state; - int tmp = 0, j = 0; - while ((move = pos_next_legal(pos, &pseudo, &tmp)) != MOVE_NONE) { + int j = 0; + for (move = movelist.move; move < last; ++move) { //pos_print(pos); //printf("i=%d j=%d turn=%d move=[%s]\n", i, j, pos->turn, // move_str(movebuf, move, 0)); //move_p - move_do(pos, move); + move_do(pos, *move, &state); //pos_print(pos); //fflush(stdout); if (!pos_ok(pos, false)) { - printf("*** fen %d move %d [%s] invalid position after move_do\n", - test_line, j, movebuf); + printf("*** fen %d [%s] move %d [%s] invalid position after move_do\n", + test_line, fen, j, move_str(movebuf, *move, 0)); exit(0); } //printf("%d/%d move_do check ok\n", i, j); - move_undo(pos, move); + move_undo(pos, *move, &state); pos->state = state; if (!pos_ok(pos, false)) { - printf("*** fen %d move %d [%s] invalid position after move_undo\n", - test_line, j, movebuf); + printf("*** fen %d [%s] move %d [%s] invalid position after move_undo\n", + test_line, fen, j, movebuf); exit(0); } if (pos_cmp(pos, savepos) != true) { - printf("*** fen %d move %d [%s] position differ after move_{do,undo}\n", - test_line, j, movebuf); + printf("*** fen %d [%s] move %d [%s] position differ after move_{do,undo}\n", + test_line, fen, j, movebuf); exit(0); } //fflush(stdout); diff --git a/test/perft-test.c b/test/perft-test.c index 85334ee..87cfbe9 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -374,7 +374,7 @@ int main(int ac, char**av) if (run & 2) { clock_start(&clock); - my_count = perft_test(pos, depth, 1, perft_output); + my_count = perft_alt(pos, depth, 1, perft_output); ms = clock_elapsed_ms(&clock); if (!ms) { res[1].skipped++; From 49b678e3ce81fa10d0d4ef28773a708484b0a566 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 12 Jun 2024 07:40:13 +0200 Subject: [PATCH 02/10] fix piece_t_from_char() --- src/piece.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/piece.c b/src/piece.c index e00f9d5..96c3994 100644 --- a/src/piece.c +++ b/src/piece.c @@ -78,8 +78,12 @@ char *piece_to_name(piece_t p) piece_type_t piece_t_from_char(char c) { - char *p = strchr(pieces_str, c); - return p? (p - pieces_str) % 6 + 1: NO_PIECE_TYPE; + char *p; + piece_type_t pt = NO_PIECE_TYPE; + if (c && (p = strchr(pieces_str, c))) { + pt = (p - pieces_str) % 6 + 1; + } + return pt; } //piece_type_t piece_from_promotion(char c, color_t color) From ec2d2291d4f705c8a26bfeb669ee9146be948f04 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 12 Jun 2024 07:45:22 +0200 Subject: [PATCH 03/10] clean move_t: Remove capture/dpush/promotion flags, captured piece --- src/move-do.c | 4 +-- src/move-gen.c | 3 ++- src/move.c | 7 +++--- src/move.h | 68 ++++++++++++++++++++++++++++---------------------- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/move-do.c b/src/move-do.c index 53a16f6..465c0a8 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -100,7 +100,7 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state) 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 (from + up + up == to) { /* if pawn double push, set e.p. */ square_t ep = from + up;; if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { pos->en_passant = ep; @@ -265,7 +265,7 @@ pos_t *move_do_alt(pos_t *pos, const move_t move) //, state_t *state) 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 (from + up + up == to) { /* if pawn double push, set e.p. */ square_t ep = from + up;; if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { pos->en_passant = ep; diff --git a/src/move-gen.c b/src/move-gen.c index e6bef55..fd435e9 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -460,7 +460,8 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist) while(to_bb) { to = bb_next(&to_bb); from = to - shift - shift; - *moves++ = move_make_flags(from, to, M_DPUSH); + //*moves++ = move_make_flags(from, to, M_DPUSH); + *moves++ = move_make(from, to); } /* pawn: captures */ diff --git a/src/move.c b/src/move.c index bad6375..a8a094f 100644 --- a/src/move.c +++ b/src/move.c @@ -89,7 +89,7 @@ */ /** - * move_str() - get a move string + * move_to_str() - get a move string * @dst: destination memory * @move: move * @flags: moves selection and display options. @@ -102,7 +102,7 @@ * M_PR_NL: print a newline after move * M_PR_EVAL: print move eval */ -char *move_str(char *dst, const move_t move, __unused const int flags) +char *move_to_str(char *dst, const move_t move, __unused const int flags) { square_t from = move_from(move); square_t to = move_to(move); @@ -116,7 +116,6 @@ char *move_str(char *dst, const move_t move, __unused const int flags) return dst; } - /** * moves_print() - print movelist moves. * @moves: &movelist_t moves list @@ -135,7 +134,7 @@ void moves_print(movelist_t *moves, __unused int flags) char str[16]; //printf("%2d:", moves->nmoves); for (int m = 0; m < moves->nmoves; ++m) - printf("%s ", move_str(str, moves->move[m], flags)); + printf("%s ", move_to_str(str, moves->move[m], flags)); printf("\n"); } diff --git a/src/move.h b/src/move.h index 368fdbb..0d7f33e 100644 --- a/src/move.h +++ b/src/move.h @@ -42,30 +42,30 @@ enum { M_OFF_FROM = 0, M_OFF_TO = 6, M_OFF_PROMOTED = 12, - M_OFF_CAPTURED = 15, - M_OFF_FLAGS = 18 +// M_OFF_CAPTURED = 15, + M_OFF_FLAGS = 15 }; typedef enum { - M_CAPTURE = BIT(M_OFF_FLAGS + 0), - M_ENPASSANT = BIT(M_OFF_FLAGS + 1), - M_PROMOTION = BIT(M_OFF_FLAGS + 2), - M_CASTLE_K = BIT(M_OFF_FLAGS + 3), /* maybe only one ? */ - M_CASTLE_Q = BIT(M_OFF_FLAGS + 5), /* maybe only one ? */ - M_CHECK = BIT(M_OFF_FLAGS + 6), /* maybe unknown/useless ? */ - M_DPUSH = BIT(M_OFF_FLAGS + 7) /* pawn double push */ + M_PROMOTION = 070000, +// M_CAPTURE = BIT(M_OFF_FLAGS + 0), + M_ENPASSANT = BIT(M_OFF_FLAGS + 0), + M_CASTLE_K = BIT(M_OFF_FLAGS + 1), /* maybe only one ? */ + M_CASTLE_Q = BIT(M_OFF_FLAGS + 2), /* maybe only one ? */ + M_CHECK = BIT(M_OFF_FLAGS + 3), /* maybe unknown/useless ? */ +// M_DPUSH = BIT(M_OFF_FLAGS + 7) /* pawn double push */ } move_flags_t; #define move_set_flags(move, flags) ((move) | (flags)) -#define is_capture(m) ((m) & M_CAPTURE) +//#define is_capture(m) ((m) & M_CAPTURE) #define is_enpassant(m) ((m) & M_ENPASSANT) #define is_promotion(m) ((m) & M_PROMOTION) #define is_castle(m) ((m) & (M_CASTLE_K | M_CASTLE_Q)) #define is_castle_K(m) ((m) & M_CASTLE_K) #define is_castle_Q(m) ((m) & M_CASTLE_Q) #define is_check(m) ((m) & M_CHECK) -#define is_dpush(m) ((m) & M_DPUSH) +//#define is_dpush(m) ((m) & M_DPUSH) #define MOVES_MAX 256 @@ -89,10 +89,12 @@ static inline piece_type_t move_promoted(move_t move) return (move >> M_OFF_PROMOTED) & 07; } -static inline piece_type_t move_captured(move_t move) -{ - return (move >> M_OFF_CAPTURED) & 07; -} +/* + * static inline piece_type_t move_captured(move_t move) + * { + * return (move >> M_OFF_CAPTURED) & 07; + * } + */ static inline move_t move_make(square_t from, square_t to) { @@ -105,10 +107,12 @@ static inline move_t move_make_flags(square_t from, square_t to, move_flags_t fl //move_set_flags(move_make(from, to), flags); } -static inline move_t move_make_capture(square_t from, square_t to) -{ - return move_make_flags(from, to, M_CAPTURE); -} +/* + * static inline move_t move_make_capture(square_t from, square_t to) + * { + * return move_make_flags(from, to, M_CAPTURE); + * } + */ static inline move_t move_make_enpassant(square_t from, square_t to) { @@ -118,19 +122,23 @@ static inline move_t move_make_enpassant(square_t from, square_t to) static inline move_t move_make_promote(square_t from, square_t to, piece_type_t promoted) { - return move_make_flags(from, to, M_PROMOTION) | (promoted << M_OFF_PROMOTED); + return move_make(from, to) | (promoted << M_OFF_PROMOTED); } -static inline move_t move_make_promote_capture(square_t from, square_t to, - piece_type_t promoted) -{ - return move_make_promote(from, to, promoted) | M_CAPTURE; -} +/* + * static inline move_t move_make_promote_capture(square_t from, square_t to, + * piece_type_t promoted) + * { + * return move_make_promote(from, to, promoted) | M_CAPTURE; + * } + */ -static inline move_t move_set_captured(move_t move, piece_type_t captured) -{ - return move | (captured << M_OFF_CAPTURED); -} +/* + * static inline move_t move_set_captured(move_t move, piece_type_t captured) + * { + * return move | (captured << M_OFF_CAPTURED); + * } + */ /* moves_print flags */ @@ -144,7 +152,7 @@ static inline move_t move_set_captured(move_t move, piece_type_t captured) #define M_PR_LONG 0x80 //int move_print(int movenum, move_t *move, move_flags_t flags); -char *move_str(char *dst, const move_t move, __unused const int flags); +char *move_to_str(char *dst, const move_t move, __unused const int flags); void moves_print(movelist_t *moves, int flags); void move_sort_by_sq(movelist_t *moves); From 8be03c62307224e45d9b3afd94cccebe94f9b995 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 12 Jun 2024 07:50:19 +0200 Subject: [PATCH 04/10] rename TT funcs to TT_xxx() --- src/chessdefs.h | 6 +- src/hash.c | 144 +++++++++++++++++++++++++++++++++++++++++---- src/hash.h | 38 ++++++++++-- src/init.c | 2 +- test/movedo-test.c | 2 +- 5 files changed, 171 insertions(+), 21 deletions(-) diff --git a/src/chessdefs.h b/src/chessdefs.h index c531ed4..03658d5 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -16,8 +16,8 @@ #include "brlib.h" /* brlib types */ -#define ONE 1ull -#define U64(const_u64) const_u64##ULL +#define ONE 1ul +#define U64(const_u64) const_u64##UL #define BIT(i) ( (u64) (ONE << (i)) ) #define BOARDSIZE (8*8) @@ -176,7 +176,7 @@ s64 clock_elapsed_μs(mclock_t *clock); s64 clock_elapsed_ms(mclock_t *clock); double clock_elapsed_sec(mclock_t *clock); -#define RAND_SEED_DEFAULT U64(1) +#define RAND_SEED_DEFAULT U64(0xb0d1ccea) void rand_init(u64 seed); u64 rand64(void); diff --git a/src/hash.c b/src/hash.c index 5f3756e..e67cb70 100644 --- a/src/hash.c +++ b/src/hash.c @@ -12,6 +12,7 @@ */ #include +#include #include #include @@ -99,9 +100,17 @@ hkey_t zobrist_calc(pos_t *pos) * @return: True if Zobrist key is OK. */ #ifdef ZOBRIST_VERIFY + +#pragma push_macro("BUG_ON") /* force BUG_ON and WARN_ON */ +#pragma push_macro("WARN_ON") +#undef BUG_ON +#define BUG_ON +#undef WARN_ON +#define WARN_ON + bool zobrist_verify(pos_t *pos) { - key_t diff, key = zobrist_calc(pos); + hkey_t diff, key = zobrist_calc(pos); if (pos->key == key) return true; @@ -120,17 +129,19 @@ bool zobrist_verify(pos_t *pos) goto end; } } - for (castle_rights_t c = CASTLE_NONE; c <= CASTLE_ALL; ++c) + 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) + 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; @@ -138,11 +149,16 @@ bool zobrist_verify(pos_t *pos) warn(true, "zobrist diff %lx is unknown\n", diff); end: bug_on(false); + /* not reached */ + return true; } +#pragma pop_macro("WARN_ON") +#pragma pop_macro("BUG_ON") + #endif /** - * hash_create() - hashtable creation. + * tt_create() - create transposition table * @sizemb: s32 size of hash table in Mb * * Create a hash table of max @sizemb (or HASH_SIZE_MBif @sizemb <= 0) Mb size. @@ -164,7 +180,7 @@ end: * @return: hash table size in Mb. If memory allocation fails, the function does * not return. */ -int hash_create(s32 sizemb) +int tt_create(s32 sizemb) { size_t bytes, target_nbuckets; u32 nbits; @@ -185,7 +201,7 @@ int hash_create(s32 sizemb) if (hash_tt.nbits != nbits) { if (hash_tt.nbits) - hash_delete(); + tt_delete(); hash_tt.nbits = nbits; @@ -207,17 +223,17 @@ int hash_create(s32 sizemb) // printf("unchanged (cleared)\n"); //} /* attention - may fail ! */ - hash_clear(); + tt_clear(); return hash_tt.nbits; } /** - * hash_clear() - clear hashtable data. + * tt_clear() - clear transposition table * * Reset hashtable entries (if available) and statistic information. */ -void hash_clear() +void tt_clear() { if (hash_tt.keys) memset(hash_tt.keys, 0, hash_tt.bytes); @@ -228,13 +244,119 @@ void hash_clear() } /** - * hash_delete() - delete hashtable data. + * tt_delete() - delete transposition table * * free hashtable data. */ -void hash_delete() +void tt_delete() { if (hash_tt.keys) safe_free(hash_tt.keys); memset(&hash_tt, 0, sizeof(hash_tt)); } + +/** + * tt_probe() - probe tt for an entry + * + * + */ +hentry_t *tt_probe(hkey_t key) +{ + bucket_t *bucket; + hentry_t *entry; + int i; + + bug_on(!hash_tt.keys); + bucket = hash_tt.keys + (key & hash_tt.mask); + + /* find key in buckets */ + for (i = 0; i < NBUCKETS; ++i) { + entry = bucket->entry + i; + if (key == entry->key) + break; + } + if (i < NBUCKETS) + return entry; + return NULL; +} + +/** + * tt_probe_perft() - probe tt for an entry (perft version) + * @key: Zobrist (hkey_t) key + * @depth: depth from search root + * + * Search transposition for @key entry with @depth depth. + * + * @return: @hentry_t address is found, TT_MISS otherwise. + */ +hentry_t *tt_probe_perft(const hkey_t key, const u16 depth) +{ + bucket_t *bucket; + hentry_t *entry; + int i; + + bug_on(!hash_tt.keys); + bucket = hash_tt.keys + (key & hash_tt.mask); + + /* find key in buckets */ + for (i = 0; i < NBUCKETS; ++i) { + entry = bucket->entry + i; + if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) { + printf("tt hit: key=%lx bucket=%lu entry=%d!\n", + key, bucket - hash_tt.keys, i); + break; + } + } + if (i < NBUCKETS) + return entry; + printf("tt miss: key=%lx bucket=%lu\n", + key, bucket - hash_tt.keys); + return TT_MISS; +} + +/** + * tt_store_perft() - store a transposition table entry (perft version) + * @key: Zobrist (hkey_t) key + * @depth: depth from search root + * @nodes: value to store + * + */ +hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes) +{ + bucket_t *bucket; + hentry_t *entry; + int replace = -1, i; + // uint mindepth = 0; + u64 data = HASH_PERFT(depth, nodes); + 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)); + bug_on(!hash_tt.keys); + bucket = hash_tt.keys + (key & hash_tt.mask); + + /* find key in buckets */ + for (i = 0; i < NBUCKETS; ++i) { + entry = bucket->entry + i; + if (!entry->key) { + replace = i; + break; + } + /* + * else { + * /\* we replace hash if we are higher in tree *\/ + * if (key == entry->key && HASH_PERFT_DEPTH(entry->data) > mindepth) { + * mindepth = HASH_PERFT_DEPTH(entry->data); + * replace = i; + * } + * } + */ + } + if (replace >= 0) { + printf("replacing key=%lx=%lx bucket=%lu idx=%d val=%lu\n", + key, entry->key, bucket - hash_tt.keys, replace, nodes); + entry = bucket->entry + replace; + entry->key = key; + entry->data = data; + return entry; + } + return NULL; +} diff --git a/src/hash.h b/src/hash.h index a270c47..fb5ecaf 100644 --- a/src/hash.h +++ b/src/hash.h @@ -14,7 +14,7 @@ #ifndef HASH_H #define HASH_H -#include +#include #include "chessdefs.h" @@ -24,6 +24,8 @@ #define HASH_SIZE_MIN 4 #define HASH_SIZE_MAX 32768 /* 32Gb */ +#define TT_MISS NULL + typedef u64 hkey_t; /* cannot use typedef for key_t */ /** @@ -46,8 +48,18 @@ typedef struct { }; } hentry_t; +/* hentry perft data: + * 0-47: perft value + * 48-63: depth + */ +#define HASH_PERFT_MASK U64(0xffffffffffff) +#define HASH_PERFT(depth, val) ((((u64) depth) << 48) | ((val) & HASH_PERFT_MASK)) +#define HASH_PERFT_VAL(data) ((data) & HASH_PERFT_MASK) +#define HASH_PERFT_DEPTH(data) ((u16)((data) >> 48)) + + typedef struct { - hentry_t buckets[NBUCKETS]; + hentry_t entry[NBUCKETS]; } bucket_t; typedef struct { @@ -97,8 +109,24 @@ bool zobrist_verify(pos_t *pos); #define zobrist_verify(p) true #endif -int hash_create(int Mb); -void hash_clear(void); -void hash_delete(void); +/** + * tt_prefetch() - prefetch hash table entry + * @hash: u64 key + * + * Prefetch memory for @key. + */ +static inline void tt_prefetch(hkey_t key) +{ + bug_on(!hash_tt.keys); + __builtin_prefetch(hash_tt.keys + (key & hash_tt.mask)); +} + +int tt_create(int Mb); +void tt_clear(void); +void tt_delete(void); + +hentry_t *tt_probe(hkey_t key); +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); #endif /* HASH_H */ diff --git a/src/init.c b/src/init.c index 175594e..f62df6b 100644 --- a/src/init.c +++ b/src/init.c @@ -37,6 +37,6 @@ void init_all(void) /* zobrist tables & default tt hashtable */ zobrist_init(); - hash_create(HASH_SIZE_DEFAULT); + tt_create(HASH_SIZE_DEFAULT); } diff --git a/test/movedo-test.c b/test/movedo-test.c index 25fa11a..897db58 100644 --- a/test/movedo-test.c +++ b/test/movedo-test.c @@ -59,7 +59,7 @@ int main(int __unused ac, __unused char**av) //fflush(stdout); if (!pos_ok(pos, false)) { printf("*** fen %d [%s] move %d [%s] invalid position after move_do\n", - test_line, fen, j, move_str(movebuf, *move, 0)); + test_line, fen, j, move_to_str(movebuf, *move, 0)); exit(0); } From 148fef20ea5ab2fb4b4a16ece447b53506db18e3 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 13 Jun 2024 10:28:32 +0200 Subject: [PATCH 05/10] add info in pos_print, start perft TT testing --- Makefile | 10 ++- src/hash.c | 73 +++++++++++++++------ src/hash.h | 11 ++-- src/move-do.c | 3 +- src/position.c | 7 +- src/search.c | 44 +++++++++++-- test/common-test.h | 36 +++++++--- test/perft-test.c | 16 +++-- test/tt-test.c | 159 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 310 insertions(+), 49 deletions(-) create mode 100644 test/tt-test.c diff --git a/Makefile b/Makefile index 9fc950e..77119c4 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ CPPFLAGS += -DBUG_ON # brlib bug.h #CPPFLAGS += -DDEBUG_FEN # FEN decoding # hash.c -#CPPFLAGS += -HASH_VERIFY # chk zobrist consistency +CPPFLAGS += -DZOBRIST_VERIFY # chk zobrist consistency # attack.c #CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers @@ -339,7 +339,7 @@ memcheck: targets .PHONY: testing test TEST := piece-test fen-test bitboard-test movegen-test attack-test -TEST += movedo-test perft-test +TEST += movedo-test perft-test tt-test PIECE_OBJS := piece.o FEN_OBJS := $(PIECE_OBJS) fen.o position.o bitboard.o board.o \ @@ -349,6 +349,7 @@ MOVEGEN_OBJS := $(BB_OBJS) move.o move-gen.o ATTACK_OBJS := $(MOVEGEN_OBJS) MOVEDO_OBJS := $(ATTACK_OBJS) move-do.o misc.o PERFT_OBJS := $(MOVEDO_OBJS) search.o +TT_OBJS := $(MOVEDO_OBJS) TEST := $(addprefix $(BINDIR)/,$(TEST)) @@ -359,6 +360,7 @@ MOVEGEN_OBJS := $(addprefix $(OBJDIR)/,$(MOVEGEN_OBJS)) ATTACK_OBJS := $(addprefix $(OBJDIR)/,$(ATTACK_OBJS)) MOVEDO_OBJS := $(addprefix $(OBJDIR)/,$(MOVEDO_OBJS)) PERFT_OBJS := $(addprefix $(OBJDIR)/,$(PERFT_OBJS)) +TT_OBJS := $(addprefix $(OBJDIR)/,$(TT_OBJS)) test: echo TEST=$(TEST) @@ -394,6 +396,10 @@ bin/perft-test: test/perft-test.c test/common-test.h $(PERFT_OBJS) @echo compiling $@ test executable. @$(CC) $(ALL_CFLAGS) $< $(PERFT_OBJS) $(ALL_LDFLAGS) -o $@ +bin/tt-test: test/tt-test.c test/common-test.h $(TT_OBJS) + @echo compiling $@ test executable. + @$(CC) $(ALL_CFLAGS) $< $(TT_OBJS) $(ALL_LDFLAGS) -o $@ + ##################################### Makefile debug .PHONY: showflags wft diff --git a/src/hash.c b/src/hash.c index e67cb70..6e34c0c 100644 --- a/src/hash.c +++ b/src/hash.c @@ -206,7 +206,7 @@ int tt_create(s32 sizemb) hash_tt.nbits = nbits; hash_tt.nbuckets = BIT(hash_tt.nbits); - hash_tt.nkeys = hash_tt.nbuckets * NBUCKETS; + hash_tt.nkeys = hash_tt.nbuckets * ENTRIES_PER_BUCKET; hash_tt.bytes = hash_tt.nbuckets * sizeof(bucket_t); hash_tt.mb = hash_tt.bytes / 1024 / 1024; @@ -238,9 +238,10 @@ void tt_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_tt.hits = 0; + hash_tt.misses = 0; } /** @@ -270,12 +271,12 @@ hentry_t *tt_probe(hkey_t key) bucket = hash_tt.keys + (key & hash_tt.mask); /* find key in buckets */ - for (i = 0; i < NBUCKETS; ++i) { + for (i = 0; i < ENTRIES_PER_BUCKET; ++i) { entry = bucket->entry + i; if (key == entry->key) break; } - if (i < NBUCKETS) + if (i < ENTRIES_PER_BUCKET) return entry; return NULL; } @@ -299,18 +300,18 @@ hentry_t *tt_probe_perft(const hkey_t key, const u16 depth) bucket = hash_tt.keys + (key & hash_tt.mask); /* find key in buckets */ - for (i = 0; i < NBUCKETS; ++i) { + for (i = 0; i < ENTRIES_PER_BUCKET; ++i) { entry = bucket->entry + i; if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) { - printf("tt hit: key=%lx bucket=%lu entry=%d!\n", - key, bucket - hash_tt.keys, i); - break; + hash_tt.hits++; + //printf("tt hit: key=%lx bucket=%lu entry=%d!\n", + // key, bucket - hash_tt.keys, i); + return entry; } } - if (i < NBUCKETS) - return entry; - printf("tt miss: key=%lx bucket=%lu\n", - key, bucket - hash_tt.keys); + //printf("tt miss: key=%lx bucket=%lu\n", + // key, bucket - hash_tt.keys); + hash_tt.misses++; return TT_MISS; } @@ -325,24 +326,36 @@ hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes) { bucket_t *bucket; hentry_t *entry; - int replace = -1, i; - // uint mindepth = 0; + int replace = -1, newkey = 0; + uint mindepth = 1024; u64 data = HASH_PERFT(depth, nodes); - 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)); + + //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)); + printf("tt_store: key=%lx depth=%d nodes=%lu ", + key, depth, nodes); bug_on(!hash_tt.keys); bucket = hash_tt.keys + (key & hash_tt.mask); /* find key in buckets */ - for (i = 0; i < NBUCKETS; ++i) { + for (int i = 0; i < ENTRIES_PER_BUCKET; ++i) { entry = bucket->entry + i; + if (key == entry->key && HASH_PERFT_DEPTH(entry->data)) { + printf("tt_store: sup key/depth, this should not happen!\n"); + return NULL; + } if (!entry->key) { replace = i; break; } + /* we replace hash if we are higher in tree */ + if (HASH_PERFT_DEPTH(entry->data) < mindepth) { + mindepth = HASH_PERFT_DEPTH(entry->data); + replace = i; + } /* * else { - * /\* we replace hash if we are higher in tree *\/ + * * if (key == entry->key && HASH_PERFT_DEPTH(entry->data) > mindepth) { * mindepth = HASH_PERFT_DEPTH(entry->data); * replace = i; @@ -351,12 +364,32 @@ hentry_t *tt_store_perft(const hkey_t key, const u16 depth, const u64 nodes) */ } if (replace >= 0) { - printf("replacing key=%lx=%lx bucket=%lu idx=%d val=%lu\n", - key, entry->key, bucket - hash_tt.keys, replace, nodes); entry = bucket->entry + replace; + if (HASH_PERFT_VAL(entry->data)) { + printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n", + bucket - hash_tt.keys, replace, + entry->key, key, + 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->data = data; return entry; + } else { + printf("TT full, skip\n"); } return NULL; } + +void tt_stats() +{ + printf("TT: sz=%u bits=%u bcks=%'lu entries=%'lu mask=%10x" + "used=%lu hits/miss=%'lu/%'lu\n", + hash_tt.mb, hash_tt.nbits, hash_tt.nbuckets, hash_tt.nkeys, hash_tt.mask, + hash_tt.used_keys, hash_tt.hits, hash_tt.misses); + //printf("\tused=%lu hits/miss=%lu/%lu\n", + // hash_tt.used_keys, hash_tt.hits, hash_tt.misses); +} diff --git a/src/hash.h b/src/hash.h index fb5ecaf..2ebb145 100644 --- a/src/hash.h +++ b/src/hash.h @@ -18,7 +18,7 @@ #include "chessdefs.h" -#define NBUCKETS 4 /* buckets per hash table entry */ +#define ENTRIES_PER_BUCKET 4 /* buckets per hash table entry */ #define HASH_SIZE_DEFAULT 32 /* default: 32Mb */ #define HASH_SIZE_MIN 4 @@ -35,7 +35,7 @@ typedef u64 hkey_t; /* cannot use typedef for key_ * 16 bytes in future, it should be updated to be exactly 32 bytes. */ typedef struct { - hkey_t key; /* zobrist */ + hkey_t key; /* zobrist */ union { u64 data; struct { @@ -59,7 +59,7 @@ typedef struct { typedef struct { - hentry_t entry[NBUCKETS]; + hentry_t entry[ENTRIES_PER_BUCKET]; } bucket_t; typedef struct { @@ -78,9 +78,11 @@ typedef struct { u32 mask; /* nbuckets - 1, key mask */ /* stats - unsure about usage */ - size_t used_buckets; + //size_t used_buckets; size_t used_keys; u64 collisions; + u64 hits; + u64 misses; } hasht_t; /* hack: @@ -128,5 +130,6 @@ void tt_delete(void); hentry_t *tt_probe(hkey_t key); 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); +void tt_stats(void); #endif /* HASH_H */ diff --git a/src/move-do.c b/src/move-do.c index 465c0a8..f8979b8 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -108,7 +108,8 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state) } } else if (is_enpassant(move)) { /* clear grabbed pawn */ 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); } } diff --git a/src/position.c b/src/position.c index 53fd591..bb737ac 100644 --- a/src/position.c +++ b/src/position.c @@ -429,9 +429,10 @@ void pos_print(const pos_t *pos) char str[128]; board_print(pos->board); - printf("fen %s\n", pos2fen(pos, str)); - printf("checkers: %s\n", pos_checkers2str(pos, str, sizeof(str))); - printf("pinners : %s\n", pos_pinners2str(pos, str, sizeof(str))); + printf("key:%lx ", pos->key); + printf("fen: %s\n", pos2fen(pos, str)); + printf("checkers:%s ", pos_checkers2str(pos, str, sizeof(str))); + printf("pinners: %s ", pos_pinners2str(pos, str, sizeof(str))); printf("blockers: %s\n", pos_blockers2str(pos, str, sizeof(str))); } diff --git a/src/search.c b/src/search.c index 9cdd0f7..4718927 100644 --- a/src/search.c +++ b/src/search.c @@ -43,13 +43,16 @@ */ u64 perft(pos_t *pos, int depth, int ply, bool output) { - int subnodes; - u64 nodes = 0; + static movelist_t stack; + //int subnodes; + u64 subnodes, nodes = 0; movelist_t movelist; move_t *move, *last; state_t state; - movelist.nmoves = 0; + if (ply == 1) + stack.nmoves = 0; + pos_set_checkers_pinners_blockers(pos); pos_legal(pos, pos_gen_pseudo(pos, &movelist)); @@ -59,19 +62,46 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) nodes++; } else { move_do(pos, *move, &state); + stack.move[stack.nmoves++] = *move; + if (ply == 2 && + //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) { 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); + hentry_t *entry; + //if (ply >= 4 && ply <= 8) { + if (ply == 4) { + if ((entry = tt_probe_perft(pos->key, depth))) { + subnodes = HASH_PERFT_VAL(entry->data); + printf("tt hit key=%lx ply=%d depth=%d nodes=%lu\n", + pos->key, ply, depth, subnodes); + } else { + subnodes = perft(pos, depth - 1, ply + 1, output); + tt_store_perft(pos->key, depth, subnodes); + } + } 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); + printf("%s: %lu\n", move_to_str(movestr, *move, 0), subnodes); } nodes += subnodes; move_undo(pos, *move, &state); + stack.nmoves--; } } @@ -99,7 +129,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output) move_t *move, *last; state_t state; - movelist.nmoves = 0; + //movelist.nmoves = 0; pos_set_checkers_pinners_blockers(pos); state = pos->state; @@ -119,7 +149,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output) } if (output && ply == 1) { char movestr[8]; - printf("%s: %d\n", move_str(movestr, *move, 0), subnodes); + printf("%s: %d\n", move_to_str(movestr, *move, 0), subnodes); } nodes += subnodes; move_undo_alt(pos, *move); diff --git a/test/common-test.h b/test/common-test.h index 8973c8f..ba01990 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -28,14 +28,30 @@ struct fentest { char *comment; char *fen; } fentest[] = { - /* - { __LINE__, 1, - "", - "" - }, - */ + /******************* TEMP TESTS BELOW *******************/ -/* ***************** TEMP TESTS ABOVE ************************** */ + /* + * { __LINE__, MOVEGEN | MOVEDO | PERFT, + * "bug perft TT après 1.b4 f5", + * "1nbqkbn1/ppp1p1pp/8/r1rpPpK1/1P6/8/P1PP1PPP/RNBQ1BNR w - f6 0 2" + * }, + */ + + /* + * { __LINE__, MOVEGEN | MOVEDO | PERFT, + * "bug perft TT après 1.b4", + * "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__, MOVEGEN | MOVEDO | PERFT, "illegal white e.p.", @@ -138,7 +154,7 @@ struct fentest { "checker: h4", "4k3/8/8/8/7b/8/8/4K3 w - - 0 1" }, -// First game moves + // First game moves { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "startpos", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" @@ -425,6 +441,10 @@ static int fentest_cur = -1; static char *next_fen(uint module) { fentest_cur++; + + /* skip first entry if NULL - for special testing, see */ + if (fentest_cur == 0 && fentest[fentest_cur].fen == NULL) + fentest_cur++; while (fentest[fentest_cur].fen && !(fentest[fentest_cur].modules & module)) fentest_cur++; return fentest[fentest_cur].fen; diff --git a/test/perft-test.c b/test/perft-test.c index 87cfbe9..ef476f6 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -255,8 +255,8 @@ static __unused void compare_moves(movelist_t *fish, movelist_t *me) static int usage(char *prg) { - fprintf(stderr, "Usage: %s [-d depth] [-p pertf-modules] [-n][-v]\n", prg); - fprintf(stderr, "\t-d: depth, -p: 1-3, -n: no SF res check, -v: output moves\n"); + fprintf(stderr, "Usage: %s [-cmv][-d depth] [-p perft-version] \n", prg); + fprintf(stderr, "\t-c/m: print comments/moves, -n: no SF check, -d: depth, -p: 1-3, \n"); return 1; } @@ -264,6 +264,7 @@ int main(int ac, char**av) { int test_line; u64 sf_count = 0, my_count; + bool comment = false; char *fen; pos_t *pos = NULL, *fenpos; pos_t *fishpos = pos_new(); @@ -283,8 +284,11 @@ int main(int ac, char**av) int opt, depth = 6, run = 3; bool sf_run = true, perft_output = false; - while ((opt = getopt(ac, av, "vnd:p:")) != -1) { + while ((opt = getopt(ac, av, "cmnd:p:")) != -1) { switch (opt) { + case 'c': + comment = true; + break; case 'd': depth = atoi(optarg); break; @@ -294,7 +298,7 @@ int main(int ac, char**av) case 'n': sf_run = false; break; - case 'v': + case 'm': perft_output = true; break; default: @@ -315,7 +319,10 @@ int main(int ac, char**av) CLOCK_DEFINE(clock, CLOCK_MONOTONIC); while ((fen = next_fen(PERFT | MOVEDO))) { + if (comment) + printf("%s\n", *cur_comment()? cur_comment(): ""); test_line = cur_line(); + tt_clear(); if (!(fenpos = fen2pos(pos, fen))) { printf("wrong fen line = %d: [%s]\n", test_line, fen); continue; @@ -370,6 +377,7 @@ int main(int ac, char**av) printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", test_line, sf_count, my_count, fen); } + tt_stats(); } if (run & 2) { diff --git a/test/tt-test.c b/test/tt-test.c new file mode 100644 index 0000000..2d1f112 --- /dev/null +++ b/test/tt-test.c @@ -0,0 +1,159 @@ +/* tt-test.c - transposition table test. + * + * 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 +#include +#include +#include + +#include + +#include "chessdefs.h" +#include "fen.h" +#include "position.h" +#include "move.h" +#include "move-do.h" +#include "move-gen.h" +#include "search.h" + +// #include "common-test.h" + +static move_t move_in_movelist(movelist_t *ml, square_t from, square_t to, piece_type_t pt) +{ + const int nmoves = ml->nmoves; + const move_t *moves = ml->move; + int movenum = 0; + move_t move; + for (movenum = 0; movenum < nmoves; ++movenum) { + move = moves[movenum]; + printf("compare %s%s to %s%s pt=%d ", + sq_to_string(from), sq_to_string(to), + sq_to_string(move_from(move)), + sq_to_string(move_to(move)), + pt + ); + if (move_from(move) == from && move_to(move) == to) { + printf("HIT!\n"); + if (pt != NO_PIECE_TYPE && move_promoted(move) != pt) + continue; + printf("move_in_movelist(%s%s) found from=%s to=%s\n", + sq_to_string(from), sq_to_string(to), + sq_to_string(move_from(move)), + sq_to_string(move_to(move))); + return move; + } else + puts(""); + } + return MOVE_NONE; +} + +static move_t move_from_str(pos_t *pos, const char *move) +{ + movelist_t movelist; + square_t from = sq_from_string(move); + square_t to = sq_from_string(move + 2); + piece_type_t promoted = piece_t_from_char(*(move + 4)); + printf("from=%o to=%o promoted=%d\n", from, to, promoted); + + pos_set_checkers_pinners_blockers(pos); + pos_legal(pos, pos_gen_pseudo(pos, &movelist)); + return move_in_movelist(&movelist, from, to, promoted); +} + +static void pr_entry(hentry_t *entry) +{ + if (!entry) + printf("entry: NULL\n"); + else { + printf("entry: key=%lx depth=%d n=%lu\n", + entry->key, HASH_PERFT_DEPTH(entry->data), + HASH_PERFT_VAL(entry->data)); + } +} + +int main() +{ + pos_t *pos = NULL; + char *token, *str, buf[128]; + hentry_t *entry; + move_t move; + state_t state; + //movelist_t movelist; + + const char *moves_array[] = { + "e2e4 e7e5 g1f3 b8c6", + "e2e4 b8c6 g1f3 e7e5" + }; + + init_all(); + + for (uint i = 0; i < ARRAY_SIZE(moves_array); ++i) { + int depth = 0; + str = strdup(moves_array[i]); + printf("%2d: ", i + 1); + + pos = startpos(pos); + entry = tt_store_perft(pos->key, 0, 123 + depth); + pr_entry(entry); + token = strtok(str, " \t"); + while (token) { + depth++; + printf("%s ", token); + + //pos_set_checkers_pinners_blockers(pos); + //pos_legal(pos, pos_gen_pseudo(pos, &movelist)); + move = move_from_str(pos, token); + printf("move: %s\n", move_to_str(buf, move, 0)); + move_do(pos, move, &state); + if ((entry = tt_probe_perft(pos->key, depth))) { + printf("tt hit: depth=%d val=%lu", + HASH_PERFT_DEPTH(entry->data), + HASH_PERFT_VAL(entry->data)); + } else { + tt_store_perft(pos->key, i + 1, depth); + printf("tt store: depth=%d val=%lu", depth, (u64)i * 123); + }; + + token = strtok(NULL, " \t"); + } + printf("\n"); + free(str); + } + return 0; +} + +/* ccls bug report: https://github.com/emacs-lsp/emacs-ccls/issues/126 + */ +/* + * int called(int), caller(); + * + * /\** + * * called() - test ccls. + * * @x: int, the test value + * * + * * @called() description. + * * + * * @return: int, a very interesting value. + * *\/ + * int called(int x) { return x; } + * + * int caller() + * { + * int i = 0; + * called(int x) + * return i; + * } + */ From 2fbad1319e6fd1b66fee4fcb427a1c3e932c4971 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 17 Jun 2024 07:35:14 +0200 Subject: [PATCH 06/10] fix EP_ZOBRIST_IDX macro (oops !) --- Makefile | 9 +++++---- src/hash.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 77119c4..3effa97 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ # SHELL := /bin/bash -#CC := gcc -CC := gcc-13 +CC := gcc +#CC := gcc-13 #CC := clang BEAR := bear TOUCH := touch @@ -69,8 +69,9 @@ CPPFLAGS += -DBUG_ON # brlib bug.h # fen.c #CPPFLAGS += -DDEBUG_FEN # FEN decoding -# hash.c -CPPFLAGS += -DZOBRIST_VERIFY # chk zobrist consistency +# hash / TT +#CPPFLAGS += -DZOBRIST_VERIFY # double chk zobrist +#CPPFLAGS += -DPERFT_MOVE_HISTORY # perft, keep prev moves # attack.c #CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers diff --git a/src/hash.h b/src/hash.h index 2ebb145..7119bd1 100644 --- a/src/hash.h +++ b/src/hash.h @@ -93,7 +93,7 @@ typedef struct { * we use the formula: * 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_castling[4 * 4 + 1]; From e61e1518f4fea6d65424c3ca61b8222878624b31 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 17 Jun 2024 07:37:22 +0200 Subject: [PATCH 07/10] move_do_alt: use &state (as move_do) --- src/move-do.c | 15 +++++++++------ src/move-do.h | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/move-do.c b/src/move-do.c index f8979b8..7f36bf7 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -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. */ pos->clock_50 = 0; 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]) { pos->en_passant = ep; 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) */ -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); 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); hkey_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]; @@ -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. */ pos->clock_50 = 0; 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]) { pos->en_passant = ep; 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]; + piece_t pc = pos->board[grabbed]; + key ^= zobrist_pieces[pc][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; } -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); 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->state = *state; /* restore irreversible changes */ + pos->state = *state; /* restore irreversible changes */ pos->turn = us; return pos; } diff --git a/src/move-do.h b/src/move-do.h index 1bfe032..17ca7dd 100644 --- a/src/move-do.h +++ b/src/move-do.h @@ -19,7 +19,8 @@ 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_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); +/* new version testing */ +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 */ From dfecad9ea6add880d12adeb4b227b42774f88bc7 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 17 Jun 2024 07:38:43 +0200 Subject: [PATCH 08/10] cleanup --- src/move-gen.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/move-gen.c b/src/move-gen.c index fd435e9..0a76655 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -353,19 +353,12 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist) bitboard_t from_bb, to_bb; bitboard_t tmp_bb; move_t *moves = movelist->move; - //int *nmoves = &movelist->nmoves; square_t from, to; square_t king = pos->king[us]; - //*nmoves = 0; - /* king - MUST BE FIRST */ to_bb = bb_king_moves(dest_squares, king); 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 */ goto finish; @@ -382,8 +375,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist) * Attention ! Castling flags are assumed correct */ 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. * 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 */ from_bb = pos->bb[us][BISHOP] | pos->bb[us][QUEEN]; while (from_bb) { from = bb_next(&from_bb); to_bb = hyperbola_bishop_moves(occ, from) & dest_squares; 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]; while (from_bb) { from = bb_next(&from_bb); to_bb = hyperbola_rook_moves(occ, from) & dest_squares; moves = moves_gen(moves, from, to_bb); - //while(to_bb) { - // to = bb_next(&to_bb); - // *moves++ = move_make(from, to); - //} } /* knight */ @@ -428,10 +412,6 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist) from = bb_next(&from_bb); to_bb = bb_knight_moves(dest_squares, from); 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 */ @@ -460,13 +440,11 @@ movelist_t *pos_gen_pseudo(pos_t *pos, movelist_t *movelist) while(to_bb) { to = bb_next(&to_bb); from = to - shift - shift; - //*moves++ = move_make_flags(from, to, M_DPUSH); *moves++ = move_make(from, to); } /* pawn: captures */ tmp_bb = bb_pawns_attacks(pos->bb[us][PAWN], shift) & enemy_pieces; - //bb_print("FAIL", tmp_bb); to_bb = tmp_bb & ~rel_rank8; while (to_bb) { to = bb_next(&to_bb); From 840202dc0e7f7a89f6b12a10545c6a2c68139a44 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 17 Jun 2024 07:39:13 +0200 Subject: [PATCH 09/10] perft-test.c/common-test.h: add sentinel for temp tests / TT stats --- test/common-test.h | 81 ++++++++++++++--------------- test/perft-test.c | 124 ++++++++++++++++++++++++++------------------- 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/test/common-test.h b/test/common-test.h index ba01990..6328899 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -28,29 +28,25 @@ struct fentest { char *comment; char *fen; } fentest[] = { - /******************* TEMP TESTS BELOW *******************/ + /************************************************************ + * TEMP TESTS BELOW - only run them (till sentinel below) * + ************************************************************/ /* - * { __LINE__, MOVEGEN | MOVEDO | PERFT, - * "bug perft TT après 1.b4 f5", - * "1nbqkbn1/ppp1p1pp/8/r1rpPpK1/1P6/8/P1PP1PPP/RNBQ1BNR w - f6 0 2" + * { __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" + * }, + * { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, + * "", + * "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" * }, */ - /* - * { __LINE__, MOVEGEN | MOVEDO | PERFT, - * "bug perft TT après 1.b4", - * "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 */ + /****************************************************************** + * DO NOT DELETE NEXT LINE - sentinel entry for temp tests above. * + * ignored if first array entry. * + ******************************************************************/ { __LINE__, 0, NULL, NULL }, { __LINE__, MOVEGEN | MOVEDO | PERFT, @@ -154,40 +150,45 @@ struct fentest { "checker: h4", "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 { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "startpos", "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, - "1.e3 Nc6 2.Ke2 Nd4+ - perft bug", - "r1bqkbnr/pppppppp/8/8/3n4/4P3/PPPPKPPP/RNBQ1BNR w kq - 3 3" - }, - { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, - "1.e4", + "startpos + 1.e4", "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1" }, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, - "1.Nh3", - "rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1" + "startpos + 1.e4 e5 2.Nf3", + "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2" }, { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, - "1.e4 e5 2.Nf3 Nc6", - "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1" + "startpos + 1.e4 e5 2.Nf3 Nc6", + "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 // both can castle queen only { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, diff --git a/test/perft-test.c b/test/perft-test.c index ef476f6..8db151d 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -255,21 +255,29 @@ static __unused void compare_moves(movelist_t *fish, movelist_t *me) static int usage(char *prg) { - fprintf(stderr, "Usage: %s [-cmv][-d depth] [-p perft-version] \n", prg); - fprintf(stderr, "\t-c/m: print comments/moves, -n: no SF check, -d: depth, -p: 1-3, \n"); + fprintf(stderr, "Usage: %s [-cms][-d depth] [-p version] [-t size:\n", prg); + 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; } int main(int ac, char**av) { - int test_line; + int curtest = 0; u64 sf_count = 0, my_count; - bool comment = false; + bool comment = true, sf_run = false, moves_output = false; char *fen; pos_t *pos = NULL, *fenpos; pos_t *fishpos = pos_new(); movelist_t fishmoves; FILE *outfd = NULL; + s64 ms, lps; + int opt, depth = 6, run = 3, tt = 32, newtt = 32; struct { s64 count, ms; s64 minlps, maxlps; @@ -279,54 +287,73 @@ int main(int ac, char**av) { .minlps=LONG_MAX }, { .minlps=LONG_MAX }, }; - s64 ms, lps; - int opt, depth = 6, run = 3; - bool sf_run = true, perft_output = false; - - while ((opt = getopt(ac, av, "cmnd:p:")) != -1) { + while ((opt = getopt(ac, av, "cd:mp:st:")) != -1) { switch (opt) { case 'c': - comment = true; + comment = false; break; case 'd': depth = atoi(optarg); - break; - case 'p': /* 1 or 2 or 3 for both */ - run = atoi(optarg); - break; - case 'n': - sf_run = false; + if (depth <= 0) + depth = 6; break; 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; default: return usage(*av); } } - printf("perft: depth = %d run = %x stockfish = %s\n", - depth, run, sf_run? "true": "false"); + if (!run) { + printf("Nothing to do, exiting\n"); + exit(0); + } 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) - exit(0); + tt_info(); + printf("\n"); + + printf("move_t size:%lu\n", sizeof(move_t)); if (sf_run) outfd = open_stockfish(); CLOCK_DEFINE(clock, CLOCK_MONOTONIC); while ((fen = next_fen(PERFT | MOVEDO))) { - if (comment) - printf("%s\n", *cur_comment()? cur_comment(): ""); - test_line = cur_line(); - tt_clear(); 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; } + 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; if (sf_run) { stockfish_fen(outfd, fen); @@ -345,15 +372,13 @@ int main(int ac, char**av) 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); + printf("Stockfish : perft:%'lu ms:%'ld lps:%'lu\n", + sf_count, ms, lps); } if (run & 1) { clock_start(&clock); - my_count = perft(pos, depth, 1, perft_output); + my_count = perft(pos, depth, 1, moves_output); ms = clock_elapsed_ms(&clock); if (!ms) { res[0].skipped++; @@ -369,20 +394,17 @@ int main(int ac, char**av) } if (!sf_run || sf_count == my_count) { - printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", - test_line, my_count, ms, - lps, - fen); + printf("perft : perft:%'lu ms:%'ld lps:%'lu ", + my_count, ms, lps); + tt_stats(); } else { - printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", - test_line, sf_count, my_count, fen); + printf("perft : perft:%'lu ***ERROR***\n", my_count); } - tt_stats(); } if (run & 2) { 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); if (!ms) { res[1].skipped++; @@ -398,13 +420,10 @@ int main(int ac, char**av) } if (!sf_run || sf_count == my_count) { - printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", - test_line, my_count, ms, - lps, - fen); + printf("perft_alt : perft:%'lu ms:%'ld lps:%'lu\n", + my_count, ms, lps); } else { - printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", - test_line, sf_count, my_count, fen); + printf("perft_alt : perft:%'lu ***ERROR***\n", my_count); } } printf("\n"); @@ -413,29 +432,32 @@ int main(int ac, char**av) 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", + 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 * 1000l / res[2].ms, res[2].minlps, res[2].maxlps, - res[2].skipped); + res[0].skipped, curtest); } if (run & 1) { if (!res[0].ms) 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 * 1000l / res[0].ms, res[0].minlps, res[0].maxlps, - res[0].skipped); + res[0].skipped, curtest); } if (run & 2) { if (!res[1].ms) 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 * 1000l / res[1].ms, res[1].minlps, res[1].maxlps, - res[1].skipped); + res[0].skipped, curtest); } return 0; } From f1657e9806de53f6a9291028b54808c507939be1 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Mon, 17 Jun 2024 07:45:57 +0200 Subject: [PATCH 10/10] working TT with perft --- src/hash.c | 116 ++++++++++++++++++++++++++++++++------------------- src/hash.h | 5 ++- src/search.c | 67 ++++++++++++----------------- 3 files changed, 102 insertions(+), 86 deletions(-) diff --git a/src/hash.c b/src/hash.c index 6e34c0c..6615ba4 100644 --- a/src/hash.c +++ b/src/hash.c @@ -111,7 +111,6 @@ hkey_t zobrist_calc(pos_t *pos) bool zobrist_verify(pos_t *pos) { hkey_t diff, key = zobrist_calc(pos); - if (pos->key == key) return true; @@ -251,9 +250,11 @@ void tt_clear() */ void tt_delete() { - if (hash_tt.keys) + if (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; if (key == entry->key && HASH_PERFT_DEPTH(entry->data) == depth) { 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; } } - //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++; 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; hentry_t *entry; - int replace = -1, newkey = 0; + int replace = -1; uint mindepth = 1024; u64 data = HASH_PERFT(depth, nodes); //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)); - 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); bucket = hash_tt.keys + (key & hash_tt.mask); /* find key in buckets */ for (int i = 0; i < ENTRIES_PER_BUCKET; ++i) { entry = bucket->entry + i; - if (key == entry->key && HASH_PERFT_DEPTH(entry->data)) { - printf("tt_store: sup key/depth, this should not happen!\n"); - return NULL; + //if (!entry->key) { + // 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; + } } - 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) { mindepth = HASH_PERFT_DEPTH(entry->data); replace = i; } - /* - * else { - * - * if (key == entry->key && HASH_PERFT_DEPTH(entry->data) > mindepth) { - * mindepth = HASH_PERFT_DEPTH(entry->data); - * replace = i; - * } - * } - */ } + if (replace >= 0) { entry = bucket->entry + replace; - if (HASH_PERFT_VAL(entry->data)) { - printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n", - bucket - hash_tt.keys, replace, - entry->key, key, - 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); - } + + hash_tt.used_keys += entry->key == 0; + hash_tt.collisions += entry->key && (key != entry->key); + /* + * if (HASH_PERFT_VAL(entry->data)) { + * printf("REPL entry=%lu[%d] key=%lx->%lx val=%lu->%lu\n", + * bucket - hash_tt.keys, replace, + * entry->key, key, + * 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->data = data; return entry; } else { - printf("TT full, skip\n"); + //printf("TT full, skip\n"); } 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() { - printf("TT: sz=%u bits=%u bcks=%'lu entries=%'lu mask=%10x" - "used=%lu hits/miss=%'lu/%'lu\n", - hash_tt.mb, hash_tt.nbits, hash_tt.nbuckets, hash_tt.nkeys, hash_tt.mask, - hash_tt.used_keys, hash_tt.hits, hash_tt.misses); - //printf("\tused=%lu hits/miss=%lu/%lu\n", - // hash_tt.used_keys, hash_tt.hits, hash_tt.misses); + if (hash_tt.keys) { + float percent = 100.0 * hash_tt.used_keys / hash_tt.nkeys; + printf("hash: used:%'lu/%'lu (%.2f%%) hit:%'lu miss:%'lu coll:%'lu\n", + hash_tt.used_keys, hash_tt.nkeys, percent, + hash_tt.hits, hash_tt.misses, + hash_tt.collisions); + } else { + printf("hash: not set.\n"); + } } diff --git a/src/hash.h b/src/hash.h index 7119bd1..c57eed8 100644 --- a/src/hash.h +++ b/src/hash.h @@ -24,7 +24,9 @@ #define HASH_SIZE_MIN 4 #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 */ @@ -130,6 +132,7 @@ void tt_delete(void); hentry_t *tt_probe(hkey_t key); 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); +void tt_info(void); void tt_stats(void); #endif /* HASH_H */ diff --git a/src/search.c b/src/search.c index 4718927..411272b 100644 --- a/src/search.c +++ b/src/search.c @@ -23,9 +23,10 @@ /** * perft() - Perform perft on position - * @pos: &position to search - * @depth: Wanted depth. - * @ply: perft depth level. + * @pos: &position to search + * @depth: Wanted depth. + * @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 * 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) { - static movelist_t stack; - //int subnodes; u64 subnodes, nodes = 0; movelist_t movelist; move_t *move, *last; state_t state; - +# ifdef PERFT_MOVE_HISTORY + static movelist_t stack; if (ply == 1) stack.nmoves = 0; +# endif pos_set_checkers_pinners_blockers(pos); @@ -62,38 +63,23 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) nodes++; } else { move_do(pos, *move, &state); +# ifdef PERFT_MOVE_HISTORY stack.move[stack.nmoves++] = *move; - if (ply == 2 && - //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); - } +# endif if (depth == 2) { movelist_t movelist2; pos_set_checkers_pinners_blockers(pos); subnodes = pos_legal(pos, pos_gen_pseudo(pos, &movelist2))->nmoves; - } else { - hentry_t *entry; - //if (ply >= 4 && ply <= 8) { - if (ply == 4) { - if ((entry = tt_probe_perft(pos->key, depth))) { - subnodes = HASH_PERFT_VAL(entry->data); - printf("tt hit key=%lx ply=%d depth=%d nodes=%lu\n", - pos->key, ply, depth, subnodes); - } else { - subnodes = perft(pos, depth - 1, ply + 1, output); - tt_store_perft(pos->key, depth, subnodes); - } + } else if (ply >= 4) { + hentry_t *entry = tt_probe_perft(pos->key, depth); + if (entry != TT_MISS) { + subnodes = HASH_PERFT_VAL(entry->data); } else { subnodes = perft(pos, depth - 1, ply + 1, output); + tt_store_perft(pos->key, depth, subnodes); } + } else { + subnodes = perft(pos, depth - 1, ply + 1, output); } if (output && ply == 1) { char movestr[8]; @@ -101,7 +87,9 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) } nodes += subnodes; move_undo(pos, *move, &state); +# ifdef PERFT_MOVE_HISTORY stack.nmoves--; +# endif } } @@ -112,9 +100,10 @@ u64 perft(pos_t *pos, int depth, int ply, bool output) /** * perft_alt() - Perform perft on position, experimental version. - * @pos: &position to search - * @depth: Wanted depth. - * @ply: perft depth level. + * @pos: &position to search + * @depth: Wanted depth. + * @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 * 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) { - int subnodes; - u64 nodes = 0; + u64 subnodes, nodes = 0; movelist_t movelist; move_t *move, *last; state_t state; - //movelist.nmoves = 0; pos_set_checkers_pinners_blockers(pos); - state = pos->state; pos_legal(pos, pos_gen_pseudo(pos, &movelist)); last = movelist.move + movelist.nmoves; @@ -139,7 +125,7 @@ u64 perft_alt(pos_t *pos, int depth, int ply, bool output) if (depth == 1) { nodes++; } else { - move_do_alt(pos, *move); + move_do_alt(pos, *move, &state); if (depth == 2) { movelist_t movelist2; 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) { 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; - move_undo_alt(pos, *move); - pos->state = state; + move_undo_alt(pos, *move, &state); } }