]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Rewrite pv_info_xxx() signatures
[stockfish] / src / search.cpp
index bab8f398cc8f0ebc684c94df9d2e24a29f145a17..df09007f1538f8f15134e6df34edbc8dafacb93e 100644 (file)
@@ -51,6 +51,11 @@ using std::endl;
 using Eval::evaluate;
 using namespace Search;
 
+// For some reason argument-dependent lookup (ADL) doesn't work for Android's
+// STLPort, so explicitly qualify following functions.
+using std::count;
+using std::find;
+
 namespace {
 
   // Set to true to force running with one thread. Used for debugging
@@ -67,7 +72,7 @@ namespace {
   const Depth RazorDepth = 4 * ONE_PLY;
 
   // Dynamic razoring margin based on depth
-  inline Value razor_margin(Depth d) { return Value(0x200 + 0x10 * int(d)); }
+  inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
 
   // Maximum depth for use of dynamic threat detection when null move fails low
   const Depth ThreatDepth = 5 * ONE_PLY;
@@ -77,13 +82,13 @@ namespace {
 
   // At Non-PV nodes we do an internal iterative deepening search
   // when the static evaluation is bigger then beta - IIDMargin.
-  const Value IIDMargin = Value(0x100);
+  const Value IIDMargin = Value(256);
 
   // Minimum depth for use of singular extension
   const Depth SingularExtensionDepth[] = { 8 * ONE_PLY, 6 * ONE_PLY };
 
   // Futility margin for quiescence search
-  const Value FutilityMarginQS = Value(0x80);
+  const Value FutilityMarginQS = Value(128);
 
   // Futility lookup tables (initialized at startup) and their access functions
   Value FutilityMargins[16][64]; // [depth][moveNumber]
@@ -140,8 +145,8 @@ namespace {
   Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval);
   Move do_skill_level();
   string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE);
-  void pv_info_to_log(Position& pos, int depth, Value score, int time, Move pv[]);
-  void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta);
+  string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
+  string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
 
   // MovePickerExt class template extends MovePicker and allows to choose at
   // compile time the proper moves source according to the type of node. In the
@@ -166,21 +171,21 @@ namespace {
   // 'dangerous' moves so that we avoid to prune it.
   FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
 
-    // Test for a pawn pushed to 7th or a passed pawn move
-    if (type_of(pos.piece_moved(m)) == PAWN)
-    {
-        Color c = pos.side_to_move();
-        if (   relative_rank(c, to_sq(m)) == RANK_7
-            || pos.pawn_is_passed(c, to_sq(m)))
-            return true;
-    }
+    // Castle move?
+    if (type_of(m) == CASTLE)
+        return true;
 
-    // Test for a capture that triggers a pawn endgame
-    if (   captureOrPromotion
-        && type_of(pos.piece_on(to_sq(m))) != PAWN
+    // Passed pawn move?
+    if (   type_of(pos.piece_moved(m)) == PAWN
+        && pos.pawn_is_passed(pos.side_to_move(), to_sq(m)))
+        return true;
+
+    // Entering a pawn endgame?
+    if (    captureOrPromotion
+        &&  type_of(pos.piece_on(to_sq(m))) != PAWN
+        &&  type_of(m) == NORMAL
         && (  pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
-            - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)
-        && !is_special(m))
+            - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO))
         return true;
 
     return false;
@@ -224,7 +229,7 @@ int64_t Search::perft(Position& pos, Depth depth) {
   StateInfo st;
   int64_t cnt = 0;
 
-  MoveList<MV_LEGAL> ml(pos);
+  MoveList<LEGAL> ml(pos);
 
   // At the last ply just return the number of moves (leaf nodes)
   if (depth == ONE_PLY)
@@ -422,7 +427,7 @@ namespace {
                 // Send full PV info to GUI if we are going to leave the loop or
                 // if we have a fail high/low and we are deep in the search.
                 if ((bestValue > alpha && bestValue < beta) || SearchTime.elapsed() > 2000)
-                    pv_info_to_uci(pos, depth, alpha, beta);
+                    cout << uci_pv(pos, depth, alpha, beta) << endl;
 
                 // In case of failing high/low increase aspiration window and
                 // research, otherwise exit the fail high/low loop.
@@ -452,7 +457,11 @@ namespace {
             skillBest = do_skill_level();
 
         if (!Signals.stop && Options["Use Search Log"])
-             pv_info_to_log(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0]);
+        {
+            Log log(Options["Search Log Filename"]);
+            log << pretty_pv(pos, depth, bestValue, SearchTime.elapsed(), &RootMoves[0].pv[0])
+                << endl;
+        }
 
         // Filter out startup noise when monitoring best move stability
         if (depth > 2 && BestMoveChanges)
@@ -622,10 +631,10 @@ namespace {
         TT.refresh(tte);
         ss->currentMove = ttMove; // Can be MOVE_NONE
 
-        if (   ttValue >= beta
-            && ttMove
+        if (    ttValue >= beta
+            &&  ttMove
             && !pos.is_capture_or_promotion(ttMove)
-            && ttMove != ss->killers[0])
+            &&  ttMove != ss->killers[0])
         {
             ss->killers[1] = ss->killers[0];
             ss->killers[0] = ttMove;
@@ -652,11 +661,11 @@ namespace {
 
     // Update gain for the parent non-capture move given the static position
     // evaluation before and after the move.
-    if (   (move = (ss-1)->currentMove) != MOVE_NULL
-        && (ss-1)->eval != VALUE_NONE
-        && ss->eval != VALUE_NONE
+    if (    (move = (ss-1)->currentMove) != MOVE_NULL
+        &&  (ss-1)->eval != VALUE_NONE
+        &&  ss->eval != VALUE_NONE
         && !pos.captured_piece_type()
-        && !is_special(move))
+        &&  type_of(move) == NORMAL)
     {
         Square to = to_sq(move);
         H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
@@ -703,16 +712,16 @@ namespace {
         ss->currentMove = MOVE_NULL;
 
         // Null move dynamic reduction based on depth
-        int R = 3 + (depth >= 5 * ONE_PLY ? depth / 8 : 0);
+        Depth R = 3 * ONE_PLY + depth / 4;
 
         // Null move dynamic reduction based on value
         if (refinedValue - PawnValueMidgame > beta)
-            R++;
+            R += ONE_PLY;
 
         pos.do_null_move<true>(st);
         (ss+1)->skipNullMove = true;
-        nullValue = depth-R*ONE_PLY < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
-                                              : - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R*ONE_PLY);
+        nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
+                                      : - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
         (ss+1)->skipNullMove = false;
         pos.do_null_move<false>(st);
 
@@ -727,7 +736,7 @@ namespace {
 
             // Do verification search at high depths
             ss->skipNullMove = true;
-            Value v = search<NonPV>(pos, ss, alpha, beta, depth-R*ONE_PLY);
+            Value v = search<NonPV>(pos, ss, alpha, beta, depth-R);
             ss->skipNullMove = false;
 
             if (v >= beta)
@@ -806,15 +815,15 @@ split_point_start: // At split points actual search starts from here
     futilityBase = ss->eval + ss->evalMargin;
     singularExtensionNode =   !RootNode
                            && !SpNode
-                           && depth >= SingularExtensionDepth[PvNode]
-                           && ttMove != MOVE_NONE
+                           &&  depth >= SingularExtensionDepth[PvNode]
+                           &&  ttMove != MOVE_NONE
                            && !excludedMove // Recursive singular search is not allowed
                            && (tte->type() & BOUND_LOWER)
-                           && tte->depth() >= depth - 3 * ONE_PLY;
+                           &&  tte->depth() >= depth - 3 * ONE_PLY;
 
     // Step 11. Loop through moves
     // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
-    while (   bestValue < beta
+    while (    bestValue < beta
            && (move = mp.next_move()) != MOVE_NONE
            && !thisThread->cutoff_occurred()
            && !Signals.stop)
@@ -863,29 +872,28 @@ split_point_start: // At split points actual search starts from here
           ext = ONE_PLY;
 
       else if (givesCheck && pos.see_sign(move) >= 0)
-          ext = PvNode ? ONE_PLY : ONE_PLY / 2;
+          ext = ONE_PLY / 2;
 
       // 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
+      if (    singularExtensionNode
           && !ext
-          && move == ttMove
-          && pos.pl_move_is_legal(move, ci.pinned))
+          &&  move == ttMove
+          &&  pos.pl_move_is_legal(move, ci.pinned)
+          &&  abs(ttValue) < VALUE_KNOWN_WIN)
       {
-          if (abs(ttValue) < VALUE_KNOWN_WIN)
-          {
-              Value rBeta = ttValue - int(depth);
-              ss->excludedMove = move;
-              ss->skipNullMove = true;
-              value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
-              ss->skipNullMove = false;
-              ss->excludedMove = MOVE_NONE;
-              if (value < rBeta)
-                  ext = ONE_PLY;
-          }
+          Value rBeta = ttValue - int(depth);
+          ss->excludedMove = move;
+          ss->skipNullMove = true;
+          value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2);
+          ss->skipNullMove = false;
+          ss->excludedMove = MOVE_NONE;
+
+          if (value < rBeta)
+              ext = ONE_PLY;
       }
 
       // Update current move (this must be done after singular extension search)
@@ -897,7 +905,6 @@ split_point_start: // At split points actual search starts from here
           && !inCheck
           && !dangerous
           &&  move != ttMove
-          && !is_castle(move)
           && (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE))
       {
           // Move count based pruning
@@ -952,11 +959,10 @@ split_point_start: // At split points actual search starts from here
 
       // Step 15. Reduced depth search (LMR). If the move fails high will be
       // re-searched at full depth.
-      if (   depth > 3 * ONE_PLY
+      if (    depth > 3 * ONE_PLY
           && !isPvMove
           && !captureOrPromotion
           && !dangerous
-          && !is_castle(move)
           &&  ss->killers[0] != move
           &&  ss->killers[1] != move)
       {
@@ -1051,9 +1057,9 @@ split_point_start: // At split points actual search starts from here
 
       // Step 19. Check for split
       if (   !SpNode
-          && depth >= Threads.min_split_depth()
-          && bestValue < beta
-          && Threads.available_slave_exists(thisThread)
+          &&  depth >= Threads.min_split_depth()
+          &&  bestValue < beta
+          &&  Threads.available_slave_exists(thisThread)
           && !Signals.stop
           && !thisThread->cutoff_occurred())
           bestValue = Threads.split<FakeSplit>(pos, ss, alpha, beta, bestValue, &bestMove,
@@ -1221,12 +1227,12 @@ split_point_start: // At split points actual search starts from here
           && !givesCheck
           &&  move != ttMove
           &&  enoughMaterial
-          && !is_promotion(move)
+          &&  type_of(move) != PROMOTION
           && !pos.is_passed_pawn_push(move))
       {
           futilityValue =  futilityBase
                          + PieceValueEndgame[pos.piece_on(to_sq(move))]
-                         + (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
+                         + (type_of(move) == ENPASSANT ? PawnValueEndgame : VALUE_ZERO);
 
           if (futilityValue < beta)
           {
@@ -1245,8 +1251,8 @@ split_point_start: // At split points actual search starts from here
 
       // Detect non-capture evasions that are candidate to be pruned
       evasionPrunable =   !PvNode
-                       && inCheck
-                       && bestValue > VALUE_MATED_IN_MAX_PLY
+                       &&  inCheck
+                       &&  bestValue > VALUE_MATED_IN_MAX_PLY
                        && !pos.is_capture(move)
                        && !pos.can_castle(pos.side_to_move());
 
@@ -1254,7 +1260,7 @@ split_point_start: // At split points actual search starts from here
       if (   !PvNode
           && (!inCheck || evasionPrunable)
           &&  move != ttMove
-          && !is_promotion(move)
+          &&  type_of(move) != PROMOTION
           &&  pos.see_sign(move) < 0)
           continue;
 
@@ -1349,7 +1355,7 @@ split_point_start: // At split points actual search starts from here
     while (b)
     {
         // Note that here we generate illegal "double move"!
-        if (futilityBase + PieceValueEndgame[pos.piece_on(pop_1st_bit(&b))] >= beta)
+        if (futilityBase + PieceValueEndgame[pos.piece_on(pop_lsb(&b))] >= beta)
             return true;
     }
 
@@ -1528,12 +1534,13 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // pv_info_to_uci() sends search info to GUI. UCI protocol requires to send all
-  // the PV lines also if are still to be searched and so refer to the previous
-  // search score.
+  // uci_pv() formats PV information according to UCI protocol. UCI requires
+  // to send all the PV lines also if are still to be searched and so refer to
+  // the previous search score.
 
-  void pv_info_to_uci(const Position& pos, int depth, Value alpha, Value beta) {
+  string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
 
+    std::stringstream s;
     int t = SearchTime.elapsed();
     int selDepth = 0;
 
@@ -1550,26 +1557,30 @@ split_point_start: // At split points actual search starts from here
 
         int d = (updated ? depth : depth - 1);
         Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore);
-        std::stringstream s;
 
-        for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
-            s <<  " " << move_to_uci(RootMoves[i].pv[j], Chess960);
+        if (s.rdbuf()->in_avail())
+            s << "\n";
 
-        cout << "info depth " << d
-             << " seldepth " << selDepth
-             << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
-             << " nodes " << pos.nodes_searched()
-             << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0)
-             << " time " << t
-             << " multipv " << i + 1
-             << " pv" << s.str() << endl;
+        s << "info depth " << d
+          << " seldepth " << selDepth
+          << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
+          << " nodes " << pos.nodes_searched()
+          << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0)
+          << " time " << t
+          << " multipv " << i + 1
+          << " pv";
+
+        for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
+            s <<  " " << move_to_uci(RootMoves[i].pv[j], Chess960);
     }
+
+    return s.str();
   }
 
 
-  // pv_info_to_log() writes human-readable search information to the log file
-  // (which is created when the UCI parameter "Use Search Log" is "true"). It
-  // uses the two below helpers to pretty format time and score respectively.
+  // pretty_pv() formats human-readable search information, typically to be
+  // appended to the search log file. It uses the two helpers below to pretty
+  // format time and score respectively.
 
   string time_to_string(int millisecs) {
 
@@ -1596,8 +1607,10 @@ split_point_start: // At split points actual search starts from here
 
     if (v >= VALUE_MATE_IN_MAX_PLY)
         s << "#" << (VALUE_MATE - v + 1) / 2;
+
     else if (v <= VALUE_MATED_IN_MAX_PLY)
         s << "-#" << (VALUE_MATE + v) / 2;
+
     else
         s << std::setprecision(2) << std::fixed << std::showpos
           << float(v) / PawnValueMidgame;
@@ -1605,7 +1618,7 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
-  void pv_info_to_log(Position& pos, int depth, Value value, int time, Move pv[]) {
+  string pretty_pv(Position& pos, int depth, Value value, int time, Move pv[]) {
 
     const int64_t K = 1000;
     const int64_t M = 1000000;
@@ -1651,8 +1664,7 @@ split_point_start: // At split points actual search starts from here
     while (m != pv)
         pos.undo_move(*--m);
 
-    Log l(Options["Search Log Filename"]);
-    l << s.str() << endl;
+    return s.str();
   }
 
 
@@ -1849,8 +1861,8 @@ void Thread::idle_loop(SplitPoint* sp_master) {
 
           // Wake up master thread so to allow it to return from the idle loop in
           // case we are the last slave of the split point.
-          if (   Threads.use_sleeping_threads()
-              && this != sp->master
+          if (    Threads.use_sleeping_threads()
+              &&  this != sp->master
               && !sp->master->is_searching)
               sp->master->wake_up();