Only set ep square if ep capture is possible

for positions such as:

position fen rr6/2q2p1k/2P1b1pp/bB2P1n1/R2B2PN/p4P1P/P1Q4K/1R6 b - - 2 38 moves f7f5
position fen 8/p2r1pK1/6p1/1kp1P1P1/2p5/2P5/8/4R3 b - - 0 43 moves f7f5
position fen 4k3/4p3/2b3b1/3P1P2/4K3/8/8/8 b - - moves e7e5

ep is not possible for various reasons. Do not set the ep square and the do not change the zobrist key, as if ep were possible.

This fixes 3-fold detection, which could in rare cases be observed as a warning generated by fastchess for PVs that continued beyond an actual 3-fold.

Also fixes wrong evals for certain moves e.g. a2a1 for
position fen 4k3/1b2p2b/8/3P1P2/4K3/8/8/r7 b - - 0 1 moves e7e5 e4e3 a1a2 e3e4 a2a1 e4f3 a1a2 f3e4

fixes https://github.com/official-stockfish/Stockfish/issues/6138

originally written and tested by rn5f107s2, with small buglet as
https://tests.stockfishchess.org/tests/view/685af90843ce022d15794400

failed STC
LLR: -2.93 (-2.94,2.94) <-1.75,0.25>
Total: 130688 W: 33986 L: 34362 D: 62340
Ptnml(0-2): 414, 13292, 38302, 12928, 408
https://tests.stockfishchess.org/tests/view/688bcb35502b34dd5e7111c9

passed LTC
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 99018 W: 25402 L: 25273 D: 48343
Ptnml(0-2): 54, 9097, 31089, 9204, 65
https://tests.stockfishchess.org/tests/view/688c613c502b34dd5e7112d3

https://github.com/official-stockfish/Stockfish/pull/6211

Bench: 2946135

Co-authored-by: Joost VandeVondele <Joost.VandeVondele@gmail.com>
This commit is contained in:
rn5f107s2
2025-08-04 15:16:21 +02:00
committed by Joost VandeVondele
co-authored by Joost VandeVondele
parent 377a3a5269
commit 94175524b1
2 changed files with 63 additions and 16 deletions
+1 -1
View File
@@ -903,7 +903,7 @@ help:
echo "Supported archs:" && \
echo "" && \
echo "native > select the best architecture for the host processor (default)" && \
echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lane or AMD Zen 4" && \
echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lake or AMD Zen 4" && \
echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \
echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \
echo "x86-64-avx512 > x86 64-bit with avx512 support" && \
+62 -15
View File
@@ -717,6 +717,8 @@ DirtyPiece Position::do_move(Move m,
Piece pc = piece_on(from);
Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
bool checkEP = false;
DirtyPiece dp;
dp.pc = pc;
dp.from = from;
@@ -804,21 +806,14 @@ DirtyPiece Position::do_move(Move m,
// Move the piece. The tricky Chess960 castling is handled earlier
if (m.type_of() != CASTLING)
{
move_piece(from, to);
}
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
{
// Set en passant square if the moved pawn can be captured
if ((int(to) ^ int(from)) == 16
&& (attacks_bb<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
// Check later if the en passant square needs to be set
if ((int(to) ^ int(from)) == 16)
checkEP = true;
else if (m.type_of() == PROMOTION)
{
@@ -863,11 +858,6 @@ DirtyPiece Position::do_move(Move m,
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
}
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// Set capture piece
st->capturedPiece = captured;
@@ -879,6 +869,63 @@ DirtyPiece Position::do_move(Move m,
// Update king attacks used for fast check detection
set_check_info();
// Accurate e.p. info is needed for correct zobrist key generation and 3-fold checking
while (checkEP)
{
auto updateEpSquare = [&] {
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
};
Bitboard pawns = attacks_bb<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN);
// If there are no pawns attacking the ep square, ep is not possible
if (!pawns)
break;
// If there are checkers other than the to be captured pawn, ep is never legal
if (checkers() & ~square_bb(to))
break;
if (more_than_one(pawns))
{
// If there are two pawns potentially being abled to capture and at least one
// is not pinned, ep is legal as there are no horizontal exposed checks
if (!more_than_one(blockers_for_king(them) & pawns))
{
updateEpSquare();
break;
}
// If there is no pawn on our king's file, and thus both pawns are pinned
// by bishops, ep is not legal as the king square must be in front of the to square.
// And because the ep square and the king are not on a common diagonal, either ep capture
// would expose the king to a check from one of the bishops
if (!(file_bb(square<KING>(them)) & pawns))
break;
// Otherwise remove the pawn on the king file, as an ep capture by it can never be legal and the
// check below relies on there only being one pawn
pawns &= ~file_bb(square<KING>(them));
}
Square ksq = square<KING>(them);
Square capsq = to;
Bitboard occupied = (pieces() ^ lsb(pawns) ^ capsq) | (to - pawn_push(us));
// If our king is not attacked after making the move, ep is legal.
if (!(attacks_bb<ROOK>(ksq, occupied) & pieces(us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(us, QUEEN, BISHOP)))
updateEpSquare();
break;
}
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero
// if the position was not repeated.