/// The Stats struct stores moves statistics. According to the template parameter
-/// the class can store History and Countermoves. History records how often
+/// the class can store History, Gains and Countermoves. History records how often
/// different moves have been successful or unsuccessful during the current search
-/// and is used for reduction and move ordering decisions.
+/// and is used for reduction and move ordering decisions. Gains records the move's
+/// best evaluation gain from one ply to the next and is used for pruning decisions.
/// Countermoves store the move that refute a previous one. Entries are stored
/// according only to moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical.
-template<typename T>
+template<bool Gain, typename T>
struct Stats {
static const Value Max = Value(2000);
void update(Piece p, Square to, Value v) {
- if (abs(table[p][to] + v) < Max)
+ if (Gain)
+ table[p][to] = std::max(v, table[p][to] - 1);
+
+ else if (abs(table[p][to] + v) < Max)
table[p][to] += v;
}
T table[PIECE_NB][SQUARE_NB];
};
-typedef Stats<Value> HistoryStats;
-typedef Stats<std::pair<Move, Move> > CountermovesStats;
+typedef Stats< true, Value> GainsStats;
+typedef Stats<false, Value> HistoryStats;
+typedef Stats<false, std::pair<Move, Move> > CountermovesStats;
/// MovePicker class is used to pick one pseudo legal move at a time from the
double BestMoveChanges;
Value DrawValue[COLOR_NB];
HistoryStats History;
+ GainsStats Gains;
CountermovesStats Countermoves;
template <NodeType NT>
Value bestValue, alpha, beta, delta;
std::memset(ss-2, 0, 5 * sizeof(Stack));
+ (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = 0;
BestMoveChanges = 0;
TT.new_search();
History.clear();
+ Gains.clear();
Countermoves.clear();
PVSize = Options["MultiPV"];
SplitPoint* splitPoint;
Key posKey;
Move ttMove, move, excludedMove, bestMove, threatMove;
- Depth ext, newDepth;
- Value bestValue, value, ttValue;
- Value eval, nullValue;
+ Depth ext, newDepth, predictedDepth;
+ Value bestValue, value, ttValue, eval, nullValue, futilityValue;
bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount, quietCount;
TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval);
}
+ if ( !pos.captured_piece_type()
+ && ss->staticEval != VALUE_NONE
+ && (ss-1)->staticEval != VALUE_NONE
+ && (move = (ss-1)->currentMove) != MOVE_NULL
+ && type_of(move) == NORMAL)
+ {
+ Square to = to_sq(move);
+ Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval);
+ }
+
// Step 6. Razoring (skipped when in check)
if ( !PvNode
&& depth < 4 * ONE_PLY
continue;
}
- Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
+ predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
// Futility pruning: parent node
if (predictedDepth < 7 * ONE_PLY)
{
- Value futilityValue = ss->staticEval + futility_margin(predictedDepth) + Value(128);
+ futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ + Value(128) + Gains[pos.moved_piece(move)][to_sq(move)];
if (futilityValue <= alpha)
{
}
// 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();
// Check for legality only before to do the move
if (!RootNode && !SpNode && !pos.legal(move, ci.pinned))
{
- --moveCount;
+ moveCount--;
continue;
}