]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Move extract_pv_from_tt() and insert_pv_in_tt() under RootMove
[stockfish] / src / search.cpp
index 7fa7c1bec53f37bf9e8bb2b92508ff2b0cedfd12..69187d3ae29dcb97ff93767c8f61a838a952f886 100644 (file)
@@ -107,43 +107,37 @@ namespace {
 
   // RootMove struct is used for moves at the root at the tree. For each root
   // move, we store two scores, a node count, and a PV (really a refutation
-  // in the case of moves which fail low). Value pvScore is normally set at
-  // -VALUE_INFINITE for all non-pv moves, while nonPvScore is computed
+  // in the case of moves which fail low). Value pv_score is normally set at
+  // -VALUE_INFINITE for all non-pv moves, while non_pv_score is computed
   // according to the order in which moves are returned by MovePicker.
 
   struct RootMove {
 
-    RootMove() : nodes(0) { pv_score = non_pv_score = -VALUE_INFINITE; }
+    RootMove();
+    RootMove(const RootMove& rm) { *this = rm; }
+    RootMove& operator=(const RootMove& rm);
 
     // RootMove::operator<() is the comparison function used when
     // sorting the moves. A move m1 is considered to be better
-    // than a move m2 if it has an higher pvScore, or if it has
-    // equal pvScore but m1 has the higher nonPvScore. In this way
-    // we are guaranteed that PV moves are always sorted as first.
+    // than a move m2 if it has an higher pv_score, or if it has
+    // equal pv_score but m1 has the higher non_pv_score. In this
+    // way we are guaranteed that PV moves are always sorted as first.
     bool operator<(const RootMove& m) const {
-      return pv_score != m.pv_score ? pv_score < m.pv_score : non_pv_score <= m.non_pv_score;
+      return pv_score != m.pv_score ? pv_score < m.pv_score
+                                    : non_pv_score < m.non_pv_score;
     }
-    void set_pv(const Move newPv[]);
 
-    Move move;
+    void extract_pv_from_tt(Position& pos);
+    void insert_pv_in_tt(Position& pos);
+
+    int64_t nodes;
     Value pv_score;
     Value non_pv_score;
-    int64_t nodes;
     Move pv[PLY_MAX_PLUS_2];
   };
 
-  void RootMove::set_pv(const Move newPv[]) {
-
-    int i = -1;
-
-    while (newPv[++i] != MOVE_NONE)
-        pv[i] = newPv[i];
-
-    pv[i] = MOVE_NONE;
-  }
-
 
-  // The RootMoveList struct is essentially a std::vector<> of RootMove objects,
+  // RootMoveList struct is essentially a std::vector<> of RootMove objects,
   // with an handful of methods above the standard ones.
 
   struct RootMoveList : public std::vector<RootMove> {
@@ -151,10 +145,10 @@ namespace {
     typedef std::vector<RootMove> Base;
 
     RootMoveList(Position& pos, Move searchMoves[]);
-    void sort() { sort_multipv((int)size() - 1); } // Sort all items
-
     void set_non_pv_scores(const Position& pos);
-    void sort_multipv(int n);
+
+    void sort() { insertion_sort<RootMove, Base::iterator>(begin(), end()); }
+    void sort_multipv(int n) { insertion_sort<RootMove, Base::iterator>(begin(), begin() + n); }
   };
 
 
@@ -281,7 +275,7 @@ namespace {
   /// Local functions
 
   Value id_loop(Position& pos, Move searchMoves[]);
-  Value root_search(Position& pos, SearchStack* ss, Move* pv, RootMoveList& rml, Value* alphaPtr, Value* betaPtr);
+  Value root_search(Position& pos, SearchStack* ss, Value* alphaPtr, Value* betaPtr, Depth depth, RootMoveList& rml);
 
   template <NodeType PvNode, bool SpNode>
   Value search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth, int ply);
@@ -319,8 +313,6 @@ namespace {
   void wait_for_stop_or_ponderhit();
   void init_ss_array(SearchStack* ss, int size);
   void print_pv_info(const Position& pos, Move pv[], Value alpha, Value beta, Value value);
-  void insert_pv_in_tt(const Position& pos, Move pv[]);
-  void extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]);
 
 #if !defined(_MSC_VER)
   void* init_thread(void* threadID);
@@ -521,7 +513,7 @@ namespace {
   Value id_loop(Position& pos, Move searchMoves[]) {
 
     SearchStack ss[PLY_MAX_PLUS_2];
-    Move pv[PLY_MAX_PLUS_2];
+    Depth depth;
     Move EasyMove = MOVE_NONE;
     Value value, alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
 
@@ -546,20 +538,19 @@ namespace {
          << " time " << current_search_time()
          << " nodes " << pos.nodes_searched()
          << " nps " << nps(pos)
-         << " pv " << rml[0].move << "\n";
+         << " pv " << rml[0].pv[0] << "\n";
 
     // Initialize
     TT.new_search();
     H.clear();
     init_ss_array(ss, PLY_MAX_PLUS_2);
-    pv[0] = pv[1] = MOVE_NONE;
     ValueByIteration[1] = rml[0].pv_score;
     Iteration = 1;
 
     // 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].move;
+        EasyMove = rml[0].pv[0];
 
     // Iterative deepening loop
     while (Iteration < PLY_MAX)
@@ -583,12 +574,11 @@ namespace {
             beta  = Min(ValueByIteration[Iteration - 1] + AspirationDelta,  VALUE_INFINITE);
         }
 
-        // Search to the current depth, rml is updated and sorted, alpha and beta could change
-        value = root_search(pos, ss, pv, rml, &alpha, &beta);
+        // Search to the current depth, rml is updated and sorted,
+        // alpha and beta could change.
+        depth = (Iteration - 2) * ONE_PLY + InitialDepth;
 
-        // Write PV to transposition table, in case the relevant entries have
-        // been overwritten during the search.
-        insert_pv_in_tt(pos, pv);
+        value = root_search(pos, ss, &alpha, &beta, depth, rml);
 
         if (AbortSearch)
             break; // Value cannot be trusted. Break out immediately!
@@ -597,7 +587,7 @@ namespace {
         ValueByIteration[Iteration] = value;
 
         // Drop the easy move if differs from the new best move
-        if (pv[0] != EasyMove)
+        if (rml[0].pv[0] != EasyMove)
             EasyMove = MOVE_NONE;
 
         if (UseTimeManagement)
@@ -618,7 +608,7 @@ namespace {
 
             // Stop search early if one move seems to be much better than the others
             if (   Iteration >= 8
-                && EasyMove == pv[0]
+                && EasyMove == rml[0].pv[0]
                 && (  (   rml[0].nodes > (pos.nodes_searched() * 85) / 100
                        && current_search_time() > TimeMgr.available_time() / 16)
                     ||(   rml[0].nodes > (pos.nodes_searched() * 98) / 100
@@ -660,18 +650,10 @@ namespace {
              << " time " << current_search_time() << endl;
 
     // Print the best move and the ponder move to the standard output
-    if (pv[0] == MOVE_NONE || MultiPV > 1)
-    {
-        pv[0] = rml[0].move;
-        pv[1] = MOVE_NONE;
-    }
-
-    assert(pv[0] != MOVE_NONE);
+    cout << "bestmove " << rml[0].pv[0];
 
-    cout << "bestmove " << pv[0];
-
-    if (pv[1] != MOVE_NONE)
-        cout << " ponder " << pv[1];
+    if (rml[0].pv[1] != MOVE_NONE)
+        cout << " ponder " << rml[0].pv[1];
 
     cout << endl;
 
@@ -685,12 +667,12 @@ namespace {
 
         LogFile << "\nNodes: " << pos.nodes_searched()
                 << "\nNodes/second: " << nps(pos)
-                << "\nBest move: " << move_to_san(pos, pv[0]);
+                << "\nBest move: " << move_to_san(pos, rml[0].pv[0]);
 
         StateInfo st;
-        pos.do_move(pv[0], st);
+        pos.do_move(rml[0].pv[0], st);
         LogFile << "\nPonder move: "
-                << move_to_san(pos, pv[1]) // Works also with MOVE_NONE
+                << move_to_san(pos, rml[0].pv[1]) // Works also with MOVE_NONE
                 << endl;
     }
     return rml[0].pv_score;
@@ -702,13 +684,13 @@ namespace {
   // scheme, prints some information to the standard output and handles
   // the fail low/high loops.
 
-  Value root_search(Position& pos, SearchStack* ss, Move* pv, RootMoveList& rml, Value* alphaPtr, Value* betaPtr) {
-
+  Value root_search(Position& pos, SearchStack* ss, Value* alphaPtr,
+                    Value* betaPtr, Depth depth, RootMoveList& rml) {
     StateInfo st;
     CheckInfo ci(pos);
     int64_t nodes;
     Move move;
-    Depth depth, ext, newDepth;
+    Depth ext, newDepth;
     Value value, alpha, beta;
     bool isCheck, moveIsCheck, captureOrPromotion, dangerous;
     int researchCountFH, researchCountFL;
@@ -717,7 +699,6 @@ namespace {
     alpha = *alphaPtr;
     beta = *betaPtr;
     isCheck = pos.is_check();
-    depth = (Iteration - 2) * ONE_PLY + InitialDepth;
 
     // Step 1. Initialize node (polling is omitted at root)
     ss->currentMove = ss->bestMove = MOVE_NONE;
@@ -756,7 +737,7 @@ namespace {
 
             // Pick the next root move, and print the move and the move number to
             // the standard output.
-            move = ss->currentMove = rml[i].move;
+            move = ss->currentMove = rml[i].pv[0];
 
             if (current_search_time() >= 1000)
                 cout << "info currmove " << move
@@ -813,18 +794,6 @@ namespace {
                             value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, 1);
                             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 * ONE_PLY)
-                        {
-                            assert(newDepth - ONE_PLY >= ONE_PLY);
-
-                            ss->reduction = ONE_PLY;
-                            value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, 1);
-                            doFullDepthSearch = (value > alpha);
-                        }
                         ss->reduction = DEPTH_ZERO; // Restore original reduction
                     }
 
@@ -850,13 +819,12 @@ 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[i].pv_score = value;
                 ss->bestMove = move;
-                extract_pv_from_tt(pos, move, pv);
-                rml[i].set_pv(pv);
+                rml[i].pv_score = value;
+                rml[i].extract_pv_from_tt(pos);
 
                 // Print information to the standard output
-                print_pv_info(pos, pv, alpha, beta, value);
+                print_pv_info(pos, rml[i].pv, alpha, beta, value);
 
                 // Prepare for a research after a fail high, each time with a wider window
                 *betaPtr = beta = Min(beta + AspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
@@ -886,10 +854,9 @@ namespace {
                 // PV move or new best move!
 
                 // Update PV
-                rml[i].pv_score = value;
                 ss->bestMove = move;
-                extract_pv_from_tt(pos, move, pv);
-                rml[i].set_pv(pv);
+                rml[i].pv_score = value;
+                rml[i].extract_pv_from_tt(pos);
 
                 if (MultiPV == 1)
                 {
@@ -900,7 +867,7 @@ namespace {
                         BestMoveChangesByIteration[Iteration]++;
 
                     // Print information to the standard output
-                    print_pv_info(pos, pv, alpha, beta, value);
+                    print_pv_info(pos, rml[i].pv, alpha, beta, value);
 
                     // Raise alpha to setup proper non-pv search upper bound
                     if (value > alpha)
@@ -949,6 +916,11 @@ namespace {
     // Sort the moves before to return
     rml.sort();
 
+    // Write PV lines to transposition table, in case the relevant entries
+    // have been overwritten during the search.
+    for (int i = 0; i < MultiPV; i++)
+        rml[i].insert_pv_in_tt(pos);
+
     return alpha;
   }
 
@@ -1332,19 +1304,6 @@ split_point_start: // At split points actual search starts from here
 
                   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 * ONE_PLY)
-              {
-                  assert(newDepth - ONE_PLY >= ONE_PLY);
-
-                  ss->reduction = ONE_PLY;
-                  alpha = SpNode ? sp->alpha : alpha;
-                  value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, ply+1);
-                  doFullDepthSearch = (value > alpha);
-              }
               ss->reduction = DEPTH_ZERO; // Restore original reduction
           }
 
@@ -2184,60 +2143,6 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // insert_pv_in_tt() is called at the end of a search iteration, and inserts
-  // the PV back into the TT. This makes sure the old PV moves are searched
-  // first, even if the old TT entries have been overwritten.
-
-  void insert_pv_in_tt(const Position& pos, Move pv[]) {
-
-    StateInfo st;
-    TTEntry* tte;
-    Position p(pos, pos.thread());
-    Value v, m = VALUE_NONE;
-
-    for (int i = 0; pv[i] != MOVE_NONE; i++)
-    {
-        tte = TT.retrieve(p.get_key());
-        if (!tte || tte->move() != pv[i])
-        {
-            v = (p.is_check() ? VALUE_NONE : evaluate(p, m));
-            TT.store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[i], v, m);
-        }
-        p.do_move(pv[i], st);
-    }
-  }
-
-
-  // extract_pv_from_tt() builds a PV by adding moves from the transposition table.
-  // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
-  // allow to always have a ponder move even when we fail high at root and also a
-  // long PV to print that is important for position analysis.
-
-  void extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]) {
-
-    StateInfo st;
-    TTEntry* tte;
-    Position p(pos, pos.thread());
-    int ply = 0;
-
-    assert(bestMove != MOVE_NONE);
-
-    pv[ply] = bestMove;
-    p.do_move(pv[ply++], st);
-
-    while (   (tte = TT.retrieve(p.get_key())) != NULL
-           && tte->move() != MOVE_NONE
-           && move_is_legal(p, tte->move())
-           && ply < PLY_MAX
-           && (!p.is_draw() || ply < 2))
-    {
-        pv[ply] = tte->move();
-        p.do_move(pv[ply++], st);
-    }
-    pv[ply] = MOVE_NONE;
-  }
-
-
   // init_thread() is the function which is called when a new thread is
   // launched. It simply calls the idle_loop() function with the supplied
   // threadID. There are two versions of this function; one for POSIX
@@ -2665,16 +2570,96 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  /// The RootMoveList class
+  /// RootMove and RootMoveList method's definitions
+
+  RootMove::RootMove() {
+
+    nodes = 0;
+    pv_score = non_pv_score = -VALUE_INFINITE;
+    pv[0] = MOVE_NONE;
+  }
+
+  RootMove& RootMove::operator=(const RootMove& rm) {
+
+    const Move* src = rm.pv;
+    Move* dst = pv;
+
+    // Avoid a costly full rm.pv[] copy
+    do *dst++ = *src; while (*src++ != MOVE_NONE);
+
+    nodes = rm.nodes;
+    pv_score = rm.pv_score;
+    non_pv_score = rm.non_pv_score;
+    return *this;
+  }
+
+  // extract_pv_from_tt() builds a PV by adding moves from the transposition table.
+  // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
+  // allow to always have a ponder move even when we fail high at root and also a
+  // long PV to print that is important for position analysis.
+
+  void RootMove::extract_pv_from_tt(Position& pos) {
+
+    StateInfo state[PLY_MAX_PLUS_2], *st = state;
+    TTEntry* tte;
+    int ply = 1;
+
+    assert(pv[0] != MOVE_NONE && move_is_legal(pos, pv[0]));
+
+    pos.do_move(pv[0], *st++);
+
+    while (   (tte = TT.retrieve(pos.get_key())) != NULL
+           && tte->move() != MOVE_NONE
+           && move_is_legal(pos, tte->move())
+           && ply < PLY_MAX
+           && (!pos.is_draw() || ply < 2))
+    {
+        pv[ply] = tte->move();
+        pos.do_move(pv[ply++], *st++);
+    }
+    pv[ply] = MOVE_NONE;
+
+    do pos.undo_move(pv[--ply]); while (ply);
+  }
+
+  // insert_pv_in_tt() is called at the end of a search iteration, and inserts
+  // the PV back into the TT. This makes sure the old PV moves are searched
+  // first, even if the old TT entries have been overwritten.
+
+  void RootMove::insert_pv_in_tt(Position& pos) {
+
+    StateInfo state[PLY_MAX_PLUS_2], *st = state;
+    TTEntry* tte;
+    Key k;
+    Value v, m = VALUE_NONE;
+    int ply = 0;
+
+    assert(pv[0] != MOVE_NONE && move_is_legal(pos, pv[0]));
+
+    do {
+        k = pos.get_key();
+        tte = TT.retrieve(k);
+
+        // Don't overwrite exsisting correct entries
+        if (!tte || tte->move() != pv[ply])
+        {
+            v = (pos.is_check() ? VALUE_NONE : evaluate(pos, m));
+            TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m);
+        }
+        pos.do_move(pv[ply], *st++);
+
+    } while (pv[++ply] != MOVE_NONE);
+
+    do pos.undo_move(pv[--ply]); while (ply);
+  }
 
-  // RootMoveList c'tor
 
   RootMoveList::RootMoveList(Position& pos, Move searchMoves[]) {
 
     SearchStack ss[PLY_MAX_PLUS_2];
     MoveStack mlist[MOVES_MAX];
     StateInfo st;
-    bool includeAllMoves = (searchMoves[0] == MOVE_NONE);
+    Move* sm;
 
     // Initialize search stack
     init_ss_array(ss, PLY_MAX_PLUS_2);
@@ -2683,25 +2668,26 @@ split_point_start: // At split points actual search starts from here
     // Generate all legal moves
     MoveStack* last = generate_moves(pos, mlist);
 
-    // Add each move to the moves[] array
+    // Add each move to the RootMoveList's vector
     for (MoveStack* cur = mlist; cur != last; cur++)
     {
-        bool includeMove = includeAllMoves;
-
-        for (int k = 0; !includeMove && searchMoves[k] != MOVE_NONE; k++)
-            includeMove = (searchMoves[k] == cur->move);
+        // If we have a searchMoves[] list then verify cur->move
+        // is in the list before to add it.
+        for (sm = searchMoves; *sm && *sm != cur->move; sm++) {}
 
-        if (!includeMove)
+        if (searchMoves[0] && *sm != cur->move)
             continue;
 
         // Find a quick score for the move and add to the list
+        pos.do_move(cur->move, st);
+
         RootMove rm;
-        rm.move = ss[0].currentMove = rm.pv[0] = cur->move;
+        rm.pv[0] = ss[0].currentMove = cur->move;
         rm.pv[1] = MOVE_NONE;
-        pos.do_move(cur->move, st);
         rm.pv_score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, DEPTH_ZERO, 1);
-        pos.undo_move(cur->move);
         push_back(rm);
+
+        pos.undo_move(cur->move);
     }
     sort();
   }
@@ -2719,29 +2705,11 @@ split_point_start: // At split points actual search starts from here
 
       while ((move = mp.get_next_move()) != MOVE_NONE)
           for (Base::iterator it = begin(); it != end(); ++it)
-              if (it->move == move)
+              if (it->pv[0] == move)
               {
                   it->non_pv_score = score--;
                   break;
               }
   }
 
-  // RootMoveList::sort_multipv() sorts the first few moves in the root move
-  // list by their scores and depths. It is used to order the different PVs
-  // correctly in MultiPV mode.
-
-  void RootMoveList::sort_multipv(int n) {
-
-    int i,j;
-
-    for (i = 1; i <= n; i++)
-    {
-        const RootMove& rm = this->at(i);
-        for (j = i; j > 0 && this->at(j - 1) < rm; j--)
-            (*this)[j] = this->at(j - 1);
-
-        (*this)[j] = rm;
-    }
-  }
-
 } // namespace