]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Move prefetch() out of TT
[stockfish] / src / position.cpp
index fb17f209e3e4d24adeb9a45542f77f0b4fa8a3ba..a069cf404be6d012a7ac129c4386b0c3a1721586 100644 (file)
@@ -1,7 +1,7 @@
 /*
   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
   Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
-  Copyright (C) 2008-2009 Marco Costalba
+  Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, 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
@@ -44,12 +44,9 @@ using std::string;
 //// Variables
 ////
 
-int Position::castleRightsMask[64];
-
 Key Position::zobrist[2][8][64];
 Key Position::zobEp[64];
 Key Position::zobCastle[16];
-Key Position::zobMaterial[2][8][16];
 Key Position::zobSideToMove;
 Key Position::zobExclusion;
 
@@ -76,12 +73,36 @@ CheckInfo::CheckInfo(const Position& pos) {
   checkSq[KING] = EmptyBoardBB;
 }
 
-Position::Position(const Position& pos) {
-  copy(pos);
+
+/// Position c'tors. Here we always create a copy of the original position
+/// or the FEN string, we want the new born Position object do not depend
+/// on any external data so we detach state pointer from the source one.
+
+Position::Position(int th) : threadID(th) {}
+
+Position::Position(const Position& pos, int th) {
+
+  memcpy(this, &pos, sizeof(Position));
+  detach(); // Always detach() in copy c'tor to avoid surprises
+  threadID = th;
 }
 
-Position::Position(const string& fen) {
+Position::Position(const string& fen, int th) {
+
   from_fen(fen);
+  threadID = th;
+}
+
+
+/// Position::detach() copies the content of the current state and castling
+/// masks inside the position itself. This is needed when the st pointee could
+/// become stale, as example because the caller is about to going out of scope.
+
+void Position::detach() {
+
+  startState = *st;
+  st = &startState;
+  st->previous = NULL; // as a safe guard
 }
 
 
@@ -143,61 +164,61 @@ void Position::from_fen(const string& fen) {
   }
 
   i++;
-  while(strchr("KQkqabcdefghABCDEFGH-", fen[i])) {
-    if (fen[i] == '-')
-    {
-      i++;
-      break;
-    }
-    else if(fen[i] == 'K') allow_oo(WHITE);
-    else if(fen[i] == 'Q') allow_ooo(WHITE);
-    else if(fen[i] == 'k') allow_oo(BLACK);
-    else if(fen[i] == 'q') allow_ooo(BLACK);
-    else if(fen[i] >= 'A' && fen[i] <= 'H') {
-      File rookFile, kingFile = FILE_NONE;
-      for(Square square = SQ_B1; square <= SQ_G1; square++)
-        if(piece_on(square) == WK)
-          kingFile = square_file(square);
-      if(kingFile == FILE_NONE) {
-        std::cout << "Error in FEN at character " << i << std::endl;
-        return;
-      }
-      initialKFile = kingFile;
-      rookFile = File(fen[i] - 'A') + FILE_A;
-      if(rookFile < initialKFile) {
-        allow_ooo(WHITE);
-        initialQRFile = rookFile;
-      }
-      else {
-        allow_oo(WHITE);
-        initialKRFile = rookFile;
+  while (strchr("KQkqabcdefghABCDEFGH-", fen[i])) {
+      if (fen[i] == '-')
+      {
+          i++;
+          break;
       }
-    }
-    else if(fen[i] >= 'a' && fen[i] <= 'h') {
-      File rookFile, kingFile = FILE_NONE;
-      for(Square square = SQ_B8; square <= SQ_G8; square++)
-        if(piece_on(square) == BK)
-          kingFile = square_file(square);
-      if(kingFile == FILE_NONE) {
-        std::cout << "Error in FEN at character " << i << std::endl;
-        return;
+      else if (fen[i] == 'K') allow_oo(WHITE);
+      else if (fen[i] == 'Q') allow_ooo(WHITE);
+      else if (fen[i] == 'k') allow_oo(BLACK);
+      else if (fen[i] == 'q') allow_ooo(BLACK);
+      else if (fen[i] >= 'A' && fen[i] <= 'H') {
+          File rookFile, kingFile = FILE_NONE;
+          for (Square square = SQ_B1; square <= SQ_G1; square++)
+              if (piece_on(square) == WK)
+                  kingFile = square_file(square);
+          if (kingFile == FILE_NONE) {
+              std::cout << "Error in FEN at character " << i << std::endl;
+              return;
+          }
+          initialKFile = kingFile;
+          rookFile = File(fen[i] - 'A') + FILE_A;
+          if (rookFile < initialKFile) {
+              allow_ooo(WHITE);
+              initialQRFile = rookFile;
+          }
+          else {
+              allow_oo(WHITE);
+              initialKRFile = rookFile;
+          }
       }
-      initialKFile = kingFile;
-      rookFile = File(fen[i] - 'a') + FILE_A;
-      if(rookFile < initialKFile) {
-        allow_ooo(BLACK);
-        initialQRFile = rookFile;
+      else if (fen[i] >= 'a' && fen[i] <= 'h') {
+          File rookFile, kingFile = FILE_NONE;
+          for (Square square = SQ_B8; square <= SQ_G8; square++)
+              if (piece_on(square) == BK)
+                  kingFile = square_file(square);
+          if (kingFile == FILE_NONE) {
+              std::cout << "Error in FEN at character " << i << std::endl;
+              return;
+          }
+          initialKFile = kingFile;
+          rookFile = File(fen[i] - 'a') + FILE_A;
+          if (rookFile < initialKFile) {
+              allow_ooo(BLACK);
+              initialQRFile = rookFile;
+          }
+          else {
+              allow_oo(BLACK);
+              initialKRFile = rookFile;
+          }
       }
       else {
-        allow_oo(BLACK);
-        initialKRFile = rookFile;
+          std::cout << "Error in FEN at character " << i << std::endl;
+          return;
       }
-    }
-    else {
-      std::cout << "Error in FEN at character " << i << std::endl;
-      return;
-    }
-    i++;
+      i++;
   }
 
   // Skip blanks
@@ -271,10 +292,24 @@ const string Position::to_fen() const {
   fen += (sideToMove == WHITE ? "w " : "b ");
   if (st->castleRights != NO_CASTLES)
   {
-    if (can_castle_kingside(WHITE))  fen += 'K';
-    if (can_castle_queenside(WHITE)) fen += 'Q';
-    if (can_castle_kingside(BLACK))  fen += 'k';
-    if (can_castle_queenside(BLACK)) fen += 'q';
+     if (initialKFile == FILE_E && initialQRFile == FILE_A && initialKRFile == FILE_H)
+     {
+        if (can_castle_kingside(WHITE))  fen += 'K';
+        if (can_castle_queenside(WHITE)) fen += 'Q';
+        if (can_castle_kingside(BLACK))  fen += 'k';
+        if (can_castle_queenside(BLACK)) fen += 'q';
+     }
+     else
+     {
+        if (can_castle_kingside(WHITE))
+           fen += char(toupper(file_to_char(initialKRFile)));
+        if (can_castle_queenside(WHITE))
+           fen += char(toupper(file_to_char(initialQRFile)));
+        if (can_castle_kingside(BLACK))
+           fen += file_to_char(initialKRFile);
+        if (can_castle_queenside(BLACK))
+           fen += file_to_char(initialQRFile);
+     }
   } else
       fen += '-';
 
@@ -305,8 +340,9 @@ void Position::print(Move m) const {
   std::cout << std::endl;
   if (m != MOVE_NONE)
   {
+      Position p(*this, thread());
       string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
-      std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl;
+      std::cout << "Move is: " << col << move_to_san(p, m) << std::endl;
   }
   for (Rank rank = RANK_8; rank >= RANK_1; rank--)
   {
@@ -331,15 +367,6 @@ void Position::print(Move m) const {
 }
 
 
-/// Position::copy() creates a copy of the input position.
-
-void Position::copy(const Position& pos) {
-
-  memcpy(this, &pos, sizeof(Position));
-  saveState(); // detach and copy state info
-}
-
-
 /// Position:hidden_checkers<>() returns a bitboard of all pinned (against the
 /// king) pieces for the given color and for the given pinner type. Or, when
 /// template parameter FindPinned is false, the pieces of the given color
@@ -669,14 +696,14 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   assert(is_ok());
   assert(move_is_ok(m));
 
-  Bitboard key = st->key;
+  Key key = st->key;
 
   // Copy some fields of old state to our new StateInfo object except the
   // ones which are recalculated from scratch anyway, then switch our state
   // pointer to point to the new, ready to be updated, state.
   struct ReducedStateInfo {
     Key pawnKey, materialKey;
-    int castleRights, rule50, pliesFromNull;
+    int castleRights, rule50, gamePly, pliesFromNull;
     Square epSquare;
     Score value;
     Value npMaterial[2];
@@ -688,8 +715,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
 
   // Save the current key to the history[] array, in order to be able to
   // detect repetition draws.
-  history[gamePly] = key;
-  gamePly++;
+  history[st->gamePly++] = key;
 
   // Update side to move
   key ^= zobSideToMove;
@@ -746,7 +772,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   }
 
   // Prefetch TT access as soon as we know key is updated
-  TT.prefetch(key);
+  prefetch((char*)TT.first_entry(key));
 
   // Move the piece
   Bitboard move_bb = make_move_bb(from, to);
@@ -781,6 +807,46 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
               key ^= zobEp[st->epSquare];
           }
       }
+
+      if (pm) // promotion ?
+      {
+          PieceType promotion = move_promotion_piece(m);
+
+          assert(promotion >= KNIGHT && promotion <= QUEEN);
+
+          // Insert promoted piece instead of pawn
+          clear_bit(&(byTypeBB[PAWN]), to);
+          set_bit(&(byTypeBB[promotion]), to);
+          board[to] = piece_of_color_and_type(us, promotion);
+
+          // Update piece counts
+          pieceCount[us][promotion]++;
+          pieceCount[us][PAWN]--;
+
+          // Update material key
+          st->materialKey ^= zobrist[us][PAWN][pieceCount[us][PAWN]];
+          st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]-1];
+
+          // Update piece lists, move the last pawn at index[to] position
+          // and shrink the list. Add a new promotion piece to the list.
+          Square lastPawnSquare = pieceList[us][PAWN][pieceCount[us][PAWN]];
+          index[lastPawnSquare] = index[to];
+          pieceList[us][PAWN][index[lastPawnSquare]] = lastPawnSquare;
+          pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE;
+          index[to] = pieceCount[us][promotion] - 1;
+          pieceList[us][promotion][index[to]] = to;
+
+          // Partially revert hash keys update
+          key ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to];
+          st->pawnKey ^= zobrist[us][PAWN][to];
+
+          // Partially revert and update incremental scores
+          st->value -= pst(us, PAWN, to);
+          st->value += pst(us, promotion, to);
+
+          // Update material
+          st->npMaterial[us] += piece_value_midgame(promotion);
+      }
   }
 
   // Update incremental scores
@@ -789,46 +855,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   // Set capture piece
   st->capture = capture;
 
-  if (pm) // promotion ?
-  {
-      PieceType promotion = move_promotion_piece(m);
-
-      assert(promotion >= KNIGHT && promotion <= QUEEN);
-
-      // Insert promoted piece instead of pawn
-      clear_bit(&(byTypeBB[PAWN]), to);
-      set_bit(&(byTypeBB[promotion]), to);
-      board[to] = piece_of_color_and_type(us, promotion);
-
-      // Update material key
-      st->materialKey ^= zobMaterial[us][PAWN][pieceCount[us][PAWN]];
-      st->materialKey ^= zobMaterial[us][promotion][pieceCount[us][promotion]+1];
-
-      // Update piece counts
-      pieceCount[us][PAWN]--;
-      pieceCount[us][promotion]++;
-
-      // Update piece lists, move the last pawn at index[to] position
-      // and shrink the list. Add a new promotion piece to the list.
-      Square lastPawnSquare = pieceList[us][PAWN][pieceCount[us][PAWN]];
-      index[lastPawnSquare] = index[to];
-      pieceList[us][PAWN][index[lastPawnSquare]] = lastPawnSquare;
-      pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE;
-      index[to] = pieceCount[us][promotion] - 1;
-      pieceList[us][promotion][index[to]] = to;
-
-      // Partially revert hash keys update
-      key ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to];
-      st->pawnKey ^= zobrist[us][PAWN][to];
-
-      // Partially revert and update incremental scores
-      st->value -= pst(us, PAWN, to);
-      st->value += pst(us, promotion, to);
-
-      // Update material
-      st->npMaterial[us] += piece_value_midgame(promotion);
-  }
-
   // Update the key with the final value
   st->key = key;
 
@@ -868,23 +894,31 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
 /// Position::do_capture_move() is a private method used to update captured
 /// piece info. It is called from the main Position::do_move function.
 
-void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep) {
+void Position::do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep) {
 
     assert(capture != KING);
 
     Square capsq = to;
 
-    if (ep) // en passant ?
+    // If the captured piece was a pawn, update pawn hash key,
+    // otherwise update non-pawn material.
+    if (capture == PAWN)
     {
-        capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S);
+        if (ep) // en passant ?
+        {
+            capsq = (them == BLACK)? (to - DELTA_N) : (to - DELTA_S);
 
-        assert(to == st->epSquare);
-        assert(relative_rank(opposite_color(them), to) == RANK_6);
-        assert(piece_on(to) == EMPTY);
-        assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN));
+            assert(to == st->epSquare);
+            assert(relative_rank(opposite_color(them), to) == RANK_6);
+            assert(piece_on(to) == EMPTY);
+            assert(piece_on(capsq) == piece_of_color_and_type(them, PAWN));
 
-        board[capsq] = EMPTY;
+            board[capsq] = EMPTY;
+        }
+        st->pawnKey ^= zobrist[them][PAWN][capsq];
     }
+    else
+        st->npMaterial[them] -= piece_value_midgame(capture);
 
     // Remove captured piece
     clear_bit(&(byColorBB[them]), capsq);
@@ -897,19 +931,12 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ
     // Update incremental scores
     st->value -= pst(them, capture, capsq);
 
-    // If the captured piece was a pawn, update pawn hash key,
-    // otherwise update non-pawn material.
-    if (capture == PAWN)
-        st->pawnKey ^= zobrist[them][PAWN][capsq];
-    else
-        st->npMaterial[them] -= piece_value_midgame(capture);
-
-    // Update material hash key
-    st->materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
-
     // Update piece count
     pieceCount[them][capture]--;
 
+    // Update material hash key
+    st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]];
+
     // Update piece list, move the last piece at index[capsq] position
     //
     // WARNING: This is a not perfectly revresible operation. When we
@@ -1033,7 +1060,6 @@ void Position::undo_move(Move m) {
   assert(is_ok());
   assert(move_is_ok(m));
 
-  gamePly--;
   sideToMove = opposite_color(sideToMove);
 
   if (move_is_castle(m))
@@ -1083,7 +1109,6 @@ void Position::undo_move(Move m) {
       pieceList[us][PAWN][index[to]] = to;
   }
 
-
   // Put the piece back at the source square
   Bitboard move_bb = make_move_bb(to, from);
   do_move_bb(&(byColorBB[us]), move_bb);
@@ -1218,21 +1243,20 @@ void Position::do_null_move(StateInfo& backupSt) {
 
   // Save the current key to the history[] array, in order to be able to
   // detect repetition draws.
-  history[gamePly] = st->key;
+  history[st->gamePly++] = st->key;
 
   // Update the necessary information
   if (st->epSquare != SQ_NONE)
       st->key ^= zobEp[st->epSquare];
 
   st->key ^= zobSideToMove;
-  TT.prefetch(st->key);
+  prefetch((char*)TT.first_entry(st->key));
 
   sideToMove = opposite_color(sideToMove);
   st->epSquare = SQ_NONE;
   st->rule50++;
   st->pliesFromNull = 0;
   st->value += (sideToMove == WHITE) ?  TempoValue : -TempoValue;
-  gamePly++;
 }
 
 
@@ -1254,7 +1278,7 @@ void Position::undo_null_move() {
   // Update the necessary information
   sideToMove = opposite_color(sideToMove);
   st->rule50--;
-  gamePly--;
+  st->gamePly--;
 }
 
 
@@ -1429,19 +1453,6 @@ int Position::see(Square from, Square to) const {
 }
 
 
-/// Position::saveState() copies the content of the current state
-/// inside startState and makes st point to it. This is needed
-/// when the st pointee could become stale, as example because
-/// the caller is about to going out of scope.
-
-void Position::saveState() {
-
-  startState = *st;
-  st = &startState;
-  st->previous = NULL; // as a safe guard
-}
-
-
 /// Position::clear() erases the position object to a pristine state, with an
 /// empty board, white to move, and no castling rights.
 
@@ -1464,7 +1475,6 @@ void Position::clear() {
           pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
 
   sideToMove = WHITE;
-  gamePly = 0;
   initialKFile = FILE_E;
   initialKRFile = FILE_H;
   initialQRFile = FILE_A;
@@ -1479,7 +1489,7 @@ void Position::clear() {
 
 void Position::reset_game_ply() {
 
-  gamePly = 0;
+  st->gamePly = 0;
 }
 
 
@@ -1560,7 +1570,7 @@ Key Position::compute_pawn_key() const {
   for (Color c = WHITE; c <= BLACK; c++)
   {
       b = pieces(PAWN, c);
-      while(b)
+      while (b)
       {
           s = pop_1st_bit(&b);
           result ^= zobrist[c][PAWN][s];
@@ -1583,8 +1593,8 @@ Key Position::compute_material_key() const {
       for (PieceType pt = PAWN; pt <= QUEEN; pt++)
       {
           int count = piece_count(c, pt);
-          for (int i = 0; i <= count; i++)
-              result ^= zobMaterial[c][pt][i];
+          for (int i = 0; i < count; i++)
+              result ^= zobrist[c][pt][i];
       }
   return result;
 }
@@ -1604,7 +1614,7 @@ Score Position::compute_value() const {
       for (PieceType pt = PAWN; pt <= KING; pt++)
       {
           b = pieces(pt, c);
-          while(b)
+          while (b)
           {
               s = pop_1st_bit(&b);
               assert(piece_on(s) == piece_of_color_and_type(c, pt));
@@ -1643,6 +1653,7 @@ Value Position::compute_non_pawn_material(Color c) const {
 /// Position::is_draw() tests whether the position is drawn by material,
 /// repetition, or the 50 moves rule. It does not detect stalemates, this
 /// must be done by the search.
+// FIXME: Currently we are not handling 50 move rule correctly when in check
 
 bool Position::is_draw() const {
 
@@ -1656,8 +1667,8 @@ bool Position::is_draw() const {
       return true;
 
   // Draw by repetition?
-  for (int i = 2; i < Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2)
-      if (history[gamePly - i] == st->key)
+  for (int i = 4, e = Min(Min(st->gamePly, st->rule50), st->pliesFromNull); i <= e; i += 2)
+      if (history[st->gamePly - i] == st->key)
           return true;
 
   return false;
@@ -1736,15 +1747,6 @@ void Position::init_zobrist() {
       zobCastle[i] = genrand_int64();
 
   zobSideToMove = genrand_int64();
-
-  for (int i = 0; i < 2; i++)
-      for (int j = 0; j < 8; j++)
-          for (int k = 0; k < 16; k++)
-              zobMaterial[i][j][k] = (k > 0)? Key(genrand_int64()) : Key(0LL);
-
-  for (int i = 0; i < 16; i++)
-      zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
-
   zobExclusion = genrand_int64();
 }
 
@@ -1781,6 +1783,7 @@ void Position::flipped_copy(const Position& pos) {
   assert(pos.is_ok());
 
   clear();
+  threadID = pos.thread();
 
   // Board
   for (Square s = SQ_A1; s <= SQ_H8; s++)
@@ -1850,6 +1853,7 @@ bool Position::is_ok(int* failedStep) const {
   static const bool debugNonPawnMaterial = false;
   static const bool debugPieceCounts = false;
   static const bool debugPieceList = false;
+  static const bool debugCastleSquares = false;
 
   if (failedStep) *failedStep = 1;
 
@@ -1975,9 +1979,9 @@ bool Position::is_ok(int* failedStep) const {
   if (failedStep) (*failedStep)++;
   if (debugPieceList)
   {
-      for(Color c = WHITE; c <= BLACK; c++)
-          for(PieceType pt = PAWN; pt <= KING; pt++)
-              for(int i = 0; i < pieceCount[c][pt]; i++)
+      for (Color c = WHITE; c <= BLACK; c++)
+          for (PieceType pt = PAWN; pt <= KING; pt++)
+              for (int i = 0; i < pieceCount[c][pt]; i++)
               {
                   if (piece_on(piece_list(c, pt, i)) != piece_of_color_and_type(c, pt))
                       return false;
@@ -1986,6 +1990,25 @@ bool Position::is_ok(int* failedStep) const {
                       return false;
               }
   }
+
+  if (failedStep) (*failedStep)++;
+  if (debugCastleSquares) {
+      for (Color c = WHITE; c <= BLACK; c++) {
+          if (can_castle_kingside(c) && piece_on(initial_kr_square(c)) != piece_of_color_and_type(c, ROOK))
+              return false;
+          if (can_castle_queenside(c) && piece_on(initial_qr_square(c)) != piece_of_color_and_type(c, ROOK))
+              return false;
+      }
+      if (castleRightsMask[initial_kr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OO))
+          return false;
+      if (castleRightsMask[initial_qr_square(WHITE)] != (ALL_CASTLES ^ WHITE_OOO))
+          return false;
+      if (castleRightsMask[initial_kr_square(BLACK)] != (ALL_CASTLES ^ BLACK_OO))
+          return false;
+      if (castleRightsMask[initial_qr_square(BLACK)] != (ALL_CASTLES ^ BLACK_OOO))
+          return false;
+  }
+
   if (failedStep) *failedStep = 0;
   return true;
 }