X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=4cf8f47122e5143bed143abbf9c1e876a38844d7;hp=ef247420d161345abb37ed6e18127a61db9c575b;hb=c2d42ea8339b49e52a116e488214a14fda09d413;hpb=af4fadebda5766b2c73a08227ddfb2911b43017e diff --git a/src/search.cpp b/src/search.cpp index ef247420..4cf8f471 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -46,9 +46,9 @@ namespace Search { Position RootPosition; } +using std::string; using std::cout; using std::endl; -using std::string; using namespace Search; namespace { @@ -149,20 +149,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 +169,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); @@ -192,32 +190,10 @@ namespace { MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, Stack* ss, Value b) : MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {} - Move get_next_move() { return mp->get_next_move(); } + Move next_move() { return mp->next_move(); } 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 +282,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 +293,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; } } @@ -404,21 +377,17 @@ void Search::think() { 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 + cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) + << " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << endl; } @@ -507,7 +476,7 @@ namespace { // If search has been stopped exit the aspiration window loop. // Sorting and writing PV back to TT is safe becuase RootMoves - // is still valid, although refers to previous iteration. + // is still valid, although refers to previous iteration. if (Signals.stop) break; @@ -600,7 +569,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)); } } @@ -684,7 +653,7 @@ namespace { // We don't want the score of a partial search to overwrite a previous full search // TT value, so we use a different position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = excludedMove ? pos.get_exclusion_key() : pos.get_key(); + posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey); ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; @@ -847,7 +816,7 @@ namespace { MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); CheckInfo ci(pos); - while ((move = mp.get_next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != MOVE_NONE) if (pos.pl_move_is_legal(move, ci.pinned)) { pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); @@ -898,7 +867,7 @@ split_point_start: // At split points actual search starts from here // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.get_next_move()) != MOVE_NONE + && (move = mp.next_move()) != MOVE_NONE && !thread.cutoff_occurred()) { assert(is_ok(move)); @@ -909,7 +878,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,14 +895,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 + << " currmove " << move_to_uci(move, Chess960) << " currmovenumber " << moveCount + PVIdx << endl; } @@ -1095,7 +1062,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 +1133,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 +1142,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); + } } } @@ -1242,7 +1220,7 @@ split_point_start: // At split points actual search starts from here // Transposition table lookup. At PV nodes, we don't use the TT for // pruning, but only for move ordering. - tte = TT.probe(pos.get_key()); + tte = TT.probe(pos.key()); ttMove = (tte ? tte->move() : MOVE_NONE); if (!PvNode && tte && can_return_tt(tte, ttDepth, beta, ss->ply)) @@ -1274,7 +1252,7 @@ split_point_start: // At split points actual search starts from here if (bestValue >= beta) { if (!tte) - TT.store(pos.get_key(), value_to_tt(bestValue, ss->ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, evalMargin); return bestValue; } @@ -1295,7 +1273,7 @@ split_point_start: // At split points actual search starts from here // Loop through the moves until no moves remain or a beta cutoff occurs while ( bestValue < beta - && (move = mp.get_next_move()) != MOVE_NONE) + && (move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1395,7 +1373,7 @@ split_point_start: // At split points actual search starts from here vt = bestValue <= oldAlpha ? VALUE_TYPE_UPPER : bestValue >= beta ? VALUE_TYPE_LOWER : VALUE_TYPE_EXACT; - TT.store(pos.get_key(), value_to_tt(bestValue, ss->ply), vt, ttDepth, move, ss->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), vt, ttDepth, move, ss->eval, evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1513,8 +1491,8 @@ split_point_start: // At split points actual search starts from here // value_to_tt() adjusts a mate score from "plies to mate from the root" to - // "plies to mate from the current ply". Non-mate scores are unchanged. - // The function is called before storing a value to the transposition table. + // "plies to mate from the current ply". Non-mate scores are unchanged. The + // function is called before storing a value to the transposition table. Value value_to_tt(Value v, int ply) { @@ -1616,27 +1594,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. @@ -1645,9 +1602,9 @@ split_point_start: // At split points actual search starts from here static int searchStartTime; if (reset) - searchStartTime = get_system_time(); + searchStartTime = system_time(); - return get_system_time() - searchStartTime; + return system_time() - searchStartTime; } @@ -1694,21 +1651,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); + std::stringstream s; - cout << "info" - << " depth " << d + for (int j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) + s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); + + cout << "info depth " << d << " seldepth " << selDepth - << (i == PVIdx ? score_to_uci(s, alpha, beta) : score_to_uci(s)) + << (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]; - - cout << endl; + << " multipv " << i + 1 + << " pv" << s.str() << endl; } } @@ -1762,8 +1718,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); @@ -1813,10 +1768,10 @@ split_point_start: // At split points actual search starts from here static RKISS rk; // PRNG sequence should be not deterministic - for (int i = abs(get_system_time() % 50); i > 0; i--) + for (int i = abs(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; @@ -1866,7 +1821,7 @@ split_point_start: // At split points actual search starts from here pv.push_back(m); pos.do_move(m, *st++); - while ( (tte = TT.probe(pos.get_key())) != NULL + while ( (tte = TT.probe(pos.key())) != NULL && tte->move() != MOVE_NONE && pos.is_pseudo_legal(tte->move()) && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces()) @@ -1898,7 +1853,7 @@ split_point_start: // At split points actual search starts from here assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); do { - k = pos.get_key(); + k = pos.key(); tte = TT.probe(k); // Don't overwrite existing correct entries @@ -2016,9 +1971,9 @@ void do_timer_event() { static int lastInfoTime; int e = elapsed_time(); - if (get_system_time() - lastInfoTime >= 1000 || !lastInfoTime) + if (system_time() - lastInfoTime >= 1000 || !lastInfoTime) { - lastInfoTime = get_system_time(); + lastInfoTime = system_time(); dbg_print_mean(); dbg_print_hit_rate();