void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
void update_killers(Move m, Move killers[]);
void update_gains(const Position& pos, Move move, Value before, Value after);
- void qsearch_scoring(Position& pos, MoveStack* mlist, MoveStack* last);
int current_search_time();
std::string value_to_uci(Value v);
if (!StopRequest && (Pondering || InfiniteSearch))
wait_for_stop_or_ponderhit();
- // Could be both MOVE_NONE when searching on a stalemate position
- cout << "bestmove " << bestMove << " ponder " << ponderMove << endl;
+ // Could be MOVE_NONE when searching on a stalemate position
+ cout << "bestmove " << bestMove;
+
+ // UCI protol is not clear on allowing sending an empty ponder move, instead
+ // it is clear that ponder move is optional. So skip it if empty.
+ if (ponderMove != MOVE_NONE)
+ cout << " ponder " << ponderMove;
+
+ cout << endl;
return !QuitRequest;
}
SearchStack ss[PLY_MAX_PLUS_2];
Value bestValues[PLY_MAX_PLUS_2];
int bestMoveChanges[PLY_MAX_PLUS_2];
- int depth, researchCountFL, researchCountFH, aspirationDelta;
+ int depth, aspirationDelta;
Value value, alpha, beta;
Move bestMove, easyMove;
- // Moves to search are verified, scored and sorted
- Rml.init(pos, searchMoves);
-
- // Initialize FIXME move before Rml.init()
+ // Initialize stuff before a new search
+ memset(ss, 0, 4 * sizeof(SearchStack));
TT.new_search();
H.clear();
- memset(ss, 0, 4 * sizeof(SearchStack));
*ponderMove = bestMove = easyMove = MOVE_NONE;
depth = aspirationDelta = 0;
- ss->currentMove = MOVE_NULL; // Hack to skip update_gains()
alpha = -VALUE_INFINITE, beta = VALUE_INFINITE;
+ ss->currentMove = MOVE_NULL; // Hack to skip update_gains()
+
+ // Moves to search are verified and copied
+ Rml.init(pos, searchMoves);
// Handle special case of searching on a mate/stalemate position
if (Rml.size() == 0)
return MOVE_NONE;
}
- // Is one move significantly better than others after initial scoring ?
- if ( Rml.size() == 1
- || Rml[0].pv_score > Rml[1].pv_score + EasyMoveMargin)
- easyMove = Rml[0].pv[0];
-
// Iterative deepening loop
while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest)
{
- Rml.bestMoveChanges = researchCountFL = researchCountFH = 0;
- cout << "info depth " << depth << endl;
+ Rml.bestMoveChanges = 0;
+ cout << set960(pos.is_chess960()) << "info depth " << depth << endl;
// Calculate dynamic aspiration window based on previous iterations
if (MultiPV == 1 && depth >= 5 && abs(bestValues[depth - 1]) < VALUE_KNOWN_WIN)
// Start with a small aspiration window and, in case of fail high/low,
// research with bigger window until not failing high/low anymore.
- while (true)
- {
+ do {
// Search starting from ss+1 to allow calling update_gains()
value = search<PV, false, true>(pos, ss+1, alpha, beta, depth * ONE_PLY, 0);
- // Send PV line to GUI and write to transposition table in case the
- // relevant entries have been overwritten during the search.
+ // Write PV back to transposition table in case the relevant entries
+ // have been overwritten during the search.
for (int i = 0; i < Min(MultiPV, (int)Rml.size()); i++)
- {
Rml[i].insert_pv_in_tt(pos);
- cout << set960(pos.is_chess960())
- << Rml[i].pv_info_to_uci(pos, depth, alpha, beta, i) << endl;
- }
// Value cannot be trusted. Break out immediately!
if (StopRequest)
// otherwise exit the fail high/low loop.
if (value >= beta)
{
- beta = Min(beta + aspirationDelta * (1 << researchCountFH), VALUE_INFINITE);
- researchCountFH++;
+ beta = Min(beta + aspirationDelta, VALUE_INFINITE);
+ aspirationDelta += aspirationDelta / 2;
}
else if (value <= alpha)
{
AspirationFailLow = true;
StopOnPonderhit = false;
- alpha = Max(alpha - aspirationDelta * (1 << researchCountFL), -VALUE_INFINITE);
- researchCountFL++;
+ alpha = Max(alpha - aspirationDelta, -VALUE_INFINITE);
+ aspirationDelta += aspirationDelta / 2;
}
else
break;
- }
+
+ } while (abs(value) < VALUE_KNOWN_WIN);
// Collect info about search result
bestMove = Rml[0].pv[0];
bestValues[depth] = value;
bestMoveChanges[depth] = Rml.bestMoveChanges;
+ // Send PV line to GUI and to log file
+ for (int i = 0; i < Min(MultiPV, (int)Rml.size()); i++)
+ cout << Rml[i].pv_info_to_uci(pos, depth, alpha, beta, i) << endl;
+
if (UseLogFile)
LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl;
- // Drop the easy move if differs from the new best move
- if (bestMove != easyMove)
+ // Init easyMove after first iteration or drop if differs from the best move
+ if (depth == 1 && (Rml.size() == 1 || Rml[0].pv_score > Rml[1].pv_score + EasyMoveMargin))
+ easyMove = bestMove;
+ else if (bestMove != easyMove)
easyMove = MOVE_NONE;
if (UseTimeManagement && !StopRequest)
<< " currmovenumber " << moveCount << endl;
}
- isPvMove = (PvNode && moveCount <= (Root ? MultiPV : 1));
+ // At Root and at first iteration do a PV search on all the moves
+ // to score root moves. Otherwise only the first one is the PV.
+ isPvMove = (PvNode && moveCount <= (Root ? MultiPV + 1000 * (depth <= ONE_PLY) : 1));
moveIsCheck = pos.move_is_check(move, ci);
captureOrPromotion = pos.move_is_capture_or_promotion(move);
if (abs(ttValue) < VALUE_KNOWN_WIN)
{
- Value b = ttValue - depth;
+ Value b = ttValue - int(depth);
ss->excludedMove = move;
ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, b - 1, b, depth / 2, ply);
bestValue = futilityValue;
continue;
}
+
+ // Prune moves with negative or equal SEE
+ if ( futilityBase < beta
+ && depth < DEPTH_ZERO
+ && pos.see(move) <= 0)
+ continue;
}
// Detect non-capture evasions that are candidate to be pruned
}
- // qsearch_scoring() scores each move of a list using a qsearch() evaluation,
- // it is used in RootMoveList to get an initial scoring.
- void qsearch_scoring(Position& pos, MoveStack* mlist, MoveStack* last) {
-
- SearchStack ss[PLY_MAX_PLUS_2];
- StateInfo st;
-
- memset(ss, 0, 4 * sizeof(SearchStack));
- ss[0].eval = ss[0].evalMargin = VALUE_NONE;
-
- for (MoveStack* cur = mlist; cur != last; cur++)
- {
- ss[0].currentMove = cur->move;
- pos.do_move(cur->move, st);
- cur->score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, DEPTH_ZERO, 1);
- pos.undo_move(cur->move);
- }
- }
-
-
// check_is_dangerous() tests if a checking move can be pruned in qsearch().
// bestValue is updated only when returning false because in that case move
// will be pruned.
clear();
bestMoveChanges = 0;
- // Generate all legal moves and score them
+ // Generate all legal moves and add them to RootMoveList
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
- qsearch_scoring(pos, mlist, last);
-
- // Add each move to the RootMoveList's vector
for (MoveStack* cur = mlist; cur != last; cur++)
{
// If we have a searchMoves[] list then verify cur->move
RootMove rm;
rm.pv[0] = cur->move;
rm.pv[1] = MOVE_NONE;
- rm.pv_score = Value(cur->score);
+ rm.pv_score = -VALUE_INFINITE;
push_back(rm);
}
- sort();
}
} // namespace