Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 76 additions & 6 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ namespace ironphoenix {
constexpr int HISTORY_MOVE_PRUNE_MAX_DEPTH = 4;
constexpr int HISTORY_MOVE_PRUNE_MARGIN_PER_DEPTH = 1'500;

// Post-LMR continuation history: reward a reduced quiet move that proves
// strong enough to beat alpha and require a full-depth re-search.
constexpr int POST_LMR_CONTINUATION_BONUS = 1'334;

constexpr int NULL_MOVE_MIN_DEPTH = 2;
constexpr int NULL_MOVE_DIVISOR = 190;
constexpr int REVERSE_FUTILITY_MAX_DEPTH = 4;
constexpr int REVERSE_FUTILITY_MARGIN_PER_DEPTH = 200;
constexpr std::size_t CHECK_BIT_WORDS = (MAX_MOVES + 63u) / 64u;

// Non-PV nodes normally cut off quickly. Selecting only the strongest
Expand Down Expand Up @@ -516,10 +522,43 @@ namespace ironphoenix {
const bool inCheckNode = pos.inCheck();
const auto& tuning = Tuning::search();
const Color us = pos.sideToMove();
const Color partner = static_cast<Color>(static_cast<unsigned>(us) ^ 2u);
const bool mateWindow = !(alpha > -MATE_THRESHOLD && beta < MATE_THRESHOLD);

// Null Move Pruning (NMP): keep expensive partner-check detection and
// static evaluation completely off nodes where NMP cannot run anyway.
// Static pruning shares one evaluator result between reverse futility
// pruning and null-move pruning. Keep partner-check detection lazy so PV,
// check and mate-window nodes do not pay for it.
int staticEval = 0;
bool staticEvalValid = false;
bool partnerInCheck = false;
bool partnerCheckKnown = false;

// Reverse Futility Pruning (RFP): at shallow non-PV nodes, if the static
// evaluation already exceeds beta by a depth-scaled safety margin, the
// position is unlikely to fall below beta after a normal move. Avoid RFP
// while either teammate is in check and never return a synthetic mate.
if (!pvNode
&& ply > 0
&& depth <= REVERSE_FUTILITY_MAX_DEPTH
&& !inCheckNode
&& !mateWindow
&& hasNonPawnMaterial(pos, us)) {

partnerInCheck = pos.inCheck(partner);
partnerCheckKnown = true;

if (!partnerInCheck) {
staticEval = evaluate(pos);
staticEvalValid = true;

const int margin = REVERSE_FUTILITY_MARGIN_PER_DEPTH * depth;
if (staticEval < MATE_THRESHOLD && staticEval - margin >= beta)
return staticEval;
}
}

// Null Move Pruning (NMP): reuse the static evaluation and partner-check
// result when RFP already computed them at shallow nodes.
if (allowNull
&& !pvNode
&& ply > 0
Expand All @@ -528,9 +567,16 @@ namespace ironphoenix {
&& !mateWindow
&& hasNonPawnMaterial(pos, us)) {

const Color partner = static_cast<Color>(static_cast<unsigned>(us) ^ 2u);
if (!pos.inCheck(partner)) {
const int staticEval = evaluate(pos);
if (!partnerCheckKnown) {
partnerInCheck = pos.inCheck(partner);
partnerCheckKnown = true;
}

if (!partnerInCheck) {
if (!staticEvalValid) {
staticEval = evaluate(pos);
staticEvalValid = true;
}

if (staticEval >= beta) {
StateInfo nullState;
Expand Down Expand Up @@ -716,8 +762,32 @@ namespace ironphoenix {
const int reducedDepth = depth - 1 - reduction;
score = -negamax(pos, reducedDepth, -alpha - 1, -alpha, ply + 1, false);

if (!shouldStop() && score > alpha)
if (!shouldStop() && score > alpha) {
// Post-LMR continuation-history update: the move was
// reduced because it looked unpromising, but the
// reduced search still beat alpha. Reward the
// continuation contexts before the full-depth retry.
if (ply > 0 && searchStack_[ply].valid()) {
history_.updateContinuation(
ContinuationKind::PreviousPly,
searchStack_[ply],
movingPiece,
move.to(),
POST_LMR_CONTINUATION_BONUS);
}

if (ply >= 4 && searchStack_[ply - 3].valid()
&& searchStack_[ply - 3].mover == us) {
history_.updateContinuation(
ContinuationKind::SamePlayer,
searchStack_[ply - 3],
movingPiece,
move.to(),
(POST_LMR_CONTINUATION_BONUS * 3) / 4);
}

score = -negamax(pos, depth - 1, -alpha - 1, -alpha, ply + 1, false);
}
}
else {
score = -negamax(pos, depth - 1, -alpha - 1, -alpha, ply + 1, false);
Expand Down
Loading