From c710da4bf94a603fccce165358c1381dfb776aaa Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 18 Apr 2024 09:54:58 +0200 Subject: [PATCH] move_do2: save/restore state inside func; perft: add silent option --- Makefile | 2 +- src/move-do.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++ src/move-do.h | 3 ++ src/search.c | 22 ++++---- src/search.h | 4 +- test/perft-test.c | 81 ++++++++++++++++++++++------- 6 files changed, 206 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 3033549..542bb56 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,7 @@ CFLAGS += -Wmissing-declarations CFLAGS := $(strip $(CFLAGS)) # development CFLAGS - unused - TODO -#DEV_CFLAGS := -O1 +#DEV_CFLAGS := -Og #DEV_CFLAGS += -g # release CFLAGS - unused - TODO diff --git a/src/move-do.c b/src/move-do.c index 18a2cde..fe62ea2 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -131,6 +131,95 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) 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); + + *state = pos->state; /* save irreversible changes */ + + ++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); + 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); + } + 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; + else if (is_enpassant(move)) { /* clear grabbed pawn */ + square_t grabbed = to - up; + pos_clr_sq(pos, grabbed); + } + } + + pos_clr_sq(pos, from); /* always 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)) { /* do we save time with this test ? */ + 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); + } + + return pos; +} + /** * move_undo() - undo move. * @pos: &pos_t position @@ -186,3 +275,42 @@ 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 7b95b5e..fb2dbce 100644 --- a/src/move-do.h +++ b/src/move-do.h @@ -19,4 +19,7 @@ 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); + #endif /* MOVE_DO_H */ diff --git a/src/search.c b/src/search.c index d71a9fb..1fbef4b 100644 --- a/src/search.c +++ b/src/search.c @@ -41,7 +41,7 @@ * * @return: total moves found at @depth level. */ -u64 perft(pos_t *pos, int depth, int ply) +u64 perft(pos_t *pos, int depth, int ply, bool output) { int subnodes; u64 nodes = 0; @@ -60,8 +60,8 @@ u64 perft(pos_t *pos, int depth, int ply) nodes++; } else { move_do(pos, *move); - subnodes = perft(pos, depth - 1, ply + 1); - if (ply == 1) { + 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); } @@ -71,7 +71,7 @@ u64 perft(pos_t *pos, int depth, int ply) } } - if (ply == 1) + if (output && ply == 1) printf("Total: %lu\n", nodes); return nodes; } @@ -87,7 +87,7 @@ u64 perft(pos_t *pos, int depth, int ply) * * @return: total moves found at @depth level. */ -u64 perft_test(pos_t *pos, int depth, int ply) +u64 perft_test(pos_t *pos, int depth, int ply, bool output) { int subnodes; u64 nodes = 0; @@ -97,7 +97,6 @@ u64 perft_test(pos_t *pos, int depth, int ply) 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; @@ -105,19 +104,18 @@ u64 perft_test(pos_t *pos, int depth, int ply) if (depth == 1) { nodes++; } else { - move_do(pos, *move); - subnodes = perft(pos, depth - 1, ply + 1); - if (ply == 1) { + move_do2(pos, *move, &state); + 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); } nodes += subnodes; - move_undo(pos, *move); - pos->state = state; + move_undo2(pos, *move, &state); } } - if (ply == 1) + if (output && ply == 1) printf("Total: %lu\n", nodes); return nodes; } diff --git a/src/search.h b/src/search.h index c76ce2e..a2d1531 100644 --- a/src/search.h +++ b/src/search.h @@ -19,7 +19,7 @@ //eval_t negamax(pos_t *pos, int depth, int color); //eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color); -u64 perft(pos_t *pos, int depth, int ply); -u64 perft_test(pos_t *pos, int depth, int ply); +u64 perft(pos_t *pos, int depth, int ply, bool output); +u64 perft_test(pos_t *pos, int depth, int ply, bool output); #endif /* SEARCH_H */ diff --git a/test/perft-test.c b/test/perft-test.c index 86f6033..9e75aad 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "chessdefs.h" #include "fen.h" @@ -105,6 +106,7 @@ static u64 send_stockfish_fen(FILE *desc, pos_t *pos, movelist_t *movelist, //sprintf(str, "stockfish \"position fen %s\ngo perft depth\n\"", fen); fprintf(desc, "position fen %s\ngo perft %d\n", fen, depth); //fflush(desc); + while ((buflen = getline(&buf, &alloc, stdin)) > 0) { buf[--buflen] = 0; if (buflen == 0) @@ -233,17 +235,25 @@ int main(int __unused ac, __unused char**av) int i = 0, test_line; u64 sf_count = 0, my_count; char *fen; - pos_t *pos, *savepos, *fishpos = pos_new(); + pos_t *pos; + pos_t *fishpos = pos_new(); movelist_t fishmoves; //move_t move; FILE *outfd = NULL; - s64 ms1 = 0, ms1_total = 0; - s64 ms2 = 0, ms2_total = 0; + struct { + s64 count, ms; + s64 minlps, maxlps; + int skipped; + } res[2] = { + { .minlps=LONG_MAX }, + { .minlps=LONG_MAX }, + }; + s64 ms, lps; int opt, depth = 6, run = 3; - bool sf_run = true; + bool sf_run = true, perft_output = false; - while ((opt = getopt(ac, av, "nd:p:")) != -1) { + while ((opt = getopt(ac, av, "vnd:p:")) != -1) { switch (opt) { case 'd': depth = atoi(optarg); @@ -254,6 +264,9 @@ int main(int __unused ac, __unused char**av) case 'n': sf_run = false; break; + case 'v': + perft_output = true; + break; default: return usage(*av); } @@ -285,18 +298,29 @@ int main(int __unused ac, __unused char**av) if (sf_run) sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth); - savepos = pos_dup(pos); + // savepos = pos_dup(pos); if (run & 1) { clock_start(&clock); - my_count = perft(pos, depth, 1); - ms1 = clock_elapsed_ms(&clock); - ms1_total += ms1; + my_count = perft(pos, depth, 1, perft_output); + ms = clock_elapsed_ms(&clock); + if (!ms) { + res[0].skipped++; + lps = 0; + } else { + lps = my_count * 1000l / ms; + res[0].ms += ms; + res[0].count += my_count; + if (lps > res[0].maxlps) + res[0].maxlps = lps; + if (lps < res[0].minlps) + res[0].minlps = lps; + } if (!sf_run || sf_count == my_count) { printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", - test_line, my_count, ms1, - ms1? my_count*1000l/ms1: 0, + test_line, my_count, ms, + lps, fen); } else { printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", @@ -306,14 +330,25 @@ int main(int __unused ac, __unused char**av) if (run & 2) { clock_start(&clock); - my_count = perft_test(pos, depth, 1); - ms2 = clock_elapsed_ms(&clock); - ms2_total += ms2; + my_count = perft_test(pos, depth, 1, perft_output); + ms = clock_elapsed_ms(&clock); + if (!ms) { + res[1].skipped++; + lps = 0; + } else { + lps = my_count * 1000l / ms; + res[1].ms += ms; + res[1].count += my_count; + if (lps > res[1].maxlps) + res[1].maxlps = lps; + if (lps < res[1].minlps) + res[1].minlps = lps; + } if (!sf_run || sf_count == my_count) { printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", - test_line, my_count, ms2, - ms2? my_count*1000l/ms2: 0, + test_line, my_count, ms, + lps, fen); } else { printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", @@ -321,15 +356,23 @@ int main(int __unused ac, __unused char**av) } } printf("\n"); - pos_del(savepos); + // pos_del(savepos); pos_del(pos); i++; /* to run first test only */ // exit(0); } if (run & 1) - printf("total perft %'ldms\n", ms1_total); + printf("total perft %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", + res[0].count, res[0].ms, + res[0].count * 1000l / res[0].ms, + res[0].minlps, res[0].maxlps, + res[0].skipped); if (run & 2) - printf("total perft2 %'ldms\n", ms2_total); + printf("total perft2 %'lums %'lums lps=%'lu min=%'lu max=%'lu (skipped %d)\n", + res[1].count, res[1].ms, + res[1].count * 1000l / res[1].ms, + res[1].minlps, res[1].maxlps, + res[1].skipped); return 0; }