X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fmovegen.cpp;h=4b36360c56ae7a13c67178e14d495b320897b531;hp=0af95e2d34f0b5cc7320201a9afe1f456788bb87;hb=7a68916ff992115d8013e7ce31850aec558d8ac5;hpb=deecb3757ca03fa2c9ebc87fd9efe4c2ba05f740 diff --git a/src/movegen.cpp b/src/movegen.cpp index 0af95e2d..4b36360c 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -57,8 +57,7 @@ namespace { MoveStack* generate_castle_moves(const Position&, MoveStack*); template - MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard = EmptyBoardBB, - Square = SQ_NONE, Bitboard = EmptyBoardBB); + MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square); // Template generate_piece_moves (captures and non-captures) with specializations and overloads template @@ -68,19 +67,19 @@ namespace { MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard); template - inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us) { + inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) { assert(Piece == PAWN); - assert(Type == CAPTURE || Type == NON_CAPTURE); + assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION); - return (us == WHITE ? generate_pawn_moves(p, m) - : generate_pawn_moves(p, m)); + return (us == WHITE ? generate_pawn_moves(p, m, t, SQ_NONE) + : generate_pawn_moves(p, m, t, SQ_NONE)); } // Templates for non-capture checks generation template - MoveStack* generate_discovered_checks(const Position& pos, Square from, MoveStack* mlist); + MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square); template MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square); @@ -91,18 +90,6 @@ namespace { return (us == WHITE ? generate_pawn_moves(p, m, dc, ksq) : generate_pawn_moves(p, m, dc, ksq)); } - - // Template generate_piece_evasions with specializations - template - MoveStack* generate_piece_evasions(const Position&, MoveStack*, Color, Bitboard, Bitboard); - - template<> - inline MoveStack* generate_piece_evasions(const Position& p, MoveStack* m, - Color us, Bitboard t, Bitboard pnd) { - - return (us == WHITE ? generate_pawn_moves(p, m, pnd, SQ_NONE, t) - : generate_pawn_moves(p, m, pnd, SQ_NONE, t)); - } } @@ -126,7 +113,7 @@ MoveStack* generate_captures(const Position& pos, MoveStack* mlist) { mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); - mlist = generate_piece_moves(pos, mlist, us); + mlist = generate_piece_moves(pos, mlist, us, target); return generate_piece_moves(pos, mlist, us, target); } @@ -142,7 +129,7 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) { Color us = pos.side_to_move(); Bitboard target = pos.empty_squares(); - mlist = generate_piece_moves(pos, mlist, us); + mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); @@ -153,31 +140,34 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) { } -/// generate_non_capture_checks() generates all pseudo-legal non-captures and +/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. -MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) { +MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); assert(!pos.is_check()); + Bitboard b, dc; + Square from; Color us = pos.side_to_move(); Square ksq = pos.king_square(opposite_color(us)); assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING)); // Discovered non-capture checks - Bitboard b = dc; + b = dc = pos.discovered_check_candidates(us); + while (b) { - Square from = pop_1st_bit(&b); + from = pop_1st_bit(&b); switch (pos.type_of_piece_on(from)) { case PAWN: /* Will be generated togheter with pawns direct checks */ break; - case KNIGHT: mlist = generate_discovered_checks(pos, from, mlist); break; - case BISHOP: mlist = generate_discovered_checks(pos, from, mlist); break; - case ROOK: mlist = generate_discovered_checks(pos, from, mlist); break; - case KING: mlist = generate_discovered_checks(pos, from, mlist); break; + case KNIGHT: mlist = generate_discovered_checks(pos, mlist, from); break; + case BISHOP: mlist = generate_discovered_checks(pos, mlist, from); break; + case ROOK: mlist = generate_discovered_checks(pos, mlist, from); break; + case KING: mlist = generate_discovered_checks(pos, mlist, from); break; default: assert(false); break; } } @@ -194,16 +184,15 @@ MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bi /// generate_evasions() generates all pseudo-legal check evasions when /// the side to move is in check. Returns a pointer to the end of the move list. -MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { +MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); assert(pos.is_check()); - Bitboard b; - Square from, to, checksq; + Bitboard b, target; + Square from, checksq; int checkersCnt = 0; Color us = pos.side_to_move(); - Color them = opposite_color(us); Square ksq = pos.king_square(us); Bitboard checkers = pos.checkers(); Bitboard sliderAttacks = EmptyBoardBB; @@ -220,7 +209,7 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pin checkersCnt++; checksq = pop_1st_bit(&b); - assert(pos.color_of_piece_on(checksq) == them); + assert(pos.color_of_piece_on(checksq) == opposite_color(us)); switch (pos.type_of_piece_on(checksq)) { @@ -247,62 +236,15 @@ MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pin if (checkersCnt > 1) return mlist; - Bitboard target = squares_between(checksq, ksq); + // Find squares where a blocking evasion or a capture of the + // checker piece is possible. + target = squares_between(checksq, ksq) | checkers; - // Pawn captures - b = pos.attacks_from(checksq, them) & pos.pieces(PAWN, us) & ~pinned; - while (b) - { - from = pop_1st_bit(&b); - if (relative_rank(us, checksq) == RANK_8) - { - (*mlist++).move = make_promotion_move(from, checksq, QUEEN); - (*mlist++).move = make_promotion_move(from, checksq, ROOK); - (*mlist++).move = make_promotion_move(from, checksq, BISHOP); - (*mlist++).move = make_promotion_move(from, checksq, KNIGHT); - } else - (*mlist++).move = make_move(from, checksq); - } - - // Pawn blocking evasions (possible only if the checking piece is a slider) - if (sliderAttacks) - mlist = generate_piece_evasions(pos, mlist, us, target, pinned); - - // Add the checking piece to the target squares - target |= checkers; - - // Captures and blocking evasions for the other pieces + mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); - mlist = generate_piece_moves(pos, mlist, us, target); - - // 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.pieces(PAWN, them))) - { - to = pos.ep_square(); - b = pos.attacks_from(to, them) & pos.pieces(PAWN, us); - - // 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(b) == 1 || count_1s(b) == 2); - - b &= ~pinned; - while (b) - { - from = pop_1st_bit(&b); - // 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 mlist; + return generate_piece_moves(pos, mlist, us, target); } @@ -314,26 +256,25 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega assert(pos.is_ok()); - MoveStack* last; + MoveStack *last, *cur = mlist; Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); // Generate pseudo-legal moves if (pos.is_check()) - last = generate_evasions(pos, mlist, pinned); - else { - last = generate_captures(pos, mlist); - last = generate_noncaptures(pos, last); - } + last = generate_evasions(pos, mlist); + else + last = generate_noncaptures(pos, generate_captures(pos, mlist)); + if (pseudoLegal) return last; // Remove illegal moves from the list - for (MoveStack* cur = mlist; cur != last; cur++) - if (!pos.pl_move_is_legal(cur->move, pinned)) - { + while (cur != last) + if (pos.pl_move_is_legal(cur->move, pinned)) + cur++; + else cur->move = (--last)->move; - cur--; - } + return last; } @@ -345,10 +286,11 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega bool move_is_legal(const Position& pos, const Move m) { MoveStack mlist[256]; - MoveStack* last = generate_moves(pos, mlist, true); - for (MoveStack* cur = mlist; cur != last; cur++) + MoveStack *cur, *last = generate_moves(pos, mlist, true); + + for (cur = mlist; cur != last; cur++) if (cur->move == m) - return pos.pl_move_is_legal(m); + return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move())); return false; } @@ -356,25 +298,23 @@ bool move_is_legal(const Position& pos, const Move m) { /// Fast version of move_is_legal() that takes a position a move and a /// bitboard of pinned pieces as input, and tests whether the move is legal. -/// This version must only be used when the side to move is not in check. bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { assert(pos.is_ok()); - assert(!pos.is_check()); assert(move_is_ok(m)); assert(pinned == pos.pinned_pieces(pos.side_to_move())); - // Use a slower but simpler function for uncommon cases - if (move_is_ep(m) || move_is_castle(m)) - return move_is_legal(pos, m); - Color us = pos.side_to_move(); Color them = opposite_color(us); Square from = move_from(m); Square to = move_to(m); Piece pc = pos.piece_on(from); + // Use a slower but simpler function for uncommon cases + if (move_is_ep(m) || move_is_castle(m)) + return move_is_legal(pos, m); + // If the from square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. if (color_of_piece(pc) != us) @@ -443,13 +383,13 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { return false; } // The move is pseudo-legal, check if it is also legal - return pos.pl_move_is_legal(m, pinned); + return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned); } // Luckly we can handle all the other pieces in one go - return ( bit_is_set(pos.attacks_from(pc, from), to) - && pos.pl_move_is_legal(m, pinned) - && !move_is_promotion(m)); + return bit_is_set(pos.attacks_from(pc, from), to) + && (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned)) + && !move_is_promotion(m); } @@ -458,8 +398,8 @@ namespace { template MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { - Square from; Bitboard b; + Square from; const Square* ptr = pos.piece_list_begin(us, Piece); while ((from = *ptr++) != SQ_NONE) @@ -481,24 +421,6 @@ namespace { return mlist; } - template - MoveStack* generate_piece_evasions(const Position& pos, MoveStack* mlist, - Color us, Bitboard target, Bitboard pinned) { - Square from; - Bitboard b; - const Square* ptr = pos.piece_list_begin(us, Piece); - - while ((from = *ptr++) != SQ_NONE) - { - if (pinned && bit_is_set(pinned, from)) - continue; - - b = pos.attacks_from(from) & target; - SERIALIZE_MOVES(b); - } - return mlist; - } - template inline Bitboard move_pawns(Bitboard p) { @@ -512,8 +434,8 @@ namespace { return p; } - template - MoveStack* generate_pawn_diagonal_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool promotion) { + template + inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) { // Calculate our parametrized parameters at compile time const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); @@ -522,136 +444,152 @@ namespace { const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW); const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW); + Bitboard b1, b2; Square to; // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) - Bitboard b1 = move_pawns(pawns) & ~TFileABB & enemyPieces; + b1 = move_pawns(pawns) & ~TFileABB & enemyPieces; - // Capturing promotions - if (promotion) + // Capturing promotions and under-promotions + if (b1 & TRank8BB) { - Bitboard b2 = b1 & TRank8BB; + b2 = b1 & TRank8BB; b1 &= ~TRank8BB; while (b2) { to = pop_1st_bit(&b2); - (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN); + + if (Type == CAPTURE || Type == EVASION) + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN); + + if (Type == NON_CAPTURE || Type == EVASION) + { + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK); + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP); + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); + } + + // This is the only possible under promotion that can give a check + // not already included in the queen-promotion. It is not sure that + // the promoted knight will give check, but it doesn't worth to verify. + if (Type == CHECK) + (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT); } } - // Capturing non-promotions - SERIALIZE_MOVES_D(b1, -TTDELTA_NE); + // Serialize standard captures + if (Type == CAPTURE || Type == EVASION) + SERIALIZE_MOVES_D(b1, -TTDELTA_NE); + return mlist; } template - MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard dcp, - Square ksq, Bitboard blockSquares) { + MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { // Calculate our parametrized parameters at compile time const Color Them = (Us == WHITE ? BLACK : WHITE); const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE); - const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW); const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); - Bitboard b1, b2, dcPawns1, dcPawns2; Square to; - Bitboard pawns = (Type == EVASION ? pos.pieces(PAWN, Us) & ~dcp : pos.pieces(PAWN, Us)); - bool possiblePromotion = pawns & TRank7BB; + Bitboard b1, b2, enemyPieces, emptySquares; + Bitboard pawns = pos.pieces(PAWN, Us); - if (Type == CAPTURE) + // Standard captures and capturing promotions and underpromotions + if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB)) { - // Standard captures and capturing promotions in both directions - Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); - mlist = generate_pawn_diagonal_captures(mlist, pawns, enemyPieces, possiblePromotion); - mlist = generate_pawn_diagonal_captures(mlist, pawns, enemyPieces, possiblePromotion); - } + enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us))); - if (possiblePromotion) - { - // When generating checks consider under-promotion moves (both captures - // and non captures) only if can give a discovery check. Note that dcp - // is dc bitboard or pinned bitboard when Type == EVASION. - Bitboard pp = (Type == CHECK ? pawns & dcp : pawns); - - if (Type != EVASION && Type != CAPTURE) - { - Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); + if (Type == EVASION) + enemyPieces &= target; // Capture only the checker piece - // Underpromotion captures in the a1-h8 (a8-h1 for black) direction - b1 = move_pawns(pp) & ~FileABB & enemyPieces & TRank8BB; - while (b1) - { - to = pop_1st_bit(&b1); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, ROOK); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, BISHOP); - (*mlist++).move = make_promotion_move(to - TDELTA_NE, to, KNIGHT); - } + mlist = generate_pawn_captures(mlist, pawns, enemyPieces); + mlist = generate_pawn_captures(mlist, pawns, enemyPieces); + } - // Underpromotion captures in the h1-a8 (h8-a1 for black) direction - b1 = move_pawns(pp) & ~FileHBB & enemyPieces & TRank8BB; - while (b1) - { - to = pop_1st_bit(&b1); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, ROOK); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, BISHOP); - (*mlist++).move = make_promotion_move(to - TDELTA_NW, to, KNIGHT); - } - } + // Non-capturing promotions and underpromotions + if (pawns & TRank7BB) + { + b1 = move_pawns(pawns) & TRank8BB & pos.empty_squares(); - // Underpromotion pawn pushes. Also queen promotions for evasions and captures. - b1 = move_pawns(pp) & TRank8BB; - b1 &= (Type == EVASION ? blockSquares : pos.empty_squares()); + if (Type == EVASION) + b1 &= target; // Only blocking promotion pushes while (b1) { to = pop_1st_bit(&b1); - if (Type == EVASION || Type == CAPTURE) + + if (Type == CAPTURE || Type == EVASION) (*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN); - if (Type != CAPTURE) + if (Type == NON_CAPTURE || Type == EVASION) { (*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK); (*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP); (*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT); } + + // This is the only possible under promotion that can give a check + // not already included in the queen-promotion. + if (Type == CHECK && bit_is_set(pos.attacks_from(to), pos.king_square(Them))) + (*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT); } } + // Standard pawn pushes and double pushes if (Type != CAPTURE) { - Bitboard emptySquares = pos.empty_squares(); - dcPawns1 = dcPawns2 = EmptyBoardBB; - if (Type == CHECK && (pawns & dcp)) + emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares()); + + // Single and double pawn pushes + b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; + b2 = move_pawns(b1 & TRank3BB) & emptySquares; + + // Filter out unwanted pushes according to the move type + if (Type == EVASION) { - // Pawn moves which gives discovered check. This is possible only if the - // pawn is not on the same file as the enemy king, because we don't - // generate captures. - dcPawns1 = move_pawns(pawns & dcp & ~file_bb(ksq)) & emptySquares & ~TRank8BB; - dcPawns2 = move_pawns(dcPawns1 & TRank3BB) & emptySquares; + b1 &= target; + b2 &= target; } + else if (Type == CHECK) + { + // Pawn moves which give direct cheks + b1 &= pos.attacks_from(ksq, Them); + b2 &= pos.attacks_from(ksq, Them); + + // Pawn moves which gives discovered check. This is possible only if + // the pawn is not on the same file as the enemy king, because we + // don't generate captures. + if (pawns & target) // For CHECK type target is dc bitboard + { + Bitboard dc1 = move_pawns(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB; + Bitboard dc2 = move_pawns(dc1 & TRank3BB) & emptySquares; - // Single pawn pushes - b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; - b2 = (Type == CHECK ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns1 : - (Type == EVASION ? b1 & blockSquares : b1)); - SERIALIZE_MOVES_D(b2, -TDELTA_N); - - // Double pawn pushes - b1 = move_pawns(b1 & TRank3BB) & emptySquares; - b2 = (Type == CHECK ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns2 : - (Type == EVASION ? b1 & blockSquares : b1)); + b1 |= dc1; + b2 |= dc2; + } + } + SERIALIZE_MOVES_D(b1, -TDELTA_N); SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N); } - else if (pos.ep_square() != SQ_NONE) // En passant captures + + // En passant captures + if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE) { assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6); assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3); + // An en passant capture can be an evasion only if the checking piece + // is the double pushed pawn and so is in the target. Otherwise this + // is a discovery check and we are forced to do otherwise. + if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N)) + return mlist; + b1 = pawns & pos.attacks_from(pos.ep_square(), Them); + assert(b1 != EmptyBoardBB); while (b1) @@ -664,7 +602,7 @@ namespace { } template - MoveStack* generate_discovered_checks(const Position& pos, Square from, MoveStack* mlist) { + MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { assert(Piece != QUEEN); @@ -683,8 +621,8 @@ namespace { Bitboard dc, Square ksq) { assert(Piece != KING); + Bitboard checkSqs, b; Square from; - Bitboard checkSqs; const Square* ptr = pos.piece_list_begin(us, Piece); if ((from = *ptr++) == SQ_NONE) @@ -702,8 +640,8 @@ namespace { if (dc && bit_is_set(dc, from)) continue; - Bitboard bb = pos.attacks_from(from) & checkSqs; - SERIALIZE_MOVES(bb); + b = pos.attacks_from(from) & checkSqs; + SERIALIZE_MOVES(b); } while ((from = *ptr++) != SQ_NONE);