X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=main.c;h=aaadc822f16b9512f1f976589831e722cd008c4f;hb=bb6c47f77f59067c358579a71cefa1ae65180a30;hp=e34fb059dc52d3446ad5db0281d46dc5eeb9a030;hpb=acb140befabd8b0f1a8606470013b420d05b4fb7;p=polyglot.git diff --git a/main.c b/main.c index e34fb05..aaadc82 100644 --- a/main.c +++ b/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "attack.h" #include "board.h" @@ -31,20 +32,22 @@ #include "util.h" #include "xboard2uci.h" #include "uci2uci.h" +#include "ini.h" +#include "util.h" + // constants -static const char * const Version = "1.4.35b"; +static const char * const Version = "1.4.46b"; static const char * const HelpMessage = "\ SYNTAX\n\ -* polyglot [configfile]\n\ -* polyglot -ec enginecommand\n\ +* polyglot [configfile] [-noini] [-ec engine] [-ed enginedirectory] [-en enginename] [-log] [-lf logfile] [-hash value] [-bk book] [-pg =]* [-uci =]*\n\ * polyglot make-book [-pgn inputfile] [-bin outputfile] [-max-ply ply] [-min-game games] [-min-score score] [-only-white] [-only-black] [-uniform]\n\ * polyglot merge-book -in1 inputfile1 -in2 inputfile2 [-out outputfile]\n\ * polyglot info-book [-bin inputfile] [-exact]\n\ * polyglot dump-book [-bin inputfile] -color color [-out outputfile]\n\ -* polyglot [configfile] epd-test [-epd inputfile] [-min-depth depth] [-max-depth depth] [-min-time time] [-max-time time] [-depth-delta delta]\n\ +* polyglot [configfile] epd-test [engineoptions] [-epd inputfile] [-min-depth depth] [-max-depth depth] [-min-time time] [-max-time time] [-depth-delta delta]\n\ * polyglot perft [-fen fen] [-max-depth depth]\ "; @@ -52,23 +55,129 @@ static const int SearchDepth = 63; static const double SearchTime = 3600.0; static const int StringSize = 4096; +static const char * const IniIntro= + "; This ini file is used internally by PolyGlot\n" + "; to remember the settings for the UCI engine\n" + "; whose name is \"%s\".\n" + "\n" + "; The values for these settings would be typicallly\n" + "; obtained from the engine settings dialog\n" + "; in WinBoard/xboard 4.4.0 and higher.\n" + "\n" + "; If the value of the option \"Persist\" is false\n" + "; then the content of this file is ignored.\n" + "\n" + "; It is allowed to manually edit this file\n" + "; and you may safely delete it as well.\n" + "\n"; + // variables static bool Init; // prototypes -static void parse_option (); static void init_book (); -static bool parse_line (char line[], char * * name_ptr, char * * value_ptr); static void stop_search (); // functions +// arg_shift_left() + +static void arg_shift_left(char **argv, int index){ + int i; + for(i=index; argv[i]!=NULL; i++){ + argv[i]=argv[i+1]; + } +} + + +// make_ini() + +static void make_ini(ini_t *ini){ + option_t *opt; + char tmp[StringSize]; + ini_insert_ex(ini,"polyglot", + "EngineName", + option_get_string(Option,"EngineName")); + ini_insert_ex(ini,"polyglot", + "EngineCommand", + option_get_string(Option,"EngineCommand")); + ini_insert_ex(ini,"polyglot", + "EngineDir", + option_get_string(Option,"EngineDir")); + option_start_iter(Option); + while((opt=option_next(Option))){ + if(!my_string_equal(opt->value,opt->default_)&& + !IS_BUTTON(opt->type) && + (opt->mode & XBOARD)){ + ini_insert_ex(ini,"polyglot",opt->name,opt->value); + } + } + option_start_iter(Uci->option); + while((opt=option_next(Uci->option))){ + if(!my_string_equal(opt->value,opt->default_)&& + !IS_BUTTON(opt->type)){ + ini_insert_ex(ini,"engine",opt->name,opt->value); + } + } +} + + +// write_ini() + +static void write_ini(const char *filename, + ini_t *ini){ + ini_entry_t *entry; + char tmp[StringSize]; + FILE *f; + time_t t=time(NULL); + f=fopen(filename,"w"); + if(!f){ + // alas this does nothing.... + gui_send(GUI,"tellusererror write_ini(): %s: %s.",filename,strerror(errno)); + // but at least we log the error + my_log("POLYGLOT write_ini(): %s: %s.\n",filename,strerror(errno)); + return; + } + fprintf(f,"; %s\n",ctime(&t)); + fprintf(f,IniIntro,option_get_string(Option,"EngineName")); + fprintf(f,"[PolyGlot]\n"); + ini_start_iter(ini); + while((entry=ini_next(ini))){ + if(my_string_case_equal(entry->section,"polyglot")){ + snprintf(tmp,sizeof(tmp),"%s=%s\n", + entry->name, + entry->value); + tmp[sizeof(tmp)-1]='\0'; + fprintf(f,"%s",tmp); + } + } + fprintf(f,"[Engine]\n"); + ini_start_iter(ini); + while((entry=ini_next(ini))){ + if(my_string_case_equal(entry->section,"engine")){ + snprintf(tmp,sizeof(tmp),"%s=%s\n", + entry->name, + entry->value); + tmp[sizeof(tmp)-1]='\0'; + fprintf(f,"%s",tmp); + } + } + fclose(f); +} + + // main() int main(int argc, char * argv[]) { - + ini_t ini[1],ini_save[1]; + ini_entry_t *entry; + char *arg; + int arg_index; + bool NoIni, Persist; + char persist_path[StringSize]; + if(!DEBUG){ printf("PolyGlot %s by Fabien Letouzey.\n",Version); }else{ @@ -92,10 +201,13 @@ int main(int argc, char * argv[]) { attack_init(); hash_init(); - + my_random_init(); - // build book + ini_init(ini); + ini_init(ini_save); + + // book utilities if (argc >= 2 && my_string_equal(argv[1],"make-book")) { book_make(argc,argv); @@ -107,72 +219,348 @@ int main(int argc, char * argv[]) { return EXIT_SUCCESS; } - if (argc >= 2 && my_string_equal(argv[1],"merge-book")) { - book_merge(argc,argv); - return EXIT_SUCCESS; - } - - if (argc >= 2 && my_string_equal(argv[1],"dump-book")) { - book_dump(argc,argv); - return EXIT_SUCCESS; - } + if (argc >= 2 && my_string_equal(argv[1],"dump-book")) { + book_dump(argc,argv); + return EXIT_SUCCESS; + } + + if (argc >= 2 && my_string_equal(argv[1],"info-book")) { + book_info(argc,argv); + return EXIT_SUCCESS; + } - if (argc >= 2 && my_string_equal(argv[1],"info-book")) { - book_info(argc,argv); - return EXIT_SUCCESS; - } + // perft if (argc >= 2 && my_string_equal(argv[1],"perft")) { do_perft(argc,argv); return EXIT_SUCCESS; } + + // TODO: If logging is enabled on the command line turn it on NOW + // and do not allow it to be overridden later. - if (argc >= 3 && my_string_equal(argv[1],"-ec")) { - option_set(Option,"EngineCommand",argv[2]); - engine_open(Engine); - if(!engine_active(Engine)){ - my_fatal("Could not start \"%s\"\n", - option_get(Option,"EngineCommand")); + // What is the config file? This is very hacky right now. + + // Do we want a config file at all? + + arg_index=0; + NoIni=FALSE; + while((arg=argv[arg_index++])){ + if(my_string_equal(arg,"-noini")){ + NoIni=TRUE; + break; } - Init=TRUE; - gui_init(GUI); - uci_open(Uci,Engine); - if (my_string_equal(option_get_string(Option,"EngineName"),"")) { - option_set(Option,"EngineName",Uci->name); + } + arg_shift_left(argv,arg_index-1); + if(NoIni){ + option_set(Option,"OptionFile",""); + } + + // Ok see if first argument looks like config file + + if(argv[1] && !my_string_equal(argv[1],"epd-test") && !(argv[1][0]=='-')){ + // first argument must be config file + if(!NoIni){ + option_set(Option,"OptionFile",argv[1]); + }else{ + // ignore + } + arg_shift_left(argv,1); + }else{ + // Config file is the default. + // This has already been set above or in "option_init_pg()" + } + + + // if we use a config file: load it! + + if(!my_string_equal(option_get_string(Option,"OptionFile"),"")){ + if(ini_parse(ini,option_get_string(Option,"OptionFile"))){ + my_fatal("main(): Can't open file \"%s\": %s\n", + option_get_string(Option,"OptionFile"), + strerror(errno)); } - mainloop(); - return EXIT_SUCCESS; } + // remind the reader of what options are in effect + + my_log("POLYGLOG Options from ini file\n"); + ini_disp(ini); + + // extract PG options - // read options + ini_start_iter(ini); + while((entry=ini_next(ini))){ + if(my_string_case_equal(entry->section,"polyglot")){ + option_set(Option,entry->name,entry->value); + option_set_default(Option,entry->name,entry->value); + } + } + // start logging if required - if (argc == 2) option_set(Option,"OptionFile",argv[1]); // HACK for compatibility + if (option_get_bool(Option,"Log")) { + my_log_open(option_get_string(Option,"LogFile")); + } + + // log welcome stuff + + if(!DEBUG){ + my_log("PolyGlot %s by Fabien Letouzey\n",Version); + }else{ + my_log("PolyGlot %s by Fabien Letouzey (debug build)\n",Version); + } + my_log("POLYGLOT *** START ***\n"); + my_log("POLYGLOT INI file \"%s\"\n",option_get_string(Option,"OptionFile")); + + // open book (presumably this should go else where) + + init_book(); + + // scavenge command line for options necessary to start the engine + + arg_index=1; + while((arg=argv[arg_index])){ + if(my_string_equal(arg,"-ec") && argv[arg_index+1]){ + option_set(Option,"EngineCommand",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + if(my_string_equal(arg,"-ed") && argv[arg_index+1]){ + option_set(Option,"EngineDir",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + if(my_string_equal(arg,"-en") && argv[arg_index+1]){ + option_set(Option,"EngineName",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + arg_index++; + } + + // start engine + + engine_open(Engine); + if(!engine_active(Engine)){ + my_fatal("Could not start \"%s\"\n",option_get(Option,"EngineCommand")); + } + + // switch to UCI mode if necessary + + if (option_get_bool(Option,"UCI")) { + my_log("POLYGLOT *** Switching to UCI mode ***\n"); + } + + // initialize uci parsing and send uci command. + // Parse options and wait for uciok + + uci_open(Uci,Engine); + + // get engine name from engine if not supplied in config file + + if (my_string_equal(option_get_string(Option,"EngineName"),"")) { + option_set(Option,"EngineName",Uci->name); + } + + // what is the name of the persist file? + + if(my_string_equal(option_get_string(Option,"PersistFile"),"")){ + char tmp[StringSize]; + int i; + snprintf(tmp,sizeof(tmp),"%s.ini", + option_get_string(Option,"EngineName")); + tmp[sizeof(tmp)-1]='\0'; + for(i=0;ivalue,"false") || + my_string_equal(entry->value,"0"))?FALSE:TRUE; + } + + // if "Persist" now happens to be false, forget about the + // persist file + + if(!Persist){ + my_log("POLYGLOT Ignoring PersistFile"); + ini_clear(ini_save); + } + + option_set(Option,"Persist",Persist?"true":"false"); + + // parse the command line and merge remaining options + + arg_index=1; + while((arg=argv[arg_index])){ + if(my_string_equal(arg,"-log")){ + ini_insert_ex(ini_save,"PolyGlot","Log","true"); + arg_shift_left(argv,arg_index); + continue; + } + if(my_string_equal(arg,"-lf") && argv[arg_index+1]){ + ini_insert_ex(ini_save,"PolyGlot","LogFile",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + if(my_string_equal(arg,"-hash") && argv[arg_index+1]){ + ini_insert_ex(ini_save,"Engine","Hash",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + if(my_string_equal(arg,"-bk") && argv[arg_index+1]){ + ini_insert_ex(ini_save,"PolyGlot","Book","true"); + ini_insert_ex(ini_save,"PolyGlot","BookFile",argv[arg_index+1]); + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + if((my_string_equal(arg,"-pg")||my_string_equal(arg,"-uci")) && + argv[arg_index]){ + int ret; + char section[StringSize]; + char name[StringSize]; + char value[StringSize]; + ret=ini_line_parse(argv[arg_index++],section,name,value); + if(ret==NAME_VALUE){ + if(my_string_equal(arg,"-pg")){ + ini_insert_ex(ini_save,"PolyGlot",name,value); + }else{ + ini_insert_ex(ini_save,"Engine",name,value); + } + } + arg_shift_left(argv,arg_index); + arg_shift_left(argv,arg_index); + continue; + } + arg_index++; + } + + // remind the reader once again about options + + my_log("POLYGLOG Options from PersistFile and command line\n"); + ini_disp(ini_save); + + // Extract PG options; this time do not set the default. + // polyglot_set_option() performs the necessary actions such + // as opening the log file/opening book etcetera. + // Ignore EngineName, EngineCommand and EngineDir + // as these are really meant as comments. + + ini_start_iter(ini_save); + while((entry=ini_next(ini_save))){ + if(my_string_case_equal(entry->section,"polyglot")){ + if(my_string_case_equal(entry->value,"EngineName")) + continue; + if(my_string_case_equal(entry->value,"EngineCommand")) + continue; + if(my_string_case_equal(entry->value,"EngineDir")) + continue; + polyglot_set_option(entry->name,entry->value); + } + } - parse_option(); // HACK: also launches the engine + // done initializing + + Init = TRUE; + + // collect engine options from config file(s) and send to engine + + ini_start_iter(ini); + while((entry=ini_next(ini))){ + if(my_string_case_equal(entry->section,"engine")){ + // also updates value in Uci->option + uci_send_option(Uci,entry->name,"%s",entry->value); + // since this comes from the ini file, also update default + option_set_default(Uci->option,entry->name,entry->value); + // this is inherited, it probably does not work correctly + if(my_string_case_equal(entry->name,"MultiPV") && + atoi(entry->value)>1){ + Uci->multipv_mode=TRUE; + } + } + } + ini_start_iter(ini_save); + while((entry=ini_next(ini_save))){ + if(my_string_case_equal(entry->section,"engine")){ + // also updates value in Uci->option + uci_send_option(Uci,entry->name,"%s",entry->value); + // this is inherited, it probably does not work correctly + if(my_string_case_equal(entry->name,"MultiPV") && + atoi(entry->value)>1){ + Uci->multipv_mode=TRUE; + } + } + } + // EPD test - if (argc >= 2 && my_string_equal(argv[1],"epd-test")){ - epd_test(argc,argv); - return EXIT_SUCCESS; - }else if(argc >= 3 && my_string_equal(argv[2],"epd-test")){ - epd_test(argc-1,argv+1); + if (argv[1] && my_string_equal(argv[1],"epd-test")){ + argc=0; + while((arg=argv[argc++])); + epd_test(argc-1,argv); return EXIT_SUCCESS; } - if (argc >= 3) my_fatal("Too many arguments\n"); + // Anything that hasn't been parsed yet is a syntax error + + if(argv[1]){ + my_fatal("main(): Unknown option: %s\n",argv[1]); + } - init_book(); gui_init(GUI); mainloop(); return EXIT_SUCCESS; } -// polyglot_set_option - -void polyglot_set_option(char *name, char *value){ // this must be cleaned up! +// polyglot_set_option() + +void polyglot_set_option(const char *name, const char *value){ // this must be cleaned up! + option_t *opt; + my_log("POLYGLOT Setting PolyGlot option %s=\"%s\"\n",name,value); + if(my_string_case_equal(name,"Defaults")){ + option_start_iter(Uci->option); + while((opt=option_next(Uci->option))){ + if(!IS_BUTTON(opt->type)){ + // also sets opt->value + uci_send_option(Uci,opt->name,opt->default_); + } + } + option_start_iter(Option); + while((opt=option_next(Option))){ + if(!IS_BUTTON(opt->type)){ + polyglot_set_option(opt->name,opt->default_); + } + } + xboard2uci_send_options(); + } option_set(Option,name,value); if(option_get_bool(Option,"Book")&&(my_string_case_equal(name,"BookFile")||my_string_case_equal(name,"Book"))){ my_log("POLYGLOT *** SETTING BOOK ***\n"); @@ -184,7 +572,7 @@ void polyglot_set_option(char *name, char *value){ // this must be cleaned up! my_log("POLYGLOT Unable to open book \"%s\"\n",option_get_string(Option,"BookFile")); } }else if(option_get_bool(Option,"Log")&&(my_string_case_equal(name,"LogFile") ||my_string_case_equal(name,"Log"))){ - my_log("POLYGLOT *** SETTING LOGFILE ***\n"); + my_log("POLYGLOT *** SWITCHING LOGFILE ***\n"); my_log("POLYGLOT LOGFILE \"%s\"\n",option_get_string(Option,"LogFile")); my_log_close(); my_log_open(option_get_string(Option,"LogFile")); @@ -219,135 +607,17 @@ static void init_book(){ } } -// parse_option() - -static void parse_option() { - - const char * file_name; - FILE * file; - char line[256]; - char * name, * value; - file_name = option_get_string(Option,"OptionFile"); - - file = fopen(file_name,"r"); - if (file == NULL) { - my_fatal("Can't open file \"%s\": %s\n",file_name,strerror(errno)); - } - - // PolyGlot options (assumed first) - - while (TRUE) { - - if (!my_file_read_line(file,line,256)) { - my_fatal("parse_option(): missing [Engine] section\n"); - } - - if (my_string_case_equal(line,"[engine]")) break; - - if (parse_line(line,&name,&value)) { - option_set(Option,name,value); - option_set_default(Option,name,value); - } - } - - if (option_get_bool(Option,"Log")) { - my_log_open(option_get_string(Option,"LogFile")); - } - - if(!DEBUG){ - my_log("PolyGlot %s by Fabien Letouzey\n",Version); - }else{ - my_log("PolyGlot %s by Fabien Letouzey (debug build)\n",Version); - } - - my_log("POLYGLOT *** START ***\n"); - my_log("POLYGLOT INI file \"%s\"\n",file_name); - engine_open(Engine); - if(!engine_active(Engine)){ - my_fatal("Could not start \"%s\"\n",option_get(Option,"EngineCommand")); - } - - if (option_get_bool(Option,"UCI")) { - my_log("POLYGLOT *** Switching to UCI mode ***\n"); - } - uci_open(Uci,Engine); - Init = TRUE; - while (my_file_read_line(file,line,256)) { - if (line[0] == '[') my_fatal("parse_option(): unknown section %s\n",line); - if (parse_line(line,&name,&value)) { - uci_send_option(Uci,name,"%s",value); - //to get a decent display in winboard_x we need to now if an engine really is doing multipv analysis - // "multipv 1" in the pv is meaningless,f.i. toga sends that all the time - //therefore check if MultiPV is set to a decent value in the polyglot ini file - if(my_string_case_equal(name,"MultiPV") && atoi(value)>1) Uci->multipv_mode=TRUE; - } - } - if (my_string_equal(option_get_string(Option,"EngineName"),"")) { - option_set(Option,"EngineName",Uci->name); - } - - fclose(file); -} - -// parse_line() - -static bool parse_line(char line[], char * * name_ptr, char * * value_ptr) { - - char * ptr; - char * name, * value; - - ASSERT(line!=NULL); - ASSERT(name_ptr!=NULL); - ASSERT(value_ptr!=NULL); - - // remove comments - - ptr = strchr(line,';'); - if (ptr != NULL) *ptr = '\0'; - - ptr = strchr(line,'#'); - if (ptr != NULL) *ptr = '\0'; - - // split at '=' - - ptr = strchr(line,'='); - if (ptr == NULL) return FALSE; - - name = line; - value = ptr+1; - - // cleanup name - - while (*name == ' ') name++; // remove leading spaces - - while (ptr > name && ptr[-1] == ' ') ptr--; // remove trailing spaces - *ptr = '\0'; - - if (*name == '\0') return FALSE; - - // cleanup value - - ptr = &value[strlen(value)]; // pointer to string terminator - - while (*value == ' ') value++; // remove leading spaces - - while (ptr > value && ptr[-1] == ' ') ptr--; // remove trailing spaces - *ptr = '\0'; - - if (*value == '\0') return FALSE; - - // end - - *name_ptr = name; - *value_ptr = value; - - return TRUE; -} // quit() void quit() { + ini_t ini[1]; + char persist_path[StringSize]; + int ret; + + ini_init(ini); + my_log("POLYGLOT *** QUIT ***\n"); if (Init) { @@ -358,7 +628,34 @@ void quit() { engine_close(Engine); } - my_log("POLYGLOT Calling exit\n"); + ret=my_mkdir(option_get(Option,"PersistDir")); + if(ret){ + my_log("POLYGLOT quit(): %s: %s\n",option_get(Option,"PersistDir"),strerror(errno)); + } + // PersistFile can be named "" in case of a crash before the + // engine is started. + if(!my_string_case_equal(option_get(Option,"PersistFile"), + "")){ + // Persistence should only work in XBOARD mode. + // In UCI mode the GUI is responsible for remembering options. + if(!option_get_bool(Option,"UCI")){ + my_path_join(persist_path, + option_get(Option,"PersistDir"), + option_get(Option,"PersistFile")); + make_ini(ini); + if(option_get_bool(Option,"Persist")){ + write_ini(persist_path,ini); + }else if(!my_string_case_equal(option_get_default(Option,"Persist"), + option_get_string(Option,"Persist"))){ + // Hack + ini_insert_ex(ini,"polyglot","Persist","false"); + write_ini(persist_path,ini); + }else{ + write_ini(persist_path,ini); + } + my_log("POLYGLOT Calling exit\n"); + } + } exit(EXIT_SUCCESS); }