From 08ba9891701b3172059cbba1e652ebdc53518ea9 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 26 Mar 2024 17:37:18 +0100 Subject: [PATCH] is_legal: fix check+pinned and knight check; perft-test + perft2() --- src/move-gen.c | 20 ++- src/search.c | 55 +++++++-- src/search.h | 1 + test/common-test.h | 175 ++++++++++++++++----------- test/movedo-test.c | 2 +- test/movegen-test.c | 3 + test/perft-test.c | 288 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 457 insertions(+), 87 deletions(-) create mode 100644 test/perft-test.c diff --git a/src/move-gen.c b/src/move-gen.c index a453790..14f1d23 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -40,6 +40,7 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) square_t from = move_from(move), to = move_to(move), king = pos->king[us], sq; piece_type_t piece = PIECE(pos->board[from]); bitboard_t kingbb = pos->bb[us][KING], tmp; + u64 pinned = mask(from) & pos->blockers & pos->bb[us][ALL_PIECES]; /* (1) - King * For castling, we already tested intermediate squares attacks @@ -53,14 +54,22 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) /* (2) - King is in check * Double-check is already handled, as only K moves were generated. - * Here, allowed moves are only on King-attacker line, including - * attacker.We can move a piece on - * in pseudo move generation, so we only care destination square here. + * Here, allowed dest squares are only on King-checker line, or on checker + * square. + * attacker. + * Special cases: + * e.p., legal if the taken pawn is giving check + * pinned piece: always illegal */ if (pos->checkers) { if (popcount64(pos->checkers) == 1) { /* one checker */ square_t checker = ctz64(pos->checkers); - bitboard_t between = bb_between[king][checker]; + if (pinned) + return false; + if (is_enpassant(move)) { + return pawn_push_up(pos->en_passant, them) == checker; + } + bitboard_t between = bb_between[king][checker] | pos->checkers; return mask(to) & between; } return false; /* double check */ @@ -69,7 +78,8 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) /* (3) - pinned pieces * We verify here that pinned piece P stays on line King-P. */ - if (mask(from) & pos->blockers & pos->bb[us][ALL_PIECES]) { + //if (mask(from) & pos->blockers & pos->bb[us][ALL_PIECES]) { + if (pinned) { bitboard_t line = bb_line[from][king]; return line & mask(to); /* to is not on pin line */ } diff --git a/src/search.c b/src/search.c index 983370d..418fd12 100644 --- a/src/search.c +++ b/src/search.c @@ -18,10 +18,11 @@ #include "position.h" #include "move-gen.h" #include "move-do.h" +#include "search.h" +#include "attack.h" //#include "move.h" //#include "eval.h" -//#include "search.h" /** * perft() - Perform perft on position @@ -36,22 +37,22 @@ */ u64 perft(pos_t *pos, int depth, int ply) { - int movetmp = 0, nodes = 0, subnodes, nmove = 0; + int subnodes, nmove = 0; + u64 nodes = 0; movelist_t pseudo = { .nmoves = 0 }, legal = { .nmoves = 0 }; move_t move; if (depth == 0) return 1; pos->checkers = pos_checkers(pos, pos->turn); - pos->pinners = pos_king_pinners(pos, pos->turn); - pos->blockers = pos_king_blockers(pos, pos->turn, pos->pinners); + pos_set_pinners_blockers(pos); pos_gen_pseudomoves(pos, &pseudo); pos_all_legal(pos, &pseudo, &legal); - //for (nmove = 0; nmove < legal.nmoves; ++nmove ) { - while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) { - + for (nmove = 0; nmove < legal.nmoves; ++nmove ) { + //while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) { + //printf("depth=%d movetmp=%d\n", depth, movetmp); state_t state; move = legal.move[nmove]; move_do(pos, move, &state); @@ -64,7 +65,45 @@ u64 perft(pos_t *pos, int depth, int ply) move_undo(pos, move, &state); } if (ply == 1) - printf("Total: %d\n", nodes); + printf("\nTotal: %lu\n", nodes); + return nodes; +} + +u64 perft2(pos_t *pos, int depth, int ply) +{ + int subnodes, nmove = 0; + u64 nodes = 0; + movelist_t pseudo = { .nmoves = 0 }; + move_t move; + + if (is_in_check(pos, OPPONENT(pos->turn))) + return 0; + if (depth == 0) + return 1; + pos->checkers = pos_checkers(pos, pos->turn); + pos->pinners = pos_king_pinners(pos, pos->turn); + pos->blockers = pos_king_blockers(pos, pos->turn, pos->pinners); + + pos_gen_pseudomoves(pos, &pseudo); + //pos_all_legal(pos, &pseudo, &legal); + + for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) { + //while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) { + //printf("depth=%d movetmp=%d\n", depth, movetmp); + state_t state; + move = pseudo.move[nmove]; + move_do(pos, move, &state); + subnodes = perft2(pos, depth - 1, ply + 1); + + if (ply == 1) { + char movestr[8]; + printf("%s: %d\n", move_str(movestr, move, 0), subnodes); + } + nodes += subnodes; + move_undo(pos, move, &state); + } + if (ply == 1) + printf("\nTotal: %lu\n", nodes); return nodes; } diff --git a/src/search.h b/src/search.h index d4f8f12..9fbf9e7 100644 --- a/src/search.h +++ b/src/search.h @@ -20,5 +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); +u64 perft2(pos_t *pos, int depth, int ply); #endif /* SEARCH_H */ diff --git a/test/common-test.h b/test/common-test.h index 800671e..465275d 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -20,6 +20,7 @@ #define MOVEGEN 4 #define ATTACK 8 #define MOVEDO 16 +#define PERFT 32 struct fentest { int line; @@ -38,41 +39,53 @@ struct fentest { */ //"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1", //"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1", - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal white e.p.", "3k4/8/8/2qpPK2/8/8/8/8 w - d6 0 1", }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal black e.p.", "8/8/8/8/2QPpk2/8/8/3K4 b - d3 0 1", }, - - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal white e.p.", "3k4/8/5K2/3pP3/8/2b5/8/8 w - d6 0 1", }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal black e.p.", "8/8/2B5/8/3Pp3/5k2/8/3K4 b - d3 0 1", }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "legal white e.p.", "1K1k4/8/8/3pP3/8/6b1/8/8 w - d6 0 1", }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "legal black e.p.", "8/8/6B1/8/3Pp3/8/8/1k1K4 b - d3 0 1", }, - - { __LINE__, MOVEGEN, - "white mate.", - "1nbqkbn1/ppp1pppp/8/r1rpP1K1/8/8/PPPP1PPP/RNBQ1BNR w - d6 0 1", - }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal e.p.", "1nbqkbn1/ppp1pppp/8/r1rpP1K1/8/8/PPPP1PPP/RNBQ1BNR w - d6 0 1", }, + { __LINE__, MOVEGEN | MOVEDO | PERFT, + "illegal e.p. bug perft at depth 4", + "1nbqkbn1/ppp2ppp/4p3/r1rpP3/6K1/P7/1PPP1PPP/RNBQ1BNR b - - 1 2" + }, + /* + * { __LINE__, MOVEGEN | MOVEDO | PERFT, + * "illegal e.p. bug perft depth 3", + * "1nbqkbn1/ppp2ppp/4p3/2rpP3/r5K1/P7/1PPP1PPP/RNBQ1BNR w - - 2 3" + * }, + * { __LINE__, MOVEGEN | MOVEDO | PERFT, + * "illegal e.p. bug perft depth 2", + * "1nbqkbn1/ppp2ppp/4p3/2rpP3/r4PK1/P7/1PPP2PP/RNBQ1BNR b - - 0 3" + * }, + * { __LINE__, MOVEGEN | MOVEDO | PERFT | PERFT, + * "illegal e.p. bug perft depth 1 - fixed", + * "1nb1kbn1/ppp2ppp/4p3/2rpP1q1/r4PK1/P7/1PPP2PP/RNBQ1BNR w - - 1 4" + * }, + */ { __LINE__, ATTACK, "checkers: a1 h1", "1k6/8/8/8/8/8/8/r2K3r w - - 1 1" @@ -113,7 +126,7 @@ struct fentest { "3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 1 0" }, - { __LINE__, MOVEGEN, + { __LINE__, MOVEGEN | MOVEDO | PERFT, "only pawn captures", "4k3/8/2p1p3/2PpP3/8/pp4pp/PP4PP/4K3 w - d6 0 1" }, @@ -123,59 +136,75 @@ struct fentest { "4k3/8/8/8/7b/8/8/4K3 w - - 0 1" }, // First game moves - { __LINE__, FEN | MOVEGEN, - "initial pos", + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, + "startpos", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + //{ __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", "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "1.Nh3", "rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "1.e4 e5 2.Nf3 Nc6", "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1" }, // castling test // both can castle queen only - { __LINE__, FEN | MOVEGEN, - "", + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, + "no Q castling", "r3k2r/8/3B4/8/8/3b4/8/R3K2R w KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, - "", + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, + "no castling, 2 mates in 1", "r3k2r/8/3BB3/8/8/3bb3/8/R3K2R w KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r2bkb1r/8/8/8/8/3bb3/8/R2BKB1R w KQkq - 0 1" }, // - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "4 castle possible, only K+R", "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1" }, // - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "only kings on A1/A8, white to play", "k7/8/8/8/8/8/8/K7 w - - 0 1" }, // - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "only one move possible (Pf2xBg3)", "k7/8/8/1p1p4/pPpPp3/P1PpPpb1/NBNP1P2/KBB1B3 w - - 0 1" }, // - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "only 2 moves possible (Ph5xg6 e.p., Ph5-h6)", "k7/8/8/1p1p2pP/pPpPp3/P1PpPp2/NBNP1P2/KBB1B3 w - g6 0 1" }, // - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "2 Kings, W/B/ pawns on 7th for promotion", "k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1" }, @@ -183,20 +212,20 @@ struct fentest { // white is a pawn down // white has 36 moves: P=11 + 1 e.p. N=6+3 B=5+5 R=1 Q=3 K=1 + 1 e.p. // black has 33 moves: P=11 N=2+7 B=5 R=3 Q=3 K=1 + castle - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "rnbqk2r/pp1pbpp1/7p/2pPp3/4n3/3B1N2/PPP2PPP/RNBQ1RK1 w kq c6 0 7" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "4k3/4p3/8/b7/1BR1p2p/1Q3P2/5N2/4K3 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1bq1rk1/pppp1ppp/2n2n2/4p3/2B1P3/3PPN2/PPP3PP/RN1QK2R b KQ - 1 7" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "6k1/6pp/R2p4/p1p5/8/1P1r3P/6P1/6K1 b - - 3 3" }, @@ -204,152 +233,152 @@ struct fentest { // some of tests below are from: // - Rodent IV // - https://www.chessprogramming.net/perfect-perft/ - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "8/2p5/3p4/Kp5r/1R3p1k/8/4P1P1/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "1rbqk1nr/p3ppbp/2np2p1/2p5/1p2PP2/3PB1P1/PPPQ2BP/R2NK1NR b KQk - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1bqk2r/pp1p1ppp/2n1pn2/2p5/1bPP4/2NBP3/PP2NPPP/R1BQK2R b KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "rnb1kb1r/ppp2ppp/1q2p3/4P3/2P1Q3/5N2/PP1P1PPP/R1B1KB1R b KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1b2rk1/pp2nppp/1b2p3/3p4/3N1P2/2P2NP1/PP3PBP/R3R1K1 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "n1q1r1k1/3b3n/p2p1bp1/P1pPp2p/2P1P3/2NBB2P/3Q1PK1/1R4N1 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "2r5/8/1n6/1P1p1pkp/p2P4/R1P1PKP1/8/1R6 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r2q1rk1/1b1nbppp/4p3/3pP3/p1pP4/PpP2N1P/1P3PP1/R1BQRNK1 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "6k1/5pp1/7p/p1p2n1P/P4N2/6P1/1P3P1K/8 w - - 0 35" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r4rk1/1pp1q1pp/p2p4/3Pn3/1PP1Pp2/P7/3QB1PP/2R2RK1 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "", "1k6/1b6/8/8/7R/8/8/4K2R b K - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Illegal ep move #1", "3k4/3p4/8/K1P4r/8/8/8/8 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Illegal ep move #2", "8/8/4k3/8/2p5/8/B2P2K1/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "EP Capture Checks Opponent", "8/8/1k6/2b5/2pP4/8/5K2/8 b - d3 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Short Castling Gives Check", "5k2/8/8/8/8/8/8/4K2R w K - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Long Castling Gives Check", "3k4/8/8/8/8/8/8/R3K3 w Q - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Castle Rights", "r3k2r/1b4bq/8/8/8/8/7B/R3K2R w KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Castling Prevented", "r3k2r/8/3Q4/8/8/5q2/8/R3K2R b KQkq - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Promote out of Check", "2K2r2/4P3/8/8/8/8/8/3k4 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Discovered Check", "8/8/1P2K3/8/2n5/1q6/8/5k2 b - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Promote to give check", "4k3/1P6/8/8/8/8/K7/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Under Promote to give check", "8/P1k5/K7/8/8/8/8/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Self Stalemate", "K1k5/8/P7/8/8/8/8/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Stalemate & Checkmate", "8/k1P5/8/1K6/8/8/8/8 w - - 0 1" }, - { __LINE__, FEN | MOVEGEN, + { __LINE__, FEN | MOVEGEN | MOVEDO | PERFT, "Stalemate & Checkmate", "8/8/2k5/5q2/5n2/8/5K2/8 b - - 0 1" }, @@ -370,15 +399,15 @@ struct fentest { "illegal, SF crash", "2r1k3/3P4/8/8/8/8/8/4K3 w - - 0 1" }, - { __LINE__, MOVEDO, + { __LINE__, MOVEDO | PERFT, "simple movedo/undo: only 2 W knights", "8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1" }, - { __LINE__, MOVEDO, + { __LINE__, MOVEDO | PERFT, "simple movedo/undo: only 2 W knights", "8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1" }, - { __LINE__, MOVEDO, + { __LINE__, MOVEDO | PERFT, "simple movedo/undo: only 2 W knights", "5n2/1k6/8/8/5K2/8/P7/1N6 w - - 0 1" }, diff --git a/test/movedo-test.c b/test/movedo-test.c index 62a6179..c470af4 100644 --- a/test/movedo-test.c +++ b/test/movedo-test.c @@ -37,7 +37,7 @@ int main(int __unused ac, __unused char**av) bitboard_init(); hyperbola_init(); - while ((fen = next_fen(MOVEGEN | MOVEDO))) { + while ((fen = next_fen(MOVEDO))) { test_line = cur_line(); if (!(pos = fen2pos(NULL, fen))) { printf("wrong fen %d: [%s]\n", i, fen); diff --git a/test/movegen-test.c b/test/movegen-test.c index 4d71a0e..9cfbfc6 100644 --- a/test/movegen-test.c +++ b/test/movegen-test.c @@ -238,7 +238,10 @@ int main(int __unused ac, __unused char**av) /* print movelists */ send_stockfish_fen(outfd, fishpos, &fishmoves, fen); pos_gen_pseudomoves(pos, &pseudo); + //moves_print(&pseudo, 0); pos_all_legal(pos, &pseudo, &legal); + + //moves_print(&legal, 0); //printf("Fu "); //moves_print(fishpos, 0); //fflush(stdout); diff --git a/test/perft-test.c b/test/perft-test.c new file mode 100644 index 0000000..3647f8b --- /dev/null +++ b/test/perft-test.c @@ -0,0 +1,288 @@ +/* perft-test.c - perft 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 "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" + +#define RD 0 +#define WR 1 + +typedef struct { + move_t move; + int count; +} ddperft; + +/** + * return write pipd fd + */ +static FILE *open_stockfish() +{ + int rpipe[2], wpipe[2]; + FILE *out_desc; + pid_t pid; + + if ((pipe(rpipe) < 0) || (pipe(wpipe) < 0)) { + perror("pipe"); + return NULL; + } + if ((pid = fork()) < 0) { + perror("fork"); + return NULL; + } + + if (!pid) { /* stockfish */ + setvbuf(stdin, NULL, _IOLBF, 0); + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + + dup2(wpipe[RD], STDIN_FILENO); + dup2(rpipe[WR], STDOUT_FILENO); + dup2(rpipe[WR], STDERR_FILENO); + + close(wpipe[RD]); + close(wpipe[WR]); + close(rpipe[RD]); + close(rpipe[WR]); + if (execlp("stockfish", "stockfish", NULL) == -1) { + perror("execlp"); + return NULL; + } + return 0; /* not reached */ + } + /* us */ + dup2(rpipe[RD], STDIN_FILENO); + setvbuf(stdin, NULL, _IOLBF, 0); + + close(wpipe[RD]); + close(rpipe[RD]); + close(rpipe[WR]); + + out_desc = fdopen(wpipe[WR], "w"); + setvbuf(out_desc, NULL, _IOLBF, 0); + + return out_desc; +} + +static u64 send_stockfish_fen(FILE *desc, pos_t *pos, movelist_t *movelist, + char *fen, int depth) +{ + char *buf = NULL; + u64 count, mycount = 0, fishcount; + size_t alloc = 0; + ssize_t buflen; + + pos_clear(pos); + + move_t *moves = movelist->move; + int *nmoves = &movelist->nmoves; + *nmoves = 0; + //char nodescount[] = "Nodes searched"; + //printf("nmoves = %d\n", nmoves); + fflush(stdout); + //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) + continue; + if (sscanf(buf, "Nodes searched: %lu", &fishcount) == 1) { + break; + } + //printf("%d: %s\n", line++, buf); + if (sscanf(buf, "%*4s: %lu", &count) == 1) { + square_t from = sq_from_string(buf); + square_t to = sq_from_string(buf + 2); + mycount += count; + //printf("move found: %c%c->%c%c %s->%s count=%d\n", + // buf[0], buf[1], buf[2], buf[3], + // sq_to_string(from), sq_to_string(to), + // count); + moves[(*nmoves)++] = move_make(from, to); + + } else if (sscanf(buf, "%*5s: %lu", &count) == 1) { + square_t from = sq_from_string(buf); + square_t to = sq_from_string(buf + 2); + piece_type_t promoted = piece_t_from_char(*(buf + 4)); + mycount += count; + //printf("move found: %c%c->%c%c %s->%s count=%d\n", + // buf[0], buf[1], buf[2], buf[3], + // sq_to_string(from), sq_to_string(to), + // count); + moves[(*nmoves)++] = move_make_promote(from, to, promoted); + } + } + //pos->moves.nmoves = nmoves; + // printf("fishcount=%d mycount=%d\n", fishcount, mycount); + free(buf); + return mycount; +} + +static __unused bool movelists_equal(movelist_t *fish, movelist_t *me) +{ + move_t *m1 = fish->move, *m2 = me->move; + int n1 = fish->nmoves, n2 = me->nmoves; + int mask = 077777; + + if (n1 != n2) + return false; + for (int cur = 0; cur < n1; ++cur) { + if ((m1[cur] & mask) != (m2[cur] & mask)) + return false; + } + return true; +} + +static __unused void compare_moves(movelist_t *fish, movelist_t *me) +{ + char str1[1024] = {0}, str2[1024] = {0}, tmpstr[1024]; + char *skip = " "; + move_t *m1 = fish->move; + move_t *m2 = me->move; + int n1 = fish->nmoves; + int n2 = me->nmoves; + +#define f(c) move_from(c) +#define t(c) move_to(c) + + for (move_t *c1 = m1, *c2 = m2; (c1 - m1 < n1) || (c2 - m2 < n2);) { + // square_t f1 = move_from(*c1); square_t t1 = move_to(*c1); + // square_t f2 = move_from(*c2); square_t t2 = move_to(*c2); + + /* no more move in c2 */ + if (c2 - m2 >= n2) { + while (c1 - m1 < n1) { + sprintf(tmpstr, " %s-%s", sq_to_string(f(*c1)), sq_to_string(t(*c1))); + strcat (str1, tmpstr); + c1++; + } + break; + } + if (c1 - m1 >= n1) { + while (c2 - m2 < n2) { + sprintf(tmpstr, " %s-%s", sq_to_string(f(*c2)), sq_to_string(t(*c2))); + strcat (str2, tmpstr); + c2++; + } + break; + } + + /* missing move in c2 */ + if (f(*c1) < f(*c2) || + (f(*c1) == f(*c2) && t(*c1) < t(*c2))) { + strcat(str2, skip); + sprintf(tmpstr, " %s-%s", sq_to_string(f(*c1)), sq_to_string(t(*c1))); + strcat (str1, tmpstr); + while ((c1 - m1 < n1) && (f(*c1) < f(*c2) || + (f(*c1) == f(*c2) && t(*c1) < t(*c2)))) { + c1++; + } + continue; + } + /* missing move in c1 */ + if (f(*c1) > f(*c2) || + (f(*c1) == f(*c2) && t(*c1) > t(*c2))) { + strcat(str1, skip); + sprintf(tmpstr, " %s-%s", sq_to_string(f(*c2)), sq_to_string(t(*c2))); + strcat (str2, tmpstr); + while ((c2 - m2 < n2) && (f(*c1) > f(*c2) || + (f(*c1) == f(*c2) && t(*c1) > t(*c2)))) { + c2++; + } + continue; + } + sprintf(tmpstr, " %s-%s", sq_to_string(f(*c1)), sq_to_string(t(*c1))); + strcat(str1, tmpstr); + strcat(str2, tmpstr); + c1++, c2++; + } + printf("F(%2d): %s\nM(%2d): %s\n", n1, str1, n2, str2); +} + + +int main(int __unused ac, __unused char**av) +{ + int i = 0, test_line; + u64 sf_count, my_count; + char *fen; + pos_t *pos, *savepos, *fishpos = pos_new(); + movelist_t fishmoves; + //move_t move; + FILE *outfd; + int depth = 6; + + if (ac > 1) + depth = atoi(av[1]); + printf("depth = %d\n", depth); + + setlocale(LC_NUMERIC, ""); + setlinebuf(stdout); /* line-buffered stdout */ + outfd = open_stockfish(); + + bitboard_init(); + hyperbola_init(); + + while ((fen = next_fen(PERFT | MOVEDO))) { + test_line = cur_line(); + if (!(pos = fen2pos(NULL, fen))) { + printf("wrong fen %d: [%s]\n", i, fen); + continue; + } + sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth); + //pos_gen_pseudomoves(pos, &pseudo); + savepos = pos_dup(pos); + //int j = 0; + +#define NANOSEC 1000000000 /* nano sec in sec */ +#define MILLISEC 1000000 /* milli sec in sec */ + + struct timespec ts1, ts2; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts1); + + my_count = perft(pos, depth, 1); + + clock_gettime(CLOCK_MONOTONIC_RAW, &ts2); + + // 1 sec = 1000 millisec + // 1 millisec = 1000 microsec + // 1 microsec = 1000 nanosec + // milli = sec * 1000 + nanosec / 1000000 + s64 microsecs = ((s64)ts2.tv_sec - (s64)ts1.tv_sec) * 1000000l + + ((s64)ts2.tv_nsec - (s64)ts1.tv_nsec) / 1000l ; + if (sf_count == my_count) { + printf("OK : fen line=%03d \"%s\" perft=%lu in %'ldms (LPS: %'lu)\n", + test_line, fen, my_count, microsecs/1000l, + my_count*1000000l/microsecs); + } else { + printf("ERR: fen line=%03d [%s] sf=%lu me=%lu\n", + test_line, fen, sf_count, my_count); + } + pos_del(savepos); + pos_del(pos); + i++; + } + return 0; +}