exchanged some sprintf with snprintf
[xboard.git] / backend.c
index 7dfb130..3205cde 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -2,8 +2,10 @@
  * backend.c -- Common back end for X and Windows NT versions of\r
  * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $\r
  *\r
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
+ * Massachusetts.  Enhancements Copyright\r
+ * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
+ * Foundation, Inc.\r
  *\r
  * The following terms apply to Digital Equipment Corporation's copyright\r
  * interest in XBoard:\r
  * SOFTWARE.\r
  * ------------------------------------------------------------------------\r
  *\r
- * The following terms apply to the enhanced version of XBoard distributed\r
- * by the Free Software Foundation:\r
+ * The following terms apply to the enhanced version of XBoard\r
+ * distributed by the Free Software Foundation:\r
  * ------------------------------------------------------------------------\r
- * This program is free software; you can redistribute it and/or modify\r
+ *\r
+ * GNU XBoard is free software: you can redistribute it and/or modify\r
  * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
+ * the Free Software Foundation, either version 3 of the License, or (at\r
+ * your option) any later version.\r
  *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
+ * GNU XBoard is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * General Public License for more details.\r
  *\r
  * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
- * ------------------------------------------------------------------------\r
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
  *\r
- * See the file ChangeLog for a revision history.  */\r
+ *------------------------------------------------------------------------\r
+ ** See the file ChangeLog for a revision history.  */\r
 \r
 /* [AS] Also useful here for debugging */\r
 #ifdef WIN32\r
@@ -68,6 +70,7 @@
 #include <sys/types.h>\r
 #include <sys/stat.h>\r
 #include <math.h>\r
+#include <ctype.h>\r
 \r
 #if STDC_HEADERS\r
 # include <stdlib.h>\r
@@ -154,7 +157,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList));
 int LoadPositionFromFile P((char *filename, int n, char *title));\r
 int SavePositionToFile P((char *filename));\r
 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
-                 Board board));\r
+                 Board board, char *castle, char *ep));\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
 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
@@ -208,6 +211,10 @@ 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
+void OutputKibitz(int window, char *text);\r
+int PerpetualChase(int first, int last);\r
+int EngineOutputIsUp();\r
+void InitDrawingSizes(int x, int y);\r
 \r
 #ifdef WIN32\r
        extern void ConsoleCreate();\r
@@ -226,8 +233,7 @@ extern char installDir[MSG_SIZ];
 extern int tinyLayout, smallLayout;\r
 ChessProgramStats programStats;\r
 static int exiting = 0; /* [HGM] moved to top */\r
-static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
-extern int startedFromPositionFile;\r
+static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;\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
@@ -288,6 +294,8 @@ static char * safeStrCpy( char * dst, const char * src, size_t count )
     return dst;\r
 }\r
 \r
+#if 0\r
+//[HGM] for future use? Conditioned out for now to suppress warning.\r
 static char * safeStrCat( char * dst, const char * src, size_t count )\r
 {\r
     size_t  dst_len;\r
@@ -304,6 +312,7 @@ static char * safeStrCat( char * dst, const char * src, size_t count )
 \r
     return dst;\r
 }\r
+#endif\r
 \r
 /* Some compiler can't cast u64 to double\r
  * This function do the job for us:\r
@@ -342,6 +351,8 @@ PosFlags(index)
     flags &= ~F_ALL_CASTLE_OK;\r
   case VariantGiveaway:                // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
     flags |= F_IGNORE_CHECK;\r
+  case VariantLosers:\r
+    flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist\r
     break;\r
   case VariantAtomic:\r
     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
@@ -608,6 +619,7 @@ InitBackEnd1()
     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
 \r
     GetTimeMark(&programStartTime);\r
+    srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level\r
 \r
     ClearProgramStats();\r
     programStats.ok_to_send = 1;\r
@@ -657,7 +669,7 @@ InitBackEnd1()
     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
                          appData.movesPerSession)) {\r
        char buf[MSG_SIZ];\r
-       sprintf(buf, _("bad timeControl option %s"), appData.timeControl);\r
+       snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);\r
        DisplayFatalError(buf, 0, 2);\r
     }\r
 \r
@@ -672,7 +684,7 @@ InitBackEnd1()
            searchTime = min * 60 + sec;\r
        } else {\r
            char buf[MSG_SIZ];\r
-           sprintf(buf, _("bad searchTime option %s"), appData.searchTime);\r
+           snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);\r
            DisplayFatalError(buf, 0, 2);\r
        }\r
     }\r
@@ -737,6 +749,8 @@ InitBackEnd1()
     first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
     first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
     /* End of new features added by Tord. */\r
+    first.fenOverride  = appData.fenOverride1;\r
+    second.fenOverride = appData.fenOverride2;\r
 \r
     /* [HGM] time odds: set factor for each machine */\r
     first.timeOdds  = appData.firstTimeOdds;\r
@@ -817,7 +831,7 @@ InitBackEnd1()
        q = first.program;\r
        while (*q != ' ' && *q != NULLCHAR) q++;\r
        p = q;\r
-       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
+       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] backslash 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
@@ -1110,7 +1124,7 @@ InitBackEnd3 P((void))
                sprintf(buf, _("Could not open comm port %s"),  \r
                        appData.icsCommPort);\r
            } else {\r
-               sprintf(buf, _("Could not connect to host %s, port %s"),  \r
+               snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),  \r
                        appData.icsHost, appData.icsPort);\r
            }\r
            DisplayFatalError(buf, err, 1);\r
@@ -1305,18 +1319,18 @@ establish()
     } else if (*appData.gateway != NULLCHAR) {\r
        if (*appData.remoteShell == NULLCHAR) {\r
            /* Use the rcmd protocol to run telnet program on a gateway host */\r
-           sprintf(buf, "%s %s %s",\r
+           snprintf(buf, sizeof(buf), "%s %s %s",\r
                    appData.telnetProgram, appData.icsHost, appData.icsPort);\r
            return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
 \r
        } else {\r
            /* Use the rsh program to run telnet program on a gateway host */\r
            if (*appData.remoteUser == NULLCHAR) {\r
-               sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
+               snprintf(buf, sizeof(buf), "%s %s %s %s %s", appData.remoteShell,\r
                        appData.gateway, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
            } else {\r
-               sprintf(buf, "%s %s -l %s %s %s %s",\r
+               snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s",\r
                        appData.remoteShell, appData.gateway, \r
                        appData.remoteUser, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
@@ -1941,7 +1955,7 @@ VariantSwitch(Board board, VariantClass newVariant)
 {\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
+//   Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
 \r
    startedFromPositionFile = FALSE;\r
    if(gameInfo.variant == newVariant) return;\r
@@ -2262,6 +2276,12 @@ read_from_ics(isr, closure, data, count, error)
                            nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');\r
                        }\r
                        if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
+                           int depth=0; float score;\r
+                           if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {\r
+                               // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph\r
+                               pvInfoList[forwardMostMove-1].depth = depth;\r
+                               pvInfoList[forwardMostMove-1].score = 100*score;\r
+                           }\r
                            OutputKibitz(suppressKibitz, parse);\r
                        } else {\r
                            char tmp[MSG_SIZ];\r
@@ -2325,7 +2345,7 @@ read_from_ics(isr, closure, data, count, error)
 \r
            if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
              char buf[MSG_SIZ];\r
-             sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
+             snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost);\r
              DisplayIcsInteractionTitle(buf);\r
              have_set_title = TRUE;\r
            }\r
@@ -2357,10 +2377,10 @@ read_from_ics(isr, closure, data, count, error)
                   (StrStr(star_match[0], gameInfo.white) == star_match[0] || \r
                    StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent\r
                        suppressKibitz = TRUE;\r
-                       if((StrStr(star_match[0], gameInfo.white) == star_match[0])\r
-                               && (gameMode == IcsPlayingWhite) ||\r
-                          (StrStr(star_match[0], gameInfo.black) == star_match[0])\r
-                               && (gameMode == IcsPlayingBlack)   ) // opponent kibitz\r
+                       if((StrStr(star_match[0], gameInfo.white) == star_match[0]\r
+                               && (gameMode == IcsPlayingWhite)) ||\r
+                          (StrStr(star_match[0], gameInfo.black) == star_match[0]\r
+                               && (gameMode == IcsPlayingBlack))   ) // opponent kibitz\r
                            started = STARTED_CHATTER; // own kibitz we simply discard\r
                        else {\r
                            started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
@@ -2850,7 +2870,7 @@ read_from_ics(isr, closure, data, count, error)
                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.nodes = programStats.depth = programStats.time = \r
                    programStats.score = programStats.got_only_move = 0;\r
                    sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -3333,7 +3353,7 @@ ParseBoard12(string)
               &ticking);\r
 \r
     if (n < 21) {\r
-       sprintf(str, _("Failed to parse board string:\n\"%s\""), string);\r
+        snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);\r
        DisplayError(str, 0);\r
        return;\r
     }\r
@@ -3704,7 +3724,7 @@ ParseBoard12(string)
          else  strcpy(buf, str); // might be castling\r
          if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) \r
                strcat(buf, prom); // long move lacks promo specification!\r
-         if(!appData.testLegality) {\r
+         if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)\r
                if(appData.debugMode) \r
                        fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
                strcpy(move_str, buf);\r
@@ -3730,6 +3750,7 @@ ParseBoard12(string)
                     strcat(parseList[moveNum - 1], "+");\r
                break;\r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate\r
                strcat(parseList[moveNum - 1], "#");\r
                break;\r
            }\r
@@ -3880,7 +3901,7 @@ ParseBoard12(string)
     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.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
        sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -4100,7 +4121,7 @@ ProcessICSInitScript(f)
 void\r
 AlphaRank(char *move, int n)\r
 {\r
-    char *p = move, c; int x, y;\r
+//    char *p = move, c; int x, y;\r
 \r
     if (appData.debugMode) {\r
         fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
@@ -4244,106 +4265,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     }\r
 }\r
 \r
-/* [AS] FRC game initialization */\r
-static int FindEmptySquare( Board board, int n )\r
-{\r
-    int i = 0;\r
-\r
-    while( 1 ) {\r
-        while( board[0][i] != EmptySquare ) i++;\r
-        if( n == 0 )\r
-            break;\r
-        n--;\r
-        i++;\r
-    }\r
-\r
-    return i;\r
-}\r
-\r
-#if 0\r
-static void ShuffleFRC( Board board )\r
-{\r
-    int i;\r
-\r
-    srand( time(0) );\r
-    \r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    board[0][(rand() % 4)*2  ] = WhiteBishop; /* On dark square */\r
-    board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */\r
-    board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;\r
-    board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;\r
-    board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
-    }\r
-}\r
-\r
-static unsigned char FRC_KnightTable[10] = {\r
-    0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33\r
-};\r
-\r
-static void SetupFRC( Board board, int pos_index )\r
-{\r
-    int i;\r
-    unsigned char knights;\r
-\r
-    /* Bring the position index into a safe range (just in case...) */\r
-    if( pos_index < 0 ) pos_index = 0;\r
-\r
-    pos_index %= 960;\r
-\r
-    /* Clear the board */\r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    /* Place bishops and queen */\r
-    board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */\r
-    pos_index /= 4;\r
-    \r
-    board[0][ (pos_index % 4)*2     ] = WhiteBishop; /* On dark square */\r
-    pos_index /= 4;\r
-\r
-    board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;\r
-    pos_index /= 6;\r
-\r
-    /* Place knigths */\r
-    knights = FRC_KnightTable[ pos_index ];\r
-\r
-    board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;\r
-    board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
-\r
-    /* Place rooks and king */\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    /* Mirror piece placement for black */\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        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
+// [HGM] shuffle: a general way to suffle opening setups, applicable to arbitrary 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
@@ -4352,7 +4274,7 @@ static void SetupFRC( Board board, int pos_index )
 \r
 int squaresLeft[4];\r
 int piecesLeft[(int)BlackPawn];\r
-long long int seed, nrOfShuffles;\r
+int seed, nrOfShuffles;\r
 \r
 void GetPositionNumber()\r
 {      // sets global variable seed\r
@@ -4360,7 +4282,6 @@ void GetPositionNumber()
 \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
@@ -4373,9 +4294,9 @@ int put(Board board, int pieceType, int rank, int n, int shade)
        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
+               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[((i-BOARD_LEFT)&1) + 1]--;\r
                        squaresLeft[ANY]--;\r
                        piecesLeft[pieceType]--; \r
                        return i;\r
@@ -4442,7 +4363,7 @@ void SetUpShuffle(Board board, int number)
            // in variants with super-numerary Kings and Rooks, we leave these for the shuffle\r
        }\r
 \r
-       if((BOARD_RGHT-BOARD_LEFT & 1) == 0)\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
@@ -4499,8 +4420,6 @@ void SetUpShuffle(Board board, int number)
        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
@@ -4618,11 +4537,6 @@ InitPosition(redraw)
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
-      nrCastlingRights = 8;                 /* add rights for second King */\r
-      castlingRights[0][6] = initialRights[2] = 5;\r
-      castlingRights[0][7] = initialRights[5] = 5;\r
-      castlingRank[6] = 0;\r
-      castlingRank[7] = BOARD_HEIGHT-1;\r
       break;\r
     case VariantCapaRandom:\r
       shuffleOpenings = TRUE;\r
@@ -4643,10 +4557,10 @@ InitPosition(redraw)
       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][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
+        castlingRights[0][5] = initialRights[5] =(BOARD_WIDTH-1)>>1;\r
       break;\r
     case VariantFalcon:\r
       pieces = FalconArray;\r
@@ -4788,8 +4702,8 @@ InitPosition(redraw)
 \r
      if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
      if(gameInfo.variant == VariantGreat) { // promotion commoners\r
-       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-1] = WhiteMan;\r
-       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-2] = 9;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9;\r
        initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
        initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
      }\r
@@ -4848,7 +4762,7 @@ SendBoard(cps, moveNum)
     char message[MSG_SIZ];\r
     \r
     if (cps->useSetboard) {\r
-      char* fen = PositionToFEN(moveNum, cps->useFEN960);\r
+      char* fen = PositionToFEN(moveNum, cps->fenOverride);\r
       sprintf(message, "setboard %s\n", fen);\r
       SendToProgram(message, cps);\r
       free(fen);\r
@@ -5303,9 +5217,9 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move
        * If they don't match, display an error message.\r
        */\r
       int saveAnimate;\r
-      Board testBoard;\r
+      Board testBoard; char testRights[BOARD_SIZE]; char testStatus;\r
       CopyBoard(testBoard, boards[currentMove]);\r
-      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus);\r
 \r
       if (CompareBoards(testBoard, boards[currentMove+1])) {\r
        ForwardInner(currentMove+1);\r
@@ -5349,19 +5263,6 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move
 \r
   MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
 \r
-    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
-               && 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
@@ -5415,6 +5316,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", move
     case MT_CHECK:\r
       break;\r
     case MT_CHECKMATE:\r
+    case MT_STAINMATE:\r
       if (WhiteOnMove(currentMove)) {\r
        GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
       } else {\r
@@ -5440,7 +5342,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", move
   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.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
        sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -5473,7 +5375,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", move
 \r
 void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )\r
 {\r
-    char * hint = lastHint;\r
+//    char * hint = lastHint;\r
     FrontEndProgramStats stats;\r
 \r
     stats.which = cps == &first ? 0 : 1;\r
@@ -5667,8 +5569,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
             sprintf(buf1, _("Illegal move \"%s\" from %s machine"),\r
                    machineMove, cps->which);\r
            DisplayError(buf1, 0);\r
-            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d%c",\r
-                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
+            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",\r
+                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);\r
            if (gameMode == TwoMachinesPlay) {\r
              GameEnds(machineWhite ? BlackWins : WhiteWins,\r
                        buf1, GE_XBOARD);\r
@@ -5699,6 +5601,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                         machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
                 GameEnds(machineWhite ? BlackWins : WhiteWins,\r
                            buf1, GE_XBOARD);\r
+               return;\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
@@ -5713,6 +5616,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                toX--;\r
                currentMoveString[2]--;\r
                break;\r
+            default: ; // nothing to do, but suppresses warning of pedantic compilers\r
            }\r
         }\r
        hintRequested = FALSE;\r
@@ -5731,12 +5635,12 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
          if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [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
+               sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
                        programStats.score / 100.,\r
+                       programStats.depth,\r
                        programStats.time / 100.,\r
-                       (double) programStats.nodes,\r
-                       programStats.nodes / (10*abs(programStats.time) + 1.),\r
+                       u64ToDouble(programStats.nodes),\r
+                       u64ToDouble(programStats.nodes) / (10*abs(programStats.time) + 1.),\r
                        programStats.movelist);\r
                SendToICS(buf);\r
          }\r
@@ -5791,50 +5695,26 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
             int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;\r
          if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {\r
 \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
-                                       castlingRights[forwardMostMove]) ) {\r
-             case MT_NONE:\r
-             case MT_CHECK:\r
-             default:\r
-               break;\r
-             case MT_STALEMATE:\r
-               epStatus[forwardMostMove] = EP_STALEMATE;\r
-                if(appData.checkMates) {\r
-                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\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
-               epStatus[forwardMostMove] = EP_CHECKMATE;\r
-                if(appData.checkMates) {\r
-                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\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, NrW=0, bishopsColor = 0,\r
-                    NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
+                    NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,\r
+                    NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;\r
                static int moveCount = 6;\r
+               ChessMove result;\r
+               char *reason = NULL;\r
 \r
-                /* First absolutely insufficient mating material. Count what is on board. */\r
+                /* 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
 \r
                    switch((int) p)\r
                    {   /* count B,N,R and other of each side */\r
+                        case WhiteKing:\r
+                        case BlackKing:\r
+                            NrK++; break; // [HGM] atomic: count Kings\r
                         case WhiteKnight:\r
                              NrWN++; break;\r
                         case WhiteBishop:\r
@@ -5871,7 +5751,106 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                    }\r
                 }\r
 \r
-                if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&\r
+               /* Some material-based adjudications that have to be made before stalemate test */\r
+               if(gameInfo.variant == VariantAtomic && NrK < 2) {\r
+                   // [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal\r
+                    epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as if stm is checkmated\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+                                                       "Xboard adjudication: King destroyed", GE_XBOARD );\r
+                         return;\r
+                    }\r
+               }\r
+\r
+               /* Bare King in Shatranj (loses) or Losers (wins) */\r
+                if( NrW == 1 || NrPieces - NrW == 1) {\r
+                  if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)\r
+                    epStatus[forwardMostMove] = EP_WINS;  // mark as win, so it becomes claimable\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                         return;\r
+                    }\r
+                 } else\r
+                  if( gameInfo.variant == VariantShatranj && --bare < 0)\r
+                  {    /* bare King */\r
+                       epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm\r
+                       if(appData.checkMates) {\r
+                           /* but only adjudicate if adjudication enabled */\r
+                           SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\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
+                 }\r
+                } else bare = 1;\r
+\r
+\r
+            // don't wait for engine to announce game end if we can judge ourselves\r
+            switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile,\r
+                                       castlingRights[forwardMostMove]) ) {\r
+             case MT_CHECK:\r
+               if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time\r
+                   int i, checkCnt = 0;    // (should really be done by making nr of checks part of game state)\r
+                   for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) {\r
+                       if(MateTest(boards[i], PosFlags(i), epStatus[i], castlingRights[i]) == MT_CHECK)\r
+                           checkCnt++;\r
+                       if(checkCnt >= 2) {\r
+                           reason = "Xboard adjudication: 3rd check";\r
+                           epStatus[forwardMostMove] = EP_CHECKMATE;\r
+                           break;\r
+                       }\r
+                   }\r
+               }\r
+             case MT_NONE:\r
+             default:\r
+               break;\r
+             case MT_STALEMATE:\r
+             case MT_STAINMATE:\r
+               reason = "Xboard adjudication: Stalemate";\r
+               if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt\r
+                   epStatus[forwardMostMove] = EP_STALEMATE;   // default result for stalemate is draw\r
+                   if(gameInfo.variant == VariantLosers  || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
+                       epStatus[forwardMostMove] = EP_WINS;    // in these variants stalemated is always a win\r
+                   else if(gameInfo.variant == VariantSuicide) // in suicide it depends\r
+                       epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE :\r
+                                                  ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?\r
+                                                                       EP_CHECKMATE : EP_WINS);\r
+                   else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)\r
+                       epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses\r
+               }\r
+               break;\r
+             case MT_CHECKMATE:\r
+               reason = "Xboard adjudication: Checkmate";\r
+               epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);\r
+               break;\r
+           }\r
+\r
+               switch(i = epStatus[forwardMostMove]) {\r
+                   case EP_STALEMATE:\r
+                       result = GameIsDrawn; break;\r
+                   case EP_CHECKMATE:\r
+                       result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break;\r
+                   case EP_WINS:\r
+                       result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break;\r
+                   default:\r
+                       result = (ChessMove) 0;\r
+               }\r
+                if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( result, reason, GE_XBOARD );\r
+                   return;\r
+               }\r
+\r
+                /* Next absolutely insufficient mating material. */\r
+                if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && \r
+                                    gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible\r
                        (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||\r
                         NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color\r
                 {    /* KBK, KNK, KK of KBKB with like Bishops */\r
@@ -5889,20 +5868,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                      }\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
-                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\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
@@ -5994,6 +5959,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                                                        EP_NONE, castlingRights[m-1]) != MT_CHECK)\r
                                        hisPerpetual = 0; // the opponent did not always check\r
                                }\r
+                               if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",\r
+                                                                       ourPerpetual, hisPerpetual);\r
                                if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
                                    GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
                                           "Xboard adjudication: perpetual checking", GE_XBOARD );\r
@@ -6001,8 +5968,19 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                                }\r
                                if(hisPerpetual && !ourPerpetual)   // he is checking us, but did not repeat yet\r
                                    break; // (or we would have caught him before). Abort repetition-checking loop.\r
-                               // if neither of us is checking all the time, or both are, it is draw\r
-                               // (illegal-chase forfeits not implemented yet!)\r
+                               // Now check for perpetual chases\r
+                               if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase\r
+                                   hisPerpetual = PerpetualChase(k, forwardMostMove);\r
+                                   ourPerpetual = PerpetualChase(k+1, forwardMostMove);\r
+                                   if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit\r
+                                       GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                                     "Xboard adjudication: perpetual chasing", GE_XBOARD );\r
+                                       return;\r
+                                   }\r
+                                   if(hisPerpetual && !ourPerpetual)   // he is chasing us, but did not repeat yet\r
+                                       break; // Abort repetition-checking loop.\r
+                               }\r
+                               // if neither of us is checking or chasing all the time, or both are, it is draw\r
                             }\r
                              GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
                              return;\r
@@ -6111,7 +6089,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                strcat(bookMove, bookHit);\r
                message = bookMove;\r
                cps = cps->other;\r
-               programStats.depth = programStats.nodes = programStats.time = \r
+               programStats.nodes = programStats.depth = programStats.time = \r
                programStats.score = programStats.got_only_move = 0;\r
                sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -6176,7 +6154,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellopponent ", 13)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);\r
+         snprintf(buf1, sizeof(buf1), "%ssay %s\n", ics_prefix, message + 13);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -6187,7 +6165,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellothers ", 11)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);\r
+         snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11);\r
          SendToICS(buf1);\r
        }\r
       }\r
@@ -6196,7 +6174,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellall ", 8)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);\r
+         snprintf(buf1, sizeof(buf1), "%skibitz %s\n", ics_prefix, message + 8);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -6366,7 +6344,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        || (StrStr(message, "Permission denied") != NULL)) {\r
 \r
        cps->maybeThinking = FALSE;\r
-       sprintf(buf1, _("Failed to start %s chess program %s on %s: %s\n"),\r
+       snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),\r
                cps->which, cps->program, cps->host, message);\r
        RemoveInputSource(cps->isr);\r
        DisplayFatalError(buf1, 0, 1);\r
@@ -6384,11 +6362,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                (void) CoordsToAlgebraic(boards[forwardMostMove],\r
                                    PosFlags(forwardMostMove), EP_UNKNOWN,\r
                                    fromY, fromX, toY, toX, promoChar, buf1);\r
-               sprintf(buf2, _("Hint: %s"), buf1);\r
+               snprintf(buf2, sizeof(buf2), _("Hint: %s"), buf1);\r
                DisplayInformation(buf2);\r
            } else {\r
                /* Hint move could not be parsed!? */\r
-               sprintf(buf2,\r
+             snprintf(buf2, sizeof(buf2),\r
                        _("Illegal hint move \"%s\"\nfrom %s chess program"),\r
                        buf1, cps->which);\r
                DisplayError(buf2, 0);\r
@@ -6645,8 +6623,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                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(cps->nps == 0) ticklen = 10*time;                    // use engine reported time\r
+                       else ticklen = (1000. * u64ToDouble(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
@@ -6805,7 +6783,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         else {\r
            buf1[0] = NULLCHAR;\r
 \r
-           if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+           if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",\r
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) \r
             {\r
                 ChessProgramStats cpstats;\r
@@ -7015,11 +6993,13 @@ ParseGameHistory(game)
                                 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
                                 parseList[boardIndex]);\r
        CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+        {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[boardIndex+1][i] = castlingRights[boardIndex][i];}\r
        /* currentMoveString is set as a side-effect of yylex */\r
        strcpy(moveList[boardIndex], currentMoveString);\r
        strcat(moveList[boardIndex], "\n");\r
        boardIndex++;\r
-       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex], \r
+                                       castlingRights[boardIndex], &epStatus[boardIndex]);\r
         switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
                                  EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
          case MT_NONE:\r
@@ -7031,6 +7011,7 @@ ParseGameHistory(game)
                 strcat(parseList[boardIndex - 1], "+");\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            strcat(parseList[boardIndex - 1], "#");\r
            break;\r
        }\r
@@ -7040,55 +7021,55 @@ ParseGameHistory(game)
 \r
 /* Apply a move to the given board  */\r
 void\r
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep)\r
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
      Board board;\r
+     char *castling;\r
+     char *ep;\r
 {\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
+    /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */\r
+    { int i;\r
 \r
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
-      oldEP = epStatus[p-1];\r
-      epStatus[p] = EP_NONE;\r
+      oldEP = *ep;\r
+      *ep = EP_NONE;\r
 \r
       if( board[toY][toX] != EmptySquare ) \r
-           epStatus[p] = EP_CAPTURE;  \r
+           *ep = EP_CAPTURE;  \r
 \r
       if( board[fromY][fromX] == WhitePawn ) {\r
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
-              epStatus[p] = EP_PAWN_MOVE;\r
+              *ep = EP_PAWN_MOVE;\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
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
           }\r
       } else \r
       if( board[fromY][fromX] == BlackPawn ) {\r
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
-              epStatus[p] = EP_PAWN_MOVE; \r
+              *ep = EP_PAWN_MOVE; \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
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
           }\r
        }\r
 \r
        for(i=0; i<nrCastlingRights; i++) {\r
-           castlingRights[p][i] = castlingRights[p-1][i];\r
-           if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
-              castlingRights[p][i] == toX   && castlingRank[i] == toY   \r
-             ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+           if(castling[i] == fromX && castlingRank[i] == fromY ||\r
+              castling[i] == toX   && castlingRank[i] == toY   \r
+             ) castling[i] = -1; // revoke for moved or captured piece\r
        }\r
 \r
     }\r
@@ -7327,6 +7308,19 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
     }\r
 \r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
+               && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+       // [HGM] superchess: take promotion piece out of holdings\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if((int)piece < (int)BlackPawn) { // determine stm from piece color\r
+           if(!--board[k][BOARD_WIDTH-2])\r
+               board[k][BOARD_WIDTH-1] = EmptySquare;\r
+       } else {\r
+           if(!--board[BOARD_HEIGHT-1-k][1])\r
+               board[BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+       }\r
+    }\r
+\r
 }\r
 \r
 /* Updates forwardMostMove */\r
@@ -7392,7 +7386,9 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
        commentList[forwardMostMove+1] = NULL;\r
     }\r
     CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);\r
-    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1]);\r
+    {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[forwardMostMove+1][i] = castlingRights[forwardMostMove][i];}\r
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1], \r
+                               castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);\r
     forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board\r
     gameInfo.result = GameUnfinished;\r
     if (gameInfo.resultDetails != NULL) {\r
@@ -7417,6 +7413,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
             strcat(parseList[forwardMostMove - 1], "+");\r
        break;\r
       case MT_CHECKMATE:\r
+      case MT_STAINMATE:\r
        strcat(parseList[forwardMostMove - 1], "#");\r
        break;\r
     }\r
@@ -7572,7 +7569,7 @@ InitChessProgram(cps, setup)
     }\r
 \r
     if (cps->sendICS) {\r
-      sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
+      snprintf(buf, sizeof(buf), "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
       SendToProgram(buf, cps);\r
     }\r
     cps->maybeThinking = FALSE;\r
@@ -7620,10 +7617,10 @@ StartChessProgram(cps)
        err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);\r
     } else {\r
        if (*appData.remoteUser == NULLCHAR) {\r
-           sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,\r
+         snprintf(buf, sizeof(buf), "%s %s %s", appData.remoteShell, cps->host,\r
                    cps->program);\r
        } else {\r
-           sprintf(buf, "%s %s -l %s %s", appData.remoteShell,\r
+         snprintf(buf, sizeof(buf), "%s %s -l %s %s", appData.remoteShell,\r
                    cps->host, appData.remoteUser, cps->program);\r
        }\r
        err = StartChildProcess(buf, "", &cps->pr);\r
@@ -7775,20 +7772,35 @@ GameEnds(result, resultDetails, whosays)
         if(gameMode == TwoMachinesPlay && appData.testClaims) {\r
            if(appData.testLegality && whosays >= GE_ENGINE1 ) {\r
                 char claimer;\r
+               ChessMove trueResult = (ChessMove) -1;\r
 \r
                 claimer = whosays == GE_ENGINE1 ?      /* color of claimer */\r
                                             first.twoMachinesColor[0] :\r
                                             second.twoMachinesColor[0] ;\r
-                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) &&\r
-                    (result == WhiteWins && claimer == 'w' ||\r
-                     result == BlackWins && claimer == 'b'   ) ) {\r
-               if (appData.debugMode) {\r
-                    fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
-                       result, epStatus[forwardMostMove], forwardMostMove);\r
+\r
+               // [HGM] losers: because the logic is becoming a bit hairy, determine true result first\r
+               if(epStatus[forwardMostMove] == EP_CHECKMATE) {\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins;\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now\r
+                   trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE\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
+\r
+               // now verify win claims, but not in drop games, as we don't understand those yet\r
+                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper\r
+                                                || gameInfo.variant == VariantGreat) &&\r
+                    (result == WhiteWins && claimer == 'w' ||\r
+                     result == BlackWins && claimer == 'b'   ) ) { // case to verify: engine claims own win\r
+                     if (appData.debugMode) {\r
+                       fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
+                               result, epStatus[forwardMostMove], forwardMostMove);\r
+                     }\r
+                     if(result != trueResult) {\r
                              sprintf(buf, "False win claim: '%s'", resultDetails);\r
                              result = claimer == 'w' ? BlackWins : WhiteWins;\r
                              resultDetails = buf;\r
@@ -7808,7 +7820,9 @@ GameEnds(result, resultDetails, whosays)
            }\r
            /* [HGM] bare: don't allow bare King to win */\r
            if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
-                        && result != GameIsDrawn)\r
+              && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway \r
+              && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...\r
+              && 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
@@ -8386,6 +8400,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8421,6 +8436,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8525,7 +8541,7 @@ LoadGameFromFile(filename, n, title, useList)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+         snprintf(buf, sizeof(buf),  _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        }\r
@@ -8581,6 +8597,7 @@ MakeRegisteredMove()
                break;\r
                \r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE:\r
                if (WhiteOnMove(currentMove)) {\r
                    GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
                } else {\r
@@ -8751,7 +8768,7 @@ LoadGame(f, gameNumber, title, useList)
     yynewfile(f);\r
 \r
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
-       sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
+      snprintf(buf, sizeof(buf), "%s vs. %s", lg->gameInfo.white,\r
                lg->gameInfo.black);\r
            DisplayTitle(buf);\r
     } else if (*title != NULLCHAR) {\r
@@ -9178,7 +9195,7 @@ LoadPositionFromFile(filename, n, title)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+            snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9392,7 +9409,7 @@ SaveGameToFile(filename, append)
     } else {\r
        f = fopen(filename, append ? "a" : "w");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9501,7 +9518,7 @@ SaveGamePGN(f)
 {\r
     int i, offset, linelen, newblock;\r
     time_t tm;\r
-    char *movetext;\r
+//    char *movetext;\r
     char numtext[32];\r
     int movelen, numlen, blank;\r
     char move_buffer[100]; /* [AS] Buffer for move+PV info */\r
@@ -9513,7 +9530,7 @@ SaveGamePGN(f)
     PrintPGNTags(f, &gameInfo);\r
     \r
     if (backwardMostMove > 0 || startedFromSetupPosition) {\r
-        char *fen = PositionToFEN(backwardMostMove, 1);\r
+        char *fen = PositionToFEN(backwardMostMove, NULL);\r
         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);\r
        fprintf(f, "\n{--------------\n");\r
        PrintPosition(f, backwardMostMove);\r
@@ -9576,7 +9593,16 @@ SaveGamePGN(f)
        linelen += numlen;\r
 \r
        /* Get move */\r
-       movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */\r
+       strcpy(move_buffer, parseList[i]); // [HGM] pgn: print move via buffer, so it can be edited\r
+       movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */\r
+        if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {\r
+               int p = movelen - 1;\r
+               if(move_buffer[p] == ' ') p--;\r
+               if(move_buffer[p] == ')') { // [HGM] pgn: strip off ICS time if we have extended info\r
+                   while(p && move_buffer[--p] != '(');\r
+                   if(p && move_buffer[p-1] == ' ') move_buffer[movelen=p-1] = 0;\r
+               }\r
+        }\r
 \r
        /* Print move */\r
        blank = linelen > 0 && movelen > 0;\r
@@ -9589,7 +9615,7 @@ SaveGamePGN(f)
            fprintf(f, " ");\r
            linelen++;\r
        }\r
-       fprintf(f, parseList[i]);\r
+       fprintf(f, move_buffer);\r
        linelen += movelen;\r
 \r
         /* [AS] Add PV info if present */\r
@@ -9597,25 +9623,19 @@ SaveGamePGN(f)
             /* [HGM] add time */\r
             char buf[MSG_SIZ]; int seconds = 0;\r
 \r
-#if 0\r
+#if 1\r
             if(i >= backwardMostMove) {\r
                if(WhiteOnMove(i))\r
                        seconds = timeRemaining[0][i] - timeRemaining[0][i+1]\r
-                                 + GetTimeQuota(i/2) / WhitePlayer()->timeOdds;\r
+                                 + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds);\r
                else\r
                        seconds = timeRemaining[1][i] - timeRemaining[1][i+1]\r
-                                  + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds;\r
+                                  + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds);\r
             }\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 < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {\r
@@ -9763,7 +9783,7 @@ SavePositionToFile(filename)
     } else {\r
        f = fopen(filename, "a");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9792,7 +9812,7 @@ SavePosition(f, dummy, dummy2)
        PrintPosition(f, currentMove);\r
        fprintf(f, "--------------]\n");\r
     } else {\r
-       fen = PositionToFEN(currentMove, 1);\r
+       fen = PositionToFEN(currentMove, NULL);\r
        fprintf(f, "%s\n", fen);\r
        free(fen);\r
     }\r
@@ -10428,7 +10448,7 @@ MachineWhiteEvent()
     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.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
        sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -10503,7 +10523,7 @@ MachineBlackEvent()
     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.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
        sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -10649,7 +10669,7 @@ TwoMachinesEvent P((void))
     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.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
        sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
@@ -12383,8 +12403,9 @@ ParseOption(Option *opt, ChessProgramState *cps)
        } else return FALSE;\r
        *p = 0; // terminate option name\r
        // now look if the command-line options define a setting for this engine option.\r
-       p = strstr(cps->optionSettings, opt->name);\r
-       if(p == cps->optionSettings || p[-1] == ',') {\r
+       if(cps->optionSettings && cps->optionSettings[0])\r
+           p = strstr(cps->optionSettings, opt->name); else p = NULL;\r
+       if(p && (p == cps->optionSettings || p[-1] == ',')) {\r
                sprintf(buf, "option %s", p);\r
                if(p = strstr(buf, ",")) *p = 0;\r
                strcat(buf, "\n");\r
@@ -12750,16 +12771,16 @@ DisplayComment(moveNumber, text)
                    WhiteOnMove(moveNumber) ? " " : ".. ",\r
                    parseList[moveNumber]);\r
         }\r
+       // [HGM] PV info: display PV info together with (or as) comment\r
+       if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+           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+50)/100, text);\r
+           text = buf;\r
+       }\r
     } else title[0] = 0;\r
 \r
-    // [HGM] PV info: display PV info together with (or as) comment\r
-    if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
-        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+50)/100, text);\r
-        CommentPopUp(title, buf);\r
-    } else\r
     if (text != NULL)\r
         CommentPopUp(title, text);\r
 }\r
@@ -12884,6 +12905,11 @@ DisplayBothClocks()
    you have neither ftime nor gettimeofday.\r
 */\r
 \r
+/* VS 2008 requires the #include outside of the function */\r
+#if !HAVE_GETTIMEOFDAY && HAVE_FTIME\r
+#include <sys/timeb.h>\r
+#endif\r
+\r
 /* Get the current time as a TimeMark */\r
 void\r
 GetTimeMark(tm)\r
@@ -12901,7 +12927,7 @@ GetTimeMark(tm)
 #else /*!HAVE_GETTIMEOFDAY*/\r
 #if HAVE_FTIME\r
 \r
-#include <sys/timeb.h>\r
+// include <sys/timeb.h> / moved to just above start of function\r
     struct timeb timeB;\r
 \r
     ftime(&timeB);\r
@@ -13322,9 +13348,9 @@ PGNDate()
 \r
 \r
 char *\r
-PositionToFEN(move, useFEN960)\r
+PositionToFEN(move, overrideCastling)\r
      int move;\r
-     int useFEN960;\r
+     char *overrideCastling;\r
 {\r
     int i, j, fromX, fromY, toX, toY;\r
     int whiteToPlay;\r
@@ -13399,6 +13425,9 @@ PositionToFEN(move, useFEN960)
     *p++ = whiteToPlay ? 'w' : 'b';\r
     *p++ = ' ';\r
 \r
+  if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines\r
+    while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' ';\r
+  } else {\r
   if(nrCastlingRights) {\r
      q = p;\r
      if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {\r
@@ -13456,6 +13485,7 @@ PositionToFEN(move, useFEN960)
     }\r
     *p++ = ' ';\r
   }\r
+  }\r
 \r
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r