fixed Makefile.am to handle config file correctly
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index e58cfa0..65fd74e 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -1,9 +1,13 @@
 /*
  * moves.c - Move generation and checking
- * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $
  *
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
- * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009 Free Software Foundation, Inc.
+ *
+ * Enhancements Copyright 2005 Alessandro Scotti
  *
  * The following terms apply to Digital Equipment Corporation's copyright
  * interest in XBoard:
  * SOFTWARE.
  * ------------------------------------------------------------------------
  *
- * The following terms apply to the enhanced version of XBoard distributed
- * by the Free Software Foundation:
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
  * ------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
+ *
+ * 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 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
  *
- * This program 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.
+ * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * ------------------------------------------------------------------------
- */
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
 
 #include "config.h"
 
@@ -62,8 +67,9 @@
 int WhitePiece P((ChessSquare));
 int BlackPiece P((ChessSquare));
 int SameColor P((ChessSquare, ChessSquare));
+int PosFlags(int index);
 
-extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */
+extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
 
 
 int WhitePiece(piece)
@@ -134,7 +140,7 @@ ChessSquare PromoPiece(moveType)
 
 char pieceToChar[] = {
                         'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', 
-                        'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 's', 'U', 'K',
+                        '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' };
@@ -221,6 +227,9 @@ void CopyBoard(to, from)
     for (i = 0; i < BOARD_HEIGHT; i++)
       for (j = 0; j < BOARD_WIDTH; j++)
        to[i][j] = from[i][j];
+    for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
+       to[CASTLING][j] = from[CASTLING][j];
+    to[HOLDINGS_SET] = 0; // flag used in ICS play
 }
 
 int CompareBoards(board1, board2)
@@ -245,15 +254,16 @@ int CompareBoards(board1, board2)
    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
    Promotion moves generated are to Queen only.
 */
-void GenPseudoLegal(board, flags, epfile, callback, closure)
+void GenPseudoLegal(board, flags, callback, closure)
      Board board;
      int flags;
-     int epfile;
      MoveCallback callback;
      VOIDSTAR closure;
 {
     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 ? 3 : 1;
 
     for (rf = 0; rf < BOARD_HEIGHT; rf++) 
       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
@@ -297,7 +307,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
               }
               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
                  callback(board, flags,
-                          rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
+                          rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotionQueen : NormalMove,
                           rf, ff, rf + 1, ff, closure);
              }
              if (rf == 1 && board[2][ff] == EmptySquare &&
@@ -312,7 +322,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                      ((flags & F_KRIEGSPIEL_CAPTURE) ||
                       BlackPiece(board[rf + 1][ff + s]))) {
                      callback(board, flags, 
-                              rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove,
+                              rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotionQueen : NormalMove,
                               rf, ff, rf + 1, ff + s, closure);
                  }
                  if (rf == BOARD_HEIGHT-4) {
@@ -347,7 +357,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
               }
              if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
                  callback(board, flags, 
-                          rf == 1 ? BlackPromotionQueen : NormalMove,
+                          rf <= promoRank ? BlackPromotionQueen : NormalMove,
                           rf, ff, rf - 1, ff, closure);
              }
              if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
@@ -362,7 +372,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                      ((flags & F_KRIEGSPIEL_CAPTURE) ||
                       WhitePiece(board[rf - 1][ff + s]))) {
                      callback(board, flags, 
-                              rf == 1 ? BlackPromotionQueen : NormalMove,
+                              rf <= promoRank ? BlackPromotionQueen : NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  }
                  if (rf == 3) {
@@ -580,7 +590,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                      if (board[rt][ft] != EmptySquare) break;
                  }
                 if(m==1) goto mounted;
-                if(m==2) goto finishGold;
+                if(m==2) goto finishSilver;
              break;
 
            case WhiteQueen:
@@ -602,6 +612,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
 
             /* Shogi Pawn and Silver General: first the Pawn move,    */
             /* then the General continues like a Ferz                 */
+            case WhiteMan:
+                if(gameInfo.variant != VariantMakruk) goto commoner;
             case SHOGI WhitePawn:
             case SHOGI WhiteFerz:
                   if (rf < BOARD_HEIGHT-1 &&
@@ -611,6 +623,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
               if(piece != SHOGI WhitePawn) goto finishSilver;
               break;
 
+            case BlackMan:
+                if(gameInfo.variant != VariantMakruk) goto commoner;
             case SHOGI BlackPawn:
             case SHOGI BlackFerz:
                   if (rf > 0 &&
@@ -638,13 +652,12 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
            case WhiteSilver:
            case BlackSilver:
                m++; // [HGM] superchess: use for Centaur
-            case WhiteMan:
-            case BlackMan:
+            commoner:
             case SHOGI WhiteKing:
             case SHOGI BlackKing:
            case WhiteKing:
            case BlackKing:
-            walking:
+//            walking:
              for (i = -1; i <= 1; i++)
                for (j = -1; j <= 1; j++) {
                    if (i == 0 && j == 0) continue;
@@ -674,6 +687,14 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                     }
                  }
              break;
+           case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
+           case BlackFalcon:
+           case WhiteCobra:
+           case BlackCobra:
+           case WhiteLance:
+           case BlackLance:
+             callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
+             break;
 
          }
       }
@@ -723,6 +744,7 @@ void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
 typedef struct {
     int rf, ff, rt, ft;
     ChessMove kind;
+    int captures; // [HGM] losers
 } LegalityTestClosure;
 
 
@@ -733,22 +755,20 @@ typedef struct {
    true if castling is not yet ruled out by a move of the king or
    rook.  Return TRUE if the player on move is currently in check and
    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
-int GenLegal(board, flags, epfile, castlingRights, callback, closure)
+int GenLegal(board, flags, callback, closure)
      Board board;
      int flags;
-     int epfile;
-     char castlingRights[];
      MoveCallback callback;
      VOIDSTAR closure;
 {
     GenLegalClosure cl;
     int ff, ft, k, left, right;
     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
-    ChessSquare wKing = WhiteKing, bKing = BlackKing;
+    ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
 
     cl.cb = callback;
     cl.cl = closure;
-    GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl);
+    GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
 
     if (!ignoreCheck &&
        CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
@@ -767,7 +787,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
             board[0][BOARD_RGHT-3] == EmptySquare &&
             board[0][BOARD_RGHT-2] == EmptySquare &&
             board[0][BOARD_RGHT-1] == WhiteRook &&
-            castlingRights[0] >= 0 && /* [HGM] check rights */
+            castlingRights[0] != NoRights && /* [HGM] check rights */
             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
             (ignoreCheck ||                             
             (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
@@ -787,7 +807,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
             board[0][BOARD_LEFT+2] == EmptySquare &&
             board[0][BOARD_LEFT+1] == EmptySquare &&
             board[0][BOARD_LEFT+0] == WhiteRook &&
-            castlingRights[1] >= 0 && /* [HGM] check rights */
+            castlingRights[1] != NoRights && /* [HGM] check rights */
             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
            (ignoreCheck ||
             (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
@@ -806,7 +826,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
             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] >= 0 && /* [HGM] check rights */
+            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) &&
@@ -826,7 +846,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
             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] >= 0 && /* [HGM] check rights */
+            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) &&
@@ -839,7 +859,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
        }
     }
 
-  if(gameInfo.variant == VariantFischeRandom) {
+  if(flags & F_FRC_TYPE_CASTLING) {
 
     /* generate all potential FRC castling moves (KxR), ignoring flags */
     /* [HGM] test if the Rooks we find have castling rights */
@@ -847,7 +867,7 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
 
     if ((flags & F_WHITE_ON_MOVE) != 0) {
         ff = castlingRights[2]; /* King file if we have any rights */
-        if(ff > 0 && board[0][ff] == WhiteKing) {
+        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]);
@@ -856,49 +876,49 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
             left  = ff+1;
             right = BOARD_RGHT-2;
             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
-            for(k=left; k<=right && ft >= 0; k++) /* first test if blocked */
-                if(k != ft && board[0][k] != EmptySquare) ft = -1;
-            for(k=left; k<right && ft >= 0; k++) /* then if not checked */
-                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
-            if(ft >= 0 && board[0][ft] == WhiteRook)
+            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)
                 callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, 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 >= 0; k++) /* first test if blocked */
-                if(k != ft && board[0][k] != EmptySquare) ft = -1;
+            for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+                if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
             if(ff > BOARD_LEFT+2) 
-            for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
-                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = -1;
-            if(ft >= 0 && board[0][ft] == WhiteRook)
+            for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
+                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
+            if(ft != NoRights && board[0][ft] == WhiteRook)
                 callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
         }
     } else {
         ff = castlingRights[5]; /* King file if we have any rights */
-        if(ff > 0 && board[BOARD_HEIGHT-1][ff] == BlackKing) {
+        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 >= 0; k++) /* first test if blocked */
-                if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
-            for(k=left; k<right && ft >= 0; k++) /* then if not checked */
-                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
-            if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
+            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)
                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, 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 >= 0; k++) /* first test if blocked */
-                if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = -1;
+            for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
+                if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
             if(ff > BOARD_LEFT+2) 
-            for(k=left+1; k<=right && ft >= 0; k++) /* then if not checked */
-                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = -1;
-            if(ft >= 0 && board[BOARD_HEIGHT-1][ft] == BlackRook)
+            for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
+                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
+            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
         }
     }
@@ -984,8 +1004,7 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
                           cl.check++;
               }
 
-             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
-                            CheckTestCallback, (VOIDSTAR) &cl);
+             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
              goto undo_move;  /* 2-level break */
          }
       }
@@ -1002,7 +1021,7 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
        }
     }
 
-    return cl.check;
+    return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
 }
 
 
@@ -1022,17 +1041,18 @@ void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
 //    if (appData.debugMode) {
 //        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
 //    }
+    if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
+       cl->captures++; // [HGM] losers: count legal captures
     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
       cl->kind = kind;
 }
 
-ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar)
+ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
      Board board;
-     int flags, epfile;
+     int flags;
      int rf, ff, rt, ft, promoChar;
-     char castlingRights[];
 {
-    LegalityTestClosure cl; ChessSquare piece = board[rf][ff];
+    LegalityTestClosure cl; ChessSquare piece = board[rf][ff], *castlingRights = board[CASTLING];
     
     if (appData.debugMode) {
         int i;
@@ -1044,14 +1064,18 @@ ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, pro
     if(piece == WhiteFalcon || piece == BlackFalcon ||
        piece == WhiteCobra  || piece == BlackCobra  ||
        piece == WhiteLance  || piece == BlackLance)
-        return NormalMove;
+        return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
     cl.ff = ff;
     cl.rt = rt;
     cl.ft = ft;
     cl.kind = IllegalMove;
-    GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
+    cl.captures = 0; // [HGM] losers: prepare to count legal captures.
+    GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
+    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(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. '=' means defer */
@@ -1121,21 +1145,50 @@ void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
 }
 
 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
-int MateTest(board, flags, epfile, castlingRights)
+int MateTest(board, flags)
      Board board;
-     int flags, epfile;
-     char castlingRights[];
+     int flags;
 {
     MateTestClosure cl;
-    int inCheck;
+    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++;
+       }
+    }
+    if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
+    switch(gameInfo.variant) { // [HGM] losers: extinction wins
+       case VariantShatranj:
+               if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
+       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, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl);
+    inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
+    // [HGM] 3check: yet to do!
     if (cl.count > 0) {
        return inCheck ? MT_CHECK : MT_NONE;
     } else {
-        return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ?
-                         MT_CHECKMATE : MT_STALEMATE;
+       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) ? 
+                         MT_STAINMATE : MT_STALEMATE;
     }
 }
 
@@ -1152,6 +1205,13 @@ void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
      VOIDSTAR closure;
 {
     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
+    int wildCard = FALSE; ChessSquare piece = board[rf][ff];
+
+    // [HGM] wild: for wild-card pieces rt and rf are dummies
+    if(piece == WhiteFalcon || piece == BlackFalcon ||
+       piece == WhiteCobra  || piece == BlackCobra  ||
+       piece == WhiteLance  || piece == BlackLance)
+        wildCard = TRUE;
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
          || PieceToChar(board[rf][ff]) == '~'
@@ -1159,25 +1219,26 @@ void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
                                                                       ) &&
        (cl->rfIn == -1 || cl->rfIn == rf) &&
        (cl->ffIn == -1 || cl->ffIn == ff) &&
-       (cl->rtIn == -1 || cl->rtIn == rt) &&
-       (cl->ftIn == -1 || cl->ftIn == ft)) {
+       (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
+       (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
 
        cl->count++;
         cl->piece = board[rf][ff];
        cl->rf = rf;
        cl->ff = ff;
-       cl->rt = rt;
-       cl->ft = ft;
+       cl->rt = wildCard ? cl->rtIn : rt;
+       cl->ft = wildCard ? cl->ftIn : ft;
        cl->kind = kind;
     }
 }
 
-void Disambiguate(board, flags, epfile, closure)
+void Disambiguate(board, flags, closure)
      Board board;
-     int flags, epfile;
+     int flags;
      DisambiguateClosure *closure;
 {
     int illegal = 0; char c = closure->promoCharIn;
+
     closure->count = 0;
     closure->rf = closure->ff = closure->rt = closure->ft = 0;
     closure->kind = ImpossibleMove;
@@ -1186,12 +1247,11 @@ void Disambiguate(board, flags, epfile, closure)
                              closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
                              closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
     }
-    GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure);
+    GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
     if (closure->count == 0) {
        /* See if it's an illegal move due to check */
         illegal = 1;
-        GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback,
-                (VOIDSTAR) closure);   
+        GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);       
        if (closure->count == 0) {
            /* No, it's not even that */
     if (appData.debugMode) { int i, j;
@@ -1209,24 +1269,10 @@ void Disambiguate(board, flags, epfile, closure)
         /* [HGM] Shogi promotions. '=' means defer */
         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
             ChessSquare piece = closure->piece;
-#if 0
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate A:   %d(%d,%d)-(%d,%d) = %d (%c)\n",
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
-                          closure->promoCharIn,closure->promoCharIn);
-    }
-#endif
             if(c != NULLCHAR && c != 'x' && c != '+' && c != '=' &&
                ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) ) 
                     closure->kind = IllegalMove;
             else if(flags & F_WHITE_ON_MOVE) {
-#if 0
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate B:   %d(%d,%d)-(%d,%d) = %d (%c)\n",
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
-                          closure->promoCharIn,closure->promoCharIn);
-    }
-#endif
                 if( (int) piece < (int) WhiteWazir &&
                      (closure->rf > BOARD_HEIGHT-4 || closure->rt > BOARD_HEIGHT-4) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
@@ -1260,13 +1306,6 @@ void Disambiguate(board, flags, epfile, closure)
            closure->kind = IllegalMove;
        }
     }
-#if 0
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate C:   %d(%d,%d)-(%d,%d) = %d (%c)\n",
-                          closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
-                          closure->promoCharIn,closure->promoCharIn);
-    }
-#endif
     /* [HGM] returns 'q' for optional promotion, 'n' for mandatory */
     if(closure->promoCharIn != '=')
         closure->promoChar = ToLower(closure->promoCharIn);
@@ -1342,10 +1381,9 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
 /* Convert coordinates to normal algebraic notation.
    promoChar must be NULLCHAR or 'x' if not a promotion.
 */
-ChessMove CoordsToAlgebraic(board, flags, epfile,
-                           rf, ff, rt, ft, promoChar, out)
+ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
      Board board;
-     int flags, epfile;
+     int flags;
      int rf, ff, rt, ft;
      int promoChar;
      char out[MOVE_LEN];
@@ -1376,12 +1414,11 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
     switch (piece) {
       case WhitePawn:
       case BlackPawn:
-        kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar);
+        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, epfile, initialRights,
-                              rf, ff, rt, ft, promoChar);
+            kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
            if (kind == IllegalMove) break;
            kind = IllegalMove;
        }
@@ -1425,8 +1462,7 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
        if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
           (piece == BlackKing && board[rt][ft] == BlackRook)) {
          if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
-            return LegalityTest(board, flags, epfile, initialRights,
-                               rf, ff, rt, ft, promoChar);
+            return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
        }
        /* End of code added by Tord */
        /* Test for castling or ICS wild castling */
@@ -1448,8 +1484,7 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
               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, epfile, initialRights,
-                               rf, ff, rt, ft, promoChar);
+            return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
        }
 
        /* else fall through */
@@ -1462,15 +1497,13 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
        cl.piece = piece;
        cl.kind = IllegalMove;
        cl.rank = cl.file = cl.either = 0;
-        GenLegal(board, flags, epfile, initialRights,
-                CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+        GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
 
        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, epfile, initialRights,
-                    CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
+            GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
            if (cl.kind == IllegalMove) break;
            cl.kind = IllegalMove;
        }
@@ -1587,4 +1620,221 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
     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 {
+char rf, ff, rt, ft;
+} chaseStack[100];
 
+struct {
+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, flags, kind, rf, ff, rt, ft, closure)
+     Board board;
+     int flags;
+     ChessMove kind;
+     int rf, ff, rt, 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, flags, kind, rf, ff, rt, ft, closure)
+     Board board;
+     int flags;
+     ChessMove kind;
+     int rf, ff, rt, 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, flags, kind, rf, ff, rt, ft, closure)
+     Board board;
+     int flags;
+     ChessMove kind;
+     int rf, ff, rt, 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);
+       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;
+       GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl);
+       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
+            // 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);
+           }
+            GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
+           // 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; // if any piece was left on preyStack, it has been perpetually chased
+}