X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fmovegen.cpp;h=f5890a1a2fa19737c6411deae1841b3293c6fc6b;hp=9bc149d5103359a802a27d1fd61ad6adba93dded;hb=68d36b6f59c5e071a60b4a8559c055bbcdc36aa0;hpb=769f2fdecb2ff2ba01f9cff6bee12926866510ed diff --git a/src/movegen.cpp b/src/movegen.cpp index 9bc149d5..f5890a1a 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -40,10 +40,12 @@ namespace { QUEEN_SIDE }; - static const bool CAPTURE = true; - static const bool NON_CAPTURE = false; + enum MoveType { + CAPTURE, + NON_CAPTURE + }; - // Function + // Functions bool castling_is_check(const Position&, CastlingSide); // Helper templates @@ -81,14 +83,14 @@ namespace { MoveStack* generate_piece_moves(const Position&, MoveStack*, Color us, Bitboard); template<> - MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target); + MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard); - template + template inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us) { assert(Piece == PAWN); - if (Capture) + if (Type == CAPTURE) return (us == WHITE ? generate_pawn_captures(p, m) : generate_pawn_captures(p, m)); else @@ -96,17 +98,16 @@ namespace { : generate_pawn_noncaptures(p, m)); } - // Template generate_piece_blocking_evasions() with specializations template - MoveStack* generate_piece_blocking_evasions(const Position&, MoveStack*, Color us, Bitboard, Bitboard); + MoveStack* generate_piece_moves(const Position&, MoveStack*, Color us, Bitboard, Bitboard); template<> - inline MoveStack* generate_piece_blocking_evasions(const Position& p, MoveStack* m, Color us, - Bitboard np, Bitboard bs) { + inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, + Color us, Bitboard t, Bitboard pnd) { if (us == WHITE) - return generate_pawn_blocking_evasions(p, np, bs, m); + return generate_pawn_blocking_evasions(p, pnd, t, m); else - return generate_pawn_blocking_evasions(p, np, bs, m); + return generate_pawn_blocking_evasions(p, pnd, t, m); } } @@ -200,11 +201,10 @@ int generate_checks(const Position& pos, MoveStack* mlist, Bitboard dc) { /// generate_evasions() generates all check evasions when the side to move is -/// in check. Unlike the other move generation functions, this one generates -/// only legal moves. It returns the number of generated moves. This -/// function is very ugly, and needs cleaning up some time later. FIXME +/// in check. Unlike the other move generation functions, this one generates +/// only legal moves. It returns the number of generated moves. -int generate_evasions(const Position& pos, MoveStack* mlist) { +int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { assert(pos.is_ok()); assert(pos.is_check()); @@ -218,26 +218,27 @@ int generate_evasions(const Position& pos, MoveStack* mlist) { assert(pos.piece_on(ksq) == king_of_color(us)); // The bitboard of occupied pieces without our king - Bitboard b2 = pos.occupied_squares(); - clear_bit(&b2, ksq); + Bitboard b_noKing = pos.occupied_squares(); + clear_bit(&b_noKing, ksq); // Find squares attacked by slider checkers, we will // remove them from king evasions set so to avoid a couple - // of cycles in the slow king evasions legality check loop. + // of cycles in the slow king evasions legality check loop + // and to be able to use square_is_attacked(). Bitboard checkers = pos.checkers(); Bitboard checkersAttacks = EmptyBoardBB; Bitboard b = checkers & (pos.queens() | pos.bishops()); while (b) { from = pop_1st_bit(&b); - checkersAttacks |= bishop_attacks_bb(from, b2); + checkersAttacks |= bishop_attacks_bb(from, b_noKing); } b = checkers & (pos.queens() | pos.rooks()); while (b) { from = pop_1st_bit(&b); - checkersAttacks |= rook_attacks_bb(from, b2); + checkersAttacks |= rook_attacks_bb(from, b_noKing); } // Generate evasions for king @@ -245,17 +246,9 @@ int generate_evasions(const Position& pos, MoveStack* mlist) { while (b1) { to = pop_1st_bit(&b1); - - // Make sure 'to' is not attacked by the other side. This is a bit ugly, - // because we can't use Position::square_is_attacked. Instead we use - // the low-level bishop_attacks_bb and rook_attacks_bb with the bitboard - // b2 (the occupied squares with the king removed) in order to test whether - // the king will remain in check on the destination square. - if (!( (pos.piece_attacks(to) & pos.knights(them)) - || (pos.pawn_attacks(us, to) & pos.pawns(them)) - || (bishop_attacks_bb(to, b2) & pos.bishops_and_queens(them)) - || (rook_attacks_bb(to, b2) & pos.rooks_and_queens(them)) - || (pos.piece_attacks(to) & pos.kings(them)))) + // Note that we can use square_is_attacked() only because we + // have already removed slider checkers. + if (!pos.square_is_attacked(to, them)) (*mlist++).move = make_move(ksq, to); } @@ -268,13 +261,10 @@ int generate_evasions(const Position& pos, MoveStack* mlist) { assert(pos.color_of_piece_on(checksq) == them); - // Find pinned pieces - Bitboard not_pinned = ~pos.pinned_pieces(us); - // Generate captures of the checking piece // Pawn captures - b1 = pos.pawn_attacks(them, checksq) & pos.pawns(us) & not_pinned; + b1 = pos.pawn_attacks(them, checksq) & pos.pawns(us) & ~pinned; while (b1) { from = pop_1st_bit(&b1); @@ -291,7 +281,7 @@ int generate_evasions(const Position& pos, MoveStack* mlist) { // Pieces captures b1 = ( (pos.piece_attacks(checksq) & pos.knights(us)) | (pos.piece_attacks(checksq) & pos.bishops_and_queens(us)) - | (pos.piece_attacks(checksq) & pos.rooks_and_queens(us)) ) & not_pinned; + | (pos.piece_attacks(checksq) & pos.rooks_and_queens(us)) ) & ~pinned; while (b1) { @@ -300,52 +290,49 @@ int generate_evasions(const Position& pos, MoveStack* mlist) { } // Blocking check evasions are possible only if the checking piece is - // a slider + // a slider. if (checkers & pos.sliders()) { Bitboard blockSquares = squares_between(checksq, ksq); assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB); - // Pieces moves - mlist = generate_piece_blocking_evasions(pos, mlist, us, not_pinned, blockSquares); - mlist = generate_piece_blocking_evasions(pos, mlist, us, not_pinned, blockSquares); - mlist = generate_piece_blocking_evasions(pos, mlist, us, not_pinned, blockSquares); - mlist = generate_piece_blocking_evasions(pos, mlist, us, not_pinned, blockSquares); - mlist = generate_piece_blocking_evasions(pos, mlist, us, not_pinned, blockSquares); - } + if (blockSquares != EmptyBoardBB) + { + // Pieces moves + mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); + mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); + mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); + mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); + mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); + } + } - // Finally, the ugly special case of en passant captures. An en passant - // capture can only be a check evasion if the check is not a discovered - // check. If pos.ep_square() is set, the last move made must have been - // a double pawn push. If, furthermore, the checking piece is a pawn, - // an en passant check evasion may be possible. - if (pos.ep_square() != SQ_NONE && (checkers & pos.pawns(them))) - { - to = pos.ep_square(); - b1 = pos.pawn_attacks(them, to) & pos.pawns(us); + // Finally, the special case of en passant captures. An en passant + // capture can only be a check evasion if the check is not a discovered + // check. If pos.ep_square() is set, the last move made must have been + // a double pawn push. If, furthermore, the checking piece is a pawn, + // an en passant check evasion may be possible. + if (pos.ep_square() != SQ_NONE && (checkers & pos.pawns(them))) + { + to = pos.ep_square(); + b1 = pos.pawn_attacks(them, to) & pos.pawns(us); - assert(b1 != EmptyBoardBB); + // The checking pawn cannot be a discovered (bishop) check candidate + // otherwise we were in check also before last double push move. + assert(!bit_is_set(pos.discovered_check_candidates(them), checksq)); + assert(count_1s(b1) == 1 || count_1s(b1) == 2); - b1 &= not_pinned; - while (b1) - { - from = pop_1st_bit(&b1); - - // Before generating the move, we have to make sure it is legal. - // This is somewhat tricky, because the two disappearing pawns may - // cause new "discovered checks". We test this by removing the - // two relevant bits from the occupied squares bitboard, and using - // the low-level bitboard functions for bishop and rook attacks. - b2 = pos.occupied_squares(); - clear_bit(&b2, from); - clear_bit(&b2, checksq); - if (!( (bishop_attacks_bb(ksq, b2) & pos.bishops_and_queens(them)) - ||(rook_attacks_bb(ksq, b2) & pos.rooks_and_queens(them)))) - - (*mlist++).move = make_ep_move(from, to); - } - } + b1 &= ~pinned; + while (b1) + { + from = pop_1st_bit(&b1); + // Move is always legal because checking pawn is not a discovered + // check candidate and our capturing pawn has been already tested + // against pinned pieces. + (*mlist++).move = make_ep_move(from, to); + } + } } return int(mlist - mlist_start); } @@ -361,15 +348,15 @@ int generate_legal_moves(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); + Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); + if (pos.is_check()) - return generate_evasions(pos, mlist); + return generate_evasions(pos, mlist, pinned); // Generate pseudo-legal moves int n = generate_captures(pos, mlist); n += generate_noncaptures(pos, mlist + n); - Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); - // Remove illegal moves from the list for (int i = 0; i < n; i++) if (!pos.pl_move_is_legal(mlist[i].move, pinned)) @@ -576,6 +563,24 @@ namespace { return mlist; } + template + MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, + Color us, Bitboard target, Bitboard pinned) { + Square from; + Bitboard b; + + for (int i = 0, e = pos.piece_count(us, Piece); i < e; i++) + { + from = pos.piece_list(us, Piece, i); + if (pinned && bit_is_set(pinned, from)) + continue; + + b = pos.piece_attacks(from) & target; + SERIALIZE_MOVES(b); + } + return mlist; + } + template<> MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { @@ -587,20 +592,6 @@ namespace { return mlist; } - template - MoveStack* generate_piece_blocking_evasions(const Position& pos, MoveStack* mlist, Color us, - Bitboard not_pinned, Bitboard blockSquares) { - - Bitboard b = pos.pieces_of_color_and_type(us, Piece) & not_pinned; - while (b) - { - Square from = pop_1st_bit(&b); - Bitboard bb = pos.piece_attacks(from) & blockSquares; - SERIALIZE_MOVES(bb); - } - return mlist; - } - template @@ -823,12 +814,12 @@ namespace { } template - MoveStack* generate_pawn_blocking_evasions(const Position& pos, Bitboard not_pinned, + MoveStack* generate_pawn_blocking_evasions(const Position& pos, Bitboard pinned, Bitboard blockSquares, MoveStack* mlist) { Square to; // Find non-pinned pawns - Bitboard b1 = pos.pawns(Us) & not_pinned; + Bitboard b1 = pos.pawns(Us) & ~pinned; // Single pawn pushes. We don't have to AND with empty squares here, // because the blocking squares will always be empty. @@ -851,7 +842,7 @@ namespace { // Double pawn pushes b2 = (Us == WHITE ? b1 << 8 : b1 >> 8) & pos.empty_squares() & TRank3BB; - b2 = (Us == WHITE ? b2 << 8 : b2 >> 8) & blockSquares;; + b2 = (Us == WHITE ? b2 << 8 : b2 >> 8) & blockSquares; while (b2) { to = pop_1st_bit(&b2);