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