1 | /* |
2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 |
3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) |
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
5 | Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
6 | |
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 |
9 | the Free Software Foundation, either version 3 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | Stockfish is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
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/>. |
19 | */ |
20 | |
21 | #include <cassert> |
22 | #include <iostream> |
23 | #include <sstream> |
24 | #include <string> |
25 | |
26 | #include "evaluate.h" |
27 | #include "movegen.h" |
28 | #include "position.h" |
29 | #include "search.h" |
30 | #include "thread.h" |
31 | #include "timeman.h" |
32 | #include "tt.h" |
33 | #include "uci.h" |
34 | #include "syzygy/tbprobe.h" |
35 | |
36 | using namespace std; |
37 | |
38 | extern vector<string> setup_bench(const Position&, istream&); |
39 | |
40 | namespace { |
41 | |
42 | // FEN string of the initial position, normal chess |
43 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" ; |
44 | |
45 | |
46 | // position() is called when engine receives the "position" UCI command. |
47 | // The function sets up the position described in the given FEN string ("fen") |
48 | // or the starting position ("startpos") and then makes the moves given in the |
49 | // following move list ("moves"). |
50 | |
51 | void position(Position& pos, istringstream& is, StateListPtr& states) { |
52 | |
53 | Move m; |
54 | string token, fen; |
55 | |
56 | is >> token; |
57 | |
58 | if (token == "startpos" ) |
59 | { |
60 | fen = StartFEN; |
61 | is >> token; // Consume "moves" token if any |
62 | } |
63 | else if (token == "fen" ) |
64 | while (is >> token && token != "moves" ) |
65 | fen += token + " " ; |
66 | else |
67 | return; |
68 | |
69 | states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one |
70 | pos.set(fen, Options["UCI_Chess960" ], &states->back(), Threads.main()); |
71 | |
72 | // Parse move list (if any) |
73 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
74 | { |
75 | states->emplace_back(); |
76 | pos.do_move(m, states->back()); |
77 | } |
78 | } |
79 | |
80 | |
81 | // setoption() is called when engine receives the "setoption" UCI command. The |
82 | // function updates the UCI option ("name") to the given value ("value"). |
83 | |
84 | void setoption(istringstream& is) { |
85 | |
86 | string token, name, value; |
87 | |
88 | is >> token; // Consume "name" token |
89 | |
90 | // Read option name (can contain spaces) |
91 | while (is >> token && token != "value" ) |
92 | name += (name.empty() ? "" : " " ) + token; |
93 | |
94 | // Read option value (can contain spaces) |
95 | while (is >> token) |
96 | value += (value.empty() ? "" : " " ) + token; |
97 | |
98 | if (Options.count(name)) |
99 | Options[name] = value; |
100 | else |
101 | sync_cout << "No such option: " << name << sync_endl; |
102 | } |
103 | |
104 | |
105 | // go() is called when engine receives the "go" UCI command. The function sets |
106 | // the thinking time and other parameters from the input string, then starts |
107 | // the search. |
108 | |
109 | void go(Position& pos, istringstream& is, StateListPtr& states) { |
110 | |
111 | Search::LimitsType limits; |
112 | string token; |
113 | bool ponderMode = false; |
114 | |
115 | limits.startTime = now(); // As early as possible! |
116 | |
117 | while (is >> token) |
118 | if (token == "searchmoves" ) |
119 | while (is >> token) |
120 | limits.searchmoves.push_back(UCI::to_move(pos, token)); |
121 | |
122 | else if (token == "wtime" ) is >> limits.time[WHITE]; |
123 | else if (token == "btime" ) is >> limits.time[BLACK]; |
124 | else if (token == "winc" ) is >> limits.inc[WHITE]; |
125 | else if (token == "binc" ) is >> limits.inc[BLACK]; |
126 | else if (token == "movestogo" ) is >> limits.movestogo; |
127 | else if (token == "depth" ) is >> limits.depth; |
128 | else if (token == "nodes" ) is >> limits.nodes; |
129 | else if (token == "movetime" ) is >> limits.movetime; |
130 | else if (token == "mate" ) is >> limits.mate; |
131 | else if (token == "perft" ) is >> limits.perft; |
132 | else if (token == "infinite" ) limits.infinite = 1; |
133 | else if (token == "ponder" ) ponderMode = true; |
134 | |
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(); elapsed = now(); } // Search::clear() may take some while |
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; |
178 | } |
179 | |
180 | } // namespace |
181 | |
182 | |
183 | /// UCI::loop() waits for a command from stdin, parses it and calls the appropriate |
184 | /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the |
185 | /// GUI dies unexpectedly. When called with some command line arguments, e.g. to |
186 | /// run 'bench', once the command is executed the function returns immediately. |
187 | /// In addition to the UCI ones, also some additional debug commands are supported. |
188 | |
189 | void UCI::loop(int argc, char* argv[]) { |
190 | |
191 | Position pos; |
192 | string token, cmd; |
193 | StateListPtr states(new std::deque<StateInfo>(1)); |
194 | auto uiThread = std::make_shared<Thread>(0); |
195 | |
196 | pos.set(StartFEN, false, &states->back(), uiThread.get()); |
197 | |
198 | for (int i = 1; i < argc; ++i) |
199 | cmd += std::string(argv[i]) + " " ; |
200 | |
201 | do { |
202 | if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF |
203 | cmd = "quit" ; |
204 | |
205 | istringstream is(cmd); |
206 | |
207 | token.clear(); // Avoid a stale if getline() returns empty or blank line |
208 | is >> skipws >> token; |
209 | |
210 | if ( token == "quit" |
211 | || token == "stop" ) |
212 | Threads.stop = true; |
213 | |
214 | // The GUI sends 'ponderhit' to tell us the user has played the expected move. |
215 | // So 'ponderhit' will be sent if we were told to ponder on the same move the |
216 | // user has played. We should continue searching but switch from pondering to |
217 | // normal search. |
218 | else if (token == "ponderhit" ) |
219 | Threads.main()->ponder = false; // Switch to normal search |
220 | |
221 | else if (token == "uci" ) |
222 | sync_cout << "id name " << engine_info(true) |
223 | << "\n" << Options |
224 | << "\nuciok" << sync_endl; |
225 | |
226 | else if (token == "setoption" ) setoption(is); |
227 | else if (token == "go" ) go(pos, is, states); |
228 | else if (token == "position" ) position(pos, is, states); |
229 | else if (token == "ucinewgame" ) Search::clear(); |
230 | else if (token == "isready" ) sync_cout << "readyok" << sync_endl; |
231 | |
232 | // Additional custom non-UCI commands, mainly for debugging |
233 | else if (token == "flip" ) pos.flip(); |
234 | else if (token == "bench" ) bench(pos, is, states); |
235 | else if (token == "d" ) sync_cout << pos << sync_endl; |
236 | else if (token == "eval" ) sync_cout << Eval::trace(pos) << sync_endl; |
237 | else |
238 | sync_cout << "Unknown command: " << cmd << sync_endl; |
239 | |
240 | } while (token != "quit" && argc == 1); // Command line args are one-shot |
241 | } |
242 | |
243 | |
244 | /// UCI::value() converts a Value to a string suitable for use with the UCI |
245 | /// protocol specification: |
246 | /// |
247 | /// cp <x> The score from the engine's point of view in centipawns. |
248 | /// mate <y> Mate in y moves, not plies. If the engine is getting mated |
249 | /// use negative values for y. |
250 | |
251 | string UCI::value(Value v) { |
252 | |
253 | assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); |
254 | |
255 | stringstream ss; |
256 | |
257 | if (abs(v) < VALUE_MATE - MAX_PLY) |
258 | ss << "cp " << v * 100 / PawnValueEg; |
259 | else |
260 | ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; |
261 | |
262 | return ss.str(); |
263 | } |
264 | |
265 | |
266 | /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) |
267 | |
268 | std::string UCI::square(Square s) { |
269 | return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; |
270 | } |
271 | |
272 | |
273 | /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). |
274 | /// The only special case is castling, where we print in the e1g1 notation in |
275 | /// normal chess mode, and in e1h1 notation in chess960 mode. Internally all |
276 | /// castling moves are always encoded as 'king captures rook'. |
277 | |
278 | string UCI::move(Move m, bool chess960) { |
279 | |
280 | Square from = from_sq(m); |
281 | Square to = to_sq(m); |
282 | |
283 | if (m == MOVE_NONE) |
284 | return "(none)" ; |
285 | |
286 | if (m == MOVE_NULL) |
287 | return "0000" ; |
288 | |
289 | if (type_of(m) == CASTLING && !chess960) |
290 | to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); |
291 | |
292 | string move = UCI::square(from) + UCI::square(to); |
293 | |
294 | if (type_of(m) == PROMOTION) |
295 | move += " pnbrqk" [promotion_type(m)]; |
296 | |
297 | return move; |
298 | } |
299 | |
300 | |
301 | /// UCI::to_move() converts a string representing a move in coordinate notation |
302 | /// (g1f3, a7a8q) to the corresponding legal Move, if any. |
303 | |
304 | Move UCI::(const Position& pos, string& str) { |
305 | |
306 | if (str.length() == 5) // Junior could send promotion piece in uppercase |
307 | str[4] = char(tolower(str[4])); |
308 | |
309 | for (const auto& m : MoveList<LEGAL>(pos)) |
310 | if (str == UCI::move(m, pos.is_chess960())) |
311 | return m; |
312 | |
313 | return MOVE_NONE; |
314 | } |
315 | |