#include <algorithm>
#include <cassert>
-#include <cfloat>
#include <cmath>
#include <cstring>
#include <iostream>
enum NodeType { Root, PV, NonPV };
// Dynamic razoring margin based on depth
- inline Value razor_margin(Depth d) { return Value(512 + 16 * d); }
+ inline Value razor_margin(Depth d) { return Value(512 + 32 * d); }
// Futility lookup tables (initialized at startup) and their access functions
int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d) {
- return Value(100 * d);
+ return Value(200 * d);
}
// Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
-
- return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
+ return (Depth) Reductions[PvNode][i][std::min(int(d), 63)][std::min(mn, 63)];
}
size_t PVIdx;
{
double pvRed = 0.00 + log(double(hd)) * log(double(mc)) / 3.00;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
- Reductions[1][1][hd][mc] = int8_t( pvRed >= 1.0 ? pvRed * int(ONE_PLY) : 0);
- Reductions[0][1][hd][mc] = int8_t(nonPVRed >= 1.0 ? nonPVRed * int(ONE_PLY) : 0);
+
+ Reductions[1][1][hd][mc] = int8_t( pvRed >= 1.0 ? pvRed + 0.5: 0);
+ Reductions[0][1][hd][mc] = int8_t(nonPVRed >= 1.0 ? nonPVRed + 0.5: 0);
Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc];
Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc];
- if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
- Reductions[0][0][hd][mc] += ONE_PLY;
-
- else if (Reductions[0][0][hd][mc] > 1 * ONE_PLY)
- Reductions[0][0][hd][mc] += ONE_PLY / 2;
+ if (Reductions[0][0][hd][mc] >= 2)
+ Reductions[0][0][hd][mc] += 1;
}
// Init futility move count array
for (d = 0; d < 32; ++d)
{
- FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.00, 1.8));
- FutilityMoveCounts[1][d] = int(3.0 + 0.300 * pow(d + 0.98, 1.8));
+ FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8));
+ FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8));
}
}
StateInfo st;
uint64_t cnt, nodes = 0;
CheckInfo ci(pos);
- const bool leaf = depth == 2 * ONE_PLY;
+ const bool leaf = (depth == 2 * ONE_PLY);
for (MoveList<LEGAL> it(pos); *it; ++it)
{
TimeMgr.init(Limits, RootPos.game_ply(), RootPos.side_to_move());
- int cf = Options["Contempt Factor"] * PawnValueEg / 100; // From centipawns
+ int cf = Options["Contempt"] * PawnValueEg / 100; // From centipawns
DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(cf);
DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(cf);
goto finalize;
}
- if (Options["Write Search Log"])
- {
- Log log(Options["Search Log Filename"]);
- log << "\nSearching: " << RootPos.fen()
- << "\ninfinite: " << Limits.infinite
- << " ponder: " << Limits.ponder
- << " time: " << Limits.time[RootPos.side_to_move()]
- << " increment: " << Limits.inc[RootPos.side_to_move()]
- << " moves to go: " << Limits.movestogo
- << "\n" << std::endl;
- }
-
// Reset the threads, still sleeping: will wake up at split time
for (size_t i = 0; i < Threads.size(); ++i)
Threads[i]->maxPly = 0;
Threads.timer->run = false; // Stop the timer
- if (Options["Write Search Log"])
- {
- Time::point elapsed = Time::now() - SearchTime + 1;
-
- Log log(Options["Search Log Filename"]);
- log << "Nodes: " << RootPos.nodes_searched()
- << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed
- << "\nBest move: " << move_to_uci(RootMoves[0].pv[0], RootPos.is_chess960())
- << "\nPonder move: " << move_to_uci(RootMoves[0].pv[1], RootPos.is_chess960())
- << std::endl;
- }
-
finalize:
// When search is stopped this info is not printed
if (skill.candidates_size() && skill.time_to_pick(depth))
skill.pick_move();
- if (Options["Write Search Log"])
- {
- RootMove& rm = RootMoves[0];
- if (skill.best != MOVE_NONE)
- rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
-
- Log log(Options["Search Log Filename"]);
- log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
- << std::endl;
- }
-
// Have we found a "mate in x"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
&& !ss->skipNullMove
&& depth < 7 * ONE_PLY
&& eval - futility_margin(depth) >= beta
- && abs(beta) < VALUE_MATE_IN_MAX_PLY
- && abs(eval) < VALUE_KNOWN_WIN
+ && eval < VALUE_KNOWN_WIN // Do not return unproven wins
&& pos.non_pawn_material(pos.side_to_move()))
return eval - futility_margin(depth);
assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value
- Depth R = 3 * ONE_PLY
- + depth / 4
- + (abs(beta) < VALUE_KNOWN_WIN ? int(eval - beta) / PawnValueMg * ONE_PLY
- : DEPTH_ZERO);
+ Depth R = (3 + depth / 4 + std::min(int(eval - beta) / PawnValueMg, 3)) * ONE_PLY;
pos.do_null_move(st);
(ss+1)->skipNullMove = true;
&& !ttMove
&& (PvNode || ss->staticEval + 256 >= beta))
{
- Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
-
+ Depth d = 2 * (depth - 2 * ONE_PLY) - (PvNode ? DEPTH_ZERO : depth / 2);
ss->skipNullMove = true;
- search<PvNode ? PV : NonPV, false>(pos, ss, alpha, beta, d, true);
+ search<PvNode ? PV : NonPV, false>(pos, ss, alpha, beta, d / 2, true);
ss->skipNullMove = false;
tte = TT.probe(posKey);
singularExtensionNode = !RootNode
&& !SpNode
&& depth >= 8 * ONE_PLY
- && abs(beta) < VALUE_KNOWN_WIN
&& ttMove != MOVE_NONE
/* && ttValue != VALUE_NONE Already implicit in the next condition */
&& abs(ttValue) < VALUE_KNOWN_WIN
Signals.firstRootMove = (moveCount == 1);
if (thisThread == Threads.main() && Time::now() - SearchTime > 3000)
- sync_cout << "info depth " << depth / ONE_PLY
+ sync_cout << "info depth " << depth
<< " currmove " << move_to_uci(move, pos.is_chess960())
<< " currmovenumber " << moveCount + PVIdx << sync_endl;
}
&& !ext
&& pos.legal(move, ci.pinned))
{
- Value rBeta = ttValue - int(depth);
+ Value rBeta = ttValue - int(2 * depth);
ss->excludedMove = move;
ss->skipNullMove = true;
value = search<NonPV, false>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
{
// Move count based pruning
if ( depth < 16 * ONE_PLY
- && moveCount >= FutilityMoveCounts[improving][depth] )
+ && moveCount >= FutilityMoveCounts[improving][depth])
{
if (SpNode)
splitPoint->mutex.lock();
// Futility pruning: parent node
if (predictedDepth < 7 * ONE_PLY)
{
- futilityValue = ss->staticEval + futility_margin(predictedDepth)
- + 128 + Gains[pos.moved_piece(move)][to_sq(move)];
+ futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ + 128 + Gains[pos.moved_piece(move)][to_sq(move)];
if (futilityValue <= alpha)
{
}
}
+ // Speculative prefetch as early as possible
+ prefetch((char*)TT.first_entry(pos.key_after(move)));
+
// Check for legality just before making the move
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
{
{
ss->reduction = reduction<PvNode>(improving, depth, moveCount);
- if (!PvNode && cutNode)
+ if ( (!PvNode && cutNode)
+ || History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
ss->reduction += ONE_PLY;
- else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
- ss->reduction += ONE_PLY / 2;
-
if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
if (SpNode)
alpha = splitPoint->alpha;
- value = newDepth < ONE_PLY ?
- givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : - search<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
+ value = newDepth < ONE_PLY ?
+ givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+ : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
+ : - search<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
}
// For PV nodes only, do a full PV search on the first move or after a fail
// high (in the latter case search only if value < beta), otherwise let the
// parent node fail low with value <= alpha and to try another move.
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
- value = newDepth < ONE_PLY ?
- givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
- : -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
- : - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false);
+ value = newDepth < ONE_PLY ?
+ givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
+ : -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
+ : - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo move
pos.undo_move(move);
&& pos.see_sign(move) < VALUE_ZERO)
continue;
+ // Speculative prefetch as early as possible
+ prefetch((char*)TT.first_entry(pos.key_after(move)));
+
// Check for legality just before making the move
if (!pos.legal(move, ci.pinned))
continue;
// Increase history value of the cut-off move and decrease all the other
// played quiet moves.
- Value bonus = Value(int(depth) * int(depth));
+ Value bonus = Value(4 * int(depth) * int(depth));
History.update(pos.moved_piece(move), to_sq(move), bonus);
for (int i = 0; i < quietsCnt; ++i)
{
assert(!this_sp || (this_sp->masterThread == this && searching));
- while (true)
+ while (!exit)
{
- // If we are not searching, wait for a condition to be signaled instead of
- // wasting CPU time polling for work.
- while (!searching || exit)
- {
- if (exit)
- {
- assert(!this_sp);
- return;
- }
-
- // Grab the lock to avoid races with Thread::notify_one()
- mutex.lock();
-
- // If we are master and all slaves have finished then exit idle_loop
- if (this_sp && this_sp->slavesMask.none())
- {
- mutex.unlock();
- break;
- }
-
- // Do sleep after retesting sleep conditions under lock protection. In
- // particular we need to avoid a deadlock in case a master thread has,
- // in the meanwhile, allocated us and sent the notify_one() call before
- // we had the chance to grab the lock.
- if (!searching && !exit)
- sleepCondition.wait(mutex);
-
- mutex.unlock();
- }
-
// If this thread has been assigned work, launch a search
- if (searching)
+ while (searching)
{
- assert(!exit);
-
Threads.mutex.lock();
- assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint;
}
}
- // If this thread is the master of a split point and all slaves have finished
- // their work at this split point, return from the idle loop.
+ // Grab the lock to avoid races with Thread::notify_one()
+ mutex.lock();
+
+ // If we are master and all slaves have finished then exit idle_loop
if (this_sp && this_sp->slavesMask.none())
{
- this_sp->mutex.lock();
- bool finished = this_sp->slavesMask.none(); // Retest under lock protection
- this_sp->mutex.unlock();
- if (finished)
- return;
+ assert(!searching);
+ mutex.unlock();
+ break;
}
+
+ // If we are not searching, wait for a condition to be signaled instead of
+ // wasting CPU time polling for work.
+ if (!searching && !exit)
+ sleepCondition.wait(mutex);
+
+ mutex.unlock();
}
}