Fix Seirawan gating at Rook square in PGN castling moves
[xboard.git] / backend.c
index 3792971..c531656 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;
@@ -913,13 +913,79 @@ InitEngine (ChessProgramState *cps, int n)
 
 ChessProgramState *savCps;
 
-GameMode oldMode;
+GameMode oldMode, tryNr;
+
+extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
+extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
+char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine
+static char newEngineCommand[MSG_SIZ];
+
+void
+FloatToFront(char **list, char *engineLine)
+{
+    char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
+    int i=0;
+    if(appData.recentEngines <= 0) return;
+    TidyProgramName(engineLine, "localhost", tidy+1);
+    tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
+    strncpy(buf+1, *list, MSG_SIZ-50);
+    if(p = strstr(buf, tidy)) { // tidy name appears in list
+       q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
+       while(*p++ = *++q); // squeeze out
+    }
+    strcat(tidy, buf+1); // put list behind tidy name
+    p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
+    if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
+    ASSIGN(*list, tidy+1);
+}
+
+void
+SaveEngineList ()
+{
+       FILE *f;
+       if(*engineListFile && (f = fopen(engineListFile, "w"))) {
+         fprintf(f, "-firstChessProgramNames {%s}\n", firstChessProgramNames);
+         fclose(f);
+       }
+}
+
+void
+AddToEngineList (int i)
+{
+    if(addToList) {
+       int len;
+       char quote, buf[MSG_SIZ];
+       char *q = firstChessProgramNames, *p = newEngineCommand;
+       if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
+       quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s",
+                       quote, p, quote, appData.directory[i],
+                       useNick ? " -fn \"" : "",
+                       useNick ? nickName : "",
+                       useNick ? "\"" : "",
+                       v1 ? " -firstProtocolVersion 1" : "",
+                       hasBook ? "" : " -fNoOwnBookUCI",
+                       isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
+                       storeVariant ? " -variant " : "",
+                       storeVariant ? VariantName(gameInfo.variant) : "");
+       if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions);
+       firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2);
+       if(insert != q) insert[-1] = NULLCHAR;
+       snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert);
+       if(q)   free(q);
+       SaveEngineList();
+       FloatToFront(&appData.recentEngineList, buf);
+       ASSIGN(currentEngine[i], buf);
+    }
+}
 
 void
 LoadEngine ()
 {
     int i;
     if(WaitForEngine(savCps, LoadEngine)) return;
+    if(tryNr == 1 && !isUCI) { SendToProgram("uci\n", savCps); tryNr = 2; ScheduleDelayedEvent(LoadEngine, FEATURE_TIMEOUT); return; }
+    if(tryNr) v1 |= (tryNr == 2), tryNr = 0, AddToEngineList(0); // deferred to after protocol determination
     CommonEngineInit(); // recalculate time odds
     if(gameInfo.variant != StringToVariant(appData.variant)) {
        // we changed variant when loading the engine; this forces us to reset
@@ -948,14 +1014,11 @@ ReplaceEngine (ChessProgramState *cps, int n)
     appData.clockMode = TRUE;
     InitEngine(cps, n);
     UpdateLogos(TRUE);
-    if(n) return; // only startup first engine immediately; second can wait
+    if(n && !tryNr) return; // only startup first engine immediately; second can wait (unless autodetect)
     savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-(
     LoadEngine();
 }
 
-extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
-extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
-
 static char resetOptions[] =
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
        "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
@@ -963,27 +1026,6 @@ static char resetOptions[] =
        "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
 
 void
-FloatToFront(char **list, char *engineLine)
-{
-    char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
-    int i=0;
-    if(appData.recentEngines <= 0) return;
-    TidyProgramName(engineLine, "localhost", tidy+1);
-    tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
-    strncpy(buf+1, *list, MSG_SIZ-50);
-    if(p = strstr(buf, tidy)) { // tidy name appears in list
-       q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
-       while(*p++ = *++q); // squeeze out
-    }
-    strcat(tidy, buf+1); // put list behind tidy name
-    p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
-    if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
-    ASSIGN(*list, tidy+1);
-}
-
-char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine
-
-void
 Load (ChessProgramState *cps, int i)
 {
     char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar;
@@ -1020,35 +1062,14 @@ Load (ChessProgramState *cps, int i)
     }
     if(jar) { snprintf(buf3, MSG_SIZ, "java -jar %s", p); p = buf3; }
     ASSIGN(appData.chessProgram[i], p);
+    tryNr = 3; // requests adding to list without auto-detect
+    if(isUCI == 3) tryNr = 1, isUCI = 0; // auto-detect
     appData.isUCI[i] = isUCI;
     appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
     appData.hasOwnBookUCI[i] = hasBook;
     if(!nickName[0]) useNick = FALSE;
     if(useNick) ASSIGN(appData.pgnName[i], nickName);
-    if(addToList) {
-       int len;
-       char quote;
-       q = firstChessProgramNames;
-       if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
-       quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
-       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s",
-                       quote, p, quote, appData.directory[i],
-                       useNick ? " -fn \"" : "",
-                       useNick ? nickName : "",
-                       useNick ? "\"" : "",
-                       v1 ? " -firstProtocolVersion 1" : "",
-                       hasBook ? "" : " -fNoOwnBookUCI",
-                       isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
-                       storeVariant ? " -variant " : "",
-                       storeVariant ? VariantName(gameInfo.variant) : "");
-       if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions);
-       firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2);
-       if(insert != q) insert[-1] = NULLCHAR;
-       snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert);
-       if(q)   free(q);
-       FloatToFront(&appData.recentEngineList, buf);
-       ASSIGN(currentEngine[i], buf);
-    }
+    safeStrCpy(newEngineCommand, p, MSG_SIZ);
     ReplaceEngine(cps, i);
 }
 
@@ -2485,7 +2506,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 +2515,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 +4230,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 +4246,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 +4260,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);
@@ -4738,7 +4762,13 @@ ParseBoard12 (char *string)
     boards[moveNum][EP_STATUS] = EP_NONE;
     if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
     if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
-    if(double_push !=  -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+    if(double_push !=  -1) {
+       int dir = WhiteOnMove(moveNum) ? 1 : -1, last = BOARD_HEIGHT-1;
+       boards[moveNum][EP_FILE] = // also set new e.p. variables
+       boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+       boards[moveNum][EP_RANK] = (last + 3*dir)/2;
+       boards[moveNum][LAST_TO] = 128*(last + dir) + boards[moveNum][EP_FILE];
+    } else boards[moveNum][EP_FILE] = boards[moveNum][EP_RANK] = 100;
 
 
     if (ics_getting_history == H_GOT_REQ_HEADER ||
@@ -4904,7 +4934,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 +4942,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 +6156,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);
@@ -6339,10 +6369,11 @@ InitPosition (int redraw)
       shuffleOpenings = 1;
       break;
     case VariantNoCastle:
-      pieces = FIDEArray;
-      nrCastlingRights = 0;
       /* !!?unconstrained back-rank shuffle */
       shuffleOpenings = 1;
+    case VariantSuicide:
+      pieces = FIDEArray;
+      nrCastlingRights = 0;
       break;
     }
 
@@ -6356,13 +6387,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 +6472,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 +6483,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 +6740,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 +6764,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 +6782,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 +6851,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 +7223,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 +7280,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 +7529,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 +7562,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
@@ -7542,6 +7609,7 @@ void ReportClick(char *action, int x, int y)
 
 Boolean right; // instructs front-end to use button-1 events as if they were button 3
 Boolean deferChoice;
+int createX = -1, createY = -1; // square where we last created a piece in EditPosition mode
 
 void
 LeftClick (ClickType clickType, int xPix, int yPix)
@@ -7565,13 +7633,30 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        x = BOARD_WIDTH - 1 - x;
     }
 
-    if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) {
+    // 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 || x == createX && y == createY) ) {
        static int dummy;
        RightClick(clickType, xPix, yPix, &dummy, &dummy);
        right = TRUE;
        return;
     }
 
+    createX = createY = -1;
+
     if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
 
     prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
@@ -7880,8 +7965,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);
@@ -8002,9 +8087,15 @@ RightClick (ClickType action, int x, int y, int *fromX, int *fromY)
        if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
        if (xSqr < 0 || ySqr < 0) return -1;
        if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+       if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+       if(xSqr == createX && ySqr == createY && xSqr != BOARD_LEFT-2 && xSqr != BOARD_RGHT+1) {
+           ChessSquare p = boards[currentMove][ySqr][xSqr];
+           do { if(++p == EmptySquare) p = WhitePawn; } while(PieceToChar(p) == '.');
+           boards[currentMove][ySqr][xSqr] = p; DrawPosition(FALSE, boards[currentMove]);
+           return -2;
+        }
        pieceSweep = shiftKey ? BlackPawn : WhitePawn;  // [HGM] sweep: prepare selecting piece by mouse sweep
-       toX = xSqr; toY = ySqr; lastX = x, lastY = y;
-       if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
+       createX = toX = xSqr; createY = toY = ySqr; lastX = x, lastY = y;
        NextPiece(0);
        return 2; // grab
       case IcsObserving:
@@ -8437,6 +8528,32 @@ Adjudicate (ChessProgramState *cps)
                           return 1;
                      }
                 } else moveCount = 6;
+
+                if(gameInfo.variant == VariantMakruk && // Makruk counting rules
+                  (nrW == 1 || nrB == 1 || nr[WhitePawn] + nr[BlackPawn] == 0)) { // which only kick in when pawnless or bare King
+                    int maxcnt, his, mine, c, wom = WhiteOnMove(forwardMostMove);
+                    count = forwardMostMove;
+                    while(count >= backwardMostMove) {
+                        int np = nr[WhitePawn] + nr[BlackPawn];
+                        if(wom) mine = nrW, his = nrB, c = BlackPawn;
+                        else    mine = nrB, his = nrW, c = WhitePawn;
+                        if(mine > 1 && np) { count++; break; }
+                        if(mine > 1) maxcnt = 64; else
+                        maxcnt = (nr[WhiteRook+c] > 1 ? 8 : nr[WhiteRook+c] ? 16 : nr[WhiteMan+c] > 1 ? 22 :
+                                                            nr[WhiteKnight+c] > 1 ? 32 : nr[WhiteMan+c] ? 44 : 64) - his - 1;
+                        while(boards[count][EP_STATUS] != EP_CAPTURE && count > backwardMostMove) count--; // seek previous character
+                        if(count == backwardMostMove) break;
+                        if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) break;
+                        Count(boards[--count], nr, &nrW, &nrB, &staleW, &staleB, &bishopColor);
+                    }
+                    if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) {
+                        boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
+                        if(canAdjudicate && appData.ruleMoves >= 0) {
+                            GameEnds( GameIsDrawn, "Xboard adjudication: counting rule", GE_XBOARD );
+                            return 1;
+                        }
+                    }
+                }
            }
 
        // Repetition draws and 50-move rule can be applied independently of legality testing
@@ -8545,7 +8662,7 @@ Adjudicate (ChessProgramState *cps)
                                i++;
                        }
                }
-                if( count >= 100)
+                if( count >= 100 && gameInfo.variant != VariantMakruk) // do not accept 50-move claims in Makruk
                          boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
                          /* this is used to judge if draw claims are legal */
                 if(canAdjudicate && appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {
@@ -8940,7 +9057,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                 GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
                            buf1, GE_XBOARD);
                return;
-           } else if(!appData.fischerCastling)
+           } else if(!appData.fischerCastling && toX != BOARD_WIDTH>>1)
            /* [HGM] Kludge to handle engines that send FRC-style castling
               when they shouldn't (like TSCP-Gothic) */
            switch(moveType) {
@@ -9131,7 +9248,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        cps->useSigterm = FALSE;
     }
     if (strncmp(message, "feature ", 8) == 0) { // [HGM] moved forward to pre-empt non-compliant commands
-      ParseFeatures(message+8, cps);
+      ParseFeatures(message+8, cps); if(tryNr && tryNr < 3) tryNr = 3;
       return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
     }
 
@@ -9151,7 +9268,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
@@ -9341,6 +9459,12 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        first.highlight = f;
        return;
     }
+    if(strncmp(message, "uciok", 5) == 0) { // response to "uci" probe
+       int nr = (cps == &second);
+       appData.isUCI[nr] = isUCI = 1;
+       ReplaceEngine(cps, nr); // retry install as UCI
+       return;
+    }
     /*
      * If the move is illegal, cancel it and redraw the board.
      * Also deal with other error cases.  Matching is rather loose
@@ -10030,7 +10154,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 +10315,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 +10347,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 +10405,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 +10419,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 +10632,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 +10665,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 +10706,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 +10716,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 +10812,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], "#");
@@ -11275,6 +11405,7 @@ SaveEngineSettings (int n)
 {
     int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings;
     if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded
+    if(*engineListFile) ParseSettingsFile(engineListFile, &engineListFile); // update engine list
     p = strstr(firstChessProgramNames, currentEngine[n]);
     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);
@@ -11294,6 +11425,7 @@ SaveEngineSettings (int n)
     s = malloc(len);
     snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q);
     FREE(firstChessProgramNames); firstChessProgramNames = s; // new list
+    if(*engineListFile) SaveEngineList();
 }
 
 // following implemented as macro to avoid type limitations
@@ -11389,6 +11521,7 @@ RecentEngineEvent (int nr)
     if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
        ReplaceEngine(&first, 0);
        FloatToFront(&appData.recentEngineList, command[n]);
+       ASSIGN(currentEngine[0], command[n]);
     }
 }
 
@@ -12090,6 +12223,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
@@ -14723,18 +14857,25 @@ EditTagsEvent ()
 }
 
 void
-ToggleSecond ()
+StartSecond ()
 {
-  if(second.analyzing) {
-    SendToProgram("exit\n", &second);
-    second.analyzing = FALSE;
-  } else {
-    if (second.pr == NoProc) StartChessProgram(&second);
+    if(WaitForEngine(&second, StartSecond)) return;
     InitChessProgram(&second, FALSE);
     FeedMovesToProgram(&second, currentMove);
 
     SendToProgram("analyze\n", &second);
     second.analyzing = TRUE;
+    ThawUI();
+}
+
+void
+ToggleSecond ()
+{
+  if(second.analyzing) {
+    SendToProgram("exit\n", &second);
+    second.analyzing = FALSE;
+  } else {
+    StartSecond();
   }
 }
 
@@ -15635,7 +15776,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) {
@@ -15703,8 +15844,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);
@@ -17301,6 +17442,8 @@ ParseOption (Option *opt, ChessProgramState *cps)
            opt->type = SaveButton;
        } else return FALSE;
        *p = 0; // terminate option name
+       *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
+       if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
        // now look if the command-line options define a setting for this engine option.
        if(cps->optionSettings && cps->optionSettings[0])
            p = strstr(cps->optionSettings, opt->name); else p = NULL;
@@ -17313,6 +17456,8 @@ ParseOption (Option *opt, ChessProgramState *cps)
                            if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
                        break;
                    case TextBox:
+                   case FileName:
+                   case PathName:
                        safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
                        break;
                    case Spin:
@@ -17324,8 +17469,6 @@ ParseOption (Option *opt, ChessProgramState *cps)
                strcat(buf, "\n");
                SendToProgram(buf, cps);
        }
-       *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
-       if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
        return TRUE;
 }
 
@@ -17335,7 +17478,7 @@ FeatureDone (ChessProgramState *cps, int val)
   DelayedEventCallback cb = GetDelayedEvent();
   if ((cb == InitBackEnd3 && cps == &first) ||
       (cb == SettingsMenuIfReady && cps == &second) ||
-      (cb == LoadEngine) ||
+      (cb == LoadEngine) || (cb == StartSecond) ||
       (cb == TwoMachinesEventIfReady)) {
     CancelDelayedEvent();
     ScheduleDelayedEvent(cb, val ? 1 : 3600000);
@@ -18325,9 +18468,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);
         }
 
@@ -18435,13 +18578,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++ = '-';
        }
@@ -18460,6 +18605,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;
 
@@ -18573,7 +18724,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 */
@@ -18597,8 +18748,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;
@@ -18624,9 +18775,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;
                         }
                     }
@@ -18812,18 +18963,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;