/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
+ Copyright (C) 2004-2023 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
#include <cstring> // For std::memset, std::memcmp
#include <iomanip>
#include <sstream>
+#include <string_view>
#include "bitboard.h"
#include "misc.h"
namespace {
-const string PieceToChar(" PNBRQK pnbrqk");
+constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
// Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition"
// situations. Description of the algorithm in the following paper:
-// https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
+// http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
// First and second hash functions for indexing the cuckoo tables
inline int H1(Key h) { return h & 0x1fff; }
// Prepare the cuckoo tables
std::memset(cuckoo, 0, sizeof(cuckoo));
std::memset(cuckooMove, 0, sizeof(cuckooMove));
- int count = 0;
+ [[maybe_unused]] int count = 0;
for (Piece pc : Pieces)
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
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))))
- && ( file_of(square<KING>(sideToMove)) == file_of(st->epSquare)
- || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove))));
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
}
- // 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
+ if (!enpassant)
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
chess960 = isChess960;
thisThread = th;
- st->accumulator.state[WHITE] = Eval::NNUE::INIT;
- st->accumulator.state[BLACK] = Eval::NNUE::INIT;
+ set_state(st);
assert(pos_is_ok());
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
- // st->previous->blockersForKing consider capsq as empty.
- // If pinned, it has to move along the king ray.
+ // 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) == EN_PASSANT)
- return !(st->previous->blockersForKing[sideToMove] & from)
- || aligned(from, to, square<KING>(us));
+ {
+ 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));
+ }
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
case PROMOTION:
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
- // 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.
+ // 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 EN_PASSANT:
- return st->previous->checkersBB
- || ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
- && st->previous->blockersForKing[~sideToMove] & from);
+ {
+ Square capsq = make_square(file_of(to), rank_of(from));
+ Bitboard b = (pieces() ^ from ^ capsq) | to;
+ return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
+ | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
+ }
default: //CASTLING
{
// Castling is encoded as 'king captures the rook'
++st->pliesFromNull;
// Used by NNUE
- st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
- st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
+ st->accumulator.computed[WHITE] = false;
+ st->accumulator.computed[BLACK] = false;
auto& dp = st->dirtyPiece;
dp.dirty_num = 1;
st->dirtyPiece.dirty_num = 0;
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
- st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
- st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
+ st->accumulator.computed[WHITE] = false;
+ st->accumulator.computed[BLACK] = false;
if (st->epSquare != SQ_NONE)
{
}
st->key ^= Zobrist::side;
+ ++st->rule50;
prefetch(TT.first_entry(key()));
- ++st->rule50;
st->pliesFromNull = 0;
sideToMove = ~sideToMove;
if (captured)
k ^= Zobrist::psq[captured][to];
- return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
+
+ return (captured || type_of(pc) == PAWN)
+ ? k : adjust_key50<true>(k);
}
if (swap <= 0)
return true;
+ assert(color_of(piece_on(from)) == sideToMove);
Bitboard occupied = pieces() ^ from ^ to;
- Color stm = color_of(piece_on(from));
+ Color stm = sideToMove;
Bitboard attackers = attackers_to(to, occupied);
Bitboard stmAttackers, bb;
int res = 1;
// Don't allow pinned pieces to attack as long as there are
// pinners on their original square.
if (pinners(~stm) & occupied)
+ {
stmAttackers &= ~blockers_for_king(stm);
- if (!stmAttackers)
- break;
+ if (!stmAttackers)
+ break;
+ }
res ^= 1;