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_prune(const Position& pos, Move m, Move threat);
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);
void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
void update_killers(Move m, SearchStack* ss);
// 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
// Initialize a MovePicker object for the current position
MovePicker mp = MovePicker(pos, ttMove, depth, H, ss, (PvNode ? -VALUE_INFINITE : beta));
CheckInfo ci(pos);
+ bool singularExtensionNode = depth >= SingularExtensionDepth[PvNode]
+ && tte && tte->move()
+ && !excludedMove // Do not allow recursive singular extension search
+ && is_lower_bound(tte->type())
+ && tte->depth() >= depth - 3 * OnePly;
// Step 10. Loop through moves
// Loop through all legal moves until no moves remain or a beta cutoff occurs
// Singular extension search. We extend the TT move if its value is much better than
// its siblings. To verify this we do a reduced search on all the other moves but the
// ttMove, if result is lower then ttValue minus a margin then we extend ttMove.
- if ( depth >= SingularExtensionDepth[PvNode]
- && tte
+ if ( singularExtensionNode
&& move == tte->move()
- && !excludedMove // Do not allow recursive singular extension search
- && ext < OnePly
- && is_lower_bound(tte->type())
- && tte->depth() >= depth - 3 * OnePly)
+ && ext < OnePly)
{
Value ttValue = value_from_tt(tte->value(), ply);
// 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)
- && ok_to_prune(pos, move, ss->threatMove)
+ && !(ss->threatMove && connected_threat(pos, move, ss->threatMove))
&& bestValue > value_mated_in(PLY_MAX))
continue;
bool doFullDepthSearch = true;
if ( depth >= 3 * OnePly
- && !dangerous
&& !captureOrPromotion
+ && !dangerous
&& !move_is_castle(move)
&& !move_is_killer(move, ss))
{
ss->reduction = reduction<PvNode>(depth, moveCount);
if (ss->reduction)
{
- Depth r = newDepth - ss->reduction;
- value = r < OnePly ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID)
- : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, r, true, threadID);
+ Depth d = newDepth - ss->reduction;
+ value = d < OnePly ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID)
+ : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true, threadID);
doFullDepthSearch = (value > alpha);
}
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth-ss->reduction, true, threadID);
doFullDepthSearch = (value > alpha);
}
+ ss->reduction = Depth(0); // Restore original reduction
}
// Step 15. Full depth search
if (doFullDepthSearch)
{
- ss->reduction = Depth(0);
value = newDepth < OnePly ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, Depth(0), threadID)
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, true, threadID);
}
// 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);
}
{
// Move count based pruning
if ( moveCount >= futility_move_count(sp->depth)
- && ok_to_prune(pos, move, ss->threatMove)
+ && !(ss->threatMove && connected_threat(pos, move, ss->threatMove))
&& sp->bestValue > value_mated_in(PLY_MAX))
{
lock_grab(&(sp->lock));
}
- // 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);
- }
-
-
- // ok_to_prune() tests whether it is safe to forward prune a move. Only
- // non-tactical moves late in the move list close to the leaves are
- // candidates for pruning.
+ // 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.
- bool ok_to_prune(const Position& pos, Move m, Move threat) {
+ bool connected_threat(const Position& pos, Move m, Move threat) {
assert(move_is_ok(m));
- assert(threat == MOVE_NONE || move_is_ok(threat));
+ assert(threat && move_is_ok(threat));
assert(!pos.move_is_check(m));
assert(!pos.move_is_capture_or_promotion(m));
assert(!pos.move_is_passed_pawn_push(m));
Square mfrom, mto, tfrom, tto;
- // Prune if there isn't any threat move
- if (threat == MOVE_NONE)
- return true;
-
mfrom = move_from(m);
mto = move_to(m);
tfrom = move_from(threat);
// Case 1: Don't prune moves which move the threatened piece
if (mfrom == tto)
- return false;
+ return true;
// Case 2: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune move which defend it.
&& ( pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto)
|| pos.type_of_piece_on(tfrom) == KING)
&& pos.move_attacks_square(m, tto))
- return false;
+ return true;
// Case 3: If the moving piece in the threatened move is a slider, don't
// prune safe moves which block its ray.
if ( piece_is_slider(pos.piece_on(tfrom))
&& bit_is_set(squares_between(tfrom, tto), mto)
&& pos.see_sign(m) >= 0)
- return false;
+ return true;
- return true;
+ return false;
}