From ece8f0780f14b62e0694f9ca9cbed4ccef1fe112 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 10 Jan 2015 11:32:26 +0000 Subject: [PATCH] Remove more files --- src/psqtab.h | 98 --- src/search.cpp | 1678 --------------------------------------------- src/search.h | 113 --- src/thread.cpp | 385 ----------- src/thread.h | 176 ----- src/timeman.cpp | 116 ---- src/timeman.h | 39 -- src/tt.cpp | 96 --- src/tt.h | 115 ---- src/uci.cpp | 282 -------- src/uci.h | 79 --- src/ucioption.cpp | 160 ----- 12 files changed, 3337 deletions(-) delete mode 100644 src/psqtab.h delete mode 100644 src/search.cpp delete mode 100644 src/search.h delete mode 100644 src/thread.cpp delete mode 100644 src/thread.h delete mode 100644 src/timeman.cpp delete mode 100644 src/timeman.h delete mode 100644 src/tt.cpp delete mode 100644 src/tt.h delete mode 100644 src/uci.cpp delete mode 100644 src/uci.h delete mode 100644 src/ucioption.cpp diff --git a/src/psqtab.h b/src/psqtab.h deleted file mode 100644 index a88d31d6..00000000 --- a/src/psqtab.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef PSQTAB_H_INCLUDED -#define PSQTAB_H_INCLUDED - -#include "types.h" - -#define S(mg, eg) make_score(mg, eg) - - -/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on -/// a given square a (middlegame, endgame) score pair is assigned. PSQT is defined -/// for the white side and the tables are symmetric for the black side. - -static const Score PSQT[][SQUARE_NB] = { - { }, - { // Pawn - S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), - S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0), - S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0), - S(-20, 0), S( 0, 0), S(20, 0), S(40, 0), S(40, 0), S(20, 0), S( 0, 0), S(-20, 0), - S(-20, 0), S( 0, 0), S(10, 0), S(20, 0), S(20, 0), S(10, 0), S( 0, 0), S(-20, 0), - S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0), - S(-20, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S(-20, 0), - S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) - }, - { // Knight - S(-144,-98), S(-109,-83), S(-85,-51), S(-73,-16), S(-73,-16), S(-85,-51), S(-109,-83), S(-144,-98), - S( -88,-68), S( -43,-53), S(-19,-21), S( -7, 14), S( -7, 14), S(-19,-21), S( -43,-53), S( -88,-68), - S( -69,-53), S( -24,-38), S( 0, -6), S( 12, 29), S( 12, 29), S( 0, -6), S( -24,-38), S( -69,-53), - S( -28,-42), S( 17,-27), S( 41, 5), S( 53, 40), S( 53, 40), S( 41, 5), S( 17,-27), S( -28,-42), - S( -30,-42), S( 15,-27), S( 39, 5), S( 51, 40), S( 51, 40), S( 39, 5), S( 15,-27), S( -30,-42), - S( -10,-53), S( 35,-38), S( 59, -6), S( 71, 29), S( 71, 29), S( 59, -6), S( 35,-38), S( -10,-53), - S( -64,-68), S( -19,-53), S( 5,-21), S( 17, 14), S( 17, 14), S( 5,-21), S( -19,-53), S( -64,-68), - S(-200,-98), S( -65,-83), S(-41,-51), S(-29,-16), S(-29,-16), S(-41,-51), S( -65,-83), S(-200,-98) - }, - { // Bishop - S(-54,-65), S(-27,-42), S(-34,-44), S(-43,-26), S(-43,-26), S(-34,-44), S(-27,-42), S(-54,-65), - S(-29,-43), S( 8,-20), S( 1,-22), S( -8, -4), S( -8, -4), S( 1,-22), S( 8,-20), S(-29,-43), - S(-20,-33), S( 17,-10), S( 10,-12), S( 1, 6), S( 1, 6), S( 10,-12), S( 17,-10), S(-20,-33), - S(-19,-35), S( 18,-12), S( 11,-14), S( 2, 4), S( 2, 4), S( 11,-14), S( 18,-12), S(-19,-35), - S(-22,-35), S( 15,-12), S( 8,-14), S( -1, 4), S( -1, 4), S( 8,-14), S( 15,-12), S(-22,-35), - S(-28,-33), S( 9,-10), S( 2,-12), S( -7, 6), S( -7, 6), S( 2,-12), S( 9,-10), S(-28,-33), - S(-32,-43), S( 5,-20), S( -2,-22), S(-11, -4), S(-11, -4), S( -2,-22), S( 5,-20), S(-32,-43), - S(-49,-65), S(-22,-42), S(-29,-44), S(-38,-26), S(-38,-26), S(-29,-44), S(-22,-42), S(-49,-65) - }, - { // Rook - S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3), - S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3), - S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3), - S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3), - S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3), - S(-22, 3), S( -7, 3), S( -2, 3), S( 2, 3), S( 2, 3), S( -2, 3), S( -7, 3), S(-22, 3), - S(-11, 3), S( 4, 3), S( 9, 3), S(13, 3), S(13, 3), S( 9, 3), S( 4, 3), S(-11, 3), - S(-22, 3), S(-17, 3), S(-12, 3), S(-8, 3), S(-8, 3), S(-12, 3), S(-17, 3), S(-22, 3) - }, - { // Queen - S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80), - S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54), - S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42), - S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30), - S(-2,-30), S( 8, -6), S( 8, 6), S( 8, 18), S( 8, 18), S( 8, 6), S( 8, -6), S(-2,-30), - S(-2,-42), S( 8,-18), S( 8, -6), S( 8, 6), S( 8, 6), S( 8, -6), S( 8,-18), S(-2,-42), - S(-2,-54), S( 8,-30), S( 8,-18), S( 8, -6), S( 8, -6), S( 8,-18), S( 8,-30), S(-2,-54), - S(-2,-80), S(-2,-54), S(-2,-42), S(-2,-30), S(-2,-30), S(-2,-42), S(-2,-54), S(-2,-80) - }, - { // King - S(298, 27), S(332, 81), S(273,108), S(225,116), S(225,116), S(273,108), S(332, 81), S(298, 27), - S(287, 74), S(321,128), S(262,155), S(214,163), S(214,163), S(262,155), S(321,128), S(287, 74), - S(224,111), S(258,165), S(199,192), S(151,200), S(151,200), S(199,192), S(258,165), S(224,111), - S(196,135), S(230,189), S(171,216), S(123,224), S(123,224), S(171,216), S(230,189), S(196,135), - S(173,135), S(207,189), S(148,216), S(100,224), S(100,224), S(148,216), S(207,189), S(173,135), - S(146,111), S(180,165), S(121,192), S( 73,200), S( 73,200), S(121,192), S(180,165), S(146,111), - S(119, 74), S(153,128), S( 94,155), S( 46,163), S( 46,163), S( 94,155), S(153,128), S(119, 74), - S( 98, 27), S(132, 81), S( 73,108), S( 25,116), S( 25,116), S( 73,108), S(132, 81), S( 98, 27) - } -}; - -#undef S - -#endif // #ifndef PSQTAB_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp deleted file mode 100644 index 587506ec..00000000 --- a/src/search.cpp +++ /dev/null @@ -1,1678 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include // For std::memset -#include -#include - -#include "evaluate.h" -#include "misc.h" -#include "movegen.h" -#include "movepick.h" -#include "search.h" -#include "timeman.h" -#include "thread.h" -#include "tt.h" -#include "uci.h" -#include "syzygy/tbprobe.h" - -namespace Search { - - volatile SignalsType Signals; - LimitsType Limits; - RootMoveVector RootMoves; - Position RootPos; - Time::point SearchTime; - StateStackPtr SetupStates; -} - -namespace Tablebases { - - int Cardinality; - uint64_t Hits; - bool RootInTB; - bool UseRule50; - Depth ProbeDepth; - Value Score; -} - -namespace TB = Tablebases; - -using std::string; -using Eval::evaluate; -using namespace Search; - -namespace { - - // Different node types, used as template parameter - enum NodeType { Root, PV, NonPV }; - - // Dynamic razoring margin based on depth - inline Value razor_margin(Depth d) { return Value(512 + 32 * d); } - - // Futility lookup tables (initialized at startup) and their access functions - int FutilityMoveCounts[2][16]; // [improving][depth] - - inline Value futility_margin(Depth d) { - return Value(200 * d); - } - - // Reduction lookup tables (initialized at startup) and their access function - int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber] - - template inline Depth reduction(bool i, Depth d, int mn) { - return (Depth) Reductions[PvNode][i][std::min(int(d), 63)][std::min(mn, 63)]; - } - - size_t PVIdx; - TimeManager TimeMgr; - double BestMoveChanges; - Value DrawValue[COLOR_NB]; - HistoryStats History; - GainsStats Gains; - MovesStats Countermoves, Followupmoves; - - template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); - - template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); - - void id_loop(Position& pos); - Value value_to_tt(Value v, int ply); - Value value_from_tt(Value v, int ply); - void update_pv(Move* pv, Move move, Move* childPv); - void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt); - string uci_pv(const Position& pos, Depth depth, Value alpha, Value beta); - - struct Skill { - Skill(int l, size_t rootSize) : level(l), - candidates(l < 20 ? std::min(4, (int)rootSize) : 0), - best(MOVE_NONE) {} - ~Skill() { - if (candidates) // Swap best PV line with the sub-optimal one - std::swap(RootMoves[0], *std::find(RootMoves.begin(), - RootMoves.end(), best ? best : pick_move())); - } - - size_t candidates_size() const { return candidates; } - bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; } - Move pick_move(); - - int level; - size_t candidates; - Move best; - }; - -} // namespace - - -/// Search::init() is called during startup to initialize various lookup tables - -void Search::init() { - - // Init reductions array - for (int d = 1; d < 64; ++d) - for (int mc = 1; mc < 64; ++mc) - { - double pvRed = 0.00 + log(double(d)) * log(double(mc)) / 3.00; - double nonPVRed = 0.33 + log(double(d)) * log(double(mc)) / 2.25; - - Reductions[1][1][d][mc] = int8_t( pvRed >= 1.0 ? pvRed + 0.5: 0); - Reductions[0][1][d][mc] = int8_t(nonPVRed >= 1.0 ? nonPVRed + 0.5: 0); - - Reductions[1][0][d][mc] = Reductions[1][1][d][mc]; - Reductions[0][0][d][mc] = Reductions[0][1][d][mc]; - - // Increase reduction when eval is not improving - if (Reductions[0][0][d][mc] >= 2) - Reductions[0][0][d][mc] += 1; - } - - // Init futility move count array - for (int d = 0; d < 16; ++d) - { - FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 1.8)); - FutilityMoveCounts[1][d] = int(2.9 + 1.045 * pow(d + 0.49, 1.8)); - } -} - - -/// Search::perft() is our utility to verify move generation. All the leaf nodes -/// up to the given depth are generated and counted and the sum returned. -template -uint64_t Search::perft(Position& pos, Depth depth) { - - StateInfo st; - uint64_t cnt, nodes = 0; - CheckInfo ci(pos); - const bool leaf = (depth == 2 * ONE_PLY); - - for (MoveList it(pos); *it; ++it) - { - if (Root && depth <= ONE_PLY) - cnt = 1, nodes++; - else - { - pos.do_move(*it, st, ci, pos.gives_check(*it, ci)); - cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); - nodes += cnt; - pos.undo_move(*it); - } - if (Root) - sync_cout << UCI::move(*it, pos.is_chess960()) << ": " << cnt << sync_endl; - } - return nodes; -} - -template uint64_t Search::perft(Position& pos, Depth depth); - - -/// Search::think() is the external interface to Stockfish's search, and is -/// called by the main thread when the program receives the UCI 'go' command. It -/// searches from RootPos and at the end prints the "bestmove" to output. - -void Search::think() { - - TimeMgr.init(Limits, RootPos.side_to_move(), RootPos.game_ply()); - - int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns - DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(contempt); - DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(contempt); - - TB::Hits = 0; - TB::RootInTB = false; - TB::UseRule50 = Options["Syzygy50MoveRule"]; - TB::ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; - TB::Cardinality = Options["SyzygyProbeLimit"]; - - // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality - if (TB::Cardinality > TB::MaxCardinality) - { - TB::Cardinality = TB::MaxCardinality; - TB::ProbeDepth = DEPTH_ZERO; - } - - if (RootMoves.empty()) - { - RootMoves.push_back(MOVE_NONE); - sync_cout << "info depth 0 score " - << UCI::value(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) - << sync_endl; - } - else - { - if (TB::Cardinality >= RootPos.count(WHITE) - + RootPos.count(BLACK)) - { - // If the current root position is in the tablebases then RootMoves - // contains only moves that preserve the draw or win. - TB::RootInTB = Tablebases::root_probe(RootPos, RootMoves, TB::Score); - - if (TB::RootInTB) - TB::Cardinality = 0; // Do not probe tablebases during the search - - else // If DTZ tables are missing, use WDL tables as a fallback - { - // Filter out moves that do not preserve a draw or win - TB::RootInTB = Tablebases::root_probe_wdl(RootPos, RootMoves, TB::Score); - - // Only probe during search if winning - if (TB::Score <= VALUE_DRAW) - TB::Cardinality = 0; - } - - if (TB::RootInTB) - { - TB::Hits = RootMoves.size(); - - if (!TB::UseRule50) - TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 - : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 - : VALUE_DRAW; - } - } - - for (size_t i = 0; i < Threads.size(); ++i) - Threads[i]->maxPly = 0; - - Threads.timer->run = true; - Threads.timer->notify_one(); // Wake up the recurring timer - - id_loop(RootPos); // Let's start searching ! - - Threads.timer->run = false; - } - - // When we reach the maximum depth, we can arrive here without a raise of - // Signals.stop. However, if we are pondering or in an infinite search, - // the UCI protocol states that we shouldn't print the best move before the - // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here - // until the GUI sends one of those commands (which also raises Signals.stop). - if (!Signals.stop && (Limits.ponder || Limits.infinite)) - { - Signals.stopOnPonderhit = true; - RootPos.this_thread()->wait_for(Signals.stop); - } - - sync_cout << "bestmove " << UCI::move(RootMoves[0].pv[0], RootPos.is_chess960()); - - if (RootMoves[0].pv.size() > 1) - std::cout << " ponder " << UCI::move(RootMoves[0].pv[1], RootPos.is_chess960()); - - std::cout << sync_endl; -} - - -namespace { - - // id_loop() is the main iterative deepening loop. It calls search() repeatedly - // with increasing depth until the allocated thinking time has been consumed, - // user stops the search, or the maximum search depth is reached. - - void id_loop(Position& pos) { - - Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) - Depth depth; - Value bestValue, alpha, beta, delta; - - std::memset(ss-2, 0, 5 * sizeof(Stack)); - - depth = DEPTH_ZERO; - BestMoveChanges = 0; - bestValue = delta = alpha = -VALUE_INFINITE; - beta = VALUE_INFINITE; - - TT.new_search(); - History.clear(); - Gains.clear(); - Countermoves.clear(); - Followupmoves.clear(); - - size_t multiPV = Options["MultiPV"]; - Skill skill(Options["Skill Level"], RootMoves.size()); - - // Do we have to play with skill handicap? In this case enable MultiPV search - // that we will use behind the scenes to retrieve a set of possible moves. - multiPV = std::max(multiPV, skill.candidates_size()); - - // Iterative deepening loop until requested to stop or target depth reached - while (++depth < DEPTH_MAX && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) - { - // Age out PV variability metric - BestMoveChanges *= 0.5; - - // Save the last iteration's scores before first PV line is searched and - // all the move scores except the (new) PV are set to -VALUE_INFINITE. - for (size_t i = 0; i < RootMoves.size(); ++i) - RootMoves[i].previousScore = RootMoves[i].score; - - // MultiPV loop. We perform a full root search for each PV line - for (PVIdx = 0; PVIdx < std::min(multiPV, RootMoves.size()) && !Signals.stop; ++PVIdx) - { - // Reset aspiration window starting size - if (depth >= 5 * ONE_PLY) - { - delta = Value(16); - alpha = std::max(RootMoves[PVIdx].previousScore - delta,-VALUE_INFINITE); - beta = std::min(RootMoves[PVIdx].previousScore + delta, VALUE_INFINITE); - } - - // Start with a small aspiration window and, in the case of a fail - // high/low, re-search with a bigger window until we're not failing - // high/low anymore. - while (true) - { - bestValue = search(pos, ss, alpha, beta, depth, false); - - // Bring the best move to the front. It is critical that sorting - // is done with a stable algorithm because all the values but the - // first and eventually the new best one are set to -VALUE_INFINITE - // and we want to keep the same order for all the moves except the - // new PV that goes to the front. Note that in case of MultiPV - // search the already searched PV lines are preserved. - std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end()); - - // Write PV back to transposition table in case the relevant - // entries have been overwritten during the search. - for (size_t i = 0; i <= PVIdx; ++i) - RootMoves[i].insert_pv_in_tt(pos); - - // If search has been stopped break immediately. Sorting and - // writing PV back to TT is safe because RootMoves is still - // valid, although it refers to previous iteration. - if (Signals.stop) - break; - - // When failing high/low give some update (without cluttering - // the UI) before a re-search. - if ( (bestValue <= alpha || bestValue >= beta) - && Time::now() - SearchTime > 3000) - sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; - - // In case of failing low/high increase aspiration window and - // re-search, otherwise exit the loop. - if (bestValue <= alpha) - { - beta = (alpha + beta) / 2; - alpha = std::max(bestValue - delta, -VALUE_INFINITE); - - Signals.failedLowAtRoot = true; - Signals.stopOnPonderhit = false; - } - else if (bestValue >= beta) - { - alpha = (alpha + beta) / 2; - beta = std::min(bestValue + delta, VALUE_INFINITE); - } - else - break; - - delta += delta / 2; - - assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); - } - - // Sort the PV lines searched so far and update the GUI - std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); - - if (Signals.stop) - sync_cout << "info nodes " << RootPos.nodes_searched() - << " time " << Time::now() - SearchTime << sync_endl; - - else if ( PVIdx + 1 == std::min(multiPV, RootMoves.size()) - || Time::now() - SearchTime > 3000) - sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; - } - - // If skill levels are enabled and time is up, pick a sub-optimal best move - if (skill.candidates_size() && skill.time_to_pick(depth)) - skill.pick_move(); - - // Have we found a "mate in x"? - if ( Limits.mate - && bestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - bestValue <= 2 * Limits.mate) - Signals.stop = true; - - // Do we have time for the next iteration? Can we stop searching now? - if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit) - { - // Take some extra time if the best move has changed - if (depth > 4 * ONE_PLY && multiPV == 1) - TimeMgr.pv_instability(BestMoveChanges); - - // Stop the search if only one legal move is available or all - // of the available time has been used. - if ( RootMoves.size() == 1 - || Time::now() - SearchTime > TimeMgr.available_time()) - { - // If we are allowed to ponder do not stop the search now but - // keep pondering until the GUI sends "ponderhit" or "stop". - if (Limits.ponder) - Signals.stopOnPonderhit = true; - else - Signals.stop = true; - } - } - } - } - - - // search<>() is the main search function for both PV and non-PV nodes and for - // normal and SplitPoint nodes. When called just after a split point the search - // is simpler because we have already probed the hash table, done a null move - // search, and searched the first move before splitting, so we don't have to - // repeat all this work again. We also don't need to store anything to the hash - // table here: This is taken care of after we return from the split point. - - template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - - const bool RootNode = NT == Root; - const bool PvNode = NT == PV || NT == Root; - - assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); - assert(PvNode || (alpha == beta - 1)); - assert(depth > DEPTH_ZERO); - - Move pv[MAX_PLY+1], quietsSearched[64]; - StateInfo st; - TTEntry* tte; - SplitPoint* splitPoint; - Key posKey; - Move ttMove, move, excludedMove, bestMove; - Depth extension, newDepth, predictedDepth; - Value bestValue, value, ttValue, eval, nullValue, futilityValue; - bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; - bool captureOrPromotion, dangerous, doFullDepthSearch; - int moveCount, quietCount; - - // Step 1. Initialize node - Thread* thisThread = pos.this_thread(); - inCheck = pos.checkers(); - - if (SpNode) - { - splitPoint = ss->splitPoint; - bestMove = splitPoint->bestMove; - bestValue = splitPoint->bestValue; - tte = NULL; - ttHit = false; - ttMove = excludedMove = MOVE_NONE; - ttValue = VALUE_NONE; - - assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); - - goto moves_loop; - } - - moveCount = quietCount = 0; - bestValue = -VALUE_INFINITE; - ss->ply = (ss-1)->ply + 1; - - // Used to send selDepth info to GUI - if (PvNode && thisThread->maxPly < ss->ply) - thisThread->maxPly = ss->ply; - - if (!RootNode) - { - // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY) - return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; - - // Step 3. Mate distance pruning. Even if we mate at the next move our score - // would be at best mate_in(ss->ply+1), but if alpha is already bigger because - // a shorter mate was found upward in the tree then there is no need to search - // because we will never beat the current alpha. Same logic but with reversed - // signs applies also in the opposite condition of being mated instead of giving - // mate. In this case return a fail-high score. - alpha = std::max(mated_in(ss->ply), alpha); - beta = std::min(mate_in(ss->ply+1), beta); - if (alpha >= beta) - return alpha; - } - - assert(0 <= ss->ply && ss->ply < MAX_PLY); - - ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; - (ss+1)->skipEarlyPruning = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - - // Step 4. Transposition table lookup - // 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.exclusion_key() : pos.key(); - tte = TT.probe(posKey, ttHit); - ss->ttMove = ttMove = RootNode ? RootMoves[PVIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; - - // At non-PV nodes we check for a fail high/low. We don't probe at PV nodes - if ( !PvNode - && ttHit - && tte->depth() >= depth - && ttValue != VALUE_NONE // Only in case of TT access race - && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) - { - ss->currentMove = ttMove; // Can be MOVE_NONE - - // If ttMove is quiet, update killers, history, counter move and followup move on TT hit - if (ttValue >= beta && ttMove && !pos.capture_or_promotion(ttMove) && !inCheck) - update_stats(pos, ss, ttMove, depth, NULL, 0); - - return ttValue; - } - - // Step 4a. Tablebase probe - if (!RootNode && TB::Cardinality) - { - int piecesCnt = pos.count(WHITE) + pos.count(BLACK); - - if ( piecesCnt <= TB::Cardinality - && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) - && pos.rule50_count() == 0) - { - int found, v = Tablebases::probe_wdl(pos, &found); - - if (found) - { - TB::Hits++; - - int drawScore = TB::UseRule50 ? 1 : 0; - - value = v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply - : v > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - : VALUE_DRAW + 2 * v * drawScore; - - tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, - std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), - MOVE_NONE, VALUE_NONE, TT.generation()); - - return value; - } - } - } - - // Step 5. Evaluate the position statically and update parent's gain statistics - if (inCheck) - { - ss->staticEval = eval = VALUE_NONE; - goto moves_loop; - } - - else if (ttHit) - { - // Never assume anything on values stored in TT - if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE) - eval = ss->staticEval = evaluate(pos); - - // Can ttValue be used as a better position evaluation? - if (ttValue != VALUE_NONE) - if (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER)) - eval = ttValue; - } - else - { - eval = ss->staticEval = - (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo; - - tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation()); - } - - if (ss->skipEarlyPruning) - goto moves_loop; - - if ( !pos.captured_piece_type() - && ss->staticEval != VALUE_NONE - && (ss-1)->staticEval != VALUE_NONE - && (move = (ss-1)->currentMove) != MOVE_NULL - && move != MOVE_NONE - && type_of(move) == NORMAL) - { - Square to = to_sq(move); - Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); - } - - // Step 6. Razoring (skipped when in check) - if ( !PvNode - && depth < 4 * ONE_PLY - && eval + razor_margin(depth) <= alpha - && ttMove == MOVE_NONE - && !pos.pawn_on_7th(pos.side_to_move())) - { - if ( depth <= ONE_PLY - && eval + razor_margin(3 * ONE_PLY) <= alpha) - return qsearch(pos, ss, alpha, beta, DEPTH_ZERO); - - Value ralpha = alpha - razor_margin(depth); - Value v = qsearch(pos, ss, ralpha, ralpha+1, DEPTH_ZERO); - if (v <= ralpha) - return v; - } - - // Step 7. Futility pruning: child node (skipped when in check) - if ( !PvNode - && depth < 7 * ONE_PLY - && eval - futility_margin(depth) >= beta - && eval < VALUE_KNOWN_WIN // Do not return unproven wins - && pos.non_pawn_material(pos.side_to_move())) - return eval - futility_margin(depth); - - // Step 8. Null move search with verification search (is omitted in PV nodes) - if ( !PvNode - && depth >= 2 * ONE_PLY - && eval >= beta - && pos.non_pawn_material(pos.side_to_move())) - { - ss->currentMove = MOVE_NULL; - - assert(eval - beta >= 0); - - // Null move dynamic reduction based on depth and value - Depth R = ((823 + 67 * depth) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; - - pos.do_null_move(st); - (ss+1)->skipEarlyPruning = true; - nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); - (ss+1)->skipEarlyPruning = false; - pos.undo_null_move(); - - if (nullValue >= beta) - { - // Do not return unproven mate scores - if (nullValue >= VALUE_MATE_IN_MAX_PLY) - nullValue = beta; - - if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) - return nullValue; - - // Do verification search at high depths - ss->skipEarlyPruning = true; - Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta, DEPTH_ZERO) - : search(pos, ss, beta-1, beta, depth-R, false); - ss->skipEarlyPruning = false; - - if (v >= beta) - return nullValue; - } - } - - // Step 9. ProbCut (skipped when in check) - // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) - // and a reduced search returns a value much above beta, we can (almost) safely - // prune the previous move. - if ( !PvNode - && depth >= 5 * ONE_PLY - && abs(beta) < VALUE_MATE_IN_MAX_PLY) - { - Value rbeta = std::min(beta + 200, VALUE_INFINITE); - Depth rdepth = depth - 4 * ONE_PLY; - - assert(rdepth >= ONE_PLY); - assert((ss-1)->currentMove != MOVE_NONE); - assert((ss-1)->currentMove != MOVE_NULL); - - MovePicker mp(pos, ttMove, History, pos.captured_piece_type()); - CheckInfo ci(pos); - - while ((move = mp.next_move()) != MOVE_NONE) - if (pos.legal(move, ci.pinned)) - { - ss->currentMove = move; - pos.do_move(move, st, ci, pos.gives_check(move, ci)); - value = -search(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode); - pos.undo_move(move); - if (value >= rbeta) - return value; - } - } - - // Step 10. Internal iterative deepening (skipped when in check) - if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) - && !ttMove - && (PvNode || ss->staticEval + 256 >= beta)) - { - Depth d = 2 * (depth - 2 * ONE_PLY) - (PvNode ? DEPTH_ZERO : depth / 2); - ss->skipEarlyPruning = true; - search(pos, ss, alpha, beta, d / 2, true); - ss->skipEarlyPruning = false; - - tte = TT.probe(posKey, ttHit); - ttMove = ttHit ? tte->move() : MOVE_NONE; - } - -moves_loop: // When in check and at SpNode search starts from here - - Square prevMoveSq = to_sq((ss-1)->currentMove); - Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first, - Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second }; - - Square prevOwnMoveSq = to_sq((ss-2)->currentMove); - Move followupmoves[] = { Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].first, - Followupmoves[pos.piece_on(prevOwnMoveSq)][prevOwnMoveSq].second }; - - MovePicker mp(pos, ttMove, depth, History, countermoves, followupmoves, ss); - CheckInfo ci(pos); - value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc - improving = ss->staticEval >= (ss-2)->staticEval - || ss->staticEval == VALUE_NONE - ||(ss-2)->staticEval == VALUE_NONE; - - singularExtensionNode = !RootNode - && !SpNode - && depth >= 8 * ONE_PLY - && ttMove != MOVE_NONE - /* && ttValue != VALUE_NONE Already implicit in the next condition */ - && abs(ttValue) < VALUE_KNOWN_WIN - && !excludedMove // Recursive singular search is not allowed - && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3 * ONE_PLY; - - // Step 11. Loop through moves - // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs - while ((move = mp.next_move()) != MOVE_NONE) - { - assert(is_ok(move)); - - if (move == excludedMove) - continue; - - // 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)) - continue; - - if (SpNode) - { - // Shared counter cannot be decremented later if the move turns out to be illegal - if (!pos.legal(move, ci.pinned)) - continue; - - moveCount = ++splitPoint->moveCount; - splitPoint->mutex.unlock(); - } - else - ++moveCount; - - if (RootNode) - { - Signals.firstRootMove = (moveCount == 1); - - if (thisThread == Threads.main() && Time::now() - SearchTime > 3000) - sync_cout << "info depth " << depth / ONE_PLY - << " currmove " << UCI::move(move, pos.is_chess960()) - << " currmovenumber " << moveCount + PVIdx << sync_endl; - } - - if (PvNode) - (ss+1)->pv = NULL; - - extension = DEPTH_ZERO; - captureOrPromotion = pos.capture_or_promotion(move); - - givesCheck = type_of(move) == NORMAL && !ci.dcCandidates - ? ci.checkSq[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) - : pos.gives_check(move, ci); - - dangerous = givesCheck - || type_of(move) != NORMAL - || pos.advanced_pawn_push(move); - - // Step 12. Extend checks - if (givesCheck && pos.see_sign(move) >= VALUE_ZERO) - extension = ONE_PLY; - - // Singular extension search. If all moves but one fail low on a search of - // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move - // is singular and should be extended. To verify this we do a reduced search - // on all the other moves but the ttMove and if the result is lower than - // ttValue minus a margin then we extend the ttMove. - if ( singularExtensionNode - && move == ttMove - && !extension - && pos.legal(move, ci.pinned)) - { - Value rBeta = ttValue - 2 * depth / ONE_PLY; - ss->excludedMove = move; - ss->skipEarlyPruning = true; - value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); - ss->skipEarlyPruning = false; - ss->excludedMove = MOVE_NONE; - - if (value < rBeta) - extension = ONE_PLY; - } - - // Update the current move (this must be done after singular extension search) - newDepth = depth - ONE_PLY + extension; - - // Step 13. Pruning at shallow depth (exclude PV nodes) - if ( !PvNode - && !captureOrPromotion - && !inCheck - && !dangerous - && bestValue > VALUE_MATED_IN_MAX_PLY) - { - // Move count based pruning - if ( depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth]) - { - if (SpNode) - splitPoint->mutex.lock(); - - continue; - } - - predictedDepth = newDepth - reduction(improving, depth, moveCount); - - // Futility pruning: parent node - if (predictedDepth < 7 * ONE_PLY) - { - futilityValue = ss->staticEval + futility_margin(predictedDepth) - + 128 + Gains[pos.moved_piece(move)][to_sq(move)]; - - if (futilityValue <= alpha) - { - bestValue = std::max(bestValue, futilityValue); - - if (SpNode) - { - splitPoint->mutex.lock(); - if (bestValue > splitPoint->bestValue) - splitPoint->bestValue = bestValue; - } - continue; - } - } - - // Prune moves with negative SEE at low depths - if (predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < VALUE_ZERO) - { - if (SpNode) - splitPoint->mutex.lock(); - - continue; - } - } - - // Speculative prefetch as early as possible - prefetch((char*)TT.first_entry(pos.key_after(move))); - - // Check for legality just before making the move - if (!RootNode && !SpNode && !pos.legal(move, ci.pinned)) - { - moveCount--; - continue; - } - - ss->currentMove = move; - if (!SpNode && !captureOrPromotion && quietCount < 64) - quietsSearched[quietCount++] = move; - - // Step 14. Make the move - pos.do_move(move, st, ci, givesCheck); - - // Step 15. Reduced depth search (LMR). If the move fails high it will be - // re-searched at full depth. - if ( depth >= 3 * ONE_PLY - && moveCount > 1 - && !captureOrPromotion - && move != ss->killers[0] - && move != ss->killers[1]) - { - ss->reduction = reduction(improving, depth, moveCount); - - if ( (!PvNode && cutNode) - || History[pos.piece_on(to_sq(move))][to_sq(move)] < 0) - ss->reduction += ONE_PLY; - - if (move == countermoves[0] || move == countermoves[1]) - ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); - - // Decrease reduction for moves that escape a capture - if ( ss->reduction - && type_of(move) == NORMAL - && type_of(pos.piece_on(to_sq(move))) != PAWN - && pos.see(make_move(to_sq(move), from_sq(move))) < 0) - ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); - - Depth d = std::max(newDepth - ss->reduction, ONE_PLY); - if (SpNode) - alpha = splitPoint->alpha; - - value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - - // Re-search at intermediate depth if reduction is very high - if (value > alpha && ss->reduction >= 4 * ONE_PLY) - { - Depth d2 = std::max(newDepth - 2 * ONE_PLY, ONE_PLY); - value = -search(pos, ss+1, -(alpha+1), -alpha, d2, true); - } - - doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); - ss->reduction = DEPTH_ZERO; - } - else - doFullDepthSearch = !PvNode || moveCount > 1; - - // Step 16. Full depth search, when LMR is skipped or fails high - if (doFullDepthSearch) - { - if (SpNode) - alpha = splitPoint->alpha; - - value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); - } - - // For PV nodes only, do a full PV search on the first move or after a fail - // high (in the latter case search only if value < beta), otherwise let the - // parent node fail low with value <= alpha and to try another move. - if (PvNode && (moveCount == 1 || (value > alpha && (RootNode || value < beta)))) - { - (ss+1)->pv = pv; - (ss+1)->pv[0] = MOVE_NONE; - - value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, newDepth, false); - } - - // Step 17. Undo move - pos.undo_move(move); - - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - - // Step 18. Check for new best move - if (SpNode) - { - splitPoint->mutex.lock(); - bestValue = splitPoint->bestValue; - alpha = splitPoint->alpha; - } - - // Finished searching the move. If a stop or a cutoff occurred, the return - // value of the search cannot be trusted, and we return immediately without - // updating best move, PV and TT. - if (Signals.stop || thisThread->cutoff_occurred()) - return VALUE_ZERO; - - if (RootNode) - { - RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); - - // PV move or new best move ? - if (moveCount == 1 || value > alpha) - { - rm.score = value; - rm.pv.resize(1); - - assert((ss+1)->pv); - - for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) - rm.pv.push_back(*m); - - // We record how often the best move has been changed in each - // iteration. This information is used for time management: When - // the best move changes frequently, we allocate some more time. - if (moveCount > 1) - ++BestMoveChanges; - } - else - // All other moves but the PV are set to the lowest value: this is - // not a problem when sorting because the sort is stable and the - // move position in the list is preserved - just the PV is pushed up. - rm.score = -VALUE_INFINITE; - } - - if (value > bestValue) - { - bestValue = SpNode ? splitPoint->bestValue = value : value; - - if (value > alpha) - { - bestMove = SpNode ? splitPoint->bestMove = move : move; - - if (PvNode && !RootNode) // Update pv even in fail-high case - update_pv(SpNode ? splitPoint->ss->pv : ss->pv, move, (ss+1)->pv); - - if (PvNode && value < beta) // Update alpha! Always alpha < beta - alpha = SpNode ? splitPoint->alpha = value : value; - else - { - assert(value >= beta); // Fail high - - if (SpNode) - splitPoint->cutoff = true; - - break; - } - } - } - - // Step 19. Check for splitting the search - if ( !SpNode - && Threads.size() >= 2 - && depth >= Threads.minimumSplitDepth - && ( !thisThread->activeSplitPoint - || !thisThread->activeSplitPoint->allSlavesSearching) - && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) - { - assert(bestValue > -VALUE_INFINITE && bestValue < beta); - - thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, - depth, moveCount, &mp, NT, cutNode); - - if (Signals.stop || thisThread->cutoff_occurred()) - return VALUE_ZERO; - - if (bestValue >= beta) - break; - } - } - - if (SpNode) - return bestValue; - - // Following condition would detect a stop or a cutoff set only after move - // loop has been completed. But in this case bestValue is valid because we - // have fully searched our subtree, and we can anyhow save the result in TT. - /* - if (Signals.stop || thisThread->cutoff_occurred()) - return VALUE_DRAW; - */ - - // Step 20. Check for mate and stalemate - // All legal moves have been searched and if there are no legal moves, it - // must be mate or stalemate. If we are in a singular extension search then - // return a fail low score. - if (!moveCount) - bestValue = excludedMove ? alpha - : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; - - // Quiet best move: update killers, history, countermoves and followupmoves - else if (bestValue >= beta && !pos.capture_or_promotion(bestMove) && !inCheck) - update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount - 1); - - tte->save(posKey, value_to_tt(bestValue, ss->ply), - bestValue >= beta ? BOUND_LOWER : - PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, - depth, bestMove, ss->staticEval, TT.generation()); - - assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); - - return bestValue; - } - - - // qsearch() is the quiescence search function, which is called by the main - // search function when the remaining depth is zero (or, to be more precise, - // less than ONE_PLY). - - template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { - - const bool PvNode = NT == PV; - - assert(NT == PV || NT == NonPV); - assert(InCheck == !!pos.checkers()); - assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); - assert(PvNode || (alpha == beta - 1)); - assert(depth <= DEPTH_ZERO); - - Move pv[MAX_PLY+1]; - StateInfo st; - TTEntry* tte; - Key posKey; - Move ttMove, move, bestMove; - Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, givesCheck, evasionPrunable; - Depth ttDepth; - - if (PvNode) - { - oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves - (ss+1)->pv = pv; - ss->pv[0] = MOVE_NONE; - } - - ss->currentMove = bestMove = MOVE_NONE; - ss->ply = (ss-1)->ply + 1; - - // Check for an instant draw or if the maximum ply has been reached - if (pos.is_draw() || ss->ply >= MAX_PLY) - return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; - - assert(0 <= ss->ply && ss->ply < MAX_PLY); - - // Decide whether or not to include checks: this fixes also the type of - // TT entry depth that we are going to use. Note that in qsearch we use - // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. - ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS - : DEPTH_QS_NO_CHECKS; - - // Transposition table lookup - posKey = pos.key(); - tte = TT.probe(posKey, ttHit); - ttMove = ttHit ? tte->move() : MOVE_NONE; - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; - - if ( !PvNode - && ttHit - && tte->depth() >= ttDepth - && ttValue != VALUE_NONE // Only in case of TT access race - && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) - { - ss->currentMove = ttMove; // Can be MOVE_NONE - return ttValue; - } - - // Evaluate the position statically - if (InCheck) - { - ss->staticEval = VALUE_NONE; - bestValue = futilityBase = -VALUE_INFINITE; - } - else - { - if (ttHit) - { - // Never assume anything on values stored in TT - if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE) - ss->staticEval = bestValue = evaluate(pos); - - // Can ttValue be used as a better position evaluation? - if (ttValue != VALUE_NONE) - if (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)) - bestValue = ttValue; - } - else - ss->staticEval = bestValue = - (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo; - - // Stand pat. Return immediately if static value is at least beta - if (bestValue >= beta) - { - if (!ttHit) - tte->save(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, - DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation()); - - return bestValue; - } - - if (PvNode && bestValue > alpha) - alpha = bestValue; - - futilityBase = bestValue + 128; - } - - // 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 >= DEPTH_QS_CHECKS) will - // be generated. - MovePicker mp(pos, ttMove, depth, History, to_sq((ss-1)->currentMove)); - CheckInfo ci(pos); - - // Loop through the moves until no moves remain or a beta cutoff occurs - while ((move = mp.next_move()) != MOVE_NONE) - { - assert(is_ok(move)); - - givesCheck = type_of(move) == NORMAL && !ci.dcCandidates - ? ci.checkSq[type_of(pos.piece_on(from_sq(move)))] & to_sq(move) - : pos.gives_check(move, ci); - - // Futility pruning - if ( !PvNode - && !InCheck - && !givesCheck - && futilityBase > -VALUE_KNOWN_WIN - && !pos.advanced_pawn_push(move)) - { - assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push - - futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; - - if (futilityValue < beta) - { - bestValue = std::max(bestValue, futilityValue); - continue; - } - - if (futilityBase < beta && pos.see(move) <= VALUE_ZERO) - { - bestValue = std::max(bestValue, futilityBase); - continue; - } - } - - // Detect non-capture evasions that are candidates to be pruned - evasionPrunable = InCheck - && bestValue > VALUE_MATED_IN_MAX_PLY - && !pos.capture(move) - && !pos.can_castle(pos.side_to_move()); - - // Don't search moves with negative SEE values - if ( !PvNode - && (!InCheck || evasionPrunable) - && type_of(move) != PROMOTION - && pos.see_sign(move) < VALUE_ZERO) - continue; - - // Speculative prefetch as early as possible - prefetch((char*)TT.first_entry(pos.key_after(move))); - - // Check for legality just before making the move - if (!pos.legal(move, ci.pinned)) - continue; - - ss->currentMove = move; - - // Make and search the move - pos.do_move(move, st, ci, givesCheck); - value = givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY) - : -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY); - pos.undo_move(move); - - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - - // Check for new best move - if (value > bestValue) - { - bestValue = value; - - if (value > alpha) - { - if (PvNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss+1)->pv); - - if (PvNode && value < beta) // Update alpha here! Always alpha < beta - { - alpha = value; - bestMove = move; - } - else // Fail high - { - tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, - ttDepth, move, ss->staticEval, TT.generation()); - - return value; - } - } - } - } - - // All legal moves have been searched. A special case: If we're in check - // and no legal moves were found, it is checkmate. - if (InCheck && bestValue == -VALUE_INFINITE) - return mated_in(ss->ply); // Plies to mate from the root - - tte->save(posKey, value_to_tt(bestValue, ss->ply), - PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, - ttDepth, bestMove, ss->staticEval, TT.generation()); - - assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); - - return bestValue; - } - - - // value_to_tt() adjusts a mate score from "plies to mate from the root" to - // "plies to mate from the current position". Non-mate scores are unchanged. - // The function is called before storing a value in the transposition table. - - Value value_to_tt(Value v, int ply) { - - assert(v != VALUE_NONE); - - return v >= VALUE_MATE_IN_MAX_PLY ? v + ply - : v <= VALUE_MATED_IN_MAX_PLY ? v - ply : v; - } - - - // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score - // from the transposition table (which refers to the plies to mate/be mated - // from current position) to "plies to mate/be mated from the root". - - Value value_from_tt(Value v, int ply) { - - return v == VALUE_NONE ? VALUE_NONE - : v >= VALUE_MATE_IN_MAX_PLY ? v - ply - : v <= VALUE_MATED_IN_MAX_PLY ? v + ply : v; - } - - - // update_pv() adds current move and appends child pv[] - - void update_pv(Move* pv, Move move, Move* childPv) { - - for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) - *pv++ = *childPv++; - *pv = MOVE_NONE; - } - - // update_stats() updates killers, history, countermoves and followupmoves stats after a fail-high - // of a quiet move. - - void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt) { - - if (ss->killers[0] != move) - { - ss->killers[1] = ss->killers[0]; - ss->killers[0] = move; - } - - // Increase history value of the cut-off move and decrease all the other - // played quiet moves. - Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY)); - History.update(pos.moved_piece(move), to_sq(move), bonus); - for (int i = 0; i < quietsCnt; ++i) - { - Move m = quiets[i]; - History.update(pos.moved_piece(m), to_sq(m), -bonus); - } - - if (is_ok((ss-1)->currentMove)) - { - Square prevMoveSq = to_sq((ss-1)->currentMove); - Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, move); - } - - if (is_ok((ss-2)->currentMove) && (ss-1)->currentMove == (ss-1)->ttMove) - { - Square prevOwnMoveSq = to_sq((ss-2)->currentMove); - Followupmoves.update(pos.piece_on(prevOwnMoveSq), prevOwnMoveSq, move); - } - } - - - // When playing with a strength handicap, choose best move among the first 'candidates' - // RootMoves using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. - - Move Skill::pick_move() { - - // PRNG sequence should be non-deterministic, so we seed it with the time at init - static PRNG rng(Time::now()); - - // RootMoves are already sorted by score in descending order - int variance = std::min(RootMoves[0].score - RootMoves[candidates - 1].score, PawnValueMg); - int weakness = 120 - 2 * level; - int maxScore = -VALUE_INFINITE; - best = MOVE_NONE; - - // Choose best move. For each move score we add two terms both dependent on - // weakness. One deterministic and bigger for weaker moves, and one random, - // then we choose the move with the resulting highest score. - for (size_t i = 0; i < candidates; ++i) - { - int score = RootMoves[i].score; - - // Don't allow crazy blunders even at very low skills - if (i > 0 && RootMoves[i - 1].score > score + 2 * PawnValueMg) - break; - - // This is our magic formula - score += ( weakness * int(RootMoves[0].score - score) - + variance * (rng.rand() % weakness)) / 128; - - if (score > maxScore) - { - maxScore = score; - best = RootMoves[i].pv[0]; - } - } - return best; - } - - - // uci_pv() formats PV information according to the UCI protocol. UCI - // requires that all (if any) unsearched PV lines are sent using a previous - // search score. - - string uci_pv(const Position& pos, Depth depth, Value alpha, Value beta) { - - std::stringstream ss; - Time::point elapsed = Time::now() - SearchTime + 1; - size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); - int selDepth = 0; - - for (size_t i = 0; i < Threads.size(); ++i) - if (Threads[i]->maxPly > selDepth) - selDepth = Threads[i]->maxPly; - - for (size_t i = 0; i < uciPVSize; ++i) - { - bool updated = (i <= PVIdx); - - if (depth == ONE_PLY && !updated) - continue; - - Depth d = updated ? depth : depth - ONE_PLY; - Value v = updated ? RootMoves[i].score : RootMoves[i].previousScore; - - bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; - v = tb ? TB::Score : v; - - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; - - ss << "info depth " << d / ONE_PLY - << " seldepth " << selDepth - << " multipv " << i + 1 - << " score " << UCI::value(v); - - if (!tb && i == PVIdx) - ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - - ss << " nodes " << pos.nodes_searched() - << " nps " << pos.nodes_searched() * 1000 / elapsed - << " tbhits " << TB::Hits - << " time " << elapsed - << " pv"; - - for (size_t j = 0; j < RootMoves[i].pv.size(); ++j) - ss << " " << UCI::move(RootMoves[i].pv[j], pos.is_chess960()); - } - - return ss.str(); - } - -} // namespace - - -/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and -/// inserts the PV back into the TT. This makes sure the old PV moves are searched -/// first, even if the old TT entries have been overwritten. - -void RootMove::insert_pv_in_tt(Position& pos) { - - StateInfo state[MAX_PLY], *st = state; - size_t idx = 0; - - for ( ; idx < pv.size(); ++idx) - { - bool ttHit; - TTEntry* tte = TT.probe(pos.key(), ttHit); - - if (!ttHit || tte->move() != pv[idx]) // Don't overwrite correct entries - tte->save(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[idx], VALUE_NONE, TT.generation()); - - assert(MoveList(pos).contains(pv[idx])); - - pos.do_move(pv[idx], *st++); - } - - while (idx) pos.undo_move(pv[--idx]); -} - - -/// Thread::idle_loop() is where the thread is parked when it has no work to do - -void Thread::idle_loop() { - - // Pointer 'this_sp' is not null only if we are called from split(), and not - // at the thread creation. This means we are the split point's master. - SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; - - assert(!this_sp || (this_sp->masterThread == this && searching)); - - while (!exit) - { - // If this thread has been assigned work, launch a search - while (searching) - { - Threads.mutex.lock(); - - assert(activeSplitPoint); - SplitPoint* sp = activeSplitPoint; - - Threads.mutex.unlock(); - - Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) - Position pos(*sp->pos, this); - - std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); - ss->splitPoint = sp; - - sp->mutex.lock(); - - assert(activePosition == NULL); - - activePosition = &pos; - - if (sp->nodeType == NonPV) - search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); - - else if (sp->nodeType == PV) - search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); - - else if (sp->nodeType == Root) - search(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode); - - else - assert(false); - - assert(searching); - - searching = false; - activePosition = NULL; - sp->slavesMask.reset(idx); - sp->allSlavesSearching = false; - sp->nodes += pos.nodes_searched(); - - // Wake up the master thread so to allow it to return from the idle - // loop in case we are the last slave of the split point. - if ( this != sp->masterThread - && sp->slavesMask.none()) - { - assert(!sp->masterThread->searching); - sp->masterThread->notify_one(); - } - - // After releasing the lock we can't access any SplitPoint related data - // in a safe way because it could have been released under our feet by - // the sp master. - sp->mutex.unlock(); - - // Try to late join to another split point if none of its slaves has - // already finished. - if (Threads.size() > 2) - for (size_t i = 0; i < Threads.size(); ++i) - { - const int size = Threads[i]->splitPointsSize; // Local copy - sp = size ? &Threads[i]->splitPoints[size - 1] : NULL; - - if ( sp - && sp->allSlavesSearching - && available_to(Threads[i])) - { - // Recheck the conditions under lock protection - Threads.mutex.lock(); - sp->mutex.lock(); - - if ( sp->allSlavesSearching - && available_to(Threads[i])) - { - sp->slavesMask.set(idx); - activeSplitPoint = sp; - searching = true; - } - - sp->mutex.unlock(); - Threads.mutex.unlock(); - - break; // Just a single attempt - } - } - } - - // Grab the lock to avoid races with Thread::notify_one() - mutex.lock(); - - // If we are master and all slaves have finished then exit idle_loop - if (this_sp && this_sp->slavesMask.none()) - { - assert(!searching); - mutex.unlock(); - break; - } - - // If we are not searching, wait for a condition to be signaled instead of - // wasting CPU time polling for work. - if (!searching && !exit) - sleepCondition.wait(mutex); - - mutex.unlock(); - } -} - - -/// check_time() is called by the timer thread when the timer triggers. It is -/// used to print debug info and, more importantly, to detect when we are out of -/// available time and thus stop the search. - -void check_time() { - - static Time::point lastInfoTime = Time::now(); - Time::point elapsed = Time::now() - SearchTime; - - if (Time::now() - lastInfoTime >= 1000) - { - lastInfoTime = Time::now(); - dbg_print(); - } - - // An engine may not stop pondering until told so by the GUI - if (Limits.ponder) - return; - - if (Limits.use_time_management()) - { - bool stillAtFirstMove = Signals.firstRootMove - && !Signals.failedLowAtRoot - && elapsed > TimeMgr.available_time() * 75 / 100; - - if ( stillAtFirstMove - || elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution) - Signals.stop = true; - } - else if (Limits.movetime && elapsed >= Limits.movetime) - Signals.stop = true; - - else if (Limits.nodes) - { - Threads.mutex.lock(); - - int64_t nodes = RootPos.nodes_searched(); - - // Loop across all split points and sum accumulated SplitPoint nodes plus - // all the currently active positions nodes. - for (size_t i = 0; i < Threads.size(); ++i) - for (int j = 0; j < Threads[i]->splitPointsSize; ++j) - { - SplitPoint& sp = Threads[i]->splitPoints[j]; - - sp.mutex.lock(); - - nodes += sp.nodes; - - for (size_t idx = 0; idx < Threads.size(); ++idx) - if (sp.slavesMask.test(idx) && Threads[idx]->activePosition) - nodes += Threads[idx]->activePosition->nodes_searched(); - - sp.mutex.unlock(); - } - - Threads.mutex.unlock(); - - if (nodes >= Limits.nodes) - Signals.stop = true; - } -} diff --git a/src/search.h b/src/search.h deleted file mode 100644 index 20cfd474..00000000 --- a/src/search.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef SEARCH_H_INCLUDED -#define SEARCH_H_INCLUDED - -#include -#include -#include - -#include "misc.h" -#include "position.h" -#include "types.h" - -struct SplitPoint; - -namespace Search { - -/// The Stack struct keeps track of the information we need to remember from -/// nodes shallower and deeper in the tree during the search. Each search thread -/// has its own array of Stack objects, indexed by the current ply. - -struct Stack { - SplitPoint* splitPoint; - Move* pv; - int ply; - Move currentMove; - Move ttMove; - Move excludedMove; - Move killers[2]; - Depth reduction; - Value staticEval; - bool skipEarlyPruning; -}; - - -/// RootMove struct is used for moves at the root of the tree. For each root -/// move we store a score, a node count, and a PV (really a refutation in the -/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for -/// all non-pv moves. -struct RootMove { - - RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {} - - bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort - bool operator==(const Move& m) const { return pv[0] == m; } - - void insert_pv_in_tt(Position& pos); - - Value score; - Value previousScore; - std::vector pv; -}; - -typedef std::vector RootMoveVector; - -/// The LimitsType struct stores information sent by GUI about available time -/// to search the current move, maximum depth/time, if we are in analysis mode -/// or if we have to ponder while it's our opponent's turn to move. - -struct LimitsType { - - LimitsType() { // Using memset on a std::vector is undefined behavior - nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo = - depth = movetime = mate = infinite = ponder = 0; - } - bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } - - std::vector searchmoves; - int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder; - int64_t nodes; -}; - - -/// The SignalsType struct stores volatile flags updated during the search -/// typically in an async fashion e.g. to stop the search by the GUI. - -struct SignalsType { - bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; -}; - -typedef std::auto_ptr > StateStackPtr; - -extern volatile SignalsType Signals; -extern LimitsType Limits; -extern RootMoveVector RootMoves; -extern Position RootPos; -extern Time::point SearchTime; -extern StateStackPtr SetupStates; - -void init(); -void think(); -template uint64_t perft(Position& pos, Depth depth); - -} // namespace Search - -#endif // #ifndef SEARCH_H_INCLUDED diff --git a/src/thread.cpp b/src/thread.cpp deleted file mode 100644 index 9400b38d..00000000 --- a/src/thread.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include // For std::count -#include - -#include "movegen.h" -#include "search.h" -#include "thread.h" -#include "uci.h" - -using namespace Search; - -ThreadPool Threads; // Global object - -extern void check_time(); - -namespace { - - // start_routine() is the C function which is called when a new thread - // is launched. It is a wrapper to the virtual function idle_loop(). - - extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } } - - - // Helpers to launch a thread after creation and joining before delete. Must be - // outside Thread c'tor and d'tor because the object must be fully initialized - // when start_routine (and hence virtual idle_loop) is called and when joining. - - template T* new_thread() { - T* th = new T(); - thread_create(th->handle, start_routine, th); // Will go to sleep - return th; - } - - void delete_thread(ThreadBase* th) { - - th->mutex.lock(); - th->exit = true; // Search must be already finished - th->mutex.unlock(); - - th->notify_one(); - thread_join(th->handle); // Wait for thread termination - delete th; - } - -} - - -// ThreadBase::notify_one() wakes up the thread when there is some work to do - -void ThreadBase::notify_one() { - - mutex.lock(); - sleepCondition.notify_one(); - mutex.unlock(); -} - - -// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true - -void ThreadBase::wait_for(volatile const bool& condition) { - - mutex.lock(); - while (!condition) sleepCondition.wait(mutex); - mutex.unlock(); -} - - -// Thread c'tor makes some init but does not launch any execution thread that -// will be started only when c'tor returns. - -Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC - - searching = false; - maxPly = splitPointsSize = 0; - activeSplitPoint = NULL; - activePosition = NULL; - idx = Threads.size(); // Starts from 0 -} - - -// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the -// current active split point, or in some ancestor of the split point. - -bool Thread::cutoff_occurred() const { - - for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint) - if (sp->cutoff) - return true; - - return false; -} - - -// Thread::available_to() checks whether the thread is available to help the -// thread 'master' at a split point. An obvious requirement is that thread must -// be idle. With more than two threads, this is not sufficient: If the thread is -// the master of some split point, it is only available as a slave to the slaves -// which are busy searching the split point at the top of slave's split point -// stack (the "helpful master concept" in YBWC terminology). - -bool Thread::available_to(const Thread* master) const { - - if (searching) - return false; - - // Make a local copy to be sure it doesn't become zero under our feet while - // testing next condition and so leading to an out of bounds access. - const int size = splitPointsSize; - - // No split points means that the thread is available as a slave for any - // other thread otherwise apply the "helpful master" concept if possible. - return !size || splitPoints[size - 1].slavesMask.test(master->idx); -} - - -// Thread::split() does the actual work of distributing the work at a node between -// several available threads. If it does not succeed in splitting the node -// (because no idle threads are available), the function immediately returns. -// If splitting is possible, a SplitPoint object is initialized with all the -// data that must be copied to the helper threads and then helper threads are -// informed that they have been assigned work. This will cause them to instantly -// leave their idle loops and call search(). When all threads have returned from -// search() then split() returns. - -void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, - Move* bestMove, Depth depth, int moveCount, - MovePicker* movePicker, int nodeType, bool cutNode) { - - assert(searching); - assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); - assert(depth >= Threads.minimumSplitDepth); - assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); - - // Pick and init the next available split point - SplitPoint& sp = splitPoints[splitPointsSize]; - - sp.masterThread = this; - sp.parentSplitPoint = activeSplitPoint; - sp.slavesMask = 0, sp.slavesMask.set(idx); - sp.depth = depth; - sp.bestValue = *bestValue; - sp.bestMove = *bestMove; - sp.alpha = alpha; - sp.beta = beta; - sp.nodeType = nodeType; - sp.cutNode = cutNode; - sp.movePicker = movePicker; - sp.moveCount = moveCount; - sp.pos = &pos; - sp.nodes = 0; - sp.cutoff = false; - sp.ss = ss; - - // Try to allocate available threads and ask them to start searching setting - // 'searching' flag. This must be done under lock protection to avoid concurrent - // allocation of the same slave by another master. - Threads.mutex.lock(); - sp.mutex.lock(); - - sp.allSlavesSearching = true; // Must be set under lock protection - ++splitPointsSize; - activeSplitPoint = &sp; - activePosition = NULL; - - Thread* slave; - - while ((slave = Threads.available_slave(this)) != NULL) - { - sp.slavesMask.set(slave->idx); - slave->activeSplitPoint = &sp; - slave->searching = true; // Slave leaves idle_loop() - slave->notify_one(); // Could be sleeping - } - - // Everything is set up. The master thread enters the idle loop, from which - // it will instantly launch a search, because its 'searching' flag is set. - // The thread will return from the idle loop when all slaves have finished - // their work at this split point. - sp.mutex.unlock(); - Threads.mutex.unlock(); - - Thread::idle_loop(); // Force a call to base class idle_loop() - - // In the helpful master concept, a master can help only a sub-tree of its - // split point and because everything is finished here, it's not possible - // for the master to be booked. - assert(!searching); - assert(!activePosition); - - // We have returned from the idle loop, which means that all threads are - // finished. Note that setting 'searching' and decreasing splitPointsSize must - // be done under lock protection to avoid a race with Thread::available_to(). - Threads.mutex.lock(); - sp.mutex.lock(); - - searching = true; - --splitPointsSize; - activeSplitPoint = sp.parentSplitPoint; - activePosition = &pos; - pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); - *bestMove = sp.bestMove; - *bestValue = sp.bestValue; - - sp.mutex.unlock(); - Threads.mutex.unlock(); -} - - -// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds -// and then calls check_time(). When not searching, thread sleeps until it's woken up. - -void TimerThread::idle_loop() { - - while (!exit) - { - mutex.lock(); - - if (!exit) - sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX); - - mutex.unlock(); - - if (run) - check_time(); - } -} - - -// MainThread::idle_loop() is where the main thread is parked waiting to be started -// when there is a new search. The main thread will launch all the slave threads. - -void MainThread::idle_loop() { - - while (!exit) - { - mutex.lock(); - - thinking = false; - - while (!thinking && !exit) - { - Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed - sleepCondition.wait(mutex); - } - - mutex.unlock(); - - if (!exit) - { - searching = true; - - Search::think(); - - assert(searching); - - searching = false; - } - } -} - - -// ThreadPool::init() is called at startup to create and launch requested threads, -// that will go immediately to sleep. We cannot use a c'tor because Threads is a -// static object and we need a fully initialized engine at this point due to -// allocation of Endgames in Thread c'tor. - -void ThreadPool::init() { - - timer = new_thread(); - push_back(new_thread()); - read_uci_options(); -} - - -// ThreadPool::exit() terminates the threads before the program exits. Cannot be -// done in d'tor because threads must be terminated before freeing us. - -void ThreadPool::exit() { - - delete_thread(timer); // As first because check_time() accesses threads data - - for (iterator it = begin(); it != end(); ++it) - delete_thread(*it); -} - - -// ThreadPool::read_uci_options() updates internal threads parameters from the -// corresponding UCI options and creates/destroys threads to match the requested -// number. Thread objects are dynamically allocated to avoid creating all possible -// threads in advance (which include pawns and material tables), even if only a -// few are to be used. - -void ThreadPool::read_uci_options() { - - minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; - size_t requested = Options["Threads"]; - - assert(requested > 0); - - // If zero (default) then set best minimum split depth automatically - if (!minimumSplitDepth) - minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY; - - while (size() < requested) - push_back(new_thread()); - - while (size() > requested) - { - delete_thread(back()); - pop_back(); - } -} - - -// ThreadPool::available_slave() tries to find an idle thread which is available -// as a slave for the thread 'master'. - -Thread* ThreadPool::available_slave(const Thread* master) const { - - for (const_iterator it = begin(); it != end(); ++it) - if ((*it)->available_to(master)) - return *it; - - return NULL; -} - - -// ThreadPool::wait_for_think_finished() waits for main thread to finish the search - -void ThreadPool::wait_for_think_finished() { - - MainThread* th = main(); - th->mutex.lock(); - while (th->thinking) sleepCondition.wait(th->mutex); - th->mutex.unlock(); -} - - -// ThreadPool::start_thinking() wakes up the main thread sleeping in -// MainThread::idle_loop() and starts a new search, then returns immediately. - -void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, - StateStackPtr& states) { - wait_for_think_finished(); - - SearchTime = Time::now(); // As early as possible - - Signals.stopOnPonderhit = Signals.firstRootMove = false; - Signals.stop = Signals.failedLowAtRoot = false; - - RootMoves.clear(); - RootPos = pos; - Limits = limits; - if (states.get()) // If we don't set a new position, preserve current state - { - SetupStates = states; // Ownership transfer here - assert(!states.get()); - } - - for (MoveList it(pos); *it; ++it) - if ( limits.searchmoves.empty() - || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), *it)) - RootMoves.push_back(RootMove(*it)); - - main()->thinking = true; - main()->notify_one(); // Starts main thread -} diff --git a/src/thread.h b/src/thread.h deleted file mode 100644 index 8a512c96..00000000 --- a/src/thread.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef THREAD_H_INCLUDED -#define THREAD_H_INCLUDED - -#include -#include - -#include "material.h" -#include "movepick.h" -#include "pawns.h" -#include "position.h" -#include "search.h" - -const int MAX_THREADS = 128; -const int MAX_SPLITPOINTS_PER_THREAD = 8; - -struct Mutex { - Mutex() { lock_init(l); } - ~Mutex() { lock_destroy(l); } - - void lock() { lock_grab(l); } - void unlock() { lock_release(l); } - -private: - friend struct ConditionVariable; - - Lock l; -}; - -struct ConditionVariable { - ConditionVariable() { cond_init(c); } - ~ConditionVariable() { cond_destroy(c); } - - void wait(Mutex& m) { cond_wait(c, m.l); } - void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); } - void notify_one() { cond_signal(c); } - -private: - WaitCondition c; -}; - -struct Thread; - -struct SplitPoint { - - // Const data after split point has been setup - const Position* pos; - Search::Stack* ss; - Thread* masterThread; - Depth depth; - Value beta; - int nodeType; - bool cutNode; - - // Const pointers to shared data - MovePicker* movePicker; - SplitPoint* parentSplitPoint; - - // Shared data - Mutex mutex; - std::bitset slavesMask; - volatile bool allSlavesSearching; - volatile uint64_t nodes; - volatile Value alpha; - volatile Value bestValue; - volatile Move bestMove; - volatile int moveCount; - volatile bool cutoff; -}; - - -/// ThreadBase struct is the base of the hierarchy from where we derive all the -/// specialized thread classes. - -struct ThreadBase { - - ThreadBase() : handle(NativeHandle()), exit(false) {} - virtual ~ThreadBase() {} - virtual void idle_loop() = 0; - void notify_one(); - void wait_for(volatile const bool& b); - - Mutex mutex; - ConditionVariable sleepCondition; - NativeHandle handle; - volatile bool exit; -}; - - -/// Thread struct keeps together all the thread related stuff like locks, state -/// and especially split points. We also use per-thread pawn and material hash -/// tables so that once we get a pointer to an entry its life time is unlimited -/// and we don't have to care about someone changing the entry under our feet. - -struct Thread : public ThreadBase { - - Thread(); - virtual void idle_loop(); - bool cutoff_occurred() const; - bool available_to(const Thread* master) const; - - void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, - Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); - - SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; - Material::Table materialTable; - Endgames endgames; - Pawns::Table pawnsTable; - Position* activePosition; - size_t idx; - int maxPly; - SplitPoint* volatile activeSplitPoint; - volatile int splitPointsSize; - volatile bool searching; -}; - - -/// MainThread and TimerThread are derived classes used to characterize the two -/// special threads: the main one and the recurring timer. - -struct MainThread : public Thread { - MainThread() : thinking(true) {} // Avoid a race with start_thinking() - virtual void idle_loop(); - volatile bool thinking; -}; - -struct TimerThread : public ThreadBase { - TimerThread() : run(false) {} - virtual void idle_loop(); - bool run; - static const int Resolution = 5; // msec between two check_time() calls -}; - - -/// ThreadPool struct handles all the threads related stuff like init, starting, -/// parking and, most importantly, launching a slave thread at a split point. -/// All the access to shared thread data is done through this class. - -struct ThreadPool : public std::vector { - - void init(); // No c'tor and d'tor, threads rely on globals that should - void exit(); // be initialized and are valid during the whole thread lifetime. - - MainThread* main() { return static_cast((*this)[0]); } - void read_uci_options(); - Thread* available_slave(const Thread* master) const; - void wait_for_think_finished(); - void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&); - - Depth minimumSplitDepth; - Mutex mutex; - ConditionVariable sleepCondition; - TimerThread* timer; -}; - -extern ThreadPool Threads; - -#endif // #ifndef THREAD_H_INCLUDED diff --git a/src/timeman.cpp b/src/timeman.cpp deleted file mode 100644 index 9fedd1ce..00000000 --- a/src/timeman.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -#include "search.h" -#include "timeman.h" -#include "uci.h" - -namespace { - - enum TimeType { OptimumTime, MaxTime }; - - const int MoveHorizon = 50; // Plan time management at most this many moves ahead - const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio - const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio - - - // move_importance() is a skew-logistic function based on naive statistical - // analysis of "how many games are still undecided after n half-moves". Game - // is considered "undecided" as long as neither side has >275cp advantage. - // Data was extracted from CCRL game database with some simple filtering criteria. - - double move_importance(int ply) { - - const double XScale = 9.3; - const double XShift = 59.8; - const double Skew = 0.172; - - return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero - } - - template - int remaining(int myTime, int movesToGo, int ply, int slowMover) - { - const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); - const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); - - double moveImportance = (move_importance(ply) * slowMover) / 100; - double otherMovesImportance = 0; - - for (int i = 1; i < movesToGo; ++i) - otherMovesImportance += move_importance(ply + 2 * i); - - double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); - double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); - - return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast - } - -} // namespace - - -/// init() is called at the beginning of the search and calculates the allowed -/// thinking time out of the time control and current game ply. We support four -/// different kinds of time controls, passed in 'limits': -/// -/// inc == 0 && movestogo == 0 means: x basetime [sudden death!] -/// inc == 0 && movestogo != 0 means: x moves in y minutes -/// inc > 0 && movestogo == 0 means: x basetime + z increment -/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment - -void TimeManager::init(const Search::LimitsType& limits, Color us, int ply) -{ - int minThinkingTime = Options["Minimum Thinking Time"]; - int moveOverhead = Options["Move Overhead"]; - int slowMover = Options["Slow Mover"]; - - // Initialize unstablePvFactor to 1 and search times to maximum values - unstablePvFactor = 1; - optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime); - - const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; - - // We calculate optimum time usage for different hypothetical "moves to go"-values - // and choose the minimum of calculated search time values. Usually the greatest - // hypMTG gives the minimum values. - for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG) - { - // Calculate thinking time for hypothetical "moves to go"-value - int hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); - - hypMyTime = std::max(hypMyTime, 0); - - int t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - int t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - - optimumSearchTime = std::min(t1, optimumSearchTime); - maximumSearchTime = std::min(t2, maximumSearchTime); - } - - if (Options["Ponder"]) - optimumSearchTime += optimumSearchTime / 4; - - optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); -} diff --git a/src/timeman.h b/src/timeman.h deleted file mode 100644 index b9bd18b4..00000000 --- a/src/timeman.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef TIMEMAN_H_INCLUDED -#define TIMEMAN_H_INCLUDED - -/// The TimeManager class computes the optimal time to think depending on the -/// maximum available time, the game move number and other parameters. - -class TimeManager { -public: - void init(const Search::LimitsType& limits, Color us, int ply); - void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; } - int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); } - int maximum_time() const { return maximumSearchTime; } - -private: - int optimumSearchTime; - int maximumSearchTime; - double unstablePvFactor; -}; - -#endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp deleted file mode 100644 index 76cd9218..00000000 --- a/src/tt.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include // For std::memset -#include - -#include "bitboard.h" -#include "tt.h" - -TranspositionTable TT; // Our global transposition table - - -/// TranspositionTable::resize() sets the size of the transposition table, -/// measured in megabytes. Transposition table consists of a power of 2 number -/// of clusters and each cluster consists of TTClusterSize number of TTEntry. - -void TranspositionTable::resize(size_t mbSize) { - - size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(TTCluster)); - - if (newClusterCount == clusterCount) - return; - - clusterCount = newClusterCount; - - free(mem); - mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1); - - if (!mem) - { - std::cerr << "Failed to allocate " << mbSize - << "MB for transposition table." << std::endl; - exit(EXIT_FAILURE); - } - - table = (TTCluster*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); -} - - -/// TranspositionTable::clear() overwrites the entire transposition table -/// with zeros. It is called whenever the table is resized, or when the -/// user asks the program to clear the table (from the UCI interface). - -void TranspositionTable::clear() { - - std::memset(table, 0, clusterCount * sizeof(TTCluster)); -} - - -/// TranspositionTable::probe() looks up the current position in the transposition -/// table. It returns true and a pointer to the TTEntry if the position is found. -/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry -/// to be replaced later. A TTEntry t1 is considered to be more valuable than a -/// TTEntry t2 if t1 is from the current search and t2 is from a previous search, -/// or if the depth of t1 is bigger than the depth of t2. - -TTEntry* TranspositionTable::probe(const Key key, bool& found) const { - - TTEntry* const tte = first_entry(key); - const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster - - for (unsigned i = 0; i < TTClusterSize; ++i) - if (!tte[i].key16 || tte[i].key16 == key16) - { - if (tte[i].key16) - tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh - - return found = (bool)tte[i].key16, &tte[i]; - } - - // Find an entry to be replaced according to the replacement strategy - TTEntry* replace = tte; - for (unsigned i = 1; i < TTClusterSize; ++i) - if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT) - - ((replace->genBound8 & 0xFC) == generation8) - - (tte[i].depth8 < replace->depth8) < 0) - replace = &tte[i]; - - return found = false, replace; -} diff --git a/src/tt.h b/src/tt.h deleted file mode 100644 index 89c95d55..00000000 --- a/src/tt.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef TT_H_INCLUDED -#define TT_H_INCLUDED - -#include "misc.h" -#include "types.h" - -/// The TTEntry is the 10 bytes transposition table entry, defined as below: -/// -/// key 16 bit -/// move 16 bit -/// value 16 bit -/// eval value 16 bit -/// generation 6 bit -/// bound type 2 bit -/// depth 8 bit - -struct TTEntry { - - Move move() const { return (Move )move16; } - Value value() const { return (Value)value16; } - Value eval_value() const { return (Value)evalValue; } - Depth depth() const { return (Depth)depth8; } - Bound bound() const { return (Bound)(genBound8 & 0x3); } - - void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { - - if (m || (k >> 48) != key16) // Preserve any existing move for the same position - move16 = (uint16_t)m; - - key16 = (uint16_t)(k >> 48); - value16 = (int16_t)v; - evalValue = (int16_t)ev; - genBound8 = (uint8_t)(g | b); - depth8 = (int8_t)d; - } - -private: - friend class TranspositionTable; - - uint16_t key16; - uint16_t move16; - int16_t value16; - int16_t evalValue; - uint8_t genBound8; - int8_t depth8; -}; - -/// TTCluster is a 32 bytes cluster of TT entries consisting of: -/// -/// 3 x TTEntry (3 x 10 bytes) -/// padding (2 bytes) - -static const unsigned TTClusterSize = 3; - -struct TTCluster { - TTEntry entry[TTClusterSize]; - char padding[2]; -}; - -/// A TranspositionTable consists of a power of 2 number of clusters and each -/// cluster consists of TTClusterSize number of TTEntry. Each non-empty entry -/// contains information of exactly one position. The size of a cluster should -/// not be bigger than a cache line size. In case it is less, it should be padded -/// to guarantee always aligned accesses. - -class TranspositionTable { - -public: - ~TranspositionTable() { free(mem); } - void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound - uint8_t generation() const { return generation8; } - TTEntry* probe(const Key key, bool& found) const; - TTEntry* first_entry(const Key key) const; - void resize(size_t mbSize); - void clear(); - -private: - size_t clusterCount; - TTCluster* table; - void* mem; - uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 -}; - -extern TranspositionTable TT; - - -/// TranspositionTable::first_entry() returns a pointer to the first entry of -/// a cluster given a position. The lowest order bits of the key are used to -/// get the index of the cluster inside the table. - -inline TTEntry* TranspositionTable::first_entry(const Key key) const { - - return &table[(size_t)key & (clusterCount - 1)].entry[0]; -} - -#endif // #ifndef TT_H_INCLUDED diff --git a/src/uci.cpp b/src/uci.cpp deleted file mode 100644 index bc291b29..00000000 --- a/src/uci.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -#include "evaluate.h" -#include "movegen.h" -#include "position.h" -#include "search.h" -#include "thread.h" -#include "tt.h" -#include "uci.h" - -using namespace std; - -extern void benchmark(const Position& pos, istream& is); - -namespace { - - // FEN string of the initial position, normal chess - const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - - // Stack to keep track of the position states along the setup moves (from the - // start position to the position just before the search starts). Needed by - // 'draw by repetition' detection. - Search::StateStackPtr SetupStates; - - - // position() is called when engine receives the "position" UCI command. - // The function sets up the position described in the given FEN string ("fen") - // or the starting position ("startpos") and then makes the moves given in the - // following move list ("moves"). - - void position(Position& pos, istringstream& is) { - - Move m; - string token, fen; - - is >> token; - - if (token == "startpos") - { - fen = StartFEN; - is >> token; // Consume "moves" token if any - } - else if (token == "fen") - while (is >> token && token != "moves") - fen += token + " "; - else - return; - - pos.set(fen, Options["UCI_Chess960"], Threads.main()); - SetupStates = Search::StateStackPtr(new std::stack()); - - // Parse move list (if any) - while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) - { - SetupStates->push(StateInfo()); - pos.do_move(m, SetupStates->top()); - } - } - - - // setoption() is called when engine receives the "setoption" UCI command. The - // function updates the UCI option ("name") to the given value ("value"). - - void setoption(istringstream& is) { - - string token, name, value; - - is >> token; // Consume "name" token - - // Read option name (can contain spaces) - while (is >> token && token != "value") - name += string(" ", !name.empty()) + token; - - // Read option value (can contain spaces) - while (is >> token) - value += string(" ", !value.empty()) + token; - - if (Options.count(name)) - Options[name] = value; - else - sync_cout << "No such option: " << name << sync_endl; - } - - - // go() is called when engine receives the "go" UCI command. The function sets - // the thinking time and other parameters from the input string, then starts - // the search. - - void go(const Position& pos, istringstream& is) { - - Search::LimitsType limits; - string token; - - while (is >> token) - if (token == "searchmoves") - while (is >> token) - limits.searchmoves.push_back(UCI::to_move(pos, token)); - - else if (token == "wtime") is >> limits.time[WHITE]; - else if (token == "btime") is >> limits.time[BLACK]; - else if (token == "winc") is >> limits.inc[WHITE]; - else if (token == "binc") is >> limits.inc[BLACK]; - else if (token == "movestogo") is >> limits.movestogo; - else if (token == "depth") is >> limits.depth; - else if (token == "nodes") is >> limits.nodes; - else if (token == "movetime") is >> limits.movetime; - else if (token == "mate") is >> limits.mate; - else if (token == "infinite") limits.infinite = true; - else if (token == "ponder") limits.ponder = true; - - Threads.start_thinking(pos, limits, SetupStates); - } - -} // namespace - - -/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate -/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the -/// GUI dies unexpectedly. When called with some command line arguments, e.g. to -/// run 'bench', once the command is executed the function returns immediately. -/// In addition to the UCI ones, also some additional debug commands are supported. - -void UCI::loop(int argc, char* argv[]) { - - Position pos(StartFEN, false, Threads.main()); // The root position - string token, cmd; - - for (int i = 1; i < argc; ++i) - cmd += std::string(argv[i]) + " "; - - do { - if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF - cmd = "quit"; - - istringstream is(cmd); - - token.clear(); // getline() could return empty or blank line - is >> skipws >> token; - - // The GUI sends 'ponderhit' to tell us to ponder on the same move the - // opponent has played. In case Signals.stopOnPonderhit is set we are - // waiting for 'ponderhit' to stop the search (for instance because we - // already ran out of time), otherwise we should continue searching but - // switching from pondering to normal search. - if ( token == "quit" - || token == "stop" - || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) - { - Search::Signals.stop = true; - Threads.main()->notify_one(); // Could be sleeping - } - else if (token == "ponderhit") - Search::Limits.ponder = false; // Switch to normal search - - else if (token == "uci") - sync_cout << "id name " << engine_info(true) - << "\n" << Options - << "\nuciok" << sync_endl; - - else if (token == "isready") sync_cout << "readyok" << sync_endl; - else if (token == "ucinewgame") TT.clear(); - else if (token == "go") go(pos, is); - else if (token == "position") position(pos, is); - else if (token == "setoption") setoption(is); - - // Additional custom non-UCI commands, useful for debugging - else if (token == "flip") pos.flip(); - else if (token == "bench") benchmark(pos, is); - else if (token == "d") sync_cout << pos << sync_endl; - else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; - else if (token == "perft") - { - int depth; - stringstream ss; - - is >> depth; - ss << Options["Hash"] << " " - << Options["Threads"] << " " << depth << " current perft"; - - benchmark(pos, ss); - } - else - sync_cout << "Unknown command: " << cmd << sync_endl; - - } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour - - Threads.wait_for_think_finished(); // Cannot quit whilst the search is running -} - - -/// UCI::value() converts a Value to a string suitable for use with the UCI -/// protocol specification: -/// -/// cp The score from the engine's point of view in centipawns. -/// mate Mate in y moves, not plies. If the engine is getting mated -/// use negative values for y. - -string UCI::value(Value v) { - - stringstream ss; - - if (abs(v) < VALUE_MATE - MAX_PLY) - ss << "cp " << v * 100 / PawnValueEg; - else - ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; - - return ss.str(); -} - - -/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) - -std::string UCI::square(Square s) { - - char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated - return sq; -} - - -/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). -/// The only special case is castling, where we print in the e1g1 notation in -/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all -/// castling moves are always encoded as 'king captures rook'. - -string UCI::move(Move m, bool chess960) { - - Square from = from_sq(m); - Square to = to_sq(m); - - if (m == MOVE_NONE) - return "(none)"; - - if (m == MOVE_NULL) - return "0000"; - - if (type_of(m) == CASTLING && !chess960) - to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); - - string move = UCI::square(from) + UCI::square(to); - - if (type_of(m) == PROMOTION) - move += " pnbrqk"[promotion_type(m)]; - - return move; -} - - -/// UCI::to_move() converts a string representing a move in coordinate notation -/// (g1f3, a7a8q) to the corresponding legal Move, if any. - -Move UCI::to_move(const Position& pos, string& str) { - - if (str.length() == 5) // Junior could send promotion piece in uppercase - str[4] = char(tolower(str[4])); - - for (MoveList it(pos); *it; ++it) - if (str == UCI::move(*it, pos.is_chess960())) - return *it; - - return MOVE_NONE; -} diff --git a/src/uci.h b/src/uci.h deleted file mode 100644 index 744f3209..00000000 --- a/src/uci.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef UCIOPTION_H_INCLUDED -#define UCIOPTION_H_INCLUDED - -#include -#include - -#include "types.h" - -class Position; - -namespace UCI { - -class Option; - -/// Custom comparator because UCI options should be case insensitive -struct CaseInsensitiveLess { - bool operator() (const std::string&, const std::string&) const; -}; - -/// Our options container is actually a std::map -typedef std::map OptionsMap; - -/// Option class implements an option as defined by UCI protocol -class Option { - - typedef void (*OnChange)(const Option&); - -public: - Option(OnChange = NULL); - Option(bool v, OnChange = NULL); - Option(const char* v, OnChange = NULL); - Option(int v, int min, int max, OnChange = NULL); - - Option& operator=(const std::string& v); - void operator<<(const Option& o); - operator int() const; - operator std::string() const; - -private: - friend std::ostream& operator<<(std::ostream&, const OptionsMap&); - - std::string defaultValue, currentValue, type; - int min, max; - size_t idx; - OnChange on_change; -}; - -void init(OptionsMap&); -void loop(int argc, char* argv[]); - -std::string value(Value v); -std::string square(Square s); -std::string move(Move m, bool chess960); -Move to_move(const Position& pos, std::string& str); - -} // namespace UCI - -extern UCI::OptionsMap Options; - -#endif // #ifndef UCIOPTION_H_INCLUDED diff --git a/src/ucioption.cpp b/src/ucioption.cpp deleted file mode 100644 index 23e00180..00000000 --- a/src/ucioption.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include - -#include "misc.h" -#include "thread.h" -#include "tt.h" -#include "uci.h" -#include "syzygy/tbprobe.h" - -using std::string; - -UCI::OptionsMap Options; // Global object - -namespace UCI { - -/// 'On change' actions, triggered by an option's value change -void on_clear_hash(const Option&) { TT.clear(); } -void on_hash_size(const Option& o) { TT.resize(o); } -void on_logger(const Option& o) { start_logger(o); } -void on_threads(const Option&) { Threads.read_uci_options(); } -void on_tb_path(const Option& o) { Tablebases::init(o); } - - -/// Our case insensitive less() function as required by UCI protocol -bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); } - -bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less); -} - - -/// init() initializes the UCI options to their hard-coded default values - -void init(OptionsMap& o) { - - o["Write Debug Log"] << Option(false, on_logger); - o["Contempt"] << Option(0, -100, 100); - o["Min Split Depth"] << Option(0, 0, 12, on_threads); - o["Threads"] << Option(1, 1, MAX_THREADS, on_threads); - o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size); - o["Clear Hash"] << Option(on_clear_hash); - o["Ponder"] << Option(true); - o["MultiPV"] << Option(1, 1, 500); - o["Skill Level"] << Option(20, 0, 20); - o["Move Overhead"] << Option(30, 0, 5000); - o["Minimum Thinking Time"] << Option(20, 0, 5000); - o["Slow Mover"] << Option(80, 10, 1000); - o["UCI_Chess960"] << Option(false); - o["SyzygyPath"] << Option("", on_tb_path); - o["SyzygyProbeDepth"] << Option(1, 1, 100); - o["Syzygy50MoveRule"] << Option(true); - o["SyzygyProbeLimit"] << Option(6, 0, 6); -} - - -/// operator<<() is used to print all the options default values in chronological -/// insertion order (the idx field) and in the format defined by the UCI protocol. - -std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { - - for (size_t idx = 0; idx < om.size(); ++idx) - for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it) - if (it->second.idx == idx) - { - const Option& o = it->second; - os << "\noption name " << it->first << " type " << o.type; - - if (o.type != "button") - os << " default " << o.defaultValue; - - if (o.type == "spin") - os << " min " << o.min << " max " << o.max; - - break; - } - return os; -} - - -/// Option class constructors and conversion operators - -Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) -{ defaultValue = currentValue = v; } - -Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) -{ defaultValue = currentValue = (v ? "true" : "false"); } - -Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) -{} - -Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) -{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); } - - -Option::operator int() const { - assert(type == "check" || type == "spin"); - return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true"); -} - -Option::operator std::string() const { - assert(type == "string"); - return currentValue; -} - - -/// operator<<() inits options and assigns idx in the correct printing order - -void Option::operator<<(const Option& o) { - - static size_t insert_order = 0; - - *this = o; - idx = insert_order++; -} - - -/// operator=() updates currentValue and triggers on_change() action. It's up to -/// the GUI to check for option's limits, but we could receive the new value from -/// the user by console window, so let's check the bounds anyway. - -Option& Option::operator=(const string& v) { - - assert(!type.empty()); - - if ( (type != "button" && v.empty()) - || (type == "check" && v != "true" && v != "false") - || (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max))) - return *this; - - if (type != "button") - currentValue = v; - - if (on_change) - on_change(*this); - - return *this; -} - -} // namespace UCI