Skip to content

Commit 7965d33

Browse files
mcostalbajoergoster
authored andcommitted
Delay castling legality check
Delay legality check of castling moves at search time, just before making the move, as is the standard with all the other move types. This should avoid an useless and not trivial legality check when the castling is then not tried later. For instance due to a previous cut-off. The patch is also a big simplification and allows to entirely remove generate_castling() Bench changes due to a different move sequence out of MovePicker. STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 45073 W: 9918 L: 9843 D: 25312 http://tests.stockfishchess.org/tests/view/5c2f176f0ebc596a450bdfb3 LTC: LLR: 3.15 (-2.94,2.94) [-3.00,1.00] Total: 10156 W: 1707 L: 1560 D: 6889 http://tests.stockfishchess.org/tests/view/5c2e7dfd0ebc596a450bcdf4 Verified with perft both in standard and Chess960 cases. Closes official-stockfish/Stockfish#1929 Bench: 3559104
1 parent 9c67b6a commit 7965d33

File tree

4 files changed

+38
-62
lines changed

4 files changed

+38
-62
lines changed

src/movegen.cpp

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -25,48 +25,6 @@
2525

2626
namespace {
2727

28-
template<Color Us, CastlingSide Cs, bool Checks, bool Chess960>
29-
ExtMove* generate_castling(const Position& pos, ExtMove* moveList) {
30-
31-
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
32-
constexpr CastlingRight Cr = Us | Cs;
33-
constexpr bool KingSide = (Cs == KING_SIDE);
34-
35-
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
36-
return moveList;
37-
38-
// After castling, the rook and king final positions are the same in Chess960
39-
// as they would be in standard chess.
40-
Square kfrom = pos.square<KING>(Us);
41-
Square rfrom = pos.castling_rook_square(Cr);
42-
Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1);
43-
Bitboard enemies = pos.pieces(Them);
44-
45-
assert(!pos.checkers());
46-
47-
const Direction step = Chess960 ? kto > kfrom ? WEST : EAST
48-
: KingSide ? WEST : EAST;
49-
50-
for (Square s = kto; s != kfrom; s += step)
51-
if (pos.attackers_to(s) & enemies)
52-
return moveList;
53-
54-
// Because we generate only legal castling moves we need to verify that
55-
// when moving the castling rook we do not discover some hidden checker.
56-
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
57-
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(Them, ROOK, QUEEN)))
58-
return moveList;
59-
60-
Move m = make<CASTLING>(kfrom, rfrom);
61-
62-
if (Checks && !pos.gives_check(m))
63-
return moveList;
64-
65-
*moveList++ = m;
66-
return moveList;
67-
}
68-
69-
7028
template<GenType Type, Direction D>
7129
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
7230

@@ -261,7 +219,9 @@ namespace {
261219
template<Color Us, GenType Type>
262220
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
263221

264-
constexpr bool Checks = Type == QUIET_CHECKS;
222+
constexpr CastlingRight OO = Us | KING_SIDE;
223+
constexpr CastlingRight OOO = Us | QUEEN_SIDE;
224+
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
265225

266226
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
267227
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
@@ -275,19 +235,14 @@ namespace {
275235
Bitboard b = pos.attacks_from<KING>(ksq) & target;
276236
while (b)
277237
*moveList++ = make_move(ksq, pop_lsb(&b));
278-
}
279238

280-
if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
281-
{
282-
if (pos.is_chess960())
283-
{
284-
moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
285-
moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList);
286-
}
287-
else
239+
if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO)))
288240
{
289-
moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
290-
moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
241+
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
242+
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
243+
244+
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
245+
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
291246
}
292247
}
293248

src/position.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ bool Position::legal(Move m) const {
519519

520520
Color us = sideToMove;
521521
Square from = from_sq(m);
522+
Square to = to_sq(m);
522523

523524
assert(color_of(moved_piece(m)) == us);
524525
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
@@ -529,7 +530,6 @@ bool Position::legal(Move m) const {
529530
if (type_of(m) == ENPASSANT)
530531
{
531532
Square ksq = square<KING>(us);
532-
Square to = to_sq(m);
533533
Square capsq = to - pawn_push(us);
534534
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
535535

@@ -542,16 +542,35 @@ bool Position::legal(Move m) const {
542542
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
543543
}
544544

545-
// If the moving piece is a king, check whether the destination
546-
// square is attacked by the opponent. Castling moves are checked
547-
// for legality during move generation.
545+
// Castling moves generation does not check if the castling path is clear of
546+
// enemy attacks, it is delayed at a later time: now!
547+
if (type_of(m) == CASTLING)
548+
{
549+
// After castling, the rook and king final positions are the same in
550+
// Chess960 as they would be in standard chess.
551+
to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
552+
Direction step = to > from ? WEST : EAST;
553+
554+
for (Square s = to; s != from; s += step)
555+
if (attackers_to(s) & pieces(~us))
556+
return false;
557+
558+
// In case of Chess960, verify that when moving the castling rook we do
559+
// not discover some hidden checker.
560+
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
561+
return !chess960
562+
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
563+
}
564+
565+
// If the moving piece is a king, check whether the destination square is
566+
// attacked by the opponent.
548567
if (type_of(piece_on(from)) == KING)
549-
return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
568+
return !(attackers_to(to) & pieces(~us));
550569

551570
// A non-king move is legal if and only if it is not pinned or it
552571
// is moving along the ray towards or away from the king.
553572
return !(blockers_for_king(us) & from)
554-
|| aligned(from, to_sq(m), square<KING>(us));
573+
|| aligned(from, to, square<KING>(us));
555574
}
556575

557576

src/position.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ inline bool Position::can_castle(CastlingRight cr) const {
260260
}
261261

262262
inline int Position::castling_rights(Color c) const {
263-
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
263+
return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING);
264264
}
265265

266266
inline bool Position::castling_impeded(CastlingRight cr) const {

src/types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ enum CastlingRight {
141141
WHITE_OOO = WHITE_OO << 1,
142142
BLACK_OO = WHITE_OO << 2,
143143
BLACK_OOO = WHITE_OO << 3,
144-
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
144+
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
145+
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
146+
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
145147
CASTLING_RIGHT_NB = 16
146148
};
147149

0 commit comments

Comments
 (0)