Implement engine-defined pieces
authorH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 17 Sep 2014 11:41:58 +0000 (13:41 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Sun, 28 Sep 2014 20:14:27 +0000 (22:14 +0200)
When legality testing is off, XBoard now listens to 'piece' commands
to redefine its move generator. After such a piece command is received,
target-square marking also can be used when legality testing is off.
The engine keeps in charge of legality testing, though.
 Only a very basic version of Betza notation is currently understood for
the move description: mode modifiers have to appear in the order mcd,
and gpz modifiers are ignored. Multi-leg moves as in Betza 2.0 is not
implemented at all. A leading i modifier limits the moves to the first
two ranks, which is no good at all. (But XBoard does not keep track of
which pieces have moved, and this at least would work for Pawns in a
FIDE-like setup, in particular Berolina.)

backend.c
moves.c
moves.h

index aa81da2..88848ef 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -295,6 +295,7 @@ ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
 int promoDefaultAltered;
 int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
 static int initPing = -1;
+static Boolean pieceDefs;
 
 /* States for ics_getting_history */
 #define H_FALSE 0
@@ -954,7 +955,7 @@ extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
 static char resetOptions[] =
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
        "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
-       "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
+       "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 -fd \".\" "
        "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
 
 void
@@ -7277,7 +7278,7 @@ MarkTargetSquares (int clear)
   } else {
     int capt = 0;
     if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
-       !appData.testLegality || gameMode == EditPosition) return;
+       !appData.testLegality && !pieceDefs || gameMode == EditPosition) return;
     GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker, EmptySquare);
     if(PosFlags(0) & F_MANDATORY_CAPTURE) {
       for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
@@ -8886,6 +8887,11 @@ printf("score=%d count=%d\n",score,count);
       startedFromSetupPosition = TRUE;
       return;
     }
+    if(sscanf(message, "piece %c %s", &promoChar, buf1) == 2) {
+      ChessSquare piece = CharToPiece(promoChar);
+      if(piece < EmptySquare && !appData.testLegality) { ASSIGN(pieceDesc[piece], buf1); pieceDefs = TRUE; }
+      return;
+    }
     /* [HGM] Allow engine to set up a position. Don't ask me why one would
      * want this, I was asked to put it in, and obliged.
      */
@@ -11607,6 +11613,8 @@ Reset (int redraw, int init)
        fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
                redraw, init, gameMode);
     }
+    pieceDefs = FALSE; // [HGM] gen: reset engine-defined piece moves
+    for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; }
     CleanupTail(); // [HGM] vari: delete any stored variations
     CommentPopDown(); // [HGM] make sure no comments to the previous game keep hanging on
     pausing = pauseExamInvalid = FALSE;
diff --git a/moves.c b/moves.c
index 3b497d9..2e772e1 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -54,6 +54,8 @@
 #include "config.h"
 
 #include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
 #if HAVE_STRING_H
 # include <string.h>
 #else /* not HAVE_STRING_H */
@@ -71,6 +73,7 @@ int PosFlags(int index);
 
 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
 int quickFlag;
+char *pieceDesc[EmptySquare];
 
 int
 WhitePiece (ChessSquare piece)
@@ -165,6 +168,112 @@ CompareBoards (Board board1, Board board2)
     return TRUE;
 }
 
+// [HGM] gen: configurable move generation from Betza notation sent by engine.
+
+//  alphabet      "abcdefghijklmnopqrstuvwxyz"
+char symmetry[] = "FBNW.F.WFNKN.N..QR....W..N";
+char xStep[]    = "2110.1.03102.10.00....0..2";
+char yStep[]    = "2132.1.33313.20.11....1..3";
+char dirType[]  = "01000104000200000260050000";
+//  alphabet   "a b    c d e f    g h    i j k l    m n o p q r    s    t u v    w x y z "
+int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0,   0,0,0,0xF0,0,0,0,0,0,0x0F,0   ,0,0,0   ,0,0,0,0 };
+int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 };
+
+int rot[][4] = { // rotation matrices for each direction
+  { 1, 0, 0, 1 },
+  { 0, 1, 1, 0 },
+  { 0, 1,-1, 0 },
+  { 1, 0, 0,-1 },
+  {-1, 0, 0,-1 },
+  { 0,-1,-1, 0 },
+  { 0,-1, 1, 0 },
+  {-1, 0, 0, 1 }
+};
+
+void
+MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
+{
+    char *p = desc;
+    int mine, his, dir, bit, occup, i;
+    if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
+    while(*p) {                  // more moves to go
+       int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0;
+       if(*p == 'i') initial = 1, p++;
+       while(islower(*p)) p++;  // skip prefixes
+       if(!isupper(*p)) return; // syntax error: no atom
+       dirSet = 0;              // build direction set based on atom symmetry
+       switch(symmetry[*p-'A']) {
+         case 'B': expo = 0;    // bishop, slide
+         case 'F':              // diagonal atom (degenerate 4-fold)
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs1[*desc-'a']; // use wide version
+                       if( islower(desc[1]) &&
+                                ((i | dirType[desc[1]-'a']) & 3) == 3) {   // combinable (perpendicular dim)
+                           b = dirs1[*desc-'a'] & dirs1[desc[1]-'a'];      // intersect wide & perp wide
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
+                   break;
+         case 'R': expo = 0;    // rook, slide
+         case 'W':              // orthogonal atom (non-deg 4-fold)
+                   while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
+                   dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+                   break;
+         case 'N':              // oblique atom (degenerate 8-fold)
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs2[*desc-'a']; // when alone, use narrow version
+                       if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
+                       else if(islower(desc[1]) && i < '4'
+                               && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
+                           b = dirs1[*desc-'a'] & dirs2[desc[1]-'a'];      // intersect wide & perp narrow
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   if(!dirSet) dirSet = 0xFF;
+                   break;
+         case 'Q': expo = 0;    // queen, slide
+         case 'K':              // non-deg (pseudo) 8-fold
+                   dirSet=0x55; // start with orthogonal moves
+                   retry = 1;   // and schedule the diagonal moves for later
+                   break;       // should not have direction indicators
+         default:  return;      // syntax error: invalid atom
+       }
+       if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
+       mode = 0;                // build mode mask
+       if(*desc == 'm') mode |= 4, desc++;
+       if(*desc == 'c') mode |= his, desc++;
+       if(*desc == 'd') mode |= mine, desc++;
+       if(!mode) mode = his + 4;// no mode spec, use default = mc
+       dx = xStep[*p-'A'] - '0';                     // step vector of atom
+       dy = yStep[*p-'A'] - '0';
+       if(isdigit(*++p)) expo = atoi(p++);           // read exponent
+       if(expo > 9) p++;                             // allow double-digit
+       desc = p;                                     // this is start of next move
+       if(initial && (mine == 1 ? r > 1 : r < BOARD_HEIGHT - 2)) continue;
+        do {
+         for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
+           int i = expo;
+           if(!(bit & dirSet)) continue;             // does not move in this direction
+           x = f; y = r;                             // start square
+           do {
+               x += dx*rot[dir][0] + dy*rot[dir][1]; // step to next square
+               y += dx*rot[dir][2] + dy*rot[dir][3];
+               if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
+               if(board[y][x] < BlackPawn)   occup = 1; else
+               if(board[y][x] < EmptySquare) occup = 2; else
+                                             occup = 4;
+               if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
+               if(occup != 4) break; // not valid transit square
+           } while(--i);
+         }
+         dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
+       } while(retry--);             // and start doing them
+    }
+} // next atom
+
 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
 
 void
@@ -394,6 +503,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
           if(PieceToChar(piece) == '~')
                  piece = (ChessSquare) ( DEMOTED piece );
           if(filter != EmptySquare && piece != filter) continue;
+          if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
           if(IS_SHOGI(gameInfo.variant))
                  piece = (ChessSquare) ( SHOGI piece );
 
diff --git a/moves.h b/moves.h
index a86cca5..5579cbd 100644 (file)
--- a/moves.h
+++ b/moves.h
@@ -61,6 +61,7 @@ extern void CopyBoard P((Board to, Board from));
 extern int CompareBoards P((Board board1, Board board2));
 extern char pieceToChar[(int)EmptySquare+1];
 extern char pieceNickName[(int)EmptySquare];
+extern char *pieceDesc[(int)EmptySquare];
 
 typedef void (*MoveCallback) P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,