Implement variant ASEAN
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index 02ced64..8cd3434 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -146,7 +146,8 @@ CopyBoard (Board to, Board from)
     for (i = 0; i < BOARD_HEIGHT; i++)
       for (j = 0; j < BOARD_WIDTH; j++)
        to[i][j] = from[i][j];
-    for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
+    for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
+       to[VIRGIN][j] = from[VIRGIN][j],
        to[CASTLING][j] = from[CASTLING][j];
     to[HOLDINGS_SET] = 0; // flag used in ICS play
 }
@@ -519,7 +520,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR 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:
@@ -572,7 +573,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR 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;
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
             case SHOGI WhitePawn:
             case SHOGI WhiteFerz:
                   if (rf < BOARD_HEIGHT-1 &&
@@ -583,7 +584,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
               break;
 
             case BlackMan:
-                if(gameInfo.variant != VariantMakruk) goto commoner;
+                if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
             case SHOGI BlackPawn:
             case SHOGI BlackFerz:
                   if (rf > 0 &&
@@ -672,14 +673,14 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                           rf, ff, rf + 1, ff, closure);
              for (s = -1; s <= 1; s += 2) {
                  if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
-                     callback(board, flags, 
+                     callback(board, flags,
                               rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
                               rf, ff, rf + 1, ff + s, closure);
                  if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
                      callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
              }
              break;
-               
+
            case BlackLance:
              if(gameInfo.variant == VariantSuper) goto Amazon;
              if (rf > 0 && WhitePiece(board[rf - 1][ff]))
@@ -688,7 +689,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
                           rf, ff, rf - 1, ff, closure);
              for (s = -1; s <= 1; s += 2) {
                  if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
-                     callback(board, flags, 
+                     callback(board, flags,
                               rf <= promoRank ? BlackPromotion : NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
@@ -720,7 +721,7 @@ extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
                                VOIDSTAR closure));
 
-void 
+void
 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     register GenLegalClosure *cl = (GenLegalClosure *) closure;
@@ -1053,7 +1054,8 @@ LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
     n = PieceToNumber(piece);
-    if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+    if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+       && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
         return ImpossibleMove; // piece not available
     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
@@ -1088,9 +1090,6 @@ LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, in
 {
     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
 
-//    if (appData.debugMode) {
-//        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
-//    }
     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
        cl->captures++; // [HGM] losers: count legal captures
     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
@@ -1100,18 +1099,13 @@ LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, in
 ChessMove
 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
 {
-    LegalityTestClosure cl; ChessSquare piece, filterPiece, *castlingRights = board[CASTLING];
+    LegalityTestClosure cl; ChessSquare piece, filterPiece;
 
     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
     piece = filterPiece = board[rf][ff];
-    if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece; 
+    if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
 
-    if (appData.debugMode) {
-        int i;
-        for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
-        fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
-    }
     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
     /* (perhaps we should disallow moves that obviously leave us in check?)              */
     if(piece == WhiteFalcon || piece == BlackFalcon ||
@@ -1134,9 +1128,11 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC
     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
         if(board[rf][ff] < BlackPawn) { // white
             if(rf != 0) return IllegalMove; // must be on back rank
+            if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
         } else {
             if(rf != BOARD_HEIGHT-1) return IllegalMove;
+            if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
         }
     } else
@@ -1155,7 +1151,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
             else if(flags & F_WHITE_ON_MOVE) {
                 if( (int) piece < (int) WhiteWazir &&
-                     (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
+                     (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
@@ -1234,7 +1230,6 @@ MateTest (Board board, int flags)
            else hisPieces++;
        }
     }
-    if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
     switch(gameInfo.variant) { // [HGM] losers: extinction wins
        case VariantShatranj:
                if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
@@ -1269,7 +1264,7 @@ MateTest (Board board, int flags)
        else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
 
         return inCheck ? MT_CHECKMATE
-                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
+                      : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantShogi) ?
                          MT_STAINMATE : MT_STALEMATE;
     }
 }
@@ -1322,11 +1317,6 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
     closure->count = closure->captures = 0;
     closure->rf = closure->ff = closure->rt = closure->ft = 0;
     closure->kind = ImpossibleMove;
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
-                             closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
-                             closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
-    }
     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
     fFilter = closure->ftIn;
     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
@@ -1345,14 +1335,23 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
        if (closure->count == 0) {
            /* No, it's not even that */
-    if (appData.debugMode) { int i, j;
-       for(i=BOARD_HEIGHT-1; i>=0; i--) {
-               for(j=0; j<BOARD_WIDTH; j++)
-                       fprintf(debugFP, "%3d", (int) board[i][j]);
-               fprintf(debugFP, "\n");
-       }
-    }
+         if(!appData.testLegality && closure->pieceIn != EmptySquare) {
+           int f, r; // if there is only a single piece of the requested type on the board, use that
+           closure->rt = closure->rtIn, closure->ft = closure->ftIn;
+           for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+               if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
+           if(closure->count > 1) illegal = 0; // ambiguous
+         }
+         if(closure->count == 0) {
+           if (appData.debugMode) { int i, j;
+               for(i=BOARD_HEIGHT-1; i>=0; i--) {
+                   for(j=0; j<BOARD_WIDTH; j++)
+                       fprintf(debugFP, "%3d", (int) board[i][j]);
+                   fprintf(debugFP, "\n");
+               }
+           }
            return;
+         }
        }
     }
 
@@ -1360,9 +1359,11 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
         if(closure->piece < BlackPawn) { // white
             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
+            if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
         } else {
             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
+            if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
         }
     } else
@@ -1382,7 +1383,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
                     else /* promotion optional, default is defer */
-                       closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; 
+                       closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
             } else {
                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
@@ -1399,7 +1400,8 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
     } else
     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
-            if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
+            if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+               gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
                 c = PieceToChar(BlackFerz);
             else if(gameInfo.variant == VariantGreat)
                 c = PieceToChar(BlackMan);
@@ -1423,11 +1425,6 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure)
        */
        closure->kind = IllegalMove;
     }
-    if (appData.debugMode) {
-        fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
-        closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
-       closure->promoChar >= ' ' ? closure->promoChar:'-');
-    }
 }
 
 
@@ -1501,8 +1498,6 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
     piece = board[rf][ff];
     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
 
-  if (appData.debugMode)
-          fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
     switch (piece) {
       case WhitePawn:
       case BlackPawn:
@@ -1532,8 +1527,6 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
        }
        /* Use promotion suffix style "=Q" */
        *outp = NULLCHAR;
-  if (appData.debugMode)
-          fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
         if (promoChar != NULLCHAR) {
             if(gameInfo.variant == VariantShogi) {
                 /* [HGM] ... but not in Shogi! */
@@ -1637,7 +1630,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
             /* [HGM] in Shogi non-pawns can promote */
             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
         }
-        else if (gameInfo.variant != VariantSuper && promoChar && 
+        else if (gameInfo.variant != VariantSuper && promoChar &&
                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
             *outp++ = '=';
             *outp++ = ToUpper(promoChar);
@@ -1648,7 +1641,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p
         }
        *outp = NULLCHAR;
         return cl.kind;
-       
+
       case EmptySquare:
        /* Moving a nonexistent piece */
        break;