}
+/// Position::clear() erases the position object to a pristine state, with an
+/// empty board, white to move, and no castling rights.
+
+void Position::clear() {
+
+ std::memset(this, 0, sizeof(Position));
+ startState.epSquare = SQ_NONE;
+ st = &startState;
+
+ for (int i = 0; i < PIECE_TYPE_NB; ++i)
+ for (int j = 0; j < 16; ++j)
+ pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
+}
+
+
/// Position::set() initializes the position object with the given FEN string.
/// This function is not very robust - make sure that input FENs are correct,
/// this is assumed to be the responsibility of the GUI.
// handle also common incorrect FEN with fullmove = 0.
gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
- st->key = compute_key();
- st->pawnKey = compute_pawn_key();
- st->materialKey = compute_material_key();
- st->psq = compute_psq_score();
- st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
- st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
- st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
chess960 = isChess960;
thisThread = th;
+ set_state(st);
assert(pos_is_ok());
}
}
+/// Position::set_state() computes the hash keys of the position, and other
+/// data that once computed is updated incrementally as moves are made.
+/// The function is only used when a new position is set up, and to verify
+/// the correctness of the StateInfo data when running in debug mode.
+
+void Position::set_state(StateInfo* si) const {
+
+ si->key = si->pawnKey = si->materialKey = 0;
+ si->npMaterial[WHITE] = si->npMaterial[BLACK] = VALUE_ZERO;
+ si->psq = SCORE_ZERO;
+
+ si->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
+
+ for (Bitboard b = pieces(); b; )
+ {
+ Square s = pop_lsb(&b);
+ Piece pc = piece_on(s);
+ si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s];
+ si->psq += psq[color_of(pc)][type_of(pc)][s];
+ }
+
+ if (ep_square() != SQ_NONE)
+ si->key ^= Zobrist::enpassant[file_of(ep_square())];
+
+ if (sideToMove == BLACK)
+ si->key ^= Zobrist::side;
+
+ si->key ^= Zobrist::castling[st->castlingRights];
+
+ for (Bitboard b = pieces(PAWN); b; )
+ {
+ Square s = pop_lsb(&b);
+ si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
+ }
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
+ si->materialKey ^= Zobrist::psq[c][pt][cnt];
+
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
+ si->npMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
+}
+
+
/// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
// the move is made.
if (type_of(m) == ENPASSANT)
{
- Color them = ~us;
- Square to = to_sq(m);
- Square capsq = to + pawn_push(them);
Square ksq = king_square(us);
- Bitboard b = (pieces() ^ from ^ capsq) | to;
+ Square to = to_sq(m);
+ Square capsq = to - pawn_push(us);
+ Bitboard occ = (pieces() ^ from ^ capsq) | to;
assert(to == ep_square());
assert(moved_piece(m) == make_piece(us, PAWN));
- assert(piece_on(capsq) == make_piece(them, PAWN));
+ assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
- return !(attacks_bb< ROOK>(ksq, b) & pieces(them, QUEEN, ROOK))
- && !(attacks_bb<BISHOP>(ksq, b) & pieces(them, QUEEN, BISHOP));
+ return !(attacks_bb< ROOK>(ksq, occ) & pieces(~us, QUEEN, ROOK))
+ && !(attacks_bb<BISHOP>(ksq, occ) & pieces(~us, QUEEN, BISHOP));
}
// If the moving piece is a king, check whether the destination
&& !aligned(from, to, ci.ksq))
return true;
- // Can we skip the ugly special cases?
- if (type_of(m) == NORMAL)
- return false;
-
switch (type_of(m))
{
+ case NORMAL:
+ return false;
+
case PROMOTION:
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ci.ksq;
{
assert(pc == make_piece(us, KING));
- bool kingSide = to > from;
- Square rfrom = to; // Castling is encoded as "king captures friendly rook"
- Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
- to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
- captured = NO_PIECE_TYPE;
-
- do_castling(from, to, rfrom, rto);
+ Square rfrom, rto;
+ do_castling<true>(from, to, rfrom, rto);
+ captured = NO_PIECE_TYPE;
st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
}
// Update the key with the final value
st->key = k;
- // Update checkers bitboard: piece must be already moved
+ // Update checkers bitboard: piece must be already moved due to attacks_from()
st->checkersBB = 0;
if (moveIsCheck)
st->checkersBB |= to;
// Discovered checks
- if (ci.dcCandidates && (ci.dcCandidates & from))
+ if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
{
if (pt != ROOK)
st->checkersBB |= attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN, ROOK);
sideToMove = ~sideToMove;
Color us = sideToMove;
- Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(piece_on(to));
- PieceType captured = st->capturedType;
assert(empty(from) || type_of(m) == CASTLING);
- assert(captured != KING);
+ assert(st->capturedType != KING);
if (type_of(m) == PROMOTION)
{
- PieceType promotion = promotion_type(m);
-
- assert(promotion == pt);
+ assert(pt == promotion_type(m));
assert(relative_rank(us, to) == RANK_8);
- assert(promotion >= KNIGHT && promotion <= QUEEN);
+ assert(promotion_type(m) >= KNIGHT && promotion_type(m) <= QUEEN);
- remove_piece(to, us, promotion);
+ remove_piece(to, us, promotion_type(m));
put_piece(to, us, PAWN);
pt = PAWN;
}
if (type_of(m) == CASTLING)
{
- bool kingSide = to > from;
- Square rfrom = to; // Castling is encoded as "king captures friendly rook"
- Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
- to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
- captured = NO_PIECE_TYPE;
- pt = KING;
- do_castling(to, from, rto, rfrom);
+ Square rfrom, rto;
+ do_castling<false>(from, to, rfrom, rto);
}
else
- move_piece(to, from, us, pt); // Put the piece back at the source square
-
- if (captured)
{
- Square capsq = to;
+ move_piece(to, from, us, pt); // Put the piece back at the source square
- if (type_of(m) == ENPASSANT)
+ if (st->capturedType)
{
- capsq -= pawn_push(us);
+ Square capsq = to;
- assert(pt == PAWN);
- assert(to == st->previous->epSquare);
- assert(relative_rank(us, to) == RANK_6);
- assert(piece_on(capsq) == NO_PIECE);
- }
+ if (type_of(m) == ENPASSANT)
+ {
+ capsq -= pawn_push(us);
+
+ assert(pt == PAWN);
+ assert(to == st->previous->epSquare);
+ assert(relative_rank(us, to) == RANK_6);
+ assert(piece_on(capsq) == NO_PIECE);
+ }
- put_piece(capsq, them, captured); // Restore the captured piece
+ put_piece(capsq, ~us, st->capturedType); // Restore the captured piece
+ }
}
// Finally point our state pointer back to the previous state
/// Position::do_castling() is a helper used to do/undo a castling move. This
/// is a bit tricky, especially in Chess960.
+template<bool Do>
+void Position::do_castling(Square from, Square& to, Square& rfrom, Square& rto) {
-void Position::do_castling(Square kfrom, Square kto, Square rfrom, Square rto) {
+ bool kingSide = to > from;
+ rfrom = to; // Castling is encoded as "king captures friendly rook"
+ rto = relative_square(sideToMove, kingSide ? SQ_F1 : SQ_D1);
+ to = relative_square(sideToMove, kingSide ? SQ_G1 : SQ_C1);
// Remove both pieces first since squares could overlap in Chess960
- remove_piece(kfrom, sideToMove, KING);
- remove_piece(rfrom, sideToMove, ROOK);
- board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us
- put_piece(kto, sideToMove, KING);
- put_piece(rto, sideToMove, ROOK);
+ remove_piece(Do ? from : to, sideToMove, KING);
+ remove_piece(Do ? rfrom : rto, sideToMove, ROOK);
+ board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
+ put_piece(Do ? to : from, sideToMove, KING);
+ put_piece(Do ? rto : rfrom, sideToMove, ROOK);
}
}
-/// Position::clear() erases the position object to a pristine state, with an
-/// empty board, white to move, and no castling rights.
-
-void Position::clear() {
-
- std::memset(this, 0, sizeof(Position));
- startState.epSquare = SQ_NONE;
- st = &startState;
-
- for (int i = 0; i < PIECE_TYPE_NB; ++i)
- for (int j = 0; j < 16; ++j)
- pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
-}
-
-
-/// Position::compute_key() computes the hash key of the position. The hash
-/// key is usually updated incrementally as moves are made and unmade. The
-/// compute_key() function is only used when a new position is set up, and
-/// to verify the correctness of the hash key when running in debug mode.
-
-Key Position::compute_key() const {
-
- Key k = Zobrist::castling[st->castlingRights];
-
- for (Bitboard b = pieces(); b; )
- {
- Square s = pop_lsb(&b);
- k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s];
- }
-
- if (ep_square() != SQ_NONE)
- k ^= Zobrist::enpassant[file_of(ep_square())];
-
- if (sideToMove == BLACK)
- k ^= Zobrist::side;
-
- return k;
-}
-
-
-/// Position::compute_pawn_key() computes the hash key of the position. The
-/// hash key is usually updated incrementally as moves are made and unmade.
-/// The compute_pawn_key() function is only used when a new position is set
-/// up, and to verify the correctness of the pawn hash key when running in
-/// debug mode.
-
-Key Position::compute_pawn_key() const {
-
- Key k = 0;
-
- for (Bitboard b = pieces(PAWN); b; )
- {
- Square s = pop_lsb(&b);
- k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
- }
-
- return k;
-}
-
-
-/// Position::compute_material_key() computes the hash key of the position.
-/// The hash key is usually updated incrementally as moves are made and unmade.
-/// The compute_material_key() function is only used when a new position is set
-/// up, and to verify the correctness of the material hash key when running in
-/// debug mode.
-
-Key Position::compute_material_key() const {
-
- Key k = 0;
-
- for (Color c = WHITE; c <= BLACK; ++c)
- for (PieceType pt = PAWN; pt <= KING; ++pt)
- for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
- k ^= Zobrist::psq[c][pt][cnt];
-
- return k;
-}
-
-
-/// Position::compute_psq_score() computes the incremental scores for the middlegame
-/// and the endgame. These functions are used to initialize the incremental scores
-/// when a new position is set up, and to verify that the scores are correctly
-/// updated by do_move and undo_move when the program is running in debug mode.
-
-Score Position::compute_psq_score() const {
-
- Score score = SCORE_ZERO;
-
- for (Bitboard b = pieces(); b; )
- {
- Square s = pop_lsb(&b);
- Piece pc = piece_on(s);
- score += psq[color_of(pc)][type_of(pc)][s];
- }
-
- return score;
-}
-
-
-/// Position::compute_non_pawn_material() computes the total non-pawn middlegame
-/// material value for the given side. Material values are updated incrementally
-/// during the search. This function is only used when initializing a new Position
-/// object.
-
-Value Position::compute_non_pawn_material(Color c) const {
-
- Value value = VALUE_ZERO;
-
- for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
- value += pieceCount[c][pt] * PieceValue[MG][pt];
-
- return value;
-}
-
-
/// Position::is_draw() tests whether the position is drawn by material, 50 moves
/// rule or repetition. It does not detect stalemates.
/// Position::pos_is_ok() performs some consistency checks for the position object.
/// This is meant to be helpful when debugging.
-bool Position::pos_is_ok(int* failedStep) const {
-
- int dummy, *step = failedStep ? failedStep : &dummy;
+bool Position::pos_is_ok(int* step) const {
- // What features of the position should be verified?
+ // Which parts of the position should be verified?
const bool all = false;
- const bool debugBitboards = all || false;
- const bool debugKingCount = all || false;
- const bool debugKingCapture = all || false;
- const bool debugCheckerCount = all || false;
- const bool debugKey = all || false;
- const bool debugMaterialKey = all || false;
- const bool debugPawnKey = all || false;
- const bool debugIncrementalEval = all || false;
- const bool debugNonPawnMaterial = all || false;
- const bool debugPieceCounts = all || false;
- const bool debugPieceList = all || false;
- const bool debugCastlingSquares = all || false;
-
- *step = 1;
-
- if (sideToMove != WHITE && sideToMove != BLACK)
- return false;
-
- if ((*step)++, piece_on(king_square(WHITE)) != W_KING)
- return false;
-
- if ((*step)++, piece_on(king_square(BLACK)) != B_KING)
- return false;
-
- if ((*step)++, debugKingCount)
- {
- int kingCount[COLOR_NB] = {};
-
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
- if (type_of(piece_on(s)) == KING)
- ++kingCount[color_of(piece_on(s))];
-
- if (kingCount[0] != 1 || kingCount[1] != 1)
- return false;
- }
-
- if ((*step)++, debugKingCapture)
- if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
- return false;
-
- if ((*step)++, debugCheckerCount && popcount<Full>(st->checkersBB) > 2)
+ const bool testBitboards = all || false;
+ const bool testState = all || false;
+ const bool testKingCount = all || false;
+ const bool testKingCapture = all || false;
+ const bool testPieceCounts = all || false;
+ const bool testPieceList = all || false;
+ const bool testCastlingSquares = all || false;
+
+ if (step)
+ *step = 1;
+
+ if ( (sideToMove != WHITE && sideToMove != BLACK)
+ || piece_on(king_square(WHITE)) != W_KING
+ || piece_on(king_square(BLACK)) != B_KING
+ || ( ep_square() != SQ_NONE
+ && relative_rank(sideToMove, ep_square()) != RANK_6))
return false;
- if ((*step)++, debugBitboards)
+ if (step && ++*step, testBitboards)
{
// The intersection of the white and black pieces must be empty
if (pieces(WHITE) & pieces(BLACK))
return false;
}
- if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)
- return false;
-
- if ((*step)++, debugKey && st->key != compute_key())
- return false;
-
- if ((*step)++, debugPawnKey && st->pawnKey != compute_pawn_key())
- return false;
-
- if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key())
- return false;
+ if (step && ++*step, testState)
+ {
+ StateInfo si;
+ set_state(&si);
+ if ( st->key != si.key
+ || st->pawnKey != si.pawnKey
+ || st->materialKey != si.materialKey
+ || st->npMaterial[WHITE] != si.npMaterial[WHITE]
+ || st->npMaterial[BLACK] != si.npMaterial[BLACK]
+ || st->psq != si.psq
+ || st->checkersBB != si.checkersBB)
+ return false;
+ }
- if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score())
- return false;
+ if (step && ++*step, testKingCount)
+ if ( std::count(board, board + SQUARE_NB, W_KING) != 1
+ || std::count(board, board + SQUARE_NB, B_KING) != 1)
+ return false;
- if ((*step)++, debugNonPawnMaterial)
- if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE)
- || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK))
+ if (step && ++*step, testKingCapture)
+ if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
return false;
- if ((*step)++, debugPieceCounts)
+ if (step && ++*step, testPieceCounts)
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
return false;
- if ((*step)++, debugPieceList)
+ if (step && ++*step, testPieceList)
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int i = 0; i < pieceCount[c][pt]; ++i)
|| index[pieceList[c][pt][i]] != i)
return false;
- if ((*step)++, debugCastlingSquares)
+ if (step && ++*step, testCastlingSquares)
for (Color c = WHITE; c <= BLACK; ++c)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
{
return false;
}
- *step = 0;
return true;
}