Improve SAN of Pawn moves and allow Betza e.p. definition
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index 12eed07..9700dbf 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -54,6 +54,8 @@
 #include "config.h"
 
 #include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
 #if HAVE_STRING_H
 # include <string.h>
 #else /* not HAVE_STRING_H */
@@ -71,22 +73,23 @@ int PosFlags(int index);
 
 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
 int quickFlag;
+char *pieceDesc[EmptySquare];
 
-int WhitePiece(piece)
-     ChessSquare piece;
+int
+WhitePiece (ChessSquare piece)
 {
     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
 }
 
-int BlackPiece(piece)
-     ChessSquare piece;
+int
+BlackPiece (ChessSquare piece)
 {
     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
 }
 
 #if 0
-int SameColor(piece1, piece2)
-     ChessSquare piece1, piece2;
+int
+SameColor (ChessSquare piece1, ChessSquare piece2)
 {
     return ((int) piece1 >= (int) WhitePawn &&   /* [HGM] can be > King ! */
             (int) piece1 <  (int) BlackPawn &&
@@ -98,7 +101,7 @@ int SameColor(piece1, piece2)
             (int) piece2 <  (int) EmptySquare);
 }
 #else
-#define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn))
+#define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
 #endif
 
 char pieceToChar[] = {
@@ -109,15 +112,15 @@ char pieceToChar[] = {
                         'x' };
 char pieceNickName[EmptySquare];
 
-char PieceToChar(p)
-     ChessSquare p;
+char
+PieceToChar (ChessSquare p)
 {
     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
     return pieceToChar[(int) p];
 }
 
-int PieceToNumber(p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
-     ChessSquare p;
+int
+PieceToNumber (ChessSquare p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
 {
     int i=0;
     ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
@@ -126,8 +129,8 @@ int PieceToNumber(p)  /* [HGM] holdings: count piece type, ignoring non-particip
     return i;
 }
 
-ChessSquare CharToPiece(c)
-     int c;
+ChessSquare
+CharToPiece (int c)
 {
      int i;
      if(c == '.') return EmptySquare;
@@ -138,21 +141,22 @@ ChessSquare CharToPiece(c)
      return EmptySquare;
 }
 
-void CopyBoard(to, from)
-     Board to, from;
+void
+CopyBoard (Board to, Board from)
 {
     int i, j;
 
     for (i = 0; i < BOARD_HEIGHT; i++)
       for (j = 0; j < BOARD_WIDTH; j++)
        to[i][j] = from[i][j];
-    for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
+    for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
+       to[VIRGIN][j] = from[VIRGIN][j],
        to[CASTLING][j] = from[CASTLING][j];
     to[HOLDINGS_SET] = 0; // flag used in ICS play
 }
 
-int CompareBoards(board1, board2)
-     Board board1, board2;
+int
+CompareBoards (Board board1, Board board2)
 {
     int i, j;
 
@@ -164,6 +168,322 @@ int CompareBoards(board1, board2)
     return TRUE;
 }
 
+// [HGM] gen: configurable move generation from Betza notation sent by engine.
+
+//  alphabet      "abcdefghijklmnopqrstuvwxyz"
+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";
+//  alphabet   "a b    c d e f    g h    i j k l    m n o p q r    s    t u v    w x y z "
+int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0,   0,0,0,0xF0,0,0,0,0,0,0x0F,0   ,0,0,0   ,0,0,0,0 };
+int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 };
+
+int rot[][4] = { // rotation matrices for each direction
+  { 1, 0, 0, 1 },
+  { 0, 1, 1, 0 },
+  { 0, 1,-1, 0 },
+  { 1, 0, 0,-1 },
+  {-1, 0, 0,-1 },
+  { 0,-1,-1, 0 },
+  { 0,-1, 1, 0 },
+  {-1, 0, 0, 1 }
+};
+
+void
+MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
+{
+    char *p = desc;
+    int mine, his, dir, bit, occup, i;
+    if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
+    while(*p) {                  // more moves to go
+       int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1;
+       if(*p == 'i') initial = 1, desc = ++p;
+       while(islower(*p)) p++;  // skip prefixes
+       if(!isupper(*p)) return; // syntax error: no atom
+       dirSet = 0;              // build direction set based on atom symmetry
+       switch(symmetry[*p-'A']) {
+         case 'B': expo = 0;    // bishop, slide
+         case 'F':              // diagonal atom (degenerate 4-fold)
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs1[*desc-'a']; // use wide version
+                       if( islower(desc[1]) &&
+                                ((i | dirType[desc[1]-'a']) & 3) == 3) {   // combinable (perpendicular dim)
+                           b = dirs1[*desc-'a'] & dirs1[desc[1]-'a'];      // intersect wide & perp wide
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
+                   break;
+         case 'R': expo = 0;    // rook, slide
+         case 'W':              // orthogonal atom (non-deg 4-fold)
+                   while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
+                   dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+                   break;
+         case 'N':              // oblique atom (degenerate 8-fold)
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs2[*desc-'a']; // when alone, use narrow version
+                       if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
+                       else if(islower(desc[1]) && i < '4'
+                               && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
+                           b = dirs1[*desc-'a'] & dirs2[desc[1]-'a'];      // intersect wide & perp narrow
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   if(!dirSet) dirSet = 0xFF;
+                   break;
+         case 'Q': expo = 0;    // queen, slide
+         case 'K':              // non-deg (pseudo) 8-fold
+                   dirSet=0x55; // start with orthogonal moves
+                   retry = 1;   // and schedule the diagonal moves for later
+                   break;       // should not have direction indicators
+         default:  return;      // syntax error: invalid atom
+       }
+       if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
+       mode = 0;                // build mode mask
+       if(*desc == 'm') mode |= 4, desc++;
+       if(*desc == 'c') mode |= his, desc++;
+       if(*desc == 'd') mode |= mine, desc++;
+       if(*desc == 'e') mode |= 8, desc++;
+       if(*desc == 'n') jump = 0, desc++;
+       while(*desc == 'j') jump++, desc++;
+       if(!mode) mode = his + 4;// no mode spec, use default = mc
+       dx = xStep[*p-'A'] - '0';                     // step vector of atom
+       dy = yStep[*p-'A'] - '0';
+       if(isdigit(*++p)) expo = atoi(p++);           // read exponent
+       if(expo > 9) p++;                             // allow double-digit
+       desc = p;                                     // this is start of next move
+       if(initial && board[r][f] != initialPosition[r][f]) continue;
+        do {
+         for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
+           int i = expo, vx, vy;
+           if(!(bit & dirSet)) continue;             // does not move in this direction
+           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 {
+               x += vx; y += vy;                     // step to next square
+               if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
+               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
+                                             occup = 4;
+               if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
+                   cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
+               }
+               if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
+               if(occup != 4) break; // not valid transit square
+           } while(--i);
+         }
+         dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
+       } while(retry--);             // and start doing them
+    }
+} // next atom
+
+// [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
+
+void
+SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int i, rt, ft = ff;
+  for (i = 1;; i++) {
+      rt = rf + i;
+      if (rt >= BOARD_HEIGHT) break;
+      if (SameColor(board[rf][ff], board[rt][ft])) break;
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+      if (board[rt][ft] != EmptySquare) break;
+  }
+}
+
+void
+SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int i, rt, ft = ff;
+  for (i = 1;; i++) {
+      rt = rf - i;
+      if (rt < 0) break;
+      if (SameColor(board[rf][ff], board[rt][ft])) break;
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+      if (board[rt][ft] != EmptySquare) break;
+  }
+}
+
+void
+SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  SlideForward(board, flags, rf, ff, callback, closure);
+  SlideBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int i, s, rt = rf, ft;
+  for(s = -1; s <= 1; s+= 2) {
+    for (i = 1;; i++) {
+      ft = ff + i*s;
+      if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+      if (SameColor(board[rf][ff], board[rt][ft])) break;
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+      if (board[rt][ft] != EmptySquare) break;
+    }
+  }
+}
+
+void
+SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int i, s, rt, ft;
+  for(s = -1; s <= 1; s+= 2) {
+    for (i = 1;; i++) {
+      rt = rf + i;
+      ft = ff + i * s;
+      if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+      if (SameColor(board[rf][ff], board[rt][ft])) break;
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+      if (board[rt][ft] != EmptySquare) break;
+    }
+  }
+}
+
+void
+SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int i, s, rt, ft;
+  for(s = -1; s <= 1; s+= 2) {
+    for (i = 1;; i++) {
+      rt = rf - i;
+      ft = ff + i * s;
+      if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+      if (SameColor(board[rf][ff], board[rt][ft])) break;
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+      if (board[rt][ft] != EmptySquare) break;
+    }
+  }
+}
+
+void
+Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  SlideVertical(board, flags, rf, ff, callback, closure);
+  SlideSideways(board, flags, rf, ff, callback, closure);
+}
+
+void
+Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  SlideDiagForward(board, flags, rf, ff, callback, closure);
+  SlideDiagBackward(board, flags, rf, ff, callback, 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
+  int ft = ff + dx, rt = rf + dy;
+  if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+  if (!SameColor(board[rf][ff], board[rt][ft]))
+    callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
+  ft += dx; rt += dy;
+  if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+  if (!SameColor(board[rf][ff], board[rt][ft]))
+    callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int ft = ff, rt = rf + 1;
+  if (rt >= BOARD_HEIGHT) return;
+  if (SameColor(board[rf][ff], board[rt][ft])) return;
+  callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int ft = ff, rt = rf - 1;
+  if (rt < 0) return;
+  if (SameColor(board[rf][ff], board[rt][ft])) return;
+  callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int ft, rt = rf;
+  ft = ff + 1;
+  if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+  ft = ff - 1;
+  if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int ft, rt = rf + 1;
+  if (rt >= BOARD_HEIGHT) return;
+  ft = ff + 1;
+  if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+  ft = ff - 1;
+  if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  int ft, rt = rf - 1;
+  if(rt < 0) return;
+  ft = ff + 1;
+  if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+  ft = ff - 1;
+  if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
+      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+}
+
+void
+StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  StepForward(board, flags, rf, ff, callback, closure);
+  StepBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  StepDiagForward(board, flags, rf, ff, callback, closure);
+  StepDiagBackward(board, flags, rf, ff, callback, closure);
+}
+
+void
+Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+  StepVertical(board, flags, rf, ff, callback, closure);
+  StepSideways(board, flags, rf, ff, callback, closure);
+}
+
+void
+Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+    int i, j, s, rt, ft;
+    for (i = -1; i <= 1; i += 2)
+       for (j = -1; j <= 1; j += 2)
+           for (s = 1; s <= 2; s++) {
+               rt = rf + i*s;
+               ft = ff + j*(3-s);
+               if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
+                   && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
+                   && !SameColor(board[rf][ff], board[rt][ft]))
+                   callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+           }
+}
 
 /* Call callback once for each pseudo-legal move in the given
    position, except castling moves. A move is pseudo-legal if it is
@@ -173,31 +493,27 @@ int CompareBoards(board1, board2)
    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
    Promotion moves generated are to Queen only.
 */
-void GenPseudoLegal(board, flags, callback, closure, filter)
-     Board board;
-     int flags;
-     MoveCallback callback;
-     VOIDSTAR closure;
-     ChessSquare filter; // [HGM] speed: only do moves with this piece type
+void
+GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
+// speed: only do moves with this piece type
 {
     int rf, ff;
     int i, j, d, s, fs, rs, rt, ft, m;
     int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
-    int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
+    int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
 
     for (rf = 0; rf < BOARD_HEIGHT; rf++)
       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
           ChessSquare piece;
-          int rookRange;
 
          if(board[rf][ff] == EmptySquare) continue;
          if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
-         rookRange = 1000;
           m = 0; piece = board[rf][ff];
           if(PieceToChar(piece) == '~')
                  piece = (ChessSquare) ( DEMOTED piece );
           if(filter != EmptySquare && piece != filter) continue;
-          if(gameInfo.variant == VariantShogi)
+          if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
+          if(IS_SHOGI(gameInfo.variant))
                  piece = (ChessSquare) ( SHOGI piece );
 
           switch ((int)piece) {
@@ -311,7 +627,6 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
             case BlackUnicorn:
            case WhiteKnight:
            case BlackKnight:
-            mounted:
              for (i = -1; i <= 1; i += 2)
                for (j = -1; j <= 1; j += 2)
                  for (s = 1; s <= 2; s++) {
@@ -368,47 +683,77 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
 
             /* Gold General (and all its promoted versions) . First do the */
             /* diagonal forward steps, then proceed as normal Wazir        */
-            case SHOGI WhiteWazir:
             case SHOGI (PROMOTED WhitePawn):
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI (PROMOTED BlackPawn):
+               if(gameInfo.variant == VariantShogi) goto BlackGold;
+               SlideVertical(board, flags, rf, ff, callback, closure);
+               break;
+
             case SHOGI (PROMOTED WhiteKnight):
-            case SHOGI (PROMOTED WhiteQueen):
-            case SHOGI (PROMOTED WhiteFerz):
-             for (s = -1; s <= 1; s += 2) {
-                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                      !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
-                      callback(board, flags, NormalMove,
-                              rf, ff, rf + 1, ff + s, closure);
-                 }
-              }
-              goto finishGold;
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI BlackDrunk:
+            case SHOGI BlackAlfil:
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepSideways(board, flags, rf, ff, callback, closure);
+               StepBackward(board, flags, rf, ff, callback, closure);
+               break;
 
-            case SHOGI BlackWazir:
-            case SHOGI (PROMOTED BlackPawn):
             case SHOGI (PROMOTED BlackKnight):
+               if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI WhiteDrunk:
+            case SHOGI WhiteAlfil:
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepSideways(board, flags, rf, ff, callback, closure);
+               StepForward(board, flags, rf, ff, callback, closure);
+               break;
+
+
+            case SHOGI WhiteStag:
+            case SHOGI BlackStag:
+               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 WhiteTokin:
+            case SHOGI WhiteWazir:
+           WhiteGold:
+               StepDiagForward(board, flags, rf, ff, callback, closure);
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
+
             case SHOGI (PROMOTED BlackQueen):
-            case SHOGI (PROMOTED BlackFerz):
-             for (s = -1; s <= 1; s += 2) {
-                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                      !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
-                      callback(board, flags, NormalMove,
-                              rf, ff, rf - 1, ff + s, closure);
-                 }
-             }
+            case SHOGI BlackTokin:
+            case SHOGI BlackWazir:
+            BlackGold:
+               StepDiagBackward(board, flags, rf, ff, callback, closure);
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
 
             case WhiteWazir:
             case BlackWazir:
-            finishGold:
-              for (d = 0; d <= 1; d++)
-                for (s = -1; s <= 1; s += 2) {
-                      rt = rf + s * d;
-                      ft = ff + s * (1 - d);
-                      if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
-                          && !SameColor(board[rf][ff], board[rt][ft]) &&
-                          (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
-                               callback(board, flags, NormalMove,
-                                        rf, ff, rt, ft, closure);
-                      }
-             break;
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteMarshall:
+            case SHOGI BlackMarshall:
+               Ferz(board, flags, rf, ff, callback, closure);
+               for (d = 0; d <= 1; d++)
+                   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]) )
+                           callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+                   }
+               break;
+
+            case SHOGI WhiteAngel:
+            case SHOGI BlackAngel:
+               Wazir(board, flags, rf, ff, callback, closure);
 
             case WhiteAlfil:
             case BlackAlfil:
@@ -424,8 +769,8 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                           && !SameColor(board[rf][ff], board[rt][ft]))
                                callback(board, flags, NormalMove,
                                         rf, ff, rt, ft, closure);
-                      if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier
-                                                             || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
+                      if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+                         gameInfo.variant == VariantChu      || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
                       ft = ff + fs;
                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
@@ -444,6 +789,7 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
            case WhiteCardinal:
            case BlackCardinal:
+              if(gameInfo.variant == VariantChuChess) goto DragonHorse;
               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
                 for (s = -2; s <= 2; s += 4) {
                      rt = rf + s * d;
@@ -456,36 +802,31 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
             case SHOGI WhiteCardinal:
             case SHOGI BlackCardinal:
-              m++;
+            case SHOGI WhitePCardinal:
+            case SHOGI BlackPCardinal:
+            DragonHorse:
+               Bishop(board, flags, rf, ff, callback, closure);
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
 
             /* Capablanca Archbishop continues as Knight                  */
             case WhiteAngel:
             case BlackAngel:
-              m++;
+               Knight(board, flags, rf, ff, callback, closure);
 
             /* Shogi Bishops are ordinary Bishops */
             case SHOGI WhiteBishop:
             case SHOGI BlackBishop:
+            case SHOGI WhitePBishop:
+            case SHOGI BlackPBishop:
            case WhiteBishop:
            case BlackBishop:
-             for (rs = -1; rs <= 1; rs += 2)
-                for (fs = -1; fs <= 1; fs += 2)
-                 for (i = 1;; i++) {
-                     rt = rf + (i * rs);
-                     ft = ff + (i * fs);
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;
-                     callback(board, flags, NormalMove,
-                              rf, ff, rt, ft, closure);
-                     if (board[rt][ft] != EmptySquare) break;
-                 }
-                if(m==1) goto mounted;
-                if(m==2) goto finishGold;
-                /* Bishop falls through */
-             break;
+               Bishop(board, flags, rf, ff, callback, closure);
+               break;
 
             /* Shogi Lance is unlike anything, and asymmetric at that */
             case SHOGI WhiteQueen:
+              if(gameInfo.variant == VariantChu) goto doQueen;
               for(i = 1;; i++) {
                       rt = rf + i;
                       ft = ff;
@@ -498,6 +839,7 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
               break;
 
             case SHOGI BlackQueen:
+              if(gameInfo.variant == VariantChu) goto doQueen;
               for(i = 1;; i++) {
                       rt = rf - i;
                       ft = ff;
@@ -512,126 +854,102 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
            case WhiteDragon:
            case BlackDragon:
+              if(gameInfo.variant == VariantChuChess) 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 || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
+                      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 (SameColor(board[rf][ff], board[rt][ft])) continue;
                      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
                  }
-              if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
-              goto doRook;
-              
+              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;
+
             /* Shogi Dragon King has to continue as Ferz after Rook moves */
             case SHOGI WhiteDragon:
             case SHOGI BlackDragon:
+            case SHOGI WhitePDragon:
+            case SHOGI BlackPDragon:
+            DragonKing:
+               Rook(board, flags, rf, ff, callback, closure);
+               Ferz(board, flags, rf, ff, callback, closure);
+               break;
               m++;
 
             /* Capablanca Chancellor sets flag to continue as Knight      */
             case WhiteMarshall:
             case BlackMarshall:
-              m++;
-              m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
+               Rook(board, flags, rf, ff, callback, closure);
+               if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
+                   Ferz(board, flags, rf, ff, callback, closure);
+               else
+                   Knight(board, flags, rf, ff, callback, closure);
+               break;
 
             /* Shogi Rooks are ordinary Rooks */
             case SHOGI WhiteRook:
             case SHOGI BlackRook:
+            case SHOGI WhitePRook:
+            case SHOGI BlackPRook:
            case WhiteRook:
            case BlackRook:
-          doRook:
-              for (d = 0; d <= 1; d++)
-                for (s = -1; s <= 1; s += 2)
-                 for (i = 1;; i++) {
-                     rt = rf + (i * s) * d;
-                     ft = ff + (i * s) * (1 - d);
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;
-                     callback(board, flags, NormalMove,
-                              rf, ff, rt, ft, closure);
-                     if (board[rt][ft] != EmptySquare || i == rookRange) break;
-                 }
-                if(m==1) goto mounted;
-                if(m==2) goto finishSilver;
-             break;
+               Rook(board, flags, rf, ff, callback, closure);
+               break;
 
            case WhiteQueen:
            case BlackQueen:
-             for (rs = -1; rs <= 1; rs++)
-               for (fs = -1; fs <= 1; fs++) {
-                   if (rs == 0 && fs == 0) continue;
-                   for (i = 1;; i++) {
-                       rt = rf + (i * rs);
-                       ft = ff + (i * fs);
-                        if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
-                       if (SameColor(board[rf][ff], board[rt][ft])) break;
-                       callback(board, flags, NormalMove,
-                                rf, ff, rt, ft, closure);
-                       if (board[rt][ft] != EmptySquare) break;
-                   }
-               }
-             break;
+            case SHOGI WhiteMother:
+            case SHOGI BlackMother:
+           doQueen:
+               Rook(board, flags, rf, ff, callback, closure);
+               Bishop(board, flags, rf, ff, callback, closure);
+               break;
+
+           case SHOGI WhitePawn:
+               StepForward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackPawn:
+               StepBackward(board, flags, rf, ff, callback, closure);
+               break;
 
-            /* Shogi Pawn and Silver General: first the Pawn move,    */
-            /* then the General continues like a Ferz                 */
             case WhiteMan:
-                if(gameInfo.variant != VariantMakruk) goto commoner;
-            case SHOGI WhitePawn:
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
             case SHOGI WhiteFerz:
-                  if (rf < BOARD_HEIGHT-1 &&
-                           !SameColor(board[rf][ff], board[rf + 1][ff]) )
-                           callback(board, flags, NormalMove,
-                                    rf, ff, rf + 1, ff, closure);
-              if(piece != SHOGI WhitePawn) goto finishSilver;
-              break;
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepForward(board, flags, rf, ff, callback, closure);
+               break;
 
             case BlackMan:
-                if(gameInfo.variant != VariantMakruk) goto commoner;
-            case SHOGI BlackPawn:
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
             case SHOGI BlackFerz:
-                  if (rf > 0 &&
-                           !SameColor(board[rf][ff], board[rf - 1][ff]) )
-                           callback(board, flags, NormalMove,
-                                    rf, ff, rf - 1, ff, closure);
-              if(piece == SHOGI BlackPawn) break;
+               StepBackward(board, flags, rf, ff, callback, closure);
 
             case WhiteFerz:
             case BlackFerz:
-            finishSilver:
                 /* [HGM] support Shatranj pieces */
-                for (rs = -1; rs <= 1; rs += 2)
-                  for (fs = -1; fs <= 1; fs += 2) {
-                      rt = rf + rs;
-                      ft = ff + fs;
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
-                      if (!SameColor(board[rf][ff], board[rt][ft]) &&
-                          (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
-                               callback(board, flags, NormalMove,
-                                        rf, ff, rt, ft, closure);
-                 }
-                break;
+               Ferz(board, flags, rf, ff, callback, closure);
+               break;
 
            case WhiteSilver:
            case BlackSilver:
-               m++; // [HGM] superchess: use for Centaur
+               Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
+
             commoner:
+            case SHOGI WhiteMonarch:
+            case SHOGI BlackMonarch:
             case SHOGI WhiteKing:
             case SHOGI BlackKing:
            case WhiteKing:
            case BlackKing:
-//            walking:
-             for (i = -1; i <= 1; i++)
-               for (j = -1; j <= 1; j++) {
-                   if (i == 0 && j == 0) continue;
-                   rt = rf + i;
-                   ft = ff + j;
-                    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);
-               }
-               if(m==1) goto mounted;
-             break;
+               Ferz(board, flags, rf, ff, callback, closure);
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
 
            case WhiteNightrider:
            case BlackNightrider:
@@ -651,20 +969,10 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
              break;
 
            Amazon:
-             /* First do Bishop,then continue like Chancellor */
-             for (rs = -1; rs <= 1; rs += 2)
-                for (fs = -1; fs <= 1; fs += 2)
-                 for (i = 1;; i++) {
-                     rt = rf + (i * rs);
-                     ft = ff + (i * fs);
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;
-                     callback(board, flags, NormalMove,
-                              rf, ff, rt, ft, closure);
-                     if (board[rt][ft] != EmptySquare) break;
-                 }
-             m++;
-             goto doRook;
+               Bishop(board, flags, rf, ff, callback, closure);
+               Rook(board, flags, rf, ff, callback, closure);
+               Knight(board, flags, rf, ff, callback, closure);
+               break;
 
            // Use Lance as Berolina / Spartan Pawn.
            case WhiteLance:
@@ -675,14 +983,14 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                           rf, ff, rf + 1, ff, closure);
              for (s = -1; s <= 1; s += 2) {
                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
-                     callback(board, flags, 
+                     callback(board, flags,
                               rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
                               rf, ff, rf + 1, ff + s, closure);
                  if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
                      callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
              }
              break;
-               
+
            case BlackLance:
              if(gameInfo.variant == VariantSuper) goto Amazon;
              if (rf > 0 && WhitePiece(board[rf - 1][ff]))
@@ -691,7 +999,7 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                           rf, ff, rf - 1, ff, closure);
              for (s = -1; s <= 1; s += 2) {
                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
-                     callback(board, flags, 
+                     callback(board, flags,
                               rf <= promoRank ? BlackPromotion : NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
@@ -699,6 +1007,127 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
              }
             break;
 
+            case SHOGI WhiteNothing:
+            case SHOGI BlackNothing:
+            case SHOGI WhiteLion:
+            case SHOGI BlackLion:
+            case WhiteLion:
+            case BlackLion:
+              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,
+                         rf, ff, rt, ft, closure);
+              }
+              break;
+
+            case SHOGI WhiteFalcon:
+            case SHOGI BlackFalcon:
+            case SHOGI WhitePDagger:
+            case SHOGI BlackPDagger:
+               SlideSideways(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteCobra:
+            case SHOGI BlackCobra:
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI (PROMOTED WhiteFerz):
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI (PROMOTED BlackFerz):
+               if(gameInfo.variant == VariantShogi) goto BlackGold;
+            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:
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteMan:
+               StepDiagForward(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackMan:
+               StepDiagBackward(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteHCrown:
+            case SHOGI BlackHCrown:
+               Bishop(board, flags, rf, ff, callback, closure);
+               SlideSideways(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteCrown:
+            case SHOGI BlackCrown:
+               Bishop(board, flags, rf, ff, callback, closure);
+               SlideVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteHorned:
+               Sting(board, flags, rf, ff, 1, 0, callback, closure);
+               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+               if(killX >= 0) break;
+               Bishop(board, flags, rf, ff, callback, closure);
+               SlideSideways(board, flags, rf, ff, callback, closure);
+               SlideBackward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackHorned:
+               Sting(board, flags, rf, ff, -1, 0, callback, closure);
+               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+               if(killX >= 0) break;
+               Bishop(board, flags, rf, ff, callback, closure);
+               SlideSideways(board, flags, rf, ff, callback, closure);
+               SlideForward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteEagle:
+               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);
+               if(killX >= 0) break;
+               Rook(board, flags, rf, ff, callback, closure);
+               SlideDiagBackward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackEagle:
+               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);
+               if(killX >= 0) break;
+               Rook(board, flags, rf, ff, callback, closure);
+               SlideDiagForward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteDolphin:
+            case SHOGI BlackHorse:
+               SlideDiagBackward(board, flags, rf, ff, callback, closure);
+               SlideVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackDolphin:
+            case SHOGI WhiteHorse:
+               SlideDiagForward(board, flags, rf, ff, callback, closure);
+               SlideVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteLance:
+               SlideForward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackLance:
+               SlideBackward(board, flags, rf, ff, callback, closure);
+               break;
+
            case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
            case BlackFalcon:
            case WhiteCobra:
@@ -723,17 +1152,15 @@ extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register GenLegalClosure *cl = (GenLegalClosure *) closure;
 
     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 (!(flags & F_IGNORE_CHECK) ) {
       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
       if(promo) {
@@ -783,12 +1210,8 @@ typedef struct {
    true if castling is not yet ruled out by a move of the king or
    rook.  Return TRUE if the player on move is currently in check and
    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
-int GenLegal(board, flags, callback, closure, filter)
-     Board board;
-     int flags;
-     MoveCallback callback;
-     VOIDSTAR closure;
-     ChessSquare filter;
+int
+GenLegal (Board board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
 {
     GenLegalClosure cl;
     int ff, ft, k, left, right, swap;
@@ -912,8 +1335,10 @@ int GenLegal(board, flags, callback, closure, filter)
                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
-            if(ft != NoRights && board[0][ft] == WhiteRook)
-                callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+            if(ft != NoRights && board[0][ft] == WhiteRook) {
+                if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
+                if(swap)                        callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
+            }
 
             ft = castlingRights[1]; /* Rook file if we have A-side rights */
             left  = BOARD_LEFT+2;
@@ -921,11 +1346,14 @@ int GenLegal(board, flags, callback, closure, filter)
             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
+            if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
             if(ff > BOARD_LEFT+2)
             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
-            if(ft != NoRights && board[0][ft] == WhiteRook)
-                callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+            if(ft != NoRights && board[0][ft] == WhiteRook) {
+                if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
+                if(swap)                        callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
+            }
         }
     } else {
         ff = castlingRights[5]; /* King file if we have any rights */
@@ -938,8 +1366,10 @@ int GenLegal(board, flags, callback, closure, filter)
                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
-            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
-                callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+                if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+                if(swap)                        callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+            }
 
             ft = castlingRights[4]; /* Rook file if we have A-side rights */
             left  = BOARD_LEFT+2;
@@ -947,11 +1377,14 @@ int GenLegal(board, flags, callback, closure, filter)
             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
+            if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
             if(ff > BOARD_LEFT+2)
             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
-            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
-                callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+                if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+                if(swap)                        callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+            }
         }
     }
 
@@ -972,12 +1405,8 @@ extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
                                 VOIDSTAR closure));
 
 
-void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register CheckTestClosure *cl = (CheckTestClosure *) closure;
 
@@ -986,6 +1415,9 @@ void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
        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)
+       && (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
 }
 
 
@@ -996,32 +1428,48 @@ void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
    back rank is not accounted for (i.e., we still return nonzero), as
    this is illegal anyway.  Return value is the number of times the
    king is in check. */
-int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
-     Board board;
-     int flags;
-     int rf, ff, rt, ft, enPassant;
+int
+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;
-    ChessSquare captured = EmptySquare;
+    ChessSquare captured = EmptySquare, ep=0, trampled=0;
     /*  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
+       int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
+       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!)
+               king = board[r][f]; // remember hich one we had
+           }
+       }
+    }
 
     if (rt >= 0) {
        if (enPassant) {
            captured = board[rf][ft];
            board[rf][ft] = EmptySquare;
        } else {
-           captured = board[rt][ft];
+           captured = board[rt][ft];
+           if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
        }
        if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
            board[rt][ft] = board[rf][ff];
            board[rf][ff] = EmptySquare;
        }
+       ep = board[EP_STATUS];
+       if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
+           ChessSquare victim = killX < 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
+               (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
+                    board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
+       }
     }
 
     /* For compatibility with ICS wild 9, we scan the board in the
@@ -1056,24 +1504,34 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
            board[rf][ft] = captured;
            board[rt][ft] = EmptySquare;
        } else {
+           if(killX >= 0) board[killY][killX] = trampled;
            board[rt][ft] = captured;
        }
+       board[EP_STATUS] = ep;
     }
 
     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
 }
 
-ChessMove LegalDrop(board, flags, piece, rt, ft)
-     Board board;
-     int flags;
-     ChessSquare piece;
-     int rt, ft;
+int
+HasLion (Board board, int flags)
+{
+    int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
+    int r, f;
+    for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+        if(board[r][f] == lion) return 1;
+    return 0;
+}
+
+ChessMove
+LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
 {   // [HGM] put drop legality testing in separate routine for clarity
     int n;
 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
     n = PieceToNumber(piece);
-    if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+    if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+       && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
         return ImpossibleMove; // piece not available
     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
@@ -1081,9 +1539,9 @@ if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt
             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
         if(piece == WhitePawn || piece == BlackPawn) {
-            int r;
+            int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
             for(r=1; r<BOARD_HEIGHT-1; r++)
-                if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
+                if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
             // should still test if we mate with this Pawn
         }
     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
@@ -1103,45 +1561,31 @@ extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
                                    int rf, int ff, int rt, int ft,
                                    VOIDSTAR closure));
 
-void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
 
-//    if (appData.debugMode) {
-//        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
-//    }
     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
        cl->captures++; // [HGM] losers: count legal captures
     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
       cl->kind = kind;
 }
 
-ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
-     Board board;
-     int flags;
-     int rf, ff, rt, ft, promoChar;
+ChessMove
+LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
 {
-    LegalityTestClosure cl; ChessSquare piece, filterPiece, *castlingRights = board[CASTLING];
+    LegalityTestClosure cl; ChessSquare piece, filterPiece;
 
     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;
 
-    if (appData.debugMode) {
-        int i;
-        for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
-        fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
-    }
     /* [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)
+    if((piece == WhiteFalcon || piece == BlackFalcon ||
+        piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu)
         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
@@ -1160,12 +1604,25 @@ ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
         if(board[rf][ff] < BlackPawn) { // white
             if(rf != 0) return IllegalMove; // must be on back rank
+            if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
+            if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
+            if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
         } else {
             if(rf != BOARD_HEIGHT-1) return IllegalMove;
+            if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
+            if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
+            if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
         }
     } else
+    if(gameInfo.variant == VariantChu) {
+        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;
+        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) {
@@ -1181,7 +1638,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 &&
-                     (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
+                     (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
@@ -1200,16 +1657,26 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
         }
     } else
     if (promoChar != NULLCHAR) {
+       if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
+            ChessSquare piece = board[rf][ff];
+            if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
+            // 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;
+        } else
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
            ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
            if(piece == EmptySquare)
                 cl.kind = ImpossibleMove; // non-existing piece
-           if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
+           if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
+                cl.kind = IllegalMove; // no two Lions
+           } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
                if(promoChar != PieceToChar(BlackKing)) {
                    if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
                    if(piece == BlackLance) cl.kind = ImpossibleMove;
-               } else { // promotion to King allowed only if we do not haave two yet
+               } else { // promotion to King allowed only if we do not have two yet
                    int r, f, kings = 0;
                    for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
                    if(kings == 2) cl.kind = IllegalMove;
@@ -1235,12 +1702,8 @@ extern void MateTestCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register MateTestClosure *cl = (MateTestClosure *) closure;
 
@@ -1248,9 +1711,8 @@ void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
 }
 
 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
-int MateTest(board, flags)
-     Board board;
-     int flags;
+int
+MateTest (Board board, int flags)
 {
     MateTestClosure cl;
     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
@@ -1265,7 +1727,6 @@ int MateTest(board, flags)
            else hisPieces++;
        }
     }
-    if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
     switch(gameInfo.variant) { // [HGM] losers: extinction wins
        case VariantShatranj:
                if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
@@ -1300,7 +1761,7 @@ int MateTest(board, flags)
        else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
 
         return inCheck ? MT_CHECKMATE
-                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
+                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
                          MT_STAINMATE : MT_STALEMATE;
     }
 }
@@ -1310,12 +1771,8 @@ extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
                                    int rf, int ff, int rt, int ft,
                                    VOIDSTAR closure));
 
-void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
@@ -1348,10 +1805,8 @@ void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
     }
 }
 
-void Disambiguate(board, flags, closure)
-     Board board;
-     int flags;
-     DisambiguateClosure *closure;
+void
+Disambiguate (Board board, int flags, DisambiguateClosure *closure)
 {
     int illegal = 0; char c = closure->promoCharIn;
 
@@ -1359,11 +1814,6 @@ void Disambiguate(board, flags, closure)
     closure->count = closure->captures = 0;
     closure->rf = closure->ff = closure->rt = closure->ft = 0;
     closure->kind = ImpossibleMove;
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
-                             closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
-                             closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
-    }
     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
     fFilter = closure->ftIn;
     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
@@ -1382,14 +1832,23 @@ void Disambiguate(board, flags, closure)
         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
        if (closure->count == 0) {
            /* No, it's not even that */
-    if (appData.debugMode) { int i, j;
-       for(i=BOARD_HEIGHT-1; i>=0; i--) {
-               for(j=0; j<BOARD_WIDTH; j++)
-                       fprintf(debugFP, "%3d", (int) board[i][j]);
-               fprintf(debugFP, "\n");
-       }
-    }
+         if(!appData.testLegality && 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++)
+               if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
+           if(closure->count > 1) illegal = 0; // ambiguous
+         }
+         if(closure->count == 0) {
+           if (appData.debugMode) { int i, j;
+               for(i=BOARD_HEIGHT-1; i>=0; i--) {
+                   for(j=0; j<BOARD_WIDTH; j++)
+                       fprintf(debugFP, "%3d", (int) board[i][j]);
+                   fprintf(debugFP, "\n");
+               }
+           }
            return;
+         }
        }
     }
 
@@ -1397,12 +1856,21 @@ void Disambiguate(board, flags, closure)
     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
         if(closure->piece < BlackPawn) { // white
             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
+            if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
+            if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
+            if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
         } else {
             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
+            if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
+            if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
+            if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
         }
     } else
+    if(gameInfo.variant == VariantChu) {
+        if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
+    } else
     if(gameInfo.variant == VariantShogi) {
         /* [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) {
@@ -1419,7 +1887,7 @@ void Disambiguate(board, flags, closure)
                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
                     else /* promotion optional, default is defer */
-                       closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; 
+                       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) ) {
@@ -1436,7 +1904,8 @@ void Disambiguate(board, flags, closure)
     } else
     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)
+            if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+               gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
                 c = PieceToChar(BlackFerz);
             else if(gameInfo.variant == VariantGreat)
                 c = PieceToChar(BlackMan);
@@ -1445,6 +1914,12 @@ void Disambiguate(board, flags, closure)
             else
                 c = PieceToChar(BlackQueen);
         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
+        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) != '+')
+            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->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
@@ -1460,11 +1935,6 @@ void Disambiguate(board, flags, closure)
        */
        closure->kind = IllegalMove;
     }
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
-        closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
-       closure->promoChar >= ' ' ? closure->promoChar:'-');
-    }
 }
 
 
@@ -1483,12 +1953,8 @@ extern void CoordsToAlgebraicCallback P((Board board, int flags,
                                         ChessMove kind, int rf, int ff,
                                         int rt, int ft, VOIDSTAR closure));
 
-void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register CoordsToAlgebraicClosure *cl =
       (CoordsToAlgebraicClosure *) closure;
@@ -1517,16 +1983,12 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
 /* Convert coordinates to normal algebraic notation.
    promoChar must be NULLCHAR or 'x' if not a promotion.
 */
-ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
-     Board board;
-     int flags;
-     int rf, ff, rt, ft;
-     int promoChar;
-     char out[MOVE_LEN];
+ChessMove
+CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
 {
     ChessSquare piece;
     ChessMove kind;
-    char *outp = out, c;
+    char *outp = out, c, capture;
     CoordsToAlgebraicClosure cl;
 
     if (rf == DROP_RANK) {
@@ -1546,8 +2008,6 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
     piece = board[rf][ff];
     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
 
-  if (appData.debugMode)
-          fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
     switch (piece) {
       case WhitePawn:
       case BlackPawn:
@@ -1561,14 +2021,15 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
        }
        /* Pawn move */
         *outp++ = ff + AAA;
-        if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
+       capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
+        if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
            /* Non-capture; use style "e5" */
             if(rt+ONE <= '9')
                *outp++ = rt + ONE;
             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
        } else {
            /* Capture; use style "exd5" */
-            if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
+            if(capture)
             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
             *outp++ = ft + AAA;
             if(rt+ONE <= '9')
@@ -1577,10 +2038,8 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
        }
        /* Use promotion suffix style "=Q" */
        *outp = NULLCHAR;
-  if (appData.debugMode)
-          fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
         if (promoChar != NULLCHAR) {
-            if(gameInfo.variant == VariantShogi) {
+            if(IS_SHOGI(gameInfo.variant)) {
                 /* [HGM] ... but not in Shogi! */
                 *outp++ = promoChar == '=' ? '=' : '+';
             } else {
@@ -1657,7 +2116,7 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
        */
         if( c == '~' || c == '+') {
            /* [HGM] print nonexistent piece as its demoted version */
-           piece = (ChessSquare) (DEMOTED piece);
+           piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
         }
         if(c=='+') *outp++ = c;
         *outp++ = ToUpper(PieceToChar(piece));
@@ -1678,11 +2137,12 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
         if(rt+ONE <= '9')
            *outp++ = rt + ONE;
         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
-        if (gameInfo.variant == VariantShogi) {
+        if (IS_SHOGI(gameInfo.variant)) {
             /* [HGM] in Shogi non-pawns can promote */
             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
         }
-        else if (gameInfo.variant != VariantSuper && promoChar && 
+        else if (gameInfo.variant == VariantChuChess && promoChar ||
+                 gameInfo.variant != VariantSuper && promoChar &&
                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
             *outp++ = '=';
             *outp++ = ToUpper(promoChar);
@@ -1693,7 +2153,7 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
         }
        *outp = NULLCHAR;
         return cl.kind;
-       
+
       case EmptySquare:
        /* Moving a nonexistent piece */
        break;
@@ -1766,12 +2226,8 @@ extern void AtacksCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {   // For adding captures that can lead to chase indictment to the chaseStack
     if(board[rt][ft] == EmptySquare) return;                               // non-capture
     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
@@ -1790,12 +2246,8 @@ extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
     int i;
     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
@@ -1819,12 +2271,8 @@ extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+void
+ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {   // for determining if a piece (given through the closure) is protected
     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
 
@@ -1835,7 +2283,8 @@ void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
 
 extern char moveList[MAX_MOVES][MOVE_LEN];
 
-int PerpetualChase(int first, int last)
+int
+PerpetualChase (int first, int last)
 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
     int i, j, k, tail;
     ChaseClosure cl;