1
2 // main.c
3
4 // includes
5
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11
12 #include "attack.h"
13 #include "board.h"
14 #include "book.h"
15 #include "book_make.h"
16 #include "book_merge.h"
17 #include "engine.h"
18 #include "epd.h"
19 #include "fen.h"
20 #include "gui.h"
21 #include "hash.h"
22 #include "list.h"
23 #include "main.h"
24 #include "mainloop.h"
25 #include "move.h"
26 #include "move_gen.h"
27 #include "option.h"
28 #include "piece.h"
29 #include "search.h"
30 #include "square.h"
31 #include "uci.h"
32 #include "util.h"
33 #include "xboard2uci.h"
34 #include "uci2uci.h"
35 #include "ini.h"
36 #include "util.h"
37
38
39 // constants
40
41
42 static const char * const Version = "1.4.70b";
43 static const char * const HelpMessage = "\
44 SYNTAX\n\
45 * polyglot [configfile] [-noini] [-ec engine] [-ed enginedirectory] [-en enginename] [-log true/false] [-lf logfile] [-pg <name>=<value>]* [-uci <name>=<value>]*\n\
46 * polyglot make-book [-pgn inputfile] [-bin outputfile] [-max-ply ply] [-min-game games] [-min-score score] [-only-white] [-only-black] [-uniform]\n\
47 * polyglot merge-book -in1 inputfile1 -in2 inputfile2 [-out outputfile]\n\
48 * polyglot info-book [-bin inputfile] [-exact]\n\
49 * polyglot dump-book [-bin inputfile] -color color [-out outputfile]\n\
50 * polyglot [configfile] epd-test [engineoptions] [-epd inputfile] [-min-depth depth] [-max-depth depth] [-min-time time] [-max-time time] [-depth-delta delta]\n\
51 * polyglot perft [-fen fen] [-max-depth depth]\
52 ";
53
54 static const int SearchDepth = 63;
55 static const double SearchTime = 3600.0;
56
57 // variables
58
59 static bool Init;
60
61 // prototypes
62
63 static void stop_search ();
64
65 // functions
66
67 // arg_shift_left()
68
arg_shift_left(char ** argv,int index)69 static void arg_shift_left(char **argv, int index){
70 int i;
71 for(i=index; argv[i]!=NULL; i++){
72 argv[i]=argv[i+1];
73 }
74 }
75
76 // parse_args()
77
parse_args(ini_t * ini,char ** argv)78 static void parse_args(ini_t *ini, char **argv){
79 int arg_index;
80 char *arg;
81 arg_index=0;
82 while((arg=argv[arg_index])){
83 if(my_string_equal(arg,"-ec") && argv[arg_index+1]){
84 ini_insert_ex(ini,"PolyGlot","EngineCommand",argv[arg_index+1]);
85 arg_shift_left(argv,arg_index);
86 arg_shift_left(argv,arg_index);
87 continue;
88 }if(my_string_equal(arg,"-ed") && argv[arg_index+1]){
89 ini_insert_ex(ini,"PolyGlot","EngineDir",argv[arg_index+1]);
90 arg_shift_left(argv,arg_index);
91 arg_shift_left(argv,arg_index);
92 continue;
93 }
94 if(my_string_equal(arg,"-en") && argv[arg_index+1]){
95 ini_insert_ex(ini,"PolyGlot","EngineName",argv[arg_index+1]);
96 arg_shift_left(argv,arg_index);
97 arg_shift_left(argv,arg_index);
98 continue;
99 }
100 if(my_string_equal(arg,"-log") &&
101 argv[arg_index+1] &&
102 IS_BOOL(argv[arg_index+1])){
103 ini_insert_ex(ini,
104 "PolyGlot",
105 "Log",
106 TO_BOOL(argv[arg_index+1])?"true":"false");
107 arg_shift_left(argv,arg_index);
108 arg_shift_left(argv,arg_index);
109 continue;
110 }
111 if(my_string_equal(arg,"-lf") && argv[arg_index+1]){
112 ini_insert_ex(ini,"PolyGlot","LogFile",argv[arg_index+1]);
113 arg_shift_left(argv,arg_index);
114 arg_shift_left(argv,arg_index);
115 continue;
116 }
117 if(my_string_equal(arg,"-wb") &&
118 argv[arg_index+1]&&
119 IS_BOOL(argv[arg_index+1])){
120 ini_insert_ex(ini,"PolyGlot",
121 "OnlyWbOptions",
122 TO_BOOL(argv[arg_index+1])?"true":"false");
123 arg_shift_left(argv,arg_index);
124 arg_shift_left(argv,arg_index);
125 continue;
126 }
127 if((my_string_equal(arg,"-pg")||my_string_equal(arg,"-uci")) &&
128 argv[arg_index+1]){
129 int ret;
130 char section[StringSize];
131 char name[StringSize];
132 char value[StringSize];
133 ret=ini_line_parse(argv[arg_index+1],section,name,value);
134 if(ret==NAME_VALUE){
135 if(my_string_equal(arg,"-pg")){
136 ini_insert_ex(ini,"PolyGlot",name,value);
137 }else{
138 ini_insert_ex(ini,"Engine",name,value);
139 }
140 }
141 arg_shift_left(argv,arg_index);
142 arg_shift_left(argv,arg_index);
143 continue;
144 }
145 arg_index++;
146 }
147 }
148
149
150 // make_ini()
151
make_ini(ini_t * ini)152 static void make_ini(ini_t *ini){
153 option_t *opt;
154 ini_insert_ex(ini,"polyglot",
155 "EngineCommand",
156 option_get(Option,"EngineCommand"));
157 ini_insert_ex(ini,"polyglot",
158 "EngineDir",
159 option_get(Option,"EngineDir"));
160 option_start_iter(Option);
161 while((opt=option_next(Option))){
162 if(my_string_case_equal(opt->name,"SettingsFile")) continue;
163 if(my_string_case_equal(opt->name,"EngineCommand")) continue;
164 if(my_string_case_equal(opt->name,"EngineDir")) continue;
165 if(!my_string_equal(opt->value,opt->default_)&& !IS_BUTTON(opt->type))
166 {
167 ini_insert_ex(ini,"polyglot",opt->name,opt->value);
168 }
169 }
170 option_start_iter(Uci->option);
171 while((opt=option_next(Uci->option))){
172 if(!strncmp(opt->name,"UCI_",4) &&
173 !my_string_case_equal(opt->name,"UCI_LimitStrength") &&
174 !my_string_case_equal(opt->name,"UCI_Elo"))continue;
175 if(!my_string_equal(opt->value,opt->default_)&&
176 !IS_BUTTON(opt->type)){
177 ini_insert_ex(ini,"engine",opt->name,opt->value);
178 }
179 }
180 }
181
182
183 // write_ini()
184
write_ini(const char * filename,ini_t * ini)185 static void write_ini(const char *filename,
186 ini_t *ini){
187 // TODO Quote, dequote
188 const char *quote;
189 ini_entry_t *entry;
190 char tmp[StringSize];
191 char tmp1[StringSize];
192 char tmp2[StringSize];
193 FILE *f;
194 time_t t=time(NULL);
195 f=fopen(filename,"w");
196 if(!f){
197 gui_send(GUI,"tellusererror write_ini(): %s: %s.",filename,strerror(errno));
198 my_log("POLYGLOT write_ini(): %s: %s.\n",filename,strerror(errno));
199 return;
200 }
201 fprintf(f,"; Created: %s\n",ctime(&t));
202 fprintf(f,"[PolyGlot]\n");
203 ini_start_iter(ini);
204 while((entry=ini_next(ini))){
205 if(my_string_case_equal(entry->section,"polyglot")){
206 my_quote(tmp1,entry->name,ini_specials);
207 my_quote(tmp2,entry->value,ini_specials);
208 snprintf(tmp,sizeof(tmp),"%s=%s\n",
209 tmp1,
210 tmp2);
211 tmp[sizeof(tmp)-1]='\0';
212 fprintf(f,"%s",tmp);
213 }
214 }
215 fprintf(f,"[Engine]\n");
216 ini_start_iter(ini);
217 while((entry=ini_next(ini))){
218 if(my_string_case_equal(entry->section,"engine")){
219 my_quote(tmp1,entry->name,ini_specials);
220 my_quote(tmp2,entry->value,ini_specials);
221 snprintf(tmp,sizeof(tmp),"%s=%s\n",
222 tmp1,
223 tmp2);
224 tmp[sizeof(tmp)-1]='\0';
225 fprintf(f,"%s",tmp);
226 }
227 }
228 fclose(f);
229 }
230
231 // welcome_message()
232
welcome_message(char * buf)233 void welcome_message(char *buf){
234 if(!DEBUG){
235 sprintf(buf,
236 "PolyGlot %s by Fabien Letouzey.\n",
237 Version);
238 }else{
239 sprintf(buf,
240 "PolyGlot %s by Fabien Letouzey (debug build).\n",
241 Version);
242 }
243 }
244
wb_select()245 void wb_select(){
246 option_t *opt;
247 option_start_iter(Option);
248 while((opt=option_next(Option))){
249 opt->mode&=~XBOARD;
250 if(opt->mode & XBSEL){
251 opt->mode|=XBOARD;
252 }
253 }
254 }
255
256 // main()
257
main(int argc,char * argv[])258 int main(int argc, char * argv[]) {
259 ini_t ini[1], ini_command[1];
260 ini_entry_t *entry;
261 char *arg;
262 int arg_index;
263 bool NoIni;
264 option_t *opt;
265 char welcome[StringSize];
266
267
268 welcome_message(welcome);
269
270 printf("%s",welcome);
271
272
273 if(argc>=2 && ((my_string_case_equal(argv[1],"help")) || (my_string_case_equal(argv[1],"-help")) || (my_string_case_equal(argv[1],"--help")) || (my_string_case_equal(argv[1],"-h")) || my_string_case_equal(argv[1],"/?"))){
274 printf("%s\n",HelpMessage);
275 return EXIT_SUCCESS;
276 }
277
278 // init
279
280 Init = FALSE;
281
282 gui_init(GUI);
283
284 util_init();
285 option_init_pg();
286
287 square_init();
288 piece_init();
289 attack_init();
290
291 hash_init();
292
293 my_random_init();
294
295 ini_init(ini);
296 ini_init(ini_command);
297
298 // book utilities: do not touch these
299
300 if (argc >= 2 && my_string_equal(argv[1],"make-book")) {
301 book_make(argc,argv);
302 return EXIT_SUCCESS;
303 }
304
305 if (argc >= 2 && my_string_equal(argv[1],"merge-book")) {
306 book_merge(argc,argv);
307 return EXIT_SUCCESS;
308 }
309
310 if (argc >= 2 && my_string_equal(argv[1],"dump-book")) {
311 book_dump(argc,argv);
312 return EXIT_SUCCESS;
313 }
314
315 if (argc >= 2 && my_string_equal(argv[1],"info-book")) {
316 book_info(argc,argv);
317 return EXIT_SUCCESS;
318 }
319
320 // perft
321
322 if (argc >= 2 && my_string_equal(argv[1],"perft")) {
323 do_perft(argc,argv);
324 return EXIT_SUCCESS;
325 }
326
327 // What is the config file? This is very hacky right now.
328
329 // Do we want a config file at all?
330
331 arg_index=0;
332 NoIni=FALSE;
333 while((arg=argv[arg_index++])){
334 if(my_string_equal(arg,"-noini")){
335 NoIni=TRUE;
336 break;
337 }
338 }
339 arg_shift_left(argv,arg_index-1);
340 parse_args(ini_command,argv+1);
341 if(NoIni){
342 option_set(Option,"SettingsFile","<empty>");
343 }
344
345 // Ok see if first argument looks like config file
346
347 if(argv[1] && !my_string_equal(argv[1],"epd-test") && !(argv[1][0]=='-')){
348 // first argument must be config file
349 if(!NoIni){
350 option_set(Option,"SettingsFile",argv[1]);
351 }else{
352 // ignore
353 }
354 arg_shift_left(argv,1);
355 }else{
356 // Config file is the default.
357 // This has already been set above or in "option_init_pg()"
358 }
359
360
361
362 // if we use a config file: load it!
363
364 if(!my_string_equal(option_get_string(Option,"SettingsFile"),"<empty>")){
365 if(ini_parse(ini,option_get_string(Option,"SettingsFile"))){
366 my_fatal("main(): Can't open config file \"%s\": %s\n",
367 option_get_string(Option,"SettingsFile"),
368 strerror(errno));
369 }
370 }
371
372 // Extract some important options
373
374 if((entry=ini_find(ini,"polyglot","EngineCommand"))){
375 option_set(Option,entry->name,entry->value);
376 }
377 if((entry=ini_find(ini,"polyglot","EngineDir"))){
378 option_set(Option,entry->name,entry->value);
379 }
380 if((entry=ini_find(ini,"polyglot","EngineName"))){
381 option_set(Option,entry->name,entry->value);
382 }
383 if((entry=ini_find(ini,"polyglot","Log"))){
384 polyglot_set_option(entry->name,entry->value);
385 }
386 if((entry=ini_find(ini,"polyglot","LogFile"))){
387 polyglot_set_option(entry->name,entry->value);
388 }
389
390 // Concession to WB 4.4.0
391 // Treat "polyglot_1st.ini" and "polyglot_2nd.ini" specially
392
393 if(option_get_bool(Option,"WbWorkArounds3")){
394 const char *SettingsFile=option_get(Option,"SettingsFile");
395 if(strstr(SettingsFile,"polyglot_1st.ini")||
396 strstr(SettingsFile,"polyglot_2nd.ini")){
397 option_set(Option,"SettingsFile","<empty>");
398 }
399 }
400
401 // Look at command line for logging option. It is important
402 // to start logging as soon as possible.
403
404 if((entry=ini_find(ini_command,"PolyGlot","Log"))){
405 option_set(Option,entry->name,entry->value);
406 }
407 if((entry=ini_find(ini_command,"PolyGlot","LogFile"))){
408 option_set(Option,entry->name,entry->value);
409 }
410
411 // start logging if required
412
413 if (option_get_bool(Option,"Log")) {
414 my_log_open(option_get_string(Option,"LogFile"));
415 }
416
417 // log welcome stuff
418
419 my_log("%s",welcome);
420 my_log("POLYGLOT *** START ***\n");
421 if(!my_string_equal(option_get_string(Option,"SettingsFile"),"<empty>")){
422 my_log("POLYGLOT INI file \"%s\"\n",option_get_string(Option,"SettingsFile"));
423 }
424
425
426 // scavenge command line for options necessary to start the engine
427
428
429 if((entry=ini_find(ini_command,"PolyGlot","EngineCommand"))){
430 option_set(Option,entry->name,entry->value);
431 }
432 if((entry=ini_find(ini_command,"PolyGlot","EngineDir"))){
433 option_set(Option,entry->name,entry->value);
434 }
435 if((entry=ini_find(ini_command,"PolyGlot","EngineName"))){
436 option_set(Option,entry->name,entry->value);
437 }
438
439 // Make sure that EngineCommand has been set
440 if(my_string_case_equal(option_get(Option,"EngineCommand"),"<empty>")){
441 my_fatal("main(): EngineCommand not set\n");
442 }
443
444 // start engine
445
446 engine_open(Engine);
447
448 if(!engine_active(Engine)){
449 my_fatal("main(): Could not start \"%s\"\n",option_get(Option,"EngineCommand"));
450 }
451
452 // switch to UCI mode if necessary
453
454 if (option_get_bool(Option,"UCI")) {
455 my_log("POLYGLOT *** Switching to UCI mode ***\n");
456 }
457
458 // initialize uci parsing and send uci command.
459 // Parse options and wait for uciok
460
461 // XXX
462 uci_open(Uci,Engine);
463
464 option_set_default(Option,"EngineName",Uci->name);
465
466 // get engine name from engine if not supplied in config file or on
467 // the command line
468
469 if (my_string_equal(option_get_string(Option,"EngineName"),"<empty>")) {
470 option_set(Option,"EngineName",Uci->name);
471 }
472
473
474 // In the case we have been invoked with NoIni or StandardIni
475 // we still have to load a config file.
476
477 if(my_string_equal(option_get_string(Option,"SettingsFile"),"<empty>")){
478
479 // construct the name of the ConfigFile from the EngineName
480
481 char tmp[StringSize];
482 char option_file[StringSize];
483 int i;
484 snprintf(tmp,sizeof(tmp),"%s.ini",
485 option_get_string(Option,"EngineName"));
486 tmp[sizeof(tmp)-1]='\0';
487 for(i=0;i<strlen(tmp);i++){
488 if(tmp[i]==' '){
489 tmp[i]='_';
490 }
491 }
492 my_path_join(option_file,
493 option_get_string(Option,"SettingsDir"),
494 tmp);
495 // Load the config file
496 option_set(Option,"SettingsFile",option_file);
497
498 my_log("POLYGLOT INI file \"%s\"\n",option_get_string(Option,"SettingsFile"));
499 if(ini_parse(ini,option_file)){
500 my_log("POLYGLOT Unable to open %s\n",
501 option_get_string(Option,"SettingsFile"));
502 }
503 }
504
505
506 // Parse the command line and merge remaining options.
507
508 ini_start_iter(ini_command);
509 while((entry=ini_next(ini_command))){
510 ini_insert(ini,entry);
511 }
512
513 // Remind the reader about the options that are now in effect.
514
515 my_log("POLYGLOG OPTIONS \n");
516 ini_disp(ini);
517
518 // extract PG options
519
520 ini_start_iter(ini);
521 while((entry=ini_next(ini))){
522 if(my_string_case_equal(entry->section,"polyglot")){
523 opt=option_find(Option,entry->name);
524 if(opt && !IS_BUTTON(opt->type)){
525 polyglot_set_option(entry->name,entry->value);
526 }
527 }
528 }
529
530 // Cater to our biggest customer:-)
531
532 if(option_get_bool(Option,"OnlyWbOptions")){
533 wb_select();
534 }
535
536 // done initializing
537
538 Init = TRUE;
539
540 // collect engine options from config file(s) and send to engine
541
542 ini_start_iter(ini);
543 while((entry=ini_next(ini))){
544 if(my_string_case_equal(entry->section,"engine")){
545 // also updates value in Uci->option
546 uci_send_option(Uci,entry->name,"%s",entry->value);
547 }
548 }
549
550
551
552 // EPD test
553
554 if (argv[1] && my_string_equal(argv[1],"epd-test")){
555 argc=0;
556 while((arg=argv[argc++]));
557 epd_test(argc-1,argv);
558 return EXIT_SUCCESS;
559 }
560
561 // Anything that hasn't been parsed yet is a syntax error
562 // It seems that XBoard sometimes passes empty strings as arguments
563 // to PolyGlot. We ignore these.
564
565 argc=1;
566 while((arg=argv[argc++])){
567 if(!my_string_equal(arg,"")){
568 my_fatal("main(): Incorrect use of option: \"%s\"\n",argv[argc-1]);
569 }
570 }
571
572 // gui_init(GUI);
573 mainloop();
574 return EXIT_SUCCESS;
575 }
576
577 // polyglot_set_option()
578
polyglot_set_option(const char * name,const char * value)579 void polyglot_set_option(const char *name, const char *value){ // this must be cleaned up!
580 ini_t ini[1];
581 int ret;
582 ini_init(ini);
583 my_log("POLYGLOT Setting PolyGlot option \"%s=%s\"\n",name,value);
584 if(my_string_case_equal(name,"Save")){
585 ret=my_mkdir(option_get(Option,"SettingsDir"));
586 if(ret){
587 my_log("POLYGLOT polyglot_set_option(): %s: %s\n",
588 option_get(Option,"SettingsDir"),
589 strerror(errno));
590 }
591 make_ini(ini);
592 write_ini(option_get(Option,"SettingsFile"),ini);
593 return;
594 }
595 // if(my_string_equal(option_get(Option,name),value)){
596 // my_log("Not setting PolyGlot option \"%s\" "
597 // "since it already as the correct value.\n",
598 // name);
599 // return;
600 // }
601 option_set(Option,name,value);
602 if(option_get_bool(Option,"Book")&&(my_string_case_equal(name,"BookFile")||my_string_case_equal(name,"Book"))){
603 my_log("POLYGLOT *** SETTING BOOK ***\n");
604 my_log("POLYGLOT BOOK \"%s\"\n",option_get_string(Option,"BookFile"));
605 book_close();
606 book_clear();
607 book_open(option_get_string(Option,"BookFile"));
608 if(!book_is_open()){
609 my_log("POLYGLOT Unable to open book \"%s\"\n",option_get_string(Option,"BookFile"));
610 }
611 }else if(option_get_bool(Option,"Log")&&(my_string_case_equal(name,"LogFile") ||my_string_case_equal(name,"Log"))){
612 my_log("POLYGLOT *** SWITCHING LOGFILE ***\n");
613 my_log("POLYGLOT NEW LOGFILE \"%s\"\n",option_get_string(Option,"LogFile"));
614 my_log_close();
615 my_log_open(option_get_string(Option,"LogFile"));
616 }else if(option_get_bool(Option,"UseNice") &&(my_string_case_equal(name,"NiceValue")||my_string_case_equal(name,"UseNice"))){
617 my_log("POLYGLOT Adjust Engine Piority\n");
618 engine_set_nice_value(Engine,atoi(option_get_string(Option,"NiceValue")));
619 }else if(my_string_case_equal(name,"Book") && !option_get_bool(Option,"Book")){
620 book_close();
621 book_clear();
622 }else if(my_string_case_equal(name,"UseNice") && !option_get_bool(Option,"UseNice")){
623 my_log("POLYGLOT Adjust Engine Piority\n");
624 engine_set_nice_value(Engine,0);
625 }else if(my_string_case_equal(name,"Log") && !option_get_bool(Option,"Log")){
626 my_log("POLYGLOT QUIT LOGGING\n");
627 my_log_close();
628 }
629 }
630
631
632
633 // quit()
634
quit()635 void quit() {
636 my_log("POLYGLOT *** QUIT ***\n");
637 if (Init && !Engine->pipex->quit_pending) {
638 stop_search();
639 Engine->pipex->quit_pending=TRUE;
640 engine_send(Engine,"quit");
641 engine_close(Engine);
642
643 }
644 my_sleep(200);
645 my_log("POLYGLOT Calling exit\n");
646 exit(EXIT_SUCCESS);
647 }
648
649 // stop_search()
650
stop_search()651 static void stop_search() {
652
653 if (Init && Uci->searching) {
654
655 ASSERT(Uci->searching);
656 ASSERT(Uci->pending_nb>=1);
657
658 my_log("POLYGLOT STOP SEARCH\n");
659
660 if (option_get_bool(Option,"SyncStop")) {
661 uci_send_stop_sync(Uci);
662 } else {
663 uci_send_stop(Uci);
664 }
665 }
666 }
667
668
669 // end of main.c
670
671