changes from H.G. Muller; version 4.3.15
[xboard.git] / backend.c
index 1f0af61..5496797 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -164,7 +164,7 @@ void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
                  Board board));\r
 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
 void ShowMove P((int fromX, int fromY, int toX, int toY));\r
-void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
+int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
                   /*char*/int promoChar));\r
 void BackwardInner P((int target));\r
 void ForwardInner P((int target));\r
@@ -215,16 +215,24 @@ void ParseFeatures P((char* args, ChessProgramState *cps));
 void InitBackEnd3 P((void));\r
 void FeatureDone P((ChessProgramState* cps, int val));\r
 void InitChessProgram P((ChessProgramState *cps, int setup));\r
+ChessProgramState *WhitePlayer();\r
 \r
 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
+void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c\r
+char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move\r
+char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book\r
+extern char installDir[MSG_SIZ];\r
 \r
 extern int tinyLayout, smallLayout;\r
 static ChessProgramStats programStats;\r
 static int exiting = 0; /* [HGM] moved to top */\r
 static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
 extern int startedFromPositionFile;\r
-int startedFromPositionFile = FALSE; Board filePosition;    /* [HGM] loadPos */\r
-char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
+int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */\r
+char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
+int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */\r
+VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */\r
+int lastIndex = 0;      /* [HGM] autoinc: last game/position used in match mode */\r
 \r
 /* States for ics_getting_history */\r
 #define H_FALSE 0\r
@@ -320,10 +328,12 @@ PosFlags(index)
   case VariantKriegspiel:\r
     flags |= F_KRIEGSPIEL_CAPTURE;\r
     break;\r
-/*  case VariantCapaRandom: */\r
+  case VariantCapaRandom: \r
   case VariantFischeRandom:\r
     flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */\r
   case VariantNoCastle:\r
+  case VariantShatranj:\r
+  case VariantCourier:\r
     flags &= ~F_ALL_CASTLE_OK;\r
     break;\r
   default:\r
@@ -425,6 +435,7 @@ int   initialRulePlies, FENrulePlies;
 char  FENepStatus;\r
 FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)\r
 int loadFlag = 0; \r
+int shuffleOpenings;\r
 \r
 ChessSquare  FIDEArray[2][BOARD_SIZE] = {\r
     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
@@ -484,6 +495,13 @@ ChessSquare CapablancaArray[2][BOARD_SIZE] = {
         BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
 };\r
 \r
+ChessSquare JanusArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, \r
+        WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },\r
+    { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing, \r
+        BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }\r
+};\r
+\r
 #ifdef GOTHIC\r
 ChessSquare GothicArray[2][BOARD_SIZE] = {\r
     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, \r
@@ -562,6 +580,8 @@ InitBackEnd1()
 {\r
     int matched, min, sec;\r
 \r
+    ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
+\r
     GetTimeMark(&programStartTime);\r
 \r
     ClearProgramStats();\r
@@ -665,6 +685,8 @@ InitBackEnd1()
     first.useSigterm = second.useSigterm = TRUE;\r
     first.reuse = appData.reuseFirst;\r
     second.reuse = appData.reuseSecond;\r
+    first.nps = appData.firstNPS;   // [HGM] nps: copy nodes per second\r
+    second.nps = appData.secondNPS;\r
     first.useSetboard = second.useSetboard = FALSE;\r
     first.useSAN = second.useSAN = FALSE;\r
     first.usePing = second.usePing = FALSE;\r
@@ -710,6 +732,7 @@ InitBackEnd1()
 \r
     /* [HGM] debug */\r
     first.debug = second.debug = FALSE;\r
+    first.supportsNPS = second.supportsNPS = UNKNOWN;\r
 \r
     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
@@ -760,15 +783,22 @@ InitBackEnd1()
                                        + strlen(PATCHLEVEL));\r
        sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);\r
     } else {\r
+#if 0\r
        char *p, *q;\r
        q = first.program;\r
        while (*q != ' ' && *q != NULLCHAR) q++;\r
        p = q;\r
-       while (p > first.program && *(p-1) != '/') p--;\r
+       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
        programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
                                        + strlen(PATCHLEVEL) + (q - p));\r
        sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
        strncat(programVersion, p, q - p);\r
+#else\r
+       /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */\r
+       programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
+                                       + strlen(PATCHLEVEL) + strlen(first.tidy));\r
+       sprintf(programVersion, "%s %s.%s + %s", PRODUCT, VERSION, PATCHLEVEL, first.tidy);\r
+#endif\r
     }\r
 \r
     if (!appData.icsActive) {\r
@@ -815,7 +845,7 @@ InitBackEnd1()
       case VariantNormal:     /* definitely works! */\r
       case VariantWildCastle: /* pieces not automatically shuffled */\r
       case VariantNoCastle:   /* pieces not automatically shuffled */\r
-      case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */\r
+      case VariantFischeRandom: /* [HGM] works and shuffles pieces */\r
       case VariantLosers:     /* should work except for win condition,\r
                                 and doesn't know captures are mandatory */\r
       case VariantSuicide:    /* should work except for win condition,\r
@@ -825,10 +855,17 @@ InitBackEnd1()
       case VariantTwoKings:   /* should work */\r
       case VariantAtomic:     /* should work except for win condition */\r
       case Variant3Check:     /* should work except for win condition */\r
-      case VariantShatranj:   /* might work if TestLegality is off */\r
+      case VariantShatranj:   /* should work except for all win conditions */\r
+      case VariantBerolina:   /* might work if TestLegality is off */\r
+      case VariantCapaRandom: /* should work */\r
+      case VariantJanus:      /* should work */\r
+      case VariantSuper:      /* experimental */\r
        break;\r
       }\r
     }\r
+\r
+    InitEngineUCI( installDir, &first );  // [HGM] moved here from winboard.c, to make available in xboard\r
+    InitEngineUCI( installDir, &second );\r
 }\r
 \r
 int NextIntegerFromString( char ** str, long * value )\r
@@ -1005,6 +1042,12 @@ InitBackEnd2()
     } else if (appData.matchMode) {\r
        appData.matchGames = 1;\r
     }\r
+    if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */\r
+       appData.matchGames = appData.sameColorGames;\r
+    if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */\r
+       if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;\r
+       if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;\r
+    }\r
     Reset(TRUE, FALSE);\r
     if (appData.noChessProgram || first.protocolVersion == 1) {\r
       InitBackEnd3();\r
@@ -1023,9 +1066,6 @@ InitBackEnd3 P((void))
     char buf[MSG_SIZ];\r
     int err;\r
 \r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From InitBackend3\n");\r
-    }\r
     InitChessProgram(&first, startedFromSetupPosition);\r
 \r
     if (appData.icsActive) {\r
@@ -1095,15 +1135,19 @@ InitBackEnd3 P((void))
        matchMode = TRUE;\r
        matchGame = 1;\r
        if (*appData.loadGameFile != NULLCHAR) {\r
+           int index = appData.loadGameIndex; // [HGM] autoinc\r
+           if(index<0) lastIndex = index = 1;\r
            if (!LoadGameFromFile(appData.loadGameFile,\r
-                                 appData.loadGameIndex,\r
+                                 index,\r
                                  appData.loadGameFile, FALSE)) {\r
                DisplayFatalError("Bad game file", 0, 1);\r
                return;\r
            }\r
        } else if (*appData.loadPositionFile != NULLCHAR) {\r
+           int index = appData.loadPositionIndex; // [HGM] autoinc\r
+           if(index<0) lastIndex = index = 1;\r
            if (!LoadPositionFromFile(appData.loadPositionFile,\r
-                                     appData.loadPositionIndex,\r
+                                     index,\r
                                      appData.loadPositionFile)) {\r
                DisplayFatalError("Bad position file", 0, 1);\r
                return;\r
@@ -1154,7 +1198,8 @@ InitBackEnd3 P((void))
          }\r
          AnalyzeModeEvent();\r
        } else if (initialMode == AnalyzeFile) {\r
-         ShowThinkingEvent(TRUE);\r
+         appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent\r
+         ShowThinkingEvent();\r
          AnalyzeFileEvent();\r
          AnalysisPeriodicEvent(1);\r
        } else if (initialMode == MachinePlaysWhite) {\r
@@ -1616,7 +1661,18 @@ StringToVariant(e)
         case 45:\r
           v = VariantFalcon;\r
          break;\r
-\r
+        case 46:\r
+          v = VariantCapaRandom;\r
+         break;\r
+        case 47:\r
+          v = VariantBerolina;\r
+         break;\r
+        case 48:\r
+          v = VariantJanus;\r
+         break;\r
+        case 49:\r
+          v = VariantSuper;\r
+         break;\r
        case -1:\r
          /* Found "wild" or "w" in the string but no number;\r
             must assume it's normal chess. */\r
@@ -1840,12 +1896,12 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
 \r
 }\r
 \r
-char startBoard[MSG_SIZ]; /* [HGM] variantswitch */\r
 \r
 void\r
 VariantSwitch(Board board, VariantClass newVariant)\r
 {\r
-   int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j, oldCurrentMove = currentMove;\r
+   int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\r
+   int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;\r
    Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
 \r
    startedFromPositionFile = FALSE;\r
@@ -1869,8 +1925,9 @@ VariantSwitch(Board board, VariantClass newVariant)
                VariantName(gameInfo.variant), VariantName(newVariant));\r
     setbuf(debugFP, NULL);\r
   }\r
+    shuffleOpenings = 0;       /* [HGM] shuffle */\r
     gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
-         switch(newVariant) {\r
+    switch(newVariant) {\r
             case VariantShogi:\r
               newWidth = 9;  newHeight = 9;\r
               gameInfo.holdingsSize = 7;\r
@@ -1915,14 +1972,16 @@ VariantSwitch(Board board, VariantClass newVariant)
         saveEP = epStatus[0];\r
 #endif\r
         InitPosition(FALSE);          /* this sets up board[0], but also other stuff        */\r
-        forwardMostMove = backwardMostMove =\r
-        currentMove = oldCurrentMove; /* InitPos reset this, but we need still to redraw it */\r
 #if 0\r
         epStatus[0] = saveEP;\r
         for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];\r
         CopyBoard(tempBoard, board); /* restore position received from ICS   */\r
 #endif\r
     } else { gameInfo.variant = newVariant; InitPosition(FALSE); }\r
+\r
+    forwardMostMove = oldForwardMostMove;\r
+    backwardMostMove = oldBackwardMostMove;\r
+    currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */\r
 }\r
 \r
 static int loggedOn = FALSE;\r
@@ -1997,6 +2056,9 @@ read_from_ics(isr, closure, data, count, error)
        for (i = 0; i < count; i++) {\r
            if (data[i] != NULLCHAR && data[i] != '\r')\r
              buf[buf_len++] = data[i];\r
+           if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' && \r
+                               buf[buf_len-3]==' '  && buf[buf_len-2]==' '  && buf[buf_len-1]==' ') \r
+               buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous\r
        }\r
 \r
        buf[buf_len] = NULLCHAR;\r
@@ -2904,6 +2966,7 @@ read_from_ics(isr, closure, data, count, error)
                    /* Send "new" early, in case this command takes\r
                       a long time to finish, so that we'll be ready\r
                       for the next challenge. */\r
+                   gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'\r
                    Reset(TRUE, TRUE);\r
                }\r
 #endif /*ZIPPY*/\r
@@ -3086,7 +3149,7 @@ read_from_ics(isr, closure, data, count, error)
  * Additional trailing fields may be added in the future.  \r
  */\r
 \r
-#define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"\r
+#define PATTERN "%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"\r
 \r
 #define RELATION_OBSERVING_PLAYED    0\r
 #define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */\r
@@ -3104,7 +3167,7 @@ ParseBoard12(string)
     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;\r
     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;\r
     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;\r
-    char to_play, board_chars[72];\r
+    char to_play, board_chars[200];\r
     char move_str[500], str[500], elapsed_time[500];\r
     char black[32], white[32];\r
     Board board;\r
@@ -3113,6 +3176,8 @@ ParseBoard12(string)
     ChessMove moveType;\r
     int fromX, fromY, toX, toY;\r
     char promoChar;\r
+    int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */\r
+    char *bookHit = NULL; // [HGM] book\r
 \r
     fromX = fromY = toX = toY = -1;\r
     \r
@@ -3123,14 +3188,25 @@ ParseBoard12(string)
 \r
     move_str[0] = NULLCHAR;\r
     elapsed_time[0] = NULLCHAR;\r
-    n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,\r
+    {   /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */\r
+        int  i = 0, j;\r
+        while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {\r
+           if(string[i] == ' ') { ranks++; files = 0; }\r
+            else files++;\r
+           i++;\r
+       }\r
+       for(j = 0; j <i; j++) board_chars[j] = string[j];\r
+        board_chars[i] = '\0';\r
+       string += i + 1;\r
+    }\r
+    n = sscanf(string, PATTERN, &to_play, &double_push,\r
               &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,\r
               &gamenum, white, black, &relation, &basetime, &increment,\r
               &white_stren, &black_stren, &white_time, &black_time,\r
               &moveNum, str, elapsed_time, move_str, &ics_flip,\r
               &ticking);\r
 \r
-    if (n < 22) {\r
+    if (n < 21) {\r
        sprintf(str, "Failed to parse board string:\n\"%s\"", string);\r
        DisplayError(str, 0);\r
        return;\r
@@ -3325,10 +3401,13 @@ ParseBoard12(string)
        }\r
     }\r
     \r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "load %dx%d board\n", files, ranks);\r
+  }\r
     /* Parse the board */\r
-    for (k = 0; k < 8; k++) {\r
-      for (j = 0; j < 8; j++)\r
-        board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(7-k)*9 + j]);\r
+    for (k = 0; k < ranks; k++) {\r
+      for (j = 0; j < files; j++)\r
+        board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);\r
       if(gameInfo.holdingsWidth > 1) {\r
            board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare;\r
            board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;\r
@@ -3341,11 +3420,6 @@ ParseBoard12(string)
         if(startedFromSetupPosition)\r
             initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */\r
     }\r
-    /* [HGM] variantswitch: remember the last initial position parsed, */\r
-    /* because it might have been parsed in the wrong variant, so that */\r
-    /* we can re-parse it once we know the proper variant (which might */\r
-    /* have different piece assignments for the same letters).         */\r
-    if(moveNum == 0) strcpy(startBoard, string);\r
 \r
     /* [HGM] Set castling rights. Take the outermost Rooks,\r
        to make it also work for FRC opening positions. Note that board12\r
@@ -3358,7 +3432,7 @@ ParseBoard12(string)
        but in ICS mode that is not its job anyway.\r
     */\r
     if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)\r
-    { int i, j;\r
+    { int i, j; ChessSquare wKing = WhiteKing, bKing = BlackKing;\r
 \r
         for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
             if(board[0][i] == WhiteRook) j = i;\r
@@ -3373,10 +3447,11 @@ ParseBoard12(string)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
         initialRights[4] = castlingRights[moveNum][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
 \r
+       if(gameInfo.variant == VariantKnightmate) { wKing = WhiteUnicorn; bKing = BlackUnicorn; }\r
         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
-            if(board[0][k] == WhiteKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
+            if(board[0][k] == wKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
-            if(board[BOARD_HEIGHT-1][k] == BlackKing)\r
+            if(board[BOARD_HEIGHT-1][k] == bKing)\r
                 initialRights[5] = castlingRights[moveNum][5] = k;\r
     } else { int r;\r
         r = castlingRights[moveNum][0] = initialRights[0];\r
@@ -3544,8 +3619,8 @@ ParseBoard12(string)
                    if (first.sendTime) {\r
                        SendTimeRemaining(&first, gameMode == IcsPlayingWhite);\r
                    }\r
-                   SendMoveToProgram(moveNum - 1, &first);\r
-                   if (firstMove) {\r
+                   bookHit = SendMoveToBookUser(moveNum - 1, &first, FALSE); // [HGM] book\r
+                   if (firstMove && !bookHit) {\r
                        firstMove = FALSE;\r
                        if (first.useColors) {\r
                          SendToProgram(gameMode == IcsPlayingWhite ?\r
@@ -3562,6 +3637,7 @@ ParseBoard12(string)
                sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);\r
                DisplayError(str, 0);\r
              } else {\r
+               if(gameInfo.variant == currentlyInitializedVariant) // [HGM] refrain sending moves engine can't understand!\r
                SendMoveToProgram(moveNum - 1, &first);\r
              }\r
            }\r
@@ -3601,15 +3677,28 @@ ParseBoard12(string)
     if (gameInfo.variant != VariantBughouse &&\r
        gameInfo.variant != VariantCrazyhouse) {\r
        if (tinyLayout || smallLayout) {\r
-           sprintf(str, "%s(%d) %s(%d) {%d %d}", \r
+           if(gameInfo.variant == VariantNormal)\r
+               sprintf(str, "%s(%d) %s(%d) {%d %d}", \r
                    gameInfo.white, white_stren, gameInfo.black, black_stren,\r
                    basetime, increment);\r
+           else\r
+               sprintf(str, "%s(%d) %s(%d) {%d %d w%d}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment, (int) gameInfo.variant);\r
        } else {\r
-           sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", \r
+           if(gameInfo.variant == VariantNormal)\r
+               sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", \r
                    gameInfo.white, white_stren, gameInfo.black, black_stren,\r
                    basetime, increment);\r
+           else\r
+               sprintf(str, "%s (%d) vs. %s (%d) {%d %d %s}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment, VariantName(gameInfo.variant));\r
        }\r
        DisplayTitle(str);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Display title '%s, gameInfo.variant = %d'\n", str, gameInfo.variant);\r
+  }\r
     }\r
 \r
    \r
@@ -3629,6 +3718,19 @@ ParseBoard12(string)
     }\r
 \r
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+#if ZIPPY\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+#endif\r
 }\r
 \r
 void\r
@@ -3682,13 +3784,16 @@ SendMoveToProgram(moveNum, cps)
       }\r
       SendToProgram(buf, cps);\r
     } else {\r
+      if(cps->alphaRank) { /* [HGM] shogi: temporarily convert to shogi coordinates before sending */\r
+       AlphaRank(moveList[moveNum], 4);\r
+       SendToProgram(moveList[moveNum], cps);\r
+       AlphaRank(moveList[moveNum], 4); // and back\r
+      } else\r
       /* Added by Tord: Send castle moves in "O-O" in FRC games if required by\r
        * the engine. It would be nice to have a better way to identify castle \r
        * moves here. */\r
-      if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {\r
-  if (appData.debugMode) {\r
-    fprintf(debugFP, "Tord's FRC castling code\n");\r
-  }\r
+      if((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom)\r
+                                                                        && cps->useOOCastle) {\r
         int fromX = moveList[moveNum][0] - AAA; \r
         int fromY = moveList[moveNum][1] - ONE;\r
         int toX = moveList[moveNum][2] - AAA; \r
@@ -3813,7 +3918,6 @@ CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
                     AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);\r
        }\r
     }\r
-    AlphaRank(move, 4);\r
 }\r
 \r
 void\r
@@ -3836,8 +3940,6 @@ AlphaRank(char *move, int n)
 {\r
     char *p = move, c; int x, y;\r
 \r
-    if( !appData.alphaRank ) return;\r
-\r
     if (appData.debugMode) {\r
         fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
     }\r
@@ -3845,6 +3947,7 @@ AlphaRank(char *move, int n)
     if(move[1]=='*' && \r
        move[2]>='0' && move[2]<='9' &&\r
        move[3]>='a' && move[3]<='x'    ) {\r
+        move[1] = '@';\r
         move[2] = BOARD_RGHT  -1 - (move[2]-'1') + AAA;\r
         move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
     } else\r
@@ -3951,7 +4054,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
       case BlackDrop:\r
        *fromX = *moveType == WhiteDrop ?\r
          (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
-       (int) CharToPiece(ToLower(currentMoveString[0]));\r
+         (int) CharToPiece(ToLower(currentMoveString[0]));\r
        *fromY = DROP_RANK;\r
         *toX = currentMoveString[2] - AAA;\r
         *toY = currentMoveString[3] - ONE;\r
@@ -3995,6 +4098,7 @@ static int FindEmptySquare( Board board, int n )
     return i;\r
 }\r
 \r
+#if 0\r
 static void ShuffleFRC( Board board )\r
 {\r
     int i;\r
@@ -4076,12 +4180,170 @@ static void SetupFRC( Board board, int pos_index )
         board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
     }\r
 }\r
+#else\r
+// [HGM] shuffle: a more general way to suffle opening setups, applicable to arbitrry variants.\r
+// All positions will have equal probability, but the current method will not provide a unique\r
+// numbering scheme for arrays that contain 3 or more pieces of the same kind.\r
+#define DARK 1\r
+#define LITE 2\r
+#define ANY 3\r
+\r
+int squaresLeft[4];\r
+int piecesLeft[(int)BlackPawn];\r
+long long int seed, nrOfShuffles;\r
+\r
+void GetPositionNumber()\r
+{      // sets global variable seed\r
+       int i;\r
+\r
+       seed = appData.defaultFrcPosition;\r
+       if(seed < 0) { // randomize based on time for negative FRC position numbers\r
+               srandom(time(0)); \r
+               for(i=0; i<50; i++) seed += random();\r
+               seed = random() ^ random() >> 8 ^ random() << 8;\r
+               if(seed<0) seed = -seed;\r
+       }\r
+}\r
+\r
+int put(Board board, int pieceType, int rank, int n, int shade)\r
+// put the piece on the (n-1)-th empty squares of the given shade\r
+{\r
+       int i;\r
+\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
+               if( ((i-BOARD_LEFT)&1)+1 & shade && board[rank][i] == EmptySquare && n-- == 0) {\r
+                       board[rank][i] = (ChessSquare) pieceType;\r
+                       squaresLeft[(i-BOARD_LEFT&1) + 1]--;\r
+                       squaresLeft[ANY]--;\r
+                       piecesLeft[pieceType]--; \r
+                       return i;\r
+               }\r
+       }\r
+        return -1;\r
+}\r
+\r
+\r
+void AddOnePiece(Board board, int pieceType, int rank, int shade)\r
+// calculate where the next piece goes, (any empty square), and put it there\r
+{\r
+       int i;\r
+\r
+        i = seed % squaresLeft[shade];\r
+       nrOfShuffles *= squaresLeft[shade];\r
+       seed /= squaresLeft[shade];\r
+        put(board, pieceType, rank, i, shade);\r
+}\r
+\r
+void AddTwoPieces(Board board, int pieceType, int rank)\r
+// calculate where the next 2 identical pieces go, (any empty square), and put it there\r
+{\r
+       int i, n=squaresLeft[ANY], j=n-1, k;\r
+\r
+       k = n*(n-1)/2; // nr of possibilities, not counting permutations\r
+        i = seed % k;  // pick one\r
+       nrOfShuffles *= k;\r
+       seed /= k;\r
+       while(i >= j) i -= j--;\r
+        j = n - 1 - j; i += j;\r
+        put(board, pieceType, rank, j, ANY);\r
+        put(board, pieceType, rank, i, ANY);\r
+}\r
+\r
+void SetUpShuffle(Board board, int number)\r
+{\r
+       int i, p, first=1;\r
+\r
+       GetPositionNumber(); nrOfShuffles = 1;\r
+\r
+       squaresLeft[DARK] = (BOARD_RGHT - BOARD_LEFT + 1)/2;\r
+       squaresLeft[ANY]  = BOARD_RGHT - BOARD_LEFT;\r
+       squaresLeft[LITE] = squaresLeft[ANY] - squaresLeft[DARK];\r
+\r
+       for(p = 0; p<=(int)WhiteKing; p++) piecesLeft[p] = 0;\r
+\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // count pieces and clear board\r
+           p = (int) board[0][i];\r
+           if(p < (int) BlackPawn) piecesLeft[p] ++;\r
+           board[0][i] = EmptySquare;\r
+       }\r
+\r
+       if(PosFlags(0) & F_ALL_CASTLE_OK) {\r
+           // shuffles restricted to allow normal castling put KRR first\r
+           if(piecesLeft[(int)WhiteKing]) // King goes rightish of middle\r
+               put(board, WhiteKing, 0, (gameInfo.boardWidth+1)/2, ANY);\r
+           else if(piecesLeft[(int)WhiteUnicorn]) // in Knightmate Unicorn castles\r
+               put(board, WhiteUnicorn, 0, (gameInfo.boardWidth+1)/2, ANY);\r
+           if(piecesLeft[(int)WhiteRook]) // First supply a Rook for K-side castling\r
+               put(board, WhiteRook, 0, gameInfo.boardWidth-2, ANY);\r
+           if(piecesLeft[(int)WhiteRook]) // Then supply a Rook for Q-side castling\r
+               put(board, WhiteRook, 0, 0, ANY);\r
+           // in variants with super-numerary Kings and Rooks, we leave these for the shuffle\r
+       }\r
 \r
-BOOL SetCharTable( char *table, const char * map )\r
+       if((BOARD_RGHT-BOARD_LEFT & 1) == 0)\r
+           // only for even boards make effort to put pairs of colorbound pieces on opposite colors\r
+           for(p = (int) WhiteKing; p > (int) WhitePawn; p--) {\r
+               if(p != (int) WhiteBishop && p != (int) WhiteFerz && p != (int) WhiteAlfil) continue;\r
+               while(piecesLeft[p] >= 2) {\r
+                   AddOnePiece(board, p, 0, LITE);\r
+                   AddOnePiece(board, p, 0, DARK);\r
+               }\r
+               // Odd color-bound pieces are shuffled with the rest (to not run out of paired squares)\r
+           }\r
+\r
+       for(p = (int) WhiteKing - 2; p > (int) WhitePawn; p--) {\r
+           // Remaining pieces (non-colorbound, or odd color bound) can be put anywhere\r
+           // but we leave King and Rooks for last, to possibly obey FRC restriction\r
+           if(p == (int)WhiteRook) continue;\r
+           while(piecesLeft[p] >= 2) AddTwoPieces(board, p, 0); // add in pairs, for not counting permutations\r
+           if(piecesLeft[p]) AddOnePiece(board, p, 0, ANY);     // add the odd piece\r
+       }\r
+\r
+       // now everything is placed, except perhaps King (Unicorn) and Rooks\r
+\r
+       if(PosFlags(0) & F_FRC_TYPE_CASTLING) {\r
+           // Last King gets castling rights\r
+           while(piecesLeft[(int)WhiteUnicorn]) {\r
+               i = put(board, WhiteUnicorn, 0, piecesLeft[(int)WhiteRook]/2, ANY);\r
+               initialRights[2]  = initialRights[5]  = castlingRights[0][2] = castlingRights[0][5] = i;\r
+           }\r
+\r
+           while(piecesLeft[(int)WhiteKing]) {\r
+               i = put(board, WhiteKing, 0, piecesLeft[(int)WhiteRook]/2, ANY);\r
+               initialRights[2]  = initialRights[5]  = castlingRights[0][2] = castlingRights[0][5] = i;\r
+           }\r
+\r
+\r
+       } else {\r
+           while(piecesLeft[(int)WhiteKing])    AddOnePiece(board, WhiteKing, 0, ANY);\r
+           while(piecesLeft[(int)WhiteUnicorn]) AddOnePiece(board, WhiteUnicorn, 0, ANY);\r
+       }\r
+\r
+       // Only Rooks can be left; simply place them all\r
+       while(piecesLeft[(int)WhiteRook]) {\r
+               i = put(board, WhiteRook, 0, 0, ANY);\r
+               if(PosFlags(0) & F_FRC_TYPE_CASTLING) { // first and last Rook get FRC castling rights\r
+                       if(first) {\r
+                               first=0;\r
+                               initialRights[1]  = initialRights[4]  = castlingRights[0][1] = castlingRights[0][4] = i;\r
+                       }\r
+                       initialRights[0]  = initialRights[3]  = castlingRights[0][0] = castlingRights[0][3] = i;\r
+               }\r
+       }\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // copy black from white\r
+           board[BOARD_HEIGHT-1][i] =  (int) board[0][i] < BlackPawn ? WHITE_TO_BLACK board[0][i] : EmptySquare;\r
+       }\r
+\r
+       if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize\r
+}\r
+\r
+#endif\r
+\r
+int SetCharTable( char *table, const char * map )\r
 /* [HGM] moved here from winboard.c because of its general usefulness */\r
 /*       Basically a safe strcpy that uses the last character as King */\r
 {\r
-    BOOL result = FALSE; int NrPieces;\r
+    int result = FALSE; int NrPieces;\r
 \r
     if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
                     && NrPieces >= 12 && !(NrPieces&1)) {\r
@@ -4101,6 +4363,42 @@ BOOL SetCharTable( char *table, const char * map )
     return result;\r
 }\r
 \r
+void Prelude(Board board)\r
+{      // [HGM] superchess: random selection of exo-pieces\r
+       int i, j, k; ChessSquare p; \r
+       static ChessSquare exoPieces[4] = { WhiteAngel, WhiteMarshall, WhiteSilver, WhiteLance };\r
+\r
+       GetPositionNumber(); // use FRC position number\r
+\r
+       if(appData.pieceToCharTable != NULL) { // select pieces to participate from given char table\r
+           SetCharTable(pieceToChar, appData.pieceToCharTable);\r
+           for(i=(int)WhiteQueen+1, j=0; i<(int)WhiteKing && j<4; i++) \r
+               if(PieceToChar((ChessSquare)i) != '.') exoPieces[j++] = (ChessSquare) i;\r
+       }\r
+\r
+       j = seed%4;                 seed /= 4; \r
+       p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%3 + (seed%3 >= j); seed /= 3; \r
+       p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%3;                 seed /= 3; \r
+       p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%2 + (seed%2 >= j); seed /= 2; \r
+       p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%4; seed /= 4; put(board, exoPieces[3],    0, j, ANY);\r
+       j = seed%3; seed /= 3; put(board, exoPieces[2],   0, j, ANY);\r
+       j = seed%2; seed /= 2; put(board, exoPieces[1], 0, j, ANY);\r
+       put(board, exoPieces[0],    0, 0, ANY);\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) board[BOARD_HEIGHT-1][i] = WHITE_TO_BLACK board[0][i];\r
+}\r
+\r
 void\r
 InitPosition(redraw)\r
      int redraw;\r
@@ -4113,6 +4411,7 @@ InitPosition(redraw)
     oldv = gameInfo.variant;\r
 \r
     currentMove = forwardMostMove = backwardMostMove = 0;\r
+    if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request\r
 \r
     /* [AS] Initialize pv info list [HGM] and game status */\r
     {\r
@@ -4145,6 +4444,8 @@ InitPosition(redraw)
     SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k"); \r
 \r
     switch (gameInfo.variant) {\r
+    case VariantFischeRandom:\r
+      shuffleOpenings = TRUE;\r
     default:\r
       pieces = FIDEArray;\r
       break;\r
@@ -4161,6 +4462,8 @@ InitPosition(redraw)
       castlingRank[6] = 0;\r
       castlingRank[7] = BOARD_HEIGHT-1;\r
       break;\r
+    case VariantCapaRandom:\r
+      shuffleOpenings = TRUE;\r
     case VariantCapablanca:\r
       pieces = CapablancaArray;\r
       gameInfo.boardWidth = 10;\r
@@ -4171,6 +4474,18 @@ InitPosition(redraw)
       gameInfo.boardWidth = 10;\r
       SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
       break;\r
+    case VariantJanus:\r
+      pieces = JanusArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..JKpnbrq..jk"); \r
+      nrCastlingRights = 6;\r
+        castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
+        castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
+        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH-1>>1;\r
+        castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
+        castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
+        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH-1>>1;\r
+      break;\r
     case VariantFalcon:\r
       pieces = FalconArray;\r
       gameInfo.boardWidth = 10;\r
@@ -4206,6 +4521,12 @@ InitPosition(redraw)
       pieces = fairyArray;\r
       SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
       break;\r
+    case VariantSuper:\r
+      pieces = FIDEArray;\r
+      SetCharTable(pieceToChar, "PNBRQ..SE.......V.AKpnbrq..se.......v.ak");\r
+      gameInfo.holdingsSize = 8;\r
+      startedFromSetupPosition = TRUE;\r
+      break;\r
     case VariantCrazyhouse:\r
     case VariantBughouse:\r
       pieces = FIDEArray;\r
@@ -4215,12 +4536,14 @@ InitPosition(redraw)
     case VariantWildCastle:\r
       pieces = FIDEArray;\r
       /* !!?shuffle with kings guaranteed to be on d or e file */\r
+      shuffleOpenings = 1;\r
       break;\r
     case VariantNoCastle:\r
       pieces = FIDEArray;\r
       nrCastlingRights = 0;\r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       /* !!?unconstrained back-rank shuffle */\r
+      shuffleOpenings = 1;\r
       break;\r
     }\r
 \r
@@ -4295,6 +4618,8 @@ InitPosition(redraw)
         castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
      }\r
 \r
+     if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
+#if 0\r
     if(gameInfo.variant == VariantFischeRandom) {\r
       if( appData.defaultFrcPosition < 0 ) {\r
         ShuffleFRC( initialPosition );\r
@@ -4303,7 +4628,17 @@ InitPosition(redraw)
         SetupFRC( initialPosition, appData.defaultFrcPosition );\r
       }\r
       startedFromSetupPosition = TRUE;\r
-    } else if(startedFromPositionFile) {\r
+    } else \r
+#else\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);\r
+  }\r
+    if(shuffleOpenings) {\r
+       SetUpShuffle(initialPosition, appData.defaultFrcPosition);\r
+       startedFromSetupPosition = TRUE;\r
+    }\r
+#endif\r
+    if(startedFromPositionFile) {\r
       /* [HGM] loadPos: use PositionFile for every new game */\r
       CopyBoard(initialPosition, filePosition);\r
       for(i=0; i<nrCastlingRights; i++)\r
@@ -4355,7 +4690,7 @@ SendBoard(cps, moveNum)
       SendToProgram("edit\n", cps);\r
       SendToProgram("#\n", cps);\r
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
-       bp = &boards[moveNum][i][0];\r
+       bp = &boards[moveNum][i][BOARD_LEFT];\r
         for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {\r
          if ((int) *bp < (int) BlackPawn) {\r
            sprintf(message, "%c%c%c\n", PieceToChar(*bp), \r
@@ -4365,7 +4700,7 @@ SendBoard(cps, moveNum)
                         PieceToChar((ChessSquare)(DEMOTED *bp)),\r
                         AAA + j, ONE + i);\r
             }\r
-            if(appData.alphaRank) {\r
+            if(cps->alphaRank) { /* [HGM] shogi: translate coords */\r
                 message[1] = BOARD_RGHT   - 1 - j + '1';\r
                 message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
             }\r
@@ -4376,7 +4711,7 @@ SendBoard(cps, moveNum)
     \r
       SendToProgram("c\n", cps);\r
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
-       bp = &boards[moveNum][i][0];\r
+       bp = &boards[moveNum][i][BOARD_LEFT];\r
         for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {\r
          if (((int) *bp != (int) EmptySquare)\r
              && ((int) *bp >= (int) BlackPawn)) {\r
@@ -4387,7 +4722,7 @@ SendBoard(cps, moveNum)
                         PieceToChar((ChessSquare)(DEMOTED *bp)),\r
                         AAA + j, ONE + i);\r
             }\r
-            if(appData.alphaRank) {\r
+            if(cps->alphaRank) { /* [HGM] shogi: translate coords */\r
                 message[1] = BOARD_RGHT   - 1 - j + '1';\r
                 message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
             }\r
@@ -4446,7 +4781,7 @@ PieceForSquare (x, y)
      int x;\r
      int y;\r
 {\r
-  if (x < BOARD_LEFT || x >= BOARD_RGHT || y < 0 || y >= BOARD_HEIGHT)\r
+  if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)\r
      return -1;\r
   else\r
      return boards[currentMove][y][x];\r
@@ -4589,7 +4924,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
              WhitePawn <= pup && pup < BlackPawn  ||\r
              BlackPawn <= pdown && pdown < EmptySquare &&\r
              BlackPawn <= pup && pup < EmptySquare \r
-            ) && !(gameInfo.variant == VariantFischeRandom &&\r
+            ) && !((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&\r
                     (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0||\r
                      pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1  ) \r
         )           )\r
@@ -4704,11 +5039,11 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
           click-click move is possible */\r
        if (toX == -2 || toY == -2) {\r
            boards[0][fromY][fromX] = EmptySquare;\r
-           DrawPosition(FALSE, boards[currentMove]);\r
+           return AmbiguousMove;\r
        } else if (toX >= 0 && toY >= 0) {\r
            boards[0][toY][toX] = boards[0][fromY][fromX];\r
            boards[0][fromY][fromX] = EmptySquare;\r
-           DrawPosition(FALSE, boards[currentMove]);\r
+           return AmbiguousMove;\r
        }\r
         return ImpossibleMove;\r
     }\r
@@ -4754,15 +5089,27 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
 }\r
 \r
 /* Common tail of UserMoveEvent and DropMenuEvent */\r
-void\r
+int\r
 FinishMove(moveType, fromX, fromY, toX, toY, promoChar)\r
      ChessMove moveType;\r
      int fromX, fromY, toX, toY;\r
      /*char*/int promoChar;\r
 {\r
-    /* [HGM] <popupFix> kludge to avoid having know the exact promotion\r
+    char *bookHit = 0;\r
+\r
+    if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR) { \r
+       // [HGM] superchess: suppress promotions to non-available piece\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if(WhiteOnMove(currentMove)) {\r
+           if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;\r
+       } else {\r
+           if(!boards[currentMove][BOARD_HEIGHT-1-k][1]) return 0;\r
+       }\r
+    }\r
+\r
+    /* [HGM] <popupFix> kludge to avoid having to know the exact promotion\r
        move type in caller when we know the move is a legal promotion */\r
-    if(moveType == NormalMove)\r
+    if(moveType == NormalMove && promoChar)\r
         moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
 \r
     /* [HGM] convert drag-and-drop piece drops to standard form */\r
@@ -4808,7 +5155,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
       } else {\r
        DisplayError("Incorrect move", 0);\r
       }\r
-      return;\r
+      return 1;\r
     }\r
 \r
   /* Ok, now we know that the move is good, so we can kill\r
@@ -4828,6 +5175,18 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
 \r
   MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
 \r
+    if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+       // [HGM] superchess: take promotion piece out of holdings\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if(WhiteOnMove(forwardMostMove-1)) {\r
+           if(!--boards[forwardMostMove][k][BOARD_WIDTH-2])\r
+               boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare;\r
+       } else {\r
+           if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1])\r
+               boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+       }\r
+    }\r
+\r
   if (gameMode == BeginningOfGame) {\r
     if (appData.noChessProgram) {\r
       gameMode = EditGame;\r
@@ -4835,6 +5194,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
     } else {\r
       char buf[MSG_SIZ];\r
       gameMode = MachinePlaysBlack;\r
+      StartClocks();\r
       SetGameInfo();\r
       sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
       DisplayTitle(buf);\r
@@ -4842,6 +5202,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
        sprintf(buf, "name %s\n", gameInfo.white);\r
        SendToProgram(buf, &first);\r
       }\r
+      StartClocks();\r
     }\r
     ModeHighlight();\r
   }\r
@@ -4859,10 +5220,11 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
                           gameMode == MachinePlaysBlack)) {\r
       SendTimeRemaining(&first, gameMode != MachinePlaysBlack);\r
     }\r
-    SendMoveToProgram(forwardMostMove-1, &first);\r
     if (gameMode != EditGame && gameMode != PlayFromGameFile) {\r
-      first.maybeThinking = TRUE;\r
-    }\r
+        // [HGM] book: if program might be playing, let it use book\r
+       bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE);\r
+       first.maybeThinking = TRUE;\r
+    } else SendMoveToProgram(forwardMostMove-1, &first);\r
     if (currentMove == cmailOldMove + 1) {\r
       cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
     }\r
@@ -4899,6 +5261,19 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
   default:\r
     break;\r
   }\r
+\r
+  if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+  }\r
+  return 1;\r
 }\r
 \r
 void\r
@@ -4945,6 +5320,50 @@ void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cp
     SetProgramStats( &stats );\r
 }\r
 \r
+char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)\r
+{   // [HGM] book: this routine intercepts moves to simulate book replies\r
+    char *bookHit = NULL;\r
+\r
+    //first determine if the incoming move brings opponent into his book\r
+    if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI))\r
+       bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move\r
+    if(appData.debugMode && bookHit) fprintf(debugFP, "book hit = %s\n", bookHit);\r
+    if(bookHit != NULL && !cps->bookSuspend) {\r
+       // make sure opponent is not going to reply after receiving move to book position\r
+       SendToProgram("force\n", cps);\r
+       cps->bookSuspend = TRUE; // flag indicating it has to be restarted\r
+    }\r
+    if(!initial) SendMoveToProgram(moveNr, cps); // with hit on initial position there is no move\r
+    // now arrange restart after book miss\r
+    if(bookHit) {\r
+       // after a book hit we never send 'go', and the code after the call to this routine\r
+       // has '&& !bookHit' added to suppress potential sending there (based on 'firstMove').\r
+       char buf[MSG_SIZ];\r
+       if (cps->useUsermove) sprintf(buf, "usermove "); // sorry, no SAN yet :(\r
+       sprintf(buf, "%s\n", bookHit); // force book move into program supposed to play it\r
+       SendToProgram(buf, cps);\r
+       if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go'\r
+    } else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine\r
+       SendToProgram("go\n", cps);\r
+       cps->bookSuspend = FALSE; // after a 'go' we are never suspended\r
+    } else { // 'go' might be sent based on 'firstMove' after this routine returns\r
+       if(cps->bookSuspend && !firstMove) // 'go' needed, and it will not be done after we return\r
+           SendToProgram("go\n", cps); \r
+       cps->bookSuspend = FALSE; // anyhow, we will not be suspended after a miss\r
+    }\r
+    return bookHit; // notify caller of hit, so it can take action to send move to opponent\r
+}\r
+\r
+char *savedMessage;\r
+ChessProgramState *savedState;\r
+void DeferredBookMove(void)\r
+{\r
+       if(savedState->lastPing != savedState->lastPong)\r
+                   ScheduleDelayedEvent(DeferredBookMove, 10);\r
+       else\r
+       HandleMachineMove(savedMessage, savedState);\r
+}\r
+\r
 void\r
 HandleMachineMove(message, cps)\r
      char *message;\r
@@ -4957,7 +5376,9 @@ HandleMachineMove(message, cps)
     char promoChar;\r
     char *p;\r
     int machineWhite;\r
+    char *bookHit;\r
 \r
+FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit\r
     /*\r
      * Kludge to ignore BEL characters\r
      */\r
@@ -5064,7 +5485,7 @@ HandleMachineMove(message, cps)
         fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,\r
                 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
     }\r
-        AlphaRank(machineMove, 4);\r
+        if(cps->alphaRank) AlphaRank(machineMove, 4);\r
         if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
                               &fromX, &fromY, &toX, &toY, &promoChar)) {\r
            /* Machine move could not be parsed; ignore it. */\r
@@ -5103,7 +5524,7 @@ HandleMachineMove(message, cps)
                         machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
                 GameEnds(machineWhite ? BlackWins : WhiteWins,\r
                            buf1, GE_XBOARD);\r
-           } else if(gameInfo.variant != VariantFischeRandom)\r
+           } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom)\r
            /* [HGM] Kludge to handle engines that send FRC-style castling\r
               when they shouldn't (like TSCP-Gothic) */\r
            switch(moveType) {\r
@@ -5132,6 +5553,18 @@ HandleMachineMove(message, cps)
            first.initDone) {\r
          SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
          ics_user_moved = 1;\r
+         if(appData.autoKibitz) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
+               char buf[3*MSG_SIZ];\r
+\r
+               sprintf(buf, "kibitz %d/%+.2f (%.2f sec, %.0f nodes, %1.0f knps) PV = %s\n",\r
+                       programStats.depth,\r
+                       programStats.score / 100.,\r
+                       programStats.time / 100.,\r
+                       (double) programStats.nodes,\r
+                       programStats.nodes / (10*abs(programStats.time) + 1.),\r
+                       programStats.movelist);\r
+               SendToICS(buf);\r
+         }\r
        }\r
 #endif\r
        /* currentMoveString is set as a side-effect of ParseOneMove */\r
@@ -5178,12 +5611,12 @@ HandleMachineMove(message, cps)
             }\r
         }\r
 \r
-#ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines\r
-\r
-        if( gameMode == TwoMachinesPlay && gameInfo.holdingsSize == 0) {\r
-            int count = 0, epFile = epStatus[forwardMostMove];\r
+       if( gameMode == TwoMachinesPlay ) {\r
+         // [HGM] some adjudications useful with buggy engines\r
+            int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;\r
+         if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper) {\r
 \r
-            if(appData.testLegality && appData.checkMates) \r
+            if(appData.testLegality)\r
             // don't wait for engine to announce game end if we can judge ourselves\r
             switch (MateTest(boards[forwardMostMove],\r
                                  PosFlags(forwardMostMove), epFile,\r
@@ -5193,32 +5626,38 @@ HandleMachineMove(message, cps)
              default:\r
                break;\r
              case MT_STALEMATE:\r
-                ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
-                    GE_XBOARD );\r
+               epStatus[forwardMostMove] = EP_STALEMATE;\r
+                if(appData.checkMates) {\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
+                       GE_XBOARD );\r
+               }\r
                break;\r
              case MT_CHECKMATE:\r
-                ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
-                    "Xboard adjudication: Checkmate", \r
-                    GE_XBOARD );\r
+               epStatus[forwardMostMove] = EP_CHECKMATE;\r
+                if(appData.checkMates) {\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+                   "Xboard adjudication: Checkmate", \r
+                   GE_XBOARD );\r
+               }\r
                break;\r
            }\r
 \r
-            if( appData.testLegality )\r
-            {   /* [HGM] Some more adjudications for obstinate engines */\r
-                int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
-                    NrWQ=0, NrBQ=0, bishopsColor = 0,\r
+           if( appData.testLegality )\r
+           {   /* [HGM] Some more adjudications for obstinate engines */\r
+               int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
+                    NrWQ=0, NrBQ=0, NrW=0, bishopsColor = 0,\r
                     NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
-                static int moveCount;\r
+               static int moveCount = 6;\r
 \r
                 /* First absolutely insufficient mating material. Count what is on board. */\r
-                for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
-                {   ChessSquare p = boards[forwardMostMove][i][j];\r
-                    int m=i;\r
+               for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
+               {   ChessSquare p = boards[forwardMostMove][i][j];\r
+                   int m=i;\r
 \r
-                    switch((int) p)\r
-                    {   /* count B,N,R and other of each side */\r
+                   switch((int) p)\r
+                   {   /* count B,N,R and other of each side */\r
                         case WhiteKnight:\r
                              NrWN++; break;\r
                         case WhiteBishop:\r
@@ -5245,10 +5684,11 @@ HandleMachineMove(message, cps)
                              PawnAdvance += m; NrPawns++;\r
                     }\r
                     NrPieces += (p != EmptySquare);\r
+                    NrW += ((int)p < (int)BlackPawn);\r
                 }\r
 \r
                 if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2\r
-                 || NrPieces == 4 && NrBB+NrWB==2 && bishopsColor != 3)\r
+                 || NrPieces == 4 && NrBB+NrWB == NrPieces-2 && bishopsColor != 3)\r
                 {    /* KBK, KNK, KK of KBKB with like Bishops */\r
 \r
                      /* always flag draws, for judging claims */\r
@@ -5262,6 +5702,19 @@ HandleMachineMove(message, cps)
                      }\r
                 }\r
 \r
+               /* Shatranj baring rule */\r
+                if( gameInfo.variant == VariantShatranj && (NrW == 1 || NrPieces - NrW == 1) )\r
+                {    /* bare King */\r
+\r
+                     if(--bare < 0 && appData.checkMates) {\r
+                         /* but only adjudicate them if adjudication enabled */\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                         return;\r
+                     }\r
+                } else bare = 1;\r
+\r
                 /* Then some trivial draws (only adjudicate, cannot be claimed) */\r
                 if(NrPieces == 4 && \r
                    (   NrWR == 1 && NrBR == 1 /* KRKR */\r
@@ -5276,7 +5729,9 @@ HandleMachineMove(message, cps)
                           return;\r
                      }\r
                 } else moveCount = 6;\r
-#if 0\r
+           }\r
+         }\r
+#if 1\r
     if (appData.debugMode) { int i;\r
       fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",\r
               forwardMostMove, backwardMostMove, epStatus[backwardMostMove],\r
@@ -5382,11 +5837,9 @@ HandleMachineMove(message, cps)
                          }\r
                 }\r
 \r
-            }\r
-\r
 \r
         }\r
-#endif\r
+\r
         if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
            ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
 \r
@@ -5395,6 +5848,7 @@ HandleMachineMove(message, cps)
             return;\r
         }\r
 \r
+       bookHit = NULL;\r
        if (gameMode == TwoMachinesPlay) {\r
             /* [HGM] relaying draw offers moved to after reception of move */\r
             /* and interpreting offer as claim if it brings draw condition */\r
@@ -5405,8 +5859,8 @@ HandleMachineMove(message, cps)
                SendTimeRemaining(cps->other,\r
                                  cps->other->twoMachinesColor[0] == 'w');\r
            }\r
-           SendMoveToProgram(forwardMostMove-1, cps->other);\r
-           if (firstMove) {\r
+           bookHit = SendMoveToBookUser(forwardMostMove-1, cps->other, FALSE);\r
+           if (firstMove && !bookHit) {\r
                firstMove = FALSE;\r
                if (cps->other->useColors) {\r
                  SendToProgram(cps->other->twoMachinesColor, cps->other);\r
@@ -5429,6 +5883,29 @@ HandleMachineMove(message, cps)
        if (gameMode != TwoMachinesPlay)\r
            SetUserThinkingEnables();\r
 \r
+       // [HGM] book: after book hit opponent has received move and is now in force mode\r
+       // force the book reply into it, and then fake that it outputted this move by jumping\r
+       // back to the beginning of HandleMachineMove, with cps toggled and message set to this move\r
+       if(bookHit) {\r
+               static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+               strcpy(bookMove, "move ");\r
+               strcat(bookMove, bookHit);\r
+               message = bookMove;\r
+               cps = cps->other;\r
+               programStats.depth = programStats.nodes = programStats.time = \r
+               programStats.score = programStats.got_only_move = 0;\r
+               sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+               if(cps->lastPing != cps->lastPong) {\r
+                   savedMessage = message; // args for deferred call\r
+                   savedState = cps;\r
+                   ScheduleDelayedEvent(DeferredBookMove, 10);\r
+                   return;\r
+               }\r
+               goto FakeBookMove;\r
+       }\r
+\r
        return;\r
     }\r
 \r
@@ -5549,7 +6026,7 @@ HandleMachineMove(message, cps)
       ParseFeatures(message+8, cps);\r
     }\r
     if (sscanf(message, "pong %d", &cps->lastPong) == 1) {\r
-      return;\r
+       return;\r
     }\r
     /*\r
      * If the move is illegal, cancel it and redraw the board.\r
@@ -5888,7 +6365,9 @@ HandleMachineMove(message, cps)
     /*\r
      * Look for thinking output\r
      */\r
-    if ( appData.showThinking) {\r
+    if ( appData.showThinking // [HGM] thinking: test all options that cause this output\r
+         || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp()\r
+                               ) {\r
        int plylev, mvleft, mvtot, curscore, time;\r
        char mvname[MOVE_LEN];\r
        unsigned long nodes;\r
@@ -5942,6 +6421,16 @@ HandleMachineMove(message, cps)
                programStats.score = curscore;\r
                programStats.got_only_move = 0;\r
 \r
+               if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */\r
+                       int ticklen;\r
+\r
+                       if(cps->nps == 0) ticklen = 10*time;       // use engine reported time\r
+                       else ticklen = (1000. * nodes) / cps->nps; // convert node count to time\r
+                       if(WhiteOnMove(forwardMostMove)) \r
+                            whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;\r
+                       else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;\r
+               }\r
+\r
                /* Buffer overflow protection */\r
                if (buf1[0] != NULLCHAR) {\r
                    if (strlen(buf1) >= sizeof(programStats.movelist)\r
@@ -6332,13 +6821,15 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      int promoChar;\r
      Board board;\r
 {\r
-  ChessSquare captured = board[toY][toX], piece, king; int p;\r
+  ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;\r
 \r
     /* [HGM] compute & store e.p. status and castling rights for new position */\r
     /* if we are updating a board for which those exist (i.e. in boards[])    */\r
     if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
     { int i, j;\r
 \r
+      if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
+      oldEP = epStatus[p-1];\r
       epStatus[p] = EP_NONE;\r
 \r
       if( board[toY][toX] != EmptySquare ) \r
@@ -6346,17 +6837,23 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
 \r
       if( board[fromY][fromX] == WhitePawn ) {\r
            epStatus[p] = EP_PAWN_MOVE; \r
-           if( toY-fromY==2 &&\r
-               (toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn ||\r
-                toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn ) )\r
-              epStatus[p] = toX;\r
+           if( toY-fromY==2)\r
+               if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&\r
+                       gameInfo.variant != VariantBerolina || toX < fromX)\r
+                     epStatus[p] = toX | berolina;\r
+               if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
+                       gameInfo.variant != VariantBerolina || toX > fromX) \r
+                     epStatus[p] = toX;\r
       } else \r
       if( board[fromY][fromX] == BlackPawn ) {\r
            epStatus[p] = EP_PAWN_MOVE; \r
-           if( toY-fromY== -2 &&\r
-               (toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn ||\r
-                toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn ) )\r
-              epStatus[p] = toX;\r
+           if( toY-fromY== -2)\r
+               if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&\r
+                       gameInfo.variant != VariantBerolina || toX < fromX)\r
+                     epStatus[p] = toX | berolina;\r
+               if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
+                       gameInfo.variant != VariantBerolina || toX > fromX) \r
+                     epStatus[p] = toX;\r
        }\r
 \r
        for(i=0; i<nrCastlingRights; i++) {\r
@@ -6435,12 +6932,26 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
     } else if ((fromY == BOARD_HEIGHT-4)\r
               && (toX != fromX)\r
                && gameInfo.variant != VariantXiangqi\r
+               && gameInfo.variant != VariantBerolina\r
               && (board[fromY][fromX] == WhitePawn)\r
               && (board[toY][toX] == EmptySquare)) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = WhitePawn;\r
        captured = board[toY - 1][toX];\r
        board[toY - 1][toX] = EmptySquare;\r
+    } else if ((fromY == BOARD_HEIGHT-4)\r
+              && (toX == fromX)\r
+               && gameInfo.variant == VariantBerolina\r
+              && (board[fromY][fromX] == WhitePawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = WhitePawn;\r
+       if(oldEP & EP_BEROLIN_A) {\r
+               captured = board[fromY][fromX-1];\r
+               board[fromY][fromX-1] = EmptySquare;\r
+       }else{  captured = board[fromY][fromX+1];\r
+               board[fromY][fromX+1] = EmptySquare;\r
+       }\r
     } else if (board[fromY][fromX] == king\r
         && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
                && toY == fromY && toX > fromX+1) {\r
@@ -6485,12 +6996,26 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
     } else if ((fromY == 3)\r
               && (toX != fromX)\r
                && gameInfo.variant != VariantXiangqi\r
+               && gameInfo.variant != VariantBerolina\r
               && (board[fromY][fromX] == BlackPawn)\r
               && (board[toY][toX] == EmptySquare)) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = BlackPawn;\r
        captured = board[toY + 1][toX];\r
        board[toY + 1][toX] = EmptySquare;\r
+    } else if ((fromY == 3)\r
+              && (toX == fromX)\r
+               && gameInfo.variant == VariantBerolina\r
+              && (board[fromY][fromX] == BlackPawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = BlackPawn;\r
+       if(oldEP & EP_BEROLIN_A) {\r
+               captured = board[fromY][fromX-1];\r
+               board[fromY][fromX-1] = EmptySquare;\r
+       }else{  captured = board[fromY][fromX+1];\r
+               board[fromY][fromX+1] = EmptySquare;\r
+       }\r
     } else {\r
        board[toY][toX] = board[fromY][fromX];\r
        board[fromY][fromX] = EmptySquare;\r
@@ -6525,7 +7050,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       }\r
       if (captured != EmptySquare && gameInfo.holdingsSize > 0\r
           && gameInfo.variant != VariantBughouse        ) {\r
-        /* Add to holdings, if holdings exist */\r
+        /* [HGM] holdings: Add to holdings, if holdings exist */\r
+       if(gameInfo.variant == VariantSuper) { \r
+               // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip\r
+               captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;\r
+       }\r
         p = (int) captured;\r
         if (p >= (int) BlackPawn) {\r
           p -= (int)BlackPawn;\r
@@ -6537,8 +7066,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
           p = PieceToNumber((ChessSquare)p);\r
           if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; }\r
           board[p][BOARD_WIDTH-2]++;\r
-          board[p][BOARD_WIDTH-1] =\r
-                                   BLACK_TO_WHITE captured;\r
+          board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured;\r
        } else {\r
           p -= (int)WhitePawn;\r
           if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {\r
@@ -6548,8 +7076,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
           p = PieceToNumber((ChessSquare)p);\r
           if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }\r
           board[BOARD_HEIGHT-1-p][1]++;\r
-          board[BOARD_HEIGHT-1-p][0] =\r
-                                  WHITE_TO_BLACK captured;\r
+          board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;\r
        }\r
       }\r
 \r
@@ -6697,6 +7224,38 @@ ShowMove(fromX, fromY, toX, toY)
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
 }\r
 \r
+void SendEgtPath(ChessProgramState *cps)\r
+{       /* [HGM] EGT: match formats given in feature with those given by user, and send info for each match */\r
+       char buf[MSG_SIZ], name[MSG_SIZ], *p;\r
+\r
+       if((p = cps->egtFormats) == NULL || appData.egtFormats == NULL) return;\r
+\r
+       while(*p) {\r
+           char c, *q = name+1, *r, *s;\r
+\r
+           name[0] = ','; // extract next format name from feature and copy with prefixed ','\r
+           while(*p && *p != ',') *q++ = *p++;\r
+           *q++ = ':'; *q = 0;\r
+           if( appData.defaultPathEGTB && appData.defaultPathEGTB[0] && \r
+               strcmp(name, ",nalimov:") == 0 ) {\r
+               // take nalimov path from the menu-changeable option first, if it is defined\r
+               sprintf(buf, "egtpath nalimov %s\n", appData.defaultPathEGTB);\r
+               SendToProgram(buf,cps);     // send egtbpath command for nalimov\r
+           } else\r
+           if( (s = StrStr(appData.egtFormats, name+1)) == appData.egtFormats ||\r
+               (s = StrStr(appData.egtFormats, name)) != NULL) {\r
+               // format name occurs amongst user-supplied formats, at beginning or immediately after comma\r
+               s = r = StrStr(s, ":") + 1; // beginning of path info\r
+               while(*r && *r != ',') r++; // path info is everything upto next ';' or end of string\r
+               c = *r; *r = 0;             // temporarily null-terminate path info\r
+                   *--q = 0;               // strip of trailig ':' from name\r
+                   sprintf(buf, "egtbpath %s %s\n", name+1, s);\r
+               *r = c;\r
+               SendToProgram(buf,cps);     // send egtbpath command for this format\r
+           }\r
+           if(*p == ',') p++; // read away comma to position for next format name\r
+       }\r
+}\r
 \r
 void\r
 InitChessProgram(cps, setup)\r
@@ -6707,11 +7266,24 @@ InitChessProgram(cps, setup)
     if (appData.noChessProgram) return;\r
     hintRequested = FALSE;\r
     bookRequested = FALSE;\r
+\r
+    /* [HGM] some new WB protocol commands to configure engine are sent now, if engine supports them */\r
+    /*       moved to before sending initstring in 4.3.15, so Polyglot can delay UCI 'isready' to recepton of 'new' */\r
+    if(cps->memSize) { /* [HGM] memory */\r
+       sprintf(buf, "memory %d\n", appData.defaultHashSize + appData.defaultCacheSizeEGTB);\r
+       SendToProgram(buf, cps);\r
+    }\r
+    SendEgtPath(cps); /* [HGM] EGT */\r
+    if(cps->maxCores) { /* [HGM] SMP: (protocol specified must be last settings command before new!) */\r
+       sprintf(buf, "cores %d\n", appData.smpCores);\r
+       SendToProgram(buf, cps);\r
+    }\r
+\r
     SendToProgram(cps->initString, cps);\r
     if (gameInfo.variant != VariantNormal &&\r
        gameInfo.variant != VariantLoadable\r
         /* [HGM] also send variant if board size non-standard */\r
-        || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8\r
+        || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0\r
                                             ) {\r
       char *v = VariantName(gameInfo.variant);\r
       if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) {\r
@@ -6729,10 +7301,13 @@ InitChessProgram(cps, setup)
            overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;\r
       if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )\r
            overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;\r
-      if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantGothic  || gameInfo.variant == VariantFalcon )\r
+      if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom || \r
+                               gameInfo.variant == VariantGothic  || gameInfo.variant == VariantFalcon )\r
            overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
       if( gameInfo.variant == VariantCourier )\r
            overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantSuper )\r
+           overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
 \r
       if(overruled) {\r
            sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
@@ -6753,14 +7328,17 @@ InitChessProgram(cps, setup)
       } else sprintf(b, "%s", VariantName(gameInfo.variant));\r
       sprintf(buf, "variant %s\n", b);\r
       SendToProgram(buf, cps);\r
-      /* [HGM] send opening position in FRC to first engine */\r
-      if(setup /* cps == &first && gameInfo.variant == VariantFischeRandom */) {\r
+    }\r
+    currentlyInitializedVariant = gameInfo.variant;\r
+\r
+    /* [HGM] send opening position in FRC to first engine */\r
+    if(setup) {\r
           SendToProgram("force\n", cps);\r
           SendBoard(cps, 0);\r
           /* engine is now in force mode! Set flag to wake it up after first move. */\r
           setboardSpoiledMachineBlack = 1;\r
-      }\r
     }\r
+\r
     if (cps->sendICS) {\r
       sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
       SendToProgram(buf, cps);\r
@@ -6772,7 +7350,10 @@ InitChessProgram(cps, setup)
                        timeIncrement, appData.searchDepth,\r
                        searchTime);\r
     }\r
-    if (appData.showThinking) {\r
+    if (appData.showThinking \r
+       // [HGM] thinking: four options require thinking output to be sent\r
+       || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp()\r
+                               ) {\r
        SendToProgram("post\n", cps);\r
     }\r
     SendToProgram("hard\n", cps);\r
@@ -6854,14 +7435,25 @@ TwoMachinesEventIfReady P((void))
 void\r
 NextMatchGame P((void))\r
 {\r
+    int index; /* [HGM] autoinc: step lod index during match */\r
     Reset(FALSE, TRUE);\r
     if (*appData.loadGameFile != NULLCHAR) {\r
+       index = appData.loadGameIndex;\r
+       if(index < 0) { // [HGM] autoinc\r
+           lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;\r
+           if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;\r
+       } \r
        LoadGameFromFile(appData.loadGameFile,\r
-                        appData.loadGameIndex,\r
+                        index,\r
                         appData.loadGameFile, FALSE);\r
     } else if (*appData.loadPositionFile != NULLCHAR) {\r
+       index = appData.loadPositionIndex;\r
+       if(index < 0) { // [HGM] autoinc\r
+           lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;\r
+           if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;\r
+       } \r
        LoadPositionFromFile(appData.loadPositionFile,\r
-                            appData.loadPositionIndex,\r
+                            index,\r
                             appData.loadPositionFile);\r
     }\r
     TwoMachinesEventIfReady();\r
@@ -6942,57 +7534,71 @@ GameEnds(result, resultDetails, whosays)
     if (!isIcsGame || whosays == GE_ICS) {\r
        /* OK -- not an ICS game, or ICS said it was done */\r
        StopClocks();\r
-    if (appData.debugMode) {\r
-      fprintf(debugFP, "GameEnds(%d, %s, %d) clock stopped\n",\r
-             result, resultDetails ? resultDetails : "(null)", whosays);\r
-    }\r
        if (!isIcsGame && !appData.noChessProgram) \r
          SetUserThinkingEnables();\r
     \r
         /* [HGM] if a machine claims the game end we verify this claim */\r
-        if( appData.testLegality && gameMode == TwoMachinesPlay &&\r
-            appData.testClaims && whosays >= GE_ENGINE1 ) {\r
+        if(gameMode == TwoMachinesPlay && appData.testClaims) {\r
+           if(appData.testLegality && whosays >= GE_ENGINE1 ) {\r
                 char claimer;\r
 \r
-    if (appData.debugMode) {\r
-      fprintf(debugFP, "GameEnds(%d, %s, %d) test claims\n",\r
-             result, resultDetails ? resultDetails : "(null)", whosays);\r
-    }\r
                 claimer = whosays == GE_ENGINE1 ?      /* color of claimer */\r
                                             first.twoMachinesColor[0] :\r
                                             second.twoMachinesColor[0] ;\r
-                if( gameInfo.holdingsWidth == 0 &&\r
+                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) &&\r
                     (result == WhiteWins && claimer == 'w' ||\r
                      result == BlackWins && claimer == 'b'   ) ) {\r
-                      /* Xboard immediately adjudicates all mates, so win claims must be false */\r
-                      sprintf(buf, "False win claim: '%s'", resultDetails);\r
-                      result = claimer == 'w' ? BlackWins : WhiteWins;\r
-                      resultDetails = buf;\r
+               if (appData.debugMode) {\r
+                    fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
+                       result, epStatus[forwardMostMove], forwardMostMove);\r
+               }\r
+                      /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                     if(epStatus[forwardMostMove] != EP_CHECKMATE &&\r
+                        result != (WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins)) {\r
+                             sprintf(buf, "False win claim: '%s'", resultDetails);\r
+                             result = claimer == 'w' ? BlackWins : WhiteWins;\r
+                             resultDetails = buf;\r
+                     }\r
                 } else\r
                 if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS\r
                     && (forwardMostMove <= backwardMostMove ||\r
                         epStatus[forwardMostMove-1] > EP_DRAWS ||\r
                         (claimer=='b')==(forwardMostMove&1))\r
                                                                                   ) {\r
-                      /* Draw that was not flagged by Xboard is false */\r
+                      /* [HGM] verify: draws that were not flagged are false claims */\r
                       sprintf(buf, "False draw claim: '%s'", resultDetails);\r
                       result = claimer == 'w' ? BlackWins : WhiteWins;\r
                       resultDetails = buf;\r
                 }\r
                 /* (Claiming a loss is accepted no questions asked!) */\r
+           }\r
+           /* [HGM] bare: don't allow bare King to win */\r
+           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) && result != GameIsDrawn)\r
+           {   int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);\r
+               for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {\r
+                       int p = (int)boards[forwardMostMove][i][j] - color;\r
+                       if(p >= 0 && p <= (int)WhiteKing) k++;\r
+               }\r
+               if (appData.debugMode) {\r
+                    fprintf(debugFP, "GE(%d, %s, %d) bare king k=%d color=%d\n",\r
+                       result, resultDetails ? resultDetails : "(null)", whosays, k, color);\r
+               }\r
+               if(k <= 1) {\r
+                       result = GameIsDrawn;\r
+                       sprintf(buf, "%s but bare king", resultDetails);\r
+                       resultDetails = buf;\r
+               }\r
+           }\r
         }\r
 \r
+\r
         if(serverMoves != NULL && !loadFlag) { char c = '=';\r
             if(result==WhiteWins) c = '+';\r
             if(result==BlackWins) c = '-';\r
             if(resultDetails != NULL)\r
                 fprintf(serverMoves, ";%c;%s\n", c, resultDetails);\r
         }\r
-    if (appData.debugMode) {\r
-      fprintf(debugFP, "GameEnds(%d, %s, %d) after test\n",\r
-             result, resultDetails ? resultDetails : "(null)", whosays);\r
-    }\r
-       if (resultDetails != NULL) {\r
+       if (resultDetails != NULL) {\r
            gameInfo.result = result;\r
            gameInfo.resultDetails = StrSave(resultDetails);\r
 \r
@@ -7176,9 +7782,11 @@ GameEnds(result, resultDetails, whosays)
        }\r
        if (matchGame < appData.matchGames) {\r
            char *tmp;\r
-           tmp = first.twoMachinesColor;\r
-           first.twoMachinesColor = second.twoMachinesColor;\r
-           second.twoMachinesColor = tmp;\r
+           if(appData.sameColorGames <= 1) { /* [HGM] alternate: suppress color swap */\r
+               tmp = first.twoMachinesColor;\r
+               first.twoMachinesColor = second.twoMachinesColor;\r
+               second.twoMachinesColor = tmp;\r
+           }\r
            gameMode = nextGameMode;\r
            matchGame++;\r
             if(appData.matchPause>10000 || appData.matchPause<10)\r
@@ -7217,6 +7825,14 @@ FeedMovesToProgram(cps, upto)
       fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",\r
              startedFromSetupPosition ? "position and " : "",\r
              backwardMostMove, upto, cps->which);\r
+    if(currentlyInitializedVariant != gameInfo.variant) { char buf[MSG_SIZ];\r
+        // [HGM] variantswitch: make engine aware of new variant\r
+       if(cps->protocolVersion > 1 && StrStr(cps->variants, VariantName(gameInfo.variant)) == NULL)\r
+               return; // [HGM] refrain from feeding moves altogether if variant is unsupported!\r
+       sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));\r
+       SendToProgram(buf, cps);\r
+        currentlyInitializedVariant = gameInfo.variant;\r
+    }\r
     SendToProgram("force\n", cps);\r
     if (startedFromSetupPosition) {\r
        SendBoard(cps, backwardMostMove);\r
@@ -7239,9 +7855,6 @@ ResurrectChessProgram()
     if (appData.noChessProgram || first.pr != NoProc) return;\r
     \r
     StartChessProgram(&first);\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From ResurrectChessProgram\n");\r
-    }\r
     InitChessProgram(&first, FALSE);\r
     FeedMovesToProgram(&first, currentMove);\r
 \r
@@ -7281,6 +7894,8 @@ Reset(redraw, init)
     hintRequested = bookRequested = FALSE;\r
     first.maybeThinking = FALSE;\r
     second.maybeThinking = FALSE;\r
+    first.bookSuspend = FALSE; // [HGM] book\r
+    second.bookSuspend = FALSE;\r
     thinkOutput[0] = NULLCHAR;\r
     lastHint[0] = NULLCHAR;\r
     ClearGameInfo(&gameInfo);\r
@@ -7315,6 +7930,7 @@ Reset(redraw, init)
     ExitAnalyzeMode();\r
     gameMode = BeginningOfGame;\r
     ModeHighlight();\r
+    if(appData.icsActive) gameInfo.variant = VariantNormal;\r
     InitPosition(redraw);\r
     for (i = 0; i < MAX_MOVES; i++) {\r
        if (commentList[i] != NULL) {\r
@@ -7329,10 +7945,8 @@ Reset(redraw, init)
        StartChessProgram(&first);\r
     }\r
     if (init) {\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From Reset\n");\r
+           InitChessProgram(&first, startedFromSetupPosition);\r
     }\r
-InitChessProgram(&first, startedFromSetupPosition);}\r
     DisplayTitle("");\r
     DisplayMessage("", "");\r
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
@@ -7452,6 +8066,8 @@ LoadGameOneMove(readAhead)
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
+      case WhitePromotionCentaur:\r
+      case BlackPromotionCentaur:\r
       case WhitePromotionQueen:\r
       case BlackPromotionQueen:\r
       case WhitePromotionRook:\r
@@ -7624,7 +8240,7 @@ LoadGameOneMove(readAhead)
       default:\r
       case ImpossibleMove:\r
        if (appData.debugMode)\r
-         fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);\r
+         fprintf(debugFP, "Parsed ImpossibleMove (type = %d): %s\n", moveType, yy_text);\r
        sprintf(move, "Illegal move: %d.%s%s",\r
                (forwardMostMove / 2) + 1,\r
                WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
@@ -8075,13 +8691,6 @@ LoadGame(f, gameNumber, title, useList)
            return FALSE;\r
          }\r
          CopyBoard(boards[0], initial_position);\r
-          /* [HGM] copy FEN attributes as well */\r
-          {   int i;\r
-              initialRulePlies = FENrulePlies;\r
-              epStatus[0] = FENepStatus;\r
-              for( i=0; i< nrCastlingRights; i++ )\r
-                  castlingRights[0][i] = FENcastlingRights[i];\r
-          }\r
          if (blackPlaysFirst) {\r
            currentMove = forwardMostMove = backwardMostMove = 1;\r
            CopyBoard(boards[1], initial_position);\r
@@ -8096,6 +8705,13 @@ LoadGame(f, gameNumber, title, useList)
          } else {\r
            currentMove = forwardMostMove = backwardMostMove = 0;\r
          }\r
+          /* [HGM] copy FEN attributes as well. Bugfix 4.3.14m and 4.3.15e: moved to after 'blackPlaysFirst' */\r
+          {   int i;\r
+              initialRulePlies = FENrulePlies;\r
+              epStatus[forwardMostMove] = FENepStatus;\r
+              for( i=0; i< nrCastlingRights; i++ )\r
+                  initialRights[i] = castlingRights[forwardMostMove][i] = FENcastlingRights[i];\r
+          }\r
          yyboardindex = forwardMostMove;\r
          free(gameInfo.fen);\r
          gameInfo.fen = NULL;\r
@@ -8198,9 +8814,6 @@ LoadGame(f, gameNumber, title, useList)
     if (first.pr == NoProc) {\r
        StartChessProgram(&first);\r
     }\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From LoadGame\n");\r
-    }\r
     InitChessProgram(&first, FALSE);\r
     SendToProgram("force\n", &first);\r
     if (startedFromSetupPosition) {\r
@@ -8364,9 +8977,6 @@ LoadPosition(f, positionNumber, title)
     strcpy(lastLoadPositionTitle, title);\r
     if (first.pr == NoProc) {\r
       StartChessProgram(&first);\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From LoadPosition\n");\r
-    }\r
       InitChessProgram(&first, FALSE);\r
     }    \r
     pn = positionNumber;\r
@@ -8418,7 +9028,7 @@ LoadPosition(f, positionNumber, title)
     if (pn >= 2) {\r
        if (fenMode || line[0] == '#') pn--;\r
        while (pn > 0) {\r
-           /* skip postions before number pn */\r
+           /* skip positions before number pn */\r
            if (fgets(line, MSG_SIZ, f) == NULL) {\r
                Reset(TRUE, TRUE);\r
                DisplayError("Position not found in file", 0);\r
@@ -8457,13 +9067,6 @@ LoadPosition(f, positionNumber, title)
     \r
     SendToProgram("force\n", &first);\r
     CopyBoard(boards[0], initial_position);\r
-          /* [HGM] copy FEN attributes as well */\r
-          {   int i;\r
-              initialRulePlies = FENrulePlies;\r
-              epStatus[0] = FENepStatus;\r
-              for( i=0; i< nrCastlingRights; i++ )\r
-                  castlingRights[0][i] = FENcastlingRights[i];\r
-          }\r
     if (blackPlaysFirst) {\r
        currentMove = forwardMostMove = backwardMostMove = 1;\r
        strcpy(moveList[0], "");\r
@@ -8474,8 +9077,18 @@ LoadPosition(f, positionNumber, title)
        currentMove = forwardMostMove = backwardMostMove = 0;\r
        DisplayMessage("", "White to play");\r
     }\r
+          /* [HGM] copy FEN attributes as well */\r
+          {   int i;\r
+              initialRulePlies = FENrulePlies;\r
+              epStatus[forwardMostMove] = FENepStatus;\r
+              for( i=0; i< nrCastlingRights; i++ )\r
+                  castlingRights[forwardMostMove][i] = FENcastlingRights[i];\r
+          }\r
     SendBoard(&first, forwardMostMove);\r
     if (appData.debugMode) {\r
+int i, j;\r
+  for(i=0;i<2;i++){for(j=0;j<6;j++)fprintf(debugFP, " %d", castlingRights[i][j]);fprintf(debugFP,"\n");}\r
+  for(j=0;j<6;j++)fprintf(debugFP, " %d", initialRights[j]);fprintf(debugFP,"\n");\r
         fprintf(debugFP, "Load Position\n");\r
     }\r
 \r
@@ -8727,56 +9340,76 @@ SaveGamePGN(f)
        linelen += numlen;\r
 \r
        /* Get move */\r
-       movetext = SavePart(parseList[i]);\r
+       movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */\r
+\r
+       /* Print move */\r
+       blank = linelen > 0 && movelen > 0;\r
+       if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
+           fprintf(f, "\n");\r
+           linelen = 0;\r
+           blank = 0;\r
+       }\r
+       if (blank) {\r
+           fprintf(f, " ");\r
+           linelen++;\r
+       }\r
+       fprintf(f, parseList[i]);\r
+       linelen += movelen;\r
 \r
         /* [AS] Add PV info if present */\r
         if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {\r
             /* [HGM] add time */\r
             char buf[MSG_SIZ]; int seconds = 0;\r
 \r
+#if 0\r
             if(i >= backwardMostMove) {\r
-                /* take the time that changed */\r
-                seconds = timeRemaining[0][i] - timeRemaining[0][i+1];\r
-              if(seconds <= 0)\r
-                    seconds = timeRemaining[1][i] - timeRemaining[1][i+1];\r
+               if(WhiteOnMove(i))\r
+                       seconds = timeRemaining[0][i] - timeRemaining[0][i+1]\r
+                                 + GetTimeQuota(i/2) / WhitePlayer()->timeOdds;\r
+               else\r
+                       seconds = timeRemaining[1][i] - timeRemaining[1][i+1]\r
+                                  + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds;\r
             }\r
-            seconds /= 1000;\r
-            seconds = pvInfoList[i].time/100;\r
-    if (appData.debugMode) {\r
+            seconds = (seconds+50)/100; // deci-seconds, rounded to nearest\r
+#else\r
+            seconds = (pvInfoList[i].time + 5)/10; // [HGM] PVtime: use engine time\r
+#endif\r
+    if (appData.debugMode,0) {\r
         fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
                 timeRemaining[0][i+1], timeRemaining[0][i],\r
                      timeRemaining[1][i+1], timeRemaining[1][i], seconds\r
         );\r
     }\r
 \r
-            if( seconds < 0 ) buf[0] = 0; else\r
-            if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0);\r
-            else    sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0);\r
+            if( seconds <= 0) buf[0] = 0; else\r
+            if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {\r
+               seconds = (seconds + 4)/10; // round to full seconds\r
+               if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0); else\r
+                                  sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0);\r
+           }\r
 \r
-            sprintf( move_buffer, "%s {%s%.2f/%d%s}", \r
-                movetext, \r
+            sprintf( move_buffer, "{%s%.2f/%d%s}", \r
                 pvInfoList[i].score >= 0 ? "+" : "",\r
                 pvInfoList[i].score / 100.0,\r
                 pvInfoList[i].depth,\r
-                buf );\r
-            movetext = move_buffer;\r
-        }\r
+               buf );\r
 \r
-       movelen = strlen(movetext);\r
+           movelen = strlen(move_buffer); /* [HGM] pgn: line-break point after move */\r
 \r
-       /* Print move */\r
-       blank = linelen > 0 && movelen > 0;\r
-       if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
-           fprintf(f, "\n");\r
-           linelen = 0;\r
-           blank = 0;\r
-       }\r
-       if (blank) {\r
-           fprintf(f, " ");\r
-           linelen++;\r
-       }\r
-       fprintf(f, movetext);\r
-       linelen += movelen;\r
+           /* Print score/depth */\r
+           blank = linelen > 0 && movelen > 0;\r
+           if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
+               fprintf(f, "\n");\r
+               linelen = 0;\r
+               blank = 0;\r
+           }\r
+           if (blank) {\r
+               fprintf(f, " ");\r
+               linelen++;\r
+           }\r
+           fprintf(f, move_buffer);\r
+           linelen += movelen;\r
+        }\r
 \r
        i++;\r
     }\r
@@ -9307,7 +9940,7 @@ ExitEvent(status)
     GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
 #else\r
     /* [HGM] crash: leave writing PGN and position entirely to GameEnds() */\r
-    GameEnds(gameInfo.result, gameInfo.resultDetails==NULL ? "aborted" : gameInfo.resultDetails, GE_PLAYER);\r
+    GameEnds(gameInfo.result, gameInfo.resultDetails==NULL ? "xboard exit" : gameInfo.resultDetails, GE_PLAYER);\r
 #endif\r
     /* [HGM] crash: the above GameEnds() is a dud if another one was running */\r
     /* make sure this other one finishes before killing it!                  */\r
@@ -9491,6 +10124,7 @@ void
 MachineWhiteEvent()\r
 {\r
     char buf[MSG_SIZ];\r
+    char *bookHit = NULL;\r
 \r
     if (appData.noChessProgram || (gameMode == MachinePlaysWhite))\r
       return;\r
@@ -9519,6 +10153,10 @@ MachineWhiteEvent()
        TruncateGame();\r
 \r
     ResurrectChessProgram();   /* in case it isn't running */\r
+    if(gameMode == BeginningOfGame) { /* [HGM] time odds: to get right odds in human mode */\r
+       gameMode = MachinePlaysWhite;\r
+       ResetClocks();\r
+    } else\r
     gameMode = MachinePlaysWhite;\r
     pausing = FALSE;\r
     ModeHighlight();\r
@@ -9536,10 +10174,9 @@ MachineWhiteEvent()
       SendTimeRemaining(&first, TRUE);\r
     }\r
     if (first.useColors) {\r
-      SendToProgram("white\ngo\n", &first);\r
-    } else {\r
-      SendToProgram("go\n", &first);\r
+      SendToProgram("white\n", &first); // [HGM] book: send 'go' separately\r
     }\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
     SetMachineThinkingEnables();\r
     first.maybeThinking = TRUE;\r
     StartClocks();\r
@@ -9548,12 +10185,25 @@ MachineWhiteEvent()
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
     }\r
+\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
 }\r
 \r
 void\r
 MachineBlackEvent()\r
 {\r
     char buf[MSG_SIZ];\r
+   char *bookHit = NULL;\r
 \r
     if (appData.noChessProgram || (gameMode == MachinePlaysBlack))\r
        return;\r
@@ -9599,10 +10249,9 @@ MachineBlackEvent()
       SendTimeRemaining(&first, FALSE);\r
     }\r
     if (first.useColors) {\r
-      SendToProgram("black\ngo\n", &first);\r
-    } else {\r
-      SendToProgram("go\n", &first);\r
+      SendToProgram("black\n", &first); // [HGM] book: 'go' sent separately\r
     }\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
     SetMachineThinkingEnables();\r
     first.maybeThinking = TRUE;\r
     StartClocks();\r
@@ -9611,6 +10260,17 @@ MachineBlackEvent()
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
     }\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
 }\r
 \r
 \r
@@ -9642,6 +10302,7 @@ TwoMachinesEvent P((void))
     int i;\r
     char buf[MSG_SIZ];\r
     ChessProgramState *onmove;\r
+    char *bookHit = NULL;\r
     \r
     if (appData.noChessProgram) return;\r
 \r
@@ -9689,9 +10350,6 @@ TwoMachinesEvent P((void))
        return;\r
     }\r
     DisplayMessage("", "");\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From TwoMachines\n");\r
-    }\r
     InitChessProgram(&second, FALSE);\r
     SendToProgram("force\n", &second);\r
     if (startedFromSetupPosition) {\r
@@ -9727,8 +10385,8 @@ TwoMachinesEvent P((void))
       SendToProgram(buf, &second);\r
     }\r
 \r
+    ResetClocks();\r
     if (!first.sendTime || !second.sendTime) {\r
-       ResetClocks();\r
        timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
        timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
     }\r
@@ -9741,11 +10399,24 @@ TwoMachinesEvent P((void))
     if (onmove->useColors) {\r
       SendToProgram(onmove->twoMachinesColor, onmove);\r
     }\r
-    SendToProgram("go\n", onmove);\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, onmove, TRUE); // [HGM] book: send go or retrieve book move\r
+//    SendToProgram("go\n", onmove);\r
     onmove->maybeThinking = TRUE;\r
     SetMachineThinkingEnables();\r
 \r
     StartClocks();\r
+\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
 }\r
 \r
 void\r
@@ -9934,9 +10605,6 @@ void
 EditPositionDone()\r
 {\r
     startedFromSetupPosition = TRUE;\r
-    if (appData.debugMode) {\r
-       fprintf(debugFP, "From EditPosition\n");\r
-    }\r
     InitChessProgram(&first, FALSE);\r
     SendToProgram("force\n", &first);\r
     if (blackPlaysFirst) {\r
@@ -9944,6 +10612,11 @@ EditPositionDone()
        strcpy(parseList[0], "");\r
        currentMove = forwardMostMove = backwardMostMove = 1;\r
        CopyBoard(boards[1], boards[0]);\r
+       /* [HGM] copy rights as well, as this code is also used after pasting a FEN */\r
+       { int i;\r
+         epStatus[1] = epStatus[0];\r
+         for(i=0; i<nrCastlingRights; i++) castlingRights[1][i] = castlingRights[0][i];\r
+       }\r
     } else {\r
        currentMove = forwardMostMove = backwardMostMove = 0;\r
     }\r
@@ -10046,7 +10719,8 @@ EditPositionMenuEvent(selection, x, y)
            SendToICS(ics_prefix);\r
            SendToICS("clearboard\n");\r
        } else {\r
-            for (x = 0; x < BOARD_WIDTH; x++) {\r
+            for (x = 0; x < BOARD_WIDTH; x++) { ChessSquare p = EmptySquare;\r
+               if(x == BOARD_LEFT-1 || x == BOARD_RGHT) p = (ChessSquare) 0; /* [HGM] holdings */\r
                 for (y = 0; y < BOARD_HEIGHT; y++) {\r
                    if (gameMode == IcsExamining) {\r
                        if (boards[currentMove][y][x] != EmptySquare) {\r
@@ -10055,7 +10729,7 @@ EditPositionMenuEvent(selection, x, y)
                            SendToICS(buf);\r
                        }\r
                    } else {\r
-                       boards[0][y][x] = EmptySquare;\r
+                       boards[0][y][x] = p;\r
                    }\r
                }\r
            }\r
@@ -11044,7 +11718,7 @@ char *GetInfoFromComment( int index, char * text )
     if( text != NULL && index > 0 ) {\r
         int score = 0;\r
         int depth = 0;\r
-        int time = -1, sec = 0;\r
+        int time = -1, sec = 0, deci;\r
         char * s_eval = FindStr( text, "[%eval " );\r
         char * s_emt = FindStr( text, "[%emt " );\r
 \r
@@ -11074,8 +11748,9 @@ char *GetInfoFromComment( int index, char * text )
                 return text;\r
             }\r
 \r
-            time = -1; sec = -1;\r
+            time = -1; sec = -1; deci = -1;\r
             if( sscanf( text, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&\r
+               sscanf( text, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&\r
                 sscanf( text, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&\r
                 sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3   ) {\r
                 return text;\r
@@ -11085,7 +11760,9 @@ char *GetInfoFromComment( int index, char * text )
                 return text;\r
             }\r
 \r
-            if(sec >= 0) time = 60*time + sec;\r
+            if(sec >= 0) time = 600*time + 10*sec; else\r
+            if(deci >= 0) time = 10*time + deci; else time *= 10; // deci-sec\r
+\r
             score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;\r
 \r
             /* [HGM] PV time: now locate end of PV info */\r
@@ -11094,6 +11771,8 @@ char *GetInfoFromComment( int index, char * text )
             while( *++sep >= '0' && *sep <= '9'); // strip time\r
             if(sec >= 0)\r
             while( *++sep >= '0' && *sep <= '9'); // strip seconds\r
+            if(deci >= 0)\r
+            while( *++sep >= '0' && *sep <= '9'); // strip fractional seconds\r
             while(*sep == ' ') sep++;\r
         }\r
 \r
@@ -11107,7 +11786,7 @@ char *GetInfoFromComment( int index, char * text )
 \r
         pvInfoList[index-1].depth = depth;\r
         pvInfoList[index-1].score = score;\r
-        pvInfoList[index-1].time  = time;\r
+        pvInfoList[index-1].time  = 10*time; // centi-sec\r
     }\r
     return sep;\r
 }\r
@@ -11201,11 +11880,28 @@ ReceiveFromProgram(isr, closure, message, count, error)
       *end_str = NULLCHAR;\r
     \r
     if (appData.debugMode) {\r
-       TimeMark now;\r
-       GetTimeMark(&now);\r
-       fprintf(debugFP, "%ld <%-6s: %s\n", \r
-               SubtractTimeMarks(&now, &programStartTime),\r
-               cps->which, message);\r
+       TimeMark now; int print = 1;\r
+       char *quote = ""; char c; int i;\r
+\r
+       if(appData.engineComments != 1) { /* [HGM] debug: decide if protocol-violating output is written */\r
+               char start = message[0];\r
+               if(start >='A' && start <= 'Z') start += 'a' - 'A'; // be tolerant to capitalizing\r
+               if(sscanf(message, "%d%c%d%d%d", &i, &c, &i, &i, &i) != 5 && \r
+                  sscanf(message, "move %c", &c)!=1  && sscanf(message, "offer%c", &c)!=1 &&\r
+                  sscanf(message, "resign%c", &c)!=1 && sscanf(message, "feature %c", &c)!=1 &&\r
+                  sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&\r
+                  sscanf(message, "tell%c", &c)!=1   && sscanf(message, "0-1 %c", &c)!=1 &&\r
+                  sscanf(message, "1-0 %c", &c)!=1   && sscanf(message, "1/2-1/2 %c", &c)!=1 && start != '#')\r
+                       { quote = "# "; print = (appData.engineComments == 2); }\r
+               message[0] = start; // restore original message\r
+       }\r
+       if(print) {\r
+               GetTimeMark(&now);\r
+               fprintf(debugFP, "%ld <%-6s: %s%s\n", \r
+                       SubtractTimeMarks(&now, &programStartTime), cps->which, \r
+                       quote,\r
+                       message);\r
+       }\r
     }\r
     HandleMachineMove(message, cps);\r
 }\r
@@ -11268,12 +11964,21 @@ SendTimeControl(cps, mps, tc, inc, sd, st)
       }\r
       SendToProgram(buf, cps);\r
     }\r
+\r
+    if(cps->nps > 0) { /* [HGM] nps */\r
+       if(cps->supportsNPS == FALSE) cps->nps = -1; // don't use if engine explicitly says not supported!\r
+       else {\r
+               sprintf(buf, "nps %d\n", cps->nps);\r
+             SendToProgram(buf, cps);\r
+       }\r
+    }\r
 }\r
 \r
 ChessProgramState *WhitePlayer()\r
 /* [HGM] return pointer to 'first' or 'second', depending on who plays white */\r
 {\r
-    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b')\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b' || \r
+       gameMode == BeginningOfGame || gameMode == MachinePlaysBlack)\r
         return &second;\r
     return &first;\r
 }\r
@@ -11434,8 +12139,6 @@ ParseFeatures(args, cps)
     if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;\r
     if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;\r
     if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */\r
-    if (BoolFeature(&p, "debug", &cps->debug, cps)) continue;\r
-    if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;\r
     if (IntFeature(&p, "done", &val, cps)) {\r
       FeatureDone(cps, val);\r
       continue;\r
@@ -11445,6 +12148,15 @@ ParseFeatures(args, cps)
     if (BoolFeature(&p, "oocastle", &cps->useOOCastle, cps)) continue;\r
     /* End of additions by Tord */\r
 \r
+    /* [HGM] added features: */\r
+    if (BoolFeature(&p, "debug", &cps->debug, cps)) continue;\r
+    if (BoolFeature(&p, "nps", &cps->supportsNPS, cps)) continue;\r
+    if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;\r
+    if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;\r
+    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
+    if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;\r
+    /* End of additions by HGM */\r
+\r
     /* unknown feature: complain and skip */\r
     q = p;\r
     while (*q && *q != '=') q++;\r
@@ -11506,12 +12218,33 @@ PonderNextMoveEvent(newState)
 }\r
 \r
 void\r
-ShowThinkingEvent(newState)\r
-     int newState;\r
+NewSettingEvent(option, command, value)\r
+     char *command;\r
+     int option, value;\r
 {\r
-    if (newState == appData.showThinking) return;\r
+    char buf[MSG_SIZ];\r
+\r
     if (gameMode == EditPosition) EditPositionDone();\r
-    if (newState) {\r
+    sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);\r
+    SendToProgram(buf, &first);\r
+    if (gameMode == TwoMachinesPlay) {\r
+       SendToProgram(buf, &second);\r
+    }\r
+}\r
+\r
+void\r
+ShowThinkingEvent()\r
+// [HGM] thinking: this routine is now also called from "Options -> Engine..." popup\r
+{\r
+    static int oldState = 2; // kludge alert! Neither true nor fals, so first time oldState is always updated\r
+    int newState = appData.showThinking\r
+       // [HGM] thinking: other features now need thinking output as well\r
+       || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp();\r
+    \r
+    if (oldState == newState) return;\r
+    oldState = newState;\r
+    if (gameMode == EditPosition) EditPositionDone();\r
+    if (oldState) {\r
        SendToProgram("post\n", &first);\r
        if (gameMode == TwoMachinesPlay) {\r
            SendToProgram("post\n", &second);\r
@@ -11523,7 +12256,7 @@ ShowThinkingEvent(newState)
            SendToProgram("nopost\n", &second);\r
        }\r
     }\r
-    appData.showThinking = newState;\r
+//    appData.showThinking = newState; // [HGM] thinking: responsible option should already have be changed when calling this routine!\r
 }\r
 \r
 void\r
@@ -11700,7 +12433,7 @@ DisplayComment(moveNumber, text)
         if(text == NULL) text = "";                                           \r
         score = pvInfoList[moveNumber].score;\r
         sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
-                              depth, pvInfoList[moveNumber].time, text);\r
+                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
         CommentPopUp(title, buf);\r
     } else\r
     if (text != NULL)\r
@@ -11798,7 +12531,7 @@ CheckTimeControl()
        gameMode == PlayFromGameFile || forwardMostMove == 0) return;\r
 \r
     /*\r
-     * add time to clocks when time control is achieved ([HGM] now also used fot increment)\r
+     * add time to clocks when time control is achieved ([HGM] now also used for increment)\r
      */\r
     if ( !WhiteOnMove(forwardMostMove) )\r
        /* White made time control */\r
@@ -11948,10 +12681,12 @@ DecrementClocks()
     if (fudge < 0 || fudge > FUDGE) fudge = 0;\r
 \r
     if (WhiteOnMove(forwardMostMove)) {\r
+       if(whiteNPS >= 0) lastTickLength = 0;\r
        timeRemaining = whiteTimeRemaining -= lastTickLength;\r
        DisplayWhiteClock(whiteTimeRemaining - fudge,\r
                          WhiteOnMove(currentMove));\r
     } else {\r
+       if(blackNPS >= 0) lastTickLength = 0;\r
        timeRemaining = blackTimeRemaining -= lastTickLength;\r
        DisplayBlackClock(blackTimeRemaining - fudge,\r
                          !WhiteOnMove(currentMove));\r
@@ -12006,15 +12741,17 @@ SwitchClocks()
     if (StopClockTimer() && appData.clockMode) {\r
        lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
        if (WhiteOnMove(forwardMostMove)) {\r
+           if(blackNPS >= 0) lastTickLength = 0;\r
            blackTimeRemaining -= lastTickLength;\r
            /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
-            if(pvInfoList[forwardMostMove-1].time == -1)\r
-                 pvInfoList[forwardMostMove-1].time = \r
+//         if(pvInfoList[forwardMostMove-1].time == -1)\r
+                 pvInfoList[forwardMostMove-1].time =               // use GUI time\r
                       (timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;\r
        } else {\r
-           whiteTimeRemaining -= lastTickLength;\r
+          if(whiteNPS >= 0) lastTickLength = 0;\r
+          whiteTimeRemaining -= lastTickLength;\r
            /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
-            if(pvInfoList[forwardMostMove-1].time == -1)\r
+//         if(pvInfoList[forwardMostMove-1].time == -1)\r
                  pvInfoList[forwardMostMove-1].time = \r
                       (timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;\r
        }\r
@@ -12061,9 +12798,11 @@ StopClocks()
 \r
     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
     if (WhiteOnMove(forwardMostMove)) {\r
+       if(whiteNPS >= 0) lastTickLength = 0;\r
        whiteTimeRemaining -= lastTickLength;\r
        DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));\r
     } else {\r
+       if(blackNPS >= 0) lastTickLength = 0;\r
        blackTimeRemaining -= lastTickLength;\r
        DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));\r
     }\r
@@ -12085,6 +12824,21 @@ StartClocks()
     GetTimeMark(&tickStartTM);\r
     intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?\r
       whiteTimeRemaining : blackTimeRemaining);\r
+\r
+   /* [HGM] nps: figure out nps factors, by determining which engine plays white and/or black once and for all */\r
+    whiteNPS = blackNPS = -1; \r
+    if(gameMode == MachinePlaysWhite || gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w'\r
+       || appData.zippyPlay && gameMode == IcsPlayingBlack) // first (perhaps only) engine has white\r
+       whiteNPS = first.nps;\r
+    if(gameMode == MachinePlaysBlack || gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b'\r
+       || appData.zippyPlay && gameMode == IcsPlayingWhite) // first (perhaps only) engine has black\r
+       blackNPS = first.nps;\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b') // second only used in Two-Machines mode\r
+       whiteNPS = second.nps;\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w')\r
+       blackNPS = second.nps;\r
+    if(appData.debugMode) fprintf(debugFP, "nps: w=%d, b=%d\n", whiteNPS, blackNPS);\r
+\r
     StartClockTimer(intendedTickLength);\r
 }\r
 \r
@@ -12323,7 +13077,7 @@ PositionToFEN(move, useFEN960)
 \r
   if(nrCastlingRights) {\r
      q = p;\r
-     if(gameInfo.variant == VariantFischeRandom) {\r
+     if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {\r
        /* [HGM] write directly from rights */\r
            if(castlingRights[move][2] >= 0 &&\r
               castlingRights[move][0] >= 0   )\r
@@ -12481,12 +13235,14 @@ ParseFEN(board, blackPlaysFirst, fen)
                 *p++;\r
                 if((int) piece >= (int) BlackPawn ) {\r
                     i = (int)piece - (int)BlackPawn;\r
-                    if( i >= BOARD_HEIGHT ) return FALSE;\r
+                   i = PieceToNumber((ChessSquare)i);\r
+                    if( i >= gameInfo.holdingsSize ) return FALSE;\r
                     board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */\r
                     board[BOARD_HEIGHT-1-i][1]++;       /* black counts   */\r
                 } else {\r
                     i = (int)piece - (int)WhitePawn;\r
-                    if( i >= BOARD_HEIGHT ) return FALSE;\r
+                   i = PieceToNumber((ChessSquare)i);\r
+                    if( i >= gameInfo.holdingsSize ) return FALSE;\r
                     board[i][BOARD_WIDTH-1] = piece;    /* white holdings */\r
                     board[i][BOARD_WIDTH-2]++;          /* black holdings */\r
                 }\r
@@ -12516,7 +13272,7 @@ ParseFEN(board, blackPlaysFirst, fen)
     FENepStatus = EP_UNKNOWN;\r
     for(i=0; i<nrCastlingRights; i++ ) {\r
         FENcastlingRights[i] =\r
-            gameInfo.variant == VariantFischeRandom ? -1 : initialRights[i];\r
+            gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom ? -1 : initialRights[i];\r
     }   /* assume possible unless obviously impossible */\r
     if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;\r
     if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;\r
@@ -12535,7 +13291,7 @@ ParseFEN(board, blackPlaysFirst, fen)
           }\r
       }\r
       while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' ||\r
-             gameInfo.variant == VariantFischeRandom &&\r
+             (gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&\r
              ( *p >= 'a' && *p < 'a' + gameInfo.boardWidth) ||\r
              ( *p >= 'A' && *p < 'A' + gameInfo.boardWidth)   ) {\r
         char c = *p++; int whiteKingFile=-1, blackKingFile=-1;\r