]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Disable fake-mode
[stockfish] / src / search.cpp
index 22d5d647447d520accc4c23ec785766e6abfde0b..e47657fdcca35d6392c0e5d311e7c738c3246c90 100644 (file)
@@ -83,6 +83,8 @@ namespace {
     void wake_sleeping_threads();
     void put_threads_to_sleep();
     void idle_loop(int threadID, SplitPoint* sp);
+
+    template <bool Fake>
     bool split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
                Depth depth, bool mateThreat, int* moves, MovePicker* mp, int master, bool pvNode);
 
@@ -282,11 +284,14 @@ namespace {
   Value search(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, bool allowNullmove, int threadID,  Move excludedMove = MOVE_NONE);
 
   template <NodeType PvNode>
-  Depth extension(const Position& pos, Move m, bool captureOrPromotion, bool moveIsCheck, bool singleEvasion, bool mateThreat, bool* dangerous);
-
   Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID);
+
+  template <NodeType PvNode>
   void sp_search(SplitPoint* sp, int threadID);
-  void sp_search_pv(SplitPoint* sp, int threadID);
+
+  template <NodeType PvNode>
+  Depth extension(const Position& pos, Move m, bool captureOrPromotion, bool moveIsCheck, bool singleEvasion, bool mateThreat, bool* dangerous);
+
   void init_node(SearchStack ss[], int ply, int threadID);
   void update_pv(SearchStack ss[], int ply);
   void sp_update_pv(SearchStack* pss, SearchStack ss[], int ply);
@@ -1030,6 +1035,7 @@ namespace {
 
     assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
     assert(beta > alpha && beta <= VALUE_INFINITE);
+    assert(PvNode || alpha == beta - 1);
     assert(ply >= 0 && ply < PLY_MAX);
     assert(threadID >= 0 && threadID < TM.active_threads());
 
@@ -1048,7 +1054,7 @@ namespace {
     oldAlpha = alpha;
 
     if (depth < OnePly)
-        return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID);
+        return qsearch<PvNode>(pos, ss, alpha, beta, Depth(0), ply, threadID);
 
     // Step 1. Initialize node and poll
     // Polling can abort search.
@@ -1118,7 +1124,7 @@ namespace {
         && !pos.has_pawn_on_7th(pos.side_to_move()))
     {
         Value rbeta = beta - razor_margin(depth);
-        Value v = qsearch(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID);
+        Value v = qsearch<NonPV>(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID);
         if (v < rbeta)
             // Logically we should return (v + razor_margin(depth)), but
             // surprisingly this did slightly weaker in tests.
@@ -1339,7 +1345,9 @@ namespace {
           bestValue = value;
           if (value > alpha)
           {
-              alpha = value;
+              if (PvNode && value < beta) // This guarantees that always: alpha < beta
+                  alpha = value;
+
               update_pv(ss, ply);
               if (value == value_mate_in(ply + 1))
                   ss[ply].mateKiller = move;
@@ -1354,9 +1362,21 @@ namespace {
           && TM.available_thread_exists(threadID)
           && !AbortSearch
           && !TM.thread_should_stop(threadID)
-          && TM.split(pos, ss, ply, &alpha, beta, &bestValue,
+          && TM.split<false>(pos, ss, ply, &alpha, beta, &bestValue,
+                      depth, mateThreat, &moveCount, &mp, threadID, PvNode))
+          break;
+
+      // Uncomment to debug sp_search() in single thread mode
+      /*
+      if (   bestValue < beta
+          && depth >= 4
+          && Iteration <= 99
+          && !AbortSearch
+          && !TM.thread_should_stop(threadID)
+          && TM.split<true>(pos, ss, ply, &alpha, beta, &bestValue,
                       depth, mateThreat, &moveCount, &mp, threadID, PvNode))
           break;
+      */
     }
 
     // Step 19. Check for mate and stalemate
@@ -1399,11 +1419,13 @@ namespace {
   // search function when the remaining depth is zero (or, to be more precise,
   // less than OnePly).
 
+  template <NodeType PvNode>
   Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta,
                 Depth depth, int ply, int threadID) {
 
     assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
     assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
+    assert(PvNode || alpha == beta - 1);
     assert(depth <= 0);
     assert(ply >= 0 && ply < PLY_MAX);
     assert(threadID >= 0 && threadID < TM.active_threads());
@@ -1415,7 +1437,6 @@ namespace {
     bool isCheck, enoughMaterial, moveIsCheck, evasionPrunable;
     const TTEntry* tte = NULL;
     int moveCount = 0;
-    bool pvNode = (beta - alpha != 1);
     Value oldAlpha = alpha;
 
     // Initialize, and make an early exit in case of an aborted search,
@@ -1434,7 +1455,7 @@ namespace {
     tte = TT.retrieve(pos.get_key());
     ttMove = (tte ? tte->move() : MOVE_NONE);
 
-    if (!pvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
+    if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
     {
         assert(tte->type() != VALUE_TYPE_EVAL);
 
@@ -1499,9 +1520,9 @@ namespace {
       ss[ply].currentMove = move;
 
       // Futility pruning
-      if (   enoughMaterial
+      if (   !PvNode
+          &&  enoughMaterial
           && !isCheck
-          && !pvNode
           && !moveIsCheck
           &&  move != ttMove
           && !move_is_promotion(move)
@@ -1527,8 +1548,8 @@ namespace {
                        && !pos.can_castle(pos.side_to_move());
 
       // Don't search moves with negative SEE values
-      if (   (!isCheck || evasionPrunable)
-          && !pvNode
+      if (   !PvNode
+          && (!isCheck || evasionPrunable)
           &&  move != ttMove
           && !move_is_promotion(move)
           &&  pos.see_sign(move) < 0)
@@ -1536,7 +1557,7 @@ namespace {
 
       // Make and search the move
       pos.do_move(move, st, ci, moveIsCheck);
-      value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID);
+      value = -qsearch<PvNode>(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID);
       pos.undo_move(move);
 
       assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
@@ -1593,15 +1614,16 @@ namespace {
   // also don't need to store anything to the hash table here:  This is taken
   // care of after we return from the split point.
 
+  template <NodeType PvNode>
   void sp_search(SplitPoint* sp, int threadID) {
 
     assert(threadID >= 0 && threadID < TM.active_threads());
-    assert(TM.active_threads() > 1);
 
     StateInfo st;
     Move move;
     Depth ext, newDepth;
-    Value value, futilityValueScaled;
+    Value value;
+    Value futilityValueScaled; // NonPV specific
     bool isCheck, moveIsCheck, captureOrPromotion, dangerous;
     int moveCount;
     value = -VALUE_INFINITE;
@@ -1628,14 +1650,15 @@ namespace {
       captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       // Step 11. Decide the new search depth
-      ext = extension<NonPV>(pos, move, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous);
+      ext = extension<PvNode>(pos, move, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous);
       newDepth = sp->depth - OnePly + ext;
 
       // Update current move
       ss[sp->ply].currentMove = move;
 
-      // Step 12. Futility pruning
-      if (   !isCheck
+      // Step 12. Futility pruning (is omitted in PV nodes)
+      if (   !PvNode
+          && !isCheck
           && !dangerous
           && !captureOrPromotion
           && !move_is_castle(move))
@@ -1676,112 +1699,7 @@ namespace {
           && !move_is_castle(move)
           && !move_is_killer(move, ss[sp->ply]))
       {
-          ss[sp->ply].reduction = reduction<NonPV>(sp->depth, moveCount);
-          if (ss[sp->ply].reduction)
-          {
-              value = -search<NonPV>(pos, ss, -(sp->alpha+1), -(sp->alpha), newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID);
-              doFullDepthSearch = (value >= sp->beta && !TM.thread_should_stop(threadID));
-          }
-      }
-
-      // Step 15. Full depth search
-      if (doFullDepthSearch)
-      {
-          ss[sp->ply].reduction = Depth(0);
-          value = -search<NonPV>(pos, ss, -(sp->alpha+1), -(sp->alpha), newDepth, sp->ply+1, true, threadID);
-      }
-
-      // Step 16. Undo move
-      pos.undo_move(move);
-
-      assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
-
-      // Step 17. Check for new best move
-      lock_grab(&(sp->lock));
-
-      if (value > sp->bestValue && !TM.thread_should_stop(threadID))
-      {
-          sp->bestValue = value;
-          if (sp->bestValue >= sp->beta)
-          {
-              sp->stopRequest = true;
-              sp_update_pv(sp->parentSstack, ss, sp->ply);
-          }
-      }
-    }
-
-    /* Here we have the lock still grabbed */
-
-    sp->slaves[threadID] = 0;
-    sp->cpus--;
-
-    lock_release(&(sp->lock));
-  }
-
-
-  // sp_search_pv() is used to search from a PV split point.  This function
-  // is called by each thread working at the split point.  It is similar to
-  // the normal search_pv() function, but simpler.  Because we have already
-  // probed the hash table and searched the first move before splitting, we
-  // don't have to repeat all this work in sp_search_pv().  We also don't
-  // need to store anything to the hash table here: This is taken care of
-  // after we return from the split point.
-
-  void sp_search_pv(SplitPoint* sp, int threadID) {
-
-    assert(threadID >= 0 && threadID < TM.active_threads());
-    assert(TM.active_threads() > 1);
-
-    StateInfo st;
-    Move move;
-    Depth ext, newDepth;
-    Value value;
-    bool moveIsCheck, captureOrPromotion, dangerous;
-    int moveCount;
-    value = -VALUE_INFINITE;
-
-    Position pos(*sp->pos);
-    CheckInfo ci(pos);
-    SearchStack* ss = sp->sstack[threadID];
-
-    // Step 10. Loop through moves
-    // Loop through all legal moves until no moves remain or a beta cutoff occurs
-    lock_grab(&(sp->lock));
-
-    while (    sp->alpha < sp->beta
-           && !TM.thread_should_stop(threadID)
-           && (move = sp->mp->get_next_move()) != MOVE_NONE)
-    {
-      moveCount = ++sp->moves;
-      lock_release(&(sp->lock));
-
-      assert(move_is_ok(move));
-
-      moveIsCheck = pos.move_is_check(move, ci);
-      captureOrPromotion = pos.move_is_capture_or_promotion(move);
-
-      // Step 11. Decide the new search depth
-      ext = extension<PV>(pos, move, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous);
-      newDepth = sp->depth - OnePly + ext;
-
-      // Update current move
-      ss[sp->ply].currentMove = move;
-
-      // Step 12. Futility pruning (is omitted in PV nodes)
-
-      // Step 13. Make the move
-      pos.do_move(move, st, ci, moveIsCheck);
-
-      // Step 14. Reduced search
-      // if the move fails high will be re-searched at full depth.
-      bool doFullDepthSearch = true;
-
-      if (   !dangerous
-          && !captureOrPromotion
-          && !move_is_castle(move)
-          && !move_is_killer(move, ss[sp->ply]))
-      {
-          ss[sp->ply].reduction = reduction<PV>(sp->depth, moveCount);
+          ss[sp->ply].reduction = reduction<PvNode>(sp->depth, moveCount);
           if (ss[sp->ply].reduction)
           {
               Value localAlpha = sp->alpha;
@@ -1793,18 +1711,12 @@ namespace {
       // Step 15. Full depth search
       if (doFullDepthSearch)
       {
-          Value localAlpha = sp->alpha;
           ss[sp->ply].reduction = Depth(0);
+          Value localAlpha = sp->alpha;
           value = -search<NonPV>(pos, ss, -(localAlpha+1), -localAlpha, newDepth, sp->ply+1, true, threadID);
 
-          if (value > localAlpha && value < sp->beta && !TM.thread_should_stop(threadID))
-          {
-              // If another thread has failed high then sp->alpha has been increased
-              // to be higher or equal then beta, if so, avoid to start a PV search.
-              localAlpha = sp->alpha;
-              if (localAlpha < sp->beta)
-                  value = -search<PV>(pos, ss, -sp->beta, -localAlpha, newDepth, sp->ply+1, false, threadID);
-          }
+          if (PvNode && value > localAlpha && value < sp->beta && !TM.thread_should_stop(threadID))
+              value = -search<PV>(pos, ss, -sp->beta, -sp->alpha, newDepth, sp->ply+1, false, threadID);
       }
 
       // Step 16. Undo move
@@ -1818,16 +1730,18 @@ namespace {
       if (value > sp->bestValue && !TM.thread_should_stop(threadID))
       {
           sp->bestValue = value;
-          if (value > sp->alpha)
+
+          if (sp->bestValue > sp->alpha)
           {
-              // Ask threads to stop before to modify sp->alpha
-              if (value >= sp->beta)
+              if (!PvNode || value >= sp->beta)
                   sp->stopRequest = true;
 
-              sp->alpha = value;
+              if (PvNode && value < sp->beta) // This guarantees that always: sp->alpha < sp->beta
+                  sp->alpha = value;
 
               sp_update_pv(sp->parentSstack, ss, sp->ply);
-              if (value == value_mate_in(sp->ply + 1))
+
+              if (PvNode && value == value_mate_in(sp->ply + 1))
                   ss[sp->ply].mateKiller = move;
           }
       }
@@ -1841,7 +1755,6 @@ namespace {
     lock_release(&(sp->lock));
   }
 
-
   // init_node() is called at the beginning of all the search functions
   // (search() qsearch(), and so on) and initializes the
   // search stack object corresponding to the current node. Once every
@@ -1866,9 +1779,10 @@ namespace {
     }
     ss[ply].init(ply);
     ss[ply + 2].initKillers();
-  }
 
 
+  }
+
   // update_pv() is called whenever a search returns a value > alpha.
   // It updates the PV in the SearchStack object corresponding to the
   // current node.
@@ -2495,9 +2409,9 @@ namespace {
             threads[threadID].state = THREAD_SEARCHING;
 
             if (threads[threadID].splitPoint->pvNode)
-                sp_search_pv(threads[threadID].splitPoint, threadID);
+                sp_search<PV>(threads[threadID].splitPoint, threadID);
             else
-                sp_search(threads[threadID].splitPoint, threadID);
+                sp_search<NonPV>(threads[threadID].splitPoint, threadID);
 
             assert(threads[threadID].state == THREAD_SEARCHING);
 
@@ -2695,6 +2609,7 @@ namespace {
   // threads have returned from sp_search_pv (or, equivalently, when
   // splitPoint->cpus becomes 0), split() returns true.
 
+  template <bool Fake>
   bool ThreadsManager::split(const Position& p, SearchStack* sstck, int ply,
              Value* alpha, const Value beta, Value* bestValue,
              Depth depth, bool mateThreat, int* moves, MovePicker* mp, int master, bool pvNode) {
@@ -2703,13 +2618,12 @@ namespace {
     assert(sstck != NULL);
     assert(ply >= 0 && ply < PLY_MAX);
     assert(*bestValue >= -VALUE_INFINITE);
-    assert(   ( pvNode && *bestValue <= *alpha)
-           || (!pvNode && *bestValue <   beta ));
-    assert(!pvNode || *alpha < beta);
+    assert(*bestValue <= *alpha);
+    assert(*alpha < beta);
     assert(beta <= VALUE_INFINITE);
     assert(depth > Depth(0));
     assert(master >= 0 && master < ActiveThreads);
-    assert(ActiveThreads > 1);
+    assert(Fake || ActiveThreads > 1);
 
     SplitPoint* splitPoint;
 
@@ -2717,7 +2631,7 @@ namespace {
 
     // If no other thread is available to help us, or if we have too many
     // active split points, don't split.
-    if (   !available_thread_exists(master)
+    if (   (!Fake && !available_thread_exists(master))
         || threads[master].activeSplitPoints >= ACTIVE_SPLIT_POINTS_MAX)
     {
         lock_release(&MPLock);
@@ -2754,7 +2668,7 @@ namespace {
 
     // Allocate available threads setting state to THREAD_BOOKED
     for (int i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint; i++)
-        if (thread_is_available(i, master))
+        if (!Fake && thread_is_available(i, master))
         {
             threads[i].state = THREAD_BOOKED;
             threads[i].splitPoint = splitPoint;
@@ -2762,7 +2676,7 @@ namespace {
             splitPoint->cpus++;
         }
 
-    assert(splitPoint->cpus > 1);
+    assert(Fake || splitPoint->cpus > 1);
 
     // We can release the lock because slave threads are already booked and master is not available
     lock_release(&MPLock);
@@ -2867,7 +2781,7 @@ namespace {
         init_ss_array(ss);
         pos.do_move(cur->move, st);
         moves[count].move = cur->move;
-        moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0);
+        moves[count].score = -qsearch<PV>(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0);
         moves[count].pv[0] = cur->move;
         moves[count].pv[1] = MOVE_NONE;
         pos.undo_move(cur->move);