]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Send again all the PV lines in multiPV searching
[stockfish] / src / search.cpp
index 0f2373cd28fd28838c1e79990c5d26349ae554a2..4bb701a216b0c907b7ae94c990a695868b6b2f47 100644 (file)
@@ -270,7 +270,7 @@ namespace {
     if (moveIsCheck && pos.see_sign(m) >= 0)
         result += CheckExtension[PvNode];
 
-    if (piece_type(pos.piece_on(move_from(m))) == PAWN)
+    if (type_of(pos.piece_on(move_from(m))) == PAWN)
     {
         Color c = pos.side_to_move();
         if (relative_rank(c, move_to(m)) == RANK_7)
@@ -286,10 +286,10 @@ namespace {
     }
 
     if (   captureOrPromotion
-        && piece_type(pos.piece_on(move_to(m))) != PAWN
+        && type_of(pos.piece_on(move_to(m))) != PAWN
         && (  pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
             - piece_value_midgame(pos.piece_on(move_to(m))) == VALUE_ZERO)
-        && !move_is_special(m))
+        && !is_special(m))
     {
         result += PawnEndgameExtension[PvNode];
         *dangerous = true;
@@ -363,7 +363,7 @@ int64_t perft(Position& pos, Depth depth) {
 
 bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
 
-  static Book book;
+  static Book book; // Define static to initialize the PRNG only once
 
   // Initialize global search-related variables
   StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = false;
@@ -391,7 +391,7 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
       if (Options["Book File"].value<string>() != book.name())
           book.open(Options["Book File"].value<string>());
 
-      Move bookMove = book.get_move(pos, Options["Best Book Move"].value<bool>());
+      Move bookMove = book.probe(pos, Options["Best Book Move"].value<bool>());
       if (bookMove != MOVE_NONE)
       {
           if (Limits.ponder)
@@ -534,10 +534,8 @@ namespace {
 
         Rml.bestMoveChanges = 0;
 
-        // MultiPV iteration loop. At depth 1 perform at least 2 iterations to
-        // get a score of the second best move for easy move detection.
-        int e = Min(Max(MultiPV, 2 * int(depth == 1)), (int)Rml.size());
-        for (MultiPVIteration = 0; MultiPVIteration < e; MultiPVIteration++)
+        // MultiPV iteration loop
+        for (MultiPVIteration = 0; MultiPVIteration < Min(MultiPV, (int)Rml.size()); MultiPVIteration++)
         {
             // Calculate dynamic aspiration window based on previous iterations
             if (depth >= 5 && abs(Rml[MultiPVIteration].prevScore) < VALUE_KNOWN_WIN)
@@ -586,16 +584,27 @@ namespace {
                     break;
 
                 // 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 ((value > alpha && value < beta) || current_search_time() > 2000)
-                    for (int i = 0; i < Min(UCIMultiPV, MultiPVIteration + 1); i++)
+                // if we have a fail high/low and we are deep in the search. Note
+                // that UCI protol requires to send all the PV lines also if are
+                // still to be searched and so refer to the previous search's score.
+                if ((value > alpha && value < beta) || current_search_time() > 5000)
+                    for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++)
+                    {
+                        bool updated = (i <= MultiPVIteration);
+
+                        if (depth == 1 && !updated)
+                            continue;
+
+                        Depth d = (updated ? depth : depth - 1) * ONE_PLY;
+                        Value s = (updated ? Rml[i].score : Rml[i].prevScore);
+
                         cout << "info"
-                             << depth_to_uci(depth * ONE_PLY)
-                             << (i == MultiPVIteration ? score_to_uci(Rml[i].score, alpha, beta) :
-                                                         score_to_uci(Rml[i].score))
+                             << depth_to_uci(d)
+                             << (i == MultiPVIteration ? score_to_uci(s, alpha, beta) : score_to_uci(s))
                              << speed_to_uci(pos.nodes_searched())
                              << pv_to_uci(&Rml[i].pv[0], i + 1, pos.is_chess960())
                              << endl;
+                    }
 
                 // In case of failing high/low increase aspiration window and research,
                 // otherwise exit the fail high/low loop.
@@ -780,8 +789,18 @@ namespace {
                                     : can_return_tt(tte, depth, beta, ss->ply)))
     {
         TT.refresh(tte);
-        ss->bestMove = ttMove; // Can be MOVE_NONE
-        return value_from_tt(tte->value(), ss->ply);
+        ss->bestMove = move = ttMove; // Can be MOVE_NONE
+        value = value_from_tt(tte->value(), ss->ply);
+
+        if (   value >= beta
+            && move
+            && !pos.is_capture_or_promotion(move)
+            && move != ss->killers[0])
+        {
+            ss->killers[1] = ss->killers[0];
+            ss->killers[0] = move;
+        }
+        return value;
     }
 
     // Step 5. Evaluate the position statically and update parent's gain statistics
@@ -964,7 +983,7 @@ split_point_start: // At split points actual search starts from here
            && (move = mp.get_next_move()) != MOVE_NONE
            && !thread.cutoff_occurred())
     {
-      assert(move_is_ok(move));
+      assert(is_ok(move));
 
       if (move == excludedMove)
           continue;
@@ -1002,9 +1021,10 @@ split_point_start: // At split points actual search starts from here
                    << " currmovenumber " << moveCount + MultiPVIteration << endl;
       }
 
-      isPvMove = (PvNode && moveCount == 1);
+      // At Root and at first iteration do a PV search on all the moves to score root moves
+      isPvMove = (PvNode && moveCount <= (RootNode && depth <= ONE_PLY ? MAX_MOVES : 1));
       givesCheck = pos.move_gives_check(move, ci);
-      captureOrPromotion = pos.move_is_capture_or_promotion(move);
+      captureOrPromotion = pos.is_capture_or_promotion(move);
 
       // Step 12. Decide the new search depth
       ext = extension<PvNode>(pos, move, captureOrPromotion, givesCheck, &dangerous);
@@ -1044,7 +1064,7 @@ split_point_start: // At split points actual search starts from here
           && !inCheck
           && !dangerous
           &&  move != ttMove
-          && !move_is_castle(move))
+          && !is_castle(move))
       {
           // Move count based pruning
           if (   moveCount >= futility_move_count(depth)
@@ -1118,7 +1138,7 @@ split_point_start: // At split points actual search starts from here
           if (    depth > 3 * ONE_PLY
               && !captureOrPromotion
               && !dangerous
-              && !move_is_castle(move)
+              && !is_castle(move)
               &&  ss->killers[0] != move
               &&  ss->killers[1] != move
               && (ss->reduction = reduction<PvNode>(depth, moveCount)) != DEPTH_ZERO)
@@ -1243,7 +1263,7 @@ split_point_start: // At split points actual search starts from here
 
         // Update killers and history only for non capture moves that fails high
         if (    bestValue >= beta
-            && !pos.move_is_capture_or_promotion(move))
+            && !pos.is_capture_or_promotion(move))
         {
             if (move != ss->killers[0])
             {
@@ -1363,7 +1383,7 @@ split_point_start: // At split points actual search starts from here
     while (   bestValue < beta
            && (move = mp.get_next_move()) != MOVE_NONE)
     {
-      assert(move_is_ok(move));
+      assert(is_ok(move));
 
       givesCheck = pos.move_gives_check(move, ci);
 
@@ -1373,12 +1393,12 @@ split_point_start: // At split points actual search starts from here
           && !givesCheck
           &&  move != ttMove
           &&  enoughMaterial
-          && !move_is_promotion(move)
-          && !pos.move_is_passed_pawn_push(move))
+          && !is_promotion(move)
+          && !pos.is_passed_pawn_push(move))
       {
           futilityValue =  futilityBase
                          + piece_value_endgame(pos.piece_on(move_to(move)))
-                         + (move_is_ep(move) ? PawnValueEndgame : VALUE_ZERO);
+                         + (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
 
           if (futilityValue < beta)
           {
@@ -1399,14 +1419,14 @@ split_point_start: // At split points actual search starts from here
       evasionPrunable =   !PvNode
                        && inCheck
                        && bestValue > VALUE_MATED_IN_PLY_MAX
-                       && !pos.move_is_capture(move)
+                       && !pos.is_capture(move)
                        && !pos.can_castle(pos.side_to_move());
 
       // Don't search moves with negative SEE values
       if (   !PvNode
           && (!inCheck || evasionPrunable)
           &&  move != ttMove
-          && !move_is_promotion(move)
+          && !is_promotion(move)
           &&  pos.see_sign(move) < 0)
           continue;
 
@@ -1415,7 +1435,7 @@ split_point_start: // At split points actual search starts from here
           && !inCheck
           &&  givesCheck
           &&  move != ttMove
-          && !pos.move_is_capture_or_promotion(move)
+          && !pos.is_capture_or_promotion(move)
           &&  ss->eval + PawnValueMidgame / 4 < beta
           && !check_is_dangerous(pos, move, futilityBase, beta, &bestValue))
       {
@@ -1484,7 +1504,7 @@ split_point_start: // At split points actual search starts from here
 
     from = move_from(move);
     to = move_to(move);
-    them = opposite_color(pos.side_to_move());
+    them = flip(pos.side_to_move());
     ksq = pos.king_square(them);
     kingAtt = pos.attacks_from<KING>(ksq);
     pc = pos.piece_on(from);
@@ -1500,7 +1520,7 @@ split_point_start: // At split points actual search starts from here
         return true;
 
     // Rule 2. Queen contact check is very dangerous
-    if (   piece_type(pc) == QUEEN
+    if (   type_of(pc) == QUEEN
         && bit_is_set(kingAtt, to))
         return true;
 
@@ -1539,8 +1559,8 @@ split_point_start: // At split points actual search starts from here
     Piece p1, p2;
     Square ksq;
 
-    assert(m1 && move_is_ok(m1));
-    assert(m2 && move_is_ok(m2));
+    assert(is_ok(m1));
+    assert(is_ok(m2));
 
     // Case 1: The moving piece is the same in both moves
     f2 = move_from(m2);
@@ -1615,10 +1635,10 @@ split_point_start: // At split points actual search starts from here
 
   bool connected_threat(const Position& pos, Move m, Move threat) {
 
-    assert(move_is_ok(m));
-    assert(threat && move_is_ok(threat));
-    assert(!pos.move_is_capture_or_promotion(m));
-    assert(!pos.move_is_passed_pawn_push(m));
+    assert(is_ok(m));
+    assert(is_ok(threat));
+    assert(!pos.is_capture_or_promotion(m));
+    assert(!pos.is_passed_pawn_push(m));
 
     Square mfrom, mto, tfrom, tto;
 
@@ -1633,9 +1653,9 @@ split_point_start: // At split points actual search starts from here
 
     // Case 2: If the threatened piece has value less than or equal to the
     // value of the threatening piece, don't prune moves which defend it.
-    if (   pos.move_is_capture(threat)
+    if (   pos.is_capture(threat)
         && (   piece_value_midgame(pos.piece_on(tfrom)) >= piece_value_midgame(pos.piece_on(tto))
-            || piece_type(pos.piece_on(tfrom)) == KING)
+            || type_of(pos.piece_on(tfrom)) == KING)
         && pos.move_attacks_square(m, tto))
         return true;
 
@@ -1713,7 +1733,7 @@ split_point_start: // At split points actual search starts from here
         && before != VALUE_NONE
         && after != VALUE_NONE
         && pos.captured_piece_type() == PIECE_TYPE_NONE
-        && !move_is_special(m))
+        && !is_special(m))
         H.update_gain(pos.piece_on(move_to(m)), move_to(m), -(before + after));
   }
 
@@ -2074,7 +2094,7 @@ split_point_start: // At split points actual search starts from here
     int ply = 1;
     Move m = pv[0];
 
-    assert(m != MOVE_NONE && pos.move_is_pl(m));
+    assert(m != MOVE_NONE && pos.is_pseudo_legal(m));
 
     pv.clear();
     pv.push_back(m);
@@ -2082,7 +2102,7 @@ split_point_start: // At split points actual search starts from here
 
     while (   (tte = TT.probe(pos.get_key())) != NULL
            && tte->move() != MOVE_NONE
-           && pos.move_is_pl(tte->move())
+           && pos.is_pseudo_legal(tte->move())
            && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces())
            && ply < PLY_MAX
            && (!pos.is_draw<false>() || ply < 2))
@@ -2108,7 +2128,7 @@ split_point_start: // At split points actual search starts from here
     Value v, m = VALUE_NONE;
     int ply = 0;
 
-    assert(pv[0] != MOVE_NONE && pos.move_is_pl(pv[0]));
+    assert(pv[0] != MOVE_NONE && pos.is_pseudo_legal(pv[0]));
 
     do {
         k = pos.get_key();
@@ -2158,17 +2178,16 @@ void Thread::idle_loop(SplitPoint* sp) {
       {
           assert((!sp && threadID) || Threads.use_sleeping_threads());
 
-          // Grab the lock to avoid races with Thread::wake_up()
-          lock_grab(&sleepLock);
-
           // Slave thread should exit as soon as do_terminate flag raises
           if (do_terminate)
           {
               assert(!sp);
-              lock_release(&sleepLock);
               return;
           }
 
+          // Grab the lock to avoid races with Thread::wake_up()
+          lock_grab(&sleepLock);
+
           // If we are master and all slaves have finished don't go to sleep
           if (sp && all_slaves_finished(sp))
           {