X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=0ca539ae235797afa29867368938a3a948f0aaab;hp=ef247420d161345abb37ed6e18127a61db9c575b;hb=d543a64cc7fc06daed275b332b10ea06ba738001;hpb=af4fadebda5766b2c73a08227ddfb2911b43017e diff --git a/src/search.cpp b/src/search.cpp index ef247420..0ca539ae 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -20,9 +20,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -46,8 +46,6 @@ namespace Search { Position RootPosition; } -using std::cout; -using std::endl; using std::string; using namespace Search; @@ -149,20 +147,19 @@ namespace { TimeManager TimeMgr; int BestMoveChanges; int SkillLevel; - bool SkillLevelEnabled; + bool SkillLevelEnabled, Chess960; History H; /// Local functions - void id_loop(Position& pos); - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); + void id_loop(Position& pos); bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta, Value *bValue); bool connected_moves(const Position& pos, Move m1, Move m2); Value value_to_tt(Value v, int ply); @@ -170,7 +167,6 @@ namespace { bool can_return_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); Move do_skill_level(); int elapsed_time(bool reset = false); string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); @@ -196,28 +192,6 @@ namespace { MovePicker* mp; }; - // Overload operator<<() to make it easier to print moves in a coordinate - // notation compatible with UCI protocol. - std::ostream& operator<<(std::ostream& os, Move m) { - - bool chess960 = (os.iword(0) != 0); // See set960() - return os << move_to_uci(m, chess960); - } - - // When formatting a move for std::cout we must know if we are in Chess960 or - // not. To keep using the handy operator<<() on the move the trick is to embed - // this flag in the stream itself. Function-like named enum set960 is used as - // a custom manipulator and the stream internal general-purpose array, accessed - // through ios_base::iword(), is used to pass the flag to the move's operator<< - // that will read it to properly format castling moves. - enum set960 {}; - - std::ostream& operator<<(std::ostream& os, const set960& f) { - - os.iword(0) = f; - return os; - } - // is_dangerous() checks whether a move belongs to some classes of known // 'dangerous' moves so that we avoid to prune it. FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { @@ -306,6 +280,7 @@ void Search::think() { static Book book; // Defined static to initialize the PRNG only once Position& pos = RootPosition; + Chess960 = pos.is_chess960(); elapsed_time(true); TimeMgr.init(Limits, pos.startpos_ply_counter()); TT.new_search(); @@ -316,25 +291,21 @@ void Search::think() { // is given, with the subset of legal moves to search. for (MoveList ml(pos); !ml.end(); ++ml) if ( SearchMoves.empty() - || std::count(SearchMoves.begin(), SearchMoves.end(), ml.move())) + || count(SearchMoves.begin(), SearchMoves.end(), ml.move())) RootMoves.push_back(RootMove(ml.move())); - // Set output stream mode: normal or chess960. Castling notation is different - cout << set960(pos.is_chess960()); - if (Options["OwnBook"].value()) { if (Options["Book File"].value() != book.name()) book.open(Options["Book File"].value()); Move bookMove = book.probe(pos, Options["Best Book Move"].value()); - if (bookMove != MOVE_NONE) - { - if (!Signals.stop && (Limits.ponder || Limits.infinite)) - Threads.wait_for_stop_or_ponderhit(); - cout << "bestmove " << bookMove << endl; - return; + if ( bookMove != MOVE_NONE + && count(RootMoves.begin(), RootMoves.end(), bookMove)) + { + std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove)); + goto finish; } } @@ -366,7 +337,7 @@ void Search::think() { << " time: " << Limits.time << " increment: " << Limits.increment << " moves to go: " << Limits.movesToGo - << endl; + << std::endl; } for (int i = 0; i < Threads.size(); i++) @@ -400,25 +371,22 @@ void Search::think() { StateInfo st; pos.do_move(RootMoves[0].pv[0], st); - log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << endl; + log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << std::endl; pos.undo_move(RootMoves[0].pv[0]); } +finish: + // When we reach max depth we arrive here even without a StopRequest, but if // we are pondering or in infinite search, we shouldn't print the best move // before we are told to do so. if (!Signals.stop && (Limits.ponder || Limits.infinite)) Threads.wait_for_stop_or_ponderhit(); - // Could be MOVE_NONE when searching on a stalemate position - cout << "bestmove " << RootMoves[0].pv[0]; - - // 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 (RootMoves[0].pv[1] != MOVE_NONE) - cout << " ponder " << RootMoves[0].pv[1]; - - cout << endl; + // Best move could be MOVE_NONE when searching on a stalemate position + printf("bestmove %s ponder %s\n", + move_to_uci(RootMoves[0].pv[0], Chess960).c_str(), + move_to_uci(RootMoves[0].pv[1], Chess960).c_str()); } @@ -444,8 +412,8 @@ namespace { // Handle the special case of a mate/stalemate position if (RootMoves.empty()) { - cout << "info depth 0" - << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl; + printf("info depth 0%s\n", + score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW).c_str()); RootMoves.push_back(MOVE_NONE); return; @@ -600,7 +568,7 @@ namespace { if (skillBest == MOVE_NONE) // Still unassigned ? skillBest = do_skill_level(); - std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest)); + std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), skillBest)); } } @@ -909,7 +877,7 @@ split_point_start: // At split points actual search starts from here // At root obey the "searchmoves" option and skip moves not listed in Root // Move List, as a consequence any illegal move is also skipped. In MultiPV // mode we also skip PV moves which have been already searched. - if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) + if (RootNode && !count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) continue; // At PV and SpNode nodes we want all moves to be legal since the beginning @@ -926,15 +894,12 @@ split_point_start: // At split points actual search starts from here if (RootNode) { - // This is used by time management Signals.firstRootMove = (moveCount == 1); - nodes = pos.nodes_searched(); if (pos.thread() == 0 && elapsed_time() > 2000) - cout << "info depth " << depth / ONE_PLY - << " currmove " << move - << " currmovenumber " << moveCount + PVIdx << endl; + printf("info depth %i currmove %s currmovenumber %i\n", depth / ONE_PLY, + move_to_uci(move, Chess960).c_str(), moveCount + PVIdx); } isPvMove = (PvNode && moveCount <= 1); @@ -1095,7 +1060,7 @@ split_point_start: // At split points actual search starts from here // be trusted, and we don't update the best move and/or PV. if (RootNode && !Signals.stop) { - RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); + RootMove& rm = *find(RootMoves.begin(), RootMoves.end(), move); rm.nodes += pos.nodes_searched() - nodes; // PV move or new best move ? @@ -1166,7 +1131,7 @@ split_point_start: // At split points actual search starts from here } // Step 21. Update tables - // Update transposition table entry, history and killers + // Update transposition table entry, killers and history if (!SpNode && !Signals.stop && !thread.cutoff_occurred()) { move = bestValue <= oldAlpha ? MOVE_NONE : ss->bestMove; @@ -1175,16 +1140,27 @@ split_point_start: // At split points actual search starts from here TT.store(posKey, value_to_tt(bestValue, ss->ply), vt, depth, move, ss->eval, ss->evalMargin); - // Update killers and history only for non capture moves that fails high + // Update killers and history for non capture cut-off moves if ( bestValue >= beta - && !pos.is_capture_or_promotion(move)) + && !pos.is_capture_or_promotion(move) + && !inCheck) { if (move != ss->killers[0]) { ss->killers[1] = ss->killers[0]; ss->killers[0] = move; } - update_history(pos, move, depth, movesSearched, playedMoveCount); + + // Increase history value of the cut-off move + Value bonus = Value(int(depth) * int(depth)); + H.add(pos.piece_on(move_from(move)), move_to(move), bonus); + + // Decrease history of all the other played non-capture moves + for (int i = 0; i < playedMoveCount - 1; i++) + { + Move m = movesSearched[i]; + H.add(pos.piece_on(move_from(m)), move_to(m), -bonus); + } } } @@ -1616,27 +1592,6 @@ split_point_start: // At split points actual search starts from here } - // update_history() registers a good move that produced a beta-cutoff in - // history and marks as failures all the other moves of that ply. - - void update_history(const Position& pos, Move move, Depth depth, - Move movesSearched[], int moveCount) { - Move m; - Value bonus = Value(int(depth) * int(depth)); - - H.update(pos.piece_on(move_from(move)), move_to(move), bonus); - - for (int i = 0; i < moveCount - 1; i++) - { - m = movesSearched[i]; - - assert(m != move); - - H.update(pos.piece_on(move_from(m)), move_to(m), -bonus); - } - } - - // current_search_time() returns the number of milliseconds which have passed // since the beginning of the current search. @@ -1681,6 +1636,7 @@ split_point_start: // At split points actual search starts from here int t = elapsed_time(); int selDepth = 0; + std::stringstream s; for (int i = 0; i < Threads.size(); i++) if (Threads[i].maxPly > selDepth) @@ -1694,21 +1650,20 @@ split_point_start: // At split points actual search starts from here continue; int d = (updated ? depth : depth - 1); - Value s = (updated ? RootMoves[i].score : RootMoves[i].prevScore); + Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); - cout << "info" - << " depth " << d - << " seldepth " << selDepth - << (i == PVIdx ? score_to_uci(s, alpha, beta) : score_to_uci(s)) - << " nodes " << pos.nodes_searched() - << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) - << " time " << t - << " multipv " << i + 1 << " pv"; + s << "info depth " << d + << " seldepth " << selDepth + << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " nodes " << pos.nodes_searched() + << " nps " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) + << " time " << t + << " multipv " << i + 1 << " pv"; for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) - cout << " " << RootMoves[i].pv[j]; + s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); - cout << endl; + printf("%s\n", s.str().c_str()); // Much faster than std::cout } } @@ -1762,8 +1717,7 @@ split_point_start: // At split points actual search starts from here size_t length; std::stringstream s; - s << set960(pos.is_chess960()) - << std::setw(2) << depth + s << std::setw(2) << depth << std::setw(8) << score_to_string(value) << std::setw(8) << time_to_string(time); @@ -1799,7 +1753,7 @@ split_point_start: // At split points actual search starts from here pos.undo_move(*--m); Log l(Options["Search Log Filename"].value()); - l << s.str() << endl; + l << s.str() << std::endl; } @@ -1816,7 +1770,7 @@ split_point_start: // At split points actual search starts from here for (int i = abs(get_system_time() % 50); i > 0; i--) rk.rand(); - // Rml list is already sorted by score in descending order + // RootMoves are already sorted by score in descending order size_t size = std::min(MultiPV, RootMoves.size()); int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMidgame); int weakness = 120 - 2 * SkillLevel;