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.
// Razoring and futility margin based on depth
const int razor_margin[4] = { 483, 570, 603, 554 };
// Razoring and futility margin based on depth
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]
- Depth Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
+ int FutilityMoveCounts[2][16]; // [improving][depth]
+ 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
- Reductions[NonPV][imp][d][mc] = int(std::round(r)) * ONE_PLY;
- Reductions[PV][imp][d][mc] = std::max(Reductions[NonPV][imp][d][mc] - ONE_PLY, DEPTH_ZERO);
+ Reductions[NonPV][imp][d][mc] = int(std::round(r));
+ 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)
- Reductions[NonPV][imp][d][mc] += ONE_PLY;
+ if (!imp && Reductions[NonPV][imp][d][mc] >= 2)
+ Reductions[NonPV][imp][d][mc]++;
}
for (int d = 0; d < 16; ++d)
}
for (int d = 0; d < 16; ++d)
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.
- while (++rootDepth < DEPTH_MAX && !Signals.stop && (!Limits.depth || Threads.main()->rootDepth <= Limits.depth))
+ // Iterative deepening loop until requested to stop or the target depth is reached
+ 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
// 2nd ply (using a half-density matrix).
if (!mainThread)
{
const Row& row = HalfDensity[(idx - 1) % HalfDensitySize];
{
// Set up the new depths for the helper threads skipping on average every
// 2nd ply (using a half-density matrix).
if (!mainThread)
{
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()])
assert(PvNode || (alpha == beta - 1));
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
assert(!(PvNode && cutNode));
assert(PvNode || (alpha == beta - 1));
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
assert(!(PvNode && cutNode));
+ assert(depth / ONE_PLY * ONE_PLY == depth);
Move pv[MAX_PLY+1], quietsSearched[64];
StateInfo st;
Move pv[MAX_PLY+1], quietsSearched[64];
StateInfo st;
// Step 6. Razoring (skipped when in check)
if ( !PvNode
&& depth < 4 * ONE_PLY
// Step 6. Razoring (skipped when in check)
if ( !PvNode
&& depth < 4 * ONE_PLY
- && eval + razor_margin[depth] <= alpha
+ && eval + razor_margin[depth / ONE_PLY] <= alpha
&& ttMove == MOVE_NONE)
{
if ( depth <= ONE_PLY
&& eval + razor_margin[3 * ONE_PLY] <= alpha)
return qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO);
&& ttMove == MOVE_NONE)
{
if ( depth <= ONE_PLY
&& eval + razor_margin[3 * ONE_PLY] <= alpha)
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);
if (v <= ralpha)
return v;
Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1, DEPTH_ZERO);
if (v <= ralpha)
return v;
assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value
assert(eval - beta >= 0);
// 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);
(ss+1)->skipEarlyPruning = true;
pos.do_null_move(st);
(ss+1)->skipEarlyPruning = true;
&& !ttMove
&& (PvNode || ss->staticEval + 256 >= beta))
{
&& !ttMove
&& (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;
tte = TT.probe(posKey, ttHit);
ss->skipEarlyPruning = false;
tte = TT.probe(posKey, ttHit);
: pos.gives_check(move, ci);
moveCountPruning = depth < 16 * ONE_PLY
: pos.gives_check(move, ci);
moveCountPruning = depth < 16 * ONE_PLY
- && moveCount >= FutilityMoveCounts[improving][depth];
+ && moveCount >= FutilityMoveCounts[improving][depth / ONE_PLY];
// Step 12. Extend checks
if ( givesCheck
// Step 12. Extend checks
if ( givesCheck
&& pos.legal(move, ci.pinned))
{
Value rBeta = ttValue - 2 * depth / ONE_PLY;
&& pos.legal(move, ci.pinned))
{
Value rBeta = ttValue - 2 * depth / ONE_PLY;
+ Depth d = (depth / (2 * ONE_PLY)) * ONE_PLY;
ss->excludedMove = move;
ss->skipEarlyPruning = true;
ss->excludedMove = move;
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->excludedMove = MOVE_NONE;
ss->skipEarlyPruning = false;
ss->excludedMove = MOVE_NONE;
if (predictedDepth < 8 * ONE_PLY)
{
Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO
if (predictedDepth < 8 * ONE_PLY)
{
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)
continue;
if (pos.see_sign(move) < see_v)
continue;
r -= r ? ONE_PLY : DEPTH_ZERO;
else
{
r -= r ? ONE_PLY : DEPTH_ZERO;
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
if (cutNode)
r += 2 * ONE_PLY;
// Increase reduction for cut nodes
if (cutNode)
r += 2 * ONE_PLY;
r -= 2 * ONE_PLY;
// Decrease/increase reduction for moves with a good/bad history
r -= 2 * ONE_PLY;
// 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);
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
assert(depth <= DEPTH_ZERO);
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
assert(depth <= DEPTH_ZERO);
+ assert(depth / ONE_PLY * ONE_PLY == depth);
Move pv[MAX_PLY+1];
StateInfo st;
Move pv[MAX_PLY+1];
StateInfo st;
// 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
// age correctly even after generation8 overflows into the next cycle.
// 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
// age correctly even after generation8 overflows into the next cycle.
- if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY
- > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY)
+ if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
+ > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
replace = &tte[i];
return found = false, replace;
replace = &tte[i];
return found = false, replace;
Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; }
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); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
Bound bound() const { return (Bound)(genBound8 & 0x3); }
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
if (m || (k >> 48) != key16)
move16 = (uint16_t)m;
// Don't overwrite more valuable entries
if ( (k >> 48) != key16
// Preserve any existing move for the same position
if (m || (k >> 48) != key16)
move16 = (uint16_t)m;
// Don't overwrite more valuable entries
if ( (k >> 48) != key16
+ || d / ONE_PLY > depth8 - 4
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT)
{
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT)
{
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
+ depth8 = (int8_t)(d / ONE_PLY);
- DEPTH_ZERO = 0,
- DEPTH_QS_CHECKS = 0,
- DEPTH_QS_NO_CHECKS = -1,
- DEPTH_QS_RECAPTURES = -5,
+ DEPTH_ZERO = 0 * ONE_PLY,
+ DEPTH_QS_CHECKS = 0 * ONE_PLY,
+ DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
+ DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
- DEPTH_NONE = -6,
- DEPTH_MAX = MAX_PLY
+ DEPTH_NONE = -6 * ONE_PLY,
+ DEPTH_MAX = MAX_PLY * ONE_PLY
+static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
+
enum Square {
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,
enum Square {
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,