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;
63 bool new_hack; // "new" is a C++ keyword
86 typedef enum { WAIT, THINK, PONDER, ANALYSE } dummy_state_t;
90 static state_t State[1];
95 static void comp_move (int move);
96 static void move_step (int move);
97 static void board_update ();
100 static void no_mess (int move);
102 static void search_update ();
103 static void search_clear ();
104 static void update_remaining_time();
105 static int report_best_score();
106 static bool kibitz_throttle (bool searching);
107 static void start_protected_command();
108 static void end_protected_command();
110 static bool active ();
111 static bool ponder ();
112 static bool ponder_ok (int ponder_move);
114 static void stop_search ();
116 static void send_board (int extra_move);
117 static void send_pv ();
118 static void send_info ();
120 static void send_xboard_options ();
122 static void learn (int result);
129 void xboard2uci_init() {
138 State->computer[White] = FALSE;
139 State->computer[Black] = TRUE;
141 State->exp_move = MoveNone;
142 State->hint_move = MoveNone;
143 State->resign_nb = 0;
144 my_timer_reset(State->timer);
146 // yes there are engines that do not have the "Hash" option....
147 XB->has_feature_memory= (option_find(Uci->option,"Hash")!=NULL);
148 XB->has_feature_smp = (uci_thread_option(Uci)!=NULL);
149 // TODO: support for other types of table bases
150 // This is a quick hack.
151 XB->has_feature_egt_nalimov = (option_find(Uci->option,"NalimovPath")!=NULL);
152 XB->has_feature_egt_gaviota = (option_find(Uci->option,"GaviotaTbPath")!=NULL);
154 XB->computer = FALSE;
156 my_string_set(&XB->name,"<empty>");
169 XB->time_limit = FALSE;
172 XB->depth_limit = FALSE;
176 XB->opp_time = 300.0;
182 static list_t move_list[1];
184 // xboard2uci_gui_step()
186 void xboard2uci_gui_step(char string[]) {
189 char move_string[256];
194 } else if (match(string,"accepted *")) {
198 } else if (match(string,"analyze")) {
200 State->computer[White] = FALSE;
201 State->computer[Black] = FALSE;
204 XB->new_hack = FALSE;
210 } else if (match(string,"bk")) {
212 if (option_get_bool(Option,"Book")) {
213 game_get_board(Game,board);
215 } else { // [HGM] without book, print all legal moves
216 int i, gen=!list_size(move_list);
217 game_get_board(Game,board);
218 if(gen) gen_legal_moves(move_list,board);
219 for(i=0; i<list_size(move_list); i++){
220 if(gen) move_list->value[i] = 0;
221 move_to_san(move_list->move[i],board,move_string,256);
222 printf(" %s%6s\n", move_list->value[i]? "* " : "", move_string);
224 // this is necessary by the xboard protocol
228 } else if (match(string,"exclude *") || match(string,"option Polyglot exclude move=*")) { // [HGM]
230 int i, all = !strcmp(Star[0], "all"), change=FALSE, cnt=0;
231 game_get_board(Game,board);
232 if(!list_size(move_list)) {
233 gen_legal_moves(move_list,board);
234 for(i=0; i<list_size(move_list); i++){
235 move_list->value[i] = 0;
238 move = move_from_san(Star[0],board);
240 for(i=0; i<list_size(move_list); i++){
241 if(all || move_list->move[i] == move)
242 change |= !move_list->value[i], move_list->value[i] = 1;
243 cnt += !move_list->value[i];
245 if(change && cnt) mess(); // do not relay to engine if no change or no moves left
247 } else if (match(string,"include *")) { // [HGM]
249 int i, all = !strcmp(Star[0], "all"), change = FALSE;
250 game_get_board(Game,board);
251 move = move_from_san(Star[0],board);
253 for(i=0; i<list_size(move_list); i++){
254 if(all || move_list->move[i] == move)
255 change |= move_list->value[i], move_list->value[i] = 0;
259 } else if (match(string,"black")) {
261 if (colour_is_black(game_turn(Game))) {
263 State->computer[White] = TRUE;
264 State->computer[Black] = FALSE;
272 } else if (match(string,"computer")) {
276 } else if (match(string,"draw")) {
277 if(option_find(Uci->option,"UCI_DrawOffers")){
278 my_log("POLYGLOT draw from XB received");
279 uci_send_option(Uci,"DrawOffer","%s","draw");}
280 else if (option_get_bool(Option,"HandleDraws") && Uci->root_move_nb > 20) { // [HGM] PG draw handling
281 my_log("POLYGLOT draw from XB received");
282 if (Uci->best_score <= -option_get_int(Option,"ContemptScore"))
283 gui_send(GUI,"offer draw");}
284 } else if (match(string,"easy")) {
290 } else if (match(string,"edit")) {
294 gui_send(GUI,"Error (unknown command): %s",string);
296 } else if (match(string,"exit")) {
298 State->computer[White] = FALSE;
299 State->computer[Black] = FALSE;
305 } else if (match(string,"force")) {
307 State->computer[White] = FALSE;
308 State->computer[Black] = FALSE;
312 } else if (match(string,"go")) {
314 State->computer[game_turn(Game)] = TRUE;
315 State->computer[colour_opp(game_turn(Game))] = FALSE;
317 XB->new_hack = FALSE;
323 } else if (match(string,"hard")) {
329 } else if (match(string,"hint")) {
332 game_get_board(Game,board);
333 if (option_get_bool(Option,"Book")) {
335 move = book_move(board,FALSE);
337 if(move==MoveNone && State->hint_move!=MoveNone){
338 move=State->hint_move;
341 if (move != MoveNone && move_is_legal(move,board)) {
342 move_to_san(move,board,move_string,256);
343 gui_send(GUI,"Hint: %s",move_string);
346 } else if (match(string,"ics *")) {
350 } else if (match(string,"level * *:* *")) {
352 XB->mps = atoi(Star[0]);
353 XB->base = ((double)atoi(Star[1])) * 60.0 + ((double)atoi(Star[2]));
354 XB->inc = ((double)atoi(Star[3]));
356 } else if (match(string,"level * * *")) {
358 XB->mps = atoi(Star[0]);
359 XB->base = ((double)atoi(Star[1])) * 60.0;
360 XB->inc = ((double)atoi(Star[2]));
362 } else if (match(string,"name *")) {
364 my_string_set(&XB->name,Star[0]);
366 } else if (match(string,"new")) {
368 uci_send_isready_sync(Uci);
369 my_log("POLYGLOT NEW GAME\n");
371 option_set(Option,"Chess960","false");
375 move_list->size = 0; // [HGM] clear all exclude moves
378 State->computer[White] = FALSE;
379 State->computer[Black] = FALSE;
381 State->computer[White] = FALSE;
382 State->computer[Black] = TRUE;
383 State->playedAllMoves[White] = TRUE; // [HGM]
384 State->playedAllMoves[Black] = TRUE;
390 XB->depth_limit = FALSE;
393 XB->computer = FALSE;
394 my_string_set(&XB->name,"<empty>");
399 uci_send_ucinewgame(Uci);
401 } else if (match(string,"nopost")) {
405 } else if (match(string,"otim *")) {
407 XB->opp_time = ((double)atoi(Star[0])) / 100.0;
408 if (XB->opp_time < 0.0) XB->opp_time = 0.0;
410 } else if (match(string,"pause")) {
414 gui_send(GUI,"Error (unknown command): %s",string);
416 } else if (match(string,"ping *")) {
418 // HACK; TODO: answer only after an engine move
421 if (XB->ping >= 0) gui_send(GUI,"pong %d",XB->ping); // HACK: get rid of old ping
422 XB->ping = atoi(Star[0]);
423 uci_send_isready_sync(Uci);
425 ASSERT(XB->ping==-1);
426 gui_send(GUI,"pong %s",Star[0]);
428 } else if (match(string,"nps *")) {
430 // fake WB play-by-nodes mode
431 XB->node_rate = atoi(Star[0]);
432 } else if (match(string,"playother")) {
434 State->computer[game_turn(Game)] = FALSE;
435 State->computer[colour_opp(game_turn(Game))] = TRUE;
437 XB->new_hack = FALSE;
443 } else if (match(string,"post")) {
447 } else if (match(string,"protover *")) {
448 XB->proto_ver = atoi(Star[0]);
449 ASSERT(XB->proto_ver>=2);
450 send_xboard_options();
452 } else if (match(string,"quit")) {
453 my_log("POLYGLOT *** \"quit\" from GUI ***\n");
455 } else if (match(string,"random")) {
459 } else if (match(string,"rating * *")) {
463 } else if (match(string,"remove")) {
465 if (game_pos(Game) >= 2) {
467 game_goto(Game,game_pos(Game)-2);
469 ASSERT(!XB->new_hack);
470 XB->new_hack = FALSE; // HACK?
477 } else if (match(string,"rejected *")) {
481 } else if (match(string,"reset")) { // protover 3?
485 gui_send(GUI,"Error (unknown command): %s",string);
488 || match(string,"result * {*}")
489 || match(string,"result * {* }")
490 || match(string,"result * { *}")
491 || match(string,"result * { * }")) {
493 my_log("POLYGLOT GAME END\n");
501 if (option_get_bool(Option,"Book") &&
502 option_get_bool(Option,"BookLearn")) {
505 } else if (my_string_equal(Star[0],"1-0")) {
507 } else if (my_string_equal(Star[0],"0-1")) {
509 } else if (my_string_equal(Star[0],"1/2-1/2")) {
513 } else if (match(string,"resume")) {
517 gui_send(GUI,"Error (unknown command): %s",string);
519 } else if (match(string,"option *=*") ||
520 match(string,"option * =*") ||
521 match(string,"option *= *") ||
522 match(string,"option * = *")
526 if(match(name, "Polyglot *")){
527 char *pg_name=Star[0];
528 polyglot_set_option(pg_name,value);
530 option_t *opt=option_find(Uci->option,name);
532 if(my_string_case_equal(opt->type,"check")){
533 value=my_string_equal(value,"1")?"true":"false";
535 start_protected_command();
536 uci_send_option(Uci, name, "%s", value);
537 end_protected_command();
539 gui_send(GUI,"Error (unknown option): %s",name);
542 } else if (match(string,"option *")){
544 if(match(name, "Polyglot *")){
545 char *pg_name=Star[0];
546 polyglot_set_option(pg_name,"<empty>");
548 start_protected_command();
550 if(!uci_send_option(Uci, name, "%s", "<empty>")){
551 gui_send(GUI,"Error (unknown option): %s",name);
553 end_protected_command();
555 } else if (XB->has_feature_smp && match(string,"cores *")){
556 int cores=atoi(Star[0]);
558 // updating the number of cores
559 my_log("POLYGLOT setting the number of cores to %d\n",cores);
560 start_protected_command();
561 uci_set_threads(Uci,cores);
562 end_protected_command();
565 gui_send(GUI,"Error (unknown command): %s",string);
567 } else if (match(string,"egtpath * *")){
570 if(my_string_empty(path)){
572 gui_send(GUI,"Error (unknown command): %s",string);
574 if(my_string_case_equal(type,"nalimov") && XB->has_feature_egt_nalimov){
575 // updating NalimovPath
576 my_log("POLYGLOT setting the Nalimov path to %s\n",path);
577 start_protected_command();
578 uci_send_option(Uci,"NalimovPath","%s",path);
579 end_protected_command();
580 }else if(my_string_case_equal(type,"gaviota") && XB->has_feature_egt_gaviota){
581 // updating GaviotaPath
582 my_log("POLYGLOT setting the Gaviota path to %s\n",path);
583 start_protected_command();
584 uci_send_option(Uci,"GaviotaTbPath","%s",path);
585 end_protected_command();
588 gui_send(GUI,"Error (unsupported table base format): %s",string);
591 } else if (XB->has_feature_memory && match(string,"memory *")){
592 int memory = atoi(Star[0]);
596 // updating the available memory
598 my_log("POLYGLOT setting the amount of memory to %dMb\n",memory);
599 if(XB->has_feature_egt_nalimov && (opt=option_find(Uci->option,"NalimovCache"))){
600 egt_cache=atoi(opt->value);
601 }else if(XB->has_feature_egt_gaviota &&
602 (opt=option_find(Uci->option,"GaviotaTbCache"))){
603 egt_cache=atoi(opt->value);
607 my_log("POLYGLOT EGTB Cache is %dMb\n",egt_cache);
608 real_memory=memory-egt_cache;
609 opt=option_find(Uci->option,"Hash");
610 if(opt && real_memory > atoi(opt->max)) real_memory = atoi(opt->max); // [HGM] top off
612 start_protected_command();
613 uci_send_option(Uci,"Hash", "%d", real_memory);
614 end_protected_command();
618 gui_send(GUI,"Error (unknown command): %s",string);
621 } else if (match(string,"sd *")) {
623 XB->depth_limit = TRUE;
624 XB->depth_max = atoi(Star[0]);
626 } else if (match(string,"setboard *")) {
628 my_log("POLYGLOT FEN %s\n",Star[0]);
630 if (!game_init(Game,Star[0])) my_fatal("xboard_step(): bad FEN \"%s\"\n",Star[0]);
632 move_list->size = 0; // [HGM] clear all exclude moves
634 State->computer[White] = FALSE;
635 State->computer[Black] = FALSE;
637 XB->new_hack = TRUE; // HACK?
643 } else if (match(string,"st *")) {
645 XB->time_limit = TRUE;
646 XB->time_max = ((double)atoi(Star[0]));
648 } else if (match(string,"time *")) {
650 XB->my_time = ((double)atoi(Star[0])) / 100.0;
651 if (XB->my_time < 0.0) XB->my_time = 0.0;
653 } else if (match(string,"undo")) {
655 move_list->size = 0; // [HGM] clear all exclude moves
657 if (game_pos(Game) >= 1) {
659 game_goto(Game,game_pos(Game)-1);
661 ASSERT(!XB->new_hack);
662 XB->new_hack = FALSE; // HACK?
669 } else if (match(string,"usermove *")) {
671 move_list->size = 0; // [HGM] clear all exclude moves
673 game_get_board(Game,board);
674 move = move_from_san(Star[0],board);
676 if (move != MoveNone && move_is_legal(move,board)) {
678 XB->new_hack = FALSE;
682 // [HGM] externally supplied move means we did not fully play the current stm
683 State->playedAllMoves[colour_is_white(game_turn(Game)) ? White : Black] = FALSE;
690 gui_send(GUI,"Illegal move: %s",Star[0]);
693 } else if (match(string,"variant *")) {
695 if (my_string_equal(Star[0],"fischerandom")) {
696 option_set(Option,"Chess960","true");
698 option_set(Option,"Chess960","false");
701 } else if (match(string,"white")) {
703 if (colour_is_white(game_turn(Game))) {
705 State->computer[White] = FALSE;
706 State->computer[Black] = TRUE;
714 } else if (match(string,"xboard")) {
718 } else if (match(string,".")) { // analyse info
720 if (State->state == ANALYSE) {
721 int depth=Uci->best_depth;//HACK: don't clear engine-output window...
723 ASSERT(Uci->searching);
724 ASSERT(Uci->pending_nb>=1);
726 if (Uci->root_move != MoveNone && move_is_legal(Uci->root_move,Uci->board)) {
727 move_to_san(Uci->root_move,Uci->board,move_string,256);
728 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);
730 gui_send(GUI,"stat01: %.0f "S64_FORMAT" %d %d %d",Uci->time*100.0,Uci->node_nb,/*Uci->*/depth,0,0); // HACK
734 } else if (match(string,"?")) { // move now
736 if (State->state == THINK) {
738 ASSERT(Uci->searching);
739 ASSERT(Uci->pending_nb>=1);
741 // HACK: just send "stop" to the engine
743 if (Uci->searching) {
744 my_log("POLYGLOT STOP SEARCH\n");
745 engine_send(Engine,"stop");
749 } else { // unknown command, maybe a move?
751 game_get_board(Game,board);
752 move = move_from_san(string,board);
754 if (move != MoveNone && move_is_legal(move,board)) {
756 XB->new_hack = FALSE;
763 } else if (move != MoveNone) {
765 gui_send(GUI,"Illegal move: %s",string);
769 gui_send(GUI,"Error (unknown command): %s",string);
775 // xboard2uci_engine_step()
777 void xboard2uci_engine_step(char string[]) {
781 event = uci_parse(Uci,string);
785 if ((event & EVENT_READY) != 0) {
787 // the engine is now ready
791 // if (XB->proto_ver >= 2) xboard_send(XBoard,"feature done=1");
794 if (!DelayPong && XB->ping >= 0) {
795 gui_send(GUI,"pong %d",XB->ping);
800 if ((event & EVENT_MOVE) != 0 && State->state == THINK) {
802 // the engine is playing a move
804 // MEGA HACK: estimate remaining time because XBoard won't send it!
806 my_timer_stop(State->timer);
808 XB->my_time -= my_timer_elapsed_real(State->timer);
809 XB->my_time += XB->inc;
810 if (XB->mps != 0 && (game_move_nb(Game) + 1) % XB->mps == 0) XB->my_time += XB->base;
812 if (XB->my_time < 0.0) XB->my_time = 0.0;
814 // make sure to remember the ponder move
816 State->hint_move=Uci->ponder_move;
818 // play the engine move
820 comp_move(Uci->best_move);
824 if ((event & EVENT_PV) != 0) {
826 // the engine has sent a new PV
830 if ((event & EVENT_INFO) != 0) {
832 // the engine has sent info
836 if((event & (EVENT_DRAW|EVENT_RESIGN))!=0){
837 my_log("POYGLOT draw offer/resign from engine\n");
838 if(option_find(Uci->option,"UCI_DrawOffers")){
839 if(event & EVENT_DRAW)
840 gui_send(GUI,"offer draw");
842 gui_send(GUI,"resign");
845 if(((event & EVENT_ILLEGAL_MOVE)!=0) && (State->state == THINK)){
846 game_get_board(Game,board);
847 if(board->turn==White){
848 gui_send(GUI,"0-1 {polyglot: resign"
849 " (illegal engine move by white: %s)}",Uci->bestmove);
851 gui_send(GUI,"1-0 {polyglot: resign"
852 " (illegal engine move by black: %s)}",Uci->bestmove);
860 // format_xboard_option_line
862 void format_xboard_option_line(char * option_line, option_t *opt){
864 char option_string[StringSize];
866 strcpy(option_line,"");
867 // buffer overflow alert
868 strcat(option_line,"feature option=\"");
870 strcat(option_line,"Polyglot ");
872 sprintf(option_string,"%s",opt->name);
873 strcat(option_line,option_string);
874 sprintf(option_string," -%s",opt->type);
875 strcat(option_line,option_string);
876 if(!IS_BUTTON(opt->type) && strcmp(opt->type,"combo")){
877 if(strcmp(opt->type,"check")){
878 sprintf(option_string," %s",opt->value);
880 sprintf(option_string," %d",
881 my_string_case_equal(opt->value,"true")||
882 my_string_equal(opt->value,"1")
885 strcat(option_line,option_string);
887 if(IS_SPIN(opt->type)){
888 sprintf(option_string," %s",opt->min);
889 strcat(option_line,option_string);
891 if(IS_SPIN(opt->type)){
892 sprintf(option_string," %s",opt->max);
893 strcat(option_line,option_string);
895 for(j=0;j<opt->var_nb;j++){
896 if(!strcmp(opt->var[j],opt->value)){
897 sprintf(option_string," *%s",opt->var[j]);
899 sprintf(option_string," %s",opt->var[j]);
901 strcat(option_line,option_string);
902 if(j!=opt->var_nb-1){
903 strcat(option_line," ///");
906 strcat(option_line,"\"");
907 if(option_get_bool(Option,"WbWorkArounds") &&
908 (tmp=strstr(option_line,"Draw"))){
910 my_log("POLYGLOT Decapitalizing \"Draw\" in option \"%s\"\n",
915 // disarm() // [HGM] cleanse a string of offending double-quotes
917 static char*disarm(const char *s){
921 q = buf + strlen(buf) - 1;
922 while(*q == '"') *q-- = '\0'; // strip trailing quotes
923 while(*p == '"') p++; // strip leading quotes
924 while((q = strchr(p, '"'))) *q = '\''; // replace internal quotes
928 // send_xboard_options()
930 static void send_xboard_options(){
932 char egtfeature[StringSize];
935 gui_send(GUI,"feature done=0");
937 gui_send(GUI,"feature analyze=1");
938 gui_send(GUI,"feature exclude=1");
939 gui_send(GUI,"feature colors=0");
940 gui_send(GUI,"feature draw=1");
941 gui_send(GUI,"feature ics=1");
942 gui_send(GUI,"feature myname=\"%s\"",
943 disarm(option_get_string(Option,"EngineName")));
944 gui_send(GUI,"feature name=1");
945 gui_send(GUI,"feature pause=0");
946 gui_send(GUI,"feature ping=1");
947 gui_send(GUI,"feature playother=1");
948 gui_send(GUI,"feature sigint=1");
949 gui_send(GUI,"feature reuse=1");
950 gui_send(GUI,"feature san=0");
951 gui_send(GUI,"feature setboard=1");
952 gui_send(GUI,"feature sigint=0");
953 gui_send(GUI,"feature sigterm=0");
954 gui_send(GUI,"feature time=1");
955 gui_send(GUI,"feature usermove=1");
956 gui_send(GUI,"feature nps=1");
957 if (XB->has_feature_memory){
958 gui_send(GUI,"feature memory=1");
960 gui_send(GUI,"feature memory=0");
962 if (XB->has_feature_smp){
963 gui_send(GUI,"feature smp=1");
965 gui_send(GUI,"feature smp=0");
968 strncat(egtfeature,"feature egt=\"",StringSize);
969 if (XB->has_feature_egt_nalimov){
971 strncat(egtfeature,"nalimov",StringSize-strlen(egtfeature));
973 if (XB->has_feature_egt_gaviota){
975 strncat(egtfeature,",",StringSize-strlen(egtfeature));
977 strncat(egtfeature,"gaviota",StringSize-strlen(egtfeature));
979 strncat(egtfeature,"\"",StringSize-strlen(egtfeature));
980 egtfeature[StringSize-1]='\0';
981 gui_send(GUI,egtfeature);
983 if (option_find(Uci->option,"UCI_Chess960")) {
984 gui_send(GUI,"feature variants=\"normal,fischerandom\"");
986 gui_send(GUI,"feature variants=\"normal\"");
989 xboard2uci_send_options();
992 void xboard2uci_send_options(){
993 char option_line[StringSize]="";
997 option_start_iter(Uci->option);
998 while((opt=option_next(Uci->option))){
999 if(my_string_case_equal(opt->name,"UCI_AnalyseMode")) continue;
1000 if(my_string_case_equal(opt->name,"UCI_Opponent")) continue;
1001 if(my_string_case_equal(opt->name,"UCI_Chess960")) continue;
1002 if(my_string_case_equal(opt->name,"UCI_ShowCurrLine")) continue;
1003 if(my_string_case_equal(opt->name,"UCI_ShowRefutations")) continue;
1004 if(my_string_case_equal(opt->name,"UCI_ShredderbasesPath")) continue;
1005 if(my_string_case_equal(opt->name,"UCI_SetPositionValue")) continue;
1006 if(my_string_case_equal(opt->name,"UCI_DrawOffers")) continue;
1007 if(my_string_case_equal(opt->name,"Ponder")) continue;
1008 if(my_string_case_equal(opt->name,"Hash")) continue;
1009 if(my_string_case_equal(opt->name,"NalimovPath")) continue;
1010 if(my_string_case_equal(opt->name,"GaviotaTbPath")) continue;
1011 if((name=uci_thread_option(Uci))!=NULL &&
1012 my_string_case_equal(opt->name,name)) continue;
1013 format_xboard_option_line(option_line,opt);
1015 gui_send(GUI,"%s",option_line);
1019 gui_send(GUI,"feature option=\"Polyglot exclude move -string \"");
1021 option_start_iter(Option);
1022 while((opt=option_next(Option))){
1023 if(opt->mode &XBOARD){
1024 format_xboard_option_line(option_line,opt);
1025 gui_send(GUI,"%s",option_line);
1028 gui_send(GUI,"feature done=1");
1032 // report_best_score()
1034 static int report_best_score(){
1035 if(!option_get_bool(Option,"ScoreWhite") ||
1036 colour_is_white(Uci->board->turn)){
1037 return Uci->best_score;
1039 return -Uci->best_score;
1045 static void comp_move(int move) {
1050 ASSERT(move_is_ok(move));
1052 ASSERT(State->state==THINK);
1053 ASSERT(!XB->analyse);
1055 if(option_get_bool(Option,"RepeatPV"))
1056 send_pv(); // to update time and nodes
1060 game_get_board(Game,board);
1062 if (move_is_castle(move,board) && option_get_bool(Option,"Chess960")) {
1063 if (!move_to_san(move,board,string,256)) my_fatal("comp_move(): move_to_san() failed\n"); // O-O/O-O-O
1065 if (!move_to_can(move,board,string,256)) my_fatal("comp_move(): move_to_can() failed\n");
1068 gui_send(GUI,"move %s",string);
1072 if (option_get_bool(Option,"Resign") && Uci->root_move_nb > 1) {
1074 if (Uci->best_score <= -abs(option_get_int(Option,"ResignScore"))) {
1077 my_log("POLYGLOT %d move%s with resign score\n",State->resign_nb,(State->resign_nb>1)?"s":"");
1079 if (State->resign_nb >= option_get_int(Option,"ResignMoves")) {
1080 if (!option_get_bool(Option,"QueenNeverResigns") || !board_has_queen(board, board->turn)) { // [HGM] suppress resignig with Queen
1081 my_log("POLYGLOT *** RESIGN ***\n");
1082 gui_send(GUI,"resign");
1088 if (State->resign_nb > 0) my_log("POLYGLOT resign reset (State->resign_nb=%d)\n",State->resign_nb);
1089 State->resign_nb = 0;
1101 static void move_step(int move) {
1104 char move_string[256];
1106 ASSERT(move_is_ok(move));
1110 game_get_board(Game,board);
1112 if (move != MoveNone && move_is_legal(move,board)) {
1114 move_to_san(move,board,move_string,256);
1115 my_log("POLYGLOT MOVE %s\n",move_string);
1119 move_to_can(move,board,move_string,256);
1120 my_log("POLYGLOT ILLEGAL MOVE \"%s\"\n",move_string);
1123 my_fatal("move_step(): illegal move \"%s\"\n",move_string);
1128 game_add_move(Game,move);
1134 static void board_update() {
1138 ASSERT(!XB->result);
1140 switch (game_status(Game)) {
1144 gui_send(GUI,"1-0 {White mates}");
1147 gui_send(GUI,"0-1 {Black mates}");
1150 gui_send(GUI,"1/2-1/2 {Stalemate}");
1153 gui_send(GUI,"1/2-1/2 {Draw by insufficient material}");
1156 gui_send(GUI,"1/2-1/2 {Draw by fifty-move rule}");
1158 case DRAW_REPETITION:
1159 gui_send(GUI,"1/2-1/2 {Draw by repetition}");
1169 static void mess() {
1171 // clear state variables
1173 State->resign_nb = 0;
1174 State->exp_move = MoveNone;
1175 my_timer_reset(State->timer);
1177 // abort a possible search
1181 // calculate the new state
1184 } else if (!active()) {
1185 State->state = WAIT;
1186 my_log("POLYGLOT WAIT\n");
1187 } else if (XB->analyse) {
1188 State->state = ANALYSE;
1189 my_log("POLYGLOT ANALYSE\n");
1190 } else if (State->computer[game_turn(Game)]) {
1191 State->state = THINK;
1192 my_log("POLYGLOT THINK\n");
1194 State->state = WAIT;
1195 my_log("POLYGLOT WAIT\n");
1203 static void no_mess(int move) {
1205 ASSERT(move_is_ok(move));
1207 // just received a move, calculate the new state
1211 } else if (!active()) {
1213 stop_search(); // abort a possible search
1215 State->state = WAIT;
1216 State->exp_move = MoveNone;
1218 my_log("POLYGLOT WAIT\n");
1220 } else if (State->state == WAIT) {
1222 ASSERT(State->computer[game_turn(Game)]);
1223 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1224 ASSERT(!XB->analyse);
1226 my_log("POLYGLOT WAIT -> THINK\n");
1228 State->state = THINK;
1229 State->exp_move = MoveNone;
1231 } else if (State->state == THINK) {
1233 ASSERT(!State->computer[game_turn(Game)]);
1234 ASSERT(State->computer[colour_opp(game_turn(Game))]);
1235 ASSERT(!XB->analyse);
1237 if (ponder() && ponder_ok(Uci->ponder_move)) {
1239 my_log("POLYGLOT THINK -> PONDER\n");
1241 State->state = PONDER;
1242 State->exp_move = Uci->ponder_move;
1246 my_log("POLYGLOT THINK -> WAIT\n");
1248 State->state = WAIT;
1249 State->exp_move = MoveNone;
1252 } else if (State->state == PONDER) {
1254 ASSERT(State->computer[game_turn(Game)]);
1255 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
1256 ASSERT(!XB->analyse);
1258 if (move == State->exp_move && Uci->searching) {
1260 ASSERT(Uci->searching);
1261 ASSERT(Uci->pending_nb>=1);
1263 my_timer_start(State->timer);//also resets
1265 my_log("POLYGLOT PONDER -> THINK (*** HIT ***)\n");
1266 engine_send(Engine,"ponderhit");
1268 State->state = THINK;
1269 State->exp_move = MoveNone;
1271 send_pv(); // update display
1273 return; // do not launch a new search
1277 my_log("POLYGLOT PONDER -> THINK (miss)\n");
1281 State->state = THINK;
1282 State->exp_move = MoveNone;
1285 } else if (State->state == ANALYSE) {
1287 ASSERT(XB->analyse);
1289 my_log("POLYGLOT ANALYSE -> ANALYSE\n");
1301 // start_protected_command()
1303 static void start_protected_command(){
1307 static void end_protected_command(){
1308 if(Uci->ready){ // not init faze
1309 uci_send_isready_sync(Uci); // gobble up spurious "bestmove"
1311 update_remaining_time();
1312 search_update(); // relaunch search if necessary
1315 // update_remaining_time()
1317 static void update_remaining_time(){
1319 if(State->timer->running){
1320 my_timer_stop(State->timer);
1321 reduce = my_timer_elapsed_real(State->timer);
1322 my_log("POLYGLOT reducing remaing time by %f seconds\n",reduce);
1323 XB->my_time -= reduce;
1324 if(XB->my_time<0.0){
1333 static void search_update() {
1339 ASSERT(!Uci->searching);
1344 // launch a new search if needed
1348 if (State->state == THINK || State->state == PONDER || State->state == ANALYSE) {
1350 // [VdB] moved up as we need the move number
1352 game_get_board(Game,Uci->board);
1356 if (State->state == THINK &&
1357 option_get_bool(Option,"Book") &&
1358 Uci->board->move_nb<option_get_int(Option,"BookDepth")
1362 move = book_move(Uci->board,option_get_bool(Option,"BookRandom"));
1364 if (move != MoveNone && move_is_legal(move,Uci->board)) {
1366 my_log("POLYGLOT *BOOK MOVE*\n");
1368 search_clear(); // clears Uci->ponder_move
1369 Uci->best_move = move;
1371 board_copy(board,Uci->board);
1372 move_do(board,move);
1373 Uci->ponder_move = book_move(board,FALSE); // expected move = best book move
1375 Uci->best_pv[0] = Uci->best_move;
1376 Uci->best_pv[1] = Uci->ponder_move; // can be MoveNone
1377 Uci->best_pv[2] = MoveNone;
1379 comp_move(Uci->best_move);
1387 my_log("POLYGLOT START SEARCH\n");
1391 uci_send_option(Uci,"UCI_Chess960","%s",
1392 option_get_bool(Option,"Chess960")?"true":"false");
1394 if (option_get_int(Option,"UCIVersion") >= 2) {
1395 uci_send_option(Uci,"UCI_Opponent","none none %s %s",(XB->computer)?"computer":"human",XB->name);
1396 uci_send_option(Uci,"UCI_AnalyseMode","%s",(XB->analyse)?"true":"false");
1399 uci_send_option(Uci,"Ponder","%s",ponder()?"true":"false");
1403 move = (State->state == PONDER) ? State->exp_move : MoveNone;
1404 send_board(move); // updates Uci->board global variable
1408 if (State->state == THINK || State->state == PONDER) {
1410 engine_send_queue(Engine,"go");
1412 if (XB->time_limit) {
1414 // fixed time per move
1416 if(XB->node_rate > 0){
1417 engine_send_queue(Engine,
1419 XB->time_max*((double)XB->node_rate));
1421 double computed_time;
1423 st_fudge=(double) option_get_int(Option,"STFudge");
1424 my_log("POLYGLOT Giving engine %.0fmsec extra time.\n",st_fudge);
1425 computed_time=XB->time_max*1000.0-st_fudge;
1426 if(computed_time< 1.0){
1429 engine_send_queue(Engine,
1438 if(XB->node_rate > 0) {
1442 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1444 time = XB->my_time / move_nb;
1448 if(time > XB->my_time){
1451 engine_send_queue(Engine,
1453 time*XB->node_rate);
1456 if (colour_is_white(Uci->board->turn)) {
1457 engine_send_queue(Engine,
1458 " wtime %.0f btime %.0f",
1459 XB->my_time*1000.0,XB->opp_time*1000.0);
1461 engine_send_queue(Engine,
1462 " wtime %.0f btime %.0f",
1463 XB->opp_time*1000.0,XB->my_time*1000.0);
1466 if (XB->inc != 0.0){
1467 engine_send_queue(Engine,
1468 " winc %.0f binc %.0f",
1469 XB->inc*1000.0,XB->inc*1000.0);
1473 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
1474 ASSERT(move_nb>=1&&move_nb<=XB->mps);
1476 engine_send_queue(Engine," movestogo %d",move_nb);
1480 if (XB->depth_limit) engine_send_queue(Engine," depth %d",XB->depth_max);
1482 if (State->state == PONDER) engine_send_queue(Engine," ponder");
1484 engine_send(Engine,""); // newline
1486 } else if (State->state == ANALYSE) {
1488 char move_string[256];
1490 engine_send_queue(Engine,"go infinite");
1492 if(list_size(move_list)) {
1494 game_get_board(Game,board);
1495 engine_send_queue(Engine," searchmoves");
1496 for(i=0; i<list_size(move_list); i++) {
1497 if(!move_list->value[i]) {
1498 move_to_can(move_list->move[i],board,move_string,256);
1499 engine_send_queue(Engine," %s",move_string);
1503 engine_send(Engine,""); // newline
1512 ASSERT(!Uci->searching);
1516 Uci->searching = TRUE;
1523 static void search_clear() {
1529 my_timer_start(State->timer);//also resets
1534 static bool active() {
1538 if (game_status(Game) != PLAYING) return FALSE; // game ended
1542 if (XB->analyse) return TRUE; // analysing
1543 if (!State->computer[White] && !State->computer[Black]) return FALSE; // force mode
1544 if (XB->new_hack || XB->result) return FALSE; // unstarted or ended game
1546 return TRUE; // playing
1551 static bool ponder() {
1553 return XB->ponder && (option_get_bool(Option,"CanPonder") ||
1554 option_find(Uci->option,"Ponder"));
1558 static bool ponder_ok(int move) {
1562 ASSERT(move==MoveNone||move_is_ok(move));
1564 // legal ponder move?
1566 if (move == MoveNone) return FALSE;
1568 game_get_board(Game,board);
1569 if (!move_is_legal(move,board)) return FALSE;
1571 // UCI-legal resulting position?
1573 game_add_move(Game,move);
1575 game_get_board(Game,board);
1576 status = game_status(Game);
1578 game_rem_move(Game);
1580 if (status != PLAYING) return FALSE; // game ended
1582 if (option_get_bool(Option,"Book") && is_in_book(board)) {
1591 static void stop_search() {
1593 if (Uci->searching) {
1595 ASSERT(Uci->searching);
1596 ASSERT(Uci->pending_nb>=1);
1598 my_log("POLYGLOT STOP SEARCH\n");
1601 engine_send(Engine,"stop");
1602 Uci->searching = FALSE;
1605 if (option_get_bool(Option,"SyncStop")) {
1606 uci_send_stop_sync(Uci);
1615 static void send_board(int extra_move) {
1624 ASSERT(extra_move==MoveNone||move_is_ok(extra_move));
1626 ASSERT(!Uci->searching);
1630 game_get_board(Game,Uci->board);
1631 if (extra_move != MoveNone) move_do(Uci->board,extra_move);
1633 board_to_fen(Uci->board,fen,256);
1634 my_log("POLYGLOT FEN %s\n",fen);
1636 ASSERT(board_can_play(Uci->board));
1641 end = game_pos(Game);
1646 game_get_board_ex(Game,board,start);
1647 board_to_fen(board,string,256);
1649 engine_send_queue(Engine,"position");
1651 if (my_string_equal(string,StartFen)) {
1652 engine_send_queue(Engine," startpos");
1654 engine_send_queue(Engine," fen %s",string);
1659 if (end > start || extra_move != MoveNone) engine_send_queue(Engine," moves");
1661 for (pos = start; pos < end; pos++) { // game moves
1663 move = game_move(Game,pos);
1665 move_to_can(move,board,string,256);
1666 engine_send_queue(Engine," %s",string);
1668 move_do(board,move);
1671 if (extra_move != MoveNone) { // move to ponder on
1672 move_to_can(extra_move,board,string,256);
1673 engine_send_queue(Engine," %s",string);
1678 engine_send(Engine,""); // newline
1683 static void send_info() {
1685 if(option_get_bool(Option,"WbWorkArounds2")){
1686 // Silly bug in some versions of WinBoard.
1687 // depth <=1 clears the engine output window.
1688 // Why shouldn't an engine be allowed to send info at depth 1?
1693 if(!strncmp(Uci->info, "xboard ", 7)) gui_send(GUI,"%s",Uci->info+7); else // kludge to allow UCI engines to use WB protocol
1694 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth>min_depth?Uci->best_depth:min_depth,
1695 0,0.0,U64(0),Uci->info);
1700 static void send_pv() {
1702 char pv_string[StringSize];
1705 char move_string[StringSize];
1707 ASSERT(State->state!=WAIT);
1709 if (Uci->best_depth == 0) return;
1711 // xboard search information
1715 if (State->state == THINK || State->state == ANALYSE) {
1717 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1719 if(Uci->depth==-1) //hack to clear the engine output window
1720 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" ",0,report_best_score(),Uci->time*100.0,Uci->node_nb);
1721 if(option_get_bool(Option,"ShowTbHits"))
1722 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" {"S64_FORMAT"} %s",Uci->best_depth,report_best_score(),Uci->time*100.0,Uci->node_nb,Uci->tbhit_nb,pv_string);
1724 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth,report_best_score(),Uci->time*100.0,Uci->node_nb,pv_string);
1726 } else if (State->state == PONDER &&
1727 option_get_bool(Option,"ShowPonder")) {
1729 game_get_board(Game,board);
1730 move = State->exp_move;
1732 if (move != MoveNone && move_is_legal(move,board)) {
1733 move_to_san(move,board,move_string,256);
1734 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1735 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);
1742 if ((Uci->searching &&
1743 option_get_bool(Option,"KibitzPV") &&
1744 Uci->time >= option_get_double(Option,"KibitzDelay"))
1745 || (!Uci->searching && option_get_bool(Option,"KibitzMove"))) {
1747 if (State->state == THINK || State->state == ANALYSE) {
1749 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1750 if(kibitz_throttle(Uci->searching)){
1751 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);
1753 } else if (State->state == PONDER) {
1755 game_get_board(Game,board);
1756 move = State->exp_move;
1758 if (move != MoveNone && move_is_legal(move,board)) {
1759 move_to_san(move,board,move_string,256);
1760 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
1761 if(kibitz_throttle(Uci->searching)){
1762 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);
1769 // kibitz_throttle()
1771 static bool kibitz_throttle(bool searching){
1773 static time_t lastKibitzMove=0;
1774 static time_t lastKibitzPV=0;
1775 curr_time = time(NULL);
1776 if(searching){ // KibitzPV
1778 (option_get_int(Option,"KibitzInterval") + lastKibitzPV)){
1779 lastKibitzPV=curr_time;
1782 }else{ // KibitzMove
1784 (option_get_int(Option,"KibitzInterval") + lastKibitzMove)){
1785 lastKibitzPV=curr_time;
1786 lastKibitzMove=curr_time;
1795 static void learn(int result) {
1801 ASSERT(result>=-1&&result<=+1);
1804 // ASSERT(State->computer[White]||State->computer[Black]);
1810 // [HGM] does not account for the hypothetical possibility we played both sides!
1811 if (State->playedAllMoves[White]) {
1813 } else if (State->playedAllMoves[Black]) {
1817 return; // [HGM] if we did not play all moves for some side, do not learn, but don't make a fuss!
1821 } else if (result > 0) {
1822 my_log("POLYGLOT *LEARN WIN*\n");
1823 } else if (result < 0) {
1824 my_log("POLYGLOT *LEARN LOSS*\n");
1826 my_log("POLYGLOT *LEARN DRAW*\n");
1831 for (; pos < Game->size; pos += 2) {
1833 game_get_board_ex(Game,board,pos);
1834 move = game_move(Game,pos);
1836 book_learn_move(board,move,result);
1842 // end of xboard2uci.c