]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Optimize generate_evasions()
[stockfish] / src / search.cpp
index ec4272a9e0f9bcb7ebf601515621e34be4e632ba..c323de5d48f35a62d00e56552449c1255f946f4e 100644 (file)
@@ -288,7 +288,6 @@ namespace {
   bool ok_to_do_nullmove(const Position& pos);
   bool ok_to_prune(const Position& pos, Move m, Move threat, Depth d);
   bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
-  bool ok_to_history(const Position& pos, Move m);
   void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount);
   void update_killers(Move m, SearchStack& ss);
 
@@ -327,6 +326,37 @@ namespace {
 //// Functions
 ////
 
+
+/// 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)
+{
+    Move move;
+    MovePicker mp = MovePicker(pos, MOVE_NONE, depth, H);
+    Bitboard dcCandidates = mp.discovered_check_candidates();
+    int sum = 0;
+
+    // If we are at the last ply we don't need to do and undo
+    // the moves, just to count them.
+    if (depth <= OnePly) // Replace with '<' to test also qsearch
+    {
+        while ((move = mp.get_next_move()) != MOVE_NONE) sum++;
+        return sum;
+    }
+
+    // Loop through all legal moves
+    while ((move = mp.get_next_move()) != MOVE_NONE)
+    {
+      StateInfo st;
+      pos.do_move(move, st, dcCandidates);
+      sum += perft(pos, depth - OnePly);
+      pos.undo_move(move);
+    }
+    return sum;
+}
+
+
 /// think() is the external interface to Stockfish's search, and is called when
 /// the program receives the UCI 'go' command. It initializes various
 /// search-related global variables, and calls root_search(). It returns false
@@ -448,7 +478,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
       if (movesToGo == 1)
       {
           MaxSearchTime = myTime / 2;
-          AbsoluteMaxSearchTime = 
+          AbsoluteMaxSearchTime =
              (myTime > 3000)? (myTime - 500) : ((myTime * 3) / 4);
       } else {
           MaxSearchTime = myTime / Min(movesToGo, 20);
@@ -868,9 +898,9 @@ namespace {
                       << " currmovenumber " << i + 1 << std::endl;
 
         // Decide search depth for this move
-        bool moveIsCapture = pos.move_is_capture(move);
+        bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
         bool dangerous;
-        ext = extension(pos, move, true, moveIsCapture, pos.move_is_check(move), false, false, &dangerous);
+        ext = extension(pos, move, true, captureOrPromotion, pos.move_is_check(move), false, false, &dangerous);
         newDepth = (Iteration - 2) * OnePly + ext + InitialDepth;
 
         // Make the move, and search it
@@ -897,8 +927,7 @@ namespace {
             if (   newDepth >= 3*OnePly
                 && i >= MultiPV + LMRPVMoves
                 && !dangerous
-                && !moveIsCapture
-                && !move_is_promotion(move)
+                && !captureOrPromotion
                 && !move_is_castle(move))
             {
                 ss[0].reduction = OnePly;
@@ -978,7 +1007,7 @@ namespace {
                 std::cout << std::endl;
 
                 if (UseLogFile)
-                    LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value, 
+                    LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value,
                                          ((value >= beta)? VALUE_TYPE_LOWER
                                           : ((value <= alpha)? VALUE_TYPE_UPPER : VALUE_TYPE_EXACT)),
                                          ss[0].pv)
@@ -1093,13 +1122,13 @@ namespace {
 
       bool singleReply = (isCheck && mp.number_of_evasions() == 1);
       bool moveIsCheck = pos.move_is_check(move, dcCandidates);
-      bool moveIsCapture = pos.move_is_capture(move);
+      bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       movesSearched[moveCount++] = ss[ply].currentMove = move;
 
       // Decide the new search depth
       bool dangerous;
-      Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous);
+      Depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous);
       Depth newDepth = depth - OnePly + ext;
 
       // Make and search the move
@@ -1115,8 +1144,7 @@ namespace {
         if (    depth >= 3*OnePly
             &&  moveCount >= LMRPVMoves
             && !dangerous
-            && !moveIsCapture
-            && !move_is_promotion(move)
+            && !captureOrPromotion
             && !move_is_castle(move)
             && !move_is_killer(move, ss[ply]))
         {
@@ -1200,7 +1228,7 @@ namespace {
     {
         BetaCounter.add(pos.side_to_move(), depth, threadID);
         Move m = ss[ply].pv[ply];
-        if (ok_to_history(pos, m)) // Only non capture moves are considered
+        if (!pos.move_is_capture_or_promotion(m))
         {
             update_history(pos, m, depth, movesSearched, moveCount);
             update_killers(m, ss[ply]);
@@ -1354,20 +1382,19 @@ namespace {
 
       bool singleReply = (isCheck && mp.number_of_evasions() == 1);
       bool moveIsCheck = pos.move_is_check(move, dcCandidates);
-      bool moveIsCapture = pos.move_is_capture(move);
+      bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       movesSearched[moveCount++] = ss[ply].currentMove = move;
 
       // Decide the new search depth
       bool dangerous;
-      Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous);
+      Depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous);
       Depth newDepth = depth - OnePly + ext;
 
       // Futility pruning
       if (    useFutilityPruning
           && !dangerous
-          && !moveIsCapture
-          && !move_is_promotion(move))
+          && !captureOrPromotion)
       {
           // History pruning. See ok_to_prune() definition
           if (   moveCount >= 2 + int(depth)
@@ -1400,8 +1427,7 @@ namespace {
       if (    depth >= 3*OnePly
           &&  moveCount >= LMRNonPVMoves
           && !dangerous
-          && !moveIsCapture
-          && !move_is_promotion(move)
+          && !captureOrPromotion
           && !move_is_castle(move)
           && !move_is_killer(move, ss[ply]))
       {
@@ -1460,7 +1486,7 @@ namespace {
     {
         BetaCounter.add(pos.side_to_move(), depth, threadID);
         Move m = ss[ply].pv[ply];
-        if (ok_to_history(pos, m)) // Only non capture moves are considered
+        if (!pos.move_is_capture_or_promotion(m))
         {
             update_history(pos, m, depth, movesSearched, moveCount);
             update_killers(m, ss[ply]);
@@ -1596,6 +1622,7 @@ namespace {
 
       // Don't search captures and checks with negative SEE values
       if (   !isCheck
+          &&  move != ttMove
           && !move_is_promotion(move)
           &&  pos.see_sign(move) < 0)
           continue;
@@ -1622,7 +1649,7 @@ namespace {
 
     // All legal moves have been searched.  A special case: If we're in check
     // and no legal moves were found, it is checkmate.
-    if (pos.is_check() && moveCount == 0) // Mate!
+    if (!moveCount && pos.is_check()) // Mate!
         return value_mated_in(ply);
 
     assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1643,7 +1670,7 @@ namespace {
     }
 
     // Update killers only for good check moves
-    if (alpha >= beta && ok_to_history(pos, m)) // Only non capture moves are considered
+    if (alpha >= beta && !pos.move_is_capture_or_promotion(m))
         update_killers(m, ss[ply]);
 
     return bestValue;
@@ -1678,7 +1705,7 @@ namespace {
       assert(move_is_ok(move));
 
       bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
-      bool moveIsCapture = pos.move_is_capture(move);
+      bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       lock_grab(&(sp->lock));
       int moveCount = ++sp->moves;
@@ -1688,14 +1715,13 @@ namespace {
 
       // Decide the new search depth.
       bool dangerous;
-      Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, false, false, &dangerous);
+      Depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, false, &dangerous);
       Depth newDepth = sp->depth - OnePly + ext;
 
       // Prune?
       if (    useFutilityPruning
           && !dangerous
-          && !moveIsCapture
-          && !move_is_promotion(move))
+          && !captureOrPromotion)
       {
           // History pruning. See ok_to_prune() definition
           if (   moveCount >= 2 + int(sp->depth)
@@ -1735,8 +1761,7 @@ namespace {
       // if the move fails high will be re-searched at full depth.
       if (   !dangerous
           &&  moveCount >= LMRNonPVMoves
-          && !moveIsCapture
-          && !move_is_promotion(move)
+          && !captureOrPromotion
           && !move_is_castle(move)
           && !move_is_killer(move, ss[sp->ply]))
       {
@@ -1818,7 +1843,7 @@ namespace {
            && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE)
     {
       bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
-      bool moveIsCapture = pos.move_is_capture(move);
+      bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
 
       assert(move_is_ok(move));
 
@@ -1830,7 +1855,7 @@ namespace {
 
       // Decide the new search depth.
       bool dangerous;
-      Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, false, false, &dangerous);
+      Depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous);
       Depth newDepth = sp->depth - OnePly + ext;
 
       // Make and search the move.
@@ -1841,8 +1866,7 @@ namespace {
       // if the move fails high will be re-searched at full depth.
       if (   !dangerous
           &&  moveCount >= LMRPVMoves
-          && !moveIsCapture
-          && !move_is_promotion(move)
+          && !captureOrPromotion
           && !move_is_castle(move)
           && !move_is_killer(move, ss[sp->ply]))
       {
@@ -2267,8 +2291,8 @@ namespace {
   // extended, as example because the corresponding UCI option is set to zero,
   // the move is marked as 'dangerous' so, at least, we avoid to prune it.
 
-  Depth extension(const Position& pos, Move m, bool pvNode, bool capture, bool check,
-                  bool singleReply, bool mateThreat, bool* dangerous) {
+  Depth extension(const Position& pos, Move m, bool pvNode, bool captureOrPromotion,
+                  bool check, bool singleReply, bool mateThreat, bool* dangerous) {
 
     assert(m != MOVE_NONE);
 
@@ -2302,7 +2326,7 @@ namespace {
         }
     }
 
-    if (   capture
+    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(0))
@@ -2314,7 +2338,7 @@ namespace {
     }
 
     if (   pvNode
-        && capture
+        && captureOrPromotion
         && pos.type_of_piece_on(move_to(m)) != PAWN
         && pos.see_sign(m) >= 0)
     {
@@ -2348,9 +2372,8 @@ namespace {
 
     assert(move_is_ok(m));
     assert(threat == MOVE_NONE || move_is_ok(threat));
-    assert(!move_is_promotion(m));
     assert(!pos.move_is_check(m));
-    assert(!pos.move_is_capture(m));
+    assert(!pos.move_is_capture_or_promotion(m));
     assert(!pos.move_is_passed_pawn_push(m));
     assert(d >= OnePly);
 
@@ -2412,15 +2435,6 @@ namespace {
   }
 
 
-  // ok_to_history() returns true if a move m can be stored
-  // in history. Should be a non capturing move nor a promotion.
-
-  bool ok_to_history(const Position& pos, Move m) {
-
-    return !pos.move_is_capture(m) && !move_is_promotion(m);
-  }
-
-
   // update_history() registers a good move that produced a beta-cutoff
   // in history and marks as failures all the other moves of that ply.
 
@@ -2432,7 +2446,7 @@ namespace {
     for (int i = 0; i < moveCount - 1; i++)
     {
         assert(m != movesSearched[i]);
-        if (ok_to_history(pos, movesSearched[i]))
+        if (!pos.move_is_capture_or_promotion(movesSearched[i]))
             H.failure(pos.piece_on(move_from(movesSearched[i])), move_to(movesSearched[i]));
     }
   }