]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Move time related global variables under TimeManager
[stockfish] / src / search.cpp
index 29b7c5f3ebab93a78b832a23905a0e07af535800..fa7052d0dc0c2605201a17bd283742541e1c3c59 100644 (file)
@@ -38,6 +38,7 @@
 #include "lock.h"
 #include "san.h"
 #include "search.h"
+#include "timeman.h"
 #include "thread.h"
 #include "tt.h"
 #include "ucioption.h"
@@ -198,7 +199,7 @@ namespace {
   Depth PassedPawnExtension[2], PawnEndgameExtension[2], MateThreatExtension[2];
 
   // Minimum depth for use of singular extension
-  const Depth SingularExtensionDepth[2] = { 8 * OnePly /* non-PV */, 6 * OnePly /* PV */};
+  const Depth SingularExtensionDepth[2] = { 7 * OnePly /* non-PV */, 6 * OnePly /* PV */};
 
   // If the TT move is at least SingularExtensionMargin better then the
   // remaining ones we will extend it.
@@ -250,10 +251,10 @@ namespace {
   int MultiPV;
 
   // Time managment variables
-  int SearchStartTime, MaxNodes, MaxDepth, MaxSearchTime;
-  int AbsoluteMaxSearchTime, ExtraSearchTime, ExactMaxTime;
+  int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime;
   bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit;
   bool FirstRootMove, AbortSearch, Quit, AspirationFailLow;
+  TimeManager TimeMgr;
 
   // Log file
   bool UseLogFile;
@@ -400,7 +401,6 @@ bool think(const Position& pos, bool infinite, bool ponder, int time[], int incr
 
   // Initialize global search variables
   StopOnPonderhit = AbortSearch = Quit = AspirationFailLow = false;
-  MaxSearchTime = AbsoluteMaxSearchTime = ExtraSearchTime = 0;
   NodesSincePoll = 0;
   TM.resetNodeCounters();
   SearchStartTime = get_system_time();
@@ -472,40 +472,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int time[], int incr
   int myTime = time[pos.side_to_move()];
   int myIncrement = increment[pos.side_to_move()];
   if (UseTimeManagement)
-  {
-      if (!movesToGo) // Sudden death time control
-      {
-          if (myIncrement)
-          {
-              MaxSearchTime = myTime / 30 + myIncrement;
-              AbsoluteMaxSearchTime = Max(myTime / 4, myIncrement - 100);
-          }
-          else // Blitz game without increment
-          {
-              MaxSearchTime = myTime / 30;
-              AbsoluteMaxSearchTime = myTime / 8;
-          }
-      }
-      else // (x moves) / (y minutes)
-      {
-          if (movesToGo == 1)
-          {
-              MaxSearchTime = myTime / 2;
-              AbsoluteMaxSearchTime = (myTime > 3000)? (myTime - 500) : ((myTime * 3) / 4);
-          }
-          else
-          {
-              MaxSearchTime = myTime / Min(movesToGo, 20);
-              AbsoluteMaxSearchTime = Min((4 * myTime) / movesToGo, myTime / 3);
-          }
-      }
-
-      if (get_option_value_bool("Ponder"))
-      {
-          MaxSearchTime += MaxSearchTime / 4;
-          MaxSearchTime = Min(MaxSearchTime, AbsoluteMaxSearchTime);
-      }
-  }
+      TimeMgr.update(myTime, myIncrement, movesToGo, pos.startpos_ply_counter());
 
   // Set best NodesBetweenPolls interval to avoid lagging under
   // heavy time pressure.
@@ -649,20 +616,20 @@ namespace {
             if (   Iteration >= 8
                 && EasyMove == pv[0]
                 && (  (   rml.get_move_cumulative_nodes(0) > (nodes * 85) / 100
-                       && current_search_time() > MaxSearchTime / 16)
+                       && current_search_time() > TimeMgr.optimumSearchTime / 16)
                     ||(   rml.get_move_cumulative_nodes(0) > (nodes * 98) / 100
-                       && current_search_time() > MaxSearchTime / 32)))
+                       && current_search_time() > TimeMgr.optimumSearchTime / 32)))
                 stopSearch = true;
 
             // Add some extra time if the best move has changed during the last two iterations
             if (Iteration > 5 && Iteration <= 50)
-                ExtraSearchTime = BestMoveChangesByIteration[Iteration]   * (MaxSearchTime / 2)
-                                + BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
+                TimeMgr.best_move_changes(BestMoveChangesByIteration[Iteration],
+                                          BestMoveChangesByIteration[Iteration-1]);
 
             // Stop search if most of MaxSearchTime is consumed at the end of the
             // iteration. We probably don't have enough time to search the first
             // move at the next iteration anyway.
-            if (current_search_time() > ((MaxSearchTime + ExtraSearchTime) * 80) / 128)
+            if (current_search_time() > (TimeMgr.available_time() * 80) / 128)
                 stopSearch = true;
 
             if (stopSearch)
@@ -1002,7 +969,7 @@ namespace {
     Move movesSearched[256];
     EvalInfo ei;
     StateInfo st;
-    const TTEntry* tte;
+    const TTEntry *tte, *ttx;
     Key posKey;
     Move ttMove, move, excludedMove, threatMove;
     Depth ext, newDepth;
@@ -1062,32 +1029,31 @@ namespace {
         // Refresh tte entry to avoid aging
         TT.store(posKey, tte->value(), tte->type(), tte->depth(), ttMove, tte->static_value(), tte->king_danger());
 
-        ss->currentMove = ttMove; // Can be MOVE_NONE
+        ss->bestMove = ttMove; // Can be MOVE_NONE
         return value_from_tt(tte->value(), ply);
     }
 
-    // Step 5. Evaluate the position statically
-    // At PV nodes we do this only to update gain statistics
+    // Step 5. Evaluate the position statically and
+    // update gain statistics of parent move.
     isCheck = pos.is_check();
-    if (!isCheck)
+    if (isCheck)
+        ss->eval = VALUE_NONE;
+    else if (tte)
     {
-        if (tte)
-        {
-            assert(tte->static_value() != VALUE_NONE);
-            ss->eval = tte->static_value();
-            ei.kingDanger[pos.side_to_move()] = tte->king_danger();
-        }
-        else
-        {
-            ss->eval = evaluate(pos, ei);
-            TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
-        }
+        assert(tte->static_value() != VALUE_NONE);
 
-        refinedValue = refine_eval(tte, ss->eval, ply); // Enhance accuracy with TT value if possible
-        update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
+        ss->eval = tte->static_value();
+        ei.kingDanger[pos.side_to_move()] = tte->king_danger();
+        refinedValue = refine_eval(tte, ss->eval, ply);
     }
     else
-        ss->eval = VALUE_NONE;
+    {
+        refinedValue = ss->eval = evaluate(pos, ei);
+        TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+    }
+
+    // Save gain for the parent non-capture move
+    update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
 
     // Step 6. Razoring (is omitted in PV nodes)
     if (   !PvNode
@@ -1113,8 +1079,8 @@ namespace {
     if (   !PvNode
         && !ss->skipNullMove
         &&  depth < RazorDepth
-        &&  refinedValue >= beta + futility_margin(depth, 0)
         && !isCheck
+        &&  refinedValue >= beta + futility_margin(depth, 0)
         && !value_is_mate(beta)
         &&  pos.non_pawn_material(pos.side_to_move()))
         return refinedValue - futility_margin(depth, 0);
@@ -1126,8 +1092,8 @@ namespace {
     if (   !PvNode
         && !ss->skipNullMove
         &&  depth > OnePly
-        &&  refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0)
         && !isCheck
+        &&  refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0)
         && !value_is_mate(beta)
         &&  pos.non_pawn_material(pos.side_to_move()))
     {
@@ -1176,7 +1142,7 @@ namespace {
             if (nullValue == value_mated_in(ply + 2))
                 mateThreat = true;
 
-            threatMove = (ss+1)->currentMove;
+            threatMove = (ss+1)->bestMove;
             if (   depth < ThreatDepth
                 && (ss-1)->reduction
                 && connected_moves(pos, (ss-1)->currentMove, threatMove))
@@ -1206,9 +1172,11 @@ namespace {
     // Initialize a MovePicker object for the current position
     MovePicker mp = MovePicker(pos, ttMove, depth, H, ss, (PvNode ? -VALUE_INFINITE : beta));
     CheckInfo ci(pos);
+    ss->bestMove = MOVE_NONE;
     singleEvasion = isCheck && mp.number_of_evasions() == 1;
     singularExtensionNode =   depth >= SingularExtensionDepth[PvNode]
-                           && tte && tte->move()
+                           && tte
+                           && tte->move()
                            && !excludedMove // Do not allow recursive singular extension search
                            && is_lower_bound(tte->type())
                            && tte->depth() >= depth - 3 * OnePly;
@@ -1238,9 +1206,22 @@ namespace {
           && move == tte->move()
           && ext < OnePly)
       {
+          // Avoid to do an expensive singular extension search on nodes where
+          // such search have already been done in the past, so assume the last
+          // singular extension search result is still valid.
+          if (  !PvNode
+              && depth < SingularExtensionDepth[PvNode] + 5 * OnePly
+              && (ttx = TT.retrieve(pos.get_exclusion_key())) != NULL)
+          {
+              if (is_upper_bound(ttx->type()))
+                  ext = OnePly;
+
+              singularExtensionNode = false;
+          }
+
           Value ttValue = value_from_tt(tte->value(), ply);
 
-          if (abs(ttValue) < VALUE_KNOWN_WIN)
+          if (singularExtensionNode && abs(ttValue) < VALUE_KNOWN_WIN)
           {
               Value b = ttValue - SingularExtensionMargin;
               ss->excludedMove = move;
@@ -1248,6 +1229,7 @@ namespace {
               Value v = search<NonPV>(pos, ss, b - 1, b, depth / 2, ply);
               ss->skipNullMove = false;
               ss->excludedMove = MOVE_NONE;
+              ss->bestMove = MOVE_NONE;
               if (v < b)
                   ext = OnePly;
           }
@@ -1357,7 +1339,7 @@ namespace {
           bestValue = value;
           if (value > alpha)
           {
-              if (PvNode && value < beta) // This guarantees that always: alpha < beta
+              if (PvNode && value < beta) // We want always alpha < beta
                   alpha = value;
 
               if (value == value_mate_in(ply + 1))
@@ -1384,7 +1366,7 @@ namespace {
     // no legal moves, it must be mate or stalemate.
     // If one move was excluded return fail low score.
     if (!moveCount)
-        return excludedMove ? oldAlpha : (isCheck ? value_mated_in(ply) : VALUE_DRAW);
+        return excludedMove ? oldAlpha : isCheck ? value_mated_in(ply) : VALUE_DRAW;
 
     // Step 20. Update tables
     // If the search is not aborted, update the transposition table,
@@ -1392,9 +1374,9 @@ namespace {
     if (AbortSearch || TM.thread_should_stop(threadID))
         return bestValue;
 
-    ValueType f = (bestValue <= oldAlpha ? VALUE_TYPE_UPPER : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT);
+    ValueType vt = (bestValue <= oldAlpha ? VALUE_TYPE_UPPER : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT);
     move = (bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove);
-    TT.store(posKey, value_to_tt(bestValue, ply), f, depth, move, ss->eval, ei.kingDanger[pos.side_to_move()]);
+    TT.store(posKey, value_to_tt(bestValue, ply), vt, depth, move, ss->eval, ei.kingDanger[pos.side_to_move()]);
 
     // Update killers and history only for non capture moves that fails high
     if (bestValue >= beta)
@@ -1449,7 +1431,7 @@ namespace {
 
     if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
     {
-        ss->currentMove = ttMove; // Can be MOVE_NONE
+        ss->bestMove = ttMove; // Can be MOVE_NONE
         return value_from_tt(tte->value(), ply);
     }
 
@@ -1467,6 +1449,7 @@ namespace {
         if (tte)
         {
             assert(tte->static_value() != VALUE_NONE);
+
             ei.kingDanger[pos.side_to_move()] = tte->king_danger();
             bestValue = tte->static_value();
         }
@@ -1576,8 +1559,8 @@ namespace {
 
     // Update transposition table
     Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1));
-    ValueType f = (bestValue <= oldAlpha ? VALUE_TYPE_UPPER : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT);
-    TT.store(pos.get_key(), value_to_tt(bestValue, ply), f, d, ss->bestMove, ss->eval, ei.kingDanger[pos.side_to_move()]);
+    ValueType vt = (bestValue <= oldAlpha ? VALUE_TYPE_UPPER : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT);
+    TT.store(pos.get_key(), value_to_tt(bestValue, ply), vt, d, ss->bestMove, ss->eval, ei.kingDanger[pos.side_to_move()]);
 
     // Update killers only for checking moves that fails high
     if (    bestValue >= beta
@@ -1996,8 +1979,7 @@ namespace {
 
   Value refine_eval(const TTEntry* tte, Value defaultEval, int ply) {
 
-      if (!tte)
-          return defaultEval;
+      assert(tte);
 
       Value v = value_from_tt(tte->value(), ply);
 
@@ -2053,8 +2035,7 @@ namespace {
         && before != VALUE_NONE
         && after != VALUE_NONE
         && pos.captured_piece() == NO_PIECE_TYPE
-        && !move_is_castle(m)
-        && !move_is_promotion(m))
+        && !move_is_special(m))
         H.set_gain(pos.piece_on(move_to(m)), move_to(m), -(before + after));
   }
 
@@ -2154,9 +2135,9 @@ namespace {
 
     bool stillAtFirstMove =    FirstRootMove
                            && !AspirationFailLow
-                           &&  t > MaxSearchTime + ExtraSearchTime;
+                           &&  t > TimeMgr.available_time();
 
-    bool noMoreTime =   t > AbsoluteMaxSearchTime
+    bool noMoreTime =   t > TimeMgr.maximumSearchTime
                      || stillAtFirstMove;
 
     if (   (Iteration >= 3 && UseTimeManagement && noMoreTime)
@@ -2177,9 +2158,9 @@ namespace {
 
     bool stillAtFirstMove =    FirstRootMove
                            && !AspirationFailLow
-                           &&  t > MaxSearchTime + ExtraSearchTime;
+                           &&  t > TimeMgr.available_time();
 
-    bool noMoreTime =   t > AbsoluteMaxSearchTime
+    bool noMoreTime =   t > TimeMgr.maximumSearchTime
                      || stillAtFirstMove;
 
     if (Iteration >= 3 && UseTimeManagement && (noMoreTime || StopOnPonderhit))