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); } }