15 #include "move_do.h"
\r
16 #include "move_legal.h"
\r
25 static const bool UseDebug = FALSE;
\r
27 #define StringSize ((int)4096)
\r
33 // Hopefully the following confusion is temporary
\r
34 // Normally we should check for the engine name but this is a hack anyway
\r
35 // Some of there where provided by Marc Lacrosse
\r
37 const char * thread_options[]={
\r
38 "number of threads", // toga
\r
39 "number threads", // Deep Learning Toga
\r
40 "threads", // glaurung, zappa, cyclone, grapefruit,
\r
41 // Deep Shredder, Deep Junior, bright
\r
42 "core threads", // HIARCS
\r
43 "max cpus", // rybka
\r
44 "cpus", // Deep Sjeng, Fruit2.3.5
\r
45 "maxthreads", // Naum
\r
51 static bool uci_is_ok (const uci_t * uci);
\r
53 static int parse_bestmove (uci_t * uci, const char string[]);
\r
54 static void parse_id (uci_t * uci, const char string[]);
\r
55 static int parse_info (uci_t * uci, const char string[]);
\r
56 static void parse_option (uci_t * uci, const char string[]);
\r
57 static void parse_score (uci_t * uci, const char string[]);
\r
59 static int mate_score (int dist);
\r
66 static void apply_UCI3_heuristics(option_t *opt){
\r
67 if(option_get_int(Option,"UCIVersion")>2){
\r
70 if(!my_string_equal(opt->type,"string")){
\r
73 if(!strncmp(opt->name,"UCI_",4)){
\r
76 if(my_string_case_contains(opt->name,"file")){
\r
77 my_string_set(&opt->type,"file");
\r
80 if(my_string_case_contains(opt->name,"path")){
\r
81 my_string_set(&opt->type,"path");
\r
86 // uci_set_threads()
\r
88 void uci_set_threads(uci_t * uci, int n) {
\r
89 const char *thread_option=uci_thread_option(uci);
\r
92 uci_send_option(uci,thread_option,"%d",n);
\r
97 const char * uci_thread_option(uci_t * uci){
\r
98 const char **p = thread_options;
\r
99 const char *thread_option;
\r
101 while((thread_option = *(p++))){
\r
102 if((opt=option_find(uci->option,thread_option))){
\r
112 static bool uci_is_ok(const uci_t * uci) {
\r
114 if (uci == NULL) return FALSE;
\r
115 if (uci->engine == NULL) return FALSE;
\r
116 if (!option_is_ok(uci->option)) return FALSE;
\r
122 void uci_open(uci_t * uci, engine_t * engine) {
\r
124 char string[StringSize];
\r
128 ASSERT(engine!=NULL);
\r
132 uci->engine = engine;
\r
135 my_string_set(&uci->name,"<empty>");
\r
136 uci->author = NULL;
\r
137 my_string_set(&uci->author,"<empty>");
\r
138 option_init(uci->option);
\r
141 uci->searching = 0;
\r
142 uci->pending_nb = 0;
\r
143 uci->multipv_mode = FALSE;
\r
144 board_start(uci->board);
\r
147 // send "uci" and wait for "uciok"
\r
149 engine_send(uci->engine,"uci");
\r
152 engine_get(uci->engine,string);
\r
153 // Handle the case that the engine is really a WB engine somewhat gracefully.
\r
154 if((strstr(string,"Illegal") || strstr(string,"Error"))
\r
155 &&strstr(string,"uci")){
\r
156 my_fatal("uci_open(): Not a UCI engine.\n");
\r
158 event = uci_parse(uci,string);
\r
159 } while (!engine_eof(Engine) && (event & EVENT_UCI) == 0);
\r
164 void uci_close(uci_t * uci) {
\r
166 ASSERT(uci_is_ok(uci));
\r
167 engine_close(uci->engine);
\r
168 uci->engine = NULL;
\r
169 my_string_clear(&uci->name);
\r
170 my_string_clear(&uci->author);
\r
172 option_clear(uci->option);
\r
177 void uci_clear(uci_t * uci) {
\r
179 ASSERT(uci_is_ok(uci));
\r
181 ASSERT(!uci->searching);
\r
183 uci->best_move = MoveNone;
\r
184 uci->ponder_move = MoveNone;
\r
188 uci->sel_depth = 0;
\r
189 line_clear(uci->pv);
\r
191 uci->best_score = 0;
\r
192 uci->best_depth = 0;
\r
193 uci->best_sel_depth = 0;
\r
194 line_clear(uci->best_pv);
\r
195 // make the default 1 instead of 0 so that info lines can be recognized by their node number 0
\r
201 line_clear(uci->current_line);
\r
203 uci->root_move = MoveNone;
\r
204 uci->root_move_pos = 0;
\r
205 uci->root_move_nb = board_mobility(uci->board);
\r
210 // uci_send_isready()
\r
212 void uci_send_isready(uci_t * uci) {
\r
216 engine_send(uci->engine,"isready");
\r
220 // uci_send_isready_sync()
\r
222 void uci_send_isready_sync(uci_t * uci) {
\r
224 char string[StringSize];
\r
227 ASSERT(uci_is_ok(uci));
\r
229 // send "isready" and wait for "readyok"
\r
231 uci_send_isready(uci);
\r
234 engine_get(uci->engine,string);
\r
235 event = uci_parse(uci,string);
\r
236 } while (!engine_eof(Engine) && (event & EVENT_READY) == 0);
\r
241 void uci_send_stop(uci_t * uci) {
\r
243 ASSERT(uci_is_ok(uci));
\r
245 ASSERT(uci->searching);
\r
246 ASSERT(uci->pending_nb>=1);
\r
248 engine_send(Engine,"stop");
\r
249 uci->searching = FALSE;
\r
252 // uci_send_stop_sync()
\r
254 void uci_send_stop_sync(uci_t * uci) {
\r
256 char string[StringSize];
\r
259 ASSERT(uci_is_ok(uci));
\r
261 ASSERT(uci->searching);
\r
262 ASSERT(uci->pending_nb>=1);
\r
264 // send "stop" and wait for "bestmove"
\r
266 uci_send_stop(uci);
\r
269 engine_get(uci->engine,string);
\r
270 event = uci_parse(uci,string);
\r
271 } while (!engine_eof(Engine) && (event & EVENT_STOP) == 0);
\r
274 // uci_send_ucinewgame()
\r
276 void uci_send_ucinewgame(uci_t * uci) {
\r
280 if (option_get_int(Option,"UCIVersion") >= 2) {
\r
281 engine_send(uci->engine,"ucinewgame");
\r
285 // uci_send_option()
\r
287 bool uci_send_option(uci_t * uci, const char option[], const char format[], ...) {
\r
289 char value[FormatBufferSize];
\r
293 ASSERT(uci_is_ok(uci));
\r
294 ASSERT(option!=NULL);
\r
295 ASSERT(format!=NULL);
\r
299 CONSTRUCT_ARG_STRING(format,value);
\r
301 if (UseDebug) my_log("POLYGLOT OPTION %s VALUE %s\n",option,value);
\r
303 opt=option_find(uci->option,option);
\r
306 if(!IS_BUTTON(opt->type)){
\r
307 if(!my_string_equal(opt->value,value)){
\r
308 engine_send(uci->engine,"setoption name %s value %s",
\r
310 my_string_set(&opt->value,value);
\r
312 my_log("POLYGLOT Not sending option \"%s\" since it "
\r
313 "already has the correct value.\n",opt->name);
\r
316 engine_send(uci->engine,"setoption name %s",opt->name);
\r
324 int uci_parse(uci_t * uci, const char string[]) {
\r
328 char command[StringSize];
\r
329 char argument[StringSize];
\r
331 ASSERT(uci_is_ok(uci));
\r
332 ASSERT(string!=NULL);
\r
336 event = EVENT_NONE;
\r
340 parse_open(parse,string);
\r
342 if (parse_get_word(parse,command,StringSize)) {
\r
344 parse_get_string(parse,argument,StringSize);
\r
345 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" ARGUMENT \"%s\"\n",command,argument);
\r
349 } else if (my_string_equal(command,"bestmove")) {
\r
353 ASSERT(uci->pending_nb>0);
\r
355 if (uci->searching && uci->pending_nb == 1) {
\r
359 uci->searching = FALSE;
\r
362 event = parse_bestmove(uci,argument); // updates uci->best_move and uci->ponder_move
\r
368 if (uci->pending_nb > 0) {
\r
370 if (uci->pending_nb == 0) event = EVENT_STOP;
\r
374 } else if (my_string_equal(command,"id")) {
\r
376 parse_id(uci,argument);
\r
378 } else if (my_string_equal(command,"info")) {
\r
380 // search information
\r
382 if (uci->searching && uci->pending_nb == 1) { // current search
\r
383 event = parse_info(uci,argument);
\r
386 } else if (my_string_equal(command,"option")) {
\r
388 parse_option(uci,argument);
\r
390 } else if (my_string_equal(command,"readyok")) {
\r
394 ASSERT(uci->ready_nb>0);
\r
396 if (uci->ready_nb > 0) {
\r
398 if (uci->ready_nb == 0) event = EVENT_READY;
\r
401 } else if (my_string_equal(command,"uciok")) {
\r
407 if (UseDebug) my_log("POLYGLOT unknown command \"%s\"\n",command);
\r
411 parse_close(parse);
\r
416 // parse_bestmove()
\r
418 static int parse_bestmove(uci_t * uci, const char string[]) {
\r
421 char command[StringSize];
\r
422 char option[StringSize];
\r
423 char argument[StringSize];
\r
426 ASSERT(uci_is_ok(uci));
\r
427 ASSERT(string!=NULL);
\r
431 strcpy(command,"bestmove");
\r
433 parse_open(parse,string);
\r
434 parse_add_keyword(parse,"ponder");
\r
438 uci->bestmove[0]='\0';
\r
439 if (!parse_get_string(parse,argument,StringSize)) {
\r
440 strcpy(uci->bestmove,"nomove");
\r
441 return EVENT_ILLEGAL_MOVE;
\r
442 // my_fatal("parse_bestmove(): missing argument\n");
\r
444 strncpy(uci->bestmove,argument,UciStringSize);
\r
445 uci->bestmove[UciStringSize-1]='\0';
\r
447 uci->best_move = move_from_can(argument,uci->board);
\r
448 if (uci->best_move == MoveNone) {
\r
449 return EVENT_ILLEGAL_MOVE;
\r
450 // my_fatal("parse_bestmove(): not a move \"%s\"\n",argument);
\r
453 if(!move_is_legal(uci->best_move,uci->board)){
\r
454 return EVENT_ILLEGAL_MOVE;
\r
456 ASSERT(uci->best_move!=MoveNone);
\r
457 ASSERT(move_is_legal(uci->best_move,uci->board));
\r
461 while (parse_get_word(parse,option,StringSize)) {
\r
463 parse_get_string(parse,argument,StringSize);
\r
465 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" OPTION \"%s\" ARGUMENT \"%s\"\n",command,option,argument);
\r
469 } else if (my_string_equal(option,"ponder")) {
\r
471 ASSERT(!my_string_empty(argument));
\r
473 board_copy(board,uci->board);
\r
474 move_do(board,uci->best_move);
\r
476 uci->ponder_move = move_from_can(argument,board);
\r
477 // if (uci->ponder_move == MoveNone) my_fatal("parse_bestmove(): not a move \"%s\"\n",argument);
\r
479 ASSERT(uci->ponder_move!=MoveNone);
\r
480 ASSERT(move_is_legal(uci->ponder_move,board));
\r
484 my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);
\r
488 parse_close(parse);
\r
495 static void parse_id(uci_t * uci, const char string[]) {
\r
498 char command[StringSize];
\r
499 char option[StringSize];
\r
500 char argument[StringSize];
\r
503 ASSERT(string!=NULL);
\r
507 strcpy(command,"id");
\r
509 parse_open(parse,string);
\r
510 parse_add_keyword(parse,"author");
\r
511 parse_add_keyword(parse,"name");
\r
515 while (parse_get_word(parse,option,StringSize)) {
\r
517 parse_get_string(parse,argument,StringSize);
\r
518 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" OPTION \"%s\" ARGUMENT \"%s\"\n",command,option,argument);
\r
521 } else if (my_string_equal(option,"author")) {
\r
522 ASSERT(!my_string_empty(argument));
\r
523 my_string_set(&uci->author,argument);
\r
524 } else if (my_string_equal(option,"name")) {
\r
525 ASSERT(!my_string_empty(argument));
\r
526 my_string_set(&uci->name,argument);
\r
528 my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);
\r
532 parse_close(parse);
\r
534 if (UseDebug) my_log("POLYGLOT engine name \"%s\" author \"%s\"\n",uci->name,uci->author);
\r
539 static int parse_info(uci_t * uci, const char string[]) {
\r
543 char command[StringSize];
\r
544 char option[StringSize];
\r
545 char argument[StringSize];
\r
552 ASSERT(uci_is_ok(uci));
\r
553 ASSERT(string!=NULL);
\r
557 event = EVENT_NONE;
\r
559 strcpy(command,"info");
\r
561 parse_open(parse,string);
\r
562 parse_add_keyword(parse,"cpuload");
\r
563 parse_add_keyword(parse,"currline");
\r
564 parse_add_keyword(parse,"currmove");
\r
565 parse_add_keyword(parse,"currmovenumber");
\r
566 parse_add_keyword(parse,"depth");
\r
567 parse_add_keyword(parse,"hashfull");
\r
568 parse_add_keyword(parse,"multipv");
\r
569 parse_add_keyword(parse,"nodes");
\r
570 parse_add_keyword(parse,"nps");
\r
571 parse_add_keyword(parse,"pv");
\r
572 parse_add_keyword(parse,"refutation");
\r
573 parse_add_keyword(parse,"score");
\r
574 parse_add_keyword(parse,"seldepth");
\r
575 parse_add_keyword(parse,"string");
\r
576 parse_add_keyword(parse,"tbhits");
\r
577 parse_add_keyword(parse,"time");
\r
581 while (parse_get_word(parse,option,StringSize)) {
\r
583 parse_get_string(parse,argument,StringSize);
\r
585 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" OPTION \"%s\" ARGUMENT \"%s\"\n",command,option,argument);
\r
589 } else if (my_string_equal(option,"cpuload")) {
\r
591 ASSERT(!my_string_empty(argument));
\r
593 n = atoi(argument);
\r
596 if (n >= 0) uci->cpu = ((double)n) / 1000.0;
\r
598 } else if (my_string_equal(option,"currline")) {
\r
600 ASSERT(!my_string_empty(argument));
\r
602 line_from_can(uci->current_line,uci->board,argument,LineSize);
\r
604 } else if (my_string_equal(option,"currmove")) {
\r
606 ASSERT(!my_string_empty(argument));
\r
608 uci->root_move = move_from_can(argument,uci->board);
\r
609 ASSERT(uci->root_move!=MoveNone);
\r
611 } else if (my_string_equal(option,"currmovenumber")) {
\r
613 ASSERT(!my_string_empty(argument));
\r
615 n = atoi(argument);
\r
616 ASSERT(n>=1&&n<=uci->root_move_nb);
\r
618 if (n >= 1 && n <= uci->root_move_nb) {
\r
619 uci->root_move_pos = n - 1;
\r
620 ASSERT(uci->root_move_pos>=0&&uci->root_move_pos<uci->root_move_nb);
\r
623 } else if (my_string_equal(option,"depth")) {
\r
625 ASSERT(!my_string_empty(argument));
\r
627 n = atoi(argument);
\r
631 if (n > uci->depth) event |= EVENT_DEPTH;
\r
635 } else if (my_string_equal(option,"hashfull")) {
\r
637 ASSERT(!my_string_empty(argument));
\r
639 n = atoi(argument);
\r
642 if (n >= 0) uci->hash = ((double)n) / 1000.0;
\r
644 } else if (my_string_equal(option,"multipv")) {
\r
646 ASSERT(!my_string_empty(argument));
\r
648 n = atoi(argument);
\r
653 } else if (my_string_equal(option,"nodes")) {
\r
655 ASSERT(!my_string_empty(argument));
\r
657 ln = my_atoll(argument);
\r
660 if (ln >= 0) uci->node_nb = ln;
\r
662 } else if (my_string_equal(option,"nps")) {
\r
664 ASSERT(!my_string_empty(argument));
\r
666 n = atoi(argument);
\r
669 if (n >= 0) uci->speed = ((double)n);
\r
671 } else if (my_string_equal(option,"pv")) {
\r
673 ASSERT(!my_string_empty(argument));
\r
675 line_from_can(uci->pv,uci->board,argument,LineSize);
\r
678 } else if (my_string_equal(option,"refutation")) {
\r
680 ASSERT(!my_string_empty(argument));
\r
682 line_from_can(uci->pv,uci->board,argument,LineSize);
\r
684 } else if (my_string_equal(option,"score")) {
\r
686 ASSERT(!my_string_empty(argument));
\r
688 parse_score(uci,argument);
\r
690 } else if (my_string_equal(option,"seldepth")) {
\r
692 ASSERT(!my_string_empty(argument));
\r
694 n = atoi(argument);
\r
697 if (n >= 0) uci->sel_depth = n;
\r
699 } else if (my_string_equal(option,"string")) {
\r
700 if(my_string_case_equal(argument,"DrawOffer")){
\r
701 event |= EVENT_DRAW;
\r
702 }else if(my_string_case_equal(argument,"Resign")){
\r
703 event |= EVENT_RESIGN;
\r
705 snprintf(uci->info,sizeof(uci->info),"%s",argument);
\r
706 uci->info[sizeof(uci->info)-1]='\0';
\r
709 // TODO: argument to EOS
\r
711 ASSERT(!my_string_empty(argument));
\r
713 } else if (my_string_equal(option,"tbhits")) {
\r
715 ASSERT(!my_string_empty(argument));
\r
717 ln = my_atoll(argument);
\r
720 } else if (my_string_equal(option,"time")) {
\r
722 ASSERT(!my_string_empty(argument));
\r
724 n = atoi(argument);
\r
727 if (n >= 0) uci->time = ((double)n) / 1000.0;
\r
731 my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);
\r
732 // This should probably be protected
\r
733 // by a "WorkAround" option.
\r
734 snprintf(uci->info,sizeof(uci->info),"%s %s",option,argument);
\r
735 uci->info[sizeof(uci->info)-1]='\0';
\r
740 parse_close(parse);
\r
744 if ((event & EVENT_PV) != 0) {
\r
745 uci->best_score = uci->score;
\r
746 uci->best_sel_depth = uci->sel_depth;
\r
747 line_copy(uci->best_pv,uci->pv);
\r
749 if(uci->depth < uci->best_depth){
\r
750 // ignore lines of lower depth
\r
751 event &= ~EVENT_PV;
\r
753 if(uci->depth > uci->best_depth) {
\r
754 // clear stack when we start new depth
\r
755 uci->multipvSP = 0;
\r
757 uci->best_depth = uci->depth;
\r
758 if(multipvline >= 1) {
\r
760 for(i=0; i<uci->multipvSP; i++) {
\r
761 if(uci->score == uci->multipvScore[i] && uci->pv[0] == uci->multipvMove[i]) {
\r
762 event &= ~EVENT_PV; // ignore duplicates
\r
765 if(event & EVENT_PV){
\r
766 // line is new, try to add to stack
\r
767 if(uci->multipvSP<MultiPVStackSize){
\r
768 uci->multipvMove[uci->multipvSP] = uci->pv[0];
\r
769 uci->multipvScore[uci->multipvSP] = uci->score;
\r
772 my_fatal("parse_info(): multipv stack overflow.");
\r
784 static void parse_option(uci_t * uci, const char string[]) {
\r
788 char command[StringSize];
\r
789 char option[StringSize];
\r
790 char argument[StringSize];
\r
793 ASSERT(string!=NULL);
\r
797 strcpy(command,"option");
\r
799 memset(opt,0,sizeof(option_t));
\r
801 my_string_set(&opt->value,"<empty>");
\r
802 my_string_set(&opt->name,"<empty>");
\r
803 my_string_set(&opt->default_,"<empty>");
\r
804 my_string_set(&opt->max,"<empty>");
\r
805 my_string_set(&opt->min,"<empty>");
\r
806 my_string_set(&opt->type,"<empty>");
\r
810 parse_open(parse,string);
\r
811 parse_add_keyword(parse,"default");
\r
812 parse_add_keyword(parse,"max");
\r
813 parse_add_keyword(parse,"min");
\r
814 parse_add_keyword(parse,"name");
\r
815 parse_add_keyword(parse,"type");
\r
816 parse_add_keyword(parse,"var");
\r
820 while (parse_get_word(parse,option,StringSize)) {
\r
821 parse_get_string(parse,argument,StringSize);
\r
822 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" OPTION \"%s\" ARGUMENT \"%s\"\n",command,option,argument);
\r
826 } else if (my_string_equal(option,"default")) {
\r
828 // ASSERT(!my_string_empty(argument)); // HACK for Pepito
\r
830 if (!my_string_empty(argument)) {
\r
831 my_string_set(&opt->default_,argument);
\r
832 my_string_set(&opt->value,argument);
\r
835 } else if (my_string_equal(option,"max")) {
\r
837 ASSERT(!my_string_empty(argument));
\r
838 my_string_set(&opt->max,argument);
\r
840 } else if (my_string_equal(option,"min")) {
\r
842 ASSERT(!my_string_empty(argument));
\r
843 my_string_set(&opt->min,argument);
\r
845 } else if (my_string_equal(option,"name")) {
\r
847 ASSERT(!my_string_empty(argument));
\r
849 if (!my_string_empty(argument)) {
\r
850 my_string_set(&opt->name,argument);
\r
853 } else if (my_string_equal(option,"type")) {
\r
855 ASSERT(!my_string_empty(argument));
\r
856 my_string_set(&opt->type,argument);
\r
858 } else if (my_string_equal(option,"var")) {
\r
860 ASSERT(!my_string_empty(argument));
\r
861 my_string_set(&opt->var[opt->var_nb++],argument);
\r
862 if(opt->var_nb==VarNb) break;
\r
866 my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);
\r
870 parse_close(parse);
\r
872 apply_UCI3_heuristics(opt);
\r
873 option_insert(uci->option,opt);
\r
876 if (UseDebug) my_log("POLYGLOT option name \"%s\" default \"%s\"\n",opt->name,opt->default_);
\r
881 static void parse_score(uci_t * uci, const char string[]) {
\r
884 char command[StringSize];
\r
885 char option[StringSize];
\r
886 char argument[StringSize];
\r
889 ASSERT(uci_is_ok(uci));
\r
890 ASSERT(string!=NULL);
\r
894 strcpy(command,"score");
\r
896 parse_open(parse,string);
\r
897 parse_add_keyword(parse,"cp");
\r
898 parse_add_keyword(parse,"lowerbound");
\r
899 parse_add_keyword(parse,"mate");
\r
900 parse_add_keyword(parse,"upperbound");
\r
904 while (parse_get_word(parse,option,StringSize)) {
\r
906 parse_get_string(parse,argument,StringSize);
\r
908 if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" OPTION \"%s\" ARGUMENT \"%s\"\n",command,option,argument);
\r
912 } else if (my_string_equal(option,"cp")) {
\r
914 ASSERT(!my_string_empty(argument));
\r
916 n = atoi(argument);
\r
920 } else if (my_string_equal(option,"lowerbound")) {
\r
922 ASSERT(my_string_empty(argument));
\r
924 } else if (my_string_equal(option,"mate")) {
\r
926 ASSERT(!my_string_empty(argument));
\r
928 n = atoi(argument);
\r
931 uci->score = mate_score(n);
\r
933 } else if (my_string_equal(option,"upperbound")) {
\r
935 ASSERT(my_string_empty(argument));
\r
939 my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);
\r
943 parse_close(parse);
\r
948 static int mate_score(int dist) {
\r
953 } else if (dist > 0) {
\r
954 return +option_get_int(Option,"MateScore") - (+dist) * 2 + 1;
\r
955 } else if (dist < 0) {
\r
956 return -option_get_int(Option,"MateScore") + (-dist) * 2;
\r