]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Set LSNTime to 100 ms
[stockfish] / src / search.cpp
index 8fa1a37928399d45da5d8bf4173e7bbe20f7d08a..4c90db53d93619a3a34cf0c3a9f0e6876a507ef8 100644 (file)
@@ -236,7 +236,7 @@ namespace {
 
   // Last seconds noise filtering (LSN)
   const bool UseLSNFiltering = true;
-  const int LSNTime = 4000; // In milliseconds
+  const int LSNTime = 100; // In milliseconds
   const Value LSNValue = value_from_centipawns(200);
   bool loseOnTime = false;
 
@@ -296,8 +296,8 @@ namespace {
   template <NodeType PvNode>
   Depth extension(const Position& pos, Move m, bool captureOrPromotion, bool moveIsCheck, bool singleEvasion, bool mateThreat, bool* dangerous);
 
-  void update_pv(SearchStack* ss, int ply);
-  void sp_update_pv(SearchStack* pss, SearchStack* ss, int ply);
+  void update_pv(SearchStack* ss);
+  void sp_update_pv(SearchStack* pss, SearchStack* ss);
   bool connected_moves(const Position& pos, Move m1, Move m2);
   bool value_is_mate(Value value);
   bool move_is_killer(Move m, SearchStack* ss);
@@ -366,9 +366,9 @@ void init_search() {
 
 // SearchStack::init() initializes a search stack. Used at the beginning of a
 // new search from the root.
-void SearchStack::init(int ply) {
+void SearchStack::init() {
 
-  pv[ply] = pv[ply + 1] = MOVE_NONE;
+  pv[0] = pv[1] = MOVE_NONE;
   currentMove = threatMove = MOVE_NONE;
   reduction = Depth(0);
   eval = VALUE_NONE;
@@ -892,17 +892,31 @@ namespace {
                         ss->reduction = reduction<PV>(depth, i - MultiPV + 2);
                         if (ss->reduction)
                         {
+                            assert(newDepth-ss->reduction >= OnePly);
+
                             // Reduced depth non-pv search using alpha as upperbound
                             value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction);
                             doFullDepthSearch = (value > alpha);
                         }
+
+                        // The move failed high, but if reduction is very big we could
+                        // face a false positive, retry with a less aggressive reduction,
+                        // if the move fails high again then go with full depth search.
+                        if (doFullDepthSearch && ss->reduction > 2 * OnePly)
+                        {
+                            assert(newDepth - OnePly >= OnePly);
+
+                            ss->reduction = OnePly;
+                            value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction);
+                            doFullDepthSearch = (value > alpha);
+                        }
+                        ss->reduction = Depth(0); // Restore original reduction
                     }
 
                     // Step 15. Full depth search
                     if (doFullDepthSearch)
                     {
                         // Full depth non-pv search using alpha as upperbound
-                        ss->reduction = Depth(0);
                         value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth);
 
                         // If we are above alpha then research at same depth but as PV
@@ -922,7 +936,7 @@ namespace {
                 // We are failing high and going to do a research. It's important to update
                 // the score before research in case we run out of time while researching.
                 rml.set_move_score(i, value);
-                update_pv(ss, 0);
+                update_pv(ss);
                 TT.extract_pv(pos, ss->pv, PLY_MAX);
                 rml.set_move_pv(i, ss->pv);
 
@@ -962,7 +976,7 @@ namespace {
 
                 // Update PV
                 rml.set_move_score(i, value);
-                update_pv(ss, 0);
+                update_pv(ss);
                 TT.extract_pv(pos, ss->pv, PLY_MAX);
                 rml.set_move_pv(i, ss->pv);
 
@@ -1058,8 +1072,8 @@ namespace {
 
     // Step 1. Initialize node and poll. Polling can abort search
     TM.incrementNodeCounter(threadID);
-    ss->init(ply);
-    (ss + 2)->initKillers();
+    ss->init();
+    (ss+2)->initKillers();
 
     if (threadID == 0 && ++NodesSincePoll > NodesBetweenPolls)
     {
@@ -1134,6 +1148,10 @@ namespace {
         && !value_is_mate(beta)
         && !pos.has_pawn_on_7th(pos.side_to_move()))
     {
+        // Pass ss->eval to qsearch() and avoid an evaluate call
+        if (!tte || tte->static_value() == VALUE_NONE)
+            TT.store(posKey, ss->eval, VALUE_TYPE_EXACT, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+
         Value rbeta = beta - razor_margin(depth);
         Value v = qsearch<NonPV>(pos, ss, rbeta-1, rbeta, Depth(0));
         if (v < rbeta)
@@ -1230,7 +1248,7 @@ namespace {
         search<PvNode>(pos, ss, alpha, beta, d);
         ss->skipNullMove = false;
 
-        ttMove = ss->pv[ply];
+        ttMove = ss->pv[0];
         tte = TT.retrieve(posKey);
     }
 
@@ -1395,7 +1413,7 @@ namespace {
               if (PvNode && value < beta) // This guarantees that always: alpha < beta
                   alpha = value;
 
-              update_pv(ss, ply);
+              update_pv(ss);
 
               if (value == value_mate_in(ply + 1))
                   ss->mateKiller = move;
@@ -1433,7 +1451,7 @@ namespace {
     else if (bestValue >= beta)
     {
         TM.incrementBetaCounter(pos.side_to_move(), depth, threadID);
-        move = ss->pv[ply];
+        move = ss->pv[0];
         TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move, ss->eval, ei.kingDanger[pos.side_to_move()]);
         if (!pos.move_is_capture_or_promotion(move))
         {
@@ -1442,7 +1460,7 @@ namespace {
         }
     }
     else
-        TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss->pv[ply], ss->eval, ei.kingDanger[pos.side_to_move()]);
+        TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss->pv[0], ss->eval, ei.kingDanger[pos.side_to_move()]);
 
     assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
 
@@ -1467,15 +1485,15 @@ namespace {
     EvalInfo ei;
     StateInfo st;
     Move ttMove, move;
-    Value staticValue, bestValue, value, futilityBase, futilityValue;
-    bool isCheck, enoughMaterial, moveIsCheck, evasionPrunable;
-    const TTEntry* tte = NULL;
-    int moveCount = 0;
-    int ply = pos.ply();
+    Value bestValue, value, futilityValue, futilityBase;
+    bool isCheck, deepChecks, enoughMaterial, moveIsCheck, evasionPrunable;
+    const TTEntry* tte;
     Value oldAlpha = alpha;
+    int ply = pos.ply();
 
     TM.incrementNodeCounter(pos.thread());
-    ss->init(ply);
+    ss->pv[0] = ss->pv[1] = ss->currentMove = MOVE_NONE;
+    ss->eval = VALUE_NONE;
 
     // Check for an instant draw or maximum ply reached
     if (pos.is_draw() || ply >= PLY_MAX - 1)
@@ -1496,39 +1514,42 @@ namespace {
 
     // Evaluate the position statically
     if (isCheck)
-        staticValue = -VALUE_INFINITE;
-    else if (tte && tte->static_value() != VALUE_NONE)
     {
-        staticValue = tte->static_value();
-        ei.kingDanger[pos.side_to_move()] = tte->king_danger();
+        bestValue = futilityBase = -VALUE_INFINITE;
+        deepChecks = enoughMaterial = false;
     }
     else
-        staticValue = evaluate(pos, ei);
-
-    if (!isCheck)
     {
-        ss->eval = staticValue;
+        if (tte && tte->static_value() != VALUE_NONE)
+        {
+            ei.kingDanger[pos.side_to_move()] = tte->king_danger();
+            bestValue = tte->static_value();
+        }
+        else
+            bestValue = evaluate(pos, ei);
+
+        ss->eval = bestValue;
         update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
-    }
 
-    // Initialize "stand pat score", and return it immediately if it is
-    // at least beta.
-    bestValue = staticValue;
+        // Stand pat. Return immediately if static value is at least beta
+        if (bestValue >= beta)
+        {
+            if (!tte)
+                TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
 
-    if (bestValue >= beta)
-    {
-        // Store the score to avoid a future costly evaluation() call
-        if (!isCheck && !tte)
-            TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+            return bestValue;
+        }
 
-        return bestValue;
-    }
+        if (PvNode && bestValue > alpha)
+            alpha = bestValue;
 
-    if (bestValue > alpha)
-        alpha = bestValue;
+        // If we are near beta then try to get a cutoff pushing checks a bit further
+        deepChecks = (depth == -OnePly && bestValue >= beta - PawnValueMidgame / 8);
 
-    // If we are near beta then try to get a cutoff pushing checks a bit further
-    bool deepChecks = (depth == -OnePly && staticValue >= beta - PawnValueMidgame / 8);
+        // Futility pruning parameters, not needed when in check
+        futilityBase = bestValue + FutilityMarginQS + ei.kingDanger[pos.side_to_move()];
+        enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
+    }
 
     // Initialize a MovePicker object for the current position, and prepare
     // to search the moves. Because the depth is <= 0 here, only captures,
@@ -1536,8 +1557,6 @@ namespace {
     // and we are near beta) will be generated.
     MovePicker mp = MovePicker(pos, ttMove, deepChecks ? Depth(0) : depth, H);
     CheckInfo ci(pos);
-    enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
-    futilityBase = staticValue + FutilityMarginQS + ei.kingDanger[pos.side_to_move()];
 
     // Loop through the moves until no moves remain or a beta cutoff occurs
     while (   alpha < beta
@@ -1547,16 +1566,12 @@ namespace {
 
       moveIsCheck = pos.move_is_check(move, ci);
 
-      // Update current move
-      moveCount++;
-      ss->currentMove = move;
-
       // Futility pruning
       if (   !PvNode
-          &&  enoughMaterial
           && !isCheck
           && !moveIsCheck
           &&  move != ttMove
+          &&  enoughMaterial
           && !move_is_promotion(move)
           && !pos.move_is_passed_pawn_push(move))
       {
@@ -1587,6 +1602,9 @@ namespace {
           &&  pos.see_sign(move) < 0)
           continue;
 
+      // Update current move
+      ss->currentMove = move;
+
       // Make and search the move
       pos.do_move(move, st, ci, moveIsCheck);
       value = -qsearch<PvNode>(pos, ss+1, -beta, -alpha, depth-OnePly);
@@ -1601,27 +1619,23 @@ namespace {
           if (value > alpha)
           {
               alpha = value;
-              update_pv(ss, ply);
+              update_pv(ss);
           }
        }
     }
 
     // All legal moves have been searched. A special case: If we're in check
     // and no legal moves were found, it is checkmate.
-    if (!moveCount && isCheck) // Mate!
+    if (isCheck && bestValue == -VALUE_INFINITE)
         return value_mated_in(ply);
 
     // Update transposition table
     Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1));
     if (bestValue <= oldAlpha)
-    {
-        // If bestValue isn't changed it means it is still the static evaluation
-        // of the node, so keep this info to avoid a future evaluation() call.
         TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, d, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
-    }
     else if (bestValue >= beta)
     {
-        move = ss->pv[ply];
+        move = ss->pv[0];
         TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move, ss->eval, ei.kingDanger[pos.side_to_move()]);
 
         // Update killers only for good checking moves
@@ -1629,7 +1643,7 @@ namespace {
             update_killers(move, ss);
     }
     else
-        TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, d, ss->pv[ply], ss->eval, ei.kingDanger[pos.side_to_move()]);
+        TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, d, ss->pv[0], ss->eval, ei.kingDanger[pos.side_to_move()]);
 
     assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
 
@@ -1662,7 +1676,6 @@ namespace {
 
     Position pos(*sp->pos, threadID);
     CheckInfo ci(pos);
-    int ply = pos.ply();
     SearchStack* ss = sp->sstack[threadID] + 1;
     isCheck = pos.is_check();
 
@@ -1792,7 +1805,7 @@ namespace {
               if (PvNode && value < sp->beta) // This guarantees that always: sp->alpha < sp->beta
                   sp->alpha = value;
 
-              sp_update_pv(sp->parentSstack, ss, ply);
+              sp_update_pv(sp->parentSstack, ss);
           }
       }
     }
@@ -1808,18 +1821,16 @@ namespace {
   // It updates the PV in the SearchStack object corresponding to the
   // current node.
 
-  void update_pv(SearchStack* ss, int ply) {
-
-    assert(ply >= 0 && ply < PLY_MAX);
-
-    int p;
+  void update_pv(SearchStack* ss) {
 
-    ss->pv[ply] = ss->currentMove;
+    Move* src = (ss+1)->pv;
+    Move* dst = ss->pv;
 
-    for (p = ply + 1; (ss+1)->pv[p] != MOVE_NONE; p++)
-        ss->pv[p] = (ss+1)->pv[p];
+    *dst = ss->currentMove;
 
-    ss->pv[p] = MOVE_NONE;
+    do
+        *++dst = *src;
+    while (*src++ != MOVE_NONE);
   }
 
 
@@ -1827,18 +1838,17 @@ namespace {
   // difference between the two functions is that sp_update_pv also updates
   // the PV at the parent node.
 
-  void sp_update_pv(SearchStack* pss, SearchStack* ss, int ply) {
-
-    assert(ply >= 0 && ply < PLY_MAX);
-
-    int p;
+  void sp_update_pv(SearchStack* pss, SearchStack* ss) {
 
-    ss->pv[ply] = pss->pv[ply] = ss->currentMove;
+    Move* src = (ss+1)->pv;
+    Move* dst = ss->pv;
+    Move* pdst = pss->pv;
 
-    for (p = ply + 1; (ss+1)->pv[p] != MOVE_NONE; p++)
-        ss->pv[p] = pss->pv[p] = (ss+1)->pv[p];
+    *dst = *pdst = ss->currentMove;
 
-    ss->pv[p] = pss->pv[p] = MOVE_NONE;
+    do
+        *++dst = *++pdst = *src;
+    while (*src++ != MOVE_NONE);
   }
 
 
@@ -2243,7 +2253,7 @@ namespace {
 
         if (i < 3)
         {
-            ss->init(i);
+            ss->init();
             ss->initKillers();
         }
     }