]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Ressurect move.cpp
[stockfish] / src / search.cpp
index a73e1b80ff335183543a8ccd16acf6b14d8fdb82..011bec694ff3c52639c8a4a86a74a4515b910f3b 100644 (file)
 #include "evaluate.h"
 #include "history.h"
 #include "misc.h"
+#include "move.h"
 #include "movegen.h"
 #include "movepick.h"
 #include "lock.h"
-#include "san.h"
 #include "search.h"
 #include "timeman.h"
 #include "thread.h"
@@ -146,7 +146,7 @@ namespace {
     typedef std::vector<RootMove> Base;
 
     RootMoveList(Position& pos, Move searchMoves[]);
-    void set_non_pv_scores(const Position& pos);
+    void set_non_pv_scores(const Position& pos, Move ttm, SearchStack* ss);
 
     void sort() { insertion_sort<RootMove, Base::iterator>(begin(), end()); }
     void sort_multipv(int n) { insertion_sort<RootMove, Base::iterator>(begin(), begin() + n); }
@@ -161,13 +161,22 @@ namespace {
   // operator<<() that will use it to properly format castling moves.
   enum set960 {};
 
-  std::ostream& operator<< (std::ostream& os, const set960& m) {
+  std::ostream& operator<< (std::ostream& os, const set960& f) {
 
-    os.iword(0) = int(m);
+    os.iword(0) = int(f);
     return os;
   }
 
 
+  // 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
@@ -304,7 +313,7 @@ namespace {
   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, SearchStack* ss);
+  void update_killers(Move m, Move killers[]);
   void update_gains(const Position& pos, Move move, Value before, Value after);
 
   int current_search_time();
@@ -364,15 +373,15 @@ void init_search() {
 /// 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.
 
-int perft(Position& pos, Depth depth)
+int64_t perft(Position& pos, Depth depth)
 {
     MoveStack mlist[MOVES_MAX];
     StateInfo st;
     Move m;
-    int sum = 0;
+    int64_t sum = 0;
 
     // Generate all legal moves
-    MoveStack* last = generate_moves(pos, mlist);
+    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.
@@ -424,7 +433,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
               wait_for_stop_or_ponderhit();
 
           cout << "bestmove " << bookMove << endl;
-          return true;
+          return !QuitRequest;
       }
   }
 
@@ -513,6 +522,9 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
               << move_to_san(pos, ponderMove) // Works also with MOVE_NONE
               << endl;
 
+      // Return from think() with unchanged position
+      pos.undo_move(bestMove);
+
       LogFile.close();
   }
 
@@ -674,13 +686,15 @@ namespace {
   Value root_search(Position& pos, SearchStack* ss, Value alpha,
                     Value beta, Depth depth, RootMoveList& rml) {
     StateInfo st;
+    Move movesSearched[MOVES_MAX];
     CheckInfo ci(pos);
     int64_t nodes;
     Move move;
     Depth ext, newDepth;
     Value value, oldAlpha;
-    bool isCheck, moveIsCheck, captureOrPromotion, dangerous;
-    int researchCountFH, researchCountFL;
+    RootMoveList::iterator rm;
+    bool isCheck, moveIsCheck, captureOrPromotion, dangerous, isPvMove;
+    int moveCount, researchCountFH, researchCountFL;
 
     researchCountFH = researchCountFL = 0;
     oldAlpha = alpha;
@@ -709,14 +723,15 @@ namespace {
     while (1)
     {
         // Sort the moves before to (re)search
-        rml.set_non_pv_scores(pos);
+        rml.set_non_pv_scores(pos, rml[0].pv[0], ss);
         rml.sort();
+        moveCount = 0;
 
         // Step 10. Loop through all moves in the root move list
-        for (int i = 0; i < (int)rml.size() && !StopRequest; i++)
+        for (rm = rml.begin(); rm != rml.end() && !StopRequest; ++rm)
         {
             // This is used by time management
-            FirstRootMove = (i == 0);
+            FirstRootMove = (rm == rml.begin());
 
             // Save the current node count before the move is searched
             nodes = pos.nodes_searched();
@@ -733,11 +748,13 @@ namespace {
 
             // Pick the next root move, and print the move and the move number to
             // the standard output.
-            move = ss->currentMove = rml[i].pv[0];
+            move = ss->currentMove = rm->pv[0];
+            movesSearched[moveCount++] = move;
+            isPvMove = (moveCount <= MultiPV);
 
             if (current_search_time() >= 1000)
                 cout << "info currmove " << move
-                     << " currmovenumber " << i + 1 << endl;
+                     << " currmovenumber " << moveCount << endl;
 
             moveIsCheck = pos.move_is_check(move);
             captureOrPromotion = pos.move_is_capture_or_promotion(move);
@@ -759,9 +776,8 @@ namespace {
                 pos.do_move(move, st, ci, moveIsCheck);
 
                 // Step extra. pv search
-                // We do pv search for first moves (i < MultiPV)
-                // and for fail high research (value > alpha)
-                if (i < MultiPV || value > alpha)
+                // We do pv search for PV moves and when failing high
+                if (isPvMove || value > alpha)
                 {
                     // Aspiration window is disabled in multi-pv case
                     if (MultiPV > 1)
@@ -781,7 +797,7 @@ namespace {
                         && !captureOrPromotion
                         && !move_is_castle(move))
                     {
-                        ss->reduction = reduction<PV>(depth, i - MultiPV + 2);
+                        ss->reduction = reduction<PV>(depth, moveCount - MultiPV + 1);
                         if (ss->reduction)
                         {
                             assert(newDepth-ss->reduction >= ONE_PLY);
@@ -816,11 +832,18 @@ namespace {
                 // We are failing high and going to do a research. It's important to update
                 // the score before research in case we run out of time while researching.
                 ss->bestMove = move;
-                rml[i].pv_score = value;
-                rml[i].extract_pv_from_tt(pos);
+                rm->pv_score = value;
+                rm->extract_pv_from_tt(pos);
+
+                // Update killers and history only for non capture moves that fails high
+                if (!pos.move_is_capture_or_promotion(move))
+                {
+                    update_history(pos, move, depth, movesSearched, moveCount);
+                    update_killers(move, ss->killers);
+                }
 
                 // Inform GUI that PV has changed
-                cout << rml[i].pv_info_to_uci(pos, alpha, beta) << endl;
+                cout << rm->pv_info_to_uci(pos, alpha, beta) << endl;
 
                 // Prepare for a research after a fail high, each time with a wider window
                 beta = Min(beta + AspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
@@ -837,32 +860,32 @@ namespace {
                 break;
 
             // Remember searched nodes counts for this move
-            rml[i].nodes += pos.nodes_searched() - nodes;
+            rm->nodes += pos.nodes_searched() - nodes;
 
             assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
             assert(value < beta);
 
             // Step 17. Check for new best move
-            if (value <= alpha && i >= MultiPV)
-                rml[i].pv_score = -VALUE_INFINITE;
+            if (!isPvMove && value <= alpha)
+                rm->pv_score = -VALUE_INFINITE;
             else
             {
                 // PV move or new best move!
 
                 // Update PV
                 ss->bestMove = move;
-                rml[i].pv_score = value;
-                rml[i].extract_pv_from_tt(pos);
+                rm->pv_score = value;
+                rm->extract_pv_from_tt(pos);
 
                 // We record how often the best move has been changed in each
                 // iteration. This information is used for time managment: When
                 // the best move changes frequently, we allocate some more time.
-                if (MultiPV == 1 && i > 0)
+                if (!isPvMove && MultiPV == 1)
                     BestMoveChangesByIteration[Iteration]++;
 
                 // Inform GUI that PV has changed, in case of multi-pv UCI protocol
                 // requires we send all the PV lines properly sorted.
-                rml.sort_multipv(i);
+                rml.sort_multipv(moveCount);
 
                 for (int j = 0; j < Min(MultiPV, (int)rml.size()); j++)
                     cout << rml[j].pv_info_to_uci(pos, alpha, beta, j) << endl;
@@ -875,7 +898,7 @@ namespace {
                         alpha = value;
                 }
                 else // Set alpha equal to minimum score among the PV lines
-                    alpha = rml[Min(i, MultiPV - 1)].pv_score;
+                    alpha = rml[Min(moveCount, MultiPV) - 1].pv_score; // FIXME why moveCount?
 
             } // PV move or new best move
 
@@ -903,7 +926,7 @@ namespace {
 
     // Write PV lines to transposition table, in case the relevant entries
     // have been overwritten during the search.
-    for (int i = 0; i < MultiPV; i++)
+    for (int i = 0; i < Min(MultiPV, (int)rml.size()); i++)
         rml[i].insert_pv_in_tt(pos);
 
     return alpha;
@@ -1384,7 +1407,7 @@ split_point_start: // At split points actual search starts from here
             && !pos.move_is_capture_or_promotion(move))
         {
             update_history(pos, move, depth, movesSearched, moveCount);
-            update_killers(move, ss);
+            update_killers(move, ss->killers);
         }
     }
 
@@ -1903,13 +1926,13 @@ 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, SearchStack* ss) {
+  void update_killers(Move m, Move killers[]) {
 
-    if (m == ss->killers[0])
+    if (m == killers[0])
         return;
 
-    ss->killers[1] = ss->killers[0];
-    ss->killers[0] = m;
+    killers[1] = killers[0];
+    killers[0] = m;
   }
 
 
@@ -1927,12 +1950,21 @@ 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.
+  // init_ss_array() does a fast reset of the first entries of a SearchStack
+  // array and of all the excludedMove and skipNullMove entries.
 
-  int current_search_time() {
+  void init_ss_array(SearchStack* ss, int size) {
 
-    return get_system_time() - SearchStartTime;
+    for (int i = 0; i < size; i++, ss++)
+    {
+        ss->excludedMove = MOVE_NONE;
+        ss->skipNullMove = false;
+        ss->reduction = DEPTH_ZERO;
+        ss->sp = NULL;
+
+        if (i < 3)
+            ss->killers[0] = ss->killers[1] = ss->mateKiller = MOVE_NONE;
+    }
   }
 
 
@@ -1955,7 +1987,17 @@ split_point_start: // At split points actual search starts from here
     return s.str();
   }
 
-  // nps() computes the current nodes/second count.
+
+  // 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;
+  }
+
+
+  // nps() computes the current nodes/second count
 
   int nps(const Position& pos) {
 
@@ -1973,15 +2015,8 @@ split_point_start: // At split points actual search starts from here
     static int lastInfoTime;
     int t = current_search_time();
 
-    bool stillAtFirstMove =    FirstRootMove
-                           && !AspirationFailLow
-                           &&  t > TimeMgr.available_time();
-
-    bool noMoreTime =   t > TimeMgr.maximum_time()
-                     || stillAtFirstMove;
-
     //  Poll for input
-    if (data_available())
+    if (input_available())
     {
         // We are line oriented, don't read single chars
         std::string command;
@@ -2010,8 +2045,7 @@ split_point_start: // At split points actual search starts from here
             // should continue searching but switching from pondering to normal search.
             Pondering = false;
 
-            if (   Iteration >= 3 && UseTimeManagement
-                && (noMoreTime || StopOnPonderhit))
+            if (StopOnPonderhit)
                 StopRequest = true;
         }
     }
@@ -2043,28 +2077,17 @@ split_point_start: // At split points actual search starts from here
     if (Pondering)
         return;
 
-    if (   (Iteration >= 3 && UseTimeManagement && noMoreTime)
-        || (ExactMaxTime && t >= ExactMaxTime)
-        || (Iteration >= 3 && MaxNodes && pos.nodes_searched() >= MaxNodes))
-        StopRequest = true;
-  }
-
-
-  // init_ss_array() does a fast reset of the first entries of a SearchStack
-  // array and of all the excludedMove and skipNullMove entries.
-
-  void init_ss_array(SearchStack* ss, int size) {
+    bool stillAtFirstMove =    FirstRootMove
+                           && !AspirationFailLow
+                           &&  t > TimeMgr.available_time();
 
-    for (int i = 0; i < size; i++, ss++)
-    {
-        ss->excludedMove = MOVE_NONE;
-        ss->skipNullMove = false;
-        ss->reduction = DEPTH_ZERO;
-        ss->sp = NULL;
+    bool noMoreTime =   t > TimeMgr.maximum_time()
+                     || stillAtFirstMove;
 
-        if (i < 3)
-            ss->killers[0] = ss->killers[1] = ss->mateKiller = MOVE_NONE;
-    }
+    if (   (UseTimeManagement && noMoreTime)
+        || (ExactMaxTime && t >= ExactMaxTime)
+        || (MaxNodes && pos.nodes_searched() >= MaxNodes)) // FIXME
+        StopRequest = true;
   }
 
 
@@ -2073,7 +2096,7 @@ split_point_start: // At split points actual search starts from here
   // the UCI protocol: When pondering, the engine is not allowed to give a
   // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
   // We simply wait here until one of these commands is sent, and return,
-  // after which the bestmove and pondermove will be printed (in id_loop()).
+  // after which the bestmove and pondermove will be printed.
 
   void wait_for_stop_or_ponderhit() {
 
@@ -2081,6 +2104,7 @@ split_point_start: // At split points actual search starts from here
 
     while (true)
     {
+        // Wait for a command from stdin
         if (!std::getline(std::cin, command))
             command = "quit";
 
@@ -2650,7 +2674,7 @@ split_point_start: // At split points actual search starts from here
     ss[0].eval = ss[0].evalMargin = VALUE_NONE;
 
     // Generate all legal moves
-    MoveStack* last = generate_moves(pos, mlist);
+    MoveStack* last = generate<MV_LEGAL>(pos, mlist);
 
     // Add each move to the RootMoveList's vector
     for (MoveStack* cur = mlist; cur != last; cur++)
@@ -2681,11 +2705,11 @@ split_point_start: // At split points actual search starts from here
   // This is the second order score that is used to compare the moves when
   // the first order pv scores of both moves are equal.
 
-  void RootMoveList::set_non_pv_scores(const Position& pos)
+  void RootMoveList::set_non_pv_scores(const Position& pos, Move ttm, SearchStack* ss)
   {
       Move move;
       Value score = VALUE_ZERO;
-      MovePicker mp(pos, MOVE_NONE, ONE_PLY, H);
+      MovePicker mp(pos, ttm, ONE_PLY, H, ss);
 
       while ((move = mp.get_next_move()) != MOVE_NONE)
           for (Base::iterator it = begin(); it != end(); ++it)