Add -afterTourney option
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index dcb4300..093df63 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -70,7 +70,7 @@ int SameColor P((ChessSquare, ChessSquare));
 int PosFlags(int index);
 
 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
-
+int quickFlag;
 
 int WhitePiece(piece)
      ChessSquare piece;
@@ -84,6 +84,7 @@ int BlackPiece(piece)
     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
 }
 
+#if 0
 int SameColor(piece1, piece2)
      ChessSquare piece1, piece2;
 {
@@ -96,6 +97,9 @@ int SameColor(piece1, piece2)
            (int) piece2 >= (int) BlackPawn &&
             (int) piece2 <  (int) EmptySquare);
 }
+#else
+#define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn))
+#endif
 
 char pieceToChar[] = {
                         'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
@@ -179,18 +183,16 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
     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;
+    int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
 
     for (rf = 0; rf < BOARD_HEIGHT; rf++)
       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
           ChessSquare piece;
-          int rookRange = 1000;
+          int rookRange;
 
-         if (flags & F_WHITE_ON_MOVE) {
-             if (!WhitePiece(board[rf][ff])) continue;
-         } else {
-             if (!BlackPiece(board[rf][ff])) continue;
-         }
+         if(board[rf][ff] == EmptySquare) continue;
+         if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
+         rookRange = 1000;
           m = 0; piece = board[rf][ff];
           if(PieceToChar(piece) == '~')
                  piece = (ChessSquare) ( DEMOTED piece );
@@ -228,12 +230,12 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                           rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
                           rf, ff, rf + 1, ff, closure);
              }
-             if (rf == 1 && board[2][ff] == EmptySquare &&
+             if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
                   gameInfo.variant != VariantShatranj && /* [HGM] */
                   gameInfo.variant != VariantCourier  && /* [HGM] */
-                  board[3][ff] == EmptySquare ) {
+                  board[rf+2][ff] == EmptySquare ) {
                       callback(board, flags, NormalMove,
-                               rf, ff, 3, ff, closure);
+                               rf, ff, rf+2, ff, closure);
              }
              for (s = -1; s <= 1; s += 2) {
                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
@@ -243,13 +245,13 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                               rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
                               rf, ff, rf + 1, ff + s, closure);
                  }
-                 if (rf == BOARD_HEIGHT-4) {
+                 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                         (epfile == ff + s || epfile == EP_UNKNOWN) &&
-                          board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
-                          board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
+                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
+                          board[rf][ff + s] == BlackPawn &&
+                          board[rf+1][ff + s] == EmptySquare) {
                          callback(board, flags, WhiteCapturesEnPassant,
-                                  rf, ff, 5, ff + s, closure);
+                                  rf, ff, rf+1, ff + s, closure);
                      }
                  }
              }
@@ -278,12 +280,12 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                           rf <= promoRank ? BlackPromotion : NormalMove,
                           rf, ff, rf - 1, ff, closure);
              }
-             if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
+             if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
                   gameInfo.variant != VariantShatranj && /* [HGM] */
                   gameInfo.variant != VariantCourier  && /* [HGM] */
-                 board[BOARD_HEIGHT-4][ff] == EmptySquare) {
+                 board[rf-2][ff] == EmptySquare) {
                  callback(board, flags, NormalMove,
-                          rf, ff, BOARD_HEIGHT-4, ff, closure);
+                          rf, ff, rf-2, ff, closure);
              }
              for (s = -1; s <= 1; s += 2) {
                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
@@ -293,13 +295,13 @@ void GenPseudoLegal(board, flags, callback, closure, filter)
                               rf <= promoRank ? BlackPromotion : NormalMove,
                               rf, ff, rf - 1, ff + s, closure);
                  }
-                 if (rf == 3) {
+                 if (rf < BOARD_HEIGHT>>1) {
                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
-                         (epfile == ff + s || epfile == EP_UNKNOWN) &&
-                         board[3][ff + s] == WhitePawn &&
-                         board[2][ff + s] == EmptySquare) {
+                         (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
+                         board[rf][ff + s] == WhitePawn &&
+                         board[rf-1][ff + s] == EmptySquare) {
                          callback(board, flags, BlackCapturesEnPassant,
-                                  rf, ff, 2, ff + s, closure);
+                                  rf, ff, rf-1, ff + s, closure);
                      }
                  }
              }
@@ -715,6 +717,7 @@ typedef struct {
 } GenLegalClosure;
 
 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
+Board xqCheckers, nullBoard;
 
 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
                                int rf, int ff, int rt, int ft,
@@ -783,14 +786,15 @@ int GenLegal(board, flags, callback, closure, filter)
     int ff, ft, k, left, right, swap;
     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
+    int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
 
     cl.cb = callback;
     cl.cl = closure;
+    xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
     if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
 
-    if (!ignoreCheck &&
-       CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
+    if (inCheck) return TRUE;
 
     /* Generate castling moves */
     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
@@ -969,7 +973,11 @@ void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
 {
     register CheckTestClosure *cl = (CheckTestClosure *) closure;
 
-    if (rt == cl->rking && ft == cl->fking) cl->check++;
+    if (rt == cl->rking && ft == cl->fking) {
+       if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
+       cl->check++;
+       xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
+    }
 }
 
 
@@ -1109,6 +1117,7 @@ ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
 {
     LegalityTestClosure cl; ChessSquare piece, filterPiece, *castlingRights = board[CASTLING];
 
+    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; 
@@ -1194,7 +1203,8 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo
                    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 == WhitePawn && rt == BOARD_HEIGHT-1 ||
+                         piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
            else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
            else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
@@ -1262,7 +1272,8 @@ int MateTest(board, flags)
     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
+                                                                      && gameInfo.variant != VariantGrand) { // drop game
             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
@@ -1333,6 +1344,7 @@ void Disambiguate(board, flags, closure)
 {
     int illegal = 0; char c = closure->promoCharIn;
 
+    if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
     closure->count = closure->captures = 0;
     closure->rf = closure->ff = closure->rt = closure->ft = 0;
     closure->kind = ImpossibleMove;
@@ -1343,6 +1355,15 @@ void Disambiguate(board, flags, closure)
     }
     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
     fFilter = closure->ftIn;
+    if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
+        GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
+        if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
+            closure->count = closure->captures = 0;
+            closure->rf = closure->ff = closure->rt = closure->ft = 0;
+            closure->kind = ImpossibleMove;
+            GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
+        }
+    } else
     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
     if (closure->count == 0) {
        /* See if it's an illegal move due to check */
@@ -1408,6 +1429,8 @@ void Disambiguate(board, flags, closure)
                 c = PieceToChar(BlackFerz);
             else if(gameInfo.variant == VariantGreat)
                 c = PieceToChar(BlackMan);
+            else if(gameInfo.variant == VariantGrand)
+                   closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
             else
                 c = PieceToChar(BlackQueen);
         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
@@ -1496,6 +1519,7 @@ ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
     CoordsToAlgebraicClosure cl;
 
     if (rf == DROP_RANK) {
+       if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
        /* Bughouse piece drop */
        *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
        *outp++ = '@';
@@ -1823,7 +1847,9 @@ 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;
+       CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
        GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
+       xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
        if(appData.debugMode) { int n;
            for(n=0; n<chaseStackPointer; n++)
                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
@@ -1852,6 +1878,8 @@ int PerpetualChase(int first, int last)
            }
 
            // the attack is on a lower piece, or on a pinned or blocked equal one
+           CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
+           CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
             // test if the victim is protected by a true protector. First make the capture.
            captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
            boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
@@ -1863,7 +1891,9 @@ 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);
            }
+           xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
+           xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
            // unmake the capture
            boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
@@ -1917,5 +1947,6 @@ int PerpetualChase(int first, int last)
             }
         }
     }
-    return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
+    return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
+                               : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
 }