Cache evaluation score in qsearch
[stockfish] / src / search.cpp
index 92d2c75ed2c6fd5a3ff3b89d376e60c704c048f4..3617853e5f23d2f3589b7af48da9decbf48e4593 100644 (file)
@@ -121,6 +121,9 @@ namespace {
   // Depth limit for selective search:
   Depth SelectiveDepth = 7*OnePly;
 
+  // Use dynamic LMR?
+  const bool UseDynamicLMR = false;
+
   // Use internal iterative deepening?
   const bool UseIIDAtPVNodes = true;
   const bool UseIIDAtNonPVNodes = false;
@@ -149,16 +152,6 @@ namespace {
   // evaluation of the position is more than NullMoveMargin below beta.
   const Value NullMoveMargin = Value(0x300);
 
-  //Null move search refutes move when Nullvalue >= Beta - Delta. Index is depth
-  //in full plies. Last index is 9+.
-  const Value NullMoveDeltaMidgame[] =
-    { Value(-8), Value( 6), Value(-15), Value( 9), Value(21),
-      Value(34), Value(54), Value( 59), Value(61), Value(61) };
-
-  const Value NullMoveDeltaEndgame[] =
-    { Value( 6), Value( 0), Value(-13), Value(-9), Value(-35),
-      Value(12), Value(24), Value(  9), Value( 5), Value(  5) };
-
   // Pruning criterions.  See the code and comments in ok_to_prune() to
   // understand their precise meaning.
   const bool PruneEscapeMoves = false;
@@ -203,7 +196,6 @@ namespace {
 
   // Iteration counters
   int Iteration;
-  bool LastIterations;
   BetaCounterType BetaCounter;
 
   // Scores and number of times the best move changed for each iteration:
@@ -217,7 +209,7 @@ namespace {
   int SearchStartTime;
   int MaxNodes, MaxDepth;
   int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
-  Move BestRootMove, PonderMove, EasyMove;
+  Move EasyMove;
   int RootMoveNumber;
   bool InfiniteSearch;
   bool PonderSearch;
@@ -379,8 +371,6 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
   // Initialize global search variables
   Idle = false;
   SearchStartTime = get_system_time();
-  BestRootMove = MOVE_NONE;
-  PonderMove = MOVE_NONE;
   EasyMove = MOVE_NONE;
   for (int i = 0; i < THREAD_MAX; i++)
   {
@@ -671,7 +661,6 @@ namespace {
     ValueByIteration[0] = Value(0);
     ValueByIteration[1] = rml.get_move_score(0);
     Iteration = 1;
-    LastIterations = false;
 
     EasyMove = rml.scan_for_easy_move();
 
@@ -726,9 +715,6 @@ namespace {
                 ExtraSearchTime = BestMoveChangesByIteration[Iteration]   * (MaxSearchTime / 2)
                                 + BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
 
-            // Try to guess if the current iteration is the last one or the last two
-            LastIterations = (current_search_time() > ((MaxSearchTime + ExtraSearchTime)*58) / 128);
-
             // 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.
@@ -765,6 +751,11 @@ namespace {
                   << " hashfull " << TT.full() << std::endl;
 
     // Print the best move and the ponder move to the standard output
+    if (ss[0].pv[0] == MOVE_NONE)
+    {
+        ss[0].pv[0] = rml.get_move(0);
+        ss[0].pv[1] = MOVE_NONE;
+    }
     std::cout << "bestmove " << ss[0].pv[0];
     if (ss[0].pv[1] != MOVE_NONE)
         std::cout << " ponder " << ss[0].pv[1];
@@ -1207,19 +1198,13 @@ namespace {
         &&  ok_to_do_nullmove(pos)
         &&  approximateEval >= beta - NullMoveMargin)
     {
-        //Calculate correct delta. Idea and tuning from Joona Kiiski.
-        ScaleFactor factor[2] = { SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL };
-        Phase phase = pos.game_phase();
-        int i = Min(depth / OnePly, 9);
-        Value delta = scale_by_game_phase(NullMoveDeltaMidgame[i], NullMoveDeltaEndgame[i], phase, factor);
-
         ss[ply].currentMove = MOVE_NULL;
 
         StateInfo st;
         pos.do_null_move(st);
         int R = (depth >= 4 * OnePly ? 4 : 3); // Null move dynamic reduction
 
-        Value nullValue = -search(pos, ss, -(beta-delta-1), depth-R*OnePly, ply+1, false, threadID);
+        Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
 
         pos.undo_null_move();
 
@@ -1227,7 +1212,7 @@ namespace {
         {
             /* Do not return unproven mates */
         }
-        else if (nullValue >= beta - delta)
+        else if (nullValue >= beta)
         {
             if (depth < 6 * OnePly)
                 return beta;
@@ -1263,8 +1248,8 @@ namespace {
     {
         Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
         if (   (v < beta - RazorMargin - RazorMargin / 4)
-            || (depth < 3*OnePly && v < beta - RazorMargin)
-            || (depth < 2*OnePly && v < beta - RazorMargin / 2))
+            || (depth <= 2*OnePly && v < beta - RazorMargin)
+            || (depth <=   OnePly && v < beta - RazorMargin / 2))
             return v;
     }
 
@@ -1350,8 +1335,13 @@ namespace {
           && !move_is_castle(move)
           && !move_is_killer(move, ss[ply]))
       {
-          ss[ply].reduction = OnePly;
-          value = -search(pos, ss, -(beta-1), newDepth-OnePly, ply+1, true, threadID);
+          // LMR dynamic reduction
+          Depth R =    UseDynamicLMR
+                    && moveCount >= 2 * LMRNonPVMoves
+                    && depth > 7*OnePly ? 2*OnePly : OnePly;
+
+          ss[ply].reduction = R;
+          value = -search(pos, ss, -(beta-1), newDepth-R, ply+1, true, threadID);
       }
       else
         value = beta; // Just to trigger next condition
@@ -1412,6 +1402,9 @@ namespace {
         }
         TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
     }
+
+    assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
+
     return bestValue;
   }
 
@@ -1440,15 +1433,39 @@ namespace {
     if (pos.is_draw())
         return VALUE_DRAW;
 
-    // Transposition table lookup
-    const TTEntry* tte = TT.retrieve(pos);
-    if (tte && ok_to_use_TT(tte, depth, beta, ply))
-        return value_from_tt(tte->value(), ply);
+    // Transposition table lookup, only when not in PV
+    TTEntry* tte = NULL;
+    bool pvNode = (beta - alpha != 1);
+    if (!pvNode)
+    {
+        tte = TT.retrieve(pos);
+        if (tte && ok_to_use_TT(tte, depth, beta, ply))
+        {
+            assert(tte->type() != VALUE_TYPE_EVAL);
+
+            return value_from_tt(tte->value(), ply);
+        }
+    }
 
     // Evaluate the position statically
     EvalInfo ei;
+    Value staticValue;
     bool isCheck = pos.is_check();
-    Value staticValue = (isCheck ? -VALUE_INFINITE : evaluate(pos, ei, threadID));
+
+    if (isCheck)
+        staticValue = -VALUE_INFINITE;
+
+    else if (tte && tte->type() == VALUE_TYPE_EVAL)
+    {
+        // Use the cached evaluation score if possible
+        assert(tte->value() == evaluate(pos, ei, threadID));
+        assert(ei.futilityMargin == Value(0));
+
+        staticValue = tte->value();
+        ei.futilityMargin = Value(0); // manually initialize futilityMargin
+    }
+    else
+        staticValue = evaluate(pos, ei, threadID);
 
     if (ply == PLY_MAX - 1)
         return evaluate(pos, ei, threadID);
@@ -1458,7 +1475,16 @@ namespace {
     Value bestValue = staticValue;
 
     if (bestValue >= beta)
+    {
+        // Update transposition table before to leave
+        TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_EXACT);
         return bestValue;
+    }
+    else if (!isCheck && !tte && ei.futilityMargin == 0)
+    {
+        // Store the score to avoid a future costly evaluation() call
+        TT.store(pos, value_to_tt(bestValue, ply), Depth(-127*OnePly), MOVE_NONE, VALUE_TYPE_EVAL);
+    }
 
     if (bestValue > alpha)
         alpha = bestValue;
@@ -1466,8 +1492,7 @@ namespace {
     // Initialize a MovePicker object for the current position, and prepare
     // to search the moves.  Because the depth is <= 0 here, only captures,
     // queen promotions and checks (only if depth == 0) will be generated.
-    bool pvNode = (beta - alpha != 1);
-    MovePicker mp = MovePicker(pos, pvNode, MOVE_NONE, EmptySearchStack, depth, isCheck ? NULL : &ei);
+    MovePicker mp = MovePicker(pos, pvNode, MOVE_NONE, EmptySearchStack, depth);
     Move move;
     int moveCount = 0;
     Bitboard dcCandidates = mp.discovered_check_candidates();
@@ -1543,9 +1568,6 @@ namespace {
 
     assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
 
-    // Update transposition table
-    TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_EXACT);
-
     // Update killers only for good check moves
     Move m = ss[ply].currentMove;
     if (alpha >= beta && ok_to_history(pos, m)) // Only non capture moves are considered
@@ -2421,7 +2443,7 @@ namespace {
                      || (  !FailHigh && !fail_high_ply_1() && !Problem
                          && t > 6*(MaxSearchTime + ExtraSearchTime));
 
-    if (   (Iteration >= 2 && (!InfiniteSearch && overTime))
+    if (   (Iteration >= 3 && (!InfiniteSearch && overTime))
         || (ExactMaxTime && t >= ExactMaxTime)
         || (Iteration >= 3 && MaxNodes && nodes_searched() >= MaxNodes))
         AbortSearch = true;
@@ -2435,7 +2457,7 @@ namespace {
   void ponderhit() {
     int t = current_search_time();
     PonderSearch = false;
-    if(Iteration >= 2 &&
+    if(Iteration >= 3 &&
        (!InfiniteSearch && (StopOnPonderhit ||
                             t > AbsoluteMaxSearchTime ||
                             (RootMoveNumber == 1 &&