]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Assorted code style and comments in search.cpp
[stockfish] / src / search.cpp
index eb19b51d9c994c5d11e8c9c7abfb195806e475af..27a67a639401e3d18e48a3416a1ea4592ce1f072 100644 (file)
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-
-////
-//// Includes
-////
-
 #include <cassert>
 #include <cmath>
 #include <cstring>
 using std::cout;
 using std::endl;
 
-////
-//// Local definitions
-////
-
 namespace {
 
-  // Types
+  // Different node types, used as template parameter
   enum NodeType { NonPV, PV };
 
-  // Set to true to force running with one thread.
-  // Used for debugging SMP code.
+  // Set to true to force running with one thread. Used for debugging.
   const bool FakeSplit = false;
 
-  // Fast lookup table of sliding pieces indexed by Piece
+  // Lookup table to check if a Piece is a slider and its access function
   const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 };
   inline bool piece_is_slider(Piece p) { return Slidings[p]; }
 
-  // ThreadsManager class is used to handle all the threads related stuff in search,
-  // init, starting, parking and, the most important, launching a slave thread at a
-  // split point are what this class does. All the access to shared thread data is
-  // done through this class, so that we avoid using global variables instead.
+  // ThreadsManager class is used to handle all the threads related stuff like init,
+  // starting, parking and, the most important, launching a slave thread at a split
+  // point. All the access to shared thread data is done through this class.
 
   class ThreadsManager {
     /* As long as the single ThreadsManager object is defined as a global we don't
@@ -105,7 +94,7 @@ namespace {
   };
 
 
-  // RootMove struct is used for moves at the root at the tree. For each root
+  // RootMove struct is used for moves at the root of 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 pv_score is normally set at
   // -VALUE_INFINITE for all non-pv moves, while non_pv_score is computed
@@ -120,8 +109,8 @@ namespace {
     // 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 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.
+    // 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;
@@ -129,7 +118,7 @@ namespace {
 
     void extract_pv_from_tt(Position& pos);
     void insert_pv_in_tt(Position& pos);
-    std::string pv_info_to_uci(Position& pos, Depth depth, Value alpha, Value beta, int pvLine = 0);
+    std::string pv_info_to_uci(Position& pos, int depth, Value alpha, Value beta, int pvIdx);
 
     int64_t nodes;
     Value pv_score;
@@ -138,7 +127,7 @@ namespace {
   };
 
 
-  // RootMoveList struct is essentially a std::vector<> of RootMove objects,
+  // RootMoveList struct is just a std::vector<> of RootMove objects,
   // with an handful of methods above the standard ones.
 
   struct RootMoveList : public std::vector<RootMove> {
@@ -153,12 +142,21 @@ namespace {
   };
 
 
+  // Overload operator<<() to make it easier to print moves in a coordinate
+  // notation compatible with UCI protocol.
+  std::ostream& operator<<(std::ostream& os, Move m) {
+
+    bool chess960 = (os.iword(0) != 0); // See set960()
+    return os << move_to_uci(m, chess960);
+  }
+
+
   // When formatting a move for std::cout we must know if we are in Chess960
   // or not. To keep using the handy operator<<() on the move the trick is to
   // embed this flag in the stream itself. Function-like named enum set960 is
   // used as a custom manipulator and the stream internal general-purpose array,
   // accessed through ios_base::iword(), is used to pass the flag to the move's
-  // operator<<() that will use it to properly format castling moves.
+  // operator<<() that will read it to properly format castling moves.
   enum set960 {};
 
   std::ostream& operator<< (std::ostream& os, const set960& f) {
@@ -168,15 +166,6 @@ namespace {
   }
 
 
-  // Overload operator << for moves to make it easier to print moves in
-  // coordinate notation compatible with UCI protocol.
-  std::ostream& operator<<(std::ostream& os, Move m) {
-
-    bool chess960 = (os.iword(0) != 0); // See set960()
-    return os << move_to_uci(m, chess960);
-  }
-
-
   /// Adjustments
 
   // Step 6. Razoring
@@ -209,16 +198,12 @@ 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 then the
-  // remaining ones we will extend it.
-  const Value SingularExtensionMargin = Value(0x20);
-
   // Step 12. Futility pruning
 
   // Futility margin for quiescence search
   const Value FutilityMarginQS = Value(0x80);
 
-  // Futility lookup tables (initialized at startup) and their getter functions
+  // Futility lookup tables (initialized at startup) and their access functions
   Value FutilityMarginsMatrix[16][64]; // [depth][moveNumber]
   int FutilityMoveCountArray[32]; // [depth]
 
@@ -240,16 +225,16 @@ namespace {
 
   /// Namespace variables
 
-  // Book object
+  // Book
   Book OpeningBook;
 
   // Root move list
   RootMoveList Rml;
 
   // MultiPV mode
-  int MultiPV;
+  int MultiPV, UCIMultiPV;
 
-  // Time managment variables
+  // Time management variables
   int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime;
   bool UseTimeManagement, InfiniteSearch, Pondering, StopOnPonderhit;
   bool FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
@@ -259,7 +244,12 @@ namespace {
   bool UseLogFile;
   std::ofstream LogFile;
 
-  // Multi-threads manager object
+  // Skill level adjustment
+  int SkillLevel;
+  bool SkillLevelEnabled;
+  RKISS RK;
+
+  // Multi-threads manager
   ThreadsManager ThreadsMgr;
 
   // Node counters, used only by thread[0] but try to keep in different cache
@@ -271,6 +261,7 @@ namespace {
   // History table
   History H;
 
+
   /// Local functions
 
   Move id_loop(Position& pos, Move searchMoves[], Move* ponderMove);
@@ -284,8 +275,8 @@ namespace {
   template <NodeType PvNode>
   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, false>(pos, ss, alpha, beta, depth, ply);
+    return depth < ONE_PLY ? qsearch<PvNode>(pos, ss, alpha, beta, DEPTH_ZERO, ply)
+                           : search<PvNode, false, false>(pos, ss, alpha, beta, depth, ply);
   }
 
   template <NodeType PvNode>
@@ -293,20 +284,18 @@ namespace {
 
   bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta, Value *bValue);
   bool connected_moves(const Position& pos, Move m1, Move m2);
-  bool value_is_mate(Value value);
   Value value_to_tt(Value v, int ply);
   Value value_from_tt(Value v, int ply);
   bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
   bool connected_threat(const Position& pos, Move m, Move threat);
   Value refine_eval(const TTEntry* tte, Value defaultEval, int ply);
   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);
+  void do_skill_level(Move* best, Move* ponder);
 
   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();
 
@@ -321,7 +310,7 @@ namespace {
   // the proper move source according to the type of node.
   template<bool SpNode, bool Root> struct MovePickerExt;
 
-  // In Root nodes use RootMoveList Rml as source. Score and sort the root moves
+  // In Root nodes use RootMoveList as source. Score and sort the root moves
   // before to search them.
   template<> struct MovePickerExt<false, true> : public MovePicker {
 
@@ -330,10 +319,10 @@ namespace {
       Move move;
       Value score = VALUE_ZERO;
 
-      // Score root moves using the standard way used in main search, the moves
-      // are scored according to the order in which are returned by MovePicker.
+      // Score root moves using standard ordering used in main search, the moves
+      // are scored according to the order in which they are returned by MovePicker.
       // This is the second order score that is used to compare the moves when
-      // the first order pv scores of both moves are equal.
+      // the first orders pv_score of both moves are equal.
       while ((move = MovePicker::get_next_move()) != MOVE_NONE)
           for (rm = Rml.begin(); rm != Rml.end(); ++rm)
               if (rm->pv[0] == move)
@@ -363,9 +352,8 @@ namespace {
   // In SpNodes use split point's shared MovePicker object as move source
   template<> struct MovePickerExt<true, false> : public MovePicker {
 
-    MovePickerExt(const Position& p, Move ttm, Depth d, const History& h,
-                  SearchStack* ss, Value b) : MovePicker(p, ttm, d, h, ss, b),
-                  mp(ss->sp->mp) {}
+    MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss, Value b)
+                  : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {}
 
     Move get_next_move() { return mp->get_next_move(); }
 
@@ -376,8 +364,8 @@ namespace {
   // Default case, create and use a MovePicker object as source
   template<> struct MovePickerExt<false, false> : public MovePicker {
 
-    MovePickerExt(const Position& p, Move ttm, Depth d, const History& h,
-                  SearchStack* ss, Value b) : MovePicker(p, ttm, d, h, ss, b) {}
+    MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss, Value b)
+                  : MovePicker(p, ttm, d, h, ss, b) {}
 
     RootMoveList::iterator rm; // Dummy, needed to compile
   };
@@ -385,20 +373,10 @@ namespace {
 } // namespace
 
 
-////
-//// Functions
-////
-
-/// init_threads(), exit_threads() and nodes_searched() are helpers to
-/// give accessibility to some TM methods from outside of current file.
+/// init_threads() is called during startup. It initializes various lookup tables
+/// and creates and launches search threads.
 
-void init_threads() { ThreadsMgr.init_threads(); }
-void exit_threads() { ThreadsMgr.exit_threads(); }
-
-
-/// init_search() is called during startup. It initializes various lookup tables
-
-void init_search() {
+void init_threads() {
 
   int d;  // depth (ONE_PLY == 2)
   int hd; // half depth (ONE_PLY == 1)
@@ -420,49 +398,56 @@ void init_search() {
   // Init futility move count array
   for (d = 0; d < 32; d++)
       FutilityMoveCountArray[d] = int(3.001 + 0.25 * pow(d, 2.0));
+
+  // Create and startup threads
+  ThreadsMgr.init_threads();
 }
 
 
-/// perft() is our utility to verify move generation is bug free. All the legal
-/// moves up to given depth are generated and counted and the sum returned.
+/// exit_threads() is a trampoline to access ThreadsMgr from outside of current file
+void exit_threads() { ThreadsMgr.exit_threads(); }
 
-int64_t perft(Position& pos, Depth depth)
-{
-    MoveStack mlist[MOVES_MAX];
-    StateInfo st;
-    Move m;
-    int64_t sum = 0;
 
-    // Generate all legal moves
-    MoveStack* last = generate<MV_LEGAL>(pos, mlist);
+/// perft() is our utility to verify move generation. All the legal moves up to
+/// given depth are generated and counted and the sum returned.
 
-    // If we are at the last ply we don't need to do and undo
-    // the moves, just to count them.
-    if (depth <= ONE_PLY)
-        return int(last - mlist);
+int64_t perft(Position& pos, Depth depth) {
 
-    // Loop through all legal moves
-    CheckInfo ci(pos);
-    for (MoveStack* cur = mlist; cur != last; cur++)
-    {
-        m = cur->move;
-        pos.do_move(m, st, ci, pos.move_is_check(m, ci));
-        sum += perft(pos, depth - ONE_PLY);
-        pos.undo_move(m);
-    }
-    return sum;
+  MoveStack mlist[MOVES_MAX];
+  StateInfo st;
+  Move m;
+  int64_t sum = 0;
+
+  // Generate all legal moves
+  MoveStack* last = generate<MV_LEGAL>(pos, mlist);
+
+  // If we are at the last ply we don't need to do and undo
+  // the moves, just to count them.
+  if (depth <= ONE_PLY)
+      return int(last - mlist);
+
+  // Loop through all legal moves
+  CheckInfo ci(pos);
+  for (MoveStack* cur = mlist; cur != last; cur++)
+  {
+      m = cur->move;
+      pos.do_move(m, st, ci, pos.move_is_check(m, ci));
+      sum += perft(pos, depth - ONE_PLY);
+      pos.undo_move(m);
+  }
+  return sum;
 }
 
 
 /// think() is the external interface to Stockfish's search, and is called when
-/// the program receives the UCI 'go' command. It initializes various
-/// search-related global variables, and calls id_loop(). It returns false
-/// when a quit command is received during the search.
+/// the program receives the UCI 'go' command. It initializes various global
+/// variables, and calls id_loop(). It returns false when a quit command is
+/// received during the search.
 
 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
+  // Initialize global search-related variables
   StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = SendSearchedNodes = false;
   NodesSincePoll = 0;
   SearchStartTime = get_system_time();
@@ -490,14 +475,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
       }
   }
 
-  // Read UCI option values
-  TT.set_size(Options["Hash"].value<int>());
-  if (Options["Clear Hash"].value<bool>())
-  {
-      Options["Clear Hash"].set_value("false");
-      TT.clear();
-  }
-
+  // Read UCI options
   CheckExtension[1]         = Options["Check Extension (PV nodes)"].value<Depth>();
   CheckExtension[0]         = Options["Check Extension (non-PV nodes)"].value<Depth>();
   PawnPushTo7thExtension[1] = Options["Pawn Push to 7th Extension (PV nodes)"].value<Depth>();
@@ -508,16 +486,29 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   PawnEndgameExtension[0]   = Options["Pawn Endgame Extension (non-PV nodes)"].value<Depth>();
   MateThreatExtension[1]    = Options["Mate Threat Extension (PV nodes)"].value<Depth>();
   MateThreatExtension[0]    = Options["Mate Threat Extension (non-PV nodes)"].value<Depth>();
-  MultiPV                   = Options["MultiPV"].value<int>();
+  UCIMultiPV                = Options["MultiPV"].value<int>();
+  SkillLevel                = Options["Skill level"].value<int>();
   UseLogFile                = Options["Use Search Log"].value<bool>();
 
   read_evaluation_uci_options(pos.side_to_move());
 
+  if (Options["Clear Hash"].value<bool>())
+  {
+      Options["Clear Hash"].set_value("false");
+      TT.clear();
+  }
+  TT.set_size(Options["Hash"].value<int>());
+
+  // Do we have to play with skill handicap? In this case enable MultiPV that
+  // we will use behind the scenes to retrieve a set of possible moves.
+  SkillLevelEnabled = (SkillLevel < 20);
+  MultiPV = (SkillLevelEnabled ? Max(UCIMultiPV, 4) : UCIMultiPV);
+
   // Set the number of active threads
   ThreadsMgr.read_uci_options();
   init_eval(ThreadsMgr.active_threads());
 
-  // Wake up needed threads
+  // Wake up needed threads. Main thread, with threadID == 0, is always active
   for (int i = 1; i < ThreadsMgr.active_threads(); i++)
       ThreadsMgr.wake_sleeping_thread(i);
 
@@ -527,8 +518,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   if (UseTimeManagement)
       TimeMgr.init(myTime, myIncrement, movesToGo, pos.startpos_ply_counter());
 
-  // Set best NodesBetweenPolls interval to avoid lagging under
-  // heavy time pressure.
+  // Set best NodesBetweenPolls interval to avoid lagging under time pressure
   if (MaxNodes)
       NodesBetweenPolls = Min(MaxNodes, 30000);
   else if (myTime && myTime < 1000)
@@ -544,12 +534,13 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
       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;
+      LogFile << "\nSearching: "  << 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
@@ -557,25 +548,20 @@ 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)
   {
-      LogFile << "\nNodes: " << pos.nodes_searched()
-              << "\nNodes/second: " << nps(pos)
-              << "\nBest move: " << move_to_san(pos, bestMove);
+      int t = current_search_time();
+
+      LogFile << "Nodes: "          << pos.nodes_searched()
+              << "\nNodes/second: " << (t > 0 ? int(pos.nodes_searched() * 1000 / t) : 0)
+              << "\nBest move: "    << move_to_san(pos, bestMove);
 
       StateInfo st;
       pos.do_move(bestMove, st);
-      LogFile << "\nPonder move: "
-              << move_to_san(pos, ponderMove) // Works also with MOVE_NONE
-              << endl;
-
-      // Return from think() with unchanged position
-      pos.undo_move(bestMove);
-
+      LogFile << "\nPonder move: " << move_to_san(pos, ponderMove) << endl;
+      pos.undo_move(bestMove); // Return from think() with unchanged position
       LogFile.close();
   }
 
@@ -587,8 +573,15 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   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;
+  // Could be MOVE_NONE when searching on a stalemate position
+  cout << "bestmove " << bestMove;
+
+  // UCI protol is not clear on allowing sending an empty ponder move, instead
+  // it is clear that ponder move is optional. So skip it if empty.
+  if (ponderMove != MOVE_NONE)
+      cout << " ponder " << ponderMove;
+
+  cout << endl;
 
   return !QuitRequest;
 }
@@ -605,73 +598,63 @@ namespace {
     SearchStack ss[PLY_MAX_PLUS_2];
     Value bestValues[PLY_MAX_PLUS_2];
     int bestMoveChanges[PLY_MAX_PLUS_2];
-    int iteration, researchCountFL, researchCountFH, aspirationDelta;
+    int depth, aspirationDelta, skillSamplingDepth;
     Value value, alpha, beta;
-    Depth depth;
-    Move bestMove, easyMove;
+    Move bestMove, easyMove, skillBest, skillPonder;
 
-    // Moves to search are verified, scored and sorted
-    Rml.init(pos, searchMoves);
-
-    // Initialize FIXME move before Rml.init()
+    // Initialize stuff before a new search
+    memset(ss, 0, 4 * sizeof(SearchStack));
     TT.new_search();
     H.clear();
-    memset(ss, 0, PLY_MAX_PLUS_2 * sizeof(SearchStack));
+    *ponderMove = bestMove = easyMove = skillBest = skillPonder = MOVE_NONE;
+    depth = aspirationDelta = skillSamplingDepth = 0;
     alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
-    *ponderMove = bestMove = easyMove = MOVE_NONE;
-    aspirationDelta = 0;
-    iteration = 1;
     ss->currentMove = MOVE_NULL; // Hack to skip update_gains()
 
-    // Handle special case of searching on a mate/stale position
+    // Moves to search are verified and copied
+    Rml.init(pos, searchMoves);
+
+    // Handle special case of searching on a mate/stalemate position
     if (Rml.size() == 0)
     {
-        cout << "info depth " << iteration << " score "
+        cout << "info depth 0 score "
              << value_to_uci(pos.is_check() ? -VALUE_MATE : VALUE_DRAW)
              << endl;
 
         return MOVE_NONE;
     }
 
-    // Send initial scoring (iteration 1)
-    cout << set960(pos.is_chess960()) // Is enough to set once at the beginning
-         << "info depth " << iteration
-         << "\n" << Rml[0].pv_info_to_uci(pos, ONE_PLY, alpha, beta) << endl;
-
-    // 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];
+    // Choose a random sampling depth according to SkillLevel so that at low
+    // skills there is an higher risk to pick up a blunder.
+    if (SkillLevelEnabled)
+        skillSamplingDepth = 4 + SkillLevel + (RK.rand<unsigned>() % 4);
 
     // Iterative deepening loop
-    while (++iteration <= PLY_MAX && (!MaxDepth || iteration <= MaxDepth) && !StopRequest)
+    while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest)
     {
-        cout << "info depth " << iteration << endl;
-
-        Rml.bestMoveChanges = researchCountFL = researchCountFH = 0;
-        depth = (iteration - 1) * ONE_PLY;
+        Rml.bestMoveChanges = 0;
+        cout << set960(pos.is_chess960()) << "info depth " << depth << endl;
 
         // Calculate dynamic aspiration window based on previous iterations
-        if (MultiPV == 1 && iteration >= 6 && abs(bestValues[iteration - 1]) < VALUE_KNOWN_WIN)
+        if (MultiPV == 1 && depth >= 5 && abs(bestValues[depth - 1]) < VALUE_KNOWN_WIN)
         {
-            int prevDelta1 = bestValues[iteration - 1] - bestValues[iteration - 2];
-            int prevDelta2 = bestValues[iteration - 2] - bestValues[iteration - 3];
+            int prevDelta1 = bestValues[depth - 1] - bestValues[depth - 2];
+            int prevDelta2 = bestValues[depth - 2] - bestValues[depth - 3];
 
-            aspirationDelta = Max(abs(prevDelta1) + abs(prevDelta2) / 2, 16);
+            aspirationDelta = Min(Max(abs(prevDelta1) + abs(prevDelta2) / 2, 16), 24);
             aspirationDelta = (aspirationDelta + 7) / 8 * 8; // Round to match grainSize
 
-            alpha = Max(bestValues[iteration - 1] - aspirationDelta, -VALUE_INFINITE);
-            beta  = Min(bestValues[iteration - 1] + aspirationDelta,  VALUE_INFINITE);
+            alpha = Max(bestValues[depth - 1] - aspirationDelta, -VALUE_INFINITE);
+            beta  = Min(bestValues[depth - 1] + aspirationDelta,  VALUE_INFINITE);
         }
 
         // Start with a small aspiration window and, in case of fail high/low,
         // research with bigger window until not failing high/low anymore.
-        while (true)
-        {
+        do {
             // Search starting from ss+1 to allow calling update_gains()
-            value = search<PV, false, true>(pos, ss+1, alpha, beta, depth, 0);
+            value = search<PV, false, true>(pos, ss+1, alpha, beta, depth * ONE_PLY, 0);
 
-            // Write PV lines to transposition table, in case the relevant entries
+            // Write PV back 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);
@@ -686,28 +669,43 @@ namespace {
             // otherwise exit the fail high/low loop.
             if (value >= beta)
             {
-                beta = Min(beta + aspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
-                researchCountFH++;
+                beta = Min(beta + aspirationDelta, VALUE_INFINITE);
+                aspirationDelta += aspirationDelta / 2;
             }
             else if (value <= alpha)
             {
                 AspirationFailLow = true;
                 StopOnPonderhit = false;
 
-                alpha = Max(alpha - aspirationDelta * (1 << researchCountFL), -VALUE_INFINITE);
-                researchCountFL++;
+                alpha = Max(alpha - aspirationDelta, -VALUE_INFINITE);
+                aspirationDelta += aspirationDelta / 2;
             }
             else
                 break;
-        }
+
+        } while (abs(value) < VALUE_KNOWN_WIN);
 
         // Collect info about search result
         bestMove = Rml[0].pv[0];
-        bestValues[iteration] = value;
-        bestMoveChanges[iteration] = Rml.bestMoveChanges;
+        *ponderMove = Rml[0].pv[1];
+        bestValues[depth] = value;
+        bestMoveChanges[depth] = Rml.bestMoveChanges;
+
+        // Do we need to pick now the best and the ponder moves ?
+        if (SkillLevelEnabled && depth == skillSamplingDepth)
+            do_skill_level(&skillBest, &skillPonder);
 
-        // Drop the easy move if differs from the new best move
-        if (bestMove != easyMove)
+        // Send PV line to GUI and to log file
+        for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++)
+            cout << Rml[i].pv_info_to_uci(pos, depth, alpha, beta, i) << endl;
+
+        if (UseLogFile)
+            LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl;
+
+        // 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)
@@ -716,15 +714,15 @@ namespace {
             bool noMoreTime = false;
 
             // Stop search early when the last two iterations returned a mate score
-            if (   iteration >= 6
-                && abs(bestValues[iteration])   >= abs(VALUE_MATE) - 100
-                && abs(bestValues[iteration-1]) >= abs(VALUE_MATE) - 100)
+            if (   depth >= 5
+                && abs(bestValues[depth])     >= abs(VALUE_MATE) - 100
+                && abs(bestValues[depth - 1]) >= abs(VALUE_MATE) - 100)
                 noMoreTime = true;
 
             // Stop search early if one move seems to be much better than the
             // others or if there is only a single legal move. In this latter
             // case we search up to Iteration 8 anyway to get a proper score.
-            if (   iteration >= 8
+            if (   depth >= 7
                 && easyMove == bestMove
                 && (   Rml.size() == 1
                     ||(   Rml[0].nodes > (pos.nodes_searched() * 85) / 100
@@ -734,8 +732,8 @@ namespace {
                 noMoreTime = true;
 
             // Add some extra time if the best move has changed during the last two iterations
-            if (iteration > 5 && iteration <= 50)
-                TimeMgr.pv_instability(bestMoveChanges[iteration], bestMoveChanges[iteration-1]);
+            if (depth > 4 && depth < 50)
+                TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth-1]);
 
             // Stop search if most of MaxSearchTime is consumed at the end of the
             // iteration. We probably don't have enough time to search the first
@@ -753,7 +751,16 @@ namespace {
         }
     }
 
-    *ponderMove = Rml[0].pv[1];
+    // When using skills fake best and ponder moves with the sub-optimal ones
+    if (SkillLevelEnabled)
+    {
+        if (skillBest == MOVE_NONE) // Still unassigned ?
+            do_skill_level(&skillBest, &skillPonder);
+
+        bestMove = skillBest;
+        *ponderMove = skillPonder;
+    }
+
     return bestMove;
   }
 
@@ -784,7 +791,7 @@ namespace {
     ValueType vt;
     Value bestValue, value, oldAlpha;
     Value refinedValue, nullValue, futilityBase, futilityValueScaled; // Non-PV specific
-    bool isPvMove, isCheck, singularExtensionNode, moveIsCheck, captureOrPromotion, dangerous;
+    bool isPvMove, isCheck, singularExtensionNode, moveIsCheck, captureOrPromotion, dangerous, isBadCap;
     bool mateThreat = false;
     int moveCount = 0, playedMoveCount = 0;
     int threadID = pos.thread();
@@ -807,7 +814,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)
@@ -831,29 +839,27 @@ namespace {
 
     // Step 4. Transposition table lookup
     // We don't want the score of a partial search to overwrite a previous full search
-    // TT value, so we use a different position key in case of an excluded move exists.
+    // TT value, so we use a different position key in case of an excluded move.
     excludedMove = ss->excludedMove;
     posKey = excludedMove ? pos.get_exclusion_key() : pos.get_key();
 
     tte = TT.retrieve(posKey);
     ttMove = tte ? tte->move() : MOVE_NONE;
 
-    // At PV nodes, we don't use the TT for pruning, but only for move ordering.
-    // This is to avoid problems in the following areas:
-    //
-    // * Repetition draw detection
-    // * Fifty move rule detection
-    // * Searching for a mate
-    // * Printing of full PV line
-    if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
+    // At PV nodes we check for exact scores, while at non-PV nodes we check for
+    // a fail high/low. Biggest advantage at probing at PV nodes is to have a
+    // smooth experience in analysis mode.
+    if (   !Root
+        && tte
+        && (PvNode ? tte->depth() >= depth && tte->type() == VALUE_TYPE_EXACT
+                   : ok_to_use_TT(tte, depth, beta, ply)))
     {
         TT.refresh(tte);
         ss->bestMove = ttMove; // Can be MOVE_NONE
         return value_from_tt(tte->value(), ply);
     }
 
-    // Step 5. Evaluate the position statically and
-    // update gain statistics of parent move.
+    // Step 5. Evaluate the position statically and update parent's gain statistics
     if (isCheck)
         ss->eval = ss->evalMargin = VALUE_NONE;
     else if (tte)
@@ -877,9 +883,9 @@ namespace {
     if (   !PvNode
         &&  depth < RazorDepth
         && !isCheck
-        &&  refinedValue < beta - razor_margin(depth)
+        &&  refinedValue + razor_margin(depth) < beta
         &&  ttMove == MOVE_NONE
-        && !value_is_mate(beta)
+        &&  abs(beta) < VALUE_MATE_IN_PLY_MAX
         && !pos.has_pawn_on_7th(pos.side_to_move()))
     {
         Value rbeta = beta - razor_margin(depth);
@@ -897,8 +903,8 @@ namespace {
         && !ss->skipNullMove
         &&  depth < RazorDepth
         && !isCheck
-        &&  refinedValue >= beta + futility_margin(depth, 0)
-        && !value_is_mate(beta)
+        &&  refinedValue - futility_margin(depth, 0) >= beta
+        &&  abs(beta) < VALUE_MATE_IN_PLY_MAX
         &&  pos.non_pawn_material(pos.side_to_move()))
         return refinedValue - futility_margin(depth, 0);
 
@@ -908,7 +914,7 @@ namespace {
         &&  depth > ONE_PLY
         && !isCheck
         &&  refinedValue >= beta
-        && !value_is_mate(beta)
+        &&  abs(beta) < VALUE_MATE_IN_PLY_MAX
         &&  pos.non_pawn_material(pos.side_to_move()))
     {
         ss->currentMove = MOVE_NULL;
@@ -917,7 +923,7 @@ namespace {
         int R = 3 + (depth >= 5 * ONE_PLY ? depth / 8 : 0);
 
         // Null move dynamic reduction based on value
-        if (refinedValue - beta > PawnValueMidgame)
+        if (refinedValue - PawnValueMidgame > beta)
             R++;
 
         pos.do_null_move(st);
@@ -929,7 +935,7 @@ namespace {
         if (nullValue >= beta)
         {
             // Do not return unproven mate scores
-            if (nullValue >= value_mate_in(PLY_MAX))
+            if (nullValue >= VALUE_MATE_IN_PLY_MAX)
                 nullValue = beta;
 
             if (depth < 6 * ONE_PLY)
@@ -955,6 +961,7 @@ namespace {
                 mateThreat = true;
 
             threatMove = (ss+1)->bestMove;
+
             if (   depth < ThreatDepth
                 && (ss-1)->reduction
                 && threatMove != MOVE_NONE
@@ -966,7 +973,7 @@ namespace {
     // Step 9. Internal iterative deepening
     if (   depth >= IIDDepth[PvNode]
         && ttMove == MOVE_NONE
-        && (PvNode || (!isCheck && ss->eval >= beta - IIDMargin)))
+        && (PvNode || (!isCheck && ss->eval + IIDMargin >= beta)))
     {
         Depth d = (PvNode ? depth - 2 * ONE_PLY : depth / 2);
 
@@ -978,7 +985,7 @@ namespace {
         tte = TT.retrieve(posKey);
     }
 
-    // Expensive mate threat detection (only for PV nodes)
+    // Mate threat detection for PV nodes, otherwise we use null move search
     if (PvNode)
         mateThreat = pos.has_mate_threat();
 
@@ -1034,27 +1041,27 @@ 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)
+          if (current_search_time() > 2000)
               cout << "info currmove " << move
                    << " 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
+      isPvMove = (PvNode && moveCount <= (Root ? depth <= ONE_PLY ? 1000 : MultiPV : 1));
       moveIsCheck = pos.move_is_check(move, ci);
       captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       // Step 11. Decide the new search depth
       ext = extension<PvNode>(pos, move, captureOrPromotion, moveIsCheck, mateThreat, &dangerous);
 
-      // Singular extension search. If all moves but one fail low on a search of (alpha-s, beta-s),
-      // and just one fails high on (alpha, beta), then that move is singular and should be extended.
-      // To verify this we do a reduced search on all the other moves but the ttMove, if result is
-      // lower then ttValue minus a margin then we extend ttMove.
+      // Singular extension search. If all moves but one fail low on a search of
+      // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
+      // is singular and should be extended. To verify this we do a reduced search
+      // on all the other moves but the ttMove, if result is lower than ttValue minus
+      // a margin then we extend ttMove.
       if (   singularExtensionNode
           && move == tte->move()
           && ext < ONE_PLY)
@@ -1063,21 +1070,21 @@ split_point_start: // At split points actual search starts from here
 
           if (abs(ttValue) < VALUE_KNOWN_WIN)
           {
-              Value b = ttValue - SingularExtensionMargin;
+              Value rBeta = ttValue - int(depth);
               ss->excludedMove = move;
               ss->skipNullMove = true;
-              Value v = search<NonPV>(pos, ss, b - 1, b, depth / 2, ply);
+              Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, ply);
               ss->skipNullMove = false;
               ss->excludedMove = MOVE_NONE;
               ss->bestMove = MOVE_NONE;
-              if (v < b)
+              if (v < rBeta)
                   ext = ONE_PLY;
           }
       }
 
       // Update current move (this must be done after singular extension search)
       ss->currentMove = move;
-      newDepth = depth - (!Root ? ONE_PLY : DEPTH_ZERO) + ext;
+      newDepth = depth - ONE_PLY + ext;
 
       // Step 12. Futility pruning (is omitted in PV nodes)
       if (   !PvNode
@@ -1089,8 +1096,8 @@ split_point_start: // At split points actual search starts from here
       {
           // Move count based pruning
           if (   moveCount >= futility_move_count(depth)
-              && !(threatMove && connected_threat(pos, move, threatMove))
-              && bestValue > value_mated_in(PLY_MAX)) // FIXME bestValue is racy
+              && (!threatMove || !connected_threat(pos, move, threatMove))
+              && bestValue > VALUE_MATED_IN_PLY_MAX) // FIXME bestValue is racy
           {
               if (SpNode)
                   lock_grab(&(sp->lock));
@@ -1121,7 +1128,7 @@ split_point_start: // At split points actual search starts from here
 
           // Prune moves with negative SEE at low depths
           if (   predictedDepth < 2 * ONE_PLY
-              && bestValue > value_mated_in(PLY_MAX)
+              && bestValue > VALUE_MATED_IN_PLY_MAX
               && pos.see_sign(move) < 0)
           {
               if (SpNode)
@@ -1131,6 +1138,16 @@ split_point_start: // At split points actual search starts from here
           }
       }
 
+      // Bad capture detection. Will be used by prob-cut search
+      isBadCap =   depth >= 3 * ONE_PLY
+                && depth < 8 * ONE_PLY
+                && captureOrPromotion
+                && move != ttMove
+                && !dangerous
+                && !move_is_promotion(move)
+                &&  abs(alpha) < VALUE_MATE_IN_PLY_MAX
+                &&  pos.see_sign(move) < 0;
+
       // Step 13. Make the move
       pos.do_move(move, st, ci, moveIsCheck);
 
@@ -1152,6 +1169,7 @@ split_point_start: // At split points actual search starts from here
           // Step 14. Reduced depth search
           // If the move fails high will be re-searched at full depth.
           bool doFullDepthSearch = true;
+          alpha = SpNode ? sp->alpha : alpha;
 
           if (    depth >= 3 * ONE_PLY
               && !captureOrPromotion
@@ -1160,8 +1178,7 @@ split_point_start: // At split points actual search starts from here
               &&  ss->killers[0] != move
               &&  ss->killers[1] != move)
           {
-              ss->reduction = Root ? reduction<PvNode>(depth, moveCount - MultiPV + 1)
-                                   : reduction<PvNode>(depth, moveCount);
+              ss->reduction = reduction<PvNode>(depth, moveCount);
               if (ss->reduction)
               {
                   alpha = SpNode ? sp->alpha : alpha;
@@ -1173,6 +1190,18 @@ split_point_start: // At split points actual search starts from here
               ss->reduction = DEPTH_ZERO; // Restore original reduction
           }
 
+          // Probcut search for bad captures. If a reduced search returns a value
+          // very below beta then we can (almost) safely prune the bad capture.
+          if (isBadCap)
+          {
+              ss->reduction = 3 * ONE_PLY;
+              Value rAlpha = alpha - 300;
+              Depth d = newDepth - ss->reduction;
+              value = -search<NonPV>(pos, ss+1, -(rAlpha+1), -rAlpha, d, ply+1);
+              doFullDepthSearch = (value > rAlpha);
+              ss->reduction = DEPTH_ZERO; // Restore original reduction
+          }
+
           // Step 15. Full depth search
           if (doFullDepthSearch)
           {
@@ -1200,14 +1229,14 @@ split_point_start: // At split points actual search starts from here
           alpha = sp->alpha;
       }
 
-      if (!Root && value > bestValue && !(SpNode && ThreadsMgr.cutoff_at_splitpoint(threadID)))
+      if (value > bestValue && !(SpNode && ThreadsMgr.cutoff_at_splitpoint(threadID)))
       {
           bestValue = value;
 
           if (SpNode)
               sp->bestValue = value;
 
-          if (value > alpha)
+          if (!Root && value > alpha)
           {
               if (PvNode && value < beta) // We want always alpha < beta
               {
@@ -1225,16 +1254,12 @@ split_point_start: // At split points actual search starts from here
               ss->bestMove = move;
 
               if (SpNode)
-                  sp->parentSstack->bestMove = move;
+                  sp->ss->bestMove = move;
           }
       }
 
       if (Root)
       {
-          // To avoid to exit with bestValue == -VALUE_INFINITE
-          if (value > bestValue)
-              bestValue = value;
-
           // 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
@@ -1246,40 +1271,33 @@ split_point_start: // At split points actual search starts from here
           // Remember searched nodes counts for this move
           mp.rm->nodes += pos.nodes_searched() - nodes;
 
-          // Step 17. Check for new best move
-          if (!isPvMove && value <= alpha)
-              mp.rm->pv_score = -VALUE_INFINITE;
-          else
+          // PV move or new best move ?
+          if (isPvMove || value > alpha)
           {
-              // PV move or new best move!
-
               // Update PV
               ss->bestMove = move;
               mp.rm->pv_score = value;
               mp.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
+              // iteration. This information is used for time management: When
               // the best move changes frequently, we allocate some more time.
               if (!isPvMove && MultiPV == 1)
                   Rml.bestMoveChanges++;
 
-              // 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, depth, alpha, beta, j) << endl;
-
               // Update alpha. In multi-pv we don't use aspiration window, so
               // set alpha equal to minimum score among the PV lines.
               if (MultiPV > 1)
                   alpha = Rml[Min(moveCount, MultiPV) - 1].pv_score; // FIXME why moveCount?
               else if (value > alpha)
                   alpha = value;
+          }
+          else
+              mp.rm->pv_score = -VALUE_INFINITE;
 
-          } // PV move or new best move
-      }
+      } // Root
 
       // Step 18. Check for split
       if (   !Root
@@ -1316,8 +1334,12 @@ split_point_start: // At split points actual search starts from here
         if (    bestValue >= beta
             && !pos.move_is_capture_or_promotion(move))
         {
+            if (move != ss->killers[0])
+            {
+                ss->killers[1] = ss->killers[0];
+                ss->killers[0] = move;
+            }
             update_history(pos, move, depth, movesSearched, playedMoveCount);
-            update_killers(move, ss->killers);
         }
     }
 
@@ -1451,11 +1473,17 @@ 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
       evasionPrunable =   isCheck
-                       && bestValue > value_mated_in(PLY_MAX)
+                       && bestValue > VALUE_MATED_IN_PLY_MAX
                        && !pos.move_is_capture(move)
                        && !pos.can_castle(pos.side_to_move());
 
@@ -1519,26 +1547,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.
@@ -1649,28 +1657,16 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // value_is_mate() checks if the given value is a mate one eventually
-  // compensated for the ply.
-
-  bool value_is_mate(Value value) {
-
-    assert(abs(value) <= VALUE_INFINITE);
-
-    return   value <= value_mated_in(PLY_MAX)
-          || value >= value_mate_in(PLY_MAX);
-  }
-
-
   // value_to_tt() adjusts a mate score from "plies to mate from the root" to
   // "plies to mate from the current ply".  Non-mate scores are unchanged.
   // The function is called before storing a value to the transposition table.
 
   Value value_to_tt(Value v, int ply) {
 
-    if (v >= value_mate_in(PLY_MAX))
+    if (v >= VALUE_MATE_IN_PLY_MAX)
       return v + ply;
 
-    if (v <= value_mated_in(PLY_MAX))
+    if (v <= VALUE_MATED_IN_PLY_MAX)
       return v - ply;
 
     return v;
@@ -1682,10 +1678,10 @@ split_point_start: // At split points actual search starts from here
 
   Value value_from_tt(Value v, int ply) {
 
-    if (v >= value_mate_in(PLY_MAX))
+    if (v >= VALUE_MATE_IN_PLY_MAX)
       return v - ply;
 
-    if (v <= value_mated_in(PLY_MAX))
+    if (v <= VALUE_MATED_IN_PLY_MAX)
       return v + ply;
 
     return v;
@@ -1742,21 +1738,12 @@ split_point_start: // At split points actual search starts from here
         *dangerous = true;
     }
 
-    if (   PvNode
-        && captureOrPromotion
-        && pos.type_of_piece_on(move_to(m)) != PAWN
-        && pos.see_sign(m) >= 0)
-    {
-        result += ONE_PLY / 2;
-        *dangerous = true;
-    }
-
     return Min(result, ONE_PLY);
   }
 
 
   // connected_threat() tests whether it is safe to forward prune a move or if
-  // is somehow coonected to the threat move returned by null search.
+  // is somehow connected to the threat move returned by null search.
 
   bool connected_threat(const Position& pos, Move m, Move threat) {
 
@@ -1778,7 +1765,7 @@ split_point_start: // At split points actual search starts from here
         return true;
 
     // Case 2: If the threatened piece has value less than or equal to the
-    // value of the threatening piece, don't prune move which defend it.
+    // value of the threatening piece, don't prune moves which defend it.
     if (   pos.move_is_capture(threat)
         && (   pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto)
             || pos.type_of_piece_on(tfrom) == KING)
@@ -1804,8 +1791,8 @@ split_point_start: // At split points actual search starts from here
     Value v = value_from_tt(tte->value(), ply);
 
     return   (   tte->depth() >= depth
-              || v >= Max(value_mate_in(PLY_MAX), beta)
-              || v < Min(value_mated_in(PLY_MAX), beta))
+              || v >= Max(VALUE_MATE_IN_PLY_MAX, beta)
+              || v < Min(VALUE_MATED_IN_PLY_MAX, beta))
 
           && (   ((tte->type() & VALUE_TYPE_LOWER) && v >= beta)
               || ((tte->type() & VALUE_TYPE_UPPER) && v < beta));
@@ -1850,19 +1837,6 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // update_killers() add a good move that produced a beta-cutoff
-  // among the killer moves of that ply.
-
-  void update_killers(Move m, Move killers[]) {
-
-    if (m != killers[0])
-    {
-        killers[1] = killers[0];
-        killers[0] = m;
-    }
-  }
-
-
   // update_gains() updates the gains table of a non-capture move given
   // the static position evaluation before and after the move.
 
@@ -1877,6 +1851,15 @@ 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;
+  }
+
+
   // value_to_uci() converts a value to a string suitable for use with the UCI
   // protocol specifications:
   //
@@ -1891,27 +1874,25 @@ split_point_start: // At split points actual search starts from here
     if (abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY)
       s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns
     else
-      s << "mate " << (v > 0 ? (VALUE_MATE - v + 1) / 2 : -(VALUE_MATE + v) / 2 );
+      s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
 
     return s.str();
   }
 
 
-  // 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();
   }
 
 
@@ -1930,10 +1911,7 @@ split_point_start: // At split points actual search starts from here
         // We are line oriented, don't read single chars
         std::string command;
 
-        if (!std::getline(std::cin, command))
-            command = "quit";
-
-        if (command == "quit")
+        if (!std::getline(std::cin, command) || command == "quit")
         {
             // Quit the program as soon as possible
             Pondering = false;
@@ -2011,20 +1989,12 @@ split_point_start: // At split points actual search starts from here
 
     std::string command;
 
-    while (true)
-    {
-        // Wait for a command from stdin
-        if (!std::getline(std::cin, command))
-            command = "quit";
+    // Wait for a command from stdin
+    while (   std::getline(std::cin, command)
+           && command != "ponderhit" && command != "stop" && command != "quit") {};
 
-        if (command == "quit")
-        {
-            QuitRequest = true;
-            break;
-        }
-        else if (command == "ponderhit" || command == "stop")
-            break;
-    }
+    if (command != "ponderhit" && command != "stop")
+        QuitRequest = true; // Must be "quit" or getline() returned false
   }
 
 
@@ -2128,16 +2098,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);
 
@@ -2382,7 +2355,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;
 
@@ -2409,12 +2382,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()
@@ -2525,7 +2496,7 @@ split_point_start: // At split points actual search starts from here
         k = pos.get_key();
         tte = TT.retrieve(k);
 
-        // Don't overwrite exsisting correct entries
+        // Don't overwrite existing correct entries
         if (!tte || tte->move() != pv[ply])
         {
             v = (pos.is_check() ? VALUE_NONE : evaluate(pos, m));
@@ -2539,34 +2510,24 @@ split_point_start: // At split points actual search starts from here
   }
 
   // pv_info_to_uci() returns a string with information on the current PV line
-  // 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(Position& pos, Depth depth, Value alpha, Value beta, int pvLine) {
+  // formatted according to UCI specification.
 
+  std::string RootMove::pv_info_to_uci(Position& pos, int depth, Value alpha,
+                                       Value beta, int pvIdx) {
     std::stringstream s, l;
     Move* m = pv;
 
     while (*m != MOVE_NONE)
         l << *m++ << " ";
 
-    s << "info depth " << depth / ONE_PLY
+    s << "info depth " << depth
       << " seldepth " << int(m - pv)
-      << " multipv " << pvLine + 1
+      << " multipv " << pvIdx + 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();
 
-    if (UseLogFile && pvLine == 0)
-    {
-        ValueType t = pv_score >= beta  ? VALUE_TYPE_LOWER :
-                      pv_score <= alpha ? VALUE_TYPE_UPPER : VALUE_TYPE_EXACT;
-
-        LogFile << pretty_pv(pos, current_search_time(), depth / ONE_PLY, pv_score, t, pv) << endl;
-    }
     return s.str();
   }
 
@@ -2579,11 +2540,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
@@ -2596,10 +2554,51 @@ 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();
+  }
+
+
+  // When playing with strength handicap choose best move among the MultiPV set
+  // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
+  void do_skill_level(Move* best, Move* ponder) {
+
+    assert(MultiPV > 1);
+
+    // Rml list is already sorted by pv_score in descending order
+    int s;
+    int max_s = -VALUE_INFINITE;
+    int size = Min(MultiPV, (int)Rml.size());
+    int max = Rml[0].pv_score;
+    int var = Min(max - Rml[size - 1].pv_score, PawnValueMidgame);
+    int wk = 120 - 2 * SkillLevel;
+
+    // PRNG sequence should be non deterministic
+    for (int i = abs(get_system_time() % 50); i > 0; i--)
+        RK.rand<unsigned>();
+
+    // Choose best move. For each move's score we add two terms both dependent
+    // on wk, one deterministic and bigger for weaker moves, and one random,
+    // then we choose the move with the resulting highest score.
+    for (int i = 0; i < size; i++)
+    {
+        s = Rml[i].pv_score;
+
+        // Don't allow crazy blunders even at very low skills
+        if (i > 0 && Rml[i-1].pv_score > s + EasyMoveMargin)
+            break;
+
+        // This is our magical formula
+        s += ((max - s) * wk + var * (RK.rand<unsigned>() % wk)) / 128;
+
+        if (s > max_s)
+        {
+            max_s = s;
+            *best = Rml[i].pv[0];
+            *ponder = Rml[i].pv[1];
+        }
+    }
   }
 
 } // namespace