Let -sweepPromotions also work for click-click moves
[xboard.git] / backend.c
index f028af4..eaaa87a 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -396,6 +396,7 @@ PosFlags(index)
   case VariantShatranj:
   case VariantCourier:
   case VariantMakruk:
+  case VariantGrand:
     flags &= ~F_ALL_CASTLE_OK;
     break;
   default:
@@ -599,6 +600,13 @@ ChessSquare JanusArray[2][BOARD_FILES] = {
         BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
 };
 
+ChessSquare GrandArray[2][BOARD_FILES] = {
+    { EmptySquare, WhiteKnight, WhiteBishop, WhiteQueen, WhiteKing,
+        WhiteMarshall, WhiteAngel, WhiteBishop, WhiteKnight, EmptySquare },
+    { EmptySquare, BlackKnight, BlackBishop, BlackQueen, BlackKing,
+        BlackMarshall, BlackAngel, BlackBishop, BlackKnight, EmptySquare }
+};
+
 #ifdef GOTHIC
 ChessSquare GothicArray[2][BOARD_FILES] = {
     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
@@ -1112,13 +1120,14 @@ InitBackEnd1()
       case VariantAtomic:     /* should work except for win condition */
       case Variant3Check:     /* should work except for win condition */
       case VariantShatranj:   /* should work except for all win conditions */
-      case VariantMakruk:     /* should work except for daw countdown */
+      case VariantMakruk:     /* should work except for draw countdown */
       case VariantBerolina:   /* might work if TestLegality is off */
       case VariantCapaRandom: /* should work */
       case VariantJanus:      /* should work */
       case VariantSuper:      /* experimental */
       case VariantGreat:      /* experimental, requires legality testing to be off */
       case VariantSChess:     /* S-Chess, should work */
+      case VariantGrand:      /* should work */
       case VariantSpartan:    /* should work */
        break;
       }
@@ -4641,8 +4650,8 @@ ParseBoard12(string)
            safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0]));
            strcat(moveList[moveNum - 1], "\n");
 
-            if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper
-                                 && gameInfo.variant != VariantGreat) // inherit info that ICS does not give from previous board
+            if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
+                                 && gameInfo.variant != VariantGrand) // inherit info that ICS does not give from previous board
               for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
                 ChessSquare old, new = boards[moveNum][k][j];
                   if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
@@ -4888,6 +4897,15 @@ SendMoveToProgram(moveNum, cps)
          else SendToProgram("O-O-O\n", cps);
        }
        else SendToProgram(moveList[moveNum], cps);
+      } else
+      if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
+       if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move
+         snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0],
+                                             moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
+       } else
+         snprintf(buf, MSG_SIZ, "%c%d%c%d%s", moveList[moveNum][0], moveList[moveNum][1] - '0',
+                                              moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
+       SendToProgram(buf, cps);
       }
       else SendToProgram(moveList[moveNum], cps);
       /* End of additions by Tord */
@@ -5746,6 +5764,14 @@ InitPosition(redraw)
     case VariantTwoKings:
       pieces = twoKingsArray;
       break;
+    case VariantGrand:
+      pieces = GrandArray;
+      nrCastlingRights = 0;
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
+      gameInfo.boardWidth = 10;
+      gameInfo.boardHeight = 10;
+      gameInfo.holdingsSize = 7;
+      break;
     case VariantCapaRandom:
       shuffleOpenings = TRUE;
     case VariantCapablanca:
@@ -5862,7 +5888,7 @@ InitPosition(redraw)
 
     pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
     if(pawnRow < 1) pawnRow = 1;
-    if(gameInfo.variant == VariantMakruk) pawnRow = 2;
+    if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) pawnRow = 2;
 
     /* User pieceToChar list overrules defaults */
     if(appData.pieceToCharTable != NULL)
@@ -5876,7 +5902,7 @@ InitPosition(redraw)
             initialPosition[i][j] = s;
 
         if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue;
-        initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth];
+        initialPosition[gameInfo.variant == VariantGrand][j] = pieces[0][j-gameInfo.holdingsWidth];
         initialPosition[pawnRow][j] = WhitePawn;
         initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn;
         if(gameInfo.variant == VariantXiangqi) {
@@ -5889,7 +5915,13 @@ InitPosition(redraw)
                 }
             }
         }
-        initialPosition[BOARD_HEIGHT-1][j] =  pieces[1][j-gameInfo.holdingsWidth];
+        if(gameInfo.variant == VariantGrand) {
+            if(j==BOARD_LEFT || j>=BOARD_RGHT-1) {
+               initialPosition[0][j] = WhiteRook;
+               initialPosition[BOARD_HEIGHT-1][j] = BlackRook;
+            }
+        }
+        initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand)][j] =  pieces[1][j-gameInfo.holdingsWidth];
     }
     if( (gameInfo.variant == VariantShogi) && !overrule ) {
 
@@ -6048,7 +6080,7 @@ DefaultPromoChoice(int white)
 static int autoQueen; // [HGM] oneclick
 
 int
-HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
+HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice, int sweepSelect)
 {
     /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
     /* [HGM] add Shogi promotions */
@@ -6068,7 +6100,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
     if(gameInfo.variant == VariantShogi) {
         promotionZoneSize = BOARD_HEIGHT/3;
         highestPromotingPiece = (int)WhiteFerz;
-    } else if(gameInfo.variant == VariantMakruk) {
+    } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) {
         promotionZoneSize = 3;
     }
 
@@ -6130,9 +6162,9 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
     }
     // give caller the default choice even if we will not make it
     *promoChoice = ToLower(PieceToChar(defaultPromoChoice));
-    if(gameInfo.variant == VariantShogi) *promoChoice = '+';
-    if(appData.sweepSelect && gameInfo.variant != VariantGreat
-                          && gameInfo.variant != VariantShogi
+    if(gameInfo.variant == VariantShogi) *promoChoice = (defaultPromoChoice == piece ? '=' : '+');
+    if(        sweepSelect && gameInfo.variant != VariantGreat
+                          && gameInfo.variant != VariantGrand
                           && gameInfo.variant != VariantSuper) return FALSE;
     if(autoQueen) return FALSE; // predetermined
 
@@ -6272,7 +6304,7 @@ OKToStartUserMove(x, y)
 Boolean
 OnlyMove(int *x, int *y, Boolean captures) {
     DisambiguateClosure cl;
-    if (appData.zippyPlay) return FALSE;
+    if (appData.zippyPlay || !appData.testLegality) return FALSE;
     switch(gameMode) {
       case MachinePlaysBlack:
       case IcsPlayingWhite:
@@ -6514,8 +6546,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
 {
     char *bookHit = 0;
 
-    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) {
-       // [HGM] superchess: suppress promotions to non-available piece
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && promoChar != NULLCHAR) {
+       // [HGM] superchess: suppress promotions to non-available piece (but P always allowed)
        int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
        if(WhiteOnMove(currentMove)) {
            if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;
@@ -6718,7 +6750,7 @@ MarkTargetSquares(int clear)
     for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
   } else {
     int capt = 0;
-    GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker);
+    GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker, EmptySquare);
     if(PosFlags(0) & F_MANDATORY_CAPTURE) {
       for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
       if(capt)
@@ -6773,7 +6805,6 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 
     if (clickType == Press) ErrorPopDown();
-    MarkTargetSquares(1);
 
     x = EventToSquare(xPix, BOARD_WIDTH);
     y = EventToSquare(yPix, BOARD_HEIGHT);
@@ -6788,7 +6819,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        defaultPromoChoice = promoSweep;
        promoSweep = EmptySquare;   // terminate sweep
        promoDefaultAltered = TRUE;
-       if(!selectFlag) x = fromX, y = fromY; // and fake up-click on same square if we were still selecting
+       if(!selectFlag && (x != toX || y != toY)) x = fromX, y = fromY; // and fake up-click on same square if we were still selecting
     }
 
     if(promotionChoice) { // we are waiting for a click to indicate promotion piece
@@ -6797,13 +6828,14 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
        if(gameInfo.holdingsWidth &&
                (WhiteOnMove(currentMove)
-                       ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
-                       : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+                       ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y >= 0
+                       : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT) ) {
            // click in right holdings, for determining promotion piece
            ChessSquare p = boards[currentMove][y][x];
            if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
-           if(p != EmptySquare) {
-               FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+           if(p == WhitePawn || p == BlackPawn) p = EmptySquare; // [HGM] Pawns could be valid as deferral
+           if(p != EmptySquare || gameInfo.variant == VariantGrand && toY != 0 && toY != BOARD_HEIGHT-1) { // [HGM] grand: empty square means defer
+               FinishMove(NormalMove, fromX, fromY, toX, toY, p==EmptySquare ? NULLCHAR : ToLower(PieceToChar(p)));
                fromX = fromY = -1;
                return;
            }
@@ -6840,7 +6872,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        }
        return;
       }
-      fromX = x; fromY = y;
+      fromX = x; fromY = y; toX = toY = -1;
       if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
         // even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
         appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) {
@@ -6848,7 +6880,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
            if (OKToStartUserMove(fromX, fromY)) {
                second = 0;
                MarkTargetSquares(0);
-               DragPieceBegin(xPix, yPix); dragging = 1;
+               DragPieceBegin(xPix, yPix, FALSE); dragging = 1;
                if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) {
                    promoSweep = defaultPromoChoice;
                    selectFlag = 0; lastX = xPix; lastY = yPix;
@@ -6887,6 +6919,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
            /* Clicked again on same color piece -- changed his mind */
            second = (x == fromX && y == fromY);
            promoDefaultAltered = FALSE;
+           MarkTargetSquares(1);
           if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
            if (appData.highlightDragging) {
                SetHighlights(x, y, -1, -1);
@@ -6902,7 +6935,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                fromX = x;
                fromY = y; dragging = 1;
                MarkTargetSquares(0);
-               DragPieceBegin(xPix, yPix);
+               DragPieceBegin(xPix, yPix, FALSE);
                if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
                    promoSweep = defaultPromoChoice;
                    selectFlag = 0; lastX = xPix; lastY = yPix;
@@ -6953,13 +6986,25 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     toX = x;
     toY = y;
     saveAnimate = appData.animate;
+    MarkTargetSquares(1);
     if (clickType == Press) {
        if(gameMode == EditPosition && boards[currentMove][fromY][fromX] == EmptySquare) {
            // must be Edit Position mode with empty-square selected
-           fromX = x; fromY = y; DragPieceBegin(xPix, yPix); dragging = 1; // consider this a new attempt to drag
+           fromX = x; fromY = y; DragPieceBegin(xPix, yPix, FALSE); dragging = 1; // consider this a new attempt to drag
            if(x >= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click
            return;
        }
+       if(appData.sweepSelect && HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
+           ChessSquare piece = boards[currentMove][fromY][fromX];
+           DragPieceBegin(xPix, yPix, TRUE); dragging = 1;
+           promoSweep = defaultPromoChoice;
+           if(PieceToChar(PROMOTED piece) == '+') promoSweep = PROMOTED piece;
+           selectFlag = 0; lastX = xPix; lastY = yPix;
+           Sweep(0); // Pawn that is going to promote: preview promotion piece
+           DisplayMessage("", _("Pull pawn backwards to under-promote"));
+           DrawPosition(FALSE, boards[currentMove]);
+           return;
+       }
        /* Finish clickclick move */
        if (appData.animate || appData.highlightLastMove) {
            SetHighlights(fromX, fromY, toX, toY);
@@ -7010,9 +7055,9 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
 
     if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece));
 
-    if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
+    if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
        SetHighlights(fromX, fromY, toX, toY);
-       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
            // [HGM] super: promotion to captured piece selected from holdings
            ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
            promotionChoice = TRUE;
@@ -8951,7 +8996,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      Board board;
 {
   ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
-  int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
+  int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
 
     /* [HGM] compute & store e.p. status and castling rights for new position */
     /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
@@ -9049,18 +9094,15 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[fromY][BOARD_LEFT] = EmptySquare;
     } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi ||
                 board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
-               && toY >= BOARD_HEIGHT-promoRank
+               && toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere
                ) {
        /* white pawn promotion */
         board[toY][toX] = CharToPiece(ToUpper(promoChar));
-        if (board[toY][toX] == EmptySquare) {
-            board[toY][toX] = WhiteQueen;
-       }
         if(gameInfo.variant==VariantBughouse ||
            gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
-    } else if ((fromY == BOARD_HEIGHT-4)
+    } else if ((fromY >= BOARD_HEIGHT>>1)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
@@ -9113,18 +9155,15 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][2] = BlackRook;
     } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi ||
                 board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
-              && toY < promoRank
+              && toY < promoRank && promoChar
                ) {
        /* black pawn promotion */
        board[toY][toX] = CharToPiece(ToLower(promoChar));
-       if (board[toY][toX] == EmptySquare) {
-           board[toY][toX] = BlackQueen;
-       }
         if(gameInfo.variant==VariantBughouse ||
            gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
-    } else if ((fromY == 3)
+    } else if ((fromY < BOARD_HEIGHT>>1)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
@@ -9185,7 +9224,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       if (captured != EmptySquare && gameInfo.holdingsSize > 0
           && gameInfo.variant != VariantBughouse && gameInfo.variant != VariantSChess        ) {
         /* [HGM] holdings: Add to holdings, if holdings exist */
-       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
                // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip
                captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;
        }
@@ -9236,8 +9275,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
     } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
         board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
     }
-    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) 
-               && promoChar != NULLCHAR && gameInfo.holdingsSize) { 
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
+               && promoChar != NULLCHAR && gameInfo.holdingsSize) {
        // [HGM] superchess: take promotion piece out of holdings
        int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
        if((int)piece < (int)BlackPawn) { // determine stm from piece color
@@ -9465,6 +9504,8 @@ InitChessProgram(cps, setup)
            overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
       if( gameInfo.variant == VariantSChess )
            overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7;
+      if( gameInfo.variant == VariantGrand )
+           overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7;
 
       if(overruled) {
        snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight,
@@ -9654,7 +9695,7 @@ WriteTourneyFile(char *results)
        fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex);
        fprintf(f, "-rewindIndex %d\n", appData.rewindIndex);
        if(searchTime > 0)
-               fprintf(f, "-searchTime \"%s\"\n", appData.searchTime);
+               fprintf(f, "-searchTime \"%d:%02d\"\n", searchTime/60, searchTime%60);
        else {
                fprintf(f, "-mps %d\n", appData.movesPerSession);
                fprintf(f, "-tc %s\n", appData.timeControl);
@@ -10018,7 +10059,7 @@ GameEnds(result, resultDetails, whosays)
 
                // now verify win claims, but not in drop games, as we don't understand those yet
                 if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper
-                                                || gameInfo.variant == VariantGreat) &&
+                                                || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) &&
                     (result == WhiteWins && claimer == 'w' ||
                      result == BlackWins && claimer == 'b'   ) ) { // case to verify: engine claims own win
                      if (appData.debugMode) {
@@ -10044,7 +10085,8 @@ GameEnds(result, resultDetails, whosays)
                 /* (Claiming a loss is accepted no questions asked!) */
            }
            /* [HGM] bare: don't allow bare King to win */
-           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
+           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper
+                                           || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
               && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway
               && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...
               && result != GameIsDrawn)
@@ -15791,7 +15833,7 @@ PositionToFEN(move, overrideCastling)
 {
     int i, j, fromX, fromY, toX, toY;
     int whiteToPlay;
-    char buf[128];
+    char buf[MSG_SIZ];
     char *p, *q;
     int emptycount;
     ChessSquare piece;
@@ -15802,6 +15844,7 @@ PositionToFEN(move, overrideCastling)
 
     /* Piece placement data */
     for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
+       if(MSG_SIZ - (p - buf) < BOARD_RGHT - BOARD_LEFT + 20) { *p = 0; return StrSave(buf); }
        emptycount = 0;
         for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
            if (boards[move][i][j] == EmptySquare) {