/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
4) En passant target square (in algebraic notation). If there's no en passant
target square, this is "-". If a pawn has just made a 2-square move, this
- is the position "behind" the pawn. This is recorded only if there is a pawn
- in position to make an en passant capture, and if there really is a pawn
- that might have advanced two squares.
+ is the position "behind" the pawn. Following X-FEN standard, this is recorded only
+ if there is a pawn in position to make an en passant capture, and if there really
+ is a pawn that might have advanced two squares.
5) Halfmove clock. This is the number of halfmoves since the last pawn advance
or capture. This is used to determine if a draw can be claimed under the
else if (token == '/')
sq += 2 * SOUTH;
- else if ((idx = PieceToChar.find(token)) != string::npos)
- {
+ else if ((idx = PieceToChar.find(token)) != string::npos) {
put_piece(Piece(idx), sq);
++sq;
}
set_castling_right(c, rsq);
}
- // 4. En passant square. Ignore if no pawn capture is possible
+ // 4. En passant square.
+ // Ignore if square is invalid or not on side to move relative rank 6.
+ bool enpassant = false;
+
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == '3' || row == '6')))
+ && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
{
st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
- if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
- || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
- st->epSquare = SQ_NONE;
+ // En passant square will be considered only if
+ // 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
+ 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))));
}
- else
+
+ if (!enpassant)
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
thisThread = th;
set_state(st);
- assert(pos_is_ok());
-
return *this;
}
++st->rule50;
++st->pliesFromNull;
+ // Used by NNUE
+ st->accumulator.computed_accumulation = false;
+ auto& dp = st->dirtyPiece;
+ dp.dirty_num = 1;
+
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
else
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
+ if (Eval::useNNUE)
+ {
+ dp.dirty_num = 2; // 1 piece moved, 1 piece captured
+ dp.piece[1] = captured;
+ dp.from[1] = capsq;
+ dp.to[1] = SQ_NONE;
+ }
+
// Update board and piece lists
remove_piece(capsq);
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
+ {
+ if (Eval::useNNUE)
+ {
+ dp.piece[0] = pc;
+ dp.from[0] = from;
+ dp.to[0] = to;
+ }
+
move_piece(from, to);
+ }
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
remove_piece(to);
put_piece(promotion, to);
+ if (Eval::useNNUE)
+ {
+ // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
+ dp.to[0] = SQ_NONE;
+ dp.piece[dp.dirty_num] = promotion;
+ dp.from[dp.dirty_num] = SQ_NONE;
+ dp.to[dp.dirty_num] = to;
+ dp.dirty_num++;
+ }
+
// Update hash keys
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
st->pawnKey ^= Zobrist::psq[pc][to];
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
+ if (Do && Eval::useNNUE)
+ {
+ auto& dp = st->dirtyPiece;
+ dp.piece[0] = make_piece(us, KING);
+ dp.from[0] = from;
+ dp.to[0] = to;
+ dp.piece[1] = make_piece(us, ROOK);
+ dp.from[1] = rfrom;
+ dp.to[1] = rto;
+ dp.dirty_num = 2;
+ }
+
// Remove both pieces first since squares could overlap in Chess960
remove_piece(Do ? from : to);
remove_piece(Do ? rfrom : rto);
assert(!checkers());
assert(&newSt != st);
- std::memcpy(&newSt, st, sizeof(StateInfo));
+ if (Eval::useNNUE)
+ {
+ std::memcpy(&newSt, st, sizeof(StateInfo));
+ }
+ else
+ std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
+
newSt.previous = st;
st = &newSt;
// Don't allow pinned pieces to attack (except the king) as long as
// there are pinners on their original square.
- if (st->pinners[~stm] & occupied)
- stmAttackers &= ~st->blockersForKing[stm];
+ if (pinners(~stm) & occupied)
+ stmAttackers &= ~blockers_for_king(stm);
if (!stmAttackers)
break;