Rev 169 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
96 | pmbaty | 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 |
||
185 | pmbaty | 5 | Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
96 | pmbaty | 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 | |||
169 | pmbaty | 21 | #include <cassert> |
96 | pmbaty | 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" |
||
185 | pmbaty | 31 | #include "timeman.h" |
169 | pmbaty | 32 | #include "tt.h" |
96 | pmbaty | 33 | #include "uci.h" |
169 | pmbaty | 34 | #include "syzygy/tbprobe.h" |
96 | pmbaty | 35 | |
36 | using namespace std; |
||
37 | |||
169 | pmbaty | 38 | extern vector<string> setup_bench(const Position&, istream&); |
96 | pmbaty | 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 | |||
169 | pmbaty | 51 | void position(Position& pos, istringstream& is, StateListPtr& states) { |
96 | pmbaty | 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 | |||
169 | pmbaty | 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()); |
||
96 | pmbaty | 71 | |
72 | // Parse move list (if any) |
||
73 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
||
74 | { |
||
169 | pmbaty | 75 | states->emplace_back(); |
76 | pos.do_move(m, states->back()); |
||
96 | pmbaty | 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") |
||
185 | pmbaty | 92 | name += (name.empty() ? "" : " ") + token; |
96 | pmbaty | 93 | |
94 | // Read option value (can contain spaces) |
||
95 | while (is >> token) |
||
185 | pmbaty | 96 | value += (value.empty() ? "" : " ") + token; |
96 | pmbaty | 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 | |||
169 | pmbaty | 109 | void go(Position& pos, istringstream& is, StateListPtr& states) { |
96 | pmbaty | 110 | |
111 | Search::LimitsType limits; |
||
112 | string token; |
||
169 | pmbaty | 113 | bool ponderMode = false; |
96 | pmbaty | 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; |
||
169 | pmbaty | 131 | else if (token == "perft") is >> limits.perft; |
96 | pmbaty | 132 | else if (token == "infinite") limits.infinite = 1; |
169 | pmbaty | 133 | else if (token == "ponder") ponderMode = true; |
96 | pmbaty | 134 | |
169 | pmbaty | 135 | Threads.start_thinking(pos, states, limits, ponderMode); |
96 | pmbaty | 136 | } |
137 | |||
169 | pmbaty | 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; |
||
178 | } |
||
179 | |||
96 | pmbaty | 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 | |||
154 | pmbaty | 191 | Position pos; |
96 | pmbaty | 192 | string token, cmd; |
169 | pmbaty | 193 | StateListPtr states(new std::deque<StateInfo>(1)); |
194 | auto uiThread = std::make_shared<Thread>(0); |
||
96 | pmbaty | 195 | |
169 | pmbaty | 196 | pos.set(StartFEN, false, &states->back(), uiThread.get()); |
154 | pmbaty | 197 | |
96 | pmbaty | 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 | |||
169 | pmbaty | 207 | token.clear(); // Avoid a stale if getline() returns empty or blank line |
96 | pmbaty | 208 | is >> skipws >> token; |
209 | |||
169 | pmbaty | 210 | // The GUI sends 'ponderhit' to tell us the user has played the expected move. |
211 | // So 'ponderhit' will be sent if we were told to ponder on the same move the |
||
212 | // user has played. We should continue searching but switch from pondering to |
||
213 | // normal search. In case Threads.stopOnPonderhit is set we are waiting for |
||
214 | // 'ponderhit' to stop the search, for instance if max search depth is reached. |
||
96 | pmbaty | 215 | if ( token == "quit" |
216 | || token == "stop" |
||
169 | pmbaty | 217 | || (token == "ponderhit" && Threads.stopOnPonderhit)) |
218 | Threads.stop = true; |
||
219 | |||
96 | pmbaty | 220 | else if (token == "ponderhit") |
169 | pmbaty | 221 | Threads.ponder = false; // Switch to normal search |
96 | pmbaty | 222 | |
223 | else if (token == "uci") |
||
224 | sync_cout << "id name " << engine_info(true) |
||
225 | << "\n" << Options |
||
226 | << "\nuciok" << sync_endl; |
||
227 | |||
169 | pmbaty | 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(); |
||
96 | pmbaty | 232 | else if (token == "isready") sync_cout << "readyok" << sync_endl; |
233 | |||
169 | pmbaty | 234 | // Additional custom non-UCI commands, mainly for debugging |
235 | else if (token == "flip") pos.flip(); |
||
236 | else if (token == "bench") bench(pos, is, states); |
||
237 | else if (token == "d") sync_cout << pos << sync_endl; |
||
238 | else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; |
||
96 | pmbaty | 239 | else |
240 | sync_cout << "Unknown command: " << cmd << sync_endl; |
||
241 | |||
169 | pmbaty | 242 | } while (token != "quit" && argc == 1); // Command line args are one-shot |
96 | pmbaty | 243 | } |
244 | |||
245 | |||
246 | /// UCI::value() converts a Value to a string suitable for use with the UCI |
||
247 | /// protocol specification: |
||
248 | /// |
||
249 | /// cp <x> The score from the engine's point of view in centipawns. |
||
250 | /// mate <y> Mate in y moves, not plies. If the engine is getting mated |
||
251 | /// use negative values for y. |
||
252 | |||
253 | string UCI::value(Value v) { |
||
254 | |||
169 | pmbaty | 255 | assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); |
256 | |||
96 | pmbaty | 257 | stringstream ss; |
258 | |||
259 | if (abs(v) < VALUE_MATE - MAX_PLY) |
||
260 | ss << "cp " << v * 100 / PawnValueEg; |
||
261 | else |
||
262 | ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; |
||
263 | |||
264 | return ss.str(); |
||
265 | } |
||
266 | |||
267 | |||
268 | /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) |
||
269 | |||
270 | std::string UCI::square(Square s) { |
||
271 | return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; |
||
272 | } |
||
273 | |||
274 | |||
275 | /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). |
||
276 | /// The only special case is castling, where we print in the e1g1 notation in |
||
277 | /// normal chess mode, and in e1h1 notation in chess960 mode. Internally all |
||
278 | /// castling moves are always encoded as 'king captures rook'. |
||
279 | |||
280 | string UCI::move(Move m, bool chess960) { |
||
281 | |||
282 | Square from = from_sq(m); |
||
283 | Square to = to_sq(m); |
||
284 | |||
285 | if (m == MOVE_NONE) |
||
286 | return "(none)"; |
||
287 | |||
288 | if (m == MOVE_NULL) |
||
289 | return "0000"; |
||
290 | |||
291 | if (type_of(m) == CASTLING && !chess960) |
||
292 | to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); |
||
293 | |||
294 | string move = UCI::square(from) + UCI::square(to); |
||
295 | |||
296 | if (type_of(m) == PROMOTION) |
||
297 | move += " pnbrqk"[promotion_type(m)]; |
||
298 | |||
299 | return move; |
||
300 | } |
||
301 | |||
302 | |||
303 | /// UCI::to_move() converts a string representing a move in coordinate notation |
||
304 | /// (g1f3, a7a8q) to the corresponding legal Move, if any. |
||
305 | |||
306 | Move UCI::to_move(const Position& pos, string& str) { |
||
307 | |||
308 | if (str.length() == 5) // Junior could send promotion piece in uppercase |
||
309 | str[4] = char(tolower(str[4])); |
||
310 | |||
311 | for (const auto& m : MoveList<LEGAL>(pos)) |
||
312 | if (str == UCI::move(m, pos.is_chess960())) |
||
313 | return m; |
||
314 | |||
315 | return MOVE_NONE; |
||
316 | } |