Silence more rpm warnings
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index 830e0bb..f930231 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -126,6 +126,7 @@ ChessSquare CharToPiece(c)
      int c;
 {
      int i;
      int c;
 {
      int i;
+     if(c == '.') return EmptySquare;
      for(i=0; i< (int) EmptySquare; i++)
           if(pieceNickName[i] == c) return (ChessSquare) i;
      for(i=0; i< (int) EmptySquare; i++)
      for(i=0; i< (int) EmptySquare; i++)
           if(pieceNickName[i] == c) return (ChessSquare) i;
      for(i=0; i< (int) EmptySquare; i++)
@@ -182,6 +183,7 @@ void GenPseudoLegal(board, flags, callback, closure)
     for (rf = 0; rf < BOARD_HEIGHT; rf++)
       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
           ChessSquare piece;
     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;
 
          if (flags & F_WHITE_ON_MOVE) {
              if (!WhitePiece(board[rf][ff])) continue;
@@ -194,7 +196,7 @@ void GenPseudoLegal(board, flags, callback, closure)
           if(gameInfo.variant == VariantShogi)
                  piece = (ChessSquare) ( SHOGI piece );
 
           if(gameInfo.variant == VariantShogi)
                  piece = (ChessSquare) ( SHOGI piece );
 
-          switch (piece) {
+          switch ((int)piece) {
             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
            default:
              /* can't happen ([HGM] except for faries...) */
             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
            default:
              /* can't happen ([HGM] except for faries...) */
@@ -418,7 +420,8 @@ void GenPseudoLegal(board, flags, callback, closure)
                           && !SameColor(board[rf][ff], board[rt][ft]))
                                callback(board, flags, NormalMove,
                                         rf, ff, rt, ft, closure);
                           && !SameColor(board[rf][ff], board[rt][ft]))
                                callback(board, flags, NormalMove,
                                         rf, ff, rt, ft, closure);
-                      if(gameInfo.variant != VariantFairy && gameInfo.variant != VariantGreat) continue;
+                      if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier
+                                                             || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
                       ft = ff + fs;
                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
                       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)
@@ -426,8 +429,26 @@ void GenPseudoLegal(board, flags, callback, closure)
                                callback(board, flags, NormalMove,
                                         rf, ff, rt, ft, closure);
                  }
                                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;
 
                 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:
             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
             case SHOGI WhiteCardinal:
             case SHOGI BlackCardinal:
@@ -495,6 +516,7 @@ void GenPseudoLegal(board, flags, callback, closure)
                      if (SameColor(board[rf][ff], board[rt][ft])) continue;
                      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
                  }
                      if (SameColor(board[rf][ff], board[rt][ft])) continue;
                      callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
                  }
+              if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
               goto doRook;
               
             /* Shogi Dragon King has to continue as Ferz after Rook moves */
               goto doRook;
               
             /* Shogi Dragon King has to continue as Ferz after Rook moves */
@@ -506,6 +528,7 @@ void GenPseudoLegal(board, flags, callback, closure)
             case WhiteMarshall:
             case BlackMarshall:
               m++;
             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:
 
             /* Shogi Rooks are ordinary Rooks */
             case SHOGI WhiteRook:
@@ -522,7 +545,7 @@ void GenPseudoLegal(board, flags, callback, closure)
                      if (SameColor(board[rf][ff], board[rt][ft])) break;
                      callback(board, flags, NormalMove,
                               rf, ff, rt, ft, 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;
                  }
                 if(m==1) goto mounted;
                 if(m==2) goto finishSilver;
@@ -622,12 +645,60 @@ void GenPseudoLegal(board, flags, callback, closure)
                     }
                  }
              break;
                     }
                  }
              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:
            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;
 
              callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
              break;
 
@@ -654,10 +725,15 @@ void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
 {
     register GenLegalClosure *cl = (GenLegalClosure *) 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 == 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) {
     if (flags & F_ATOMIC_CAPTURE) {
       if (board[rt][ft] != EmptySquare ||
          kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
@@ -697,7 +773,7 @@ int GenLegal(board, flags, callback, closure)
      VOIDSTAR closure;
 {
     GenLegalClosure cl;
      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, *castlingRights = board[CASTLING];
 
     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
 
@@ -794,10 +870,11 @@ int GenLegal(board, flags, callback, closure)
        }
     }
 
        }
     }
 
-  if(flags & F_FRC_TYPE_CASTLING) {
+  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 */
 
     /* 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) {
 
 
     if ((flags & F_WHITE_ON_MOVE) != 0) {
@@ -816,7 +893,7 @@ int GenLegal(board, flags, callback, closure)
             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)
             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);
+                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;
 
             ft = castlingRights[1]; /* Rook file if we have A-side rights */
             left  = BOARD_LEFT+2;
@@ -828,7 +905,7 @@ int GenLegal(board, flags, callback, closure)
             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)
             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);
+                callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
         }
     } else {
         ff = castlingRights[5]; /* King file if we have any rights */
         }
     } else {
         ff = castlingRights[5]; /* King file if we have any rights */
@@ -842,7 +919,7 @@ int GenLegal(board, flags, callback, closure)
             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)
             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);
+                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;
 
             ft = castlingRights[4]; /* Rook file if we have A-side rights */
             left  = BOARD_LEFT+2;
@@ -854,7 +931,7 @@ int GenLegal(board, flags, callback, closure)
             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)
             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);
+                callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
         }
     }
 
         }
     }
 
@@ -924,10 +1001,10 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
     /* For compatibility with ICS wild 9, we scan the board in the
        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
        and we test only whether that one is in check. */
     /* For 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) {
     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
        for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
           if (board[cl.rking][cl.fking] == king) {
+             cl.check = 0;
               if(gameInfo.variant == VariantXiangqi) {
                   /* [HGM] In Xiangqi opposing Kings means check as well */
                   int i, dir;
               if(gameInfo.variant == VariantXiangqi) {
                   /* [HGM] In Xiangqi opposing Kings means check as well */
                   int i, dir;
@@ -938,9 +1015,9 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
                           cl.check++;
               }
                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
                           cl.check++;
               }
-
              GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
              GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
-             goto undo_move;  /* 2-level break */
+             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 */
          }
       }
 
          }
       }
 
@@ -982,6 +1059,8 @@ if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt
                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
             // should still test if we mate with this Pawn
         }
                 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 ) )
     } else {
         if( (piece == WhitePawn || piece == BlackPawn) &&
             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
@@ -1030,11 +1109,10 @@ ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
         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);
     }
         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 ||
     /* (perhaps we should disallow moves that obviously leave us in check?)              */
     if(piece == WhiteFalcon || piece == BlackFalcon ||
-       piece == WhiteCobra  || piece == BlackCobra  ||
-       piece == WhiteLance  || piece == BlackLance)
+       piece == WhiteCobra  || piece == BlackCobra)
         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
 
     cl.rf = rf;
@@ -1049,6 +1127,15 @@ ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
        return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
 
     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
        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) {
     if(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. '=' means defer */
         if(rf != DROP_RANK && cl.kind == NormalMove) {
@@ -1085,8 +1172,23 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
     if (promoChar != NULLCHAR) {
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
     if (promoChar != NULLCHAR) {
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
-           if(CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar)) == EmptySquare)
+           ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
+           if(piece == EmptySquare)
                 cl.kind = ImpossibleMove; // non-existing piece
                 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;
        }
        } else {
            cl.kind = IllegalMove;
        }
@@ -1150,13 +1252,13 @@ int MateTest(board, flags)
     if (cl.count > 0) {
        return inCheck ? MT_CHECK : MT_NONE;
     } else {
     if (cl.count > 0) {
        return inCheck ? MT_CHECK : MT_NONE;
     } else {
-        if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper || gameInfo.variant != VariantGreat) { // drop game
+        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);
             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 MT_CHECK; // we can resolve check by legal drop
+                        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
                     }
         }
        if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
@@ -1188,8 +1290,7 @@ void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
 
     // [HGM] wild: for wild-card pieces rt and rf are dummies
     if(piece == WhiteFalcon || piece == BlackFalcon ||
 
     // [HGM] wild: for wild-card pieces rt and rf are dummies
     if(piece == WhiteFalcon || piece == BlackFalcon ||
-       piece == WhiteCobra  || piece == BlackCobra  ||
-       piece == WhiteLance  || piece == BlackLance)
+       piece == WhiteCobra  || piece == BlackCobra)
         wildCard = TRUE;
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
         wildCard = TRUE;
 
     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
@@ -1211,7 +1312,7 @@ void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
          cl->ft = wildCard ? cl->ftIn : ft;
          cl->kind = kind;
        }
          cl->ft = wildCard ? cl->ftIn : ft;
          cl->kind = kind;
        }
-       cl->captures += (board[cl->rt][cl->ft] != EmptySquare); // [HGM] oneclick: count captures
+       cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
     }
 }
 
     }
 }
 
@@ -1249,6 +1350,15 @@ void Disambiguate(board, flags, closure)
     }
 
     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
     }
 
     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. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
     if(gameInfo.variant == VariantShogi) {
         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
@@ -1337,7 +1447,7 @@ void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
     register CoordsToAlgebraicClosure *cl =
       (CoordsToAlgebraicClosure *) 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)
         (board[rf][ff] == cl->piece
          || PieceToChar(board[rf][ff]) == '~' &&
             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
@@ -1456,9 +1566,9 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
             ((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)
             ((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)
-             safeStrCpy(out, "O-O", MOVE_LEN);
+             snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
             else
             else
-             safeStrCpy(out, "O-O-O", MOVE_LEN);
+             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"
 
            /* This notation is always unambiguous, unless there are
               kings on both the d and e files, with "wild castling"
@@ -1525,16 +1635,18 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
             /* [HGM] in Shogi non-pawns can promote */
             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
         }
             /* [HGM] in Shogi non-pawns can promote */
             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
         }
+        else if (gameInfo.variant != VariantSuper && promoChar && 
+                 (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;
        *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;
       case EmptySquare:
        /* Moving a nonexistent piece */
        break;
@@ -1549,13 +1661,19 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
        a piece of the same color.
     */
     outp = out;
        a piece of the same color.
     */
     outp = out;
+    c = 0;
     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
     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));
     }
        *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'; }
     *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')
     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
     *outp++ = ft + AAA;
     if(rt+ONE <= '9')
@@ -1585,11 +1703,11 @@ typedef struct {
 int preyStackPointer, chaseStackPointer;
 
 struct {
 int preyStackPointer, chaseStackPointer;
 
 struct {
-char rf, ff, rt, ft;
+unsigned char rf, ff, rt, ft;
 } chaseStack[100];
 
 struct {
 } chaseStack[100];
 
 struct {
-char rank, file;
+unsigned char rank, file;
 } preyStack[100];
 
 
 } preyStack[100];