Add XBoard protocol drivers
authorH.G. Muller <h.g.muller@hccnet.nl>
Wed, 20 Oct 2010 18:25:57 +0000 (20:25 +0200)
committerH.G. Muller <h.g.muller@hccnet.nl>
Sun, 15 Sep 2013 08:32:12 +0000 (10:32 +0200)
So far only those commands needed for playing games from the initial
position. Commands like 'memory', 'post', 'nopost', 'analyze' do not
work yet. Neither does pondering, as Bonanza's board is not valid when a
move comes in during pondering, so the wrong piece type is appended to
the move to convert t to CSA format.

Makefile
book.c
io.c
main.c
proce.c
searchr.c
time.c
utility.c

index f5189ed..9eed265 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,11 +19,12 @@ OBJS =data.o main.o io.o proce.o utility.o ini.o attack.o book.o makemove.o \
 # -DMNJ_LAN          enables a client-mode of cluster searches.
 # -DNO_LOGGING       suppresses dumping log files.
 # -DUSI              enables USI mode (not implemented).
+# -DXBOARD           enables WinBoard/XBoard mode (implemented!).
 # -DINANIWA_SHIFT    enables an Inaniwa strategy detection.
 # -DDFPN             build the DFPN worker of mate-problems server.
 # -DDFPN_CLIENT      enables the client-mode of mate-problem server.
 
-OPT =-DNDEBUG -DMINIMUM -DHAVE_SSE4 -msse4.1 -DDFPN -DTLP -DDFPN_CLIENT -DINANIWA_SHIFT -DMNJ_LAN -DCSA_LAN
+OPT =-DNDEBUG -DMINIMUM -DHAVE_SSE4 -msse4.1 -DDFPN -DTLP -DDFPN_CLIENT -DINANIWA_SHIFT -DMNJ_LAN -DCSA_LAN -DXBOARD
 
 help:
        @echo "try targets as:"
diff --git a/book.c b/book.c
index 9a732b8..55e6d37 100644 (file)
--- a/book.c
+++ b/book.c
@@ -140,7 +140,12 @@ book_probe( tree_t * restrict ptree )
   /* show results */
   if ( ! ( game_status & ( flag_pondering | flag_puzzling ) ) )
     {
-      Out( "    move     freq\n" );
+#ifdef XBOARD
+#  define SIGN "#"
+#else
+#  define SIGN
+#endif
+      Out( SIGN "    move     freq\n" );
       OutCsaShogi( "info" );
       for ( i = 0; i < moves; i++ )
        {
diff --git a/io.c b/io.c
index c0595f1..75f12bb 100644 (file)
--- a/io.c
+++ b/io.c
@@ -233,7 +233,12 @@ show_prompt( void )
       else             { Out( "Black Suspend> " ); }
     }
   else if ( root_turn ) { Out( "White %d> ", record_game.moves+1 ); }
-  else                  { Out( "Black %d> ", record_game.moves+1 ); }
+#ifdef XBOARD
+#  define SIGN "#"
+#else
+#  define SIGN
+#endif
+  else                  { Out( SIGN "Black %d> ", record_game.moves+1 ); }
 }
 
 
diff --git a/main.c b/main.c
index 1591760..89c0316 100644 (file)
--- a/main.c
+++ b/main.c
@@ -18,6 +18,9 @@ main()
   int iret;
   tree_t * restrict ptree;
 
+#ifdef XBOARD
+  Out("feature done=0\n");
+#endif
 #if defined(TLP)
   ptree = tlp_atree_work;
 #else
diff --git a/proce.c b/proce.c
index 2578634..6538fb9 100644 (file)
--- a/proce.c
+++ b/proce.c
@@ -122,6 +122,92 @@ procedure( tree_t * restrict ptree )
   return proce_cui( ptree );
 }
 
+#ifdef XBOARD
+#define IF(X) else if(!strcmp(command, X))
+
+int myTime, hisTime, movesPerSession, inc, plyNr;
+char xboard_mode;
+
+static void
+SetTimes(void)
+{ // set white and black times from own and opponent time.
+  int moves;
+  if(movesPerSession <= 0) moves = 35; else {
+    moves = - plyNr/2;
+    while(moves <= 0) moves += movesPerSession;
+  }
+  time_limit = (myTime-inc-30)/(moves+2) + inc;
+  time_max_limit = 3*time_limit;
+  if(time_max_limit > myTime - 30)time_max_limit = myTime - 30; // keep 0.3 sec margin, as Bonanza reads the clock infrequently
+  time_limit *= 10; // msec
+  time_max_limit *= 10;
+Out("# moves=%d, time=%d inc=%d t=%d, max=%d\n", moves, myTime, inc, time_limit, time_max_limit);
+}
+
+static int
+proce_xboard(char *line, const char *command, tree_t * restrict ptree)
+{ // [HGM] added to make Bonanza.exe a native WinBoard engine
+  int value = -100000;
+  static char forceMode = 0;
+  sscanf(line + strlen(command) + 1, "%d", &value);
+Out("# command = '%s'\n", line);
+  if(0) ;
+  IF("protover") { Out("feature variants=\"shogi\" usermove=1 myname=\"Bonanza " BNZ_VER
+                       "\" memory=1 smp=1 debug=1 colors=0 setboard=1 ping=1 done=1\n"); }
+  IF("new")      { forceMode = plyNr = 0; SetTimes(); return 0; }
+  IF("easy")     { strcpy(line, "ponder off"); return 0; }
+  IF("hard")     { strcpy(line, "ponder on");  return 0; }
+  IF("post")     { ; }
+  IF("nopost")   { ; }
+  IF("time")     { sscanf(line+5, "%d", &myTime); }
+  IF("otim")     { sscanf(line+5, "%d", &hisTime); }
+  IF("force")    { forceMode = 1; }
+  IF("go")       { forceMode = 0; SetTimes(); plyNr++; strcpy(line, "move"); return 0; }
+  IF("memory")   { ; }
+  IF("cores")    { sprintf(line, "tlp num %d", value); return 0; }
+  IF("sd")       { sprintf(line, "limit depth %d", value); return 0; }
+  IF("st")       { ; }
+  IF("quit")     { return 0; }
+  IF("analyze")  { ; }
+  IF("exit")     { ; }
+  IF("variant")  { /* ignore, since it must be Shogi */; }
+  IF("setboard") { ; }
+  IF("option")   { ; }
+  IF("level")    { int min, sec; float fsec=0.;
+                   if(sscanf(line+6, "%d %d:%d %f", &movesPerSession, &min, &sec, &fsec) != 4)
+                      sscanf(line+6, "%d %d %f", &movesPerSession, &min, &fsec);
+                   min = 60*min + sec; myTime = hisTime = 100*min; inc = 100 * fsec;
+                 }
+  IF("usermove") { char fromX=line[9], fromY=line[10], toX=line[11], toY=line[12], promo=line[13];
+                   int from;
+{int i,j;for(i=0;i<81;i+=9){printf("# ");for(j=0;j<9;j++)printf(" %3d", BOARD[i+j]);printf("\n");}}
+                   if(forceMode) strcpy(line, "move "), line += 5; else plyNr++, SetTimes();
+                   if(fromY == '@') { // drop
+                       if(fromX >= 'a') fromX += 'A' - 'a';
+                       switch(fromX) { // encode piece
+                         case 'P': fromX = pawn;   break;
+                         case 'L': fromX = lance;  break;
+                         case 'N': fromX = knight; break;
+                         case 'S': fromX = silver; break;
+                         case 'G': fromX = gold;   break;
+                         case 'B': fromX = bishop; break;
+                         case 'R': fromX = rook;   break;
+                       }
+                       sprintf(line, "00%c%c%s", 'a'+'9'-toX, '1'+'9'-toY, astr_table_piece[(int)fromX]);
+                   } else {
+                       from = ('9' - fromY)*9 + (fromX - 'a');
+Out("# from=%d\n",from);
+                       sprintf(line, "%c%c%c%c%s", 'a'+'9'-fromX, '1'+'9'-fromY, 'a'+'9'-toX, '1'+'9'-toY,
+                                 astr_table_piece[abs(BOARD[from]) + (promo == '+' ? promote : 0)]);
+                   }
+                   plyNr++; return 0;
+                 }
+  IF("undo")     { ; }
+  IF("remove")   { ; }
+  IF("ping")     { Out("pong %d\n", value); }
+  return 1;
+}
+#endif
 
 static int CONV proce_cui( tree_t * restrict ptree )
 {
@@ -131,6 +217,16 @@ static int CONV proce_cui( tree_t * restrict ptree )
   token = strtok_r( str_cmdline, str_delimiters, &last );
 
   if ( token == NULL || *token == '#' ) { return 1; }
+#ifdef XBOARD
+  { 
+    if(xboard_mode) {
+      if( proce_xboard(str_cmdline, token, ptree) )  return 1; // command already processed
+      Out("# translated command '%s'\n", str_cmdline);         // command translated for processing by Bonanza
+      token = strtok_r( str_cmdline, str_delimiters, &last );  // redo parsing
+    } else
+    if ( ! strcmp( token, "xboard" ) )  { xboard_mode = 1; game_status |= flag_noprompt; return 1; }
+  }
+#endif
   if ( is_move( token ) ) { return cmd_usrmove( ptree, token, &last ); }
   if ( ! strcmp( token, "s" ) )         { return cmd_move_now(); }
   if ( ! strcmp( token, "beep" ) )      { return cmd_beep( &last); }
@@ -699,6 +795,9 @@ cmd_usrmove( tree_t * restrict ptree, const char *str_move, char **lasts )
 
        game_status      &= ~flag_pondering;
        game_status      |= flag_thinking;
+#ifdef XBOARD
+      if(!xboard_mode)
+#endif
        set_search_limit_time( root_turn );
 
        OutCsaShogi( "info ponder end\n" );
index 3afd2aa..216967d 100644 (file)
--- a/searchr.c
+++ b/searchr.c
@@ -24,6 +24,10 @@ static int CONV next_root_move( tree_t * restrict ptree );
 static int CONV next_root_move( tree_t * restrict ptree, int turn );
 #endif
 
+#ifdef XBOARD
+extern char xboard_mode;
+#endif
+
 static int CONV
 search_wrapper( tree_t * restrict ptree, int alpha, int beta, int turn,
                int depth, int ply, unsigned int state_node )
@@ -351,6 +355,9 @@ out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time )
 
       if ( ptree->pv[0].length )
        {
+#ifdef XBOARD
+          if(xboard_mode) Out("%2d %6d %6d %8d ", iteration_depth, value, time/10, 1); else
+#endif
          if ( root_move_list[root_index].status & flag_first )
            {
              Out( " %2d %6s %7.2f ", iteration_depth, str, dvalue );
@@ -361,6 +368,17 @@ out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time )
 
   for ( ply = 1; ply <= ptree->pv[0].length; ply++ )
     {
+#ifdef XBOARD
+      if(xboard_mode && is_out) { // print PV move in WB format
+        int move = ptree->pv[0].a[ply], from = I2From(move), to = I2To(move);
+        char inPromoZone = tt > 0 ? from >= 6*9 || to >= 6*9 : to < 3*9|| from < 3*9;
+        if(from >= nsquare)
+          Out(" %c@%c%c", "PLNSGBR"[From2Drop(from)-1], 'a'+to%9, '9'-to/9);
+        else
+          Out(" %c%c%c%c%s", 'a'+from%9, '9'-from/9, 'a'+to%9, '9'-to/9,
+              inPromoZone ? I2IsPromote(move) ? "+" : "=" : "");
+      } else
+#endif
       if ( is_out )
        {
          if ( ply > 1 && ! ( (ply-1) % 5 ) )
@@ -407,6 +425,17 @@ out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time )
          for ( i = 1; i < ply; i++ )
            if ( ptree->pv[0].a[i] == ptree->pv[0].a[ply] ) { goto rep_esc; }
          
+#ifdef XBOARD
+      if(xboard_mode && is_out) { // print PV move in WB format
+        int move = ptree->pv[0].a[ply], from = I2From(move), to = I2To(move);
+        char inPromoZone = tt > 0 ? from >= 6*9 || to >= 6*9 : to < 3*9|| from < 3*9;
+        if(from >= nsquare)
+          Out(" %c@%c%c", "PLNSGBR"[From2Drop(from)-1], 'a'+to%9, '9'-to/9);
+        else
+          Out(" %c%c%c%c%s", 'a'+from%9, '9'-from/9, 'a'+to%9, '9'-to/9,
+              inPromoZone ? I2IsPromote(move) ? "+" : "=" : "");
+      } else
+#endif
          if ( is_out )
            {
              if ( ply > 1 && ! ( (ply-1) % 5 ) )
@@ -546,6 +575,9 @@ next_root_move( tree_t * restrict ptree, int turn )
       root_index                = i;
 
 #if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) )
+#ifdef XBOARD
+      if(!xboard_mode)
+#endif
       if ( iteration_depth > 5 )
        {
          const char *str_move;
diff --git a/time.c b/time.c
index fbeb04a..8e2f7c0 100644 (file)
--- a/time.c
+++ b/time.c
@@ -39,7 +39,10 @@ set_search_limit_time( int turn )
 */
 {
   unsigned int u0, u1;
-
+#ifdef XBOARD
+  extern char xboard_mode;
+  if(xboard_mode) return;
+#endif
   /* no time-control */
   if ( sec_limit_up == UINT_MAX || ( game_status & flag_pondering ) )
     {
index 198aa90..2d6d8ec 100644 (file)
--- a/utility.c
+++ b/utility.c
@@ -586,6 +586,20 @@ com_turn_start( tree_t * restrict ptree, int flag )
   }
 
   OutCsaShogi( "info tt %03u:%02u\n", sec_total / 60U, sec_total % 60U );
+#ifdef XBOARD
+  { extern char xboard_mode;
+    if(xboard_mode) { // print move in WB format and defuse next line
+      if(str_move[0] < '0' || str_move[0] > '9') Out("\n%s\n# ", str_move); // only resign?
+      else if(str_move[0] == '0')
+        Out("\nmove %c@%c%c\n# ", // drop
+               "PLNSGBR"[(move>>7&127)-nsquare],
+               '9'+'a'-str_move[2], '1'+'9'-str_move[3]);
+      else Out("\n#t=%d tm=%d\nmove %c%c%c%c%s\n# ", time_limit, time_max_limit,
+               '9'+'a'-str_move[0], '1'+'9'-str_move[1],
+               '9'+'a'-str_move[2], '1'+'9'-str_move[3], (move & FLAG_PROMO ? "+" : "="));
+    }
+  }
+#endif
   Out( "%s '(%d%s) %03u:%02u/%03u:%02u  elapsed: b%u, w%u\n",
        str_move, value,
        ( last_pv.type == pv_fail_high ) ? "!" : "",