X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=45e91e591ad768e213d7b003dd7dad5f4ffc3137;hp=6173a9a4b410d98efb1c9ca3f7fc5646fa8db3b5;hb=62b43130e27e4322c5edde976d0827840b9deee4;hpb=5b2fc1e1c06d8348efb4bf45bac9c8f4916fd01c diff --git a/src/search.cpp b/src/search.cpp index 6173a9a4..45e91e59 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -32,7 +32,6 @@ #include "book.h" #include "evaluate.h" #include "history.h" -#include "maxgain.h" #include "misc.h" #include "movegen.h" #include "movepick.h" @@ -264,9 +263,6 @@ namespace { // History table History H; - // MaxGain table - MaxGain MG; - /// Functions Value id_loop(const Position& pos, Move searchMoves[]); @@ -289,6 +285,7 @@ namespace { 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_gains(const Position& pos, Move move, Value before, Value after); bool fail_high_ply_1(); int current_search_time(); @@ -703,7 +700,6 @@ namespace { // Initialize TT.new_search(); H.clear(); - MG.clear(); init_ss_array(ss); IterationInfo[1] = IterationInfoType(rml.get_move_score(0), rml.get_move_score(0)); Iteration = 1; @@ -1165,21 +1161,14 @@ namespace { tte = TT.retrieve(pos.get_key()); } - // Evaluate the position statically isCheck = pos.is_check(); - EvalInfo ei; if (!isCheck) { + // Update gain statistics of the previous move that lead + // us in this position. + EvalInfo ei; ss[ply].eval = evaluate(pos, ei, threadID); - - // Store gain statistics - Move m = ss[ply - 1].currentMove; - if ( m != MOVE_NULL - && pos.captured_piece() == NO_PIECE_TYPE - && !move_is_castle(m) - && !move_is_promotion(m)) - MG.store(pos.piece_on(move_to(m)), move_from(m), move_to(m), ss[ply - 1].eval, -ss[ply].eval); - + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); } // Initialize a MovePicker object for the current position, and prepare @@ -1359,7 +1348,7 @@ namespace { Move ttMove, move; Depth ext, newDepth; Value bestValue, staticValue, nullValue, value, futilityValue, futilityValueScaled; - bool isCheck, useFutilityPruning, singleEvasion, moveIsCheck, captureOrPromotion, dangerous; + bool isCheck, singleEvasion, moveIsCheck, captureOrPromotion, dangerous; bool mateThreat = false; int moveCount = 0; futilityValue = staticValue = bestValue = value = -VALUE_INFINITE; @@ -1419,19 +1408,17 @@ namespace { ss[ply].eval = staticValue; futilityValue = staticValue + PostFutilityValueMargin; //FIXME: Remove me, only for split staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible - - // Store gain statistics - Move m = ss[ply - 1].currentMove; - if ( m != MOVE_NULL - && pos.captured_piece() == NO_PIECE_TYPE - && !move_is_castle(m) - && !move_is_promotion(m)) - MG.store(pos.piece_on(move_to(m)), move_from(m), move_to(m), ss[ply - 1].eval, -ss[ply].eval); + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); } - // Post futility pruning - if (staticValue - PostFutilityValueMargin >= beta) - return (staticValue - PostFutilityValueMargin); + // Do a "stand pat". If we are above beta by a good margin then + // return immediately. + // FIXME: test with added condition 'allowNullmove || depth <= OnePly' and !value_is_mate(beta) + // FIXME: test with modified condition 'depth < RazorDepth' + if ( !isCheck + && depth < SelectiveDepth + && staticValue - PostFutilityValueMargin >= beta) + return staticValue - PostFutilityValueMargin; // Null move search if ( allowNullmove @@ -1510,7 +1497,6 @@ namespace { // to search all moves. MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]); CheckInfo ci(pos); - useFutilityPruning = depth < SelectiveDepth && !isCheck; // Loop through all legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta @@ -1556,8 +1542,38 @@ namespace { // Update current move movesSearched[moveCount++] = ss[ply].currentMove = move; + // Futility pruning for captures + // FIXME: test disabling 'Futility pruning for captures' + // FIXME: test with 'newDepth < RazorDepth' + Color them = opposite_color(pos.side_to_move()); + + if ( !isCheck + && newDepth < SelectiveDepth + && !dangerous + && pos.move_is_capture(move) + && !pos.move_is_check(move, ci) + && !move_is_promotion(move) + && move != ttMove + && !move_is_ep(move) + && (pos.type_of_piece_on(move_to(move)) != PAWN || !pos.pawn_is_passed(them, move_to(move)))) // Do not prune passed pawn captures + { + int preFutilityValueMargin = 0; + + if (newDepth >= OnePly) + preFutilityValueMargin = 112 * bitScanReverse32(int(newDepth) * int(newDepth) / 2); + + Value futilityCaptureValue = ss[ply].eval + pos.endgame_value_of_piece_on(move_to(move)) + preFutilityValueMargin + ei.futilityMargin + 90; + + if (futilityCaptureValue < beta) + { + if (futilityCaptureValue > bestValue) + bestValue = futilityCaptureValue; + continue; + } + } + // Futility pruning - if ( useFutilityPruning + if ( !isCheck && !dangerous && !captureOrPromotion && !move_is_castle(move) @@ -1577,19 +1593,22 @@ namespace { if (red >= 1.0) predictedDepth -= int(floor(red * int(OnePly))); - int preFutilityValueMargin = 0; - if (predictedDepth >= OnePly) - preFutilityValueMargin = 112 * bitScanReverse32(int(predictedDepth) * int(predictedDepth) / 2); + if (predictedDepth < SelectiveDepth) + { + int preFutilityValueMargin = 0; + if (predictedDepth >= OnePly) + preFutilityValueMargin = 112 * bitScanReverse32(int(predictedDepth) * int(predictedDepth) / 2); - preFutilityValueMargin += MG.retrieve(pos.piece_on(move_from(move)), move_from(move), move_to(move)) + 45; + preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_from(move), move_to(move)) + 45; - futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin; + futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin; - if (futilityValueScaled < beta) - { - if (futilityValueScaled > bestValue) - bestValue = futilityValueScaled; - continue; + if (futilityValueScaled < beta) + { + if (futilityValueScaled > bestValue) + bestValue = futilityValueScaled; + continue; + } } } @@ -1739,16 +1758,9 @@ namespace { if (!isCheck) { ss[ply].eval = staticValue; - // Store gain statistics - Move m = ss[ply - 1].currentMove; - if ( m != MOVE_NULL - && pos.captured_piece() == NO_PIECE_TYPE - && !move_is_castle(m) - && !move_is_promotion(m)) - MG.store(pos.piece_on(move_to(m)), move_from(m), move_to(m), ss[ply - 1].eval, -ss[ply].eval); + update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); } - // Initialize "stand pat score", and return it immediately if it is // at least beta. bestValue = staticValue; @@ -1892,28 +1904,29 @@ namespace { SearchStack* ss = sp->sstack[threadID]; Value value = -VALUE_INFINITE; Move move; + int moveCount; bool isCheck = pos.is_check(); bool useFutilityPruning = sp->depth < SelectiveDepth && !isCheck; const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8)); - while ( sp->bestValue < sp->beta + while ( lock_grab_bool(&(sp->lock)) + && sp->bestValue < sp->beta && !thread_should_stop(threadID) - && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) + && (move = sp->mp->get_next_move()) != MOVE_NONE) { + moveCount = ++sp->moves; + lock_release(&(sp->lock)); + assert(move_is_ok(move)); bool moveIsCheck = pos.move_is_check(move, ci); bool captureOrPromotion = pos.move_is_capture_or_promotion(move); - lock_grab(&(sp->lock)); - int moveCount = ++sp->moves; - lock_release(&(sp->lock)); - ss[sp->ply].currentMove = move; - // Decide the new search depth. + // Decide the new search depth bool dangerous; Depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; @@ -1977,7 +1990,10 @@ namespace { assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); if (thread_should_stop(threadID)) + { + lock_grab(&(sp->lock)); break; + } // New best move? if (value > sp->bestValue) // Less then 2% of cases @@ -2000,7 +2016,7 @@ namespace { } } - lock_grab(&(sp->lock)); + /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of // a beta cutoff higher up in the tree, stop all slave threads. @@ -2033,24 +2049,25 @@ namespace { CheckInfo ci(pos); SearchStack* ss = sp->sstack[threadID]; Value value = -VALUE_INFINITE; + int moveCount; Move move; - while ( sp->alpha < sp->beta + while ( lock_grab_bool(&(sp->lock)) + && sp->alpha < sp->beta && !thread_should_stop(threadID) - && (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE) + && (move = sp->mp->get_next_move()) != MOVE_NONE) { - bool moveIsCheck = pos.move_is_check(move, ci); - bool captureOrPromotion = pos.move_is_capture_or_promotion(move); + moveCount = ++sp->moves; + lock_release(&(sp->lock)); assert(move_is_ok(move)); - lock_grab(&(sp->lock)); - int moveCount = ++sp->moves; - lock_release(&(sp->lock)); + bool moveIsCheck = pos.move_is_check(move, ci); + bool captureOrPromotion = pos.move_is_capture_or_promotion(move); ss[sp->ply].currentMove = move; - // Decide the new search depth. + // Decide the new search depth bool dangerous; Depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; @@ -2110,7 +2127,10 @@ namespace { assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); if (thread_should_stop(threadID)) + { + lock_grab(&(sp->lock)); break; + } // New best move? if (value > sp->bestValue) // Less then 2% of cases @@ -2149,7 +2169,7 @@ namespace { } } - lock_grab(&(sp->lock)); + /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of // a beta cutoff higher up in the tree, stop all slave threads. @@ -2630,6 +2650,21 @@ namespace { } + // update_gains() updates the gains table of a non-capture move given + // the static position evaluation before and after the move. + + void update_gains(const Position& pos, Move m, Value before, Value after) { + + if ( m != MOVE_NULL + && before != VALUE_NONE + && after != VALUE_NONE + && pos.captured_piece() == NO_PIECE_TYPE + && !move_is_castle(m) + && !move_is_promotion(m)) + H.set_gain(pos.piece_on(move_to(m)), move_from(m), move_to(m), -(before + after)); + } + + // fail_high_ply_1() checks if some thread is currently resolving a fail // high at ply 1 at the node below the first root node. This information // is used for time management.