case VariantShatranj:
   case VariantCourier:
   case VariantMakruk:
+  case VariantGrand:
     flags &= ~F_ALL_CASTLE_OK;
     break;
   default:
         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,
       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;
       }
            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
     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:
 
     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)
             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) {
                 }
             }
         }
-        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 ) {
 
     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;
     }
 
 {
     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;
        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;
            }
 
     if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
        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;
      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 */
         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
        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
       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;
        }
     } 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
            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,
 
                // 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) {
                 /* (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)
 
     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++) {
                           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 &&
                               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);
                      }
                  }
              }
                           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 &&
                               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);
                      }
                  }
              }
                    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 = IllegalMove; // 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)
     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
                 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
 
 { VariantSuicide, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("suicide")},
 { VariantCapaRandom, 1, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("CRC (10x8)")},
 { VariantGiveaway, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("give-away")},
-{ VariantSpartan, 1, 135, NULL, (void*) &Pick, "#FF0000", NULL, Button, N_("Spartan")},
+{ VariantGrand, 1, 135, NULL, (void*) &Pick, "#5070FF", NULL, Button, N_("grand (10x10)")},
 { VariantLosers, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("losers")},
+{ VariantSpartan, 1, 135, NULL, (void*) &Pick, "#FF0000", NULL, Button, N_("Spartan")},
 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("Board size ( -1 = default for selected variant):")},
 { 0, -1, BOARD_RANKS-1, NULL, (void*) &appData.NrRanks, "", NULL, Spin, N_("Number of Board Ranks:") },
 { 0, -1, BOARD_FILES, NULL, (void*) &appData.NrFiles, "", NULL, Spin, N_("Number of Board Files:") },