mirror of
https://github.com/sockspls/badfish
synced 2025-05-01 09:13:08 +00:00
Make engine ONE_PLY value independent
This non-functional change patch is a deep work to allow SF to be independent from the actual value of ONE_PLY (currently set to 1). I have verified SF is now independent for ONE_PLY values 1, 2, 4, 8, 16, 32 and 256. This patch gives consistency to search code and enables future work, opening the door to safely tweaking the ONE_PLY value for any reason. Verified for no speed regression at STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 95643 W: 17728 L: 17737 D: 60178 No functional change.
This commit is contained in:
parent
133808851d
commit
4c5cbb1b14
4 changed files with 45 additions and 36 deletions
|
@ -65,14 +65,14 @@ namespace {
|
||||||
|
|
||||||
// Razoring and futility margin based on depth
|
// Razoring and futility margin based on depth
|
||||||
const int razor_margin[4] = { 483, 570, 603, 554 };
|
const int razor_margin[4] = { 483, 570, 603, 554 };
|
||||||
Value futility_margin(Depth d) { return Value(150 * d); }
|
Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); }
|
||||||
|
|
||||||
// Futility and reductions lookup tables, initialized at startup
|
// Futility and reductions lookup tables, initialized at startup
|
||||||
int FutilityMoveCounts[2][16]; // [improving][depth]
|
int FutilityMoveCounts[2][16]; // [improving][depth]
|
||||||
Depth Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
|
int Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
|
||||||
|
|
||||||
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
|
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
|
||||||
return Reductions[PvNode][i][std::min(d, 63 * ONE_PLY)][std::min(mn, 63)];
|
return Reductions[PvNode][i][std::min(d / ONE_PLY, 63)][std::min(mn, 63)] * ONE_PLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skill structure is used to implement strength limit
|
// Skill structure is used to implement strength limit
|
||||||
|
@ -187,12 +187,12 @@ void Search::init() {
|
||||||
if (r < 0.80)
|
if (r < 0.80)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Reductions[NonPV][imp][d][mc] = int(std::round(r)) * ONE_PLY;
|
Reductions[NonPV][imp][d][mc] = int(std::round(r));
|
||||||
Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - ONE_PLY, DEPTH_ZERO);
|
Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - 1, 0);
|
||||||
|
|
||||||
// Increase reduction for non-PV nodes when eval is not improving
|
// Increase reduction for non-PV nodes when eval is not improving
|
||||||
if (!imp && Reductions[NonPV][imp][d][mc] >= 2 * ONE_PLY)
|
if (!imp && Reductions[NonPV][imp][d][mc] >= 2)
|
||||||
Reductions[NonPV][imp][d][mc] += ONE_PLY;
|
Reductions[NonPV][imp][d][mc]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int d = 0; d < 16; ++d)
|
for (int d = 0; d < 16; ++d)
|
||||||
|
@ -368,15 +368,17 @@ void Thread::search() {
|
||||||
|
|
||||||
multiPV = std::min(multiPV, rootMoves.size());
|
multiPV = std::min(multiPV, rootMoves.size());
|
||||||
|
|
||||||
// Iterative deepening loop until requested to stop or the target depth is reached.
|
// Iterative deepening loop until requested to stop or the target depth is reached
|
||||||
while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth <= Limits.depth))
|
while ( (rootDepth += ONE_PLY) < DEPTH_MAX
|
||||||
|
&& !Signals.stop
|
||||||
|
&& (!Limits.depth || Threads.main()->rootDepth / ONE_PLY <= Limits.depth))
|
||||||
{
|
{
|
||||||
// Set up the new depths for the helper threads skipping on average every
|
// Set up the new depths for the helper threads skipping on average every
|
||||||
// 2nd ply (using a half-density matrix).
|
// 2nd ply (using a half-density matrix).
|
||||||
if (!mainThread)
|
if (!mainThread)
|
||||||
{
|
{
|
||||||
const Row& row = HalfDensity[(idx - 1) % HalfDensitySize];
|
const Row& row = HalfDensity[(idx - 1) % HalfDensitySize];
|
||||||
if (row[(rootDepth + rootPos.game_ply()) % row.size()])
|
if (row[(rootDepth / ONE_PLY + rootPos.game_ply()) % row.size()])
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,6 +554,7 @@ namespace {
|
||||||
assert(PvNode || (alpha == beta - 1));
|
assert(PvNode || (alpha == beta - 1));
|
||||||
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
|
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
|
||||||
assert(!(PvNode && cutNode));
|
assert(!(PvNode && cutNode));
|
||||||
|
assert(depth / ONE_PLY * ONE_PLY == depth);
|
||||||
|
|
||||||
Move pv[MAX_PLY+1], quietsSearched[64];
|
Move pv[MAX_PLY+1], quietsSearched[64];
|
||||||
StateInfo st;
|
StateInfo st;
|
||||||
|
@ -723,14 +726,14 @@ namespace {
|
||||||
// Step 6. Razoring (skipped when in check)
|
// Step 6. Razoring (skipped when in check)
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& depth < 4 * ONE_PLY
|
&& depth < 4 * ONE_PLY
|
||||||
&& eval + razor_margin[depth] <= alpha
|
&& eval + razor_margin[depth / ONE_PLY] <= alpha
|
||||||
&& ttMove == MOVE_NONE)
|
&& ttMove == MOVE_NONE)
|
||||||
{
|
{
|
||||||
if ( depth <= ONE_PLY
|
if ( depth <= ONE_PLY
|
||||||
&& eval + razor_margin[3 * ONE_PLY] <= alpha)
|
&& eval + razor_margin[3 * ONE_PLY] <= alpha)
|
||||||
return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO);
|
return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO);
|
||||||
|
|
||||||
Value ralpha = alpha - razor_margin[depth];
|
Value ralpha = alpha - razor_margin[depth / ONE_PLY];
|
||||||
Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1, DEPTH_ZERO);
|
Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1, DEPTH_ZERO);
|
||||||
if (v <= ralpha)
|
if (v <= ralpha)
|
||||||
return v;
|
return v;
|
||||||
|
@ -756,7 +759,7 @@ namespace {
|
||||||
assert(eval - beta >= 0);
|
assert(eval - beta >= 0);
|
||||||
|
|
||||||
// Null move dynamic reduction based on depth and value
|
// Null move dynamic reduction based on depth and value
|
||||||
Depth R = ((823 + 67 * depth) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY;
|
Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY;
|
||||||
|
|
||||||
pos.do_null_move(st);
|
pos.do_null_move(st);
|
||||||
(ss+1)->skipEarlyPruning = true;
|
(ss+1)->skipEarlyPruning = true;
|
||||||
|
@ -821,8 +824,9 @@ namespace {
|
||||||
&& !ttMove
|
&& !ttMove
|
||||||
&& (PvNode || ss->staticEval + 256 >= beta))
|
&& (PvNode || ss->staticEval + 256 >= beta))
|
||||||
{
|
{
|
||||||
|
Depth d = (3 * depth / (4 * ONE_PLY) - 2) * ONE_PLY;
|
||||||
ss->skipEarlyPruning = true;
|
ss->skipEarlyPruning = true;
|
||||||
search<NT>(pos, ss, alpha, beta, 3 * depth / 4 - 2 * ONE_PLY, cutNode);
|
search<NT>(pos, ss, alpha, beta, d, cutNode);
|
||||||
ss->skipEarlyPruning = false;
|
ss->skipEarlyPruning = false;
|
||||||
|
|
||||||
tte = TT.probe(posKey, ttHit);
|
tte = TT.probe(posKey, ttHit);
|
||||||
|
@ -886,7 +890,7 @@ moves_loop: // When in check search starts from here
|
||||||
: pos.gives_check(move, ci);
|
: pos.gives_check(move, ci);
|
||||||
|
|
||||||
moveCountPruning = depth < 16 * ONE_PLY
|
moveCountPruning = depth < 16 * ONE_PLY
|
||||||
&& moveCount >= FutilityMoveCounts[improving][depth];
|
&& moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY];
|
||||||
|
|
||||||
// Step 12. Extend checks
|
// Step 12. Extend checks
|
||||||
if ( givesCheck
|
if ( givesCheck
|
||||||
|
@ -905,9 +909,10 @@ moves_loop: // When in check search starts from here
|
||||||
&& pos.legal(move, ci.pinned))
|
&& pos.legal(move, ci.pinned))
|
||||||
{
|
{
|
||||||
Value rBeta = ttValue - 2 * depth / ONE_PLY;
|
Value rBeta = ttValue - 2 * depth / ONE_PLY;
|
||||||
|
Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY;
|
||||||
ss->excludedMove = move;
|
ss->excludedMove = move;
|
||||||
ss->skipEarlyPruning = true;
|
ss->skipEarlyPruning = true;
|
||||||
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
|
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, d, cutNode);
|
||||||
ss->skipEarlyPruning = false;
|
ss->skipEarlyPruning = false;
|
||||||
ss->excludedMove = MOVE_NONE;
|
ss->excludedMove = MOVE_NONE;
|
||||||
|
|
||||||
|
@ -950,7 +955,7 @@ moves_loop: // When in check search starts from here
|
||||||
if (predictedDepth < 8 * ONE_PLY)
|
if (predictedDepth < 8 * ONE_PLY)
|
||||||
{
|
{
|
||||||
Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO
|
Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO
|
||||||
: -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY);
|
: -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY) / ONE_PLY;
|
||||||
|
|
||||||
if (pos.see_sign(move) < see_v)
|
if (pos.see_sign(move) < see_v)
|
||||||
continue;
|
continue;
|
||||||
|
@ -985,12 +990,6 @@ moves_loop: // When in check search starts from here
|
||||||
r -= r ? ONE_PLY : DEPTH_ZERO;
|
r -= r ? ONE_PLY : DEPTH_ZERO;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Value val = thisThread->history[moved_piece][to_sq(move)]
|
|
||||||
+ (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
|
||||||
+ (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
|
||||||
+ (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO)
|
|
||||||
+ thisThread->fromTo.get(~pos.side_to_move(), move);
|
|
||||||
|
|
||||||
// Increase reduction for cut nodes
|
// Increase reduction for cut nodes
|
||||||
if (cutNode)
|
if (cutNode)
|
||||||
r += 2 * ONE_PLY;
|
r += 2 * ONE_PLY;
|
||||||
|
@ -1005,8 +1004,13 @@ moves_loop: // When in check search starts from here
|
||||||
r -= 2 * ONE_PLY;
|
r -= 2 * ONE_PLY;
|
||||||
|
|
||||||
// Decrease/increase reduction for moves with a good/bad history
|
// Decrease/increase reduction for moves with a good/bad history
|
||||||
|
Value val = thisThread->history[moved_piece][to_sq(move)]
|
||||||
|
+ (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
||||||
|
+ (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
|
||||||
|
+ (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO)
|
||||||
|
+ thisThread->fromTo.get(~pos.side_to_move(), move);
|
||||||
int rHist = (val - 8000) / 20000;
|
int rHist = (val - 8000) / 20000;
|
||||||
r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY);
|
r = std::max(DEPTH_ZERO, (r / ONE_PLY - rHist) * ONE_PLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
Depth d = std::max(newDepth - r, ONE_PLY);
|
Depth d = std::max(newDepth - r, ONE_PLY);
|
||||||
|
@ -1181,6 +1185,7 @@ moves_loop: // When in check search starts from here
|
||||||
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
|
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
|
||||||
assert(PvNode || (alpha == beta - 1));
|
assert(PvNode || (alpha == beta - 1));
|
||||||
assert(depth <= DEPTH_ZERO);
|
assert(depth <= DEPTH_ZERO);
|
||||||
|
assert(depth / ONE_PLY * ONE_PLY == depth);
|
||||||
|
|
||||||
Move pv[MAX_PLY+1];
|
Move pv[MAX_PLY+1];
|
||||||
StateInfo st;
|
StateInfo st;
|
||||||
|
|
|
@ -92,8 +92,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||||
// nature we add 259 (256 is the modulus plus 3 to keep the lowest
|
// nature we add 259 (256 is the modulus plus 3 to keep the lowest
|
||||||
// two bound bits from affecting the result) to calculate the entry
|
// two bound bits from affecting the result) to calculate the entry
|
||||||
// age correctly even after generation8 overflows into the next cycle.
|
// age correctly even after generation8 overflows into the next cycle.
|
||||||
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY
|
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
|
||||||
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY)
|
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
|
||||||
replace = &tte[i];
|
replace = &tte[i];
|
||||||
|
|
||||||
return found = false, replace;
|
return found = false, replace;
|
||||||
|
|
8
src/tt.h
8
src/tt.h
|
@ -39,18 +39,20 @@ struct TTEntry {
|
||||||
Move move() const { return (Move )move16; }
|
Move move() const { return (Move )move16; }
|
||||||
Value value() const { return (Value)value16; }
|
Value value() const { return (Value)value16; }
|
||||||
Value eval() const { return (Value)eval16; }
|
Value eval() const { return (Value)eval16; }
|
||||||
Depth depth() const { return (Depth)depth8; }
|
Depth depth() const { return (Depth)(depth8 * ONE_PLY); }
|
||||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||||
|
|
||||||
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
|
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
|
||||||
|
|
||||||
|
assert(d / ONE_PLY * ONE_PLY == d);
|
||||||
|
|
||||||
// Preserve any existing move for the same position
|
// Preserve any existing move for the same position
|
||||||
if (m || (k >> 48) != key16)
|
if (m || (k >> 48) != key16)
|
||||||
move16 = (uint16_t)m;
|
move16 = (uint16_t)m;
|
||||||
|
|
||||||
// Don't overwrite more valuable entries
|
// Don't overwrite more valuable entries
|
||||||
if ( (k >> 48) != key16
|
if ( (k >> 48) != key16
|
||||||
|| d > depth8 - 4
|
|| d / ONE_PLY > depth8 - 4
|
||||||
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|
||||||
|| b == BOUND_EXACT)
|
|| b == BOUND_EXACT)
|
||||||
{
|
{
|
||||||
|
@ -58,7 +60,7 @@ struct TTEntry {
|
||||||
value16 = (int16_t)v;
|
value16 = (int16_t)v;
|
||||||
eval16 = (int16_t)ev;
|
eval16 = (int16_t)ev;
|
||||||
genBound8 = (uint8_t)(g | b);
|
genBound8 = (uint8_t)(g | b);
|
||||||
depth8 = (int8_t)d;
|
depth8 = (int8_t)(d / ONE_PLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/types.h
14
src/types.h
|
@ -209,15 +209,17 @@ enum Depth {
|
||||||
|
|
||||||
ONE_PLY = 1,
|
ONE_PLY = 1,
|
||||||
|
|
||||||
DEPTH_ZERO = 0,
|
DEPTH_ZERO = 0 * ONE_PLY,
|
||||||
DEPTH_QS_CHECKS = 0,
|
DEPTH_QS_CHECKS = 0 * ONE_PLY,
|
||||||
DEPTH_QS_NO_CHECKS = -1,
|
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||||
DEPTH_QS_RECAPTURES = -5,
|
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
||||||
|
|
||||||
DEPTH_NONE = -6,
|
DEPTH_NONE = -6 * ONE_PLY,
|
||||||
DEPTH_MAX = MAX_PLY
|
DEPTH_MAX = MAX_PLY * ONE_PLY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
||||||
|
|
||||||
enum Square {
|
enum Square {
|
||||||
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
||||||
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
|
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
|
||||||
|
|
Loading…
Add table
Reference in a new issue