]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Retire test for king moves in see()
[stockfish] / src / position.cpp
index 48614fdfa14d4303e64525f02d5f789e81b3f248..a9c98626980fa56bbf5995e2c0d186f342fddef2 100644 (file)
@@ -29,6 +29,7 @@
 #include "position.h"
 #include "psqtab.h"
 #include "rkiss.h"
+#include "thread.h"
 #include "tt.h"
 #include "ucioption.h"
 
@@ -622,7 +623,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
 
 bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
 {
-  assert(is_check());
+  assert(in_check());
 
   Color us = side_to_move();
   Square from = move_from(m);
@@ -643,15 +644,124 @@ bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
   return bit_is_set(target, to) && pl_move_is_legal(m, pinned);
 }
 
+/// Position::move_is_legal() takes a position and a (not necessarily pseudo-legal)
+/// move and tests whether the move is legal. This version is not very fast and
+/// should be used only in non time-critical paths.
 
-/// Position::move_is_check() tests whether a pseudo-legal move is a check
+bool Position::move_is_legal(const Move m) const {
 
-bool Position::move_is_check(Move m) const {
+  MoveStack mlist[MAX_MOVES];
+  MoveStack *cur, *last = generate<MV_PSEUDO_LEGAL>(*this, mlist);
 
-  return move_is_check(m, CheckInfo(*this));
+   for (cur = mlist; cur != last; cur++)
+      if (cur->move == m)
+          return pl_move_is_legal(m, pinned_pieces(sideToMove));
+
+  return false;
+}
+
+
+/// Fast version of Position::move_is_legal() that takes a position a move and
+/// a bitboard of pinned pieces as input, and tests whether the move is legal.
+
+bool Position::move_is_legal(const Move m, Bitboard pinned) const {
+
+  assert(is_ok());
+  assert(pinned == pinned_pieces(sideToMove));
+
+  Color us = sideToMove;
+  Color them = opposite_color(sideToMove);
+  Square from = move_from(m);
+  Square to = move_to(m);
+  Piece pc = piece_on(from);
+
+  // Use a slower but simpler function for uncommon cases
+  if (move_is_special(m))
+      return move_is_legal(m);
+
+  // If the from square is not occupied by a piece belonging to the side to
+  // move, the move is obviously not legal.
+  if (color_of_piece(pc) != us)
+      return false;
+
+  // The destination square cannot be occupied by a friendly piece
+  if (color_of_piece_on(to) == us)
+      return false;
+
+  // Handle the special case of a pawn move
+  if (type_of_piece(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 8/1th rank.
+      if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1)
+          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 (color_of_piece_on(to) != them)
+              return false;
+          break;
+
+      case DELTA_N:
+      case DELTA_S:
+      // Pawn push. The destination square must be empty.
+          if (!square_is_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 (   square_rank(to) != RANK_4
+          || !square_is_empty(to)
+          || !square_is_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 (   square_rank(to) != RANK_5
+              || !square_is_empty(to)
+              || !square_is_empty(from + DELTA_S))
+              return false;
+          break;
+
+      default:
+          return false;
+      }
+  }
+  else if (!bit_is_set(attacks_from(pc, from), to))
+      return false;
+
+  // The move is pseudo-legal, check if it is also legal
+  return in_check() ? pl_move_is_evasion(m, pinned) : pl_move_is_legal(m, pinned);
+}
+
+
+/// Position::move_gives_check() tests whether a pseudo-legal move is a check
+
+bool Position::move_gives_check(Move m) const {
+
+  return move_gives_check(m, CheckInfo(*this));
 }
 
-bool Position::move_is_check(Move m, const CheckInfo& ci) const {
+bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
 
   assert(is_ok());
   assert(move_is_ok(m));
@@ -743,9 +853,8 @@ bool Position::move_is_check(Move m, const CheckInfo& ci) const {
 }
 
 
-/// Position::do_setup_move() makes a permanent move on the board.
-/// It should be used when setting up a position on board.
-/// You can't undo the move.
+/// Position::do_setup_move() makes a permanent move on the board. It should
+/// be used when setting up a position on board. You can't undo the move.
 
 void Position::do_setup_move(Move m) {
 
@@ -762,18 +871,19 @@ void Position::do_setup_move(Move m) {
   startPosPlyCounter++;
 
   // Our StateInfo newSt is about going out of scope so copy
-  // its content inside pos before it disappears.
+  // its content before it disappears.
   detach();
 }
 
+
 /// Position::do_move() makes a move, and saves all information necessary
-/// to a StateInfo object. The move is assumed to be legal.
-/// Pseudo-legal moves should be filtered out before this function is called.
+/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
+/// moves should be filtered out before this function is called.
 
 void Position::do_move(Move m, StateInfo& newSt) {
 
   CheckInfo ci(*this);
-  do_move(m, newSt, ci, move_is_check(m, ci));
+  do_move(m, newSt, ci, move_gives_check(m, ci));
 }
 
 void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
@@ -885,7 +995,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
 
       // Update pawn hash key and prefetch in L1/L2 cache
       st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
-      prefetchPawn(st->pawnKey, threadID);
 
       // Set en passant square, only if moved pawn can be captured
       if ((to ^ from) == 16)
@@ -938,6 +1047,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
       }
   }
 
+  // Prefetch pawn and material hash tables
+  Threads[threadID].pawnTable.prefetch(st->pawnKey);
+  Threads[threadID].materialTable.prefetch(st->materialKey);
+
   // Update incremental scores
   st->value += pst_delta(piece, from, to);
 
@@ -1317,7 +1430,7 @@ void Position::undo_castle_move(Move m) {
 void Position::do_null_move(StateInfo& backupSt) {
 
   assert(is_ok());
-  assert(!is_check());
+  assert(!in_check());
 
   // Back up the information necessary to undo the null move to the supplied
   // StateInfo object.
@@ -1354,7 +1467,7 @@ void Position::do_null_move(StateInfo& backupSt) {
 void Position::undo_null_move() {
 
   assert(is_ok());
-  assert(!is_check());
+  assert(!in_check());
 
   // Restore information from the our backup StateInfo object
   StateInfo* backupSt = st->previous;
@@ -1377,12 +1490,6 @@ void Position::undo_null_move() {
 /// move, and one which takes a 'from' and a 'to' square. The function does
 /// not yet understand promotions captures.
 
-int Position::see(Move m) const {
-
-  assert(move_is_ok(m));
-  return see(move_from(m), move_to(m));
-}
-
 int Position::see_sign(Move m) const {
 
   assert(move_is_ok(m));
@@ -1396,25 +1503,22 @@ int Position::see_sign(Move m) const {
   if (midgame_value_of_piece_on(to) >= midgame_value_of_piece_on(from))
       return 1;
 
-  return see(from, to);
+  return see(m);
 }
 
-int Position::see(Square from, Square to) const {
+int Position::see(Move m) const {
 
+  Square from, to;
   Bitboard occupied, attackers, stmAttackers, b;
   int swapList[32], slIndex = 1;
   PieceType capturedType, pt;
   Color stm;
 
-  assert(square_is_ok(from));
-  assert(square_is_ok(to));
+  assert(move_is_ok(m));
 
+  from = move_from(m);
+  to = move_to(m);
   capturedType = type_of_piece_on(to);
-
-  // King cannot be recaptured
-  if (capturedType == KING)
-      return seeValues[capturedType];
-
   occupied = occupied_squares();
 
   // Handle en passant moves
@@ -1684,48 +1788,8 @@ bool Position::is_draw() const {
 
 bool Position::is_mate() const {
 
-  MoveStack moves[MOVES_MAX];
-  return is_check() && generate<MV_LEGAL>(*this, moves) == moves;
-}
-
-
-/// Position::has_mate_threat() tests whether the side to move is under
-/// a threat of being mated in one from the current position.
-
-bool Position::has_mate_threat() {
-
-  MoveStack mlist[MOVES_MAX], *last, *cur;
-  StateInfo st1, st2;
-  bool mateFound = false;
-
-  // If we are under check it's up to evasions to do the job
-  if (is_check())
-      return false;
-
-  // First pass the move to our opponent doing a null move
-  do_null_move(st1);
-
-  // Then generate pseudo-legal moves that could give check
-  last = generate<MV_NON_CAPTURE_CHECK>(*this, mlist);
-  last = generate<MV_CAPTURE>(*this, last);
-
-  // Loop through the moves, and see if one of them gives mate
-  Bitboard pinned = pinned_pieces(sideToMove);
-  CheckInfo ci(*this);
-  for (cur = mlist; !mateFound && cur != last; cur++)
-  {
-      Move move = cur->move;
-      if (   !pl_move_is_legal(move, pinned)
-          || !move_is_check(move, ci))
-          continue;
-
-      do_move(move, st2, ci, true);
-      mateFound = is_mate();
-      undo_move(move);
-  }
-
-  undo_null_move();
-  return mateFound;
+  MoveStack moves[MAX_MOVES];
+  return in_check() && generate<MV_LEGAL>(*this, moves) == moves;
 }
 
 
@@ -1769,13 +1833,15 @@ void Position::init_piece_square_tables() {
 }
 
 
-/// Position::flipped_copy() makes a copy of the input position, but with
-/// the white and black sides reversed. This is only useful for debugging,
-/// especially for finding evaluation symmetry bugs.
+/// Position::flip() flips position with the white and black sides reversed. This
+/// is only useful for debugging especially for finding evaluation symmetry bugs.
 
-void Position::flipped_copy(const Position& pos) {
+void Position::flip() {
+
+  assert(is_ok());
 
-  assert(pos.is_ok());
+  // Make a copy of current position before to start changing
+  const Position pos(*this, threadID);
 
   clear();
   threadID = pos.thread();