e06d59c9c34800999bd161086962d93761769645
[polyglot.git] / main.c
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
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
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
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
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
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
245 int 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
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
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
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
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