bool RootInTB;
bool UseRule50;
Depth ProbeDepth;
- Value Score;
}
namespace TB = Tablebases;
// History and stats update bonus, based on depth
int stat_bonus(Depth depth) {
int d = depth / ONE_PLY;
- return d > 17 ? 0 : d * d + 2 * d - 2;
+ return d > 17 ? 0 : 32 * d * d + 64 * d - 64;
}
// Skill structure is used to implement strength limit
multiPV = std::min(multiPV, rootMoves.size());
- int ct = Options["Contempt"] * PawnValueEg / 100; // From centipawns
+ int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns
// In analysis mode, adjust contempt in accordance with user preference
if (Limits.infinite || Options["UCI_AnalyseMode"])
for (RootMove& rm : rootMoves)
rm.previousScore = rm.score;
+ size_t PVFirst = 0;
+ PVLast = 0;
+
// MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < multiPV && !Threads.stop; ++PVIdx)
{
+ if (PVIdx == PVLast)
+ {
+ PVFirst = PVLast;
+ for (PVLast++; PVLast < rootMoves.size(); PVLast++)
+ if (rootMoves[PVLast].TBRank != rootMoves[PVFirst].TBRank)
+ break;
+ }
+
// Reset UCI info selDepth for each depth and each PV line
selDepth = 0;
beta = std::min(previousScore + delta, VALUE_INFINITE);
// Adjust contempt based on root move's previousScore (dynamic contempt)
- int dct = ct + int(std::round(48 * atan(float(previousScore) / 128)));
+ int dct = ct + 88 * previousScore / (abs(previousScore) + 200);
contempt = (us == WHITE ? make_score(dct, dct / 2)
: -make_score(dct, dct / 2));
// and we want to keep the same order for all the moves except the
// new PV that goes to the front. Note that in case of MultiPV
// search the already searched PV lines are preserved.
- std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.end());
+ std::stable_sort(rootMoves.begin() + PVIdx, rootMoves.begin() + PVLast);
// If search has been stopped, we break immediately. Sorting is
// safe because RootMoves is still valid, although it refers to
}
// Sort the PV lines searched so far and update the GUI
- std::stable_sort(rootMoves.begin(), rootMoves.begin() + PVIdx + 1);
+ std::stable_sort(rootMoves.begin() + PVFirst, rootMoves.begin() + PVIdx + 1);
if ( mainThread
&& (Threads.stop || PVIdx + 1 == multiPV || Time.elapsed() > 3000))
}
// Step 11. Internal iterative deepening (skipped when in check, ~2 Elo)
- if ( depth >= 6 * ONE_PLY
- && !ttMove
- && (PvNode || ss->staticEval + 128 >= beta))
+ if ( depth >= 8 * ONE_PLY
+ && !ttMove)
{
Depth d = 3 * depth / 4 - 2 * ONE_PLY;
search<NT>(pos, ss, alpha, beta, d, cutNode, true);
const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory };
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
- MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers);
+ MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
+ &thisThread->captureHistory,
+ contHist,
+ countermove,
+ ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
skipQuiets = false;
// At root obey the "searchmoves" option and skip moves not listed in Root
// Move List. As a consequence any illegal move is also skipped. In MultiPV
- // mode we also skip PV moves which have been already searched.
+ // mode we also skip PV moves which have been already searched and those
+ // of lower "TB rank" if we are in a TB root position.
if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->PVIdx,
- thisThread->rootMoves.end(), move))
+ thisThread->rootMoves.begin() + thisThread->PVLast, move))
continue;
ss->moveCount = ++moveCount;
{
Depth r = reduction<PvNode>(improving, depth, moveCount);
- if (captureOrPromotion)
+ if (captureOrPromotion) // (~5 Elo)
r -= r ? ONE_PLY : DEPTH_ZERO;
else
{
- // Decrease reduction if opponent's move count is high
+ // Decrease reduction if opponent's move count is high (~5 Elo)
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
- // Decrease reduction for exact PV nodes
+ // Decrease reduction for exact PV nodes (~0 Elo)
if (pvExact)
r -= ONE_PLY;
- // Increase reduction if ttMove is a capture
+ // Increase reduction if ttMove is a capture (~0 Elo)
if (ttCapture)
r += ONE_PLY;
- // Increase reduction for cut nodes
+ // Increase reduction for cut nodes (~5 Elo)
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().
+ // hence break make_move(). (~5 Elo)
else if ( type_of(move) == NORMAL
&& !pos.see_ge(make_move(to_sq(move), from_sq(move))))
r -= 2 * ONE_PLY;
+ (*contHist[3])[movedPiece][to_sq(move)]
- 4000;
- // Decrease/increase reduction by comparing opponent's stat score
+ // Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
if (ss->statScore >= 0 && (ss-1)->statScore < 0)
r -= ONE_PLY;
else if ((ss-1)->statScore >= 0 && ss->statScore < 0)
r += ONE_PLY;
- // Decrease/increase reduction for moves with a good/bad history
+ // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->statScore / 20000) * ONE_PLY);
}
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated.
- MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove));
+ MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory,
+ &pos.this_thread()->captureHistory,
+ to_sq((ss-1)->currentMove));
// Loop through the moves until no moves remain or a beta cutoff occurs
while ((move = mp.next_move()) != MOVE_NONE)
Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore;
bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY;
- v = tb ? TB::Score : v;
+ v = tb ? rootMoves[i].TBScore : v;
if (ss.rdbuf()->in_avail()) // Not at first line
ss << "\n";
return pv.size() > 1;
}
-
-void Tablebases::filter_root_moves(Position& pos, Search::RootMoves& rootMoves) {
+void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
RootInTB = false;
- UseRule50 = Options["Syzygy50MoveRule"];
- ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY;
- Cardinality = Options["SyzygyProbeLimit"];
+ UseRule50 = bool(Options["Syzygy50MoveRule"]);
+ ProbeDepth = int(Options["SyzygyProbeDepth"]) * ONE_PLY;
+ Cardinality = int(Options["SyzygyProbeLimit"]);
+ bool dtz_available = true;
- // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality
+ // Tables with fewer pieces than SyzygyProbeLimit are searched with
+ // ProbeDepth == DEPTH_ZERO
if (Cardinality > MaxCardinality)
{
Cardinality = MaxCardinality;
ProbeDepth = DEPTH_ZERO;
}
- if (Cardinality < popcount(pos.pieces()) || pos.can_castle(ANY_CASTLING))
- return;
-
- // Don't filter any moves if the user requested analysis on multiple
- if (Options["MultiPV"] != 1)
- return;
+ if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
+ {
+ // Rank moves using DTZ tables
+ RootInTB = root_probe(pos, rootMoves);
- // 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)
+ {
+ // DTZ tables are missing; try to rank moves using WDL tables
+ dtz_available = false;
+ RootInTB = root_probe_wdl(pos, rootMoves);
+ }
+ }
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);
+ // Sort moves according to TB rank
+ std::sort(rootMoves.begin(), rootMoves.end(),
+ [](const RootMove &a, const RootMove &b) { return a.TBRank > b.TBRank; } );
- // Only probe during search if winning
- if (RootInTB && TB::Score <= VALUE_DRAW)
+ // Probe during search only if DTZ is not available and we are winning
+ if (dtz_available || rootMoves[0].TBScore <= VALUE_DRAW)
Cardinality = 0;
}
-
- if (RootInTB && !UseRule50)
- TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1
- : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1
- : VALUE_DRAW;
-
- // Since root_probe() and root_probe_wdl() dirty the root move scores,
- // we reset them to -VALUE_INFINITE
- for (RootMove& rm : rootMoves)
- rm.score = -VALUE_INFINITE;
+ else
+ {
+ // Assign the same rank to all moves
+ for (auto& m : rootMoves)
+ m.TBRank = 0;
+ }
}