}
+/// 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.
incremented after Black's move.
*/
- char col, row, token;
+ unsigned char col, row, token;
size_t idx;
Square sq = SQ_A8;
std::istringstream ss(fenStr);
for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {}
else if (token >= 'A' && token <= 'H')
- rsq = File(token - 'A') | relative_rank(c, RANK_1);
+ rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
else
continue;
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
&& ((ss >> row) && (row == '3' || row == '6')))
{
- st->epSquare = File(col - 'a') | Rank(row - '1');
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
st->epSquare = SQ_NONE;
// Convert from fullmove starting from 1 to ply starting from 0,
// 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);
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+
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.
int emptyCnt;
std::ostringstream ss;
- for (Rank rank = RANK_8; rank >= RANK_1; --rank)
+ for (Rank r = RANK_8; r >= RANK_1; --r)
{
- for (File file = FILE_A; file <= FILE_H; ++file)
+ for (File f = FILE_A; f <= FILE_H; ++f)
{
- for (emptyCnt = 0; file <= FILE_H && empty(file | rank); ++file)
+ for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f)
++emptyCnt;
if (emptyCnt)
ss << emptyCnt;
- if (file <= FILE_H)
- ss << PieceToChar[piece_on(file | rank)];
+ if (f <= FILE_H)
+ ss << PieceToChar[piece_on(make_square(f, r))];
}
- if (rank > RANK_1)
+ if (r > RANK_1)
ss << '/';
}
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ")
- << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
+ << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
return ss.str();
}
/// Position::pretty() returns an ASCII representation of the position to be
/// printed to the standard output together with the move's san notation.
-const string Position::pretty(Move move) const {
+const string Position::pretty(Move m) const {
- const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
- const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
- + dottedLine + "\n| . | | . | | . | | . | |";
+ std::ostringstream ss;
- string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
+ if (m)
+ ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
+ << move_to_san(*const_cast<Position*>(this), m);
- for (Bitboard b = pieces(); b; )
- {
- Square s = pop_lsb(&b);
- brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
- }
+ ss << "\n +---+---+---+---+---+---+---+---+\n";
- std::ostringstream ss;
+ for (Rank r = RANK_8; r >= RANK_1; --r)
+ {
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ ss << " | " << PieceToChar[piece_on(make_square(f, r))];
- if (move)
- ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
- << move_to_san(*const_cast<Position*>(this), move);
+ ss << " |\n +---+---+---+---+---+---+---+---+\n";
+ }
- ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
+ ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
for (Bitboard b = checkers(); b; )
// 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
// the captured pawn.
case ENPASSANT:
{
- Square capsq = file_of(to) | rank_of(from);
+ Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to;
return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK))
{
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];
}
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
- if (type_of(m) == PROMOTION)
+ else if (type_of(m) == PROMOTION)
{
PieceType promotion = promotion_type(m);
// 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);
- put_piece(capsq, them, captured); // Restore the captured piece
+ assert(pt == PAWN);
+ assert(to == st->previous->epSquare);
+ assert(relative_rank(us, to) == RANK_6);
+ assert(piece_on(capsq) == NO_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.
string f, token;
std::stringstream ss(fen());
- for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement
+ for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
{
- std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
+ std::getline(ss, token, r > RANK_1 ? '/' : ' ');
f.insert(0, token + (f.empty() ? " " : "/"));
}
/// 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;
-
- if (*step = 1, 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)
- if ( std::count(board, board + SQUARE_NB, W_KING) != 1
- || std::count(board, board + SQUARE_NB, B_KING) != 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;
}