Fix deferral on sweep promotions
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index 5af3c47..6a747a2 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,23 +215,67 @@ 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.
 // [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
+// if the move has 1 or 2 legs. Only the marking of squares makes use of this info, by only marking
+// target squares of leg 1 (rejecting null move). A dummy move with MoveType 'FirstLeg' to the relay square
+// is generated, so a cyan marker can be put there, and other functions can ignore such a move. When the
+// user selects this square, it becomes the kill-square. Once a kill-square is set, only 2-leg moves are
+// generated that use that square as relay, plus 1-leg moves, so the 1-leg move that goes to the kill-square
+// can be marked during 2nd-leg entry to terminate the move there. For judging the pseudo-legality of the
+// 2nd leg, the from-square has to be considered empty, although the moving piece is still on it.
 
 Boolean pieceDefs;
 
 
 Boolean pieceDefs;
 
@@ -226,9 +284,14 @@ 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[]  = "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 };
 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 };
 //  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 dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 };
+int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 };
 
 int rot[][4] = { // rotation matrices for each direction
   { 1, 0, 0, 1 },
 
 int rot[][4] = { // rotation matrices for each direction
   { 1, 0, 0, 1 },
@@ -242,20 +305,34 @@ int rot[][4] = { // rotation matrices for each direction
 };
 
 void
 };
 
 void
-MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
+OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
 {
 {
-    char *p = desc;
-    int mine, his, dir, bit, occup, i;
+    (*(int*)cl)++;
+}
+
+void
+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, *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, retry=0, initial=0, jump=1;
-       if(*p == 'i') initial = 1, desc = ++p;
+       int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
+       char *cont = NULL;
+       while(*p == 'i') initial++, desc = ++p;
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
+       dx = xStep[*p-'A'] - '0';// step vector of atom
+       dy = yStep[*p-'A'] - '0';
        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)
+         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]) &&
                    while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
                        int b = dirs1[*desc-'a']; // use wide version
                        if( islower(desc[1]) &&
@@ -265,14 +342,20 @@ MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback
                        } else desc++;
                        dirSet |= b;
                    }
                        } else desc++;
                        dirSet |= b;
                    }
-                   dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
+                   dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
                    break;
          case 'R': expo = 0;    // rook, slide
                    break;
          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;
                    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;
                    break;
-         case 'N':              // oblique atom (degenerate 8-fold)
+         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
                    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
@@ -286,71 +369,155 @@ MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback
                    if(!dirSet) dirSet = 0xFF;
                    break;
          case 'Q': expo = 0;    // queen, slide
                    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
+         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
+                       else if(islower(desc[1]) && i < '4'
+                               && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
+                           b = dirs3[*desc-'a'] & dirs3[desc[1]-'a'];      // intersect wide & perp wide
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   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
+                        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
        }
-       if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
+       if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255;   // invert black moves
        mode = 0;                // build mode mask
        mode = 0;                // build mode mask
-       if(*desc == 'm') mode |= 4, desc++;
-       if(*desc == 'c') mode |= his, desc++;
-       if(*desc == 'd') mode |= mine, desc++;
-       if(*desc == 'e') mode |= 8, desc++;
-       if(!mode) mode = his + 4;// no mode spec, use default = mc
-       if(*desc == 'p') mode |= 32, desc++;
-       if(*desc == 'g') mode |= 64, desc++;
-       if(*desc == 'o') mode |= 128, desc++;
-       if(*desc == 'n') jump = 0, desc++;
-       while(*desc == 'j') jump++, desc++;
-       dx = xStep[*p-'A'] - '0';                     // step vector of atom
-       dy = yStep[*p-'A'] - '0';
+       if(*desc == 'm') mode |= 4, desc++;           // move to empty
+       if(*desc == 'c') mode |= his, desc++;         // capture foe
+       if(*desc == 'd') mode |= mine, desc++;        // destroy (capture friend)
+       if(*desc == 'e') mode |= 8, desc++;           // e.p. capture last mover
+       if(*desc == 't') mode |= 16, desc++;          // exclude enemies as hop platform ('test')
+       if(*desc == 'p') mode |= 32, desc++;          // hop over occupied
+       if(*desc == 'g') mode |= 64, desc++;          // hop and toggle range
+       if(*desc == 'o') mode |= 128, desc++;         // wrap around cylinder board
+       if(*desc == 'y') mode |= 512, desc++;         // toggle range on empty square
+       if(*desc == 'n') jump = 0, desc++;            // non-jumping
+       while(*desc == 'j') jump++, desc++;           // must jump (on B,R,Q: skip first square)
+       if(*desc == 'a') cont = ++desc;               // move again after doing what preceded it
        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
-           mode |= 16; dy = 1;
+                      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;
+       }
+       if(expo < 0) expo = 1;                        // use 1 for default
+        if(!cont) {
+           if(!(mode & 15)) mode |= his + 4;         // no mode spec, use default = mc
+       } 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'
+               *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 |= 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
         do {
          for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
-           int i = expo, 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 || 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];
            vx = dx*rot[dir][0] + dy*rot[dir][1];     // rotate step vector
            vy = dx*rot[dir][2] + dy*rot[dir][3];
-           x = f; y = r;                             // start square
-           do {
+           if(tx < 0) x = f, y = r;                  // start square
+           else      x = tx, y = ty;                 // from previous to-square if continuation
+           do {                                      // traverse ray
                x += vx; y += vy;                     // step to next square
                if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
                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(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(board[y][x] < BlackPawn)   occup = 1; else
-               if(board[y][x] < EmptySquare) occup = 2; else
+               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;
                                              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
+                   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
+                       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;
+                       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;
+                           MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
+                           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(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);
                }
                    cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
                }
-               if(mode & 16) {              // castling
+               if(mode & 1024) {            // castling
                    i = 2;                   // kludge to elongate move indefinitely
                    if(occup == 4) continue; // skip empty squares
                    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 = 0x99; // prepare for diagonal moves of K,Q
-       } while(retry--);             // and start doing them
+         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
     }
 } // next atom
 
     }
 } // next atom
 
@@ -452,15 +619,21 @@ 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;
   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]))
   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;
   ft += dx; rt += 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, NormalMove, rf, ff, rt, ft, closure);
   if (!SameColor(board[rf][ff], board[rt][ft]))
     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+  if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
+    callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+  legNr -= 2;
 }
 
 void
 }
 
 void
@@ -581,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, 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))
@@ -757,15 +930,18 @@ 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;
                if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI WhiteAxe:
+            case SHOGI BlackAxe:
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI (PROMOTED WhiteKnight):
+            case SHOGI (PROMO WhiteKnight):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI WhiteClaw:
             case SHOGI BlackDrunk:
             case SHOGI BlackAlfil:
                Ferz(board, flags, rf, ff, callback, closure);
             case SHOGI BlackDrunk:
             case SHOGI BlackAlfil:
                Ferz(board, flags, rf, ff, callback, closure);
@@ -773,8 +949,9 @@ 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;
                if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI BlackClaw:
             case SHOGI WhiteDrunk:
             case SHOGI WhiteAlfil:
                Ferz(board, flags, rf, ff, callback, closure);
             case SHOGI WhiteDrunk:
             case SHOGI WhiteAlfil:
                Ferz(board, flags, rf, ff, callback, closure);
@@ -783,15 +960,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:
@@ -799,7 +976,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:
@@ -928,19 +1105,16 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
            case WhiteDragon:
            case BlackDragon:
             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
            case WhiteDragon:
            case BlackDragon:
-              if(gameInfo.variant == VariantChuChess) goto DragonKing;
+              if(gameInfo.variant == VariantChuChess || gameInfo.variant == VariantSpartan) goto DragonKing;
               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
                 for (s = -2; s <= 2; s += 4) {
                      rt = rf + s * d;
                      ft = ff + s * (1 - d);
                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
                 for (s = -2; s <= 2; s += 4) {
                      rt = rf + s * d;
                      ft = ff + s * (1 - d);
                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
-                      if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
+                      if (board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
                      if (SameColor(board[rf][ff], board[rt][ft])) continue;
                      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
                  }
                      if (SameColor(board[rf][ff], board[rt][ft])) continue;
                      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
                  }
-              if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
-               Wazir(board, flags, rf, ff, callback, closure);
-             else
                Rook(board, flags, rf, ff, callback, closure);
               break;
 
                Rook(board, flags, rf, ff, callback, closure);
               break;
 
@@ -965,6 +1139,19 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                    Knight(board, flags, rf, ff, callback, closure);
                break;
 
                    Knight(board, flags, rf, ff, callback, closure);
                break;
 
+            case WhiteTower:
+            case BlackTower:
+                for (d = 0; d <= 1; d++) // Dababba moves
+                  for (s = -2; s <= 2; s += 4) {
+                     rt = rf + s * d;
+                     ft = ff + s * (1 - d);
+                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+                     if (SameColor(board[rf][ff], board[rt][ft])) continue;
+                     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+                 }
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
+
             /* Shogi Rooks are ordinary Rooks */
             case SHOGI WhiteRook:
             case SHOGI BlackRook:
             /* Shogi Rooks are ordinary Rooks */
             case SHOGI WhiteRook:
             case SHOGI BlackRook:
@@ -1006,6 +1193,12 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
 
             case WhiteFerz:
             case BlackFerz:
 
             case WhiteFerz:
             case BlackFerz:
+               if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
+                   int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
+                   int ft = BOARD_WIDTH>>1;
+                   if(!SameColor(board[rf][ff], board[rt][ft]))
+                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+               } else
                 /* [HGM] support Shatranj pieces */
                Ferz(board, flags, rf, ff, callback, closure);
                break;
                 /* [HGM] support Shatranj pieces */
                Ferz(board, flags, rf, ff, callback, closure);
                break;
@@ -1090,13 +1283,15 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
               for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
                 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;
               for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
                 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;
-                callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
+                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 && !i ? FirstLeg : NormalMove,
                          rf, ff, rt, ft, closure);
                          rf, ff, rt, ft, closure);
+                legNr -= 2*i;
               }
               break;
 
               }
               break;
 
-            case SHOGI WhiteFalcon:
-            case SHOGI BlackFalcon:
+            case SHOGI WhiteDagger:
+            case SHOGI BlackDagger:
             case SHOGI WhitePDagger:
             case SHOGI BlackPDagger:
                SlideSideways(board, flags, rf, ff, callback, closure);
             case SHOGI WhitePDagger:
             case SHOGI BlackPDagger:
                SlideSideways(board, flags, rf, ff, callback, closure);
@@ -1108,28 +1303,30 @@ 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;
                if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI WhiteSword:
+            case SHOGI BlackSword:
             case SHOGI WhitePSword:
             case SHOGI BlackPSword:
                SlideVertical(board, flags, rf, ff, callback, closure);
                StepSideways(board, flags, rf, ff, callback, closure);
                break;
 
             case SHOGI WhitePSword:
             case SHOGI BlackPSword:
                SlideVertical(board, flags, rf, ff, callback, closure);
                StepSideways(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI WhiteUnicorn:
-            case SHOGI BlackUnicorn:
+            case SHOGI WhiteCat:
+            case SHOGI BlackCat:
                Ferz(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
                Ferz(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI WhiteMan:
+            case SHOGI WhiteCopper:
                StepDiagForward(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
                StepDiagForward(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
 
-            case SHOGI BlackMan:
+            case SHOGI BlackCopper:
                StepDiagBackward(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
                StepDiagBackward(board, flags, rf, ff, callback, closure);
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
@@ -1146,7 +1343,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 WhiteUnicorn:
                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;
@@ -1155,7 +1352,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 BlackUnicorn:
                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;
@@ -1164,7 +1361,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 WhiteFalcon:
                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);
@@ -1173,7 +1370,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 BlackFalcon:
                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);
@@ -1233,7 +1430,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);
@@ -1489,11 +1686,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
 }
@@ -1511,15 +1708,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!)
@@ -1534,18 +1733,22 @@ 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];
-           board[rf][ff] = EmptySquare;
+           if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
        }
        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
        }
     }
@@ -1582,7 +1785,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;
@@ -1658,12 +1864,12 @@ 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?)              */
     if((piece == WhiteFalcon || piece == BlackFalcon ||
 
     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
     /* (perhaps we should disallow moves that obviously leave us in check?)              */
     if((piece == WhiteFalcon || piece == BlackFalcon ||
-        piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu)
+        piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
@@ -1698,13 +1904,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)   ||
@@ -1716,7 +1926,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;
@@ -1724,7 +1934,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;
@@ -1741,7 +1951,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) {
@@ -1854,22 +2064,35 @@ 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 ||
        piece == WhiteCobra  || piece == BlackCobra)
 
     // [HGM] wild: for wild-card pieces rt and rf are dummies
     if(piece == WhiteFalcon || piece == BlackFalcon ||
        piece == WhiteCobra  || piece == BlackCobra)
-        wildCard = TRUE;
+        wildCard = !pieceDefs; // no wildcards when engine defined pieces
 
     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) {
@@ -1912,7 +2135,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++)
@@ -1930,7 +2153,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
            return;
          }
        }
            return;
          }
        }
-    } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
+    } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
        DisambiguateClosure spare = *closure;
        pieceDefs = FALSE; spare.count = 0;     // See if the (erroneous) built-in rules would resolve that
         GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
        DisambiguateClosure spare = *closure;
        pieceDefs = FALSE; spare.count = 0;     // See if the (erroneous) built-in rules would resolve that
         GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
@@ -1961,6 +2184,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) )
@@ -1968,7 +2192,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;
@@ -1976,7 +2200,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;
@@ -1991,8 +2215,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)
@@ -2003,7 +2229,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;
@@ -2048,7 +2274,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) {
@@ -2092,7 +2318,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:
@@ -2184,13 +2410,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;
        }
@@ -2202,10 +2428,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;
@@ -2259,7 +2486,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;