// Razor and futility margins
const int RazorMargin1 = 590;
const int RazorMargin2 = 604;
- Value futility_margin(Depth d) { return Value(150 * d / ONE_PLY); }
+ Value futility_margin(Depth d, bool improving) {
+ return Value((175 - 50 * improving) * d / ONE_PLY);
+ }
// Futility and reductions lookup tables, initialized at startup
int FutilityMoveCounts[2][16]; // [improving][depth]
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode, bool skipEarlyPruning);
- template <NodeType NT, bool InCheck>
+ template <NodeType NT>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = DEPTH_ZERO);
Value value_to_tt(Value v, int ply);
ct = Options["Contempt"] * PawnValueEg / 100; // From centipawns
// Adjust contempt based on current bestValue (dynamic contempt)
- int sign = (bestValue > 0) - (bestValue < 0);
- ct += bestValue > 500 ? 70 :
- bestValue < -500 ? -70 :
- bestValue / 10 + sign * int(std::round(3.22 * log(1 + abs(bestValue))));
+ ct += int(std::round(48 * atan(float(bestValue) / 128)));
Eval::Contempt = (us == WHITE ? make_score(ct, ct / 2)
: -make_score(ct, ct / 2));
if (inCheck)
{
ss->staticEval = eval = VALUE_NONE;
+ improving = true;
goto moves_loop;
}
else if (ttHit)
ss->staticEval, TT.generation());
}
+ improving = ss->staticEval >= (ss-2)->staticEval
+ ||(ss-2)->staticEval == VALUE_NONE;
+
if (skipEarlyPruning || !pos.non_pawn_material(pos.side_to_move()))
goto moves_loop;
{
if ( depth == ONE_PLY
&& eval + RazorMargin1 <= alpha)
- return qsearch<NonPV, false>(pos, ss, alpha, alpha+1);
+ return qsearch<NonPV>(pos, ss, alpha, alpha+1);
else if (eval + RazorMargin2 <= alpha)
{
Value ralpha = alpha - RazorMargin2;
- Value v = qsearch<NonPV, false>(pos, ss, ralpha, ralpha+1);
+ Value v = qsearch<NonPV>(pos, ss, ralpha, ralpha+1);
if (v <= ralpha)
return v;
// Step 8. Futility pruning: child node (skipped when in check)
if ( !rootNode
&& depth < 7 * ONE_PLY
- && eval - futility_margin(depth) >= beta
+ && eval - futility_margin(depth, improving) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
ss->contHistory = thisThread->contHistory[NO_PIECE][0].get();
pos.do_null_move(st);
- Value nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -beta+1)
+ Value nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -beta+1)
: - search<NonPV>(pos, ss+1, -beta, -beta+1, depth-R, !cutNode, true);
pos.undo_null_move();
thisThread->nmp_ply = ss->ply + 3 * (depth-R) / 4;
thisThread->nmp_odd = ss->ply % 2;
- Value v = depth-R < ONE_PLY ? qsearch<NonPV, false>(pos, ss, beta-1, beta)
+ Value v = depth-R < ONE_PLY ? qsearch<NonPV>(pos, ss, beta-1, beta)
: search<NonPV>(pos, ss, beta-1, beta, depth-R, false, true);
thisThread->nmp_odd = thisThread->nmp_ply = 0;
Value rbeta = std::min(beta + 200, VALUE_INFINITE);
MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);
int probCutCount = 0;
+
while ( (move = mp.next_move()) != MOVE_NONE
&& probCutCount < depth / ONE_PLY - 3)
if (pos.legal(move))
pos.do_move(move, st);
- // Perform a preliminary search at depth 1 to verify that the move holds.
- // We will only do this search if the depth is not 5, thus avoiding two
- // searches at depth 1 in a row.
- if (depth > 5 * ONE_PLY)
- value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, ONE_PLY, !cutNode, true);
+ // Perform a preliminary qsearch to verify that the move holds
+ value = -qsearch<NonPV>(pos, ss+1, -rbeta, -rbeta+1);
- // If the first search was skipped or was performed and held, perform
- // the regular search.
- if (depth == 5 * ONE_PLY || value >= rbeta)
+ // If the qsearch held perform the regular search
+ if (value >= rbeta)
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, depth - 4 * ONE_PLY, !cutNode, false);
pos.undo_move(move);
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
- improving = ss->staticEval >= (ss-2)->staticEval
- /* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */
- ||(ss-2)->staticEval == VALUE_NONE;
singularExtensionNode = !rootNode
&& depth >= 8 * ONE_PLY
// Step 17. Full depth search when LMR is skipped or fails high
if (doFullDepthSearch)
- value = newDepth < ONE_PLY ?
- givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha)
- : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha)
+ value = newDepth < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha)
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode, false);
// For PV nodes only, do a full PV search on the first move or after a fail
(ss+1)->pv = pv;
(ss+1)->pv[0] = MOVE_NONE;
- value = newDepth < ONE_PLY ?
- givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha)
- : -qsearch<PV, false>(pos, ss+1, -beta, -alpha)
+ value = newDepth < ONE_PLY ? -qsearch<PV>(pos, ss+1, -beta, -alpha)
: - search<PV>(pos, ss+1, -beta, -alpha, newDepth, false, false);
}
// qsearch() is the quiescence search function, which is called by the main
// search function with depth zero, or recursively with depth less than ONE_PLY.
-
- template <NodeType NT, bool InCheck>
+ template <NodeType NT>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
const bool PvNode = NT == PV;
+ const bool InCheck = bool(pos.checkers());
assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
assert(PvNode || (alpha == beta - 1));
assert(depth <= DEPTH_ZERO);
assert(depth / ONE_PLY * ONE_PLY == depth);
- assert(InCheck == bool(pos.checkers()));
Move pv[MAX_PLY+1];
StateInfo st;
// Make and search the move
pos.do_move(move, st, givesCheck);
- value = givesCheck ? -qsearch<NT, true>(pos, ss+1, -beta, -alpha, depth - ONE_PLY)
- : -qsearch<NT, false>(pos, ss+1, -beta, -alpha, depth - ONE_PLY);
+ value = -qsearch<NT>(pos, ss+1, -beta, -alpha, depth - ONE_PLY);
pos.undo_move(move);
assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);