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);
}
|