Alter version number to 2.0.4
[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 = "2.0.4";
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     if((entry=ini_find(ini,"polyglot","Affinity"))){ // must be known during start
447         polyglot_set_option(entry->name,entry->value);
448     }
449
450     engine_open(Engine);
451
452     if(!engine_active(Engine)){
453         my_fatal("main(): Could not start \"%s\"\n",option_get(Option,"EngineCommand"));
454     }
455
456         // switch to UCI mode if necessary
457     
458     if (option_get_bool(Option,"UCI")) {
459         my_log("POLYGLOT *** Switching to UCI mode ***\n");
460     }
461
462         // initialize uci parsing and send uci command. 
463         // Parse options and wait for uciok
464     
465     // XXX
466     uci_open(Uci,Engine);
467
468     option_set_default(Option,"EngineName",Uci->name);
469
470         // get engine name from engine if not supplied in config file or on
471         // the command line
472
473     if (my_string_equal(option_get_string(Option,"EngineName"),"<empty>")) {
474         option_set(Option,"EngineName",Uci->name);
475     }
476
477
478         // In the case we have been invoked with NoIni or StandardIni
479         // we still have to load a config file.
480
481     if(my_string_equal(option_get_string(Option,"SettingsFile"),"<empty>")){
482
483             //  construct the name of the ConfigFile from the EngineName
484         
485         char tmp[StringSize];
486         char option_file[StringSize];
487         int i;
488         snprintf(tmp,sizeof(tmp),"%s.ini",
489                  option_get_string(Option,"EngineName"));
490         tmp[sizeof(tmp)-1]='\0';
491         for(i=0;i<strlen(tmp);i++){
492             if(tmp[i]==' '){
493                 tmp[i]='_';
494             }
495         }
496         my_path_join(option_file,
497                      option_get_string(Option,"SettingsDir"),
498                      tmp);
499     // Load the config file
500         option_set(Option,"SettingsFile",option_file);
501
502         my_log("POLYGLOT INI file \"%s\"\n",option_get_string(Option,"SettingsFile"));
503         if(ini_parse(ini,option_file)){
504             my_log("POLYGLOT Unable to open %s\n",
505                    option_get_string(Option,"SettingsFile")); 
506         }
507     }
508
509
510     // Parse the command line and merge remaining options.
511
512     ini_start_iter(ini_command);
513     while((entry=ini_next(ini_command))){
514         ini_insert(ini,entry);
515     }
516
517         // Remind the reader about the options that are now in effect.
518
519     my_log("POLYGLOG OPTIONS \n");
520     ini_disp(ini);
521
522             // extract PG options
523     
524     ini_start_iter(ini);
525     while((entry=ini_next(ini))){
526         if(my_string_case_equal(entry->section,"polyglot")){
527             opt=option_find(Option,entry->name);
528             if(opt && !IS_BUTTON(opt->type)){
529                 polyglot_set_option(entry->name,entry->value);
530             }
531         }
532     }
533
534         // Cater to our biggest customer:-)
535     
536     if(option_get_bool(Option,"OnlyWbOptions")){
537         wb_select();
538     }
539
540         // done initializing
541     
542     Init = TRUE;
543     
544         // collect engine options from config file(s) and send to engine
545     
546     ini_start_iter(ini);
547     while((entry=ini_next(ini))){
548         if(my_string_case_equal(entry->section,"engine")){
549                 // also updates value in Uci->option
550             uci_send_option(Uci,entry->name,"%s",entry->value);
551         }
552     }
553
554     
555     
556         // EPD test
557     
558     if (argv[1] && my_string_equal(argv[1],"epd-test")){
559         argc=0;
560         while((arg=argv[argc++]));
561         epd_test(argc-1,argv);
562         return EXIT_SUCCESS;
563     }
564     
565         // Anything that hasn't been parsed yet is a syntax error
566         // It seems that XBoard sometimes passes empty strings as arguments
567         // to PolyGlot. We ignore these. 
568
569     argc=1;
570     while((arg=argv[argc++])){
571         if(!my_string_equal(arg,"")){
572             my_fatal("main(): Incorrect use of option: \"%s\"\n",argv[argc-1]);
573         }
574     }
575
576     //    gui_init(GUI);
577     mainloop();
578     return EXIT_SUCCESS; 
579 }
580
581 // polyglot_set_option()
582
583 void polyglot_set_option(const char *name, const char *value){ // this must be cleaned up!
584     ini_t ini[1];
585     int ret;
586     ini_init(ini);
587     my_log("POLYGLOT Setting PolyGlot option \"%s=%s\"\n",name,value);
588     if(my_string_case_equal(name,"Save")){
589         ret=my_mkdir(option_get(Option,"SettingsDir"));
590         if(ret){
591             my_log("POLYGLOT polyglot_set_option(): %s: %s\n",
592                    option_get(Option,"SettingsDir"),
593                    strerror(errno));
594         }
595         make_ini(ini);
596         write_ini(option_get(Option,"SettingsFile"),ini);
597         return;
598     }
599 //    if(my_string_equal(option_get(Option,name),value)){
600 //        my_log("Not setting PolyGlot option \"%s\" "
601 //               "since it already as the correct value.\n",
602 //               name);
603 //        return;
604 //    }
605     option_set(Option,name,value);
606     if(option_get_bool(Option,"Book")&&(my_string_case_equal(name,"BookFile")||my_string_case_equal(name,"Book"))){
607         my_log("POLYGLOT *** SETTING BOOK ***\n");
608         my_log("POLYGLOT BOOK \"%s\"\n",option_get_string(Option,"BookFile"));
609         book_close();
610         book_clear();
611         book_open(option_get_string(Option,"BookFile"));
612         if(!book_is_open()){
613             my_log("POLYGLOT Unable to open book \"%s\"\n",option_get_string(Option,"BookFile"));
614         }
615     }else if(option_get_bool(Option,"Log")&&(my_string_case_equal(name,"LogFile") ||my_string_case_equal(name,"Log"))){
616         my_log("POLYGLOT *** SWITCHING LOGFILE ***\n");
617         my_log("POLYGLOT NEW LOGFILE \"%s\"\n",option_get_string(Option,"LogFile"));
618         my_log_close();
619         my_log_open(option_get_string(Option,"LogFile"));
620     }else if(option_get_bool(Option,"UseNice") &&(my_string_case_equal(name,"NiceValue")||my_string_case_equal(name,"UseNice"))){
621         my_log("POLYGLOT Adjust Engine Piority\n");
622         engine_set_nice_value(Engine,atoi(option_get_string(Option,"NiceValue")));
623     }else if(my_string_case_equal(name,"Book") && !option_get_bool(Option,"Book")){
624         book_close();
625         book_clear();
626     }else if(my_string_case_equal(name,"UseNice") && !option_get_bool(Option,"UseNice")){
627         my_log("POLYGLOT Adjust Engine Piority\n");
628         engine_set_nice_value(Engine,0);
629     }else if(my_string_case_equal(name,"Log") && !option_get_bool(Option,"Log")){
630         my_log("POLYGLOT QUIT LOGGING\n");
631         my_log_close();
632     }
633 }
634
635
636
637 // quit()
638
639 void quit() {
640     my_log("POLYGLOT *** QUIT ***\n");
641     if (Init && !Engine->pipex->quit_pending) {
642         stop_search();
643         Engine->pipex->quit_pending=TRUE;
644         engine_send(Engine,"quit");
645         engine_close(Engine);
646         
647     }
648     my_sleep(200);
649     my_log("POLYGLOT Calling exit\n");
650     exit(EXIT_SUCCESS);
651 }
652
653 // stop_search()
654
655 static void stop_search() {
656     
657     if (Init && Uci->searching) {
658         
659         ASSERT(Uci->searching);
660         ASSERT(Uci->pending_nb>=1);
661         
662         my_log("POLYGLOT STOP SEARCH\n");
663         
664         if (option_get_bool(Option,"SyncStop")) {
665             uci_send_stop_sync(Uci);
666         } else {
667             uci_send_stop(Uci);
668         }
669     }
670 }
671
672
673 // end of main.c
674