Value DrawValue[COLOR_NB];
HistoryStats History;
GainsStats Gains;
- CountermovesStats Countermoves;
+ MovesStats Countermoves, Followupmoves;
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
History.clear();
Gains.clear();
Countermoves.clear();
+ Followupmoves.clear();
PVSize = Options["MultiPV"];
Skill skill(Options["Skill Level"]);
moveCount = quietCount = 0;
bestValue = -VALUE_INFINITE;
- ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
+ ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1;
(ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO;
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
excludedMove = ss->excludedMove;
posKey = excludedMove ? pos.exclusion_key() : pos.key();
tte = TT.probe(posKey);
- ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE;
+ ss->ttMove = ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE;
ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
// At PV nodes we check for exact scores, whilst at non-PV nodes we check for
TT.refresh(tte);
ss->currentMove = ttMove; // Can be MOVE_NONE
- // If ttMove is quiet, update killers, history, and counter move on TT hit
+ // If ttMove is quiet, update killers, history, counter move and followup move on TT hit
if (ttValue >= beta && ttMove && !pos.capture_or_promotion(ttMove) && !inCheck)
update_stats(pos, ss, ttMove, depth, NULL, 0);
{
ss->currentMove = MOVE_NULL;
- // Null move dynamic reduction based on depth
- Depth R = 3 * ONE_PLY + depth / 4;
+ assert(eval - beta >= 0);
- // Null move dynamic reduction based on value
- if (eval - PawnValueMg > beta)
- R += ONE_PLY;
+ // Null move dynamic reduction based on depth and value
+ Depth R = 3 * ONE_PLY
+ + depth / (2 * ONE_PLY)
+ + int(eval - beta) / PawnValueMg * ONE_PLY;
pos.do_null_move(st);
(ss+1)->skipNullMove = true;
// Do verification search at high depths
ss->skipNullMove = true;
- Value v = search<NonPV>(pos, ss, alpha, beta, depth-R, false);
+ Value v = depth-R < ONE_PLY ? qsearch<NonPV, false>(pos, ss, alpha, beta, DEPTH_ZERO)
+ : search<NonPV>(pos, ss, alpha, beta, depth-R, false);
ss->skipNullMove = false;
if (v >= beta)
Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first,
Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second };
- MovePicker mp(pos, ttMove, depth, History, countermoves, ss);
+ Square prevOwnMoveSq = to_sq((ss-2)->currentMove);
+ Move followupmoves[] = { Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].first,
+ Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].second };
+
+ MovePicker mp(pos, ttMove, depth, History, countermoves, followupmoves, ss);
CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval);
- // Quiet best move: update killers, history and countermoves
+ // Quiet best move: update killers, history, countermoves and followupmoves
if (bestValue >= beta && !pos.capture_or_promotion(bestMove) && !inCheck)
update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount - 1);
}
- // update_stats() updates killers, history and countermoves stats after a fail-high
+ // update_stats() updates killers, history, countermoves and followupmoves stats after a fail-high
// of a quiet move.
void update_stats(Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt) {
Square prevMoveSq = to_sq((ss-1)->currentMove);
Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, move);
}
+
+ if (is_ok((ss-2)->currentMove) && (ss-1)->currentMove == (ss-1)->ttMove)
+ {
+ Square prevOwnMoveSq = to_sq((ss-2)->currentMove);
+ Followupmoves.update(pos.piece_on(prevOwnMoveSq), prevOwnMoveSq, move);
+ }
}
Time::point elapsed = Time::now() - SearchTime;
bool stillAtFirstMove = Signals.firstRootMove
&& !Signals.failedLowAtRoot
- && elapsed > (TimeMgr.available_time() * 62) / 100
- && elapsed > IterationTime * 1.4;
+ && ( elapsed > TimeMgr.available_time()
+ || ( elapsed > (TimeMgr.available_time() * 62) / 100
+ && elapsed > IterationTime * 1.4));
bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution
|| stillAtFirstMove;