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