// Razoring and futility margin based on depth
const int razor_margin[4] = { 483, 570, 603, 554 };
- Value futility_margin(Depth d) { return Value(200 * d); }
+ Value futility_margin(Depth d) { return Value(150 * d); }
// Futility and reductions lookup tables, initialized at startup
int FutilityMoveCounts[2][16]; // [improving][depth]
{
th->history.clear();
th->counterMoves.clear();
+ th->fromTo.clear();
}
Threads.main()->previousScore = VALUE_INFINITE;
DrawValue[ us] = VALUE_DRAW - Value(contempt);
DrawValue[~us] = VALUE_DRAW + Value(contempt);
- TB::Hits = 0;
- TB::RootInTB = false;
- TB::UseRule50 = Options["Syzygy50MoveRule"];
- TB::ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
- TB::Cardinality = Options["SyzygyProbeLimit"];
-
- // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality
- if (TB::Cardinality > TB::MaxCardinality)
- {
- TB::Cardinality = TB::MaxCardinality;
- TB::ProbeDepth = DEPTH_ZERO;
- }
-
if (rootMoves.empty())
{
rootMoves.push_back(RootMove(MOVE_NONE));
}
else
{
- if ( TB::Cardinality >= rootPos.count<ALL_PIECES>(WHITE)
- + rootPos.count<ALL_PIECES>(BLACK)
- && !rootPos.can_castle(ANY_CASTLING))
- {
- // If the current root position is in the tablebases, then RootMoves
- // contains only moves that preserve the draw or the win.
- TB::RootInTB = Tablebases::root_probe(rootPos, rootMoves, TB::Score);
-
- if (TB::RootInTB)
- TB::Cardinality = 0; // Do not probe tablebases during the search
-
- else // If DTZ tables are missing, use WDL tables as a fallback
- {
- // Filter out moves that do not preserve the draw or the win.
- TB::RootInTB = Tablebases::root_probe_wdl(rootPos, rootMoves, TB::Score);
-
- // Only probe during search if winning
- if (TB::Score <= VALUE_DRAW)
- TB::Cardinality = 0;
- }
-
- if (TB::RootInTB)
- {
- TB::Hits = rootMoves.size();
-
- if (!TB::UseRule50)
- TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
- : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
- : VALUE_DRAW;
- }
- }
-
for (Thread* th : Threads)
if (th != this)
th->start_searching();
// search the already searched PV lines are preserved.
std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
- // Write PV back to the transposition table in case the relevant
- // entries have been overwritten during the search.
- for (size_t i = 0; i <= PVIdx; ++i)
- rootMoves[i].insert_pv_in_tt(rootPos);
-
// If search has been stopped, break immediately. Sorting and
// writing PV back to TT is safe because RootMoves is still
// valid, although it refers to the previous iteration.
std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1);
if (!mainThread)
- break;
+ continue;
if (Signals.stop)
sync_cout << "info nodes " << Threads.nodes_searched()
// Stop the search if only one legal move is available, or if all
// of the available time has been used, or if we matched an easyMove
// from the previous search and just did a fast verification.
- const bool F[] = { !mainThread->failedLow,
- bestValue >= mainThread->previousScore };
+ const int F[] = { mainThread->failedLow,
+ bestValue - mainThread->previousScore };
- int improvingFactor = 640 - 160*F[0] - 126*F[1] - 124*F[0]*F[1];
+ int improvingFactor = std::max(229, std::min(715, 357 + 119 * F[0] - 6 * F[1]));
double unstablePvFactor = 1 + mainThread->bestMoveChanges;
bool doEasyMove = rootMoves[0].pv[0] == easyMove
&& mainThread->bestMoveChanges < 0.03
- && Time.elapsed() > Time.optimum() * 25 / 204;
+ && Time.elapsed() > Time.optimum() * 5 / 42;
if ( rootMoves.size() == 1
- || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 634
+ || Time.elapsed() > Time.optimum() * unstablePvFactor * improvingFactor / 628
|| (mainThread->easyMovePlayed = doEasyMove))
{
// If we are allowed to ponder do not stop the search now but
assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
assert(DEPTH_ZERO < depth && depth < DEPTH_MAX);
+ assert(!(PvNode && cutNode));
Move pv[MAX_PLY+1], quietsSearched[64];
StateInfo st;
Key posKey;
Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth, predictedDepth;
- Value bestValue, value, ttValue, eval, nullValue, futilityValue;
+ Value bestValue, value, ttValue, eval, nullValue;
bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
- bool captureOrPromotion, doFullDepthSearch;
+ bool captureOrPromotion, doFullDepthSearch, moveCountPruning;
+ Piece moved_piece;
int moveCount, quietCount;
// Step 1. Initialize node
// Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode
- && depth >= 2 * ONE_PLY
&& eval >= beta
+ && (ss->staticEval >= beta - 35 * (depth / ONE_PLY - 6) || depth >= 13 * ONE_PLY)
&& pos.non_pawn_material(pos.side_to_move()))
{
ss->currentMove = MOVE_NULL;
{
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
ss->skipEarlyPruning = true;
- search<NT>(pos, ss, alpha, beta, d, true);
+ search<NT>(pos, ss, alpha, beta, d, cutNode);
ss->skipEarlyPruning = false;
tte = TT.probe(posKey, ttHit);
moves_loop: // When in check search starts from here
- Square prevSq = to_sq((ss-1)->currentMove);
- const CounterMoveStats& cmh = CounterMoveHistory[pos.piece_on(prevSq)][prevSq];
+ const CounterMoveStats* cmh = (ss-1)->counterMoves;
+ const CounterMoveStats* fmh = (ss-2)->counterMoves;
+ const CounterMoveStats* fmh2 = (ss-4)->counterMoves;
MovePicker mp(pos, ttMove, depth, ss);
CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
- || ss->staticEval == VALUE_NONE
+ /* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */
||(ss-2)->staticEval == VALUE_NONE;
singularExtensionNode = !rootNode
extension = DEPTH_ZERO;
captureOrPromotion = pos.capture_or_promotion(move);
+ moved_piece = pos.moved_piece(move);
givesCheck = type_of(move) == NORMAL && !ci.dcCandidates
? ci.checkSquares[type_of(pos.piece_on(from_sq(move)))] & to_sq(move)
: pos.gives_check(move, ci);
+ moveCountPruning = depth < 16 * ONE_PLY
+ && moveCount >= FutilityMoveCounts[improving][depth];
+
// Step 12. Extend checks
- if (givesCheck && pos.see_sign(move) >= VALUE_ZERO)
+ if ( givesCheck
+ && !moveCountPruning
+ && pos.see_sign(move) >= VALUE_ZERO)
extension = ONE_PLY;
// Singular extension search. If all moves but one fail low on a search of
&& bestValue > VALUE_MATED_IN_MAX_PLY)
{
// Move count based pruning
- if ( depth < 16 * ONE_PLY
- && moveCount >= FutilityMoveCounts[improving][depth])
+ if (moveCountPruning)
continue;
- // History based pruning
- if ( depth <= 4 * ONE_PLY
+ predictedDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
+
+ // Countermoves based pruning
+ if ( predictedDepth < 3 * ONE_PLY
&& move != ss->killers[0]
- && thisThread->history[pos.moved_piece(move)][to_sq(move)] < VALUE_ZERO
- && cmh[pos.moved_piece(move)][to_sq(move)] < VALUE_ZERO)
+ && (!cmh || (*cmh )[moved_piece][to_sq(move)] < VALUE_ZERO)
+ && (!fmh || (*fmh )[moved_piece][to_sq(move)] < VALUE_ZERO)
+ && (!fmh2 || (*fmh2)[moved_piece][to_sq(move)] < VALUE_ZERO || (cmh && fmh)))
continue;
- predictedDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
-
// Futility pruning: parent node
- if (predictedDepth < 7 * ONE_PLY)
+ if ( predictedDepth < 7 * ONE_PLY
+ && ss->staticEval + 256 + 200 * predictedDepth / ONE_PLY <= alpha)
+ continue;
+
+ // Prune moves with negative SEE at low depths and below a decreasing
+ // threshold at higher depths.
+ if (predictedDepth < 8 * ONE_PLY)
{
- futilityValue = ss->staticEval + futility_margin(predictedDepth) + 256;
+ Value see_v = predictedDepth < 4 * ONE_PLY ? VALUE_ZERO
+ : -PawnValueMg * 2 * int(predictedDepth - 3 * ONE_PLY);
- if (futilityValue <= alpha)
- {
- bestValue = std::max(bestValue, futilityValue);
+ if (pos.see_sign(move) < see_v)
continue;
- }
}
-
- // Prune moves with negative SEE at low depths
- if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < VALUE_ZERO)
- continue;
}
// Speculative prefetch as early as possible
}
ss->currentMove = move;
- ss->counterMoves = &CounterMoveHistory[pos.moved_piece(move)][to_sq(move)];
+ ss->counterMoves = &CounterMoveHistory[moved_piece][to_sq(move)];
// Step 14. Make the move
pos.do_move(move, st, givesCheck);
&& !captureOrPromotion)
{
Depth r = reduction<PvNode>(improving, depth, moveCount);
- Value hValue = thisThread->history[pos.piece_on(to_sq(move))][to_sq(move)];
- Value cmhValue = cmh[pos.piece_on(to_sq(move))][to_sq(move)];
-
- const CounterMoveStats* fm = (ss - 2)->counterMoves;
- const CounterMoveStats* fm2 = (ss - 4)->counterMoves;
- Value fmValue = (fm ? (*fm)[pos.piece_on(to_sq(move))][to_sq(move)] : VALUE_ZERO);
- Value fm2Value = (fm2 ? (*fm2)[pos.piece_on(to_sq(move))][to_sq(move)] : VALUE_ZERO);
+ Value val = thisThread->history[moved_piece][to_sq(move)]
+ + (cmh ? (*cmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
+ + (fmh ? (*fmh )[moved_piece][to_sq(move)] : VALUE_ZERO)
+ + (fmh2 ? (*fmh2)[moved_piece][to_sq(move)] : VALUE_ZERO)
+ + thisThread->fromTo.get(~pos.side_to_move(), move);
- // Increase reduction for cut nodes and moves with a bad history
- if ( (!PvNode && cutNode)
- || (hValue < VALUE_ZERO && cmhValue <= VALUE_ZERO))
- r += ONE_PLY;
-
- // Decrease/increase reduction for moves with a good/bad history
- int rHist = (hValue + cmhValue + fmValue + fm2Value) / 20000;
- r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY);
+ // Increase reduction for cut nodes
+ if (cutNode)
+ r += 2 * ONE_PLY;
// Decrease reduction for moves that escape a capture. Filter out
// castling moves, because they are coded as "king captures rook" and
// hence break make_move(). Also use see() instead of see_sign(),
// because the destination square is empty.
- if ( r
- && type_of(move) == NORMAL
- && type_of(pos.piece_on(to_sq(move))) != PAWN
- && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO)
- r = std::max(DEPTH_ZERO, r - ONE_PLY);
+ else if ( type_of(move) == NORMAL
+ && type_of(pos.piece_on(to_sq(move))) != PAWN
+ && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO)
+ r -= 2 * ONE_PLY;
+
+ // Decrease/increase reduction for moves with a good/bad history
+ int rHist = (val - 10000) / 20000;
+ r = std::max(DEPTH_ZERO, r - rHist * ONE_PLY);
Depth d = std::max(newDepth - r, ONE_PLY);
// Bonus for prior countermove that caused the fail low
else if ( depth >= 3 * ONE_PLY
&& !bestMove
- && !inCheck
&& !pos.captured_piece_type()
&& is_ok((ss-1)->currentMove))
{
- Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
+ Square prevSq = to_sq((ss-1)->currentMove);
+ Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2);
if ((ss-2)->counterMoves)
(ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, bonus);
ss->killers[0] = move;
}
- Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + depth / ONE_PLY - 1);
+ Color c = pos.side_to_move();
+ Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY) + 2 * depth / ONE_PLY - 2);
Square prevSq = to_sq((ss-1)->currentMove);
CounterMoveStats* cmh = (ss-1)->counterMoves;
Thread* thisThread = pos.this_thread();
thisThread->history.update(pos.moved_piece(move), to_sq(move), bonus);
+ thisThread->fromTo.update(c, move, bonus);
if (cmh)
{
for (int i = 0; i < quietsCnt; ++i)
{
thisThread->history.update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
+ thisThread->fromTo.update(c, quiets[i], -bonus);
if (cmh)
cmh->update(pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
if ((ss-1)->moveCount == 1 && !pos.captured_piece_type())
{
if ((ss-2)->counterMoves)
- (ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
+ (ss-2)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1);
if ((ss-3)->counterMoves)
- (ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
+ (ss-3)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1);
if ((ss-5)->counterMoves)
- (ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY);
+ (ss-5)->counterMoves->update(pos.piece_on(prevSq), prevSq, -bonus - 2 * (depth + 1) / ONE_PLY - 1);
}
}
}
-/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and
-/// inserts the PV back into the TT. This makes sure the old PV moves are searched
-/// first, even if the old TT entries have been overwritten.
-
-void RootMove::insert_pv_in_tt(Position& pos) {
-
- StateInfo state[MAX_PLY], *st = state;
- bool ttHit;
-
- for (Move m : pv)
- {
- assert(MoveList<LEGAL>(pos).contains(m));
-
- TTEntry* tte = TT.probe(pos.key(), ttHit);
-
- if (!ttHit || tte->move() != m) // Don't overwrite correct entries
- tte->save(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE,
- m, VALUE_NONE, TT.generation());
-
- pos.do_move(m, *st++, pos.gives_check(m, CheckInfo(pos)));
- }
-
- for (size_t i = pv.size(); i > 0; )
- pos.undo_move(pv[--i]);
-}
-
-
/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
/// before exiting the search, for instance, in case we stop the search during a
/// fail high at root. We try hard to have a ponder move to return to the GUI,
pos.do_move(pv[0], st, pos.gives_check(pv[0], CheckInfo(pos)));
TTEntry* tte = TT.probe(pos.key(), ttHit);
- pos.undo_move(pv[0]);
if (ttHit)
{
Move m = tte->move(); // Local copy to be SMP safe
if (MoveList<LEGAL>(pos).contains(m))
- return pv.push_back(m), true;
+ pv.push_back(m);
+ }
+
+ pos.undo_move(pv[0]);
+ return pv.size() > 1;
+}
+
+void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) {
+
+ Hits = 0;
+ RootInTB = false;
+ UseRule50 = Options["Syzygy50MoveRule"];
+ ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
+ Cardinality = Options["SyzygyProbeLimit"];
+
+ // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality
+ if (Cardinality > MaxCardinality)
+ {
+ Cardinality = MaxCardinality;
+ ProbeDepth = DEPTH_ZERO;
}
- return false;
+ if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING))
+ return;
+
+ // If the current root position is in the tablebases, then RootMoves
+ // contains only moves that preserve the draw or the win.
+ RootInTB = root_probe(pos, rootMoves, TB::Score);
+
+ if (RootInTB)
+ Cardinality = 0; // Do not probe tablebases during the search
+
+ else // If DTZ tables are missing, use WDL tables as a fallback
+ {
+ // Filter out moves that do not preserve the draw or the win.
+ RootInTB = root_probe_wdl(pos, rootMoves, TB::Score);
+
+ // Only probe during search if winning
+ if (TB::Score <= VALUE_DRAW)
+ Cardinality = 0;
+ }
+
+ if (RootInTB)
+ {
+ Hits = rootMoves.size();
+
+ if (!UseRule50)
+ TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
+ : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
+ : VALUE_DRAW;
+ }
}