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-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2015-2017 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
const string PieceToChar(" PNBRQK pnbrqk");
+const 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.
Bitboard b = stmAttackers & bb[Pt];
if (!b)
- return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
+ return min_attacker<Pt + 1>(bb, to, stmAttackers, occupied, attackers);
occupied ^= b & ~(b - 1);
/// operator<<(Position) returns an ASCII representation of the position
-std::ostream& operator<<(std::ostream& os, Position& pos) {
+std::ostream& operator<<(std::ostream& os, const Position& pos) {
os << "\n +---+---+---+---+---+---+---+---+\n";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
{
+ StateInfo st;
+ Position p;
+ p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
Tablebases::ProbeState s1, s2;
- Tablebases::WDLScore wdl = Tablebases::probe_wdl(pos, &s1);
- int dtz = Tablebases::probe_dtz(pos, &s2);
+ Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
+ int dtz = Tablebases::probe_dtz(p, &s2);
os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
<< "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
}
/// Position::set() is an overload to initialize the position object with
-/// the given endgame code string like "KBPKN". It is manily an helper to
-/// get the material key out of an endgame code. Position is not playable,
-/// indeed is even not guaranteed to be legal.
+/// the given endgame code string like "KBPKN". It is mainly a helper to
+/// get the material key out of an endgame code.
Position& Position::set(const string& code, Color c, StateInfo* si) {
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
- string fenStr = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
- + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
+ string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
+ + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
return set(fenStr, false, si, nullptr);
}
}
-/// Position::game_phase() calculates the game phase interpolating total non-pawn
-/// material between endgame and midgame limits.
-
-Phase Position::game_phase() const {
-
- Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
-
- npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
-
- return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
-}
-
-
/// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
/// slider if removing that piece from the board would result in a position where
pinners = 0;
// Snipers are sliders that attack 's' when a piece is removed
- Bitboard snipers = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
+ Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
while (snipers)
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
- | (attacks_bb<ROOK >(s, occupied) & pieces(ROOK, QUEEN))
+ | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
| (attacks_from<KING>(s) & pieces(KING));
}
&& empty(to - pawn_push(us))))
return false;
}
- else if (!(attacks_from(pc, from) & to))
+ else if (!(attacks_from(type_of(pc), from) & to))
return false;
// Evasions generator already takes care to avoid some kind of illegal moves
return false;
case PROMOTION:
- return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & square<KING>(~sideToMove);
+ return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we
assert(is_ok(m));
assert(&newSt != st);
- ++nodes;
+ thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
Key k = st->key ^ Zobrist::side;
// Copy some fields of the old state to our new StateInfo object except the
// Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
- prefetch(thisThread->pawnsTable[st->pawnKey]);
+ prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
st->rule50 = 0;
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
-/// SEE value of move is greater or equal to the given value. We'll use an
+/// SEE value of move is greater or equal to the given threshold. We'll use an
/// algorithm similar to alpha-beta pruning with a null window.
-bool Position::see_ge(Move m, Value v) const {
+bool Position::see_ge(Move m, Value threshold) const {
assert(is_ok(m));
// handled correctly. Simply assume the SEE value is VALUE_ZERO that is always
// correct unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLING)
- return VALUE_ZERO >= v;
+ return VALUE_ZERO >= threshold;
Square from = from_sq(m), to = to_sq(m);
PieceType nextVictim = type_of(piece_on(from));
occupied = 0;
}
- if (balance < v)
+ if (balance < threshold)
return false;
if (nextVictim == KING)
balance -= PieceValue[MG][nextVictim];
- if (balance >= v)
+ if (balance >= threshold)
return true;
bool relativeStm = true; // True if the opponent is to move
relativeStm = !relativeStm;
- if (relativeStm == (balance >= v))
+ if (relativeStm == (balance >= threshold))
return relativeStm;
stm = ~stm;
/// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates.
-bool Position::is_draw() const {
+bool Position::is_draw(int ply) const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
- int e = std::min(st->rule50, st->pliesFromNull);
+ int end = std::min(st->rule50, st->pliesFromNull);
- if (e < 4)
+ if (end < 4)
return false;
StateInfo* stp = st->previous->previous;
+ int cnt = 0;
- do {
+ for (int i = 4; i <= end; i += 2)
+ {
stp = stp->previous->previous;
- if (stp->key == st->key)
- return true; // Draw at first repetition
-
- } while ((e -= 2) >= 4);
+ // At root position ply is 1, so 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 - 1 > i) == 2)
+ return true;
+ }
return false;
}
}
-/// Position::pos_is_ok() performs some consistency checks for the position object.
+/// Position::pos_is_ok() performs some consistency checks for the
+/// position object and raises an asserts if something wrong is detected.
/// This is meant to be helpful when debugging.
-bool Position::pos_is_ok(int* failedStep) const {
+bool Position::pos_is_ok() const {
const bool Fast = true; // Quick (default) or full check?
- enum { Default, King, Bitboards, State, Lists, Castling };
+ if ( (sideToMove != WHITE && sideToMove != BLACK)
+ || piece_on(square<KING>(WHITE)) != W_KING
+ || piece_on(square<KING>(BLACK)) != B_KING
+ || ( ep_square() != SQ_NONE
+ && relative_rank(sideToMove, ep_square()) != RANK_6))
+ assert(0 && "pos_is_ok: Default");
- for (int step = Default; step <= (Fast ? Default : Castling); step++)
- {
- if (failedStep)
- *failedStep = step;
-
- if (step == Default)
- if ( (sideToMove != WHITE && sideToMove != BLACK)
- || piece_on(square<KING>(WHITE)) != W_KING
- || piece_on(square<KING>(BLACK)) != B_KING
- || ( ep_square() != SQ_NONE
- && relative_rank(sideToMove, ep_square()) != RANK_6))
- return false;
+ if (Fast)
+ return true;
- if (step == King)
- if ( std::count(board, board + SQUARE_NB, W_KING) != 1
- || std::count(board, board + SQUARE_NB, B_KING) != 1
- || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
- return false;
+ if ( pieceCount[W_KING] != 1
+ || pieceCount[B_KING] != 1
+ || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
+ assert(0 && "pos_is_ok: Kings");
- if (step == Bitboards)
- {
- if ( (pieces(WHITE) & pieces(BLACK))
- ||(pieces(WHITE) | pieces(BLACK)) != pieces())
- return false;
+ if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
+ || pieceCount[W_PAWN] > 8
+ || pieceCount[B_PAWN] > 8)
+ assert(0 && "pos_is_ok: Pawns");
- for (PieceType p1 = PAWN; p1 <= KING; ++p1)
- for (PieceType p2 = PAWN; p2 <= KING; ++p2)
- if (p1 != p2 && (pieces(p1) & pieces(p2)))
- return false;
- }
+ if ( (pieces(WHITE) & pieces(BLACK))
+ || (pieces(WHITE) | pieces(BLACK)) != pieces()
+ || popcount(pieces(WHITE)) > 16
+ || popcount(pieces(BLACK)) > 16)
+ assert(0 && "pos_is_ok: Bitboards");
- if (step == State)
- {
- StateInfo si = *st;
- set_state(&si);
- if (std::memcmp(&si, st, sizeof(StateInfo)))
- return false;
- }
+ for (PieceType p1 = PAWN; p1 <= KING; ++p1)
+ for (PieceType p2 = PAWN; p2 <= KING; ++p2)
+ if (p1 != p2 && (pieces(p1) & pieces(p2)))
+ assert(0 && "pos_is_ok: Bitboards");
- if (step == Lists)
- for (Piece pc : Pieces)
- {
- if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))))
- return false;
+ StateInfo si = *st;
+ set_state(&si);
+ if (std::memcmp(&si, st, sizeof(StateInfo)))
+ assert(0 && "pos_is_ok: State");
- for (int i = 0; i < pieceCount[pc]; ++i)
- if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
- return false;
- }
+ for (Piece pc : Pieces)
+ {
+ if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
+ || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
+ assert(0 && "pos_is_ok: Pieces");
- if (step == Castling)
- for (Color c = WHITE; c <= BLACK; ++c)
- for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
- {
- if (!can_castle(c | s))
- 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))
- return false;
- }
+ for (int i = 0; i < pieceCount[pc]; ++i)
+ if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
+ 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))
+ {
+ if (!can_castle(c | s))
+ 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))
+ assert(0 && "pos_is_ok: Castling");
+ }
+
return true;
}