12 #include <sys/select.h>
\r
13 #include <sys/types.h> // Mac OS X needs this one
\r
17 #include "adapter.h"
\r
28 #include "move_do.h"
\r
29 #include "move_legal.h"
\r
40 static const bool UseDebug = false;
\r
41 static const bool DelayPong = false;
\r
43 static const int StringSize = 4096;
\r
49 bool computer[ColourNb];
\r
52 my_timer_t timer[1];
\r
56 bool has_feature_memory;
\r
57 bool has_feature_smp;
\r
58 bool has_feature_egt;
\r
63 bool new_hack; // "new" is a C++ keyword
\r
84 enum dummy_state_t { WAIT, THINK, PONDER, ANALYSE };
\r
88 static state_t State[1];
\r
94 static void xboard_step (char string[]);
\r
95 static void engine_step (char string[]);
\r
97 static void comp_move (int move);
\r
98 static void move_step (int move);
\r
99 static void board_update ();
\r
101 static void mess ();
\r
102 static void no_mess (int move);
\r
104 static void search_update ();
\r
105 static void search_clear ();
\r
106 static void update_remaining_time();
\r
107 static void start_protected_command();
\r
108 static void end_protected_command();
\r
110 static bool active ();
\r
111 static bool ponder ();
\r
112 static bool ponder_ok (int ponder_move);
\r
114 static void stop_search ();
\r
116 static void send_board (int extra_move);
\r
117 static void send_pv ();
\r
120 static void learn (int result);
\r
122 static void adapter_step ();
\r
128 void adapter_loop() {
\r
136 State->state = WAIT;
\r
138 State->computer[White] = false;
\r
139 State->computer[Black] = true;
\r
141 State->exp_move = MoveNone;
\r
142 State->resign_nb = 0;
\r
143 my_timer_reset(State->timer);
\r
145 // yes there are engines that do not have the "Hash" option....
\r
146 XB->has_feature_memory= uci_option_exist(Uci,"Hash");
\r
147 XB->has_feature_smp = uci_thread_option_exist(Uci);
\r
148 // TODO: support for other types of table bases
\r
149 XB->has_feature_egt = uci_option_exist(Uci,"NalimovPath");
\r
150 XB->analyse = false;
\r
151 XB->computer = false;
\r
153 my_string_set(&XB->name,"<empty>");
\r
155 XB->new_hack = true;
\r
157 XB->ponder = false;
\r
160 XB->result = false;
\r
166 XB->time_limit = false;
\r
167 XB->time_max = 5.0;
\r
169 XB->depth_limit = false;
\r
170 XB->depth_max = 127;
\r
172 XB->my_time = 300.0;
\r
173 XB->opp_time = 300.0;
\r
174 while (true) adapter_step();
\r
180 static void adapter_step(){ // polling!
\r
182 char string[StringSize];
\r
183 if(option_get_bool("UCI")){
\r
184 xin=gui_get_non_blocking(GUI,string,StringSize);
\r
185 if(xin) uci_gui_step(string);
\r
186 ein=engine_get_non_blocking(Engine,string,StringSize);
\r
187 if(ein) uci_engine_step(string);
\r
189 xin=gui_get_non_blocking(GUI,string,StringSize);
\r
190 if(xin) xboard_step(string);
\r
191 ein=engine_get_non_blocking(Engine,string,StringSize);
\r
192 if(ein) engine_step(string);
\r
194 if(xin==false && ein==false) Idle();//nobody wants me,lets have a beauty nap
\r
197 static void adapter_step() {
\r
202 char string[StringSize];
\r
204 // process buffered lines
\r
206 while (io_line_ready(GUI->io)){
\r
207 gui_get(GUI,string,StringSize);
\r
208 if(option_get_bool("UCI")){
\r
209 uci_gui_step(string);
\r
211 xboard_step(string); // process available xboard lines
\r
214 while (io_line_ready(Engine->io)){
\r
215 engine_get(Engine,string,StringSize);
\r
216 if(option_get_bool("UCI")){
\r
217 uci_engine_step(string); // process available engine lines
\r
219 engine_step(string);
\r
226 fd_max = -1; // HACK
\r
228 // add xboard input
\r
230 ASSERT(GUI->io->in_fd>=0);
\r
232 FD_SET(GUI->io->in_fd,set);
\r
233 if (GUI->io->in_fd > fd_max) fd_max = GUI->io->in_fd;
\r
235 // add engine input
\r
237 ASSERT(Engine->io->in_fd>=0);
\r
239 FD_SET(Engine->io->in_fd,set);
\r
240 if (Engine->io->in_fd > fd_max) fd_max = Engine->io->in_fd;
\r
242 // wait for something to read (no timeout)
\r
246 val = select(fd_max+1,set,NULL,NULL,NULL);
\r
247 if (val == -1 && errno != EINTR) my_fatal("adapter_step(): select(): %s\n",strerror(errno));
\r
250 if (FD_ISSET(GUI->io->in_fd,set)) io_get_update(GUI->io); // read some xboard input
\r
251 if (FD_ISSET(Engine->io->in_fd,set)) io_get_update(Engine->io); // read some engine input
\r
256 static void xboard_step(char string[]) {
\r
259 char move_string[256];
\r
261 static bool firsttime=true;
\r
264 if((match(string,"uci"))){
\r
265 my_log("POLYGLOT *** Switching to UCI mode ***\n");
\r
266 send_uci_options();
\r
267 option_set("UCI","true");
\r
270 if(!book_is_open() && option_get_bool("Book")){
\r
271 // restore old behaviour in xboard mode
\r
272 // missing book is fatal
\r
273 my_fatal("xboard_step(): can't open file \"%s\": %s\n",
\r
274 option_get_string("BookFile"),strerror(errno));
\r
276 //uci_send_isready(Uci); // In UCI mode this done by the GUI
\r
277 //Grrr...Toga can fixes the number of threads after "isready"
\r
278 //So we delay "isready"
\r
284 } else if (match(string,"accepted *")) {
\r
288 } else if (match(string,"analyze")) {
\r
290 State->computer[White] = false;
\r
291 State->computer[Black] = false;
\r
293 XB->analyse = true;
\r
294 XB->new_hack = false;
\r
295 ASSERT(!XB->result);
\r
296 XB->result = false;
\r
300 } else if (match(string,"bk")) {
\r
302 if (option_get_bool("Book")) {
\r
303 game_get_board(Game,board);
\r
307 } else if (match(string,"black")) {
\r
309 if (colour_is_black(game_turn(Game))) {
\r
311 State->computer[White] = true;
\r
312 State->computer[Black] = false;
\r
314 XB->new_hack = true;
\r
315 XB->result = false;
\r
320 } else if (match(string,"computer")) {
\r
322 XB->computer = true;
\r
324 } else if (match(string,"draw")) {
\r
325 if(uci_option_exist(Uci,"UCI_DrawOffers")){
\r
326 my_log("POLYGLOT draw from XB received");
\r
327 uci_send_option(Uci,"DrawOffer","%s","draw");}
\r
328 } else if (match(string,"easy")) {
\r
330 XB->ponder = false;
\r
334 } else if (match(string,"edit")) {
\r
338 gui_send(GUI,"Error (unknown command): %s",string);
\r
340 } else if (match(string,"exit")) {
\r
342 State->computer[White] = false;
\r
343 State->computer[Black] = false;
\r
345 XB->analyse = false;
\r
349 } else if (match(string,"force")) {
\r
351 State->computer[White] = false;
\r
352 State->computer[Black] = false;
\r
356 } else if (match(string,"go")) {
\r
358 State->computer[game_turn(Game)] = true;
\r
359 State->computer[colour_opp(game_turn(Game))] = false;
\r
361 XB->new_hack = false;
\r
362 ASSERT(!XB->result);
\r
363 XB->result = false;
\r
367 } else if (match(string,"hard")) {
\r
373 } else if (match(string,"hint")) {
\r
375 if (option_get_bool("Book")) {
\r
377 game_get_board(Game,board);
\r
378 move = book_move(board,false);
\r
380 if (move != MoveNone && move_is_legal(move,board)) {
\r
381 move_to_san(move,board,move_string,256);
\r
382 gui_send(GUI,"Hint: %s",move_string);
\r
386 } else if (match(string,"ics *")) {
\r
390 } else if (match(string,"level * *:* *")) {
\r
392 XB->mps = atoi(Star[0]);
\r
393 XB->base = double(atoi(Star[1])) * 60.0 + double(atoi(Star[2]));
\r
394 XB->inc = double(atoi(Star[3]));
\r
396 } else if (match(string,"level * * *")) {
\r
398 XB->mps = atoi(Star[0]);
\r
399 XB->base = double(atoi(Star[1])) * 60.0;
\r
400 XB->inc = double(atoi(Star[2]));
\r
402 } else if (match(string,"name *")) {
\r
404 my_string_set(&XB->name,Star[0]);
\r
406 } else if (match(string,"new")) {
\r
408 uci_send_isready(Uci);
\r
409 my_log("POLYGLOT NEW GAME\n");
\r
411 option_set("Chess960","false");
\r
416 State->computer[White] = false;
\r
417 State->computer[Black] = false;
\r
419 State->computer[White] = false;
\r
420 State->computer[Black] = true;
\r
423 XB->new_hack = true;
\r
424 XB->result = false;
\r
426 XB->depth_limit = false;
\r
428 XB->computer = false;
\r
429 my_string_set(&XB->name,"<empty>");
\r
434 uci_send_ucinewgame(Uci);
\r
436 } else if (match(string,"nopost")) {
\r
440 } else if (match(string,"otim *")) {
\r
442 XB->opp_time = double(atoi(Star[0])) / 100.0;
\r
443 if (XB->opp_time < 0.0) XB->opp_time = 0.0;
\r
445 } else if (match(string,"pause")) {
\r
449 gui_send(GUI,"Error (unknown command): %s",string);
\r
451 } else if (match(string,"ping *")) {
\r
453 // HACK; TODO: answer only after an engine move
\r
456 if (XB->ping >= 0) gui_send(GUI,"pong %d",XB->ping); // HACK: get rid of old ping
\r
457 XB->ping = atoi(Star[0]);
\r
458 uci_send_isready(Uci);
\r
460 ASSERT(XB->ping==-1);
\r
461 gui_send(GUI,"pong %s",Star[0]);
\r
464 } else if (match(string,"playother")) {
\r
466 State->computer[game_turn(Game)] = false;
\r
467 State->computer[colour_opp(game_turn(Game))] = true;
\r
469 XB->new_hack = false;
\r
470 ASSERT(!XB->result);
\r
471 XB->result = false;
\r
475 } else if (match(string,"post")) {
\r
479 } else if (match(string,"protover *")) {
\r
481 XB->proto_ver = atoi(Star[0]);
\r
482 ASSERT(XB->proto_ver>=2);
\r
484 gui_send(GUI,"feature done=0");
\r
486 gui_send(GUI,"feature analyze=1");
\r
487 gui_send(GUI,"feature colors=0");
\r
488 gui_send(GUI,"feature draw=1");
\r
489 gui_send(GUI,"feature ics=1");
\r
490 gui_send(GUI,"feature myname=\"%s\"",option_get_string("EngineName"));
\r
491 gui_send(GUI,"feature name=1");
\r
492 gui_send(GUI,"feature pause=0");
\r
493 gui_send(GUI,"feature ping=1");
\r
494 gui_send(GUI,"feature playother=1");
\r
495 gui_send(GUI,"feature reuse=1");
\r
496 gui_send(GUI,"feature san=0");
\r
497 gui_send(GUI,"feature setboard=1");
\r
498 gui_send(GUI,"feature sigint=0");
\r
499 gui_send(GUI,"feature sigterm=0");
\r
500 gui_send(GUI,"feature time=1");
\r
501 gui_send(GUI,"feature usermove=1");
\r
502 if (XB->has_feature_memory){
\r
503 gui_send(GUI,"feature memory=1");
\r
505 if (XB->has_feature_smp){
\r
506 gui_send(GUI,"feature smp=1");
\r
508 if (XB->has_feature_egt){
\r
509 // TODO: support for other types of table bases
\r
510 gui_send(GUI,"feature egt=\"nalimov\"");
\r
513 if (uci_option_exist(Uci,"UCI_Chess960")) {
\r
514 gui_send(GUI,"feature variants=\"normal,fischerandom\"");
\r
516 gui_send(GUI,"feature variants=\"normal\"");
\r
518 gui_send(GUI,"feature done=1"); // moved from engine_step
\r
521 //if (Uci->ready) xboard_send(XBoard,"feature done=1");
\r
523 // otherwise "feature done=1" will be sent when the engine is ready
\r
525 } else if (match(string,"quit")) {
\r
526 my_log("POLYGLOT *** \"quit\" from GUI ***\n");
\r
528 } else if (match(string,"random")) {
\r
532 } else if (match(string,"rating * *")) {
\r
536 } else if (match(string,"remove")) {
\r
538 if (game_pos(Game) >= 2) {
\r
540 game_goto(Game,game_pos(Game)-2);
\r
542 ASSERT(!XB->new_hack);
\r
543 XB->new_hack = false; // HACK?
\r
544 XB->result = false;
\r
550 } else if (match(string,"rejected *")) {
\r
554 } else if (match(string,"reset")) { // protover 3?
\r
558 gui_send(GUI,"Error (unknown command): %s",string);
\r
561 || match(string,"result * {*}")
\r
562 || match(string,"result * {* }")
\r
563 || match(string,"result * { *}")
\r
564 || match(string,"result * { * }")) {
\r
566 my_log("POLYGLOT GAME END\n");
\r
574 if (option_get_bool("Book") && option_get_bool("BookLearn")) {
\r
577 } else if (my_string_equal(Star[0],"1-0")) {
\r
579 } else if (my_string_equal(Star[0],"0-1")) {
\r
581 } else if (my_string_equal(Star[0],"1/2-1/2")) {
\r
585 } else if (match(string,"resume")) {
\r
589 gui_send(GUI,"Error (unknown command): %s",string);
\r
591 } else if (XB->has_feature_smp && match(string,"cores *")){
\r
592 int cores=atoi(Star[0]);
\r
594 // updating the number of cores
\r
595 my_log("POLYGLOT setting the number of cores to %d\n",cores);
\r
596 start_protected_command();
\r
597 uci_set_threads(Uci,cores);
\r
598 end_protected_command();
\r
601 gui_send(GUI,"Error (unknown command): %s",string);
\r
603 } else if (XB->has_feature_egt && match(string,"egtpath * *")){
\r
604 char *type=Star[0];
\r
605 char *path=Star[1];
\r
606 if(!my_string_case_equal(Star[0],"nalimov")){
\r
608 gui_send(GUI,"Error (unsupported table base format): %s",string);
\r
609 }else if(my_string_empty(path)){
\r
611 gui_send(GUI,"Error (unknown command): %s",string);
\r
613 // updating NalimovPath
\r
614 my_log("POLYGLOT setting the Nalimov path to %s\n",path);
\r
615 start_protected_command();
\r
616 uci_send_option(Uci,"NalimovPath","%s",path);
\r
617 end_protected_command();
\r
619 } else if (XB->has_feature_memory && match(string,"memory *")){
\r
620 int memory = atoi(Star[0]);
\r
624 // updating the available memory
\r
625 my_log("POLYGLOT setting the amount of memory to %dMb\n",memory);
\r
626 if(uci_get_option(Uci,"NalimovCache")>=0){
\r
627 nalimov_cache=atoi(Uci->option[uci_get_option(Uci,"NalimovCache")].value);
\r
631 my_log("POLYGLOT Nalimov Cache is %dMb\n",nalimov_cache);
\r
632 real_memory=memory-nalimov_cache;
\r
634 start_protected_command();
\r
635 uci_send_option(Uci,"Hash", "%d", real_memory);
\r
636 end_protected_command();
\r
640 gui_send(GUI,"Error (unknown command): %s",string);
\r
643 } else if (match(string,"sd *")) {
\r
645 XB->depth_limit = true;
\r
646 XB->depth_max = atoi(Star[0]);
\r
648 } else if (match(string,"setboard *")) {
\r
650 my_log("POLYGLOT FEN %s\n",Star[0]);
\r
652 if (!game_init(Game,Star[0])) my_fatal("xboard_step(): bad FEN \"%s\"\n",Star[0]);
\r
654 State->computer[White] = false;
\r
655 State->computer[Black] = false;
\r
657 XB->new_hack = true; // HACK?
\r
658 XB->result = false;
\r
663 } else if (match(string,"st *")) {
\r
665 XB->time_limit = true;
\r
666 XB->time_max = double(atoi(Star[0]));
\r
668 } else if (match(string,"time *")) {
\r
670 XB->my_time = double(atoi(Star[0])) / 100.0;
\r
671 if (XB->my_time < 0.0) XB->my_time = 0.0;
\r
673 } else if (match(string,"undo")) {
\r
675 if (game_pos(Game) >= 1) {
\r
677 game_goto(Game,game_pos(Game)-1);
\r
679 ASSERT(!XB->new_hack);
\r
680 XB->new_hack = false; // HACK?
\r
681 XB->result = false;
\r
687 } else if (match(string,"usermove *")) {
\r
689 game_get_board(Game,board);
\r
690 move = move_from_san(Star[0],board);
\r
692 if (move != MoveNone && move_is_legal(move,board)) {
\r
694 XB->new_hack = false;
\r
695 ASSERT(!XB->result);
\r
696 XB->result = false;
\r
703 gui_send(GUI,"Illegal move: %s",Star[0]);
\r
706 } else if (match(string,"variant *")) {
\r
708 if (my_string_equal(Star[0],"fischerandom")) {
\r
709 option_set("Chess960","true");
\r
711 option_set("Chess960","false");
\r
714 } else if (match(string,"white")) {
\r
716 if (colour_is_white(game_turn(Game))) {
\r
718 State->computer[White] = false;
\r
719 State->computer[Black] = true;
\r
721 XB->new_hack = true;
\r
722 XB->result = false;
\r
727 } else if (match(string,"xboard")) {
\r
731 } else if (match(string,".")) { // analyse info
\r
733 if (State->state == ANALYSE) {
\r
734 int depth=Uci->best_depth;//HACK: don't clear engine-output window...
\r
736 ASSERT(Uci->searching);
\r
737 ASSERT(Uci->pending_nb>=1);
\r
739 if (Uci->root_move != MoveNone && move_is_legal(Uci->root_move,Uci->board)) {
\r
740 move_to_san(Uci->root_move,Uci->board,move_string,256);
\r
741 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);
\r
743 gui_send(GUI,"stat01: %.0f "S64_FORMAT" %d %d %d",Uci->time*100.0,Uci->node_nb,/*Uci->*/depth,0,0); // HACK
\r
747 } else if (match(string,"?")) { // move now
\r
749 if (State->state == THINK) {
\r
751 ASSERT(Uci->searching);
\r
752 ASSERT(Uci->pending_nb>=1);
\r
754 // HACK: just send "stop" to the engine
\r
756 if (Uci->searching) {
\r
757 my_log("POLYGLOT STOP SEARCH\n");
\r
758 engine_send(Engine,"stop");
\r
762 } else { // unknown command, maybe a move?
\r
764 game_get_board(Game,board);
\r
765 move = move_from_san(string,board);
\r
767 if (move != MoveNone && move_is_legal(move,board)) {
\r
769 XB->new_hack = false;
\r
770 ASSERT(!XB->result);
\r
771 XB->result = false;
\r
776 } else if (move != MoveNone) {
\r
778 gui_send(GUI,"Illegal move: %s",string);
\r
782 gui_send(GUI,"Error (unknown command): %s",string);
\r
790 static void engine_step(char string[]) {
\r
793 event = uci_parse(Uci,string);
\r
797 if ((event & EVENT_READY) != 0) {
\r
799 // the engine is now ready
\r
803 // if (XB->proto_ver >= 2) xboard_send(XBoard,"feature done=1");
\r
806 if (!DelayPong && XB->ping >= 0) {
\r
807 gui_send(GUI,"pong %d",XB->ping);
\r
812 if ((event & EVENT_MOVE) != 0 && State->state == THINK) {
\r
814 // the engine is playing a move
\r
816 // MEGA HACK: estimate remaining time because XBoard won't send it!
\r
818 my_timer_stop(State->timer);
\r
820 XB->my_time -= my_timer_elapsed_real(State->timer);
\r
821 XB->my_time += XB->inc;
\r
822 if (XB->mps != 0 && (game_move_nb(Game) + 1) % XB->mps == 0) XB->my_time += XB->base;
\r
824 if (XB->my_time < 0.0) XB->my_time = 0.0;
\r
826 // play the engine move
\r
828 comp_move(Uci->best_move);
\r
831 if ((event & EVENT_PV) != 0) {
\r
833 // the engine has sent a new PV
\r
837 if((event & (EVENT_DRAW|EVENT_RESIGN))!=0){
\r
838 my_log("POYGLOT draw offer/resign from engine\n");
\r
839 if(uci_option_exist(Uci,"UCI_DrawOffers")){
\r
840 if(event & EVENT_DRAW)
\r
841 gui_send(GUI,"offer draw");
\r
843 gui_send(GUI,"resign");
\r
850 static void comp_move(int move) {
\r
855 ASSERT(move_is_ok(move));
\r
857 ASSERT(State->state==THINK);
\r
858 ASSERT(!XB->analyse);
\r
860 if(option_get_bool("RepeatPV")==true)
\r
861 send_pv(); // to update time and nodes
\r
865 game_get_board(Game,board);
\r
867 if (move_is_castle(move,board) && option_get_bool("Chess960")) {
\r
868 if (!move_to_san(move,board,string,256)) my_fatal("comp_move(): move_to_san() failed\n"); // O-O/O-O-O
\r
870 if (!move_to_can(move,board,string,256)) my_fatal("comp_move(): move_to_can() failed\n");
\r
873 gui_send(GUI,"move %s",string);
\r
877 if (option_get_bool("Resign") && Uci->root_move_nb > 1) {
\r
879 if (Uci->best_score <= -abs(option_get_int("ResignScore"))) {
\r
881 State->resign_nb++;
\r
882 my_log("POLYGLOT %d move%s with resign score\n",State->resign_nb,(State->resign_nb>1)?"s":"");
\r
884 if (State->resign_nb >= option_get_int("ResignMoves")) {
\r
885 my_log("POLYGLOT *** RESIGN ***\n");
\r
886 gui_send(GUI,"resign");
\r
891 if (State->resign_nb > 0) my_log("POLYGLOT resign reset (State->resign_nb=%d)\n",State->resign_nb);
\r
892 State->resign_nb = 0;
\r
904 static void move_step(int move) {
\r
907 char move_string[256];
\r
909 ASSERT(move_is_ok(move));
\r
913 game_get_board(Game,board);
\r
915 if (move != MoveNone && move_is_legal(move,board)) {
\r
917 move_to_san(move,board,move_string,256);
\r
918 my_log("POLYGLOT MOVE %s\n",move_string);
\r
922 move_to_can(move,board,move_string,256);
\r
923 my_log("POLYGLOT ILLEGAL MOVE \"%s\"\n",move_string);
\r
926 my_fatal("move_step(): illegal move \"%s\"\n",move_string);
\r
931 game_add_move(Game,move);
\r
937 static void board_update() {
\r
941 ASSERT(!XB->result);
\r
943 switch (game_status(Game)) {
\r
947 gui_send(GUI,"1-0 {White mates}");
\r
950 gui_send(GUI,"0-1 {Black mates}");
\r
953 gui_send(GUI,"1/2-1/2 {Stalemate}");
\r
955 case DRAW_MATERIAL:
\r
956 gui_send(GUI,"1/2-1/2 {Draw by insufficient material}");
\r
959 gui_send(GUI,"1/2-1/2 {Draw by fifty-move rule}");
\r
961 case DRAW_REPETITION:
\r
962 gui_send(GUI,"1/2-1/2 {Draw by repetition}");
\r
972 static void mess() {
\r
974 // clear state variables
\r
976 State->resign_nb = 0;
\r
977 State->exp_move = MoveNone;
\r
978 my_timer_reset(State->timer);
\r
980 // abort a possible search
\r
984 // calculate the new state
\r
987 } else if (!active()) {
\r
988 State->state = WAIT;
\r
989 my_log("POLYGLOT WAIT\n");
\r
990 } else if (XB->analyse) {
\r
991 State->state = ANALYSE;
\r
992 my_log("POLYGLOT ANALYSE\n");
\r
993 } else if (State->computer[game_turn(Game)]) {
\r
994 State->state = THINK;
\r
995 my_log("POLYGLOT THINK\n");
\r
997 State->state = WAIT;
\r
998 my_log("POLYGLOT WAIT\n");
\r
1006 static void no_mess(int move) {
\r
1008 ASSERT(move_is_ok(move));
\r
1010 // just received a move, calculate the new state
\r
1014 } else if (!active()) {
\r
1016 stop_search(); // abort a possible search
\r
1018 State->state = WAIT;
\r
1019 State->exp_move = MoveNone;
\r
1021 my_log("POLYGLOT WAIT\n");
\r
1023 } else if (State->state == WAIT) {
\r
1025 ASSERT(State->computer[game_turn(Game)]);
\r
1026 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
\r
1027 ASSERT(!XB->analyse);
\r
1029 my_log("POLYGLOT WAIT -> THINK\n");
\r
1031 State->state = THINK;
\r
1032 State->exp_move = MoveNone;
\r
1034 } else if (State->state == THINK) {
\r
1036 ASSERT(!State->computer[game_turn(Game)]);
\r
1037 ASSERT(State->computer[colour_opp(game_turn(Game))]);
\r
1038 ASSERT(!XB->analyse);
\r
1040 if (ponder() && ponder_ok(Uci->ponder_move)) {
\r
1042 my_log("POLYGLOT THINK -> PONDER\n");
\r
1044 State->state = PONDER;
\r
1045 State->exp_move = Uci->ponder_move;
\r
1049 my_log("POLYGLOT THINK -> WAIT\n");
\r
1051 State->state = WAIT;
\r
1052 State->exp_move = MoveNone;
\r
1055 } else if (State->state == PONDER) {
\r
1057 ASSERT(State->computer[game_turn(Game)]);
\r
1058 ASSERT(!State->computer[colour_opp(game_turn(Game))]);
\r
1059 ASSERT(!XB->analyse);
\r
1061 if (move == State->exp_move && Uci->searching) {
\r
1063 ASSERT(Uci->searching);
\r
1064 ASSERT(Uci->pending_nb>=1);
\r
1066 my_timer_start(State->timer);//also resets
\r
1068 my_log("POLYGLOT PONDER -> THINK (*** HIT ***)\n");
\r
1069 engine_send(Engine,"ponderhit");
\r
1071 State->state = THINK;
\r
1072 State->exp_move = MoveNone;
\r
1074 send_pv(); // update display
\r
1076 return; // do not launch a new search
\r
1080 my_log("POLYGLOT PONDER -> THINK (miss)\n");
\r
1084 State->state = THINK;
\r
1085 State->exp_move = MoveNone;
\r
1088 } else if (State->state == ANALYSE) {
\r
1090 ASSERT(XB->analyse);
\r
1092 my_log("POLYGLOT ANALYSE -> ANALYSE\n");
\r
1104 // start_protected_command()
\r
1106 static void start_protected_command(){
\r
1110 static void end_protected_command(){
\r
1111 if(Uci->ready){ // not init faze
\r
1112 uci_send_isready_sync(Uci); // gobble up spurious "bestmove"
\r
1114 update_remaining_time();
\r
1115 search_update(); // relaunch search if necessary
\r
1118 // update_remaining_time()
\r
1120 static void update_remaining_time(){
\r
1122 if(State->timer->running){
\r
1123 my_timer_stop(State->timer);
\r
1124 reduce = my_timer_elapsed_real(State->timer);
\r
1125 my_log("POLYGLOT reducing remaing time by %f seconds\n",reduce);
\r
1126 XB->my_time -= reduce;
\r
1127 if(XB->my_time<0.0){
\r
1134 // search_update()
\r
1136 static void search_update() {
\r
1141 int nalimov_cache;
\r
1144 ASSERT(!Uci->searching);
\r
1149 // launch a new search if needed
\r
1153 if (State->state == THINK || State->state == PONDER || State->state == ANALYSE) {
\r
1157 if (State->state == THINK && option_get_bool("Book")) {
\r
1159 game_get_board(Game,Uci->board);
\r
1161 move = book_move(Uci->board,option_get_bool("BookRandom"));
\r
1163 if (move != MoveNone && move_is_legal(move,Uci->board)) {
\r
1165 my_log("POLYGLOT *BOOK MOVE*\n");
\r
1167 search_clear(); // clears Uci->ponder_move
\r
1168 Uci->best_move = move;
\r
1170 board_copy(board,Uci->board);
\r
1171 move_do(board,move);
\r
1172 Uci->ponder_move = book_move(board,false); // expected move = best book move
\r
1174 Uci->best_pv[0] = Uci->best_move;
\r
1175 Uci->best_pv[1] = Uci->ponder_move; // can be MoveNone
\r
1176 Uci->best_pv[2] = MoveNone;
\r
1178 comp_move(Uci->best_move);
\r
1186 my_log("POLYGLOT START SEARCH\n");
\r
1190 uci_send_option(Uci,"UCI_Chess960","%s",option_get_bool("Chess960")?"true":"false");
\r
1192 if (option_get_int("UCIVersion") >= 2) {
\r
1193 uci_send_option(Uci,"UCI_Opponent","none none %s %s",(XB->computer)?"computer":"human",XB->name);
\r
1194 uci_send_option(Uci,"UCI_AnalyseMode","%s",(XB->analyse)?"true":"false");
\r
1197 uci_send_option(Uci,"Ponder","%s",ponder()?"true":"false");
\r
1201 move = (State->state == PONDER) ? State->exp_move : MoveNone;
\r
1202 send_board(move); // updates Uci->board global variable
\r
1206 if (State->state == THINK || State->state == PONDER) {
\r
1208 engine_send_queue(Engine,"go");
\r
1210 if (XB->time_limit) {
\r
1212 // fixed time per move
\r
1214 engine_send_queue(Engine," movetime %.0f",XB->time_max*1000.0);
\r
1220 if (colour_is_white(Uci->board->turn)) {
\r
1221 engine_send_queue(Engine," wtime %.0f btime %.0f",XB->my_time*1000.0,XB->opp_time*1000.0);
\r
1223 engine_send_queue(Engine," wtime %.0f btime %.0f",XB->opp_time*1000.0,XB->my_time*1000.0);
\r
1226 if (XB->inc != 0.0) engine_send_queue(Engine," winc %.0f binc %.0f",XB->inc*1000.0,XB->inc*1000.0);
\r
1228 if (XB->mps != 0) {
\r
1230 move_nb = XB->mps - (Uci->board->move_nb % XB->mps);
\r
1231 ASSERT(move_nb>=1&&move_nb<=XB->mps);
\r
1233 engine_send_queue(Engine," movestogo %d",move_nb);
\r
1237 if (XB->depth_limit) engine_send_queue(Engine," depth %d",XB->depth_max);
\r
1239 if (State->state == PONDER) engine_send_queue(Engine," ponder");
\r
1241 engine_send(Engine,""); // newline
\r
1243 } else if (State->state == ANALYSE) {
\r
1245 engine_send(Engine,"go infinite");
\r
1252 // init search info
\r
1254 ASSERT(!Uci->searching);
\r
1258 Uci->searching = true;
\r
1259 Uci->pending_nb++;
\r
1265 static void search_clear() {
\r
1271 my_timer_start(State->timer);//also resets
\r
1276 static bool active() {
\r
1280 if (game_status(Game) != PLAYING) return false; // game ended
\r
1284 if (XB->analyse) return true; // analysing
\r
1285 if (!State->computer[White] && !State->computer[Black]) return false; // force mode
\r
1286 if (XB->new_hack || XB->result) return false; // unstarted or ended game
\r
1288 return true; // playing
\r
1293 static bool ponder() {
\r
1295 return XB->ponder && (option_get_bool("CanPonder") || uci_option_exist(Uci,"Ponder"));
\r
1299 static bool ponder_ok(int move) {
\r
1303 ASSERT(move==MoveNone||move_is_ok(move));
\r
1305 // legal ponder move?
\r
1307 if (move == MoveNone) return false;
\r
1309 game_get_board(Game,board);
\r
1310 if (!move_is_legal(move,board)) return false;
\r
1312 // UCI-legal resulting position?
\r
1314 game_add_move(Game,move);
\r
1316 game_get_board(Game,board);
\r
1317 status = game_status(Game);
\r
1319 game_rem_move(Game);
\r
1321 if (status != PLAYING) return false; // game ended
\r
1323 if (option_get_bool("Book") && is_in_book(board)) {
\r
1332 static void stop_search() {
\r
1334 if (Uci->searching) {
\r
1336 ASSERT(Uci->searching);
\r
1337 ASSERT(Uci->pending_nb>=1);
\r
1339 my_log("POLYGLOT STOP SEARCH\n");
\r
1342 engine_send(Engine,"stop");
\r
1343 Uci->searching = false;
\r
1346 if (option_get_bool("SyncStop")) {
\r
1347 uci_send_stop_sync(Uci);
\r
1349 uci_send_stop(Uci);
\r
1356 static void send_board(int extra_move) {
\r
1365 ASSERT(extra_move==MoveNone||move_is_ok(extra_move));
\r
1367 ASSERT(!Uci->searching);
\r
1371 game_get_board(Game,Uci->board);
\r
1372 if (extra_move != MoveNone) move_do(Uci->board,extra_move);
\r
1374 board_to_fen(Uci->board,fen,256);
\r
1375 my_log("POLYGLOT FEN %s\n",fen);
\r
1377 ASSERT(board_can_play(Uci->board));
\r
1382 end = game_pos(Game);
\r
1383 ASSERT(end>=start);
\r
1387 game_get_board(Game,board,start);
\r
1388 board_to_fen(board,string,256);
\r
1390 engine_send_queue(Engine,"position");
\r
1392 if (my_string_equal(string,StartFen)) {
\r
1393 engine_send_queue(Engine," startpos");
\r
1395 engine_send_queue(Engine," fen %s",string);
\r
1400 if (end > start || extra_move != MoveNone) engine_send_queue(Engine," moves");
\r
1402 for (pos = start; pos < end; pos++) { // game moves
\r
1404 move = game_move(Game,pos);
\r
1406 move_to_can(move,board,string,256);
\r
1407 engine_send_queue(Engine," %s",string);
\r
1409 move_do(board,move);
\r
1412 if (extra_move != MoveNone) { // move to ponder on
\r
1413 move_to_can(extra_move,board,string,256);
\r
1414 engine_send_queue(Engine," %s",string);
\r
1419 engine_send(Engine,""); // newline
\r
1424 static void send_pv() {
\r
1426 char pv_string[StringSize];
\r
1429 char move_string[StringSize];
\r
1431 ASSERT(State->state!=WAIT);
\r
1433 if (Uci->best_depth == 0) return;
\r
1435 // xboard search information
\r
1439 if (State->state == THINK || State->state == ANALYSE) {
\r
1441 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
\r
1443 if(Uci->depth==-1) //hack to clear the engine output window
\r
1444 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" ",0,Uci->best_score,Uci->time*100.0,Uci->node_nb);
\r
1446 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" %s",Uci->best_depth,Uci->best_score,Uci->time*100.0,Uci->node_nb,pv_string);
\r
1448 } else if (State->state == PONDER && option_get_bool("ShowPonder")) {
\r
1450 game_get_board(Game,board);
\r
1451 move = State->exp_move;
\r
1453 if (move != MoveNone && move_is_legal(move,board)) {
\r
1454 move_to_san(move,board,move_string,256);
\r
1455 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
\r
1456 gui_send(GUI,"%d %+d %.0f "S64_FORMAT" (%s) %s",Uci->best_depth,Uci->best_score,Uci->time*100.0,Uci->node_nb,move_string,pv_string);
\r
1463 if ((Uci->searching && option_get_bool("KibitzPV") && Uci->time >= option_get_double("KibitzDelay"))
\r
1464 || (!Uci->searching && option_get_bool("KibitzMove"))) {
\r
1466 if (State->state == THINK || State->state == ANALYSE) {
\r
1468 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
\r
1469 gui_send(GUI,"%s depth=%d time=%.2f node="S64_FORMAT" speed=%.0f score=%+.2f pv=\"%s\"",option_get_string("KibitzCommand"),Uci->best_depth,Uci->time,Uci->node_nb,Uci->speed,double(Uci->best_score)/100.0,pv_string);
\r
1471 } else if (State->state == PONDER) {
\r
1473 game_get_board(Game,board);
\r
1474 move = State->exp_move;
\r
1476 if (move != MoveNone && move_is_legal(move,board)) {
\r
1477 move_to_san(move,board,move_string,256);
\r
1478 line_to_san(Uci->best_pv,Uci->board,pv_string,StringSize);
\r
1479 gui_send(GUI,"%s depth=%d time=%.2f node="S64_FORMAT" speed=%.0f score=%+.2f pv=\"(%s) %s\"",option_get_string("KibitzCommand"),Uci->best_depth,Uci->time,Uci->node_nb,Uci->speed,double(Uci->best_score)/100.0,move_string,pv_string);
\r
1487 static void learn(int result) {
\r
1493 ASSERT(result>=-1&&result<=+1);
\r
1495 ASSERT(XB->result);
\r
1496 ASSERT(State->computer[White]||State->computer[Black]);
\r
1503 } else if (State->computer[White]) {
\r
1505 } else if (State->computer[Black]) {
\r
1509 my_fatal("learn(): unknown side\n");
\r
1513 } else if (result > 0) {
\r
1514 my_log("POLYGLOT *LEARN WIN*\n");
\r
1515 } else if (result < 0) {
\r
1516 my_log("POLYGLOT *LEARN LOSS*\n");
\r
1518 my_log("POLYGLOT *LEARN DRAW*\n");
\r
1523 for (; pos < Game->size; pos += 2) {
\r
1525 game_get_board(Game,board,pos);
\r
1526 move = game_move(Game,pos);
\r
1528 book_learn_move(board,move,result);
\r
1534 // end of adapter.cpp
\r