summaryrefslogtreecommitdiff
path: root/src/lmr_testing/searchsup_GetLMRReduction.c
blob: 0ee69f68f838d744ab36c5062b8ee90cc926e05f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Replace searchsup.c's GetLMRReduction body with this (signature/name
// unchanged, so search.c/split.c call sites need no changes).

INT
GetLMRReduction(IN SCORE iRoughEval,
               IN SCORE iAlpha,
               IN SCORE iBeta,
               IN SEARCHER_THREAD_CONTEXT *ctx,
               IN ULONG uRemainingDepth,
               IN ULONG uLegalMoves,
               IN MOVE mv,
               IN ULONG uMoveNum,
               IN INT iExtend)
/**

Routine description:

    Decide how much (if any) to reduce this move's search depth by --
    graded LMR, replacing the old fixed -ONE_PLY history pruning.
    Note: this function is called after the move has been played on
    the board (ctx->uPly is already the *child's* ply; ctx->uPly - 1
    is the node whose move loop we're in, i.e. the parent of the
    search we're about to reduce).

    PV-parent protection: if the node whose move this is (uPly - 1)
    was itself reached with a wide window (a genuine PV node, not
    just non-null-window), don't reduce at all here.  This is
    deliberately about the *parent*, not "am I a PV node myself" --
    every non-first move at a PV node is searched null-window
    regardless (standard PVS), so that alone doesn't distinguish
    "one ply below a real PV" from "deep inside an already-non-PV
    subtree".  Confirmed empirically (not just theoretically) to
    matter: without this, graded LMR measured worse than baseline;
    with it, break-even.  Grandparent protection (uPly - 2) was
    tried and measured worse (23/30, EBF 4.258 vs this config's
    24/30, EBF 4.223 on ecm_quick.ep_ @ sn=4M) -- not worth it.

Parameters:

    SEARCHER_THREAD_CONTEXT *ctx,
    ULONG uRemainingDepth,
    ULONG uLegalMoves,
    MOVE mv,
    INT iExtend

Return value:

    INT : 0 if no reduction, else a negative ply-fraction (ONE_PLY
        units) suitable for adding directly into iExtend.

**/
{
    ULONG uDepthPly, uMoveIdx;
    INT iReduction;

    ASSERT(ctx->uPly > 0);
    ASSERT(mv.uMove);
    ASSERT((uMoveNum > 0) || (uLegalMoves == 0));
    if ((uRemainingDepth >= TWO_PLY) &&
        (iBeta == (iAlpha + 1)) &&
        (uLegalMoves > 3) &&
        (0 == iExtend) &&
        (!IS_ESCAPING_CHECK(mv)) &&
        (!IS_CAPTURE_OR_PROMOTION(mv)) &&
        (!IS_CHECKING_MOVE(mv)) &&
        (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-1][0])) &&
        (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-1][1])) &&
        ((ctx->uPly < 3) ||
         (!IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][0]) &&
          !IS_SAME_MOVE(mv, ctx->mvKiller[ctx->uPly-3][1]))) &&
        (!IS_SAME_MOVE(mv, ctx->mvCounter[MOVE_TO_INDEX(ctx->sPlyInfo[ctx->uPly-1].mv)][0])) &&
        (!IS_SAME_MOVE(mv, ctx->mvCounter[MOVE_TO_INDEX(ctx->sPlyInfo[ctx->uPly-1].mv)][1])) &&
        (GetMoveFailHighPercentage(mv) <= 10))
    {
        ASSERT(!InCheck(&ctx->sPosition, ctx->sPosition.uToMove));
        uDepthPly = MINU(uRemainingDepth / ONE_PLY, MAX_PLY_PER_SEARCH);
        uMoveIdx = MINU(uLegalMoves, LMR_TABLE_MAX_MOVES);

        // Base reduction (matches the old fixed -ONE_PLY history-pruning
        // behavior) plus a graded extra on top from the table, same
        // structure as the prior validated implementation.
        iReduction = -(ONE_PLY + g_iLMRQuietReduction[uDepthPly][uMoveIdx]);

        // NOTE: tried an "improving" signal here (Crafty/Berserk/SF-style,
        // comparing static eval to 2 plies ago), both with
        // GetRoughEvalScore's material-only fallback and with a real
        // Eval() call (LAZY_EVAL-fast-pathed) feeding pi->iEval -- both
        // measured identically worse (23/30, EBF 4.252 vs this config's
        // 24/30, EBF 4.223). The signal itself isn't paying off here, not
        // just the eval-quality proxy; not worth pursuing further without
        // a different formulation.

        // Soft PV-adjacency discount (Crafty-style): if the parent node
        // (whose move loop we're in) was itself a genuine PV node, reduce
        // one ply less rather than skipping the reduction outright.
        if (TRUE == ctx->sPlyInfo[ctx->uPly - 1].fPvNode)
        {
            iReduction += ONE_PLY;
            if (iReduction > 0) iReduction = 0;
        }
        if (0 == iReduction) return(0);

        // Never reduce past leaving less than TWO_PLY of remaining depth --
        // a reduced child must still get a real search, not be treated as
        // a leaf/qsearch node by accident.
        if ((INT)uRemainingDepth + iReduction < TWO_PLY)
        {
            iReduction = -MAX((INT)uRemainingDepth - TWO_PLY, 0);
        }
        ASSERT(iReduction <= 0);
        return(iReduction);
    }
    return(0);
}