]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Retire seeValues[] and move PieceValue[] out of Position
[stockfish] / src / search.cpp
index 1ffab9d3f16055b5227317e2340c56861c6d7383..81f96ccdc74162d7ac63e1b37eece83b23ad9a60 100644 (file)
@@ -72,8 +72,7 @@ namespace {
 
     void extract_pv_from_tt(Position& pos);
     void insert_pv_in_tt(Position& pos);
-    std::string pv_info_to_uci(Position& pos, int depth, int selDepth,
-                               Value alpha, Value beta, int pvIdx);
+
     int64_t nodes;
     Value pv_score;
     Value non_pv_score;
@@ -218,8 +217,10 @@ namespace {
   void do_skill_level(Move* best, Move* ponder);
 
   int current_search_time(int set = 0);
-  std::string value_to_uci(Value v);
+  std::string score_to_uci(Value v, Value alpha, Value beta);
   std::string speed_to_uci(int64_t nodes);
+  std::string pv_to_uci(Move pv[], int pvNum);
+  std::string depth_to_uci(Depth depth);
   void poll(const Position& pos);
   void wait_for_stop_or_ponderhit();
 
@@ -317,7 +318,7 @@ namespace {
     if (   captureOrPromotion
         && pos.type_of_piece_on(move_to(m)) != PAWN
         && (  pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
-            - pos.midgame_value_of_piece_on(move_to(m)) == VALUE_ZERO)
+            - piece_value_midgame(pos.piece_on(move_to(m))) == VALUE_ZERO)
         && !move_is_special(m))
     {
         result += PawnEndgameExtension[PvNode];
@@ -404,6 +405,9 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
   Limits = limits;
   TimeMgr.init(Limits, pos.startpos_ply_counter());
 
+  // Set output steram in normal or chess960 mode
+  cout << set960(pos.is_chess960());
+
   // Set best NodesBetweenPolls interval to avoid lagging under time pressure
   if (Limits.maxNodes)
       NodesBetweenPolls = Min(Limits.maxNodes, 30000);
@@ -480,8 +484,6 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
   Move ponderMove = MOVE_NONE;
   Move bestMove = id_loop(pos, searchMoves, &ponderMove);
 
-  cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
-
   // Write final search statistics and close log file
   if (LogFile.is_open())
   {
@@ -531,7 +533,7 @@ namespace {
     SearchStack ss[PLY_MAX_PLUS_2];
     Value bestValues[PLY_MAX_PLUS_2];
     int bestMoveChanges[PLY_MAX_PLUS_2];
-    int depth, selDepth, aspirationDelta;
+    int depth, aspirationDelta;
     Value value, alpha, beta;
     Move bestMove, easyMove, skillBest, skillPonder;
 
@@ -548,11 +550,10 @@ namespace {
     Rml.init(pos, searchMoves);
 
     // Handle special case of searching on a mate/stalemate position
-    if (Rml.size() == 0)
+    if (!Rml.size())
     {
-        cout << "info depth 0 score "
-             << value_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW)
-             << endl;
+        cout << "info" << depth_to_uci(DEPTH_ZERO)
+             << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW, alpha, beta) << endl;
 
         return MOVE_NONE;
     }
@@ -561,7 +562,6 @@ namespace {
     while (!StopRequest && ++depth <= PLY_MAX && (!Limits.maxDepth || depth <= Limits.maxDepth))
     {
         Rml.bestMoveChanges = 0;
-        cout << set960(pos.is_chess960()) << "info depth " << depth << endl;
 
         // Calculate dynamic aspiration window based on previous iterations
         if (MultiPV == 1 && depth >= 5 && abs(bestValues[depth - 1]) < VALUE_KNOWN_WIN)
@@ -591,6 +591,16 @@ namespace {
             if (StopRequest)
                 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, (int)Rml.size()); i++)
+                    cout << "info"
+                         << depth_to_uci(depth * ONE_PLY)
+                         << score_to_uci(Rml[i].pv_score, alpha, beta)
+                         << speed_to_uci(pos.nodes_searched())
+                         << pv_to_uci(Rml[i].pv, i + 1) << endl;
+
             // In case of failing high/low increase aspiration window and research,
             // otherwise exit the fail high/low loop.
             if (value >= beta)
@@ -621,16 +631,6 @@ namespace {
         if (SkillLevelEnabled && depth == 1 + SkillLevel)
             do_skill_level(&skillBest, &skillPonder);
 
-        // Retrieve max searched depth among threads
-        selDepth = 0;
-        for (int i = 0; i < Threads.size(); i++)
-            if (Threads[i].maxPly > selDepth)
-                selDepth = Threads[i].maxPly;
-
-        // 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, selDepth, alpha, beta, i) << endl;
-
         if (LogFile.is_open())
             LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl;
 
@@ -717,7 +717,6 @@ namespace {
     StateInfo st;
     const TTEntry *tte;
     Key posKey;
-    Bitboard pinned;
     Move ttMove, move, excludedMove, threatMove;
     Depth ext, newDepth;
     ValueType vt;
@@ -764,10 +763,13 @@ namespace {
         return VALUE_DRAW;
 
     // Step 3. Mate distance pruning
-    alpha = Max(value_mated_in(ss->ply), alpha);
-    beta = Min(value_mate_in(ss->ply+1), beta);
-    if (alpha >= beta)
-        return alpha;
+    if (!RootNode)
+    {
+        alpha = Max(value_mated_in(ss->ply), alpha);
+        beta = Min(value_mate_in(ss->ply+1), beta);
+        if (alpha >= beta)
+            return alpha;
+    }
 
     // Step 4. Transposition table lookup
     // We don't want the score of a partial search to overwrite a previous full search
@@ -914,13 +916,13 @@ namespace {
 
         assert(rdepth >= ONE_PLY);
 
-        MovePicker mp(pos, ttMove, H, Position::see_value(pos.captured_piece_type()));
-        pinned = pos.pinned_pieces(pos.side_to_move());
+        MovePicker mp(pos, ttMove, H, pos.captured_piece_type());
+        CheckInfo ci(pos);
 
         while ((move = mp.get_next_move()) != MOVE_NONE)
-            if (pos.pl_move_is_legal(move, pinned))
+            if (pos.pl_move_is_legal(move, ci.pinned))
             {
-                pos.do_move(move, st);
+                pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
                 value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth);
                 pos.undo_move(move);
                 if (value >= rbeta)
@@ -948,7 +950,6 @@ split_point_start: // At split points actual search starts from here
     // Initialize a MovePicker object for the current position
     MovePickerExt<NT> mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
     CheckInfo ci(pos);
-    pinned = pos.pinned_pieces(pos.side_to_move());
     ss->bestMove = MOVE_NONE;
     futilityBase = ss->eval + ss->evalMargin;
     singularExtensionNode =   !RootNode
@@ -976,7 +977,7 @@ split_point_start: // At split points actual search starts from here
           continue;
 
       // At PV and SpNode nodes we want the moves to be legal
-      if ((PvNode || SpNode) && !pos.pl_move_is_legal(move, pinned))
+      if ((PvNode || SpNode) && !pos.pl_move_is_legal(move, ci.pinned))
           continue;
 
       if (SpNode)
@@ -1003,15 +1004,16 @@ split_point_start: // At split points actual search starts from here
               cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
           }
 
+          // For long searches send to GUI current move
           if (current_search_time() > 2000)
-              cout << "info currmove " << move
-                   << " currmovenumber " << moveCount << endl;
+              cout << "info" << depth_to_uci(depth)
+                   << " currmove " << move << " currmovenumber " << moveCount << endl;
       }
 
       // 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 ? 1000 : MultiPV : 1));
+      isPvMove = (PvNode && moveCount <= (RootNode ? depth <= ONE_PLY ? MAX_MOVES : MultiPV : 1));
       givesCheck = pos.move_gives_check(move, ci);
-      captureOrPromotion = pos.move_is_capture(move) || move_is_promotion(move);
+      captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       // Step 12. Decide the new search depth
       ext = extension<PvNode>(pos, move, captureOrPromotion, givesCheck, &dangerous);
@@ -1023,7 +1025,7 @@ split_point_start: // At split points actual search starts from here
       // a margin then we extend ttMove.
       if (   singularExtensionNode
           && move == ttMove
-          && pos.pl_move_is_legal(move, pinned)
+          && pos.pl_move_is_legal(move, ci.pinned)
           && ext < ONE_PLY)
       {
           Value ttValue = value_from_tt(tte->value(), ss->ply);
@@ -1098,7 +1100,7 @@ split_point_start: // At split points actual search starts from here
       }
 
       // Check for legality only before to do the move
-      if (!pos.pl_move_is_legal(move, pinned))
+      if (!pos.pl_move_is_legal(move, ci.pinned))
       {
           moveCount--;
           continue;
@@ -1115,14 +1117,8 @@ 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 (isPvMove)
-      {
-          // Aspiration window is disabled in multi-pv case
-          if (RootNode && MultiPV > 1)
-              alpha = -VALUE_INFINITE;
-
           value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
                                      : - search<PV>(pos, ss+1, -beta, -alpha, newDepth);
-      }
       else
       {
           // Step 15. Reduced depth search
@@ -1231,7 +1227,7 @@ split_point_start: // At split points actual search starts from here
                   Rml.bestMoveChanges++;
 
               // It is critical that sorting is done with a stable algorithm
-              // becuase all the values but the first are usually set to
+              // because all the values but the first are usually set to
               // -VALUE_INFINITE and we want to keep the same order for all
               // the moves but the new PV that goes to head.
               Rml.sort_first(moveCount);
@@ -1283,8 +1279,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(move)
-            && !move_is_promotion(move))
+            && !pos.move_is_capture_or_promotion(move))
         {
             if (move != ss->killers[0])
             {
@@ -1398,7 +1393,6 @@ split_point_start: // At split points actual search starts from here
     // be generated.
     MovePicker mp(pos, ttMove, depth, H, move_to((ss-1)->currentMove));
     CheckInfo ci(pos);
-    Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
 
     // Loop through the moves until no moves remain or a beta cutoff occurs
     while (   alpha < beta
@@ -1418,7 +1412,7 @@ split_point_start: // At split points actual search starts from here
           && !pos.move_is_passed_pawn_push(move))
       {
           futilityValue =  futilityBase
-                         + pos.endgame_value_of_piece_on(move_to(move))
+                         + piece_value_endgame(pos.piece_on(move_to(move)))
                          + (move_is_ep(move) ? PawnValueEndgame : VALUE_ZERO);
 
           if (futilityValue < alpha)
@@ -1455,8 +1449,7 @@ split_point_start: // At split points actual search starts from here
           && !inCheck
           &&  givesCheck
           &&  move != ttMove
-          && !pos.move_is_capture(move)
-          && !move_is_promotion(move)
+          && !pos.move_is_capture_or_promotion(move)
           &&  ss->eval + PawnValueMidgame / 4 < beta
           && !check_is_dangerous(pos, move, futilityBase, beta, &bestValue))
       {
@@ -1467,7 +1460,7 @@ split_point_start: // At split points actual search starts from here
       }
 
       // Check for legality only before to do the move
-      if (!pos.pl_move_is_legal(move, pinned))
+      if (!pos.pl_move_is_legal(move, ci.pinned))
           continue;
 
       // Update current move
@@ -1547,7 +1540,7 @@ split_point_start: // At split points actual search starts from here
     while (b)
     {
         victimSq = pop_1st_bit(&b);
-        futilityValue = futilityBase + pos.endgame_value_of_piece_on(victimSq);
+        futilityValue = futilityBase + piece_value_endgame(pos.piece_on(victimSq));
 
         // Note that here we generate illegal "double move"!
         if (   futilityValue >= beta
@@ -1655,8 +1648,7 @@ split_point_start: // At split points actual search starts from here
 
     assert(move_is_ok(m));
     assert(threat && move_is_ok(threat));
-    assert(!pos.move_gives_check(m));
-    assert(!pos.move_is_capture(m) && !move_is_promotion(m));
+    assert(!pos.move_is_capture_or_promotion(m));
     assert(!pos.move_is_passed_pawn_push(m));
 
     Square mfrom, mto, tfrom, tto;
@@ -1673,7 +1665,7 @@ 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)
-        && (   pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto)
+        && (   piece_value_midgame(pos.piece_on(tfrom)) >= piece_value_midgame(pos.piece_on(tto))
             || pos.type_of_piece_on(tfrom) == KING)
         && pos.move_attacks_square(m, tto))
         return true;
@@ -1771,21 +1763,23 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // value_to_uci() converts a value to a string suitable for use with the UCI
+  // score_to_uci() converts a value to a string suitable for use with the UCI
   // protocol specifications:
   //
   // cp <x>     The score from the engine's point of view in centipawns.
   // mate <y>   Mate in y moves, not plies. If the engine is getting mated
   //            use negative values for y.
 
-  std::string value_to_uci(Value v) {
+  std::string score_to_uci(Value v, Value alpha, Value beta) {
 
     std::stringstream s;
 
     if (abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY)
-        s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns
+        s << " score cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns
     else
-        s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
+        s << " score mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
+
+    s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
 
     return s.str();
   }
@@ -1800,12 +1794,45 @@ split_point_start: // At split points actual search starts from here
     int t = current_search_time();
 
     s << " nodes " << nodes
-      << " nps "   << (t > 0 ? int(nodes * 1000 / t) : 0)
+      << " nps " << (t > 0 ? int(nodes * 1000 / t) : 0)
       << " time "  << t;
 
     return s.str();
   }
 
+  // pv_to_uci() returns a string with information on the current PV line
+  // formatted according to UCI specification.
+
+  std::string pv_to_uci(Move pv[], int pvNum) {
+
+    std::stringstream s;
+
+    s << " multipv " << pvNum << " pv ";
+
+    for ( ; *pv != MOVE_NONE; pv++)
+        s << *pv << " ";
+
+    return s.str();
+  }
+
+  // depth_to_uci() returns a string with information on the current depth and
+  // seldepth formatted according to UCI specification.
+
+  std::string depth_to_uci(Depth depth) {
+
+    std::stringstream s;
+
+    // Retrieve max searched depth among threads
+    int selDepth = 0;
+    for (int i = 0; i < Threads.size(); i++)
+        if (Threads[i].maxPly > selDepth)
+            selDepth = Threads[i].maxPly;
+
+     s << " depth " << depth / ONE_PLY << " seldepth " << selDepth;
+
+    return s.str();
+  }
+
 
   // poll() performs two different functions: It polls for user input, and it
   // looks at the time consumed so far and decides if it's time to abort the
@@ -2061,27 +2088,6 @@ split_point_start: // At split points actual search starts from here
     do pos.undo_move(pv[--ply]); while (ply);
   }
 
-  // pv_info_to_uci() returns a string with information on the current PV line
-  // formatted according to UCI specification.
-
-  std::string RootMove::pv_info_to_uci(Position& pos, int depth, int selDepth, Value alpha,
-                                       Value beta, int pvIdx) {
-    std::stringstream s;
-
-    s << "info depth " << depth
-      << " seldepth " << selDepth
-      << " multipv " << pvIdx + 1
-      << " score " << value_to_uci(pv_score)
-      << (pv_score >= beta ? " lowerbound" : pv_score <= alpha ? " upperbound" : "")
-      << speed_to_uci(pos.nodes_searched())
-      << " pv ";
-
-    for (Move* m = pv; *m != MOVE_NONE; m++)
-        s << *m << " ";
-
-    return s.str();
-  }
-
   // Specializations for MovePickerExt in case of Root node
   MovePickerExt<Root>::MovePickerExt(const Position& p, Move ttm, Depth d,
                                      const History& h, SearchStack* ss, Value b)