]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Score root move list during first iteration
[stockfish] / src / search.cpp
index fd5378f92e751050f2217a7d877213926b9dd39c..915e1af25d69ccee4e5faead5f68cf58b14ecd96 100644 (file)
@@ -209,10 +209,6 @@ namespace {
   // Minimum depth for use of singular extension
   const Depth SingularExtensionDepth[2] = { 8 * ONE_PLY /* non-PV */, 6 * ONE_PLY /* PV */};
 
-  // If the TT move is at least SingularExtensionMargin better than the
-  // remaining ones we will extend it.
-  const Value SingularExtensionMargin = Value(0x20);
-
   // Step 12. Futility pruning
 
   // Futility margin for quiescence search
@@ -302,11 +298,10 @@ namespace {
   void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
   void update_killers(Move m, Move killers[]);
   void update_gains(const Position& pos, Move move, Value before, Value after);
-  void qsearch_scoring(Position& pos, MoveStack* mlist, MoveStack* last);
 
   int current_search_time();
   std::string value_to_uci(Value v);
-  int nps(const Position& pos);
+  std::string speed_to_uci(int64_t nodes);
   void poll(const Position& pos);
   void wait_for_stop_or_ponderhit();
 
@@ -558,14 +553,14 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   Move bestMove = id_loop(pos, searchMoves, &ponderMove);
 
   // Print final search statistics
-  cout << "info nodes " << pos.nodes_searched()
-       << " nps " << nps(pos)
-       << " time " << current_search_time() << endl;
+  cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
 
   if (UseLogFile)
   {
+      int t = current_search_time();
+
       LogFile << "Nodes: "          << pos.nodes_searched()
-              << "\nNodes/second: " << nps(pos)
+              << "\nNodes/second: " << (t > 0 ? int(pos.nodes_searched() * 1000 / t) : 0)
               << "\nBest move: "    << move_to_san(pos, bestMove);
 
       StateInfo st;
@@ -605,13 +600,13 @@ namespace {
     Value value, alpha, beta;
     Move bestMove, easyMove;
 
-    // Moves to search are verified, scored and sorted
+    // Moves to search are verified and copied
     Rml.init(pos, searchMoves);
 
     // Initialize FIXME move before Rml.init()
     TT.new_search();
     H.clear();
-    memset(ss, 0, PLY_MAX_PLUS_2 * sizeof(SearchStack));
+    memset(ss, 0, 4 * sizeof(SearchStack));
     *ponderMove = bestMove = easyMove = MOVE_NONE;
     depth = aspirationDelta = 0;
     ss->currentMove = MOVE_NULL; // Hack to skip update_gains()
@@ -627,11 +622,6 @@ namespace {
         return MOVE_NONE;
     }
 
-    // Is one move significantly better than others after initial scoring ?
-    if (   Rml.size() == 1
-        || Rml[0].pv_score > Rml[1].pv_score + EasyMoveMargin)
-        easyMove = Rml[0].pv[0];
-
     // Iterative deepening loop
     while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest)
     {
@@ -700,8 +690,10 @@ namespace {
         if (UseLogFile)
             LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl;
 
-        // Drop the easy move if differs from the new best move
-        if (bestMove != easyMove)
+        // Init easyMove after first iteration or drop if differs from the best move
+        if (depth == 1 && (Rml.size() == 1 || Rml[0].pv_score > Rml[1].pv_score + EasyMoveMargin))
+            easyMove = bestMove;
+        else if (bestMove != easyMove)
             easyMove = MOVE_NONE;
 
         if (UseTimeManagement && !StopRequest)
@@ -801,7 +793,8 @@ namespace {
         bestValue = alpha;
 
     // Step 1. Initialize node and poll. Polling can abort search
-    ss->currentMove = ss->bestMove = threatMove = MOVE_NONE;
+    ss->currentMove = ss->bestMove = threatMove = (ss+1)->excludedMove = MOVE_NONE;
+    (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
     (ss+2)->killers[0] = (ss+2)->killers[1] = (ss+2)->mateKiller = MOVE_NONE;
 
     if (threadID == 0 && ++NodesSincePoll > NodesBetweenPolls)
@@ -1027,9 +1020,7 @@ split_point_start: // At split points actual search starts from here
           if (SendSearchedNodes)
           {
               SendSearchedNodes = false;
-              cout << "info nodes " << nodes
-                   << " nps " << nps(pos)
-                   << " time " << current_search_time() << endl;
+              cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
           }
 
           if (current_search_time() >= 1000)
@@ -1037,7 +1028,9 @@ split_point_start: // At split points actual search starts from here
                    << " currmovenumber " << moveCount << endl;
       }
 
-      isPvMove = (PvNode && moveCount <= (Root ? MultiPV : 1));
+      // At Root and at first iteration do a PV search on all the moves
+      // to score root moves. Otherwise only the first one is the PV.
+      isPvMove = (PvNode && moveCount <= (Root ? MultiPV + 1000 * (depth <= ONE_PLY) : 1));
       moveIsCheck = pos.move_is_check(move, ci);
       captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
@@ -1056,7 +1049,7 @@ split_point_start: // At split points actual search starts from here
 
           if (abs(ttValue) < VALUE_KNOWN_WIN)
           {
-              Value b = ttValue - SingularExtensionMargin;
+              Value b = ttValue - depth;
               ss->excludedMove = move;
               ss->skipNullMove = true;
               Value v = search<NonPV>(pos, ss, b - 1, b, depth / 2, ply);
@@ -1217,7 +1210,7 @@ split_point_start: // At split points actual search starts from here
               ss->bestMove = move;
 
               if (SpNode)
-                  sp->parentSstack->bestMove = move;
+                  sp->ss->bestMove = move;
           }
       }
 
@@ -1432,6 +1425,12 @@ split_point_start: // At split points actual search starts from here
                   bestValue = futilityValue;
               continue;
           }
+
+          // Prune moves with negative or equal SEE
+          if (   futilityBase < beta
+              && depth < DEPTH_ZERO
+              && pos.see(move) <= 0)
+              continue;
       }
 
       // Detect non-capture evasions that are candidate to be pruned
@@ -1500,26 +1499,6 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // qsearch_scoring() scores each move of a list using a qsearch() evaluation,
-  // it is used in RootMoveList to get an initial scoring.
-  void qsearch_scoring(Position& pos, MoveStack* mlist, MoveStack* last) {
-
-    SearchStack ss[PLY_MAX_PLUS_2];
-    StateInfo st;
-
-    memset(ss, 0, 4 * sizeof(SearchStack));
-    ss[0].eval = ss[0].evalMargin = VALUE_NONE;
-
-    for (MoveStack* cur = mlist; cur != last; cur++)
-    {
-        ss[0].currentMove = cur->move;
-        pos.do_move(cur->move, st);
-        cur->score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, DEPTH_ZERO, 1);
-        pos.undo_move(cur->move);
-    }
-  }
-
-
   // check_is_dangerous() tests if a checking move can be pruned in qsearch().
   // bestValue is updated only when returning false because in that case move
   // will be pruned.
@@ -1857,6 +1836,14 @@ split_point_start: // At split points actual search starts from here
         H.update_gain(pos.piece_on(move_to(m)), move_to(m), -(before + after));
   }
 
+  // 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;
+  }
+
 
   // value_to_uci() converts a value to a string suitable for use with the UCI
   // protocol specifications:
@@ -1878,21 +1865,19 @@ 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.
-
-  int current_search_time() {
-
-    return get_system_time() - SearchStartTime;
-  }
+  // speed_to_uci() returns a string with time stats of current search suitable
+  // to be sent to UCI gui.
 
+  std::string speed_to_uci(int64_t nodes) {
 
-  // nps() computes the current nodes/second count
+    std::stringstream s;
+    int t = current_search_time();
 
-  int nps(const Position& pos) {
+    s << " nodes " << nodes
+      << " nps "   << (t > 0 ? int(nodes * 1000 / t) : 0)
+      << " time "  << t;
 
-    int t = current_search_time();
-    return (t > 0 ? int((pos.nodes_searched() * 1000) / t) : 0);
+    return s.str();
   }
 
 
@@ -2109,16 +2094,19 @@ split_point_start: // At split points actual search starts from here
 
             threads[threadID].state = THREAD_SEARCHING;
 
-            // Here we call search() with SplitPoint template parameter set to true
+            // Copy SplitPoint position and search stack and call search()
+            // with SplitPoint template parameter set to true.
+            SearchStack ss[PLY_MAX_PLUS_2];
             SplitPoint* tsp = threads[threadID].splitPoint;
             Position pos(*tsp->pos, threadID);
-            SearchStack* ss = tsp->sstack[threadID] + 1;
-            ss->sp = tsp;
+
+            memcpy(ss, tsp->ss - 1, 4 * sizeof(SearchStack));
+            (ss+1)->sp = tsp;
 
             if (tsp->pvNode)
-                search<PV, true, false>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
+                search<PV, true, false>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
             else
-                search<NonPV, true, false>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
+                search<NonPV, true, false>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
 
             assert(threads[threadID].state == THREAD_SEARCHING);
 
@@ -2363,7 +2351,7 @@ split_point_start: // At split points actual search starts from here
     splitPoint.moveCount = moveCount;
     splitPoint.pos = &pos;
     splitPoint.nodes = 0;
-    splitPoint.parentSstack = ss;
+    splitPoint.ss = ss;
     for (i = 0; i < activeThreads; i++)
         splitPoint.slaves[i] = 0;
 
@@ -2390,12 +2378,10 @@ split_point_start: // At split points actual search starts from here
     lock_release(&mpLock);
 
     // Tell the threads that they have work to do. This will make them leave
-    // their idle loop. But before copy search stack tail for each thread.
+    // their idle loop.
     for (i = 0; i < activeThreads; i++)
         if (i == master || splitPoint.slaves[i])
         {
-            memcpy(splitPoint.sstack[i], ss - 1, 4 * sizeof(SearchStack));
-
             assert(i == master || threads[i].state == THREAD_BOOKED);
 
             threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop()
@@ -2536,9 +2522,7 @@ split_point_start: // At split points actual search starts from here
       << " 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)
+      << speed_to_uci(pos.nodes_searched())
       << " pv "    << l.str();
 
     return s.str();
@@ -2553,11 +2537,8 @@ split_point_start: // At split points actual search starts from here
     clear();
     bestMoveChanges = 0;
 
-    // Generate all legal moves and score them
+    // Generate all legal moves and add them to RootMoveList
     MoveStack* last = generate<MV_LEGAL>(pos, mlist);
-    qsearch_scoring(pos, mlist, last);
-
-    // Add each move to the RootMoveList's vector
     for (MoveStack* cur = mlist; cur != last; cur++)
     {
         // If we have a searchMoves[] list then verify cur->move
@@ -2570,10 +2551,9 @@ split_point_start: // At split points actual search starts from here
         RootMove rm;
         rm.pv[0] = cur->move;
         rm.pv[1] = MOVE_NONE;
-        rm.pv_score = Value(cur->score);
+        rm.pv_score = -VALUE_INFINITE;
         push_back(rm);
     }
-    sort();
   }
 
 } // namespace