Line data Source code
1 : #include "uci.hpp"
2 :
3 : #include "cli.hpp"
4 : #include "com.hpp"
5 : #include "dynamicConfig.hpp"
6 : #include "logging.hpp"
7 : #include "opponent.hpp"
8 : #include "option.hpp"
9 : #include "position.hpp"
10 : #include "searcher.hpp"
11 : #include "timeMan.hpp"
12 : #include "tools.hpp"
13 :
14 : namespace UCI {
15 :
16 : bool iterate;
17 :
18 1 : void init() {
19 1 : Logging::ct = Logging::CT_uci;
20 1 : Logging::LogIt(Logging::logInfo) << "Init uci";
21 1 : COM::init(COM::p_uci);
22 1 : }
23 :
24 1 : void uci() {
25 1 : iterate = true;
26 46 : while (iterate) {
27 45 : COM::readLine();
28 45 : if(COM::command.empty()){
29 0 : Logging::LogIt(Logging::logWarn) << "Empty command";
30 : continue;
31 : }
32 : std::lock_guard lock(COM::mutexGUI); // cannot treat GUI bestmove while receiving new position
33 45 : processCommand(COM::command);
34 : }
35 1 : Logging::LogIt(Logging::logInfo) << "Leaving UCI loop";
36 :
37 : // write last game
38 1 : if (DynamicConfig::pgnOut){
39 0 : std::ofstream os("games_" + std::to_string(GETPID()) + "_" + std::to_string(0) + ".pgn", std::ofstream::app);
40 0 : COM::GetGameInfo().write(os);
41 0 : os.close();
42 0 : }
43 1 : }
44 :
45 45 : void processCommand(const std::string & command) {
46 : ///@todo protect some stuff when search is running !
47 : std::string uciCommand;
48 45 : std::istringstream iss(command);
49 45 : iss >> uciCommand;
50 45 : Logging::LogIt(Logging::logInfo) << "uci received command " << uciCommand;
51 :
52 45 : if (uciCommand == "uci") {
53 : std::string version = MinicVersion;
54 : if (Distributed::moreThanOneProcess()) version += "_" + std::to_string(Distributed::worldSize) + "proc";
55 1 : Logging::LogIt(Logging::logGUI) << "id name Minic " << version;
56 1 : Logging::LogIt(Logging::logGUI) << "id author Vivien Clauzon";
57 1 : Options::displayOptionsUCI();
58 1 : Logging::LogIt(Logging::logGUI) << "uciok";
59 : }
60 44 : else if (uciCommand == "setoption") {
61 : std::string tmpstr;
62 : std::string key;
63 : std::string val;
64 10 : iss >> tmpstr; //"name"
65 10 : iss >> key;
66 10 : iss >> tmpstr; //"value"
67 10 : getline(iss, val); // get all the remaining stuff on the line
68 : //iss >> val;
69 10 : val = trim(val);
70 10 : if (!Options::SetValue(key, val)) Logging::LogIt(Logging::logError) << "Unable to set value " << key << " = " << val;
71 : }
72 34 : else if (uciCommand == "isready") {
73 : // callback that are not triggered immediatly when the option is received.
74 3 : Opponent::init(); // only implemented for UCI
75 :
76 3 : Logging::LogIt(Logging::logGUI) << "readyok";
77 : }
78 31 : else if (uciCommand == "debug") {
79 : std::string str;
80 1 : iss >> str;
81 1 : if (str == "on") DynamicConfig::minOutputLevel = Logging::logInfo;
82 : else
83 0 : DynamicConfig::minOutputLevel = Logging::logGUI;
84 : }
85 30 : else if (uciCommand == "stop") {
86 2 : Logging::LogIt(Logging::logInfo) << "stop requested";
87 2 : COM::stop();
88 : }
89 28 : else if (uciCommand == "position") {
90 6 : const auto startTimePos = Clock::now();
91 6 : COM::position.h = nullHash; // invalidate position
92 : std::string type;
93 :
94 13 : while (iss >> type) {
95 7 : if (type == "startpos") {
96 4 : readFEN(std::string(startPosition), COM::position, false, true);
97 : // let's start by resetting history
98 4 : COM::GetGameInfo().clear(COM::position);
99 : }
100 3 : else if (type == "fen") {
101 : std::string fen;
102 14 : for (int i = 0; i < 6; i++) { // suppose always full fen ... ///@todo better?
103 : std::string component;
104 12 : iss >> component;
105 12 : fen += component + " ";
106 : }
107 2 : if (!readFEN(fen, COM::position, false, true)) Logging::LogIt(Logging::logFatal) << "Illegal FEN " << fen;
108 : // let's start by resetting history
109 2 : COM::GetGameInfo().clear(COM::position);
110 : }
111 1 : else if (type == "moves") {
112 1 : if (COM::position.h != nullHash) {
113 : std::string mstr;
114 5 : while (iss >> mstr) {
115 4 : const Move m = COM::moveFromCOM(mstr);
116 8 : Logging::LogIt(Logging::logInfo) << "UCI applying move " << ToString(m);
117 8 : if (!COM::makeMove(m, false, "")) { // make move
118 0 : Logging::LogIt(Logging::logInfo) << "Bad move ! : " << mstr;
119 0 : Logging::LogIt(Logging::logInfo) << ToString(COM::position);
120 : }
121 : else {
122 : // in UCI mode, we expect current position to always being sent
123 : // using an initial position and a serie of moves
124 : // Here, we append all game moves again
125 8 : COM::GetGameInfo().append({COM::position, m});
126 : }
127 : }
128 : }
129 : else
130 0 : Logging::LogIt(Logging::logGUI) << "info string no start position specified";
131 : }
132 : }
133 : // we take into account the time needed to parse input and get current position
134 6 : TimeMan::overHead = getTimeDiff(startTimePos);
135 : }
136 22 : else if (uciCommand == "go") {
137 9 : if (ThreadPool::instance().main().searching()) {
138 4 : Logging::LogIt(Logging::logGUI) << "info string go command received, but search already in progress";
139 : }
140 : else {
141 5 : if (COM::position.h != nullHash) {
142 : ///@todo MoveList root_moves;
143 :
144 5 : TimeMan::isDynamic = false;
145 5 : TimeMan::nbMoveInTC = -1;
146 5 : TimeMan::msecPerMove = -1;
147 5 : TimeMan::msecInTC = -1;
148 5 : TimeMan::msecInc = -1;
149 5 : TimeMan::msecUntilNextTC = -1;
150 5 : TimeMan::moveToGo = -1;
151 5 : COM::depth = MAX_DEPTH; // infinity
152 :
153 5 : DynamicConfig::mateFinder = false;
154 : COM::State state = COM::st_searching; //uci is/should be stateless but we use this anyhow
155 :
156 : std::string param;
157 : bool noParam = true;
158 10 : while (iss >> param) {
159 5 : Logging::LogIt(Logging::logInfo) << "received parameter " << param;
160 5 : if (param == "infinite") {
161 : noParam = false;
162 1 : TimeMan::msecPerMove = INFINITETIME;
163 : state = COM::st_analyzing;
164 : }
165 4 : else if (param == "depth") {
166 : noParam = false;
167 2 : int d = 0;
168 2 : iss >> d;
169 2 : COM::depth = clampDepth(d);
170 2 : TimeMan::msecPerMove = INFINITETIME;
171 : }
172 2 : else if (param == "movetime") {
173 : noParam = false;
174 : iss >> TimeMan::msecPerMove;
175 : }
176 1 : else if (param == "nodes") {
177 : noParam = false;
178 : iss >> TimeMan::maxNodes;
179 : }
180 1 : else if (param == "searchmoves") { Logging::LogIt(Logging::logGUI) << "info string " << param << " not implemented yet"; }
181 1 : else if (param == "wtime") {
182 : noParam = false;
183 : int t;
184 0 : iss >> t;
185 0 : if (COM::position.c == Co_White) {
186 0 : TimeMan::msecUntilNextTC = t;
187 0 : TimeMan::isDynamic = true;
188 : }
189 : }
190 1 : else if (param == "btime") {
191 : noParam = false;
192 : int t;
193 0 : iss >> t;
194 0 : if (COM::position.c == Co_Black) {
195 0 : TimeMan::msecUntilNextTC = t;
196 0 : TimeMan::isDynamic = true;
197 : }
198 : }
199 1 : else if (param == "winc") {
200 : int t;
201 0 : iss >> t;
202 0 : if (COM::position.c == Co_White) {
203 0 : TimeMan::msecInc = t;
204 0 : TimeMan::isDynamic = true;
205 : }
206 : }
207 1 : else if (param == "binc") {
208 : int t;
209 0 : iss >> t;
210 0 : if (COM::position.c == Co_Black) {
211 0 : TimeMan::msecInc = t;
212 0 : TimeMan::isDynamic = true;
213 : }
214 : }
215 1 : else if (param == "movestogo") {
216 : noParam = false;
217 : int t;
218 0 : iss >> t;
219 0 : TimeMan::moveToGo = t;
220 0 : TimeMan::isDynamic = true;
221 : }
222 1 : else if (param == "ponder") { state = COM::st_pondering; }
223 0 : else if (param == "mate") {
224 0 : int d = 0;
225 0 : iss >> d;
226 0 : COM::depth = clampDepth(d);
227 0 : DynamicConfig::mateFinder = true;
228 0 : TimeMan::msecPerMove = INFINITETIME;
229 : }
230 0 : else { Logging::LogIt(Logging::logGUI) << "info string " << param << " not implemented"; }
231 : }
232 5 : if (noParam) {
233 1 : Logging::LogIt(Logging::logWarn) << "no parameters given for go command, going for a depth 10 search ...";
234 1 : COM::depth = 10;
235 1 : TimeMan::msecPerMove = INFINITETIME;
236 : }
237 5 : Logging::LogIt(Logging::logInfo) << "uci search launched";
238 5 : COM::thinkAsync(state);
239 5 : Logging::LogIt(Logging::logInfo) << "uci async started";
240 : }
241 0 : else { Logging::LogIt(Logging::logGUI) << "info string search command received, but no position specified"; }
242 : }
243 : }
244 13 : else if (uciCommand == "ponderhit") {
245 1 : Logging::LogIt(Logging::logInfo) << "received command ponderhit";
246 1 : COM::state = COM::st_searching; // will allow move reporting to GUI
247 1 : ThreadPool::instance().main().getData().isPondering = false; // will unlock busy loop
248 : }
249 12 : else if (uciCommand == "ucinewgame") {
250 2 : if (ThreadPool::instance().main().searching()) {
251 1 : Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " received but search in progress ...";
252 : }
253 : else {
254 1 : COM::init(COM::p_uci);
255 : }
256 : }
257 10 : else if (uciCommand == "eval") {
258 0 : Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " not implemented yet";
259 : }
260 10 : else if (uciCommand == "tbprobe") {
261 : std::string type;
262 0 : iss >> type;
263 0 : Logging::LogIt(Logging::logGUI) << "info string " << uciCommand << " not implemented yet";
264 : }
265 10 : else if (uciCommand == "print") { Logging::LogIt(Logging::logInfo) << ToString(COM::position); }
266 10 : else if (uciCommand == "d") { Logging::LogIt(Logging::logInfo) << GetFEN(COM::position); }
267 10 : else if (uciCommand == "quit") {
268 1 : COM::stop();
269 1 : iterate = false;
270 : }
271 9 : else if (uciCommand == "wait") { // only used for testing purpose
272 : using namespace std::chrono_literals;
273 9 : Logging::LogIt(Logging::logInfo) << "Start to wait";
274 13 : while (!ThreadPool::instance().main().stopFlag) {
275 4 : std::this_thread::sleep_for(10ms);
276 : }
277 9 : std::this_thread::sleep_for(200ms);
278 9 : Logging::LogIt(Logging::logInfo) << "End of wait";
279 : }
280 0 : else if (uciCommand == "bench") {
281 0 : int bd = 10;
282 0 : iss >> bd;
283 0 : bench(clampDepth(bd));
284 : }
285 0 : else if (uciCommand == "benchBig") {
286 0 : int bd = 10;
287 0 : iss >> bd;
288 0 : benchBig(clampDepth(bd));
289 : }
290 0 : else if (!uciCommand.empty()) { Logging::LogIt(Logging::logGUI) << "info string unrecognised command " << uciCommand; }
291 106 : }
292 :
293 0 : std::string wdlStat(const ScoreType score, const unsigned int ply) {
294 0 : std::stringstream ss;
295 0 : const int wdlW = static_cast<int>(toWDLModel(score, ply));
296 0 : const int wdlL = static_cast<int>(toWDLModel(-score, ply));
297 0 : const int wdlD = 1000 - wdlW - wdlL;
298 0 : ss << " wdl " << wdlW << " " << wdlD << " " << wdlL;
299 0 : return ss.str();
300 0 : }
301 :
302 274 : std::string uciScore(const ScoreType score, const unsigned int ply) {
303 274 : if (isMatedScore(score)) return "mate " + std::to_string((-MATE - score) / 2);
304 274 : if (isMateScore(score)) return "mate " + std::to_string((MATE - score + 1) / 2);
305 822 : return "cp " + std::to_string(score) + (DynamicConfig::withWDL ? wdlStat(score, ply) : "");
306 : }
307 :
308 0 : void handleVariant(){
309 0 : if (DynamicConfig::chessvariant == "chess"){
310 0 : DynamicConfig::FRC = false;
311 0 : DynamicConfig::armageddon = false;
312 0 : DynamicConfig::antichess = false;
313 : }
314 0 : else if (DynamicConfig::chessvariant == "antichess"){
315 0 : DynamicConfig::FRC = false;
316 0 : DynamicConfig::armageddon = false;
317 0 : DynamicConfig::antichess = true;
318 0 : DynamicConfig::useNNUE = false;
319 : }
320 0 : else if (DynamicConfig::chessvariant == "armageddon"){
321 0 : DynamicConfig::FRC = false;
322 0 : DynamicConfig::armageddon = true;
323 0 : DynamicConfig::antichess = false;
324 0 : DynamicConfig::useNNUE = false;
325 : }
326 0 : else if (DynamicConfig::chessvariant == "fischerandom"){
327 0 : DynamicConfig::FRC = true;
328 0 : DynamicConfig::armageddon = false;
329 0 : DynamicConfig::antichess = false;
330 0 : DynamicConfig::useNNUE = false;
331 : }
332 : else {
333 0 : Logging::LogIt(Logging::logGUI) << "info string unhandled variant : " << DynamicConfig::chessvariant;
334 : }
335 0 : }
336 :
337 : } // namespace UCI
|