diff --git a/src/Makefile b/src/Makefile index 047a9e71c..cec623f52 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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" && \ diff --git a/src/position.cpp b/src/position.cpp index 5e2c27822..4ac1369d4 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(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(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(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(them)); + } + + Square ksq = square(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(ksq, occupied) & pieces(us, QUEEN, ROOK)) + && !(attacks_bb(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.