Updated copyright notice to 2011
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index b4572fe..769604d 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -60,7 +60,7 @@
 # include <strings.h>
 #endif /* not HAVE_STRING_H */
 #include "common.h"
-#include "backend.h" 
+#include "backend.h"
 #include "moves.h"
 #include "parser.h"
 
@@ -69,7 +69,7 @@ int BlackPiece P((ChessSquare));
 int SameColor P((ChessSquare, ChessSquare));
 int PosFlags(int index);
 
-extern signed 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)
@@ -97,53 +97,13 @@ int SameColor(piece1, piece2)
             (int) piece2 <  (int) EmptySquare);
 }
 
-ChessSquare PromoPiece(moveType)
-     ChessMove moveType;
-{
-    switch (moveType) {
-      default:
-       return EmptySquare;
-      case WhitePromotionQueen:
-       return WhiteQueen;
-      case BlackPromotionQueen:
-       return BlackQueen;
-      case WhitePromotionRook:
-       return WhiteRook;
-      case BlackPromotionRook:
-       return BlackRook;
-      case WhitePromotionBishop:
-       return WhiteBishop;
-      case BlackPromotionBishop:
-       return BlackBishop;
-      case WhitePromotionKnight:
-       return WhiteKnight;
-      case BlackPromotionKnight:
-       return BlackKnight;
-      case WhitePromotionKing:
-       return WhiteKing;
-      case BlackPromotionKing:
-       return BlackKing;
-      case WhitePromotionChancellor:
-        return WhiteMarshall;
-      case BlackPromotionChancellor:
-        return BlackMarshall;
-      case WhitePromotionArchbishop:
-        return WhiteAngel;
-      case BlackPromotionArchbishop:
-        return BlackAngel;
-      case WhitePromotionCentaur:
-        return WhiteSilver;
-      case BlackPromotionCentaur:
-        return BlackSilver;
-    }
-}
-
 char pieceToChar[] = {
-                        'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', 
+                        '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', 
+                        'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
+                        'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
                         'x' };
+char pieceNickName[EmptySquare];
 
 char PieceToChar(p)
      ChessSquare p;
@@ -167,73 +127,30 @@ ChessSquare CharToPiece(c)
 {
      int i;
      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;
 }
 
-ChessMove PromoCharToMoveType(whiteOnMove, promoChar)
-     int whiteOnMove;
-     int promoChar;
-{      /* [HGM] made dependent on CharToPiece to alow alternate piece letters */
-       ChessSquare piece = CharToPiece(whiteOnMove ? ToUpper(promoChar) : ToLower(promoChar) );
-
-
-       if(promoChar == NULLCHAR) return NormalMove;
-
-       switch(piece) {
-               case WhiteQueen:
-                       return WhitePromotionQueen;
-               case WhiteRook:
-                       return WhitePromotionRook;
-               case WhiteBishop:
-                       return WhitePromotionBishop;
-               case WhiteKnight:
-                       return WhitePromotionKnight;
-               case WhiteKing:
-                       return WhitePromotionKing;
-               case WhiteAngel:
-                       return WhitePromotionArchbishop;
-               case WhiteMarshall:
-                       return WhitePromotionChancellor;
-               case WhiteSilver:
-                       return WhitePromotionCentaur;
-               case BlackQueen:
-                       return BlackPromotionQueen;
-               case BlackRook:
-                       return BlackPromotionRook;
-               case BlackBishop:
-                       return BlackPromotionBishop;
-               case BlackKnight:
-                       return BlackPromotionKnight;
-               case BlackKing:
-                       return BlackPromotionKing;
-               case BlackAngel:
-                       return BlackPromotionArchbishop;
-               case BlackMarshall:
-                       return BlackPromotionChancellor;
-               case BlackSilver:
-                       return BlackPromotionCentaur;
-               default:
-                       // not all promotion implemented yet! Take Queen for those we don't know.
-                       return (whiteOnMove ? WhitePromotionQueen : BlackPromotionQueen);
-       }
-}
-
 void CopyBoard(to, from)
      Board to, from;
 {
     int i, j;
-    
+
     for (i = 0; i < BOARD_HEIGHT; i++)
       for (j = 0; j < BOARD_WIDTH; j++)
        to[i][j] = from[i][j];
+    for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
+       to[CASTLING][j] = from[CASTLING][j];
+    to[HOLDINGS_SET] = 0; // flag used in ICS play
 }
 
 int CompareBoards(board1, board2)
      Board board1, 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])
@@ -251,19 +168,21 @@ 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 (rf = 0; rf < BOARD_HEIGHT; rf++)
       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
           ChessSquare piece;
+          int rookRange = 1000;
 
          if (flags & F_WHITE_ON_MOVE) {
              if (!WhitePiece(board[rf][ff])) continue;
@@ -271,7 +190,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
              if (!BlackPiece(board[rf][ff])) continue;
          }
           m = 0; piece = board[rf][ff];
-          if(PieceToChar(piece) == '~') 
+          if(PieceToChar(piece) == '~')
                  piece = (ChessSquare) ( DEMOTED piece );
           if(gameInfo.variant == VariantShogi)
                  piece = (ChessSquare) ( SHOGI piece );
@@ -303,7 +222,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 ? WhitePromotion : NormalMove,
                           rf, ff, rf + 1, ff, closure);
              }
              if (rf == 1 && board[2][ff] == EmptySquare &&
@@ -317,8 +236,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                   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-2 ? WhitePromotionQueen : NormalMove,
+                     callback(board, flags,
+                              rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
                               rf, ff, rf + 1, ff + s, closure);
                  }
                  if (rf == BOARD_HEIGHT-4) {
@@ -330,7 +249,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                                   rf, ff, 5, ff + s, closure);
                      }
                  }
-             }             
+             }
              break;
 
            case BlackPawn:
@@ -352,8 +271,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                   break;
               }
              if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
-                 callback(board, flags, 
-                          rf == 1 ? BlackPromotionQueen : NormalMove,
+                 callback(board, flags,
+                          rf <= promoRank ? BlackPromotion : NormalMove,
                           rf, ff, rf - 1, ff, closure);
              }
              if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
@@ -367,8 +286,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                   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 == 1 ? BlackPromotionQueen : NormalMove,
+                     callback(board, flags,
+                              rf <= promoRank ? BlackPromotion : NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  }
                  if (rf == 3) {
@@ -380,7 +299,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                                   rf, ff, 2, ff + s, closure);
                      }
                  }
-             }             
+             }
              break;
 
             case WhiteUnicorn:
@@ -418,7 +337,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                       callback(board, flags, NormalMove,
                                rf, ff, rf - 2, ff + s, closure);
                  }
-             }             
+             }
              break;
 
             case WhiteCannon:
@@ -469,7 +388,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                       callback(board, flags, NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  }
-             }             
+             }
 
             case WhiteWazir:
             case BlackWazir:
@@ -489,20 +408,45 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
             case WhiteAlfil:
             case BlackAlfil:
                 /* [HGM] support Shatranj pieces */
-                for (rs = -1; rs <= 1; rs += 2) 
+                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) 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:
+              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:
@@ -518,8 +462,8 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
             case SHOGI BlackBishop:
            case WhiteBishop:
            case BlackBishop:
-             for (rs = -1; rs <= 1; rs += 2) 
-                for (fs = -1; fs <= 1; fs += 2) 
+             for (rs = -1; rs <= 1; rs += 2)
+                for (fs = -1; fs <= 1; fs += 2)
                  for (i = 1;; i++) {
                      rt = rf + (i * rs);
                      ft = ff + (i * fs);
@@ -559,6 +503,20 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
               }
               break;
 
+            /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
+           case WhiteDragon:
+           case BlackDragon:
+              for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
+                for (s = -2; s <= 2; s += 4) {
+                     rt = rf + s * d;
+                     ft = ff + s * (1 - d);
+                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
+                     if (SameColor(board[rf][ff], board[rt][ft])) continue;
+                     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+                 }
+              if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
+              goto doRook;
+              
             /* Shogi Dragon King has to continue as Ferz after Rook moves */
             case SHOGI WhiteDragon:
             case SHOGI BlackDragon:
@@ -568,12 +526,14 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
             case WhiteMarshall:
             case BlackMarshall:
               m++;
+              m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
 
             /* Shogi Rooks are ordinary Rooks */
             case SHOGI WhiteRook:
             case SHOGI BlackRook:
            case WhiteRook:
            case BlackRook:
+          doRook:
               for (d = 0; d <= 1; d++)
                 for (s = -1; s <= 1; s += 2)
                  for (i = 1;; i++) {
@@ -583,7 +543,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                      if (SameColor(board[rf][ff], board[rt][ft])) break;
                      callback(board, flags, NormalMove,
                               rf, ff, rt, ft, closure);
-                     if (board[rt][ft] != EmptySquare) break;
+                     if (board[rt][ft] != EmptySquare || i == rookRange) break;
                  }
                 if(m==1) goto mounted;
                 if(m==2) goto finishSilver;
@@ -591,7 +551,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
 
            case WhiteQueen:
            case BlackQueen:
-             for (rs = -1; rs <= 1; rs++) 
+             for (rs = -1; rs <= 1; rs++)
                for (fs = -1; fs <= 1; fs++) {
                    if (rs == 0 && fs == 0) continue;
                    for (i = 1;; i++) {
@@ -608,19 +568,23 @@ 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 &&
-                           !SameColor(board[rf][ff], board[rf + 1][ff]) ) 
+                           !SameColor(board[rf][ff], board[rf + 1][ff]) )
                            callback(board, flags, NormalMove,
                                     rf, ff, rf + 1, ff, closure);
               if(piece != SHOGI WhitePawn) goto finishSilver;
               break;
 
+            case BlackMan:
+                if(gameInfo.variant != VariantMakruk) goto commoner;
             case SHOGI BlackPawn:
             case SHOGI BlackFerz:
                   if (rf > 0 &&
-                           !SameColor(board[rf][ff], board[rf - 1][ff]) ) 
+                           !SameColor(board[rf][ff], board[rf - 1][ff]) )
                            callback(board, flags, NormalMove,
                                     rf, ff, rf - 1, ff, closure);
               if(piece == SHOGI BlackPawn) break;
@@ -629,7 +593,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
             case BlackFerz:
             finishSilver:
                 /* [HGM] support Shatranj pieces */
-                for (rs = -1; rs <= 1; rs += 2) 
+                for (rs = -1; rs <= 1; rs += 2)
                   for (fs = -1; fs <= 1; fs += 2) {
                       rt = rf + rs;
                       ft = ff + fs;
@@ -644,8 +608,7 @@ 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:
@@ -681,6 +644,62 @@ void GenPseudoLegal(board, flags, epfile, callback, closure)
                  }
              break;
 
+           Amazon:
+             /* First do Bishop,then continue like Chancellor */
+             for (rs = -1; rs <= 1; rs += 2)
+                for (fs = -1; fs <= 1; fs += 2)
+                 for (i = 1;; i++) {
+                     rt = rf + (i * rs);
+                     ft = ff + (i * fs);
+                      if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+                     if (SameColor(board[rf][ff], board[rt][ft])) break;
+                     callback(board, flags, NormalMove,
+                              rf, ff, rt, ft, closure);
+                     if (board[rt][ft] != EmptySquare) break;
+                 }
+             m++;
+             goto doRook;
+
+           // 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 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;
+
          }
       }
 }
@@ -704,10 +723,15 @@ void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
 {
     register GenLegalClosure *cl = (GenLegalClosure *) closure;
 
-    if (!(flags & F_IGNORE_CHECK) &&
-       CheckTest(board, flags, rf, ff, rt, ft,
+    if (!(flags & F_IGNORE_CHECK) ) {
+      int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
+      if(promo) board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
+       check = CheckTest(board, flags, rf, ff, rt, ft,
                  kind == WhiteCapturesEnPassant ||
-                 kind == BlackCapturesEnPassant)) return;
+                 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) {
@@ -740,22 +764,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 ff, ft, k, left, right, swap;
     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;
@@ -774,9 +796,9 @@ 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 ||                             
+            (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)) &&
@@ -794,7 +816,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) &&
@@ -813,7 +835,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) &&
@@ -833,7 +855,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) &&
@@ -846,15 +868,16 @@ int GenLegal(board, flags, epfile, castlingRights, callback, closure)
        }
     }
 
-  if(gameInfo.variant == VariantFischeRandom) {
+  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 > 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]);
@@ -863,50 +886,50 @@ 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)
-                callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
+            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, swap ? ft : ff, 0, swap ? ff : 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;
-            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)
-                callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
+            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 != NoRights; k++) /* then if not checked */
+                if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
+            if(ft != NoRights && board[0][ft] == WhiteRook)
+                callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
         }
     } 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)
-                callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+            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, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : 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;
-            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)
-                callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+            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 != NoRights; k++) /* then if not checked */
+                if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
+            if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
+                callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
         }
     }
 
@@ -946,7 +969,7 @@ void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
    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. */ 
+   king is in check. */
 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
      Board board;
      int flags;
@@ -971,12 +994,11 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
        }
        board[rt][ft] = board[rf][ff];
        board[rf][ff] = EmptySquare;
-    }
+    } else board[rt][ft] = ff; // [HGM] drop
 
     /* 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. */
-    cl.check = 0;
     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) {
@@ -990,10 +1012,10 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
                           cl.check++;
               }
-
-             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1,
-                            CheckTestCallback, (VOIDSTAR) &cl);
-             goto undo_move;  /* 2-level break */
+             cl.check = 0;
+             GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
+             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 */
          }
       }
 
@@ -1007,11 +1029,46 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
        } else {
            board[rt][ft] = captured;
        }
-    }
+    } else board[rt][ft] = EmptySquare; // [HGM] drop
 
     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
 }
 
+ChessMove LegalDrop(board, flags, piece, rt, ft)
+     Board board;
+     int flags;
+     ChessSquare piece;
+     int rt, ft;
+{   // [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)
+        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;
+            for(r=1; r<BOARD_HEIGHT-1; r++)
+                if(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,
@@ -1035,25 +1092,26 @@ void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
       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, *castlingRights = board[CASTLING];
+
+    if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
+    piece = board[rf][ff];
+
     if (appData.debugMode) {
         int i;
         for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
         fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
     }
-    /* [HGM] Lance, Cobra and Falcon are wildcard pieces; consider all their moves legal */
+    /* [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  ||
-       piece == WhiteLance  || piece == BlackLance)
-        return NormalMove;
+       piece == WhiteCobra  || piece == BlackCobra)
+        return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
     cl.ff = ff;
@@ -1061,55 +1119,78 @@ ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, pro
     cl.ft = ft;
     cl.kind = IllegalMove;
     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
-    GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl);
+    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(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[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
+        } else {
+            if(rf != BOARD_HEIGHT-1) return IllegalMove;
+            if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
+        }
+    } else
     if(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. '=' means defer */
         if(rf != DROP_RANK && cl.kind == NormalMove) {
             ChessSquare piece = board[rf][ff];
 
             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
-            if(promoChar != NULLCHAR && promoChar != 'x' &&
-               promoChar != '+' && promoChar != '=' &&
-               ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
-                    cl.kind = IllegalMove;
+            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-4 || rt > BOARD_HEIGHT-4) ) {
+                     (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
-                             cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
-                    else /* promotion optional, default is promote */
-                             cl.kind = promoChar == '=' ? NormalMove  : WhitePromotionQueen;
-                   
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
-                                            NormalMove : IllegalMove;
+                       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 < 3 || rt < 3) ) {
+                if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
-                             cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
-                    else /* promotion optional, default is promote */
-                             cl.kind = promoChar == '=' ? NormalMove  : BlackPromotionQueen;
-
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
-                                            NormalMove : IllegalMove;
+                       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 && promoChar != 'x') {
+    if (promoChar != NULLCHAR) {
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
-       if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
-           cl.kind = 
-             PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar);
+       if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
+           ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
+           if(piece == EmptySquare)
+                cl.kind = ImpossibleMove; // non-existing piece
+           if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
+               if(promoChar != PieceToChar(BlackKing)) {
+                   if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
+                   if(piece == BlackLance) cl.kind = ImpossibleMove;
+               } else { // promotion to King allowed only if we do not haave two yet
+                   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 || piece == BlackPawn) cl.kind = ImpossibleMove; // cannot stay Pawn 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;
        }
     }
-    /* [HGM] For promotions, 'ToQueen' = optional, 'ToKnight' = mandatory */
     return cl.kind;
 }
 
@@ -1134,16 +1215,15 @@ 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, 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++) { 
+    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 ) {
@@ -1165,24 +1245,33 @@ int MateTest(board, flags, epfile, castlingRights)
                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 {
+        if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // 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) ? 
+
+        return inCheck ? MT_CHECKMATE
+                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
                          MT_STAINMATE : MT_STALEMATE;
     }
 }
 
-     
+
 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
                                    int rf, int ff, int rt, int ft,
                                    VOIDSTAR closure));
@@ -1195,6 +1284,12 @@ 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)
+        wildCard = TRUE;
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
          || PieceToChar(board[rf][ff]) == '~'
@@ -1202,26 +1297,31 @@ 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->kind = kind;
+       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, 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->count = closure->captures = 0;
     closure->rf = closure->ff = closure->rt = closure->ft = 0;
     closure->kind = ImpossibleMove;
     if (appData.debugMode) {
@@ -1229,12 +1329,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;
@@ -1248,73 +1347,61 @@ void Disambiguate(board, flags, epfile, closure)
        }
     }
 
+    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[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
+        } else {
+            if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
+            if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
+        }
+    } else
     if(gameInfo.variant == VariantShogi) {
-        /* [HGM] Shogi promotions. '=' means defer */
+        /* [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;
-#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;
+            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 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) ) {
+                     (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
-                             closure->kind = c == '=' ? IllegalMove : WhitePromotionKnight;
-                    else /* promotion optional, default is promote */
-                             closure->kind = c == '=' ? NormalMove  : WhitePromotionQueen;
-                   
-                } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
-                                            NormalMove : IllegalMove;
+                       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 < 3 || closure->rt < 3) ) {
+                if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
-                             closure->kind = c == '=' ? IllegalMove : BlackPromotionKnight;
-                    else /* promotion optional, default is promote */
-                             closure->kind = c == '=' ? NormalMove  : BlackPromotionQueen;
-
-                } else closure->kind = (c == NULLCHAR || c == 'x' || c == '=') ?
-                                            NormalMove : IllegalMove;
+                       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->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') {
-       if (closure->kind == WhitePromotionQueen
-           || closure->kind == BlackPromotionQueen) {
-           closure->kind = 
-             PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0,
-                                 closure->promoCharIn);
-       } else {
-           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);
-    else closure->promoChar = '=';
-    if (closure->promoChar == 'x') closure->promoChar = NULLCHAR;
+    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 == VariantGreat)
+                c = PieceToChar(BlackMan);
+            else
+                c = PieceToChar(BlackQueen);
+        } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
+    } 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;
     }
@@ -1325,9 +1412,6 @@ void Disambiguate(board, flags, epfile, closure)
        */
        closure->kind = IllegalMove;
     }
-    if(closure->kind == IllegalMove)
-    /* [HGM] might be a variant we don't understand, pass on promotion info */
-        closure->promoChar = ToLower(closure->promoCharIn);
     if (appData.debugMode) {
         fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
         closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
@@ -1361,7 +1445,7 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
     register CoordsToAlgebraicClosure *cl =
       (CoordsToAlgebraicClosure *) closure;
 
-    if (rt == cl->rt && ft == cl->ft &&
+    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)
@@ -1378,17 +1462,16 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
            } 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, 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];
@@ -1397,7 +1480,7 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
     ChessMove kind;
     char *outp = out, c;
     CoordsToAlgebraicClosure cl;
-    
+
     if (rf == DROP_RANK) {
        /* Bughouse piece drop */
        *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
@@ -1419,12 +1502,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;
        }
@@ -1460,28 +1542,31 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
        }
         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) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
-            return LegalityTest(board, flags, epfile, initialRights,
-                               rf, ff, rt, ft, promoChar);
+         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)
-               strcpy(out, "O-O");
+             snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
             else
-               strcpy(out, "O-O-O");
+             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"
@@ -1491,8 +1576,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 */
@@ -1505,15 +1589,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;
        }
@@ -1547,60 +1629,29 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
         if(rt+ONE <= '9')
            *outp++ = rt + ONE;
         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
-       *outp = NULLCHAR;
         if (gameInfo.variant == VariantShogi) {
             /* [HGM] in Shogi non-pawns can promote */
-            if(flags & F_WHITE_ON_MOVE) {
-                if( (int) cl.piece < (int) WhiteWazir &&
-                     (rf > BOARD_HEIGHT-4 || rt > BOARD_HEIGHT-4) ) {
-                    if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
-                         piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
-                             cl.kind = promoChar == '=' ? IllegalMove : WhitePromotionKnight;
-                    else cl.kind =  WhitePromotionQueen; /* promotion optional */
-                   
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
-                                            NormalMove : IllegalMove;
-            } else {
-                if( (int) cl.piece < (int) BlackWazir && (rf < 3 || rt < 3) ) {
-                    if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
-                         piece == BlackKnight && rt < 2 ) /* promotion obligatory */
-                             cl.kind = promoChar == '=' ? IllegalMove : BlackPromotionKnight;
-                    else cl.kind =  BlackPromotionQueen; /* promotion optional */
-                } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
-                                            NormalMove : IllegalMove;
-            }
-            if(cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) {
-                /* for optional promotions append '+' or '=' */
-                if(promoChar == '=') {
-                    *outp++ = '=';
-                    cl.kind = NormalMove;
-                } else *outp++ = '+';
-                *outp = NULLCHAR;
-            } else if(cl.kind == IllegalMove) {
-                /* Illegal move specifies any given promotion */
-                if(promoChar != NULLCHAR && promoChar != 'x') {
-                    *outp++ = '=';
-                    *outp++ = ToUpper(promoChar);
-                    *outp = NULLCHAR;
-                }
-            }
+            *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
+        }
+        else if (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;
        
-      /* [HGM] Always long notation for fairies we don't know */
-      case WhiteFalcon:
-      case BlackFalcon:
-      case WhiteLance:
-      case BlackLance:
-      case WhiteGrasshopper:
-      case BlackGrasshopper:
       case EmptySquare:
        /* Moving a nonexistent piece */
        break;
     }
-    
+
     /* Not a legal move, even ignoring check.
-       If there was a piece on the from square, 
+       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"
@@ -1608,13 +1659,19 @@ ChessMove CoordsToAlgebraic(board, flags, epfile,
        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++ = ToUpper(PieceToChar(piece));
     }
+  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')
@@ -1700,8 +1757,8 @@ void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
     }
     // 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   ) { 
+       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;
@@ -1740,10 +1797,10 @@ int PerpetualChase(int first, int last)
         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), EP_NONE, initialRights, 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, 
+       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");
        }
@@ -1752,10 +1809,10 @@ int PerpetualChase(int first, int last)
        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), EP_NONE, initialRights, 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, 
+       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);
        }
@@ -1767,11 +1824,11 @@ int PerpetualChase(int first, int last)
            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) 
+           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), EP_NONE, initialRights, chaseStack[j].rt, 
+                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
@@ -1792,20 +1849,20 @@ int PerpetualChase(int first, int last)
            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), EP_NONE, initialRights, ProtectedCallback, &cl); // try all moves
+            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--; /* ! */ 
+               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, 
+       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");
        }
@@ -1831,8 +1888,8 @@ int PerpetualChase(int first, int last)
            }
        }
         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
-       if(appData.debugMode) { int n; 
-            for(n=0; n<preyStackPointer; n++) 
+       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);
        }