bool connected_moves(const Position& pos, Move m1, Move m2);
bool value_is_mate(Value value);
bool move_is_killer(Move m, SearchStack* ss);
- bool ok_to_do_nullmove(const Position& pos);
bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
bool connected_threat(const Position& pos, Move m, Move threat);
Value refine_eval(const TTEntry* tte, Value defaultEval, int ply);
int64_t nodes_searched() { return TM.nodes_searched(); }
+/// init_search() is called during startup. It initializes various lookup tables
+
+void init_search() {
+
+ int d; // depth (OnePly == 2)
+ int hd; // half depth (OnePly == 1)
+ int mc; // moveCount
+
+ // Init reductions array
+ for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
+ {
+ double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
+ double nonPVRed = log(double(hd)) * log(double(mc)) / 1.5;
+ ReductionMatrix[PV][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(OnePly)) : 0);
+ ReductionMatrix[NonPV][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(OnePly)) : 0);
+ }
+
+ // Init futility margins array
+ for (d = 0; d < 16; d++) for (mc = 0; mc < 64; mc++)
+ FutilityMarginsMatrix[d][mc] = 112 * int(log(double(d * d) / 2) / log(2.0) + 1) - 8 * mc + 45;
+
+ // Init futility move count array
+ for (d = 0; d < 32; d++)
+ FutilityMoveCountArray[d] = 3 + (1 << (3 * d / 8));
+}
+
+
+// SearchStack::init() initializes a search stack. Used at the beginning of a
+// new search from the root.
+void SearchStack::init(int ply) {
+
+ pv[ply] = pv[ply + 1] = MOVE_NONE;
+ currentMove = threatMove = MOVE_NONE;
+ reduction = Depth(0);
+ eval = VALUE_NONE;
+}
+
+void SearchStack::initKillers() {
+
+ mateKiller = MOVE_NONE;
+ for (int i = 0; i < KILLER_MAX; i++)
+ killers[i] = MOVE_NONE;
+}
+
+
/// perft() is our utility to verify move generation is bug free. All the legal
/// moves up to given depth are generated and counted and the sum returned.
}
-/// init_search() is called during startup. It initializes various lookup tables
-
-void init_search() {
-
- // Init our reduction lookup tables
- for (int i = 1; i < 64; i++) // i == depth (OnePly = 1)
- for (int j = 1; j < 64; j++) // j == moveNumber
- {
- double pvRed = log(double(i)) * log(double(j)) / 3.0;
- double nonPVRed = log(double(i)) * log(double(j)) / 1.5;
- ReductionMatrix[PV][i][j] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(OnePly)) : 0);
- ReductionMatrix[NonPV][i][j] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(OnePly)) : 0);
- }
-
- // Init futility margins array
- for (int i = 0; i < 16; i++) // i == depth (OnePly = 2)
- for (int j = 0; j < 64; j++) // j == moveNumber
- {
- // FIXME: test using log instead of BSR
- FutilityMarginsMatrix[i][j] = (i < 2 ? 0 : 112 * bitScanReverse32(i * i / 2)) - 8 * j + 45;
- }
-
- // Init futility move count array
- for (int i = 0; i < 32; i++) // i == depth (OnePly = 2)
- FutilityMoveCountArray[i] = 3 + (1 << (3 * i / 8));
-}
-
-
-// SearchStack::init() initializes a search stack. Used at the beginning of a
-// new search from the root.
-void SearchStack::init(int ply) {
-
- pv[ply] = pv[ply + 1] = MOVE_NONE;
- currentMove = threatMove = MOVE_NONE;
- reduction = Depth(0);
- eval = VALUE_NONE;
-}
-
-void SearchStack::initKillers() {
-
- mateKiller = MOVE_NONE;
- for (int i = 0; i < KILLER_MAX; i++)
- killers[i] = MOVE_NONE;
-}
-
namespace {
// id_loop() is the main iterative deepening loop. It calls root_search
// Step 6. Razoring (is omitted in PV nodes)
if ( !PvNode
+ && depth < RazorDepth
+ && !isCheck
&& refinedValue < beta - razor_margin(depth)
&& ttMove == MOVE_NONE
&& (ss-1)->currentMove != MOVE_NULL
- && depth < RazorDepth
- && !isCheck
&& !value_is_mate(beta)
&& !pos.has_pawn_on_7th(pos.side_to_move()))
{
if ( !PvNode
&& allowNullmove
&& depth < RazorDepth
+ && refinedValue >= beta + futility_margin(depth, 0)
&& !isCheck
&& !value_is_mate(beta)
- && ok_to_do_nullmove(pos)
- && refinedValue >= beta + futility_margin(depth, 0))
+ && pos.non_pawn_material(pos.side_to_move()))
return refinedValue - futility_margin(depth, 0);
// Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode
&& allowNullmove
&& depth > OnePly
+ && refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0)
&& !isCheck
&& !value_is_mate(beta)
- && ok_to_do_nullmove(pos)
- && refinedValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0))
+ && pos.non_pawn_material(pos.side_to_move()))
{
ss->currentMove = MOVE_NULL;
if (nullValue >= value_mate_in(PLY_MAX))
nullValue = beta;
- if (depth < 6 * OnePly)
+ // Do zugzwang verification search at high depths
+ if ( depth < 6 * OnePly
+ || search<NonPV>(pos, ss, alpha, beta, depth-5*OnePly, false, threadID) >= beta)
return nullValue;
-
- // Do zugzwang verification search
- Value v = search<NonPV>(pos, ss, alpha, beta, depth-5*OnePly, false, threadID);
- if (v >= beta)
- return nullValue;
- } else {
+ }
+ else
+ {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// Step 12. Futility pruning (is omitted in PV nodes)
if ( !PvNode
+ && !captureOrPromotion
&& !isCheck
&& !dangerous
- && !captureOrPromotion
- && !move_is_castle(move)
- && move != ttMove)
+ && move != ttMove
+ && !move_is_castle(move))
{
// Move count based pruning
if ( moveCount >= futility_move_count(depth)
bool doFullDepthSearch = true;
if ( depth >= 3 * OnePly
- && !dangerous
&& !captureOrPromotion
+ && !dangerous
&& !move_is_castle(move)
&& !move_is_killer(move, ss))
{
}
// Step 18. Check for split
- if ( TM.active_threads() > 1
+ if ( depth >= MinimumSplitDepth
+ && TM.active_threads() > 1
&& bestValue < beta
- && depth >= MinimumSplitDepth
- && Iteration <= 99
&& TM.available_thread_exists(threadID)
&& !AbortSearch
- && !TM.thread_should_stop(threadID))
+ && !TM.thread_should_stop(threadID)
+ && Iteration <= 99)
TM.split<FakeSplit>(pos, ss, &alpha, beta, &bestValue, depth,
mateThreat, &moveCount, &mp, threadID, PvNode);
}
// Step 12. Futility pruning (is omitted in PV nodes)
if ( !PvNode
+ && !captureOrPromotion
&& !isCheck
&& !dangerous
- && !captureOrPromotion
&& !move_is_castle(move))
{
// Move count based pruning
// If the move fails high will be re-searched at full depth.
bool doFullDepthSearch = true;
- if ( !dangerous
- && !captureOrPromotion
+ if ( !captureOrPromotion
+ && !dangerous
&& !move_is_castle(move)
&& !move_is_killer(move, ss))
{
if (ss->reduction)
{
Value localAlpha = sp->alpha;
- value = -search<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, newDepth-ss->reduction, true, threadID);
+ Depth d = newDepth - ss->reduction;
+ value = d < OnePly ? -qsearch<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, Depth(0), threadID)
+ : - search<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, d, true, threadID);
doFullDepthSearch = (value > localAlpha);
}
// if the move fails high again then go with full depth search.
if (doFullDepthSearch && ss->reduction > 2 * OnePly)
{
+ assert(newDepth - OnePly >= OnePly);
+
ss->reduction = OnePly;
Value localAlpha = sp->alpha;
value = -search<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, newDepth-ss->reduction, true, threadID);
doFullDepthSearch = (value > localAlpha);
}
+ ss->reduction = Depth(0); // Restore original reduction
}
// Step 15. Full depth search
if (doFullDepthSearch)
{
- ss->reduction = Depth(0);
Value localAlpha = sp->alpha;
- value = -search<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, newDepth, true, threadID);
+ value = newDepth < OnePly ? -qsearch<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, Depth(0), threadID)
+ : - search<NonPV>(pos, ss+1, -(localAlpha+1), -localAlpha, newDepth, true, threadID);
+ // Step extra. pv search (only in PV nodes)
+ // Search only for possible new PV nodes, if instead value >= beta then
+ // parent node fails low with value <= alpha and tries another move.
if (PvNode && value > localAlpha && value < sp->beta)
- value = -search<PV>(pos, ss+1, -sp->beta, -sp->alpha, newDepth, false, threadID);
+ value = newDepth < OnePly ? -qsearch<PV>(pos, ss+1, -sp->beta, -sp->alpha, Depth(0), threadID)
+ : - search<PV>(pos, ss+1, -sp->beta, -sp->alpha, newDepth, false, threadID);
}
// Step 16. Undo move
}
- // ok_to_do_nullmove() looks at the current position and decides whether
- // doing a 'null move' should be allowed. In order to avoid zugzwang
- // problems, null moves are not allowed when the side to move has very
- // little material left. Currently, the test is a bit too simple: Null
- // moves are avoided only when the side to move has only pawns left.
- // It's probably a good idea to avoid null moves in at least some more
- // complicated endgames, e.g. KQ vs KR. FIXME
-
- bool ok_to_do_nullmove(const Position& pos) {
-
- return pos.non_pawn_material(pos.side_to_move()) != Value(0);
- }
-
-
// connected_threat() tests whether it is safe to forward prune a move or if
// is somehow coonected to the threat move returned by null search.