]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Further work in pos_is_ok()
[stockfish] / src / position.cpp
index 1cb64934e95c2ace279d78a5d31f9a5e5c9a4f77..197e1e31fa3c628ec59fa02182da53d60681af50 100644 (file)
@@ -172,6 +172,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.
@@ -284,15 +299,9 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
   // 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());
 }
@@ -325,6 +334,52 @@ void Position::set_castling_right(Color c, Square rfrom) {
 }
 
 
+/// 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.
 
@@ -471,19 +526,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 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
@@ -684,14 +738,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];
   }
@@ -878,13 +928,11 @@ void Position::undo_move(Move m) {
 
   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);
+      Square rfrom, rto;
+      do_castling<false>(from, to, rfrom, rto);
+
       captured = NO_PIECE_TYPE;
       pt = KING;
-      do_castling(to, from, rto, rfrom);
   }
   else
       move_piece(to, from, us, pt); // Put the piece back at the source square
@@ -916,15 +964,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);
 }
 
 
@@ -1062,121 +1115,6 @@ 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->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.
 
@@ -1247,25 +1185,18 @@ bool Position::pos_is_ok(int* failedStep) const {
 
   int dummy, *step = failedStep ? failedStep : &dummy;
 
-  // 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)
+  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 = 1, sideToMove != WHITE && sideToMove != BLACK)
       return false;
 
   if ((*step)++, piece_on(king_square(WHITE)) != W_KING)
@@ -1274,26 +1205,10 @@ bool Position::pos_is_ok(int* failedStep) const {
   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)
+  if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)
       return false;
 
-  if ((*step)++, debugBitboards)
+  if ((*step)++, testBitboards)
   {
       // The intersection of the white and black pieces must be empty
       if (pieces(WHITE) & pieces(BLACK))
@@ -1311,33 +1226,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)++, 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)++, 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)++, testKingCapture)
+      if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove))
           return false;
 
-  if ((*step)++, debugPieceCounts)
+  if ((*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)++, testPieceList)
       for (Color c = WHITE; c <= BLACK; ++c)
           for (PieceType pt = PAWN; pt <= KING; ++pt)
               for (int i = 0; i < pieceCount[c][pt];  ++i)
@@ -1345,7 +1263,7 @@ bool Position::pos_is_ok(int* failedStep) const {
                       || index[pieceList[c][pt][i]] != i)
                       return false;
 
-  if ((*step)++, debugCastlingSquares)
+  if ((*step)++, testCastlingSquares)
       for (Color c = WHITE; c <= BLACK; ++c)
           for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
           {