]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Unify root_search() step 2
[stockfish] / src / search.cpp
index 28443b70ee6ee964cad05dd0ef7249f8915dfa3b..bc0fff881ef7f003ead9e194d35ab91b887cc207 100644 (file)
 #include "evaluate.h"
 #include "history.h"
 #include "misc.h"
+#include "move.h"
 #include "movegen.h"
 #include "movepick.h"
 #include "lock.h"
-#include "san.h"
 #include "search.h"
 #include "timeman.h"
 #include "thread.h"
@@ -129,7 +129,7 @@ namespace {
 
     void extract_pv_from_tt(Position& pos);
     void insert_pv_in_tt(Position& pos);
-    std::string pv_info_to_uci(const Position& pos, Value alpha, Value beta, int pvLine = 0);
+    std::string pv_info_to_uci(Position& pos, Value alpha, Value beta, int pvLine = 0);
 
     int64_t nodes;
     Value pv_score;
@@ -248,6 +248,9 @@ namespace {
   // Book object
   Book OpeningBook;
 
+  // Pointer to root move list
+  RootMoveList* Rml;
+
   // Iteration counter
   int Iteration;
 
@@ -288,7 +291,7 @@ namespace {
   Move id_loop(Position& pos, Move searchMoves[], Move* ponderMove);
   Value root_search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth, RootMoveList& rml);
 
-  template <NodeType PvNode, bool SpNode>
+  template <NodeType PvNode, bool SpNode, bool Root>
   Value search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth, int ply);
 
   template <NodeType PvNode>
@@ -298,7 +301,7 @@ namespace {
   inline Value search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth, int ply) {
 
       return depth < ONE_PLY ? qsearch<PvNode>(pos, ss, alpha, beta, DEPTH_ZERO, ply)
-                             : search<PvNode, false>(pos, ss, alpha, beta, depth, ply);
+                             : search<PvNode, false, false>(pos, ss, alpha, beta, depth, ply);
   }
 
   template <NodeType PvNode>
@@ -423,7 +426,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   // Look for a book move, only during games, not tests
   if (UseTimeManagement && Options["OwnBook"].value<bool>())
   {
-      if (Options["Book File"].value<std::string>() != OpeningBook.file_name())
+      if (Options["Book File"].value<std::string>() != OpeningBook.name())
           OpeningBook.open(Options["Book File"].value<std::string>());
 
       Move bookMove = OpeningBook.get_move(pos, Options["Best Book Move"].value<bool>());
@@ -556,9 +559,11 @@ namespace {
     Depth depth;
     Move EasyMove = MOVE_NONE;
     Value value, alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
+    int researchCountFL, researchCountFH;
 
     // Moves to search are verified, scored and sorted
     RootMoveList rml(pos, searchMoves);
+    Rml = &rml;
 
     // Handle special case of searching on a mate/stale position
     if (rml.size() == 0)
@@ -612,8 +617,51 @@ namespace {
 
         depth = (Iteration - 2) * ONE_PLY + InitialDepth;
 
-        // Search to the current depth, rml is updated and sorted
-        value = root_search(pos, ss, alpha, beta, depth, rml);
+        researchCountFL = researchCountFH = 0;
+
+        // We start with small aspiration window and in case of fail high/low, we
+        // research with bigger window until we are not failing high/low anymore.
+        while (true)
+        {
+            // Sort the moves before to (re)search
+            rml.set_non_pv_scores(pos, rml[0].pv[0], ss);
+            rml.sort();
+
+            // Search to the current depth, rml is updated and sorted
+            //value = root_search(pos, ss, alpha, beta, depth, rml);
+            value = search<PV, false, true>(pos, ss, alpha, beta, depth, 0);
+
+            // 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 < Min(MultiPV, (int)rml.size()); i++)
+                rml[i].insert_pv_in_tt(pos);
+
+            if (StopRequest)
+                break;
+
+            assert(value >= alpha);
+
+            if (value >= beta)
+            {
+                // Prepare for a research after a fail high, each time with a wider window
+                beta = Min(beta + AspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
+                researchCountFH++;
+            }
+            else if (value <= alpha)
+            {
+                AspirationFailLow = true;
+                StopOnPonderhit = false;
+
+                // Prepare for a research after a fail low, each time with a wider window
+                alpha = Max(alpha - AspirationDelta * (1 << researchCountFL), -VALUE_INFINITE);
+                researchCountFL++;
+            }
+            else
+                break;
+        }
 
         if (StopRequest)
             break; // Value cannot be trusted. Break out immediately!
@@ -636,7 +684,7 @@ namespace {
                 stopSearch = true;
 
             // Stop search early when the last two iterations returned a mate score
-            if (  Iteration >= 6
+            if (   Iteration >= 6
                 && abs(ValueByIteration[Iteration]) >= abs(VALUE_MATE) - 100
                 && abs(ValueByIteration[Iteration-1]) >= abs(VALUE_MATE) - 100)
                 stopSearch = true;
@@ -685,27 +733,33 @@ namespace {
 
   Value root_search(Position& pos, SearchStack* ss, Value alpha,
                     Value beta, Depth depth, RootMoveList& rml) {
-    StateInfo st;
+
+    assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
+    assert(beta > alpha && beta <= VALUE_INFINITE);
+    assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads());
+
     Move movesSearched[MOVES_MAX];
-    CheckInfo ci(pos);
-    int64_t nodes;
+    StateInfo st;
+    Key posKey;
     Move move;
     Depth ext, newDepth;
-    Value value, oldAlpha;
-    RootMoveList::iterator rm;
+    ValueType vt;
+    Value bestValue, value, oldAlpha;
     bool isCheck, moveIsCheck, captureOrPromotion, dangerous, isPvMove;
-    int moveCount, researchCountFH, researchCountFL;
+    int moveCount = 0;
 
-    researchCountFH = researchCountFL = 0;
+    bestValue = value = -VALUE_INFINITE;
     oldAlpha = alpha;
     isCheck = pos.is_check();
 
     // Step 1. Initialize node (polling is omitted at root)
     ss->currentMove = ss->bestMove = MOVE_NONE;
+    (ss+2)->killers[0] = (ss+2)->killers[1] = (ss+2)->mateKiller = MOVE_NONE;
 
     // Step 2. Check for aborted search (omitted at root)
     // Step 3. Mate distance pruning (omitted at root)
     // Step 4. Transposition table lookup (omitted at root)
+    posKey = pos.get_key();
 
     // Step 5. Evaluate the position statically
     // At root we do this only to get reference value for child nodes
@@ -717,219 +771,181 @@ namespace {
     // Step 8. Null move search with verification search (omitted at root)
     // Step 9. Internal iterative deepening (omitted at root)
 
-    // Step extra. Fail low loop
-    // We start with small aspiration window and in case of fail low, we research
-    // with bigger window until we are not failing low anymore.
-    while (1)
+    CheckInfo ci(pos);
+    int64_t nodes;
+    RootMoveList::iterator rm = rml.begin();
+    bestValue = alpha;
+
+    // Step 10. Loop through moves
+    // Loop through all legal moves until no moves remain or a beta cutoff occurs
+    while (   bestValue < beta
+           && rm != rml.end()
+           && !StopRequest)
     {
-        // Sort the moves before to (re)search
-        rml.set_non_pv_scores(pos, rml[0].pv[0], ss);
-        rml.sort();
-        moveCount = 0;
+        move = ss->currentMove = rm->pv[0];
+        movesSearched[moveCount++] = move;
+        isPvMove = (moveCount <= MultiPV);
 
-        // Step 10. Loop through all moves in the root move list
-        for (rm = rml.begin(); rm != rml.end() && !StopRequest; ++rm)
-        {
-            // This is used by time management
-            FirstRootMove = (rm == rml.begin());
+        // This is used by time management
+        FirstRootMove = (rm == rml.begin());
 
-            // Save the current node count before the move is searched
-            nodes = pos.nodes_searched();
+        // 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 = rm->pv[0];
-            movesSearched[moveCount++] = move;
-            isPvMove = (moveCount <= MultiPV);
+        // 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;
+        }
 
-            if (current_search_time() >= 1000)
-                cout << "info currmove " << move
-                     << " currmovenumber " << moveCount << endl;
+        if (current_search_time() >= 1000)
+            cout << "info currmove " << move
+                 << " currmovenumber " << moveCount << endl;
 
-            moveIsCheck = pos.move_is_check(move);
-            captureOrPromotion = pos.move_is_capture_or_promotion(move);
+        moveIsCheck = pos.move_is_check(move);
+        captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
-            // Step 11. Decide the new search depth
-            ext = extension<PV>(pos, move, captureOrPromotion, moveIsCheck, false, false, &dangerous);
-            newDepth = depth + ext;
+        // Step 11. Decide the new search depth
+        ext = extension<PV>(pos, move, captureOrPromotion, moveIsCheck, false, false, &dangerous);
+        newDepth = depth + ext;
 
-            // Step 12. Futility pruning (omitted at root)
+        // Step 12. Futility pruning (omitted at root)
+        // Step 13. Make the move
+        pos.do_move(move, st, ci, moveIsCheck);
 
-            // Step extra. Fail high loop
-            // If move fails high, we research with bigger window until we are not failing
-            // high anymore.
-            value = -VALUE_INFINITE;
+        // Step extra. pv search
+        // We do pv search for PV moves
+        if (isPvMove)
+        {
+            // Aspiration window is disabled in multi-pv case
+            if (MultiPV > 1)
+                alpha = -VALUE_INFINITE;
 
-            while (1)
+            // Full depth PV search, done on first move or after a fail high
+            value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, 1);
+        }
+        else
+        {
+            // Step 14. Reduced search
+            // if the move fails high will be re-searched at full depth
+            bool doFullDepthSearch = true;
+
+            if (    depth >= 3 * ONE_PLY
+                && !captureOrPromotion
+                && !dangerous
+                && !move_is_castle(move)
+                &&  ss->killers[0] != move
+                &&  ss->killers[1] != move)
             {
-                // Step 13. Make the move
-                pos.do_move(move, st, ci, moveIsCheck);
+                ss->reduction = reduction<PV>(depth, moveCount - MultiPV + 1);
 
-                // Step extra. pv search
-                // We do pv search for PV moves and when failing high
-                if (isPvMove || value > alpha)
+                if (ss->reduction)
                 {
-                    // Aspiration window is disabled in multi-pv case
-                    if (MultiPV > 1)
-                        alpha = -VALUE_INFINITE;
+                    Depth d = newDepth - ss->reduction;
+                    value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, 1);
 
-                    // Full depth PV search, done on first move or after a fail high
-                    value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, 1);
+                    doFullDepthSearch = (value > alpha);
                 }
-                else
-                {
-                    // Step 14. Reduced search
-                    // if the move fails high will be re-searched at full depth
-                    bool doFullDepthSearch = true;
-
-                    if (    depth >= 3 * ONE_PLY
-                        && !dangerous
-                        && !captureOrPromotion
-                        && !move_is_castle(move))
-                    {
-                        ss->reduction = reduction<PV>(depth, moveCount - MultiPV + 1);
-                        if (ss->reduction)
-                        {
-                            assert(newDepth-ss->reduction >= ONE_PLY);
-
-                            // Reduced depth non-pv search using alpha as upperbound
-                            value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, 1);
-                            doFullDepthSearch = (value > alpha);
-                        }
-                        ss->reduction = DEPTH_ZERO; // Restore original reduction
-                    }
-
-                    // Step 15. Full depth search
-                    if (doFullDepthSearch)
-                    {
-                        // Full depth non-pv search using alpha as upperbound
-                        value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, 1);
-
-                        // If we are above alpha then research at same depth but as PV
-                        // to get a correct score or eventually a fail high above beta.
-                        if (value > alpha)
-                            value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, 1);
-                    }
-                }
-
-                // Step 16. Undo move
-                pos.undo_move(move);
-
-                // Can we exit fail high loop ?
-                if (StopRequest || value < beta)
-                    break;
-
-                // 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.
-                ss->bestMove = move;
-                rm->pv_score = value;
-                rm->extract_pv_from_tt(pos);
-
-                // Update killers and history only for non capture moves that fails high
-                if (!pos.move_is_capture_or_promotion(move))
-                {
-                    update_history(pos, move, depth, movesSearched, moveCount);
-                    update_killers(move, ss->killers);
-                }
-
-                // Inform GUI that PV has changed
-                cout << rm->pv_info_to_uci(pos, alpha, beta) << endl;
-
-                // Prepare for a research after a fail high, each time with a wider window
-                beta = Min(beta + AspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
-                researchCountFH++;
-
-            } // End of fail high loop
+                ss->reduction = DEPTH_ZERO; // Restore original reduction
+            }
 
-            // Finished searching the move. If AbortSearch is true, the search
-            // was aborted because the user interrupted the search or because we
-            // 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 (StopRequest)
-                break;
+            // Step 15. Full depth search
+            if (doFullDepthSearch)
+            {
+                // Full depth non-pv search using alpha as upperbound
+                value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, 1);
 
-            // Remember searched nodes counts for this move
-            rm->nodes += pos.nodes_searched() - nodes;
+                // If we are above alpha then research at same depth but as PV
+                // to get a correct score or eventually a fail high above beta.
+                if (value > alpha)
+                    value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, 1);
+            }
+        }
 
-            assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
-            assert(value < beta);
+        // Step 16. Undo move
+        pos.undo_move(move);
 
-            // Step 17. Check for new best move
-            if (!isPvMove && value <= alpha)
-                rm->pv_score = -VALUE_INFINITE;
-            else
-            {
-                // PV move or new best move!
+        assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
 
-                // Update PV
-                ss->bestMove = move;
-                rm->pv_score = value;
-                rm->extract_pv_from_tt(pos);
+        // Finished searching the move. If StopRequest is true, the search
+        // was aborted because the user interrupted the search or because we
+        // 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 (StopRequest)
+            break;
 
-                // We record how often the best move has been changed in each
-                // iteration. This information is used for time managment: When
-                // the best move changes frequently, we allocate some more time.
-                if (!isPvMove && MultiPV == 1)
-                    BestMoveChangesByIteration[Iteration]++;
+        // Remember searched nodes counts for this move
+        rm->nodes += pos.nodes_searched() - nodes;
 
-                // Inform GUI that PV has changed, in case of multi-pv UCI protocol
-                // requires we send all the PV lines properly sorted.
-                rml.sort_multipv(moveCount);
+        // Step 17. Check for new best move
+        if (!isPvMove && value <= alpha)
+            rm->pv_score = -VALUE_INFINITE;
+        else
+        {
+            // PV move or new best move!
 
-                for (int j = 0; j < Min(MultiPV, (int)rml.size()); j++)
-                    cout << rml[j].pv_info_to_uci(pos, alpha, beta, j) << endl;
+            // Update PV
+            ss->bestMove = move;
+            rm->pv_score = value;
+            rm->extract_pv_from_tt(pos);
 
-                // Update alpha. In multi-pv we don't use aspiration window
-                if (MultiPV == 1)
-                {
-                    // Raise alpha to setup proper non-pv search upper bound
-                    if (value > alpha)
-                        alpha = value;
-                }
-                else // Set alpha equal to minimum score among the PV lines
-                    alpha = rml[Min(moveCount, MultiPV) - 1].pv_score; // FIXME why moveCount?
+            // We record how often the best move has been changed in each
+            // iteration. This information is used for time managment: When
+            // the best move changes frequently, we allocate some more time.
+            if (!isPvMove && MultiPV == 1)
+                BestMoveChangesByIteration[Iteration]++;
 
-            } // PV move or new best move
+            // Inform GUI that PV has changed, in case of multi-pv UCI protocol
+            // requires we send all the PV lines properly sorted.
+            rml.sort_multipv(moveCount);
 
-            assert(alpha >= oldAlpha);
+            for (int j = 0; j < Min(MultiPV, (int)rml.size()); j++)
+                cout << rml[j].pv_info_to_uci(pos, alpha, beta, j) << endl;
 
-            AspirationFailLow = (alpha == oldAlpha);
+            // Update alpha. In multi-pv we don't use aspiration window
+            if (MultiPV == 1)
+            {
+                // Raise alpha to setup proper non-pv search upper bound
+                if (value > alpha)
+                    alpha = bestValue = value;
+            }
+            else // Set alpha equal to minimum score among the PV lines
+                alpha = bestValue = rml[Min(moveCount, MultiPV) - 1].pv_score; // FIXME why moveCount?
 
-            if (AspirationFailLow && StopOnPonderhit)
-                StopOnPonderhit = false;
+        } // PV move or new best move
 
-        } // Root moves loop
+        ++rm;
 
-        // Can we exit fail low loop ?
-        if (StopRequest || !AspirationFailLow)
-            break;
+    } // Root moves loop
 
-        // Prepare for a research after a fail low, each time with a wider window
-        oldAlpha = alpha = Max(alpha - AspirationDelta * (1 << researchCountFL), -VALUE_INFINITE);
-        researchCountFL++;
+    // Step 20. Update tables
+    // If the search is not aborted, update the transposition table,
+    // history counters, and killer moves.
+    if (!StopRequest)
+    {
+        move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove;
+        vt   = bestValue <= oldAlpha ? VALUE_TYPE_UPPER
+             : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT;
 
-    } // Fail low loop
+        TT.store(posKey, value_to_tt(bestValue, 0), vt, depth, move, ss->eval, ss->evalMargin);
 
-    // Sort the moves before to return
-    rml.sort();
+        // Update killers and history only for non capture moves that fails high
+        if (    bestValue >= beta
+            && !pos.move_is_capture_or_promotion(move))
+        {
+            update_history(pos, move, depth, movesSearched, moveCount);
+            update_killers(move, ss->killers);
+        }
+    }
 
-    // Write PV lines to transposition table, in case the relevant entries
-    // have been overwritten during the search.
-    for (int i = 0; i < Min(MultiPV, (int)rml.size()); i++)
-        rml[i].insert_pv_in_tt(pos);
+    assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
 
-    return alpha;
+    return bestValue;
   }
 
 
@@ -940,16 +956,18 @@ namespace {
   // all this work again. We also don't need to store anything to the hash table
   // here: This is taken care of after we return from the split point.
 
-  template <NodeType PvNode, bool SpNode>
+  template <NodeType PvNode, bool SpNode, bool Root>
   Value search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth, int ply) {
 
     assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
     assert(beta > alpha && beta <= VALUE_INFINITE);
     assert(PvNode || alpha == beta - 1);
-    assert(ply > 0 && ply < PLY_MAX);
+    assert((Root || ply > 0) && ply < PLY_MAX);
     assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads());
 
     Move movesSearched[MOVES_MAX];
+    int64_t nodes;
+    RootMoveList::iterator rm;
     StateInfo st;
     const TTEntry *tte;
     Key posKey;
@@ -958,11 +976,12 @@ namespace {
     ValueType vt;
     Value bestValue, value, oldAlpha;
     Value refinedValue, nullValue, futilityBase, futilityValueScaled; // Non-PV specific
-    bool isCheck, singleEvasion, singularExtensionNode, moveIsCheck, captureOrPromotion, dangerous;
+    bool isPvMove, isCheck, singleEvasion, singularExtensionNode, moveIsCheck, captureOrPromotion, dangerous;
     bool mateThreat = false;
     int moveCount = 0;
     int threadID = pos.thread();
     SplitPoint* sp = NULL;
+
     refinedValue = bestValue = value = -VALUE_INFINITE;
     oldAlpha = alpha;
     isCheck = pos.is_check();
@@ -982,24 +1001,27 @@ namespace {
     ss->currentMove = ss->bestMove = threatMove = MOVE_NONE;
     (ss+2)->killers[0] = (ss+2)->killers[1] = (ss+2)->mateKiller = MOVE_NONE;
 
-    if (threadID == 0 && ++NodesSincePoll > NodesBetweenPolls)
+    if (!Root)
     {
-        NodesSincePoll = 0;
-        poll(pos);
-    }
-
-    // Step 2. Check for aborted search and immediate draw
-    if (   StopRequest
-        || ThreadsMgr.cutoff_at_splitpoint(threadID)
-        || pos.is_draw()
-        || ply >= PLY_MAX - 1)
-        return VALUE_DRAW;
+        if (threadID == 0 && ++NodesSincePoll > NodesBetweenPolls)
+        {
+            NodesSincePoll = 0;
+            poll(pos);
+        }
 
-    // Step 3. Mate distance pruning
-    alpha = Max(value_mated_in(ply), alpha);
-    beta = Min(value_mate_in(ply+1), beta);
-    if (alpha >= beta)
-        return alpha;
+        // Step 2. Check for aborted search and immediate draw
+        if (   StopRequest
+            || ThreadsMgr.cutoff_at_splitpoint(threadID)
+            || pos.is_draw()
+            || ply >= PLY_MAX - 1)
+            return VALUE_DRAW;
+
+        // Step 3. Mate distance pruning
+        alpha = Max(value_mated_in(ply), alpha);
+        beta = Min(value_mate_in(ply+1), beta);
+        if (alpha >= beta)
+            return alpha;
+    }
 
     // Step 4. Transposition table lookup
 
@@ -1044,7 +1066,8 @@ namespace {
     }
 
     // Save gain for the parent non-capture move
-    update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
+    if (!Root)
+        update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
 
     // Step 6. Razoring (is omitted in PV nodes)
     if (   !PvNode
@@ -1137,7 +1160,8 @@ namespace {
     }
 
     // Step 9. Internal iterative deepening
-    if (    depth >= IIDDepth[PvNode]
+    if (   !Root
+        &&  depth >= IIDDepth[PvNode]
         &&  ttMove == MOVE_NONE
         && (PvNode || (!isCheck && ss->eval >= beta - IIDMargin)))
     {
@@ -1152,7 +1176,7 @@ namespace {
     }
 
     // Expensive mate threat detection (only for PV nodes)
-    if (PvNode)
+    if (PvNode && !Root) // FIXME
         mateThreat = pos.has_mate_threat();
 
 split_point_start: // At split points actual search starts from here
@@ -1165,13 +1189,20 @@ split_point_start: // At split points actual search starts from here
     ss->bestMove = MOVE_NONE;
     singleEvasion = !SpNode && isCheck && mp.number_of_evasions() == 1;
     futilityBase = ss->eval + ss->evalMargin;
-    singularExtensionNode =  !SpNode
+    singularExtensionNode =   !Root
+                           && !SpNode
                            && depth >= SingularExtensionDepth[PvNode]
                            && tte
                            && tte->move()
                            && !excludedMove // Do not allow recursive singular extension search
                            && (tte->type() & VALUE_TYPE_LOWER)
                            && tte->depth() >= depth - 3 * ONE_PLY;
+    if (Root)
+    {
+        rm = Rml->begin();
+        bestValue = alpha;
+    }
+
     if (SpNode)
     {
         lock_grab(&(sp->lock));
@@ -1181,9 +1212,35 @@ split_point_start: // At split points actual search starts from here
     // Step 10. Loop through moves
     // Loop through all legal moves until no moves remain or a beta cutoff occurs
     while (   bestValue < beta
-           && (move = mp.get_next_move()) != MOVE_NONE
+           && (!Root || rm != Rml->end())
+           && ( Root || (move = mp.get_next_move()) != MOVE_NONE)
            && !ThreadsMgr.cutoff_at_splitpoint(threadID))
     {
+      if (Root)
+      {
+          move = rm->pv[0];
+
+          // This is used by time management
+          FirstRootMove = (rm == Rml->begin());
+
+          // 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;
+          }
+
+          if (current_search_time() >= 1000)
+              cout << "info currmove " << move
+                   << " currmovenumber " << moveCount << endl;
+      }
+
       assert(move_is_ok(move));
 
       if (SpNode)
@@ -1196,6 +1253,7 @@ split_point_start: // At split points actual search starts from here
       else
           movesSearched[moveCount++] = move;
 
+      isPvMove = (PvNode && moveCount <= (Root ? MultiPV : 1));
       moveIsCheck = pos.move_is_check(move, ci);
       captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
@@ -1228,7 +1286,7 @@ split_point_start: // At split points actual search starts from here
 
       // Update current move (this must be done after singular extension search)
       ss->currentMove = move;
-      newDepth = depth - ONE_PLY + ext;
+      newDepth = depth - (!Root ? ONE_PLY : DEPTH_ZERO) + ext;
 
       // Step 12. Futility pruning (is omitted in PV nodes)
       if (   !PvNode
@@ -1287,8 +1345,14 @@ split_point_start: // At split points actual search starts from here
 
       // Step extra. pv search (only in PV nodes)
       // The first move in list is the expected PV
-      if (PvNode && moveCount == 1)
+      if (isPvMove)
+      {
+          // Aspiration window is disabled in multi-pv case
+          if (Root && MultiPV > 1)
+              alpha = -VALUE_INFINITE;
+
           value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, ply+1);
+      }
       else
       {
           // Step 14. Reduced depth search
@@ -1302,8 +1366,8 @@ split_point_start: // At split points actual search starts from here
               &&  ss->killers[0] != move
               &&  ss->killers[1] != move)
           {
-              ss->reduction = reduction<PvNode>(depth, moveCount);
-
+              ss->reduction = Root ? reduction<PvNode>(depth, moveCount - MultiPV + 1)
+                                   : reduction<PvNode>(depth, moveCount);
               if (ss->reduction)
               {
                   alpha = SpNode ? sp->alpha : alpha;
@@ -1324,7 +1388,7 @@ split_point_start: // At split points actual search starts from here
               // Step extra. pv search (only in PV nodes)
               // Search only for possible new PV nodes, if instead value >= beta then
               // parent node fails low with value <= alpha and tries another move.
-              if (PvNode && value > alpha && value < beta)
+              if (PvNode && value > alpha && (Root || value < beta))
                   value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, ply+1);
           }
       }
@@ -1342,7 +1406,7 @@ split_point_start: // At split points actual search starts from here
           alpha = sp->alpha;
       }
 
-      if (value > bestValue && !(SpNode && ThreadsMgr.cutoff_at_splitpoint(threadID)))
+      if (!Root && value > bestValue && !(SpNode && ThreadsMgr.cutoff_at_splitpoint(threadID)))
       {
           bestValue = value;
 
@@ -1371,8 +1435,62 @@ split_point_start: // At split points actual search starts from here
           }
       }
 
+      if (Root)
+      {
+          // Finished searching the move. If StopRequest is true, the search
+          // was aborted because the user interrupted the search or because we
+          // 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 (StopRequest)
+              break;
+
+          // Remember searched nodes counts for this move
+          rm->nodes += pos.nodes_searched() - nodes;
+
+          // Step 17. Check for new best move
+          if (!isPvMove && value <= alpha)
+              rm->pv_score = -VALUE_INFINITE;
+          else
+          {
+              // PV move or new best move!
+
+              // Update PV
+              ss->bestMove = move;
+              rm->pv_score = value;
+              rm->extract_pv_from_tt(pos);
+
+              // We record how often the best move has been changed in each
+              // iteration. This information is used for time managment: When
+              // the best move changes frequently, we allocate some more time.
+              if (!isPvMove && MultiPV == 1)
+                  BestMoveChangesByIteration[Iteration]++;
+
+              // Inform GUI that PV has changed, in case of multi-pv UCI protocol
+              // requires we send all the PV lines properly sorted.
+              Rml->sort_multipv(moveCount);
+
+              for (int j = 0; j < Min(MultiPV, (int)Rml->size()); j++)
+                  cout << (*Rml)[j].pv_info_to_uci(pos, alpha, beta, j) << endl;
+
+              // Update alpha. In multi-pv we don't use aspiration window
+              if (MultiPV == 1)
+              {
+                  // Raise alpha to setup proper non-pv search upper bound
+                  if (value > alpha)
+                      alpha = bestValue = value;
+              }
+              else // Set alpha equal to minimum score among the PV lines
+                  alpha = bestValue = (*Rml)[Min(moveCount, MultiPV) - 1].pv_score; // FIXME why moveCount?
+
+          } // PV move or new best move
+
+          ++rm;
+      }
+
       // Step 18. Check for split
-      if (   !SpNode
+      if (   !Root
+          && !SpNode
           && depth >= ThreadsMgr.min_split_depth()
           && ThreadsMgr.active_threads() > 1
           && bestValue < beta
@@ -1908,8 +2026,9 @@ split_point_start: // At split points actual search starts from here
   void update_history(const Position& pos, Move move, Depth depth,
                       Move movesSearched[], int moveCount) {
     Move m;
+    Value bonus = Value(int(depth) * int(depth));
 
-    H.success(pos.piece_on(move_from(move)), move_to(move), depth);
+    H.update(pos.piece_on(move_from(move)), move_to(move), bonus);
 
     for (int i = 0; i < moveCount - 1; i++)
     {
@@ -1918,7 +2037,7 @@ split_point_start: // At split points actual search starts from here
         assert(m != move);
 
         if (!pos.move_is_capture_or_promotion(m))
-            H.failure(pos.piece_on(move_from(m)), move_to(m), depth);
+            H.update(pos.piece_on(move_from(m)), move_to(m), -bonus);
     }
   }
 
@@ -1946,7 +2065,7 @@ split_point_start: // At split points actual search starts from here
         && after != VALUE_NONE
         && pos.captured_piece_type() == PIECE_TYPE_NONE
         && !move_is_special(m))
-        H.set_gain(pos.piece_on(move_to(m)), move_to(m), -(before + after));
+        H.update_gain(pos.piece_on(move_to(m)), move_to(m), -(before + after));
   }
 
 
@@ -2226,9 +2345,9 @@ split_point_start: // At split points actual search starts from here
             ss->sp = tsp;
 
             if (tsp->pvNode)
-                search<PV, true>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
+                search<PV, true, false>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
             else
-                search<NonPV, true>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
+                search<NonPV, true, false>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
 
             assert(threads[threadID].state == THREAD_SEARCHING);
 
@@ -2633,7 +2752,7 @@ split_point_start: // At split points actual search starts from here
   // formatted according to UCI specification and eventually writes the info
   // to a log file. It is called at each iteration or after a new pv is found.
 
-  std::string RootMove::pv_info_to_uci(const Position& pos, Value alpha, Value beta, int pvLine) {
+  std::string RootMove::pv_info_to_uci(Position& pos, Value alpha, Value beta, int pvLine) {
 
     std::stringstream s, l;
     Move* m = pv;