| 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 | |