diff --git a/src/movegen.cpp b/src/movegen.cpp index c3cff08d..5ed24893 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -25,48 +25,6 @@ namespace { - template - ExtMove* generate_castling(const Position& pos, ExtMove* moveList) { - - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr CastlingRight Cr = Us | Cs; - constexpr bool KingSide = (Cs == KING_SIDE); - - if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) - return moveList; - - // After castling, the rook and king final positions are the same in Chess960 - // as they would be in standard chess. - Square kfrom = pos.square(Us); - Square rfrom = pos.castling_rook_square(Cr); - Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1); - Bitboard enemies = pos.pieces(Them); - - assert(!pos.checkers()); - - const Direction step = Chess960 ? kto > kfrom ? WEST : EAST - : KingSide ? WEST : EAST; - - for (Square s = kto; s != kfrom; s += step) - if (pos.attackers_to(s) & enemies) - return moveList; - - // Because we generate only legal castling moves we need to verify that - // when moving the castling rook we do not discover some hidden checker. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(Them, ROOK, QUEEN))) - return moveList; - - Move m = make(kfrom, rfrom); - - if (Checks && !pos.gives_check(m)) - return moveList; - - *moveList++ = m; - return moveList; - } - - template ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { @@ -261,7 +219,9 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { - constexpr bool Checks = Type == QUIET_CHECKS; + constexpr CastlingRight OO = Us | KING_SIDE; + constexpr CastlingRight OOO = Us | QUEEN_SIDE; + constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, Us, target); @@ -275,19 +235,14 @@ namespace { Bitboard b = pos.attacks_from(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); - } - if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us)) - { - if (pos.is_chess960()) + if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO))) { - moveList = generate_castling(pos, moveList); - moveList = generate_castling(pos, moveList); - } - else - { - moveList = generate_castling(pos, moveList); - moveList = generate_castling(pos, moveList); + if (!pos.castling_impeded(OO) && pos.can_castle(OO)) + *moveList++ = make(ksq, pos.castling_rook_square(OO)); + + if (!pos.castling_impeded(OOO) && pos.can_castle(OOO)) + *moveList++ = make(ksq, pos.castling_rook_square(OOO)); } } diff --git a/src/position.cpp b/src/position.cpp index 0396eb9f..21eff88c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -540,6 +540,7 @@ bool Position::legal(Move m) const { Color us = sideToMove; Square from = from_sq(m); + Square to = to_sq(m); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); @@ -550,7 +551,6 @@ bool Position::legal(Move m) const { if (type_of(m) == ENPASSANT) { Square ksq = square(us); - Square to = to_sq(m); Square capsq = to - pawn_push(us); Bitboard occupied = (pieces() ^ from ^ capsq) | to; @@ -563,16 +563,35 @@ bool Position::legal(Move m) const { && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); } - // If the moving piece is a king, check whether the destination - // square is attacked by the opponent. Castling moves are checked - // for legality during move generation. + // Castling moves generation does not check if the castling path is clear of + // enemy attacks, it is delayed at a later time: now! + if (type_of(m) == CASTLING) + { + // After castling, the rook and king final positions are the same in + // Chess960 as they would be in standard chess. + to = relative_square(us, to > from ? SQ_G1 : SQ_C1); + Direction step = to > from ? WEST : EAST; + + for (Square s = to; s != from; s += step) + if (attackers_to(s) & pieces(~us)) + return false; + + // In case of Chess960, verify that when moving the castling rook we do + // not discover some hidden checker. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + return !chess960 + || !(attacks_bb(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN)); + } + + // If the moving piece is a king, check whether the destination square is + // attacked by the opponent. if (type_of(piece_on(from)) == KING) - return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); + return !(attackers_to(to) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. return !(blockers_for_king(us) & from) - || aligned(from, to_sq(m), square(us)); + || aligned(from, to, square(us)); } diff --git a/src/position.h b/src/position.h index d94ef185..74173d03 100644 --- a/src/position.h +++ b/src/position.h @@ -265,7 +265,7 @@ inline bool Position::can_castle(CastlingRight cr) const { } inline int Position::castling_rights(Color c) const { - return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); + return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } inline bool Position::castling_impeded(CastlingRight cr) const { diff --git a/src/types.h b/src/types.h index c4c2752c..8e27606c 100644 --- a/src/types.h +++ b/src/types.h @@ -141,7 +141,9 @@ enum CastlingRight { WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, - ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, + WHITE_CASTLING = WHITE_OO | WHITE_OOO, + BLACK_CASTLING = BLACK_OO | BLACK_OOO, + ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, CASTLING_RIGHT_NB = 16 };