const bool UseIIDAtPVNodes = true;
const bool UseIIDAtNonPVNodes = false;
+ // Use null move driven internal iterative deepening?
+ bool UseNullDrivenIID = false;
+
// Internal iterative deepening margin. At Non-PV moves, when
// UseIIDAtNonPVNodes is true, we do an internal iterative deepening search
// when the static evaluation is at most IIDMargin below beta.
// Time managment variables
int SearchStartTime;
int MaxNodes, MaxDepth;
- int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, TimeAdvantage;
+ int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime;
Move BestRootMove, PonderMove, EasyMove;
int RootMoveNumber;
bool InfiniteSearch;
if (UseLogFile)
LogFile.open(get_option_value_string("Search Log Filename").c_str(), std::ios::out | std::ios::app);
+ UseNullDrivenIID = get_option_value_bool("Null driven IID");
UseQSearchFutilityPruning = get_option_value_bool("Futility Pruning (Quiescence Search)");
UseFutilityPruning = get_option_value_bool("Futility Pruning (Main Search)");
int myIncrement = increment[side_to_move];
int oppTime = time[1 - side_to_move];
- TimeAdvantage = myTime - oppTime;
-
if (!movesToGo) // Sudden death time control
{
if (myIncrement)
ExtraSearchTime = BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2)
+ BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3);
- // If we need some more and we are in time advantage take it
- if (ExtraSearchTime > 0 && TimeAdvantage > 2 * MaxSearchTime)
- ExtraSearchTime += MaxSearchTime / 2;
-
// Try to guess if the current iteration is the last one or the last two
LastIterations = (current_search_time() > ((MaxSearchTime + ExtraSearchTime)*58) / 128);
if (UseLogFile)
{
+ if (dbg_show_mean)
+ dbg_print_mean(LogFile);
+
+ if (dbg_show_hit_rate)
+ dbg_print_hit_rate(LogFile);
+
UndoInfo u;
LogFile << "Nodes: " << nodes_searched() << std::endl
<< "Nodes/second: " << nps() << std::endl
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
- ss[ply].currentMove = ttMove; // can be MOVE_NONE ?
+ ss[ply].currentMove = ttMove; // can be MOVE_NONE
return value_from_tt(tte->value(), ply);
}
Value approximateEval = quick_evaluate(pos);
bool mateThreat = false;
+ bool nullDrivenIID = false;
bool isCheck = pos.is_check();
// Null move search
if ( allowNullmove
+ && depth > OnePly
&& !isCheck
&& ok_to_do_nullmove(pos)
&& approximateEval >= beta - NullMoveMargin)
UndoInfo u;
pos.do_null_move(u);
int R = (depth > 7 ? 4 : 3);
+
Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
+
+ // Check for a null capture artifact, if the value without the null capture
+ // is above beta then there is a good possibility that this is a cut-node.
+ // We will do an IID later to find a ttMove.
+ if ( UseNullDrivenIID
+ && nullValue < beta
+ && depth > 6 * OnePly
+ && ttMove == MOVE_NONE
+ && ss[ply + 1].currentMove != MOVE_NONE
+ && pos.move_is_capture(ss[ply + 1].currentMove)
+ && pos.see(ss[ply + 1].currentMove) + nullValue >= beta)
+ nullDrivenIID = true;
+
pos.undo_null_move(u);
if (nullValue >= beta)
return beta;
} 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
+ // some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
- // move which was reduced. If a connection is found, return a fail
+ // move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
if (nullValue == value_mated_in(ply + 2))
+ {
mateThreat = true;
-
+ nullDrivenIID = false;
+ }
ss[ply].threatMove = ss[ply + 1].currentMove;
if ( depth < ThreatDepth
&& ss[ply - 1].reduction
search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
ttMove = ss[ply].pv[ply];
}
+ else if (nullDrivenIID)
+ {
+ // The null move failed low due to a suspicious capture. Perhaps we
+ // are facing a null capture artifact due to the side to move change
+ // and this is a cut-node. So it's a good time to search for a ttMove.
+ Move tm = ss[ply].threatMove;
+
+ assert(tm != MOVE_NONE);
+ assert(ttMove == MOVE_NONE);
+
+ search(pos, ss, beta, depth/2, ply, false, threadID);
+ ttMove = ss[ply].pv[ply];
+ ss[ply].threatMove = tm;
+ }
// Initialize a MovePicker object for the current position, and prepare
// to search all moves:
tto = move_to(threat);
// Case 1: Castling moves are never pruned.
- if(move_is_castle(m))
- return false;
+ if (move_is_castle(m))
+ return false;
// Case 2: Don't prune moves which move the threatened piece
- if(!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
- return false;
+ if (!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
+ return false;
// Case 3: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune move which defend it.
- if(!PruneDefendingMoves && threat != MOVE_NONE
- && (piece_value_midgame(pos.piece_on(tfrom))
- >= piece_value_midgame(pos.piece_on(tto)))
- && pos.move_attacks_square(m, tto))
+ if ( !PruneDefendingMoves
+ && threat != MOVE_NONE
+ && pos.type_of_piece_on(tto) != NO_PIECE_TYPE
+ && ( 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;
// Case 4: Don't prune moves with good history.
- if(!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
- return false;
+ if (!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
+ return false;
// Case 5: If the moving piece in the threatened move is a slider, don't
// prune safe moves which block its ray.
- if(!PruneBlockingMoves && threat != MOVE_NONE
- && piece_is_slider(pos.piece_on(tfrom))
- && bit_is_set(squares_between(tfrom, tto), mto) && pos.see(m) >= 0)
- return false;
+ if ( !PruneBlockingMoves
+ && threat != MOVE_NONE
+ && piece_is_slider(pos.piece_on(tfrom))
+ && bit_is_set(squares_between(tfrom, tto), mto) && pos.see(m) >= 0)
+ return false;
return true;
}