namespace {
- template<Color Us, CastlingSide Cs, bool Checks, bool Chess960>
- 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.count<KING>(Us) ? pos.square<KING>(Us) : make_square(FILE_E, relative_rank(Us, RANK_1, pos.max_rank()));
- Square rfrom = pos.castling_rook_square(Cr);
- Square kto = make_square(KingSide ? pos.castling_kingside_file() : pos.castling_queenside_file(),
- relative_rank(Us, RANK_1, pos.max_rank()));
- Bitboard enemies = pos.pieces(Them);
-
- assert(!pos.checkers());
-
- const Direction step = Chess960 ? kto > kfrom ? WEST : EAST
- : KingSide ? WEST : EAST;
-
- if (type_of(pos.piece_on(kfrom)) == KING)
- {
- for (Square s = kto; s != kfrom; s += step)
- if (pos.attackers_to(s, ~Us) & 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 && pos.attackers_to(kto, pos.pieces() ^ rfrom, ~Us))
- return moveList;
- }
-
- Move m = make<CASTLING>(kfrom, rfrom);
-
- if (Checks && !pos.gives_check(m))
- return moveList;
-
- *moveList++ = m;
- return moveList;
- }
-
-
- template<GenType Type, Direction D>
- ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
+ template<Color c, GenType Type, Direction D>
+ ExtMove* make_promotions(const Position& pos, ExtMove* moveList, Square to) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
- *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
+ for (PieceType pt : pos.promotion_piece_types())
+ *moveList++ = make<PROMOTION>(to - D, to, pt);
- if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
+ return moveList;
+ }
+
+ template<Color Us, bool Checks>
+ ExtMove* generate_drops(const Position& pos, ExtMove* moveList, PieceType pt, Bitboard b) {
+ if (pos.count_in_hand(Us, pt))
{
- *moveList++ = make<PROMOTION>(to - D, to, ROOK);
- *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
- *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
- }
+ // Restrict to valid target
+ b &= pos.drop_region(Us, pt);
- // Knight promotion is the only promotion that can give a direct check
- // that's not already included in the queen promotion.
- if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq))
- *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
- else
- (void)ksq; // Silence a warning under MSVC
+ // Add to move list
+ if (pos.drop_promoted() && pos.promoted_piece_type(pt))
+ {
+ Bitboard b2 = b;
+ if (Checks)
+ b2 &= pos.check_squares(pos.promoted_piece_type(pt));
+ while (b2)
+ *moveList++ = make_drop(pop_lsb(&b2), pt, pos.promoted_piece_type(pt));
+ }
+ if (Checks)
+ b &= pos.check_squares(pt);
+ while (b)
+ *moveList++ = make_drop(pop_lsb(&b), pt, pt);
+ }
return moveList;
}
template<Color Us, GenType Type>
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<Us, Type>(pos, moveList, target);
- moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
- moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
-
- if (Type != QUIET_CHECKS && Type != EVASIONS)
+ for (PieceType pt = PieceType(PAWN + 1); pt < KING; ++pt)
+ moveList = generate_moves<Checks>(pos, moveList, Us, pt, target);
+ // generate drops
+ if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES))
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
+
+ if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
{
Square ksq = pos.square<KING>(Us);
- Bitboard b = pos.attacks_from<KING>(ksq) & target;
+ Bitboard b = pos.attacks_from<KING>(Us, ksq) & target;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
- }
- if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
- {
- if (pos.is_chess960())
+ if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO)))
{
- moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
- moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList);
- }
- else
- {
- moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
- moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
+ if (!pos.castling_impeded(OO) && pos.can_castle(OO))
+ *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
+
+ if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
+ *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
}
}
++ // Castling with non-king piece
++ if (!pos.count<KING>(Us) && Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO)))
++ {
++ Square from = make_square(FILE_E, relative_rank(Us, RANK_1, pos.max_rank()));
++ if (!pos.castling_impeded(OO) && pos.can_castle(OO))
++ *moveList++ = make<CASTLING>(from, pos.castling_rook_square(OO));
++
++ if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
++ *moveList++ = make<CASTLING>(from, pos.castling_rook_square(OOO));
++ }
++
return moveList;
}
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
- Square ksq = count<KING>(us) ? square<KING>(us) : SQ_NONE;
assert(color_of(moved_piece(m)) == us);
- assert(piece_on(square<KING>(us)) == make_piece(us, KING));
+ assert(!count<KING>(us) || piece_on(square<KING>(us)) == make_piece(us, KING));
+
+ // Illegal moves to squares outside of board
+ if (!(board_bb() & to))
+ return false;
+
+ // Illegal checks
+ if ((!checking_permitted() || (sittuyin_promotion() && type_of(m) == PROMOTION)) && gives_check(m))
+ return false;
+
+ // Illegal quiet moves
+ if (must_capture() && !capture(m))
+ {
+ if (checkers())
+ {
+ for (const auto& mevasion : MoveList<EVASIONS>(*this))
+ if (capture(mevasion) && legal(mevasion))
+ return false;
+ }
+ else
+ {
+ for (const auto& mcap : MoveList<CAPTURES>(*this))
+ if (capture(mcap) && legal(mcap))
+ return false;
+ }
+ }
+
+ // Illegal non-drop moves
+ if (must_drop() && type_of(m) != DROP && count_in_hand(us, ALL_PIECES))
+ {
+ if (checkers())
+ {
+ for (const auto& mevasion : MoveList<EVASIONS>(*this))
+ if (type_of(mevasion) == DROP && legal(mevasion))
+ return false;
+ }
+ else
+ {
+ for (const auto& mquiet : MoveList<QUIETS>(*this))
+ if (type_of(mquiet) == DROP && legal(mquiet))
+ return false;
+ }
+ }
+
+ // Illegal drop move
+ if (drop_opposite_colored_bishop() && type_of(m) == DROP)
+ {
+ if (type_of(moved_piece(m)) != BISHOP)
+ {
+ Bitboard remaining = drop_region(us) & ~pieces() & ~SquareBB[to] & board_bb();
+ // Are enough squares available to drop bishops on opposite colors?
+ if ( (!( DarkSquares & pieces(us, BISHOP)) && ( DarkSquares & remaining))
+ + (!(~DarkSquares & pieces(us, BISHOP)) && (~DarkSquares & remaining)) < count_in_hand(us, BISHOP))
+ return false;
+ }
+ else
+ // Drop resulting in same-colored bishops
+ if ((DarkSquares & to ? DarkSquares : ~DarkSquares) & pieces(us, BISHOP))
+ return false;
+ }
+
+ // No legal moves from target square
+ if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(moves_bb(us, type_of(moved_piece(m)), to, 0) & board_bb()))
+ return false;
// En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after
// the move is made.
if (type_of(m) == ENPASSANT)
{
- Square ksq = square<KING>(us);
++ Square ksq = count<KING>(us) ? square<KING>(us) : SQ_NONE;
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
- return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
- && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
+ return !count<KING>(us) || !(attackers_to(ksq, occupied, ~us) & occupied);
}
+ // 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)
+ {
++ // Non-royal pieces can not be impeded from castling
++ if (type_of(piece_on(from)) != KING)
++ return true;
++
+ // 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);
++ to = make_square(to > from ? castling_kingside_file() : castling_queenside_file(), relative_rank(us, RANK_1, max_rank()));
+ Direction step = to > from ? WEST : EAST;
+
+ for (Square s = to; s != from; s += step)
- if (attackers_to(s) & pieces(~us))
++ if (attackers_to(s, ~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<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
++ || !(attackers_to(to, pieces() ^ to_sq(m), ~us));
+ }
+
- // 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 !(attackers_to(to) & pieces(~us));
+ // 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.
+ if (type_of(moved_piece(m)) == KING)
+ return type_of(m) == CASTLING || !attackers_to(to, ~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, square<KING>(us));
+ // A non-king move is legal if the king is not under attack after the move.
- return !count<KING>(us) || !(attackers_to(ksq, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us) & ~SquareBB[to]);
++ return !count<KING>(us) || !(attackers_to(square<KING>(us), (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us) & ~SquareBB[to]);
}