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-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2015-2020 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
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 };
-
-// min_attacker() is a helper function used by see_ge() to locate the least
-// valuable attacker for the side to move, remove the attacker we just found
-// from the bitboards and scan for new X-ray attacks behind it.
-
-template<int Pt>
-PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
- Bitboard& occupied, Bitboard& attackers) {
-
- Bitboard b = stmAttackers & byTypeBB[Pt];
- if (!b)
- return min_attacker<Pt + 1>(byTypeBB, to, stmAttackers, occupied, attackers);
-
- occupied ^= lsb(b); // Remove the attacker from occupied
-
- // Add any X-ray attack behind the just removed piece. For instance with
- // rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
- // Note that new added attackers can be of any color.
- if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
- attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
-
- if (Pt == ROOK || Pt == QUEEN)
- attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
-
- // X-ray may add already processed pieces because byTypeBB[] is constant: in
- // the rook example, now attackers contains _again_ rook in a7, so remove it.
- attackers &= occupied;
- return (PieceType)Pt;
-}
-
-template<>
-PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
- return KING; // No need to update bitboards: it is the last cycle
-}
-
} // namespace
{
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
}
void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = square<KING>(c);
- CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
- CastlingRight cr = (c | cs);
+ CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
st->castlingRights |= cr;
castlingRightsMask[kfrom] |= cr;
castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom;
- Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
- Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
+ Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
+ Square rto = relative_square(c, cr & 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);
}
Square s = pop_lsb(&b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s];
+
+ if (type_of(pc) == PAWN)
+ si->pawnKey ^= Zobrist::psq[pc][s];
+
+ else if (type_of(pc) != KING)
+ si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
}
if (si->epSquare != SQ_NONE)
si->key ^= Zobrist::castling[si->castlingRights];
- for (Bitboard b = pieces(PAWN); b; )
- {
- Square s = pop_lsb(&b);
- si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
- }
-
for (Piece pc : Pieces)
- {
- if (type_of(pc) != PAWN && type_of(pc) != KING)
- si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
-
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt];
- }
}
Position& Position::set(const string& code, Color c, StateInfo* si) {
- assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak
- code.substr(0, code.find('K', 1)) }; // Strong
+ code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
+
+ assert(sides[0].length() > 0 && sides[0].length() < 8);
+ assert(sides[1].length() > 0 && sides[1].length() < 8);
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
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(ANY_CASTLING))
ss << '-';
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 to = to_sq(m);
// Is there a direct check?
- if (st->checkSquares[type_of(piece_on(from))] & to)
+ if (check_squares(type_of(piece_on(from))) & to)
return true;
// Is there a discovered check?
- if ( (st->blockersForKing[~sideToMove] & from)
+ if ( (blockers_for_king(~sideToMove) & from)
&& !aligned(from, to, square<KING>(~sideToMove)))
return true;
case CASTLING:
{
Square kfrom = from;
- Square rfrom = to; // Castling is encoded as 'King captures the rook'
+ 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);
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(to) == NO_PIECE);
assert(piece_on(capsq) == make_piece(them, PAWN));
-
- board[capsq] = NO_PIECE; // Not done by remove_piece()
}
st->pawnKey ^= Zobrist::psq[captured][capsq];
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
// Update board and piece lists
- remove_piece(captured, capsq);
+ remove_piece(capsq);
+
+ if (type_of(m) == ENPASSANT)
+ board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
- move_piece(pc, from, to);
+ move_piece(from, to);
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
assert(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
- remove_piece(pc, to);
+ remove_piece(to);
put_piece(promotion, to);
// Update hash keys
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
- // Update pawn hash key and prefetch access to pawnsTable
+ // Update pawn hash key
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
- prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
st->rule50 = 0;
// 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());
}
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
- remove_piece(pc, to);
+ remove_piece(to);
pc = make_piece(us, PAWN);
put_piece(pc, to);
}
}
else
{
- move_piece(pc, to, from); // Put the piece back at the source square
+ move_piece(to, from); // Put the piece back at the source square
if (st->capturedPiece)
{
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
// Remove both pieces first since squares could overlap in Chess960
- remove_piece(make_piece(us, KING), Do ? from : to);
- remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
- board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
+ remove_piece(Do ? from : to);
+ remove_piece(Do ? rfrom : rto);
+ board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us
put_piece(make_piece(us, KING), Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
}
-/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
+/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
/// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt) {
set_check_info(st);
+ st->repetition = 0;
+
assert(pos_is_ok());
}
if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold;
- Bitboard stmAttackers;
Square from = from_sq(m), to = to_sq(m);
- PieceType nextVictim = type_of(piece_on(from));
- Color us = color_of(piece_on(from));
- Color stm = ~us; // First consider opponent's move
- Value balance; // Values of the pieces taken by us minus opponent's ones
- // The opponent may be able to recapture so this is the best result
- // we can hope for.
- balance = PieceValue[MG][piece_on(to)] - threshold;
-
- if (balance < VALUE_ZERO)
+ int swap = PieceValue[MG][piece_on(to)] - threshold;
+ if (swap < 0)
return false;
- // Now assume the worst possible result: that the opponent can
- // capture our piece for free.
- balance -= PieceValue[MG][nextVictim];
-
- // If it is enough (like in PxQ) then return immediately. Note that
- // in case nextVictim == KING we always return here, this is ok
- // if the given move is legal.
- if (balance >= VALUE_ZERO)
+ swap = PieceValue[MG][piece_on(from)] - swap;
+ if (swap <= 0)
return true;
- // Find all attackers to the destination square, with the moving piece
- // removed, but possibly an X-ray attacker added behind it.
Bitboard occupied = pieces() ^ from ^ to;
- Bitboard attackers = attackers_to(to, occupied) & occupied;
+ Color stm = color_of(piece_on(from));
+ Bitboard attackers = attackers_to(to, occupied);
+ Bitboard stmAttackers, bb;
+ int res = 1;
while (true)
{
- stmAttackers = attackers & pieces(stm);
+ stm = ~stm;
+ attackers &= occupied;
+
+ // If stm has no more attackers then give up: stm loses
+ if (!(stmAttackers = attackers & pieces(stm)))
+ break;
// 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))
+ // there are pinners 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 (!stmAttackers)
break;
+ res ^= 1;
+
// Locate and remove the next least valuable attacker, and add to
- // the bitboard 'attackers' the possibly X-ray attackers behind it.
- nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
+ // the bitboard 'attackers' any X-ray attackers behind it.
+ if ((bb = stmAttackers & pieces(PAWN)))
+ {
+ if ((swap = PawnValueMg - swap) < res)
+ break;
- stm = ~stm; // Switch side to move
+ occupied ^= lsb(bb);
+ attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
+ }
- // Negamax the balance with alpha = balance, beta = balance+1 and
- // add nextVictim's value.
- //
- // (balance, balance+1) -> (-balance-1, -balance)
- //
- assert(balance < VALUE_ZERO);
+ else if ((bb = stmAttackers & pieces(KNIGHT)))
+ {
+ if ((swap = KnightValueMg - swap) < res)
+ break;
- balance = -balance - 1 - PieceValue[MG][nextVictim];
+ occupied ^= lsb(bb);
+ }
- // If balance is still non-negative after giving away nextVictim then we
- // win. The only thing to be careful about it is that we should revert
- // stm if we captured with the king when the opponent still has attackers.
- if (balance >= VALUE_ZERO)
+ else if ((bb = stmAttackers & pieces(BISHOP)))
{
- if (nextVictim == KING && (attackers & pieces(stm)))
- stm = ~stm;
- break;
+ if ((swap = BishopValueMg - swap) < res)
+ break;
+
+ occupied ^= lsb(bb);
+ attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
- assert(nextVictim != KING);
+
+ else if ((bb = stmAttackers & pieces(ROOK)))
+ {
+ if ((swap = RookValueMg - swap) < res)
+ break;
+
+ occupied ^= lsb(bb);
+ attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
+ }
+
+ else if ((bb = stmAttackers & pieces(QUEEN)))
+ {
+ if ((swap = QueenValueMg - swap) < res)
+ break;
+
+ occupied ^= lsb(bb);
+ attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
+ | (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
+ }
+
+ else // KING
+ // If we "capture" with the king but opponent still has attackers,
+ // reverse the result.
+ return (attackers & ~pieces(stm)) ? res ^ 1 : res;
}
- return us != stm; // We break the above loop when stm loses
-}
+ return bool(res);
+}
/// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates.
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 false;
+ // Return a draw score if a position repeats once earlier but strictly
+ // after the root, or repeats twice before or at the root.
+ return st->repetition && st->repetition < ply;
}
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 (!(between_bb(s1, s2) & pieces()))
{
- // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
- // location. We select the legal one by reversing the move variable if necessary.
- if (empty(s1))
- move = make_move(s2, s1);
-
if (ply > i)
return true;
+ // For nodes before or at the root, check that the move is a
+ // repetition rather than a move to the current position.
+ // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
+ // the same location, so we have to select which square to check.
+ 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;
}
}
}
assert(0 && "pos_is_ok: Index");
}
- for (Color c = WHITE; c <= BLACK; ++c)
- for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
+ for (Color c : { WHITE, BLACK })
+ for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{
- if (!can_castle(c | s))
+ if (!can_castle(cr))
continue;
- if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
- || castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
- || (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
+ if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
+ || castlingRightsMask[castlingRookSquare[cr]] != cr
+ || (castlingRightsMask[square<KING>(c)] & cr) != cr)
assert(0 && "pos_is_ok: Castling");
}