#include <algorithm>
#include <cassert>
+#include <cfloat>
#include <cmath>
#include <cstring>
#include <iostream>
// Set to true to force running with one thread. Used for debugging
const bool FakeSplit = false;
- // This is the minimum interval in msec between two check_time() calls
- const int TimerResolution = 5;
-
// Different node types, used as template parameter
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
size_t PVSize, PVIdx;
TimeManager TimeMgr;
- float BestMoveChanges;
+ double BestMoveChanges;
Value DrawValue[COLOR_NB];
HistoryStats History;
GainsStats Gains;
int mc; // moveCount
// Init reductions array
- for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
+ for (hd = 1; hd < 64; ++hd) for (mc = 1; mc < 64; ++mc)
{
double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
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;
}
// Init futility margins array
- for (d = 1; d < 16; d++) for (mc = 0; mc < 64; mc++)
- FutilityMargins[d][mc] = Value(112 * int(log(double(d * d) / 2) / log(2.0) + 1.001) - 8 * mc + 45);
+ for (d = 1; d < 16; ++d) for (mc = 0; mc < 64; ++mc)
+ FutilityMargins[d][mc] = Value(112 * int(2.9 * log(double(d))) - 8 * mc + 45);
// Init futility move count array
- for (d = 0; d < 32; d++)
+ for (d = 0; d < 32; ++d)
{
- FutilityMoveCounts[0][d] = int(3 + 0.3 * pow(double(d ), 1.8)) * 3/4 + (2 < d && d < 5);
- FutilityMoveCounts[1][d] = int(3 + 0.3 * pow(double(d + 0.98), 1.8));
+ FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.0, 1.8));
+ FutilityMoveCounts[1][d] = int(3.0 + 0.3 * pow(d + 0.98, 1.8));
}
}
for (MoveList<LEGAL> it(pos); *it; ++it)
{
- pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci));
+ pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(*it);
}
}
// Reset the threads, still sleeping: will be wake up at split time
- for (size_t i = 0; i < Threads.size(); i++)
+ for (size_t i = 0; i < Threads.size(); ++i)
Threads[i]->maxPly = 0;
Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
-
- // Set best timer interval to avoid lagging under time pressure. Timer is
- // used to check for remaining available thinking time.
- Threads.timer->msec =
- Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) :
- Limits.nodes ? 2 * TimerResolution
- : 100;
-
+ Threads.timer->run = true;
Threads.timer->notify_one(); // Wake up the recurring timer
id_loop(RootPos); // Let's start searching !
- Threads.timer->msec = 0; // Stop the timer
+ Threads.timer->run = false; // Stop the timer
Threads.sleepWhileIdle = true; // Send idle threads to sleep
if (Options["Write Search Log"])
// Save last iteration's scores before first PV line is searched and all
// the move scores but the (new) PV are set to -VALUE_INFINITE.
- for (size_t i = 0; i < RootMoves.size(); i++)
+ for (size_t i = 0; i < RootMoves.size(); ++i)
RootMoves[i].prevScore = RootMoves[i].score;
// MultiPV loop. We perform a full root search for each PV line
- for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
+ for (PVIdx = 0; PVIdx < PVSize; ++PVIdx)
{
// Reset aspiration window starting size
if (depth >= 5)
// Write PV back to transposition table in case the relevant
// entries have been overwritten during the search.
- for (size_t i = 0; i <= PVIdx; i++)
+ for (size_t i = 0; i <= PVIdx; ++i)
RootMoves[i].insert_pv_in_tt(pos);
// If search has been stopped return immediately. Sorting and
// Stop search early if one move seems to be much better than others
if ( depth >= 12
+ && BestMoveChanges <= DBL_EPSILON
&& !stop
&& PVSize == 1
&& bestValue > VALUE_MATED_IN_MAX_PLY
if ( ttValue >= beta
&& ttMove
- && !pos.is_capture_or_promotion(ttMove)
+ && !pos.capture_or_promotion(ttMove)
&& ttMove != ss->killers[0])
{
ss->killers[1] = ss->killers[0];
// Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE)
- if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
- || ((tte->bound() & BOUND_UPPER) && ttValue < eval))
+ if (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))
eval = ttValue;
}
else
CheckInfo ci(pos);
while ((move = mp.next_move<false>()) != MOVE_NONE)
- if (pos.pl_move_is_legal(move, ci.pinned))
+ if (pos.legal(move, ci.pinned))
{
ss->currentMove = move;
- pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
+ pos.do_move(move, st, ci, pos.gives_check(move, ci));
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move);
if (value >= rbeta)
singularExtensionNode = !RootNode
&& !SpNode
- && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
+ && depth >= 8 * ONE_PLY
&& ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed
&& (tte->bound() & BOUND_LOWER)
if (SpNode)
{
// Shared counter cannot be decremented later if move turns out to be illegal
- if (!pos.pl_move_is_legal(move, ci.pinned))
+ if (!pos.legal(move, ci.pinned))
continue;
moveCount = ++splitPoint->moveCount;
splitPoint->mutex.unlock();
}
else
- moveCount++;
+ ++moveCount;
if (RootNode)
{
}
ext = DEPTH_ZERO;
- captureOrPromotion = pos.is_capture_or_promotion(move);
- givesCheck = pos.move_gives_check(move, ci);
+ captureOrPromotion = pos.capture_or_promotion(move);
+ givesCheck = pos.gives_check(move, ci);
dangerous = givesCheck
- || pos.is_passed_pawn_push(move)
+ || pos.passed_pawn_push(move)
|| type_of(move) == CASTLE;
- // Step 12. Extend checks and, in PV nodes, also dangerous moves
- if (PvNode && dangerous)
+ // Step 12. Extend checks
+ if (givesCheck && pos.see_sign(move) >= 0)
ext = ONE_PLY;
- else if (givesCheck && pos.see_sign(move) >= 0)
- ext = ONE_PLY / 2;
-
// Singular extension search. If all moves but one fail low on a search of
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
// is singular and should be extended. To verify this we do a reduced search
if ( singularExtensionNode
&& move == ttMove
&& !ext
- && pos.pl_move_is_legal(move, ci.pinned)
+ && pos.legal(move, ci.pinned)
&& abs(ttValue) < VALUE_KNOWN_WIN)
{
assert(ttValue != VALUE_NONE);
// but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
- + Gains[pos.piece_moved(move)][to_sq(move)];
+ + Gains[pos.moved_piece(move)][to_sq(move)];
if (futilityValue < beta)
{
ss->futilityMoveCount = 0;
// Check for legality only before to do the move
- if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned))
+ if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
{
- moveCount--;
+ --moveCount;
continue;
}
if (!PvNode && cutNode)
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);
+ ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode)
// iteration. This information is used for time management: When
// the best move changes frequently, we allocate some more time.
if (!pvMove)
- BestMoveChanges++;
+ ++BestMoveChanges;
}
else
// All other moves but the PV are set to the lowest value, this
// Quiet best move: update killers, history and countermoves
if ( bestValue >= beta
- && !pos.is_capture_or_promotion(bestMove)
+ && !pos.capture_or_promotion(bestMove)
&& !inCheck)
{
if (ss->killers[0] != bestMove)
// Increase history value of the cut-off move and decrease all the other
// played non-capture moves.
Value bonus = Value(int(depth) * int(depth));
- History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
- for (int i = 0; i < quietCount - 1; i++)
+ History.update(pos.moved_piece(bestMove), to_sq(bestMove), bonus);
+ for (int i = 0; i < quietCount - 1; ++i)
{
Move m = quietsSearched[i];
- History.update(pos.piece_moved(m), to_sq(m), -bonus);
+ History.update(pos.moved_piece(m), to_sq(m), -bonus);
}
if (is_ok((ss-1)->currentMove))
if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
+
+ // Can ttValue be used as a better position evaluation?
+ if (ttValue != VALUE_NONE)
+ if (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))
+ bestValue = ttValue;
}
else
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
if (PvNode && bestValue > alpha)
alpha = bestValue;
- futilityBase = ss->staticEval + ss->evalMargin + Value(128);
+ futilityBase = bestValue + ss->evalMargin + Value(128);
}
// Initialize a MovePicker object for the current position, and prepare
{
assert(is_ok(move));
- givesCheck = pos.move_gives_check(move, ci);
+ givesCheck = pos.gives_check(move, ci);
// Futility pruning
if ( !PvNode
&& move != ttMove
&& type_of(move) != PROMOTION
&& futilityBase > -VALUE_KNOWN_WIN
- && !pos.is_passed_pawn_push(move))
+ && !pos.passed_pawn_push(move))
{
futilityValue = futilityBase
+ PieceValue[EG][pos.piece_on(to_sq(move))]
// Detect non-capture evasions that are candidate to be pruned
evasionPrunable = InCheck
&& bestValue > VALUE_MATED_IN_MAX_PLY
- && !pos.is_capture(move)
+ && !pos.capture(move)
&& !pos.can_castle(pos.side_to_move());
// Don't search moves with negative SEE values
continue;
// Check for legality only before to do the move
- if (!pos.pl_move_is_legal(move, ci.pinned))
+ if (!pos.legal(move, ci.pinned))
continue;
ss->currentMove = move;
// If the threatened piece has value less than or equal to the value of the
// threat piece, don't prune moves which defend it.
- if ( pos.is_capture(second)
+ if ( pos.capture(second)
&& ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)]
|| type_of(pos.piece_on(m2from)) == KING))
{
static RKISS rk;
// PRNG sequence should be not deterministic
- for (int i = Time::now() % 50; i > 0; i--)
+ for (int i = Time::now() % 50; i > 0; --i)
rk.rand<unsigned>();
// RootMoves are already sorted by score in descending order
// Choose best move. For each move score we add two terms both dependent on
// weakness, one deterministic and bigger for weaker moves, and one random,
// then we choose the move with the resulting highest score.
- for (size_t i = 0; i < PVSize; i++)
+ for (size_t i = 0; i < PVSize; ++i)
{
int s = RootMoves[i].score;
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
int selDepth = 0;
- for (size_t i = 0; i < Threads.size(); i++)
+ for (size_t i = 0; i < Threads.size(); ++i)
if (Threads[i]->maxPly > selDepth)
selDepth = Threads[i]->maxPly;
- for (size_t i = 0; i < uciPVSize; i++)
+ for (size_t i = 0; i < uciPVSize; ++i)
{
bool updated = (i <= PVIdx);
<< " multipv " << i + 1
<< " pv";
- for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++)
+ for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; ++j)
s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960());
}
tte = TT.probe(pos.key());
} while ( tte
- && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change
- && pos.pl_move_is_legal(m, pos.pinned_pieces())
+ && pos.pseudo_legal(m = tte->move()) // Local copy, TT could change
+ && pos.legal(m, pos.pinned_pieces())
&& ply < MAX_PLY
&& (!pos.is_draw() || ply < 2));
// Loop across all split points and sum accumulated SplitPoint nodes plus
// all the currently active positions nodes.
- for (size_t i = 0; i < Threads.size(); i++)
- for (int j = 0; j < Threads[i]->splitPointsSize; j++)
+ for (size_t i = 0; i < Threads.size(); ++i)
+ for (int j = 0; j < Threads[i]->splitPointsSize; ++j)
{
SplitPoint& sp = Threads[i]->splitPoints[j];
&& !Signals.failedLowAtRoot
&& elapsed > TimeMgr.available_time();
- bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerResolution
+ bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution
|| stillAtFirstMove;
if ( (Limits.use_time_management() && noMoreTime)