]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Revert Chess960 fix
[stockfish] / src / search.cpp
index 6aef6179d28737398587acae1fe08d46c6f8d217..0e249705fa59a89e00d1f689d714bf33bbe024f9 100644 (file)
@@ -149,7 +149,7 @@ namespace {
     void set_non_pv_scores(const Position& pos);
 
     void sort() { insertion_sort<RootMove, Base::iterator>(begin(), end()); }
-    void sort_multipv(int n) { insertion_sort<RootMove, Base::iterator>(begin(), begin() + n); }
+    void sort_multipv(int n) { insertion_sort<RootMove, Base::iterator>(begin(), begin() + n + 1); }
   };
 
 
@@ -254,8 +254,8 @@ namespace {
 
   // Time managment variables
   int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime;
-  bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit;
-  bool FirstRootMove, AbortSearch, Quit, AspirationFailLow;
+  bool UseTimeManagement, InfiniteSearch, Pondering, StopOnPonderhit;
+  bool FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
   TimeManager TimeMgr;
 
   // Log file
@@ -267,6 +267,7 @@ namespace {
 
   // Node counters, used only by thread[0] but try to keep in different cache
   // lines (64 bytes each) from the heavy multi-thread read accessed variables.
+  bool SendSearchedNodes;
   int NodesSincePoll;
   int NodesBetweenPolls = 30000;
 
@@ -310,7 +311,6 @@ namespace {
   std::string value_to_uci(Value v);
   int nps(const Position& pos);
   void poll(const Position& pos);
-  void ponderhit();
   void wait_for_stop_or_ponderhit();
   void init_ss_array(SearchStack* ss, int size);
 
@@ -401,14 +401,14 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
            int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) {
 
   // Initialize global search variables
-  StopOnPonderhit = AbortSearch = Quit = AspirationFailLow = false;
+  StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = SendSearchedNodes = false;
   NodesSincePoll = 0;
   SearchStartTime = get_system_time();
   ExactMaxTime = maxTime;
   MaxDepth = maxDepth;
   MaxNodes = maxNodes;
   InfiniteSearch = infinite;
-  PonderSearch = ponder;
+  Pondering = ponder;
   UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch;
 
   // Look for a book move, only during games, not tests
@@ -420,11 +420,11 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
       Move bookMove = OpeningBook.get_move(pos, Options["Best Book Move"].value<bool>());
       if (bookMove != MOVE_NONE)
       {
-          if (PonderSearch)
+          if (Pondering)
               wait_for_stop_or_ponderhit();
 
           cout << "bestmove " << bookMove << endl;
-          return true;
+          return !QuitRequest;
       }
   }
 
@@ -451,10 +451,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   MultiPV                   = Options["MultiPV"].value<int>();
   UseLogFile                = Options["Use Search Log"].value<bool>();
 
-  if (UseLogFile)
-      LogFile.open(Options["Search Log Filename"].value<std::string>().c_str(), std::ios::out | std::ios::app);
-
-  read_weights(pos.side_to_move());
+  read_evaluation_uci_options(pos.side_to_move());
 
   // Set the number of active threads
   ThreadsMgr.read_uci_options();
@@ -483,12 +480,17 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
 
   // Write search information to log file
   if (UseLogFile)
-      LogFile << "Searching: " << pos.to_fen() << endl
-              << "infinite: "  << infinite
-              << " ponder: "   << ponder
-              << " time: "     << myTime
+  {
+      std::string name = Options["Search Log Filename"].value<std::string>();
+      LogFile.open(name.c_str(), std::ios::out | std::ios::app);
+
+      LogFile << "Searching: "  << pos.to_fen()
+              << "\ninfinite: " << infinite
+              << " ponder: "    << ponder
+              << " time: "      << myTime
               << " increment: " << myIncrement
               << " moves to go: " << movesToGo << endl;
+  }
 
   // We're ready to start thinking. Call the iterative deepening loop function
   Move ponderMove = MOVE_NONE;
@@ -499,22 +501,8 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
        << " nps " << nps(pos)
        << " time " << current_search_time() << endl;
 
-  // If we are pondering or in infinite search, we shouldn't print the
-  // best move before we are told to do so.
-  if (!AbortSearch && (PonderSearch || InfiniteSearch))
-      wait_for_stop_or_ponderhit();
-
-  // Could be both MOVE_NONE when searching on a stalemate position
-  cout << "bestmove " << bestMove << " ponder " << ponderMove << endl;
-
   if (UseLogFile)
   {
-      if (dbg_show_mean)
-          dbg_print_mean(LogFile);
-
-      if (dbg_show_hit_rate)
-          dbg_print_hit_rate(LogFile);
-
       LogFile << "\nNodes: " << pos.nodes_searched()
               << "\nNodes/second: " << nps(pos)
               << "\nBest move: " << move_to_san(pos, bestMove);
@@ -524,15 +512,25 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
       LogFile << "\nPonder move: "
               << move_to_san(pos, ponderMove) // Works also with MOVE_NONE
               << endl;
-  }
 
-  if (UseLogFile)
+      // Return from think() with unchanged position
+      pos.undo_move(bestMove);
+
       LogFile.close();
+  }
 
   // This makes all the threads to go to sleep
   ThreadsMgr.set_active_threads(1);
 
-  return !Quit;
+  // If we are pondering or in infinite search, we shouldn't print the
+  // best move before we are told to do so.
+  if (!StopRequest && (Pondering || InfiniteSearch))
+      wait_for_stop_or_ponderhit();
+
+  // Could be both MOVE_NONE when searching on a stalemate position
+  cout << "bestmove " << bestMove << " ponder " << ponderMove << endl;
+
+  return !QuitRequest;
 }
 
 
@@ -608,7 +606,7 @@ namespace {
         // Search to the current depth, rml is updated and sorted
         value = root_search(pos, ss, alpha, beta, depth, rml);
 
-        if (AbortSearch)
+        if (StopRequest)
             break; // Value cannot be trusted. Break out immediately!
 
         //Save info about search result
@@ -656,7 +654,7 @@ namespace {
 
             if (stopSearch)
             {
-                if (PonderSearch)
+                if (Pondering)
                     StopOnPonderhit = true;
                 else
                     break;
@@ -718,7 +716,7 @@ namespace {
         rml.sort();
 
         // Step 10. Loop through all moves in the root move list
-        for (int i = 0; i < (int)rml.size() && !AbortSearch; i++)
+        for (int i = 0; i < (int)rml.size() && !StopRequest; i++)
         {
             // This is used by time management
             FirstRootMove = (i == 0);
@@ -726,6 +724,16 @@ namespace {
             // Save the current node count before the move is searched
             nodes = pos.nodes_searched();
 
+            // If it's time to send nodes info, do it here where we have the
+            // correct accumulated node counts searched by each thread.
+            if (SendSearchedNodes)
+            {
+                SendSearchedNodes = false;
+                cout << "info nodes " << nodes
+                     << " nps " << nps(pos)
+                     << " time " << current_search_time() << endl;
+            }
+
             // Pick the next root move, and print the move and the move number to
             // the standard output.
             move = ss->currentMove = rml[i].pv[0];
@@ -805,7 +813,7 @@ namespace {
                 pos.undo_move(move);
 
                 // Can we exit fail high loop ?
-                if (AbortSearch || value < beta)
+                if (StopRequest || value < beta)
                     break;
 
                 // We are failing high and going to do a research. It's important to update
@@ -828,7 +836,7 @@ namespace {
             // ran out of time. In this case, the return value of the search cannot
             // be trusted, and we break out of the loop without updating the best
             // move and/or PV.
-            if (AbortSearch)
+            if (StopRequest)
                 break;
 
             // Remember searched nodes counts for this move
@@ -884,7 +892,7 @@ namespace {
         } // Root moves loop
 
         // Can we exit fail low loop ?
-        if (AbortSearch || !AspirationFailLow)
+        if (StopRequest || !AspirationFailLow)
             break;
 
         // Prepare for a research after a fail low, each time with a wider window
@@ -898,7 +906,7 @@ namespace {
 
     // Write PV lines to transposition table, in case the relevant entries
     // have been overwritten during the search.
-    for (int i = 0; i < MultiPV; i++)
+    for (int i = 0; i < Min(MultiPV, (int)rml.size()); i++)
         rml[i].insert_pv_in_tt(pos);
 
     return alpha;
@@ -961,7 +969,7 @@ namespace {
     }
 
     // Step 2. Check for aborted search and immediate draw
-    if (   AbortSearch
+    if (   StopRequest
         || ThreadsMgr.cutoff_at_splitpoint(threadID)
         || pos.is_draw()
         || ply >= PLY_MAX - 1)
@@ -1349,7 +1357,7 @@ split_point_start: // At split points actual search starts from here
           && ThreadsMgr.active_threads() > 1
           && bestValue < beta
           && ThreadsMgr.available_thread_exists(threadID)
-          && !AbortSearch
+          && !StopRequest
           && !ThreadsMgr.cutoff_at_splitpoint(threadID)
           && Iteration <= 99)
           ThreadsMgr.split<FakeSplit>(pos, ss, ply, &alpha, beta, &bestValue, depth,
@@ -1366,7 +1374,7 @@ split_point_start: // At split points actual search starts from here
     // Step 20. Update tables
     // If the search is not aborted, update the transposition table,
     // history counters, and killer moves.
-    if (!SpNode && !AbortSearch && !ThreadsMgr.cutoff_at_splitpoint(threadID))
+    if (!SpNode && !StopRequest && !ThreadsMgr.cutoff_at_splitpoint(threadID))
     {
         move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove;
         vt   = bestValue <= oldAlpha ? VALUE_TYPE_UPPER
@@ -1922,12 +1930,21 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // current_search_time() returns the number of milliseconds which have passed
-  // since the beginning of the current search.
+  // init_ss_array() does a fast reset of the first entries of a SearchStack
+  // array and of all the excludedMove and skipNullMove entries.
 
-  int current_search_time() {
+  void init_ss_array(SearchStack* ss, int size) {
 
-    return get_system_time() - SearchStartTime;
+    for (int i = 0; i < size; i++, ss++)
+    {
+        ss->excludedMove = MOVE_NONE;
+        ss->skipNullMove = false;
+        ss->reduction = DEPTH_ZERO;
+        ss->sp = NULL;
+
+        if (i < 3)
+            ss->killers[0] = ss->killers[1] = ss->mateKiller = MOVE_NONE;
+    }
   }
 
 
@@ -1950,7 +1967,17 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
-  // nps() computes the current nodes/second count.
+
+  // current_search_time() returns the number of milliseconds which have passed
+  // since the beginning of the current search.
+
+  int current_search_time() {
+
+    return get_system_time() - SearchStartTime;
+  }
+
+
+  // nps() computes the current nodes/second count
 
   int nps(const Position& pos) {
 
@@ -1979,18 +2006,28 @@ split_point_start: // At split points actual search starts from here
 
         if (command == "quit")
         {
-            AbortSearch = true;
-            PonderSearch = false;
-            Quit = true;
+            // Quit the program as soon as possible
+            Pondering = false;
+            QuitRequest = StopRequest = true;
             return;
         }
         else if (command == "stop")
         {
-            AbortSearch = true;
-            PonderSearch = false;
+            // Stop calculating as soon as possible, but still send the "bestmove"
+            // and possibly the "ponder" token when finishing the search.
+            Pondering = false;
+            StopRequest = true;
         }
         else if (command == "ponderhit")
-            ponderhit();
+        {
+            // The opponent has played the expected move. GUI sends "ponderhit" if
+            // we were told to ponder on the same move the opponent has played. We
+            // should continue searching but switching from pondering to normal search.
+            Pondering = false;
+
+            if (StopOnPonderhit)
+                StopRequest = true;
+        }
     }
 
     // Print search information
@@ -2012,12 +2049,12 @@ split_point_start: // At split points actual search starts from here
         if (dbg_show_hit_rate)
             dbg_print_hit_rate();
 
-        cout << "info nodes " << pos.nodes_searched() << " nps " << nps(pos)
-             << " time " << t << endl;
+        // Send info on searched nodes as soon as we return to root
+        SendSearchedNodes = true;
     }
 
     // Should we stop the search?
-    if (PonderSearch)
+    if (Pondering)
         return;
 
     bool stillAtFirstMove =    FirstRootMove
@@ -2027,49 +2064,10 @@ split_point_start: // At split points actual search starts from here
     bool noMoreTime =   t > TimeMgr.maximum_time()
                      || stillAtFirstMove;
 
-    if (   (Iteration >= 3 && UseTimeManagement && noMoreTime)
+    if (   (UseTimeManagement && noMoreTime)
         || (ExactMaxTime && t >= ExactMaxTime)
-        || (Iteration >= 3 && MaxNodes && pos.nodes_searched() >= MaxNodes))
-        AbortSearch = true;
-  }
-
-
-  // ponderhit() is called when the program is pondering (i.e. thinking while
-  // it's the opponent's turn to move) in order to let the engine know that
-  // it correctly predicted the opponent's move.
-
-  void ponderhit() {
-
-    int t = current_search_time();
-    PonderSearch = false;
-
-    bool stillAtFirstMove =    FirstRootMove
-                           && !AspirationFailLow
-                           &&  t > TimeMgr.available_time();
-
-    bool noMoreTime =   t > TimeMgr.maximum_time()
-                     || stillAtFirstMove;
-
-    if (Iteration >= 3 && UseTimeManagement && (noMoreTime || StopOnPonderhit))
-        AbortSearch = true;
-  }
-
-
-  // init_ss_array() does a fast reset of the first entries of a SearchStack
-  // array and of all the excludedMove and skipNullMove entries.
-
-  void init_ss_array(SearchStack* ss, int size) {
-
-    for (int i = 0; i < size; i++, ss++)
-    {
-        ss->excludedMove = MOVE_NONE;
-        ss->skipNullMove = false;
-        ss->reduction = DEPTH_ZERO;
-        ss->sp = NULL;
-
-        if (i < 3)
-            ss->killers[0] = ss->killers[1] = ss->mateKiller = MOVE_NONE;
-    }
+        || (MaxNodes && pos.nodes_searched() >= MaxNodes)) // FIXME
+        StopRequest = true;
   }
 
 
@@ -2078,7 +2076,7 @@ split_point_start: // At split points actual search starts from here
   // the UCI protocol: When pondering, the engine is not allowed to give a
   // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
   // We simply wait here until one of these commands is sent, and return,
-  // after which the bestmove and pondermove will be printed (in id_loop()).
+  // after which the bestmove and pondermove will be printed.
 
   void wait_for_stop_or_ponderhit() {
 
@@ -2086,12 +2084,13 @@ split_point_start: // At split points actual search starts from here
 
     while (true)
     {
+        // Wait for a command from stdin
         if (!std::getline(std::cin, command))
             command = "quit";
 
         if (command == "quit")
         {
-            Quit = true;
+            QuitRequest = true;
             break;
         }
         else if (command == "ponderhit" || command == "stop")
@@ -2616,19 +2615,21 @@ split_point_start: // At split points actual search starts from here
 
   std::string RootMove::pv_info_to_uci(const Position& pos, Value alpha, Value beta, int pvLine) {
 
-    std::stringstream s;
+    std::stringstream s, l;
+    Move* m = pv;
+
+    while (*m != MOVE_NONE)
+        l << *m++ << " ";
 
     s << "info depth " << Iteration // FIXME
+      << " seldepth " << int(m - pv)
       << " multipv " << pvLine + 1
       << " score " << value_to_uci(pv_score)
       << (pv_score >= beta ? " lowerbound" : pv_score <= alpha ? " upperbound" : "")
       << " time "  << current_search_time()
       << " nodes " << pos.nodes_searched()
       << " nps "   << nps(pos)
-      << " pv ";
-
-    for (Move* m = pv; *m != MOVE_NONE; m++)
-        s << *m << " ";
+      << " pv "    << l.str();
 
     if (UseLogFile && pvLine == 0)
     {