Rev 96 | Rev 169 | Go to most recent revision | 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 |
||
| 5 | Copyright (C) 2015-2016 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 <iostream> |
||
| 22 | #include <sstream> |
||
| 23 | #include <string> |
||
| 24 | |||
| 25 | #include "evaluate.h" |
||
| 26 | #include "movegen.h" |
||
| 27 | #include "position.h" |
||
| 28 | #include "search.h" |
||
| 29 | #include "thread.h" |
||
| 30 | #include "timeman.h" |
||
| 31 | #include "uci.h" |
||
| 32 | |||
| 33 | using namespace std; |
||
| 34 | |||
| 35 | extern void benchmark(const Position& pos, istream& is); |
||
| 36 | |||
| 37 | namespace { |
||
| 38 | |||
| 39 | // FEN string of the initial position, normal chess |
||
| 40 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; |
||
| 41 | |||
| 154 | pmbaty | 42 | // A list to keep track of the position states along the setup moves (from the |
| 96 | pmbaty | 43 | // start position to the position just before the search starts). Needed by |
| 44 | // 'draw by repetition' detection. |
||
| 154 | pmbaty | 45 | StateListPtr States(new std::deque<StateInfo>(1)); |
| 96 | pmbaty | 46 | |
| 47 | |||
| 48 | // position() is called when engine receives the "position" UCI command. |
||
| 49 | // 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 |
||
| 51 | // following move list ("moves"). |
||
| 52 | |||
| 53 | void position(Position& pos, istringstream& is) { |
||
| 54 | |||
| 55 | Move m; |
||
| 56 | string token, fen; |
||
| 57 | |||
| 58 | is >> token; |
||
| 59 | |||
| 60 | if (token == "startpos") |
||
| 61 | { |
||
| 62 | fen = StartFEN; |
||
| 63 | is >> token; // Consume "moves" token if any |
||
| 64 | } |
||
| 65 | else if (token == "fen") |
||
| 66 | while (is >> token && token != "moves") |
||
| 67 | fen += token + " "; |
||
| 68 | else |
||
| 69 | return; |
||
| 70 | |||
| 154 | pmbaty | 71 | States = StateListPtr(new std::deque<StateInfo>(1)); |
| 72 | pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main()); |
||
| 96 | pmbaty | 73 | |
| 74 | // Parse move list (if any) |
||
| 75 | while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) |
||
| 76 | { |
||
| 154 | pmbaty | 77 | States->push_back(StateInfo()); |
| 78 | pos.do_move(m, States->back(), pos.gives_check(m)); |
||
| 96 | pmbaty | 79 | } |
| 80 | } |
||
| 81 | |||
| 82 | |||
| 83 | // setoption() is called when engine receives the "setoption" UCI command. The |
||
| 84 | // function updates the UCI option ("name") to the given value ("value"). |
||
| 85 | |||
| 86 | void setoption(istringstream& is) { |
||
| 87 | |||
| 88 | string token, name, value; |
||
| 89 | |||
| 90 | is >> token; // Consume "name" token |
||
| 91 | |||
| 92 | // Read option name (can contain spaces) |
||
| 93 | while (is >> token && token != "value") |
||
| 94 | name += string(" ", name.empty() ? 0 : 1) + token; |
||
| 95 | |||
| 96 | // Read option value (can contain spaces) |
||
| 97 | while (is >> token) |
||
| 98 | value += string(" ", value.empty() ? 0 : 1) + token; |
||
| 99 | |||
| 100 | if (Options.count(name)) |
||
| 101 | Options[name] = value; |
||
| 102 | else |
||
| 103 | sync_cout << "No such option: " << name << sync_endl; |
||
| 104 | } |
||
| 105 | |||
| 106 | |||
| 107 | // 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 |
||
| 109 | // the search. |
||
| 110 | |||
| 154 | pmbaty | 111 | void go(Position& pos, istringstream& is) { |
| 96 | pmbaty | 112 | |
| 113 | Search::LimitsType limits; |
||
| 114 | string token; |
||
| 115 | |||
| 116 | limits.startTime = now(); // As early as possible! |
||
| 117 | |||
| 118 | while (is >> token) |
||
| 119 | if (token == "searchmoves") |
||
| 120 | while (is >> token) |
||
| 121 | limits.searchmoves.push_back(UCI::to_move(pos, token)); |
||
| 122 | |||
| 123 | else if (token == "wtime") is >> limits.time[WHITE]; |
||
| 124 | else if (token == "btime") is >> limits.time[BLACK]; |
||
| 125 | else if (token == "winc") is >> limits.inc[WHITE]; |
||
| 126 | else if (token == "binc") is >> limits.inc[BLACK]; |
||
| 127 | else if (token == "movestogo") is >> limits.movestogo; |
||
| 128 | else if (token == "depth") is >> limits.depth; |
||
| 129 | else if (token == "nodes") is >> limits.nodes; |
||
| 130 | else if (token == "movetime") is >> limits.movetime; |
||
| 131 | else if (token == "mate") is >> limits.mate; |
||
| 132 | else if (token == "infinite") limits.infinite = 1; |
||
| 133 | else if (token == "ponder") limits.ponder = 1; |
||
| 134 | |||
| 154 | pmbaty | 135 | Threads.start_thinking(pos, States, limits); |
| 96 | pmbaty | 136 | } |
| 137 | |||
| 138 | } // namespace |
||
| 139 | |||
| 140 | |||
| 141 | /// UCI::loop() waits for a command from stdin, parses it and calls the appropriate |
||
| 142 | /// function. Also intercepts EOF from stdin to ensure gracefully exiting if the |
||
| 143 | /// GUI dies unexpectedly. When called with some command line arguments, e.g. to |
||
| 144 | /// run 'bench', once the command is executed the function returns immediately. |
||
| 145 | /// In addition to the UCI ones, also some additional debug commands are supported. |
||
| 146 | |||
| 147 | void UCI::loop(int argc, char* argv[]) { |
||
| 148 | |||
| 154 | pmbaty | 149 | Position pos; |
| 96 | pmbaty | 150 | string token, cmd; |
| 151 | |||
| 154 | pmbaty | 152 | pos.set(StartFEN, false, &States->back(), Threads.main()); |
| 153 | |||
| 96 | pmbaty | 154 | for (int i = 1; i < argc; ++i) |
| 155 | cmd += std::string(argv[i]) + " "; |
||
| 156 | |||
| 157 | do { |
||
| 158 | if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF |
||
| 159 | cmd = "quit"; |
||
| 160 | |||
| 161 | istringstream is(cmd); |
||
| 162 | |||
| 163 | token.clear(); // getline() could return empty or blank line |
||
| 164 | is >> skipws >> token; |
||
| 165 | |||
| 166 | // The GUI sends 'ponderhit' to tell us to ponder on the same move the |
||
| 167 | // opponent has played. In case Signals.stopOnPonderhit is set we are |
||
| 168 | // waiting for 'ponderhit' to stop the search (for instance because we |
||
| 169 | // already ran out of time), otherwise we should continue searching but |
||
| 170 | // switching from pondering to normal search. |
||
| 171 | if ( token == "quit" |
||
| 172 | || token == "stop" |
||
| 173 | || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) |
||
| 174 | { |
||
| 175 | Search::Signals.stop = true; |
||
| 176 | Threads.main()->start_searching(true); // Could be sleeping |
||
| 177 | } |
||
| 178 | else if (token == "ponderhit") |
||
| 179 | Search::Limits.ponder = 0; // Switch to normal search |
||
| 180 | |||
| 181 | else if (token == "uci") |
||
| 182 | sync_cout << "id name " << engine_info(true) |
||
| 183 | << "\n" << Options |
||
| 184 | << "\nuciok" << sync_endl; |
||
| 185 | |||
| 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); |
||
| 195 | |||
| 196 | // Additional custom non-UCI commands, useful for debugging |
||
| 197 | else if (token == "flip") pos.flip(); |
||
| 198 | else if (token == "bench") benchmark(pos, is); |
||
| 199 | else if (token == "d") sync_cout << pos << sync_endl; |
||
| 200 | 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 |
||
| 213 | sync_cout << "Unknown command: " << cmd << sync_endl; |
||
| 214 | |||
| 215 | } while (token != "quit" && argc == 1); // Passed args have one-shot behaviour |
||
| 216 | |||
| 217 | Threads.main()->wait_for_search_finished(); |
||
| 218 | } |
||
| 219 | |||
| 220 | |||
| 221 | /// UCI::value() converts a Value to a string suitable for use with the UCI |
||
| 222 | /// protocol specification: |
||
| 223 | /// |
||
| 224 | /// 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 |
||
| 226 | /// use negative values for y. |
||
| 227 | |||
| 228 | string UCI::value(Value v) { |
||
| 229 | |||
| 230 | stringstream ss; |
||
| 231 | |||
| 232 | if (abs(v) < VALUE_MATE - MAX_PLY) |
||
| 233 | ss << "cp " << v * 100 / PawnValueEg; |
||
| 234 | else |
||
| 235 | ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; |
||
| 236 | |||
| 237 | return ss.str(); |
||
| 238 | } |
||
| 239 | |||
| 240 | |||
| 241 | /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) |
||
| 242 | |||
| 243 | std::string UCI::square(Square s) { |
||
| 244 | return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; |
||
| 245 | } |
||
| 246 | |||
| 247 | |||
| 248 | /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). |
||
| 249 | /// The only special case is castling, where we print in the e1g1 notation in |
||
| 250 | /// normal chess mode, and in e1h1 notation in chess960 mode. Internally all |
||
| 251 | /// castling moves are always encoded as 'king captures rook'. |
||
| 252 | |||
| 253 | string UCI::move(Move m, bool chess960) { |
||
| 254 | |||
| 255 | Square from = from_sq(m); |
||
| 256 | Square to = to_sq(m); |
||
| 257 | |||
| 258 | if (m == MOVE_NONE) |
||
| 259 | return "(none)"; |
||
| 260 | |||
| 261 | if (m == MOVE_NULL) |
||
| 262 | return "0000"; |
||
| 263 | |||
| 264 | if (type_of(m) == CASTLING && !chess960) |
||
| 265 | to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); |
||
| 266 | |||
| 267 | string move = UCI::square(from) + UCI::square(to); |
||
| 268 | |||
| 269 | if (type_of(m) == PROMOTION) |
||
| 270 | move += " pnbrqk"[promotion_type(m)]; |
||
| 271 | |||
| 272 | return move; |
||
| 273 | } |
||
| 274 | |||
| 275 | |||
| 276 | /// UCI::to_move() converts a string representing a move in coordinate notation |
||
| 277 | /// (g1f3, a7a8q) to the corresponding legal Move, if any. |
||
| 278 | |||
| 279 | Move UCI::to_move(const Position& pos, string& str) { |
||
| 280 | |||
| 281 | if (str.length() == 5) // Junior could send promotion piece in uppercase |
||
| 282 | str[4] = char(tolower(str[4])); |
||
| 283 | |||
| 284 | for (const auto& m : MoveList<LEGAL>(pos)) |
||
| 285 | if (str == UCI::move(m, pos.is_chess960())) |
||
| 286 | return m; |
||
| 287 | |||
| 288 | return MOVE_NONE; |
||
| 289 | } |