/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
+ Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
set_castling_right(c, rsq);
}
+ set_state(st);
+
// 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6.
bool enpassant = false;
// a) side to move have a pawn threatening epSquare
// b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare
+ // d) enemy pawn didn't block a check of its own color by moving forward
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
- && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))))
+ && ( file_of(square<KING>(sideToMove)) == file_of(st->epSquare)
+ || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove))));
}
- if (!enpassant)
+ // It's necessary for st->previous to be intialized in this way because legality check relies on its existence
+ if (enpassant) {
+ st->previous = new StateInfo();
+ remove_piece(st->epSquare - pawn_push(sideToMove));
+ st->previous->checkersBB = attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove);
+ st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), st->previous->pinners[BLACK]);
+ st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), st->previous->pinners[WHITE]);
+ put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove));
+ }
+ else
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
chess960 = isChess960;
thisThread = th;
- set_state(st);
st->accumulator.state[WHITE] = Eval::NNUE::INIT;
st->accumulator.state[BLACK] = Eval::NNUE::INIT;
/// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
-const string Position::fen() const {
+string Position::fen() const {
int emptyCnt;
std::ostringstream ss;
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
- // 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 capsq = to - pawn_push(us);
- Bitboard occupied = (pieces() ^ from ^ capsq) | to;
-
- assert(to == ep_square());
- assert(moved_piece(m) == make_piece(us, PAWN));
- 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));
- }
+ // st->previous->blockersForKing consider capsq as empty.
+ // If pinned, it has to move along the king ray.
+ if (type_of(m) == EN_PASSANT)
+ return !(st->previous->blockersForKing[sideToMove] & from)
+ || aligned(from, to, square<KING>(us));
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
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.
+ // In case of Chess960, verify if the Rook blocks some checks
// 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));
+ return !chess960 || !(blockers_for_king(us) & to_sq(m));
}
// If the moving piece is a king, check whether the destination square is
// 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));
+ return !(blockers_for_king(us) & from)
+ || aligned(from, to, square<KING>(us));
}
Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases
+ // yet we skip the legality check of MoveList<LEGAL>().
if (type_of(m) != NORMAL)
- return MoveList<LEGAL>(*this).contains(m);
+ return checkers() ? MoveList< EVASIONS>(*this).contains(m)
+ : MoveList<NON_EVASIONS>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
case PROMOTION:
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
- // En passant capture with check? We have already handled the case
- // of direct checks and ordinary discovered check, so the only case we
- // need to handle is the unusual case of a discovered check through
- // the captured pawn.
- case ENPASSANT:
- {
- Square capsq = make_square(file_of(to), rank_of(from));
- Bitboard b = (pieces() ^ from ^ capsq) | to;
+ // The double-pushed pawn blocked a check? En Passant will remove the blocker.
+ // The only discovery check that wasn't handle is through capsq and fromsq
+ // So the King must be in the same rank as fromsq to consider this possibility.
+ // st->previous->blockersForKing consider capsq as empty.
+ case EN_PASSANT:
+ return st->previous->checkersBB
+ || ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
+ && st->previous->blockersForKing[~sideToMove] & from);
- return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
- | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
- }
- case CASTLING:
+ default: //CASTLING
{
- Square kfrom = from;
- Square rfrom = to; // Castling is encoded as 'king captures the rook'
- Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
- Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
+ // Castling is encoded as 'king captures the rook'
+ Square ksq = square<KING>(~sideToMove);
+ Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
- return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
- && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
+ return (attacks_bb<ROOK>(rto) & ksq)
+ && (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
}
- default:
- assert(false);
- return false;
}
}
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = piece_on(from);
- Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
+ Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
// update non-pawn material.
if (type_of(captured) == PAWN)
{
- if (type_of(m) == ENPASSANT)
+ if (type_of(m) == EN_PASSANT)
{
capsq -= pawn_push(us);
// Update board and piece lists
remove_piece(capsq);
- if (type_of(m) == ENPASSANT)
+ if (type_of(m) == EN_PASSANT)
board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
// 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
+ // Set en passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
{
{
Square capsq = to;
- if (type_of(m) == ENPASSANT)
+ if (type_of(m) == EN_PASSANT)
{
capsq -= pawn_push(us);
}
st->key ^= Zobrist::side;
- prefetch(TT.first_entry(st->key));
+ prefetch(TT.first_entry(key()));
++st->rule50;
st->pliesFromNull = 0;
/// Position::key_after() computes the new hash key after the given move. Needed
/// for speculative prefetch. It doesn't recognize special moves like castling,
-/// en-passant and promotions.
+/// en passant and promotions.
Key Position::key_after(Move m) const {
assert(is_ok(m));
- // Only deal with normal moves, assume others pass a simple see
+ // Only deal with normal moves, assume others pass a simple SEE
if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold;