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
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);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
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];
// Reset rule 50 draw counter
if (end >= 4)
{
StateInfo* stp = st->previous->previous;
- for (int i=4; i <= end; i += 2)
+ for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
if (stp->key == st->key)
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) {
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
- // any pinners are on their original square.
+ // 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.
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");
}