diff options
Diffstat (limited to 'src/search.c')
| -rwxr-xr-x | src/search.c | 129 |
1 files changed, 49 insertions, 80 deletions
diff --git a/src/search.c b/src/search.c index ff7e661..00b37f2 100755 --- a/src/search.c +++ b/src/search.c @@ -153,7 +153,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, ULONG uStage = TRY_HASH_MOVE; ULONG u; ULONG uFutilityMargin = 0; - SCORE iMoveSee = 0; + SCORE iCheckSee; #ifdef DEBUG ASSERT(IS_VALID_SCORE(iAlpha)); ASSERT(IS_VALID_SCORE(iBeta)); @@ -167,8 +167,15 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, mvBest.uMove = 0; // Jump directly to Qsearch if remaining depth is low enough. - // This is the only place Qsearch is entered. - if (uDepth < ONE_PLY) + // This is the only place Qsearch is entered. Lowered from ONE_PLY to + // THREE_QUARTERS_PLY to match the check extension's new flat amount + // (searchsup.c's ComputeMoveExtension) -- a lone check (or short run + // of them) still buys exactly one extra full-width ply as before, but + // a long unbroken chain now pays QUARTER_PLY of real cost per check + // instead of extending for free. root.c compensates by trimming the + // same QUARTER_PLY off the per-iteration depth budget so this doesn't + // just add a blanket 1/4 ply to every search. + if (uDepth < THREE_QUARTERS_PLY) { pf->fCouldStandPat[BLACK] = pf->fCouldStandPat[WHITE] = FALSE; pf->uQsearchNodes = pf->uQsearchDepth = 0; @@ -192,7 +199,6 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, } DTEnterNode(ctx, uDepth, FALSE, iAlpha, iBeta); iInitialAlpha = iAlpha; - pi->fIsPVNode = (iBeta != iAlpha + 1); ASSERT((IS_CHECKING_MOVE(mvLast) && (TRUE == pi->fInCheck)) || (!IS_CHECKING_MOVE(mvLast) && (FALSE == pi->fInCheck))); @@ -430,19 +436,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, // prune away moves we will also make sure there is // no per-move extension. ASSERT(!uFutilityMargin); - // Narrowed from "<= TWO_PLY" to "< TWO_PLY": ExtraReduction's - // own safety cap guarantees a reduced move's child lands - // with remaining depth >= TWO_PLY, which used to hand every - // heavily-reduced line straight into futility's activation - // floor at exactly that value -- double jeopardy on the - // same move by construction, not by chance. Excluding just - // that exact floor value (not the whole fractional-depth - // window below it) un-stacks that specific overlap. - if (FALSE && // EXPERIMENT: disabled for the unscaled-LMR-table - // test -- isolate the new reduction table's - // effect without also stacking futility pruning - (iRoughEval + VALUE_ROOK <= iAlpha) && - (uDepth < TWO_PLY) && + if ((iRoughEval + VALUE_ROOK <= iAlpha) && + (uDepth <= TWO_PLY) && (ctx->uPly >= 2) && (iOrigExtend == 0) && (ctx->sPlyInfo[ctx->uPly - 2].iExtensionAmount <= 0) && @@ -530,14 +525,15 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, } #endif - // SEE must be computed on the PRE-move position (it internally - // simulates removing mv.pMoved from mv.cFrom and adding x-rays -- - // see see.c) -- so grab it now, before MakeMove mutates *pos in - // place, for the futility check below to use. Only bother when - // futility mode is actually active at this node. - if (uFutilityMargin) + // SEE must be computed on the PRE-move position -- see.c's + // exchange walk needs the piece still sitting on cFrom. Only + // needed for checking moves, where ComputeMoveExtension uses it + // to gate the check extension on soundness (Crafty-style: don't + // extend a checking move that's really just a losing sacrifice). + iCheckSee = 0; + if (IS_CHECKING_MOVE(mv)) { - iMoveSee = SEE(pos, mv); + iCheckSee = SEE(pos, mv); } if (TRUE == MakeMove(ctx, mv)) @@ -555,6 +551,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, (x - 1), // Note: x==0 if doing mvHash iRoughEval, uDepth, + iCheckSee, &iExtend); // Cap how many extension plies this line may spend in total @@ -579,22 +576,11 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, iExtend)) { ASSERT(iExtend == 0); - iExtend = -(ONE_PLY + ExtraReduction(uDepth, (x - 1), - GetMoveFailHighPercentage(mv))); - pi->iExtensionAmount = iExtend; - INC(ctx->sCounters.tree.u64HistoryPrunes); + iExtend = -ONE_PLY; + pi->iExtensionAmount = -ONE_PLY; } - // Maybe even "futility prune" this move away. Require two - // independent corroborating signals on top of the static - // margin, not just the move's ordering score alone: this - // specific move must have rarely caused a beta cutoff - // historically (same threshold WeShouldDoHistoryPruning - // uses), and it must not show a tactical gain per SEE -- - // ComputeMoveScore alone is PSQT/history/killer-bonus for - // quiet moves, with no exchange evaluation at all, so it - // was pruning purely on a positional-ordering number. -#ifdef PERF_COUNTERS + // Maybe even "futility prune" this move away. if ((x != 0) && (uLegalMoves > 1) && (uFutilityMargin) && @@ -602,25 +588,6 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, (iExtend <= 0) && (!IS_ESCAPING_CHECK(mv))) { - INC(ctx->sCounters.tree.u64FutilityCandidates); - } -#endif - if ((x != 0) && - (uLegalMoves > 1) && - (uFutilityMargin) && - (ComputeMoveScore(ctx, mv, (x - 1)) < uFutilityMargin) && - (iExtend <= 0) && - (!IS_ESCAPING_CHECK(mv)) && - // Stricter than WeShouldDoHistoryPruning's <= 10: that one - // gates a reduction with a fail-high re-search safety net - // (search.c's own counters show ~2% wrong-guess rate is - // fine there); this gates an outright, unverified skip -- - // no re-search, no recovery if wrong -- so demand much - // stronger evidence the move is truly hopeless. - (GetMoveFailHighPercentage(mv) <= 3) && - (iMoveSee <= 0)) - { - INC(ctx->sCounters.tree.u64FutilityPrunes); // TODO: test this more carefully ASSERT(!IS_CHECKING_MOVE(mv)); UnmakeMove(ctx, mv); @@ -652,9 +619,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx, // Research deeper if history pruning failed if ((iExtend < 0) && (iScore >= iBeta)) { - uNextDepth -= iExtend; // undo the full reduction, whatever its magnitude + uNextDepth += ONE_PLY; pi->iExtensionAmount = 0; - INC(ctx->sCounters.tree.u64HistoryPruneReSearches); iScore = -Search(ctx, -iBeta, -iAlpha, uNextDepth); } UnmakeMove(ctx, mv); @@ -946,20 +912,19 @@ QSearchFromCheckNoStandPat(IN SEARCHER_THREAD_CONTEXT *ctx, uMoveCount = MOVE_COUNT(ctx, ctx->uPly); if (uMoveCount > 0) { - // Consider extending the number of qsearch plies we consider - // non-capture checks at during this line. - if ((pf->fCouldStandPat[pos->uToMove] == FALSE) && - (pf->uQsearchDepth < g_uIterateDepth / 2) && - (CountKingSafetyDefects(pos, pos->uToMove) > 1)) + // Consider extending the number of qsearch plies for our opponent + // if this looks good. + if ((pf->uQsearchDepth < pf->uQsearchCheckDepth) && + (pf->uQsearchDepth < g_uIterateDepth / 4) && + (pf->fCouldStandPat[pos->uToMove] == FALSE) && + (CountKingSafetyDefects(pos, pos->uToMove) > 2)) { - if (uMoveCount == 1) { + if ((uMoveCount == 1) || + (NUM_KING_MOVES(ctx, ctx->uPly) == 0) || + (NUM_CHECKING_PIECES(ctx, ctx->uPly) > 1)) + { uQsearchCheckExtension = 2; INC(ctx->sCounters.extension.uQExtend); - } else if ((uMoveCount == 2) || - (NUM_KING_MOVES(ctx, ctx->uPly) == 0) || - (NUM_CHECKING_PIECES(ctx, ctx->uPly) > 1)) { - uQsearchCheckExtension = 1; - INC(ctx->sCounters.extension.uQExtend); } ctx->sPlyInfo[ctx->uPly].iExtensionAmount = uQsearchCheckExtension; } @@ -1080,7 +1045,7 @@ QSearchInDangerNoStandPat(IN SEARCHER_THREAD_CONTEXT *ctx, // Set futility: - // + // // iEval + move_value + margin < alpha // move_value < alpha - margin - iEval iFutility = 0; @@ -1097,7 +1062,8 @@ QSearchInDangerNoStandPat(IN SEARCHER_THREAD_CONTEXT *ctx, // have a piece trapped. Allow him to play checks in order to try // to save the situation. ASSERT(!InCheck(pos, pos->uToMove)); - fIncludeChecks = (pf->uQsearchDepth < g_uIterateDepth / 3); + fIncludeChecks = ((pf->uQsearchDepth < pf->uQsearchCheckDepth) && + (pf->fCouldStandPat[FLIP(pos->uToMove)] == FALSE)); GenerateMoves(ctx, NULLMOVE, _WhatToGen[fIncludeChecks]); for (x = ctx->sMoveStack.uBegin[ctx->uPly]; @@ -1313,7 +1279,10 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, // position looks ok enough to allow him the option to stand pat // -or- we missed when we probed the dangerhash. Also remember // that this side has had the option to stand pat when searching - // below this point. + // below this point. This also means the other side is not + // allowed to generate checks on this side because even if it + // discovers a mate, there's no force since a stand pat + // opportunity exists right here. uLegalMoves = 0; iEval = iBestScore = Eval(ctx, iAlpha, iBeta); if (iBestScore > iAlpha) @@ -1343,13 +1312,13 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, iFutility = MAX0(iFutility); } - // We know we are not in check. Generate moves (including checks - // if we are below the threshold and the other side has never been - // allowed to stand pat). Recurse. - fIncludeChecks = ((pf->uQsearchDepth < pf->uQsearchCheckDepth) && - (pf->fCouldStandPat[FLIP(pos->uToMove)] == FALSE) && - (pos->uNonPawnMaterial[pos->uToMove] > - (VALUE_KING + VALUE_BISHOP))); + // We know we are not in check. If we are early in the qsearch, + // and the other side has not yet been able to stand pat, and we + // have material, generate checks here too. + fIncludeChecks = + ((pf->uQsearchDepth < pf->uQsearchCheckDepth) && + (pf->fCouldStandPat[FLIP(pos->uToMove)] == FALSE) && + (pos->uNonPawnMaterial[pos->uToMove] > (VALUE_KING + VALUE_BISHOP))); GenerateMoves(ctx, NULLMOVE, _WhatToGen[fIncludeChecks]); for (x = ctx->sMoveStack.uBegin[ctx->uPly]; x < ctx->sMoveStack.uEnd[ctx->uPly]; |
