version 1.4.56b
[polyglot.git] / uci.c
diff --git a/uci.c b/uci.c
index 21c36e9..65d4d2a 100644 (file)
--- a/uci.c
+++ b/uci.c
@@ -10,6 +10,7 @@
 \r
 #include "board.h"\r
 #include "engine.h"\r
+#include "gui.h"\r
 #include "move.h"\r
 #include "move_do.h"\r
 #include "move_legal.h"\r
 #include "line.h"\r
 #include "uci.h"\r
 \r
+\r
 // constants\r
 \r
 static const bool UseDebug = FALSE;\r
 \r
-static const int StringSize = 4096;\r
+#define StringSize ((int)4096)\r
 \r
 // variables\r
 \r
@@ -36,7 +38,7 @@ const char * thread_options[]={
   "number of threads",        // toga\r
   "number threads",           // Deep Learning Toga\r
   "threads",                  // glaurung, zappa, cyclone, grapefruit,\r
-                              // Deep Shredder, Deep Junior\r
+                              // Deep Shredder, Deep Junior, bright\r
   "core threads",             // HIARCS\r
   "max cpus",                 // rybka\r
   "cpus",                     // Deep Sjeng, Fruit2.3.5\r
@@ -58,36 +60,47 @@ static int  mate_score     (int dist);
 \r
 // functions\r
 \r
+\r
+// uci_adapt_UCI3()\r
+\r
+static void apply_UCI3_heuristics(option_t *opt){\r
+  if(option_get_int(Option,"UCIVersion")>2){\r
+    return;\r
+  }\r
+  if(!my_string_equal(opt->type,"string")){\r
+    return;\r
+  }\r
+  if(!strncmp(opt->name,"UCI_",4)){\r
+    return;\r
+  }\r
+  if(my_string_case_contains(opt->name,"file")){\r
+    my_string_set(&opt->type,"file");\r
+    return;\r
+  }\r
+  if(my_string_case_contains(opt->name,"path")){\r
+    my_string_set(&opt->type,"path");\r
+    return;\r
+  }\r
+}\r
+\r
 // uci_set_threads()\r
 \r
 void uci_set_threads(uci_t * uci, int n) {\r
-    const char **thread_options_copy = thread_options;\r
-    const char *thread_option;\r
+    const char *thread_option=uci_thread_option(uci);\r
     ASSERT(n>=1);\r
-    while((thread_option = *(thread_options_copy++))){\r
-        uci_send_option(uci,thread_option,"%d",n); // checks also for existence\r
+    if(thread_option){\r
+        uci_send_option(uci,thread_option,"%d",n);\r
     }\r
 }\r
 \r
-// uci_thread_option_exists()\r
-\r
-bool uci_thread_option_exist(uci_t * uci) {\r
-    const char **thread_options_copy = thread_options;\r
-    const char *thread_option;\r
-    while((thread_option = *(thread_options_copy++))){\r
-        if(uci_option_exist(uci,thread_option)) return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
 \r
 const char * uci_thread_option(uci_t * uci){\r
-    const char **thread_options_copy = thread_options;\r
+    const char **p = thread_options;\r
     const char *thread_option;\r
-    int i;\r
-    while((thread_option = *(thread_options_copy++))){\r
-        i=uci_get_option(uci,thread_option);\r
-        if(i>=0){\r
-            return Uci->option[i].name;\r
+    option_t *opt;\r
+    while((thread_option = *(p++))){\r
+        if((opt=option_find(uci->option,thread_option))){\r
+            return opt->name;\r
             break;\r
         }\r
     }\r
@@ -100,8 +113,7 @@ static bool uci_is_ok(const uci_t * uci) {
 \r
    if (uci == NULL) return FALSE;\r
    if (uci->engine == NULL) return FALSE;\r
-   if (uci->option_nb < 0 || uci->option_nb >= OptionNb) return FALSE;\r
-\r
+   if (!option_is_ok(uci->option)) return FALSE;\r
    return TRUE;\r
 }\r
 \r
@@ -123,7 +135,7 @@ void uci_open(uci_t * uci, engine_t * engine) {
    my_string_set(&uci->name,"<empty>");\r
    uci->author = NULL;\r
    my_string_set(&uci->author,"<empty>");\r
-   uci->option_nb = 0;\r
+   option_init(uci->option);\r
 \r
    uci->ready_nb = 0;\r
    uci->searching = 0;\r
@@ -138,6 +150,11 @@ void uci_open(uci_t * uci, engine_t * engine) {
 \r
    do {\r
       engine_get(uci->engine,string);\r
+      // Handle the case that the engine is really a WB engine somewhat gracefully.\r
+      if((strstr(string,"Illegal") || strstr(string,"Error"))\r
+         &&strstr(string,"uci")){\r
+          my_fatal("uci_open(): Not an UCI engine.\n");\r
+      }\r
       event = uci_parse(uci,string);\r
    } while (!engine_eof(Engine) && (event & EVENT_UCI) == 0);\r
 }\r
@@ -146,22 +163,13 @@ void uci_open(uci_t * uci, engine_t * engine) {
 \r
 void uci_close(uci_t * uci) {\r
 \r
-   int i;\r
-   option_t * opt;\r
-\r
    ASSERT(uci_is_ok(uci));\r
    engine_close(uci->engine);\r
    uci->engine = NULL;\r
    my_string_clear(&uci->name);\r
    my_string_clear(&uci->author);\r
 \r
-   for (i = 0; i < uci->option_nb; i++) {\r
-      opt = &uci->option[i];\r
-      my_string_clear(&opt->name);\r
-      my_string_clear(&opt->default_);\r
-   }\r
-\r
-   uci->option_nb = 0;\r
+   option_clear(uci->option);\r
 }\r
 \r
 // uci_clear()\r
@@ -184,8 +192,8 @@ void uci_clear(uci_t * uci) {
    uci->best_depth = 0;\r
    uci->best_sel_depth = 0;\r
    line_clear(uci->best_pv);\r
-\r
-   uci->node_nb = 0;\r
+// make the default 1 instead of 0 so that info lines can be recognized by their node number 0\r
+   uci->node_nb = 1;\r
    uci->time = 0.0;\r
    uci->speed = 0.0;\r
    uci->cpu = 0.0;\r
@@ -195,6 +203,8 @@ void uci_clear(uci_t * uci) {
    uci->root_move = MoveNone;\r
    uci->root_move_pos = 0;\r
    uci->root_move_nb = board_mobility(uci->board);\r
+\r
+   uci->multipvSP=0;\r
 }\r
 \r
 // uci_send_isready()\r
@@ -267,39 +277,18 @@ void uci_send_ucinewgame(uci_t * uci) {
 \r
    ASSERT(uci!=NULL);\r
 \r
-   if (option_get_int("UCIVersion") >= 2) {\r
+   if (option_get_int(Option,"UCIVersion") >= 2) {\r
       engine_send(uci->engine,"ucinewgame");\r
    }\r
 }\r
 \r
-// uci_option_exist()\r
-\r
-bool uci_option_exist(uci_t * uci, const char option[]) {\r
-\r
-   int i;\r
-   option_t * opt;\r
-\r
-   ASSERT(uci_is_ok(uci));\r
-   ASSERT(option!=NULL);\r
-\r
-   // scan options\r
-\r
-   for (i = 0; i < uci->option_nb; i++) {\r
-      opt = &uci->option[i];\r
-      if (my_string_case_equal(opt->name,option)) return TRUE;\r
-   }\r
-\r
-   return FALSE;\r
-}\r
-\r
 // uci_send_option()\r
 \r
-void uci_send_option(uci_t * uci, const char option[], const char format[], ...) {\r
+bool uci_send_option(uci_t * uci, const char option[], const char format[], ...) {\r
 \r
-   va_list arg_list;\r
-   char value[StringSize];\r
-   int i;\r
+   char value[FormatBufferSize];\r
    option_t * opt;\r
+   bool found=FALSE;\r
 \r
    ASSERT(uci_is_ok(uci));\r
    ASSERT(option!=NULL);\r
@@ -307,24 +296,27 @@ void uci_send_option(uci_t * uci, const char option[], const char format[], ...)
 \r
    // format\r
 \r
-   va_start(arg_list,format);\r
-   vsprintf(value,format,arg_list);\r
-   va_end(arg_list);\r
+   CONSTRUCT_ARG_STRING(format,value);\r
 \r
    if (UseDebug) my_log("POLYGLOT OPTION %s VALUE %s\n",option,value);\r
 \r
-   // scan options\r
-\r
-   for (i = 0; i < uci->option_nb; i++) {\r
-\r
-      opt = &uci->option[i];\r
-\r
-      if (my_string_case_equal(opt->name,option) && !my_string_equal(opt->default_,value)) {\r
-         engine_send(uci->engine,"setoption name %s value %s",opt->name,value);\r
-         my_string_set(&opt->default_,value);\r
-         break;\r
-      }\r
+   opt=option_find(uci->option,option);\r
+   if(opt){\r
+       found=TRUE;\r
+       if(!IS_BUTTON(opt->type)){\r
+           if(!my_string_equal(opt->value,value)){\r
+               engine_send(uci->engine,"setoption name %s value %s",\r
+                           opt->name,value);\r
+               my_string_set(&opt->value,value);\r
+           }else{\r
+               my_log("POLYGLOT Not sending option \"%s\" since it "\r
+                      "already has the correct value.\n",opt->name);\r
+           }\r
+       }else{\r
+           engine_send(uci->engine,"setoption name %s",opt->name);\r
+       }\r
    }\r
+   return found;\r
 }\r
 \r
 // uci_parse()\r
@@ -348,7 +340,7 @@ int uci_parse(uci_t * uci, const char string[]) {
    parse_open(parse,string);\r
 \r
    if (parse_get_word(parse,command,StringSize)) {\r
-\r
+       \r
       parse_get_string(parse,argument,StringSize);\r
       if (UseDebug) my_log("POLYGLOT COMMAND \"%s\" ARGUMENT \"%s\"\n",command,argument);\r
 \r
@@ -387,9 +379,9 @@ int uci_parse(uci_t * uci, const char string[]) {
 \r
          // search information\r
 \r
-         if (uci->searching && uci->pending_nb == 1) { // current search\r
-            event = parse_info(uci,argument);\r
-         }\r
+              if (uci->searching && uci->pending_nb == 1) { // current search\r
+                  event = parse_info(uci,argument);\r
+               }\r
 \r
       } else if (my_string_equal(command,"option")) {\r
 \r
@@ -444,12 +436,19 @@ static int parse_bestmove(uci_t * uci, const char string[]) {
    // bestmove\r
 \r
    if (!parse_get_string(parse,argument,StringSize)) {\r
-      my_fatal("parse_bestmove(): missing argument\n");\r
+       return EVENT_ILLEGAL_MOVE;\r
+//      my_fatal("parse_bestmove(): missing argument\n");\r
    }\r
 \r
    uci->best_move = move_from_can(argument,uci->board);\r
-   if (uci->best_move == MoveNone) my_fatal("parse_bestmove(): not a move \"%s\"\n",argument);\r
+   if (uci->best_move == MoveNone) {\r
+       return EVENT_ILLEGAL_MOVE;\r
+//       my_fatal("parse_bestmove(): not a move \"%s\"\n",argument);\r
+   }\r
 \r
+   if(!move_is_legal(uci->best_move,uci->board)){\r
+       return EVENT_ILLEGAL_MOVE;\r
+   }\r
    ASSERT(uci->best_move!=MoveNone);\r
    ASSERT(move_is_legal(uci->best_move,uci->board));\r
 \r
@@ -544,6 +543,8 @@ static int parse_info(uci_t * uci, const char string[]) {
    int multipvline=0;\r
    sint64 ln;\r
 \r
+\r
+   \r
    ASSERT(uci_is_ok(uci));\r
    ASSERT(string!=NULL);\r
 \r
@@ -641,7 +642,7 @@ static int parse_info(uci_t * uci, const char string[]) {
          ASSERT(!my_string_empty(argument));\r
 \r
          n = atoi(argument);\r
-                if(Uci->multipv_mode) multipvline=n;\r
+        multipvline=n;\r
         \r
          ASSERT(n>=1);\r
 \r
@@ -691,12 +692,16 @@ static int parse_info(uci_t * uci, const char string[]) {
 \r
          if (n >= 0) uci->sel_depth = n;\r
 \r
-      } else if (my_string_equal(option,"string")) {\r
-                 if(!strncmp(argument,"DrawOffer",9))\r
+      } else  if (my_string_equal(option,"string")) {\r
+                 if(my_string_case_equal(argument,"DrawOffer")){\r
                          event |= EVENT_DRAW;\r
-                 if(!strncmp(argument,"Resign",6))\r
+          }else if(my_string_case_equal(argument,"Resign")){\r
                          event |= EVENT_RESIGN;\r
-\r
+          }else{\r
+              snprintf(uci->info,sizeof(uci->info),"%s",argument);\r
+              uci->info[sizeof(uci->info)-1]='\0';\r
+              event|=EVENT_INFO;\r
+          }\r
          // TODO: argument to EOS\r
 \r
          ASSERT(!my_string_empty(argument));\r
@@ -720,73 +725,61 @@ static int parse_info(uci_t * uci, const char string[]) {
       } else {\r
 \r
          my_log("POLYGLOT unknown option \"%s\" for command \"%s\"\n",option,command);\r
+             // This should probably be protected\r
+             // by a "WorkAround" option.\r
+         snprintf(uci->info,sizeof(uci->info),"%s %s",option,argument);\r
+         uci->info[sizeof(uci->info)-1]='\0';\r
+         event|=EVENT_INFO;\r
       }\r
    }\r
 \r
    parse_close(parse);\r
 \r
-   // update display\r
-   //lousy uci,filter out lower depth multipv lines that have been repeated from the engine \r
-   if(multipvline>1 && uci->depth<uci->best_depth) event &= ~EVENT_PV;\r
+\r
+   // code by HGM\r
    if ((event & EVENT_PV) != 0) {\r
       uci->best_score = uci->score; \r
-         uci->best_depth = uci->depth;\r
-         if(multipvline==1)uci->depth=-1; //HACK ,clears the engine outpout window,see send_pv in adapter.cpp \r
       uci->best_sel_depth = uci->sel_depth;\r
       line_copy(uci->best_pv,uci->pv);\r
    }\r
-   return event;\r
-}\r
-\r
-int uci_get_option(uci_t * uci, const char * name){\r
-    int i;\r
-    for(i=0;i<Uci->option_nb;i++){\r
-        if(my_string_case_equal(Uci->option[i].name,name)){\r
-            return i;\r
-        }\r
-    }\r
-    return -1;\r
-}\r
-\r
-\r
-\r
-// uci_set_option()\r
-\r
-void uci_set_option(uci_t * uci,\r
-                    const char * name,\r
-                    const char * default_,\r
-                    const char * type,\r
-                    const char * max,\r
-                    const char * min,\r
-                    int var_nb,\r
-                    const char * var[]){\r
-    int i,j;\r
-    for(i=0;i<Uci->option_nb;i++){\r
-        if(my_string_equal(Uci->option[i].name,name)){\r
-            break;\r
-       }\r
-   }\r
-   if(i<OptionNb){\r
-       my_string_set(&(Uci->option[i].name),name);\r
-       my_string_set(&(Uci->option[i].default_),default_);\r
-       my_string_set(&(Uci->option[i].type),type);\r
-       my_string_set(&(Uci->option[i].min),min);\r
-       my_string_set(&(Uci->option[i].max),max);\r
-       Uci->option[i].var_nb=var_nb;\r
-       for(j=0;j<var_nb;j++){\r
-           my_string_set(&(Uci->option[i].var[j]),var[j]);\r
+   if(uci->depth < uci->best_depth){\r
+     // ignore lines of lower depth\r
+     event &= ~EVENT_PV;\r
+   } else {\r
+     if(uci->depth > uci->best_depth) {\r
+       // clear stack when we start new depth\r
+       uci->multipvSP = 0; \r
+     }\r
+     uci->best_depth = uci->depth;\r
+     if(multipvline >= 1) {\r
+       int i;\r
+       for(i=0; i<uci->multipvSP; i++) {\r
+        if(uci->score == uci->multipvScore[i] && uci->pv[0] == uci->multipvMove[i]) {\r
+          event &= ~EVENT_PV; // ignore duplicates\r
+        }\r
        }\r
-       if(i==Uci->option_nb){\r
-           Uci->option_nb++;\r
+       if(event & EVENT_PV){\r
+        // line is new, try to add to stack\r
+        if(uci->multipvSP<MultiPVStackSize){\r
+          uci->multipvMove[uci->multipvSP] = uci->pv[0];\r
+          uci->multipvScore[uci->multipvSP] = uci->score;\r
+          uci->multipvSP++;\r
+        }else{\r
+          my_fatal("parse_info(): multipv stack overflow.");\r
+        }\r
        }\r
+     }\r
    }\r
+\r
+\r
+   return event;\r
 }\r
 \r
 // parse_option()\r
 \r
 static void parse_option(uci_t * uci, const char string[]) {\r
 \r
-   option_t * opt;\r
+   option_t opt[1];\r
    parse_t parse[1];\r
    char command[StringSize];\r
    char option[StringSize];\r
@@ -799,32 +792,16 @@ static void parse_option(uci_t * uci, const char string[]) {
 \r
    strcpy(command,"option");\r
 \r
-   if (uci->option_nb >= OptionNb) return;\r
-\r
-   opt = &uci->option[uci->option_nb];\r
-   uci->option_nb++;\r
-\r
-   opt->value=NULL;\r
+   memset(opt,0,sizeof(option_t));\r
+   \r
    my_string_set(&opt->value,"<empty>");\r
-   opt->mode=0;\r
-\r
-   opt->name = NULL;\r
    my_string_set(&opt->name,"<empty>");\r
-\r
-   \r
-   opt->default_ = NULL;\r
    my_string_set(&opt->default_,"<empty>");\r
-\r
-   opt->max = NULL;\r
    my_string_set(&opt->max,"<empty>");\r
-\r
-   opt->min = NULL;\r
    my_string_set(&opt->min,"<empty>");\r
-\r
-   opt->type = NULL;\r
    my_string_set(&opt->type,"<empty>");\r
-\r
    opt->var_nb=0;\r
+   opt->mode=0;\r
    \r
    parse_open(parse,string);\r
    parse_add_keyword(parse,"default");\r
@@ -888,6 +865,10 @@ static void parse_option(uci_t * uci, const char string[]) {
 \r
    parse_close(parse);\r
 \r
+   apply_UCI3_heuristics(opt);\r
+   option_insert(uci->option,opt);\r
+   option_free(opt);\r
+\r
    if (UseDebug) my_log("POLYGLOT option name \"%s\" default \"%s\"\n",opt->name,opt->default_);\r
 }\r
 \r
@@ -966,9 +947,9 @@ static int mate_score(int dist) {
 \r
    if (FALSE) {\r
    } else if (dist > 0) {\r
-      return +option_get_int("MateScore") - (+dist) * 2 + 1;\r
+       return +option_get_int(Option,"MateScore") - (+dist) * 2 + 1;\r
    } else if (dist < 0) {\r
-      return -option_get_int("MateScore") + (-dist) * 2;\r
+       return -option_get_int(Option,"MateScore") + (-dist) * 2;\r
    }\r
 \r
    return 0;\r