Fix Bold button and application of commentFont
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index bc5f415..3205d2f 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -5,7 +5,8 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+ * Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -71,7 +72,6 @@ int BlackPiece P((ChessSquare));
 int SameColor P((ChessSquare, ChessSquare));
 int PosFlags(int index);
 
 int SameColor P((ChessSquare, ChessSquare));
 int PosFlags(int index);
 
-extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
 int quickFlag;
 char *pieceDesc[EmptySquare];
 char *defaultDesc[EmptySquare] = {
 int quickFlag;
 char *pieceDesc[EmptySquare];
 char *defaultDesc[EmptySquare] = {
@@ -114,19 +114,33 @@ SameColor (ChessSquare piece1, ChessSquare piece2)
 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
 #endif
 
 #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' };
                         '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];
+int promoPartner[EmptySquare];
 
 char
 PieceToChar (ChessSquare p)
 {
 
 char
 PieceToChar (ChessSquare p)
 {
-    if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
-    return pieceToChar[(int) p];
+    int c;
+    if((int)p < 0 || (int)p >= (int)EmptySquare) return('?'); /* [HGM] for safety */
+    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
 }
 
 int
@@ -191,9 +205,9 @@ CollectPieceDescriptors ()
     // dump all engine defined pieces, and pieces with non-standard names,
     // but suppress black pieces that are the same as their white counterpart
     ChessSquare p;
     // dump all engine defined pieces, and pieces with non-standard names,
     // but suppress black pieces that are the same as their white counterpart
     ChessSquare p;
-    static char buf[MSG_SIZ];
-    char *m, c, d, *pieceName = defaultName;
-    int len;
+    static char buf[MSG_SIZ], s[2];
+    char *m, *pieceName = defaultName;
+    int len, c, d;
     *buf = NULLCHAR;
     if(!pieceDefs) return "";
     if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
     *buf = NULLCHAR;
     if(!pieceDefs) return "";
     if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
@@ -201,22 +215,57 @@ CollectPieceDescriptors ()
     if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
     for(p=WhitePawn; p<EmptySquare; p++) {
        if((c = pieceToChar[p]) == '.' || c == '~') continue;  // does not participate
     if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
     for(p=WhitePawn; p<EmptySquare; p++) {
        if((c = pieceToChar[p]) == '.' || c == '~') continue;  // does not participate
-       m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED p] : c);
-       if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c)
-             && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair
+       m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c);
+       if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32)
+             && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair
            char *wm = pieceDesc[BLACK_TO_WHITE p];
            if(!m && !wm || m && wm && !strcmp(wm, m)) continue;            // moves as a white piece
        } else                                                              // white or unpaired black
            char *wm = pieceDesc[BLACK_TO_WHITE p];
            if(!m && !wm || m && wm && !strcmp(wm, m)) continue;            // moves as a white piece
        } else                                                              // white or unpaired black
-       if((p < BlackPawn || CharToPiece(toupper(d)) != EmptySquare) &&     // white or lone black
+       if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) &&        // white or lone black
           !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
 // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
        if(!m) m = defaultDesc[p];
           !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
 // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
        if(!m) m = defaultDesc[p];
+       if(!m) continue;
        len = strlen(buf);
        len = strlen(buf);
-       snprintf(buf+len, MSG_SIZ-len, "%s%s%c:%s", len ? ";" : "", c == '+' ? "+" : "", d, m);
+       *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63);
+       snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m);
     }
     return buf;
 }
 
     }
     return buf;
 }
 
+int
+LoadPieceDesc (char *s)
+{
+    ChessSquare piece;
+    static char suf[] = SUFFIXES;
+    char *r, *p, *q = s;
+    int ok = TRUE, promoted, c;
+    while(q && *s) {
+       p = s;
+       q = strchr(s, ';');
+       if(q) *q = 0, s = q+1;
+       if(*p == '+') promoted = 1, p++; else promoted = 0;
+       c = *p++;
+       if(!c) { ok = FALSE; continue; } // bad syntax
+       if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++;
+       if(*p++ != ':') { ok = FALSE; continue; } // bad syntax
+       if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0
+        piece = CharToPiece(c);
+       if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece
+       if(promoted) {
+           piece = promoPartner[piece];
+           if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist
+       }
+       ASSIGN(pieceDesc[piece], p);
+       if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) {
+           ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p);
+       }
+       pieceDefs = TRUE;
+       if(q) *q = ';';
+    }
+    return ok;
+}
+
 // [HGM] gen: configurable move generation from Betza notation sent by engine.
 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
 // [HGM] gen: configurable move generation from Betza notation sent by engine.
 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
@@ -235,7 +284,8 @@ char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
 char xStep[]    = "2110.130.102.10.00....0..2";
 char yStep[]    = "2132.133.313.20.11....1..3";
 char dirType[]  = "01000104000200000260050000";
 char xStep[]    = "2110.130.102.10.00....0..2";
 char yStep[]    = "2132.133.313.20.11....1..3";
 char dirType[]  = "01000104000200000260050000";
-char upgrade[]  = "AKCD.QGH.JQL.NO.KK....Q..Z";
+char upgrade[]  = "AFCD.BGH.JQL.NO.KW....R..Z";
+char rotate[]   = "DRCA.WHG.JKL.NO.QB....F..Z";
 
 //  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 };
 
 //  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 };
@@ -261,15 +311,19 @@ OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOID
 }
 
 void
 }
 
 void
-MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
+MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, int range, char *desc, MoveCallback cb, VOIDSTAR cl)
 {
 {
-    char buf[80], *p = desc;
-    int mine, his, dir, bit, occup, i;
+    char buf[80], *p = desc, *atom = NULL;
+    int mine, his, dir, bit, occup, i, ep, promoRank = -1;
+    ChessMove promo= NormalMove; ChessSquare pc = board[r][f];
+    if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board
     if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
     if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
+    if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
+    if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
     while(*p) {                  // more moves to go
     while(*p) {                  // more moves to go
-       int expo = 1, dx, dy, x, y, mode, dirSet, ds2, retry=0, initial=0, jump=1, skip = 0;
+       int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
        char *cont = NULL;
        char *cont = NULL;
-       if(*p == 'i') initial = 1, desc = ++p;
+       while(*p == 'i') initial++, desc = ++p;
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
        dx = xStep[*p-'A'] - '0';// step vector of atom
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
        dx = xStep[*p-'A'] - '0';// step vector of atom
@@ -277,9 +331,9 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
        dirSet = 0;              // build direction set based on atom symmetry
        switch(symmetry[*p-'A']) {
          case 'B': expo = 0;    // bishop, slide
        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)
-                   if(tx < 0) { // for continuation legs relative directions are orthogonal!
-                     while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+         case 'F': all = 0xAA;  // diagonal atom (degenerate 4-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
+                   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)
                        int b = dirs1[*desc-'a']; // use wide version
                        if( islower(desc[1]) &&
                                 ((i | dirType[desc[1]-'a']) & 3) == 3) {   // combinable (perpendicular dim)
@@ -287,19 +341,22 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                            desc += 2;
                        } else desc++;
                        dirSet |= b;
                            desc += 2;
                        } else desc++;
                        dirSet |= b;
-                     }
-                     dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
-                     break;
                    }
                    }
+                   dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
+                   break;
          case 'R': expo = 0;    // rook, slide
          case 'R': expo = 0;    // rook, slide
-         case 'W':              // orthogonal atom (non-deg 4-fold)
+         case 'W': all = 0x55;  // orthogonal atom (non-deg 4-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
                    while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
                    dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
                    dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
                    break;
                    while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
                    dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
                    dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
                    break;
-         case 'N':              // oblique atom (degenerate 8-fold)
-                   if(tx < 0) { // for continuation legs relative directions are non-degenerate!
-                     while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+         case 'N': all = 0xFF;  // oblique atom (degenerate 8-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
+                   if(*desc == 'h') {            // chiral direction sets 'hr' and 'hl'
+                       dirSet = (desc[1] == 'r' ? 0x55 :  0xAA); desc += 2;
+                   } else
+                   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(*desc == desc[1] || islower(desc[1]) && i < '4'
                        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(*desc == desc[1] || islower(desc[1]) && i < '4'
@@ -308,12 +365,12 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                            desc += 2;
                        } else desc++;
                        dirSet |= b;
                            desc += 2;
                        } else desc++;
                        dirSet |= b;
-                     }
-                     if(!dirSet) dirSet = 0xFF;
-                     break;
                    }
                    }
+                   if(!dirSet) dirSet = 0xFF;
+                   break;
          case 'Q': expo = 0;    // queen, slide
          case 'Q': expo = 0;    // queen, slide
-         case 'K':              // non-deg (pseudo) 8-fold
+         case 'K': all = 0xFF;  // non-deg (pseudo) 8-fold
+         king:
                    while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
                        int b = dirs4[*desc-'a'];    // when alone, use narrow version
                        if(desc[1] == *desc) desc++; // doubling forces alone
                    while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
                        int b = dirs4[*desc-'a'];    // when alone, use narrow version
                        if(desc[1] == *desc) desc++; // doubling forces alone
@@ -324,12 +381,13 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                        } else desc++;
                        dirSet |= b;
                    }
                        } else desc++;
                        dirSet |= b;
                    }
-                   if(!dirSet) dirSet = 0xFF;
+                   if(!dirSet) dirSet = (tx < 0 ? 0xFF                     // default is all directions, but in continuation leg
+                                         : all == 0xFF ? 0xEF : 0x45);     // omits backward, and for 4-fold atoms also diags
                    dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
                    ds2 = dirSet & 0xAA;          // extract diagonal directions
                    if(dirSet &= 0x55)            // start with orthogonal moves, if present
                    dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
                    ds2 = dirSet & 0xAA;          // extract diagonal directions
                    if(dirSet &= 0x55)            // start with orthogonal moves, if present
-                        retry = 1;               // and schedule the diagonal moves for later
-                   else dx = 1, dirSet = ds2;    // if no orthogonal directions, do diagonal immediately
+                        retry = 1, dx = 0;       // and schedule the diagonal moves for later
+                   else dx = dy, dirSet = ds2;   // if no orthogonal directions, do diagonal immediately
                    break;       // should not have direction indicators
          default:  return;      // syntax error: invalid atom
        }
                    break;       // should not have direction indicators
          default:  return;      // syntax error: invalid atom
        }
@@ -350,34 +408,38 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
        if(isdigit(*++p)) expo = atoi(p++);           // read exponent
        if(expo > 9) p++;                             // allow double-digit
        desc = p;                                     // this is start of next move
        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 && (board[r][f] != initialPosition[r][f] ||
+       if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; initial = 0; } else
+       if(initial && !range) {
+               if(   (board[r][f] != initialPosition[r][f] ||
                       r == 0              && board[TOUCHED_W] & 1<<f ||
                       r == 0              && board[TOUCHED_W] & 1<<f ||
-                      r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f   ) ) continue;
-       if(expo > 1 && dx == 0 && dy == 0) {          // castling indicated by O + number
+                      r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f   )) continue;
+               initial = 0;
+       }
+       if(expo > 0 && dx == 0 && dy == 0) {          // castling indicated by O + number
            mode |= 1024; dy = 1;
        }
            mode |= 1024; dy = 1;
        }
+       if(expo < 0) expo = 1;                        // use 1 for default
         if(!cont) {
         if(!cont) {
-           if(!(mode & 15)) mode = his + 4;          // no mode spec, use default = mc
+           if(!(mode & 15)) mode |= his + 4;         // no mode spec, use default = mc
        } else {
        } else {
+           strncpy(buf, cont, 80); cont = buf;       // copy next leg(s), so we can modify
+           atom = buf; while(islower(*atom)) atom++; // skip to atom
            if(mode & 32) mode ^= 256 + 32;           // in non-final legs 'p' means 'pass through'
            if(mode & 64 + 512) {
                mode |= 256;                          // and 'g' too, but converts leaper <-> slider
                if(mode & 512) mode ^= 0x304;         // and 'y' is m-like 'g'
            if(mode & 32) mode ^= 256 + 32;           // in non-final legs 'p' means 'pass through'
            if(mode & 64 + 512) {
                mode |= 256;                          // and 'g' too, but converts leaper <-> slider
                if(mode & 512) mode ^= 0x304;         // and 'y' is m-like 'g'
-               strncpy(buf, cont, 80); cont = buf;   // copy next leg(s), so we can modify
-               while(islower(*cont)) cont++;         // skip to atom
-               *cont = upgrade[*cont-'A'];           // replace atom, BRQ <-> FWK
-               if(expo == 1) *++cont = '0';          // turn other leapers into riders 
-               *++cont = '\0';                       // make sure any old range is stripped off
-               cont = buf;                           // use modified string for continuation leg
+               *atom = upgrade[*atom-'A'];           // replace atom, BRQ <-> FWK
+               atom[1] = atom[2] = '\0';             // make sure any old range is stripped off
+               if(expo == 1) atom[1] = '0';          // turn other leapers into riders 
            }
            }
-           if(!(mode & 0x30F)) mode = his + 0x104;   // and default = mcp
+           if(!(mode & 0x30F)) mode |= 4;            // and default of this leg = m
        }
        if(dy == 1) skip = jump - 1, jump = 1;        // on W & F atoms 'j' = skip first square
         do {
          for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
        }
        if(dy == 1) skip = jump - 1, jump = 1;        // on W & F atoms 'j' = skip first square
         do {
          for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
-           int i = expo, j = skip, hop = mode, vx, vy;
+           int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
            if(!(bit & dirSet)) continue;             // does not move in this direction
            if(!(bit & dirSet)) continue;             // does not move in this direction
-           if(dy != 1) j = 0;                        // 
+           if(dy != 1 || mode & 1024) j = 0;         // 
            vx = dx*rot[dir][0] + dy*rot[dir][1];     // rotate step vector
            vy = dx*rot[dir][2] + dy*rot[dir][3];
            if(tx < 0) x = f, y = r;                  // start square
            vx = dx*rot[dir][0] + dy*rot[dir][1];     // rotate step vector
            vy = dx*rot[dir][2] + dy*rot[dir][3];
            if(tx < 0) x = f, y = r;                  // start square
@@ -385,52 +447,75 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
            do {                                      // traverse ray
                x += vx; y += vy;                     // step to next square
                if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
            do {                                      // traverse ray
                x += vx; y += vy;                     // step to next square
                if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
-               if(x <  BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT; else break; }
-               if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT; else break; }
+               if(x <  BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
+               if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
                if(j) { j--; continue; }              // skip irrespective of occupation
                if(j) { j--; continue; }              // skip irrespective of occupation
+               if(board[y][x] == DarkSquare) break;  // black squares are supposed to be off board
                if(!jump    && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
                if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
                if(!jump    && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
                if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
-               if(x == f && y == r)          occup = 4;     else                         // start square counts as empty
+               if(x == f && y == r && !loop) occup = 4;     else // start square counts as empty (if not around cylinder!)
                if(board[y][x] < BlackPawn)   occup = 0x101; else
                if(board[y][x] < EmptySquare) occup = 0x102; else
                                              occup = 4;
                if(board[y][x] < BlackPawn)   occup = 0x101; else
                if(board[y][x] < EmptySquare) occup = 0x102; else
                                              occup = 4;
+               if(initial && expo - i + 1 != range) { if(occup == 4) continue; else break; }
                if(cont) {                            // non-final leg
                  if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
                  if(occup & mode) {                  // valid intermediate square, do continuation
                if(cont) {                            // non-final leg
                  if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
                  if(occup & mode) {                  // valid intermediate square, do continuation
+                   char origAtom = *atom;
+                   int rg = (expo != 1 ? expo - i + 1 : range);   // pass length of last *slider* leg
+                   if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
                    if(occup & mode & 0x104)          // no side effects, merge legs to one move
                    if(occup & mode & 0x104)          // no side effects, merge legs to one move
-                       MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
-                   if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) {     // destructive first leg
+                       MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
+                   if(occup & mode & 3 && (killX < 0 || kill2X < 0 && (legNr > 1 || killX == x && killY == y) ||
+                                           (legNr == 1 ? kill2X == x && kill2Y == y : killX == x && killY == y))) {     // destructive first leg
                        int cnt = 0;
                        int cnt = 0;
-                       MovesFromString(board, flags, f, r, x, y, dir, cont, &OK, &cnt);  // count possible continuations
-                       if(cnt) {                                                         // and if there are
-                           if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl);     // then generate their first leg
+                       legNr <<= 1;
+                       MovesFromString(board, flags, f, r, x, y, dir, rg, cont, &OK, &cnt); // count possible continuations
+                       legNr >>= 1;
+                       if(cnt) {                                                            // and if there are
+                           if(legNr & 1 ? killX < 0 : kill2X < 0) cb(board, flags, FirstLeg, r, f, y, x, cl);     // then generate their first leg
                            legNr <<= 1;
                            legNr <<= 1;
-                           MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
+                           MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
                            legNr >>= 1;
                        }
                    }
                            legNr >>= 1;
                        }
                    }
+                   *atom = origAtom;        // undo any interconversion
                  }
                  if(occup != 4) break;      // occupied squares always terminate the leg
                  continue;
                }
                if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
                  }
                  if(occup != 4) break;      // occupied squares always terminate the leg
                  continue;
                }
                if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
-               if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
+               ep = board[EP_RANK];
+               if(mode & 8 && occup == 4 && board[EP_FILE] == x && (y == (ep & 127) || y - vy == ep - 128)) { // to e.p. square (or 2nd e.p. square)
                    cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
                }
                if(mode & 1024) {            // castling
                    i = 2;                   // kludge to elongate move indefinitely
                    if(occup == 4) continue; // skip empty squares
                    cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
                }
                if(mode & 1024) {            // castling
                    i = 2;                   // kludge to elongate move indefinitely
                    if(occup == 4) continue; // skip empty squares
-                   if(x == BOARD_LEFT   && board[y][x] == initialPosition[y][x]) // reached initial corner piece
+                   if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
+                                                                   && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
+                     if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook')
+                       if(killX < 0) cb(board, flags, FirstLeg,   r, f, y, x, cl); if(killX < f)
+                       legNr <<= 1,  cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
+                     } else
                        cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
                        cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
-                   if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
+                   }
+                   if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
+                                                                   && board[y][x] == initialPosition[y][x]) {
+                     if(pc != WhiteKing && pc != BlackKing || expo == 1) {
+                       if(killX < 0) cb(board, flags, FirstLeg,   r, f, y, x, cl); if(killX > f)
+                       legNr <<= 1,  cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
+                     } else
                        cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
                        cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
+                   }
                    break;
                }
                    break;
                }
-               if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl);    // allowed, generate
+               if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
+               if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
                if(occup != 4) break; // not valid transit square
            } while(--i);
          }
                if(occup != 4) break; // not valid transit square
            } while(--i);
          }
-         dx = dy = 1; dirSet = ds2;  // prepare for diagonal moves of K,Q
+         dx = dy; dirSet = ds2;      // prepare for diagonal moves of K,Q
        } while(retry-- && ds2);      // and start doing them
        if(tx >= 0) break;            // don't do other atoms in continuation legs
     }
        } while(retry-- && ds2);      // and start doing them
        if(tx >= 0) break;            // don't do other atoms in continuation legs
     }
@@ -534,12 +619,12 @@ Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR
 
 void
 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
 
 void
 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
-{ // Lion-like move of Horned Falcon and Souring Eagle
+{ // Lion-like move of Horned Falcon and Soaring Eagle
   int ft = ff + dx, rt = rf + dy;
   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
   legNr += 2;
   if (!SameColor(board[rf][ff], board[rt][ft]))
   int ft = ff + dx, rt = rf + dy;
   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
   legNr += 2;
   if (!SameColor(board[rf][ff], board[rt][ft]))
-    callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
+    callback(board, flags, killX < 0 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
   legNr -= 2;
   ft += dx; rt += dy;
   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
   legNr -= 2;
   ft += dx; rt += dy;
   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
@@ -669,10 +754,10 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
          if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
           m = 0; piece = board[rf][ff];
           if(PieceToChar(piece) == '~')
          if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
           m = 0; piece = board[rf][ff];
           if(PieceToChar(piece) == '~')
-                 piece = (ChessSquare) ( DEMOTED piece );
+                 piece = (ChessSquare) ( DEMOTED(piece) );
           if(filter != EmptySquare && piece != filter) continue;
           if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
           if(filter != EmptySquare && piece != filter) continue;
           if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
-              MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure);
+              MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], callback, closure);
               continue;
           }
           if(IS_SHOGI(gameInfo.variant))
               continue;
           }
           if(IS_SHOGI(gameInfo.variant))
@@ -845,14 +930,14 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
 
             /* Gold General (and all its promoted versions) . First do the */
             /* diagonal forward steps, then proceed as normal Wazir        */
 
             /* Gold General (and all its promoted versions) . First do the */
             /* diagonal forward steps, then proceed as normal Wazir        */
-            case SHOGI (PROMOTED WhitePawn):
+            case SHOGI (PROMO WhitePawn):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
-            case SHOGI (PROMOTED BlackPawn):
+            case SHOGI (PROMO BlackPawn):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED WhiteKnight):
+            case SHOGI (PROMO WhiteKnight):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
             case SHOGI BlackDrunk:
             case SHOGI BlackAlfil:
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
             case SHOGI BlackDrunk:
             case SHOGI BlackAlfil:
@@ -861,7 +946,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                StepBackward(board, flags, rf, ff, callback, closure);
                break;
 
                StepBackward(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED BlackKnight):
+            case SHOGI (PROMO BlackKnight):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
             case SHOGI WhiteDrunk:
             case SHOGI WhiteAlfil:
                if(gameInfo.variant == VariantShogi) goto BlackGold;
             case SHOGI WhiteDrunk:
             case SHOGI WhiteAlfil:
@@ -871,15 +956,15 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                break;
 
 
                break;
 
 
-            case SHOGI WhiteStag:
-            case SHOGI BlackStag:
+            case SHOGI WhiteGnu:
+            case SHOGI BlackGnu:
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                Ferz(board, flags, rf, ff, callback, closure);
                StepSideways(board, flags, rf, ff, callback, closure);
                break;
 
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                Ferz(board, flags, rf, ff, callback, closure);
                StepSideways(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED WhiteQueen):
+            case SHOGI (PROMO WhiteQueen):
             case SHOGI WhiteTokin:
             case SHOGI WhiteWazir:
            WhiteGold:
             case SHOGI WhiteTokin:
             case SHOGI WhiteWazir:
            WhiteGold:
@@ -887,7 +972,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                Wazir(board, flags, rf, ff, callback, closure);
                break;
 
                Wazir(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED BlackQueen):
+            case SHOGI (PROMO BlackQueen):
             case SHOGI BlackTokin:
             case SHOGI BlackWazir:
             BlackGold:
             case SHOGI BlackTokin:
             case SHOGI BlackWazir:
             BlackGold:
@@ -1179,7 +1264,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
                 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
                 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
                 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
                 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
                 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
-                callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
+                callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
                          rf, ff, rt, ft, closure);
                 legNr -= 2*i;
               }
                          rf, ff, rt, ft, closure);
                 legNr -= 2*i;
               }
@@ -1198,9 +1283,9 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED WhiteFerz):
+            case SHOGI (PROMO WhiteFerz):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
-            case SHOGI (PROMOTED BlackFerz):
+            case SHOGI (PROMO BlackFerz):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
             case SHOGI WhitePSword:
             case SHOGI BlackPSword:
                if(gameInfo.variant == VariantShogi) goto BlackGold;
             case SHOGI WhitePSword:
             case SHOGI BlackPSword:
@@ -1236,7 +1321,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI WhiteHorned:
+            case SHOGI WhiteCat:
                Sting(board, flags, rf, ff, 1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
                Sting(board, flags, rf, ff, 1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
@@ -1245,7 +1330,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                SlideBackward(board, flags, rf, ff, callback, closure);
                break;
 
                SlideBackward(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI BlackHorned:
+            case SHOGI BlackCat:
                Sting(board, flags, rf, ff, -1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
                Sting(board, flags, rf, ff, -1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
@@ -1254,7 +1339,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                SlideForward(board, flags, rf, ff, callback, closure);
                break;
 
                SlideForward(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI WhiteEagle:
+            case SHOGI WhiteDagger:
                Sting(board, flags, rf, ff, 1,  1, callback, closure);
                Sting(board, flags, rf, ff, 1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                Sting(board, flags, rf, ff, 1,  1, callback, closure);
                Sting(board, flags, rf, ff, 1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
@@ -1263,7 +1348,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                SlideDiagBackward(board, flags, rf, ff, callback, closure);
                break;
 
                SlideDiagBackward(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI BlackEagle:
+            case SHOGI BlackDagger:
                Sting(board, flags, rf, ff, -1,  1, callback, closure);
                Sting(board, flags, rf, ff, -1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                Sting(board, flags, rf, ff, -1,  1, callback, closure);
                Sting(board, flags, rf, ff, -1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
@@ -1323,7 +1408,7 @@ GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt
 
     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
 
 
     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
 
-    if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
+    if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
 
     if (!(flags & F_IGNORE_CHECK) ) {
       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
 
     if (!(flags & F_IGNORE_CHECK) ) {
       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
@@ -1579,11 +1664,11 @@ CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int r
     register CheckTestClosure *cl = (CheckTestClosure *) closure;
 
     if (rt == cl->rking && ft == cl->fking) {
     register CheckTestClosure *cl = (CheckTestClosure *) closure;
 
     if (rt == cl->rking && ft == cl->fking) {
-       if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
+       if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
        cl->check++;
        xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
     }
        cl->check++;
        xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
     }
-    if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
+    if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
        && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
        cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
 }
        && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
        cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
 }
@@ -1601,15 +1686,17 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
 {
     CheckTestClosure cl;
     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
 {
     CheckTestClosure cl;
     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
-    ChessSquare captured = EmptySquare, ep=0, trampled=0;
+    ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
+    int saveKill = killX;
     /*  Suppress warnings on uninitialized variables    */
 
     if(gameInfo.variant == VariantXiangqi)
         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
     if(gameInfo.variant == VariantKnightmate)
         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
     /*  Suppress warnings on uninitialized variables    */
 
     if(gameInfo.variant == VariantXiangqi)
         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
     if(gameInfo.variant == VariantKnightmate)
         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
-    if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
+    if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
        int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
        int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
+       if(gameInfo.variant == VariantShogi) prince -= 11;                   // White/BlackFalcon
        for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
            if(board[r][f] == k || board[r][f] == prince) {
                if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
        for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
            if(board[r][f] == k || board[r][f] == prince) {
                if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
@@ -1624,7 +1711,10 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
            board[rf][ft] = EmptySquare;
        } else {
            captured = board[rt][ft];
            board[rf][ft] = EmptySquare;
        } else {
            captured = board[rt][ft];
-           if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
+           if(killX >= 0) {
+               trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
+               if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
+           }
        }
        if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
            board[rt][ft] = board[rf][ff];
        }
        if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
            board[rt][ft] = board[rf][ff];
@@ -1632,10 +1722,11 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
        }
        ep = board[EP_STATUS];
        if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
        }
        ep = board[EP_STATUS];
        if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
-           ChessSquare victim = killX < 0 ? EmptySquare : trampled;
+           ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
            if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
                (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
            if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
                (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
-               (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
+               (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn    // no or worthless 'bridge'
+                                    || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
                     board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
        }
     }
                     board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
        }
     }
@@ -1672,7 +1763,10 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
            board[rf][ft] = captured;
            board[rt][ft] = EmptySquare;
        } else {
            board[rf][ft] = captured;
            board[rt][ft] = EmptySquare;
        } else {
-           if(killX >= 0) board[killY][killX] = trampled;
+           if(saveKill >= 0) {
+               if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
+               board[killY][killX = saveKill & 0xFFF] = trampled;
+           }
            board[rt][ft] = captured;
        }
        board[EP_STATUS] = ep;
            board[rt][ft] = captured;
        }
        board[EP_STATUS] = ep;
@@ -1748,7 +1842,7 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC
     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
     piece = filterPiece = board[rf][ff];
     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
     piece = filterPiece = board[rf][ff];
-    if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
+    if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
 
     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
     /* (perhaps we should disallow moves that obviously leave us in check?)              */
 
     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
     /* (perhaps we should disallow moves that obviously leave us in check?)              */
@@ -1788,13 +1882,17 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC
         if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
         if(promoChar != '+')
             return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
         if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
         if(promoChar != '+')
             return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
-        if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
+        if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
+           if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
+           return ImpossibleMove;
+       }
         return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
     } else
     if(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. '=' means defer */
         if(rf != DROP_RANK && cl.kind == NormalMove) {
             ChessSquare piece = board[rf][ff];
         return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
     } else
     if(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. '=' means defer */
         if(rf != DROP_RANK && cl.kind == NormalMove) {
             ChessSquare piece = board[rf][ff];
+            int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
 
             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
 
             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
@@ -1806,7 +1904,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
             else if(flags & F_WHITE_ON_MOVE) {
                 if( (int) piece < (int) WhiteWazir &&
                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
             else if(flags & F_WHITE_ON_MOVE) {
                 if( (int) piece < (int) WhiteWazir &&
-                     (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
+                     (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
@@ -1814,7 +1912,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
             } else {
                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
             } else {
-                if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
+                if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
@@ -1831,7 +1929,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
             // should test if in zone, really
             if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
                 return IllegalMove;
             // should test if in zone, really
             if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
                 return IllegalMove;
-            if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
+            if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
         } else
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
         } else
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
@@ -1944,6 +2042,7 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in
 {
     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
 {
     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
+    extern int kifu; // in parser.c
 
     // [HGM] wild: for wild-card pieces rt and rf are dummies
     if(piece == WhiteFalcon || piece == BlackFalcon ||
 
     // [HGM] wild: for wild-card pieces rt and rf are dummies
     if(piece == WhiteFalcon || piece == BlackFalcon ||
@@ -1952,14 +2051,26 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
          || PieceToChar(board[rf][ff]) == '~'
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
          || PieceToChar(board[rf][ff]) == '~'
-              && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
+              && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
                                                                       ) &&
        (cl->rfIn == -1 || cl->rfIn == rf) &&
        (cl->ffIn == -1 || cl->ffIn == ff) &&
        (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
        (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
 
                                                                       ) &&
        (cl->rfIn == -1 || cl->rfIn == rf) &&
        (cl->ffIn == -1 || cl->ffIn == ff) &&
        (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
        (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
 
-       if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
+       if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
+
+       if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
+           int this = 1, other = 1;
+           if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
+           if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
+           if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
+           if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
+           if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
+           if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
+           if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
+           if(!this) return;       // the current move does not satisfy the requested relative position, ignore it
+       }
 
        cl->count++;
        if(cl->count == 1 || board[rt][ft] != EmptySquare) {
 
        cl->count++;
        if(cl->count == 1 || board[rt][ft] != EmptySquare) {
@@ -2002,7 +2113,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
        if (closure->count == 0) {
            /* No, it's not even that */
         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
        if (closure->count == 0) {
            /* No, it's not even that */
-         if(!appData.testLegality && closure->pieceIn != EmptySquare) {
+         if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
            int f, r; // if there is only a single piece of the requested type on the board, use that
            closure->rt = closure->rtIn, closure->ft = closure->ftIn;
            for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
            int f, r; // if there is only a single piece of the requested type on the board, use that
            closure->rt = closure->rtIn, closure->ft = closure->ftIn;
            for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
@@ -2051,6 +2162,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
             ChessSquare piece = closure->piece;
         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
             ChessSquare piece = closure->piece;
+            int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
@@ -2058,7 +2170,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
             else if(flags & F_WHITE_ON_MOVE) {
                 if( (int) piece < (int) WhiteWazir &&
             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
             else if(flags & F_WHITE_ON_MOVE) {
                 if( (int) piece < (int) WhiteWazir &&
-                     (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
+                     (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
@@ -2066,7 +2178,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
             } else {
                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
             } else {
-                if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
+                if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
@@ -2081,8 +2193,10 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
-               gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
+               gameInfo.variant == VariantMakruk)
                 c = PieceToChar(BlackFerz);
                 c = PieceToChar(BlackFerz);
+            else if(gameInfo.variant == VariantASEAN)
+                c = PieceToChar(BlackRook);
             else if(gameInfo.variant == VariantGreat)
                 c = PieceToChar(BlackMan);
             else if(gameInfo.variant == VariantGrand)
             else if(gameInfo.variant == VariantGreat)
                 c = PieceToChar(BlackMan);
             else if(gameInfo.variant == VariantGrand)
@@ -2093,7 +2207,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
         else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
     } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
         ChessSquare p = closure->piece;
         else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
     } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
         ChessSquare p = closure->piece;
-        if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
+        if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
             closure->kind = ImpossibleMove; // used on non-promotable piece
         else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
     } else if (c != NULLCHAR) closure->kind = IllegalMove;
             closure->kind = ImpossibleMove; // used on non-promotable piece
         else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
     } else if (c != NULLCHAR) closure->kind = IllegalMove;
@@ -2138,7 +2252,7 @@ CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int f
     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
         (board[rf][ff] == cl->piece
          || PieceToChar(board[rf][ff]) == '~' &&
     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
         (board[rf][ff] == cl->piece
          || PieceToChar(board[rf][ff]) == '~' &&
-            (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
+            (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
                                      ) {
        if (rf == cl->rf) {
            if (ff == cl->ff) {
                                      ) {
        if (rf == cl->rf) {
            if (ff == cl->ff) {
@@ -2182,7 +2296,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
 
     if (promoChar == 'x') promoChar = NULLCHAR;
     piece = board[rf][ff];
 
     if (promoChar == 'x') promoChar = NULLCHAR;
     piece = board[rf][ff];
-    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
+    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
 
     switch (piece) {
       case WhitePawn:
 
     switch (piece) {
       case WhitePawn:
@@ -2274,13 +2388,13 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
        cl.kind = IllegalMove;
        cl.rank = cl.file = cl.either = 0;
         c = PieceToChar(piece) ;
        cl.kind = IllegalMove;
        cl.rank = cl.file = cl.either = 0;
         c = PieceToChar(piece) ;
-        GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
+        GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
 
        if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
            /* Generate pretty moves for moving into check, but
               still return IllegalMove.
            */
 
        if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
            /* Generate pretty moves for moving into check, but
               still return IllegalMove.
            */
-            GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
+            GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
            if (cl.kind == IllegalMove) break;
            cl.kind = IllegalMove;
        }
            if (cl.kind == IllegalMove) break;
            cl.kind = IllegalMove;
        }
@@ -2292,10 +2406,11 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
        */
         if( c == '~' || c == '+') {
            /* [HGM] print nonexistent piece as its demoted version */
        */
         if( c == '~' || c == '+') {
            /* [HGM] print nonexistent piece as its demoted version */
-           piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
+           piece = (ChessSquare) (CHUDEMOTED(piece));
         }
         if(c=='+') *outp++ = c;
         *outp++ = ToUpper(PieceToChar(piece));
         }
         if(c=='+') *outp++ = c;
         *outp++ = ToUpper(PieceToChar(piece));
+        if(*outp = PieceSuffix(piece)) outp++;
 
        if (cl.file || (cl.either && !cl.rank)) {
             *outp++ = ff + AAA;
 
        if (cl.file || (cl.either && !cl.rank)) {
             *outp++ = ff + AAA;
@@ -2349,7 +2464,10 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
        int r, f;
       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
                c += (board[r][f] == piece); // count on-board pieces of given type
        int r, f;
       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
                c += (board[r][f] == piece); // count on-board pieces of given type
-       *outp++ = ToUpper(PieceToChar(piece));
+        *outp = PieceToChar(piece);
+        if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
+        *outp++ = ToUpper(PieceToChar(piece));
+        if(*outp = PieceSuffix(piece)) outp++;
     }
   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
     *outp++ = ff + AAA;
     }
   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
     *outp++ = ff + AAA;