Merge branch 'official-stockfish:master' into master
commit
ea54c455db
|
@ -1076,33 +1076,22 @@ Value Eval::evaluate(const Position& pos) {
|
||||||
|
|
||||||
Value v;
|
Value v;
|
||||||
|
|
||||||
if (!useNNUE)
|
// Deciding between classical and NNUE eval: for high PSQ imbalance we use classical,
|
||||||
v = Evaluation<NO_TRACE>(pos).value();
|
// but we switch to NNUE during long shuffling or with high material on the board.
|
||||||
|
|
||||||
|
if ( !useNNUE
|
||||||
|
|| abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count()))
|
||||||
|
v = Evaluation<NO_TRACE>(pos).value(); // classical
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Scale and shift NNUE for compatibility with search and classical evaluation
|
int scale = 883
|
||||||
auto adjusted_NNUE = [&]()
|
+ 32 * pos.count<PAWN>()
|
||||||
{
|
+ 32 * pos.non_pawn_material() / 1024;
|
||||||
int scale = 883
|
|
||||||
+ 32 * pos.count<PAWN>()
|
|
||||||
+ 32 * pos.non_pawn_material() / 1024;
|
|
||||||
|
|
||||||
Value nnue = NNUE::evaluate(pos, true) * scale / 1024;
|
v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE
|
||||||
|
|
||||||
if (pos.is_chess960())
|
if (pos.is_chess960())
|
||||||
nnue += fix_FRC(pos);
|
v += fix_FRC(pos);
|
||||||
|
|
||||||
return nnue;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If there is PSQ imbalance we use the classical eval, but we switch to
|
|
||||||
// NNUE eval faster when shuffling or if the material on the board is high.
|
|
||||||
int r50 = pos.rule50_count();
|
|
||||||
Value psq = Value(abs(eg_value(pos.psq_score())));
|
|
||||||
bool classical = psq * 5 > (850 + pos.non_pawn_material() / 64) * (5 + r50);
|
|
||||||
|
|
||||||
v = classical ? Evaluation<NO_TRACE>(pos).value() // classical
|
|
||||||
: adjusted_NNUE(); // NNUE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damp down the evaluation linearly when shuffling
|
// Damp down the evaluation linearly when shuffling
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace {
|
||||||
|
|
||||||
/// Version number. If Version is left empty, then compile date in the format
|
/// Version number. If Version is left empty, then compile date in the format
|
||||||
/// DD-MM-YY and show in engine_info.
|
/// DD-MM-YY and show in engine_info.
|
||||||
const string Version = "";
|
const string Version = "14.1";
|
||||||
|
|
||||||
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||||
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||||
|
|
17
src/misc.h
17
src/misc.h
|
@ -184,22 +184,6 @@ class RunningAverage {
|
||||||
int64_t average;
|
int64_t average;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class ValueListInserter {
|
|
||||||
public:
|
|
||||||
ValueListInserter(T* v, std::size_t& s) :
|
|
||||||
values(v),
|
|
||||||
size(&s)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(const T& value) { values[(*size)++] = value; }
|
|
||||||
private:
|
|
||||||
T* values;
|
|
||||||
std::size_t* size;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, std::size_t MaxSize>
|
template <typename T, std::size_t MaxSize>
|
||||||
class ValueList {
|
class ValueList {
|
||||||
|
|
||||||
|
@ -213,7 +197,6 @@ public:
|
||||||
const T& operator[](std::size_t index) const { return values_[index]; }
|
const T& operator[](std::size_t index) const { return values_[index]; }
|
||||||
const T* begin() const { return values_; }
|
const T* begin() const { return values_; }
|
||||||
const T* end() const { return values_ + size_; }
|
const T* end() const { return values_ + size_; }
|
||||||
operator ValueListInserter<T>() { return ValueListInserter(values_, size_); }
|
|
||||||
|
|
||||||
void swap(ValueList& other) {
|
void swap(ValueList& other) {
|
||||||
const std::size_t maxSize = std::max(size_, other.size_);
|
const std::size_t maxSize = std::max(size_, other.size_);
|
||||||
|
|
|
@ -86,7 +86,7 @@ enum StatsType { NoCaptures, Captures };
|
||||||
/// unsuccessful during the current search, and is used for reduction and move
|
/// unsuccessful during the current search, and is used for reduction and move
|
||||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||||
typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
typedef Stats<int16_t, 14365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
||||||
|
|
||||||
/// At higher depths LowPlyHistory records successful quiet moves near the root
|
/// At higher depths LowPlyHistory records successful quiet moves near the root
|
||||||
/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during
|
/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during
|
||||||
|
|
|
@ -143,6 +143,7 @@ namespace Stockfish::Eval::NNUE {
|
||||||
// overaligning stack variables with alignas() doesn't work correctly.
|
// overaligning stack variables with alignas() doesn't work correctly.
|
||||||
|
|
||||||
constexpr uint64_t alignment = CacheLineSize;
|
constexpr uint64_t alignment = CacheLineSize;
|
||||||
|
int delta = 7;
|
||||||
|
|
||||||
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
||||||
TransformedFeatureType transformedFeaturesUnaligned[
|
TransformedFeatureType transformedFeaturesUnaligned[
|
||||||
|
@ -162,20 +163,14 @@ namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
const std::size_t bucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
const std::size_t bucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
||||||
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
|
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
|
||||||
const auto output = network[bucket]->propagate(transformedFeatures, buffer);
|
const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0];
|
||||||
|
|
||||||
int materialist = psqt;
|
// Give more value to positional evaluation when material is balanced
|
||||||
int positional = output[0];
|
if ( adjusted
|
||||||
|
&& abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)) <= RookValueMg - BishopValueMg)
|
||||||
int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK));
|
return static_cast<Value>(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale);
|
||||||
int entertainment = (adjusted && delta_npm <= RookValueMg - BishopValueMg ? 7 : 0);
|
else
|
||||||
|
return static_cast<Value>((psqt + positional) / OutputScale);
|
||||||
int A = 128 - entertainment;
|
|
||||||
int B = 128 + entertainment;
|
|
||||||
|
|
||||||
int sum = (A * materialist + B * positional) / 128;
|
|
||||||
|
|
||||||
return static_cast<Value>( sum / OutputScale );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NnueEvalTrace {
|
struct NnueEvalTrace {
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace Stockfish::Eval::NNUE::Features {
|
||||||
void HalfKAv2_hm::append_active_indices(
|
void HalfKAv2_hm::append_active_indices(
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
Color perspective,
|
Color perspective,
|
||||||
ValueListInserter<IndexType> active
|
IndexList& active
|
||||||
) {
|
) {
|
||||||
Square ksq = pos.square<KING>(perspective);
|
Square ksq = pos.square<KING>(perspective);
|
||||||
Bitboard bb = pos.pieces();
|
Bitboard bb = pos.pieces();
|
||||||
|
@ -55,22 +55,20 @@ namespace Stockfish::Eval::NNUE::Features {
|
||||||
|
|
||||||
void HalfKAv2_hm::append_changed_indices(
|
void HalfKAv2_hm::append_changed_indices(
|
||||||
Square ksq,
|
Square ksq,
|
||||||
StateInfo* st,
|
const DirtyPiece& dp,
|
||||||
Color perspective,
|
Color perspective,
|
||||||
ValueListInserter<IndexType> removed,
|
IndexList& removed,
|
||||||
ValueListInserter<IndexType> added
|
IndexList& added
|
||||||
) {
|
) {
|
||||||
const auto& dp = st->dirtyPiece;
|
|
||||||
for (int i = 0; i < dp.dirty_num; ++i) {
|
for (int i = 0; i < dp.dirty_num; ++i) {
|
||||||
Piece pc = dp.piece[i];
|
|
||||||
if (dp.from[i] != SQ_NONE)
|
if (dp.from[i] != SQ_NONE)
|
||||||
removed.push_back(make_index(perspective, dp.from[i], pc, ksq));
|
removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq));
|
||||||
if (dp.to[i] != SQ_NONE)
|
if (dp.to[i] != SQ_NONE)
|
||||||
added.push_back(make_index(perspective, dp.to[i], pc, ksq));
|
added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int HalfKAv2_hm::update_cost(StateInfo* st) {
|
int HalfKAv2_hm::update_cost(const StateInfo* st) {
|
||||||
return st->dirtyPiece.dirty_num;
|
return st->dirtyPiece.dirty_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +76,7 @@ namespace Stockfish::Eval::NNUE::Features {
|
||||||
return pos.count<ALL_PIECES>();
|
return pos.count<ALL_PIECES>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HalfKAv2_hm::requires_refresh(StateInfo* st, Color perspective) {
|
bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) {
|
||||||
return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
|
return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace Stockfish::Eval::NNUE::Features {
|
||||||
PS_W_QUEEN = 8 * SQUARE_NB,
|
PS_W_QUEEN = 8 * SQUARE_NB,
|
||||||
PS_B_QUEEN = 9 * SQUARE_NB,
|
PS_B_QUEEN = 9 * SQUARE_NB,
|
||||||
PS_KING = 10 * SQUARE_NB,
|
PS_KING = 10 * SQUARE_NB,
|
||||||
PS_NB = 11 * SQUARE_NB
|
PS_NB = 11 * SQUARE_NB
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
|
static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
|
||||||
|
@ -85,36 +85,38 @@ namespace Stockfish::Eval::NNUE::Features {
|
||||||
-1, -1, -1, -1, 23, 22, 21, 20,
|
-1, -1, -1, -1, 23, 22, 21, 20,
|
||||||
-1, -1, -1, -1, 19, 18, 17, 16,
|
-1, -1, -1, -1, 19, 18, 17, 16,
|
||||||
-1, -1, -1, -1, 15, 14, 13, 12,
|
-1, -1, -1, -1, 15, 14, 13, 12,
|
||||||
-1, -1, -1, -1, 11, 10, 9, 8,
|
-1, -1, -1, -1, 11, 10, 9, 8,
|
||||||
-1, -1, -1, -1, 7, 6, 5, 4,
|
-1, -1, -1, -1, 7, 6, 5, 4,
|
||||||
-1, -1, -1, -1, 3, 2, 1, 0
|
-1, -1, -1, -1, 3, 2, 1, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maximum number of simultaneously active features.
|
// Maximum number of simultaneously active features.
|
||||||
static constexpr IndexType MaxActiveDimensions = 32;
|
static constexpr IndexType MaxActiveDimensions = 32;
|
||||||
|
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
||||||
|
|
||||||
// Get a list of indices for active features
|
// Get a list of indices for active features
|
||||||
static void append_active_indices(
|
static void append_active_indices(
|
||||||
const Position& pos,
|
const Position& pos,
|
||||||
Color perspective,
|
Color perspective,
|
||||||
ValueListInserter<IndexType> active);
|
IndexList& active);
|
||||||
|
|
||||||
// Get a list of indices for recently changed features
|
// Get a list of indices for recently changed features
|
||||||
static void append_changed_indices(
|
static void append_changed_indices(
|
||||||
Square ksq,
|
Square ksq,
|
||||||
StateInfo* st,
|
const DirtyPiece& dp,
|
||||||
Color perspective,
|
Color perspective,
|
||||||
ValueListInserter<IndexType> removed,
|
IndexList& removed,
|
||||||
ValueListInserter<IndexType> added);
|
IndexList& added
|
||||||
|
);
|
||||||
|
|
||||||
// Returns the cost of updating one perspective, the most costly one.
|
// Returns the cost of updating one perspective, the most costly one.
|
||||||
// Assumes no refresh needed.
|
// Assumes no refresh needed.
|
||||||
static int update_cost(StateInfo* st);
|
static int update_cost(const StateInfo* st);
|
||||||
static int refresh_cost(const Position& pos);
|
static int refresh_cost(const Position& pos);
|
||||||
|
|
||||||
// Returns whether the change stored in this StateInfo means that
|
// Returns whether the change stored in this StateInfo means that
|
||||||
// a full accumulator refresh is required.
|
// a full accumulator refresh is required.
|
||||||
static bool requires_refresh(StateInfo* st, Color perspective);
|
static bool requires_refresh(const StateInfo* st, Color perspective);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish::Eval::NNUE::Features
|
} // namespace Stockfish::Eval::NNUE::Features
|
||||||
|
|
|
@ -370,7 +370,6 @@ namespace Stockfish::Eval::NNUE {
|
||||||
// That might depend on the feature set and generally relies on the
|
// That might depend on the feature set and generally relies on the
|
||||||
// feature set's update cost calculation to be correct and never
|
// feature set's update cost calculation to be correct and never
|
||||||
// allow updates with more added/removed features than MaxActiveDimensions.
|
// allow updates with more added/removed features than MaxActiveDimensions.
|
||||||
using IndexList = ValueList<IndexType, FeatureSet::MaxActiveDimensions>;
|
|
||||||
|
|
||||||
#ifdef VECTOR
|
#ifdef VECTOR
|
||||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
||||||
|
@ -404,12 +403,12 @@ namespace Stockfish::Eval::NNUE {
|
||||||
|
|
||||||
// Gather all features to be updated.
|
// Gather all features to be updated.
|
||||||
const Square ksq = pos.square<KING>(perspective);
|
const Square ksq = pos.square<KING>(perspective);
|
||||||
IndexList removed[2], added[2];
|
FeatureSet::IndexList removed[2], added[2];
|
||||||
FeatureSet::append_changed_indices(
|
FeatureSet::append_changed_indices(
|
||||||
ksq, next, perspective, removed[0], added[0]);
|
ksq, next->dirtyPiece, perspective, removed[0], added[0]);
|
||||||
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
|
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
|
||||||
FeatureSet::append_changed_indices(
|
FeatureSet::append_changed_indices(
|
||||||
ksq, st2, perspective, removed[1], added[1]);
|
ksq, st2->dirtyPiece, perspective, removed[1], added[1]);
|
||||||
|
|
||||||
// Mark the accumulators as computed.
|
// Mark the accumulators as computed.
|
||||||
next->accumulator.computed[perspective] = true;
|
next->accumulator.computed[perspective] = true;
|
||||||
|
@ -534,7 +533,7 @@ namespace Stockfish::Eval::NNUE {
|
||||||
// Refresh the accumulator
|
// Refresh the accumulator
|
||||||
auto& accumulator = pos.state()->accumulator;
|
auto& accumulator = pos.state()->accumulator;
|
||||||
accumulator.computed[perspective] = true;
|
accumulator.computed[perspective] = true;
|
||||||
IndexList active;
|
FeatureSet::IndexList active;
|
||||||
FeatureSet::append_active_indices(pos, perspective, active);
|
FeatureSet::append_active_indices(pos, perspective, active);
|
||||||
|
|
||||||
#ifdef VECTOR
|
#ifdef VECTOR
|
||||||
|
|
|
@ -1016,9 +1016,9 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
st->key ^= Zobrist::side;
|
st->key ^= Zobrist::side;
|
||||||
|
++st->rule50;
|
||||||
prefetch(TT.first_entry(key()));
|
prefetch(TT.first_entry(key()));
|
||||||
|
|
||||||
++st->rule50;
|
|
||||||
st->pliesFromNull = 0;
|
st->pliesFromNull = 0;
|
||||||
|
|
||||||
sideToMove = ~sideToMove;
|
sideToMove = ~sideToMove;
|
||||||
|
|
|
@ -331,7 +331,6 @@ void Thread::search() {
|
||||||
|
|
||||||
multiPV = std::min(multiPV, rootMoves.size());
|
multiPV = std::min(multiPV, rootMoves.size());
|
||||||
|
|
||||||
ttHitAverage.set(50, 100); // initialize the running average at 50%
|
|
||||||
doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0%
|
doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0%
|
||||||
doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0%
|
doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0%
|
||||||
|
|
||||||
|
@ -588,9 +587,9 @@ namespace {
|
||||||
Value bestValue, value, ttValue, eval, maxValue, probCutBeta;
|
Value bestValue, value, ttValue, eval, maxValue, probCutBeta;
|
||||||
bool givesCheck, improving, didLMR, priorCapture;
|
bool givesCheck, improving, didLMR, priorCapture;
|
||||||
bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
|
bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
|
||||||
ttCapture, singularQuietLMR, noLMRExtension;
|
ttCapture, singularQuietLMR;
|
||||||
Piece movedPiece;
|
Piece movedPiece;
|
||||||
int moveCount, captureCount, quietCount, bestMoveCount;
|
int moveCount, captureCount, quietCount, bestMoveCount, improvement;
|
||||||
|
|
||||||
// Step 1. Initialize node
|
// Step 1. Initialize node
|
||||||
ss->inCheck = pos.checkers();
|
ss->inCheck = pos.checkers();
|
||||||
|
@ -658,6 +657,7 @@ namespace {
|
||||||
ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
|
ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
|
||||||
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
||||||
: ss->ttHit ? tte->move() : MOVE_NONE;
|
: ss->ttHit ? tte->move() : MOVE_NONE;
|
||||||
|
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
|
||||||
if (!excludedMove)
|
if (!excludedMove)
|
||||||
ss->ttPv = PvNode || (ss->ttHit && tte->is_pv());
|
ss->ttPv = PvNode || (ss->ttHit && tte->is_pv());
|
||||||
|
|
||||||
|
@ -669,13 +669,10 @@ namespace {
|
||||||
&& is_ok((ss-1)->currentMove))
|
&& is_ok((ss-1)->currentMove))
|
||||||
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
|
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
|
||||||
|
|
||||||
// running average of ttHit
|
|
||||||
thisThread->ttHitAverage.update(ss->ttHit);
|
|
||||||
|
|
||||||
// At non-PV nodes we check for an early TT cutoff
|
// At non-PV nodes we check for an early TT cutoff
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& ss->ttHit
|
&& ss->ttHit
|
||||||
&& tte->depth() >= depth
|
&& tte->depth() > depth - (thisThread->id() % 2 == 1)
|
||||||
&& ttValue != VALUE_NONE // Possible in case of TT access race
|
&& ttValue != VALUE_NONE // Possible in case of TT access race
|
||||||
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
|
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER)
|
||||||
: (tte->bound() & BOUND_UPPER)))
|
: (tte->bound() & BOUND_UPPER)))
|
||||||
|
@ -686,7 +683,7 @@ namespace {
|
||||||
if (ttValue >= beta)
|
if (ttValue >= beta)
|
||||||
{
|
{
|
||||||
// Bonus for a quiet ttMove that fails high
|
// Bonus for a quiet ttMove that fails high
|
||||||
if (!pos.capture_or_promotion(ttMove))
|
if (!ttCapture)
|
||||||
update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth);
|
update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth);
|
||||||
|
|
||||||
// Extra penalty for early quiet moves of the previous ply
|
// Extra penalty for early quiet moves of the previous ply
|
||||||
|
@ -694,7 +691,7 @@ namespace {
|
||||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
|
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
|
||||||
}
|
}
|
||||||
// Penalty for a quiet ttMove that fails low
|
// Penalty for a quiet ttMove that fails low
|
||||||
else if (!pos.capture_or_promotion(ttMove))
|
else if (!ttCapture)
|
||||||
{
|
{
|
||||||
int penalty = -stat_bonus(depth);
|
int penalty = -stat_bonus(depth);
|
||||||
thisThread->mainHistory[us][from_to(ttMove)] << penalty;
|
thisThread->mainHistory[us][from_to(ttMove)] << penalty;
|
||||||
|
@ -768,6 +765,7 @@ namespace {
|
||||||
// Skip early pruning when in check
|
// Skip early pruning when in check
|
||||||
ss->staticEval = eval = VALUE_NONE;
|
ss->staticEval = eval = VALUE_NONE;
|
||||||
improving = false;
|
improving = false;
|
||||||
|
improvement = 0;
|
||||||
goto moves_loop;
|
goto moves_loop;
|
||||||
}
|
}
|
||||||
else if (ss->ttHit)
|
else if (ss->ttHit)
|
||||||
|
@ -788,15 +786,11 @@ namespace {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// In case of null move search use previous static eval with a different sign
|
ss->staticEval = eval = evaluate(pos);
|
||||||
if ((ss-1)->currentMove != MOVE_NULL)
|
|
||||||
ss->staticEval = eval = evaluate(pos);
|
|
||||||
else
|
|
||||||
ss->staticEval = eval = -(ss-1)->staticEval;
|
|
||||||
|
|
||||||
// Save static evaluation into transposition table
|
// Save static evaluation into transposition table
|
||||||
if(!excludedMove)
|
if (!excludedMove)
|
||||||
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
|
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use static evaluation difference to improve quiet move ordering
|
// Use static evaluation difference to improve quiet move ordering
|
||||||
|
@ -806,13 +800,15 @@ namespace {
|
||||||
thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
|
thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up improving flag that is used in various pruning heuristics
|
// Set up the improvement variable, which is the difference between the current
|
||||||
// We define position as improving if static evaluation of position is better
|
// static evaluation and the previous static evaluation at our turn (if we were
|
||||||
// Than the previous static evaluation at our turn
|
// in check at our previous move we look at the move prior to it). The improvement
|
||||||
// In case of us being in check at our previous move we look at move prior to it
|
// margin and the improving flag are used in various pruning heuristics.
|
||||||
improving = (ss-2)->staticEval == VALUE_NONE
|
improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval
|
||||||
? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE
|
: (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval
|
||||||
: ss->staticEval > (ss-2)->staticEval;
|
: 200;
|
||||||
|
|
||||||
|
improving = improvement > 0;
|
||||||
|
|
||||||
// Step 7. Futility pruning: child node (~50 Elo).
|
// Step 7. Futility pruning: child node (~50 Elo).
|
||||||
// The depth condition is important for mate finding.
|
// The depth condition is important for mate finding.
|
||||||
|
@ -828,7 +824,7 @@ namespace {
|
||||||
&& (ss-1)->statScore < 23767
|
&& (ss-1)->statScore < 23767
|
||||||
&& eval >= beta
|
&& eval >= beta
|
||||||
&& eval >= ss->staticEval
|
&& eval >= ss->staticEval
|
||||||
&& ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 177
|
&& ss->staticEval >= beta - 20 * depth - improvement / 15 + 204
|
||||||
&& !excludedMove
|
&& !excludedMove
|
||||||
&& pos.non_pawn_material(us)
|
&& pos.non_pawn_material(us)
|
||||||
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
|
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
|
||||||
|
@ -892,19 +888,16 @@ namespace {
|
||||||
assert(probCutBeta < VALUE_INFINITE);
|
assert(probCutBeta < VALUE_INFINITE);
|
||||||
|
|
||||||
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
|
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
|
||||||
int probCutCount = 0;
|
|
||||||
bool ttPv = ss->ttPv;
|
bool ttPv = ss->ttPv;
|
||||||
ss->ttPv = false;
|
ss->ttPv = false;
|
||||||
|
|
||||||
while ( (move = mp.next_move()) != MOVE_NONE
|
while ((move = mp.next_move()) != MOVE_NONE)
|
||||||
&& probCutCount < 2 + 2 * cutNode)
|
|
||||||
if (move != excludedMove && pos.legal(move))
|
if (move != excludedMove && pos.legal(move))
|
||||||
{
|
{
|
||||||
assert(pos.capture_or_promotion(move));
|
assert(pos.capture_or_promotion(move));
|
||||||
assert(depth >= 5);
|
assert(depth >= 5);
|
||||||
|
|
||||||
captureOrPromotion = true;
|
captureOrPromotion = true;
|
||||||
probCutCount++;
|
|
||||||
|
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
|
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
|
||||||
|
@ -951,7 +944,6 @@ namespace {
|
||||||
|
|
||||||
moves_loop: // When in check, search starts here
|
moves_loop: // When in check, search starts here
|
||||||
|
|
||||||
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
|
|
||||||
int rangeReduction = 0;
|
int rangeReduction = 0;
|
||||||
|
|
||||||
// Step 11. A small Probcut idea, when we are in check
|
// Step 11. A small Probcut idea, when we are in check
|
||||||
|
@ -984,7 +976,7 @@ moves_loop: // When in check, search starts here
|
||||||
ss->ply);
|
ss->ply);
|
||||||
|
|
||||||
value = bestValue;
|
value = bestValue;
|
||||||
singularQuietLMR = moveCountPruning = noLMRExtension = false;
|
singularQuietLMR = moveCountPruning = false;
|
||||||
|
|
||||||
// Indicate PvNodes that will probably fail low if the node was searched
|
// Indicate PvNodes that will probably fail low if the node was searched
|
||||||
// at a depth equal or greater than the current depth, and the result of this search was a fail low.
|
// at a depth equal or greater than the current depth, and the result of this search was a fail low.
|
||||||
|
@ -1108,10 +1100,7 @@ moves_loop: // When in check, search starts here
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& value < singularBeta - 75
|
&& value < singularBeta - 75
|
||||||
&& ss->doubleExtensions <= 6)
|
&& ss->doubleExtensions <= 6)
|
||||||
{
|
|
||||||
extension = 2;
|
extension = 2;
|
||||||
noLMRExtension = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-cut pruning
|
// Multi-cut pruning
|
||||||
|
@ -1122,18 +1111,9 @@ moves_loop: // When in check, search starts here
|
||||||
else if (singularBeta >= beta)
|
else if (singularBeta >= beta)
|
||||||
return singularBeta;
|
return singularBeta;
|
||||||
|
|
||||||
// If the eval of ttMove is greater than beta we try also if there is another
|
// If the eval of ttMove is greater than beta, we reduce it (negative extension)
|
||||||
// move that pushes it over beta, if so the position also has probably multiple
|
|
||||||
// moves giving fail highs. We will then reduce the ttMove (negative extension).
|
|
||||||
else if (ttValue >= beta)
|
else if (ttValue >= beta)
|
||||||
{
|
extension = -2;
|
||||||
ss->excludedMove = move;
|
|
||||||
value = search<NonPV>(pos, ss, beta - 1, beta, (depth + 3) / 2, cutNode);
|
|
||||||
ss->excludedMove = MOVE_NONE;
|
|
||||||
|
|
||||||
if (value >= beta)
|
|
||||||
extension = -2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture extensions for PvNodes and cutNodes
|
// Capture extensions for PvNodes and cutNodes
|
||||||
|
@ -1178,10 +1158,9 @@ moves_loop: // When in check, search starts here
|
||||||
// cases where we extend a son if it has good chances to be "interesting".
|
// cases where we extend a son if it has good chances to be "interesting".
|
||||||
if ( depth >= 3
|
if ( depth >= 3
|
||||||
&& moveCount > 1 + 2 * rootNode
|
&& moveCount > 1 + 2 * rootNode
|
||||||
&& ( !captureOrPromotion
|
&& ( !ss->ttPv
|
||||||
|| (cutNode && (ss-1)->moveCount > 1)
|
|| !captureOrPromotion
|
||||||
|| !ss->ttPv)
|
|| (cutNode && (ss-1)->moveCount > 1)))
|
||||||
&& (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3))
|
|
||||||
{
|
{
|
||||||
Depth r = reduction(improving, depth, moveCount, rangeReduction > 2);
|
Depth r = reduction(improving, depth, moveCount, rangeReduction > 2);
|
||||||
|
|
||||||
|
@ -1190,10 +1169,6 @@ moves_loop: // When in check, search starts here
|
||||||
&& bestMoveCount <= 3)
|
&& bestMoveCount <= 3)
|
||||||
r--;
|
r--;
|
||||||
|
|
||||||
// Decrease reduction if the ttHit running average is large (~0 Elo)
|
|
||||||
if (thisThread->ttHitAverage.is_greater(537, 1024))
|
|
||||||
r--;
|
|
||||||
|
|
||||||
// Decrease reduction if position is or has been on the PV
|
// Decrease reduction if position is or has been on the PV
|
||||||
// and node is not likely to fail low. (~3 Elo)
|
// and node is not likely to fail low. (~3 Elo)
|
||||||
if ( ss->ttPv
|
if ( ss->ttPv
|
||||||
|
@ -1232,13 +1207,11 @@ moves_loop: // When in check, search starts here
|
||||||
|
|
||||||
// In general we want to cap the LMR depth search at newDepth. But if reductions
|
// In general we want to cap the LMR depth search at newDepth. But if reductions
|
||||||
// are really negative and movecount is low, we allow this move to be searched
|
// are really negative and movecount is low, we allow this move to be searched
|
||||||
// deeper than the first move (this may lead to hidden double extensions if
|
// deeper than the first move (this may lead to hidden double extensions).
|
||||||
// newDepth got its own extension before).
|
int deeper = r >= -1 ? 0
|
||||||
int deeper = r >= -1 ? 0
|
: moveCount <= 5 ? 2
|
||||||
: noLMRExtension ? 0
|
: PvNode && depth > 6 ? 1
|
||||||
: moveCount <= 5 ? 1
|
: 0;
|
||||||
: (depth > 6 && PvNode) ? 1
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
Depth d = std::clamp(newDepth - r, 1, newDepth + deeper);
|
Depth d = std::clamp(newDepth - r, 1, newDepth + deeper);
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ public:
|
||||||
Pawns::Table pawnsTable;
|
Pawns::Table pawnsTable;
|
||||||
Material::Table materialTable;
|
Material::Table materialTable;
|
||||||
size_t pvIdx, pvLast;
|
size_t pvIdx, pvLast;
|
||||||
RunningAverage ttHitAverage;
|
|
||||||
RunningAverage doubleExtensionAverage[COLOR_NB];
|
RunningAverage doubleExtensionAverage[COLOR_NB];
|
||||||
uint64_t nodesLastExplosive;
|
uint64_t nodesLastExplosive;
|
||||||
uint64_t nodesLastNormal;
|
uint64_t nodesLastNormal;
|
||||||
|
|
Loading…
Reference in New Issue