mirror of
https://github.com/sockspls/badfish
synced 2025-04-29 16:23:09 +00:00
Eliminate ONE_PLY
Simplification that eliminates ONE_PLY, based on a suggestion in the forum that support for fractional plies has never been used, and @mcostalba's openness to the idea of eliminating it. We lose a little bit of type safety by making Depth an integer, but in return we simplify the code in search.cpp quite significantly. No functional change ------------------------------------------ The argument favoring eliminating ONE_PLY: * The term “ONE_PLY” comes up in a lot of forum posts (474 to date) https://groups.google.com/forum/?fromgroups=#!searchin/fishcooking/ONE_PLY%7Csort:relevance * There is occasionally a commit that breaks invariance of the code with respect to ONE_PLY https://groups.google.com/forum/?fromgroups=#!searchin/fishcooking/ONE_PLY%7Csort:date/fishcooking/ZIPdYj6k0fk/KdNGcPWeBgAJ * To prevent such commits, there is a Travis CI hack that doubles ONE_PLY and rechecks bench * Sustaining ONE_PLY has, alas, not resulted in any improvements to the engine, despite many individuals testing many experiments over 5 years. The strongest argument in favor of preserving ONE_PLY comes from @locutus: “If we use par example ONE_PLY=256 the parameter space is increases by the factor 256. So it seems very unlikely that the optimal setting is in the subspace of ONE_PLY=1.” There is a strong theoretical impediment to fractional depth systems: the transposition table uses depth to determine when a stored result is good enough to supply an answer for a current search. If you have fractional depths, then different pathways to the position can be at fractionally different depths. In the end, there are three separate times when a proposal to remove ONE_PLY was defeated by the suggestion to “give it a few more months.” So… it seems like time to remove this distraction from the community. See the pull request here: https://github.com/official-stockfish/Stockfish/pull/2289
This commit is contained in:
parent
328bdd0947
commit
ca7d4e9ac7
7 changed files with 86 additions and 100 deletions
|
@ -56,9 +56,6 @@ script:
|
|||
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
||||
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
|
||||
|
||||
# Verify bench number is ONE_PLY independent by doubling its value
|
||||
- sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h
|
||||
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
|
||||
#
|
||||
# Check perft and reproducible search
|
||||
- ../tests/perft.sh
|
||||
|
|
|
@ -61,7 +61,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
|
|||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
|
||||
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) {
|
||||
|
||||
assert(d > DEPTH_ZERO);
|
||||
assert(d > 0);
|
||||
|
||||
stage = pos.checkers() ? EVASION_TT : MAIN_TT;
|
||||
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
|
||||
|
@ -73,7 +73,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
|
|||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) {
|
||||
|
||||
assert(d <= DEPTH_ZERO);
|
||||
assert(d <= 0);
|
||||
|
||||
stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
|
||||
ttMove = ttm
|
||||
|
@ -206,7 +206,7 @@ top:
|
|||
endMoves = generate<QUIETS>(pos, cur);
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
|
||||
partial_insertion_sort(cur, endMoves, -4000 * depth);
|
||||
}
|
||||
|
||||
++stage;
|
||||
|
|
148
src/search.cpp
148
src/search.cpp
|
@ -64,15 +64,15 @@ namespace {
|
|||
// Razor and futility margins
|
||||
constexpr int RazorMargin = 661;
|
||||
Value futility_margin(Depth d, bool improving) {
|
||||
return Value(198 * (d / ONE_PLY - improving));
|
||||
return Value(198 * (d - improving));
|
||||
}
|
||||
|
||||
// Reductions lookup table, initialized at startup
|
||||
int Reductions[MAX_MOVES]; // [depth or moveNumber]
|
||||
|
||||
Depth reduction(bool i, Depth d, int mn) {
|
||||
int r = Reductions[d / ONE_PLY] * Reductions[mn];
|
||||
return ((r + 520) / 1024 + (!i && r > 999)) * ONE_PLY;
|
||||
int r = Reductions[d] * Reductions[mn];
|
||||
return (r + 520) / 1024 + (!i && r > 999);
|
||||
}
|
||||
|
||||
constexpr int futility_move_count(bool improving, int depth) {
|
||||
|
@ -80,8 +80,7 @@ namespace {
|
|||
}
|
||||
|
||||
// History and stats update bonus, based on depth
|
||||
int stat_bonus(Depth depth) {
|
||||
int d = depth / ONE_PLY;
|
||||
int stat_bonus(Depth d) {
|
||||
return d > 17 ? -8 : 22 * d * d + 151 * d - 140;
|
||||
}
|
||||
|
||||
|
@ -94,7 +93,7 @@ namespace {
|
|||
struct Skill {
|
||||
explicit Skill(int l) : level(l) {}
|
||||
bool enabled() const { return level < 20; }
|
||||
bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; }
|
||||
bool time_to_pick(Depth depth) const { return depth == 1 + level; }
|
||||
Move pick_best(size_t multiPV);
|
||||
|
||||
int level;
|
||||
|
@ -148,7 +147,7 @@ namespace {
|
|||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
|
||||
|
||||
template <NodeType NT>
|
||||
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO);
|
||||
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0);
|
||||
|
||||
Value value_to_tt(Value v, int ply);
|
||||
Value value_from_tt(Value v, int ply);
|
||||
|
@ -164,16 +163,16 @@ namespace {
|
|||
|
||||
StateInfo st;
|
||||
uint64_t cnt, nodes = 0;
|
||||
const bool leaf = (depth == 2 * ONE_PLY);
|
||||
const bool leaf = (depth == 2);
|
||||
|
||||
for (const auto& m : MoveList<LEGAL>(pos))
|
||||
{
|
||||
if (Root && depth <= ONE_PLY)
|
||||
if (Root && depth <= 1)
|
||||
cnt = 1, nodes++;
|
||||
else
|
||||
{
|
||||
pos.do_move(m, st);
|
||||
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
|
||||
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - 1);
|
||||
nodes += cnt;
|
||||
pos.undo_move(m);
|
||||
}
|
||||
|
@ -215,7 +214,7 @@ void MainThread::search() {
|
|||
|
||||
if (Limits.perft)
|
||||
{
|
||||
nodes = perft<true>(rootPos, Limits.perft * ONE_PLY);
|
||||
nodes = perft<true>(rootPos, Limits.perft);
|
||||
sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
|
||||
return;
|
||||
}
|
||||
|
@ -328,7 +327,7 @@ void Thread::search() {
|
|||
Move pv[MAX_PLY+1];
|
||||
Value bestValue, alpha, beta, delta;
|
||||
Move lastBestMove = MOVE_NONE;
|
||||
Depth lastBestMoveDepth = DEPTH_ZERO;
|
||||
Depth lastBestMoveDepth = 0;
|
||||
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
|
||||
double timeReduction = 1, totBestMoveChanges = 0;
|
||||
Color us = rootPos.side_to_move();
|
||||
|
@ -378,9 +377,9 @@ void Thread::search() {
|
|||
: -make_score(ct, ct / 2));
|
||||
|
||||
// Iterative deepening loop until requested to stop or the target depth is reached
|
||||
while ( (rootDepth += ONE_PLY) < DEPTH_MAX
|
||||
while ( ++rootDepth < MAX_PLY
|
||||
&& !Threads.stop
|
||||
&& !(Limits.depth && mainThread && rootDepth / ONE_PLY > Limits.depth))
|
||||
&& !(Limits.depth && mainThread && rootDepth > Limits.depth))
|
||||
{
|
||||
// Age out PV variability metric
|
||||
if (mainThread)
|
||||
|
@ -409,7 +408,7 @@ void Thread::search() {
|
|||
selDepth = 0;
|
||||
|
||||
// Reset aspiration window starting size
|
||||
if (rootDepth >= 4 * ONE_PLY)
|
||||
if (rootDepth >= 4)
|
||||
{
|
||||
Value previousScore = rootMoves[pvIdx].previousScore;
|
||||
delta = Value(23);
|
||||
|
@ -429,7 +428,7 @@ void Thread::search() {
|
|||
int failedHighCnt = 0;
|
||||
while (true)
|
||||
{
|
||||
Depth adjustedDepth = std::max(ONE_PLY, rootDepth - failedHighCnt * ONE_PLY);
|
||||
Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt);
|
||||
bestValue = ::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, false);
|
||||
|
||||
// Bring the best move to the front. It is critical that sorting
|
||||
|
@ -519,7 +518,7 @@ void Thread::search() {
|
|||
fallingEval = clamp(fallingEval, 0.5, 1.5);
|
||||
|
||||
// If the bestMove is stable over several iterations, reduce time accordingly
|
||||
timeReduction = lastBestMoveDepth + 9 * ONE_PLY < completedDepth ? 1.97 : 0.98;
|
||||
timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.97 : 0.98;
|
||||
double reduction = (1.36 + mainThread->previousTimeReduction) / (2.29 * timeReduction);
|
||||
|
||||
// Use part of the gained time from a previous stable move for the current move
|
||||
|
@ -579,14 +578,13 @@ namespace {
|
|||
}
|
||||
|
||||
// Dive into quiescence search when the depth reaches zero
|
||||
if (depth < ONE_PLY)
|
||||
if (depth <= 0)
|
||||
return qsearch<NT>(pos, ss, alpha, beta);
|
||||
|
||||
assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
|
||||
assert(PvNode || (alpha == beta - 1));
|
||||
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
|
||||
assert(0 < depth && depth < MAX_PLY);
|
||||
assert(!(PvNode && cutNode));
|
||||
assert(depth / ONE_PLY * ONE_PLY == depth);
|
||||
|
||||
Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
|
||||
StateInfo st;
|
||||
|
@ -683,7 +681,7 @@ namespace {
|
|||
|
||||
// Extra penalty for early quiet moves of the previous ply
|
||||
if ((ss-1)->moveCount <= 2 && !pos.captured_piece())
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
|
||||
}
|
||||
// Penalty for a quiet ttMove that fails low
|
||||
else if (!pos.capture_or_promotion(ttMove))
|
||||
|
@ -730,7 +728,7 @@ namespace {
|
|||
|| (b == BOUND_LOWER ? value >= beta : value <= alpha))
|
||||
{
|
||||
tte->save(posKey, value_to_tt(value, ss->ply), ttPv, b,
|
||||
std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY),
|
||||
std::min(MAX_PLY - 1, depth + 6),
|
||||
MOVE_NONE, VALUE_NONE);
|
||||
|
||||
return value;
|
||||
|
@ -785,7 +783,7 @@ namespace {
|
|||
|
||||
// Step 7. Razoring (~2 Elo)
|
||||
if ( !rootNode // The required rootNode PV handling is not available in qsearch
|
||||
&& depth < 2 * ONE_PLY
|
||||
&& depth < 2
|
||||
&& eval <= alpha - RazorMargin)
|
||||
return qsearch<NT>(pos, ss, alpha, beta);
|
||||
|
||||
|
@ -794,7 +792,7 @@ namespace {
|
|||
|
||||
// Step 8. Futility pruning: child node (~30 Elo)
|
||||
if ( !PvNode
|
||||
&& depth < 7 * ONE_PLY
|
||||
&& depth < 7
|
||||
&& eval - futility_margin(depth, improving) >= beta
|
||||
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
|
||||
return eval;
|
||||
|
@ -805,7 +803,7 @@ namespace {
|
|||
&& (ss-1)->statScore < 22661
|
||||
&& eval >= beta
|
||||
&& eval >= ss->staticEval
|
||||
&& ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 - improving * 30
|
||||
&& ss->staticEval >= beta - 33 * depth + 299 - improving * 30
|
||||
&& !excludedMove
|
||||
&& pos.non_pawn_material(us)
|
||||
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
|
||||
|
@ -813,7 +811,7 @@ namespace {
|
|||
assert(eval - beta >= 0);
|
||||
|
||||
// Null move dynamic reduction based on depth and value
|
||||
Depth R = ((835 + 70 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 185, 3)) * ONE_PLY;
|
||||
Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3);
|
||||
|
||||
ss->currentMove = MOVE_NULL;
|
||||
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
|
||||
|
@ -830,14 +828,14 @@ namespace {
|
|||
if (nullValue >= VALUE_MATE_IN_MAX_PLY)
|
||||
nullValue = beta;
|
||||
|
||||
if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13 * ONE_PLY))
|
||||
if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13))
|
||||
return nullValue;
|
||||
|
||||
assert(!thisThread->nmpMinPly); // Recursive verification is not allowed
|
||||
|
||||
// Do verification search at high depths, with null move pruning disabled
|
||||
// for us, until ply exceeds nmpMinPly.
|
||||
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / (4 * ONE_PLY);
|
||||
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4;
|
||||
thisThread->nmpColor = us;
|
||||
|
||||
Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
|
||||
|
@ -853,7 +851,7 @@ namespace {
|
|||
// If we have a good enough capture and a reduced search returns a value
|
||||
// much above beta, we can (almost) safely prune the previous move.
|
||||
if ( !PvNode
|
||||
&& depth >= 5 * ONE_PLY
|
||||
&& depth >= 5
|
||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY)
|
||||
{
|
||||
Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE);
|
||||
|
@ -869,7 +867,7 @@ namespace {
|
|||
ss->currentMove = move;
|
||||
ss->continuationHistory = &thisThread->continuationHistory[pos.moved_piece(move)][to_sq(move)];
|
||||
|
||||
assert(depth >= 5 * ONE_PLY);
|
||||
assert(depth >= 5);
|
||||
|
||||
pos.do_move(move, st);
|
||||
|
||||
|
@ -878,7 +876,7 @@ namespace {
|
|||
|
||||
// If the qsearch held, perform the regular search
|
||||
if (value >= raisedBeta)
|
||||
value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode);
|
||||
value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode);
|
||||
|
||||
pos.undo_move(move);
|
||||
|
||||
|
@ -888,9 +886,9 @@ namespace {
|
|||
}
|
||||
|
||||
// Step 11. Internal iterative deepening (~2 Elo)
|
||||
if (depth >= 7 * ONE_PLY && !ttMove)
|
||||
if (depth >= 7 && !ttMove)
|
||||
{
|
||||
search<NT>(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode);
|
||||
search<NT>(pos, ss, alpha, beta, depth - 7, cutNode);
|
||||
|
||||
tte = TT.probe(posKey, ttHit);
|
||||
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
||||
|
@ -938,13 +936,13 @@ moves_loop: // When in check, search starts from here
|
|||
ss->moveCount = ++moveCount;
|
||||
|
||||
if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
|
||||
sync_cout << "info depth " << depth / ONE_PLY
|
||||
sync_cout << "info depth " << depth
|
||||
<< " currmove " << UCI::move(move, pos.is_chess960())
|
||||
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
|
||||
if (PvNode)
|
||||
(ss+1)->pv = nullptr;
|
||||
|
||||
extension = DEPTH_ZERO;
|
||||
extension = 0;
|
||||
captureOrPromotion = pos.capture_or_promotion(move);
|
||||
movedPiece = pos.moved_piece(move);
|
||||
givesCheck = pos.gives_check(move);
|
||||
|
@ -956,28 +954,28 @@ moves_loop: // When in check, search starts from here
|
|||
// then that move is singular and should be extended. To verify this we do
|
||||
// a reduced search on all the other moves but the ttMove and if the
|
||||
// result is lower than ttValue minus a margin then we will extend the ttMove.
|
||||
if ( depth >= 6 * ONE_PLY
|
||||
if ( depth >= 6
|
||||
&& move == ttMove
|
||||
&& !rootNode
|
||||
&& !excludedMove // Avoid recursive singular search
|
||||
/* && ttValue != VALUE_NONE Already implicit in the next condition */
|
||||
&& abs(ttValue) < VALUE_KNOWN_WIN
|
||||
&& (tte->bound() & BOUND_LOWER)
|
||||
&& tte->depth() >= depth - 3 * ONE_PLY
|
||||
&& tte->depth() >= depth - 3
|
||||
&& pos.legal(move))
|
||||
{
|
||||
Value singularBeta = ttValue - 2 * depth / ONE_PLY;
|
||||
Depth halfDepth = depth / (2 * ONE_PLY) * ONE_PLY; // ONE_PLY invariant
|
||||
Value singularBeta = ttValue - 2 * depth;
|
||||
Depth halfDepth = depth / 2;
|
||||
ss->excludedMove = move;
|
||||
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode);
|
||||
ss->excludedMove = MOVE_NONE;
|
||||
|
||||
if (value < singularBeta)
|
||||
{
|
||||
extension = ONE_PLY;
|
||||
extension = 1;
|
||||
singularLMR++;
|
||||
|
||||
if (value < singularBeta - std::min(4 * depth / ONE_PLY, 36))
|
||||
if (value < singularBeta - std::min(4 * depth, 36))
|
||||
singularLMR++;
|
||||
}
|
||||
|
||||
|
@ -994,27 +992,27 @@ moves_loop: // When in check, search starts from here
|
|||
// Check extension (~2 Elo)
|
||||
else if ( givesCheck
|
||||
&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move)))
|
||||
extension = ONE_PLY;
|
||||
extension = 1;
|
||||
|
||||
// Shuffle extension
|
||||
else if ( PvNode
|
||||
&& pos.rule50_count() > 18
|
||||
&& depth < 3 * ONE_PLY
|
||||
&& depth < 3
|
||||
&& ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions
|
||||
extension = ONE_PLY;
|
||||
extension = 1;
|
||||
|
||||
// Passed pawn extension
|
||||
else if ( move == ss->killers[0]
|
||||
&& pos.advanced_pawn_push(move)
|
||||
&& pos.pawn_passed(us, to_sq(move)))
|
||||
extension = ONE_PLY;
|
||||
extension = 1;
|
||||
|
||||
// Castling extension
|
||||
if (type_of(move) == CASTLING)
|
||||
extension = ONE_PLY;
|
||||
extension = 1;
|
||||
|
||||
// Calculate new depth for this move
|
||||
newDepth = depth - ONE_PLY + extension;
|
||||
newDepth = depth - 1 + extension;
|
||||
|
||||
// Step 14. Pruning at shallow depth (~170 Elo)
|
||||
if ( !rootNode
|
||||
|
@ -1022,7 +1020,7 @@ moves_loop: // When in check, search starts from here
|
|||
&& bestValue > VALUE_MATED_IN_MAX_PLY)
|
||||
{
|
||||
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
|
||||
moveCountPruning = moveCount >= futility_move_count(improving, depth / ONE_PLY);
|
||||
moveCountPruning = moveCount >= futility_move_count(improving, depth);
|
||||
|
||||
if ( !captureOrPromotion
|
||||
&& !givesCheck
|
||||
|
@ -1033,8 +1031,7 @@ moves_loop: // When in check, search starts from here
|
|||
continue;
|
||||
|
||||
// Reduced depth of the next LMR search
|
||||
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO);
|
||||
lmrDepth /= ONE_PLY;
|
||||
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
|
||||
|
||||
// Countermoves based pruning (~20 Elo)
|
||||
if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
|
||||
|
@ -1053,7 +1050,7 @@ moves_loop: // When in check, search starts from here
|
|||
continue;
|
||||
}
|
||||
else if ( !(givesCheck && extension)
|
||||
&& !pos.see_ge(move, Value(-199) * (depth / ONE_PLY))) // (~20 Elo)
|
||||
&& !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1076,7 +1073,7 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
// Step 16. Reduced depth search (LMR). If the move fails high it will be
|
||||
// re-searched at full depth.
|
||||
if ( depth >= 3 * ONE_PLY
|
||||
if ( depth >= 3
|
||||
&& moveCount > 1 + 2 * rootNode
|
||||
&& (!rootNode || thisThread->best_move_count(move) == 0)
|
||||
&& ( !captureOrPromotion
|
||||
|
@ -1088,35 +1085,35 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
// Reduction if other threads are searching this position.
|
||||
if (th.marked())
|
||||
r += ONE_PLY;
|
||||
r++;
|
||||
|
||||
// Decrease reduction if position is or has been on the PV
|
||||
if (ttPv)
|
||||
r -= 2 * ONE_PLY;
|
||||
r -= 2;
|
||||
|
||||
// Decrease reduction if opponent's move count is high (~10 Elo)
|
||||
if ((ss-1)->moveCount > 15)
|
||||
r -= ONE_PLY;
|
||||
r--;
|
||||
|
||||
// Decrease reduction if ttMove has been singularly extended
|
||||
r -= singularLMR * ONE_PLY;
|
||||
r -= singularLMR;
|
||||
|
||||
if (!captureOrPromotion)
|
||||
{
|
||||
// Increase reduction if ttMove is a capture (~0 Elo)
|
||||
if (ttCapture)
|
||||
r += ONE_PLY;
|
||||
r++;
|
||||
|
||||
// Increase reduction for cut nodes (~5 Elo)
|
||||
if (cutNode)
|
||||
r += 2 * ONE_PLY;
|
||||
r += 2;
|
||||
|
||||
// Decrease reduction for moves that escape a capture. Filter out
|
||||
// castling moves, because they are coded as "king captures rook" and
|
||||
// hence break make_move(). (~5 Elo)
|
||||
else if ( type_of(move) == NORMAL
|
||||
&& !pos.see_ge(reverse_move(move)))
|
||||
r -= 2 * ONE_PLY;
|
||||
r -= 2;
|
||||
|
||||
ss->statScore = thisThread->mainHistory[us][from_to(move)]
|
||||
+ (*contHist[0])[movedPiece][to_sq(move)]
|
||||
|
@ -1133,16 +1130,16 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
|
||||
if (ss->statScore >= -99 && (ss-1)->statScore < -116)
|
||||
r -= ONE_PLY;
|
||||
r--;
|
||||
|
||||
else if ((ss-1)->statScore >= -117 && ss->statScore < -144)
|
||||
r += ONE_PLY;
|
||||
r++;
|
||||
|
||||
// Decrease/increase reduction for moves with a good/bad history (~30 Elo)
|
||||
r -= ss->statScore / 16384 * ONE_PLY;
|
||||
r -= ss->statScore / 16384;
|
||||
}
|
||||
|
||||
Depth d = clamp(newDepth - r, ONE_PLY, newDepth);
|
||||
Depth d = clamp(newDepth - r, 1, newDepth);
|
||||
|
||||
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
|
||||
|
||||
|
@ -1276,18 +1273,18 @@ moves_loop: // When in check, search starts from here
|
|||
// Quiet best move: update move sorting heuristics
|
||||
if (!pos.capture_or_promotion(bestMove))
|
||||
update_quiet_stats(pos, ss, bestMove, quietsSearched, quietCount,
|
||||
stat_bonus(depth + (bestValue > beta + PawnValueMg ? ONE_PLY : DEPTH_ZERO)));
|
||||
stat_bonus(depth + (bestValue > beta + PawnValueMg)));
|
||||
|
||||
update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + ONE_PLY));
|
||||
update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + 1));
|
||||
|
||||
// Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
|
||||
if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
|
||||
&& !pos.captured_piece())
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
|
||||
|
||||
}
|
||||
// Bonus for prior countermove that caused the fail low
|
||||
else if ( (depth >= 3 * ONE_PLY || PvNode)
|
||||
else if ( (depth >= 3 || PvNode)
|
||||
&& !pos.captured_piece())
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth));
|
||||
|
||||
|
@ -1315,8 +1312,7 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
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);
|
||||
assert(depth <= 0);
|
||||
|
||||
Move pv[MAX_PLY+1];
|
||||
StateInfo st;
|
||||
|
@ -1455,7 +1451,7 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
// Detect non-capture evasions that are candidates to be pruned
|
||||
evasionPrunable = inCheck
|
||||
&& (depth != DEPTH_ZERO || moveCount > 2)
|
||||
&& (depth != 0 || moveCount > 2)
|
||||
&& bestValue > VALUE_MATED_IN_MAX_PLY
|
||||
&& !pos.capture(move);
|
||||
|
||||
|
@ -1480,7 +1476,7 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
// Make and search the move
|
||||
pos.do_move(move, st, givesCheck);
|
||||
value = -qsearch<NT>(pos, ss+1, -beta, -alpha, depth - ONE_PLY);
|
||||
value = -qsearch<NT>(pos, ss+1, -beta, -alpha, depth - 1);
|
||||
pos.undo_move(move);
|
||||
|
||||
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
|
||||
|
@ -1707,10 +1703,10 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
{
|
||||
bool updated = (i <= pvIdx && rootMoves[i].score != -VALUE_INFINITE);
|
||||
|
||||
if (depth == ONE_PLY && !updated)
|
||||
if (depth == 1 && !updated)
|
||||
continue;
|
||||
|
||||
Depth d = updated ? depth : depth - ONE_PLY;
|
||||
Depth d = updated ? depth : depth - 1;
|
||||
Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore;
|
||||
|
||||
bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY;
|
||||
|
@ -1720,7 +1716,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
|||
ss << "\n";
|
||||
|
||||
ss << "info"
|
||||
<< " depth " << d / ONE_PLY
|
||||
<< " depth " << d
|
||||
<< " seldepth " << rootMoves[i].selDepth
|
||||
<< " multipv " << i + 1
|
||||
<< " score " << UCI::value(v);
|
||||
|
@ -1779,7 +1775,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
|
|||
|
||||
RootInTB = false;
|
||||
UseRule50 = bool(Options["Syzygy50MoveRule"]);
|
||||
ProbeDepth = int(Options["SyzygyProbeDepth"]) * ONE_PLY;
|
||||
ProbeDepth = int(Options["SyzygyProbeDepth"]);
|
||||
Cardinality = int(Options["SyzygyProbeLimit"]);
|
||||
bool dtz_available = true;
|
||||
|
||||
|
@ -1788,7 +1784,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
|
|||
if (Cardinality > MaxCardinality)
|
||||
{
|
||||
Cardinality = MaxCardinality;
|
||||
ProbeDepth = DEPTH_ZERO;
|
||||
ProbeDepth = 0;
|
||||
}
|
||||
|
||||
if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
|
||||
|
|
|
@ -204,7 +204,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
|||
for (Thread* th : *this)
|
||||
{
|
||||
th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
|
||||
th->rootDepth = th->completedDepth = DEPTH_ZERO;
|
||||
th->rootDepth = th->completedDepth = 0;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
}
|
||||
|
|
|
@ -35,24 +35,22 @@ TranspositionTable TT; // Our global transposition table
|
|||
|
||||
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
|
||||
|
||||
assert(d / ONE_PLY * ONE_PLY == d);
|
||||
|
||||
// Preserve any existing move for the same position
|
||||
if (m || (k >> 48) != key16)
|
||||
move16 = (uint16_t)m;
|
||||
|
||||
// Overwrite less valuable entries
|
||||
if ( (k >> 48) != key16
|
||||
||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4
|
||||
|| d - DEPTH_OFFSET > depth8 - 4
|
||||
|| b == BOUND_EXACT)
|
||||
{
|
||||
assert((d - DEPTH_OFFSET) / ONE_PLY >= 0);
|
||||
assert(d >= DEPTH_OFFSET);
|
||||
|
||||
key16 = (uint16_t)(k >> 48);
|
||||
value16 = (int16_t)v;
|
||||
eval16 = (int16_t)ev;
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY);
|
||||
depth8 = (uint8_t)(d - DEPTH_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
src/tt.h
2
src/tt.h
|
@ -40,7 +40,7 @@ struct TTEntry {
|
|||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval() const { return (Value)eval16; }
|
||||
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; }
|
||||
Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
|
||||
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
|
||||
|
|
17
src/types.h
17
src/types.h
|
@ -203,22 +203,18 @@ enum Piece {
|
|||
|
||||
extern Value PieceValue[PHASE_NB][PIECE_NB];
|
||||
|
||||
enum Depth : int {
|
||||
typedef int Depth;
|
||||
|
||||
ONE_PLY = 1,
|
||||
enum : int {
|
||||
|
||||
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_QS_CHECKS = 0,
|
||||
DEPTH_QS_NO_CHECKS = -1,
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6 * ONE_PLY,
|
||||
DEPTH_NONE = -6,
|
||||
DEPTH_OFFSET = DEPTH_NONE,
|
||||
DEPTH_MAX = MAX_PLY * ONE_PLY
|
||||
};
|
||||
|
||||
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
||||
|
||||
enum Square : int {
|
||||
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,
|
||||
|
@ -298,7 +294,6 @@ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
|
|||
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
|
||||
|
||||
ENABLE_FULL_OPERATORS_ON(Value)
|
||||
ENABLE_FULL_OPERATORS_ON(Depth)
|
||||
ENABLE_FULL_OPERATORS_ON(Direction)
|
||||
|
||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||
|
|
Loading…
Add table
Reference in a new issue