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-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
using std::string;
-namespace PSQT {
- extern Score psq[PIECE_NB][SQUARE_NB];
-}
-
namespace Zobrist {
Key psq[PIECE_NB][SQUARE_NB];
Zobrist::noPawns = rng.rand<Key>();
// Prepare the cuckoo tables
+ std::memset(cuckoo, 0, sizeof(cuckoo));
+ std::memset(cuckooMove, 0, sizeof(cuckooMove));
int count = 0;
for (Piece pc : Pieces)
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
std::swap(cuckoo[i], key);
std::swap(cuckooMove[i], move);
- if (move == 0) // Arrived at empty slot ?
+ if (move == MOVE_NONE) // Arrived at empty slot?
break;
i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
}
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
- for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
- if (s != kfrom && s != rfrom)
- castlingPath[cr] |= s;
-
- for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
- if (s != kfrom && s != rfrom)
- castlingPath[cr] |= s;
+ castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
+ & ~(square_bb(kfrom) | rfrom);
}
si->key = si->materialKey = 0;
si->pawnKey = Zobrist::noPawns;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
- si->psq = SCORE_ZERO;
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
set_check_info(si);
Square s = pop_lsb(&b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s];
- si->psq += PSQT::psq[pc][s];
}
if (si->epSquare != SQ_NONE)
ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO))
- ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K');
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
if (can_castle(WHITE_OOO))
- ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
if (can_castle(BLACK_OO))
- ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k');
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
if (can_castle(BLACK_OOO))
- ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
- if (!can_castle(WHITE) && !can_castle(BLACK))
+ if (!can_castle(ANY_CASTLING))
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
Bitboard blockers = 0;
pinners = 0;
- // Snipers are sliders that attack 's' when a piece is removed
+ // Snipers are sliders that attack 's' when a piece and other snipers are removed
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
+ Bitboard occupancy = pieces() & ~snipers;
while (snipers)
{
Square sniperSq = pop_lsb(&snipers);
- Bitboard b = between_bb(s, sniperSq) & pieces();
+ Bitboard b = between_bb(s, sniperSq) & occupancy;
if (b && !more_than_one(b))
{
Color us = sideToMove;
Square from = from_sq(m);
+ Square to = to_sq(m);
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
if (type_of(m) == ENPASSANT)
{
Square ksq = square<KING>(us);
- Square to = to_sq(m);
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
}
- // If the moving piece is a king, check whether the destination
- // square is attacked by the opponent. Castling moves are checked
- // for legality during move generation.
+ // Castling moves generation does not check if the castling path is clear of
+ // enemy attacks, it is delayed at a later time: now!
+ if (type_of(m) == CASTLING)
+ {
+ // After castling, the rook and king final positions are the same in
+ // Chess960 as they would be in standard chess.
+ to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
+ Direction step = to > from ? WEST : EAST;
+
+ for (Square s = to; s != from; s += step)
+ 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.
+ // 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));
+ }
+
+ // If the moving piece is a king, check whether the destination square is
+ // attacked by the opponent.
if (type_of(piece_on(from)) == KING)
- return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
+ return !(attackers_to(to) & pieces(~us));
// 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_sq(m), square<KING>(us));
+ || aligned(from, to, square<KING>(us));
}
{
// We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank.
- if (rank_of(to) == relative_rank(us, RANK_8))
+ if ((Rank8BB | Rank1BB) & to)
return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
Square rfrom, rto;
do_castling<true>(us, from, to, rfrom, rto);
- st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom];
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
captured = NO_PIECE;
}
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
prefetch(thisThread->materialTable[st->materialKey]);
- // Update incremental scores
- st->psq -= PSQT::psq[captured][capsq];
-
// Reset rule 50 counter
st->rule50 = 0;
}
st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
^ Zobrist::psq[pc][pieceCount[pc]];
- // Update incremental score
- st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to];
-
// Update material
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
// Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
- prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
st->rule50 = 0;
}
- // Update incremental scores
- st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
-
// Set capture piece
st->capturedPiece = captured;
// Update king attacks used for fast check detection
set_check_info(st);
+ // 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.
+ st->repetition = 0;
+ int end = std::min(st->rule50, st->pliesFromNull);
+ if (end >= 4)
+ {
+ StateInfo* stp = st->previous->previous;
+ for (int i=4; i <= end; i += 2)
+ {
+ stp = stp->previous->previous;
+ if (stp->key == st->key)
+ {
+ st->repetition = stp->repetition ? -i : i;
+ break;
+ }
+ }
+ }
+
assert(pos_is_ok());
}
set_check_info(st);
+ st->repetition = 0;
+
assert(pos_is_ok());
}
stmAttackers = attackers & pieces(stm);
// Don't allow pinned pieces to attack (except the king) as long as
- // all pinners are on their original square.
- if (!(st->pinners[~stm] & ~occupied))
+ // any pinners are on their original square.
+ if (st->pinners[~stm] & occupied)
stmAttackers &= ~st->blockersForKing[stm];
// If stm has no more attackers then give up: stm loses
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
- int end = std::min(st->rule50, st->pliesFromNull);
-
- if (end < 4)
- return false;
-
- StateInfo* stp = st->previous->previous;
- int cnt = 0;
-
- for (int i = 4; i <= end; i += 2)
- {
- stp = stp->previous->previous;
-
- // Return a draw score if a position repeats once earlier but strictly
- // after the root, or repeats twice before or at the root.
- if ( stp->key == st->key
- && ++cnt + (ply > i) == 2)
- return true;
- }
+ // Return a draw score if a position repeats once earlier but strictly
+ // after the root, or repeats twice before or at the root.
+ if (st->repetition && st->repetition < ply)
+ return true;
return false;
}
bool Position::has_repeated() const {
StateInfo* stc = st;
- while (true)
+ int end = std::min(st->rule50, st->pliesFromNull);
+ while (end-- >= 4)
{
- int i = 4, end = std::min(stc->rule50, stc->pliesFromNull);
-
- if (end < i)
- return false;
-
- StateInfo* stp = stc->previous->previous;
-
- do {
- stp = stp->previous->previous;
-
- if (stp->key == stc->key)
- return true;
-
- i += 2;
- } while (i <= end);
+ if (stc->repetition)
+ return true;
stc = stc->previous;
}
+ return false;
}
if (ply > i)
return true;
+ // For nodes before or at the root, check that the move is a repetition one
+ // rather than a move to the current position
+ if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
+ continue;
+
// For repetitions before or at the root, require one more
- StateInfo* next_stp = stp;
- for (int k = i + 2; k <= end; k += 2)
- {
- next_stp = next_stp->previous->previous;
- if (next_stp->key == stp->key)
- return true;
- }
+ if (stp->repetition)
+ return true;
}
}
}