Regularize Chu-Shogi piece assignment
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index edccef8..a53ca59 100644 (file)
--- a/moves.c
+++ b/moves.c
-/*\r
- * moves.c - Move generation and checking\r
- * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $\r
- *\r
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
- * Enhancements Copyright 1992-95 Free Software Foundation, Inc.\r
- *\r
- * The following terms apply to Digital Equipment Corporation's copyright\r
- * interest in XBoard:\r
- * ------------------------------------------------------------------------\r
- * All Rights Reserved\r
- *\r
- * Permission to use, copy, modify, and distribute this software and its\r
- * documentation for any purpose and without fee is hereby granted,\r
- * provided that the above copyright notice appear in all copies and that\r
- * both that copyright notice and this permission notice appear in\r
- * supporting documentation, and that the name of Digital not be\r
- * used in advertising or publicity pertaining to distribution of the\r
- * software without specific, written prior permission.\r
- *\r
- * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
- * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
- * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
- * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
- * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
- * SOFTWARE.\r
- * ------------------------------------------------------------------------\r
- *\r
- * The following terms apply to the enhanced version of XBoard distributed\r
- * by the Free Software Foundation:\r
- * ------------------------------------------------------------------------\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
- * ------------------------------------------------------------------------\r
- */\r
-\r
-#include "config.h"\r
-\r
-#include <stdio.h>\r
-#if HAVE_STRING_H\r
-# include <string.h>\r
-#else /* not HAVE_STRING_H */\r
-# include <strings.h>\r
-#endif /* not HAVE_STRING_H */\r
-#include "common.h"\r
-#include "backend.h" \r
-#include "moves.h"\r
-#include "parser.h"\r
-\r
-int WhitePiece P((ChessSquare));\r
-int BlackPiece P((ChessSquare));\r
-int SameColor P((ChessSquare, ChessSquare));\r
-\r
-extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */\r
-\r
-\r
-int WhitePiece(piece)\r
-     ChessSquare piece;\r
-{\r
-    return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;\r
-}\r
-\r
-int BlackPiece(piece)\r
-     ChessSquare piece;\r
-{\r
-    return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;\r
-}\r
-\r
-int SameColor(piece1, piece2)\r
-     ChessSquare piece1, piece2;\r
-{\r
-    return ((int) piece1 >= (int) WhitePawn &&   /* [HGM] can be > King ! */\r
-            (int) piece1 <  (int) BlackPawn &&\r
-           (int) piece2 >= (int) WhitePawn &&\r
-            (int) piece2 <  (int) BlackPawn)\r
-      ||   ((int) piece1 >= (int) BlackPawn &&\r
-            (int) piece1 <  (int) EmptySquare &&\r
-           (int) piece2 >= (int) BlackPawn &&\r
-            (int) piece2 <  (int) EmptySquare);\r
-}\r
-\r
-ChessSquare PromoPiece(moveType)\r
-     ChessMove moveType;\r
-{\r
-    switch (moveType) {\r
-      default:\r
-       return EmptySquare;\r
-      case WhitePromotionQueen:\r
-       return WhiteQueen;\r
-      case BlackPromotionQueen:\r
-       return BlackQueen;\r
-      case WhitePromotionRook:\r
-       return WhiteRook;\r
-      case BlackPromotionRook:\r
-       return BlackRook;\r
-      case WhitePromotionBishop:\r
-       return WhiteBishop;\r
-      case BlackPromotionBishop:\r
-       return BlackBishop;\r
-      case WhitePromotionKnight:\r
-       return WhiteKnight;\r
-      case BlackPromotionKnight:\r
-       return BlackKnight;\r
-      case WhitePromotionKing:\r
-       return WhiteKing;\r
-      case BlackPromotionKing:\r
-       return BlackKing;\r
-#ifdef FAIRY\r
-      case WhitePromotionChancellor:\r
-        return WhiteMarshall;\r
-      case BlackPromotionChancellor:\r
-        return BlackMarshall;\r
-      case WhitePromotionArchbishop:\r
-        return WhiteCardinal;\r
-      case BlackPromotionArchbishop:\r
-        return BlackCardinal;\r
-#endif\r
-    }\r
-}\r
-\r
-ChessMove PromoCharToMoveType(whiteOnMove, promoChar)\r
-     int whiteOnMove;\r
-     int promoChar;\r
-{\r
-    if (whiteOnMove) {\r
-       switch (promoChar) {\r
-         case 'n':\r
-         case 'N':\r
-           return WhitePromotionKnight;\r
-         case 'b':\r
-         case 'B':\r
-           return WhitePromotionBishop;\r
-         case 'r':\r
-         case 'R':\r
-           return WhitePromotionRook;\r
-#ifdef FAIRY\r
-          case 'a':\r
-          case 'A':\r
-            return WhitePromotionArchbishop;\r
-          case 'c':\r
-          case 'C':\r
-            return WhitePromotionChancellor;\r
-#endif\r
-         case 'q':\r
-         case 'Q':\r
-           return WhitePromotionQueen;\r
-         case 'k':\r
-         case 'K':\r
-           return WhitePromotionKing;\r
-         case NULLCHAR:\r
-         default:\r
-           return NormalMove;\r
-       }\r
-    } else {\r
-       switch (promoChar) {\r
-         case 'n':\r
-         case 'N':\r
-           return BlackPromotionKnight;\r
-         case 'b':\r
-         case 'B':\r
-           return BlackPromotionBishop;\r
-         case 'r':\r
-         case 'R':\r
-           return BlackPromotionRook;\r
-#ifdef FAIRY\r
-          case 'a':\r
-          case 'A':\r
-            return BlackPromotionArchbishop;\r
-          case 'c':\r
-          case 'C':\r
-            return BlackPromotionChancellor;\r
-#endif\r
-         case 'q':\r
-         case 'Q':\r
-           return BlackPromotionQueen;\r
-         case 'k':\r
-         case 'K':\r
-           return BlackPromotionKing;\r
-         case NULLCHAR:\r
-         default:\r
-           return NormalMove;\r
-       }\r
-    }\r
-}\r
-\r
-char pieceToChar[] = {\r
-                        'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', \r
-                        'O', 'H', 'I', 'J', 'G', 'D', 'V', 'S', 'L', 'U', 'K',\r
-                        'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm', \r
-                        'o', 'h', 'i', 'j', 'g', 'd', 'v', 's', 'l', 'u', 'k', \r
-                        'x' };\r
-\r
-char PieceToChar(p)\r
-     ChessSquare p;\r
-{\r
-    if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */\r
-    return pieceToChar[(int) p];\r
-}\r
-\r
-int PieceToNumber(p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */\r
-     ChessSquare p;\r
-{\r
-    int i=0;\r
-    ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;\r
-\r
-    while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;\r
-    return i;\r
-}\r
-\r
-ChessSquare CharToPiece(c)\r
-     int c;\r
-{\r
-     int i;\r
-     for(i=0; i< (int) EmptySquare; i++)\r
-          if(pieceToChar[i] == c) return (ChessSquare) i;\r
-     return EmptySquare;\r
-}\r
-\r
-void CopyBoard(to, from)\r
-     Board to, from;\r
-{\r
-    int i, j;\r
-    \r
-    for (i = 0; i < BOARD_HEIGHT; i++)\r
-      for (j = 0; j < BOARD_WIDTH; j++)\r
-       to[i][j] = from[i][j];\r
-}\r
-\r
-int CompareBoards(board1, board2)\r
-     Board board1, board2;\r
-{\r
-    int i, j;\r
-    \r
-    for (i = 0; i < BOARD_HEIGHT; i++)\r
-      for (j = 0; j < BOARD_WIDTH; j++) {\r
-         if (board1[i][j] != board2[i][j])\r
-           return FALSE;\r
-    }\r
-    return TRUE;\r
-}\r
-\r
-\r
-/* Call callback once for each pseudo-legal move in the given\r
-   position, except castling moves. A move is pseudo-legal if it is\r
-   legal, or if it would be legal except that it leaves the king in\r
-   check.  In the arguments, epfile is EP_NONE if the previous move\r
-   was not a double pawn push, or the file 0..7 if it was, or\r
-   EP_UNKNOWN if we don't know and want to allow all e.p. captures.\r
-   Promotion moves generated are to Queen only.\r
-*/\r
-void GenPseudoLegal(board, flags, epfile, callback, closure)\r
-     Board board;\r
-     int flags;\r
-     int epfile;\r
-     MoveCallback callback;\r
-     VOIDSTAR closure;\r
-{\r
-    int rf, ff;\r
-    int i, j, d, s, fs, rs, rt, ft, m;\r
-\r
-    for (rf = 0; rf < BOARD_HEIGHT; rf++) \r
-      for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {\r
-          ChessSquare piece;\r
-\r
-         if (flags & F_WHITE_ON_MOVE) {\r
-             if (!WhitePiece(board[rf][ff])) continue;\r
-         } else {\r
-             if (!BlackPiece(board[rf][ff])) continue;\r
-         }\r
-          m = 0; piece = board[rf][ff];\r
-          if(PieceToChar(piece) == '~') \r
-                 piece = (ChessSquare) ( DEMOTED piece );\r
-          if(gameInfo.variant == VariantShogi)\r
-                 piece = (ChessSquare) ( SHOGI piece );\r
-\r
-          switch (piece) {\r
-            /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */\r
-           default:\r
-             /* can't happen ([HGM] except for faries...) */\r
-             break;\r
-\r
-             case WhitePawn:\r
-              if(gameInfo.variant == VariantXiangqi) {\r
-                  /* [HGM] capture and move straight ahead in Xiangqi */\r
-                  if (rf < BOARD_HEIGHT-1 &&\r
-                           !SameColor(board[rf][ff], board[rf + 1][ff]) ) {\r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf + 1, ff, closure);\r
-                  }\r
-                  /* and move sideways when across the river */\r
-                  for (s = -1; s <= 1; s += 2) {\r
-                      if (rf >= BOARD_HEIGHT>>1 &&\r
-                          ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                          !WhitePiece(board[rf][ff+s]) ) {\r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf, ff+s, closure);\r
-                      }\r
-                  }\r
-                  break;\r
-              }\r
-              if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {\r
-                 callback(board, flags,\r
-                          rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
-                          rf, ff, rf + 1, ff, closure);\r
-             }\r
-             if (rf == 1 && board[2][ff] == EmptySquare &&\r
-                  gameInfo.variant != VariantShatranj && /* [HGM] */\r
-                  gameInfo.variant != VariantCourier  && /* [HGM] */\r
-                  board[3][ff] == EmptySquare ) {\r
-                      callback(board, flags, NormalMove,\r
-                               rf, ff, 3, ff, closure);\r
-             }\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                     ((flags & F_KRIEGSPIEL_CAPTURE) ||\r
-                      BlackPiece(board[rf + 1][ff + s]))) {\r
-                     callback(board, flags, \r
-                              rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,\r
-                              rf, ff, rf + 1, ff + s, closure);\r
-                 }\r
-                 if (rf == BOARD_HEIGHT-4) {\r
-                      if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                         (epfile == ff + s || epfile == EP_UNKNOWN) &&\r
-                          board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&\r
-                          board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {\r
-                         callback(board, flags, WhiteCapturesEnPassant,\r
-                                  rf, ff, 5, ff + s, closure);\r
-                     }\r
-                 }\r
-             }             \r
-             break;\r
-\r
-           case BlackPawn:\r
-              if(gameInfo.variant == VariantXiangqi) {\r
-                  /* [HGM] capture straight ahead in Xiangqi */\r
-                  if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {\r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf - 1, ff, closure);\r
-                  }\r
-                  /* and move sideways when across the river */\r
-                  for (s = -1; s <= 1; s += 2) {\r
-                      if (rf < BOARD_HEIGHT>>1 &&\r
-                          ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                          !BlackPiece(board[rf][ff+s]) ) {\r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf, ff+s, closure);\r
-                      }\r
-                  }\r
-                  break;\r
-              }\r
-             if (rf > 0 && board[rf - 1][ff] == EmptySquare) {\r
-                 callback(board, flags, \r
-                          rf == 1 ? BlackPromotionQueen : NormalMove,\r
-                          rf, ff, rf - 1, ff, closure);\r
-             }\r
-             if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&\r
-                  gameInfo.variant != VariantShatranj && /* [HGM] */\r
-                  gameInfo.variant != VariantCourier  && /* [HGM] */\r
-                 board[BOARD_HEIGHT-4][ff] == EmptySquare) {\r
-                 callback(board, flags, NormalMove,\r
-                          rf, ff, BOARD_HEIGHT-4, ff, closure);\r
-             }\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                     ((flags & F_KRIEGSPIEL_CAPTURE) ||\r
-                      WhitePiece(board[rf - 1][ff + s]))) {\r
-                     callback(board, flags, \r
-                              rf == 1 ? BlackPromotionQueen : NormalMove,\r
-                              rf, ff, rf - 1, ff + s, closure);\r
-                 }\r
-                 if (rf == 3) {\r
-                      if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                         (epfile == ff + s || epfile == EP_UNKNOWN) &&\r
-                         board[3][ff + s] == WhitePawn &&\r
-                         board[2][ff + s] == EmptySquare) {\r
-                         callback(board, flags, BlackCapturesEnPassant,\r
-                                  rf, ff, 2, ff + s, closure);\r
-                     }\r
-                 }\r
-             }             \r
-             break;\r
-\r
-            case WhiteUnicorn:\r
-            case BlackUnicorn:\r
-           case WhiteKnight:\r
-           case BlackKnight:\r
-            mounted:\r
-             for (i = -1; i <= 1; i += 2)\r
-               for (j = -1; j <= 1; j += 2)\r
-                 for (s = 1; s <= 2; s++) {\r
-                     rt = rf + i*s;\r
-                     ft = ff + j*(3-s);\r
-                      if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
-                          && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)\r
-                          && !SameColor(board[rf][ff], board[rt][ft]))\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                 }\r
-             break;\r
-\r
-            case SHOGI WhiteKnight:\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                      !SameColor(board[rf][ff], board[rf + 2][ff + s])) {\r
-                      callback(board, flags, NormalMove,\r
-                               rf, ff, rf + 2, ff + s, closure);\r
-                 }\r
-              }\r
-             break;\r
-\r
-            case SHOGI BlackKnight:\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                      !SameColor(board[rf][ff], board[rf - 2][ff + s])) {\r
-                      callback(board, flags, NormalMove,\r
-                               rf, ff, rf - 2, ff + s, closure);\r
-                 }\r
-             }             \r
-             break;\r
-\r
-            case WhiteCannon:\r
-            case BlackCannon:\r
-              for (d = 0; d <= 1; d++)\r
-                for (s = -1; s <= 1; s += 2) {\r
-                  m = 0;\r
-                 for (i = 1;; i++) {\r
-                     rt = rf + (i * s) * d;\r
-                     ft = ff + (i * s) * (1 - d);\r
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
-                      if (m == 0 && board[rt][ft] == EmptySquare)\r
-                                 callback(board, flags, NormalMove,\r
-                                          rf, ff, rt, ft, closure);\r
-                      if (m == 1 && board[rt][ft] != EmptySquare &&\r
-                          !SameColor(board[rf][ff], board[rt][ft]) )\r
-                                 callback(board, flags, NormalMove,\r
-                                          rf, ff, rt, ft, closure);\r
-                      if (board[rt][ft] != EmptySquare && m++) break;\r
-                  }\r
-                }\r
-             break;\r
-\r
-            /* Gold General (and all its promoted versions) . First do the */\r
-            /* diagonal forward steps, then proceed as normal Wazir        */\r
-            case SHOGI WhiteWazir:\r
-            case SHOGI (PROMOTED WhitePawn):\r
-            case SHOGI (PROMOTED WhiteKnight):\r
-            case SHOGI (PROMOTED WhiteQueen):\r
-            case SHOGI (PROMOTED WhiteFerz):\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                      !SameColor(board[rf][ff], board[rf + 1][ff + s])) {\r
-                      callback(board, flags, NormalMove,\r
-                              rf, ff, rf + 1, ff + s, closure);\r
-                 }\r
-              }\r
-              goto finishGold;\r
-\r
-            case SHOGI BlackWazir:\r
-            case SHOGI (PROMOTED BlackPawn):\r
-            case SHOGI (PROMOTED BlackKnight):\r
-            case SHOGI (PROMOTED BlackQueen):\r
-            case SHOGI (PROMOTED BlackFerz):\r
-             for (s = -1; s <= 1; s += 2) {\r
-                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&\r
-                      !SameColor(board[rf][ff], board[rf - 1][ff + s])) {\r
-                      callback(board, flags, NormalMove,\r
-                              rf, ff, rf - 1, ff + s, closure);\r
-                 }\r
-             }             \r
-\r
-            case WhiteWazir:\r
-            case BlackWazir:\r
-            finishGold:\r
-              for (d = 0; d <= 1; d++)\r
-                for (s = -1; s <= 1; s += 2) {\r
-                      rt = rf + s * d;\r
-                      ft = ff + s * (1 - d);\r
-                      if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
-                          && !SameColor(board[rf][ff], board[rt][ft]) &&\r
-                          (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )\r
-                               callback(board, flags, NormalMove,\r
-                                        rf, ff, rt, ft, closure);\r
-                      }\r
-             break;\r
-\r
-            case WhiteAlfil:\r
-            case BlackAlfil:\r
-                /* [HGM] support Shatranj pieces */\r
-                for (rs = -1; rs <= 1; rs += 2) \r
-                  for (fs = -1; fs <= 1; fs += 2) {\r
-                      rt = rf + 2 * rs;\r
-                      ft = ff + 2 * fs;\r
-                      if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)\r
-                          && ( gameInfo.variant != VariantXiangqi ||\r
-                               board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )\r
-                         \r
-                          && !SameColor(board[rf][ff], board[rt][ft]))\r
-                               callback(board, flags, NormalMove,\r
-                                        rf, ff, rt, ft, closure);\r
-                 }\r
-                break;\r
-\r
-            /* Shogi Dragon Horse has to continue with Wazir after Bishop */\r
-            case SHOGI WhiteCardinal:\r
-            case SHOGI BlackCardinal:\r
-              m++;\r
-\r
-            /* Capablanca Archbishop continues as Knight                  */\r
-            case WhiteAngel:\r
-            case BlackAngel:\r
-              m++;\r
-\r
-            /* Shogi Bishops are ordinary Bishops */\r
-            case SHOGI WhiteBishop:\r
-            case SHOGI BlackBishop:\r
-           case WhiteBishop:\r
-           case BlackBishop:\r
-             for (rs = -1; rs <= 1; rs += 2) \r
-                for (fs = -1; fs <= 1; fs += 2) \r
-                 for (i = 1;; i++) {\r
-                     rt = rf + (i * rs);\r
-                     ft = ff + (i * fs);\r
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                     if (board[rt][ft] != EmptySquare) break;\r
-                 }\r
-                if(m==1) goto mounted;\r
-                if(m==2) goto finishGold;\r
-                /* Bishop falls through */\r
-             break;\r
-\r
-            /* Shogi Lance is unlike anything, and asymmetric at that */\r
-            case SHOGI WhiteQueen:\r
-              for(i = 1;; i++) {\r
-                      rt = rf + i;\r
-                      ft = ff;\r
-                      if (rt >= BOARD_HEIGHT) break;\r
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                      if (board[rt][ft] != EmptySquare) break;\r
-              }\r
-              break;\r
-\r
-            case SHOGI BlackQueen:\r
-              for(i = 1;; i++) {\r
-                      rt = rf - i;\r
-                      ft = ff;\r
-                      if (rt < 0) break;\r
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                      if (board[rt][ft] != EmptySquare) break;\r
-              }\r
-              break;\r
-\r
-            /* Shogi Dragon King has to continue as Ferz after Rook moves */\r
-            case SHOGI WhiteDragon:\r
-            case SHOGI BlackDragon:\r
-              m++;\r
-\r
-            /* Capablanca Chancellor sets flag to continue as Knight      */\r
-            case WhiteMarshall:\r
-            case BlackMarshall:\r
-              m++;\r
-\r
-            /* Shogi Rooks are ordinary Rooks */\r
-            case SHOGI WhiteRook:\r
-            case SHOGI BlackRook:\r
-           case WhiteRook:\r
-           case BlackRook:\r
-              for (d = 0; d <= 1; d++)\r
-                for (s = -1; s <= 1; s += 2)\r
-                 for (i = 1;; i++) {\r
-                     rt = rf + (i * s) * d;\r
-                     ft = ff + (i * s) * (1 - d);\r
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                     if (board[rt][ft] != EmptySquare) break;\r
-                 }\r
-                if(m==1) goto mounted;\r
-                if(m==2) goto walking;\r
-             break;\r
-\r
-           case WhiteQueen:\r
-           case BlackQueen:\r
-             for (rs = -1; rs <= 1; rs++) \r
-               for (fs = -1; fs <= 1; fs++) {\r
-                   if (rs == 0 && fs == 0) continue;\r
-                   for (i = 1;; i++) {\r
-                       rt = rf + (i * rs);\r
-                       ft = ff + (i * fs);\r
-                        if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
-                       if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                       callback(board, flags, NormalMove,\r
-                                rf, ff, rt, ft, closure);\r
-                       if (board[rt][ft] != EmptySquare) break;\r
-                   }\r
-               }\r
-             break;\r
-\r
-            /* Shogi Pawn and Silver General: first the Pawn move,    */\r
-            /* then the General continues like a Ferz                 */\r
-            case SHOGI WhitePawn:\r
-            case SHOGI WhiteFerz:\r
-                  if (rf < BOARD_HEIGHT-1 &&\r
-                           !SameColor(board[rf][ff], board[rf + 1][ff]) ) \r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf + 1, ff, closure);\r
-              if(piece != SHOGI WhitePawn) goto finishSilver;\r
-              break;\r
-\r
-            case SHOGI BlackPawn:\r
-            case SHOGI BlackFerz:\r
-                  if (rf > 0 &&\r
-                           !SameColor(board[rf][ff], board[rf - 1][ff]) ) \r
-                           callback(board, flags, NormalMove,\r
-                                    rf, ff, rf - 1, ff, closure);\r
-              if(piece == SHOGI BlackPawn) break;\r
-\r
-            case WhiteFerz:\r
-            case BlackFerz:\r
-            finishSilver:\r
-                /* [HGM] support Shatranj pieces */\r
-                for (rs = -1; rs <= 1; rs += 2) \r
-                  for (fs = -1; fs <= 1; fs += 2) {\r
-                      rt = rf + rs;\r
-                      ft = ff + fs;\r
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;\r
-                      if (!SameColor(board[rf][ff], board[rt][ft]) &&\r
-                          (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )\r
-                               callback(board, flags, NormalMove,\r
-                                        rf, ff, rt, ft, closure);\r
-                 }\r
-                break;\r
-\r
-            case WhiteMan:\r
-            case BlackMan:\r
-            case SHOGI WhiteKing:\r
-            case SHOGI BlackKing:\r
-           case WhiteKing:\r
-           case BlackKing:\r
-            walking:\r
-             for (i = -1; i <= 1; i++)\r
-               for (j = -1; j <= 1; j++) {\r
-                   if (i == 0 && j == 0) continue;\r
-                   rt = rf + i;\r
-                   ft = ff + j;\r
-                    if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;\r
-                   if (SameColor(board[rf][ff], board[rt][ft])) continue;\r
-                   callback(board, flags, NormalMove,\r
-                            rf, ff, rt, ft, closure);\r
-               }\r
-             break;\r
-\r
-           case WhiteNightrider:\r
-           case BlackNightrider:\r
-             for (i = -1; i <= 1; i += 2)\r
-               for (j = -1; j <= 1; j += 2)\r
-                 for (s = 1; s <= 2; s++) {  int k;\r
-                    for(k=1;; k++) {\r
-                     rt = rf + k*i*s;\r
-                     ft = ff + k*j*(3-s);\r
-                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;\r
-                     if (SameColor(board[rf][ff], board[rt][ft])) break;\r
-                     callback(board, flags, NormalMove,\r
-                              rf, ff, rt, ft, closure);\r
-                     if (board[rt][ft] != EmptySquare) break;\r
-                    }\r
-                 }\r
-             break;\r
-\r
-         }\r
-      }\r
-}\r
-\r
-\r
-typedef struct {\r
-    MoveCallback cb;\r
-    VOIDSTAR cl;\r
-} GenLegalClosure;\r
-\r
-extern void GenLegalCallback P((Board board, int flags, ChessMove kind,\r
-                               int rf, int ff, int rt, int ft,\r
-                               VOIDSTAR closure));\r
-\r
-void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register GenLegalClosure *cl = (GenLegalClosure *) closure;\r
-\r
-    if (!(flags & F_IGNORE_CHECK) &&\r
-       CheckTest(board, flags, rf, ff, rt, ft,\r
-                 kind == WhiteCapturesEnPassant ||\r
-                 kind == BlackCapturesEnPassant)) return;\r
-    if (flags & F_ATOMIC_CAPTURE) {\r
-      if (board[rt][ft] != EmptySquare ||\r
-         kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {\r
-       int r, f;\r
-       ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;\r
-       if (board[rf][ff] == king) return;\r
-       for (r = rt-1; r <= rt+1; r++) {\r
-         for (f = ft-1; f <= ft+1; f++) {\r
-            if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&\r
-               board[r][f] == king) return;\r
-         }\r
-       }\r
-      }\r
-    }\r
-    cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);\r
-}\r
-\r
-\r
-typedef struct {\r
-    int rf, ff, rt, ft;\r
-    ChessMove kind;\r
-} LegalityTestClosure;\r
-\r
-\r
-/* Like GenPseudoLegal, but (1) include castling moves, (2) unless\r
-   F_IGNORE_CHECK is set in the flags, omit moves that would leave the\r
-   king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit\r
-   moves that would destroy your own king.  The CASTLE_OK flags are\r
-   true if castling is not yet ruled out by a move of the king or\r
-   rook.  Return TRUE if the player on move is currently in check and\r
-   F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */\r
-int GenLegal(board, flags, epfile, castlingRights, callback, closure)\r
-     Board board;\r
-     int flags;\r
-     int epfile;\r
-     char castlingRights[];\r
-     MoveCallback callback;\r
-     VOIDSTAR closure;\r
-{\r
-    GenLegalClosure cl;\r
-    int ff, ft, k, left, right;\r
-    int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;\r
-    ChessSquare wKing = WhiteKing, bKing = BlackKing;\r
-\r
-    cl.cb = callback;\r
-    cl.cl = closure;\r
-    GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);\r
-\r
-    if (!ignoreCheck &&\r
-       CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;\r
-\r
-    /* Generate castling moves */\r
-    if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */\r
-        wKing = WhiteUnicorn; bKing = BlackUnicorn;\r
-    }\r
-\r
-    for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {\r
-       if ((flags & F_WHITE_ON_MOVE) &&\r
-           (flags & F_WHITE_KCASTLE_OK) &&\r
-            board[0][ff] == wKing &&\r
-            board[0][ff + 1] == EmptySquare &&\r
-            board[0][ff + 2] == EmptySquare &&\r
-            board[0][BOARD_RGHT-3] == EmptySquare &&\r
-            board[0][BOARD_RGHT-2] == EmptySquare &&\r
-            board[0][BOARD_RGHT-1] == WhiteRook &&\r
-            castlingRights[0] >= 0 && /* [HGM] check rights */\r
-            ( castlingRights[2] == ff || castlingRights[6] == ff ) &&\r
-            (ignoreCheck ||                             \r
-            (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&\r
-              !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&\r
-              (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&\r
-             !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {\r
-\r
-           callback(board, flags,\r
-                     ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,\r
-                     0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);\r
-       }\r
-       if ((flags & F_WHITE_ON_MOVE) &&\r
-           (flags & F_WHITE_QCASTLE_OK) &&\r
-            board[0][ff] == wKing &&\r
-           board[0][ff - 1] == EmptySquare &&\r
-           board[0][ff - 2] == EmptySquare &&\r
-            board[0][BOARD_LEFT+2] == EmptySquare &&\r
-            board[0][BOARD_LEFT+1] == EmptySquare &&\r
-            board[0][BOARD_LEFT+0] == WhiteRook &&\r
-            castlingRights[1] >= 0 && /* [HGM] check rights */\r
-            ( castlingRights[2] == ff || castlingRights[6] == ff ) &&\r
-           (ignoreCheck ||\r
-            (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&\r
-              !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&\r
-             !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {\r
-\r
-           callback(board, flags,\r
-                    ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,\r
-                     0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);\r
-       }\r
-       if (!(flags & F_WHITE_ON_MOVE) &&\r
-           (flags & F_BLACK_KCASTLE_OK) &&\r
-            board[BOARD_HEIGHT-1][ff] == bKing &&\r
-           board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&\r
-           board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&\r
-            castlingRights[3] >= 0 && /* [HGM] check rights */\r
-            ( castlingRights[5] == ff || castlingRights[7] == ff ) &&\r
-           (ignoreCheck ||\r
-            (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&\r
-              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&\r
-              (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&\r
-             !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {\r
-\r
-           callback(board, flags,\r
-                    ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,\r
-                     BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);\r
-       }\r
-       if (!(flags & F_WHITE_ON_MOVE) &&\r
-           (flags & F_BLACK_QCASTLE_OK) &&\r
-            board[BOARD_HEIGHT-1][ff] == bKing &&\r
-           board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&\r
-           board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&\r
-            board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&\r
-            castlingRights[4] >= 0 && /* [HGM] check rights */\r
-            ( castlingRights[5] == ff || castlingRights[7] == ff ) &&\r
-           (ignoreCheck ||\r
-            (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&\r
-              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&\r
-              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {\r
-\r
-           callback(board, flags,\r
-                    ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,\r
-                     BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);\r
-       }\r
-    }\r
-\r
-  if(gameInfo.variant == VariantFischeRandom) {\r
-\r
-    /* generate all potential FRC castling moves (KxR), ignoring flags */\r
-    /* [HGM] test if the Rooks we find have castling rights */\r
-\r
-\r
-    if ((flags & F_WHITE_ON_MOVE) != 0) {\r
-        ff = castlingRights[2]; /* King file if we have any rights */\r
-        if(ff > 0 && board[0][ff] == WhiteKing) {\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",\r
-                castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);\r
-    }\r
-            ft = castlingRights[0]; /* Rook file if we have H-side rights */\r
-            left  = ff+1;\r
-            right = BOARD_RGHT-2;\r
-            if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */\r
-            for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */\r
-                if(k != ft && board[0][k] != EmptySquare) ft = -1;\r
-            for(k=left; k<right && ft >= 0; k++) /* then if not checked */\r
-                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;\r
-            if(ft >= 0 && board[0][ft] == WhiteRook)\r
-                callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);\r
-\r
-            ft = castlingRights[1]; /* Rook file if we have A-side rights */\r
-            left  = BOARD_LEFT+2;\r
-            right = ff-1;\r
-            if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }\r
-            for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */\r
-                if(k != ft && board[0][k] != EmptySquare) ft = -1;\r
-            if(ff > BOARD_LEFT+2) \r
-            for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */\r
-                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;\r
-            if(ft >= 0 && board[0][ft] == WhiteRook)\r
-                callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);\r
-        }\r
-    } else {\r
-        ff = castlingRights[5]; /* King file if we have any rights */\r
-        if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {\r
-            ft = castlingRights[3]; /* Rook file if we have H-side rights */\r
-            left  = ff+1;\r
-            right = BOARD_RGHT-2;\r
-            if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */\r
-            for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */\r
-                if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;\r
-            for(k=left; k<right && ft >= 0; k++) /* then if not checked */\r
-                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;\r
-            if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)\r
-                callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);\r
-\r
-            ft = castlingRights[4]; /* Rook file if we have A-side rights */\r
-            left  = BOARD_LEFT+2;\r
-            right = ff-1;\r
-            if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }\r
-            for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */\r
-                if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;\r
-            if(ff > BOARD_LEFT+2) \r
-            for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */\r
-                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;\r
-            if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)\r
-                callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);\r
-        }\r
-    }\r
-\r
-  }\r
-\r
-    return FALSE;\r
-}\r
-\r
-\r
-typedef struct {\r
-    int rking, fking;\r
-    int check;\r
-} CheckTestClosure;\r
-\r
-\r
-extern void CheckTestCallback P((Board board, int flags, ChessMove kind,\r
-                                int rf, int ff, int rt, int ft,\r
-                                VOIDSTAR closure));\r
-\r
-\r
-void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register CheckTestClosure *cl = (CheckTestClosure *) closure;\r
-\r
-    if (rt == cl->rking && ft == cl->fking) cl->check++;\r
-}\r
-\r
-\r
-/* If the player on move were to move from (rf, ff) to (rt, ft), would\r
-   he leave himself in check?  Or if rf == -1, is the player on move\r
-   in check now?  enPassant must be TRUE if the indicated move is an\r
-   e.p. capture.  The possibility of castling out of a check along the\r
-   back rank is not accounted for (i.e., we still return nonzero), as\r
-   this is illegal anyway.  Return value is the number of times the\r
-   king is in check. */ \r
-int CheckTest(board, flags, rf, ff, rt, ft, enPassant)\r
-     Board board;\r
-     int flags;\r
-     int rf, ff, rt, ft, enPassant;\r
-{\r
-    CheckTestClosure cl;\r
-    ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;\r
-    ChessSquare captured = EmptySquare;\r
-    /*  Suppress warnings on uninitialized variables    */\r
-\r
-    if(gameInfo.variant == VariantXiangqi)\r
-        king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;\r
-    if(gameInfo.variant == VariantKnightmate)\r
-        king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;\r
-\r
-    if (rf >= 0) {\r
-       if (enPassant) {\r
-           captured = board[rf][ft];\r
-           board[rf][ft] = EmptySquare;\r
-       } else {\r
-           captured = board[rt][ft];\r
-       }\r
-       board[rt][ft] = board[rf][ff];\r
-       board[rf][ff] = EmptySquare;\r
-    }\r
-\r
-    /* For compatibility with ICS wild 9, we scan the board in the\r
-       order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,\r
-       and we test only whether that one is in check. */\r
-    cl.check = 0;\r
-    for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)\r
-       for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {\r
-          if (board[cl.rking][cl.fking] == king) {\r
-              if(gameInfo.variant == VariantXiangqi) {\r
-                  /* [HGM] In Xiangqi opposing Kings means check as well */\r
-                  int i, dir;\r
-                  dir = (king >= BlackPawn) ? -1 : 1;\r
-                  for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&\r
-                                board[i][cl.fking] == EmptySquare; i+=dir );\r
-                  if(i>=0 && i<BOARD_HEIGHT &&\r
-                      board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )\r
-                          cl.check++;\r
-              }\r
-\r
-             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,\r
-                            CheckTestCallback, (VOIDSTAR) &cl);\r
-             goto undo_move;  /* 2-level break */\r
-         }\r
-      }\r
-\r
-  undo_move:\r
-\r
-    if (rf >= 0) {\r
-       board[rf][ff] = board[rt][ft];\r
-       if (enPassant) {\r
-           board[rf][ft] = captured;\r
-           board[rt][ft] = EmptySquare;\r
-       } else {\r
-           board[rt][ft] = captured;\r
-       }\r
-    }\r
-\r
-    return cl.check;\r
-}\r
-\r
-\r
-extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,\r
-                                   int rf, int ff, int rt, int ft,\r
-                                   VOIDSTAR closure));\r
-\r
-void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register LegalityTestClosure *cl = (LegalityTestClosure *) closure;\r
-\r
-//    if (appData.debugMode) {\r
-//        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
-//    }\r
-    if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)\r
-      cl->kind = kind;\r
-}\r
-\r
-ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)\r
-     Board board;\r
-     int flags, epfile;\r
-     int rf, ff, rt, ft, promoChar;\r
-     char castlingRights[];\r
-{\r
-    LegalityTestClosure cl; ChessSquare piece = board[rf][ff];\r
-    \r
-    if (appData.debugMode) {\r
-        int i;\r
-        for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);\r
-        fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
-    }\r
-    /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */\r
-    /* (perhaps we should disallow moves that obviously leave us in check?)              */\r
-    if(piece == WhiteFalcon || piece == BlackFalcon ||\r
-       piece == WhiteCobra  || piece == BlackCobra  ||\r
-       piece == WhiteLance  || piece == BlackLance)\r
-        return NormalMove;\r
-\r
-    cl.rf = rf;\r
-    cl.ff = ff;\r
-    cl.rt = rt;\r
-    cl.ft = ft;\r
-    cl.kind = IllegalMove;\r
-    GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);\r
-\r
-    if(gameInfo.variant == VariantShogi) {\r
-        /* [HGM] Shogi promotions. '=' means defer */\r
-        if(rf != DROP_RANK && cl.kind == NormalMove) {\r
-            ChessSquare piece = board[rf][ff];\r
-\r
-            if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */\r
-            if(promoChar != NULLCHAR && promoChar != 'x' &&\r
-               promoChar != '+' && promoChar != '=' &&\r
-               ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )\r
-                    cl.kind = IllegalMove;\r
-            else if(flags & F_WHITE_ON_MOVE) {\r
-                if( (int) piece < (int) WhiteWazir &&\r
-                     (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {\r
-                    if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||\r
-                         piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */\r
-                             cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;\r
-                    else /* promotion optional, default is promote */\r
-                             cl.kind = promoChar == '=' ? NormalMove  : WhitePromotionQueen;\r
-                   \r
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            } else {\r
-                if( (int) piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {\r
-                    if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||\r
-                         piece == BlackKnight && rt < 2 ) /* promotion obligatory */\r
-                             cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;\r
-                    else /* promotion optional, default is promote */\r
-                             cl.kind = promoChar == '=' ? NormalMove  : BlackPromotionQueen;\r
-\r
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            }\r
-        }\r
-    } else\r
-    if (promoChar != NULLCHAR && promoChar != 'x') {\r
-       if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {\r
-           cl.kind = \r
-             PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);\r
-       } else {\r
-           cl.kind = IllegalMove;\r
-       }\r
-    }\r
-    /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */\r
-    return cl.kind;\r
-}\r
-\r
-typedef struct {\r
-    int count;\r
-} MateTestClosure;\r
-\r
-extern void MateTestCallback P((Board board, int flags, ChessMove kind,\r
-                               int rf, int ff, int rt, int ft,\r
-                               VOIDSTAR closure));\r
-\r
-void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register MateTestClosure *cl = (MateTestClosure *) closure;\r
-\r
-    cl->count++;\r
-}\r
-\r
-/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */\r
-int MateTest(board, flags, epfile, castlingRights)\r
-     Board board;\r
-     int flags, epfile;\r
-     char castlingRights[];\r
-{\r
-    MateTestClosure cl;\r
-    int inCheck;\r
-\r
-    cl.count = 0;\r
-    inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);\r
-    if (cl.count > 0) {\r
-       return inCheck ? MT_CHECK : MT_NONE;\r
-    } else {\r
-        return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?\r
-                         MT_CHECKMATE : MT_STALEMATE;\r
-    }\r
-}\r
-\r
-     \r
-extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,\r
-                                   int rf, int ff, int rt, int ft,\r
-                                   VOIDSTAR closure));\r
-\r
-void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register DisambiguateClosure *cl = (DisambiguateClosure *) closure;\r
-\r
-    if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]\r
-         || PieceToChar(board[rf][ff]) == '~'\r
-              && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])\r
-                                                                      ) &&\r
-       (cl->rfIn == -1 || cl->rfIn == rf) &&\r
-       (cl->ffIn == -1 || cl->ffIn == ff) &&\r
-       (cl->rtIn == -1 || cl->rtIn == rt) &&\r
-       (cl->ftIn == -1 || cl->ftIn == ft)) {\r
-\r
-       cl->count++;\r
-        cl->piece = board[rf][ff];\r
-       cl->rf = rf;\r
-       cl->ff = ff;\r
-       cl->rt = rt;\r
-       cl->ft = ft;\r
-       cl->kind = kind;\r
-    }\r
-}\r
-\r
-void Disambiguate(board, flags, epfile, closure)\r
-     Board board;\r
-     int flags, epfile;\r
-     DisambiguateClosure *closure;\r
-{\r
-    int illegal = 0; char c = closure->promoCharIn;\r
-    closure->count = 0;\r
-    closure->rf = closure->ff = closure->rt = closure->ft = 0;\r
-    closure->kind = ImpossibleMove;\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",\r
-                             closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,\r
-                             closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');\r
-    }\r
-    GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);\r
-    if (closure->count == 0) {\r
-       /* See if it's an illegal move due to check */\r
-        illegal = 1;\r
-        GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,\r
-                (VOIDSTAR) closure);   \r
-       if (closure->count == 0) {\r
-           /* No, it's not even that */\r
-    if (appData.debugMode) { int i, j;\r
-       for(i=BOARD_HEIGHT-1; i>=0; i--) {\r
-               for(j=0; j<BOARD_WIDTH; j++)\r
-                       fprintf(debugFP, "%3d", (int) board[i][j]);\r
-               fprintf(debugFP, "\n");\r
-       }\r
-    }\r
-           return;\r
-       }\r
-    }\r
-\r
-    if(gameInfo.variant == VariantShogi) {\r
-        /* [HGM] Shogi promotions. '=' means defer */\r
-        if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {\r
-            ChessSquare piece = closure->piece;\r
-#if 0\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "Disambiguate A:   %d(%d,%d)-(%d,%d) = %d (%c)\n",\r
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,\r
-                          closure->promoCharIn,closure->promoCharIn);\r
-    }\r
-#endif\r
-            if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&\r
-               ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) ) \r
-                    closure->kind = IllegalMove;\r
-            else if(flags & F_WHITE_ON_MOVE) {\r
-#if 0\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "Disambiguate B:   %d(%d,%d)-(%d,%d) = %d (%c)\n",\r
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,\r
-                          closure->promoCharIn,closure->promoCharIn);\r
-    }\r
-#endif\r
-                if( (int) piece < (int) WhiteWazir &&\r
-                     (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {\r
-                    if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||\r
-                         piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */\r
-                             closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;\r
-                    else /* promotion optional, default is promote */\r
-                             closure->kind = c == '=' ? NormalMove  : WhitePromotionQueen;\r
-                   \r
-                } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            } else {\r
-                if( (int) piece < (int) BlackWazir && (closure->rf < 3 || closure->rt < 3) ) {\r
-                    if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||\r
-                         piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */\r
-                             closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;\r
-                    else /* promotion optional, default is promote */\r
-                             closure->kind = c == '=' ? NormalMove  : BlackPromotionQueen;\r
-\r
-                } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            }\r
-        }\r
-    } else\r
-    if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {\r
-       if (closure->kind == WhitePromotionQueen\r
-           || closure->kind == BlackPromotionQueen) {\r
-           closure->kind = \r
-             PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,\r
-                                 closure->promoCharIn);\r
-       } else {\r
-           closure->kind = IllegalMove;\r
-       }\r
-    }\r
-#if 0\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "Disambiguate C:   %d(%d,%d)-(%d,%d) = %d (%c)\n",\r
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,\r
-                          closure->promoCharIn,closure->promoCharIn);\r
-    }\r
-#endif\r
-    /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */\r
-    if(closure->promoCharIn != '=')\r
-        closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind)));\r
-    else closure->promoChar = '=';\r
-    if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;\r
-    if (closure->count > 1) {\r
-       closure->kind = AmbiguousMove;\r
-    }\r
-    if (illegal) {\r
-       /* Note: If more than one illegal move matches, but no legal\r
-          moves, we return IllegalMove, not AmbiguousMove.  Caller\r
-          can look at closure->count to detect this.\r
-       */\r
-       closure->kind = IllegalMove;\r
-    }\r
-    if(closure->kind == IllegalMove)\r
-    /* [HGM] might be a variant we don't understand, pass on promotion info */\r
-        closure->promoChar = closure->promoCharIn;\r
-    if (appData.debugMode) {\r
-        fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",\r
-        closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,\r
-       closure->promoChar >= ' ' ? closure->promoChar:'-');\r
-    }\r
-}\r
-\r
-\r
-typedef struct {\r
-    /* Input */\r
-    ChessSquare piece;\r
-    int rf, ff, rt, ft;\r
-    /* Output */\r
-    ChessMove kind;\r
-    int rank;\r
-    int file;\r
-    int either;\r
-} CoordsToAlgebraicClosure;\r
-\r
-extern void CoordsToAlgebraicCallback P((Board board, int flags,\r
-                                        ChessMove kind, int rf, int ff,\r
-                                        int rt, int ft, VOIDSTAR closure));\r
-\r
-void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
-     Board board;\r
-     int flags;\r
-     ChessMove kind;\r
-     int rf, ff, rt, ft;\r
-     VOIDSTAR closure;\r
-{\r
-    register CoordsToAlgebraicClosure *cl =\r
-      (CoordsToAlgebraicClosure *) closure;\r
-\r
-    if (rt == cl->rt && ft == cl->ft &&\r
-        (board[rf][ff] == cl->piece\r
-         || PieceToChar(board[rf][ff]) == '~' &&\r
-            (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)\r
-                                     ) {\r
-       if (rf == cl->rf) {\r
-           if (ff == cl->ff) {\r
-               cl->kind = kind; /* this is the move we want */\r
-           } else {\r
-               cl->file++; /* need file to rule out this move */\r
-           }\r
-       } else {\r
-           if (ff == cl->ff) {\r
-               cl->rank++; /* need rank to rule out this move */\r
-           } else {\r
-               cl->either++; /* rank or file will rule out this move */\r
-           }\r
-       }           \r
-    }\r
-}\r
-\r
-/* Convert coordinates to normal algebraic notation.\r
-   promoChar must be NULLCHAR or 'x' if not a promotion.\r
-*/\r
-ChessMove CoordsToAlgebraic(board, flags, epfile,\r
-                           rf, ff, rt, ft, promoChar, out)\r
-     Board board;\r
-     int flags, epfile;\r
-     int rf, ff, rt, ft;\r
-     int promoChar;\r
-     char out[MOVE_LEN];\r
-{\r
-    ChessSquare piece;\r
-    ChessMove kind;\r
-    char *outp = out, c;\r
-    CoordsToAlgebraicClosure cl;\r
-    \r
-    if (rf == DROP_RANK) {\r
-       /* Bughouse piece drop */\r
-       *outp++ = ToUpper(PieceToChar((ChessSquare) ff));\r
-       *outp++ = '@';\r
-        *outp++ = ft + AAA;\r
-        if(rt+ONE <= '9')\r
-           *outp++ = rt + ONE;\r
-        else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
-       *outp = NULLCHAR;\r
-       return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;\r
-    }\r
-\r
-    if (promoChar == 'x') promoChar = NULLCHAR;\r
-    piece = board[rf][ff];\r
-    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);\r
-\r
-  if (appData.debugMode)\r
-          fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');\r
-    switch (piece) {\r
-      case WhitePawn:\r
-      case BlackPawn:\r
-        kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);\r
-       if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {\r
-           /* Keep short notation if move is illegal only because it\r
-               leaves the player in check, but still return IllegalMove */\r
-            kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights,\r
-                              rf, ff, rt, ft, promoChar);\r
-           if (kind == IllegalMove) break;\r
-           kind = IllegalMove;\r
-       }\r
-       /* Pawn move */\r
-        *outp++ = ff + AAA;\r
-        if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */\r
-           /* Non-capture; use style "e5" */\r
-            if(rt+ONE <= '9')\r
-               *outp++ = rt + ONE;\r
-            else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
-       } else {\r
-           /* Capture; use style "exd5" */\r
-            if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )\r
-            *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */\r
-            *outp++ = ft + AAA;\r
-            if(rt+ONE <= '9')\r
-               *outp++ = rt + ONE;\r
-            else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
-       }\r
-       /* Use promotion suffix style "=Q" */\r
-       *outp = NULLCHAR;\r
-  if (appData.debugMode)\r
-          fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');\r
-        if (promoChar != NULLCHAR) {\r
-            if(gameInfo.variant == VariantShogi) {\r
-                /* [HGM] ... but not in Shogi! */\r
-                *outp++ = promoChar == '=' ? '=' : '+';\r
-            } else {\r
-                *outp++ = '=';\r
-                *outp++ = ToUpper(promoChar);\r
-            }\r
-            *outp = NULLCHAR;\r
-       }\r
-        return kind;\r
-\r
-       \r
-      case WhiteKing:\r
-      case BlackKing:\r
-        /* Fabien moved code: FRC castling first (if KxR), wild castling second */\r
-       /* Code added by Tord:  FRC castling. */\r
-       if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||\r
-          (piece == BlackKing && board[rt][ft] == BlackRook)) {\r
-         if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");\r
-            return LegalityTest(board, flags, epfile, initialRights,\r
-                               rf, ff, rt, ft, promoChar);\r
-       }\r
-       /* End of code added by Tord */\r
-       /* Test for castling or ICS wild castling */\r
-       /* Use style "O-O" (oh-oh) for PGN compatibility */\r
-       else if (rf == rt &&\r
-           rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&\r
-            ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||\r
-             (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {\r
-            if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)\r
-               strcpy(out, "O-O");\r
-            else\r
-               strcpy(out, "O-O-O");\r
-\r
-           /* This notation is always unambiguous, unless there are\r
-              kings on both the d and e files, with "wild castling"\r
-              possible for the king on the d file and normal castling\r
-              possible for the other.  ICS rules for wild 9\r
-              effectively make castling illegal for either king in\r
-              this situation.  So I am not going to worry about it;\r
-              I'll just generate an ambiguous O-O in this case.\r
-           */\r
-            return LegalityTest(board, flags, epfile, initialRights,\r
-                               rf, ff, rt, ft, promoChar);\r
-       }\r
-\r
-       /* else fall through */\r
-      default:\r
-       /* Piece move */\r
-       cl.rf = rf;\r
-       cl.ff = ff;\r
-       cl.rt = rt;\r
-       cl.ft = ft;\r
-       cl.piece = piece;\r
-       cl.kind = IllegalMove;\r
-       cl.rank = cl.file = cl.either = 0;\r
-        GenLegal(board, flags, epfile, initialRights,\r
-                CoordsToAlgebraicCallback, (VOIDSTAR) &cl);\r
-\r
-       if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {\r
-           /* Generate pretty moves for moving into check, but\r
-              still return IllegalMove.\r
-           */\r
-            GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights,\r
-                    CoordsToAlgebraicCallback, (VOIDSTAR) &cl);\r
-           if (cl.kind == IllegalMove) break;\r
-           cl.kind = IllegalMove;\r
-       }\r
-\r
-       /* Style is "Nf3" or "Nxf7" if this is unambiguous,\r
-          else "Ngf3" or "Ngxf7",\r
-          else "N1f3" or "N5xf7",\r
-          else "Ng1f3" or "Ng5xf7".\r
-       */\r
-        c = PieceToChar(piece) ;\r
-        if( c == '~' || c == '+') {\r
-           /* [HGM] print nonexistent piece as its demoted version */\r
-           piece = (ChessSquare) (DEMOTED piece);\r
-        }\r
-        if(c=='+') *outp++ = c;\r
-        *outp++ = ToUpper(PieceToChar(piece));\r
-\r
-       if (cl.file || (cl.either && !cl.rank)) {\r
-            *outp++ = ff + AAA;\r
-       }\r
-       if (cl.rank) {\r
-            if(rf+ONE <= '9')\r
-                *outp++ = rf + ONE;\r
-            else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }\r
-       }\r
-\r
-       if(board[rt][ft] != EmptySquare)\r
-         *outp++ = 'x';\r
-\r
-        *outp++ = ft + AAA;\r
-        if(rt+ONE <= '9')\r
-           *outp++ = rt + ONE;\r
-        else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
-       *outp = NULLCHAR;\r
-        if (gameInfo.variant == VariantShogi) {\r
-            /* [HGM] in Shogi non-pawns can promote */\r
-            if(flags & F_WHITE_ON_MOVE) {\r
-                if( (int) cl.piece < (int) WhiteWazir &&\r
-                     (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {\r
-                    if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||\r
-                         piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */\r
-                             cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;\r
-                    else cl.kind =  WhitePromotionQueen; /* promotion optional */\r
-                   \r
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            } else {\r
-                if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {\r
-                    if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||\r
-                         piece == BlackKnight && rt < 2 ) /* promotion obligatory */\r
-                             cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;\r
-                    else cl.kind =  BlackPromotionQueen; /* promotion optional */\r
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?\r
-                                            NormalMove : IllegalMove;\r
-            }\r
-            if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {\r
-                /* for optional promotions append '+' or '=' */\r
-                if(promoChar == '=') {\r
-                    *outp++ = '=';\r
-                    cl.kind = NormalMove;\r
-                } else *outp++ = '+';\r
-                *outp = NULLCHAR;\r
-            } else if(cl.kind == IllegalMove) {\r
-                /* Illegal move specifies any given promotion */\r
-                if(promoChar != NULLCHAR && promoChar != 'x') {\r
-                    *outp++ = '=';\r
-                    *outp++ = ToUpper(promoChar);\r
-                    *outp = NULLCHAR;\r
-                }\r
-            }\r
-        }\r
-        return cl.kind;\r
-       \r
-      /* [HGM] Always long notation for fairies we don't know */\r
-      case WhiteFalcon:\r
-      case BlackFalcon:\r
-      case WhiteSilver:\r
-      case BlackSilver:\r
-      case WhiteLance:\r
-      case BlackLance:\r
-      case WhiteGrasshopper:\r
-      case BlackGrasshopper:\r
-      case EmptySquare:\r
-       /* Moving a nonexistent piece */\r
-       break;\r
-    }\r
-    \r
-    /* Not a legal move, even ignoring check.\r
-       If there was a piece on the from square, \r
-       use style "Ng1g3" or "Ng1xe8";\r
-       if there was a pawn or nothing (!),\r
-       use style "g1g3" or "g1xe8".  Use "x"\r
-       if a piece was on the to square, even\r
-       a piece of the same color.\r
-    */\r
-    outp = out;\r
-    if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {\r
-       *outp++ = ToUpper(PieceToChar(piece));\r
-    }\r
-    *outp++ = ff + AAA;\r
-    if(rf+ONE <= '9')\r
-       *outp++ = rf + ONE;\r
-    else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }\r
-    if (board[rt][ft] != EmptySquare) *outp++ = 'x';\r
-    *outp++ = ft + AAA;\r
-    if(rt+ONE <= '9')\r
-       *outp++ = rt + ONE;\r
-    else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }\r
-    /* Use promotion suffix style "=Q" */\r
-    if (promoChar != NULLCHAR && promoChar != 'x') {\r
-       *outp++ = '=';\r
-       *outp++ = ToUpper(promoChar);\r
-    }\r
-    *outp = NULLCHAR;\r
-\r
-    return IllegalMove;\r
-}\r
-\r
-\r
+/*
+ * moves.c - Move generation and checking
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+ * Software Foundation, Inc.
+ *
+ * Enhancements Copyright 2005 Alessandro Scotti
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#if HAVE_STRING_H
+# include <string.h>
+#else /* not HAVE_STRING_H */
+# include <strings.h>
+#endif /* not HAVE_STRING_H */
+#include "common.h"
+#include "backend.h"
+#include "moves.h"
+#include "parser.h"
+
+int WhitePiece P((ChessSquare));
+int BlackPiece P((ChessSquare));
+int SameColor P((ChessSquare, ChessSquare));
+int PosFlags(int index);
+
+int quickFlag;
+char *pieceDesc[EmptySquare];
+char *defaultDesc[EmptySquare] = {
+ "fmWfceFifmnD", "N", "B", "R", "Q",
+ "F", "A", "BN", "RN", "W", "K",
+ "mRcpR", "N0", "BW", "RF", "gQ",
+ "", "", "QN", "", "N", "",
+ "", "", "", "", "",
+ "", "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "", "K"
+};
+
+int
+WhitePiece (ChessSquare piece)
+{
+    return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
+}
+
+int
+BlackPiece (ChessSquare piece)
+{
+    return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
+}
+
+#if 0
+int
+SameColor (ChessSquare piece1, ChessSquare piece2)
+{
+    return ((int) piece1 >= (int) WhitePawn &&   /* [HGM] can be > King ! */
+            (int) piece1 <  (int) BlackPawn &&
+           (int) piece2 >= (int) WhitePawn &&
+            (int) piece2 <  (int) BlackPawn)
+      ||   ((int) piece1 >= (int) BlackPawn &&
+            (int) piece1 <  (int) EmptySquare &&
+           (int) piece2 >= (int) BlackPawn &&
+            (int) piece2 <  (int) EmptySquare);
+}
+#else
+#define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
+#endif
+
+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' };
+unsigned char pieceNickName[EmptySquare];
+int promoPartner[EmptySquare];
+
+char
+PieceToChar (ChessSquare 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
+PieceToNumber (ChessSquare p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
+{
+    int i=0;
+    ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
+
+    while(start++ != p) if(pieceToChar[start-1] != '.' && pieceToChar[start-1] != '+') i++;
+    return i;
+}
+
+ChessSquare
+CharToPiece (int c)
+{
+     int i;
+     if(c == '.') return EmptySquare;
+     for(i=0; i< (int) EmptySquare; i++)
+          if(pieceNickName[i] == c) return (ChessSquare) i;
+     for(i=0; i< (int) EmptySquare; i++)
+          if(pieceToChar[i] == c) return (ChessSquare) i;
+     return EmptySquare;
+}
+
+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; 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 (Board board1, Board board2)
+{
+    int i, j;
+
+    for (i = 0; i < BOARD_HEIGHT; i++)
+      for (j = 0; j < BOARD_WIDTH; j++) {
+         if (board1[i][j] != board2[i][j])
+           return FALSE;
+    }
+    return TRUE;
+}
+
+char defaultName[] = "PNBRQ......................................K"  // white
+                     "pnbrq......................................k"; // black
+char shogiName[]   = "PNBRLS...G.++++++..........................K"  // white
+                     "pnbrls...g.++++++..........................k"; // black
+char xqName[]      = "PH.R.AE..K.C................................"  // white
+                     "ph.r.ae..k.c................................"; // black
+
+char *
+CollectPieceDescriptors ()
+{   // make a line of piece descriptions for use in the PGN Piece tag:
+    // 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], 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
+    if(gameInfo.variant == VariantShogi) pieceName = shogiName;
+    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] == (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
+       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];
+       if(!m) continue;
+       len = strlen(buf);
+       *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;
+}
+
+int
+LoadPieceDesc (char *s)
+{
+    ChessSquare piece;
+    static char suf[] = SUFFIXES;
+    char *r, *p, *q = s;
+    int ok = TRUE, promoted, c;
+    while(q && *s) {
+       p = s;
+       q = strchr(s, ';');
+       if(q) *q = 0, s = q+1;
+       if(*p == '+') promoted = 1, p++; else promoted = 0;
+       c = *p++;
+       if(!c) { ok = FALSE; continue; } // bad syntax
+       if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++;
+       if(*p++ != ':') { ok = FALSE; continue; } // bad syntax
+       if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0
+        piece = CharToPiece(c);
+       if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece
+       if(promoted) {
+           piece = promoPartner[piece];
+           if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist
+       }
+       ASSIGN(pieceDesc[piece], p);
+       if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) {
+           ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p);
+       }
+       pieceDefs = TRUE;
+       if(q) *q = ';';
+    }
+    return ok;
+}
+
+// [HGM] gen: configurable move generation from Betza notation sent by engine.
+// Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
+// square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
+// 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;
+
+//  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";
+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 };
+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 },
+  { 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
+OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
+{
+    (*(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(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
+       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
+       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
+         case 'F': all = 0xAA;  // diagonal atom (degenerate 4-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs1[*desc-'a']; // use wide version
+                       if( islower(desc[1]) &&
+                                ((i | dirType[desc[1]-'a']) & 3) == 3) {   // combinable (perpendicular dim)
+                           b = dirs1[*desc-'a'] & dirs1[desc[1]-'a'];      // intersect wide & perp wide
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
+                   break;
+         case 'R': expo = 0;    // rook, slide
+         case 'W': all = 0x55;  // orthogonal atom (non-deg 4-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
+                   while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
+                   dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+                   dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
+                   break;
+         case 'N': all = 0xFF;  // oblique atom (degenerate 8-fold)
+                   if(tx >= 0) goto king;        // continuation legs specified in K/Q system!
+                   if(*desc == 'h') {            // chiral direction sets 'hr' and 'hl'
+                       dirSet = (desc[1] == 'r' ? 0x55 :  0xAA); desc += 2;
+                   } else
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs2[*desc-'a']; // when alone, use narrow version
+                       if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
+                       else if(*desc == desc[1] || islower(desc[1]) && i < '4'
+                               && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
+                           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': 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
+       }
+       if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255;   // invert black moves
+       mode = 0;                // build mode mask
+       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(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 == 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
+           int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
+           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];
+           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
+               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(x == f && y == r && !loop) occup = 4;     else // start square counts as empty (if not around cylinder!)
+               if(board[y][x] < BlackPawn)   occup = 0x101; else
+               if(board[y][x] < EmptySquare) occup = 0x102; else
+                                             occup = 4;
+               if(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
+               ep = board[EP_RANK];
+               if(mode & 8 && occup == 4 && board[EP_FILE] == x && (y == (ep & 127) || y - vy == ep - 128)) { // to e.p. square (or 2nd e.p. square)
+                   cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
+               }
+               if(mode & 1024) {            // castling
+                   i = 2;                   // kludge to elongate move indefinitely
+                   if(occup == 4) continue; // skip empty squares
+                   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);
+                   }
+                   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);
+                   }
+                   break;
+               }
+               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);
+         }
+         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
+
+// [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 Soaring Eagle
+  int ft = ff + dx, rt = rf + dy;
+  if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+  legNr += 2;
+  if (!SameColor(board[rf][ff], board[rt][ft]))
+    callback(board, flags, killX < 0 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
+  legNr -= 2;
+  ft += dx; rt += dy;
+  if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
+  legNr += 2;
+  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
+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
+   legal, or if it would be legal except that it leaves the king in
+   check.  In the arguments, epfile is EP_NONE if the previous move
+   was not a double pawn push, or the file 0..7 if it was, or
+   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 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 || gameInfo.variant == VariantChuChess ? 3 : 1;
+
+    for (rf = 0; rf < BOARD_HEIGHT; rf++)
+      for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
+          ChessSquare piece;
+
+         if(board[rf][ff] == EmptySquare) continue;
+         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) );
+          if(filter != EmptySquare && piece != filter) continue;
+          if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
+              MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], callback, closure);
+              continue;
+          }
+          if(IS_SHOGI(gameInfo.variant))
+                 piece = (ChessSquare) ( SHOGI piece );
+
+          switch ((int)piece) {
+            /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
+           default:
+             /* can't happen ([HGM] except for faries...) */
+             break;
+
+             case WhitePawn:
+              if(gameInfo.variant == VariantXiangqi) {
+                  /* [HGM] capture and move straight ahead in Xiangqi */
+                  if (rf < BOARD_HEIGHT-1 &&
+                           !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
+                           callback(board, flags, NormalMove,
+                                    rf, ff, rf + 1, ff, closure);
+                  }
+                  /* and move sideways when across the river */
+                  for (s = -1; s <= 1; s += 2) {
+                      if (rf >= BOARD_HEIGHT>>1 &&
+                          ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                          !WhitePiece(board[rf][ff+s]) ) {
+                           callback(board, flags, NormalMove,
+                                    rf, ff, rf, ff+s, closure);
+                      }
+                  }
+                  break;
+              }
+              if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
+                 callback(board, flags,
+                          rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
+                          rf, ff, rf + 1, ff, closure);
+             }
+             if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
+                  gameInfo.variant != VariantShatranj && /* [HGM] */
+                  gameInfo.variant != VariantCourier  && /* [HGM] */
+                  board[rf+2][ff] == EmptySquare ) {
+                      callback(board, flags, NormalMove,
+                               rf, ff, rf+2, ff, closure);
+             }
+             for (s = -1; s <= 1; s += 2) {
+                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                     ((flags & F_KRIEGSPIEL_CAPTURE) ||
+                      BlackPiece(board[rf + 1][ff + s]))) {
+                     callback(board, flags,
+                              rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
+                              rf, ff, rf + 1, ff + s, closure);
+                 }
+                 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
+                      if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
+                          board[rf][ff + s] == BlackPawn &&
+                          board[rf+1][ff + s] == EmptySquare) {
+                         callback(board, flags, WhiteCapturesEnPassant,
+                                  rf, ff, rf+1, ff + s, closure);
+                     }
+                 }
+             }
+             break;
+
+           case BlackPawn:
+              if(gameInfo.variant == VariantXiangqi) {
+                  /* [HGM] capture straight ahead in Xiangqi */
+                  if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
+                           callback(board, flags, NormalMove,
+                                    rf, ff, rf - 1, ff, closure);
+                  }
+                  /* and move sideways when across the river */
+                  for (s = -1; s <= 1; s += 2) {
+                      if (rf < BOARD_HEIGHT>>1 &&
+                          ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                          !BlackPiece(board[rf][ff+s]) ) {
+                           callback(board, flags, NormalMove,
+                                    rf, ff, rf, ff+s, closure);
+                      }
+                  }
+                  break;
+              }
+             if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
+                 callback(board, flags,
+                          rf <= promoRank ? BlackPromotion : NormalMove,
+                          rf, ff, rf - 1, ff, closure);
+             }
+             if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
+                  gameInfo.variant != VariantShatranj && /* [HGM] */
+                  gameInfo.variant != VariantCourier  && /* [HGM] */
+                 board[rf-2][ff] == EmptySquare) {
+                 callback(board, flags, NormalMove,
+                          rf, ff, rf-2, ff, closure);
+             }
+             for (s = -1; s <= 1; s += 2) {
+                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                     ((flags & F_KRIEGSPIEL_CAPTURE) ||
+                      WhitePiece(board[rf - 1][ff + s]))) {
+                     callback(board, flags,
+                              rf <= promoRank ? BlackPromotion : NormalMove,
+                              rf, ff, rf - 1, ff + s, closure);
+                 }
+                 if (rf < BOARD_HEIGHT>>1) {
+                      if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
+                         board[rf][ff + s] == WhitePawn &&
+                         board[rf-1][ff + s] == EmptySquare) {
+                         callback(board, flags, BlackCapturesEnPassant,
+                                  rf, ff, rf-1, ff + s, closure);
+                     }
+                 }
+             }
+             break;
+
+            case WhiteUnicorn:
+            case BlackUnicorn:
+           case WhiteKnight:
+           case BlackKnight:
+             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);
+                 }
+             break;
+
+            case SHOGI WhiteKnight:
+             for (s = -1; s <= 1; s += 2) {
+                  if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                      !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
+                      callback(board, flags, NormalMove,
+                               rf, ff, rf + 2, ff + s, closure);
+                 }
+              }
+             break;
+
+            case SHOGI BlackKnight:
+             for (s = -1; s <= 1; s += 2) {
+                  if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
+                      !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
+                      callback(board, flags, NormalMove,
+                               rf, ff, rf - 2, ff + s, closure);
+                 }
+             }
+             break;
+
+            case WhiteCannon:
+            case BlackCannon:
+              for (d = 0; d <= 1; d++)
+                for (s = -1; s <= 1; s += 2) {
+                  m = 0;
+                 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 (m == 0 && board[rt][ft] == EmptySquare)
+                                 callback(board, flags, NormalMove,
+                                          rf, ff, rt, ft, closure);
+                      if (m == 1 && board[rt][ft] != EmptySquare &&
+                          !SameColor(board[rf][ff], board[rt][ft]) )
+                                 callback(board, flags, NormalMove,
+                                          rf, ff, rt, ft, closure);
+                      if (board[rt][ft] != EmptySquare && m++) break;
+                  }
+                }
+             break;
+
+            /* Gold General (and all its promoted versions) . First do the */
+            /* diagonal forward steps, then proceed as normal Wazir        */
+            case SHOGI (PROMO WhitePawn):
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI (PROMO BlackPawn):
+               if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI WhiteAxe:
+            case SHOGI BlackAxe:
+               SlideVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI (PROMO WhiteKnight):
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI WhiteClaw:
+            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 (PROMO BlackKnight):
+               if(gameInfo.variant == VariantShogi) goto BlackGold;
+            case SHOGI BlackClaw:
+            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 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;
+
+            case SHOGI (PROMO 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 (PROMO BlackQueen):
+            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:
+               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:
+                /* [HGM] support Shatranj pieces */
+                for (rs = -1; rs <= 1; rs += 2)
+                  for (fs = -1; fs <= 1; fs += 2) {
+                      rt = rf + 2 * rs;
+                      ft = ff + 2 * fs;
+                      if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
+                          && ( gameInfo.variant != VariantXiangqi ||
+                               board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
+
+                          && !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 == 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)
+                          && !SameColor(board[rf][ff], board[rt][ft]))
+                               callback(board, flags, NormalMove,
+                                        rf, ff, rt, ft, closure);
+                 }
+                if(gameInfo.variant == VariantSpartan)
+                   for(fs = -1; fs <= 1; fs += 2) {
+                      ft = ff + fs;
+                      if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
+                               callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
+                   }
+                break;
+
+            /* 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;
+                     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);
+                 }
+
+            /* Shogi Dragon Horse has to continue with Wazir after Bishop */
+            case SHOGI WhiteCardinal:
+            case SHOGI BlackCardinal:
+            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:
+               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:
+               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;
+                      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;
+              }
+              break;
+
+            case SHOGI BlackQueen:
+              if(gameInfo.variant == VariantChu) goto doQueen;
+              for(i = 1;; i++) {
+                      rt = rf - i;
+                      ft = ff;
+                      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;
+              }
+              break;
+
+            /* 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) 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) // 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:
+               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:
+               Rook(board, flags, rf, ff, callback, closure);
+               break;
+
+           case WhiteQueen:
+           case BlackQueen:
+            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;
+
+            case WhiteMan:
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
+            case SHOGI WhiteFerz:
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepForward(board, flags, rf, ff, callback, closure);
+               break;
+
+            case BlackMan:
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
+            case SHOGI BlackFerz:
+               StepBackward(board, flags, rf, ff, callback, closure);
+
+            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;
+
+           case WhiteSilver:
+           case BlackSilver:
+               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:
+               Ferz(board, flags, rf, ff, callback, closure);
+               Wazir(board, flags, rf, ff, callback, closure);
+               break;
+
+           case WhiteNightrider:
+           case BlackNightrider:
+             for (i = -1; i <= 1; i += 2)
+               for (j = -1; j <= 1; j += 2)
+                 for (s = 1; s <= 2; s++) {  int k;
+                    for(k=1;; k++) {
+                     rt = rf + k*i*s;
+                     ft = ff + k*j*(3-s);
+                      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;
+
+           Amazon:
+               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:
+             if(gameInfo.variant == VariantSuper) goto Amazon;
+             if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
+                 callback(board, flags,
+                          rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
+                          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,
+                              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]))
+                 callback(board, flags,
+                          rf <= promoRank ? BlackPromotion : NormalMove,
+                          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,
+                              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 )
+                     callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
+             }
+            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;
+                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);
+                legNr -= 2*i;
+              }
+              break;
+
+            case SHOGI WhiteDagger:
+            case SHOGI BlackDagger:
+            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 (PROMO WhiteFerz):
+               if(gameInfo.variant == VariantShogi) goto WhiteGold;
+            case SHOGI (PROMO BlackFerz):
+               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 WhiteCat:
+            case SHOGI BlackCat:
+               Ferz(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI WhiteCopper:
+               StepDiagForward(board, flags, rf, ff, callback, closure);
+               StepVertical(board, flags, rf, ff, callback, closure);
+               break;
+
+            case SHOGI BlackCopper:
+               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 WhiteUnicorn:
+               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 BlackUnicorn:
+               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 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);
+               if(killX >= 0) break;
+               Rook(board, flags, rf, ff, callback, closure);
+               SlideDiagBackward(board, flags, rf, ff, callback, closure);
+               break;
+
+            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);
+               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:
+           case BlackCobra:
+             callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+             break;
+
+         }
+      }
+}
+
+
+typedef struct {
+    MoveCallback cb;
+    VOIDSTAR cl;
+} GenLegalClosure;
+
+int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
+Board xqCheckers, nullBoard;
+
+extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
+                               int rf, int ff, int rt, int 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 ((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(promo) {
+           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)
+             promo = 0;
+           else
+               board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
+       }
+       check = CheckTest(board, flags, rf, ff, rt, ft,
+                 kind == WhiteCapturesEnPassant ||
+                 kind == BlackCapturesEnPassant);
+       if(promo) board[rf][ff] = BlackLance;
+      if(check) return;
+    }
+    if (flags & F_ATOMIC_CAPTURE) {
+      if (board[rt][ft] != EmptySquare ||
+         kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
+       int r, f;
+       ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
+       if (board[rf][ff] == king) return;
+       for (r = rt-1; r <= rt+1; r++) {
+         for (f = ft-1; f <= ft+1; f++) {
+            if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
+               board[r][f] == king) return;
+         }
+       }
+      }
+    }
+    cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
+}
+
+
+typedef struct {
+    int rf, ff, rt, ft;
+    ChessMove kind;
+    int captures; // [HGM] losers
+} LegalityTestClosure;
+
+
+/* Like GenPseudoLegal, but (1) include castling moves, (2) unless
+   F_IGNORE_CHECK is set in the flags, omit moves that would leave the
+   king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
+   moves that would destroy your own king.  The CASTLE_OK flags are
+   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 board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
+{
+    GenLegalClosure cl;
+    int ff, ft, k, left, right, swap;
+    int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
+    ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
+    int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
+    char *p;
+
+    cl.cb = callback;
+    cl.cl = closure;
+    xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
+    if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
+    GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
+
+    if (inCheck) return TRUE;
+
+    /* Generate castling moves */
+    if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
+        wKing = WhiteUnicorn; bKing = BlackUnicorn;
+    }
+
+    p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
+    if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
+
+    for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
+       if ((flags & F_WHITE_ON_MOVE) &&
+           (flags & F_WHITE_KCASTLE_OK) &&
+            board[0][ff] == wKing &&
+            board[0][ff + 1] == EmptySquare &&
+            board[0][ff + 2] == EmptySquare &&
+            board[0][BOARD_RGHT-3] == EmptySquare &&
+            board[0][BOARD_RGHT-2] == EmptySquare &&
+            board[0][BOARD_RGHT-1] == WhiteRook &&
+            castlingRights[0] != NoRights && /* [HGM] check rights */
+            ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
+            (ignoreCheck ||
+            (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
+              !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
+              (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
+             !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
+
+           callback(board, flags,
+                     ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
+                     0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
+       }
+       if ((flags & F_WHITE_ON_MOVE) &&
+           (flags & F_WHITE_QCASTLE_OK) &&
+            board[0][ff] == wKing &&
+           board[0][ff - 1] == EmptySquare &&
+           board[0][ff - 2] == EmptySquare &&
+            board[0][BOARD_LEFT+2] == EmptySquare &&
+            board[0][BOARD_LEFT+1] == EmptySquare &&
+            board[0][BOARD_LEFT+0] == WhiteRook &&
+            castlingRights[1] != NoRights && /* [HGM] check rights */
+            ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
+           (ignoreCheck ||
+            (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
+              !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
+             !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
+
+           callback(board, flags,
+                    ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
+                     0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
+       }
+       if (!(flags & F_WHITE_ON_MOVE) &&
+           (flags & F_BLACK_KCASTLE_OK) &&
+            board[BOARD_HEIGHT-1][ff] == bKing &&
+           board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
+           board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
+            castlingRights[3] != NoRights && /* [HGM] check rights */
+            ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
+           (ignoreCheck ||
+            (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
+              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
+              (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
+             !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
+
+           callback(board, flags,
+                    ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
+                     BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
+       }
+       if (!(flags & F_WHITE_ON_MOVE) &&
+           (flags & F_BLACK_QCASTLE_OK) &&
+            board[BOARD_HEIGHT-1][ff] == bKing &&
+           board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
+           board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
+            board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
+            castlingRights[4] != NoRights && /* [HGM] check rights */
+            ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
+           (ignoreCheck ||
+            (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
+              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
+              !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
+
+           callback(board, flags,
+                    ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
+                     BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
+       }
+    }
+
+  if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
+
+    /* generate all potential FRC castling moves (KxR), ignoring flags */
+    /* [HGM] test if the Rooks we find have castling rights */
+    /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
+
+
+    if ((flags & F_WHITE_ON_MOVE) != 0) {
+        ff = castlingRights[2]; /* King file if we have any rights */
+        if(ff != NoRights && board[0][ff] == WhiteKing) {
+    if (appData.debugMode) {
+        fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
+                castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
+    }
+            ft = castlingRights[0]; /* Rook file if we have H-side rights */
+            left  = ff+1;
+            right = BOARD_RGHT-2;
+            if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
+            for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+                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) {
+                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;
+            right = ff-1;
+            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) {
+                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 */
+        if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
+            ft = castlingRights[3]; /* Rook file if we have H-side rights */
+            left  = ff+1;
+            right = BOARD_RGHT-2;
+            if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
+            for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+                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) {
+                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;
+            right = ff-1;
+            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) {
+                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);
+            }
+        }
+    }
+
+  }
+
+    return FALSE;
+}
+
+
+typedef struct {
+    int rking, fking;
+    int check;
+} CheckTestClosure;
+
+
+extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
+                                int rf, int ff, int rt, int 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;
+
+    if (rt == cl->rking && ft == cl->fking) {
+       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)
+    }
+    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
+}
+
+
+/* If the player on move were to move from (rf, ff) to (rt, ft), would
+   he leave himself in check?  Or if rf == -1, is the player on move
+   in check now?  enPassant must be TRUE if the indicated move is an
+   e.p. capture.  The possibility of castling out of a check along the
+   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 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, 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;
+    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;
+       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!)
+               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];
+           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 != rt || ff != ft) board[rf][ff] = EmptySquare;
+       }
+       ep = board[EP_STATUS];
+       if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
+           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
+               (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
+       }
+    }
+
+    /* For compatibility with ICS wild 9, we scan the board in the
+       order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
+       and we test only whether that one is in check. */
+    for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
+       for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
+          if (board[cl.rking][cl.fking] == king) {
+             cl.check = 0;
+              if(gameInfo.variant == VariantXiangqi) {
+                  /* [HGM] In Xiangqi opposing Kings means check as well */
+                  int i, dir;
+                  dir = (king >= BlackPawn) ? -1 : 1;
+                  for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
+                                board[i][cl.fking] == EmptySquare; i+=dir );
+                  if(i>=0 && i<BOARD_HEIGHT &&
+                      board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
+                          cl.check++;
+              }
+             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
+             if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
+                goto undo_move;  /* 2-level break */
+         }
+      }
+
+  undo_move:
+
+    if (rt >= 0) {
+       if(rf != DROP_RANK) // [HGM] drop
+           board[rf][ff] = board[rt][ft];
+       if (enPassant) {
+           board[rf][ft] = captured;
+           board[rt][ft] = EmptySquare;
+       } else {
+           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;
+    }
+
+    return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
+}
+
+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)
+       && 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 ||
+           (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
+            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, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
+            for(r=1; r<BOARD_HEIGHT-1; r++)
+                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
+        if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
+    } else {
+        if( (piece == WhitePawn || piece == BlackPawn) &&
+            (rt == 0 || rt == BOARD_HEIGHT -1 ) )
+            return IllegalMove; /* no pawn drops on 1st/8th */
+    }
+if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
+    if (!(flags & F_IGNORE_CHECK) &&
+       CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
+    return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
+}
+
+extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
+                                   int rf, int ff, int rt, int 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(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 board, int flags, int rf, int ff, int rt, int ft, int promoChar)
+{
+    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);
+
+    /* [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 && !pieceDesc[piece])
+        return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
+
+    cl.rf = rf;
+    cl.ff = ff;
+    cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
+    cl.ft = fFilter = ft;
+    cl.kind = IllegalMove;
+    cl.captures = 0; // [HGM] losers: prepare to count legal captures.
+    if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
+    GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
+    if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
+               && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
+       return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
+
+    if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
+    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])) != '+') {
+           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];
+            int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
+
+            if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
+            if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
+               promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
+               promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
+                  promoChar = '+'; // allowed ICS notations
+if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
+            if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
+                return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
+            else if(flags & F_WHITE_ON_MOVE) {
+                if( (int) piece < (int) WhiteWazir &&
+                     (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;
+                    else /* promotion optional, default is defer */
+                       cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
+                } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
+            } else {
+                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;
+                    else /* promotion optional, default is defer */
+                       cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
+                } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
+            }
+        }
+    } 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 == 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 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;
+               }
+           } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
+                         piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
+           else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
+             cl.kind = IllegalMove; // promotion to Royal Knight not allowed
+           else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
+             cl.kind = IllegalMove; // promotion to King usually not allowed
+       } else {
+           cl.kind = IllegalMove;
+       }
+    }
+    return cl.kind;
+}
+
+typedef struct {
+    int count;
+} MateTestClosure;
+
+extern void MateTestCallback P((Board board, int flags, ChessMove kind,
+                               int rf, int ff, int rt, int 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;
+
+    cl->count++;
+}
+
+/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
+int
+MateTest (Board board, int flags)
+{
+    MateTestClosure cl;
+    int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
+    ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
+
+    for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+        // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
+       nrKing += (board[r][f] == king);   // stm has king
+        if( board[r][f] != EmptySquare ) {
+           if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
+                myPieces++;
+           else hisPieces++;
+       }
+    }
+    switch(gameInfo.variant) { // [HGM] losers: extinction wins
+       case VariantShatranj:
+               if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
+       default:
+               break;
+       case VariantAtomic:
+               if(nrKing == 0) return MT_NOKING;
+               break;
+       case VariantLosers:
+               if(myPieces == 1) return MT_BARE;
+    }
+    cl.count = 0;
+    inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
+    // [HGM] 3check: yet to do!
+    if (cl.count > 0) {
+       return inCheck ? MT_CHECK : MT_NONE;
+    } else {
+        if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
+                                 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
+            int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
+            for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
+                for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
+                    if(board[n][holdings] != EmptySquare) {
+                        int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
+                        if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
+                    }
+        }
+       if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
+               return myPieces == hisPieces ? MT_STALEMATE :
+                                       myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
+       else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
+       else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
+
+        return inCheck ? MT_CHECKMATE
+                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
+                         MT_STAINMATE : MT_STALEMATE;
+    }
+}
+
+
+extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
+                                   int rf, int ff, int rt, int 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];
+    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)
+        wildCard = !pieceDefs; // no wildcards when engine defined pieces
+
+    if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
+         || PieceToChar(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)) {
+
+       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) {
+         // [HGM] oneclick: if multiple moves, be sure we remember capture
+         cl->piece = board[rf][ff];
+         cl->rf = rf;
+         cl->ff = ff;
+         cl->rt = wildCard ? cl->rtIn : rt;
+         cl->ft = wildCard ? cl->ftIn : ft;
+         cl->kind = kind;
+       }
+       cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
+    }
+}
+
+void
+Disambiguate (Board board, int flags, DisambiguateClosure *closure)
+{
+    int illegal = 0; char c = closure->promoCharIn;
+
+    if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
+    closure->count = closure->captures = 0;
+    closure->rf = closure->ff = closure->rt = closure->ft = 0;
+    closure->kind = ImpossibleMove;
+    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
+        GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
+        if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
+            closure->count = closure->captures = 0;
+            closure->rf = closure->ff = closure->rt = closure->ft = 0;
+            closure->kind = ImpossibleMove;
+            GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
+        }
+    } else
+    GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
+    if (closure->count == 0) {
+       /* See if it's an illegal move due to check */
+        illegal = 1;
+        GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
+       if (closure->count == 0) {
+           /* No, it's not even that */
+         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++)
+               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;
+         }
+       }
+    } 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);
+       if(spare.count == 1) *closure = spare;  // It does, so use those in stead (game from file saved before gen patch?)
+       pieceDefs = TRUE;
+    }
+
+    if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
+    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) {
+            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) )
+                   c = '+'; // allowed ICS notations
+            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-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;
+                    else /* promotion optional, default is defer */
+                       closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
+                } else closure->kind = c == '+' ? IllegalMove : NormalMove;
+            } else {
+                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;
+                    else /* promotion optional, default is defer */
+                       closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
+                } else closure->kind = c == '+' ? IllegalMove : NormalMove;
+            }
+        }
+        if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
+        if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
+    } 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)
+                c = PieceToChar(BlackFerz);
+            else if(gameInfo.variant == VariantASEAN)
+                c = PieceToChar(BlackRook);
+            else if(gameInfo.variant == VariantGreat)
+                c = PieceToChar(BlackMan);
+            else if(gameInfo.variant == VariantGrand)
+                   closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
+            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.
+    if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
+       closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
+    if (closure->count > 1) {
+       closure->kind = AmbiguousMove;
+    }
+    if (illegal) {
+       /* Note: If more than one illegal move matches, but no legal
+          moves, we return IllegalMove, not AmbiguousMove.  Caller
+          can look at closure->count to detect this.
+       */
+       closure->kind = IllegalMove;
+    }
+}
+
+
+typedef struct {
+    /* Input */
+    ChessSquare piece;
+    int rf, ff, rt, ft;
+    /* Output */
+    ChessMove kind;
+    int rank;
+    int file;
+    int either;
+} CoordsToAlgebraicClosure;
+
+extern void CoordsToAlgebraicCallback P((Board board, int flags,
+                                        ChessMove kind, int rf, int ff,
+                                        int rt, int 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;
+
+    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)
+                                     ) {
+       if (rf == cl->rf) {
+           if (ff == cl->ff) {
+               cl->kind = kind; /* this is the move we want */
+           } else {
+               cl->file++; /* need file to rule out this move */
+           }
+       } else {
+           if (ff == cl->ff) {
+               cl->rank++; /* need rank to rule out this move */
+           } else {
+               cl->either++; /* rank or file will rule out this move */
+           }
+       }
+    }
+}
+
+/* Convert coordinates to normal algebraic notation.
+   promoChar must be NULLCHAR or 'x' if not a promotion.
+*/
+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, capture;
+    CoordsToAlgebraicClosure cl;
+
+    if (rf == DROP_RANK) {
+       if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
+       /* Bughouse piece drop */
+       *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
+       *outp++ = '@';
+        *outp++ = ft + AAA;
+        if(rt+ONE <= '9')
+           *outp++ = rt + ONE;
+        else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
+       *outp = NULLCHAR;
+       return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
+    }
+
+    if (promoChar == 'x') promoChar = NULLCHAR;
+    piece = board[rf][ff];
+    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
+
+    switch (piece) {
+      case WhitePawn:
+      case BlackPawn:
+        kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
+       if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
+           /* Keep short notation if move is illegal only because it
+               leaves the player in check, but still return IllegalMove */
+            kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
+           if (kind == IllegalMove) break;
+           kind = IllegalMove;
+       }
+       /* Pawn move */
+        *outp++ = ff + AAA;
+       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(capture)
+            *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
+            *outp++ = ft + AAA;
+            if(rt+ONE <= '9')
+               *outp++ = rt + ONE;
+            else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
+       }
+       /* Use promotion suffix style "=Q" */
+       *outp = NULLCHAR;
+        if (promoChar != NULLCHAR) {
+            if(IS_SHOGI(gameInfo.variant)) {
+                /* [HGM] ... but not in Shogi! */
+                *outp++ = promoChar == '=' ? '=' : '+';
+            } else {
+                *outp++ = '=';
+                *outp++ = ToUpper(promoChar);
+            }
+            *outp = NULLCHAR;
+       }
+        return kind;
+
+
+      case WhiteKing:
+      case BlackKing:
+        /* Fabien moved code: FRC castling first (if KxR), wild castling second */
+       /* Code added by Tord:  FRC castling. */
+       if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
+          (piece == BlackKing && board[rt][ft] == BlackRook)) {
+         if(ft > ff)
+           safeStrCpy(out, "O-O", MOVE_LEN);
+         else
+           safeStrCpy(out, "O-O-O", MOVE_LEN);
+         return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
+       }
+       /* End of code added by Tord */
+       /* Test for castling or ICS wild castling */
+       /* Use style "O-O" (oh-oh) for PGN compatibility */
+       else if (rf == rt &&
+           rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
+            (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
+            ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
+             (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
+            if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
+             snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
+            else
+             snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
+
+           /* This notation is always unambiguous, unless there are
+              kings on both the d and e files, with "wild castling"
+              possible for the king on the d file and normal castling
+              possible for the other.  ICS rules for wild 9
+              effectively make castling illegal for either king in
+              this situation.  So I am not going to worry about it;
+              I'll just generate an ambiguous O-O in this case.
+           */
+            return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
+       }
+
+       /* else fall through */
+      default:
+       /* Piece move */
+       cl.rf = rf;
+       cl.ff = ff;
+       cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
+       cl.ft = fFilter = ft;
+       cl.piece = 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
+
+       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)));
+           if (cl.kind == IllegalMove) break;
+           cl.kind = IllegalMove;
+       }
+
+       /* Style is "Nf3" or "Nxf7" if this is unambiguous,
+          else "Ngf3" or "Ngxf7",
+          else "N1f3" or "N5xf7",
+          else "Ng1f3" or "Ng5xf7".
+       */
+        if( c == '~' || c == '+') {
+           /* [HGM] print nonexistent piece as its demoted version */
+           piece = (ChessSquare) (CHUDEMOTED(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.rank) {
+            if(rf+ONE <= '9')
+                *outp++ = rf + ONE;
+            else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
+       }
+
+       if(board[rt][ft] != EmptySquare)
+         *outp++ = 'x';
+
+        *outp++ = ft + AAA;
+        if(rt+ONE <= '9')
+           *outp++ = rt + ONE;
+        else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
+        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 == VariantChuChess && promoChar ||
+                 gameInfo.variant != VariantSuper && promoChar &&
+                 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
+            *outp++ = '=';
+            *outp++ = ToUpper(promoChar);
+        }
+        else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
+            *outp++ = '/';
+            *outp++ = ToUpper(promoChar);
+        }
+       *outp = NULLCHAR;
+        return cl.kind;
+
+      case EmptySquare:
+       /* Moving a nonexistent piece */
+       break;
+    }
+
+    /* Not a legal move, even ignoring check.
+       If there was a piece on the from square,
+       use style "Ng1g3" or "Ng1xe8";
+       if there was a pawn or nothing (!),
+       use style "g1g3" or "g1xe8".  Use "x"
+       if a piece was on the to square, even
+       a piece of the same color.
+    */
+    outp = out;
+    c = 0;
+    if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
+       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 = 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(rf+ONE <= '9')
+       *outp++ = rf + ONE;
+    else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
+  }
+    if (board[rt][ft] != EmptySquare) *outp++ = 'x';
+    *outp++ = ft + AAA;
+    if(rt+ONE <= '9')
+       *outp++ = rt + ONE;
+    else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
+    /* Use promotion suffix style "=Q" */
+    if (promoChar != NULLCHAR && promoChar != 'x') {
+       *outp++ = '=';
+       *outp++ = ToUpper(promoChar);
+    }
+    *outp = NULLCHAR;
+
+    return IllegalMove;
+}
+
+// [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
+
+typedef struct {
+    /* Input */
+    int rf, ff, rt, ft;
+    /* Output */
+    int recaptures;
+} ChaseClosure;
+
+// I guess the following variables logically belong in the closure too, but I was too lazy and used globals
+
+int preyStackPointer, chaseStackPointer;
+
+struct {
+unsigned char rf, ff, rt, ft;
+} chaseStack[100];
+
+struct {
+unsigned char rank, file;
+} preyStack[100];
+
+
+
+
+// there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
+
+extern void AtacksCallback P((Board board, int flags, ChessMove kind,
+                               int rf, int ff, int rt, int 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
+    if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
+    if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
+    if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
+    // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
+    chaseStack[chaseStackPointer].rf = rf;
+    chaseStack[chaseStackPointer].ff = ff;
+    chaseStack[chaseStackPointer].rt = rt;
+    chaseStack[chaseStackPointer].ft = ft;
+    chaseStackPointer++;
+}
+
+extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
+                               int rf, int ff, int rt, int 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
+
+    if(board[rt][ft] == EmptySquare) return; // no capture
+    if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
+       rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
+    }
+    // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
+    for(i=0; i<chaseStackPointer; i++) {
+       if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
+          chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
+           // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
+           chaseStack[i] = chaseStack[--chaseStackPointer];
+           break;
+       }
+    }
+}
+
+extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
+                               int rf, int ff, int rt, int 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
+
+    if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
+    if(appData.debugMode && board[rt][ft] != EmptySquare)
+       fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
+}
+
+extern char moveList[MAX_MOVES][MOVE_LEN];
+
+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;
+    ChessSquare captured;
+
+    preyStackPointer = 0;        // clear stack of chased pieces
+    for(i=first; i<last; i+=2) { // for all positions with same side to move
+        if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
+       chaseStackPointer = 0;   // clear stack that is going to hold possible chases
+       // determine all captures possible after the move, and put them on chaseStack
+       GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
+       if(appData.debugMode) { int n;
+           for(n=0; n<chaseStackPointer; n++)
+                fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
+                                              chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+            fprintf(debugFP, ": all capts\n");
+       }
+       // determine all captures possible before the move, and delete them from chaseStack
+       cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
+       cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
+       cl.rt = moveList[i][3]-ONE;
+       cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
+       CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
+       GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
+       xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
+       if(appData.debugMode) { int n;
+           for(n=0; n<chaseStackPointer; n++)
+                fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
+                                              chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+            fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
+       }
+       // chaseSack now contains all captures made possible by the move
+       for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
+            int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
+            int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
+
+           if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
+           if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
+
+           if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
+               continue; // C or H attack on R is always chase; leave on chaseStack
+
+           if(attacker == victim) {
+                if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
+                   chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
+                       // we can capture back with equal piece, so this is no chase but a sacrifice
+                        chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
+                       j--; /* ! */ continue;
+               }
+
+           }
+
+           // the attack is on a lower piece, or on a pinned or blocked equal one
+           CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
+           CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
+            // test if the victim is protected by a true protector. First make the capture.
+           captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
+           boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
+           boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
+           // Then test if the opponent can recapture
+           cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
+           cl.rt = chaseStack[j].rt;
+           cl.ft = chaseStack[j].ft;
+           if(appData.debugMode) {
+               fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
+           }
+           xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
+            GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
+           xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
+           // unmake the capture
+           boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
+            boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
+           // if a recapture was found, piece is protected, and we are not chasing it.
+           if(cl.recaptures) { // attacked piece was defended by true protector, no chase
+               chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
+               j--; /* ! */
+           }
+       }
+       // chaseStack now contains all moves that chased
+       if(appData.debugMode) { int n;
+           for(n=0; n<chaseStackPointer; n++)
+                fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
+                                              chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
+            fprintf(debugFP, ": chases\n");
+       }
+        if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
+           for(j=0; j<chaseStackPointer; j++) {
+                preyStack[j].rank = chaseStack[j].rt;
+                preyStack[j].file = chaseStack[j].ft;
+           }
+           preyStackPointer = chaseStackPointer;
+       }
+       tail = 0;
+        for(j=0; j<chaseStackPointer; j++) {
+           for(k=0; k<preyStackPointer; k++) {
+               // search the victim of each chase move on the preyStack (first occurrence)
+               if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
+                   if(k < tail) break; // piece was already identified as still being chased
+                   preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
+                   preyStack[tail] = preyStack[k];                // by swapping
+                   preyStack[k] = preyStack[preyStackPointer];
+                   tail++;
+                   break;
+               }
+           }
+       }
+        preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
+       if(appData.debugMode) { int n;
+            for(n=0; n<preyStackPointer; n++)
+                fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
+            fprintf(debugFP, "always chased upto ply %d\n", i);
+       }
+        // now adjust the location of the chased pieces according to opponent move
+        for(j=0; j<preyStackPointer; j++) {
+            if(preyStack[j].rank == moveList[i+1][1]-ONE &&
+               preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
+                preyStack[j].rank = moveList[i+1][3]-ONE;
+                preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
+                break;
+            }
+        }
+    }
+    return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
+                               : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
+}