]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Implement futility move count array
[stockfish] / src / search.cpp
index d1f29b8c404c6e50c3c4df964bd126a655c39a09..e1ac91c3f58e6a3a22f07477ed62b3c893bb9f9d 100644 (file)
@@ -126,9 +126,6 @@ namespace {
   // Search depth at iteration 1
   const Depth InitialDepth = OnePly;
 
-  // Depth limit for selective search
-  const Depth SelectiveDepth = 7 * OnePly;
-
   // Use internal iterative deepening?
   const bool UseIIDAtPVNodes = true;
   const bool UseIIDAtNonPVNodes = true;
@@ -142,17 +139,6 @@ namespace {
   // better than the second best move.
   const Value EasyMoveMargin = Value(0x200);
 
-  // Problem margin. If the score of the first move at iteration N+1 has
-  // dropped by more than this since iteration N, the boolean variable
-  // "Problem" is set to true, which will make the program spend some extra
-  // time looking for a better move.
-  const Value ProblemMargin = Value(0x28);
-
-  // No problem margin. If the boolean "Problem" is true, and a new move
-  // is found at the root which is less than NoProblemMargin worse than the
-  // best move from the previous iteration, Problem is set back to false.
-  const Value NoProblemMargin = Value(0x14);
-
   // Null move margin. A null move search will not be done if the static
   // evaluation of the position is more than NullMoveMargin below beta.
   const Value NullMoveMargin = Value(0x200);
@@ -161,15 +147,6 @@ namespace {
   // remaining ones we will extend it.
   const Value SingleReplyMargin = Value(0x20);
 
-  // Margins for futility pruning in the quiescence search, and at frontier
-  // and near frontier nodes.
-  const Value FutilityMarginQS = Value(0x80);
-
-  Value FutilityMargins[2 * PLY_MAX_PLUS_2]; // Initialized at startup.
-
-  // Each move futility margin is decreased
-  const Value IncrementalFutilityMargin = Value(0x8);
-
   // Depth limit for razoring
   const Depth RazorDepth = 4 * OnePly;
 
@@ -209,7 +186,7 @@ namespace {
   int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, ExactMaxTime;
   bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit;
   bool AbortSearch, Quit;
-  bool FailLow, Problem;
+  bool AspirationFailLow;
 
   // Show current line?
   bool ShowCurrentLine;
@@ -218,6 +195,14 @@ namespace {
   bool UseLogFile;
   std::ofstream LogFile;
 
+  // Futility lookup tables and their getter functions
+  const Value FutilityMarginQS = Value(0x80);
+  int32_t FutilityMarginsMatrix[14][64]; // [depth][moveNumber]
+  int FutilityMoveCountArray[32]; // [depth]
+
+  inline Value futility_margin(Depth d, int mn) { return (Value) (d < 14? FutilityMarginsMatrix[Max(d, 0)][Min(mn, 63)] : 2*VALUE_INFINITE); }
+  inline int futility_move_count(Depth d) { return (d < 32? FutilityMoveCountArray[d] : 512); }
+
   // Reduction lookup tables and their getter functions
   // Initialized at startup
   int8_t    PVReductionMatrix[64][64]; // [depth][moveNumber]
@@ -276,7 +261,6 @@ namespace {
   void update_killers(Move m, SearchStack& ss);
   void update_gains(const Position& pos, Move move, Value before, Value after);
 
-  bool fail_high_ply_1();
   int current_search_time();
   int nps();
   void poll();
@@ -352,7 +336,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
 
   // Initialize global search variables
   Idle = StopOnPonderhit = AbortSearch = Quit = false;
-  FailLow = Problem = false;
+  AspirationFailLow = false;
   NodesSincePoll = 0;
   SearchStartTime = get_system_time();
   ExactMaxTime = maxTime;
@@ -363,7 +347,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
   UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch;
 
   // Look for a book move, only during games, not tests
-  if (UseTimeManagement && !ponder && get_option_value_bool("OwnBook"))
+  if (UseTimeManagement && get_option_value_bool("OwnBook"))
   {
       Move bookMove;
       if (get_option_value_string("Book File") != OpeningBook.file_name())
@@ -372,6 +356,9 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
       bookMove = OpeningBook.get_move(pos);
       if (bookMove != MOVE_NONE)
       {
+          if (PonderSearch)
+              wait_for_stop_or_ponderhit();
+
           cout << "bestmove " << bookMove << endl;
           return true;
       }
@@ -380,7 +367,6 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
   for (int i = 0; i < THREAD_MAX; i++)
   {
       Threads[i].nodes = 0ULL;
-      Threads[i].failHighPly1 = false;
   }
 
   if (button_was_pressed("New Game"))
@@ -564,12 +550,15 @@ void init_threads() {
       }
 
   // Init futility margins array
-  FutilityMargins[0] = FutilityMargins[1] = Value(0);
+  for (i = 0; i < 14; i++) // i == depth (OnePly = 2)
+      for (int j = 0; j < 64; j++) // j == moveNumber
+      {
+          FutilityMarginsMatrix[i][j] = (i < 2 ? 0 : 112 * bitScanReverse32(i * i / 2)) - 8 * j; // FIXME: test using log instead of BSR
+      }
 
-  for (i = 2; i < 2 * PLY_MAX_PLUS_2; i++)
-  {
-      FutilityMargins[i] = Value(112 * bitScanReverse32(i * i / 2)); // FIXME: test using log instead of BSR
-  }
+  // Init futility move count array
+  for (i = 0; i < 32; i++) // i == depth (OnePly = 2)
+      FutilityMoveCountArray[i] = 3 + (1 << (3 * i / 8));
 
   for (i = 0; i < THREAD_MAX; i++)
       Threads[i].activeSplitPoints = 0;
@@ -762,8 +751,6 @@ namespace {
         if (ss[0].pv[0] != EasyMove)
             EasyMove = MOVE_NONE;
 
-        Problem = false;
-
         if (UseTimeManagement)
         {
             // Time to stop?
@@ -933,16 +920,6 @@ namespace {
                         alpha = -VALUE_INFINITE;
 
                     value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0);
-
-                    // If the value has dropped a lot compared to the last iteration,
-                    // set the boolean variable Problem to true. This variable is used
-                    // for time managment: When Problem is true, we try to complete the
-                    // current iteration before playing a move.
-                    Problem = (   Iteration >= 2
-                               && value <= ValueByIteration[Iteration - 1] - ProblemMargin);
-
-                    if (Problem && StopOnPonderhit)
-                        StopOnPonderhit = false;
                 }
                 else
                 {
@@ -1078,11 +1055,6 @@ namespace {
                     }
                     if (value > alpha)
                         alpha = value;
-
-                    // Reset the global variable Problem to false if the value isn't too
-                    // far below the final value from the last iteration.
-                    if (value > ValueByIteration[Iteration - 1] - NoProblemMargin)
-                        Problem = false;
                 }
                 else // MultiPV > 1
                 {
@@ -1108,7 +1080,10 @@ namespace {
 
             assert(alpha >= oldAlpha);
 
-            FailLow = (alpha == oldAlpha);
+            AspirationFailLow = (alpha == oldAlpha);
+
+            if (AspirationFailLow && StopOnPonderhit)
+                StopOnPonderhit = false;
         }
 
         // Can we exit fail low loop ?
@@ -1276,19 +1251,7 @@ namespace {
             ss[ply].reduction = Depth(0);
             value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID);
             if (value > alpha && value < beta)
-            {
-                // When the search fails high at ply 1 while searching the first
-                // move at the root, set the flag failHighPly1. This is used for
-                // time managment:  We don't want to stop the search early in
-                // such cases, because resolving the fail high at ply 1 could
-                // result in a big drop in score at the root.
-                if (ply == 1 && RootMoveNumber == 1)
-                    Threads[threadID].failHighPly1 = true;
-
-                // A fail high occurred. Re-search at full window (pv search)
                 value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID);
-                Threads[threadID].failHighPly1 = false;
-          }
         }
       }
       pos.undo_move(move);
@@ -1306,13 +1269,6 @@ namespace {
               if (value == value_mate_in(ply + 1))
                   ss[ply].mateKiller = move;
           }
-          // If we are at ply 1, and we are searching the first root move at
-          // ply 0, set the 'Problem' variable if the score has dropped a lot
-          // (from the computer's point of view) since the previous iteration.
-          if (   ply == 1
-              && Iteration >= 2
-              && -value <= ValueByIteration[Iteration-1] - ProblemMargin)
-              Problem = true;
       }
 
       // Split?
@@ -1417,9 +1373,6 @@ namespace {
 
     isCheck = pos.is_check();
 
-    // Calculate depth dependant futility pruning parameters
-    const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8));
-
     // Evaluate the position statically
     if (!isCheck)
     {
@@ -1432,7 +1385,7 @@ namespace {
         }
 
         ss[ply].eval = staticValue;
-        futilityValue = staticValue + FutilityMargins[int(depth)]; //FIXME: Remove me, only for split
+        futilityValue = staticValue + futility_margin(depth, 0); //FIXME: Remove me, only for split
         staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible
         update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval);
     }
@@ -1443,8 +1396,8 @@ namespace {
     if (  !isCheck
         && allowNullmove
         && depth < RazorDepth
-        && staticValue - FutilityMargins[int(depth)] >= beta)
-        return staticValue - FutilityMargins[int(depth)];
+        && staticValue - futility_margin(depth, 0) >= beta)
+        return staticValue - futility_margin(depth, 0);
 
     // Null move search
     if (    allowNullmove
@@ -1516,7 +1469,7 @@ namespace {
     {
         search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
         ttMove = ss[ply].pv[ply];
-        tte = TT.retrieve(pos.get_key());
+        tte = TT.retrieve(posKey);
     }
 
     // Initialize a MovePicker object for the current position, and prepare
@@ -1576,35 +1529,20 @@ namespace {
           &&  move != ttMove)
       {
           // Move count based pruning
-          if (   moveCount >= FutilityMoveCountMargin
+          if (   moveCount >= futility_move_count(depth)
               && ok_to_prune(pos, move, ss[ply].threatMove)
               && bestValue > value_mated_in(PLY_MAX))
               continue;
 
           // Value based pruning
-          Depth predictedDepth = newDepth;
-
-          //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
-          ss[ply].reduction = nonpv_reduction(depth, moveCount);
-          if (ss[ply].reduction)
-              predictedDepth -= ss[ply].reduction;
+          Depth predictedDepth = newDepth - nonpv_reduction(depth, moveCount); //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
+          futilityValueScaled = ss[ply].eval + futility_margin(predictedDepth, moveCount) + H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
 
-          if (predictedDepth < SelectiveDepth)
+          if (futilityValueScaled < beta)
           {
-              int preFutilityValueMargin = 0;
-              if (predictedDepth >= OnePly)
-                  preFutilityValueMargin = FutilityMargins[int(predictedDepth)];
-
-              preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
-
-              futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin;
-
-              if (futilityValueScaled < beta)
-              {
-                  if (futilityValueScaled > bestValue)
-                      bestValue = futilityValueScaled;
-                  continue;
-              }
+              if (futilityValueScaled > bestValue)
+                  bestValue = futilityValueScaled;
+              continue;
           }
       }
 
@@ -1903,11 +1841,9 @@ namespace {
     Move move;
     int moveCount;
     bool isCheck = pos.is_check();
-    bool useFutilityPruning =     sp->depth < SelectiveDepth
+    bool useFutilityPruning =     sp->depth < 7 * OnePly //FIXME: sync with search
                               && !isCheck;
 
-    const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8));
-
     while (    lock_grab_bool(&(sp->lock))
            &&  sp->bestValue < sp->beta
            && !thread_should_stop(threadID)
@@ -1934,13 +1870,13 @@ namespace {
           && !captureOrPromotion)
       {
           // Move count based pruning
-          if (   moveCount >= FutilityMoveCountMargin
+          if (   moveCount >= futility_move_count(sp->depth)
               && ok_to_prune(pos, move, ss[sp->ply].threatMove)
               && sp->bestValue > value_mated_in(PLY_MAX))
               continue;
 
           // Value based pruning
-          Value futilityValueScaled = sp->futilityValue - moveCount * IncrementalFutilityMargin;
+          Value futilityValueScaled = sp->futilityValue - moveCount * 8; //FIXME: sync with search
 
           if (futilityValueScaled < sp->beta)
           {
@@ -2098,14 +2034,6 @@ namespace {
 
           if (value > localAlpha && value < sp->beta)
           {
-              // When the search fails high at ply 1 while searching the first
-              // move at the root, set the flag failHighPly1. This is used for
-              // time managment: We don't want to stop the search early in
-              // such cases, because resolving the fail high at ply 1 could
-              // result in a big drop in score at the root.
-              if (sp->ply == 1 && RootMoveNumber == 1)
-                  Threads[threadID].failHighPly1 = true;
-
               // If another thread has failed high then sp->alpha has been increased
               // to be higher or equal then beta, if so, avoid to start a PV search.
               localAlpha = sp->alpha;
@@ -2113,8 +2041,6 @@ namespace {
                   value = -search_pv(pos, ss, -sp->beta, -localAlpha, newDepth, sp->ply+1, threadID);
               else
                   assert(thread_should_stop(threadID));
-
-              Threads[threadID].failHighPly1 = false;
         }
       }
       pos.undo_move(move);
@@ -2152,13 +2078,6 @@ namespace {
                   if (value == value_mate_in(sp->ply + 1))
                       ss[sp->ply].mateKiller = move;
               }
-              // If we are at ply 1, and we are searching the first root move at
-              // ply 0, set the 'Problem' variable if the score has dropped a lot
-              // (from the computer's point of view) since the previous iteration.
-              if (   sp->ply == 1
-                     && Iteration >= 2
-                     && -value <= ValueByIteration[Iteration-1] - ProblemMargin)
-                  Problem = true;
           }
           lock_release(&(sp->lock));
       }
@@ -2660,20 +2579,6 @@ namespace {
   }
 
 
-  // fail_high_ply_1() checks if some thread is currently resolving a fail
-  // high at ply 1 at the node below the first root node.  This information
-  // is used for time management.
-
-  bool fail_high_ply_1() {
-
-    for (int i = 0; i < ActiveThreads; i++)
-        if (Threads[i].failHighPly1)
-            return true;
-
-    return false;
-  }
-
-
   // current_search_time() returns the number of milliseconds which have passed
   // since the beginning of the current search.
 
@@ -2760,7 +2665,7 @@ namespace {
         return;
 
     bool stillAtFirstMove =    RootMoveNumber == 1
-                           && !FailLow
+                           && !AspirationFailLow
                            &&  t > MaxSearchTime + ExtraSearchTime;
 
     bool noMoreTime =   t > AbsoluteMaxSearchTime
@@ -2783,7 +2688,7 @@ namespace {
     PonderSearch = false;
 
     bool stillAtFirstMove =    RootMoveNumber == 1
-                           && !FailLow
+                           && !AspirationFailLow
                            &&  t > MaxSearchTime + ExtraSearchTime;
 
     bool noMoreTime =   t > AbsoluteMaxSearchTime
@@ -3103,7 +3008,7 @@ namespace {
     for (int i = 0; i < ActiveThreads; i++)
         if (i == master || splitPoint->slaves[i])
         {
-            memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 3 * sizeof(SearchStack));
+            memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 4 * sizeof(SearchStack));
             Threads[i].workIsWaiting = true; // This makes the slave to exit from idle_loop()
         }