Rev 154 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 154 | Rev 169 | ||
---|---|---|---|
Line 4... | Line 4... | ||
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
5 | Copyright (C) 2015- |
5 | Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
6 | 6 | ||
7 | Stockfish is free software: you can redistribute it and/or modify |
7 | Stockfish is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation, either version 3 of the License, or |
9 | the Free Software Foundation, either version 3 of the License, or |
10 | (at your option) any later version. |
10 | (at your option) any later version. |
Line 16... | Line 16... | ||
16 | 16 | ||
17 | You should have received a copy of the GNU General Public License |
17 | You should have received a copy of the GNU General Public License |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
19 | */ |
20 | 20 | ||
- | 21 | #include <cassert> |
|
21 | #include <iostream> |
22 | #include <iostream> |
22 | #include <sstream> |
23 | #include <sstream> |
23 | #include <string> |
24 | #include <string> |
24 | 25 | ||
25 | #include "evaluate.h" |
26 | #include "evaluate.h" |
26 | #include "movegen.h" |
27 | #include "movegen.h" |
27 | #include "position.h" |
28 | #include "position.h" |
28 | #include "search.h" |
29 | #include "search.h" |
29 | #include "thread.h" |
30 | #include "thread.h" |
- | 31 | #include "tt.h" |
|
30 | #include "timeman.h" |
32 | #include "timeman.h" |
31 | #include "uci.h" |
33 | #include "uci.h" |
- | 34 | #include "syzygy/tbprobe.h" |
|
32 | 35 | ||
33 | using namespace std; |
36 | using namespace std; |
34 | 37 | ||
35 | extern |
38 | extern vector<string> setup_bench(const Position&, istream&); |
36 | 39 | ||
37 | namespace { |
40 | namespace { |
38 | 41 | ||
39 | // FEN string of the initial position, normal chess |
42 | // FEN string of the initial position, normal chess |
40 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; |
43 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; |
41 | - | ||
42 | // A list to keep track of the position states along the setup moves (from the |
- | |
43 | // start position to the position just before the search starts). Needed by |
- | |
44 | // 'draw by repetition' detection. |
- | |
45 | StateListPtr States(new std::deque<StateInfo>(1)); |
- | |
46 | 44 | ||
47 | 45 | ||
48 | // position() is called when engine receives the "position" UCI command. |
46 | // position() is called when engine receives the "position" UCI command. |
49 | // The function sets up the position described in the given FEN string ("fen") |
47 | // The function sets up the position described in the given FEN string ("fen") |
50 | // or the starting position ("startpos") and then makes the moves given in the |
48 | // or the starting position ("startpos") and then makes the moves given in the |
51 | // following move list ("moves"). |
49 | // following move list ("moves"). |
52 | 50 | ||
53 | void position(Position& pos, istringstream& |
51 | void position(Position& pos, istringstream& is, StateListPtr& states) { |
54 | 52 | ||
55 | Move m; |
53 | Move m; |
56 | string token, fen; |
54 | string token, fen; |
57 | 55 | ||
58 | is >> token; |
56 | is >> token; |
Line 66... | Line 64... | ||
66 | while (is >> token && token != "moves") |
64 | while (is >> token && token != "moves") |
67 | fen += token + " "; |
65 | fen += token + " "; |
68 | else |
66 | else |
69 | return; |
67 | return; |
70 | 68 | ||
71 |
|
69 | states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one |
72 | pos.set(fen, Options["UCI_Chess960"], & |
70 | pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); |
73 | 71 | ||
74 | // Parse move list (if any) |
72 | // Parse move list (if any) |
75 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
73 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
76 | { |
74 | { |
77 |
|
75 | states->emplace_back(); |
78 | pos.do_move(m, |
76 | pos.do_move(m, states->back()); |
79 | } |
77 | } |
80 | } |
78 | } |
81 | 79 | ||
82 | 80 | ||
83 | // setoption() is called when engine receives the "setoption" UCI command. The |
81 | // setoption() is called when engine receives the "setoption" UCI command. The |
Line 106... | Line 104... | ||
106 | 104 | ||
107 | // go() is called when engine receives the "go" UCI command. The function sets |
105 | // go() is called when engine receives the "go" UCI command. The function sets |
108 | // the thinking time and other parameters from the input string, then starts |
106 | // the thinking time and other parameters from the input string, then starts |
109 | // the search. |
107 | // the search. |
110 | 108 | ||
111 | void go(Position& pos, istringstream& |
109 | void go(Position& pos, istringstream& is, StateListPtr& states) { |
112 | 110 | ||
113 | Search::LimitsType limits; |
111 | Search::LimitsType limits; |
114 | string token; |
112 | string token; |
- | 113 | bool ponderMode = false; |
|
115 | 114 | ||
116 | limits.startTime = now(); // As early as possible! |
115 | limits.startTime = now(); // As early as possible! |
117 | 116 | ||
118 | while (is >> token) |
117 | while (is >> token) |
119 | if (token == "searchmoves") |
118 | if (token == "searchmoves") |
Line 127... | Line 126... | ||
127 | else if (token == "movestogo") is >> limits.movestogo; |
126 | else if (token == "movestogo") is >> limits.movestogo; |
128 | else if (token == "depth") is >> limits.depth; |
127 | else if (token == "depth") is >> limits.depth; |
129 | else if (token == "nodes") is >> limits.nodes; |
128 | else if (token == "nodes") is >> limits.nodes; |
130 | else if (token == "movetime") is >> limits.movetime; |
129 | else if (token == "movetime") is >> limits.movetime; |
131 | else if (token == "mate") is >> limits.mate; |
130 | else if (token == "mate") is >> limits.mate; |
- | 131 | else if (token == "perft") is >> limits.perft; |
|
132 | else if (token == "infinite") limits.infinite = 1; |
132 | else if (token == "infinite") limits.infinite = 1; |
133 | else if (token == "ponder") |
133 | else if (token == "ponder") ponderMode = true; |
134 | 134 | ||
135 | Threads.start_thinking(pos, |
135 | Threads.start_thinking(pos, states, limits, ponderMode); |
- | 136 | } |
|
- | 137 | ||
- | 138 | ||
- | 139 | // bench() is called when engine receives the "bench" command. Firstly |
|
- | 140 | // a list of UCI commands is setup according to bench parameters, then |
|
- | 141 | // it is run one by one printing a summary at the end. |
|
- | 142 | ||
- | 143 | void bench(Position& pos, istream& args, StateListPtr& states) { |
|
- | 144 | ||
- | 145 | string token; |
|
- | 146 | uint64_t num, nodes = 0, cnt = 1; |
|
- | 147 | ||
- | 148 | vector<string> list = setup_bench(pos, args); |
|
- | 149 | num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; }); |
|
- | 150 | ||
- | 151 | TimePoint elapsed = now(); |
|
- | 152 | ||
- | 153 | for (const auto& cmd : list) |
|
- | 154 | { |
|
- | 155 | istringstream is(cmd); |
|
- | 156 | is >> skipws >> token; |
|
- | 157 | ||
- | 158 | if (token == "go") |
|
- | 159 | { |
|
- | 160 | cerr << "\nPosition: " << cnt++ << '/' << num << endl; |
|
- | 161 | go(pos, is, states); |
|
- | 162 | Threads.main()->wait_for_search_finished(); |
|
- | 163 | nodes += Threads.nodes_searched(); |
|
- | 164 | } |
|
- | 165 | else if (token == "setoption") setoption(is); |
|
- | 166 | else if (token == "position") position(pos, is, states); |
|
- | 167 | else if (token == "ucinewgame") Search::clear(); |
|
- | 168 | } |
|
- | 169 | ||
- | 170 | elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' |
|
- | 171 | ||
- | 172 | dbg_print(); // Just before exiting |
|
- | 173 | ||
- | 174 | cerr << "\n===========================" |
|
- | 175 | << "\nTotal time (ms) : " << elapsed |
|
- | 176 | << "\nNodes searched : " << nodes |
|
- | 177 | << "\nNodes/second : " << 1000 * nodes / elapsed << endl; |
|
136 | } |
178 | } |
137 | 179 | ||
138 | } // namespace |
180 | } // namespace |
139 | 181 | ||
140 | 182 | ||
Line 146... | Line 188... | ||
146 | 188 | ||
147 | void UCI::loop(int argc, char* argv[]) { |
189 | void UCI::loop(int argc, char* argv[]) { |
148 | 190 | ||
149 | Position pos; |
191 | Position pos; |
150 | string token, cmd; |
192 | string token, cmd; |
- | 193 | StateListPtr states(new std::deque<StateInfo>(1)); |
|
- | 194 | auto uiThread = std::make_shared<Thread>(0); |
|
151 | 195 | ||
152 | pos.set(StartFEN, false, & |
196 | pos.set(StartFEN, false, &states->back(), uiThread.get()); |
153 | 197 | ||
154 | for (int i = 1; i < argc; ++i) |
198 | for (int i = 1; i < argc; ++i) |
155 | cmd += std::string(argv[i]) + " "; |
199 | cmd += std::string(argv[i]) + " "; |
156 | 200 | ||
157 | do { |
201 | do { |
158 | if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF |
202 | if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF |
159 | cmd = "quit"; |
203 | cmd = "quit"; |
160 | 204 | ||
161 | istringstream is(cmd); |
205 | istringstream is(cmd); |
162 | 206 | ||
163 | token.clear(); // getline() |
207 | token.clear(); // Avoid a stale if getline() returns empty or blank line |
164 | is >> skipws >> token; |
208 | is >> skipws >> token; |
165 | 209 | ||
166 | // The GUI sends 'ponderhit' to tell us |
210 | // The GUI sends 'ponderhit' to tell us the user has played the expected move. |
167 | // |
211 | // So 'ponderhit' will be sent if we were told to ponder on the same move the |
168 | // |
212 | // user has played. We should continue searching but switch from pondering to |
169 | // |
213 | // normal search. In case Threads.stopOnPonderhit is set we are waiting for |
170 | // |
214 | // 'ponderhit' to stop the search, for instance if max search depth is reached. |
171 | if ( token == "quit" |
215 | if ( token == "quit" |
172 | || token == "stop" |
216 | || token == "stop" |
173 | || (token == "ponderhit" && |
217 | || (token == "ponderhit" && Threads.stopOnPonderhit)) |
174 | { |
- | |
175 |
|
218 | Threads.stop = true; |
176 | Threads.main()->start_searching(true); // Could be sleeping |
- | |
177 | } |
219 | |
178 | else if (token == "ponderhit") |
220 | else if (token == "ponderhit") |
179 |
|
221 | Threads.ponder = false; // Switch to normal search |
180 | 222 | ||
181 | else if (token == "uci") |
223 | else if (token == "uci") |
182 | sync_cout << "id name " << engine_info(true) |
224 | sync_cout << "id name " << engine_info(true) |
183 | << "\n" << Options |
225 | << "\n" << Options |
184 | << "\nuciok" << sync_endl; |
226 | << "\nuciok" << sync_endl; |
185 | 227 | ||
186 | else if (token == "ucinewgame") |
- | |
187 | { |
- | |
188 | Search::clear(); |
- | |
189 | Time.availableNodes = 0; |
- | |
190 | } |
- | |
191 | else if (token == "isready") sync_cout << "readyok" << sync_endl; |
- | |
192 | else if (token == "go") go(pos, is); |
- | |
193 | else if (token == "position") position(pos, is); |
- | |
194 | else if (token == "setoption") setoption(is); |
228 | else if (token == "setoption") setoption(is); |
- | 229 | else if (token == "go") go(pos, is, states); |
|
- | 230 | else if (token == "position") position(pos, is, states); |
|
- | 231 | else if (token == "ucinewgame") Search::clear(); |
|
- | 232 | else if (token == "isready") sync_cout << "readyok" << sync_endl; |
|
195 | 233 | ||
196 | // Additional custom non-UCI commands, |
234 | // Additional custom non-UCI commands, mainly for debugging |
197 | else if (token == "flip") |
235 | else if (token == "flip") pos.flip(); |
198 | else if (token == "bench") |
236 | else if (token == "bench") bench(pos, is, states); |
199 | else if (token == "d") |
237 | else if (token == "d") sync_cout << pos << sync_endl; |
200 | else if (token == "eval") |
238 | else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; |
201 | else if (token == "perft") |
- | |
202 | { |
- | |
203 | int depth; |
- | |
204 | stringstream ss; |
- | |
205 | - | ||
206 | is >> depth; |
- | |
207 | ss << Options["Hash"] << " " |
- | |
208 | << Options["Threads"] << " " << depth << " current perft"; |
- | |
209 | - | ||
210 | benchmark(pos, ss); |
- | |
211 | } |
- | |
212 | else |
239 | else |
213 | sync_cout << "Unknown command: " << cmd << sync_endl; |
240 | sync_cout << "Unknown command: " << cmd << sync_endl; |
214 | 241 | ||
215 | } while (token != "quit" && argc == 1); // |
242 | } while (token != "quit" && argc == 1); // Command line args are one-shot |
216 | - | ||
217 | Threads.main()->wait_for_search_finished(); |
- | |
218 | } |
243 | } |
219 | 244 | ||
220 | 245 | ||
221 | /// UCI::value() converts a Value to a string suitable for use with the UCI |
246 | /// UCI::value() converts a Value to a string suitable for use with the UCI |
222 | /// protocol specification: |
247 | /// protocol specification: |
Line 224... | Line 249... | ||
224 | /// cp <x> The score from the engine's point of view in centipawns. |
249 | /// cp <x> The score from the engine's point of view in centipawns. |
225 | /// mate <y> Mate in y moves, not plies. If the engine is getting mated |
250 | /// mate <y> Mate in y moves, not plies. If the engine is getting mated |
226 | /// use negative values for y. |
251 | /// use negative values for y. |
227 | 252 | ||
228 | string UCI::value(Value v) { |
253 | string UCI::value(Value v) { |
- | 254 | ||
- | 255 | assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); |
|
229 | 256 | ||
230 | stringstream ss; |
257 | stringstream ss; |
231 | 258 | ||
232 | if (abs(v) < VALUE_MATE - MAX_PLY) |
259 | if (abs(v) < VALUE_MATE - MAX_PLY) |
233 | ss << "cp " << v * 100 / PawnValueEg; |
260 | ss << "cp " << v * 100 / PawnValueEg; |