Fix ICS Crazyhouse play
[xboard.git] / backend.c
index 12aadc1..7cc8f9c 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -261,7 +261,7 @@ void ics_update_width P((int new_width));
 extern char installDir[MSG_SIZ];
 VariantClass startVariant; /* [HGM] nicks: initial variant */
 Boolean abortMatch;
-int deadRanks;
+int deadRanks, handSize, handOffsets;
 
 extern int tinyLayout, smallLayout;
 ChessProgramStats programStats;
@@ -2485,7 +2485,7 @@ CopyHoldings (Board board, char *holdings, ChessSquare lowestPiece)
     if( (int)lowestPiece >= BlackPawn ) {
         holdingsColumn = 0;
         countsColumn = 1;
-        holdingsStartRow = BOARD_HEIGHT-1;
+        holdingsStartRow = handSize-1;
         direction = -1;
     } else {
         holdingsColumn = BOARD_WIDTH-1;
@@ -2494,7 +2494,7 @@ CopyHoldings (Board board, char *holdings, ChessSquare lowestPiece)
         direction = 1;
     }
 
-    for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */
+    for(i=0; i<BOARD_RANKS-1; i++) { /* clear holdings */
         board[i][holdingsColumn] = EmptySquare;
         board[i][countsColumn]   = (ChessSquare) 0;
     }
@@ -4209,6 +4209,12 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                                                         parse, currentMove);
                    if (sscanf(parse, " game %d", &gamenum) == 1) {
                      if(gamenum == ics_gamenum) { // [HGM] bughouse: old code if part of foreground game
+                       new_piece[0] = NULLCHAR;
+                       sscanf(parse, "game %d white [%s black [%s <- %s",
+                              &gamenum, white_holding, black_holding,
+                              new_piece);
+                        white_holding[strlen(white_holding)-1] = NULLCHAR;
+                        black_holding[strlen(black_holding)-1] = NULLCHAR;
                        if (gameInfo.variant == VariantNormal) {
                           /* [HGM] We seem to switch variant during a game!
                            * Presumably no holdings were displayed, so we have
@@ -4219,7 +4225,10 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                          switch(gameInfo.boardWidth) { // base guess on board width
                                case 9:  newVariant = VariantShogi; break;
                                case 10: newVariant = VariantGreat; break;
-                               default: newVariant = VariantCrazyhouse; break;
+                               default: newVariant = VariantCrazyhouse;
+                                    if(strchr(white_holding, 'E') || strchr(black_holding, 'E') || 
+                                       strchr(white_holding, 'H') || strchr(black_holding, 'H')   )
+                                        newVariant = VariantSChess;
                          }
                           VariantSwitch(boards[currentMove], newVariant); /* temp guess */
                          /* Get a move list just to see the header, which
@@ -4230,12 +4239,6 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                            SendToICS(str);
                          }
                        }
-                       new_piece[0] = NULLCHAR;
-                       sscanf(parse, "game %d white [%s black [%s <- %s",
-                              &gamenum, white_holding, black_holding,
-                              new_piece);
-                        white_holding[strlen(white_holding)-1] = NULLCHAR;
-                        black_holding[strlen(black_holding)-1] = NULLCHAR;
                         /* [HGM] copy holdings to board holdings area */
                         CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn);
                         CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn);
@@ -4904,7 +4907,7 @@ ParseBoard12 (char *string)
                && gameInfo.variant != VariantGrand&& gameInfo.variant != VariantSChess) // 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
+                  if(new == EmptySquare || fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
                   old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
                   if(old == new) continue;
                   if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones
@@ -4912,7 +4915,7 @@ ParseBoard12 (char *string)
                       if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
                            boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion
                       else boards[moveNum][k][j] = old; // preserve type of Gold
-                  } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
+                  } else if(old == WhitePawn || old == BlackPawn) // Pawn promotions (but not e.p.capture!)
                       boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece
               }
          } else {
@@ -6126,19 +6129,19 @@ Prelude (Board board)
        j = seed%4;                 seed /= 4;
        p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
        board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;
-       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;
+       board[handSize-1-k][0] = WHITE_TO_BLACK p;  board[handSize-1-k][1]++;
        j = seed%3 + (seed%3 >= j); seed /= 3;
        p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
        board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;
-       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;
+       board[handSize-1-k][0] = WHITE_TO_BLACK p;  board[handSize-1-k][1]++;
        j = seed%3;                 seed /= 3;
        p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
        board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;
-       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;
+       board[handSize-1-k][0] = WHITE_TO_BLACK p;  board[handSize-1-k][1]++;
        j = seed%2 + (seed%2 >= j); seed /= 2;
        p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
        board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;
-       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;
+       board[handSize-1-k][0] = WHITE_TO_BLACK p;  board[handSize-1-k][1]++;
        j = seed%4; seed /= 4; put(board, exoPieces[3],    0, j, ANY);
        j = seed%3; seed /= 3; put(board, exoPieces[2],   0, j, ANY);
        j = seed%2; seed /= 2; put(board, exoPieces[1], 0, j, ANY);
@@ -6356,13 +6359,14 @@ InitPosition (int redraw)
     }
     if(appData.holdingsSize >= 0) {
         i = appData.holdingsSize;
-        if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;
+//        if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;
         gameInfo.holdingsSize = i;
     }
     if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;
     if(BOARD_HEIGHT > BOARD_RANKS || BOARD_WIDTH > BOARD_FILES)
         DisplayFatalError(_("Recompile to support this BOARD_RANKS or BOARD_FILES!"), 0, 2);
 
+    if(!handSize) handSize = BOARD_HEIGHT;
     pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
     if(pawnRow < 1) pawnRow = 1;
     if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN ||
@@ -6440,8 +6444,8 @@ InitPosition (int redraw)
      if(gameInfo.variant == VariantGreat) { // promotion commoners
        initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan;
        initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9;
-       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;
-       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;
+       initialPosition[handSize-1-PieceToNumber(WhiteMan)][0] = BlackMan;
+       initialPosition[handSize-1-PieceToNumber(WhiteMan)][1] = 9;
      }
      if( gameInfo.variant == VariantSChess ) {
       initialPosition[1][0] = BlackMarshall;
@@ -6451,6 +6455,7 @@ InitPosition (int redraw)
       initialPosition[1][1] = initialPosition[2][1] =
       initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
      }
+     initialPosition[CHECK_COUNT] = (gameInfo.variant == Variant3Check ? 0x303 : 0);
   if (appData.debugMode) {
     fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
   }
@@ -6707,12 +6712,13 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
 
     piece = boards[currentMove][fromY][fromX];
     if(gameInfo.variant == VariantChu) {
-        promotionZoneSize = BOARD_HEIGHT/3;
+        promotionZoneSize = (BOARD_HEIGHT - deadRanks)/3;
         if(legal[toY][toX] == 6) return FALSE; // no promotion if highlights deny it
         highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing;
     } else if(gameInfo.variant == VariantShogi) {
-        promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8);
+        promotionZoneSize = (BOARD_HEIGHT- deadRanks)/3 +(BOARD_HEIGHT == 8);
         highestPromotingPiece = (int)WhiteAlfil;
+        if(PieceToChar(piece) != '+' || PieceToChar(CHUPROMOTED(piece)) == '+') highestPromotingPiece = piece;
     } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) {
         promotionZoneSize = 3;
     }
@@ -6730,9 +6736,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
         if(fromY < promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE;
         highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece;
     } else {
-        if(  toY < BOARD_HEIGHT - promotionZoneSize &&
-           fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;
-        if(fromY >= BOARD_HEIGHT - promotionZoneSize && gameInfo.variant == VariantChuChess)
+        if(  toY < BOARD_HEIGHT - deadRanks - promotionZoneSize &&
+           fromY < BOARD_HEIGHT - deadRanks - promotionZoneSize) return FALSE;
+        if(fromY >= BOARD_HEIGHT - deadRanks - promotionZoneSize && gameInfo.variant == VariantChuChess)
              return FALSE;
     }
 
@@ -6748,9 +6754,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
                return FALSE;
            }
        } else {
-           if(toY == BOARD_HEIGHT-1 && piece == WhitePawn ||
-              toY == BOARD_HEIGHT-1 && piece == WhiteQueen ||
-              toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) {
+           if(toY == BOARD_HEIGHT-deadRanks-1 && piece == WhitePawn ||
+              toY == BOARD_HEIGHT-deadRanks-1 && piece == WhiteQueen ||
+              toY >= BOARD_HEIGHT-deadRanks-2 && piece == WhiteKnight) {
                *promoChoice = '+';
                return FALSE;
            }
@@ -6817,12 +6823,45 @@ InPalace (int row, int column)
 int
 PieceForSquare (int x, int y)
 {
-  if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)
-     return -1;
-  else
+  if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) return -1;
+  if(x == BOARD_RGHT+1 && handOffsets & 1) y += handSize - BOARD_HEIGHT;
+  if(x == BOARD_LEFT-2 && !(handOffsets & 2)) y += handSize - BOARD_HEIGHT;
      return boards[currentMove][y][x];
 }
 
+ChessSquare
+More (Board board, int col, int start, int end)
+{
+    int k;
+    for(k=start; k<end; k++) if(board[k][col]) return (col == 1 ? WhiteMonarch : BlackMonarch); // arrow image
+    return EmptySquare;
+}
+
+void
+DrawPosition (int repaint, Board board)
+{
+    Board compactedBoard;
+    if(handSize > BOARD_HEIGHT && board) {
+       int k;
+       CopyBoard(compactedBoard, board);
+       if(handOffsets & 1) {
+           for(k=0; k<BOARD_HEIGHT; k++) {
+               compactedBoard[k][BOARD_WIDTH-1] = board[k+handSize-BOARD_HEIGHT][BOARD_WIDTH-1];
+               compactedBoard[k][BOARD_WIDTH-2] = board[k+handSize-BOARD_HEIGHT][BOARD_WIDTH-2];
+           }
+           compactedBoard[0][BOARD_WIDTH-1] = More(board, BOARD_WIDTH-2, 0, handSize-BOARD_HEIGHT+1);
+       } else compactedBoard[BOARD_HEIGHT-1][BOARD_WIDTH-1] = More(board, BOARD_WIDTH-2, BOARD_HEIGHT-1, handSize);
+       if(!(handOffsets & 2)) {
+           for(k=0; k<BOARD_HEIGHT; k++) {
+               compactedBoard[k][0] = board[k+handSize-BOARD_HEIGHT][0];
+               compactedBoard[k][1] = board[k+handSize-BOARD_HEIGHT][1];
+           }
+           compactedBoard[0][0] = More(board, 1, 0, handSize-BOARD_HEIGHT+1);
+       } else compactedBoard[BOARD_HEIGHT-1][0] = More(board, 1, BOARD_HEIGHT-1, handSize);
+       DrawPositionX(TRUE, compactedBoard);
+    } else DrawPositionX(repaint, board);
+}
+
 int
 OKToStartUserMove (int x, int y)
 {
@@ -7156,7 +7195,7 @@ UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
           if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
                moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
           // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
-          if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
+          if(fromX == 0) fromY = handSize-1 - fromY; // black holdings upside-down
           fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
           while(PieceToChar(fromX) == '.' || PieceToChar(fromX) == '+' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
          fromY = DROP_RANK;
@@ -7213,7 +7252,7 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom
        if(WhiteOnMove(currentMove)) {
            if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;
        } else {
-           if(!boards[currentMove][BOARD_HEIGHT-1-k][1]) return 0;
+           if(!boards[currentMove][handSize-1-k][1]) return 0;
        }
     }
 
@@ -7462,7 +7501,7 @@ MarkTargetSquares (int clear)
     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)
-      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = 0;
+      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = legal[y][x] = 0;
     }
   }
   DrawPosition(FALSE, NULL);
@@ -7495,9 +7534,9 @@ CanPromote (ChessSquare piece, int y)
          (gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
            gameInfo.variant == VariantMakruk) && !*engineVariant) return FALSE;
        return (piece == BlackPawn && y <= zone ||
-               piece == WhitePawn && y >= BOARD_HEIGHT-1-zone ||
+               piece == WhitePawn && y >= BOARD_HEIGHT-1-deadRanks-zone ||
                piece == BlackLance && y <= zone ||
-               piece == WhiteLance && y >= BOARD_HEIGHT-1-zone );
+               piece == WhiteLance && y >= BOARD_HEIGHT-1-deadRanks-zone );
 }
 
 void
@@ -7565,6 +7604,20 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        x = BOARD_WIDTH - 1 - x;
     }
 
+    // map clicks in offsetted holdings back to true coords (or switch the offset)
+    if(x == BOARD_RGHT+1) {
+       if(handOffsets & 1) {
+           if(y == 0) { handOffsets &= ~1; DrawPosition(TRUE, boards[currentMove]); return; }
+           y += handSize - BOARD_HEIGHT;
+       } else if(y == BOARD_HEIGHT-1) { handOffsets |= 1; DrawPosition(TRUE, boards[currentMove]); return; }
+    }
+    if(x == BOARD_LEFT-2) {
+       if(!(handOffsets & 2)) {
+           if(y == 0) { handOffsets |= 2; DrawPosition(TRUE, boards[currentMove]); return; }
+           y += handSize - BOARD_HEIGHT;
+       } else if(y == BOARD_HEIGHT-1) { handOffsets &= ~2; DrawPosition(TRUE, boards[currentMove]); return; }
+    }
+
     if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) {
        static int dummy;
        RightClick(clickType, xPix, yPix, &dummy, &dummy);
@@ -7880,8 +7933,8 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
                n = PieceToNumber(piece - (int)BlackPawn);
                if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
-               boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece;
-               boards[currentMove][BOARD_HEIGHT-1 - n][1]++;
+               boards[currentMove][handSize-1 - n][0] = piece;
+               boards[currentMove][handSize-1 - n][1]++;
            } else
            if(x == BOARD_RGHT+1 && piece < BlackPawn) {
                n = PieceToNumber(piece);
@@ -9151,7 +9204,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         while(message[s] && message[s++] != ' ');
         if(BOARD_HEIGHT != h || BOARD_WIDTH != w + 4*(hand != 0) || gameInfo.holdingsSize != hand ||
            dummy == 4 && gameInfo.variant != StringToVariant(varName) ) { // engine wants to change board format or variant
-           if(hand <= h) deadRanks = 0; else deadRanks = hand - h, h = hand; // adapt board to over-sized holdings
+//         if(hand <= h) deadRanks = 0; else deadRanks = hand - h, h = hand; // adapt board to over-sized holdings
+           if(hand > h) handSize = hand; else handSize = h;
            appData.NrFiles = w; appData.NrRanks = h; appData.holdingsSize = hand;
            if(dummy == 4) gameInfo.variant = StringToVariant(varName);     // parent variant
           InitPosition(1); // calls InitDrawingSizes to let new parameters take effect
@@ -10030,7 +10084,7 @@ void
 ParseGameHistory (char *game)
 {
     ChessMove moveType;
-    int fromX, fromY, toX, toY, boardIndex;
+    int fromX, fromY, toX, toY, boardIndex, mask;
     char promoChar;
     char *p, *q;
     char buf[MSG_SIZ];
@@ -10191,15 +10245,18 @@ ParseGameHistory (char *game)
        strcat(moveList[boardIndex], "\n");
        boardIndex++;
        ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
+       mask = (WhiteOnMove(boardIndex) ? 0xFF : 0xFF00);
         switch (MateTest(boards[boardIndex], PosFlags(boardIndex)) ) {
          case MT_NONE:
          case MT_STALEMATE:
          default:
            break;
          case MT_CHECK:
-            if(!IS_SHOGI(gameInfo.variant))
-                strcat(parseList[boardIndex - 1], "+");
-           break;
+            if(boards[boardIndex][CHECK_COUNT]) boards[boardIndex][CHECK_COUNT] -= mask & 0x101;
+            if(!boards[boardIndex][CHECK_COUNT] || boards[boardIndex][CHECK_COUNT] & mask) {
+                if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[boardIndex - 1], "+");
+                break;
+           }
          case MT_CHECKMATE:
          case MT_STAINMATE:
            strcat(parseList[boardIndex - 1], "#");
@@ -10220,9 +10277,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
     /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
 
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
-      oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_FILE],lastRank = board[LAST_RANK];
+      oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_TO] & 255,lastRank = board[LAST_TO] >> 8;
       board[EP_STATUS] = EP_NONE;
-      board[EP_FILE] = board[EP_RANK] = board[LAST_FILE] = board[LAST_RANK] = 100;
+      board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = 0x4040;
 
   if (fromY == DROP_RANK) {
        /* must be first */
@@ -10278,7 +10335,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
+              board[LAST_TO] = toX + 256*toY;
           }
       } else
       if( pawn == BlackPawn ) {
@@ -10292,7 +10349,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
+              board[LAST_TO] = toX + 256*toY;
           }
        }
 
@@ -10505,10 +10562,10 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
              p -= (int)BlackPawn;
                 p = PieceToNumber((ChessSquare)p);
              if(p >= gameInfo.holdingsSize) p = 0;
-             if(--board[BOARD_HEIGHT-1-p][1] <= 0)
-                  board[BOARD_HEIGHT-1-p][0] = EmptySquare;
-             if((int)board[BOARD_HEIGHT-1-p][1] < 0)
-                       board[BOARD_HEIGHT-1-p][1] = 0;
+             if(--board[handSize-1-p][1] <= 0)
+                  board[handSize-1-p][0] = EmptySquare;
+             if((int)board[handSize-1-p][1] < 0)
+                       board[handSize-1-p][1] = 0;
         }
       }
       if (captured != EmptySquare && gameInfo.holdingsSize > 0
@@ -10538,8 +10595,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
           }
           p = PieceToNumber((ChessSquare)p);
           if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }
-          board[BOARD_HEIGHT-1-p][1]++;
-          board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;
+          board[handSize-1-p][1]++;
+          board[handSize-1-p][0] = WHITE_TO_BLACK captured;
        }
       }
     } else if (gameInfo.variant == VariantAtomic) {
@@ -10579,8 +10636,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
            if(!--board[k][BOARD_WIDTH-2])
                board[k][BOARD_WIDTH-1] = EmptySquare;
        } else {
-           if(!--board[BOARD_HEIGHT-1-k][1])
-               board[BOARD_HEIGHT-1-k][0] = EmptySquare;
+           if(!--board[handSize-1-k][1])
+               board[handSize-1-k][0] = EmptySquare;
        }
     }
 }
@@ -10589,7 +10646,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
 void
 MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
 {
-    int x = toX, y = toY;
+    int x = toX, y = toY, mask;
     char *s = parseList[forwardMostMove];
     ChessSquare p = boards[forwardMostMove][toY][toX];
 //    forwardMostMove++; // [HGM] bare: moved downstream
@@ -10685,15 +10742,18 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
     }
     CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
                              moveList[forwardMostMove - 1]);
+    mask = (WhiteOnMove(forwardMostMove) ? 0xFF : 0xFF00);
     switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
       case MT_NONE:
       case MT_STALEMATE:
       default:
        break;
       case MT_CHECK:
-        if(!IS_SHOGI(gameInfo.variant))
-            strcat(parseList[forwardMostMove - 1], "+");
-       break;
+        if(boards[forwardMostMove][CHECK_COUNT]) boards[forwardMostMove][CHECK_COUNT] -= mask & 0x101;
+       if(!boards[forwardMostMove][CHECK_COUNT] || boards[forwardMostMove][CHECK_COUNT] & mask) {
+            if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[forwardMostMove - 1], "+");
+            break;
+        }
       case MT_CHECKMATE:
       case MT_STAINMATE:
        strcat(parseList[forwardMostMove - 1], "#");
@@ -11274,9 +11334,9 @@ void
 SaveEngineSettings (int n)
 {
     int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings;
-    if(!currentEngine[n] || !currentEngine[n][0]) return; // no engine from list is loaded
+    if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded
     p = strstr(firstChessProgramNames, currentEngine[n]);
-    if(!p) return; // sanity check; engine could be deleted from list after loading
+    if(!p) { DisplayMessage("saving failed: engine not found in list", ""); return; } // sanity check; engine could be deleted from list after loading
     optionSettings = ResendOptions(n ? &second : &first, FALSE);
     len = strlen(currentEngine[n]);
     q = p + len; *p = 0; // cut list into head and tail piece
@@ -11343,6 +11403,7 @@ GetEngineLine (char *s, int n)
     if(n == 1) SwapEngines(n);
     ParseArgsFromString(buf);
     if(n == 1) SwapEngines(n);
+    if(n < 2) { ASSIGN(currentEngine[n], command[i]); }
     if(n == 0 && *appData.secondChessProgram == NULLCHAR) {
        SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog)
        ParseArgsFromString(buf);
@@ -12089,6 +12150,7 @@ Reset (int redraw, int init)
     }
     pieceDefs = FALSE; // [HGM] gen: reset engine-defined piece moves
     deadRanks = 0; // assume entire board is used
+    handSize = 0;
     for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; }
     CleanupTail(); // [HGM] vari: delete any stored variations
     CommentPopDown(); // [HGM] make sure no comments to the previous game keep hanging on
@@ -15634,7 +15696,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
        } else {
             if(x < BOARD_LEFT || x >= BOARD_RGHT) {
                 if(x == BOARD_LEFT-2) {
-                    if(y < BOARD_HEIGHT-1-gameInfo.holdingsSize) break;
+                    if(y < handSize-1-gameInfo.holdingsSize) break;
                     boards[0][y][1] = 0;
                 } else
                 if(x == BOARD_RGHT+1) {
@@ -15702,8 +15764,8 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
                 if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
                     n = PieceToNumber(selection - BlackPawn);
                     if(n >= gameInfo.holdingsSize) { n = 0; selection = BlackPawn; }
-                    boards[0][BOARD_HEIGHT-1-n][0] = selection;
-                    boards[0][BOARD_HEIGHT-1-n][1]++;
+                    boards[0][handSize-1-n][0] = selection;
+                    boards[0][handSize-1-n][1]++;
                 } else
                 if(x == BOARD_RGHT+1 && selection < BlackPawn) {
                     n = PieceToNumber(selection);
@@ -18324,9 +18386,9 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
                   *p++ = PieceToChar(piece);
         }
         for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */
-            piece = boards[move][BOARD_HEIGHT-i-1][0];
+            piece = boards[move][handSize-i-1][0];
             if( piece != EmptySquare )
-              for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)
+              for(j=0; j<(int) boards[move][handSize-i-1][1]; j++)
                   *p++ = PieceToChar(piece);
         }
 
@@ -18434,13 +18496,15 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
         fromY = moveList[move - 1][1] - ONE;
         toX = moveList[move - 1][2] - AAA;
         toY = moveList[move - 1][3] - ONE;
-       if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) &&
-           toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) &&
-           boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
-           fromX == toX) {
+       if ((whiteToPlay ? toY < fromY - 1 : toY > fromY + 1) &&
+           boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) ) {
            /* 2-square pawn move just happened */
-            *p++ = toX + AAA;
-           *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';
+            *p++ = (3*toX + 5*fromX + 4)/8 + AAA;
+           *p++ = (3*toY + 5*fromY + 4)/8 + ONE;
+           if(gameInfo.variant == VariantBerolina) {
+               *p++ = toX + AAA;
+               *p++ = toY + ONE;
+           }
        } else {
            *p++ = '-';
        }
@@ -18459,6 +18523,12 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
   }
   }
 
+    i = boards[move][CHECK_COUNT];
+    if(i) {
+       sprintf(p, "%d+%d ", i&255, i>>8);
+       while(*p) p++;
+    }
+
     if(moveCounts)
     {   int i = 0, j=move;
 
@@ -18572,7 +18642,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
 
     /* [HGM] by default clear Crazyhouse holdings, if present */
     if(gameInfo.holdingsWidth) {
-       for(i=0; i<BOARD_HEIGHT; i++) {
+       for(i=0; i<handSize; i++) {
            board[i][0]             = EmptySquare; /* black holdings */
            board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */
            board[i][1]             = (ChessSquare) 0; /* black counts */
@@ -18596,8 +18666,8 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
                     i = (int)piece - (int)BlackPawn;
                    i = PieceToNumber((ChessSquare)i);
                     if( i >= gameInfo.holdingsSize ) return FALSE;
-                    board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */
-                    board[BOARD_HEIGHT-1-i][1]++;       /* black counts   */
+                    board[handSize-1-i][0] = piece; /* black holdings */
+                    board[handSize-1-i][1]++;       /* black counts   */
                     bcnt++;
                 } else {
                     i = (int)piece - (int)WhitePawn;
@@ -18623,9 +18693,9 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
                     if(board[BOARD_HEIGHT-1][j] == ClearBoard) {
                         if(!bcnt) return FALSE;
                         if(n >= bcnt) n = rand() % bcnt; // use same randomization for black and white if possible
-                        for(k=0, m=n; k<gameInfo.holdingsSize; k++) if((n -= board[BOARD_HEIGHT-1-k][1]) < 0) {
-                            board[BOARD_HEIGHT-1][j] = board[BOARD_HEIGHT-1-k][0]; bcnt--;
-                            if(--board[BOARD_HEIGHT-1-k][1] == 0) board[BOARD_HEIGHT-1-k][0] = EmptySquare;
+                        for(k=0, m=n; k<gameInfo.holdingsSize; k++) if((n -= board[handSize-1-k][1]) < 0) {
+                            board[BOARD_HEIGHT-1][j] = board[handSize-1-k][0]; bcnt--;
+                            if(--board[handSize-1-k][1] == 0) board[handSize-1-k][0] = EmptySquare;
                             break;
                         }
                     }
@@ -18811,18 +18881,38 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
       if(*p=='-') {
         p++; board[EP_STATUS] = EP_NONE;
       } else {
-         char c = *p++ - AAA;
-
-         if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;
-         if(*p >= '0' && *p <='9') p++;
-         board[EP_STATUS] = c;
+         int d, r, c = *p - AAA;
+
+         if(c >= BOARD_LEFT && c < BOARD_RGHT) {
+             p++;
+             board[EP_STATUS] = board[EP_FILE] = c; r = 0;
+             if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE;
+             d = (r < BOARD_HEIGHT << 1 ? 1 : -1); // assume double-push (P next to e.p. square nearer center)
+             if(board[r+d][c] == EmptySquare) d *= 2; // but if no Pawn there, triple push
+             board[LAST_TO] = 256*(r + d) + c;
+             c = *p++ - AAA;
+             if(c >= BOARD_LEFT && c < BOARD_RGHT) { // mover explicitly mentioned
+                 if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE;
+                 board[LAST_TO] = 256*r + c;
+                 if(!(board[EP_RANK]-r & 1)) board[EP_RANK] |= 128;
+             }
+         }
       }
     }
 
+    while(*p == ' ') p++;
+
+    board[CHECK_COUNT] = 0; // [HGM] 3check: check-count field
+    if(sscanf(p, "%d+%d", &i, &j) == 2) {
+       board[CHECK_COUNT] = i + 256*j;
+       while(*p && *p != ' ') p++;
+    }
 
-    if(sscanf(p, "%d", &i) == 1) {
+    c = sscanf(p, "%d%*d +%d+%d", &i, &j, &k);
+    if(c > 0) {
         FENrulePlies = i; /* 50-move ply counter */
         /* (The move number is still ignored)    */
+       if(c == 3 && !board[CHECK_COUNT]) board[CHECK_COUNT] = (3 - j) + 256*(3 - k); // SCIDB-style check count
     }
 
     return TRUE;