mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-20 20:52:18 +01:00
DroidFish: Updated stockfish engine to version DD.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
@@ -55,9 +56,6 @@ namespace {
|
||||
// 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 };
|
||||
|
||||
@@ -65,13 +63,10 @@ namespace {
|
||||
inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
|
||||
|
||||
// Futility lookup tables (initialized at startup) and their access functions
|
||||
Value FutilityMargins[16][64]; // [depth][moveNumber]
|
||||
int FutilityMoveCounts[2][32]; // [improving][depth]
|
||||
|
||||
inline Value futility_margin(Depth d, int mn) {
|
||||
|
||||
return d < 7 * ONE_PLY ? FutilityMargins[std::max(int(d), 1)][std::min(mn, 63)]
|
||||
: 2 * VALUE_INFINITE;
|
||||
inline Value futility_margin(Depth d) {
|
||||
return Value(100 * int(d));
|
||||
}
|
||||
|
||||
// Reduction lookup tables (initialized at startup) and their access function
|
||||
@@ -84,7 +79,7 @@ namespace {
|
||||
|
||||
size_t PVSize, PVIdx;
|
||||
TimeManager TimeMgr;
|
||||
int BestMoveChanges;
|
||||
double BestMoveChanges;
|
||||
Value DrawValue[COLOR_NB];
|
||||
HistoryStats History;
|
||||
GainsStats Gains;
|
||||
@@ -99,7 +94,6 @@ namespace {
|
||||
void id_loop(Position& pos);
|
||||
Value value_to_tt(Value v, int ply);
|
||||
Value value_from_tt(Value v, int ply);
|
||||
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta);
|
||||
bool allows(const Position& pos, Move first, Move second);
|
||||
bool refutes(const Position& pos, Move first, Move second);
|
||||
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
|
||||
@@ -132,7 +126,7 @@ void Search::init() {
|
||||
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;
|
||||
@@ -144,17 +138,16 @@ void Search::init() {
|
||||
|
||||
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);
|
||||
|
||||
// Init futility move count array
|
||||
for (d = 0; d < 32; d++)
|
||||
for (d = 0; d < 32; ++d)
|
||||
{
|
||||
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
|
||||
FutilityMoveCounts[1][d] = int(3.001 + 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +164,7 @@ static size_t perft(Position& pos, Depth depth) {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -237,23 +230,16 @@ void Search::think() {
|
||||
}
|
||||
|
||||
// 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"])
|
||||
@@ -304,13 +290,14 @@ namespace {
|
||||
void id_loop(Position& pos) {
|
||||
|
||||
Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
|
||||
int depth, prevBestMoveChanges;
|
||||
int depth;
|
||||
Value bestValue, alpha, beta, delta;
|
||||
|
||||
std::memset(ss-2, 0, 5 * sizeof(Stack));
|
||||
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
|
||||
|
||||
depth = BestMoveChanges = 0;
|
||||
depth = 0;
|
||||
BestMoveChanges = 0;
|
||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||
beta = VALUE_INFINITE;
|
||||
|
||||
@@ -332,16 +319,16 @@ namespace {
|
||||
// Iterative deepening loop until requested to stop or target depth reached
|
||||
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
|
||||
{
|
||||
// Age out PV variability metric
|
||||
BestMoveChanges *= 0.8;
|
||||
|
||||
// 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;
|
||||
|
||||
prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1
|
||||
BestMoveChanges = 0;
|
||||
|
||||
// MultiPV loop. We perform a full root search for each PV line
|
||||
for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
|
||||
for (PVIdx = 0; PVIdx < PVSize && !Signals.stop; ++PVIdx)
|
||||
{
|
||||
// Reset aspiration window starting size
|
||||
if (depth >= 5)
|
||||
@@ -367,14 +354,14 @@ namespace {
|
||||
|
||||
// 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
|
||||
// If search has been stopped break immediately. Sorting and
|
||||
// writing PV back to TT is safe becuase RootMoves is still
|
||||
// valid, although refers to previous iteration.
|
||||
if (Signals.stop)
|
||||
return;
|
||||
break;
|
||||
|
||||
// When failing high/low give some update (without cluttering
|
||||
// the UI) before to research.
|
||||
@@ -431,13 +418,13 @@ namespace {
|
||||
Signals.stop = true;
|
||||
|
||||
// Do we have time for the next iteration? Can we stop searching now?
|
||||
if (Limits.use_time_management() && !Signals.stopOnPonderhit)
|
||||
if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit)
|
||||
{
|
||||
bool stop = false; // Local variable, not the volatile Signals.stop
|
||||
|
||||
// Take in account some extra time if the best move has changed
|
||||
if (depth > 4 && depth < 50 && PVSize == 1)
|
||||
TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges);
|
||||
TimeMgr.pv_instability(BestMoveChanges);
|
||||
|
||||
// Stop search if most of available time is already consumed. We
|
||||
// probably don't have enough time to search the first move at the
|
||||
@@ -447,6 +434,7 @@ namespace {
|
||||
|
||||
// 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
|
||||
@@ -502,9 +490,8 @@ namespace {
|
||||
SplitPoint* splitPoint;
|
||||
Key posKey;
|
||||
Move ttMove, move, excludedMove, bestMove, threatMove;
|
||||
Depth ext, newDepth;
|
||||
Value bestValue, value, ttValue;
|
||||
Value eval, nullValue, futilityValue;
|
||||
Depth ext, newDepth, predictedDepth;
|
||||
Value bestValue, value, ttValue, eval, nullValue, futilityValue;
|
||||
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
|
||||
bool captureOrPromotion, dangerous, doFullDepthSearch;
|
||||
int moveCount, quietCount;
|
||||
@@ -532,7 +519,6 @@ namespace {
|
||||
bestValue = -VALUE_INFINITE;
|
||||
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||
ss->ply = (ss-1)->ply + 1;
|
||||
ss->futilityMoveCount = 0;
|
||||
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
|
||||
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
||||
|
||||
@@ -584,7 +570,7 @@ namespace {
|
||||
|
||||
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];
|
||||
@@ -596,32 +582,27 @@ namespace {
|
||||
// Step 5. Evaluate the position statically and update parent's gain statistics
|
||||
if (inCheck)
|
||||
{
|
||||
ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
|
||||
ss->staticEval = eval = VALUE_NONE;
|
||||
goto moves_loop;
|
||||
}
|
||||
|
||||
else if (tte)
|
||||
{
|
||||
// Never assume anything on values stored in TT
|
||||
if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE
|
||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
||||
if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE)
|
||||
eval = ss->staticEval = evaluate(pos);
|
||||
|
||||
// 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
|
||||
{
|
||||
eval = ss->staticEval = evaluate(pos, ss->evalMargin);
|
||||
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
|
||||
ss->staticEval, ss->evalMargin);
|
||||
eval = ss->staticEval = evaluate(pos);
|
||||
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
|
||||
}
|
||||
|
||||
// Update gain for the parent non-capture move given the static position
|
||||
// evaluation before and after the move.
|
||||
if ( !pos.captured_piece_type()
|
||||
&& ss->staticEval != VALUE_NONE
|
||||
&& (ss-1)->staticEval != VALUE_NONE
|
||||
@@ -648,22 +629,20 @@ namespace {
|
||||
return v;
|
||||
}
|
||||
|
||||
// Step 7. Static null move pruning (skipped when in check)
|
||||
// We're betting that the opponent doesn't have a move that will reduce
|
||||
// the score by more than futility_margin(depth) if we do a null move.
|
||||
// Step 7. Futility pruning: child node (skipped when in check)
|
||||
if ( !PvNode
|
||||
&& !ss->skipNullMove
|
||||
&& depth < 4 * ONE_PLY
|
||||
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
|
||||
&& depth < 7 * ONE_PLY
|
||||
&& eval - futility_margin(depth) >= beta
|
||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||
&& abs(eval) < VALUE_KNOWN_WIN
|
||||
&& pos.non_pawn_material(pos.side_to_move()))
|
||||
return eval - futility_margin(depth, (ss-1)->futilityMoveCount);
|
||||
return eval - futility_margin(depth);
|
||||
|
||||
// Step 8. Null move search with verification search (is omitted in PV nodes)
|
||||
if ( !PvNode
|
||||
&& !ss->skipNullMove
|
||||
&& depth > ONE_PLY
|
||||
&& depth >= 2 * ONE_PLY
|
||||
&& eval >= beta
|
||||
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
|
||||
&& pos.non_pawn_material(pos.side_to_move()))
|
||||
@@ -739,10 +718,10 @@ namespace {
|
||||
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)
|
||||
@@ -780,7 +759,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
|
||||
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)
|
||||
@@ -804,14 +783,14 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
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)
|
||||
{
|
||||
@@ -824,19 +803,16 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
}
|
||||
|
||||
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
|
||||
@@ -845,7 +821,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
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);
|
||||
@@ -864,7 +840,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// Update current move (this must be done after singular extension search)
|
||||
newDepth = depth - ONE_PLY + ext;
|
||||
|
||||
// Step 13. Futility pruning (is omitted in PV nodes)
|
||||
// Step 13. Pruning at shallow depth (exclude PV nodes)
|
||||
if ( !PvNode
|
||||
&& !captureOrPromotion
|
||||
&& !inCheck
|
||||
@@ -883,29 +859,30 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
continue;
|
||||
}
|
||||
|
||||
// Value based pruning
|
||||
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
|
||||
// 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)];
|
||||
predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
|
||||
|
||||
if (futilityValue < beta)
|
||||
// Futility pruning: parent node
|
||||
if (predictedDepth < 7 * ONE_PLY)
|
||||
{
|
||||
bestValue = std::max(bestValue, futilityValue);
|
||||
futilityValue = ss->staticEval + futility_margin(predictedDepth)
|
||||
+ Value(128) + Gains[pos.moved_piece(move)][to_sq(move)];
|
||||
|
||||
if (SpNode)
|
||||
if (futilityValue <= alpha)
|
||||
{
|
||||
splitPoint->mutex.lock();
|
||||
if (bestValue > splitPoint->bestValue)
|
||||
splitPoint->bestValue = bestValue;
|
||||
bestValue = std::max(bestValue, futilityValue);
|
||||
|
||||
if (SpNode)
|
||||
{
|
||||
splitPoint->mutex.lock();
|
||||
if (bestValue > splitPoint->bestValue)
|
||||
splitPoint->bestValue = bestValue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prune moves with negative SEE at low depths
|
||||
if ( predictedDepth < 4 * ONE_PLY
|
||||
&& pos.see_sign(move) < 0)
|
||||
if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < 0)
|
||||
{
|
||||
if (SpNode)
|
||||
splitPoint->mutex.lock();
|
||||
@@ -913,15 +890,10 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have not pruned the move that will be searched, but remember how
|
||||
// far in the move list we are to be more aggressive in the child node.
|
||||
ss->futilityMoveCount = moveCount;
|
||||
}
|
||||
else
|
||||
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--;
|
||||
continue;
|
||||
@@ -937,10 +909,9 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
|
||||
// Step 15. Reduced depth search (LMR). If the move fails high will be
|
||||
// re-searched at full depth.
|
||||
if ( depth > 3 * ONE_PLY
|
||||
if ( depth >= 3 * ONE_PLY
|
||||
&& !pvMove
|
||||
&& !captureOrPromotion
|
||||
&& !dangerous
|
||||
&& move != ttMove
|
||||
&& move != ss->killers[0]
|
||||
&& move != ss->killers[1])
|
||||
@@ -950,8 +921,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
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)
|
||||
@@ -1019,7 +993,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// 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
|
||||
@@ -1086,11 +1060,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
||||
bestValue >= beta ? BOUND_LOWER :
|
||||
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
|
||||
depth, bestMove, ss->staticEval, ss->evalMargin);
|
||||
depth, bestMove, ss->staticEval);
|
||||
|
||||
// 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)
|
||||
@@ -1102,11 +1076,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// 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))
|
||||
@@ -1179,7 +1153,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// Evaluate the position statically
|
||||
if (InCheck)
|
||||
{
|
||||
ss->staticEval = ss->evalMargin = VALUE_NONE;
|
||||
ss->staticEval = VALUE_NONE;
|
||||
bestValue = futilityBase = -VALUE_INFINITE;
|
||||
}
|
||||
else
|
||||
@@ -1187,19 +1161,23 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
if (tte)
|
||||
{
|
||||
// Never assume anything on values stored in TT
|
||||
if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE
|
||||
||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE)
|
||||
ss->staticEval = bestValue = evaluate(pos, ss->evalMargin);
|
||||
if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE)
|
||||
ss->staticEval = bestValue = evaluate(pos);
|
||||
|
||||
// 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);
|
||||
ss->staticEval = bestValue = evaluate(pos);
|
||||
|
||||
// Stand pat. Return immediately if static value is at least beta
|
||||
if (bestValue >= beta)
|
||||
{
|
||||
if (!tte)
|
||||
TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER,
|
||||
DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin);
|
||||
DEPTH_NONE, MOVE_NONE, ss->staticEval);
|
||||
|
||||
return bestValue;
|
||||
}
|
||||
@@ -1207,7 +1185,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
if (PvNode && bestValue > alpha)
|
||||
alpha = bestValue;
|
||||
|
||||
futilityBase = ss->staticEval + ss->evalMargin + Value(128);
|
||||
futilityBase = bestValue + Value(128);
|
||||
}
|
||||
|
||||
// Initialize a MovePicker object for the current position, and prepare
|
||||
@@ -1222,7 +1200,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
{
|
||||
assert(is_ok(move));
|
||||
|
||||
givesCheck = pos.move_gives_check(move, ci);
|
||||
givesCheck = pos.gives_check(move, ci);
|
||||
|
||||
// Futility pruning
|
||||
if ( !PvNode
|
||||
@@ -1230,7 +1208,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
&& !givesCheck
|
||||
&& move != ttMove
|
||||
&& type_of(move) != PROMOTION
|
||||
&& !pos.is_passed_pawn_push(move))
|
||||
&& futilityBase > -VALUE_KNOWN_WIN
|
||||
&& !pos.passed_pawn_push(move))
|
||||
{
|
||||
futilityValue = futilityBase
|
||||
+ PieceValue[EG][pos.piece_on(to_sq(move))]
|
||||
@@ -1255,7 +1234,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// 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
|
||||
@@ -1266,18 +1245,8 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
&& pos.see_sign(move) < 0)
|
||||
continue;
|
||||
|
||||
// Don't search useless checks
|
||||
if ( !PvNode
|
||||
&& !InCheck
|
||||
&& givesCheck
|
||||
&& move != ttMove
|
||||
&& !pos.is_capture_or_promotion(move)
|
||||
&& ss->staticEval + PawnValueMg / 4 < beta
|
||||
&& !check_is_dangerous(pos, move, futilityBase, beta))
|
||||
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;
|
||||
@@ -1305,7 +1274,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
else // Fail high
|
||||
{
|
||||
TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
|
||||
ttDepth, move, ss->staticEval, ss->evalMargin);
|
||||
ttDepth, move, ss->staticEval);
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -1320,7 +1289,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
|
||||
TT.store(posKey, value_to_tt(bestValue, ss->ply),
|
||||
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
|
||||
ttDepth, bestMove, ss->staticEval, ss->evalMargin);
|
||||
ttDepth, bestMove, ss->staticEval);
|
||||
|
||||
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
|
||||
|
||||
@@ -1353,42 +1322,6 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
}
|
||||
|
||||
|
||||
// check_is_dangerous() tests if a checking move can be pruned in qsearch()
|
||||
|
||||
bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta)
|
||||
{
|
||||
Piece pc = pos.piece_moved(move);
|
||||
Square from = from_sq(move);
|
||||
Square to = to_sq(move);
|
||||
Color them = ~pos.side_to_move();
|
||||
Square ksq = pos.king_square(them);
|
||||
Bitboard enemies = pos.pieces(them);
|
||||
Bitboard kingAtt = pos.attacks_from<KING>(ksq);
|
||||
Bitboard occ = pos.pieces() ^ from ^ ksq;
|
||||
Bitboard oldAtt = pos.attacks_from(pc, from, occ);
|
||||
Bitboard newAtt = pos.attacks_from(pc, to, occ);
|
||||
|
||||
// Checks which give opponent's king at most one escape square are dangerous
|
||||
if (!more_than_one(kingAtt & ~(enemies | newAtt | to)))
|
||||
return true;
|
||||
|
||||
// Queen contact check is very dangerous
|
||||
if (type_of(pc) == QUEEN && (kingAtt & to))
|
||||
return true;
|
||||
|
||||
// Creating new double threats with checks is dangerous
|
||||
Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt;
|
||||
while (b)
|
||||
{
|
||||
// Note that here we generate illegal "double move"!
|
||||
if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// allows() tests whether the 'first' move at previous ply somehow makes the
|
||||
// 'second' move possible, for instance if the moving piece is the same in
|
||||
// both moves. Normally the second move is the threat (the best move returned
|
||||
@@ -1399,7 +1332,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
assert(is_ok(first));
|
||||
assert(is_ok(second));
|
||||
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
|
||||
assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
|
||||
assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
|
||||
|
||||
Square m1from = from_sq(first);
|
||||
Square m2from = from_sq(second);
|
||||
@@ -1407,7 +1340,10 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
Square m2to = to_sq(second);
|
||||
|
||||
// The piece is the same or second's destination was vacated by the first move
|
||||
if (m1to == m2from || m2to == m1from)
|
||||
// We exclude the trivial case where a sliding piece does in two moves what
|
||||
// it could do in one move: eg. Ra1a2, Ra2a3.
|
||||
if ( m2to == m1from
|
||||
|| (m1to == m2from && !aligned(m1from, m2from, m2to)))
|
||||
return true;
|
||||
|
||||
// Second one moves through the square vacated by first one
|
||||
@@ -1450,7 +1386,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
|
||||
// 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))
|
||||
{
|
||||
@@ -1487,7 +1423,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
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
|
||||
@@ -1499,7 +1435,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
// 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;
|
||||
|
||||
@@ -1532,11 +1468,11 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
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);
|
||||
|
||||
@@ -1558,7 +1494,7 @@ moves_loop: // When in check and at SpNode search starts from here
|
||||
<< " 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());
|
||||
}
|
||||
|
||||
@@ -1591,8 +1527,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
|
||||
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(pos.side_to_move()))
|
||||
&& ply < MAX_PLY
|
||||
&& (!pos.is_draw() || ply < 2));
|
||||
|
||||
@@ -1616,7 +1552,7 @@ void RootMove::insert_pv_in_tt(Position& pos) {
|
||||
tte = TT.probe(pos.key());
|
||||
|
||||
if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries
|
||||
TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE);
|
||||
TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE);
|
||||
|
||||
assert(MoveList<LEGAL>(pos).contains(pv[ply]));
|
||||
|
||||
@@ -1773,8 +1709,8 @@ void check_time() {
|
||||
|
||||
// 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];
|
||||
|
||||
@@ -1800,7 +1736,7 @@ void check_time() {
|
||||
&& !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)
|
||||
|
||||
Reference in New Issue
Block a user