]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Restore "fail-low of reduced" and close regression
[stockfish] / src / search.cpp
index e7fbbcf3dcb3566fc794870fd5b8e05b9fcf5d21..e8809f149170c31ceeea0ff821cd2751932506df 100644 (file)
@@ -99,6 +99,7 @@ namespace {
   Value value_to_tt(Value v, int ply);
   Value value_from_tt(Value v, int ply);
   bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta);
+  bool allows_move(const Position& pos, Move first, Move second);
   bool prevents_move(const Position& pos, Move first, Move second);
   string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
 
@@ -534,7 +535,7 @@ namespace {
     if (!RootNode)
     {
         // Step 2. Check for aborted search and immediate draw
-        if (Signals.stop || pos.is_draw<true, PvNode>() || ss->ply > MAX_PLY)
+        if (Signals.stop || pos.is_draw<false>() || ss->ply > MAX_PLY)
             return DrawValue[pos.side_to_move()];
 
         // Step 3. Mate distance pruning. Even if we mate at the next move our score
@@ -667,12 +668,12 @@ namespace {
         if (eval - PawnValueMg > beta)
             R += ONE_PLY;
 
-        pos.do_null_move<true>(st);
+        pos.do_null_move(st);
         (ss+1)->skipNullMove = true;
         nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
                                       : - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
         (ss+1)->skipNullMove = false;
-        pos.do_null_move<false>(st);
+        pos.undo_null_move();
 
         if (nullValue >= beta)
         {
@@ -692,9 +693,21 @@ namespace {
                 return nullValue;
         }
         else
+        {
             // The null move failed low, which means that we may be faced with
-            // some kind of threat.
+            // some kind of threat. If the previous move was reduced, check if
+            // the move that refuted the null move was somehow connected to the
+            // move which was reduced. If a connection is found, return a fail
+            // low score (which will cause the reduced move to fail high in the
+            // parent node, which will trigger a re-search with full depth).
             threatMove = (ss+1)->currentMove;
+
+            if (   depth < 5 * ONE_PLY
+                && (ss-1)->reduction
+                && threatMove != MOVE_NONE
+                && allows_move(pos, (ss-1)->currentMove, threatMove))
+                return beta - 1;
+        }
     }
 
     // Step 9. ProbCut (is omitted in PV nodes)
@@ -835,7 +848,7 @@ split_point_start: // At split points actual search starts from here
           ss->excludedMove = MOVE_NONE;
 
           if (value < rBeta)
-              ext = rBeta >= beta ? ONE_PLY + ONE_PLY / 2 : ONE_PLY;
+              ext = ONE_PLY;
       }
 
       // Update current move (this must be done after singular extension search)
@@ -847,12 +860,13 @@ split_point_start: // At split points actual search starts from here
           && !inCheck
           && !dangerous
           &&  move != ttMove
-          && (!threatMove || !prevents_move(pos, move, threatMove))
           && (bestValue > VALUE_MATED_IN_MAX_PLY || (   bestValue == -VALUE_INFINITE
                                                      && alpha > VALUE_MATED_IN_MAX_PLY)))
       {
           // Move count based pruning
-          if (depth < 16 * ONE_PLY && moveCount >= FutilityMoveCounts[depth])
+          if (   depth < 16 * ONE_PLY
+              && moveCount >= FutilityMoveCounts[depth]
+              && (!threatMove || !prevents_move(pos, move, threatMove)))
           {
               if (SpNode)
                   sp->mutex.lock();
@@ -1109,7 +1123,7 @@ split_point_start: // At split points actual search starts from here
     ss->ply = (ss-1)->ply + 1;
 
     // Check for an instant draw or maximum ply reached
-    if (pos.is_draw<false, false>() || ss->ply > MAX_PLY)
+    if (pos.is_draw<true>() || ss->ply > MAX_PLY)
         return DrawValue[pos.side_to_move()];
 
     // Transposition table lookup. At PV nodes, we don't use the TT for
@@ -1352,6 +1366,47 @@ split_point_start: // At split points actual search starts from here
   }
 
 
+  // allows_move() tests whether the move at previous ply (first) somehow makes a
+  // second move possible, for instance if the moving piece is the same in both
+  // moves. Normally the second move is the threat move (the best move returned
+  // from a null search that fails low).
+
+  bool allows_move(const Position& pos, Move first, Move second) {
+
+    assert(is_ok(first));
+    assert(is_ok(second));
+    assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
+    assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
+
+    Square m1from = from_sq(first);
+    Square m2from = from_sq(second);
+    Square m1to = to_sq(first);
+    Square m2to = to_sq(second);
+
+    // The piece is the same or second's destination was vacated by the first move
+    if (m1to == m2from || m2to == m1from)
+        return true;
+
+    // Second one moves through the square vacated by first one
+    if (between_bb(m2from, m2to) & m1from)
+      return true;
+
+    // Second's destination is defended by the first move's piece
+    Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from);
+    if (m1att & m2to)
+        return true;
+
+    // Second move gives a discovered check through the first's checking piece
+    if (m1att & pos.king_square(pos.side_to_move()))
+    {
+        assert(between_bb(m1to, pos.king_square(pos.side_to_move())) & m2from);
+        return true;
+    }
+
+    return false;
+  }
+
+
   // prevents_move() tests whether a move (first) is able to defend against an
   // opponent's move (second). In this case will not be pruned. Normally the
   // second move is the threat move (the best move returned from a null search
@@ -1517,7 +1572,7 @@ void RootMove::extract_pv_from_tt(Position& pos) {
            && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change
            && pos.pl_move_is_legal(m, pos.pinned_pieces())
            && ply < MAX_PLY
-           && (!pos.is_draw<true, true>() || ply < 2));
+           && (!pos.is_draw<false>() || ply < 2));
 
   pv.push_back(MOVE_NONE); // Must be zero-terminating