X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmovegen.cpp;h=ce8f54cac77ef35a103a662a63c71f58c41f02cf;hb=aed542d74cf57438fef19b662a56d8d810bc1965;hp=eb65e7b9f18977dc1bcb9f730aab1e175ced4d61;hpb=ae4967744686ae6e95ca0cdb9e0d788de05d4e03;p=stockfish diff --git a/src/movegen.cpp b/src/movegen.cpp index eb65e7b9..ce8f54ca 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -66,11 +66,8 @@ namespace { template MoveStack* generate_pawn_captures_diagonal(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool promotion); - template - MoveStack* generate_pawn_noncaptures(const Position& pos, MoveStack* mlist); - - template - MoveStack* generate_pawn_checks(const Position&, Bitboard, Square, MoveStack*); + template + MoveStack* generate_pawn_noncaptures(const Position& pos, MoveStack* mlist, Bitboard dc = EmptyBoardBB, Square ksq = SQ_NONE); template inline Bitboard move_pawns(Bitboard p) { @@ -92,8 +89,8 @@ namespace { template<> inline MoveStack* generate_piece_checks(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) { - return (us == WHITE ? generate_pawn_checks(p, dc, ksq, m) - : generate_pawn_checks(p, dc, ksq, m)); + return (us == WHITE ? generate_pawn_noncaptures(p, m, dc, ksq) + : generate_pawn_noncaptures(p, m, dc, ksq)); } // Template generate_piece_moves() with specializations and overloads @@ -112,8 +109,8 @@ namespace { return (us == WHITE ? generate_pawn_captures(p, m) : generate_pawn_captures(p, m)); else - return (us == WHITE ? generate_pawn_noncaptures(p, m) - : generate_pawn_noncaptures(p, m)); + return (us == WHITE ? generate_pawn_noncaptures(p, m) + : generate_pawn_noncaptures(p, m)); } template @@ -135,38 +132,35 @@ namespace { /// generate_captures generates() all pseudo-legal captures and queen -/// promotions. The return value is the number of moves generated. +/// promotions. Returns a pointer to the end of the move list. -int generate_captures(const Position& pos, MoveStack* mlist) { +MoveStack* generate_captures(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); assert(!pos.is_check()); Color us = pos.side_to_move(); Bitboard target = pos.pieces_of_color(opposite_color(us)); - MoveStack* mlist_start = 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, target); mlist = generate_piece_moves(pos, mlist, us); - mlist = generate_piece_moves(pos, mlist, us, target); - return int(mlist - mlist_start); + return generate_piece_moves(pos, mlist, us, target); } /// generate_noncaptures() generates all pseudo-legal non-captures and -/// underpromotions. The return value is the number of moves generated. +/// underpromotions. Returns a pointer to the end of the move list. -int generate_noncaptures(const Position& pos, MoveStack* mlist) { +MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); assert(!pos.is_check()); Color us = pos.side_to_move(); Bitboard target = pos.empty_squares(); - MoveStack* mlist_start = mlist; mlist = generate_piece_moves(pos, mlist, us); mlist = generate_piece_moves(pos, mlist, us, target); @@ -175,22 +169,20 @@ int generate_noncaptures(const Position& pos, MoveStack* mlist) { mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_piece_moves(pos, mlist, us, target); mlist = generate_castle_moves(pos, mlist); - mlist = generate_castle_moves(pos, mlist); - return int(mlist - mlist_start); + return generate_castle_moves(pos, mlist); } /// generate_non_capture_checks() generates all pseudo-legal non-capturing, -/// non-promoting checks. It returns the number of generated moves. +/// non-promoting checks. Returns a pointer to the end of the move list. -int generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) { +MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) { assert(pos.is_ok()); assert(!pos.is_check()); Color us = pos.side_to_move(); Square ksq = pos.king_square(opposite_color(us)); - MoveStack* mlist_start = mlist; assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING)); @@ -213,15 +205,15 @@ int generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard && castling_is_check(pos, KING_SIDE)) mlist = generate_castle_moves(pos, mlist); - return int(mlist - mlist_start); + return mlist; } /// 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. +/// only legal moves. Returns a pointer to the end of the move list. -int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { +MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { assert(pos.is_ok()); assert(pos.is_check()); @@ -230,7 +222,8 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { Color us = pos.side_to_move(); Color them = opposite_color(us); Square ksq = pos.king_square(us); - MoveStack* mlist_start = mlist; + Bitboard sliderAttacks = EmptyBoardBB; + Bitboard checkers = pos.checkers(); assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING)); @@ -241,31 +234,30 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { // 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 - // and to be able to use square_is_attacked(). - Bitboard checkers = pos.checkers(); - Bitboard checkersAttacks = EmptyBoardBB; - Bitboard b = checkers & (pos.queens() | pos.bishops()); + // and to be able to use attackers_to(). + Bitboard b = checkers & pos.pieces(BISHOP, QUEEN); while (b) { from = pop_1st_bit(&b); - checkersAttacks |= bishop_attacks_bb(from, b_noKing); + sliderAttacks |= bishop_attacks_bb(from, b_noKing); } - b = checkers & (pos.queens() | pos.rooks()); + b = checkers & pos.pieces(ROOK, QUEEN); while (b) { from = pop_1st_bit(&b); - checkersAttacks |= rook_attacks_bb(from, b_noKing); + sliderAttacks |= rook_attacks_bb(from, b_noKing); } - // Generate evasions for king - Bitboard b1 = pos.piece_attacks(ksq) & ~pos.pieces_of_color(us) & ~checkersAttacks; + // Generate evasions for king, both captures and non captures + Bitboard b1 = pos.attacks_from(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks; + Bitboard enemy = pos.pieces_of_color(them); while (b1) { to = pop_1st_bit(&b1); - // Note that we can use square_is_attacked() only because we - // have already removed slider checkers. - if (!pos.square_is_attacked(to, them)) + // Note that we can use attackers_to() only because we + // have already removed slider checkers attacked squares. + if (!(pos.attackers_to(to) & enemy)) (*mlist++).move = make_move(ksq, to); } @@ -281,7 +273,7 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { // Generate captures of the checking piece // Pawn captures - b1 = pos.pawn_attacks(them, checksq) & pos.pawns(us) & ~pinned; + b1 = pos.attacks_from(checksq, them) & pos.pieces(PAWN, us) & ~pinned; while (b1) { from = pop_1st_bit(&b1); @@ -296,9 +288,9 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { } // 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)) ) & ~pinned; + b1 = ( (pos.attacks_from(checksq) & pos.pieces(KNIGHT, us)) + | (pos.attacks_from(checksq) & pos.pieces(BISHOP, QUEEN, us)) + | (pos.attacks_from(checksq) & pos.pieces(ROOK, QUEEN, us)) ) & ~pinned; while (b1) { @@ -306,15 +298,14 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { (*mlist++).move = make_move(from, checksq); } - // Blocking check evasions are possible only if the checking piece is - // a slider. - if (checkers & pos.sliders()) + // Blocking check evasions are possible only if the checking piece is a slider + if (sliderAttacks) { Bitboard blockSquares = squares_between(checksq, ksq); assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB); - if (blockSquares != EmptyBoardBB) + if (blockSquares) { mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); mlist = generate_piece_moves(pos, mlist, us, blockSquares, pinned); @@ -329,10 +320,10 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { // 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))) + if (pos.ep_square() != SQ_NONE && (checkers & pos.pieces(PAWN, them))) { to = pos.ep_square(); - b1 = pos.pawn_attacks(them, to) & pos.pawns(us); + b1 = 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. @@ -350,7 +341,7 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { } } } - return int(mlist - mlist_start); + return mlist; } @@ -360,7 +351,7 @@ int generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) { /// very hard to write an efficient legal move generator, but for the moment /// we don't need it. -int generate_legal_moves(const Position& pos, MoveStack* mlist) { +MoveStack* generate_legal_moves(const Position& pos, MoveStack* mlist) { assert(pos.is_ok()); @@ -370,15 +361,17 @@ int generate_legal_moves(const Position& pos, MoveStack* mlist) { return generate_evasions(pos, mlist, pinned); // Generate pseudo-legal moves - int n = generate_captures(pos, mlist); - n += generate_noncaptures(pos, mlist + n); + MoveStack* last = generate_captures(pos, mlist); + last = generate_noncaptures(pos, last); // Remove illegal moves from the list - for (int i = 0; i < n; i++) - if (!pos.pl_move_is_legal(mlist[i].move, pinned)) - mlist[i--].move = mlist[--n].move; - - return n; + for (MoveStack* cur = mlist; cur != last; cur++) + if (!pos.pl_move_is_legal(cur->move, pinned)) + { + cur->move = (--last)->move; + cur--; + } + return last; } @@ -445,7 +438,7 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { // is occupied or under attack. for (s = Min(from, g1); s <= Max(from, g1); s++) if ( (s != from && s != to && !pos.square_is_empty(s)) - || pos.square_is_attacked(s, them)) + ||(pos.attackers_to(s) & pos.pieces_of_color(them))) illegal = true; // Check if any of the squares between king and rook @@ -476,7 +469,7 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { for (s = Min(from, c1); s <= Max(from, c1); s++) if( (s != from && s != to && !pos.square_is_empty(s)) - || pos.square_is_attacked(s, them)) + ||(pos.attackers_to(s) & pos.pieces_of_color(them))) illegal = true; for (s = Min(to, d1); s <= Max(to, d1); s++) @@ -561,7 +554,7 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { } // Luckly we can handle all the other pieces in one go - return ( pos.piece_attacks_square(pos.piece_on(from), from, to) + return ( bit_is_set(pos.attacks_from(pc, from), to) && pos.pl_move_is_legal(m, pinned) && !move_is_promotion(m)); } @@ -580,11 +573,12 @@ bool move_is_legal(const Position& pos, const Move m) { else { Position p(pos); - MoveStack moves[64]; - int n = generate_evasions(p, moves, pinned); - for (int i = 0; i < n; i++) - if (moves[i].move == m) + MoveStack mlist[64]; + MoveStack* last = generate_evasions(p, mlist, pinned); + for (MoveStack* cur = mlist; cur != last; cur++) + if (cur->move == m) return true; + return false; } } @@ -601,7 +595,7 @@ namespace { for (int i = 0, e = pos.piece_count(us, Piece); i < e; i++) { from = pos.piece_list(us, Piece, i); - b = pos.piece_attacks(from) & target; + b = pos.attacks_from(from) & target; SERIALIZE_MOVES(b); } return mlist; @@ -619,7 +613,7 @@ namespace { if (pinned && bit_is_set(pinned, from)) continue; - b = pos.piece_attacks(from) & target; + b = pos.attacks_from(from) & target; SERIALIZE_MOVES(b); } return mlist; @@ -631,7 +625,7 @@ namespace { Bitboard b; Square from = pos.king_square(us); - b = pos.piece_attacks(from) & target; + b = pos.attacks_from(from) & target; SERIALIZE_MOVES(b); return mlist; } @@ -678,7 +672,7 @@ namespace { const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); Square to; - Bitboard pawns = pos.pawns(Us); + Bitboard pawns = pos.pieces(PAWN, Us); Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); bool possiblePromotion = (pawns & TRank7BB); @@ -703,7 +697,7 @@ namespace { assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6); assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3); - Bitboard b1 = pawns & pos.pawn_attacks(Them, pos.ep_square()); + Bitboard b1 = pawns & pos.attacks_from(pos.ep_square(), Them); assert(b1 != EmptyBoardBB); while (b1) @@ -715,10 +709,11 @@ namespace { return mlist; } - template - MoveStack* generate_pawn_noncaptures(const Position& pos, MoveStack* mlist) { + template + MoveStack* generate_pawn_noncaptures(const Position& pos, MoveStack* mlist, Bitboard dc, 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); @@ -726,17 +721,20 @@ namespace { const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW); const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); - Bitboard b1, b2; + Bitboard b1, b2, dcPawns1, dcPawns2; Square to; - Bitboard pawns = pos.pawns(Us); + Bitboard pawns = pos.pieces(PAWN, Us); Bitboard emptySquares = pos.empty_squares(); if (pawns & TRank7BB) // There is some promotion candidate ? { - Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); + // When generating checks consider under-promotion moves (both captures + // and non captures) only if can give a discovery check. + Bitboard pp = GenerateChecks ? pawns & dc & EmptyBoardBB: pawns; + Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us)); // Underpromotion captures in the a1-h8 (a8-h1 for black) direction - b1 = move_pawns(pawns) & ~FileABB & enemyPieces & TRank8BB; + b1 = move_pawns(pp) & ~FileABB & enemyPieces & TRank8BB; while (b1) { to = pop_1st_bit(&b1); @@ -746,7 +744,7 @@ namespace { } // Underpromotion captures in the h1-a8 (h8-a1 for black) direction - b1 = move_pawns(pawns) & ~FileHBB & enemyPieces & TRank8BB; + b1 = move_pawns(pp) & ~FileHBB & enemyPieces & TRank8BB; while (b1) { to = pop_1st_bit(&b1); @@ -756,7 +754,7 @@ namespace { } // Underpromotion pawn pushes - b1 = move_pawns(pawns) & emptySquares & TRank8BB; + b1 = move_pawns(pp) & emptySquares & TRank8BB; while (b1) { to = pop_1st_bit(&b1); @@ -766,68 +764,25 @@ namespace { } } - // Single pawn pushes - b2 = b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; - SERIALIZE_MOVES_D(b2, -TDELTA_N); - - // Double pawn pushes - b2 = move_pawns(b1 & TRank3BB) & emptySquares; - SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N); - return mlist; - } - - - template - MoveStack* generate_pawn_checks(const Position& pos, Bitboard dc, Square ksq, MoveStack* mlist) - { - // Calculate our parametrized parameters at compile time - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); - const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); - const SquareDelta TDELTA_S = (Us == WHITE ? DELTA_S : DELTA_N); - - Square to; - Bitboard b1, b2, b3; - Bitboard pawns = pos.pawns(Us); - - if (dc & pawns) + dcPawns1 = dcPawns2 = EmptyBoardBB; + if (GenerateChecks && (dc & pawns)) { - Bitboard empty = pos.empty_squares(); - // 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. - b1 = pawns & ~file_bb(ksq); - - // Discovered checks, single pawn pushes, no promotions - b2 = b3 = move_pawns(b1 & dc) & empty & ~TRank8BB; - SERIALIZE_MOVES_D(b3, -TDELTA_N); - - // Discovered checks, double pawn pushes - b3 = move_pawns(b2 & TRank3BB) & empty; - SERIALIZE_MOVES_D(b3, -TDELTA_N -TDELTA_N); + dcPawns1 = move_pawns(pawns & dc & ~file_bb(ksq)) & emptySquares & ~TRank8BB; + dcPawns2 = move_pawns(dcPawns1 & TRank3BB) & emptySquares; } - // Direct checks. These are possible only for pawns on neighboring files - // and in the two ranks that, after the push, are in front of the enemy king. - b1 = pawns & neighboring_files_bb(ksq) & ~dc; - - // We can get false positives if (ksq + x) is not in [0,63] range but - // is not a problem, they will be filtered out later. - b2 = b1 & (rank_bb(ksq + 2 * TDELTA_S) | rank_bb(ksq + 3 * TDELTA_S)); - if (!b2) - return mlist; - - // Direct checks, single pawn pushes - Bitboard empty = pos.empty_squares(); - b2 = move_pawns(b1) & empty; - b3 = b2 & pos.pawn_attacks(Them, ksq); - SERIALIZE_MOVES_D(b3, -TDELTA_N); + // Single pawn pushes + b1 = move_pawns(pawns) & emptySquares & ~TRank8BB; + b2 = GenerateChecks ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns1 : b1; + SERIALIZE_MOVES_D(b2, -TDELTA_N); - // Direct checks, double pawn pushes - b3 = move_pawns(b2 & TRank3BB) & empty & pos.pawn_attacks(Them, ksq); - SERIALIZE_MOVES_D(b3, -TDELTA_N -TDELTA_N); + // Double pawn pushes + b1 = move_pawns(b1 & TRank3BB) & emptySquares; + b2 = GenerateChecks ? (b1 & pos.attacks_from(ksq, Them)) | dcPawns2 : b1; + SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N); return mlist; } @@ -835,14 +790,14 @@ namespace { MoveStack* generate_piece_checks(const Position& pos, MoveStack* mlist, Color us, Bitboard dc, Square ksq) { - Bitboard target = pos.pieces_of_color_and_type(us, Piece); + Bitboard target = pos.pieces(Piece, us); // Discovered checks Bitboard b = target & dc; while (b) { Square from = pop_1st_bit(&b); - Bitboard bb = pos.piece_attacks(from) & pos.empty_squares(); + Bitboard bb = pos.attacks_from(from) & pos.empty_squares(); if (Piece == KING) bb &= ~QueenPseudoAttacks[ksq]; @@ -851,23 +806,20 @@ namespace { // Direct checks b = target & ~dc; - if (Piece != KING || b) + Bitboard checkSqs = pos.attacks_from(ksq) & pos.empty_squares(); + if (Piece == KING || !checkSqs) + return mlist; + + while (b) { - Bitboard checkSqs = pos.piece_attacks(ksq) & pos.empty_squares(); - if (!checkSqs) - return mlist; + Square from = pop_1st_bit(&b); + if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs)) + || (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs)) + || (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs))) + continue; - while (b) - { - Square from = pop_1st_bit(&b); - if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs)) - || (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs)) - || (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs))) - continue; - - Bitboard bb = pos.piece_attacks(from) & checkSqs; - SERIALIZE_MOVES(bb); - } + Bitboard bb = pos.attacks_from(from) & checkSqs; + SERIALIZE_MOVES(bb); } return mlist; } @@ -884,7 +836,7 @@ namespace { Square to; // Find non-pinned pawns and push them one square - Bitboard b1 = move_pawns(pos.pawns(Us) & ~pinned); + Bitboard b1 = move_pawns(pos.pieces(PAWN, Us) & ~pinned); // We don't have to AND with empty squares here, // because the blocking squares will always be empty. @@ -945,7 +897,7 @@ namespace { // It is a bit complicated to correctly handle Chess960 for (s = Min(ksq, s1); s <= Max(ksq, s1); s++) if ( (s != ksq && s != rsq && pos.square_is_occupied(s)) - || pos.square_is_attacked(s, them)) + ||(pos.attackers_to(s) & pos.pieces_of_color(them))) illegal = true; for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)