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