From 30af8865942ff531dfbce6a1f3f3436801d5ed8e Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 2 Apr 2024 21:18:01 +0200 Subject: [PATCH 01/12] comment --- src/search.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.c b/src/search.c index e505d36..07c8058 100644 --- a/src/search.c +++ b/src/search.c @@ -1,4 +1,4 @@ -/* search.c - search good moves. +/* search.c - perft + search. * * Copyright (C) 2023-2024 Bruno Raoult ("br") * Licensed under the GNU General Public License v3.0 or later. @@ -13,7 +13,7 @@ #include -#include "brlib.h" +#include #include "position.h" #include "move-gen.h" From 027aa2c1327a2c50f4b5e02ad4581ac7b3278701 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Thu, 4 Apr 2024 10:03:43 +0200 Subject: [PATCH 02/12] bb: bb_{first_bb,next,multiple{}, chessdefs: relative sq diffs --- src/bitboard.h | 52 ++++++++++++++++++++++++----- src/chessdefs.h | 5 +++ src/move-gen.c | 83 +++++++++++++++++++++++++++++------------------ test/perft-test.c | 10 +++--- 4 files changed, 106 insertions(+), 44 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index dccf58a..3b2b563 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -14,8 +14,8 @@ #ifndef _BITBOARD_H #define _BITBOARD_H -#include "brlib.h" -#include "bitops.h" +#include +#include #include "chessdefs.h" #include "board.h" @@ -183,14 +183,51 @@ static __always_inline bitboard_t bb_file(int file) } */ +/** + * bb_first_bb() - return bitboard of first square of a bitboard. + * @bb: bitboard + * + * bb must be non-zero. + * + * @return: bitboard of first square (lsb) of @bb. + */ +static __always_inline square_t bb_first_bb(bitboard_t bb) +{ + return bb & -bb; +} + +/** + * bb_next() - clear and return next (lsb) square of a bitboard. + * @bb: &bitboard + * + * The bitboard addressed by @bb must be non-zero. + * + * @return: first bit (lsb) of @bb. + */ +static __always_inline square_t bb_next(bitboard_t *bb) +{ + square_t sq = ctz64(*bb); + *bb &= *bb - 1; + return sq; +} + +/** + * bb_multiple() - test if a bitboard has multiple bits. + * @bb: bitboard + * + * @return: true if @bb has more than 1 bit, false otherwise. + */ +static __always_inline bool bb_multiple(bitboard_t bb) +{ + return !!(bb & (bb - 1)); +} + + #define bb_rank(r) ((u64) RANK_1bb << ((r) * 8)) -#define BB_FILE(f) ((u64) FILE_Abb << (f)) +#define bb_file(f) ((u64) FILE_Abb << (f)) #define bb_rel_rank(r, c) bb_rank(sq_rel_rank(r, c)) -//#define BB_REL_RANK(r, c) (RANK_1bb << (SQ_REL_RANK(r, c) * 8)) -//#define BB_REL_FILE(f, c) (FILE_Abb << (SQ_REL_RANK((f), (c)))) - /** * bb_sq_aligned() - check if two squares are aligned (same file or rank). * @sq1, @sq2: the two squares. @@ -219,7 +256,7 @@ static __always_inline bool bb_sq_aligned3(square_t sq1, square_t sq2, square_t * @sq1: square 1 * @sq2: square 2 * - * @return: bitboard of @betw if between @sq1 and @sq2. + * @return: bitboard of @sq if between @sq1 and @sq2. */ static __always_inline bitboard_t bb_sq_between(square_t sq, square_t sq1, square_t sq2) { @@ -260,7 +297,6 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) return (bb & ~FILE_Abb) << NORTH_WEST; } -#define pawn_up_value(c) ((c) == WHITE ? 8: -8) /* pawn moves/attacks (for bitboards) */ #define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb)) #define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb)) diff --git a/src/chessdefs.h b/src/chessdefs.h index c94af26..f891ae0 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -134,6 +134,11 @@ typedef enum { NORTH_WEST = (NORTH + WEST), } dir_t; +/* define diff for relative squares */ +#define sq_up(c) ((c) == WHITE ? NORTH: SOUTH) +#define sq_upleft(c) ((c) == WHITE ? NORTH_WEST: SOUTH_EAST) +#define sq_upright(c) ((c) == WHITE ? NORTH_EAST: SOUTH_WEST) + #include typedef struct mclock { diff --git a/src/move-gen.c b/src/move-gen.c index 2d156af..19baf43 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -36,18 +36,18 @@ */ bool pseudo_is_legal(const pos_t *pos, const move_t move) { - color_t us = pos->turn, them = OPPONENT(us); - 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]; + color_t us = pos->turn, them = OPPONENT(us); + square_t from = move_from(move), to = move_to(move); + square_t king = pos->king[us]; + bitboard_t kingbb = pos->bb[us][KING]; + u64 pinned = mask(from) & pos->blockers; /* (1) - King * For castling, we already tested intermediate squares attacks * in pseudo move generation, so we only care destination square here. * Attention: We need to exclude king from occupation bitboard ! */ - if (piece == KING) { + if (from == king) { bitboard_t occ = pos_occ(pos) ^ kingbb; return !sq_attackers(pos, occ, to, them); } @@ -62,17 +62,16 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) * pinned piece: always illegal */ if (pos->checkers) { - if (popcount64(pos->checkers) == 1) { /* one checker */ - square_t checker = ctz64(pos->checkers); - 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; + if (pinned) + return false; + if (bb_multiple(pos->checkers)) + return false; + square_t checker = ctz64(pos->checkers); + if (is_enpassant(move)) { + return pawn_push_up(pos->en_passant, them) == checker; } - return false; /* double check */ + bitboard_t between = bb_between[king][checker] | pos->checkers; + return mask(to) & between; } /* (3) - pinned pieces @@ -85,27 +84,24 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) } /* (4) - En-passant - * We only care the situation where our King and enemy R/Q are on - * 5th relative rank. To do so, we create an occupation bb without - * the 2 pawns. + * pinned pieces are already handled. + * One case is left: when the two "disappearing" pawns would discover + * a R/Q check. */ if (is_enpassant(move)) { - /* from rank bitboard */ - bitboard_t rank5 = bb_sqrank[from]; - /* enemy rooks/queens on from rank */ - bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5; + bitboard_t rank5 = us == WHITE? RANK_5bb: RANK_4bb; - if ((kingbb & rank5) && rooks) { /* K and enemy R/Q on rank */ - /* captured pawn square (beside from square) */ - square_t captured = sq_make(sq_file(pos->en_passant), sq_rank(from)); - /* occupation bitboard without the two "disappearing" pawns */ - bitboard_t occ = pos_occ(pos) ^ mask(from) ^ mask(captured); + if ((pos->bb[us][KING] & rank5)) { + bitboard_t exclude = mask(pos->en_passant + sq_up(them)) | mask(from); + bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5; - bit_for_each64(sq, tmp, rooks) /* check all rooks/queens */ - if (hyperbola_rank_moves(occ, sq) & kingbb) + bitboard_t occ = pos_occ(pos) ^ exclude; + while (rooks) { + square_t rook = bb_next(&rooks); + if (hyperbola_rank_moves(occ, rook) & kingbb) return false; + } } - return true; } return true; } @@ -156,6 +152,31 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de return dest; } +/** + * pos_gen_check_pseudomoves() - generate position pseudo-legal moves when in check + * @pos: position + * @movelist: &movelist_t array to store pseudo-moves + * + * Generate all @pos pseudo moves for player-to-move. + * @movelist is filled with the moves. + * + * Only a few validity checks are done here (i.e. moves are not generated): + * - castling, if king is in check + * - castling, if king passes an enemy-controlled square (not final square). + * When immediately known, a few move flags are also applied in these cases: + * - castling: M_CASTLE_{K,Q} + * - capture (excl. en-passant): M_CAPTURE + * - en-passant: M_EN_PASSANT + * - pawn double push: M_DPUSH + * - promotion: M_PROMOTION + * - promotion and capture + * + * TODO: move code to specific functions (especially castling, pawn push/capture) + * + * @Return: The total number of moves. + */ + + /** * pos_gen_pseudomoves() - generate position pseudo-legal moves * @pos: position diff --git a/test/perft-test.c b/test/perft-test.c index 929ed0c..ea2c807 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -236,7 +236,7 @@ int main(int __unused ac, __unused char**av) s64 ms1 = 0, ms1_total = 0; s64 ms2 = 0, ms2_total = 0; s64 ms3 = 0, ms3_total = 0; - int run = 3; + int run = 7; if (ac > 1) depth = atoi(av[1]); @@ -273,12 +273,12 @@ int main(int __unused ac, __unused char**av) ms1_total += ms1; if (sf_count == my_count) { - printf("pt1 OK : line=%03d perft=%lu %'ldms lps=%'lu \"%s\"\n", + printf("pt1 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n", test_line, my_count, ms1, ms1? my_count*1000l/ms1: 0, fen); } else { - printf("pt1 ERR: line=%03d sf=%lu me=%lu \"%s\"\n", + printf("pt1 ERR: line=%3d sf=%lu me=%lu \"%s\"\n", test_line, sf_count, my_count, fen); } } @@ -290,12 +290,12 @@ int main(int __unused ac, __unused char**av) ms2_total += ms2; if (sf_count == my_count) { - printf("pt2 OK : line=%03d perft=%lu %'ldms lps=%'lu \"%s\"\n", + printf("pt2 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n", test_line, my_count, ms2, ms2? my_count*1000l/ms2: 0, fen); } else { - printf("pt2 ERR: line=%03d sf=%lu me=%lu \"%s\"\n", + printf("pt2 ERR: line=%3d sf=%lu me=%lu \"%s\"\n", test_line, sf_count, my_count, fen); } } From f2ce20a50437aed5d716b3cb7c18d4ae0accb119 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 9 Apr 2024 08:08:13 +0200 Subject: [PATCH 03/12] add bb_pawn_attacks[][], bb_shift() --- src/attack.c | 5 +---- src/bitboard.c | 20 +++++++++++--------- src/bitboard.h | 22 +++++++++++++++++++--- src/chessdefs.h | 1 + test/bitboard-test.c | 24 ++++++++++++++++-------- test/common-test.h | 4 ---- test/perft-test.c | 2 -- 7 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/attack.c b/src/attack.c index 150d14d..d6177cd 100644 --- a/src/attack.c +++ b/src/attack.c @@ -43,9 +43,6 @@ bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, c bitboard_t sqbb = mask(sq); color_t opp = OPPONENT(c); - - //pos_print_raw(pos, 1); - /* bishop / queen */ if (hyperbola_bishop_moves(occ, sq) & (pos->bb[c][BISHOP] | pos->bb[c][QUEEN])) return true; @@ -55,7 +52,7 @@ bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, c return true; /* pawn */ - if ((pawn_shift_upleft(sqbb, opp) | pawn_shift_upright(sqbb, opp)) & pos->bb[c][PAWN]) + if (bb_pawn_attacks[opp][sq] & pos->bb[c][PAWN]) return true; /* knight */ diff --git a/src/bitboard.c b/src/bitboard.c index 984ccbb..ab919b0 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -27,7 +27,7 @@ bitboard_t bb_between_excl[64][64]; bitboard_t bb_between[64][64]; bitboard_t bb_line[64][64]; -bitboard_t bb_knight[64], bb_king[64]; +bitboard_t bb_knight[64], bb_king[64], bb_pawn_attacks[2][64]; /* vectors are clockwise from N */ static int knight_vector[] = { @@ -105,7 +105,9 @@ void bitboard_init(void) } ; bitboard_t tmpbb[64][4] = { 0 }; - /* 1) square to bitboard, and in-between-sq2-excluded */ + /* 1) square to bitboard + * in-between, sq2 excluded + */ for (square_t sq1 = A1; sq1 <= H8; ++sq1) { bb_sq[sq1] = mask(sq1); for (square_t sq2 = A1; sq2 <= H8; ++sq2) @@ -151,18 +153,18 @@ void bitboard_init(void) bb_line[sq1][sq2] = bb_sqdiag[sq1]; else if (bb_sqanti[sq1] == bb_sqanti[sq2]) bb_line[sq1][sq2] = bb_sqanti[sq1]; - - //if (bb_line[sq1][sq2]) { - // printf("bb_line[%d][%d] = %16lx\n", sq1, sq2, bb_line[sq1][sq2]); - //} } } } - /* 3) knight and king moves */ + /* 3) pawn, knight and king attacks + */ for (square_t sq = A1; sq <= H8; ++sq) { - //rank_t r1 = sq_rank(sq); - //file_t f1 = sq_file(sq); + if (sq >= A2 && sq <= H7) { + bb_pawn_attacks[WHITE][sq] = pawn_attacks_bb(mask(sq), WHITE); + bb_pawn_attacks[BLACK][sq] = pawn_attacks_bb(mask(sq), BLACK); + } + for (int vec = 0; vec < 8; ++vec) { int dst = sq + knight_vector[vec]; if (sq_ok(dst)) { diff --git a/src/bitboard.h b/src/bitboard.h index 3b2b563..a7000fc 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -39,9 +39,8 @@ extern bitboard_t bb_sqrank[64], bb_sqfile[64], bb_sqdiag[64], bb_sqanti[64]; /* line (rank, file, diagonal or anti-diagonal) between two squares */ extern bitboard_t bb_line[64][64]; -/* knight and king moves */ -extern bitboard_t bb_knight[64], bb_king[64]; - +/* pawn, knight and king attacks */ +extern bitboard_t bb_knight[64], bb_king[64], bb_pawn_attacks[2][64]; /* TODO (maybe C23?) when we can ensure an enum can be u64 * @@ -222,11 +221,25 @@ static __always_inline bool bb_multiple(bitboard_t bb) return !!(bb & (bb - 1)); } +/** + * bb_shift() - shift bitboard + * @bb: bitboard + * + * No control is done on off-board shifting (i.e. shifting -1 from A2 gives H3). + * + * @return: true if @bb has more than 1 bit, false otherwise. + */ +static __always_inline bitboard_t bb_shift(bitboard_t bb, int shift) +{ + return shift >= 0 ? bb << shift : bb >> -shift; +} + #define bb_rank(r) ((u64) RANK_1bb << ((r) * 8)) #define bb_file(f) ((u64) FILE_Abb << (f)) #define bb_rel_rank(r, c) bb_rank(sq_rel_rank(r, c)) +#define bb_rel_file(f, c) bb_file(sq_rel_rank(f, c)) /** * bb_sq_aligned() - check if two squares are aligned (same file or rank). @@ -301,6 +314,9 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) #define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb)) #define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb)) #define pawn_shift_upright(bb, c) ((c) == WHITE ? shift_ne(bb): shift_sw(bb)) +#define pawn_attacks_bb(bb, c) (pawn_shift_upleft(bb, c) | \ + pawn_shift_upright(bb, c)) + /* pawn move (for single pawn) - NO SQUARE CONTROL HERE ! * Need to make functions with control instead. */ diff --git a/src/chessdefs.h b/src/chessdefs.h index f891ae0..476ff1e 100644 --- a/src/chessdefs.h +++ b/src/chessdefs.h @@ -45,6 +45,7 @@ * @return: Relative rank. */ #define sq_rel_rank(rank, c) ((rank_t)((7 * (c)) ^ rank)) +#define sq_rel_file(file, c) ((file_t)((7 * (c)) ^ file)) /* castle_t bits structure */ diff --git a/test/bitboard-test.c b/test/bitboard-test.c index 762056d..cd27540 100644 --- a/test/bitboard-test.c +++ b/test/bitboard-test.c @@ -46,16 +46,24 @@ int main(int __unused ac, __unused char**av) sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", "c3-c6", "c3-f6", "c3-f3", "c3-e1", "c3-c1", "c3-a1", "c3-a3", "c3-a5"); bb_print_multi(str, 8, - bb_between[C3][C6], bb_between[C3][F6], - bb_between[C3][F3], bb_between[C3][E1], - bb_between[C3][C1], bb_between[C3][A1], - bb_between[C3][A3], bb_between[C3][A5]); + bb_between[C3][C6], bb_between[C3][F6], + bb_between[C3][F3], bb_between[C3][E1], + bb_between[C3][C1], bb_between[C3][A1], + bb_between[C3][A3], bb_between[C3][A5]); sprintf(str, "between: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", "c4-c6", "c4-f6", "c4-f3", "c4-e1", "c4-c1", "c4-a1", "c4-a3", "c4-a5"); bb_print_multi(str, 8, - bb_between[C4][C6], bb_between[C4][F6], - bb_between[C4][F3], bb_between[C4][E1], - bb_between[C4][C1], bb_between[C4][A1], - bb_between[C4][A3], bb_between[C4][A5]); + bb_between[C4][C6], bb_between[C4][F6], + bb_between[C4][F3], bb_between[C4][E1], + bb_between[C4][C1], bb_between[C4][A1], + bb_between[C4][A3], bb_between[C4][A5]); + sprintf(str, "Pwn att: %-22s%-22s%-22s%-22s%-22s%-22s%-22s%-22s", + "White a2", "Black a2", "White h7", "Black h7", + "White c3", "Black c3", "White e5", "Black e5"); + bb_print_multi(str, 8, + bb_pawn_attacks[WHITE][A2], bb_pawn_attacks[BLACK][A2], + bb_pawn_attacks[WHITE][H7], bb_pawn_attacks[BLACK][H7], + bb_pawn_attacks[WHITE][C3], bb_pawn_attacks[BLACK][C3], + bb_pawn_attacks[WHITE][E5], bb_pawn_attacks[BLACK][E5]); return 0; } diff --git a/test/common-test.h b/test/common-test.h index d433c32..5ac181b 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -391,10 +391,6 @@ struct fentest { "simple movedo/undo: only 2 W knights", "8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1" }, - { __LINE__, MOVEDO | PERFT, - "simple movedo/undo: only 2 W knights", - "8/1k6/8/8/8/8/6K1/1NN5 w - - 0 1" - }, { __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/perft-test.c b/test/perft-test.c index ea2c807..1ff0669 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -262,9 +262,7 @@ int main(int __unused ac, __unused char**av) continue; } sf_count = send_stockfish_fen(outfd, fishpos, &fishmoves, fen, depth); - //pos_gen_pseudomoves(pos, &pseudo); savepos = pos_dup(pos); - //int j = 0; if (run & 1) { clock_start(&clock); From eb590f14387d8e5e880219a0c606cd3c540f2caf Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 9 Apr 2024 08:11:31 +0200 Subject: [PATCH 04/12] bug fix, wrong perft recursion ! --- src/search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.c b/src/search.c index 07c8058..10f0a73 100644 --- a/src/search.c +++ b/src/search.c @@ -143,7 +143,7 @@ u64 perft_new_pinners(pos_t *pos, int depth, int ply) pos_gen_pseudomoves(pos, &pseudo); while ((move = pos_next_legal(pos, &pseudo, &movetmp)) != MOVE_NONE) { move_do(pos, move); - subnodes = perft(pos, depth - 1, ply + 1); + subnodes = perft_new_pinners(pos, depth - 1, ply + 1); if (ply == 1) { char movestr[8]; printf("%s: %d\n", move_str(movestr, move, 0), subnodes); From e8240c6cab88799e89029e0fcd71c75ff9391a38 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Tue, 9 Apr 2024 08:15:43 +0200 Subject: [PATCH 05/12] move_make_promotions(), clean move_do(), pos_gen_pseudomoves() --- src/move-do.c | 14 +- src/move-gen.c | 338 +++++++++++++++++++++++++++++++------------------ 2 files changed, 222 insertions(+), 130 deletions(-) diff --git a/src/move-do.c b/src/move-do.c index 60174db..9f414ca 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -52,6 +52,7 @@ pos_t *move_do(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]; + piece_t captured = pos->board[to]; piece_type_t ptype = PIECE(piece); color_t pcolor = COLOR(piece); piece_t new_piece = piece; @@ -60,6 +61,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) ++pos->plycount; pos->en_passant = SQUARE_NONE; pos->turn = them; + pos->captured = captured; bug_on(pcolor != us); @@ -68,9 +70,9 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) new_piece = MAKE_PIECE(move_promoted(move), us); } - if (is_capture(move)) { + if (captured != EMPTY) { pos->clock_50 = 0; - pos->captured = pos->board[to]; /* save capture info */ + //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 */ @@ -88,7 +90,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 (is_dpush(move)) /* if pawn double push, set e.p. */ - pos->en_passant = pawn_push_up(from, us); + pos->en_passant = from + sq_up(us); else if (is_enpassant(move)) { /* clear grabbed pawn */ square_t grabbed = pawn_push_up(to, them); pos_clr_sq(pos, grabbed); @@ -160,13 +162,13 @@ pos_t *move_undo(pos_t *pos, const move_t move)//, const state_t *state) if (is_promotion(move)) piece = MAKE_PIECE(PAWN, us); - pos_clr_sq(pos, to); /* always clear "to" and set "from" */ - pos_set_sq(pos, from, piece); + 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 (is_capture(move)) { + 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; diff --git a/src/move-gen.c b/src/move-gen.c index 19baf43..1b520a5 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -40,25 +40,32 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) square_t from = move_from(move), to = move_to(move); square_t king = pos->king[us]; bitboard_t kingbb = pos->bb[us][KING]; + bitboard_t occ = pos_occ(pos); u64 pinned = mask(from) & pos->blockers; - /* (1) - King - * For castling, we already tested intermediate squares attacks - * in pseudo move generation, so we only care destination square here. - * Attention: We need to exclude king from occupation bitboard ! + /* (1) - Castling & King + * For castling, we need to check intermediate squares attacks only. + * Attention: To test if K is in check after moving, we need to exclude + * king from occupation bitboard (to catch king moving away from checker + * on same line) ! */ + if (is_castle(move)) { + square_t dir = to > from? 1: -1; + if (sq_attackers(pos, occ, from + dir, them)) + return false; + } if (from == king) { - bitboard_t occ = pos_occ(pos) ^ kingbb; - return !sq_attackers(pos, occ, to, them); + return !sq_attackers(pos, occ ^ kingbb, to, them); } /* (2) - King is in check - * Double-check is already handled, as only K moves were generated. + * Double-check is already handled in (1), as only K moves were generated + * by pseudo legal move generator. * 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 + * e.p., legal if the grabbed pawn is giving check * pinned piece: always illegal */ if (pos->checkers) { @@ -68,8 +75,9 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) return false; square_t checker = ctz64(pos->checkers); if (is_enpassant(move)) { - return pawn_push_up(pos->en_passant, them) == checker; + return pos->en_passant + sq_up(them) == checker; } + return true; bitboard_t between = bb_between[king][checker] | pos->checkers; return mask(to) & between; } @@ -77,28 +85,25 @@ 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 (pinned) { - bitboard_t line = bb_line[from][king]; - return line & mask(to); /* to is not on pin line */ + if (mask(from) & pos->blockers) { + return bb_line[from][king] & mask(to); /* is to on pinner line ? */ } /* (4) - En-passant - * pinned pieces are already handled. - * One case is left: when the two "disappearing" pawns would discover - * a R/Q check. + * pinned pieces are handled in pinned section. + * One case not handled anywhere else: when the two "disappearing" pawns + * would discover a R/Q horizontal check. */ if (is_enpassant(move)) { bitboard_t rank5 = us == WHITE? RANK_5bb: RANK_4bb; if ((pos->bb[us][KING] & rank5)) { - bitboard_t exclude = mask(pos->en_passant + sq_up(them)) | mask(from); + bitboard_t exclude = mask(pos->en_passant - sq_up(us)) | mask(from); bitboard_t rooks = (pos->bb[them][ROOK] | pos->bb[them][QUEEN]) & rank5; - bitboard_t occ = pos_occ(pos) ^ exclude; while (rooks) { square_t rook = bb_next(&rooks); - if (hyperbola_rank_moves(occ, rook) & kingbb) + if (hyperbola_rank_moves(occ ^ exclude, rook) & kingbb) return false; } } @@ -152,6 +157,27 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de return dest; } +/** + * gen_pseudo_king() - generate king pseudo-legal moves. + * @pos: position + * @movelist: &movelist_t array to store pseudo-moves + * + */ +static inline __unused move_t *gen_pseudo_king(move_t *moves, square_t from, + bitboard_t mask) +{ + //color_t us = pos->turn; + //square_t from = pos->king[us]; + //bitboard_t not_my_pieces = ~pos->bb[us][ALL_PIECES]; + bitboard_t to_bb = bb_king_moves(mask, from); + square_t to; + while (moves) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); + } + return moves; +} + /** * pos_gen_check_pseudomoves() - generate position pseudo-legal moves when in check * @pos: position @@ -176,6 +202,48 @@ movelist_t *pos_all_legal(const pos_t *pos, movelist_t *movelist, movelist_t *de * @Return: The total number of moves. */ +/** + * gen_pseudo_escape() - generate position pseudo-legal moves when in check + * @pos: square_t king position + * @mask: possible destinations to consider + * @moves: &move_t array to store pseudo-moves + * + * Generate all @pos pseudo moves for player-to-move, when in check. + * @movelist is filled with the moves. + * + * If king is doubles-checked, only King moves will be generated. + * Otherwise, only moves where destination in between King or checker, or checker + * square. + * + */ +/* + * static void __always_inline gen_pseudo_escape(pos_t *pos, movelist_t *movelist) + * { + * color_t us = pos->turn; + * color_t them = OPPONENT(us); + * square_t king = pos->king[us]; + * + * gen_pseudo_king(pos, movelist); + * + * } + */ + +/** + * move_make_promotions() - generate all promotions for given pawn and dest. + * @moves: &move_t array where to store moves + * @from: pawn position + * @to: promotion square + * + * Generate all (Q/R/B/N) promotion moves on @to for pawn @from. + * + * @Return: New @moves (incremented by 4). + */ +static inline move_t *move_make_promotions(move_t *moves, square_t from, square_t to) +{ + for (piece_type_t pt = QUEEN; pt >= KNIGHT; --pt) + *moves++ = move_make_promote(from, to, pt); + return moves; +} /** * pos_gen_pseudomoves() - generate position pseudo-legal moves @@ -206,60 +274,88 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) color_t them = OPPONENT(us); bitboard_t my_pieces = pos->bb[us][ALL_PIECES]; - bitboard_t not_my_pieces = ~my_pieces; bitboard_t enemy_pieces = pos->bb[them][ALL_PIECES]; + bitboard_t dest_squares = ~my_pieces; bitboard_t occ = my_pieces | enemy_pieces; bitboard_t empty = ~occ; - bitboard_t movebits, from_pawns; - bitboard_t tmp1, tmp2; + bitboard_t from_bb, to_bb; + bitboard_t tmp_bb; move_t *moves = movelist->move; - int *nmoves = &movelist->nmoves; - int from, to; + //int *nmoves = &movelist->nmoves; + square_t from, to; + square_t king = pos->king[us]; - *nmoves = 0; + //*nmoves = 0; /* king - MUST BE FIRST (we stop if doubler check) */ - from = pos->king[us]; - movebits = bb_king_moves(not_my_pieces, from); - bit_for_each64(to, tmp1, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp1, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + to_bb = bb_king_moves(dest_squares, king); + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(king, to); } - if (popcount64(pos->checkers) > 1) /* double check, we stop here */ - return *nmoves; + if (bb_multiple(pos->checkers)) /* double check, we stop here */ + goto finish; + if (pos->checkers) { + /* one checker: we limit destination squares to line between + * checker and king + checker square (think: knight). + */ + square_t checker = ctz64(pos->checkers); + dest_squares &= bb_between[king][checker] | pos->checkers; + enemy_pieces &= dest_squares; + } else { + /* no checker: castling moves + * 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. + */ + if (can_oo(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb); + if (!(occ & occmask)) { + *moves++ = move_make_flags(king, king + 2, M_CASTLE_K); + } + } + if (can_ooo(pos->castle, us)) { + bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb); + if (!(occ & occmask)) { // && + *moves++ = move_make_flags(king, king - 2, M_CASTLE_Q); + } + } + } /* sliding pieces */ - bit_for_each64(from, tmp1, pos->bb[us][BISHOP] | pos->bb[us][QUEEN]) { - movebits = hyperbola_bishop_moves(occ, from) & not_my_pieces; - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + 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; + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } - bit_for_each64(from, tmp1, pos->bb[us][ROOK] | pos->bb[us][QUEEN]) { - movebits = hyperbola_rook_moves(occ, from) & not_my_pieces; - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(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; + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } /* knight */ - bit_for_each64(from, tmp1, pos->bb[us][KNIGHT]) { - movebits = bb_knight_moves(not_my_pieces, from); - bit_for_each64(to, tmp2, movebits & empty) { - moves[(*nmoves)++] = move_make(from, to); - } - bit_for_each64(to, tmp2, movebits & enemy_pieces) { - moves[(*nmoves)++] = move_make_capture(from, to); + from_bb = pos->bb[us][KNIGHT]; + while (from_bb) { + from = bb_next(&from_bb); + to_bb = bb_knight_moves(dest_squares, from); + while(to_bb) { + to = bb_next(&to_bb); + *moves++ = move_make(from, to); } } @@ -267,91 +363,85 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) bitboard_t rel_rank8 = bb_rel_rank(RANK_8, us); bitboard_t rel_rank3 = bb_rel_rank(RANK_3, us); - /* pawn: ranks 2-6 push 1 and 2 squares */ - movebits = pawn_shift_up(pos->bb[us][PAWN], us) & empty; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_up(to, them); /* reverse push */ - moves[(*nmoves)++] = move_make(from, to); + /* pawn: push */ + int shift = sq_up(us); + tmp_bb = bb_shift(pos->bb[us][PAWN], shift) & empty; + + to_bb = tmp_bb & ~rel_rank8 & dest_squares; /* non promotion */ + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { /* promotions */ - from = pawn_push_up(to, them); /* reverse push */ - moves[(*nmoves)++] = move_make_promote(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8 & dest_squares; /* promotions */ + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } /* possible second push */ - movebits = pawn_shift_up(movebits & rel_rank3, us) & empty; - bit_for_each64(to, tmp1, movebits) { - from = pawn_push_up(pawn_push_up(to, them), them); - moves[(*nmoves)++] = move_make_flags(from, to, M_DPUSH); + to_bb = bb_shift(tmp_bb & rel_rank3, shift) & empty & dest_squares; + while(to_bb) { + to = bb_next(&to_bb); + from = to - shift - shift; + *moves++ = move_make_flags(from, to, M_DPUSH); } /* pawn: captures left */ - movebits = pawn_shift_upleft(pos->bb[us][PAWN], us) & enemy_pieces; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_upleft(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_capture(from, to); + bitboard_t filter = ~bb_rel_file(FILE_A, us); + shift = sq_upleft(us); + tmp_bb = bb_shift(pos->bb[us][PAWN] & filter, shift) & enemy_pieces; + + to_bb = tmp_bb & ~rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { - from = pawn_push_upleft(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } /* pawn: captures right */ - movebits = pawn_shift_upright(pos->bb[us][PAWN], us) & enemy_pieces; - bit_for_each64(to, tmp1, movebits & ~rel_rank8) { - from = pawn_push_upright(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_capture(from, to); + filter = ~bb_rel_file(FILE_H, us); + shift = sq_upright(us); + tmp_bb = bb_shift(pos->bb[us][PAWN] & filter, shift) & enemy_pieces; + to_bb = tmp_bb & ~rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + *moves++ = move_make(from, to); } - bit_for_each64(to, tmp1, movebits & rel_rank8) { - from = pawn_push_upright(to, them); /* reverse capture */ - moves[(*nmoves)++] = move_make_promote_capture(from, to, QUEEN); - moves[(*nmoves)++] = move_make_promote_capture(from, to, ROOK); - moves[(*nmoves)++] = move_make_promote_capture(from, to, BISHOP); - moves[(*nmoves)++] = move_make_promote_capture(from, to, KNIGHT); + to_bb = tmp_bb & rel_rank8; + while (to_bb) { + to = bb_next(&to_bb); + from = to - shift; + moves = move_make_promotions(moves, from, to); } - /* pawn: en-passant */ - if ((to = pos->en_passant) != SQUARE_NONE) { - movebits = mask(to); - from_pawns = pos->bb[us][PAWN] - & (pawn_shift_upleft(movebits, them) | pawn_shift_upright(movebits, them)); - bit_for_each64(from, tmp1, from_pawns) { - moves[(*nmoves)++] = move_make_enpassant(from, to); - } - } - - /* castle - Attention ! Castling flags are assumed correct + /* pawn: en-passant + * TODO: special case when in-check here ? */ - if (!pos->checkers) { - bitboard_t rel_rank1 = bb_rel_rank(RANK_1, us); - from = pos->king[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. - */ - if (can_oo(pos->castle, us)) { - bitboard_t occmask = rel_rank1 & (FILE_Fbb | FILE_Gbb); - if (!(occ & occmask) && - !sq_attackers(pos, occ, from+1, them)) { /* f1/f8 */ - moves[(*nmoves)++] = move_make_flags(from, from + 2, M_CASTLE_K); - } - } - if (can_ooo(pos->castle, us)) { - bitboard_t occmask = rel_rank1 & (FILE_Bbb | FILE_Cbb | FILE_Dbb); - if (!(occ & occmask) && - !sq_attackers(pos, occ, from-1, them)) { /* d1/d8 */ - moves[(*nmoves)++] = move_make_flags(from, from - 2, M_CASTLE_Q); - } - } + if ((to = pos->en_passant) != SQUARE_NONE) { + to_bb = mask(to); + /* if e.p not on file H, we may add an e.p move to "up-left" */ + filter = ~bb_rel_file(FILE_A, us); + shift = sq_upleft(us); + if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + *moves++ = move_make_enpassant(to - shift, to); + + filter = ~bb_rel_file(FILE_H, us); + shift = sq_upright(us); + if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + *moves++ = move_make_enpassant(to - shift, to); } + /* TODO: add function per piece, and type, for easier debug */ - return *nmoves; +finish: + return movelist->nmoves = moves - movelist->move; } From 660722fadc5502bbe81bfabd7f7951f5aa56a5cd Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 12:53:31 +0200 Subject: [PATCH 06/12] cleanup --- src/attack.c | 1 - src/bitboard.c | 6 +++--- src/bitboard.h | 5 +++-- test/common-test.h | 27 +++++++++++++-------------- test/perft-test.c | 37 ++++++++----------------------------- 5 files changed, 27 insertions(+), 49 deletions(-) diff --git a/src/attack.c b/src/attack.c index d6177cd..7bed9b0 100644 --- a/src/attack.c +++ b/src/attack.c @@ -40,7 +40,6 @@ */ bool sq_is_attacked(const pos_t *pos, const bitboard_t occ, const square_t sq, const color_t c) { - bitboard_t sqbb = mask(sq); color_t opp = OPPONENT(c); /* bishop / queen */ diff --git a/src/bitboard.c b/src/bitboard.c index ab919b0..ad2e3c7 100644 --- a/src/bitboard.c +++ b/src/bitboard.c @@ -160,10 +160,10 @@ void bitboard_init(void) /* 3) pawn, knight and king attacks */ for (square_t sq = A1; sq <= H8; ++sq) { - if (sq >= A2 && sq <= H7) { - bb_pawn_attacks[WHITE][sq] = pawn_attacks_bb(mask(sq), WHITE); + if (sq >= A2) bb_pawn_attacks[BLACK][sq] = pawn_attacks_bb(mask(sq), BLACK); - } + if (sq <= H7) + bb_pawn_attacks[WHITE][sq] = pawn_attacks_bb(mask(sq), WHITE); for (int vec = 0; vec < 8; ++vec) { int dst = sq + knight_vector[vec]; diff --git a/src/bitboard.h b/src/bitboard.h index a7000fc..608066e 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -314,6 +314,7 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) #define pawn_shift_up(bb, c) ((c) == WHITE ? shift_n(bb): shift_s(bb)) #define pawn_shift_upleft(bb, c) ((c) == WHITE ? shift_nw(bb): shift_se(bb)) #define pawn_shift_upright(bb, c) ((c) == WHITE ? shift_ne(bb): shift_sw(bb)) + #define pawn_attacks_bb(bb, c) (pawn_shift_upleft(bb, c) | \ pawn_shift_upright(bb, c)) @@ -321,8 +322,8 @@ static __always_inline bitboard_t shift_nw(const bitboard_t bb) * Need to make functions with control instead. */ #define pawn_push_up(sq, c) ((sq) + ((c) == WHITE ? NORTH: SOUTH)) -#define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST)) -#define pawn_push_upright(sq, c) ((sq) + ((c) == WHITE ? NORTH_EAST: SOUTH_WEST)) +//#define pawn_push_upleft(sq, c) ((sq) + ((c) == WHITE ? NORTH_WEST: SOUTH_EAST)) +//#define pawn_push_upright(sq, c) ((sq) + ((c) == WHITE ? NORTH_EAST: SOUTH_WEST)) bitboard_t bitboard_between_excl(square_t sq1, square_t sq2); void bitboard_init(void); diff --git a/test/common-test.h b/test/common-test.h index 5ac181b..44443d2 100644 --- a/test/common-test.h +++ b/test/common-test.h @@ -35,10 +35,9 @@ struct fentest { "" }, */ - /* tests rank movegen bug - FIXED - */ - //"4k3/pppppppp/8/8/8/8/PPPPPPPP/2BRK3 w - - 0 1", - //"4k3/pppppppp/8/8/8/8/PPPPPPPP/1B1R1K2 w - - 0 1", + +/* ***************** TEMP TESTS ABOVE ************************** */ + { __LINE__, MOVEGEN | MOVEDO | PERFT, "illegal white e.p.", "3k4/8/5K2/3pP3/8/2b5/8/8 w - d6 0 1", @@ -92,43 +91,43 @@ struct fentest { * }, */ { __LINE__, ATTACK, - "checkers: a1 h1", - "1k6/8/8/8/8/8/8/r2K3r w - - 1 1" + "only 3 K moves (but impossible situation)", + "1k6/8/8/8/8/8/8/r2K3r w - - 0 1" }, { __LINE__, ATTACK, "checkers: a8 h8", - "R2k3R/8/8/8/8/8/8/1K6 b - - 1 1" + "R2k3R/8/8/8/8/8/8/1K6 b - - 0 1" }, { __LINE__, ATTACK, "checkers: b3 g3", - "1k6/8/8/8/8/1r1K2r1/8/8 w - - 1 1" + "1k6/8/8/8/8/1r1K2r1/8/8 w - - 0 1" }, { __LINE__, ATTACK, "checkers: b6 g6", - "8/8/1R1k2R1/8/8/8/8/1K6 b - - 1 1" + "8/8/1R1k2R1/8/8/8/8/1K6 b - - 0 1" }, { __LINE__, ATTACK, "checkers: g2 g7", - "8/k5r1/8/8/6K1/8/6r1/8 w - - 1 1" + "8/k5r1/8/8/6K1/8/6r1/8 w - - 0 1" }, { __LINE__, ATTACK, "checkers: g2 g7", - "8/6R1/8/6k1/8/8/K5R1/8 b - - 1 1" + "8/6R1/8/6k1/8/8/K5R1/8 b - - 0 1" }, { __LINE__, ATTACK, "checkers: d5 e3, pinners: none (2 pieces between attacker & K)", - "3k4/8/8/3r3b/b7/1N2nn2/2n1B3/rNBK1Rbr w - - 1 1" + "3k4/8/8/3r3b/b7/1N2nn2/2n1B3/rNBK1Rbr w - - 0 1" }, { __LINE__, ATTACK, "checkers: d4 e6 pinners: h4 a5 a8 h8", - "Rn1k1r1R/4b3/1n2N3/B7/3R3B/8/8/3K4 b - - 1 1" + "Rn1k1r1R/4b3/1n2N3/B7/3R3B/8/8/3K4 b - - 0 1" }, { __LINE__, ATTACK, "checkers: d5 e3, pinners: a1 h1 a4 h5", - "3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 1 0" + "3k4/8/8/3r3b/b7/1N2n3/4B3/rN1K1R1r w - - 0 1" }, { __LINE__, MOVEGEN | MOVEDO | PERFT, diff --git a/test/perft-test.c b/test/perft-test.c index 1ff0669..c813bf0 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -232,16 +232,15 @@ int main(int __unused ac, __unused char**av) movelist_t fishmoves; //move_t move; FILE *outfd; - int depth = 6; s64 ms1 = 0, ms1_total = 0; s64 ms2 = 0, ms2_total = 0; - s64 ms3 = 0, ms3_total = 0; - int run = 7; + + int depth = 6, run = 3; if (ac > 1) depth = atoi(av[1]); if (ac > 2) - run = atoi(av[2]); + run = atoi(av[2]) & 3; printf("depth = %d run=%d\n", depth, run); if (!run) @@ -253,7 +252,6 @@ int main(int __unused ac, __unused char**av) bitboard_init(); hyperbola_init(); - CLOCK_DEFINE(clock, CLOCK_PROCESS); while ((fen = next_fen(PERFT | MOVEDO))) { test_line = cur_line(); @@ -271,46 +269,29 @@ int main(int __unused ac, __unused char**av) ms1_total += ms1; if (sf_count == my_count) { - printf("pt1 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n", + printf("pt1 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", test_line, my_count, ms1, ms1? my_count*1000l/ms1: 0, fen); } else { - printf("pt1 ERR: line=%3d sf=%lu me=%lu \"%s\"\n", + printf("pt1 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", test_line, sf_count, my_count, fen); } } if (run & 2) { clock_start(&clock); - my_count = perft2(pos, depth, 1); + my_count = perft_new_pinners(pos, depth, 1); ms2 = clock_elapsed_ms(&clock); ms2_total += ms2; if (sf_count == my_count) { - printf("pt2 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n", + printf("pt2 OK : line=%3d perft=%'lu %'ldms lps=%'lu \"%s\"\n", test_line, my_count, ms2, ms2? my_count*1000l/ms2: 0, fen); } else { - printf("pt2 ERR: line=%3d sf=%lu me=%lu \"%s\"\n", - test_line, sf_count, my_count, fen); - } - } - - if (run & 4) { - clock_start(&clock); - my_count = perft_new_pinners(pos, depth, 1); - ms3 = clock_elapsed_ms(&clock); - ms3_total += ms3; - - if (sf_count == my_count) { - printf("pt3 OK : line=%3d perft=%lu %'ldms lps=%'lu \"%s\"\n", - test_line, my_count, ms3, - ms3? my_count*1000l/ms3: 0, - fen); - } else { - printf("pt3 ERR: line=%3d sf=%lu me=%lu \"%s\"\n", + printf("pt2 ERR: line=%3d sf=%'lu me=%'lu \"%s\"\n", test_line, sf_count, my_count, fen); } } @@ -323,7 +304,5 @@ int main(int __unused ac, __unused char**av) printf("total perft %'ldms\n", ms1_total); if (run & 2) printf("total perft2 %'ldms\n", ms2_total); - if (run & 4) - printf("total perft3 %'ldms\n", ms3_total); return 0; } From 711306c92a64e1236bf10327fb33b502b7045fb6 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 12:54:47 +0200 Subject: [PATCH 07/12] simplify/improve pos_set_checkers_pinners_blockers() --- src/position.c | 68 ++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/position.c b/src/position.c index 72f13c9..047a561 100644 --- a/src/position.c +++ b/src/position.c @@ -116,7 +116,7 @@ pos_t *pos_clear(pos_t *pos) * * @return: true if equal, false otherwise. */ -bool pos_cmp(const pos_t *pos1, const pos_t *pos2) +bool pos_cmp(__unused const pos_t *pos1, __unused const pos_t *pos2) { #define _cmpf(a) (pos1->a != pos2->a) bool ret = false; @@ -201,7 +201,7 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) bitboard_t occ = pos_occ(pos); bitboard_t attackers; bitboard_t checkers = 0, blockers = 0, pinners = 0; - bitboard_t targets, tmpcheckers, tmpblockers, tmppinners, tmpbb; + bitboard_t targets, tmpcheckers, maybeblockers, tmppinners, tmpbb; square_t king = pos->king[us]; bitboard_t king_bb = mask(king); int pinner; @@ -217,55 +217,47 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) checkers |= tmpcheckers; /* maybe blockers = not checkers */ - tmpblockers = targets & ~tmpcheckers; + maybeblockers = targets & ~tmpcheckers; - /* we find second targets, by removing only first ones (excl. checkers) */ - targets = hyperbola_bishop_moves(occ ^ tmpblockers, king) ^ tmpcheckers; + /* we find second targets, by removing first ones (excl. checkers) */ + if (maybeblockers) { + targets = hyperbola_bishop_moves(occ ^ maybeblockers, king) ^ tmpcheckers; - /* pinners = only B/Q */ - tmppinners = targets & attackers; - pinners |= tmppinners; - //tmpblockers = 0; + /* pinners = only B/Q */ + tmppinners = targets & attackers; - /* blockers = we find occupied squares between pinner and king */ - bit_for_each64(pinner, tmpbb, tmppinners) - blockers |= bb_between[pinner][king] & tmpblockers; - //blockers |= tmpblockers; + /* blockers = we find occupied squares between pinner and king */ + while (tmppinners) { + pinner = bb_next(&tmppinners); + pinners |= mask(pinner); + blockers |= bb_between[pinner][king] & maybeblockers; + } + } /* same for rook type */ attackers = pos->bb[them][ROOK] | pos->bb[them][QUEEN]; - - /* targets is all "target" pieces if K was a bishop */ targets = hyperbola_rook_moves(occ, king) & occ; - /* checkers = only opponent B/Q */ tmpcheckers = targets & attackers; checkers |= tmpcheckers; - /* maybe blockers = not checkers */ - tmpblockers = targets & ~tmpcheckers; - - /* we find second targets, by removing only first ones (excl. checkers) */ - targets = hyperbola_rook_moves(occ ^ tmpblockers, king) ^ tmpcheckers; - - /* pinners = only B/Q */ - tmppinners = targets & attackers; - pinners |= tmppinners; - //tmpblockers = 0; - - /* blockers = we find occupied squares between pinner and king */ - bit_for_each64(pinner, tmpbb, tmppinners) - blockers |= bb_between[pinner][king] & tmpblockers; - //blockers |= tmpblockers; + maybeblockers = targets & ~tmpcheckers; + if (maybeblockers) { + targets = hyperbola_rook_moves(occ ^ maybeblockers, king) ^ tmpcheckers; + tmppinners = targets & attackers; + while (tmppinners) { + pinner = bb_next(&tmppinners); + pinners |= mask(pinner); + blockers |= bb_between[pinner][king] & maybeblockers; + } + } /* pawns */ - attackers = pos->bb[them][PAWN]; - targets = pawn_shift_upleft(king_bb, us) | pawn_shift_upright(king_bb, us); - checkers |= targets & attackers; + checkers |= bb_pawn_attacks[us][king] & pos->bb[them][PAWN]; /* knight */ attackers = pos->bb[them][KNIGHT]; - targets = bb_knight_moves(attackers, king); + targets = bb_knight[king] & attackers; checkers |= targets; pos->checkers = checkers; @@ -366,10 +358,10 @@ bitboard_t pos_king_blockers(const pos_t *pos, const color_t color, const bitboa * * @return: (if @strict is false) return true if check is ok, false otherwise. */ -bool pos_ok(const pos_t *pos, const bool strict) +bool pos_ok(__unused const pos_t *pos, __unused const bool strict) { int n, count = 0, bbcount = 0, error = 0; - bitboard_t tmp; + __unused bitboard_t tmp; /* pawns on 1st ot 8th rank */ tmp = (pos->bb[WHITE][PAWN] | pos->bb[BLACK][PAWN]) & (RANK_1bb | RANK_8bb); @@ -391,7 +383,7 @@ bool pos_ok(const pos_t *pos, const bool strict) } for (square_t sq = 0; sq < 64; ++sq) { piece_t piece = pos->board[sq]; - bitboard_t match; + __unused bitboard_t match; if (piece == EMPTY) continue; color_t c = COLOR(piece); From e78eae21e6a3c5ec94a66a7c933e7486b299006f Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 12:57:08 +0200 Subject: [PATCH 08/12] cleanup move_do() --- src/move-do.c | 9 +++--- src/move-gen.c | 77 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/move-do.c b/src/move-do.c index 9f414ca..4948fef 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -56,6 +56,7 @@ pos_t *move_do(pos_t *pos, const move_t move) //, state_t *state) piece_type_t ptype = PIECE(piece); color_t pcolor = COLOR(piece); piece_t new_piece = piece; + int up = sq_up(us); ++pos->clock_50; ++pos->plycount; @@ -90,9 +91,9 @@ 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 (is_dpush(move)) /* if pawn double push, set e.p. */ - pos->en_passant = from + sq_up(us); + pos->en_passant = from + up; else if (is_enpassant(move)) { /* clear grabbed pawn */ - square_t grabbed = pawn_push_up(to, them); + square_t grabbed = to - up; pos_clr_sq(pos, grabbed); } } @@ -157,7 +158,7 @@ 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); @@ -182,7 +183,7 @@ pos_t *move_undo(pos_t *pos, const move_t move)//, const state_t *state) pos_set_sq(pos, rookto, pos->board[rookfrom]); pos_clr_sq(pos, rookfrom); } else if (is_enpassant(move)) { /* restore grabbed pawn */ - square_t grabbed = pawn_push_up(to, them); + square_t grabbed = to + up; pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them)); } diff --git a/src/move-gen.c b/src/move-gen.c index 1b520a5..f4d2f03 100644 --- a/src/move-gen.c +++ b/src/move-gen.c @@ -42,6 +42,7 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) bitboard_t kingbb = pos->bb[us][KING]; bitboard_t occ = pos_occ(pos); u64 pinned = mask(from) & pos->blockers; + u64 checkers = pos->checkers; /* (1) - Castling & King * For castling, we need to check intermediate squares attacks only. @@ -49,12 +50,12 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) * king from occupation bitboard (to catch king moving away from checker * on same line) ! */ - if (is_castle(move)) { - square_t dir = to > from? 1: -1; - if (sq_attackers(pos, occ, from + dir, them)) - return false; - } - if (from == king) { + if (unlikely(from == king)) { + if (unlikely(is_castle(move))) { + square_t dir = to > from? 1: -1; + if (sq_attackers(pos, occ, from + dir, them)) + return false; + } return !sq_attackers(pos, occ ^ kingbb, to, them); } @@ -68,18 +69,18 @@ bool pseudo_is_legal(const pos_t *pos, const move_t move) * e.p., legal if the grabbed pawn is giving check * pinned piece: always illegal */ - if (pos->checkers) { + if (checkers) { if (pinned) return false; - if (bb_multiple(pos->checkers)) + if (bb_multiple(checkers)) return false; - square_t checker = ctz64(pos->checkers); + square_t checker = ctz64(checkers); if (is_enpassant(move)) { return pos->en_passant + sq_up(them) == checker; } return true; - bitboard_t between = bb_between[king][checker] | pos->checkers; - return mask(to) & between; + //bitboard_t between = bb_between[king][checker] | pos->checkers; + //return mask(to) & between; } /* (3) - pinned pieces @@ -388,6 +389,30 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) *moves++ = move_make_flags(from, to, M_DPUSH); } + /* pawn: captures */ + /* + * tmp_bb = pawn_attacks_bb(pos->bb[us][PAWN], us) & enemy_pieces; + * //bb_print("FAIL", tmp_bb); + * to_bb = tmp_bb & ~rel_rank8; + * while (to_bb) { + * to = bb_next(&to_bb); + * from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN]; + * while (from_bb) { + * from = bb_next(&from_bb); + * *moves++ = move_make(from, to); + * } + * } + * to_bb = tmp_bb & rel_rank8; + * while (to_bb) { + * to = bb_next(&to_bb); + * from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN]; + * while (from_bb) { + * from = bb_next(&from_bb); + * moves = move_make_promotions(moves, from, to); + * } + * } + */ + /* pawn: captures left */ bitboard_t filter = ~bb_rel_file(FILE_A, us); shift = sq_upleft(us); @@ -427,18 +452,26 @@ int pos_gen_pseudomoves(pos_t *pos, movelist_t *movelist) * TODO: special case when in-check here ? */ if ((to = pos->en_passant) != SQUARE_NONE) { - to_bb = mask(to); - /* if e.p not on file H, we may add an e.p move to "up-left" */ - filter = ~bb_rel_file(FILE_A, us); - shift = sq_upleft(us); - if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) - *moves++ = move_make_enpassant(to - shift, to); - - filter = ~bb_rel_file(FILE_H, us); - shift = sq_upright(us); - if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) - *moves++ = move_make_enpassant(to - shift, to); + from_bb = bb_pawn_attacks[them][to] & pos->bb[us][PAWN]; + while (from_bb) { + from = bb_next(&from_bb); + *moves++ = move_make_enpassant(from, to); + } } + /* + * to_bb = mask(to); + * /\* if e.p not on file H, we may add an e.p move to "up-left" *\/ + * filter = ~bb_rel_file(FILE_A, us); + * shift = sq_upleft(us); + * if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + * *moves++ = move_make_enpassant(to - shift, to); + * + * filter = ~bb_rel_file(FILE_H, us); + * shift = sq_upright(us); + * if (bb_shift(pos->bb[us][PAWN] & filter, shift) & to_bb) + * *moves++ = move_make_enpassant(to - shift, to); + * } + */ /* TODO: add function per piece, and type, for easier debug */ From e301e6c726811fd1da0513d846b84c1731b9e162 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 12:57:39 +0200 Subject: [PATCH 09/12] bug fix in perft() - stupid initialization ! --- src/search.c | 51 +++++---------------------------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/src/search.c b/src/search.c index 10f0a73..e7bfe20 100644 --- a/src/search.c +++ b/src/search.c @@ -45,12 +45,13 @@ u64 perft(pos_t *pos, int depth, int ply) { int subnodes, movetmp = 0; u64 nodes = 0; - movelist_t pseudo = { .nmoves = 0 }; + movelist_t pseudo; move_t move; state_t state; if (depth == 0) return 1; + pseudo.nmoves = 0; pos->checkers = pos_checkers(pos, pos->turn); pos_set_pinners_blockers(pos); state = pos->state; @@ -74,7 +75,7 @@ u64 perft(pos_t *pos, int depth, int ply) } /** - * perft2() - Perform perft on position + * perft_new_pinners() - Perform perft on position * @pos: &position to search * @depth: Wanted depth. * @ply: perft depth level. @@ -82,61 +83,19 @@ u64 perft(pos_t *pos, int depth, int ply) * Run perft on a position. This function displays the available moves at @depth * level for each possible first move, and the total of moves. * - * This version uses the algorithm: - * if (king in check) - * finish; - * if last depth - * return 1; - * gen pseudo-legal moves - * foreach pseudo-legal move... - * * @return: total moves found at @depth level. */ -u64 perft2(pos_t *pos, int depth, int ply) -{ - int subnodes, nmove = 0; - u64 nodes = 0; - movelist_t pseudo = { .nmoves = 0 }; - move_t move; - state_t state; - - if (depth == 0) - return 1; - pos->checkers = pos_checkers(pos, pos->turn); - pos_set_pinners_blockers(pos); - state = pos->state; - - pos_gen_pseudomoves(pos, &pseudo); - - for (nmove = 0; nmove < pseudo.nmoves; ++nmove ) { - move = pseudo.move[nmove]; - move_do(pos, move); - if (!is_in_check(pos, OPPONENT(pos->turn))) { - subnodes = perft2(pos, depth - 1, ply + 1); - nodes += subnodes; - if (ply == 1) { - char movestr[8]; - printf("%s: %d\n", move_str(movestr, move, 0), subnodes); - } - } - move_undo(pos, move); - pos->state = state; - } - if (ply == 1) - printf("Total: %lu\n", nodes); - return nodes; -} - u64 perft_new_pinners(pos_t *pos, int depth, int ply) { int subnodes, movetmp = 0; u64 nodes = 0; - movelist_t pseudo = { .nmoves = 0 }; + movelist_t pseudo; move_t move; state_t state; if (depth == 0) return 1; + pseudo.nmoves = 0; pos_set_checkers_pinners_blockers(pos); state = pos->state; From 9b5c2253b1e4039f68f422e62d98ab4d9ffcfe87 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 13:02:23 +0200 Subject: [PATCH 10/12] Makefile: add -ginline-points --- .gitignore | 1 + Makefile | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 9dedd65..34d3936 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ vgcore.* *.i *.old *.save +perf.data /GPATH /GRTAGS /GTAGS diff --git a/Makefile b/Makefile index bc64b0b..b11b8ab 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,6 @@ CPPFILES := $(SRC:.c=.i) $(TSTSRC:.c=.i) CPPFLAGS := -I$(BRINCDIR) -I$(INCDIR) CPPFLAGS += -DNDEBUG # assert - CPPFLAGS += -DBUG_ON # brlib bug.h CPPFLAGS += -DWARN_ON # brlib bug.h @@ -88,9 +87,16 @@ CPPFLAGS := $(strip $(CPPFLAGS)) CFLAGS := -std=gnu11 ### dev OR release + # dev -#CFLAGS += -O1 -#CFLAGS += -g +# CFLAGS += -O1 +CFLAGS += -g # symbols (gdb, perf, etc.) +CFLAGS += -ginline-points # inlined funcs debug info +# for gprof +#CFLAGS += -pg +# Next one may be useful for valgrind (when invalid instructions) +#CFLAGS += -mno-tbm + # release CFLAGS += -Ofast @@ -99,10 +105,6 @@ CFLAGS += -flto CFLAGS += -Wall CFLAGS += -Wextra CFLAGS += -Wmissing-declarations -# for gprof -#CFLAGS += -pg -# Next one may be useful for valgrind (when invalid instructions) -# CFLAGS += -mno-tbm CFLAGS := $(strip $(CFLAGS)) From 374116b1e793f106772327bc0049e7e062e827b9 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 15:59:24 +0200 Subject: [PATCH 11/12] simplify pos_set_checkers_pinners_blockers() (cont'd) --- src/position.c | 9 +++------ test/perft-test.c | 2 ++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/position.c b/src/position.c index 047a561..533ff8f 100644 --- a/src/position.c +++ b/src/position.c @@ -201,9 +201,8 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) bitboard_t occ = pos_occ(pos); bitboard_t attackers; bitboard_t checkers = 0, blockers = 0, pinners = 0; - bitboard_t targets, tmpcheckers, maybeblockers, tmppinners, tmpbb; + bitboard_t targets, tmpcheckers, maybeblockers, tmppinners; square_t king = pos->king[us]; - bitboard_t king_bb = mask(king); int pinner; /* bishop type - we attack with a bishop from king position */ @@ -216,7 +215,7 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) tmpcheckers = targets & attackers; checkers |= tmpcheckers; - /* maybe blockers = not checkers */ + /* maybe blockers = we remove checkers, to look "behind" */ maybeblockers = targets & ~tmpcheckers; /* we find second targets, by removing first ones (excl. checkers) */ @@ -256,9 +255,7 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) checkers |= bb_pawn_attacks[us][king] & pos->bb[them][PAWN]; /* knight */ - attackers = pos->bb[them][KNIGHT]; - targets = bb_knight[king] & attackers; - checkers |= targets; + checkers |= bb_knight[king] & pos->bb[them][KNIGHT]; pos->checkers = checkers; pos->pinners = pinners; diff --git a/test/perft-test.c b/test/perft-test.c index c813bf0..695335c 100644 --- a/test/perft-test.c +++ b/test/perft-test.c @@ -299,6 +299,8 @@ int main(int __unused ac, __unused char**av) pos_del(savepos); pos_del(pos); i++; + /* to run first test only */ + // exit(0); } if (run & 1) printf("total perft %'ldms\n", ms1_total); From 926dfa07658651ee243fac82e035093723f7ac7a Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 10 Apr 2024 17:12:20 +0200 Subject: [PATCH 12/12] untabify Makefile, cosmetic change in pos_set_checkers_pinners_blockers() --- Makefile | 20 ++++++++++---------- src/position.c | 4 +--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index b11b8ab..02ad3af 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ RMDIR := rmdir MAKE := make SRCDIR := ./src -INCDIR := ./src # used by ./test sources +INCDIR := ./src # used by ./test sources OBJDIR := ./obj BINDIR := ./bin DEPDIR := ./dep @@ -34,13 +34,13 @@ BRLIBDIR := $(BRLIB)/lib CCLSROOT := .ccls-root CCLSFILE := compile_commands.json -SRC := $(wildcard $(SRCDIR)/*.c) # project sources -SRC_FN := $(notdir $(SRC)) # source basename +SRC := $(wildcard $(SRCDIR)/*.c) # project sources +SRC_FN := $(notdir $(SRC)) # source basename OBJ := $(addprefix $(OBJDIR)/,$(SRC_FN:.c=.o)) TSTSRC := $(wildcard $(TSTDIR)/*.c) -LIB := br_$(shell uname -m) # library name +LIB := br_$(shell uname -m) # library name LIBS := $(strip -l$(LIB) -lreadline) DEP_FN := $(SRC_FN) @@ -63,16 +63,16 @@ CPPFLAGS += -DWARN_ON # brlib bug.h #CPPFLAGS += -DDEBUG_DEBUG # enable log() functions #CPPFLAGS += -DDEBUG_DEBUG_C # enable log() settings #CPPFLAGS += -DDEBUG_POOL # memory pools management -#CPPFLAGS += -DDEBUG_POS # position.c +#CPPFLAGS += -DDEBUG_POS # position.c #CPPFLAGS += -DDEBUG_MOVE # move generation # fen.c #CPPFLAGS += -DDEBUG_FEN # FEN decoding # attack.c -#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS1 # sq_attackers details -CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers -CPPFLAGS += -DDEBUG_ATTACK_PINNERS # sq_pinners details +#CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS1 # sq_attackers details +CPPFLAGS += -DDEBUG_ATTACK_ATTACKERS # sq_attackers +CPPFLAGS += -DDEBUG_ATTACK_PINNERS # sq_pinners details #CPPFLAGS += -DDEBUG_EVAL # eval functions #CPPFLAGS += -DDEBUG_PIECE # piece list management @@ -90,8 +90,8 @@ CFLAGS := -std=gnu11 # dev # CFLAGS += -O1 -CFLAGS += -g # symbols (gdb, perf, etc.) -CFLAGS += -ginline-points # inlined funcs debug info +CFLAGS += -g # symbols (gdb, perf, etc.) +CFLAGS += -ginline-points # inlined funcs debug info # for gprof #CFLAGS += -pg # Next one may be useful for valgrind (when invalid instructions) diff --git a/src/position.c b/src/position.c index 533ff8f..9a9a81d 100644 --- a/src/position.c +++ b/src/position.c @@ -251,10 +251,8 @@ void pos_set_checkers_pinners_blockers(pos_t *pos) } } - /* pawns */ + /* pawns & knights */ checkers |= bb_pawn_attacks[us][king] & pos->bb[them][PAWN]; - - /* knight */ checkers |= bb_knight[king] & pos->bb[them][KNIGHT]; pos->checkers = checkers;