]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Don't need to early check PV moves for legality
[stockfish] / src / search.cpp
index 1ba954677047754fcbf99751d892789c0af6792a..6cab3f6cf7496713c1ac033bd3866ea2a9d22323 100644 (file)
@@ -42,7 +42,7 @@ namespace Search {
   LimitsType Limits;
   std::vector<RootMove> RootMoves;
   Position RootPosition;
-  Time SearchTime;
+  Time::point SearchTime;
   StateStackPtr SetupStates;
 }
 
@@ -224,7 +224,7 @@ size_t Search::perft(Position& pos, Depth depth) {
 
 void Search::think() {
 
-  static Book book; // Defined static to initialize the PRNG only once
+  static PolyglotBook book; // Defined static to initialize the PRNG only once
 
   Position& pos = RootPosition;
   Chess960 = pos.is_chess960();
@@ -290,11 +290,11 @@ void Search::think() {
 
   if (Options["Use Search Log"])
   {
-      int e = SearchTime.elapsed();
+      Time::point elapsed = Time::now() - SearchTime + 1;
 
       Log log(Options["Search Log Filename"]);
       log << "Nodes: "          << pos.nodes_searched()
-          << "\nNodes/second: " << (e > 0 ? pos.nodes_searched() * 1000 / e : 0)
+          << "\nNodes/second: " << pos.nodes_searched() * 1000 / elapsed
           << "\nBest move: "    << move_to_san(pos, RootMoves[0].pv[0]);
 
       StateInfo st;
@@ -365,7 +365,8 @@ namespace {
 
             // Start with a small aspiration window and, in case of fail high/low,
             // research with bigger window until not failing high/low anymore.
-            do {
+            while (true)
+            {
                 // Search starts from ss+1 to allow referencing (ss-1). This is
                 // needed by update gains and ss copy when splitting at Root.
                 bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
@@ -398,7 +399,7 @@ namespace {
 
                 // Send full PV info to GUI if we are going to leave the loop or
                 // if we have a fail high/low and we are deep in the search.
-                if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000)
+                if ((bestValue > alpha && bestValue < beta) || Time::now() - SearchTime > 2000)
                     sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
 
                 // In case of failing high/low increase aspiration window and
@@ -419,9 +420,15 @@ namespace {
                 else
                     break;
 
-                assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+                // Search with full window in case we have a win/mate score
+                if (abs(bestValue) >= VALUE_KNOWN_WIN)
+                {
+                    alpha = -VALUE_INFINITE;
+                    beta  =  VALUE_INFINITE;
+                }
 
-            } while (abs(bestValue) < VALUE_KNOWN_WIN);
+                assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+            }
         }
 
         // Skills: Do we need to pick now the best move ?
@@ -431,7 +438,7 @@ namespace {
         if (!Signals.stop && Options["Use Search Log"])
         {
             Log log(Options["Search Log Filename"]);
-            log << pretty_pv(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0])
+            log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0])
                 << std::endl;
         }
 
@@ -451,14 +458,14 @@ namespace {
             // Stop search if most of available time is already consumed. We
             // probably don't have enough time to search the first move at the
             // next iteration anyway.
-            if (SearchTime.elapsed() > (TimeMgr.available_time() * 62) / 100)
+            if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
                 stop = true;
 
             // Stop search early if one move seems to be much better than others
             if (    depth >= 12
                 && !stop
                 && (   (bestMoveNeverChanged &&  pos.captured_piece_type())
-                    || SearchTime.elapsed() > (TimeMgr.available_time() * 40) / 100))
+                    || Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100))
             {
                 Value rBeta = bestValue - EasyMoveMargin;
                 (ss+1)->excludedMove = RootMoves[0].pv[0];
@@ -521,7 +528,7 @@ namespace {
     Bound bt;
     Value bestValue, value, oldAlpha, ttValue;
     Value refinedValue, nullValue, futilityBase, futilityValue;
-    bool isPvMove, inCheck, singularExtensionNode, givesCheck;
+    bool pvMove, inCheck, singularExtensionNode, givesCheck;
     bool captureOrPromotion, dangerous, doFullDepthSearch;
     int moveCount = 0, playedMoveCount = 0;
     Thread* thisThread = pos.this_thread();
@@ -811,12 +818,12 @@ split_point_start: // At split points actual search starts from here
       if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move))
           continue;
 
-      // At PV and SpNode nodes we want all moves to be legal since the beginning
-      if ((PvNode || SpNode) && !pos.pl_move_is_legal(move, ci.pinned))
-          continue;
-
       if (SpNode)
       {
+          // Shared counter cannot be decremented later if move turns out to be illegal
+          if (!pos.pl_move_is_legal(move, ci.pinned))
+              continue;
+
           moveCount = ++sp->moveCount;
           sp->mutex.unlock();
       }
@@ -827,13 +834,12 @@ split_point_start: // At split points actual search starts from here
       {
           Signals.firstRootMove = (moveCount == 1);
 
-          if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000)
+          if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 2000)
               sync_cout << "info depth " << depth / ONE_PLY
                         << " currmove " << move_to_uci(move, Chess960)
                         << " currmovenumber " << moveCount + PVIdx << sync_endl;
       }
 
-      isPvMove = (PvNode && moveCount <= 1);
       captureOrPromotion = pos.is_capture_or_promotion(move);
       givesCheck = pos.move_gives_check(move, ci);
       dangerous = givesCheck || is_dangerous(pos, move, captureOrPromotion);
@@ -922,6 +928,7 @@ split_point_start: // At split points actual search starts from here
           continue;
       }
 
+      pvMove = PvNode ? moveCount <= 1 : false;
       ss->currentMove = move;
       if (!SpNode && !captureOrPromotion && playedMoveCount < 64)
           movesSearched[playedMoveCount++] = move;
@@ -932,7 +939,7 @@ split_point_start: // At split points actual search starts from here
       // Step 15. Reduced depth search (LMR). If the move fails high will be
       // re-searched at full depth.
       if (    depth > 3 * ONE_PLY
-          && !isPvMove
+          && !pvMove
           && !captureOrPromotion
           && !dangerous
           &&  ss->killers[0] != move
@@ -948,7 +955,7 @@ split_point_start: // At split points actual search starts from here
           ss->reduction = DEPTH_ZERO;
       }
       else
-          doFullDepthSearch = !isPvMove;
+          doFullDepthSearch = !pvMove;
 
       // Step 16. Full depth search, when LMR is skipped or fails high
       if (doFullDepthSearch)
@@ -961,7 +968,7 @@ split_point_start: // At split points actual search starts from here
       // Only for PV nodes do a full PV search on the first move or after a fail
       // high, in the latter case search only if value < beta, otherwise let the
       // parent node to fail low with value <= alpha and to try another move.
-      if (PvNode && (isPvMove || (value > alpha && (RootNode || value < beta))))
+      if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
           value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
                                      : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
 
@@ -987,7 +994,7 @@ split_point_start: // At split points actual search starts from here
           RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move);
 
           // PV move or new best move ?
-          if (isPvMove || value > alpha)
+          if (pvMove || value > alpha)
           {
               rm.score = value;
               rm.extract_pv_from_tt(pos);
@@ -995,7 +1002,7 @@ split_point_start: // At split points actual search starts from here
               // We record how often the best move has been changed in each
               // iteration. This information is used for time management: When
               // the best move changes frequently, we allocate some more time.
-              if (!isPvMove && MultiPV == 1)
+              if (!pvMove && MultiPV == 1)
                   BestMoveChanges++;
           }
           else
@@ -1494,7 +1501,7 @@ split_point_start: // At split points actual search starts from here
     static RKISS rk;
 
     // PRNG sequence should be not deterministic
-    for (int i = Time::current_time().msec() % 50; i > 0; i--)
+    for (int i = Time::now() % 50; i > 0; i--)
         rk.rand<unsigned>();
 
     // RootMoves are already sorted by score in descending order
@@ -1536,7 +1543,7 @@ split_point_start: // At split points actual search starts from here
   string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
 
     std::stringstream s;
-    int t = SearchTime.elapsed();
+    Time::point elaspsed = Time::now() - SearchTime + 1;
     int selDepth = 0;
 
     for (size_t i = 0; i < Threads.size(); i++)
@@ -1557,12 +1564,12 @@ split_point_start: // At split points actual search starts from here
             s << "\n";
 
         s << "info depth " << d
-          << " seldepth " << selDepth
-          << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
-          << " nodes " << pos.nodes_searched()
-          << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0)
-          << " time " << t
-          << " multipv " << i + 1
+          << " seldepth "  << selDepth
+          << " score "     << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
+          << " nodes "     << pos.nodes_searched()
+          << " nps "       << pos.nodes_searched() * 1000 / elaspsed
+          << " time "      << elaspsed
+          << " multipv "   << i + 1
           << " pv";
 
         for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
@@ -1749,26 +1756,26 @@ void Thread::idle_loop() {
 
 void check_time() {
 
-  static Time lastInfoTime = Time::current_time();
+  static Time::point lastInfoTime = Time::now();
 
-  if (lastInfoTime.elapsed() >= 1000)
+  if (Time::now() - lastInfoTime >= 1000)
   {
-      lastInfoTime.restart();
+      lastInfoTime = Time::now();
       dbg_print();
   }
 
   if (Limits.ponder)
       return;
 
-  int e = SearchTime.elapsed();
+  Time::point elapsed = Time::now() - SearchTime;
   bool stillAtFirstMove =    Signals.firstRootMove
                          && !Signals.failedLowAtRoot
-                         &&  e > TimeMgr.available_time();
+                         &&  elapsed > TimeMgr.available_time();
 
-  bool noMoreTime =   e > TimeMgr.maximum_time() - 2 * TimerResolution
+  bool noMoreTime =   elapsed > TimeMgr.maximum_time() - 2 * TimerResolution
                    || stillAtFirstMove;
 
   if (   (Limits.use_time_management() && noMoreTime)
-      || (Limits.movetime && e >= Limits.movetime))
+      || (Limits.movetime && elapsed >= Limits.movetime))
       Signals.stop = true;
 }