X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=ba5ad365af2f1caffb95fa56caa9004ec1520dc5;hp=39dcf9768157d4276ccc97fbe50a80bafc2b1c73;hb=a75aa6035b1ac27a6a2a44f523ec2ffd011485d8;hpb=e4fd9a2df7d0977cc279d73d94e79a866daddb2f diff --git a/src/search.cpp b/src/search.cpp index 39dcf976..ba5ad365 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008 Marco Costalba + Copyright (C) 2008-2009 Marco Costalba Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ //// #include +#include #include #include #include @@ -47,11 +48,32 @@ namespace { /// Types + // IterationInfoType stores search results for each iteration + // + // Because we use relatively small (dynamic) aspiration window, + // there happens many fail highs and fail lows in root. And + // because we don't do researches in those cases, "value" stored + // here is not necessarily exact. Instead in case of fail high/low + // we guess what the right value might be and store our guess + // as a "speculated value" and then move on. Speculated values are + // used just to calculate aspiration window width, so also if are + // not exact is not big a problem. + + struct IterationInfoType { + + IterationInfoType(Value v = Value(0), Value sv = Value(0)) + : value(v), speculatedValue(sv) {} + + Value value, speculatedValue; + }; + + // The BetaCounterType class is used to order moves at ply one. // Apart for the first one that has its score, following moves // normally have score -VALUE_INFINITE, so are ordered according // to the number of beta cutoffs occurred under their subtree during - // the last iteration. + // the last iteration. The counters are per thread variables to avoid + // concurrent accessing under SMP case. struct BetaCounterType { @@ -59,8 +81,6 @@ namespace { void clear(); void add(Color us, Depth d, int threadID); void read(Color us, int64_t& our, int64_t& their); - - int64_t hits[THREAD_MAX][2]; }; @@ -108,26 +128,22 @@ namespace { }; - /// Constants and variables + /// Constants and variables initialized from UCI options // Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV - // nodes: - int LMRPVMoves = 15; - int LMRNonPVMoves = 4; + // nodes + int LMRPVMoves, LMRNonPVMoves; - // Depth limit for use of dynamic threat detection: - Depth ThreatDepth = 5*OnePly; + // Depth limit for use of dynamic threat detection + Depth ThreatDepth; - // Depth limit for selective search: - Depth SelectiveDepth = 7*OnePly; + // Depth limit for selective search + const Depth SelectiveDepth = 7*OnePly; // Use internal iterative deepening? 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. @@ -159,33 +175,34 @@ namespace { const bool PruneBlockingMoves = false; // Use futility pruning? - bool UseQSearchFutilityPruning = true; - bool UseFutilityPruning = true; + bool UseQSearchFutilityPruning, UseFutilityPruning; // Margins for futility pruning in the quiescence search, and at frontier // and near frontier nodes - Value FutilityMarginQS = Value(0x80); - Value FutilityMargins[6] = { Value(0x100), Value(0x200), Value(0x250), - Value(0x2A0), Value(0x340), Value(0x3A0) }; + const Value FutilityMarginQS = Value(0x80); + + // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply + const Value FutilityMargins[12] = { Value(0x100), Value(0x120), Value(0x200), Value(0x220), Value(0x250), Value(0x270), + // 4 ply 4.5 ply 5 ply 5.5 ply 6 ply 6.5 ply + Value(0x2A0), Value(0x2C0), Value(0x340), Value(0x360), Value(0x3A0), Value(0x3C0) }; + // Razoring + const Depth RazorDepth = 4*OnePly; + + // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply + const Value RazorMargins[6] = { Value(0x180), Value(0x300), Value(0x300), Value(0x3C0), Value(0x3C0), Value(0x3C0) }; - // Razoring - const bool RazorAtDepthOne = false; - Depth RazorDepth = 4*OnePly; - Value RazorMargin = Value(0x300); + // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply + const Value RazorApprMargins[6] = { Value(0x520), Value(0x300), Value(0x300), Value(0x300), Value(0x300), Value(0x300) }; // Last seconds noise filtering (LSN) - bool UseLSNFiltering = false; + bool UseLSNFiltering; bool looseOnTime = false; - int LSNTime = 4 * 1000; // In milliseconds - Value LSNValue = Value(0x200); + int LSNTime; // In milliseconds + Value LSNValue; - // Extensions. Array index 0 is used at non-PV nodes, index 1 at PV nodes. - Depth CheckExtension[2] = {OnePly, OnePly}; - Depth SingleReplyExtension[2] = {OnePly / 2, OnePly / 2}; - Depth PawnPushTo7thExtension[2] = {OnePly / 2, OnePly / 2}; - Depth PassedPawnExtension[2] = {Depth(0), Depth(0)}; - Depth PawnEndgameExtension[2] = {OnePly, OnePly}; - Depth MateThreatExtension[2] = {Depth(0), Depth(0)}; + // Extensions. Array index 0 is used at non-PV nodes, index 1 at PV nodes. + Depth CheckExtension[2], SingleReplyExtension[2], PawnPushTo7thExtension[2]; + Depth PassedPawnExtension[2], PawnEndgameExtension[2], MateThreatExtension[2]; // Search depth at iteration 1 const Depth InitialDepth = OnePly /*+ OnePly/2*/; @@ -196,21 +213,20 @@ namespace { // Iteration counters int Iteration; - bool LastIterations; BetaCounterType BetaCounter; // Scores and number of times the best move changed for each iteration: - Value ValueByIteration[PLY_MAX_PLUS_2]; + IterationInfoType IterationInfo[PLY_MAX_PLUS_2]; int BestMoveChangesByIteration[PLY_MAX_PLUS_2]; // MultiPV mode - int MultiPV = 1; + int MultiPV; // Time managment variables int SearchStartTime; int MaxNodes, MaxDepth; int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime; - Move BestRootMove, PonderMove, EasyMove; + Move EasyMove; int RootMoveNumber; bool InfiniteSearch; bool PonderSearch; @@ -218,20 +234,21 @@ namespace { bool AbortSearch; bool Quit; bool FailHigh; + bool FailLow; bool Problem; bool PonderingEnabled; int ExactMaxTime; // Show current line? - bool ShowCurrentLine = false; + bool ShowCurrentLine; // Log file - bool UseLogFile = false; + bool UseLogFile; std::ofstream LogFile; // MP related variables - Depth MinimumSplitDepth = 4*OnePly; - int MaxThreadsPerSplitPoint = 4; + Depth MinimumSplitDepth; + int MaxThreadsPerSplitPoint; Thread Threads[THREAD_MAX]; Lock MPLock; bool AllThreadsShouldExit = false; @@ -250,24 +267,19 @@ namespace { /// Functions Value id_loop(const Position &pos, Move searchMoves[]); - Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml); - Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta, - Depth depth, int ply, int threadID); - Value search(Position &pos, SearchStack ss[], Value beta, - Depth depth, int ply, bool allowNullmove, int threadID); - Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta, - Depth depth, int ply, int threadID); + Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml, Value alpha, Value beta); + Value search_pv(Position &pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); + Value search(Position &pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID); + Value qsearch(Position &pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); void sp_search(SplitPoint *sp, int threadID); void sp_search_pv(SplitPoint *sp, int threadID); - void init_search_stack(SearchStack& ss); - void init_search_stack(SearchStack ss[]); - void init_node(const Position &pos, SearchStack ss[], int ply, int threadID); + void init_node(SearchStack ss[], int ply, int threadID); void update_pv(SearchStack ss[], int ply); void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply); bool connected_moves(const Position &pos, Move m1, Move m2); bool value_is_mate(Value value); bool move_is_killer(Move m, const SearchStack& ss); - Depth extension(const Position &pos, Move m, bool pvNode, bool check, bool singleReply, bool mateThreat, bool* dangerous); + Depth extension(const Position &pos, Move m, bool pvNode, bool capture, bool check, bool singleReply, bool mateThreat, bool* dangerous); bool ok_to_do_nullmove(const Position &pos); bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d); bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); @@ -290,9 +302,8 @@ namespace { bool thread_is_available(int slave, int master); bool idle_thread_exists(int master); bool split(const Position &pos, SearchStack *ss, int ply, - Value *alpha, Value *beta, Value *bestValue, Depth depth, - int *moves, MovePicker *mp, Bitboard dcCandidates, int master, - bool pvNode); + Value *alpha, Value *beta, Value *bestValue, Depth depth, int *moves, + MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode); void wake_sleeping_threads(); #if !defined(_MSC_VER) @@ -309,7 +320,7 @@ namespace { //// // The main transposition table -TranspositionTable TT = TranspositionTable(TTDefaultSize); +TranspositionTable TT; // Number of active threads: @@ -325,15 +336,33 @@ History H; // Should be made local? SearchStack EmptySearchStack; +// SearchStack::init() initializes a search stack. Used at the beginning of a +// new search from the root. +void SearchStack::init(int ply) { + + pv[ply] = pv[ply + 1] = MOVE_NONE; + currentMove = threatMove = MOVE_NONE; + reduction = Depth(0); +} + +void SearchStack::initKillers() { + + mateKiller = MOVE_NONE; + for (int i = 0; i < KILLER_MAX; i++) + killers[i] = MOVE_NONE; +} + + //// //// Functions //// /// think() is the external interface to Stockfish's search, and is called when -/// the program receives the UCI 'go' command. It initializes various -/// search-related global variables, and calls root_search() +/// the program receives the UCI 'go' command. It initializes various +/// search-related global variables, and calls root_search(). It returns false +/// when a quit command is received during the search. -void think(const Position &pos, bool infinite, bool ponder, int side_to_move, +bool think(const Position &pos, bool infinite, bool ponder, int side_to_move, int time[], int increment[], int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) { @@ -342,23 +371,19 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, { Move bookMove; if (get_option_value_string("Book File") != OpeningBook.file_name()) - { - OpeningBook.close(); OpeningBook.open("book.bin"); - } + bookMove = OpeningBook.get_move(pos); if (bookMove != MOVE_NONE) { std::cout << "bestmove " << bookMove << std::endl; - return; + return true; } } // Initialize global search variables Idle = false; SearchStartTime = get_system_time(); - BestRootMove = MOVE_NONE; - PonderMove = MOVE_NONE; EasyMove = MOVE_NONE; for (int i = 0; i < THREAD_MAX; i++) { @@ -372,6 +397,7 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, AbortSearch = false; Quit = false; FailHigh = false; + FailLow = false; Problem = false; ExactMaxTime = maxTime; @@ -404,7 +430,6 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, LMRPVMoves = get_option_value_int("Full Depth Moves (PV nodes)") + 1; LMRNonPVMoves = get_option_value_int("Full Depth Moves (non-PV nodes)") + 1; ThreatDepth = get_option_value_int("Threat Depth") * OnePly; - SelectiveDepth = get_option_value_int("Selective Plies") * OnePly; Chess960 = get_option_value_bool("UCI_Chess960"); ShowCurrentLine = get_option_value_bool("UCI_ShowCurrLine"); @@ -412,18 +437,9 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, 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)"); - FutilityMarginQS = value_from_centipawns(get_option_value_int("Futility Margin (Quiescence Search)")); - int fmScale = get_option_value_int("Futility Margin Scale Factor (Main Search)"); - for (int i = 0; i < 6; i++) - FutilityMargins[i] = (FutilityMargins[i] * fmScale) / 100; - - RazorDepth = (get_option_value_int("Maximum Razoring Depth") + 1) * OnePly; - RazorMargin = value_from_centipawns(get_option_value_int("Razoring Margin")); - UseLSNFiltering = get_option_value_bool("LSN filtering"); LSNTime = get_option_value_int("LSN Time Margin (sec)") * 1000; LSNValue = value_from_centipawns(get_option_value_int("LSN Value Margin")); @@ -449,7 +465,6 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, // Set thinking time: int myTime = time[side_to_move]; int myIncrement = increment[side_to_move]; - int oppTime = time[1 - side_to_move]; if (!movesToGo) // Sudden death time control { @@ -526,14 +541,8 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move, if (UseLogFile) LogFile.close(); - if (Quit) - { - OpeningBook.close(); - stop_threads(); - quit_eval(); - exit(0); - } Idle = true; + return !Quit; } @@ -590,7 +599,8 @@ void init_threads() { } // Init also the empty search stack - init_search_stack(EmptySearchStack); + EmptySearchStack.init(0); + EmptySearchStack.initKillers(); } @@ -642,17 +652,18 @@ namespace { // Initialize TT.new_search(); H.clear(); - init_search_stack(ss); - - ValueByIteration[0] = Value(0); - ValueByIteration[1] = rml.get_move_score(0); + for (int i = 0; i < 3; i++) + { + ss[i].init(i); + ss[i].initKillers(); + } + IterationInfo[1] = IterationInfoType(rml.get_move_score(0), rml.get_move_score(0)); Iteration = 1; - LastIterations = false; EasyMove = rml.scan_for_easy_move(); // Iterative deepening loop - while (!AbortSearch && Iteration < PLY_MAX) + while (Iteration < PLY_MAX) { // Initialize iteration rml.sort(); @@ -663,8 +674,62 @@ namespace { std::cout << "info depth " << Iteration << std::endl; + // Calculate dynamic search window based on previous iterations + Value alpha, beta; + + if (MultiPV == 1 && Iteration >= 6) + { + int prevDelta1 = IterationInfo[Iteration - 1].speculatedValue - IterationInfo[Iteration - 2].speculatedValue; + int prevDelta2 = IterationInfo[Iteration - 2].speculatedValue - IterationInfo[Iteration - 3].speculatedValue; + + int delta = Max(2 * abs(prevDelta1) + abs(prevDelta2), ProblemMargin); + + alpha = Max(IterationInfo[Iteration - 1].value - delta, -VALUE_INFINITE); + beta = Min(IterationInfo[Iteration - 1].value + delta, VALUE_INFINITE); + } + else + { + alpha = - VALUE_INFINITE; + beta = VALUE_INFINITE; + } + // Search to the current depth - ValueByIteration[Iteration] = root_search(p, ss, rml); + Value value = root_search(p, ss, rml, alpha, beta); + + // Write PV to transposition table, in case the relevant entries have + // been overwritten during the search. + TT.insert_pv(p, ss[0].pv); + + if (AbortSearch) + break; // Value cannot be trusted. Break out immediately! + + //Save info about search result + Value speculatedValue; + bool fHigh = false; + bool fLow = false; + Value delta = value - IterationInfo[Iteration - 1].value; + + if (value >= beta) + { + assert(delta > 0); + + fHigh = true; + speculatedValue = value + delta; + BestMoveChangesByIteration[Iteration] += 2; // Allocate more time + } + else if (value <= alpha) + { + assert(value == alpha); + assert(delta < 0); + + fLow = true; + speculatedValue = value + delta; + BestMoveChangesByIteration[Iteration] += 3; // Allocate more time + } else + speculatedValue = value; + + speculatedValue = Min(Max(speculatedValue, -VALUE_INFINITE), VALUE_INFINITE); + IterationInfo[Iteration] = IterationInfoType(value, speculatedValue); // Erase the easy move if it differs from the new best move if (ss[0].pv[0] != EasyMove) @@ -683,13 +748,15 @@ namespace { // Stop search early when the last two iterations returned a mate score if ( Iteration >= 6 - && abs(ValueByIteration[Iteration]) >= abs(VALUE_MATE) - 100 - && abs(ValueByIteration[Iteration-1]) >= abs(VALUE_MATE) - 100) + && abs(IterationInfo[Iteration].value) >= abs(VALUE_MATE) - 100 + && abs(IterationInfo[Iteration-1].value) >= abs(VALUE_MATE) - 100) stopSearch = true; // Stop search early if one move seems to be much better than the rest int64_t nodes = nodes_searched(); if ( Iteration >= 8 + && !fLow + && !fHigh && EasyMove == ss[0].pv[0] && ( ( rml.get_move_cumulative_nodes(0) > (nodes * 85) / 100 && current_search_time() > MaxSearchTime / 16) @@ -702,9 +769,6 @@ namespace { ExtraSearchTime = BestMoveChangesByIteration[Iteration] * (MaxSearchTime / 2) + BestMoveChangesByIteration[Iteration-1] * (MaxSearchTime / 3); - // Try to guess if the current iteration is the last one or the last two - LastIterations = (current_search_time() > ((MaxSearchTime + ExtraSearchTime)*58) / 128); - // Stop search if most of MaxSearchTime is consumed at the end of the // iteration. We probably don't have enough time to search the first // move at the next iteration anyway. @@ -713,15 +777,13 @@ namespace { if (stopSearch) { + //FIXME: Implement fail-low emergency measures if (!PonderSearch) break; else StopOnPonderhit = true; } } - // Write PV to transposition table, in case the relevant entries have - // been overwritten during the search: - TT.insert_pv(p, ss[0].pv); if (MaxDepth && Iteration >= MaxDepth) break; @@ -741,6 +803,11 @@ namespace { << " hashfull " << TT.full() << std::endl; // Print the best move and the ponder move to the standard output + if (ss[0].pv[0] == MOVE_NONE) + { + ss[0].pv[0] = rml.get_move(0); + ss[0].pv[1] = MOVE_NONE; + } std::cout << "bestmove " << ss[0].pv[0]; if (ss[0].pv[1] != MOVE_NONE) std::cout << " ponder " << ss[0].pv[1]; @@ -755,12 +822,12 @@ namespace { if (dbg_show_hit_rate) dbg_print_hit_rate(LogFile); - UndoInfo u; + StateInfo st; LogFile << "Nodes: " << nodes_searched() << std::endl << "Nodes/second: " << nps() << std::endl << "Best move: " << move_to_san(p, ss[0].pv[0]) << std::endl; - p.do_move(ss[0].pv[0], u); + p.do_move(ss[0].pv[0], st); LogFile << "Ponder move: " << move_to_san(p, ss[0].pv[1]) << std::endl << std::endl; } @@ -773,18 +840,26 @@ namespace { // scheme (perhaps we should try to use this at internal PV nodes, too?) // and prints some information to the standard output. - Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml) { + Value root_search(Position &pos, SearchStack ss[], RootMoveList &rml, Value alpha, Value beta) { - Value alpha = -VALUE_INFINITE; - Value beta = VALUE_INFINITE, value; + Value oldAlpha = alpha; + Value value; Bitboard dcCandidates = pos.discovered_check_candidates(pos.side_to_move()); // Loop through all the moves in the root move list for (int i = 0; i < rml.move_count() && !AbortSearch; i++) { + if (alpha >= beta) + { + // We failed high, invalidate and skip next moves, leave node-counters + // and beta-counters as they are and quickly return, we will try to do + // a research at the next iteration with a bigger aspiration window. + rml.set_move_score(i, -VALUE_INFINITE); + continue; + } int64_t nodes; Move move; - UndoInfo u; + StateInfo st; Depth ext, newDepth; RootMoveNumber = i + 1; @@ -806,20 +881,24 @@ namespace { // Decide search depth for this move bool dangerous; - ext = extension(pos, move, true, pos.move_is_check(move), false, false, &dangerous); + ext = extension(pos, move, true, pos.move_is_capture(move), pos.move_is_check(move), false, false, &dangerous); newDepth = (Iteration - 2) * OnePly + ext + InitialDepth; // Make the move, and search it - pos.do_move(move, u, dcCandidates); + pos.do_move(move, st, dcCandidates); if (i < MultiPV) { - value = -search_pv(pos, ss, -beta, VALUE_INFINITE, newDepth, 1, 0); + // Aspiration window is disabled in multi-pv case + if (MultiPV > 1) + alpha = -VALUE_INFINITE; + + value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0); // If the value has dropped a lot compared to the last iteration, // set the boolean variable Problem to true. This variable is used // for time managment: When Problem is true, we try to complete the // current iteration before playing a move. - Problem = (Iteration >= 2 && value <= ValueByIteration[Iteration-1] - ProblemMargin); + Problem = (Iteration >= 2 && value <= IterationInfo[Iteration-1].value - ProblemMargin); if (Problem && StopOnPonderhit) StopOnPonderhit = false; @@ -838,13 +917,13 @@ namespace { } } - pos.undo_move(move, u); + pos.undo_move(move); // Finished searching the move. If AbortSearch is true, the search // was aborted because the user interrupted the search or because we // ran out of time. In this case, the return value of the search cannot // be trusted, and we break out of the loop without updating the best - // move and/or PV: + // move and/or PV. if (AbortSearch) break; @@ -863,7 +942,7 @@ namespace { rml.set_move_score(i, -VALUE_INFINITE); else { - // New best move! + // PV move or new best move! // Update PV rml.set_move_score(i, value); @@ -895,11 +974,12 @@ namespace { LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value, ss[0].pv) << std::endl; - alpha = value; + if (value > alpha) + alpha = value; // Reset the global variable Problem to false if the value isn't too // far below the final value from the last iteration. - if (value > ValueByIteration[Iteration - 1] - NoProblemMargin) + if (value > IterationInfo[Iteration - 1].value - NoProblemMargin) Problem = false; } else // MultiPV > 1 @@ -923,7 +1003,11 @@ namespace { } alpha = rml.get_move_score(Min(i, MultiPV-1)); } - } + } // New best move case + + assert(alpha >= oldAlpha); + + FailLow = (alpha == oldAlpha); } return alpha; } @@ -944,7 +1028,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -967,7 +1051,7 @@ namespace { // Transposition table lookup. At PV nodes, we don't use the TT for // pruning, but only for move ordering. - const TTEntry* tte = TT.retrieve(pos); + const TTEntry* tte = TT.retrieve(pos.get_key()); Move ttMove = (tte ? tte->move() : MOVE_NONE); // Go with internal iterative deepening if we don't have a TT move @@ -985,8 +1069,9 @@ namespace { int moveCount = 0; Value value, bestValue = -VALUE_INFINITE; Bitboard dcCandidates = mp.discovered_check_candidates(); + Color us = pos.side_to_move(); bool isCheck = pos.is_check(); - bool mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move())); + bool mateThreat = pos.has_mate_threat(opposite_color(us)); // Loop through all legal moves until no moves remain or a beta cutoff // occurs. @@ -1002,20 +1087,14 @@ namespace { movesSearched[moveCount++] = ss[ply].currentMove = move; - if (moveIsCapture) - ss[ply].currentMoveCaptureValue = - move_is_ep(move)? PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move)); - else - ss[ply].currentMoveCaptureValue = Value(0); - // Decide the new search depth bool dangerous; - Depth ext = extension(pos, move, true, moveIsCheck, singleReply, mateThreat, &dangerous); + Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous); Depth newDepth = depth - OnePly + ext; // Make and search the move - UndoInfo u; - pos.do_move(move, u, dcCandidates); + StateInfo st; + pos.do_move(move, st, dcCandidates); if (moveCount == 1) // The first move in list is the PV value = -search_pv(pos, ss, -beta, -alpha, newDepth, ply+1, threadID); @@ -1057,7 +1136,7 @@ namespace { } } } - pos.undo_move(move, u); + pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1077,7 +1156,7 @@ namespace { // (from the computer's point of view) since the previous iteration: if ( ply == 1 && Iteration >= 2 - && -value <= ValueByIteration[Iteration-1] - ProblemMargin) + && -value <= IterationInfo[Iteration-1].value - ProblemMargin) Problem = true; } @@ -1105,7 +1184,7 @@ namespace { return bestValue; if (bestValue <= oldAlpha) - TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_UPPER); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE); else if (bestValue >= beta) { @@ -1116,10 +1195,10 @@ namespace { update_history(pos, m, depth, movesSearched, moveCount); update_killers(m, ss[ply]); } - TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m); } else - TT.store(pos, value_to_tt(bestValue, ply), depth, ss[ply].pv[ply], VALUE_TYPE_EXACT); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss[ply].pv[ply]); return bestValue; } @@ -1139,7 +1218,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -1161,7 +1240,7 @@ namespace { return beta - 1; // Transposition table lookup - const TTEntry* tte = TT.retrieve(pos); + const TTEntry* tte = TT.retrieve(pos.get_key()); Move ttMove = (tte ? tte->move() : MOVE_NONE); if (tte && ok_to_use_TT(tte, depth, beta, ply)) @@ -1172,7 +1251,6 @@ namespace { Value approximateEval = quick_evaluate(pos); bool mateThreat = false; - bool nullDrivenIID = false; bool isCheck = pos.is_check(); // Null move search @@ -1185,26 +1263,13 @@ namespace { { ss[ply].currentMove = MOVE_NULL; - UndoInfo u; - pos.do_null_move(u); - int R = (depth >= 4 * OnePly ? 4 : 3); // Null move dynamic reduction + StateInfo st; + pos.do_null_move(st); + int R = (depth >= 5 * OnePly ? 4 : 3); // Null move dynamic reduction 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 mark the node as a suspicious failed low. We will verify - // later if we are really under threat. - if ( UseNullDrivenIID - && nullValue < beta - && depth > 6 * OnePly - &&!value_is_mate(nullValue) - && 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); + pos.undo_null_move(); if (value_is_mate(nullValue)) { @@ -1227,10 +1292,8 @@ namespace { // 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 @@ -1240,17 +1303,15 @@ namespace { } // Null move search not allowed, try razoring else if ( !value_is_mate(beta) - && approximateEval < beta - RazorMargin && depth < RazorDepth - && (RazorAtDepthOne || depth > OnePly) + && approximateEval < beta - RazorApprMargins[int(depth) - 2] + && ss[ply - 1].currentMove != MOVE_NULL && ttMove == MOVE_NONE && !pos.has_pawn_on_7th(pos.side_to_move())) { Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID); - if ( (v < beta - RazorMargin - RazorMargin / 4) - || (depth <= 2*OnePly && v < beta - RazorMargin) - || (depth <= OnePly && v < beta - RazorMargin / 2)) - return v; + if (v < beta - RazorMargins[int(depth) - 2]) + return v; } // Go with internal iterative deepening if we don't have a TT move @@ -1260,22 +1321,6 @@ namespace { 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 position should fail high. So do a normal search with a - // reduced depth to get a good ttMove to use in the following full - // depth search. - 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: @@ -1306,7 +1351,7 @@ namespace { // Decide the new search depth bool dangerous; - Depth ext = extension(pos, move, false, moveIsCheck, singleReply, mateThreat, &dangerous); + Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous); Depth newDepth = depth - OnePly + ext; // Futility pruning @@ -1321,12 +1366,11 @@ namespace { continue; // Value based pruning - if (depth < 7 * OnePly && approximateEval < beta) + if (approximateEval < beta) { if (futilityValue == VALUE_NONE) futilityValue = evaluate(pos, ei, threadID) - + FutilityMargins[int(depth)/2 - 1] - + 32 * (depth & 1); + + FutilityMargins[int(depth) - 2]; if (futilityValue < beta) { @@ -1338,8 +1382,8 @@ namespace { } // Make and search the move - UndoInfo u; - pos.do_move(move, u, dcCandidates); + StateInfo st; + pos.do_move(move, st, dcCandidates); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -1362,7 +1406,7 @@ namespace { ss[ply].reduction = Depth(0); value = -search(pos, ss, -(beta-1), newDepth, ply+1, true, threadID); } - pos.undo_move(move, u); + pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1401,7 +1445,7 @@ namespace { return bestValue; if (bestValue < beta) - TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_UPPER); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE); else { BetaCounter.add(pos.side_to_move(), depth, threadID); @@ -1411,8 +1455,11 @@ namespace { update_history(pos, m, depth, movesSearched, moveCount); update_killers(m, ss[ply]); } - TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER); + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m); } + + assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); + return bestValue; } @@ -1432,7 +1479,7 @@ namespace { // Initialize, and make an early exit in case of an aborted search, // an instant draw, maximum ply reached, etc. - init_node(pos, ss, ply, threadID); + init_node(ss, ply, threadID); // After init_node() that calls poll() if (AbortSearch || thread_should_stop(threadID)) @@ -1441,15 +1488,40 @@ namespace { if (pos.is_draw()) return VALUE_DRAW; - // Transposition table lookup - const TTEntry* tte = TT.retrieve(pos); - if (tte && ok_to_use_TT(tte, depth, beta, ply)) - return value_from_tt(tte->value(), ply); + // Transposition table lookup, only when not in PV + TTEntry* tte = NULL; + bool pvNode = (beta - alpha != 1); + if (!pvNode) + { + tte = TT.retrieve(pos.get_key()); + if (tte && ok_to_use_TT(tte, depth, beta, ply)) + { + assert(tte->type() != VALUE_TYPE_EVAL); + + return value_from_tt(tte->value(), ply); + } + } + Move ttMove = (tte ? tte->move() : MOVE_NONE); // Evaluate the position statically EvalInfo ei; + Value staticValue; bool isCheck = pos.is_check(); - Value staticValue = (isCheck ? -VALUE_INFINITE : evaluate(pos, ei, threadID)); + ei.futilityMargin = Value(0); // Manually initialize futilityMargin + + if (isCheck) + staticValue = -VALUE_INFINITE; + + else if (tte && tte->type() == VALUE_TYPE_EVAL) + { + // Use the cached evaluation score if possible + assert(tte->value() == evaluate(pos, ei, threadID)); + assert(ei.futilityMargin == Value(0)); + + staticValue = tte->value(); + } + else + staticValue = evaluate(pos, ei, threadID); if (ply == PLY_MAX - 1) return evaluate(pos, ei, threadID); @@ -1459,7 +1531,13 @@ namespace { Value bestValue = staticValue; if (bestValue >= beta) + { + // Store the score to avoid a future costly evaluation() call + if (!isCheck && !tte && ei.futilityMargin == 0) + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EVAL, Depth(-127*OnePly), MOVE_NONE); + return bestValue; + } if (bestValue > alpha) alpha = bestValue; @@ -1467,12 +1545,12 @@ namespace { // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth == 0) will be generated. - bool pvNode = (beta - alpha != 1); - MovePicker mp = MovePicker(pos, pvNode, MOVE_NONE, EmptySearchStack, depth, isCheck ? NULL : &ei); + MovePicker mp = MovePicker(pos, pvNode, ttMove, EmptySearchStack, depth); Move move; int moveCount = 0; Bitboard dcCandidates = mp.discovered_check_candidates(); - bool enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame; + Color us = pos.side_to_move(); + bool enoughMaterial = pos.non_pawn_material(us) > RookValueMidgame; // Loop through the moves until no moves remain or a beta cutoff // occurs. @@ -1517,10 +1595,10 @@ namespace { continue; // Make and search the move. - UndoInfo u; - pos.do_move(move, u, dcCandidates); + StateInfo st; + pos.do_move(move, st, dcCandidates); Value value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID); - pos.undo_move(move, u); + pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1544,15 +1622,20 @@ namespace { assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); // Update transposition table - TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_EXACT); + Move m = ss[ply].pv[ply]; + if (!pvNode) + { + Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1)); + if (bestValue < beta) + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, d, MOVE_NONE); + else + TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, m); + } // Update killers only for good check moves - Move m = ss[ply].currentMove; if (alpha >= beta && ok_to_history(pos, m)) // Only non capture moves are considered - { - // Wrong to update history when depth is <= 0 update_killers(m, ss[ply]); - } + return bestValue; } @@ -1596,7 +1679,7 @@ namespace { // Decide the new search depth. bool dangerous; - Depth ext = extension(pos, move, false, moveIsCheck, false, false, &dangerous); + Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; // Prune? @@ -1609,8 +1692,8 @@ namespace { continue; // Make and search the move. - UndoInfo u; - pos.do_move(move, u, sp->dcCandidates); + StateInfo st; + pos.do_move(move, st, sp->dcCandidates); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -1632,7 +1715,7 @@ namespace { ss[sp->ply].reduction = Depth(0); value = -search(pos, ss, -(sp->beta - 1), newDepth, sp->ply+1, true, threadID); } - pos.undo_move(move, u); + pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1700,12 +1783,6 @@ namespace { assert(move_is_ok(move)); - if (moveIsCapture) - ss[sp->ply].currentMoveCaptureValue = - move_is_ep(move)? PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move)); - else - ss[sp->ply].currentMoveCaptureValue = Value(0); - lock_grab(&(sp->lock)); int moveCount = ++sp->moves; lock_release(&(sp->lock)); @@ -1714,12 +1791,12 @@ namespace { // Decide the new search depth. bool dangerous; - Depth ext = extension(pos, move, true, moveIsCheck, false, false, &dangerous); + Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, false, false, &dangerous); Depth newDepth = sp->depth - OnePly + ext; // Make and search the move. - UndoInfo u; - pos.do_move(move, u, sp->dcCandidates); + StateInfo st; + pos.do_move(move, st, sp->dcCandidates); // Try to reduce non-pv search depth by one ply if move seems not problematic, // if the move fails high will be re-searched at full depth. @@ -1755,7 +1832,7 @@ namespace { Threads[threadID].failHighPly1 = false; } } - pos.undo_move(move, u); + pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1788,7 +1865,7 @@ namespace { // (from the computer's point of view) since the previous iteration. if ( sp->ply == 1 && Iteration >= 2 - && -value <= ValueByIteration[Iteration-1] - ProblemMargin) + && -value <= IterationInfo[Iteration-1].value - ProblemMargin) Problem = true; } lock_release(&(sp->lock)); @@ -1816,13 +1893,13 @@ namespace { void BetaCounterType::clear() { for (int i = 0; i < THREAD_MAX; i++) - hits[i][WHITE] = hits[i][BLACK] = 0ULL; + Threads[i].betaCutOffs[WHITE] = Threads[i].betaCutOffs[BLACK] = 0ULL; } void BetaCounterType::add(Color us, Depth d, int threadID) { // Weighted count based on depth - hits[threadID][us] += int(d); + Threads[threadID].betaCutOffs[us] += unsigned(d); } void BetaCounterType::read(Color us, int64_t& our, int64_t& their) { @@ -1830,8 +1907,8 @@ namespace { our = their = 0UL; for (int i = 0; i < THREAD_MAX; i++) { - our += hits[i][us]; - their += hits[i][opposite_color(us)]; + our += Threads[i].betaCutOffs[us]; + their += Threads[i].betaCutOffs[opposite_color(us)]; } } @@ -1841,7 +1918,7 @@ namespace { // Constructor RootMove::RootMove() { - nodes = cumulativeNodes = 0ULL; + nodes = cumulativeNodes = ourBeta = theirBeta = 0ULL; } // RootMove::operator<() is the comparison function used when @@ -1877,22 +1954,20 @@ namespace { for (int k = 0; !includeMove && searchMoves[k] != MOVE_NONE; k++) includeMove = (searchMoves[k] == mlist[i].move); - if (includeMove) - { - // Find a quick score for the move - UndoInfo u; - SearchStack ss[PLY_MAX_PLUS_2]; - - moves[count].move = mlist[i].move; - moves[count].nodes = 0ULL; - pos.do_move(moves[count].move, u); - moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, - Depth(0), 1, 0); - pos.undo_move(moves[count].move, u); - moves[count].pv[0] = moves[i].move; - moves[count].pv[1] = MOVE_NONE; // FIXME - count++; - } + if (!includeMove) + continue; + + // Find a quick score for the move + StateInfo st; + SearchStack ss[PLY_MAX_PLUS_2]; + + moves[count].move = mlist[i].move; + pos.do_move(moves[count].move, st); + moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0); + pos.undo_move(moves[count].move); + moves[count].pv[0] = moves[count].move; + moves[count].pv[1] = MOVE_NONE; // FIXME + count++; } sort(); } @@ -1990,41 +2065,13 @@ namespace { } - // init_search_stack() initializes a search stack at the beginning of a - // new search from the root. - void init_search_stack(SearchStack& ss) { - - ss.pv[0] = MOVE_NONE; - ss.pv[1] = MOVE_NONE; - ss.currentMove = MOVE_NONE; - ss.threatMove = MOVE_NONE; - ss.reduction = Depth(0); - for (int j = 0; j < KILLER_MAX; j++) - ss.killers[j] = MOVE_NONE; - } - - void init_search_stack(SearchStack ss[]) { - - for (int i = 0; i < 3; i++) - { - ss[i].pv[i] = MOVE_NONE; - ss[i].pv[i+1] = MOVE_NONE; - ss[i].currentMove = MOVE_NONE; - ss[i].threatMove = MOVE_NONE; - ss[i].reduction = Depth(0); - for (int j = 0; j < KILLER_MAX; j++) - ss[i].killers[j] = MOVE_NONE; - } - } - - // init_node() is called at the beginning of all the search functions // (search(), search_pv(), qsearch(), and so on) and initializes the search // stack object corresponding to the current node. Once every // NodesBetweenPolls nodes, init_node() also calls poll(), which polls // for user input and checks whether it is time to stop the search. - void init_node(const Position &pos, SearchStack ss[], int ply, int threadID) { + void init_node(SearchStack ss[], int ply, int threadID) { assert(ply >= 0 && ply < PLY_MAX); assert(threadID >= 0 && threadID < ActiveThreads); @@ -2037,13 +2084,9 @@ namespace { NodesSincePoll = 0; } } - ss[ply].pv[ply] = ss[ply].pv[ply+1] = ss[ply].currentMove = MOVE_NONE; - ss[ply+2].mateKiller = MOVE_NONE; - ss[ply].threatMove = MOVE_NONE; - ss[ply].reduction = Depth(0); - ss[ply].currentMoveCaptureValue = Value(0); - for (int j = 0; j < KILLER_MAX; j++) - ss[ply+2].killers[j] = MOVE_NONE; + + ss[ply].init(ply); + ss[ply+2].initKillers(); if(Threads[threadID].printCurrentLine) print_current_line(ss, ply, threadID); @@ -2114,7 +2157,7 @@ namespace { // Case 4: The destination square for m2 is attacked by the moving piece // in m1: - if(pos.piece_attacks_square(t1, t2)) + if(pos.piece_attacks_square(pos.piece_on(t1), t1, t2)) return true; // Case 5: Discovered check, checking piece is the piece moved in m1: @@ -2179,7 +2222,7 @@ namespace { // extended, as example because the corresponding UCI option is set to zero, // the move is marked as 'dangerous' so, at least, we avoid to prune it. - Depth extension(const Position &pos, Move m, bool pvNode, bool check, + Depth extension(const Position& pos, Move m, bool pvNode, bool capture, bool check, bool singleReply, bool mateThreat, bool* dangerous) { assert(m != MOVE_NONE); @@ -2196,18 +2239,21 @@ namespace { if (mateThreat) result += MateThreatExtension[pvNode]; - if (pos.move_is_pawn_push_to_7th(m)) + if (pos.type_of_piece_on(move_from(m)) == PAWN) { - result += PawnPushTo7thExtension[pvNode]; - *dangerous = true; - } - if (pos.move_is_passed_pawn_push(m)) - { - result += PassedPawnExtension[pvNode]; - *dangerous = true; + if (pos.move_is_pawn_push_to_7th(m)) + { + result += PawnPushTo7thExtension[pvNode]; + *dangerous = true; + } + if (pos.move_is_passed_pawn_push(m)) + { + result += PassedPawnExtension[pvNode]; + *dangerous = true; + } } - if ( pos.move_is_capture(m) + if ( capture && pos.type_of_piece_on(move_to(m)) != PAWN && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - pos.midgame_value_of_piece_on(move_to(m)) == Value(0)) @@ -2219,7 +2265,7 @@ namespace { } if ( pvNode - && pos.move_is_capture(m) + && capture && pos.type_of_piece_on(move_to(m)) != PAWN && pos.see(m) >= 0) { @@ -2285,7 +2331,7 @@ namespace { return false; // Case 4: Don't prune moves with good history. - if (!H.ok_to_prune(pos.piece_on(move_from(m)), m, d)) + if (!H.ok_to_prune(pos.piece_on(mfrom), mto, d)) return false; // Case 5: If the moving piece in the threatened move is a slider, don't @@ -2332,13 +2378,13 @@ namespace { void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount) { - H.success(pos.piece_on(move_from(m)), m, depth); + H.success(pos.piece_on(move_from(m)), move_to(m), depth); for (int i = 0; i < moveCount - 1; i++) { assert(m != movesSearched[i]); if (ok_to_history(pos, movesSearched[i])) - H.failure(pos.piece_on(move_from(movesSearched[i])), movesSearched[i]); + H.failure(pos.piece_on(move_from(movesSearched[i])), move_to(movesSearched[i])); } } @@ -2407,6 +2453,7 @@ namespace { AbortSearch = true; PonderSearch = false; Quit = true; + return; } else if(command == "stop") { @@ -2446,11 +2493,11 @@ namespace { return; bool overTime = t > AbsoluteMaxSearchTime - || (RootMoveNumber == 1 && t > MaxSearchTime + ExtraSearchTime) - || ( !FailHigh && !fail_high_ply_1() && !Problem + || (RootMoveNumber == 1 && t > MaxSearchTime + ExtraSearchTime && !FailLow) //FIXME: We are not checking any problem flags, BUG? + || ( !FailHigh && !FailLow && !fail_high_ply_1() && !Problem && t > 6*(MaxSearchTime + ExtraSearchTime)); - if ( (Iteration >= 2 && (!InfiniteSearch && overTime)) + if ( (Iteration >= 3 && (!InfiniteSearch && overTime)) || (ExactMaxTime && t >= ExactMaxTime) || (Iteration >= 3 && MaxNodes && nodes_searched() >= MaxNodes)) AbortSearch = true; @@ -2464,12 +2511,12 @@ namespace { void ponderhit() { int t = current_search_time(); PonderSearch = false; - if(Iteration >= 2 && + if(Iteration >= 3 && (!InfiniteSearch && (StopOnPonderhit || t > AbsoluteMaxSearchTime || (RootMoveNumber == 1 && - t > MaxSearchTime + ExtraSearchTime) || - (!FailHigh && !fail_high_ply_1() && !Problem && + t > MaxSearchTime + ExtraSearchTime && !FailLow) || + (!FailHigh && !FailLow && !fail_high_ply_1() && !Problem && t > 6*(MaxSearchTime + ExtraSearchTime))))) AbortSearch = true; } @@ -2504,20 +2551,21 @@ namespace { // after which the bestmove and pondermove will be printed (in id_loop()). void wait_for_stop_or_ponderhit() { + std::string command; - while(true) { - if(!std::getline(std::cin, command)) - command = "quit"; - - if(command == "quit") { - OpeningBook.close(); - stop_threads(); - quit_eval(); - exit(0); - } - else if(command == "ponderhit" || command == "stop") - break; + while (true) + { + if (!std::getline(std::cin, command)) + command = "quit"; + + if (command == "quit") + { + Quit = true; + break; + } + else if(command == "ponderhit" || command == "stop") + break; } } @@ -2672,9 +2720,9 @@ namespace { // splitPoint->cpus becomes 0), split() returns true. bool split(const Position &p, SearchStack *sstck, int ply, - Value *alpha, Value *beta, Value *bestValue, - Depth depth, int *moves, + Value *alpha, Value *beta, Value *bestValue, Depth depth, int *moves, MovePicker *mp, Bitboard dcCandidates, int master, bool pvNode) { + assert(p.is_ok()); assert(sstck != NULL); assert(ply >= 0 && ply < PLY_MAX);