23 #include "move_legal.h"
31 #include "xboard2uci.h"
35 #define StringSize 4096
39 static const bool UseDebug = FALSE;
40 static const bool DelayPong = FALSE;
46 bool computer[ColourNb];
47 bool playedAllMoves[ColourNb];
55 bool has_feature_memory;
57 bool has_feature_egt_nalimov;
58 bool has_feature_egt_gaviota;
59 bool has_feature_egt_syzygy;
64 bool new_hack; // "new" is a C++ keyword
87 typedef enum { WAIT, THINK, PONDER, ANALYSE } dummy_state_t;
91 static state_t State[1];
96 static void comp_move (int move);
97 static void move_step (int move);
98 static void board_update ();
101 static void no_mess (int move);
103 static void search_update ();
104 static void search_clear ();
105 static void update_remaining_time();
106 static int report_best_score();
107 static bool kibitz_throttle (bool searching);
108 static void start_protected_command();
109 static void end_protected_command();
111 static bool active ();
112 static bool ponder ();
113 static bool ponder_ok (int ponder_move);
115 static void stop_search ();
117 static void send_board (int extra_move);
118 static void send_pv ();
119 static void send_info ();
121 static void send_xboard_options ();
123 static void learn (int result);
130 void xboard2uci_init() {
139 State->computer[White] = FALSE;
140 State->computer[Black] = TRUE;
142 State->exp_move = MoveNone;
143 State->hint_move = MoveNone;
144 State->resign_nb = 0;
145 my_timer_reset(State->timer);
147 // yes there are engines that do not have the "Hash" option....
148 XB->has_feature_memory= (option_find(Uci->option,"Hash")!=NULL);
149 XB->has_feature_smp = (uci_thread_option(Uci)!=NULL);
150 // TODO: support for other types of table bases
151 // This is a quick hack.
152 XB->has_feature_egt_nalimov = (option_find(Uci->option,"NalimovPath")!=NULL);
153 XB->has_feature_egt_gaviota = (option_find(Uci->option,"GaviotaTbPath")!=NULL);
154 XB->has_feature_egt_syzygy = (option_find(Uci->option,"SyzygyPath")!=NULL);
156 XB->computer = FALSE;
158 my_string_set(&XB->name,"<empty>");
171 XB->time_limit = FALSE;
174 XB->depth_limit = FALSE;
178 XB->opp_time = 300.0;
184 static list_t move_list[1];
186 // xboard2uci_gui_step()
188 void xboard2uci_gui_step(char string[]) {
191 char move_string[256];
196 } else if (match(string,"accepted *")) {
200 } else if (match(string,"analyze")) {
202 State->computer[White] = FALSE;
203 State->computer[Black] = FALSE;
206 XB->new_hack = FALSE;
212 } else if (match(string,"bk")) {
214 if (option_get_bool(Option,"Book")) {
215 game_get_board(Game,board);
217 } else { // [HGM] without book, print all legal moves
218 int i, gen=!list_size(move_list);
219 game_get_board(Game,board);
220 if(gen) gen_legal_moves(move_list,board);
221 for(i=0; i<list_size(move_list); i++){
222 if(gen) move_list->value[i] = 0;
223 move_to_san(move_list->move[i],board,move_string,256);
224 printf(" %s%6s\n", move_list->value[i]? "* " : "", move_string);
226 // this is necessary by the xboard protocol
230 } else if (match(string,"exclude *") || match(string,"option Polyglot exclude move=*")) { // [HGM]
232 int i, all = !strcmp(Star[0], "all"), change=FALSE, cnt=0;
233 game_get_board(Game,board);
234 if(!list_size(move_list)) {
235 gen_legal_moves(move_list,board);
236 for(i=0; i<list_size(move_list); i++){
237 move_list->value[i] = 0;
240 move = move_from_san(Star[0],board);
242 for(i=0; i<list_size(move_list); i++){
243 if(all || move_list->move[i] == move)
244 change |= !move_list->value[i], move_list->value[i] = 1;
245 cnt += !move_list->value[i];
247 if(change && cnt) mess(); // do not relay to engine if no change or no moves left
249 } else if (match(string,"include *")) { // [HGM]
251 int i, all = !strcmp(Star[0], "all"), change = FALSE;
252 game_get_board(Game,board);
253 move = move_from_san(Star[0],board);
255 for(i=0; i<list_size(move_list); i++){
256 if(all || move_list->move[i] == move)
257 change |= move_list->value[i], move_list->value[i] = 0;
261 } else if (match(string,"black")) {
263 if (colour_is_black(game_turn(Game))) {
265 State->computer[White] = TRUE;
266 State->computer[Black] = FALSE;
274 } else if (match(string,"computer")) {
278 } else if (match(string,"draw")) {
279 if(option_find(Uci->option,"UCI_DrawOffers")){
280 my_log("POLYGLOT draw from XB received");
281 uci_send_option(Uci,"DrawOffer","%s","draw");}
282 else if (option_get_bool(Option,"HandleDraws") && Uci->root_move_nb > 20) { // [HGM] PG draw handling
283 my_log("POLYGLOT draw from XB received");
284 if (Uci->best_score <= -option_get_int(Option,"ContemptScore"))
285 gui_send(GUI,"offer draw");}
286 } else if (match(string,"easy")) {
292 } else if (match(string,"edit")) {
296 gui_send(GUI,"Error (unknown command): %s",string);
298 } else if (match(string,"exit")) {
300 State->computer[White] = FALSE;
301 State->computer[Black] = FALSE;
307 } else if (match(string,"force")) {
309 State->computer[White] = FALSE;
310 State->computer[Black] = FALSE;
314 } else if (match(string,"go")) {
316 State->computer[game_turn(Game)] = TRUE;
317 State->computer[colour_opp(game_turn(Game))] = FALSE;
319 XB->new_hack = FALSE;
325 } else if (match(string,"hard")) {
331 } else if (match(string,"hint")) {
334 game_get_board(Game,board);
335 if (option_get_bool(Option,"Book")) {
337 move = book_move(board,FALSE);
339 if(move==MoveNone && State->hint_move!=MoveNone){
340 move=State->hint_move;
343 if (move != MoveNone && move_is_legal(move,board)) {
344 move_to_san(move,board,move_string,256);
345 gui_send(GUI,"Hint: %s",move_string);
348 } else if (match(string,"ics *")) {
352 } else if (match(string,"level * *:* *")) {
354 XB->mps = atoi(Star[0]);
355 XB->base = ((double)atoi(Star[1])) * 60.0 + ((double)atoi(Star[2]));
356 XB->inc = ((double)atoi(Star[3]));
358 } else if (match(string,"level * * *")) {
360 XB->mps = atoi(Star[0]);
361 XB->base = ((double)atoi(Star[1])) * 60.0;
362 XB->inc = ((double)atoi(Star[2]));
364 } else if (match(string,"name *")) {
366 my_string_set(&XB->name,Star[0]);
368 } else if (match(string,"new")) {
370 uci_send_isready_sync(Uci);
371 my_log("POLYGLOT NEW GAME\n");
373 option_set(Option,"Chess960","false");
377 move_list->size = 0; // [HGM] clear all exclude moves
380 State->computer[White] = FALSE;
381 State->computer[Black] = FALSE;
383 State->computer[White] = FALSE;
384 State->computer[Black] = TRUE;
385 State->playedAllMoves[White] = TRUE; // [HGM]
386 State->playedAllMoves[Black] = TRUE;
392 XB->depth_limit = FALSE;
395 XB->computer = FALSE;
396 my_string_set(&XB->name,"<empty>");
401 uci_send_ucinewgame(Uci);
403 } else if (match(string,"nopost")) {
407 } else if (match(string,"otim *")) {
409 XB->opp_time = ((double)atoi(Star[0])) / 100.0;
410 if (XB->opp_time < 0.0) XB->opp_time = 0.0;
412 } else if (match(string,"pause")) {
416 gui_send(GUI,"Error (unknown command): %s",string);
418 } else if (match(string,"ping *")) {
420 // HACK; TODO: answer only after an engine move
423 if (XB->ping >= 0) gui_send(GUI,"pong %d",XB->ping); // HACK: get rid of old ping
424 XB->ping = atoi(Star[0]);
425 uci_send_isready_sync(Uci);
427 ASSERT(XB->ping==-1);
428 gui_send(GUI,"pong %s",Star[0]);
430 } else if (match(string,"nps *")) {
432 // fake WB play-by-nodes mode
433 XB->node_rate = atoi(Star[0]);
434 } else if (match(string,"playother")) {
436 State->computer[game_turn(Game)] = FALSE;
437 State->computer[colour_opp(game_turn(Game))] = TRUE;
439 XB->new_hack = FALSE;
445 } else if (match(string,"post")) {
449 } else if (match(string,"protover *")) {
450 XB->proto_ver = atoi(Star[0]);
451 ASSERT(XB->proto_ver>=2);
452 send_xboard_options();
454 } else if (match(string,"quit")) {
455 my_log("POLYGLOT *** \"quit\" from GUI ***\n");
457 } else if (match(string,"random")) {
461 } else if (match(string,"rating * *")) {
465 } else if (match(string,"remove")) {
467 if (game_pos(Game) >= 2) {
469 game_goto(Game,game_pos(Game)-2);
471 ASSERT(!XB->new_hack);
472 XB->new_hack = FALSE; // HACK?
479 } else if (match(string,"rejected *")) {
483 } else if (match(string,"reset")) { // protover 3?
487 gui_send(GUI,"Error (unknown command): %s",string);
490 || match(string,"result * {*}")
491 || match(string,"result * {* }")
492 || match(string,"result * { *}")
493 || match(string,"result * { * }")) {
495 my_log("POLYGLOT GAME END\n");
503 if (option_get_bool(Option,"Book") &&
504 option_get_bool(Option,"BookLearn")) {
507 } else if (my_string_equal(Star[0],"1-0")) {
509 } else if (my_string_equal(Star[0],"0-1")) {
511 } else if (my_string_equal(Star[0],"1/2-1/2")) {
515 } else if (match(string,"resume")) {
519 gui_send(GUI,"Error (unknown command): %s",string);
521 } else if (match(string,"option *=*") ||
522 match(string,"option * =*") ||
523 match(string,"option *= *") ||
524 match(string,"option * = *")
528 if(match(name, "Polyglot *")){
529 char *pg_name=Star[0];
530 polyglot_set_option(pg_name,value);
532 option_t *opt=option_find(Uci->option,name);
534 if(my_string_case_equal(opt->type,"check")){
535 value=my_string_equal(value,"1")?"true":"false";
537 start_protected_command();
538 uci_send_option(Uci, name, "%s", value);
539 end_protected_command();
541 gui_send(GUI,"Error (unknown option): %s",name);
544 } else if (match(string,"option *")){
546 if(match(name, "Polyglot *")){
547 char *pg_name=Star[0];
548 polyglot_set_option(pg_name,"<empty>");
550 start_protected_command();
552 if(!uci_send_option(Uci, name, "%s", "<empty>")){
553 gui_send(GUI,"Error (unknown option): %s",name);
555 end_protected_command();
557 } else if (XB->has_feature_smp && match(string,"cores *")){
558 int cores=atoi(Star[0]);
560 // updating the number of cores
561 my_log("POLYGLOT setting the number of cores to %d\n",cores);
562 start_protected_command();
563 uci_set_threads(Uci,cores);
564 end_protected_command();
567 gui_send(GUI,"Error (unknown command): %s",string);
569 } else if (match(string,"egtpath * *")){
572 if(my_string_empty(path)){
574 gui_send(GUI,"Error (unknown command): %s",string);
576 if(my_string_case_equal(type,"nalimov") && XB->has_feature_egt_nalimov){
577 // updating NalimovPath
578 my_log("POLYGLOT setting the Nalimov path to %s\n",path);
579 start_protected_command();
580 uci_send_option(Uci,"NalimovPath","%s",path);
581 end_protected_command();
582 }else if(my_string_case_equal(type,"gaviota") && XB->has_feature_egt_gaviota){
583 // updating GaviotaPath
584 my_log("POLYGLOT setting the Gaviota path to %s\n",path);
585 start_protected_command();
586 uci_send_option(Uci,"GaviotaTbPath","%s",path);
587 end_protected_command();
588 }else if(my_string_case_equal(type,"syzygy") && XB->has_feature_egt_syzygy){
589 // updating SyzygyPath
590 my_log("POLYGLOT setting the Syzygy path to %s\n",path);
591 start_protected_command();
592 uci_send_option(Uci,"SyzygyPath","%s",path);
593 end_protected_command();
596 gui_send(GUI,"Error (unsupported table base format): %s",string);
599 } else if (XB->has_feature_memory && match(string,"memory *")){
600 int memory = atoi(Star[0]);
604 // updating the available memory
607 my_log("POLYGLOT setting the amount of memory to %dMb\n",memory);
609 if(XB->has_feature_egt_nalimov && (opt=option_find(Uci->option,"NalimovCache"))){
611 if(h>egt_cache)egt_cache=h;
613 if(XB->has_feature_egt_gaviota &&
614 (opt=option_find(Uci->option,"GaviotaTbCache"))){
616 if(h>egt_cache)egt_cache=h;
618 my_log("POLYGLOT EGTB Cache is %dMb\n",egt_cache);
619 real_memory=memory-egt_cache;
620 opt=option_find(Uci->option,"Hash");
621 if(opt && real_memory > atoi(opt->max)) real_memory = atoi(opt->max); // [HGM] top off
623 start_protected_command();
624 uci_send_option(Uci,"Hash", "%d", real_memory);
625 end_protected_command();
629 gui_send(GUI,"Error (unknown command): %s",string);
632 } else if (match(string,"sd *")) {
634 XB->depth_limit = TRUE;
635 XB->depth_max = atoi(Star[0]);
637 } else if (match(string,"setboard *")) {
639 my_log("POLYGLOT FEN %s\n",Star[0]);
641 if (!game_init(Game,Star[0])) my_fatal("xboard_step(): bad FEN \"%s\"\n",Star[0]);
643 move_list->size = 0; // [HGM] clear all exclude moves
645 State->computer[White] = FALSE;
646 State->computer[Black] = FALSE;
648 XB->new_hack = TRUE; // HACK?
654 } else if (match(string,"st *")) {
656 XB->time_limit = TRUE;
657 XB->time_max = ((double)atoi(Star[0]));
659 } else if (match(string,"time *")) {
661 XB->my_time = ((double)atoi(Star[0])) / 100.0;
662 if (XB->my_time < 0.0) XB->my_time = 0.0;
664 } else if (match(string,"undo")) {
666 move_list->size = 0; // [HGM] clear all exclude moves
668 if (game_pos(Game) >= 1) {
670 game_goto(Game,game_pos(Game)-1);
672 ASSERT(!XB->new_hack);
673 XB->new_hack = FALSE; // HACK?
680 } else if (match(string,"usermove *")) {
682 move_list->size = 0; // [HGM] clear all exclude moves
684 game_get_board(Game,board);
685 move = move_from_san(Star[0],board);
687 if (move != MoveNone && move_is_legal(move,board)) {
689 XB->new_hack = FALSE;
693 // [HGM] externally supplied move means we did not fully play the current stm
694 State->playedAllMoves[colour_is_white(game_turn(Game)) ? White : Black] = FALSE;
701 gui_send(GUI,"Illegal move: %s",Star[0]);
704 } else if (match(string,"variant *")) {
706 if (my_string_equal(Star[0],"fischerandom")) {
707 option_set(Option,"Chess960","true");
709 option_set(Option,"Chess960","false");
712 } else if (match(string,"white")) {
714 if (colour_is_white(game_turn(Game))) {
716 State->computer[White] = FALSE;
717 State->computer[Black] = TRUE;
725 } else if (match(string,"xboard")) {
729 } else if (match(string,".")) { // analyse info
731 if (State->state == ANALYSE) {
732 int depth=Uci->best_depth;//HACK: don't clear engine-output window...
734 ASSERT(Uci->searching);
735 ASSERT(Uci->pending_nb>=1);
737 if (Uci->root_move != MoveNone && move_is_legal(Uci->root_move,Uci->board)) {
738 move_to_san(Uci->root_move,Uci->board,move_string,256);
739 gui_send(GUI,"stat01: %.0f "S64_FORMAT" %d %d %d %s",Uci->time*100.0,Uci->node_nb,/*Uci->*/depth,Uci->root_move_nb-(Uci->root_move_pos+1),Uci->root_move_nb,move_string);
741 gui_send(GUI,"stat01: %.0f "S64_FORMAT" %d %d %d",Uci->time*100.0,Uci->node_nb,/*Uci->*/depth,0,0); // HACK
745 } else if (match(string,"?")) { // move now
747 if (State->state == THINK) {
749 ASSERT(Uci->searching);
750 ASSERT(Uci->pending_nb>=1);
752 // HACK: just send "stop" to the engine
754 if (Uci->searching) {
755 my_log("POLYGLOT STOP SEARCH\n");
756 engine_send(Engine,"stop");
760 } else { // unknown command, maybe a move?
762 game_get_board(Game,board);
763 move = move_from_san(string,board);
765 if (move != MoveNone && move_is_legal(move,board)) {
767 XB->new_hack = FALSE;
774 } else if (move != MoveNone) {
776 gui_send(GUI,"Illegal move: %s",string);
780 gui_send(GUI,"Error (unknown command): %s",string);
786 // xboard2uci_engine_step()
788 void xboard2uci_engine_step(char string[]) {
792 event = uci_parse(Uci,string);
796 if ((event & EVENT_READY) != 0) {
798 // the engine is now ready
802 // if (XB->proto_ver >= 2) xboard_send(XBoard,"feature done=1");
805 if (!DelayPong && XB->ping >= 0) {
806 gui_send(GUI,"pong %d",XB->ping);
811 if ((event & EVENT_MOVE) != 0 && State->state == THINK) {
813 // the engine is playing a move
815 // MEGA HACK: estimate remaining time because XBoard won't send it!
817 my_timer_stop(State->timer);
819 XB->my_time -= my_timer_elapsed_real(State->timer);
820 XB->my_time += XB->inc;
821 if (XB->mps != 0 && (game_move_nb(Game) + 1) % XB->mps == 0) XB->my_time += XB->base;
823 if (XB->my_time < 0.0) XB->my_time = 0.0;
825 // make sure to remember the ponder move
827 State->hint_move=Uci->ponder_move;
829 // play the engine move
831 comp_move(Uci->best_move);
835 if ((event & EVENT_PV) != 0) {
837 // the engine has sent a new PV
841 if ((event & EVENT_INFO) != 0) {
843 // the engine has sent info
847 if((event & (EVENT_DRAW|EVENT_RESIGN))!=0){
848 my_log("POYGLOT draw offer/resign from engine\n");
849 if(option_find(Uci->option,"UCI_DrawOffers")){
850 if(event & EVENT_DRAW)
851 gui_send(GUI,"offer draw");
853 gui_send(GUI,"resign");
856 if(((event & EVENT_ILLEGAL_MOVE)!=0) && (State->state == THINK)){
857 game_get_board(Game,board);
858 if(board->turn==White){
859 gui_send(GUI,"0-1 {polyglot: resign"
860 " (illegal engine move by white: %s)}",Uci->bestmove);
862 gui_send(GUI,"1-0 {polyglot: resign"
863 " (illegal engine move by black: %s)}",Uci->bestmove);
871 // format_xboard_option_line
873 void format_xboard_option_line(char * option_line, option_t *opt){
875 char option_string[StringSize];
877 strcpy(option_line,"");
878 // buffer overflow alert
879 strcat(option_line,"feature option=\"");
881 strcat(option_line,"Polyglot ");
883 sprintf(option_string,"%s",opt->name);
884 strcat(option_line,option_string);
885 sprintf(option_string," -%s",opt->type);
886 strcat(option_line,option_string);
887 if(!IS_BUTTON(opt->type) && strcmp(opt->type,"combo")){
888 if(strcmp(opt->type,"check")){
889 sprintf(option_string," %s",opt->value);
891 sprintf(option_string," %d",
892 my_string_case_equal(opt->value,"true")||
893 my_string_equal(opt->value,"1")
896 strcat(option_line,option_string);
898 if(IS_SPIN(opt->type)){
899 sprintf(option_string," %s",opt->min);
900 strcat(option_line,option_string);
902 if(IS_SPIN(opt->type)){
903 sprintf(option_string," %s",opt->max);
904 strcat(option_line,option_string);
906 for(j=0;j<opt->var_nb;j++){
907 if(!strcmp(opt->var[j],opt->value)){
908 sprintf(option_string," *%s",opt->var[j]);
910 sprintf(option_string," %s",opt->var[j]);
912 strcat(option_line,option_string);
913 if(j!=opt->var_nb-1){
914 strcat(option_line," ///");
917 strcat(option_line,"\"");
918 if(option_get_bool(Option,"WbWorkArounds") &&
919 (tmp=strstr(option_line,"Draw"))){
921 my_log("POLYGLOT Decapitalizing \"Draw\" in option \"%s\"\n",
926 // disarm() // [HGM] cleanse a string of offending double-quotes
928 static char*disarm(const char *s){
932 q = buf + strlen(buf) - 1;
933 while(*q == '"') *q-- = '\0'; // strip trailing quotes
934 while(*p == '"') p++; // strip leading quotes
935 while((q = strchr(p, '"'))) *q = '\''; // replace internal quotes
939 // send_xboard_options()
941 static void send_xboard_options(){
943 char egtfeature[StringSize];
946 gui_send(GUI,"feature done=0");
948 gui_send(GUI,"feature analyze=1");
949 gui_send(GUI,"feature exclude=1");
950 gui_send(GUI,"feature colors=0");
951 gui_send(GUI,"feature draw=1");
952 gui_send(GUI,"feature ics=1");
953 gui_send(GUI,"feature myname=\"%s\"",
954 disarm(option_get_string(Option,"EngineName")));
955 gui_send(GUI,"feature name=1");
956 gui_send(GUI,"feature pause=0");
957 gui_send(GUI,"feature ping=1");
958 gui_send(GUI,"feature playother=1");
959 gui_send(GUI,"feature sigint=1");
960 gui_send(GUI,"feature reuse=1");
961 gui_send(GUI,"feature san=0");
962 gui_send(GUI,"feature setboard=1");
963 gui_send(GUI,"feature sigint=0");
964 gui_send(GUI,"feature sigterm=0");
965 gui_send(GUI,"feature time=1");
966 gui_send(GUI,"feature usermove=1");
967 gui_send(GUI,"feature nps=1");
968 if (XB->has_feature_memory){
969 gui_send(GUI,"feature memory=1");
971 gui_send(GUI,"feature memory=0");
973 if (XB->has_feature_smp){
974 gui_send(GUI,"feature smp=1");
976 gui_send(GUI,"feature smp=0");
979 strncat(egtfeature,"feature egt=\"",StringSize);
980 if (XB->has_feature_egt_nalimov){
982 strncat(egtfeature,"nalimov",StringSize-strlen(egtfeature));
984 if (XB->has_feature_egt_gaviota){
986 strncat(egtfeature,",",StringSize-strlen(egtfeature));
989 strncat(egtfeature,"gaviota",StringSize-strlen(egtfeature));
991 if (XB->has_feature_egt_syzygy){
993 strncat(egtfeature,",",StringSize-strlen(egtfeature));
995 strncat(egtfeature,"syzygy",StringSize-strlen(egtfeature));
997 strncat(egtfeature,"\"",StringSize-strlen(egtfeature));
998 egtfeature[StringSize-1]='\0';
999 gui_send(GUI,egtfeature);
1001 if (option_find(Uci->option,"UCI_Chess960")) {
1002 gui_send(GUI,"feature variants=\"normal,fischerandom\"");
1004 gui_send(GUI,"feature variants=\"normal\"");
1007 xboard2uci_send_options();
1010 void xboard2uci_send_options(){
1011 char option_line[StringSize]="";
1015 option_start_iter(Uci->option);
1016 while((opt=option_next(Uci->option))){
1017 if(my_string_case_equal(opt->name,"UCI_AnalyseMode")) continue;
1018 if(my_string_case_equal(opt->name,"UCI_Opponent")) continue;
1019 if(my_string_case_equal(opt->name,"UCI_Chess960")) continue;
1020 if(my_string_case_equal(opt->name,"UCI_ShowCurrLine")) continue;
1021 if(my_string_case_equal(opt->name,"UCI_ShowRefutations")) continue;
1022 if(my_string_case_equal(opt->name,"UCI_ShredderbasesPath")) continue;
1023 if(my_string_case_equal(opt->name,"UCI_SetPositionValue")) continue;
1024 if(my_string_case_equal(opt->name,"UCI_DrawOffers")) continue;
1025 if(my_string_case_equal(opt->name,"Ponder")) continue;
1026 if(my_string_case_equal(opt->name,"Hash")) continue;
1027 if(my_string_case_equal(opt->name,"NalimovPath")) continue;
1028 if(my_string_case_equal(opt->name,"GaviotaTbPath")) continue;
1029 if(my_string_case_equal(opt->name,"SyzygyPath")) continue;
1030 if((name=uci_thread_option(Uci))!=NULL &&
1031 my_string_case_equal(opt->name,name)) continue;
1032 format_xboard_option_line(option_line,opt);
1034 gui_send(GUI,"%s",option_line);
1038 gui_send(GUI,"feature option=\"Polyglot exclude move -string \"");
1040 option_start_iter(Option);
1041 while((opt=option_next(Option))){
1042 if(opt->mode &XBOARD){
1043 format_xboard_option_line(option_line,opt);
1044 gui_send(GUI,"%s",option_line);
1047 gui_send(GUI,"feature done=1");
1051 // report_best_score()
1053 static int report_best_score(){
1054 if(!option_get_bool(Option,"ScoreWhite") ||
1055 colour_is_white(Uci->board->turn)){
1056 return Uci->best_score;
1058 return -Uci->best_score;
1064 static void comp_move(int move) {
1069 ASSERT(move_is_ok(move));
1071 ASSERT(State->state==THINK);
1072 ASSERT(!XB->analyse);
1074 if(option_get_bool(Option,"RepeatPV"))
1075 send_pv(); // to update time and nodes
1079 game_get_board(Game,board);
1081 if (move_is_castle(move,board) && option_get_bool(Option,"Chess960")) {
1082 if (!move_to_san(move,board,string,256)) my_fatal("comp_move(): move_to_san() failed\n"); // O-O/O-O-O
1084 if (!move_to_can(move,board,string,256)) my_fatal("comp_move(): move_to_can() failed\n");
1087 gui_send(GUI,"move %s",string);
1091 if (option_get_bool(Option,"Resign") && Uci->root_move_nb > 1) {
1093 if (Uci->best_score <= -abs(option_get_int(Option,"ResignScore"))) {
1096 my_log("POLYGLOT %d move%s with resign score\n",State->resign_nb,(State->resign_nb>1)?"s":"");
1098 if (State->resign_nb >= option_get_int(Option,"ResignMoves")) {
1099 if (!option_get_bool(Option,"QueenNeverResigns") || !board_has_queen(board, board->turn)) { // [HGM] suppress resignig with Queen
1100 my_log("POLYGLOT *** RESIGN ***\n");
1101 gui_send(GUI,"resign");
1107 if (State->resign_nb > 0) my_log("POLYGLOT resign reset (State->resign_nb=%d)\n",State->resign_nb);
1108 State->resign_nb = 0;
1120 static void move_step(int move) {
1123 char move_string[256];
1125 ASSERT(move_is_ok(move));
1129 game_get_board(Game,board);
1131 if (move != MoveNone && move_is_legal(move,board)) {
1133 move_to_san(move,board,move_string,256);
1134 my_log("POLYGLOT MOVE %s\n",move_string);
1138 move_to_can(move,board,move_string,256);
1139 my_log("POLYGLOT ILLEGAL MOVE \"%s\"\n",move_string);
1142 my_fatal("move_step(): illegal move \"%s\"\n",move_string);
1147 game_add_move(Game,move);
1153 static void board_update() {
1157 ASSERT(!XB->result);
1159 switch (game_status(Game)) {
1163 gui_send(GUI,"1-0 {White mates}");
1166 gui_send(GUI,"0-1 {Black mates}");
1169 gui_send(GUI,"1/2-1/2 {Stalemate}");
1172 gui_send(GUI,"1/2-1/2 {Draw by insufficient material}");
1175 gui_send(GUI,"1/2-1/2 {Draw by fifty-move rule}");
1177 case DRAW_REPETITION:
1178 gui_send(GUI,"1/2-1/2 {Draw by repetition}");
1188 static void mess() {
1190 // clear state variables
1192 State->resign_nb = 0;
1193 State->exp_move = MoveNone;
1194 my_timer_reset(State->timer);
1196 // abort a possible search
1200 // calculate the new state
1203 } else if (!active()) {
1204 State->state = WAIT;
1205 my_log("POLYGLOT WAIT\n");
1206 } else if (XB->analyse) {
1207 State->state = ANALYSE;
1208 my_log("POLYGLOT ANALYSE\n");
1209 } else if (State->computer[game_turn(Game)]) {
1210 State->state = THINK;
1211 my_log("POLYGLOT THINK\n");
1213 State->state = WAIT;
1214 my_log("POLYGLOT WAIT\n");
1222 static void no_mess(int move) {
1224 ASSERT(move_is_ok(move));
1226 // just received a move, calculate the new state
1230 } else if (!active()) {
1232 stop_search(); // abort a possible search
1234 State->state = WAIT;
1235 State->exp_move = MoveNone;
1237 my_log("POLYGLOT WAIT\n");
1239 } else if (State->state == WAIT) {
1241 ASSERT(State->computer[game_turn(Game)]);
1242 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1243 ASSERT(!XB->analyse);
1245 my_log("POLYGLOT WAIT -> THINK\n");
1247 State->state = THINK;
1248 State->exp_move = MoveNone;
1250 } else if (State->state == THINK) {
1252 ASSERT(!State->computer[game_turn(Game)]);
1253 ASSERT(State->computer[colour_opp(game_turn(Game))]);
1254 ASSERT(!XB->analyse);
1256 if (ponder() && ponder_ok(Uci->ponder_move)) {
1258 my_log("POLYGLOT THINK -> PONDER\n");
1260 State->state = PONDER;
1261 State->exp_move = Uci->ponder_move;
1265 my_log("POLYGLOT THINK -> WAIT\n");
1267 State->state = WAIT;
1268 State->exp_move = MoveNone;
1271 } else if (State->state == PONDER) {
1273 ASSERT(State->computer[game_turn(Game)]);
1274 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1275 ASSERT(!XB->analyse);
1277 if (move == State->exp_move && Uci->searching) {
1279 ASSERT(Uci->searching);
1280 ASSERT(Uci->pending_nb>=1);
1282 my_timer_start(State->timer);//also resets
1284 my_log("POLYGLOT PONDER -> THINK (*** HIT ***)\n");
1285 engine_send(Engine,"ponderhit");
1287 State->state = THINK;
1288 State->exp_move = MoveNone;
1290 send_pv(); // update display
1292 return; // do not launch a new search
1296 my_log("POLYGLOT PONDER -> THINK (miss)\n");
1300 State->state = THINK;
1301 State->exp_move = MoveNone;
1304 } else if (State->state == ANALYSE) {
1306 ASSERT(XB->analyse);
1308 my_log("POLYGLOT ANALYSE -> ANALYSE\n");
1320 // start_protected_command()
1322 static void start_protected_command(){
1326 static void end_protected_command(){
1327 if(Uci->ready){ // not init faze
1328 uci_send_isready_sync(Uci); // gobble up spurious "bestmove"
1330 update_remaining_time();
1331 search_update(); // relaunch search if necessary
1334 // update_remaining_time()
1336 static void update_remaining_time(){
1338 if(State->timer->running){
1339 my_timer_stop(State->timer);
1340 reduce = my_timer_elapsed_real(State->timer);
1341 my_log("POLYGLOT reducing remaing time by %f seconds\n",reduce);
1342 XB->my_time -= reduce;
1343 if(XB->my_time<0.0){
1352 static void search_update() {
1358 ASSERT(!Uci->searching);
1363 // launch a new search if needed
1367 if (State->state == THINK || State->state == PONDER || State->state == ANALYSE) {
1369 // [VdB] moved up as we need the move number
1371 game_get_board(Game,Uci->board);
1375 if (State->state == THINK &&
1376 option_get_bool(Option,"Book") &&
1377 Uci->board->move_nb<option_get_int(Option,"BookDepth")
1381 move = book_move(Uci->board,option_get_bool(Option,"BookRandom"));
1383 if (move != MoveNone && move_is_legal(move,Uci->board)) {
1385 my_log("POLYGLOT *BOOK MOVE*\n");
1387 search_clear(); // clears Uci->ponder_move
1388 Uci->best_move = move;
1390 board_copy(board,Uci->board);
1391 move_do(board,move);
1392 Uci->ponder_move = book_move(board,FALSE); // expected move = best book move
1394 Uci->best_pv[0] = Uci->best_move;
1395 Uci->best_pv[1] = Uci->ponder_move; // can be MoveNone
1396 Uci->best_pv[2] = MoveNone;
1398 comp_move(Uci->best_move);
1406 my_log("POLYGLOT START SEARCH\n");
1410 uci_send_option(Uci,"UCI_Chess960","%s",
1411 option_get_bool(Option,"Chess960")?"true":"false");
1413 if (option_get_int(Option,"UCIVersion") >= 2) {
1414 uci_send_option(Uci,"UCI_Opponent","none none %s %s",(XB->computer)?"computer":"human",XB->name);
1415 uci_send_option(Uci,"UCI_AnalyseMode","%s",(XB->analyse)?"true":"false");
1418 uci_send_option(Uci,"Ponder","%s",ponder()?"true":"false");
1422 move = (State->state == PONDER) ? State->exp_move : MoveNone;
1423 send_board(move); // updates Uci->board global variable
1427 if (State->state == THINK || State->state == PONDER) {
1429 engine_send_queue(Engine,"go");
1431 if (XB->time_limit) {
1433 // fixed time per move
1435 if(XB->node_rate > 0){
1436 engine_send_queue(Engine,
1438 XB->time_max*((double)XB->node_rate));
1440 double computed_time;
1442 st_fudge=(double) option_get_int(Option,"STFudge");
1443 my_log("POLYGLOT Giving engine %.0fmsec extra time.\n",st_fudge);
1444 computed_time=XB->time_max*1000.0-st_fudge;
1445 if(computed_time< 1.0){
1448 engine_send_queue(Engine,
1457 if(XB->node_rate > 0) {
1461 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1463 time = XB->my_time / move_nb;
1467 if(time > XB->my_time){
1470 engine_send_queue(Engine,
1472 time*XB->node_rate);
1475 if (colour_is_white(Uci->board->turn)) {
1476 engine_send_queue(Engine,
1477 " wtime %.0f btime %.0f",
1478 XB->my_time*1000.0,XB->opp_time*1000.0);
1480 engine_send_queue(Engine,
1481 " wtime %.0f btime %.0f",
1482 XB->opp_time*1000.0,XB->my_time*1000.0);
1485 if (XB->inc != 0.0){
1486 engine_send_queue(Engine,
1487 " winc %.0f binc %.0f",
1488 XB->inc*1000.0,XB->inc*1000.0);
1492 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1493 ASSERT(move_nb>=1&&move_nb<=XB->mps);
1495 engine_send_queue(Engine," movestogo %d",move_nb);
1499 if (XB->depth_limit) engine_send_queue(Engine," depth %d",XB->depth_max);
1501 if (State->state == PONDER) engine_send_queue(Engine," ponder");
1503 engine_send(Engine,""); // newline
1505 } else if (State->state == ANALYSE) {
1507 char move_string[256];
1509 engine_send_queue(Engine,"go infinite");
1511 if(list_size(move_list)) {
1513 game_get_board(Game,board);
1514 engine_send_queue(Engine," searchmoves");
1515 for(i=0; i<list_size(move_list); i++) {
1516 if(!move_list->value[i]) {
1517 move_to_can(move_list->move[i],board,move_string,256);
1518 engine_send_queue(Engine," %s",move_string);
1522 engine_send(Engine,""); // newline
1531 ASSERT(!Uci->searching);
1535 Uci->searching = TRUE;
1542 static void search_clear() {
1548 my_timer_start(State->timer);//also resets
1553 static bool active() {
1557 if (game_status(Game) != PLAYING) return FALSE; // game ended
1561 if (XB->analyse) return TRUE; // analysing
1562 if (!State->computer[White] && !State->computer[Black]) return FALSE; // force mode
1563 if (XB->new_hack || XB->result) return FALSE; // unstarted or ended game
1565 return TRUE; // playing
1570 static bool ponder() {
1572 return XB->ponder && (option_get_bool(Option,"CanPonder") ||
1573 option_find(Uci->option,"Ponder"));
1577 static bool ponder_ok(int move) {
1581 ASSERT(move==MoveNone||move_is_ok(move));
1583 // legal ponder move?
1585 if (move == MoveNone) return FALSE;
1587 game_get_board(Game,board);
1588 if (!move_is_legal(move,board)) return FALSE;
1590 // UCI-legal resulting position?
1592 game_add_move(Game,move);
1594 game_get_board(Game,board);
1595 status = game_status(Game);
1597 game_rem_move(Game);
1599 if (status != PLAYING) return FALSE; // game ended
1601 if (option_get_bool(Option,"Book") && is_in_book(board)) {
1610 static void stop_search() {
1612 if (Uci->searching) {
1614 ASSERT(Uci->searching);
1615 ASSERT(Uci->pending_nb>=1);
1617 my_log("POLYGLOT STOP SEARCH\n");
1620 engine_send(Engine,"stop");
1621 Uci->searching = FALSE;
1624 if (option_get_bool(Option,"SyncStop")) {
1625 uci_send_stop_sync(Uci);
1634 static void send_board(int extra_move) {
1643 ASSERT(extra_move==MoveNone||move_is_ok(extra_move));
1645 ASSERT(!Uci->searching);
1649 game_get_board(Game,Uci->board);
1650 if (extra_move != MoveNone) move_do(Uci->board,extra_move);
1652 board_to_fen(Uci->board,fen,256);
1653 my_log("POLYGLOT FEN %s\n",fen);
1655 ASSERT(board_can_play(Uci->board));
1660 end = game_pos(Game);
1665 game_get_board_ex(Game,board,start);
1666 board_to_fen(board,string,256);
1668 engine_send_queue(Engine,"position");
1670 if (my_string_equal(string,StartFen)) {
1671 engine_send_queue(Engine," startpos");
1673 engine_send_queue(Engine," fen %s",string);
1678 if (end > start || extra_move != MoveNone) engine_send_queue(Engine," moves");
1680 for (pos = start; pos < end; pos++) { // game moves
1682 move = game_move(Game,pos);
1684 move_to_can(move,board,string,256);
1685 engine_send_queue(Engine," %s",string);
1687 move_do(board,move);
1690 if (extra_move != MoveNone) { // move to ponder on
1691 move_to_can(extra_move,board,string,256);
1692 engine_send_queue(Engine," %s",string);
1697 engine_send(Engine,""); // newline
1702 static void send_info() {
1704 if(option_get_bool(Option,"WbWorkArounds2")){
1705 // Silly bug in some versions of WinBoard.
1706 // depth <=1 clears the engine output window.
1707 // Why shouldn't an engine be allowed to send info at depth 1?
1712 if(!strncmp(Uci->info, "xboard ", 7)) gui_send(GUI,"%s",Uci->info+7); else // kludge to allow UCI engines to use WB protocol
1713 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth>min_depth?Uci->best_depth:min_depth,
1714 0,0.0,U64(0),Uci->info);
1719 //define EXT_INFO_FORMAT "{%d,%.0f,"S64_FORMAT"} "
1720 #define EXT_INFO_FORMAT " %2d %4.0f "S64_FORMAT"\t"
1722 static void send_pv() {
1724 char pv_string[StringSize];
1727 char move_string[StringSize];
1729 ASSERT(State->state!=WAIT);
1731 if (Uci->best_depth == 0) return;
1733 // xboard search information
1737 if (State->state == THINK || State->state == ANALYSE) {
1739 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1741 if(Uci->depth==-1) //hack to clear the engine output window
1742 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" ",0,report_best_score(),Uci->time*100.0,Uci->node_nb);
1743 if(option_get_bool(Option,"ShowTbHits"))
1744 gui_send(GUI,"%d %+d %.0f "S64_FORMAT EXT_INFO_FORMAT"%s%c",Uci->best_depth,report_best_score(),
1745 Uci->time*100.0,Uci->node_nb,Uci->sel_depth,Uci->speed/1e3,Uci->tbhit_nb,pv_string,Uci->bound_type);
1747 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s%c",Uci->best_depth,report_best_score(),Uci->time*100.0,Uci->node_nb,pv_string,Uci->bound_type);
1749 } else if (State->state == PONDER &&
1750 option_get_bool(Option,"ShowPonder")) {
1752 game_get_board(Game,board);
1753 move = State->exp_move;
1755 if (move != MoveNone && move_is_legal(move,board)) {
1756 move_to_san(move,board,move_string,256);
1757 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1758 if(option_get_bool(Option,"ShowTbHits"))
1759 gui_send(GUI,"%d %+d %.0f "S64_FORMAT EXT_INFO_FORMAT"(%s) %s%c",Uci->best_depth,report_best_score(),
1760 Uci->time*100.0,Uci->node_nb,Uci->sel_depth,Uci->speed/1e3,Uci->tbhit_nb,move_string,pv_string,Uci->bound_type);
1762 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" (%s) %s%c",Uci->best_depth,report_best_score(),
1763 Uci->time*100.0,Uci->node_nb,move_string,pv_string,Uci->bound_type);
1770 if ((Uci->searching &&
1771 option_get_bool(Option,"KibitzPV") &&
1772 Uci->time >= option_get_double(Option,"KibitzDelay"))
1773 || (!Uci->searching && option_get_bool(Option,"KibitzMove"))) {
1775 if (State->state == THINK || State->state == ANALYSE) {
1777 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1778 if(kibitz_throttle(Uci->searching)){
1779 gui_send(GUI,"%s depth=%d time=%.2f node="S64_FORMAT" speed=%.0f score=%+.2f pv=\"%s\"",option_get_string(Option,"KibitzCommand"),Uci->best_depth,Uci->time,Uci->node_nb,Uci->speed,((double)report_best_score())/100.0,pv_string);
1781 } else if (State->state == PONDER) {
1783 game_get_board(Game,board);
1784 move = State->exp_move;
1786 if (move != MoveNone && move_is_legal(move,board)) {
1787 move_to_san(move,board,move_string,256);
1788 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1789 if(kibitz_throttle(Uci->searching)){
1790 gui_send(GUI,"%s depth=%d time=%.2f node="S64_FORMAT" speed=%.0f score=%+.2f pv=\"(%s) %s\"",option_get_string(Option,"KibitzCommand"),Uci->best_depth,Uci->time,Uci->node_nb,Uci->speed,((double)report_best_score())/100.0,move_string,pv_string);
1797 // kibitz_throttle()
1799 static bool kibitz_throttle(bool searching){
1801 static time_t lastKibitzMove=0;
1802 static time_t lastKibitzPV=0;
1803 curr_time = time(NULL);
1804 if(searching){ // KibitzPV
1806 (option_get_int(Option,"KibitzInterval") + lastKibitzPV)){
1807 lastKibitzPV=curr_time;
1810 }else{ // KibitzMove
1812 (option_get_int(Option,"KibitzInterval") + lastKibitzMove)){
1813 lastKibitzPV=curr_time;
1814 lastKibitzMove=curr_time;
1823 static void learn(int result) {
1829 ASSERT(result>=-1&&result<=+1);
1832 // ASSERT(State->computer[White]||State->computer[Black]);
1838 // [HGM] does not account for the hypothetical possibility we played both sides!
1839 if (State->playedAllMoves[White]) {
1841 } else if (State->playedAllMoves[Black]) {
1845 return; // [HGM] if we did not play all moves for some side, do not learn, but don't make a fuss!
1849 } else if (result > 0) {
1850 my_log("POLYGLOT *LEARN WIN*\n");
1851 } else if (result < 0) {
1852 my_log("POLYGLOT *LEARN LOSS*\n");
1854 my_log("POLYGLOT *LEARN DRAW*\n");
1859 for (; pos < Game->size; pos += 2) {
1861 game_get_board_ex(Game,board,pos);
1862 move = game_move(Game,pos);
1864 book_learn_move(board,move,result);
1870 // end of xboard2uci.c