23 #include "move_legal.h"
30 #include "xboard2uci.h"
34 #define StringSize 4096
38 static const bool UseDebug = FALSE;
39 static const bool DelayPong = FALSE;
45 bool computer[ColourNb];
46 bool playedAllMoves[ColourNb];
54 bool has_feature_memory;
56 bool has_feature_egt_nalimov;
57 bool has_feature_egt_gaviota;
62 bool new_hack; // "new" is a C++ keyword
85 typedef enum { WAIT, THINK, PONDER, ANALYSE } dummy_state_t;
89 static state_t State[1];
94 static void comp_move (int move);
95 static void move_step (int move);
96 static void board_update ();
99 static void no_mess (int move);
101 static void search_update ();
102 static void search_clear ();
103 static void update_remaining_time();
104 static int report_best_score();
105 static bool kibitz_throttle (bool searching);
106 static void start_protected_command();
107 static void end_protected_command();
109 static bool active ();
110 static bool ponder ();
111 static bool ponder_ok (int ponder_move);
113 static void stop_search ();
115 static void send_board (int extra_move);
116 static void send_pv ();
117 static void send_info ();
119 static void send_xboard_options ();
121 static void learn (int result);
128 void xboard2uci_init() {
137 State->computer[White] = FALSE;
138 State->computer[Black] = TRUE;
140 State->exp_move = MoveNone;
141 State->hint_move = MoveNone;
142 State->resign_nb = 0;
143 my_timer_reset(State->timer);
145 // yes there are engines that do not have the "Hash" option....
146 XB->has_feature_memory= (option_find(Uci->option,"Hash")!=NULL);
147 XB->has_feature_smp = (uci_thread_option(Uci)!=NULL);
148 // TODO: support for other types of table bases
149 // This is a quick hack.
150 XB->has_feature_egt_nalimov = (option_find(Uci->option,"NalimovPath")!=NULL);
151 XB->has_feature_egt_gaviota = (option_find(Uci->option,"GaviotaTbPath")!=NULL);
153 XB->computer = FALSE;
155 my_string_set(&XB->name,"<empty>");
168 XB->time_limit = FALSE;
171 XB->depth_limit = FALSE;
175 XB->opp_time = 300.0;
180 // xboard2uci_gui_step()
182 void xboard2uci_gui_step(char string[]) {
185 char move_string[256];
190 } else if (match(string,"accepted *")) {
194 } else if (match(string,"analyze")) {
196 State->computer[White] = FALSE;
197 State->computer[Black] = FALSE;
200 XB->new_hack = FALSE;
206 } else if (match(string,"bk")) {
208 if (option_get_bool(Option,"Book")) {
209 game_get_board(Game,board);
213 } else if (match(string,"black")) {
215 if (colour_is_black(game_turn(Game))) {
217 State->computer[White] = TRUE;
218 State->computer[Black] = FALSE;
226 } else if (match(string,"computer")) {
230 } else if (match(string,"draw")) {
231 if(option_find(Uci->option,"UCI_DrawOffers")){
232 my_log("POLYGLOT draw from XB received");
233 uci_send_option(Uci,"DrawOffer","%s","draw");}
234 else if (option_get_bool(Option,"HandleDraws") && Uci->root_move_nb > 20) { // [HGM] PG draw handling
235 my_log("POLYGLOT draw from XB received");
236 if (Uci->best_score <= -option_get_int(Option,"ContemptScore"))
237 gui_send(GUI,"offer draw");}
238 } else if (match(string,"easy")) {
244 } else if (match(string,"edit")) {
248 gui_send(GUI,"Error (unknown command): %s",string);
250 } else if (match(string,"exit")) {
252 State->computer[White] = FALSE;
253 State->computer[Black] = FALSE;
259 } else if (match(string,"force")) {
261 State->computer[White] = FALSE;
262 State->computer[Black] = FALSE;
266 } else if (match(string,"go")) {
268 State->computer[game_turn(Game)] = TRUE;
269 State->computer[colour_opp(game_turn(Game))] = FALSE;
271 XB->new_hack = FALSE;
277 } else if (match(string,"hard")) {
283 } else if (match(string,"hint")) {
286 game_get_board(Game,board);
287 if (option_get_bool(Option,"Book")) {
289 move = book_move(board,FALSE);
291 if(move==MoveNone && State->hint_move!=MoveNone){
292 move=State->hint_move;
295 if (move != MoveNone && move_is_legal(move,board)) {
296 move_to_san(move,board,move_string,256);
297 gui_send(GUI,"Hint: %s",move_string);
300 } else if (match(string,"ics *")) {
304 } else if (match(string,"level * *:* *")) {
306 XB->mps = atoi(Star[0]);
307 XB->base = ((double)atoi(Star[1])) * 60.0 + ((double)atoi(Star[2]));
308 XB->inc = ((double)atoi(Star[3]));
310 } else if (match(string,"level * * *")) {
312 XB->mps = atoi(Star[0]);
313 XB->base = ((double)atoi(Star[1])) * 60.0;
314 XB->inc = ((double)atoi(Star[2]));
316 } else if (match(string,"name *")) {
318 my_string_set(&XB->name,Star[0]);
320 } else if (match(string,"new")) {
322 uci_send_isready_sync(Uci);
323 my_log("POLYGLOT NEW GAME\n");
325 option_set(Option,"Chess960","false");
330 State->computer[White] = FALSE;
331 State->computer[Black] = FALSE;
333 State->computer[White] = FALSE;
334 State->computer[Black] = TRUE;
335 State->playedAllMoves[White] = TRUE; // [HGM]
336 State->playedAllMoves[Black] = TRUE;
342 XB->depth_limit = FALSE;
345 XB->computer = FALSE;
346 my_string_set(&XB->name,"<empty>");
351 uci_send_ucinewgame(Uci);
353 } else if (match(string,"nopost")) {
357 } else if (match(string,"otim *")) {
359 XB->opp_time = ((double)atoi(Star[0])) / 100.0;
360 if (XB->opp_time < 0.0) XB->opp_time = 0.0;
362 } else if (match(string,"pause")) {
366 gui_send(GUI,"Error (unknown command): %s",string);
368 } else if (match(string,"ping *")) {
370 // HACK; TODO: answer only after an engine move
373 if (XB->ping >= 0) gui_send(GUI,"pong %d",XB->ping); // HACK: get rid of old ping
374 XB->ping = atoi(Star[0]);
375 uci_send_isready_sync(Uci);
377 ASSERT(XB->ping==-1);
378 gui_send(GUI,"pong %s",Star[0]);
380 } else if (match(string,"nps *")) {
382 // fake WB play-by-nodes mode
383 XB->node_rate = atoi(Star[0]);
384 } else if (match(string,"playother")) {
386 State->computer[game_turn(Game)] = FALSE;
387 State->computer[colour_opp(game_turn(Game))] = TRUE;
389 XB->new_hack = FALSE;
395 } else if (match(string,"post")) {
399 } else if (match(string,"protover *")) {
400 XB->proto_ver = atoi(Star[0]);
401 ASSERT(XB->proto_ver>=2);
402 send_xboard_options();
404 } else if (match(string,"quit")) {
405 my_log("POLYGLOT *** \"quit\" from GUI ***\n");
407 } else if (match(string,"random")) {
411 } else if (match(string,"rating * *")) {
415 } else if (match(string,"remove")) {
417 if (game_pos(Game) >= 2) {
419 game_goto(Game,game_pos(Game)-2);
421 ASSERT(!XB->new_hack);
422 XB->new_hack = FALSE; // HACK?
429 } else if (match(string,"rejected *")) {
433 } else if (match(string,"reset")) { // protover 3?
437 gui_send(GUI,"Error (unknown command): %s",string);
440 || match(string,"result * {*}")
441 || match(string,"result * {* }")
442 || match(string,"result * { *}")
443 || match(string,"result * { * }")) {
445 my_log("POLYGLOT GAME END\n");
453 if (option_get_bool(Option,"Book") &&
454 option_get_bool(Option,"BookLearn")) {
457 } else if (my_string_equal(Star[0],"1-0")) {
459 } else if (my_string_equal(Star[0],"0-1")) {
461 } else if (my_string_equal(Star[0],"1/2-1/2")) {
465 } else if (match(string,"resume")) {
469 gui_send(GUI,"Error (unknown command): %s",string);
471 } else if (match(string,"option *=*") ||
472 match(string,"option * =*") ||
473 match(string,"option *= *") ||
474 match(string,"option * = *")
478 if(match(name, "Polyglot *")){
479 char *pg_name=Star[0];
480 polyglot_set_option(pg_name,value);
482 option_t *opt=option_find(Uci->option,name);
484 if(my_string_case_equal(opt->type,"check")){
485 value=my_string_equal(value,"1")?"true":"false";
487 start_protected_command();
488 uci_send_option(Uci, name, "%s", value);
489 end_protected_command();
491 gui_send(GUI,"Error (unknown option): %s",name);
494 } else if (match(string,"option *")){
496 if(match(name, "Polyglot *")){
497 char *pg_name=Star[0];
498 polyglot_set_option(pg_name,"<empty>");
500 start_protected_command();
502 if(!uci_send_option(Uci, name, "%s", "<empty>")){
503 gui_send(GUI,"Error (unknown option): %s",name);
505 end_protected_command();
507 } else if (XB->has_feature_smp && match(string,"cores *")){
508 int cores=atoi(Star[0]);
510 // updating the number of cores
511 my_log("POLYGLOT setting the number of cores to %d\n",cores);
512 start_protected_command();
513 uci_set_threads(Uci,cores);
514 end_protected_command();
517 gui_send(GUI,"Error (unknown command): %s",string);
519 } else if (match(string,"egtpath * *")){
522 if(my_string_empty(path)){
524 gui_send(GUI,"Error (unknown command): %s",string);
526 if(my_string_case_equal(type,"nalimov") && XB->has_feature_egt_nalimov){
527 // updating NalimovPath
528 my_log("POLYGLOT setting the Nalimov path to %s\n",path);
529 start_protected_command();
530 uci_send_option(Uci,"NalimovPath","%s",path);
531 end_protected_command();
532 }else if(my_string_case_equal(type,"gaviota") && XB->has_feature_egt_gaviota){
533 // updating GaviotaPath
534 my_log("POLYGLOT setting the Gaviota path to %s\n",path);
535 start_protected_command();
536 uci_send_option(Uci,"GaviotaTbPath","%s",path);
537 end_protected_command();
540 gui_send(GUI,"Error (unsupported table base format): %s",string);
543 } else if (XB->has_feature_memory && match(string,"memory *")){
544 int memory = atoi(Star[0]);
548 // updating the available memory
550 my_log("POLYGLOT setting the amount of memory to %dMb\n",memory);
551 if(XB->has_feature_egt_nalimov && (opt=option_find(Uci->option,"NalimovCache"))){
552 egt_cache=atoi(opt->value);
553 }else if(XB->has_feature_egt_gaviota &&
554 (opt=option_find(Uci->option,"GaviotaTbCache"))){
555 egt_cache=atoi(opt->value);
559 my_log("POLYGLOT EGTB Cache is %dMb\n",egt_cache);
560 real_memory=memory-egt_cache;
562 start_protected_command();
563 uci_send_option(Uci,"Hash", "%d", real_memory);
564 end_protected_command();
568 gui_send(GUI,"Error (unknown command): %s",string);
571 } else if (match(string,"sd *")) {
573 XB->depth_limit = TRUE;
574 XB->depth_max = atoi(Star[0]);
576 } else if (match(string,"setboard *")) {
578 my_log("POLYGLOT FEN %s\n",Star[0]);
580 if (!game_init(Game,Star[0])) my_fatal("xboard_step(): bad FEN \"%s\"\n",Star[0]);
582 State->computer[White] = FALSE;
583 State->computer[Black] = FALSE;
585 XB->new_hack = TRUE; // HACK?
591 } else if (match(string,"st *")) {
593 XB->time_limit = TRUE;
594 XB->time_max = ((double)atoi(Star[0]));
596 } else if (match(string,"time *")) {
598 XB->my_time = ((double)atoi(Star[0])) / 100.0;
599 if (XB->my_time < 0.0) XB->my_time = 0.0;
601 } else if (match(string,"undo")) {
603 if (game_pos(Game) >= 1) {
605 game_goto(Game,game_pos(Game)-1);
607 ASSERT(!XB->new_hack);
608 XB->new_hack = FALSE; // HACK?
615 } else if (match(string,"usermove *")) {
617 game_get_board(Game,board);
618 move = move_from_san(Star[0],board);
620 if (move != MoveNone && move_is_legal(move,board)) {
622 XB->new_hack = FALSE;
626 // [HGM] externally supplied move means we did not fully play the current stm
627 State->playedAllMoves[colour_is_white(game_turn(Game)) ? White : Black] = FALSE;
634 gui_send(GUI,"Illegal move: %s",Star[0]);
637 } else if (match(string,"variant *")) {
639 if (my_string_equal(Star[0],"fischerandom")) {
640 option_set(Option,"Chess960","true");
642 option_set(Option,"Chess960","false");
645 } else if (match(string,"white")) {
647 if (colour_is_white(game_turn(Game))) {
649 State->computer[White] = FALSE;
650 State->computer[Black] = TRUE;
658 } else if (match(string,"xboard")) {
662 } else if (match(string,".")) { // analyse info
664 if (State->state == ANALYSE) {
665 int depth=Uci->best_depth;//HACK: don't clear engine-output window...
667 ASSERT(Uci->searching);
668 ASSERT(Uci->pending_nb>=1);
670 if (Uci->root_move != MoveNone && move_is_legal(Uci->root_move,Uci->board)) {
671 move_to_san(Uci->root_move,Uci->board,move_string,256);
672 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);
674 gui_send(GUI,"stat01: %.0f "S64_FORMAT" %d %d %d",Uci->time*100.0,Uci->node_nb,/*Uci->*/depth,0,0); // HACK
678 } else if (match(string,"?")) { // move now
680 if (State->state == THINK) {
682 ASSERT(Uci->searching);
683 ASSERT(Uci->pending_nb>=1);
685 // HACK: just send "stop" to the engine
687 if (Uci->searching) {
688 my_log("POLYGLOT STOP SEARCH\n");
689 engine_send(Engine,"stop");
693 } else { // unknown command, maybe a move?
695 game_get_board(Game,board);
696 move = move_from_san(string,board);
698 if (move != MoveNone && move_is_legal(move,board)) {
700 XB->new_hack = FALSE;
707 } else if (move != MoveNone) {
709 gui_send(GUI,"Illegal move: %s",string);
713 gui_send(GUI,"Error (unknown command): %s",string);
719 // xboard2uci_engine_step()
721 void xboard2uci_engine_step(char string[]) {
725 event = uci_parse(Uci,string);
729 if ((event & EVENT_READY) != 0) {
731 // the engine is now ready
735 // if (XB->proto_ver >= 2) xboard_send(XBoard,"feature done=1");
738 if (!DelayPong && XB->ping >= 0) {
739 gui_send(GUI,"pong %d",XB->ping);
744 if ((event & EVENT_MOVE) != 0 && State->state == THINK) {
746 // the engine is playing a move
748 // MEGA HACK: estimate remaining time because XBoard won't send it!
750 my_timer_stop(State->timer);
752 XB->my_time -= my_timer_elapsed_real(State->timer);
753 XB->my_time += XB->inc;
754 if (XB->mps != 0 && (game_move_nb(Game) + 1) % XB->mps == 0) XB->my_time += XB->base;
756 if (XB->my_time < 0.0) XB->my_time = 0.0;
758 // make sure to remember the ponder move
760 State->hint_move=Uci->ponder_move;
762 // play the engine move
764 comp_move(Uci->best_move);
768 if ((event & EVENT_PV) != 0) {
770 // the engine has sent a new PV
774 if ((event & EVENT_INFO) != 0) {
776 // the engine has sent info
780 if((event & (EVENT_DRAW|EVENT_RESIGN))!=0){
781 my_log("POYGLOT draw offer/resign from engine\n");
782 if(option_find(Uci->option,"UCI_DrawOffers")){
783 if(event & EVENT_DRAW)
784 gui_send(GUI,"offer draw");
786 gui_send(GUI,"resign");
789 if(((event & EVENT_ILLEGAL_MOVE)!=0) && (State->state == THINK)){
790 game_get_board(Game,board);
791 if(board->turn==White){
792 gui_send(GUI,"0-1 {polyglot: resign"
793 " (illegal engine move by white: %s)}",Uci->bestmove);
795 gui_send(GUI,"1-0 {polyglot: resign"
796 " (illegal engine move by black: %s)}",Uci->bestmove);
804 // format_xboard_option_line
806 void format_xboard_option_line(char * option_line, option_t *opt){
808 char option_string[StringSize];
810 strcpy(option_line,"");
811 // buffer overflow alert
812 strcat(option_line,"feature option=\"");
814 strcat(option_line,"Polyglot ");
816 sprintf(option_string,"%s",opt->name);
817 strcat(option_line,option_string);
818 sprintf(option_string," -%s",opt->type);
819 strcat(option_line,option_string);
820 if(!IS_BUTTON(opt->type) && strcmp(opt->type,"combo")){
821 if(strcmp(opt->type,"check")){
822 sprintf(option_string," %s",opt->value);
824 sprintf(option_string," %d",
825 my_string_case_equal(opt->value,"true")||
826 my_string_equal(opt->value,"1")
829 strcat(option_line,option_string);
831 if(IS_SPIN(opt->type)){
832 sprintf(option_string," %s",opt->min);
833 strcat(option_line,option_string);
835 if(IS_SPIN(opt->type)){
836 sprintf(option_string," %s",opt->max);
837 strcat(option_line,option_string);
839 for(j=0;j<opt->var_nb;j++){
840 if(!strcmp(opt->var[j],opt->value)){
841 sprintf(option_string," *%s",opt->var[j]);
843 sprintf(option_string," %s",opt->var[j]);
845 strcat(option_line,option_string);
846 if(j!=opt->var_nb-1){
847 strcat(option_line," ///");
850 strcat(option_line,"\"");
851 if(option_get_bool(Option,"WbWorkArounds") &&
852 (tmp=strstr(option_line,"Draw"))){
854 my_log("POLYGLOT Decapitalizing \"Draw\" in option \"%s\"\n",
859 // disarm() // [HGM] cleanse a string of offending double-quotes
861 static char*disarm(const char *s){
865 q = buf + strlen(buf) - 1;
866 while(*q == '"') *q-- = '\0'; // strip trailing quotes
867 while(*p == '"') p++; // strip leading quotes
868 while((q = strchr(p, '"'))) *q = '\''; // replace internal quotes
872 // send_xboard_options()
874 static void send_xboard_options(){
876 char egtfeature[StringSize];
879 gui_send(GUI,"feature done=0");
881 gui_send(GUI,"feature analyze=1");
882 gui_send(GUI,"feature colors=0");
883 gui_send(GUI,"feature draw=1");
884 gui_send(GUI,"feature ics=1");
885 gui_send(GUI,"feature myname=\"%s\"",
886 disarm(option_get_string(Option,"EngineName")));
887 gui_send(GUI,"feature name=1");
888 gui_send(GUI,"feature pause=0");
889 gui_send(GUI,"feature ping=1");
890 gui_send(GUI,"feature playother=1");
891 gui_send(GUI,"feature sigint=1");
892 gui_send(GUI,"feature reuse=1");
893 gui_send(GUI,"feature san=0");
894 gui_send(GUI,"feature setboard=1");
895 gui_send(GUI,"feature sigint=0");
896 gui_send(GUI,"feature sigterm=0");
897 gui_send(GUI,"feature time=1");
898 gui_send(GUI,"feature usermove=1");
899 gui_send(GUI,"feature nps=1");
900 if (XB->has_feature_memory){
901 gui_send(GUI,"feature memory=1");
903 gui_send(GUI,"feature memory=0");
905 if (XB->has_feature_smp){
906 gui_send(GUI,"feature smp=1");
908 gui_send(GUI,"feature smp=0");
911 strncat(egtfeature,"feature egt=\"",StringSize);
912 if (XB->has_feature_egt_nalimov){
914 strncat(egtfeature,"nalimov",StringSize-strlen(egtfeature));
916 if (XB->has_feature_egt_gaviota){
918 strncat(egtfeature,",",StringSize-strlen(egtfeature));
920 strncat(egtfeature,"gaviota",StringSize-strlen(egtfeature));
922 strncat(egtfeature,"\"",StringSize-strlen(egtfeature));
923 egtfeature[StringSize-1]='\0';
924 gui_send(GUI,egtfeature);
926 if (option_find(Uci->option,"UCI_Chess960")) {
927 gui_send(GUI,"feature variants=\"normal,fischerandom\"");
929 gui_send(GUI,"feature variants=\"normal\"");
932 xboard2uci_send_options();
935 void xboard2uci_send_options(){
936 char option_line[StringSize]="";
940 option_start_iter(Uci->option);
941 while((opt=option_next(Uci->option))){
942 if(my_string_case_equal(opt->name,"UCI_AnalyseMode")) continue;
943 if(my_string_case_equal(opt->name,"UCI_Opponent")) continue;
944 if(my_string_case_equal(opt->name,"UCI_Chess960")) continue;
945 if(my_string_case_equal(opt->name,"UCI_ShowCurrLine")) continue;
946 if(my_string_case_equal(opt->name,"UCI_ShowRefutations")) continue;
947 if(my_string_case_equal(opt->name,"UCI_ShredderbasesPath")) continue;
948 if(my_string_case_equal(opt->name,"UCI_SetPositionValue")) continue;
949 if(my_string_case_equal(opt->name,"UCI_DrawOffers")) continue;
950 if(my_string_case_equal(opt->name,"Ponder")) continue;
951 if(my_string_case_equal(opt->name,"Hash")) continue;
952 if(my_string_case_equal(opt->name,"NalimovPath")) continue;
953 if(my_string_case_equal(opt->name,"GaviotaTbPath")) continue;
954 if((name=uci_thread_option(Uci))!=NULL &&
955 my_string_case_equal(opt->name,name)) continue;
956 format_xboard_option_line(option_line,opt);
958 gui_send(GUI,"%s",option_line);
962 option_start_iter(Option);
963 while((opt=option_next(Option))){
964 if(opt->mode &XBOARD){
965 format_xboard_option_line(option_line,opt);
966 gui_send(GUI,"%s",option_line);
969 gui_send(GUI,"feature done=1");
973 // report_best_score()
975 static int report_best_score(){
976 if(!option_get_bool(Option,"ScoreWhite") ||
977 colour_is_white(Uci->board->turn)){
978 return Uci->best_score;
980 return -Uci->best_score;
986 static void comp_move(int move) {
991 ASSERT(move_is_ok(move));
993 ASSERT(State->state==THINK);
994 ASSERT(!XB->analyse);
996 if(option_get_bool(Option,"RepeatPV"))
997 send_pv(); // to update time and nodes
1001 game_get_board(Game,board);
1003 if (move_is_castle(move,board) && option_get_bool(Option,"Chess960")) {
1004 if (!move_to_san(move,board,string,256)) my_fatal("comp_move(): move_to_san() failed\n"); // O-O/O-O-O
1006 if (!move_to_can(move,board,string,256)) my_fatal("comp_move(): move_to_can() failed\n");
1009 gui_send(GUI,"move %s",string);
1013 if (option_get_bool(Option,"Resign") && Uci->root_move_nb > 1) {
1015 if (Uci->best_score <= -abs(option_get_int(Option,"ResignScore"))) {
1018 my_log("POLYGLOT %d move%s with resign score\n",State->resign_nb,(State->resign_nb>1)?"s":"");
1020 if (State->resign_nb >= option_get_int(Option,"ResignMoves")) {
1021 if (!option_get_bool(Option,"QueenNeverResigns") || !board_has_queen(board, board->turn)) { // [HGM] suppress resignig with Queen
1022 my_log("POLYGLOT *** RESIGN ***\n");
1023 gui_send(GUI,"resign");
1029 if (State->resign_nb > 0) my_log("POLYGLOT resign reset (State->resign_nb=%d)\n",State->resign_nb);
1030 State->resign_nb = 0;
1042 static void move_step(int move) {
1045 char move_string[256];
1047 ASSERT(move_is_ok(move));
1051 game_get_board(Game,board);
1053 if (move != MoveNone && move_is_legal(move,board)) {
1055 move_to_san(move,board,move_string,256);
1056 my_log("POLYGLOT MOVE %s\n",move_string);
1060 move_to_can(move,board,move_string,256);
1061 my_log("POLYGLOT ILLEGAL MOVE \"%s\"\n",move_string);
1064 my_fatal("move_step(): illegal move \"%s\"\n",move_string);
1069 game_add_move(Game,move);
1075 static void board_update() {
1079 ASSERT(!XB->result);
1081 switch (game_status(Game)) {
1085 gui_send(GUI,"1-0 {White mates}");
1088 gui_send(GUI,"0-1 {Black mates}");
1091 gui_send(GUI,"1/2-1/2 {Stalemate}");
1094 gui_send(GUI,"1/2-1/2 {Draw by insufficient material}");
1097 gui_send(GUI,"1/2-1/2 {Draw by fifty-move rule}");
1099 case DRAW_REPETITION:
1100 gui_send(GUI,"1/2-1/2 {Draw by repetition}");
1110 static void mess() {
1112 // clear state variables
1114 State->resign_nb = 0;
1115 State->exp_move = MoveNone;
1116 my_timer_reset(State->timer);
1118 // abort a possible search
1122 // calculate the new state
1125 } else if (!active()) {
1126 State->state = WAIT;
1127 my_log("POLYGLOT WAIT\n");
1128 } else if (XB->analyse) {
1129 State->state = ANALYSE;
1130 my_log("POLYGLOT ANALYSE\n");
1131 } else if (State->computer[game_turn(Game)]) {
1132 State->state = THINK;
1133 my_log("POLYGLOT THINK\n");
1135 State->state = WAIT;
1136 my_log("POLYGLOT WAIT\n");
1144 static void no_mess(int move) {
1146 ASSERT(move_is_ok(move));
1148 // just received a move, calculate the new state
1152 } else if (!active()) {
1154 stop_search(); // abort a possible search
1156 State->state = WAIT;
1157 State->exp_move = MoveNone;
1159 my_log("POLYGLOT WAIT\n");
1161 } else if (State->state == WAIT) {
1163 ASSERT(State->computer[game_turn(Game)]);
1164 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1165 ASSERT(!XB->analyse);
1167 my_log("POLYGLOT WAIT -> THINK\n");
1169 State->state = THINK;
1170 State->exp_move = MoveNone;
1172 } else if (State->state == THINK) {
1174 ASSERT(!State->computer[game_turn(Game)]);
1175 ASSERT(State->computer[colour_opp(game_turn(Game))]);
1176 ASSERT(!XB->analyse);
1178 if (ponder() && ponder_ok(Uci->ponder_move)) {
1180 my_log("POLYGLOT THINK -> PONDER\n");
1182 State->state = PONDER;
1183 State->exp_move = Uci->ponder_move;
1187 my_log("POLYGLOT THINK -> WAIT\n");
1189 State->state = WAIT;
1190 State->exp_move = MoveNone;
1193 } else if (State->state == PONDER) {
1195 ASSERT(State->computer[game_turn(Game)]);
1196 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1197 ASSERT(!XB->analyse);
1199 if (move == State->exp_move && Uci->searching) {
1201 ASSERT(Uci->searching);
1202 ASSERT(Uci->pending_nb>=1);
1204 my_timer_start(State->timer);//also resets
1206 my_log("POLYGLOT PONDER -> THINK (*** HIT ***)\n");
1207 engine_send(Engine,"ponderhit");
1209 State->state = THINK;
1210 State->exp_move = MoveNone;
1212 send_pv(); // update display
1214 return; // do not launch a new search
1218 my_log("POLYGLOT PONDER -> THINK (miss)\n");
1222 State->state = THINK;
1223 State->exp_move = MoveNone;
1226 } else if (State->state == ANALYSE) {
1228 ASSERT(XB->analyse);
1230 my_log("POLYGLOT ANALYSE -> ANALYSE\n");
1242 // start_protected_command()
1244 static void start_protected_command(){
1248 static void end_protected_command(){
1249 if(Uci->ready){ // not init faze
1250 uci_send_isready_sync(Uci); // gobble up spurious "bestmove"
1252 update_remaining_time();
1253 search_update(); // relaunch search if necessary
1256 // update_remaining_time()
1258 static void update_remaining_time(){
1260 if(State->timer->running){
1261 my_timer_stop(State->timer);
1262 reduce = my_timer_elapsed_real(State->timer);
1263 my_log("POLYGLOT reducing remaing time by %f seconds\n",reduce);
1264 XB->my_time -= reduce;
1265 if(XB->my_time<0.0){
1274 static void search_update() {
1280 ASSERT(!Uci->searching);
1285 // launch a new search if needed
1289 if (State->state == THINK || State->state == PONDER || State->state == ANALYSE) {
1291 // [VdB] moved up as we need the move number
1293 game_get_board(Game,Uci->board);
1297 if (State->state == THINK &&
1298 option_get_bool(Option,"Book") &&
1299 Uci->board->move_nb<option_get_int(Option,"BookDepth")
1303 move = book_move(Uci->board,option_get_bool(Option,"BookRandom"));
1305 if (move != MoveNone && move_is_legal(move,Uci->board)) {
1307 my_log("POLYGLOT *BOOK MOVE*\n");
1309 search_clear(); // clears Uci->ponder_move
1310 Uci->best_move = move;
1312 board_copy(board,Uci->board);
1313 move_do(board,move);
1314 Uci->ponder_move = book_move(board,FALSE); // expected move = best book move
1316 Uci->best_pv[0] = Uci->best_move;
1317 Uci->best_pv[1] = Uci->ponder_move; // can be MoveNone
1318 Uci->best_pv[2] = MoveNone;
1320 comp_move(Uci->best_move);
1328 my_log("POLYGLOT START SEARCH\n");
1332 uci_send_option(Uci,"UCI_Chess960","%s",
1333 option_get_bool(Option,"Chess960")?"true":"false");
1335 if (option_get_int(Option,"UCIVersion") >= 2) {
1336 uci_send_option(Uci,"UCI_Opponent","none none %s %s",(XB->computer)?"computer":"human",XB->name);
1337 uci_send_option(Uci,"UCI_AnalyseMode","%s",(XB->analyse)?"true":"false");
1340 uci_send_option(Uci,"Ponder","%s",ponder()?"true":"false");
1344 move = (State->state == PONDER) ? State->exp_move : MoveNone;
1345 send_board(move); // updates Uci->board global variable
1349 if (State->state == THINK || State->state == PONDER) {
1351 engine_send_queue(Engine,"go");
1353 if (XB->time_limit) {
1355 // fixed time per move
1357 if(XB->node_rate > 0){
1358 engine_send_queue(Engine,
1360 XB->time_max*((double)XB->node_rate));
1362 double computed_time;
1364 st_fudge=(double) option_get_int(Option,"STFudge");
1365 my_log("POLYGLOT Giving engine %.0fmsec extra time.\n",st_fudge);
1366 computed_time=XB->time_max*1000.0-st_fudge;
1367 if(computed_time< 1.0){
1370 engine_send_queue(Engine,
1379 if(XB->node_rate > 0) {
1383 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1385 time = XB->my_time / move_nb;
1389 if(time > XB->my_time){
1392 engine_send_queue(Engine,
1394 time*XB->node_rate);
1397 if (colour_is_white(Uci->board->turn)) {
1398 engine_send_queue(Engine,
1399 " wtime %.0f btime %.0f",
1400 XB->my_time*1000.0,XB->opp_time*1000.0);
1402 engine_send_queue(Engine,
1403 " wtime %.0f btime %.0f",
1404 XB->opp_time*1000.0,XB->my_time*1000.0);
1407 if (XB->inc != 0.0){
1408 engine_send_queue(Engine,
1409 " winc %.0f binc %.0f",
1410 XB->inc*1000.0,XB->inc*1000.0);
1414 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1415 ASSERT(move_nb>=1&&move_nb<=XB->mps);
1417 engine_send_queue(Engine," movestogo %d",move_nb);
1421 if (XB->depth_limit) engine_send_queue(Engine," depth %d",XB->depth_max);
1423 if (State->state == PONDER) engine_send_queue(Engine," ponder");
1425 engine_send(Engine,""); // newline
1427 } else if (State->state == ANALYSE) {
1429 engine_send(Engine,"go infinite");
1438 ASSERT(!Uci->searching);
1442 Uci->searching = TRUE;
1449 static void search_clear() {
1455 my_timer_start(State->timer);//also resets
1460 static bool active() {
1464 if (game_status(Game) != PLAYING) return FALSE; // game ended
1468 if (XB->analyse) return TRUE; // analysing
1469 if (!State->computer[White] && !State->computer[Black]) return FALSE; // force mode
1470 if (XB->new_hack || XB->result) return FALSE; // unstarted or ended game
1472 return TRUE; // playing
1477 static bool ponder() {
1479 return XB->ponder && (option_get_bool(Option,"CanPonder") ||
1480 option_find(Uci->option,"Ponder"));
1484 static bool ponder_ok(int move) {
1488 ASSERT(move==MoveNone||move_is_ok(move));
1490 // legal ponder move?
1492 if (move == MoveNone) return FALSE;
1494 game_get_board(Game,board);
1495 if (!move_is_legal(move,board)) return FALSE;
1497 // UCI-legal resulting position?
1499 game_add_move(Game,move);
1501 game_get_board(Game,board);
1502 status = game_status(Game);
1504 game_rem_move(Game);
1506 if (status != PLAYING) return FALSE; // game ended
1508 if (option_get_bool(Option,"Book") && is_in_book(board)) {
1517 static void stop_search() {
1519 if (Uci->searching) {
1521 ASSERT(Uci->searching);
1522 ASSERT(Uci->pending_nb>=1);
1524 my_log("POLYGLOT STOP SEARCH\n");
1527 engine_send(Engine,"stop");
1528 Uci->searching = FALSE;
1531 if (option_get_bool(Option,"SyncStop")) {
1532 uci_send_stop_sync(Uci);
1541 static void send_board(int extra_move) {
1550 ASSERT(extra_move==MoveNone||move_is_ok(extra_move));
1552 ASSERT(!Uci->searching);
1556 game_get_board(Game,Uci->board);
1557 if (extra_move != MoveNone) move_do(Uci->board,extra_move);
1559 board_to_fen(Uci->board,fen,256);
1560 my_log("POLYGLOT FEN %s\n",fen);
1562 ASSERT(board_can_play(Uci->board));
1567 end = game_pos(Game);
1572 game_get_board_ex(Game,board,start);
1573 board_to_fen(board,string,256);
1575 engine_send_queue(Engine,"position");
1577 if (my_string_equal(string,StartFen)) {
1578 engine_send_queue(Engine," startpos");
1580 engine_send_queue(Engine," fen %s",string);
1585 if (end > start || extra_move != MoveNone) engine_send_queue(Engine," moves");
1587 for (pos = start; pos < end; pos++) { // game moves
1589 move = game_move(Game,pos);
1591 move_to_can(move,board,string,256);
1592 engine_send_queue(Engine," %s",string);
1594 move_do(board,move);
1597 if (extra_move != MoveNone) { // move to ponder on
1598 move_to_can(extra_move,board,string,256);
1599 engine_send_queue(Engine," %s",string);
1604 engine_send(Engine,""); // newline
1609 static void send_info() {
1611 if(option_get_bool(Option,"WbWorkArounds2")){
1612 // Silly bug in some versions of WinBoard.
1613 // depth <=1 clears the engine output window.
1614 // Why shouldn't an engine be allowed to send info at depth 1?
1619 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth>min_depth?Uci->best_depth:min_depth,
1620 0,0.0,U64(0),Uci->info);
1625 static void send_pv() {
1627 char pv_string[StringSize];
1630 char move_string[StringSize];
1632 ASSERT(State->state!=WAIT);
1634 if (Uci->best_depth == 0) return;
1636 // xboard search information
1640 if (State->state == THINK || State->state == ANALYSE) {
1642 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1644 if(Uci->depth==-1) //hack to clear the engine output window
1645 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" ",0,report_best_score(),Uci->time*100.0,Uci->node_nb);
1647 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth,report_best_score(),Uci->time*100.0,Uci->node_nb,pv_string);
1649 } else if (State->state == PONDER &&
1650 option_get_bool(Option,"ShowPonder")) {
1652 game_get_board(Game,board);
1653 move = State->exp_move;
1655 if (move != MoveNone && move_is_legal(move,board)) {
1656 move_to_san(move,board,move_string,256);
1657 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1658 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" (%s) %s",Uci->best_depth,report_best_score(),Uci->time*100.0,Uci->node_nb,move_string,pv_string);
1665 if ((Uci->searching &&
1666 option_get_bool(Option,"KibitzPV") &&
1667 Uci->time >= option_get_double(Option,"KibitzDelay"))
1668 || (!Uci->searching && option_get_bool(Option,"KibitzMove"))) {
1670 if (State->state == THINK || State->state == ANALYSE) {
1672 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1673 if(kibitz_throttle(Uci->searching)){
1674 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);
1676 } else if (State->state == PONDER) {
1678 game_get_board(Game,board);
1679 move = State->exp_move;
1681 if (move != MoveNone && move_is_legal(move,board)) {
1682 move_to_san(move,board,move_string,256);
1683 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1684 if(kibitz_throttle(Uci->searching)){
1685 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);
1692 // kibitz_throttle()
1694 static bool kibitz_throttle(bool searching){
1696 static time_t lastKibitzMove=0;
1697 static time_t lastKibitzPV=0;
1698 curr_time = time(NULL);
1699 if(searching){ // KibitzPV
1701 (option_get_int(Option,"KibitzInterval") + lastKibitzPV)){
1702 lastKibitzPV=curr_time;
1705 }else{ // KibitzMove
1707 (option_get_int(Option,"KibitzInterval") + lastKibitzMove)){
1708 lastKibitzPV=curr_time;
1709 lastKibitzMove=curr_time;
1718 static void learn(int result) {
1724 ASSERT(result>=-1&&result<=+1);
1727 // ASSERT(State->computer[White]||State->computer[Black]);
1733 // [HGM] does not account for the hypothetical possibility we played both sides!
1734 if (State->playedAllMoves[White]) {
1736 } else if (State->playedAllMoves[Black]) {
1740 return; // [HGM] if we did not play all moves for some side, do not learn, but don't make a fuss!
1744 } else if (result > 0) {
1745 my_log("POLYGLOT *LEARN WIN*\n");
1746 } else if (result < 0) {
1747 my_log("POLYGLOT *LEARN LOSS*\n");
1749 my_log("POLYGLOT *LEARN DRAW*\n");
1754 for (; pos < Game->size; pos += 2) {
1756 game_get_board_ex(Game,board,pos);
1757 move = game_move(Game,pos);
1759 book_learn_move(board,move,result);
1765 // end of xboard2uci.c