X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=e1ac91c3f58e6a3a22f07477ed62b3c893bb9f9d;hp=ff791b8ea903e85649c99ee651661f26e31f38f0;hb=6e1cb6e45b63c21ee818fe3f8266bdbcc3aaceb5;hpb=5b1043ee11489a1ad732847ee93ddb3fc5905726 diff --git a/src/search.cpp b/src/search.cpp index ff791b8e..e1ac91c3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -126,9 +126,6 @@ namespace { // Search depth at iteration 1 const Depth InitialDepth = OnePly; - // Depth limit for selective search - const Depth SelectiveDepth = 7 * OnePly; - // Use internal iterative deepening? const bool UseIIDAtPVNodes = true; const bool UseIIDAtNonPVNodes = true; @@ -142,17 +139,6 @@ namespace { // better than the second best move. const Value EasyMoveMargin = Value(0x200); - // Problem margin. If the score of the first move at iteration N+1 has - // dropped by more than this since iteration N, the boolean variable - // "Problem" is set to true, which will make the program spend some extra - // time looking for a better move. - const Value ProblemMargin = Value(0x28); - - // No problem margin. If the boolean "Problem" is true, and a new move - // is found at the root which is less than NoProblemMargin worse than the - // best move from the previous iteration, Problem is set back to false. - const Value NoProblemMargin = Value(0x14); - // Null move margin. A null move search will not be done if the static // evaluation of the position is more than NullMoveMargin below beta. const Value NullMoveMargin = Value(0x200); @@ -161,15 +147,6 @@ namespace { // remaining ones we will extend it. const Value SingleReplyMargin = Value(0x20); - // Margins for futility pruning in the quiescence search, and at frontier - // and near frontier nodes. - const Value FutilityMarginQS = Value(0x80); - - Value FutilityMargins[2 * PLY_MAX_PLUS_2]; // Initialized at startup. - - // Each move futility margin is decreased - const Value IncrementalFutilityMargin = Value(0x8); - // Depth limit for razoring const Depth RazorDepth = 4 * OnePly; @@ -209,7 +186,7 @@ namespace { int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, ExactMaxTime; bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit; bool AbortSearch, Quit; - bool FailHigh, FailLow, Problem; + bool AspirationFailLow; // Show current line? bool ShowCurrentLine; @@ -218,6 +195,14 @@ namespace { bool UseLogFile; std::ofstream LogFile; + // Futility lookup tables and their getter functions + const Value FutilityMarginQS = Value(0x80); + int32_t FutilityMarginsMatrix[14][64]; // [depth][moveNumber] + int FutilityMoveCountArray[32]; // [depth] + + inline Value futility_margin(Depth d, int mn) { return (Value) (d < 14? FutilityMarginsMatrix[Max(d, 0)][Min(mn, 63)] : 2*VALUE_INFINITE); } + inline int futility_move_count(Depth d) { return (d < 32? FutilityMoveCountArray[d] : 512); } + // Reduction lookup tables and their getter functions // Initialized at startup int8_t PVReductionMatrix[64][64]; // [depth][moveNumber] @@ -276,7 +261,6 @@ namespace { 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(); int nps(); void poll(); @@ -352,7 +336,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, // Initialize global search variables Idle = StopOnPonderhit = AbortSearch = Quit = false; - FailHigh = FailLow = Problem = false; + AspirationFailLow = false; NodesSincePoll = 0; SearchStartTime = get_system_time(); ExactMaxTime = maxTime; @@ -363,7 +347,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch; // Look for a book move, only during games, not tests - if (UseTimeManagement && !ponder && get_option_value_bool("OwnBook")) + if (UseTimeManagement && get_option_value_bool("OwnBook")) { Move bookMove; if (get_option_value_string("Book File") != OpeningBook.file_name()) @@ -372,6 +356,9 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, bookMove = OpeningBook.get_move(pos); if (bookMove != MOVE_NONE) { + if (PonderSearch) + wait_for_stop_or_ponderhit(); + cout << "bestmove " << bookMove << endl; return true; } @@ -380,7 +367,6 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, for (int i = 0; i < THREAD_MAX; i++) { Threads[i].nodes = 0ULL; - Threads[i].failHighPly1 = false; } if (button_was_pressed("New Game")) @@ -564,12 +550,15 @@ void init_threads() { } // Init futility margins array - FutilityMargins[0] = FutilityMargins[1] = Value(0); + for (i = 0; i < 14; i++) // i == depth (OnePly = 2) + for (int j = 0; j < 64; j++) // j == moveNumber + { + FutilityMarginsMatrix[i][j] = (i < 2 ? 0 : 112 * bitScanReverse32(i * i / 2)) - 8 * j; // FIXME: test using log instead of BSR + } - for (i = 2; i < 2 * PLY_MAX_PLUS_2; i++) - { - FutilityMargins[i] = Value(112 * bitScanReverse32(i * i / 2)); // FIXME: test using log instead of BSR - } + // Init futility move count array + for (i = 0; i < 32; i++) // i == depth (OnePly = 2) + FutilityMoveCountArray[i] = 3 + (1 << (3 * i / 8)); for (i = 0; i < THREAD_MAX; i++) Threads[i].activeSplitPoints = 0; @@ -762,8 +751,6 @@ namespace { if (ss[0].pv[0] != EasyMove) EasyMove = MOVE_NONE; - Problem = false; - if (UseTimeManagement) { // Time to stop? @@ -896,7 +883,6 @@ namespace { } RootMoveNumber = i + 1; - FailHigh = false; // Save the current node count before the move is searched nodes = nodes_searched(); @@ -934,16 +920,6 @@ namespace { alpha = -VALUE_INFINITE; value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0); - - // If the value has dropped a lot compared to the last iteration, - // set the boolean variable Problem to true. This variable is used - // for time managment: When Problem is true, we try to complete the - // current iteration before playing a move. - Problem = ( Iteration >= 2 - && value <= ValueByIteration[Iteration - 1] - ProblemMargin); - - if (Problem && StopOnPonderhit) - StopOnPonderhit = false; } else { @@ -970,14 +946,7 @@ namespace { value = -search(pos, ss, -alpha, newDepth, 1, true, 0); if (value > alpha) - { - // Fail high! Set the boolean variable FailHigh to true, and - // re-search the move using a PV search. The variable FailHigh - // is used for time managment: We try to avoid aborting the - // search prematurely during a fail high research. - FailHigh = true; value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0); - } } } @@ -1086,11 +1055,6 @@ namespace { } if (value > alpha) alpha = value; - - // Reset the global variable Problem to false if the value isn't too - // far below the final value from the last iteration. - if (value > ValueByIteration[Iteration - 1] - NoProblemMargin) - Problem = false; } else // MultiPV > 1 { @@ -1116,7 +1080,10 @@ namespace { assert(alpha >= oldAlpha); - FailLow = (alpha == oldAlpha); + AspirationFailLow = (alpha == oldAlpha); + + if (AspirationFailLow && StopOnPonderhit) + StopOnPonderhit = false; } // Can we exit fail low loop ? @@ -1284,19 +1251,7 @@ namespace { ss[ply].reduction = Depth(0); value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID); if (value > alpha && value < beta) - { - // When the search fails high at ply 1 while searching the first - // move at the root, set the flag failHighPly1. This is used for - // time managment: We don't want to stop the search early in - // such cases, because resolving the fail high at ply 1 could - // result in a big drop in score at the root. - if (ply == 1 && RootMoveNumber == 1) - Threads[threadID].failHighPly1 = true; - - // A fail high occurred. Re-search at full window (pv search) value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID); - Threads[threadID].failHighPly1 = false; - } } } pos.undo_move(move); @@ -1314,13 +1269,6 @@ namespace { if (value == value_mate_in(ply + 1)) ss[ply].mateKiller = move; } - // If we are at ply 1, and we are searching the first root move at - // ply 0, set the 'Problem' variable if the score has dropped a lot - // (from the computer's point of view) since the previous iteration. - if ( ply == 1 - && Iteration >= 2 - && -value <= ValueByIteration[Iteration-1] - ProblemMargin) - Problem = true; } // Split? @@ -1425,9 +1373,6 @@ namespace { isCheck = pos.is_check(); - // Calculate depth dependant futility pruning parameters - const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8)); - // Evaluate the position statically if (!isCheck) { @@ -1440,7 +1385,7 @@ namespace { } ss[ply].eval = staticValue; - futilityValue = staticValue + FutilityMargins[int(depth)]; //FIXME: Remove me, only for split + futilityValue = staticValue + futility_margin(depth, 0); //FIXME: Remove me, only for split staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval); } @@ -1451,8 +1396,8 @@ namespace { if ( !isCheck && allowNullmove && depth < RazorDepth - && staticValue - FutilityMargins[int(depth)] >= beta) - return staticValue - FutilityMargins[int(depth)]; + && staticValue - futility_margin(depth, 0) >= beta) + return staticValue - futility_margin(depth, 0); // Null move search if ( allowNullmove @@ -1524,7 +1469,7 @@ namespace { { search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID); ttMove = ss[ply].pv[ply]; - tte = TT.retrieve(pos.get_key()); + tte = TT.retrieve(posKey); } // Initialize a MovePicker object for the current position, and prepare @@ -1584,35 +1529,20 @@ namespace { && move != ttMove) { // Move count based pruning - if ( moveCount >= FutilityMoveCountMargin + if ( moveCount >= futility_move_count(depth) && ok_to_prune(pos, move, ss[ply].threatMove) && bestValue > value_mated_in(PLY_MAX)) continue; // Value based pruning - Depth predictedDepth = newDepth; - - //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG?? - ss[ply].reduction = nonpv_reduction(depth, moveCount); - if (ss[ply].reduction) - predictedDepth -= ss[ply].reduction; + Depth predictedDepth = newDepth - nonpv_reduction(depth, moveCount); //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG?? + futilityValueScaled = ss[ply].eval + futility_margin(predictedDepth, moveCount) + H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45; - if (predictedDepth < SelectiveDepth) + if (futilityValueScaled < beta) { - int preFutilityValueMargin = 0; - if (predictedDepth >= OnePly) - preFutilityValueMargin = FutilityMargins[int(predictedDepth)]; - - preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45; - - futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin; - - if (futilityValueScaled < beta) - { - if (futilityValueScaled > bestValue) - bestValue = futilityValueScaled; - continue; - } + if (futilityValueScaled > bestValue) + bestValue = futilityValueScaled; + continue; } } @@ -1911,11 +1841,9 @@ namespace { Move move; int moveCount; bool isCheck = pos.is_check(); - bool useFutilityPruning = sp->depth < SelectiveDepth + bool useFutilityPruning = sp->depth < 7 * OnePly //FIXME: sync with search && !isCheck; - const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8)); - while ( lock_grab_bool(&(sp->lock)) && sp->bestValue < sp->beta && !thread_should_stop(threadID) @@ -1942,13 +1870,13 @@ namespace { && !captureOrPromotion) { // Move count based pruning - if ( moveCount >= FutilityMoveCountMargin + if ( moveCount >= futility_move_count(sp->depth) && ok_to_prune(pos, move, ss[sp->ply].threatMove) && sp->bestValue > value_mated_in(PLY_MAX)) continue; // Value based pruning - Value futilityValueScaled = sp->futilityValue - moveCount * IncrementalFutilityMargin; + Value futilityValueScaled = sp->futilityValue - moveCount * 8; //FIXME: sync with search if (futilityValueScaled < sp->beta) { @@ -2106,14 +2034,6 @@ namespace { if (value > localAlpha && value < sp->beta) { - // When the search fails high at ply 1 while searching the first - // move at the root, set the flag failHighPly1. This is used for - // time managment: We don't want to stop the search early in - // such cases, because resolving the fail high at ply 1 could - // result in a big drop in score at the root. - if (sp->ply == 1 && RootMoveNumber == 1) - Threads[threadID].failHighPly1 = true; - // 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; @@ -2121,8 +2041,6 @@ namespace { value = -search_pv(pos, ss, -sp->beta, -localAlpha, newDepth, sp->ply+1, threadID); else assert(thread_should_stop(threadID)); - - Threads[threadID].failHighPly1 = false; } } pos.undo_move(move); @@ -2160,13 +2078,6 @@ namespace { if (value == value_mate_in(sp->ply + 1)) ss[sp->ply].mateKiller = move; } - // If we are at ply 1, and we are searching the first root move at - // ply 0, set the 'Problem' variable if the score has dropped a lot - // (from the computer's point of view) since the previous iteration. - if ( sp->ply == 1 - && Iteration >= 2 - && -value <= ValueByIteration[Iteration-1] - ProblemMargin) - Problem = true; } lock_release(&(sp->lock)); } @@ -2668,20 +2579,6 @@ namespace { } - // 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. - - bool fail_high_ply_1() { - - for (int i = 0; i < ActiveThreads; i++) - if (Threads[i].failHighPly1) - return true; - - return false; - } - - // current_search_time() returns the number of milliseconds which have passed // since the beginning of the current search. @@ -2768,18 +2665,11 @@ namespace { return; bool stillAtFirstMove = RootMoveNumber == 1 - && !FailLow + && !AspirationFailLow && t > MaxSearchTime + ExtraSearchTime; - bool noProblemFound = !FailHigh - && !FailLow - && !fail_high_ply_1() - && !Problem - && t > 6 * (MaxSearchTime + ExtraSearchTime); - bool noMoreTime = t > AbsoluteMaxSearchTime - || stillAtFirstMove //FIXME: We are not checking any problem flags, BUG? - || noProblemFound; + || stillAtFirstMove; if ( (Iteration >= 3 && UseTimeManagement && noMoreTime) || (ExactMaxTime && t >= ExactMaxTime) @@ -2798,18 +2688,11 @@ namespace { PonderSearch = false; bool stillAtFirstMove = RootMoveNumber == 1 - && !FailLow + && !AspirationFailLow && t > MaxSearchTime + ExtraSearchTime; - bool noProblemFound = !FailHigh - && !FailLow - && !fail_high_ply_1() - && !Problem - && t > 6 * (MaxSearchTime + ExtraSearchTime); - bool noMoreTime = t > AbsoluteMaxSearchTime - || stillAtFirstMove - || noProblemFound; + || stillAtFirstMove; if (Iteration >= 3 && UseTimeManagement && (noMoreTime || StopOnPonderhit)) AbortSearch = true; @@ -3125,7 +3008,7 @@ namespace { for (int i = 0; i < ActiveThreads; i++) if (i == master || splitPoint->slaves[i]) { - memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 3 * sizeof(SearchStack)); + memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 4 * sizeof(SearchStack)); Threads[i].workIsWaiting = true; // This makes the slave to exit from idle_loop() }