]> git.sesse.net Git - stockfish/blobdiff - src/position.cpp
Bug wrong evasion detection for king moves
[stockfish] / src / position.cpp
index 2172180cfb694783feddebd048e7546133475f98..52aa93ebddc918493fd720a58dace0b1ab1d8d17 100644 (file)
@@ -490,6 +490,16 @@ Bitboard Position::attackers_to(Square s) const {
         | (attacks_from<KING>(s)        & pieces(KING));
 }
 
+Bitboard Position::attackers_to(Square s, Bitboard occ) const {
+
+  return  (attacks_from<PAWN>(s, BLACK) & pieces(PAWN, WHITE))
+        | (attacks_from<PAWN>(s, WHITE) & pieces(PAWN, BLACK))
+        | (attacks_from<KNIGHT>(s)      & pieces(KNIGHT))
+        | (rook_attacks_bb(s, occ)      & pieces(ROOK, QUEEN))
+        | (bishop_attacks_bb(s, occ)    & pieces(BISHOP, QUEEN))
+        | (attacks_from<KING>(s)        & pieces(KING));
+}
+
 /// Position::attacks_from() computes a bitboard of all attacks
 /// of a given piece put in a given square.
 
@@ -570,9 +580,11 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
   assert(move_is_ok(m));
   assert(pinned == pinned_pieces(side_to_move()));
 
-  // Castling moves are checked for legality during move generation.
-  if (move_is_castle(m))
-      return true;
+  Color us = side_to_move();
+  Square from = move_from(m);
+
+  assert(color_of_piece_on(from) == us);
+  assert(piece_on(king_square(us)) == make_piece(us, KING));
 
   // En passant captures are a tricky special case. Because they are
   // rather uncommon, we do it simply by testing whether the king is attacked
@@ -600,16 +612,11 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
             && !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them));
   }
 
-  Color us = side_to_move();
-  Square from = move_from(m);
-
-  assert(color_of_piece_on(from) == us);
-  assert(piece_on(king_square(us)) == make_piece(us, KING));
-
   // If the moving piece is a king, check whether the destination
-  // square is attacked by the opponent.
+  // square is attacked by the opponent. Castling moves are checked
+  // for legality during move generation.
   if (type_of_piece_on(from) == KING)
-      return !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us)));
+      return move_is_castle(m) || !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us)));
 
   // A non-king move is legal if and only if it is not pinned or it
   // is moving along the ray towards or away from the king.
@@ -619,31 +626,6 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
 }
 
 
-/// Position::pl_move_is_evasion() tests whether a pseudo-legal move is a legal evasion
-
-bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
-{
-  assert(is_check());
-
-  Color us = side_to_move();
-  Square from = move_from(m);
-  Square to = move_to(m);
-
-  // King moves and en-passant captures are verified in pl_move_is_legal()
-  if (type_of_piece_on(from) == KING || move_is_ep(m))
-      return pl_move_is_legal(m, pinned);
-
-  Bitboard target = checkers();
-  Square checksq = pop_1st_bit(&target);
-
-  if (target) // double check ?
-      return false;
-
-  // Our move must be a blocking evasion or a capture of the checking piece
-  target = squares_between(checksq, king_square(us)) | checkers();
-  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.
@@ -679,9 +661,13 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const {
   if (move_is_special(m))
       return move_is_legal(m);
 
+  // Is not a promotion, so promotion piece must be empty
+  if (move_promotion_piece(m) - 2 != PIECE_TYPE_NONE)
+      return false;
+
   // 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)
+  if (pc == PIECE_NONE || color_of_piece(pc) != us)
       return false;
 
   // The destination square cannot be occupied by a friendly piece
@@ -711,16 +697,20 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const {
       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;
+      if (color_of_piece_on(to) != them)
+          return false;
+
+      // From and to files must be one file apart, avoids a7h5
+      if (abs(square_file(from) - square_file(to)) != 1)
+          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;
+      if (!square_is_empty(to))
+          return false;
+      break;
 
       case DELTA_NN:
       // Double white pawn push. The destination square must be on the fourth
@@ -730,17 +720,17 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const {
           || !square_is_empty(to)
           || !square_is_empty(from + DELTA_N))
           return false;
-          break;
+      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;
+      if (   square_rank(to) != RANK_5
+          || !square_is_empty(to)
+          || !square_is_empty(from + DELTA_S))
+          return false;
+      break;
 
       default:
           return false;
@@ -749,19 +739,45 @@ bool Position::move_is_legal(const Move m, Bitboard pinned) const {
   else if (!bit_is_set(attacks_from(pc, from), to))
       return false;
 
+  if (in_check())
+  {
+      // In case of king moves under check we have to remove king so to catch
+      // as invalid moves like b1a1 when opposite queen is on c1.
+      if (type_of_piece_on(from) == KING)
+      {
+          Bitboard b = occupied_squares();
+          clear_bit(&b, from);
+          if (attackers_to(move_to(m), b) & pieces_of_color(opposite_color(us)))
+              return false;
+      }
+      else
+      {
+          Bitboard target = checkers();
+          Square checksq = pop_1st_bit(&target);
+
+          if (target) // double check ? In this case a king move is required
+              return false;
+
+          // Our move must be a blocking evasion or a capture of the checking piece
+          target = squares_between(checksq, king_square(us)) | checkers();
+          if (!bit_is_set(target, move_to(m)))
+              return false;
+      }
+  }
+
   // The move is pseudo-legal, check if it is also legal
-  return is_check() ? pl_move_is_evasion(m, pinned) : pl_move_is_legal(m, pinned);
+  return pl_move_is_legal(m, pinned);
 }
 
 
-/// Position::move_is_check() tests whether a pseudo-legal move is a check
+/// Position::move_gives_check() tests whether a pseudo-legal move is a check
 
-bool Position::move_is_check(Move m) const {
+bool Position::move_gives_check(Move m) const {
 
-  return move_is_check(m, CheckInfo(*this));
+  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));
@@ -853,9 +869,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) {
 
@@ -872,18 +887,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) {
@@ -1048,8 +1064,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
   }
 
   // Prefetch pawn and material hash tables
-  ThreadsMgr[threadID].pawnTable.prefetch(st->pawnKey);
-  ThreadsMgr[threadID].materialTable.prefetch(st->materialKey);
+  Threads[threadID].pawnTable.prefetch(st->pawnKey);
+  Threads[threadID].materialTable.prefetch(st->materialKey);
 
   // Update incremental scores
   st->value += pst_delta(piece, from, to);
@@ -1430,7 +1446,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.
@@ -1467,7 +1483,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;
@@ -1490,12 +1506,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));
@@ -1509,25 +1519,28 @@ 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));
-
-  capturedType = type_of_piece_on(to);
+  assert(move_is_ok(m));
 
-  // King cannot be recaptured
-  if (capturedType == KING)
-      return seeValues[capturedType];
+  // As castle moves are implemented as capturing the rook, they have
+  // SEE == RookValueMidgame most of the times (unless the rook is under
+  // attack).
+  if (move_is_castle(m))
+      return 0;
 
+  from = move_from(m);
+  to = move_to(m);
+  capturedType = type_of_piece_on(to);
   occupied = occupied_squares();
 
   // Handle en passant moves
@@ -1546,12 +1559,7 @@ int Position::see(Square from, Square to) const {
   // Find all attackers to the destination square, with the moving piece
   // removed, but possibly an X-ray attacker added behind it.
   clear_bit(&occupied, from);
-  attackers =  (rook_attacks_bb(to, occupied)  & pieces(ROOK, QUEEN))
-             | (bishop_attacks_bb(to, occupied)& pieces(BISHOP, QUEEN))
-             | (attacks_from<KNIGHT>(to)       & pieces(KNIGHT))
-             | (attacks_from<KING>(to)         & pieces(KING))
-             | (attacks_from<PAWN>(to, WHITE)  & pieces(PAWN, BLACK))
-             | (attacks_from<PAWN>(to, BLACK)  & pieces(PAWN, WHITE));
+  attackers = attackers_to(to, occupied);
 
   // If the opponent has no attackers we are finished
   stm = opposite_color(color_of_piece_on(from));
@@ -1798,7 +1806,7 @@ bool Position::is_draw() const {
 bool Position::is_mate() const {
 
   MoveStack moves[MAX_MOVES];
-  return is_check() && generate<MV_LEGAL>(*this, moves) == moves;
+  return in_check() && generate<MV_LEGAL>(*this, moves) == moves;
 }
 
 
@@ -1842,13 +1850,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();