Implement piece suffixes
authorH.G.Muller <hgm@hgm-xboard.(none)>
Thu, 5 Mar 2015 14:58:56 +0000 (15:58 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Thu, 7 May 2015 18:53:33 +0000 (20:53 +0200)
The piece ID can now be suffixed with an 'ornament', which then
is considered part of the piece ID. This expands the number of pieces
that can be indicated in FEN or SAN to several times the alphabet.
This does not yet work in a promotion suffix, as it is mainly intended
for the large Shogi variants, which only use + as promotion suffix.
It also does not work on King (which deserves an unadulterated letter).
Currently the ornaments ' (single quote) and ! (exclamation point) are
defined, (through a macro SUFFIXES in moves.h), increasing the number
of piece types that can be represented to 78.

backend.c
moves.c
moves.h
parser.c

index a75a87a..9c734eb 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -5947,37 +5947,38 @@ SetUpShuffle (Board board, int number)
 }
 
 int
-ptclen (const char *s)
+ptclen (const char *s, char *escapes)
 {
     int n = 0;
-    while(*s) n += (*s != '\'' && *s != '"' && *s != '`' && *s != '!'), s++;
+    if(!*escapes) return strlen(s);
+    while(*s) n += (*s != ':' && !strchr(escapes, *s)), s++;
     return n;
 }
 
 int
-SetCharTable (char *table, const char * map)
+SetCharTableEsc (unsigned char *table, const char * map, char * escapes)
 /* [HGM] moved here from winboard.c because of its general usefulness */
 /*       Basically a safe strcpy that uses the last character as King */
 {
     int result = FALSE; int NrPieces;
 
-    if( map != NULL && (NrPieces=ptclen(map)) <= (int) EmptySquare
+    if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare
                     && NrPieces >= 12 && !(NrPieces&1)) {
         int i, j = 0; /* [HGM] Accept even length from 12 to 88 */
 
         for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';
         for( i=0; i<NrPieces/2-1; i++ ) {
-            if(map[j] == ':') i = CHUPROMOTED WhitePawn, j++;
+            char *p;
+            if(map[j] == ':' && *escapes) i = CHUPROMOTED WhitePawn, j++;
             table[i] = map[j++];
-            if(map[j] == '\'') table[i] += 64;
-            if(map[j] == '!') table[i] += 128;
+            if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
         }
         table[(int) WhiteKing]  = map[j++];
         for( i=0; i<NrPieces/2-1; i++ ) {
-            if(map[j] == ':') i = CHUPROMOTED BlackPawn, j++;
+            char *p;
+            if(map[j] == ':' && *escapes) i = CHUPROMOTED BlackPawn, j++;
             table[WHITE_TO_BLACK i] = map[j++];
-            if(map[j] == '\'') table[WHITE_TO_BLACK i] += 64;
-            if(map[j] == '!') table[WHITE_TO_BLACK i] += 128;
+            if(p = strchr(escapes, map[j])) j++, table[WHITE_TO_BLACK i] += 64*(p - escapes + 1);
         }
         table[(int) BlackKing]  = map[j++];
 
@@ -5987,6 +5988,12 @@ SetCharTable (char *table, const char * map)
     return result;
 }
 
+int
+SetCharTable (unsigned char *table, const char * map)
+{
+    return SetCharTableEsc(table, map, "");
+}
+
 void
 Prelude (Board board)
 {      // [HGM] superchess: random selection of exo-pieces
@@ -6160,8 +6167,8 @@ InitPosition (int redraw)
       gameInfo.boardWidth  = 12;
       gameInfo.boardHeight = 12;
       nrCastlingRights = 0;
-      SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.++.++++++++++.+++++K"
-                                "p.brqsexogcathd.vmlifn+.++.++++++++++.+++++k");
+      SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN:+.++.++++++++++.+++++K"
+                                   "p.brqsexogcathd.vmlifn:+.++.++++++++++.+++++k", SUFFIXES);
       break;
     case VariantCourier:
       pieces = CourierArray;
@@ -6247,7 +6254,7 @@ InitPosition (int redraw)
 
     /* User pieceToChar list overrules defaults */
     if(appData.pieceToCharTable != NULL)
-        SetCharTable(pieceToChar, appData.pieceToCharTable);
+        SetCharTableEsc(pieceToChar, appData.pieceToCharTable, SUFFIXES);
 
     for( j=0; j<BOARD_WIDTH; j++ ) { ChessSquare s = EmptySquare;
 
@@ -8923,7 +8930,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
       if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
       *buf = NULLCHAR;
       if(sscanf(message, "setup (%s", buf) == 1) {
-        s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+        s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTableEsc(pieceToChar, buf, SUFFIXES);
         ASSIGN(appData.pieceToCharTable, buf);
       }
       dummy = sscanf(message+s, "%dx%d+%d_%s", &w, &h, &hand, varName);
@@ -8934,7 +8941,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            appData.NrFiles = w; appData.NrRanks = h; appData.holdingsSize = hand;
            if(dummy == 4) gameInfo.variant = StringToVariant(varName);     // parent variant
           InitPosition(1); // calls InitDrawingSizes to let new parameters take effect
-          if(*buf) SetCharTable(pieceToChar, buf); // do again, for it was spoiled by InitPosition
+          if(*buf) SetCharTableEsc(pieceToChar, buf, SUFFIXES); // do again, for it was spoiled by InitPosition
           startedFromSetupPosition = FALSE;
         }
       }
@@ -17864,6 +17871,7 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
                     piece = (ChessSquare)(CHUDEMOTED piece);
                 }
                 *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece));
+                if(*p = PieceSuffix(piece)) p++;
                 if(p[-1] == '~') {
                     /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
                     p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece));
@@ -18083,13 +18091,20 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
                 if (i != 0  && i != BOARD_HEIGHT-1) return FALSE; // only on back-rank
                board[i][(j++)+gameInfo.holdingsWidth] = ClearBoard; p++; subst++; // placeHolder
             } else if (*p == '+' || isalpha(*p)) {
+               char *q, *s = SUFFIXES;
                 if (j >= gameInfo.boardWidth) return FALSE;
                 if(*p=='+') {
-                    piece = CharToPiece(*++p);
+                    char c = *++p;
+                    if(q = strchr(s, p[1])) p++;
+                    piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
                     if(piece == EmptySquare) return FALSE; /* unknown piece */
                     piece = (ChessSquare) (CHUPROMOTED piece ); p++;
                     if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */
-                } else piece = CharToPiece(*p++);
+                } else {
+                    char c = *p++;
+                   if(q = strchr(s, *p)) p++;
+                   piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
+               }
 
                 if(piece==EmptySquare) return FALSE; /* unknown piece */
                 if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */
diff --git a/moves.c b/moves.c
index 75772a4..a66c2e0 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -114,19 +114,32 @@ SameColor (ChessSquare piece1, ChessSquare piece2)
 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
 #endif
 
-char pieceToChar[] = {
+unsigned char pieceToChar[EmptySquare+1] = {
                         'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
                         'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
                         'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
                         'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
                         'x' };
-char pieceNickName[EmptySquare];
+unsigned char pieceNickName[EmptySquare];
 
 char
 PieceToChar (ChessSquare p)
 {
+    int c;
     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
-    return pieceToChar[(int) p];
+    c = pieceToChar[(int) p];
+    if(c & 128) c = c & 63 | 64;
+    return c;
+}
+
+char
+PieceSuffix (ChessSquare p)
+{
+    int c;
+    if((int)p < 0 || (int)p >= (int)EmptySquare) return 0; /* [HGM] for safety */
+    c = pieceToChar[(int) p];
+    if(c < 128) return 0;
+    return SUFFIXES[c - 128 >> 6];
 }
 
 int
@@ -2339,6 +2352,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
         }
         if(c=='+') *outp++ = c;
         *outp++ = ToUpper(PieceToChar(piece));
+        if(*outp = PieceSuffix(piece)) outp++;
 
        if (cl.file || (cl.either && !cl.rank)) {
             *outp++ = ff + AAA;
diff --git a/moves.h b/moves.h
index 6be5299..c162ffa 100644 (file)
--- a/moves.h
+++ b/moves.h
  *------------------------------------------------------------------------
  ** See the file ChangeLog for a revision history.  */
 
+#define SUFFIXES "'!"
+
 extern ChessSquare PromoPiece P((ChessMove moveType));
 extern ChessMove PromoCharToMoveType P((int whiteOnMove, int promoChar));
 extern char PieceToChar P((ChessSquare p));
+extern char PieceSuffix P((ChessSquare p));
 extern ChessSquare CharToPiece P((int c));
 extern int PieceToNumber P((ChessSquare p));
 
 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 unsigned char pieceToChar[(int)EmptySquare+1];
+extern unsigned char pieceNickName[(int)EmptySquare];
 extern char *pieceDesc[(int)EmptySquare];
 extern Board initialPosition;
 extern Boolean pieceDefs;
index 496fef1..13caa9f 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -418,7 +418,10 @@ NextUnit (char **p)
        if(**p == '+') (*p)++, promoted++;
        if(**p >= 'a' && **p <= 'z' && (*p)[1]== '@') piece =*(*p)++ + 'A' - 'a'; else
        if(**p >= 'A' && **p <= 'Z') {
+            static char s[] = SUFFIXES;
+            char *q;
             piece = *(*p)++; // Note we could test for 2-byte non-ascii names here
+            if(q = strchr(s, **p)) (*p)++, piece += 64*(q - s + 1);
             if(**p == '/') slash = *(*p)++;
        }
         while(n < 4) {
@@ -489,7 +492,7 @@ NextUnit (char **p)
            else if(toY >= BOARD_HEIGHT || toY < 0)   return ImpossibleMove; // vert off-board to-square
            if(toX < BOARD_LEFT || toX >= BOARD_RGHT) return ImpossibleMove;
            if(piece) {
-               cl.pieceIn = CharToPiece(wom ? piece : ToLower(piece));
+               cl.pieceIn = CharToPiece(wom ? piece : piece + 'a' - 'A');
                if(cl.pieceIn == EmptySquare) return ImpossibleMove; // non-existent piece
                if(promoted) cl.pieceIn = (ChessSquare) (CHUPROMOTED cl.pieceIn);
            } else cl.pieceIn = EmptySquare;