X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=4c7e1e463dc553ae53c2efee850c215fb5f24223;hp=5ea75fecfbd98d40cb084f20600f77612b6a891c;hb=ec0a650dff9ad78eff89e0c7fb04491f79fa7945;hpb=a7fcdfd6bff8baa6278306dd9e4fa72d053bb8c9 diff --git a/src/search.cpp b/src/search.cpp index 5ea75fec..4c7e1e46 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -104,6 +104,13 @@ namespace { }; + // FIXME: document me + + enum NullStatus { + ALLOW_NULLMOVE, + FORBID_NULLMOVE, + VERIFY_NULLMOVE + }; // RootMove struct is used for moves at the root at the tree. For each // root move, we store a score, a node count, and a PV (really a refutation @@ -232,7 +239,7 @@ namespace { const Value EasyMoveMargin = Value(0x200); // Last seconds noise filtering (LSN) - const bool UseLSNFiltering = false; + const bool UseLSNFiltering = true; const int LSNTime = 4000; // In milliseconds const Value LSNValue = value_from_centipawns(200); bool loseOnTime = false; @@ -281,7 +288,7 @@ namespace { Value id_loop(const Position& pos, Move searchMoves[]); Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value* alphaPtr, Value* betaPtr); Value search_pv(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); - Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove = MOVE_NONE); + Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, NullStatus nullStatus, int threadID, Move excludedMove = MOVE_NONE); Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); void sp_search(SplitPoint* sp, int threadID); void sp_search_pv(SplitPoint* sp, int threadID); @@ -294,7 +301,7 @@ namespace { Depth extension(const Position&, Move, bool, bool, bool, bool, bool, bool*); bool ok_to_do_nullmove(const Position& pos); bool ok_to_prune(const Position& pos, Move m, Move threat); - bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); + bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply, bool allowNullmove); 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); @@ -387,7 +394,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move, if (get_option_value_string("Book File") != OpeningBook.file_name()) OpeningBook.open(get_option_value_string("Book File")); - Move bookMove = OpeningBook.get_move(pos); + Move bookMove = OpeningBook.get_move(pos, get_option_value_bool("Best Book Move")); if (bookMove != MOVE_NONE) { if (PonderSearch) @@ -886,7 +893,7 @@ namespace { if (ss[0].reduction) { // Reduced depth non-pv search using alpha as upperbound - value = -search(pos, ss, -alpha, newDepth-ss[0].reduction, 1, true, 0); + value = -search(pos, ss, -alpha, newDepth-ss[0].reduction, 1, ALLOW_NULLMOVE, 0); doFullDepthSearch = (value > alpha); } } @@ -896,7 +903,7 @@ namespace { { // Full depth non-pv search using alpha as upperbound ss[0].reduction = Depth(0); - value = -search(pos, ss, -alpha, newDepth, 1, true, 0); + value = -search(pos, ss, -alpha, newDepth, 1, ALLOW_NULLMOVE, 0); // If we are above alpha then research at same depth but as PV // to get a correct score or eventually a fail high above beta. @@ -1131,7 +1138,7 @@ namespace { if (abs(ttValue) < VALUE_KNOWN_WIN) { - Value excValue = search(pos, ss, ttValue - SingularExtensionMargin, depth / 2, ply, false, threadID, move); + Value excValue = search(pos, ss, ttValue - SingularExtensionMargin, depth / 2, ply, FORBID_NULLMOVE, threadID, move); if (excValue < ttValue - SingularExtensionMargin) ext = OnePly; @@ -1167,7 +1174,7 @@ namespace { ss[ply].reduction = pv_reduction(depth, moveCount); if (ss[ply].reduction) { - value = -search(pos, ss, -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); + value = -search(pos, ss, -alpha, newDepth-ss[ply].reduction, ply+1, ALLOW_NULLMOVE, threadID); doFullDepthSearch = (value > alpha); } } @@ -1176,7 +1183,7 @@ namespace { if (doFullDepthSearch) { ss[ply].reduction = Depth(0); - value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID); + value = -search(pos, ss, -alpha, newDepth, ply+1, ALLOW_NULLMOVE, threadID); // Step extra. pv search (only in PV nodes) if (value > alpha && value < beta) @@ -1251,7 +1258,7 @@ namespace { // search() is the search function for zero-width nodes. Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, - int ply, bool allowNullmove, int threadID, Move excludedMove) { + int ply, NullStatus nullStatus, int threadID, Move excludedMove) { assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE); assert(ply >= 0 && ply < PLY_MAX); @@ -1299,7 +1306,7 @@ namespace { tte = TT.retrieve(posKey); ttMove = (tte ? tte->move() : MOVE_NONE); - if (tte && ok_to_use_TT(tte, depth, beta, ply)) + if (tte && ok_to_use_TT(tte, depth, beta, ply, nullStatus != VERIFY_NULLMOVE)) { ss[ply].currentMove = ttMove; // Can be MOVE_NONE return value_from_tt(tte->value(), ply); @@ -1339,7 +1346,7 @@ namespace { // Step 7. Static null move pruning // We're betting that the opponent doesn't have a move that will reduce // the score by more than futility_margin(depth) if we do a null move. - if ( allowNullmove + if ( nullStatus == ALLOW_NULLMOVE && depth < RazorDepth && !isCheck && !value_is_mate(beta) @@ -1351,7 +1358,7 @@ namespace { // When we jump directly to qsearch() we do a null move only if static value is // at least beta. Otherwise we do a null move if static value is not more than // NullMoveMargin under beta. - if ( allowNullmove + if ( nullStatus == ALLOW_NULLMOVE && depth > OnePly && !isCheck && !value_is_mate(beta) @@ -1369,7 +1376,7 @@ namespace { pos.do_null_move(st); - nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID); + nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, FORBID_NULLMOVE, threadID); pos.undo_null_move(); @@ -1382,13 +1389,15 @@ namespace { // Do zugzwang verification search for high depths, don't store in TT // if search was stopped. if ( ( depth < 6 * OnePly - || search(pos, ss, beta, depth-5*OnePly, ply, false, threadID) >= beta) + || search(pos, ss, beta, depth-5*OnePly, ply, VERIFY_NULLMOVE, threadID) >= beta) && !AbortSearch && !TM.thread_should_stop(threadID)) { assert(value_to_tt(nullValue, ply) == nullValue); - TT.store(posKey, nullValue, VALUE_TYPE_LOWER, depth, MOVE_NONE); + if (!tte) + TT.store(posKey, nullValue, VALUE_TYPE_NS_LO, depth, MOVE_NONE); + return nullValue; } } else { @@ -1415,7 +1424,7 @@ namespace { && !isCheck && ss[ply].eval >= beta - IIDMargin) { - search(pos, ss, beta, depth/2, ply, false, threadID); + search(pos, ss, beta, depth/2, ply, FORBID_NULLMOVE, threadID); ttMove = ss[ply].pv[ply]; tte = TT.retrieve(posKey); } @@ -1457,7 +1466,7 @@ namespace { if (abs(ttValue) < VALUE_KNOWN_WIN) { - Value excValue = search(pos, ss, ttValue - SingularExtensionMargin, depth / 2, ply, false, threadID, move); + Value excValue = search(pos, ss, ttValue - SingularExtensionMargin, depth / 2, ply, FORBID_NULLMOVE, threadID, move); if (excValue < ttValue - SingularExtensionMargin) ext = OnePly; @@ -1511,7 +1520,7 @@ namespace { ss[ply].reduction = nonpv_reduction(depth, moveCount); if (ss[ply].reduction) { - value = -search(pos, ss, -(beta-1), newDepth-ss[ply].reduction, ply+1, true, threadID); + value = -search(pos, ss, -(beta-1), newDepth-ss[ply].reduction, ply+1, ALLOW_NULLMOVE, threadID); doFullDepthSearch = (value >= beta); } } @@ -1520,7 +1529,7 @@ namespace { if (doFullDepthSearch) { ss[ply].reduction = Depth(0); - value = -search(pos, ss, -(beta-1), newDepth, ply+1, true, threadID); + value = -search(pos, ss, -(beta-1), newDepth, ply+1, ALLOW_NULLMOVE, threadID); } // Step 16. Undo move @@ -1625,7 +1634,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, true)) { assert(tte->type() != VALUE_TYPE_EVAL); @@ -1666,7 +1675,7 @@ namespace { alpha = bestValue; // If we are near beta then try to get a cutoff pushing checks a bit further - bool deepChecks = depth == -OnePly && staticValue >= beta - PawnValueMidgame / 8; + bool deepChecks = (depth == -OnePly && staticValue >= beta - PawnValueMidgame / 8); // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, @@ -1712,7 +1721,7 @@ namespace { // Detect blocking evasions that are candidate to be pruned evasionPrunable = isCheck - && bestValue != -VALUE_INFINITE + && bestValue > value_mated_in(PLY_MAX) && !pos.move_is_capture(move) && pos.type_of_piece_on(move_from(move)) != KING && !pos.can_castle(pos.side_to_move()); @@ -1870,7 +1879,7 @@ namespace { ss[sp->ply].reduction = nonpv_reduction(sp->depth, moveCount); if (ss[sp->ply].reduction) { - value = -search(pos, ss, -(sp->beta-1), newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); + value = -search(pos, ss, -(sp->beta-1), newDepth-ss[sp->ply].reduction, sp->ply+1, ALLOW_NULLMOVE, threadID); doFullDepthSearch = (value >= sp->beta && !TM.thread_should_stop(threadID)); } } @@ -1879,7 +1888,7 @@ namespace { if (doFullDepthSearch) { ss[sp->ply].reduction = Depth(0); - value = -search(pos, ss, -(sp->beta - 1), newDepth, sp->ply+1, true, threadID); + value = -search(pos, ss, -(sp->beta - 1), newDepth, sp->ply+1, ALLOW_NULLMOVE, threadID); } // Step 16. Undo move @@ -1976,7 +1985,7 @@ namespace { if (ss[sp->ply].reduction) { Value localAlpha = sp->alpha; - value = -search(pos, ss, -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); + value = -search(pos, ss, -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, ALLOW_NULLMOVE, threadID); doFullDepthSearch = (value > localAlpha && !TM.thread_should_stop(threadID)); } } @@ -1986,7 +1995,7 @@ namespace { { Value localAlpha = sp->alpha; ss[sp->ply].reduction = Depth(0); - value = -search(pos, ss, -localAlpha, newDepth, sp->ply+1, true, threadID); + value = -search(pos, ss, -localAlpha, newDepth, sp->ply+1, ALLOW_NULLMOVE, threadID); if (value > localAlpha && value < sp->beta && !TM.thread_should_stop(threadID)) { @@ -2306,14 +2315,18 @@ namespace { } - // ok_to_use_TT() returns true if a transposition table score - // can be used at a given point in search. + // ok_to_use_TT() returns true if a transposition table score can be used at a + // given point in search. To avoid zugzwang issues TT cutoffs at the root node + // of a null move verification search are not allowed if the TT value was found + // by a null search, this is implemented testing allowNullmove and TT entry type. - bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply) { + bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply, bool allowNullmove) { Value v = value_from_tt(tte->value(), ply); - return ( tte->depth() >= depth + return (allowNullmove || !(tte->type() & VALUE_TYPE_NULL)) + + && ( tte->depth() >= depth || v >= Max(value_mate_in(PLY_MAX), beta) || v < Min(value_mated_in(PLY_MAX), beta)) @@ -2769,7 +2782,7 @@ namespace { } // Wait until the thread has finished launching and is gone to sleep - while (threads[i].state != THREAD_SLEEPING); + while (threads[i].state != THREAD_SLEEPING) {} } } @@ -2810,7 +2823,7 @@ namespace { SplitPoint* sp; - for (sp = threads[threadID].splitPoint; sp && !sp->stopRequest; sp = sp->parent); + for (sp = threads[threadID].splitPoint; sp && !sp->stopRequest; sp = sp->parent) {} return sp != NULL; }