inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); }
// Futility lookup tables (initialized at startup) and their access functions
- Value FutilityMargins[14][64]; // [depth][moveNumber]
int FutilityMoveCounts[2][32]; // [improving][depth]
- inline Value futility_margin(Depth d, int mn) {
- assert(DEPTH_ZERO <= d && d < 7 * ONE_PLY);
- return FutilityMargins[d][std::min(mn, 63)];
+ inline Value futility_margin(Depth d) {
+ return Value(100 * int(d));
}
// Reduction lookup tables (initialized at startup) and their access function
double BestMoveChanges;
Value DrawValue[COLOR_NB];
HistoryStats History;
+ GainsStats Gains;
CountermovesStats Countermoves;
template <NodeType NT>
Reductions[0][0][hd][mc] += ONE_PLY / 2;
}
- // Init futility margins array
- for (d = 0; d < 14; ++d) for (mc = 0; mc < 64; ++mc)
- FutilityMargins[d][mc] = Value(112 * int(2.9 * log(d >= 1 ? double(d) : 1.0)) - 8 * mc + 45);
-
// Init futility move count array
for (d = 0; d < 32; ++d)
{
sync_cout << "info nodes " << RootPos.nodes_searched()
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
- // When we reach max depth we arrive here even without Signals.stop is raised,
+ // When we reach max depth we arrive here even without Signals.stop being raised,
// but if we are pondering or in infinite search, according to UCI protocol,
// we shouldn't print the best move before the GUI sends a "stop" or "ponderhit"
// command. We simply wait here until GUI sends one of those commands (that
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"];
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.
+ // the move scores except the (new) PV are set to -VALUE_INFINITE.
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 && !Signals.stop; ++PVIdx)
{
// Reset aspiration window starting size
if (depth >= 5)
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.
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
}
- // Do we need to pick now the sub-optimal best move ?
+ // Do we now need to pick now the sub-optimal best move ?
if (skill.enabled() && skill.time_to_pick(depth))
skill.pick_move();
<< std::endl;
}
- // Do we have found a "mate in x"?
+ // Have we found a "mate in x"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
&& VALUE_MATE - bestValue <= 2 * Limits.mate)
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
+ // Take some extra time if the best move has changed
if (depth > 4 && depth < 50 && PVSize == 1)
TimeMgr.pv_instability(BestMoveChanges);
- // Stop search if most of available time is already consumed. We
+ // Stop search if most of the available time is already consumed. We
// probably don't have enough time to search the first move at the
// next iteration anyway.
if (Time::now() - SearchTime > (TimeMgr.available_time() * 62) / 100)
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;
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;
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
Value v = qsearch<NonPV, false>(pos, ss, rbeta-1, rbeta, DEPTH_ZERO);
if (v < rbeta)
// Logically we should return (v + razor_margin(depth)), but
- // surprisingly this did slightly weaker in tests.
+ // surprisingly this performed slightly weaker in tests.
return v;
}
- // Step 7. post-Futility pruning (skipped when in check)
+ // Step 7. Futility pruning: child node (skipped when in check)
if ( !PvNode
&& !ss->skipNullMove
&& depth < 7 * ONE_PLY
- && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
+ && 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
givesCheck = pos.gives_check(move, ci);
dangerous = givesCheck
|| pos.passed_pawn_push(move)
- || type_of(move) == CASTLE;
+ || type_of(move) == CASTLING;
// Step 12. Extend checks
if (givesCheck && pos.see_sign(move) >= 0)
// Update current move (this must be done after singular extension search)
newDepth = depth - ONE_PLY + ext;
- Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
- // Step 13. Futility pruning (is omitted in PV nodes)
+ // Step 13. Pruning at shallow depth (exclude PV nodes)
if ( !PvNode
&& !captureOrPromotion
&& !inCheck
&& !dangerous
+ /* && move != ttMove Already implicit in the next condition */
&& bestValue > VALUE_MATED_IN_MAX_PLY)
{
// Move count based pruning
continue;
}
+ predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
+
+ // Futility pruning: parent node
+ if (predictedDepth < 7 * ONE_PLY)
+ {
+ futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ + Value(128) + Gains[pos.moved_piece(move)][to_sq(move)];
+
+ if (futilityValue <= alpha)
+ {
+ bestValue = std::max(bestValue, futilityValue);
+
+ if (SpNode)
+ {
+ splitPoint->mutex.lock();
+ if (bestValue > splitPoint->bestValue)
+ splitPoint->bestValue = bestValue;
+ }
+ 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();
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.legal(move, ci.pinned))
{
- --moveCount;
+ moveCount--;
continue;
}
// Only for PV nodes do a full PV search on the first move or after a fail
// high, in the latter case search only if value < beta, otherwise let the
- // parent node to fail low with value <= alpha and to try another move.
+ // parent node fail low with value <= alpha and to try another move.
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
assert(is_ok(first));
assert(is_ok(second));
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
- assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
+ assert(type_of(first) == CASTLING || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
Square m1from = from_sq(first);
Square m2from = from_sq(second);
// 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 && !squares_aligned(m1from, m2from, m2to)))
+ || (m1to == m2from && !aligned(m1from, m2from, m2to)))
return true;
// Second one moves through the square vacated by first one
return true;
// Second's destination is defended by the first move's piece
- Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from);
+ Bitboard m1att = attacks_bb(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from);
if (m1att & m2to)
return true;
Piece pc = pos.piece_on(m1from);
// The moved piece attacks the square 'tto' ?
- if (pos.attacks_from(pc, m1to, occ) & m2to)
+ if (attacks_bb(pc, m1to, occ) & m2to)
return true;
// Scan for possible X-ray attackers behind the moved piece