1 /* protocol.cpp
2
3 GNU Chess engine
4
5 Copyright (C) 2001-2015 Free Software Foundation, Inc.
6
7 This program 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 This program 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
22 // protocol.cpp
23
24 // includes
25
26 #include <cstdarg>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30 #include <pthread.h>
31
32 #include "board.h"
33 #include "book.h"
34 #include "eval.h"
35 #include "fen.h"
36 #include "material.h"
37 #include "move.h"
38 #include "move_do.h"
39 #include "move_legal.h"
40 #include "option.h"
41 #include "pawn.h"
42 #include "posix.h"
43 #include "protocol.h"
44 #include "pst.h"
45 #include "search.h"
46 #include "trans.h"
47 #include "util.h"
48 #include "config.h"
49
50 // constants
51
52 namespace engine {
53
54 static const double NormalRatio = 1.0;
55 static const double PonderRatio = 1.25;
56
57 // variables
58
59 static bool Init;
60
61 static bool Searching; // search in progress?
62 static bool Infinite; // infinite or ponder mode?
63 static bool Delay; // postpone "bestmove" in infinite/ponder mode?
64
65 extern bool UseTrans;
66
67 // Streams used for communication with the adapter
68
69 extern FILE *pipefd_a2e_0_stream;
70 extern FILE *pipefd_e2a_1_stream;
71
72 // prototypes
73
74 static void init ();
75 static void loop_step ();
76
77 static void parse_go (char string[]);
78 static void parse_position (char string[]);
79 static void parse_setoption (char string[]);
80
81 static void send_best_move ();
82
83 static bool string_equal (const char s1[], const char s2[]);
84 static bool string_start_with (const char s1[], const char s2[]);
85
86 // functions
87
88 // loop()
89
loop()90 void loop() {
91
92 // init (to help debugging)
93
94 Init = false;
95
96 Searching = false;
97 Infinite = false;
98 Delay = false;
99
100 search_clear();
101
102 board_from_fen(SearchInput->board,StartFen);
103
104 // loop
105
106 while (true) loop_step();
107 }
108
109 // init()
110
init()111 static void init() {
112
113 if (!Init) {
114
115 // late initialisation
116
117 Init = true;
118
119 if (option_get_bool("OwnBook")) {
120 book_open(option_get_string("BookFile"));
121 }
122
123 trans_alloc(Trans);
124
125 pawn_init();
126 pawn_alloc();
127
128 material_init();
129 material_alloc();
130
131 pst_init();
132 eval_init();
133 }
134 }
135
136 // event()
137
event()138 void event() {
139
140 while (!SearchInfo->stop && input_available()) loop_step();
141 }
142
143 // loop_step()
144
loop_step()145 static void loop_step() {
146
147 char string[65536];
148
149 // read a line
150
151 get(string,65536);
152
153 // parse
154
155 if (false) {
156
157 } else if (string_start_with(string,"debug ")) {
158
159 // dummy
160
161 } else if (string_start_with(string,"go ")) {
162
163 if (!Searching && !Delay) {
164 init();
165 parse_go(string);
166 } else {
167 ASSERT(false);
168 }
169
170 } else if (string_equal(string,"isready")) {
171
172 if (!Searching && !Delay) {
173 init();
174 }
175
176 send("readyok"); // no need to wait when searching (dixit SMK)
177
178 } else if (string_equal(string,"ponderhit")) {
179
180 if (Searching) {
181
182 ASSERT(Infinite);
183
184 SearchInput->infinite = false;
185 Infinite = false;
186
187 } else if (Delay) {
188
189 send_best_move();
190 Delay = false;
191
192 } else {
193
194 ASSERT(false);
195 }
196
197 } else if (string_start_with(string,"position ")) {
198
199 if (!Searching && !Delay) {
200 init();
201 parse_position(string);
202 } else {
203 ASSERT(false);
204 }
205
206 } else if (string_equal(string,"quit")) {
207
208 ASSERT(!Searching);
209 ASSERT(!Delay);
210
211 //exit(EXIT_SUCCESS);
212 pthread_exit(NULL);
213
214 } else if (string_start_with(string,"setoption ")) {
215
216 if (!Searching && !Delay) {
217 parse_setoption(string);
218 } else {
219 ASSERT(false);
220 }
221
222 } else if (string_equal(string,"stop")) {
223
224 if (Searching) {
225
226 SearchInfo->stop = true;
227 Infinite = false;
228
229 } else if (Delay) {
230
231 send_best_move();
232 Delay = false;
233 }
234
235 } else if (string_equal(string,"uci")) {
236
237 ASSERT(!Searching);
238 ASSERT(!Delay);
239
240 send("id name GNU Chess " VERSION);
241 send("id author GNU Chess team");
242
243 option_list();
244
245 send("uciok");
246
247 } else if (string_equal(string,"ucinewgame")) {
248
249 if (!Searching && !Delay && Init) {
250 trans_clear(Trans);
251 } else {
252 ASSERT(false);
253 }
254
255 } else if (string_equal(string,"hashon")) {
256
257 UseTrans = true;
258
259 } else if (string_equal(string,"hashoff")) {
260
261 UseTrans = false;
262
263 }
264 }
265
266 // parse_go()
267
parse_go(char string[])268 static void parse_go(char string[]) {
269
270 const char * ptr;
271 bool infinite, ponder;
272 int depth, mate, movestogo;
273 sint64 nodes;
274 double binc, btime, movetime, winc, wtime;
275 double time, inc;
276 double time_max, alloc;
277
278 // init
279
280 infinite = false;
281 ponder = false;
282
283 depth = -1;
284 mate = -1;
285 movestogo = -1;
286
287 nodes = -1;
288
289 binc = -1.0;
290 btime = -1.0;
291 movetime = -1.0;
292 winc = -1.0;
293 wtime = -1.0;
294
295 // parse
296
297 ptr = strtok(string," "); // skip "go"
298
299 for (ptr = strtok(NULL," "); ptr != NULL; ptr = strtok(NULL," ")) {
300
301 if (false) {
302
303 } else if (string_equal(ptr,"binc")) {
304
305 ptr = strtok(NULL," ");
306 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
307
308 binc = double(atoi(ptr)) / 1000.0;
309 ASSERT(binc>=0.0);
310
311 } else if (string_equal(ptr,"btime")) {
312
313 ptr = strtok(NULL," ");
314 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
315
316 btime = double(atoi(ptr)) / 1000.0;
317 ASSERT(btime>=0.0);
318
319 } else if (string_equal(ptr,"depth")) {
320
321 ptr = strtok(NULL," ");
322 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
323
324 depth = atoi(ptr);
325 ASSERT(depth>=0);
326
327 } else if (string_equal(ptr,"infinite")) {
328
329 infinite = true;
330
331 } else if (string_equal(ptr,"mate")) {
332
333 ptr = strtok(NULL," ");
334 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
335
336 mate = atoi(ptr);
337 ASSERT(mate>=0);
338
339 } else if (string_equal(ptr,"movestogo")) {
340
341 ptr = strtok(NULL," ");
342 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
343
344 movestogo = atoi(ptr);
345 ASSERT(movestogo>=0);
346
347 } else if (string_equal(ptr,"movetime")) {
348
349 ptr = strtok(NULL," ");
350 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
351
352 movetime = double(atoi(ptr)) / 1000.0;
353 ASSERT(movetime>=0.0);
354
355 } else if (string_equal(ptr,"nodes")) {
356
357 ptr = strtok(NULL," ");
358 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
359
360 nodes = my_atoll(ptr);
361 ASSERT(nodes>=0);
362
363 } else if (string_equal(ptr,"ponder")) {
364
365 ponder = true;
366
367 } else if (string_equal(ptr,"searchmoves")) {
368
369 // dummy
370
371 } else if (string_equal(ptr,"winc")) {
372
373 ptr = strtok(NULL," ");
374 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
375
376 winc = double(atoi(ptr)) / 1000.0;
377 ASSERT(winc>=0.0);
378
379 } else if (string_equal(ptr,"wtime")) {
380
381 ptr = strtok(NULL," ");
382 if (ptr == NULL) my_fatal("parse_go(): missing argument\n");
383
384 wtime = double(atoi(ptr)) / 1000.0;
385 ASSERT(wtime>=0.0);
386 }
387 }
388
389 // init
390
391 search_clear();
392
393 // depth limit
394
395 if (depth >= 0) {
396 SearchInput->depth_is_limited = true;
397 SearchInput->depth_limit = depth;
398 } else if (mate >= 0) {
399 SearchInput->depth_is_limited = true;
400 SearchInput->depth_limit = mate * 2 - 1; // HACK: move -> ply
401 }
402
403 // time limit
404
405 if (COLOUR_IS_WHITE(SearchInput->board->turn)) {
406 time = wtime;
407 inc = winc;
408 } else {
409 time = btime;
410 inc = binc;
411 }
412
413 if (movestogo <= 0 || movestogo > 30) movestogo = 30; // HACK
414 if (inc < 0.0) inc = 0.0;
415
416 if (movetime >= 0.0) {
417
418 // fixed time
419
420 SearchInput->time_is_limited = true;
421 SearchInput->time_limit_1 = movetime * 5.0; // HACK to avoid early exit
422 SearchInput->time_limit_2 = movetime;
423
424 } else if (time >= 0.0) {
425
426 // dynamic allocation
427
428 time_max = time * 0.95 - 1.0;
429 if (time_max < 0.0) time_max = 0.0;
430
431 SearchInput->time_is_limited = true;
432
433 alloc = (time_max + inc * double(movestogo-1)) / double(movestogo);
434 alloc *= (option_get_bool("Ponder") ? PonderRatio : NormalRatio);
435 if (alloc > time_max) alloc = time_max;
436 SearchInput->time_limit_1 = alloc;
437
438 alloc = (time_max + inc * double(movestogo-1)) * 0.5;
439 if (alloc < SearchInput->time_limit_1) alloc = SearchInput->time_limit_1;
440 if (alloc > time_max) alloc = time_max;
441 SearchInput->time_limit_2 = alloc;
442 }
443
444 if (infinite || ponder) SearchInput->infinite = true;
445
446 // search
447
448 ASSERT(!Searching);
449 ASSERT(!Delay);
450
451 Searching = true;
452 Infinite = infinite || ponder;
453 Delay = false;
454
455 search();
456 search_update_current();
457
458 ASSERT(Searching);
459 ASSERT(!Delay);
460
461 Searching = false;
462 Delay = Infinite;
463
464 if (!Delay) send_best_move();
465 }
466
467 // parse_position()
468
parse_position(char string[])469 static void parse_position(char string[]) {
470
471 const char * fen;
472 char * moves;
473 const char * ptr;
474 char move_string[256];
475 int move;
476 undo_t undo[1];
477
478 // init
479
480 fen = strstr(string,"fen ");
481 moves = strstr(string,"moves ");
482
483 // start position
484
485 if (fen != NULL) { // "fen" present
486
487 if (moves != NULL) { // "moves" present
488 ASSERT(moves>fen);
489 moves[-1] = '\0'; // dirty, but so is UCI
490 }
491
492 board_from_fen(SearchInput->board,fen+4); // CHANGE ME
493
494 } else {
495
496 // HACK: assumes startpos
497
498 board_from_fen(SearchInput->board,StartFen);
499 }
500
501 // moves
502
503 if (moves != NULL) { // "moves" present
504
505 ptr = moves + 6;
506
507 while (*ptr != '\0') {
508
509 move_string[0] = *ptr++;
510 move_string[1] = *ptr++;
511 move_string[2] = *ptr++;
512 move_string[3] = *ptr++;
513
514 if (*ptr == '\0' || *ptr == ' ') {
515 move_string[4] = '\0';
516 } else { // promote
517 move_string[4] = *ptr++;
518 move_string[5] = '\0';
519 }
520
521 move = move_from_string(move_string,SearchInput->board);
522
523 if (move == MoveNone) my_fatal("parse_position(): invalid move:%s\n",move_string);
524
525 move_do(SearchInput->board,move,undo);
526
527 while (*ptr == ' ') ptr++;
528 }
529 }
530 }
531
532 // parse_setoption()
533
parse_setoption(char string[])534 static void parse_setoption(char string[]) {
535
536 const char * name;
537 char * value;
538
539 // init
540
541 name = strstr(string,"name ");
542 value = strstr(string,"value ");
543
544 if (name == NULL || value == NULL || name >= value) return; // ignore buttons
545
546 value[-1] = '\0'; // HACK
547 name += 5;
548 value += 6;
549
550 // update
551
552 option_set(name,value);
553
554 // update transposition-table size if needed
555
556 if (Init && my_string_equal(name,"Hash")) { // Init => already allocated
557
558 ASSERT(!Searching);
559
560 if (option_get_int("Hash") >= 4) {
561 trans_free(Trans);
562 trans_alloc(Trans);
563 }
564 }
565 }
566
567 // send_best_move()
568
send_best_move()569 static void send_best_move() {
570
571 double time, speed, cpu;
572 sint64 node_nb;
573 char move_string[256];
574 char ponder_string[256];
575 int move;
576 mv_t * pv;
577
578 // info
579
580 // HACK: should be in search.cpp
581
582 time = SearchCurrent->time;
583 speed = SearchCurrent->speed;
584 cpu = SearchCurrent->cpu;
585 node_nb = SearchCurrent->node_nb;
586
587 send("info time %.0f nodes " S64_FORMAT " nps %.0f cpuload %.0f",time*1000.0,node_nb,speed,cpu*1000.0);
588
589 trans_stats(Trans);
590 // pawn_stats();
591 // material_stats();
592
593 // best move
594
595 move = SearchBest->move;
596 pv = SearchBest->pv;
597
598 move_to_string(move,move_string,256);
599
600 if (pv[0] == move && move_is_ok(pv[1])) {
601 move_to_string(pv[1],ponder_string,256);
602 send("bestmove %s ponder %s",move_string,ponder_string);
603 } else {
604 send("bestmove %s",move_string);
605 }
606 }
607
608 // get()
609
get(char string[],int size)610 void get(char string[], int size) {
611
612 ASSERT(string!=NULL);
613 ASSERT(size>=65536);
614
615 if (!my_file_read_line(pipefd_a2e_0_stream,string,size)) { // EOF
616 exit(EXIT_SUCCESS);
617 }
618 }
619
620 // send()
621
send(const char format[],...)622 void send(const char format[], ...) {
623
624 va_list arg_list;
625 char string[4096];
626
627 ASSERT(format!=NULL);
628
629 va_start(arg_list,format);
630 vsprintf(string,format,arg_list);
631 va_end(arg_list);
632
633 fprintf(pipefd_e2a_1_stream,"%s\n",string);
634 }
635
636 // string_equal()
637
string_equal(const char s1[],const char s2[])638 static bool string_equal(const char s1[], const char s2[]) {
639
640 ASSERT(s1!=NULL);
641 ASSERT(s2!=NULL);
642
643 return strcmp(s1,s2) == 0;
644 }
645
646 // string_start_with()
647
string_start_with(const char s1[],const char s2[])648 static bool string_start_with(const char s1[], const char s2[]) {
649
650 ASSERT(s1!=NULL);
651 ASSERT(s2!=NULL);
652
653 return strstr(s1,s2) == s1;
654 }
655
656 } // namespace engine
657
658 // end of protocol.cpp
659
660