]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Improve comments in UCI
[stockfish] / src / position.cpp
index dcd3eb2485316441a986f785b761588cbe0b77d2..eed6d8850873209fbf072e3a1968b783eb9f06d6 100644 (file)
 #include <sstream>
 
 #include "bitcount.h"
+#include "misc.h"
 #include "movegen.h"
-#include "notation.h"
 #include "position.h"
 #include "psqtab.h"
-#include "rkiss.h"
 #include "thread.h"
 #include "tt.h"
+#include "uci.h"
 
 using std::string;
 
-static const string PieceToChar(" PNBRQK  pnbrqk");
-
-CACHE_LINE_ALIGNMENT
-
-Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
 Value PieceValue[PHASE_NB][PIECE_NB] = {
 { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
 { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
@@ -47,7 +42,7 @@ namespace Zobrist {
 
   Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
   Key enpassant[FILE_NB];
-  Key castling[CASTLING_FLAG_NB];
+  Key castling[CASTLING_RIGHT_NB];
   Key side;
   Key exclusion;
 }
@@ -56,6 +51,9 @@ Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
 
 namespace {
 
+const string PieceToChar(" PNBRQK  pnbrqk");
+Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+
 // min_attacker() is a helper function used by see() 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.
@@ -107,6 +105,30 @@ CheckInfo::CheckInfo(const Position& pos) {
 }
 
 
+/// operator<<(Position) returns an ASCII representation of the position
+
+std::ostream& operator<<(std::ostream& os, const Position& pos) {
+
+  os << "\n +---+---+---+---+---+---+---+---+\n";
+
+  for (Rank r = RANK_8; r >= RANK_1; --r)
+  {
+      for (File f = FILE_A; f <= FILE_H; ++f)
+          os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
+
+      os << " |\n +---+---+---+---+---+---+---+---+\n";
+  }
+
+  os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
+     << std::setfill('0') << std::setw(16) << pos.st->key << std::dec << "\nCheckers: ";
+
+  for (Bitboard b = pos.checkers(); b; )
+      os << UCI::square(pop_lsb(&b)) << " ";
+
+  return os;
+}
+
+
 /// Position::init() initializes at startup the various arrays used to compute
 /// hash keys and the piece square tables. The latter is a two-step operation:
 /// Firstly, the white halves of the tables are copied from PSQT[] tables.
@@ -115,28 +137,28 @@ CheckInfo::CheckInfo(const Position& pos) {
 
 void Position::init() {
 
-  RKISS rk;
+  PRNG rng(1070372);
 
   for (Color c = WHITE; c <= BLACK; ++c)
       for (PieceType pt = PAWN; pt <= KING; ++pt)
           for (Square s = SQ_A1; s <= SQ_H8; ++s)
-              Zobrist::psq[c][pt][s] = rk.rand<Key>();
+              Zobrist::psq[c][pt][s] = rng.rand<Key>();
 
   for (File f = FILE_A; f <= FILE_H; ++f)
-      Zobrist::enpassant[f] = rk.rand<Key>();
+      Zobrist::enpassant[f] = rng.rand<Key>();
 
-  for (int cf = NO_CASTLING; cf <= ANY_CASTLING; ++cf)
+  for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
   {
-      Bitboard b = cf;
+      Bitboard b = cr;
       while (b)
       {
           Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
-          Zobrist::castling[cf] ^= k ? k : rk.rand<Key>();
+          Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
       }
   }
 
-  Zobrist::side = rk.rand<Key>();
-  Zobrist::exclusion  = rk.rand<Key>();
+  Zobrist::side = rng.rand<Key>();
+  Zobrist::exclusion  = rng.rand<Key>();
 
   for (PieceType pt = PAWN; pt <= KING; ++pt)
   {
@@ -171,6 +193,21 @@ Position& Position::operator=(const Position& pos) {
 }
 
 
+/// 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.
@@ -210,7 +247,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
       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);
@@ -258,19 +295,19 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
           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;
 
-      set_castling_flag(c, rsq);
+      set_castling_right(c, rsq);
   }
 
   // 4. En passant square. Ignore if no pawn capture is possible
   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;
@@ -281,46 +318,86 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
 
   // 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_castling_flag() is a helper function used to set castling
-/// flags given the corresponding color and the rook starting square.
+/// Position::set_castling_right() is a helper function used to set castling
+/// rights given the corresponding color and the rook starting square.
 
-void Position::set_castling_flag(Color c, Square rfrom) {
+void Position::set_castling_right(Color c, Square rfrom) {
 
   Square kfrom = king_square(c);
   CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
-  CastlingFlag cf = (c | cs);
+  CastlingRight cr = (c | cs);
 
-  st->castlingFlags |= cf;
-  castlingFlagsMask[kfrom] |= cf;
-  castlingFlagsMask[rfrom] |= cf;
-  castlingRookSquare[cf] = rfrom;
+  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);
 
   for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
       if (s != kfrom && s != rfrom)
-          castlingPath[cf] |= s;
+          castlingPath[cr] |= s;
 
   for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
       if (s != kfrom && s != rfrom)
-          castlingPath[cf] |= s;
+          castlingPath[cr] |= s;
+}
+
+
+/// 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->nonPawnMaterial[WHITE] = si->nonPawnMaterial[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->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
 }
 
 
@@ -332,82 +409,58 @@ const string Position::fen() const {
   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 << (sideToMove == WHITE ? " w " : " b ");
 
   if (can_castle(WHITE_OO))
-      ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE |  KING_SIDE)), false) : 'K');
+      ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE |  KING_SIDE))) : 'K');
 
   if (can_castle(WHITE_OOO))
-      ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)), false) : 'Q');
+      ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
 
   if (can_castle(BLACK_OO))
-      ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK |  KING_SIDE)),  true) : 'k');
+      ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK |  KING_SIDE))) : 'k');
 
   if (can_castle(BLACK_OOO))
-      ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)),  true) : 'q');
+      ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
 
   if (!can_castle(WHITE) && !can_castle(BLACK))
       ss << '-';
 
-  ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ")
-     << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
+  ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
+     << 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 dottedLine =            "\n+---+---+---+---+---+---+---+---+";
-  const string twoRows =  dottedLine + "\n|   | . |   | . |   | . |   | . |"
-                        + dottedLine + "\n| . |   | . |   | . |   | . |   |";
-
-  string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
-
-  for (Bitboard b = pieces(); b; )
-  {
-      Square s = pop_lsb(&b);
-      brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
-  }
-
-  std::ostringstream ss;
-
-  if (move)
-      ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
-         << move_to_san(*const_cast<Position*>(this), move);
+/// Position::game_phase() calculates the game phase interpolating total non-pawn
+/// material between endgame and midgame limits.
 
-  ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
-     << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
+Phase Position::game_phase() const {
 
-  for (Bitboard b = checkers(); b; )
-      ss << to_string(pop_lsb(&b)) << " ";
+  Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
 
-  ss << "\nLegal moves: ";
-  for (MoveList<LEGAL> it(*this); *it; ++it)
-      ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
+  npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
 
-  return ss.str();
+  return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
 }
 
 
@@ -439,16 +492,16 @@ Bitboard Position::check_blockers(Color c, Color kingColor) const {
 
 
 /// Position::attackers_to() computes a bitboard of all pieces which attack a
-/// given square. Slider attacks use the occ bitboard to indicate occupancy.
+/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
 
-Bitboard Position::attackers_to(Square s, Bitboard occ) const {
+Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
 
-  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, occ)     & pieces(ROOK, QUEEN))
-        | (attacks_bb<BISHOP>(s, occ)   & pieces(BISHOP, QUEEN))
-        | (attacks_from<KING>(s)        & pieces(KING));
+  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<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
+        | (attacks_from<KING>(s)           & pieces(KING));
 }
 
 
@@ -470,19 +523,18 @@ bool Position::legal(Move m, Bitboard pinned) const {
   // 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 occupied = (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, occupied) & pieces(~us, QUEEN, ROOK))
+            && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
   }
 
   // If the moving piece is a king, check whether the destination
@@ -530,71 +582,27 @@ bool Position::pseudo_legal(const Move m) const {
   // Handle the special case of a pawn move
   if (type_of(pc) == PAWN)
   {
-      // Move direction must be compatible with pawn color
-      int direction = to - from;
-      if ((us == WHITE) != (direction > 0))
-          return false;
-
       // We have already handled promotion moves, so destination
       // cannot be on the 8th/1st rank.
-      if (rank_of(to) == RANK_8 || rank_of(to) == RANK_1)
+      if (rank_of(to) == relative_rank(us, RANK_8))
           return false;
 
-      // Proceed according to the square delta between the origin and
-      // destination squares.
-      switch (direction)
-      {
-      case DELTA_NW:
-      case DELTA_NE:
-      case DELTA_SW:
-      case DELTA_SE:
-      // Capture. The destination square must be occupied by an enemy
-      // piece (en passant captures was handled earlier).
-      if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us)
-          return false;
-
-      // From and to files must be one file apart, avoids a7h5
-      if (abs(file_of(from) - file_of(to)) != 1)
-          return false;
-      break;
+      if (   !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
 
-      case DELTA_N:
-      case DELTA_S:
-      // Pawn push. The destination square must be empty.
-      if (!empty(to))
-          return false;
-      break;
-
-      case DELTA_NN:
-      // Double white pawn push. The destination square must be on the fourth
-      // rank, and both the destination square and the square between the
-      // source and destination squares must be empty.
-      if (    rank_of(to) != RANK_4
-          || !empty(to)
-          || !empty(from + DELTA_N))
-          return false;
-      break;
-
-      case DELTA_SS:
-      // Double black pawn push. The destination square must be on the fifth
-      // rank, and both the destination square and the square between the
-      // source and destination squares must be empty.
-      if (    rank_of(to) != RANK_5
-          || !empty(to)
-          || !empty(from + DELTA_S))
-          return false;
-      break;
+          && !((from + pawn_push(us) == to) && empty(to))       // Not a single push
 
-      default:
+          && !(   (from + 2 * pawn_push(us) == to)              // Not a double push
+               && (rank_of(from) == relative_rank(us, RANK_2))
+               && empty(to)
+               && empty(to - pawn_push(us))))
           return false;
-      }
   }
   else if (!(attacks_from(pc, from) & to))
       return false;
 
   // Evasions generator already takes care to avoid some kind of illegal moves
-  // and pl_move_is_legal() relies on this. We therefore have to take care that
-  // the same kind of moves are filtered out here.
+  // and legal() relies on this. We therefore have to take care that the same
+  // kind of moves are filtered out here.
   if (checkers())
   {
       if (type_of(pc) != KING)
@@ -639,12 +647,11 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
       && !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;
 
@@ -654,7 +661,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
   // 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))
@@ -728,14 +735,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   {
       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];
   }
@@ -764,7 +767,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
           st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
       }
       else
-          st->npMaterial[them] -= PieceValue[MG][captured];
+          st->nonPawnMaterial[them] -= PieceValue[MG][captured];
 
       // Update board and piece lists
       remove_piece(capsq, them, captured);
@@ -791,17 +794,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
       st->epSquare = SQ_NONE;
   }
 
-  // Update castling flags if needed
-  if (st->castlingFlags && (castlingFlagsMask[from] | castlingFlagsMask[to]))
+  // Update castling rights if needed
+  if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
   {
-      int cf = castlingFlagsMask[from] | castlingFlagsMask[to];
-      k ^= Zobrist::castling[st->castlingFlags & cf];
-      st->castlingFlags &= ~cf;
+      int cr = castlingRightsMask[from] | castlingRightsMask[to];
+      k ^= Zobrist::castling[st->castlingRights & cr];
+      st->castlingRights &= ~cr;
   }
 
-  // Prefetch TT access as soon as we know the new hash key
-  prefetch((char*)TT.first_entry(k));
-
   // Move the piece. The tricky Chess960 castling is handled earlier
   if (type_of(m) != CASTLING)
       move_piece(from, to, us, pt);
@@ -817,7 +817,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
           k ^= Zobrist::enpassant[file_of(st->epSquare)];
       }
 
-      if (type_of(m) == PROMOTION)
+      else if (type_of(m) == PROMOTION)
       {
           PieceType promotion = promotion_type(m);
 
@@ -837,7 +837,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
           st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
 
           // Update material
-          st->npMaterial[us] += PieceValue[MG][promotion];
+          st->nonPawnMaterial[us] += PieceValue[MG][promotion];
       }
 
       // Update pawn hash key and prefetch access to pawnsTable
@@ -857,7 +857,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   // 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)
@@ -871,7 +871,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
               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);
@@ -898,56 +898,49 @@ void Position::undo_move(Move m) {
   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
@@ -960,15 +953,20 @@ void Position::undo_move(Move m) {
 
 /// 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);
 }
 
 
@@ -1010,6 +1008,26 @@ void Position::undo_null_move() {
 }
 
 
+/// Position::key_after() computes the new hash key after the given move. Needed
+/// for speculative prefetch. It doesn't recognize special moves like castling,
+/// en-passant and promotions.
+
+Key Position::key_after(Move m) const {
+
+  Color us = sideToMove;
+  Square from = from_sq(m);
+  Square to = to_sq(m);
+  PieceType pt = type_of(piece_on(from));
+  PieceType captured = type_of(piece_on(to));
+  Key k = st->key ^ Zobrist::side;
+
+  if (captured)
+      k ^= Zobrist::psq[~us][captured][to];
+
+  return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from];
+}
+
+
 /// Position::see() is a static exchange evaluator: It tries to estimate the
 /// material gain or loss resulting from a move.
 
@@ -1106,130 +1124,11 @@ Value Position::see(Move m) const {
 }
 
 
-/// 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->castlingFlags];
-
-  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.
 
 bool Position::is_draw() const {
 
-  if (   !pieces(PAWN)
-      && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
-      return true;
-
   if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
       return true;
 
@@ -1258,9 +1157,9 @@ void Position::flip() {
   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() ? " " : "/"));
   }
 
@@ -1287,57 +1186,30 @@ void Position::flip() {
 /// 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))
@@ -1355,33 +1227,36 @@ bool Position::pos_is_ok(int* failedStep) const {
                   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->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE]
+          || st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[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)
@@ -1389,19 +1264,18 @@ bool Position::pos_is_ok(int* failedStep) const {
                       || 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))
           {
               if (!can_castle(c | s))
                   continue;
 
-              if (  (castlingFlagsMask[king_square(c)] & (c | s)) != (c | s)
+              if (  (castlingRightsMask[king_square(c)] & (c | s)) != (c | s)
                   || piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
-                  || castlingFlagsMask[castlingRookSquare[c | s]] != (c | s))
+                  || castlingRightsMask[castlingRookSquare[c | s]] != (c | s))
                   return false;
           }
 
-  *step = 0;
   return true;
 }