updated patchlevel to "i"
[xboard.git] / backend.c
index 6c79b03..9c49e05 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
@@ -55,7 +57,7 @@
 \r
 #else\r
 \r
-#define DoSleep( n )\r
+#define DoSleep( n ) if( (n) >= 0) sleep(n)\r
 \r
 #endif\r
 \r
@@ -120,6 +122,16 @@ extern int gettimeofday(struct timeval *, struct timezone *);
 # include "zippy.h"\r
 #endif\r
 #include "backendz.h"\r
+#include "gettext.h" \r
\r
+#ifdef ENABLE_NLS \r
+# define _(s) gettext (s) \r
+# define N_(s) gettext_noop (s) \r
+#else \r
+# define _(s) (s) \r
+# define N_(s) s \r
+#endif \r
+\r
 \r
 /* A point in time */\r
 typedef struct {\r
@@ -127,23 +139,6 @@ typedef struct {
     int ms;    /* Assuming this is >= 16 bits */\r
 } TimeMark;\r
 \r
-/* Search stats from chessprogram */\r
-typedef struct {\r
-  char movelist[2*MSG_SIZ]; /* Last PV we were sent */\r
-  int depth;              /* Current search depth */\r
-  int nr_moves;           /* Total nr of root moves */\r
-  int moves_left;         /* Moves remaining to be searched */\r
-  char move_name[MOVE_LEN];  /* Current move being searched, if provided */\r
-  unsigned long nodes;    /* # of nodes searched */\r
-  int time;               /* Search time (centiseconds) */\r
-  int score;              /* Score (centipawns) */\r
-  int got_only_move;      /* If last msg was "(only move)" */\r
-  int got_fail;           /* 0 - nothing, 1 - got "--", 2 - got "++" */\r
-  int ok_to_send;         /* handshaking between send & recv */\r
-  int line_is_book;       /* 1 if movelist is book moves */\r
-  int seen_stat;          /* 1 if we've seen the stat01: line */\r
-} ChessProgramStats;\r
-\r
 int establish P((void));\r
 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
                         char *buf, int count, int error));\r
@@ -164,7 +159,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
@@ -214,12 +209,33 @@ int string_to_rating P((char *str));
 void ParseFeatures P((char* args, ChessProgramState *cps));\r
 void InitBackEnd3 P((void));\r
 void FeatureDone P((ChessProgramState* cps, int val));\r
-void InitChessProgram P((ChessProgramState *cps));\r
+void InitChessProgram P((ChessProgramState *cps, int setup));\r
+\r
+#ifdef WIN32\r
+       extern void ConsoleCreate();\r
+#endif\r
+\r
+ChessProgramState *WhitePlayer();\r
+void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c\r
+int VerifyDisplayMode P(());\r
 \r
-void GetInfoFromComment( int, char * );\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
+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 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
+int opponentKibitzes;\r
 \r
 /* States for ics_getting_history */\r
 #define H_FALSE 0\r
@@ -291,8 +307,33 @@ static char * safeStrCat( char * dst, const char * src, size_t count )
     return dst;\r
 }\r
 \r
+/* Some compiler can't cast u64 to double\r
+ * This function do the job for us:\r
+\r
+ * We use the highest bit for cast, this only\r
+ * works if the highest bit is not\r
+ * in use (This should not happen)\r
+ *\r
+ * We used this for all compiler\r
+ */\r
+double\r
+u64ToDouble(u64 value)\r
+{\r
+  double r;\r
+  u64 tmp = value & u64Const(0x7fffffffffffffff);\r
+  r = (double)(s64)tmp;\r
+  if (value & u64Const(0x8000000000000000))\r
+       r +=  9.2233720368547758080e18; /* 2^63 */\r
+ return r;\r
+}\r
+\r
 /* Fake up flags for now, as we aren't keeping track of castling\r
-   availability yet */\r
+   availability yet. [HGM] Change of logic: the flag now only\r
+   indicates the type of castlings allowed by the rule of the game.\r
+   The actual rights themselves are maintained in the array\r
+   castlingRights, as part of the game history, and are not probed\r
+   by this function.\r
+ */\r
 int\r
 PosFlags(index)\r
 {\r
@@ -300,9 +341,9 @@ PosFlags(index)
   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
   switch (gameInfo.variant) {\r
   case VariantSuicide:\r
-  case VariantGiveaway:\r
-    flags |= F_IGNORE_CHECK;\r
     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
     break;\r
   case VariantAtomic:\r
     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
@@ -310,7 +351,12 @@ PosFlags(index)
   case VariantKriegspiel:\r
     flags |= F_KRIEGSPIEL_CAPTURE;\r
     break;\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
@@ -346,12 +392,8 @@ int gotPremove = 0;
 Boolean alarmSounded;\r
 /* end premove variables */\r
 \r
-#define ICS_GENERIC 0\r
-#define ICS_ICC 1\r
-#define ICS_FICS 2\r
-#define ICS_CHESSNET 3 /* not really supported */\r
-int ics_type = ICS_GENERIC;\r
 char *ics_prefix = "$";\r
+int ics_type = ICS_GENERIC;\r
 \r
 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;\r
 int pauseExamForwardMostMove = 0;\r
@@ -383,6 +425,7 @@ int have_sent_ICS_logon = 0;
 int movesPerSession;\r
 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
 long timeControl_2; /* [AS] Allow separate time controls */\r
+char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */\r
 long timeRemaining[2][MAX_MOVES];\r
 int matchGame = 0;\r
 TimeMark programStartTime;\r
@@ -405,10 +448,13 @@ Board boards[MAX_MOVES];
 char  epStatus[MAX_MOVES];\r
 char  castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1\r
 char  castlingRank[BOARD_SIZE]; // and corresponding ranks\r
-char  initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE];\r
+char  initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];\r
 int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status\r
 int   initialRulePlies, FENrulePlies;\r
 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
@@ -424,7 +470,6 @@ ChessSquare twoKingsArray[2][BOARD_SIZE] = {
         BlackKing, BlackKing, BlackKnight, BlackRook }\r
 };\r
 \r
-#ifdef FAIRY\r
 ChessSquare  KnightmateArray[2][BOARD_SIZE] = {\r
     { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,\r
         WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },\r
@@ -440,10 +485,10 @@ ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King
 };\r
 \r
 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
-    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,\r
-        WhiteKing, WhiteAlfil, WhiteKnight, WhiteRook },\r
-    { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
-        BlackKing, BlackAlfil, BlackKnight, BlackRook }\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,\r
+        WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackKing,\r
+        BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
 };\r
 \r
 \r
@@ -463,27 +508,53 @@ ChessSquare XiangqiArray[2][BOARD_SIZE] = {
 };\r
 \r
 ChessSquare CapablancaArray[2][BOARD_SIZE] = {\r
-    { WhiteRook, WhiteKnight, WhiteCardinal, WhiteBishop, WhiteQueen, \r
+    { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen, \r
         WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
-    { BlackRook, BlackKnight, BlackCardinal, BlackBishop, BlackQueen, \r
+    { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen, \r
         BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
 };\r
 \r
+ChessSquare GreatArray[2][BOARD_SIZE] = {\r
+    { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, \r
+        WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },\r
+    { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, \r
+        BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },\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
-        WhiteKing, WhiteCardinal, WhiteBishop, WhiteKnight, WhiteRook },\r
+        WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },\r
     { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, \r
-        BlackKing, BlackCardinal, BlackBishop, BlackKnight, BlackRook }\r
+        BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }\r
 };\r
 #else // !GOTHIC\r
 #define GothicArray CapablancaArray\r
 #endif // !GOTHIC\r
 \r
+#ifdef FALCON\r
+ChessSquare FalconArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen, \r
+        WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen, \r
+        BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !FALCON\r
+#define FalconArray CapablancaArray\r
+#endif // !FALCON\r
+\r
 #else // !(BOARD_SIZE>=10)\r
 #define XiangqiPosition FIDEArray\r
 #define CapablancaArray FIDEArray\r
 #define GothicArray FIDEArray\r
+#define GreatArray FIDEArray\r
 #endif // !(BOARD_SIZE>=10)\r
 \r
 #if (BOARD_SIZE>=12)\r
@@ -496,7 +567,6 @@ ChessSquare CourierArray[2][BOARD_SIZE] = {
 #else // !(BOARD_SIZE>=12)\r
 #define CourierArray CapablancaArray\r
 #endif // !(BOARD_SIZE>=12)\r
-#endif // FAIRY\r
 \r
 \r
 Board initialPosition;\r
@@ -525,7 +595,7 @@ ClearProgramStats()
     programStats.nr_moves = 0;\r
     programStats.moves_left = 0;\r
     programStats.nodes = 0;\r
-    programStats.time = 100;\r
+    programStats.time = -1;        // [HGM] PGNtime: make invalid to recognize engine output\r
     programStats.score = 0;\r
     programStats.got_only_move = 0;\r
     programStats.got_fail = 0;\r
@@ -537,6 +607,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
@@ -587,7 +659,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
+       sprintf(buf, _("bad timeControl option %s"), appData.timeControl);\r
        DisplayFatalError(buf, 0, 2);\r
     }\r
 \r
@@ -602,7 +674,7 @@ InitBackEnd1()
            searchTime = min * 60 + sec;\r
        } else {\r
            char buf[MSG_SIZ];\r
-           sprintf(buf, "bad searchTime option %s", appData.searchTime);\r
+           sprintf(buf, _("bad searchTime option %s"), appData.searchTime);\r
            DisplayFatalError(buf, 0, 2);\r
        }\r
     }\r
@@ -640,6 +712,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
@@ -666,6 +740,31 @@ InitBackEnd1()
     first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
     /* End of new features added by Tord. */\r
 \r
+    /* [HGM] time odds: set factor for each machine */\r
+    first.timeOdds  = appData.firstTimeOdds;\r
+    second.timeOdds = appData.secondTimeOdds;\r
+    { int norm = 1;\r
+        if(appData.timeOddsMode) {\r
+            norm = first.timeOdds;\r
+            if(norm > second.timeOdds) norm = second.timeOdds;\r
+        }\r
+        first.timeOdds /= norm;\r
+        second.timeOdds /= norm;\r
+    }\r
+\r
+    /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/\r
+    first.accumulateTC = appData.firstAccumulateTC;\r
+    second.accumulateTC = appData.secondAccumulateTC;\r
+    first.maxNrOfSessions = second.maxNrOfSessions = 1;\r
+\r
+    /* [HGM] debug */\r
+    first.debug = second.debug = FALSE;\r
+    first.supportsNPS = second.supportsNPS = UNKNOWN;\r
+\r
+    /* [HGM] options */\r
+    first.optionSettings  = appData.firstOptions;\r
+    second.optionSettings = appData.secondOptions;\r
+\r
     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
     first.isUCI = appData.firstIsUCI; /* [AS] */\r
@@ -676,7 +775,7 @@ InitBackEnd1()
     if (appData.firstProtocolVersion > PROTOVER ||\r
        appData.firstProtocolVersion < 1) {\r
       char buf[MSG_SIZ];\r
-      sprintf(buf, "protocol version %d not supported",\r
+      sprintf(buf, _("protocol version %d not supported"),\r
              appData.firstProtocolVersion);\r
       DisplayFatalError(buf, 0, 2);\r
     } else {\r
@@ -686,7 +785,7 @@ InitBackEnd1()
     if (appData.secondProtocolVersion > PROTOVER ||\r
        appData.secondProtocolVersion < 1) {\r
       char buf[MSG_SIZ];\r
-      sprintf(buf, "protocol version %d not supported",\r
+      sprintf(buf, _("protocol version %d not supported"),\r
              appData.secondProtocolVersion);\r
       DisplayFatalError(buf, 0, 2);\r
     } else {\r
@@ -715,15 +814,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
@@ -737,7 +843,7 @@ InitBackEnd1()
       case VariantBughouse:     /* need four players and two boards */\r
       case VariantKriegspiel:   /* need to hide pieces and move details */\r
       /* case VariantFischeRandom: (Fabien: moved below) */\r
-       sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);\r
+       sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);\r
        DisplayFatalError(buf, 0, 2);\r
        return;\r
 \r
@@ -752,7 +858,7 @@ InitBackEnd1()
       case Variant35:\r
       case Variant36:\r
       default:\r
-       sprintf(buf, "Unknown variant name %s", appData.variant);\r
+       sprintf(buf, _("Unknown variant name %s"), appData.variant);\r
        DisplayFatalError(buf, 0, 2);\r
        return;\r
 \r
@@ -762,14 +868,15 @@ InitBackEnd1()
       case VariantCapablanca: /* [HGM] should work */\r
       case VariantCourier:    /* [HGM] initial forced moves not implemented */\r
       case VariantShogi:      /* [HGM] drops not tested for legality */\r
-      case VariantShowgi:     /* [HGM] not a valid variant */\r
       case VariantKnightmate: /* [HGM] should work */\r
+      case VariantCylinder:   /* [HGM] untested */\r
+      case VariantFalcon:     /* [HGM] untested */\r
       case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)\r
                                 offboard interposition not understood */\r
       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
@@ -779,10 +886,18 @@ 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
+      case VariantGreat:      /* experimental, requires legality testing to be off */\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
@@ -827,22 +942,51 @@ int NextTimeControlFromString( char ** str, long * value )
     return result;\r
 }\r
 \r
-int GetTimeControlForWhite()\r
-{\r
-    int result = timeControl;\r
+int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)\r
+{   /* [HGM] routine added to read '+moves/time' for secondary time control */\r
+    int result = -1; long temp, temp2;\r
+\r
+    if(**str != '+') return -1; // old params remain in force!\r
+    (*str)++;\r
+    if( NextTimeControlFromString( str, &temp ) ) return -1;\r
 \r
+    if(**str != '/') {\r
+        /* time only: incremental or sudden-death time control */\r
+        if(**str == '+') { /* increment follows; read it */\r
+            (*str)++;\r
+            if(result = NextIntegerFromString( str, &temp2)) return -1;\r
+            *inc = temp2 * 1000;\r
+        } else *inc = 0;\r
+        *moves = 0; *tc = temp * 1000; \r
+        return 0;\r
+    } else if(temp % 60 != 0) return -1;     /* moves was given as min:sec */\r
+\r
+    (*str)++; /* classical time control */\r
+    result = NextTimeControlFromString( str, &temp2);\r
+    if(result == 0) {\r
+        *moves = temp/60;\r
+        *tc    = temp2 * 1000;\r
+        *inc   = 0;\r
+    }\r
     return result;\r
 }\r
 \r
-int GetTimeControlForBlack()\r
-{\r
-    int result = timeControl;\r
+int GetTimeQuota(int movenr)\r
+{   /* [HGM] get time to add from the multi-session time-control string */\r
+    int moves=1; /* kludge to force reading of first session */\r
+    long time, increment;\r
+    char *s = fullTimeControlString;\r
 \r
-    if( timeControl_2 > 0 ) {\r
-        result = timeControl_2;\r
-    }\r
+    if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);\r
+    do {\r
+        if(moves) NextSessionFromString(&s, &moves, &time, &increment);\r
+        if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);\r
+        if(movenr == -1) return time;    /* last move before new session     */\r
+        if(!moves) return increment;     /* current session is incremental   */\r
+        if(movenr >= 0) movenr -= moves; /* we already finished this session */\r
+    } while(movenr >= -1);               /* try again for next session       */\r
 \r
-    return result;\r
+    return 0; // no new time quota on this move\r
 }\r
 \r
 int\r
@@ -865,6 +1009,19 @@ ParseTimeControl(tc, ti, mps)
 #else\r
     long tc1;\r
     long tc2;\r
+    char buf[MSG_SIZ];\r
+\r
+    if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;\r
+    if(ti > 0) {\r
+        if(mps)\r
+             sprintf(buf, "+%d/%s+%d", mps, tc, ti);\r
+        else sprintf(buf, "+%s+%d", tc, ti);\r
+    } else {\r
+        if(mps)\r
+             sprintf(buf, "+%d/%s", mps, tc);\r
+        else sprintf(buf, "+%s", tc);\r
+    }\r
+    fullTimeControlString = StrSave(buf);\r
 \r
     if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {\r
         return FALSE;\r
@@ -917,13 +1074,19 @@ 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
     } else {\r
       /* kludge: allow timeout for initial "feature" commands */\r
       FreezeUI();\r
-      DisplayMessage("", "Starting chess program");\r
+      DisplayMessage("", _("Starting chess program"));\r
       ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);\r
     }\r
 }\r
@@ -935,16 +1098,21 @@ InitBackEnd3 P((void))
     char buf[MSG_SIZ];\r
     int err;\r
 \r
-    InitChessProgram(&first);\r
+    InitChessProgram(&first, startedFromSetupPosition);\r
+\r
 \r
     if (appData.icsActive) {\r
+#ifdef WIN32\r
+        /* [DM] Make a console window if needed [HGM] merged ifs */\r
+        ConsoleCreate(); \r
+#endif\r
        err = establish();\r
        if (err != 0) {\r
            if (*appData.icsCommPort != NULLCHAR) {\r
-               sprintf(buf, "Could not open comm port %s",  \r
+               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
+               sprintf(buf, _("Could not connect to host %s, port %s"),  \r
                        appData.icsHost, appData.icsPort);\r
            }\r
            DisplayFatalError(buf, err, 1);\r
@@ -989,7 +1157,7 @@ InitBackEnd3 P((void))
     } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {\r
       initialMode = Training;\r
     } else {\r
-      sprintf(buf, "Unknown initialMode %s", appData.initialMode);\r
+      sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);\r
       DisplayFatalError(buf, 0, 2);\r
       return;\r
     }\r
@@ -997,24 +1165,28 @@ InitBackEnd3 P((void))
     if (appData.matchMode) {\r
        /* Set up machine vs. machine match */\r
        if (appData.noChessProgram) {\r
-           DisplayFatalError("Can't have a match with no chess programs",\r
+           DisplayFatalError(_("Can't have a match with no chess programs"),\r
                              0, 2);\r
            return;\r
        }\r
        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
+               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
+               DisplayFatalError(_("Bad position file"), 0, 1);\r
                return;\r
            }\r
        }\r
@@ -1026,7 +1198,7 @@ InitBackEnd3 P((void))
        /* Set up other modes */\r
        if (initialMode == AnalyzeFile) {\r
          if (*appData.loadGameFile == NULLCHAR) {\r
-           DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);\r
+           DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);\r
            return;\r
          }\r
        }\r
@@ -1038,53 +1210,67 @@ InitBackEnd3 P((void))
            (void) LoadPositionFromFile(appData.loadPositionFile,\r
                                        appData.loadPositionIndex,\r
                                        appData.loadPositionFile);\r
+            /* [HGM] try to make self-starting even after FEN load */\r
+            /* to allow automatic setup of fairy variants with wtm */\r
+            if(initialMode == BeginningOfGame && !blackPlaysFirst) {\r
+                gameMode = BeginningOfGame;\r
+                setboardSpoiledMachineBlack = 1;\r
+            }\r
+            /* [HGM] loadPos: make that every new game uses the setup */\r
+            /* from file as long as we do not switch variant          */\r
+            if(!blackPlaysFirst) { int i;\r
+                startedFromPositionFile = TRUE;\r
+                CopyBoard(filePosition, boards[0]);\r
+                for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];\r
+            }\r
        }\r
        if (initialMode == AnalyzeMode) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("Analysis mode requires a chess engine", 0, 2);\r
+           DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("Analysis mode does not work with ICS mode",0,2);\r
+           DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);\r
            return;\r
          }\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
          if (appData.noChessProgram) {\r
-           DisplayFatalError("MachineWhite mode requires a chess engine",\r
+           DisplayFatalError(_("MachineWhite mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("MachineWhite mode does not work with ICS mode",\r
+           DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
          MachineWhiteEvent();\r
        } else if (initialMode == MachinePlaysBlack) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("MachineBlack mode requires a chess engine",\r
+           DisplayFatalError(_("MachineBlack mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("MachineBlack mode does not work with ICS mode",\r
+           DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
          MachineBlackEvent();\r
        } else if (initialMode == TwoMachinesPlay) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("TwoMachines mode requires a chess engine",\r
+           DisplayFatalError(_("TwoMachines mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("TwoMachines mode does not work with ICS mode",\r
+           DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
@@ -1095,7 +1281,7 @@ InitBackEnd3 P((void))
          EditPositionEvent();\r
        } else if (initialMode == Training) {\r
          if (*appData.loadGameFile == NULLCHAR) {\r
-           DisplayFatalError("Training mode requires a game file", 0, 2);\r
+           DisplayFatalError(_("Training mode requires a game file"), 0, 2);\r
            return;\r
          }\r
          TrainingEvent();\r
@@ -1243,14 +1429,14 @@ read_from_player(isr, closure, message, count, error)
        gotEof = 0;\r
        outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
        if (outCount < count) {\r
-           DisplayFatalError("Error writing to ICS", outError, 1);\r
+            DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
        }\r
     } else if (count < 0) {\r
        RemoveInputSource(isr);\r
-       DisplayFatalError("Error reading from keyboard", error, 1);\r
+       DisplayFatalError(_("Error reading from keyboard"), error, 1);\r
     } else if (gotEof++ > 0) {\r
        RemoveInputSource(isr);\r
-       DisplayFatalError("Got end of file from keyboard", 0, 0);\r
+       DisplayFatalError(_("Got end of file from keyboard"), 0, 0);\r
     }\r
 }\r
 \r
@@ -1265,7 +1451,7 @@ SendToICS(s)
     count = strlen(s);\r
     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);\r
     if (outCount < count) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1290,7 +1476,7 @@ SendToICSDelayed(s,msdelay)
     outCount = OutputToProcessDelayed(icsPR, s, count, &outError,\r
                                      msdelay);\r
     if (outCount < count) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1367,9 +1553,8 @@ StringToVariant(e)
     if (!e) return v;\r
 \r
     /* [HGM] skip over optional board-size prefixes */\r
-    if( sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
-        while( *e++ != '_');\r
-    } else if( sscanf(e, "%dx%d_", &i, &i) == 2 ) {\r
+    if( sscanf(e, "%dx%d_", &i, &i) == 2 ||\r
+        sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
         while( *e++ != '_');\r
     }\r
 \r
@@ -1383,7 +1568,8 @@ StringToVariant(e)
 \r
     if (!found) {\r
       if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
-         || StrCaseStr(e, "wild/fr")) {\r
+         || StrCaseStr(e, "wild/fr") \r
+         || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {\r
         v = VariantFischeRandom;\r
       } else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
                 (i = 1, p = StrCaseStr(e, "w"))) {\r
@@ -1508,16 +1694,33 @@ StringToVariant(e)
           v = VariantFairy;\r
           break;\r
         case 44:\r
-          v = VariantShowgi;\r
+          v = VariantCylinder;\r
+         break;\r
+        case 45:\r
+          v = VariantFalcon;\r
+         break;\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 50:\r
+          v = VariantGreat;\r
          break;\r
-\r
        case -1:\r
          /* Found "wild" or "w" in the string but no number;\r
             must assume it's normal chess. */\r
          v = VariantNormal;\r
          break;\r
        default:\r
-         sprintf(buf, "Unknown wild type %d", wnum);\r
+         sprintf(buf, _("Unknown wild type %d"), wnum);\r
          DisplayError(buf, 0);\r
          v = VariantUnknown;\r
          break;\r
@@ -1525,7 +1728,7 @@ StringToVariant(e)
       }\r
     }\r
     if (appData.debugMode) {\r
-      fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",\r
+      fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),\r
              e, wnum, VariantName(v));\r
     }\r
     return v;\r
@@ -1590,7 +1793,7 @@ SendToPlayer(data, length)
     int error, outCount;\r
     outCount = OutputToProcess(NoProc, data, length, &error);\r
     if (outCount < length) {\r
-       DisplayFatalError("Error writing to display", error, 1);\r
+       DisplayFatalError(_("Error writing to display"), error, 1);\r
     }\r
 }\r
 \r
@@ -1676,7 +1879,7 @@ TelnetRequest(ddww, option)
     msg[2] = option;\r
     outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);\r
     if (outCount < 3) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1727,13 +1930,101 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
         j = PieceToNumber(piece);\r
         if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
         if(j < 0) continue;               /* should not happen */\r
-        piece = (ChessSquare) ( j + (int)lowestPiece );\r
+        piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
         board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
         board[holdingsStartRow+j*direction][countsColumn]++;\r
     }\r
 \r
 }\r
 \r
+\r
+void\r
+VariantSwitch(Board board, VariantClass newVariant)\r
+{\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
+   if(gameInfo.variant == newVariant) return;\r
+\r
+   /* [HGM] This routine is called each time an assignment is made to\r
+    * gameInfo.variant during a game, to make sure the board sizes\r
+    * are set to match the new variant. If that means adding or deleting\r
+    * holdings, we shift the playing board accordingly\r
+    * This kludge is needed because in ICS observe mode, we get boards\r
+    * of an ongoing game without knowing the variant, and learn about the\r
+    * latter only later. This can be because of the move list we requested,\r
+    * in which case the game history is refilled from the beginning anyway,\r
+    * but also when receiving holdings of a crazyhouse game. In the latter\r
+    * case we want to add those holdings to the already received position.\r
+    */\r
+\r
+\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Switch board from %s to %s\n",\r
+               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
+            case VariantShogi:\r
+              newWidth = 9;  newHeight = 9;\r
+              gameInfo.holdingsSize = 7;\r
+            case VariantBughouse:\r
+            case VariantCrazyhouse:\r
+              newHoldingsWidth = 2; break;\r
+            default:\r
+              newHoldingsWidth = gameInfo.holdingsSize = 0;\r
+    }\r
+\r
+    if(newWidth  != gameInfo.boardWidth  ||\r
+       newHeight != gameInfo.boardHeight ||\r
+       newHoldingsWidth != gameInfo.holdingsWidth ) {\r
+\r
+        /* shift position to new playing area, if needed */\r
+        if(newHoldingsWidth > gameInfo.holdingsWidth) {\r
+           for(i=0; i<BOARD_HEIGHT; i++) \r
+               for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)\r
+                   board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
+                                                     board[i][j];\r
+           for(i=0; i<newHeight; i++) {\r
+               board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;\r
+               board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;\r
+           }\r
+        } else if(newHoldingsWidth < gameInfo.holdingsWidth) {\r
+           for(i=0; i<BOARD_HEIGHT; i++)\r
+               for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
+                   board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
+                                                 board[i][j];\r
+        }\r
+\r
+        gameInfo.boardWidth  = newWidth;\r
+        gameInfo.boardHeight = newHeight;\r
+        gameInfo.holdingsWidth = newHoldingsWidth;\r
+        gameInfo.variant = newVariant;\r
+        InitDrawingSizes(-2, 0);\r
+\r
+        /* [HGM] The following should definitely be solved in a better way */\r
+#if 0\r
+        CopyBoard(board, tempBoard); /* save position in case it is board[0] */\r
+        for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];\r
+        saveEP = epStatus[0];\r
+#endif\r
+        InitPosition(FALSE);          /* this sets up board[0], but also other stuff        */\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
 \r
 /*-- Game start info cache: --*/\r
@@ -1746,6 +2037,7 @@ static int player2Rating = -1;
 /*----------------------------*/\r
 \r
 ColorClass curColor = ColorNormal;\r
+int suppressKibitz = 0;\r
 \r
 void\r
 read_from_ics(isr, closure, data, count, error)\r
@@ -1777,9 +2069,9 @@ read_from_ics(isr, closure, data, count, error)
     int buf_len;\r
     int next_out;\r
     int tkind;\r
+    int backup;    /* [DM] For zippy color lines */\r
     char *p;\r
 \r
-#ifdef WIN32\r
     if (appData.debugMode) {\r
       if (!error) {\r
        fprintf(debugFP, "<ICS: ");\r
@@ -1787,8 +2079,11 @@ read_from_ics(isr, closure, data, count, error)
        fprintf(debugFP, "\n");\r
       }\r
     }\r
-#endif\r
 \r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        fprintf(debugFP, "ics input %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
     if (count > 0) {\r
        /* If last read ended with a partial line that we couldn't parse,\r
           prepend it to the new read and try again. */\r
@@ -1802,6 +2097,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
@@ -1951,7 +2249,28 @@ read_from_ics(isr, closure, data, count, error)
                parse[parse_pos++] = buf[i];\r
                if (buf[i] == '\n') {\r
                    parse[parse_pos] = NULLCHAR;\r
-                   AppendComment(forwardMostMove, StripHighlight(parse));\r
+                   if(!suppressKibitz) // [HGM] kibitz\r
+                       AppendComment(forwardMostMove, StripHighlight(parse));\r
+                   else { // [HGM kibitz: divert memorized engine kibitz to engine-output window\r
+                       int nrDigit = 0, nrAlph = 0, i;\r
+                       if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input\r
+                       { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }\r
+                       parse[parse_pos] = NULLCHAR;\r
+                       // try to be smart: if it does not look like search info, it should go to\r
+                       // ICS interaction window after all, not to engine-output window.\r
+                       for(i=0; i<parse_pos; i++) { // count letters and digits\r
+                           nrDigit += (parse[i] >= '0' && parse[i] <= '9');\r
+                           nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');\r
+                           nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');\r
+                       }\r
+                       if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
+                           OutputKibitz(suppressKibitz, parse);\r
+                       } else {\r
+                           char tmp[MSG_SIZ];\r
+                           sprintf(tmp, _("your opponent kibitzes: %s"), parse);\r
+                           SendToPlayer(tmp, strlen(tmp));\r
+                       }\r
+                   }\r
                    started = STARTED_NONE;\r
                } else {\r
                    /* Don't match patterns against characters in chatter */\r
@@ -2032,17 +2351,53 @@ read_from_ics(isr, closure, data, count, error)
            }\r
 \r
            oldi = i;\r
+           // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window\r
+           if (appData.autoKibitz && started == STARTED_NONE && \r
+                !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze\r
+               (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {\r
+               if(looking_at(buf, &i, "* kibitzes: ") &&\r
+                  (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
+                           started = STARTED_CHATTER; // own kibitz we simply discard\r
+                       else {\r
+                           started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
+                           parse_pos = 0; parse[0] = NULLCHAR;\r
+                           savingComment = TRUE;\r
+                           suppressKibitz = gameMode != IcsObserving ? 2 :\r
+                               (StrStr(star_match[0], gameInfo.white) == NULL) + 1;\r
+                       } \r
+                       continue;\r
+               } else\r
+               if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz\r
+                   started = STARTED_CHATTER;\r
+                   suppressKibitz = TRUE;\r
+               }\r
+           } // [HGM] kibitz: end of patch\r
+\r
            if (appData.zippyTalk || appData.zippyPlay) {\r
+                /* [DM] Backup address for color zippy lines */\r
+                backup = i;\r
 #if ZIPPY\r
-               if (ZippyControl(buf, &i) ||\r
-                   ZippyConverse(buf, &i) ||\r
-                   (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
-                   loggedOn = TRUE;\r
-                   continue;\r
+       #ifdef WIN32\r
+               if (loggedOn == TRUE)\r
+                       if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||\r
+                          (appData.zippyPlay && ZippyMatch(buf, &backup)));\r
+       #else\r
+                if (ZippyControl(buf, &i) ||\r
+                    ZippyConverse(buf, &i) ||\r
+                    (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
+                     loggedOn = TRUE;\r
+                      if (!appData.colorize) continue;\r
                }\r
+       #endif\r
 #endif\r
-           } else {\r
-               if (/* Don't color "message" or "messages" output */\r
+           } // [DM] 'else { ' deleted\r
+               if (/* Don't color "message" or "messages" output */\r
                    (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||\r
                    looking_at(buf, &i, "*. * at *:*: ") ||\r
                    looking_at(buf, &i, "--* (*:*): ") ||\r
@@ -2197,7 +2552,6 @@ read_from_ics(isr, closure, data, count, error)
                        curColor = ColorSeek;\r
                    }\r
                    continue;\r
-               }\r
            }\r
 \r
            if (looking_at(buf, &i, "\\   ")) {\r
@@ -2308,7 +2662,7 @@ read_from_ics(isr, closure, data, count, error)
                  case H_GOT_UNWANTED_HEADER:\r
                  case H_GETTING_MOVES:\r
                    /* Should not happen */\r
-                   DisplayError("Error gathering move list: two headers", 0);\r
+                   DisplayError(_("Error gathering move list: two headers"), 0);\r
                    ics_getting_history = H_FALSE;\r
                    break;\r
                }\r
@@ -2322,7 +2676,7 @@ read_from_ics(isr, closure, data, count, error)
                    gameInfo.whiteRating = string_to_rating(star_match[1]);\r
                    gameInfo.blackRating = string_to_rating(star_match[3]);\r
                    if (appData.debugMode)\r
-                     fprintf(debugFP, "Ratings from header: W %d, B %d\n", \r
+                     fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), \r
                              gameInfo.whiteRating, gameInfo.blackRating);\r
                }\r
                continue;\r
@@ -2332,12 +2686,11 @@ read_from_ics(isr, closure, data, count, error)
              "* * match, initial time: * minute*, increment: * second")) {\r
                /* Header for a move list -- second line */\r
                /* Initial board will follow if this is a wild game */\r
-\r
                if (gameInfo.event != NULL) free(gameInfo.event);\r
                sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);\r
                gameInfo.event = StrSave(str);\r
-               gameInfo.variant = StringToVariant(gameInfo.event);\r
-                Reset(TRUE,TRUE); /* [HGM] possibly change board or holdings size */\r
+                /* [HGM] we switched variant. Translate boards if needed. */\r
+                VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));\r
                continue;\r
            }\r
 \r
@@ -2356,7 +2709,7 @@ read_from_ics(isr, closure, data, count, error)
                    break;\r
                  case H_GETTING_MOVES:\r
                    /* Should not happen */\r
-                   DisplayError("Error gathering move list: nested", 0);\r
+                   DisplayError(_("Error gathering move list: nested"), 0);\r
                    ics_getting_history = H_FALSE;\r
                    break;\r
                  case H_GOT_REQ_HEADER:\r
@@ -2381,7 +2734,7 @@ read_from_ics(isr, closure, data, count, error)
            \r
            if (looking_at(buf, &i, "% ") ||\r
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
-                && looking_at(buf, &i, "}*"))) {\r
+                && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book\r
                savingComment = FALSE;\r
                switch (started) {\r
                  case STARTED_MOVES:\r
@@ -2400,11 +2753,18 @@ read_from_ics(isr, closure, data, count, error)
                                  }\r
                                  SendTimeRemaining(&first, TRUE);\r
                                }\r
+#if 0\r
                                if (first.useColors) {\r
                                  SendToProgram("white\ngo\n", &first);\r
                                } else {\r
                                  SendToProgram("go\n", &first);\r
                                }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos\r
+#endif\r
                                first.maybeThinking = TRUE;\r
                            } else {\r
                                if (first.usePlayother) {\r
@@ -2425,11 +2785,18 @@ read_from_ics(isr, closure, data, count, error)
                                  }\r
                                  SendTimeRemaining(&first, FALSE);\r
                                }\r
+#if 0\r
                                if (first.useColors) {\r
                                  SendToProgram("black\ngo\n", &first);\r
                                } else {\r
                                  SendToProgram("go\n", &first);\r
                                }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("black\n", &first);\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);\r
+#endif\r
                                first.maybeThinking = TRUE;\r
                            } else {\r
                                if (first.usePlayother) {\r
@@ -2482,6 +2849,17 @@ read_from_ics(isr, closure, data, count, error)
                  default:\r
                    break;\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)", bookHit);\r
+\r
+                   strcpy(bookMove, "move ");\r
+                   strcat(bookMove, bookHit);\r
+                   HandleMachineMove(bookMove, &first);\r
+               }\r
                continue;\r
            }\r
            \r
@@ -2568,7 +2946,7 @@ read_from_ics(isr, closure, data, count, error)
                    if (forwardMostMove > backwardMostMove) {\r
                        currentMove = --forwardMostMove;\r
                        DisplayMove(currentMove - 1); /* before DMError */\r
-                       DisplayMoveError("Illegal move (rejected by ICS)");\r
+                       DisplayMoveError(_("Illegal move (rejected by ICS)"));\r
                        DrawPosition(FALSE, boards[currentMove]);\r
                        SwitchClocks();\r
                        DisplayBothClocks();\r
@@ -2710,6 +3088,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
@@ -2722,6 +3101,11 @@ read_from_ics(isr, closure, data, count, error)
                if (gameMode == IcsObserving &&\r
                    atoi(star_match[0]) == ics_gamenum)\r
                  {\r
+                      /* icsEngineAnalyze */\r
+                      if (appData.icsEngineAnalyze) {\r
+                            ExitAnalyzeMode();\r
+                            ModeHighlight();\r
+                      }\r
                      StopClocks();\r
                      gameMode = IcsIdle;\r
                      ics_gamenum = -1;\r
@@ -2802,7 +3186,8 @@ read_from_ics(isr, closure, data, count, error)
                    started = STARTED_NONE;\r
                    parse[parse_pos] = NULLCHAR;\r
                    if (appData.debugMode)\r
-                     fprintf(debugFP, "Parsing holdings: %s\n", parse);\r
+                      fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",\r
+                                                        parse, currentMove);\r
                    if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
                        gamenum == ics_gamenum) {\r
                        if (gameInfo.variant == VariantNormal) {\r
@@ -2811,22 +3196,7 @@ read_from_ics(isr, closure, data, count, error)
                            * to move the position two files to the right to\r
                            * create room for them!\r
                            */\r
-                          int i, j;\r
-                          if(gameInfo.holdingsWidth == 0) /* to be sure */\r
-                          for(i=0; i<BOARD_HEIGHT; i++)\r
-                            for(j=BOARD_RGHT-1; j>=0; j--)\r
-                              boards[currentMove][i][j+2] = boards[currentMove][i][j];\r
-\r
-  if (appData.debugMode) {\r
-    fprintf(debugFP, "Switch board to Crazy\n");\r
-    setbuf(debugFP, NULL);\r
-  }\r
-                         gameInfo.variant = VariantCrazyhouse; /*temp guess*/\r
-                          gameInfo.boardWidth  = 8;  /* [HGM] guess board size as well */\r
-                          gameInfo.boardHeight = 8;\r
-                          gameInfo.holdingsSize = 5;\r
-                          gameInfo.holdingsWidth = 2;\r
-                          InitDrawingSizes(-2, 0);\r
+                          VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */\r
                          /* Get a move list just to see the header, which\r
                             will tell us whether this is really bug or zh */\r
                          if (ics_getting_history == H_FALSE) {\r
@@ -2862,7 +3232,7 @@ read_from_ics(isr, closure, data, count, error)
                                    gameInfo.black, black_holding);\r
                        }\r
 \r
-                       DrawPosition(FALSE, NULL);\r
+                        DrawPosition(FALSE, boards[currentMove]);\r
                        DisplayTitle(str);\r
                    }\r
                    /* Suppress following prompt */\r
@@ -2877,11 +3247,12 @@ read_from_ics(isr, closure, data, count, error)
            i++;                /* skip unparsed character and loop back */\r
        }\r
        \r
-       if (started != STARTED_MOVES && started != STARTED_BOARD &&\r
+       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window\r
            started != STARTED_HOLDINGS && i > next_out) {\r
            SendToPlayer(&buf[next_out], i - next_out);\r
            next_out = i;\r
        }\r
+       suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above\r
        \r
        leftover_len = buf_len - leftover_start;\r
        /* if buffer ends with something we couldn't parse,\r
@@ -2889,9 +3260,9 @@ read_from_ics(isr, closure, data, count, error)
        \r
     } else if (count == 0) {\r
        RemoveInputSource(isr);\r
-        DisplayFatalError("Connection closed by ICS", 0, 0);\r
+        DisplayFatalError(_("Connection closed by ICS"), 0, 0);\r
     } else {\r
-       DisplayFatalError("Error reading from ICS", error, 1);\r
+       DisplayFatalError(_("Error reading from ICS"), error, 1);\r
     }\r
 }\r
 \r
@@ -2906,7 +3277,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
@@ -2921,10 +3292,10 @@ ParseBoard12(string)
      char *string;\r
 { \r
     GameMode newGameMode;\r
-    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 gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;\r
+    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;\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
@@ -2933,25 +3304,38 @@ 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
     newGame = FALSE;\r
 \r
     if (appData.debugMode)\r
-      fprintf(debugFP, "Parsing board: %s\n", string);\r
+      fprintf(debugFP, _("Parsing board: %s\n"), string);\r
 \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
-       sprintf(str, "Failed to parse board string:\n\"%s\"", string);\r
+    if (n < 21) {\r
+       sprintf(str, _("Failed to parse board string:\n\"%s\""), string);\r
        DisplayError(str, 0);\r
        return;\r
     }\r
@@ -2960,7 +3344,7 @@ ParseBoard12(string)
     moveNum = (moveNum - 1) * 2;\r
     if (to_play == 'B') moveNum++;\r
     if (moveNum >= MAX_MOVES) {\r
-      DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
                        0, 1);\r
       return;\r
     }\r
@@ -3022,7 +3406,7 @@ ParseBoard12(string)
        return;\r
       case H_GETTING_MOVES:\r
        /* Should not happen */\r
-       DisplayError("Error gathering move list: extra board", 0);\r
+       DisplayError(_("Error gathering move list: extra board"), 0);\r
        ics_getting_history = H_FALSE;\r
        return;\r
     }\r
@@ -3078,27 +3462,13 @@ ParseBoard12(string)
        timeIncrement = increment * 1000;\r
        movesPerSession = 0;\r
        gameInfo.timeControl = TimeControlTagValue();\r
-       gameInfo.variant = StringToVariant(gameInfo.event);\r
+        VariantSwitch(board, StringToVariant(gameInfo.event) );\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);\r
     fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));\r
     setbuf(debugFP, NULL);\r
   }\r
 \r
-        gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
-        gameInfo.boardWidth = gameInfo.boardHeight = 8;\r
-        switch(gameInfo.variant) {\r
-            case VariantShogi:\r
-            case VariantShowgi:\r
-              gameInfo.boardWidth = 9;  gameInfo.boardHeight = 9;\r
-              gameInfo.holdingsSize = 7;\r
-            case VariantBughouse:\r
-            case VariantCrazyhouse:\r
-              gameInfo.holdingsWidth = 2; break;\r
-            default:\r
-              gameInfo.holdingsWidth = gameInfo.holdingsSize = 0;\r
-        }\r
-        InitDrawingSizes(-2, 0);\r
         gameInfo.outOfBook = NULL;\r
        \r
        /* Do we have the ratings? */\r
@@ -3159,10 +3529,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
@@ -3172,7 +3545,58 @@ ParseBoard12(string)
     if (moveNum == 0) {\r
        startedFromSetupPosition =\r
          !CompareBoards(board, initialPosition);\r
-    }\r
+        if(startedFromSetupPosition)\r
+            initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */\r
+    }\r
+\r
+    /* [HGM] Set castling rights. Take the outermost Rooks,\r
+       to make it also work for FRC opening positions. Note that board12\r
+       is really defective for later FRC positions, as it has no way to\r
+       indicate which Rook can castle if they are on the same side of King.\r
+       For the initial position we grant rights to the outermost Rooks,\r
+       and remember thos rights, and we then copy them on positions\r
+       later in an FRC game. This means WB might not recognize castlings with\r
+       Rooks that have moved back to their original position as illegal,\r
+       but in ICS mode that is not its job anyway.\r
+    */\r
+    if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)\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
+        initialRights[0] = castlingRights[moveNum][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+            if(board[0][i] == WhiteRook) j = i;\r
+        initialRights[1] = castlingRights[moveNum][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
+            if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
+        initialRights[3] = castlingRights[moveNum][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+            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] == wKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
+        for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\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
+        if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;\r
+        r = castlingRights[moveNum][1] = initialRights[1];\r
+        if(board[0][r] != WhiteRook) castlingRights[moveNum][1] = -1;\r
+        r = castlingRights[moveNum][3] = initialRights[3];\r
+        if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][3] = -1;\r
+        r = castlingRights[moveNum][4] = initialRights[4];\r
+        if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][4] = -1;\r
+        /* wildcastle kludge: always assume King has rights */\r
+        r = castlingRights[moveNum][2] = initialRights[2];\r
+        r = castlingRights[moveNum][5] = initialRights[5];\r
+    }\r
+    /* [HGM] e.p. rights. Assume that ICS sends file number here? */\r
+    epStatus[moveNum] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;\r
+\r
     \r
     if (ics_getting_history == H_GOT_REQ_HEADER ||\r
        ics_getting_history == H_GOT_UNREQ_HEADER) {\r
@@ -3183,6 +3607,16 @@ ParseBoard12(string)
     \r
     /* Update currentMove and known move number limits */\r
     newMove = newGame || moveNum > forwardMostMove;\r
+\r
+    /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */\r
+    if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {\r
+        takeback = forwardMostMove - moveNum;\r
+        for (i = 0; i < takeback; i++) {\r
+             if (appData.debugMode) fprintf(debugFP, "take back move\n");\r
+             SendToProgram("undo\n", &first);\r
+        }\r
+    }\r
+\r
     if (newGame) {\r
        forwardMostMove = backwardMostMove = currentMove = moveNum;\r
        if (gameMode == IcsExamining && moveNum == 0) {\r
@@ -3236,8 +3670,13 @@ ParseBoard12(string)
        to canonical algebraic form. */\r
     if (moveNum > 0) {\r
   if (appData.debugMode) {\r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        fprintf(debugFP, "parseboard %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
     fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);\r
-    fprintf(debugFP, "board = %d-d x%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
+    fprintf(debugFP, "moveNum = %d\n", moveNum);\r
+    fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
     setbuf(debugFP, NULL);\r
   }\r
        if (moveNum <= backwardMostMove) {\r
@@ -3247,8 +3686,37 @@ ParseBoard12(string)
            strcat(parseList[moveNum - 1], " ");\r
            strcat(parseList[moveNum - 1], elapsed_time);\r
            moveList[moveNum - 1][0] = NULLCHAR;\r
-       } else if (ParseOneMove(move_str, moveNum - 1, &moveType,\r
-                               &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+       } else if (strcmp(move_str, "none") == 0) {\r
+           // [HGM] long SAN: swapped order; test for 'none' before parsing move\r
+           /* Again, we don't know what the board looked like;\r
+              this is really the start of the game. */\r
+           parseList[moveNum - 1][0] = NULLCHAR;\r
+           moveList[moveNum - 1][0] = NULLCHAR;\r
+           backwardMostMove = moveNum;\r
+           startedFromSetupPosition = TRUE;\r
+           fromX = fromY = toX = toY = -1;\r
+       } else {\r
+         // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. \r
+         //                 So we parse the long-algebraic move string in stead of the SAN move\r
+         int valid; char buf[MSG_SIZ], *prom;\r
+\r
+         // str looks something like "Q/a1-a2"; kill the slash\r
+         if(str[1] == '/') \r
+               sprintf(buf, "%c%s", str[0], str+2);\r
+         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.debugMode) \r
+                       fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
+               strcpy(move_str, buf);\r
+          }\r
+         valid = ParseOneMove(move_str, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar)\r
+              || ParseOneMove(buf, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar);\r
+         // end of long SAN patch\r
+         if (valid) {\r
            (void) CoordsToAlgebraic(boards[moveNum - 1],\r
                                     PosFlags(moveNum - 1), EP_UNKNOWN,\r
                                     fromY, fromX, toY, toX, promoChar,\r
@@ -3272,16 +3740,12 @@ ParseBoard12(string)
            /* currentMoveString is set as a side-effect of ParseOneMove */\r
            strcpy(moveList[moveNum - 1], currentMoveString);\r
            strcat(moveList[moveNum - 1], "\n");\r
-       } else if (strcmp(move_str, "none") == 0) {\r
-           /* Again, we don't know what the board looked like;\r
-              this is really the start of the game. */\r
-           parseList[moveNum - 1][0] = NULLCHAR;\r
-           moveList[moveNum - 1][0] = NULLCHAR;\r
-           backwardMostMove = moveNum;\r
-           startedFromSetupPosition = TRUE;\r
-           fromX = fromY = toX = toY = -1;\r
-       } else {\r
+         } else {\r
            /* Move from ICS was illegal!?  Punt. */\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+  }\r
 #if 0\r
            if (appData.testLegality && appData.debugMode) {\r
                sprintf(str, "Illegal move \"%s\" from ICS", move_str);\r
@@ -3293,6 +3757,7 @@ ParseBoard12(string)
            strcat(parseList[moveNum - 1], elapsed_time);\r
            moveList[moveNum - 1][0] = NULLCHAR;\r
            fromX = fromY = toX = toY = -1;\r
+         }\r
        }\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
@@ -3307,15 +3772,15 @@ ParseBoard12(string)
            if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||\r
                (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {\r
                if (moveList[moveNum - 1][0] == NULLCHAR) {\r
-                   sprintf(str, "Couldn't parse move \"%s\" from ICS",\r
+                   sprintf(str, _("Couldn't parse move \"%s\" from ICS"),\r
                            move_str);\r
                    DisplayError(str, 0);\r
                } else {\r
                    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
@@ -3329,9 +3794,10 @@ ParseBoard12(string)
                }\r
            } else if (gameMode == IcsObserving || gameMode == IcsExamining) {\r
              if (moveList[moveNum - 1][0] == NULLCHAR) {\r
-               sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);\r
+               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
@@ -3371,15 +3837,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
@@ -3399,6 +3878,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)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+#endif\r
 }\r
 \r
 void\r
@@ -3436,6 +3928,7 @@ SendMoveToProgram(moveNum, cps)
      ChessProgramState *cps;\r
 {\r
     char buf[MSG_SIZ];\r
+\r
     if (cps->useUsermove) {\r
       SendToProgram("usermove ", cps);\r
     }\r
@@ -3449,25 +3942,26 @@ SendMoveToProgram(moveNum, cps)
       } else {\r
        sprintf(buf, "%s\n", parseList[moveNum]);\r
       }\r
-      /* [HGM] decrement all digits to code ranks starting from 0 */\r
-      if(BOARD_HEIGHT>9) {\r
-          char *p = buf;\r
-          while(*p) { if(*p < 'A') (*p)--; p++; }\r
-      }\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((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
         int toY = moveList[moveNum][3] - ONE;\r
-       if((boards[currentMove][fromY][fromX] == WhiteKing \r
-           && boards[currentMove][toY][toX] == WhiteRook)\r
-          || (boards[currentMove][fromY][fromX] == BlackKing \r
-              && boards[currentMove][toY][toX] == BlackRook)) {\r
+        if((boards[moveNum][fromY][fromX] == WhiteKing \r
+            && boards[moveNum][toY][toX] == WhiteRook)\r
+           || (boards[moveNum][fromY][fromX] == BlackKing \r
+               && boards[moveNum][toY][toX] == BlackRook)) {\r
          if(toX > fromX) SendToProgram("O-O\n", cps);\r
          else SendToProgram("O-O-O\n", cps);\r
        }\r
@@ -3476,6 +3970,21 @@ SendMoveToProgram(moveNum, cps)
       else SendToProgram(moveList[moveNum], cps);\r
       /* End of additions by Tord */\r
     }\r
+\r
+    /* [HGM] setting up the opening has brought engine in force mode! */\r
+    /*       Send 'go' if we are in a mode where machine should play. */\r
+    if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) &&\r
+        (gameMode == TwoMachinesPlay   ||\r
+#ifdef ZIPPY\r
+         gameMode == IcsPlayingBlack     || gameMode == IcsPlayingWhite ||\r
+#endif\r
+         gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) {\r
+        SendToProgram("go\n", cps);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "(extra)\n");\r
+  }\r
+    }\r
+    setboardSpoiledMachineBlack = 0;\r
 }\r
 \r
 void\r
@@ -3487,7 +3996,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
 \r
     switch (moveType) {\r
       default:\r
-       sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",\r
+       sprintf(user_move, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"),\r
                (int)moveType, fromX, fromY, toX, toY);\r
        DisplayError(user_move + strlen("say "), 0);\r
        break;\r
@@ -3521,13 +4030,20 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       case BlackPromotionKnight:\r
       case WhitePromotionKing:\r
       case BlackPromotionKing:\r
-#ifdef FAIRY\r
       case WhitePromotionChancellor:\r
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
-#endif\r
-       sprintf(user_move, "%c%c%c%c=%c\n",\r
+        if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteFerz));\r
+        else if(gameInfo.variant == VariantGreat)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteMan));\r
+        else\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
                 AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
                PieceToChar(PromoPiece(moveType)));\r
        break;\r
@@ -3566,7 +4082,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
@@ -3587,15 +4102,49 @@ ProcessICSInitScript(f)
 void\r
 AlphaRank(char *move, int n)\r
 {\r
-    char *p = move, c;\r
-\r
-    if( !appData.alphaRank ) return;\r
+    char *p = move, c; int x, y;\r
 \r
-    while(c = *p) {\r
-        if(c>='0' && c<='9') *p += 'a'-'0'; else\r
-        if(c>='a' && c<='z') *p -= 'a'-'0';\r
-        p++;\r
-        if(--n < 1) break;\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
+    }\r
+\r
+    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
+    if(move[0]>='0' && move[0]<='9' &&\r
+       move[1]>='a' && move[1]<='x' &&\r
+       move[2]>='0' && move[2]<='9' &&\r
+       move[3]>='a' && move[3]<='x'    ) {\r
+        /* input move, Shogi -> normal */\r
+        move[0] = BOARD_RGHT  -1 - (move[0]-'1') + AAA;\r
+        move[1] = BOARD_HEIGHT-1 - (move[1]-'a') + ONE;\r
+        move[2] = BOARD_RGHT  -1 - (move[2]-'1') + AAA;\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
+    } else\r
+    if(move[1]=='@' &&\r
+       move[3]>='0' && move[3]<='9' &&\r
+       move[2]>='a' && move[2]<='x'    ) {\r
+        move[1] = '*';\r
+        move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
+    } else\r
+    if(\r
+       move[0]>='a' && move[0]<='x' &&\r
+       move[3]>='0' && move[3]<='9' &&\r
+       move[2]>='a' && move[2]<='x'    ) {\r
+         /* output move, normal -> Shogi */\r
+        move[0] = BOARD_RGHT - 1 - (move[0]-AAA) + '1';\r
+        move[1] = BOARD_HEIGHT-1 - (move[1]-ONE) + 'a';\r
+        move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
+        if(move[4] == PieceToChar(BlackQueen)) move[4] = '+';\r
+    }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "   out = '%s'\n", move);\r
     }\r
 }\r
 \r
@@ -3611,16 +4160,13 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     if (appData.debugMode) {\r
         fprintf(debugFP, "move to parse: %s\n", move);\r
     }\r
-    AlphaRank(move, 10);\r
     *moveType = yylexstr(moveNum, move);\r
 \r
     switch (*moveType) {\r
-#ifdef FAIRY\r
       case WhitePromotionChancellor:\r
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
-#endif\r
       case WhitePromotionQueen:\r
       case BlackPromotionQueen:\r
       case WhitePromotionRook:\r
@@ -3656,6 +4202,9 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
        *promoChar = currentMoveString[4];\r
         if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT ||\r
             *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) {\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Off-board move (%d,%d)-(%d,%d)%c, type = %d\n", *fromX, *fromY, *toX, *toY, *promoChar, *moveType);\r
+    }\r
            *fromX = *fromY = *toX = *toY = 0;\r
            return FALSE;\r
        }\r
@@ -3669,7 +4218,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
@@ -3687,6 +4236,9 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
       case BlackWins:\r
       case GameIsDrawn:\r
       default:\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Impossible move %s, type = %d\n", currentMoveString, *moveType);\r
+    }\r
        /* bug? */\r
        *fromX = *fromY = *toX = *toY = 0;\r
        *promoChar = NULLCHAR;\r
@@ -3710,6 +4262,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
@@ -3725,9 +4278,15 @@ static void ShuffleFRC( Board board )
     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][FindEmptySquare(board, 0)] = WhiteRook;\r
-    board[0][FindEmptySquare(board, 0)] = WhiteKing;\r
-    board[0][FindEmptySquare(board, 0)] = WhiteRook;\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
@@ -3770,21 +4329,185 @@ static void SetupFRC( Board board, int pos_index )
     board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
 \r
     /* Place rooks and king */\r
-    board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\r
-    board[0][ FindEmptySquare(board, 0) ] = WhiteKing;\r
-    board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\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
+// 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
+       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
-BOOL SetCharTable( char *table, const char * map )\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
@@ -3804,6 +4527,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
@@ -3816,6 +4575,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
@@ -3826,6 +4586,9 @@ InitPosition(redraw)
         }\r
 \r
         initialRulePlies = 0; /* 50-move counter start */\r
+\r
+        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
+        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
     }\r
 \r
     \r
@@ -3845,12 +4608,15 @@ 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
     case VariantShatranj:\r
       pieces = ShatranjArray;\r
       nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k"); \r
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
@@ -3858,25 +4624,43 @@ InitPosition(redraw)
       castlingRights[0][6] = initialRights[2] = 5;\r
       castlingRights[0][7] = initialRights[5] = 5;\r
       castlingRank[6] = 0;\r
-      castlingRank[6] = BOARD_HEIGHT-1;\r
-      startedFromSetupPosition = TRUE;\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
-      SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \r
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
       break;\r
     case VariantGothic:\r
       pieces = GothicArray;\r
       gameInfo.boardWidth = 10;\r
-      SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \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
+      SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk"); \r
       break;\r
     case VariantXiangqi:\r
       pieces = XiangqiArray;\r
       gameInfo.boardWidth  = 9;\r
       gameInfo.boardHeight = 10;\r
       nrCastlingRights = 0;\r
-      SetCharTable(pieceToChar, "PH.R.AKE.C.......ph.r.ake.c......."); \r
+      SetCharTable(pieceToChar, "PH.R.AE..K.C.ph.r.ae..k.c."); \r
       break;\r
     case VariantShogi:\r
       pieces = ShogiArray;\r
@@ -3884,47 +4668,52 @@ InitPosition(redraw)
       gameInfo.boardHeight = 9;\r
       gameInfo.holdingsSize = 7;\r
       nrCastlingRights = 0;\r
-      SetCharTable(pieceToChar, "PNBRLSG.........Kpnbrlsg.........k"); \r
-      break;\r
-    case VariantShowgi:\r
-      pieces = ShogiArray;\r
-      gameInfo.boardWidth  = 9;\r
-      gameInfo.boardHeight = 9;\r
-      gameInfo.holdingsSize = 7;\r
-      nrCastlingRights = 0;\r
-      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
-      SetCharTable(pieceToChar, "PNBRQFWEMOUHACG.Kpnbrlsgpnbrls...k"); \r
+      SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k"); \r
       break;\r
     case VariantCourier:\r
       pieces = CourierArray;\r
       gameInfo.boardWidth  = 12;\r
       nrCastlingRights = 0;\r
-      SetCharTable(pieceToChar, "PNBR.FWEM.......Kpnbr.fwem.......k"); \r
+      SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk"); \r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       break;\r
     case VariantKnightmate:\r
       pieces = KnightmateArray;\r
-      strcpy(pieceToChar, "P.BRQ...M.K......p.brq...m.k......"); \r
+      SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k."); \r
       break;\r
     case VariantFairy:\r
       pieces = fairyArray;\r
-      SetCharTable(pieceToChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk"); \r
+      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
+      break;\r
+    case VariantGreat:\r
+      pieces = GreatArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PN....E...S..HWGMKpn....e...s..hwgmk");\r
+      gameInfo.holdingsSize = 8;\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
+      SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k"); \r
       gameInfo.holdingsSize = 5;\r
       break;\r
     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
@@ -3943,7 +4732,7 @@ InitPosition(redraw)
     }\r
     if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;\r
     if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE)\r
-        DisplayFatalError("Recompile to support this BOARD_SIZE!", 0, 2);\r
+        DisplayFatalError(_("Recompile to support this BOARD_SIZE!"), 0, 2);\r
 \r
     pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */\r
     if(pawnRow < 1) pawnRow = 1;\r
@@ -3975,9 +4764,8 @@ InitPosition(redraw)
         }\r
         initialPosition[BOARD_HEIGHT-1][j] =  pieces[1][j-gameInfo.holdingsWidth];\r
     }\r
-    if( (gameInfo.variant == VariantShogi\r
-       ||gameInfo.variant == VariantShowgi\r
-                                         ) && !overrule ) {\r
+    if( (gameInfo.variant == VariantShogi) && !overrule ) {\r
+\r
             j=BOARD_LEFT+1;\r
             initialPosition[1][j] = WhiteBishop;\r
             initialPosition[BOARD_HEIGHT-2][j] = BlackRook;\r
@@ -3998,11 +4786,16 @@ InitPosition(redraw)
         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;\r
-\r
-        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
-        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
      }\r
 \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[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
+       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
+     }\r
+#if 0\r
     if(gameInfo.variant == VariantFischeRandom) {\r
       if( appData.defaultFrcPosition < 0 ) {\r
         ShuffleFRC( initialPosition );\r
@@ -4010,6 +4803,23 @@ InitPosition(redraw)
       else {\r
         SetupFRC( initialPosition, appData.defaultFrcPosition );\r
       }\r
+      startedFromSetupPosition = TRUE;\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
+          castlingRights[0][i] = initialRights[i] = fileRights[i];\r
+      startedFromSetupPosition = TRUE;\r
     }\r
 \r
     CopyBoard(boards[0], initialPosition);\r
@@ -4018,9 +4828,13 @@ InitPosition(redraw)
        oldy != gameInfo.boardHeight ||\r
        oldh != gameInfo.holdingsWidth\r
 #ifdef GOTHIC\r
-       || oldv == VariantGothic ||\r
+       || oldv == VariantGothic ||        // For licensing popups\r
        gameInfo.variant == VariantGothic\r
 #endif\r
+#ifdef FALCON\r
+       || oldv == VariantFalcon ||\r
+       gameInfo.variant == VariantFalcon\r
+#endif\r
                                          )\r
             InitDrawingSizes(-2 ,0);\r
 \r
@@ -4052,11 +4866,20 @@ 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
                     AAA + j, ONE + i);\r
+            if(message[0] == '+' || message[0] == '~') {\r
+                sprintf(message, "%c%c%c+\n",\r
+                        PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+                        AAA + j, ONE + i);\r
+            }\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
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -4064,12 +4887,21 @@ 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
            sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),\r
                     AAA + j, ONE + i);\r
+            if(message[0] == '+' || message[0] == '~') {\r
+                sprintf(message, "%c%c%c+\n",\r
+                        PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+                        AAA + j, ONE + i);\r
+            }\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
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -4077,6 +4909,7 @@ SendBoard(cps, moveNum)
     \r
       SendToProgram(".\n", cps);\r
     }\r
+    setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */\r
 }\r
 \r
 int\r
@@ -4124,7 +4957,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
@@ -4165,7 +4998,7 @@ OKToStartUserMove(x, y)
       case IcsPlayingBlack:\r
        if (appData.zippyPlay) return FALSE;\r
        if (white_piece) {\r
-           DisplayMoveError("You are playing Black");\r
+           DisplayMoveError(_("You are playing Black"));\r
            return FALSE;\r
        }\r
        break;\r
@@ -4174,18 +5007,18 @@ OKToStartUserMove(x, y)
       case IcsPlayingWhite:\r
        if (appData.zippyPlay) return FALSE;\r
        if (!white_piece) {\r
-           DisplayMoveError("You are playing White");\r
+           DisplayMoveError(_("You are playing White"));\r
            return FALSE;\r
        }\r
        break;\r
 \r
       case EditGame:\r
        if (!white_piece && WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return FALSE;\r
        }           \r
        if (white_piece && !WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return FALSE;\r
        }           \r
        if (cmailMsgLoaded && (currentMove < cmailOldMove)) {\r
@@ -4205,7 +5038,7 @@ OKToStartUserMove(x, y)
        if (appData.icsActive) return FALSE;\r
        if (!appData.noChessProgram) {\r
            if (!white_piece) {\r
-               DisplayMoveError("You are playing White");\r
+               DisplayMoveError(_("You are playing White"));\r
                return FALSE;\r
            }\r
        }\r
@@ -4213,11 +5046,11 @@ OKToStartUserMove(x, y)
        \r
       case Training:\r
        if (!white_piece && WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return FALSE;\r
        }           \r
        if (white_piece && !WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return FALSE;\r
        }           \r
        break;\r
@@ -4228,7 +5061,7 @@ OKToStartUserMove(x, y)
     }\r
     if (currentMove != forwardMostMove && gameMode != AnalyzeMode\r
        && gameMode != AnalyzeFile && gameMode != Training) {\r
-       DisplayMoveError("Displayed position is not current");\r
+       DisplayMoveError(_("Displayed position is not current"));\r
        return FALSE;\r
     }\r
     return TRUE;\r
@@ -4254,6 +5087,25 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
         return ImpossibleMove;\r
     }\r
 \r
+    /* [HGM] suppress all moves into holdings area and guard band */\r
+    if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )\r
+            return ImpossibleMove;\r
+\r
+    /* [HGM] <sameColor> moved to here from winboard.c */\r
+    /* note: this code seems to exist for filtering out some obviously illegal premoves */\r
+    pdown = boards[currentMove][fromY][fromX];\r
+    pup = boards[currentMove][toY][toX];\r
+    if (    gameMode != EditPosition &&\r
+            (WhitePawn <= pdown && pdown < BlackPawn &&\r
+             WhitePawn <= pup && pup < BlackPawn  ||\r
+             BlackPawn <= pdown && pdown < EmptySquare &&\r
+             BlackPawn <= pup && pup < EmptySquare \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
+         return ImpossibleMove;\r
+\r
     /* Check if the user is playing in turn.  This is complicated because we\r
        let the user "pick up" a piece before it is his turn.  So the piece he\r
        tried to pick up may have been captured by the time he puts it down!\r
@@ -4279,7 +5131,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
       case MachinePlaysWhite:\r
        /* User is moving for Black */\r
        if (WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
             return ImpossibleMove;\r
        }\r
        break;\r
@@ -4287,7 +5139,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
       case MachinePlaysBlack:\r
        /* User is moving for White */\r
        if (!WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
             return ImpossibleMove;\r
        }\r
        break;\r
@@ -4301,13 +5153,13 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
             (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {\r
            /* User is moving for Black */\r
            if (WhiteOnMove(currentMove)) {\r
-               DisplayMoveError("It is White's turn");\r
+               DisplayMoveError(_("It is White's turn"));\r
                 return ImpossibleMove;\r
            }\r
        } else {\r
            /* User is moving for White */\r
            if (!WhiteOnMove(currentMove)) {\r
-               DisplayMoveError("It is Black's turn");\r
+               DisplayMoveError(_("It is Black's turn"));\r
                 return ImpossibleMove;\r
            }\r
        }\r
@@ -4317,7 +5169,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
        /* User is moving for Black */\r
        if (WhiteOnMove(currentMove)) {\r
            if (!appData.premove) {\r
-               DisplayMoveError("It is White's turn");\r
+               DisplayMoveError(_("It is White's turn"));\r
            } else if (toX >= 0 && toY >= 0) {\r
                premoveToX = toX;\r
                premoveToY = toY;\r
@@ -4338,7 +5190,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
        /* User is moving for White */\r
        if (!WhiteOnMove(currentMove)) {\r
            if (!appData.premove) {\r
-               DisplayMoveError("It is Black's turn");\r
+               DisplayMoveError(_("It is Black's turn"));\r
            } else if (toX >= 0 && toY >= 0) {\r
                premoveToX = toX;\r
                premoveToY = toY;\r
@@ -4363,30 +5215,15 @@ 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
 \r
-    /* [HGM] suppress all moves into holdings area and guard band */\r
-    if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )\r
-            return ImpossibleMove;\r
-\r
-    /* [HGM] <sameColor> moved to here from winboard.c */\r
-    /* note: EditPosition already filtered out and performed! */\r
-    pdown = boards[currentMove][fromY][fromX];\r
-    pup = boards[currentMove][toY][toX];\r
-    if ( \r
-            (WhitePawn <= pdown && pdown < BlackPawn &&\r
-             WhitePawn <= pup && pup < BlackPawn) ||\r
-            (BlackPawn <= pdown && pdown < EmptySquare &&\r
-             BlackPawn <= pup && pup < EmptySquare)      )\r
-         return ImpossibleMove;\r
-\r
     /* [HGM] If move started in holdings, it means a drop */\r
     if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { \r
          if( pup != EmptySquare ) return ImpossibleMove;\r
@@ -4412,11 +5249,11 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
     /* [HGM] but possibly ignore an IllegalMove result */\r
     if (appData.testLegality) {\r
        if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
-           DisplayMoveError("Illegal move");\r
+           DisplayMoveError(_("Illegal move"));\r
             return ImpossibleMove;\r
        }\r
     }\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar);\r
     return moveType;\r
     /* [HGM] <popupFix> in stead of calling FinishMove directly, this\r
        function is made into one that returns an OK move type if FinishMove\r
@@ -4428,17 +5265,29 @@ 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
+if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar);\r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && 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
+if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar);\r
     /* [HGM] convert drag-and-drop piece drops to standard form */\r
     if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {\r
          moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
@@ -4447,7 +5296,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
     }\r
 \r
     /* [HGM] <popupFix> The following if has been moved here from\r
-       UserMoveEnevt(). Because it seemed to belon here (why not allow\r
+       UserMoveEvent(). Because it seemed to belon here (why not allow\r
        piece drops in training games?), and because it can only be\r
        performed after it is known to what we promote. */\r
     if (gameMode == Training) {\r
@@ -4477,12 +5326,12 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
          gameMode = PlayFromGameFile;\r
          ModeHighlight();\r
          SetTrainingModeOff();\r
-         DisplayInformation("End of game");\r
+         DisplayInformation(_("End of game"));\r
        }\r
       } else {\r
-       DisplayError("Incorrect move", 0);\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
@@ -4502,6 +5351,19 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
 \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
@@ -4509,6 +5371,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
@@ -4516,10 +5379,11 @@ 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
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar);\r
   /* Relay move to ICS or chess engine */\r
   if (appData.icsActive) {\r
     if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
@@ -4533,10 +5397,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
@@ -4573,6 +5438,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)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+  }\r
+  return 1;\r
 }\r
 \r
 void\r
@@ -4590,7 +5468,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
        to do anything in between, can call this routine the old way. \r
     */\r
     ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar);\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);\r
     if(moveType != ImpossibleMove)\r
         FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
 }\r
@@ -4619,6 +5497,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) fprintf(debugFP, "book hit = %s\n", bookHit ? bookHit : "(NULL)");\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
@@ -4631,13 +5553,20 @@ 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
     while (*message == '\007') message++;\r
 \r
     /*\r
+     * [HGM] engine debug message: ignore lines starting with '#' character\r
+     */\r
+    if(cps->debug && *message == '#') return;\r
+\r
+    /*\r
      * Look for book output\r
      */\r
     if (cps == &first && bookRequested) {\r
@@ -4729,10 +5658,15 @@ HandleMachineMove(message, cps)
            return;\r
        }\r
 \r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        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
+        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
-            sprintf(buf1, "Illegal move \"%s\" from %s machine",\r
+            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
@@ -4767,18 +5701,18 @@ 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
              case WhiteASideCastleFR:\r
              case BlackASideCastleFR:\r
-               toY++;\r
+               toX+=2;\r
                currentMoveString[2]++;\r
                break;\r
              case WhiteHSideCastleFR:\r
              case BlackHSideCastleFR:\r
-               toY--;\r
+               toX--;\r
                currentMoveString[2]--;\r
                break;\r
            }\r
@@ -4796,6 +5730,18 @@ HandleMachineMove(message, cps)
            first.initDone) {\r
          SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
          ics_user_moved = 1;\r
+         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
+                       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
@@ -4806,7 +5752,7 @@ HandleMachineMove(message, cps)
         /* [AS] Save move info and clear stats for next move */\r
         pvInfoList[ forwardMostMove ].score = programStats.score;\r
         pvInfoList[ forwardMostMove ].depth = programStats.depth;\r
-        pvInfoList[ forwardMostMove ].time = -1;\r
+        pvInfoList[ forwardMostMove ].time =  programStats.time; // [HGM] PGNtime: take time from engine stats\r
         ClearProgramStats();\r
         thinkOutput[0] = NULLCHAR;\r
         hiddenThinkOutputState = 0;\r
@@ -4842,12 +5788,12 @@ HandleMachineMove(message, cps)
             }\r
         }\r
 \r
-#ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines\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 || gameInfo.variant == VariantGreat) {\r
 \r
-        if( gameMode == TwoMachinesPlay && gameInfo.holdingsSize == 0) {\r
-            int count = 0, epFile = epStatus[forwardMostMove];\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
@@ -4857,48 +5803,60 @@ 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
+                   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
-                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
+                   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,\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
+                        case WhiteFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
+                             bishopsColor |= 1 << ((i^j)&1);\r
                              NrWB++; break;\r
                         case BlackKnight:\r
-                             NrWN++; break;\r
+                             NrBN++; break;\r
                         case BlackBishop:\r
+                        case BlackFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
+                             bishopsColor |= 1 << ((i^j)&1);\r
                              NrBB++; break;\r
                         case WhiteRook:\r
                              NrWR++; break;\r
                         case BlackRook:\r
                              NrBR++; break;\r
                         case WhiteQueen:\r
-                             NrWR++; break;\r
+                             NrWQ++; break;\r
                         case BlackQueen:\r
-                             NrBR++; break;\r
+                             NrBQ++; break;\r
                         case EmptySquare: \r
                              break;\r
                         case BlackPawn:\r
@@ -4907,22 +5865,46 @@ HandleMachineMove(message, cps)
                              PawnAdvance += m; NrPawns++;\r
                     }\r
                     NrPieces += (p != EmptySquare);\r
+                    NrW += ((int)p < (int)BlackPawn);\r
+                   if(gameInfo.variant == VariantXiangqi && \r
+                     (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {\r
+                       NrPieces--; // [HGM] XQ: do not count purely defensive pieces\r
+                        NrW -= ((int)p < (int)BlackPawn);\r
+                   }\r
                 }\r
 \r
-                if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2 )\r
-                {    /* KBK, KNK or KK */\r
+                if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&\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
 \r
                      /* always flag draws, for judging claims */\r
                      epStatus[forwardMostMove] = EP_INSUF_DRAW;\r
 \r
                      if(appData.materialDraws) {\r
                          /* but only adjudicate them if adjudication enabled */\r
+                        SendToProgram("force\n", cps->other); // suppress reply\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
                          return;\r
                      }\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
@@ -4932,12 +5914,16 @@ HandleMachineMove(message, cps)
                   ) ) {\r
                      if(--moveCount < 0 && appData.trivialDraws)\r
                      {    /* if the first 3 moves do not show a tactical win, declare draw */\r
+                         SendToProgram("force\n", cps->other); // suppress reply\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: Trivial draw", GE_XBOARD );\r
                           return;\r
                      }\r
                 } else moveCount = 6;\r
-\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
@@ -4946,20 +5932,26 @@ HandleMachineMove(message, cps)
            fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);\r
 \r
     }\r
+#endif\r
                 /* Check for rep-draws */\r
                 count = 0;\r
                 for(k = forwardMostMove-2;\r
                     k>=backwardMostMove && k>=forwardMostMove-100 &&\r
-                        epStatus[k] <= EP_NONE && epStatus[k+1] <= EP_NONE;\r
+                        epStatus[k] < EP_UNKNOWN &&\r
+                        epStatus[k+2] <= EP_NONE && epStatus[k+1] <= EP_NONE;\r
                     k-=2)\r
                 {   int rights=0;\r
+#if 0\r
     if (appData.debugMode) {\r
       fprintf(debugFP, " loop\n");\r
     }\r
+#endif\r
                     if(CompareBoards(boards[k], boards[forwardMostMove])) {\r
+#if 0\r
     if (appData.debugMode) {\r
       fprintf(debugFP, "match\n");\r
     }\r
+#endif\r
                         /* compare castling rights */\r
                         if( castlingRights[forwardMostMove][2] != castlingRights[k][2] &&\r
                              (castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) )\r
@@ -4977,6 +5969,7 @@ HandleMachineMove(message, cps)
                                 castlingRights[forwardMostMove][4] != castlingRights[k][4] )\r
                                    rights++;\r
                         }\r
+#if 0\r
     if (appData.debugMode) {\r
       for(i=0; i<nrCastlingRights; i++)\r
       fprintf(debugFP, " (%d,%d)", castlingRights[forwardMostMove][i], castlingRights[k][i]);\r
@@ -4985,10 +5978,47 @@ HandleMachineMove(message, cps)
     if (appData.debugMode) {\r
       fprintf(debugFP, " %d %d\n", rights, k);\r
     }\r
+#endif\r
                         if( rights == 0 && ++count > appData.drawRepeats-2\r
                             && appData.drawRepeats > 1) {\r
                              /* adjudicate after user-specified nr of repeats */\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                              ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                            if(gameInfo.variant == VariantXiangqi && appData.testLegality) { \r
+                               // [HGM] xiangqi: check for forbidden perpetuals\r
+                               int m, ourPerpetual = 1, hisPerpetual = 1;\r
+                               for(m=forwardMostMove; m>k; m-=2) {\r
+                                   if(MateTest(boards[m], PosFlags(m), \r
+                                                       EP_NONE, castlingRights[m]) != MT_CHECK)\r
+                                       ourPerpetual = 0; // the current mover did not always check\r
+                                   if(MateTest(boards[m-1], PosFlags(m-1), \r
+                                                       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
+                                   return;\r
+                               }\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
+                               // 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 checking 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
                         }\r
@@ -5010,30 +6040,60 @@ HandleMachineMove(message, cps)
                          epStatus[forwardMostMove] = EP_RULE_DRAW;\r
                          /* this is used to judge if draw claims are legal */\r
                 if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {\r
+                        SendToProgram("force\n", cps->other); // suppress reply\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: 50-move rule", GE_XBOARD );\r
                          return;\r
-                 }\r
-            }\r
+                }\r
 \r
+                /* if draw offer is pending, treat it as a draw claim\r
+                 * when draw condition present, to allow engines a way to\r
+                 * claim draws before making their move to avoid a race\r
+                 * condition occurring after their move\r
+                 */\r
+                if( cps->other->offeredDraw || cps->offeredDraw ) {\r
+                         char *p = NULL;\r
+                         if(epStatus[forwardMostMove] == EP_RULE_DRAW)\r
+                             p = "Draw claim: 50-move rule";\r
+                         if(epStatus[forwardMostMove] == EP_REP_DRAW)\r
+                             p = "Draw claim: 3-fold repetition";\r
+                         if(epStatus[forwardMostMove] == EP_INSUF_DRAW)\r
+                             p = "Draw claim: insufficient mating material";\r
+                         if( p != NULL ) {\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                             GameEnds( GameIsDrawn, p, GE_XBOARD );\r
+                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                             return;\r
+                         }\r
+                }\r
 \r
-        }\r
-#endif\r
-        if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
-           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
 \r
-            GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+               if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
+                   SendToProgram("force\n", cps->other); // suppress reply\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
 \r
-            return;\r
+                   GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+\r
+                   return;\r
+               }\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
+            if (cps->offeredDraw == 1 && cps->other->sendDrawOffers) {\r
+                SendToProgram("draw\n", cps->other);\r
+            }\r
            if (cps->other->sendTime) {\r
                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
@@ -5056,6 +6116,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)", bookHit);\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
@@ -5075,10 +6158,10 @@ HandleMachineMove(message, cps)
     if (!strncmp(message, "setboard ", 9)) {\r
         Board initial_position; int i;\r
 \r
-        GameEnds(GameIsDrawn, "Engine aborts game", GE_XBOARD);\r
+        GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);\r
 \r
         if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {\r
-            DisplayError("Bad FEN received from engine", 0);\r
+            DisplayError(_("Bad FEN received from engine"), 0);\r
             return ;\r
         } else {\r
            Reset(FALSE, FALSE);\r
@@ -5176,7 +6259,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
@@ -5220,7 +6303,7 @@ HandleMachineMove(message, cps)
            cps->analysisSupport = FALSE;\r
            cps->analyzing = FALSE;\r
            Reset(FALSE, TRUE);\r
-           sprintf(buf2, "%s does not support analysis", cps->tidy);\r
+           sprintf(buf2, _("%s does not support analysis"), cps->tidy);\r
            DisplayError(buf2, 0);\r
            return;\r
        }\r
@@ -5266,7 +6349,7 @@ HandleMachineMove(message, cps)
        DisplayMove(currentMove-1); /* before DisplayMoveError */\r
        SwitchClocks();\r
        DisplayBothClocks();\r
-       sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",\r
+       sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),\r
                parseList[currentMove], cps->which);\r
        DisplayMoveError(buf1);\r
        DrawPosition(FALSE, boards[currentMove]);\r
@@ -5298,7 +6381,7 @@ HandleMachineMove(message, cps)
        || (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
+       sprintf(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
@@ -5316,12 +6399,12 @@ HandleMachineMove(message, cps)
                (void) CoordsToAlgebraic(boards[forwardMostMove],\r
                                    PosFlags(forwardMostMove), EP_UNKNOWN,\r
                                    fromY, fromX, toY, toX, promoChar, buf1);\r
-               sprintf(buf2, "Hint: %s", buf1);\r
+               sprintf(buf2, _("Hint: %s"), buf1);\r
                DisplayInformation(buf2);\r
            } else {\r
                /* Hint move could not be parsed!? */\r
                sprintf(buf2,\r
-                       "Illegal hint move \"%s\"\nfrom %s chess program",\r
+                       _("Illegal hint move \"%s\"\nfrom %s chess program"),\r
                        buf1, cps->which);\r
                DisplayError(buf2, 0);\r
            }\r
@@ -5389,6 +6472,10 @@ HandleMachineMove(message, cps)
     } else if (strncmp(message, "Black resign", 12) == 0) {\r
         GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first));\r
        return;\r
+    } else if (strncmp(message, "White matches", 13) == 0 ||\r
+               strncmp(message, "Black matches", 13) == 0   ) {\r
+        /* [HGM] ignore GNUShogi noises */\r
+        return;\r
     } else if (strncmp(message, "White", 5) == 0 &&\r
               message[5] != '(' &&\r
               StrStr(message, "Black") == NULL) {\r
@@ -5486,18 +6573,23 @@ HandleMachineMove(message, cps)
        if (gameMode == TwoMachinesPlay) {\r
            if (cps->other->offeredDraw) {\r
                GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
-           } else {\r
+            /* [HGM] in two-machine mode we delay relaying draw offer      */\r
+            /* until after we also have move, to see if it is really claim */\r
+           }\r
+#if 0\r
+              else {\r
                if (cps->other->sendDrawOffers) {\r
                    SendToProgram("draw\n", cps->other);\r
                }\r
            }\r
+#endif\r
        } else if (gameMode == MachinePlaysWhite ||\r
                   gameMode == MachinePlaysBlack) {\r
          if (userOfferedDraw) {\r
-           DisplayInformation("Machine accepts your draw offer");\r
+           DisplayInformation(_("Machine accepts your draw offer"));\r
            GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
          } else {\r
-            DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");\r
+            DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree"));\r
          }\r
        }\r
     }\r
@@ -5506,10 +6598,12 @@ 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
+       u64 nodes; // [DM]\r
        char plyext;\r
        int ignore = FALSE;\r
        int prefixHint = FALSE;\r
@@ -5526,6 +6620,9 @@ HandleMachineMove(message, cps)
            break;\r
          case AnalyzeMode:\r
          case AnalyzeFile:\r
+            break;\r
+          case IcsObserving: /* [DM] icsEngineAnalyze */\r
+            if (!appData.icsEngineAnalyze) ignore = TRUE;\r
            break;\r
          case TwoMachinesPlay:\r
            if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {\r
@@ -5539,7 +6636,7 @@ HandleMachineMove(message, cps)
 \r
        if (!ignore) {\r
            buf1[0] = NULLCHAR;\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
                if (plyext != ' ' && plyext != '\t') {\r
@@ -5560,6 +6657,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
@@ -5613,7 +6720,8 @@ HandleMachineMove(message, cps)
                     strcat( thinkOutput, buf1 );\r
                 }\r
 \r
-               if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+                if (currentMove == forwardMostMove || gameMode == AnalyzeMode\r
+                        || gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
@@ -5640,12 +6748,13 @@ HandleMachineMove(message, cps)
 \r
                 SendProgramStatsToFrontend( cps, &programStats );\r
                 \r
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || \r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
                return;\r
-           } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",\r
+           } else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s",\r
                              &time, &nodes, &plylev, &mvleft,\r
                              &mvtot, mvname) >= 5) {\r
                /* The stat01: line is from Crafty (9.29+) in response\r
@@ -5700,7 +6809,8 @@ HandleMachineMove(message, cps)
                    strcat(programStats.movelist, p);\r
                 }\r
 \r
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||\r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
@@ -5789,12 +6899,16 @@ ParseGameHistory(game)
        yyboardindex = boardIndex;\r
        moveType = (ChessMove) yylex();\r
        switch (moveType) {\r
-#ifdef FAIRY\r
+         case IllegalMove:             /* maybe suicide chess, etc. */\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Illegal move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
           case WhitePromotionChancellor:\r
           case BlackPromotionChancellor:\r
           case WhitePromotionArchbishop:\r
           case BlackPromotionArchbishop:\r
-#endif\r
          case WhitePromotionQueen:\r
          case BlackPromotionQueen:\r
          case WhitePromotionRook:\r
@@ -5822,7 +6936,6 @@ ParseGameHistory(game)
           case BlackHSideCastleFR:\r
           case BlackASideCastleFR:\r
           /* POP Fabien */\r
-         case IllegalMove:             /* maybe suicide chess, etc. */\r
             fromX = currentMoveString[0] - AAA;\r
             fromY = currentMoveString[1] - ONE;\r
             toX = currentMoveString[2] - AAA;\r
@@ -5841,18 +6954,28 @@ ParseGameHistory(game)
            break;\r
          case AmbiguousMove:\r
            /* bug? */\r
-           sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);\r
+           sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Ambiguous move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
            DisplayError(buf, 0);\r
            return;\r
          case ImpossibleMove:\r
            /* bug? */\r
-           sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);\r
+           sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Impossible move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
            DisplayError(buf, 0);\r
            return;\r
          case (ChessMove) 0:   /* end of file */\r
            if (boardIndex < backwardMostMove) {\r
                /* Oops, gap.  How did that happen? */\r
-               DisplayError("Gap in move list", 0);\r
+               DisplayError(_("Gap in move list"), 0);\r
                return;\r
            }\r
            backwardMostMove =  blackPlaysFirst ? 1 : 0;\r
@@ -5937,19 +7060,68 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      int promoChar;\r
      Board board;\r
 {\r
-  ChessSquare captured = board[toY][toX], piece; 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
+           epStatus[p] = 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
+           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
+          }\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
+           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
+\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
+       }\r
+\r
+    }\r
 \r
   /* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
   if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\r
-       && promoChar != 0) promoChar = 'F';\r
+       && promoChar != 0) promoChar = PieceToChar(WhiteFerz);\r
          \r
   if (fromX == toX && fromY == toY) return;\r
 \r
   if (fromY == DROP_RANK) {\r
        /* must be first */\r
-       board[toY][toX] = (ChessSquare) fromX;\r
+        piece = board[toY][toX] = (ChessSquare) fromX;\r
   } else {\r
      piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */\r
+     king = piece < (int) BlackPawn ? WhiteKing : BlackKing; /* [HGM] Knightmate simplify testing for castling */\r
+     if(gameInfo.variant == VariantKnightmate)\r
+         king += (int) WhiteUnicorn - (int) WhiteKing;\r
 \r
     /* Code added by Tord: */\r
     /* FRC castling assumed when king captures friendly rook. */\r
@@ -5973,34 +7145,20 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       }\r
     /* End of code added by Tord */\r
 \r
-    } else if (initialPosition[fromY][fromX] == WhiteKing\r
-       && board[fromY][fromX] == WhiteKing\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
         && toY == fromY && toX > fromX+1) {\r
        board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = WhiteKing;\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
         board[fromY][BOARD_RGHT-1] = EmptySquare;\r
-        board[toY][toX-1] = WhiteRook;\r
-    } else if (initialPosition[fromY][fromX] == WhiteKing\r
-              && board[fromY][fromX] == WhiteKing\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
                && toY == fromY && toX < fromX-1) {\r
        board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = WhiteKing;\r
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
         board[fromY][BOARD_LEFT] = EmptySquare;\r
-        board[toY][toX+1] = WhiteRook;\r
-    } else if (fromY == 0 && fromX == 3\r
-              && board[fromY][fromX] == WhiteKing\r
-              && toY == 0 && toX == 5) {\r
-       board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = WhiteKing;\r
-       board[fromY][7] = EmptySquare;\r
-       board[toY][4] = WhiteRook;\r
-    } else if (fromY == 0 && fromX == 3\r
-              && board[fromY][fromX] == WhiteKing\r
-              && toY == 0 && toX == 1) {\r
-       board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = WhiteKing;\r
-       board[fromY][0] = EmptySquare;\r
-       board[toY][2] = WhiteRook;\r
     } else if (board[fromY][fromX] == WhitePawn\r
                && toY == BOARD_HEIGHT-1\r
                && gameInfo.variant != VariantXiangqi\r
@@ -6016,26 +7174,41 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[fromY][fromX] = EmptySquare;\r
     } 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 (initialPosition[fromY][fromX] == BlackKing\r
-              && board[fromY][fromX] == BlackKing\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
        board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = BlackKing;\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
         board[fromY][BOARD_RGHT-1] = EmptySquare;\r
-        board[toY][toX-1] = BlackRook;\r
-    } else if (initialPosition[fromY][fromX] == BlackKing\r
-              && board[fromY][fromX] == BlackKing\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
                && toY == fromY && toX < fromX-1) {\r
        board[fromY][fromX] = EmptySquare;\r
-       board[toY][toX] = BlackKing;\r
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
         board[fromY][BOARD_LEFT] = EmptySquare;\r
-        board[toY][toX+1] = BlackRook;\r
     } else if (fromY == 7 && fromX == 3\r
               && board[fromY][fromX] == BlackKing\r
               && toY == 7 && toX == 5) {\r
@@ -6065,12 +7238,27 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[fromY][fromX] = EmptySquare;\r
     } 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
@@ -6105,7 +7293,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 || gameInfo.variant == VariantGreat) { \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
@@ -6117,8 +7309,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
@@ -6128,8 +7319,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
@@ -6160,54 +7350,65 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
 {\r
-    forwardMostMove++;\r
+//    forwardMostMove++; // [HGM] bare: moved downstream\r
+\r
+    if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */\r
+        int timeLeft; static int lastLoadFlag=0; int king, piece;\r
+        piece = boards[forwardMostMove][fromY][fromX];\r
+        king = piece < (int) BlackPawn ? WhiteKing : BlackKing;\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            king += (int) WhiteUnicorn - (int) WhiteKing;\r
+        if(forwardMostMove == 0) {\r
+            if(blackPlaysFirst) \r
+                fprintf(serverMoves, "%s;", second.tidy);\r
+            fprintf(serverMoves, "%s;", first.tidy);\r
+            if(!blackPlaysFirst) \r
+                fprintf(serverMoves, "%s;", second.tidy);\r
+        } else fprintf(serverMoves, loadFlag|lastLoadFlag ? ":" : ";");\r
+        lastLoadFlag = loadFlag;\r
+        // print base move\r
+        fprintf(serverMoves, "%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+toY);\r
+        // print castling suffix\r
+        if( toY == fromY && piece == king ) {\r
+            if(toX-fromX > 1)\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_RGHT-1, ONE+fromY, AAA+toX-1,ONE+toY);\r
+            if(fromX-toX >1)\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_LEFT, ONE+fromY, AAA+toX+1,ONE+toY);\r
+        }\r
+        // e.p. suffix\r
+        if( (boards[forwardMostMove][fromY][fromX] == WhitePawn ||\r
+             boards[forwardMostMove][fromY][fromX] == BlackPawn   ) &&\r
+             boards[forwardMostMove][toY][toX] == EmptySquare\r
+             && fromX != toX )\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);\r
+        // promotion suffix\r
+        if(promoChar != NULLCHAR)\r
+                fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY);\r
+        if(!loadFlag) {\r
+            fprintf(serverMoves, "/%d/%d",\r
+               pvInfoList[forwardMostMove].depth, pvInfoList[forwardMostMove].score);\r
+            if(forwardMostMove+1 & 1) timeLeft = whiteTimeRemaining/1000;\r
+            else                      timeLeft = blackTimeRemaining/1000;\r
+            fprintf(serverMoves, "/%d", timeLeft);\r
+        }\r
+        fflush(serverMoves);\r
+    }\r
 \r
-    if (forwardMostMove >= MAX_MOVES) {\r
-      DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+    if (forwardMostMove+1 >= MAX_MOVES) {\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
                        0, 1);\r
       return;\r
     }\r
     SwitchClocks();\r
-    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
-    timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
-    if (commentList[forwardMostMove] != NULL) {\r
-       free(commentList[forwardMostMove]);\r
-       commentList[forwardMostMove] = NULL;\r
-    }\r
-    CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);\r
-    /* [HGM] compute & store e.p. status and castling rights for new position */\r
-    { int i, j;\r
-\r
-      epStatus[forwardMostMove] = EP_NONE;\r
-\r
-      if( boards[forwardMostMove][toY][toX] != EmptySquare ) \r
-           epStatus[forwardMostMove] = EP_CAPTURE;  \r
-\r
-      if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) {\r
-           epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
-           if( toY-fromY==2 &&\r
-               (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
-                toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == BlackPawn ) )\r
-              epStatus[forwardMostMove] = toX;\r
-      } else \r
-      if( boards[forwardMostMove][fromY][fromX] == BlackPawn ) {\r
-           epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
-           if( toY-fromY== -2 &&\r
-               (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
-                toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
-              epStatus[forwardMostMove] = toX;\r
-       }\r
-\r
-       for(i=0; i<nrCastlingRights; i++) {\r
-           castlingRights[forwardMostMove][i] = castlingRights[forwardMostMove-1][i];\r
-           if(castlingRights[forwardMostMove][i] == fromX && castlingRank[i] == fromY ||\r
-              castlingRights[forwardMostMove][i] == toX   && castlingRank[i] == toY   \r
-             ) castlingRights[forwardMostMove][i] = -1; // revoke for moved or captured piece\r
-\r
-       }\r
-\r
-    }\r
-    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);\r
+    timeRemaining[0][forwardMostMove+1] = whiteTimeRemaining;\r
+    timeRemaining[1][forwardMostMove+1] = blackTimeRemaining;\r
+    if (commentList[forwardMostMove+1] != NULL) {\r
+       free(commentList[forwardMostMove+1]);\r
+       commentList[forwardMostMove+1] = NULL;\r
+    }\r
+    CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);\r
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[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
        free(gameInfo.resultDetails);\r
@@ -6246,6 +7447,7 @@ ShowMove(fromX, fromY, toX, toY)
 {\r
     int instant = (gameMode == PlayFromGameFile) ?\r
        (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;\r
+    if(appData.noGUI) return;\r
     if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
        if (!instant) {\r
            if (forwardMostMove == currentMove + 1) {\r
@@ -6267,29 +7469,75 @@ 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)\r
+InitChessProgram(cps, setup)\r
      ChessProgramState *cps;\r
+     int setup; /* [HGM] needed to setup FRC opening position */\r
 {\r
-    char buf[MSG_SIZ], *b; int overruled;\r
+    char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;\r
     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
         /* [HGM] in protocol 1 we have to assume all variants valid */\r
-       sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);\r
+       sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy);\r
        DisplayFatalError(buf, 0, 1);\r
        return;\r
       }\r
-      b = buf;\r
+\r
       /* [HGM] make prefix for non-standard board size. Awkward testing... */\r
       overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
       if( gameInfo.variant == VariantXiangqi )\r
@@ -6298,26 +7546,46 @@ InitChessProgram(cps)
            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 )\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
+      if( gameInfo.variant == VariantGreat )\r
+           overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
 \r
       if(overruled) {\r
-           if (cps->protocolVersion != 1 && StrStr(cps->variants, "boardsize") == NULL) {\r
-             sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
-                  gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
-             DisplayFatalError(buf, 0, 1);\r
-             return;\r
+           sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
+                               gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name\r
+           /* [HGM] varsize: try first if this defiant size variant is specifically known */\r
+           if(StrStr(cps->variants, b) == NULL) { \r
+               // specific sized variant not known, check if general sizing allowed\r
+               if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best\r
+                   if(StrStr(cps->variants, "boardsize") == NULL) {\r
+                       sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
+                            gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
+                       DisplayFatalError(buf, 0, 1);\r
+                       return;\r
+                   }\r
+                   /* [HGM] here we really should compare with the maximum supported board size */\r
+               }\r
            }\r
-           /* [HGM] here we really should compare with the maximum supported board size */\r
-           sprintf(buf, "%dx%d+%d_", gameInfo.boardWidth,\r
-                              gameInfo.boardHeight, gameInfo.holdingsSize );\r
-           while(*b++ != '_');\r
-      }\r
-      sprintf(b, "variant %s\n", VariantName(gameInfo.variant));\r
+      } else sprintf(b, "%s", VariantName(gameInfo.variant));\r
+      sprintf(buf, "variant %s\n", b);\r
       SendToProgram(buf, cps);\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
     if (cps->sendICS) {\r
       sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
       SendToProgram(buf, cps);\r
@@ -6329,7 +7597,10 @@ InitChessProgram(cps)
                        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
@@ -6374,7 +7645,7 @@ StartChessProgram(cps)
     }\r
     \r
     if (err != 0) {\r
-       sprintf(buf, "Startup failure on '%s'", cps->program);\r
+       sprintf(buf, _("Startup failure on '%s'"), cps->program);\r
        DisplayFatalError(buf, err, 1);\r
        cps->pr = NoProc;\r
        cps->isr = NULL;\r
@@ -6384,6 +7655,8 @@ StartChessProgram(cps)
     cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);\r
     if (cps->protocolVersion > 1) {\r
       sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);\r
+      cps->nrOptions = 0; // [HGM] options: clear all engine-specific options\r
+      cps->comboCnt = 0;  //                and values of combo boxes\r
       SendToProgram(buf, cps);\r
     } else {\r
       SendToProgram("xboard\n", cps);\r
@@ -6395,13 +7668,13 @@ void
 TwoMachinesEventIfReady P((void))\r
 {\r
   if (first.lastPing != first.lastPong) {\r
-    DisplayMessage("", "Waiting for first chess program");\r
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+    DisplayMessage("", _("Waiting for first chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
     return;\r
   }\r
   if (second.lastPing != second.lastPong) {\r
-    DisplayMessage("", "Waiting for second chess program");\r
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+    DisplayMessage("", _("Waiting for second chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
     return;\r
   }\r
   ThawUI();\r
@@ -6411,14 +7684,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
@@ -6451,12 +7735,15 @@ GameEnds(result, resultDetails, whosays)
     int isIcsGame;\r
     char buf[MSG_SIZ];\r
 \r
+    if(endingGame) return; /* [HGM] crash: forbid recursion */\r
+    endingGame = 1;\r
+\r
     if (appData.debugMode) {\r
       fprintf(debugFP, "GameEnds(%d, %s, %d)\n",\r
              result, resultDetails ? resultDetails : "(null)", whosays);\r
     }\r
 \r
-    if (appData.icsActive && whosays == (GE_ENGINE || whosays >= GE_ENGINE1)) {\r
+    if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {\r
        /* If we are playing on ICS, the server decides when the\r
           game is over, but the engine can offer to draw, claim \r
           a draw, or resign. \r
@@ -6473,7 +7760,8 @@ GameEnds(result, resultDetails, whosays)
            }\r
         }\r
 #endif\r
-       return;\r
+       endingGame = 0; /* [HGM] crash */\r
+        return;\r
     }\r
 \r
     /* If we're loading the game from a file, stop */\r
@@ -6483,7 +7771,7 @@ GameEnds(result, resultDetails, whosays)
     }\r
 \r
     /* Cancel draw offers */\r
-   first.offeredDraw = second.offeredDraw = 0;\r
+    first.offeredDraw = second.offeredDraw = 0;\r
 \r
     /* If this is an ICS game, only ICS can really say it's done;\r
        if not, anyone can. */\r
@@ -6495,50 +7783,99 @@ 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( 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( (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] 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
-                      /* Draw that was not flagged by Xboard is false */\r
+                if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS\r
+                    && (forwardMostMove <= backwardMostMove ||\r
+                        epStatus[forwardMostMove-1] > EP_DRAWS ||\r
+                        (claimer=='b')==(forwardMostMove&1))\r
+                                                                                  ) {\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 || gameInfo.variant == VariantGreat)\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
+                       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
-    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
+\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 (resultDetails != NULL) {\r
            gameInfo.result = result;\r
            gameInfo.resultDetails = StrSave(resultDetails);\r
 \r
+           /* display last move only if game was not loaded from file */\r
+           if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
+               DisplayMove(currentMove - 1);\r
+    \r
+           if (forwardMostMove != 0) {\r
+               if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
+                   if (*appData.saveGameFile != NULLCHAR) {\r
+                       SaveGameToFile(appData.saveGameFile, TRUE);\r
+                   } else if (appData.autoSaveGames) {\r
+                       AutoSaveGame();\r
+                   }\r
+                   if (*appData.savePositionFile != NULLCHAR) {\r
+                       SavePositionToFile(appData.savePositionFile);\r
+                   }\r
+               }\r
+           }\r
+\r
            /* Tell program how game ended in case it is learning */\r
+            /* [HGM] Moved this to after saving the PGN, just in case */\r
+            /* engine died and we got here through time loss. In that */\r
+            /* case we will get a fatal error writing the pipe, which */\r
+            /* would otherwise lose us the PGN.                       */\r
+            /* [HGM] crash: not needed anymore, but doesn't hurt;     */\r
+            /* output during GameEnds should never be fatal anymore   */\r
            if (gameMode == MachinePlaysWhite ||\r
                gameMode == MachinePlaysBlack ||\r
                gameMode == TwoMachinesPlay ||\r
@@ -6556,23 +7893,6 @@ GameEnds(result, resultDetails, whosays)
                    SendToProgram(buf, &second);\r
                }\r
            }\r
-\r
-           /* display last move only if game was not loaded from file */\r
-           if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
-               DisplayMove(currentMove - 1);\r
-    \r
-           if (forwardMostMove != 0) {\r
-               if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
-                   if (*appData.saveGameFile != NULLCHAR) {\r
-                       SaveGameToFile(appData.saveGameFile, TRUE);\r
-                   } else if (appData.autoSaveGames) {\r
-                       AutoSaveGame();\r
-                   }\r
-                   if (*appData.savePositionFile != NULLCHAR) {\r
-                       SavePositionToFile(appData.savePositionFile);\r
-                   }\r
-               }\r
-           }\r
        }\r
 \r
        if (appData.icsActive) {\r
@@ -6630,7 +7950,8 @@ GameEnds(result, resultDetails, whosays)
     if (appData.noChessProgram) {\r
        gameMode = nextGameMode;\r
        ModeHighlight();\r
-       return;\r
+       endingGame = 0; /* [HGM] crash */\r
+        return;\r
     }\r
 \r
     if (first.reuse) {\r
@@ -6711,19 +8032,22 @@ 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
                 appData.matchPause = 10000; /* [HGM] make pause adjustable */\r
             ScheduleDelayedEvent(NextMatchGame, appData.matchPause);\r
+           endingGame = 0; /* [HGM] crash */\r
            return;\r
        } else {\r
            char buf[MSG_SIZ];\r
            gameMode = nextGameMode;\r
-           sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",\r
+           sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),\r
                    first.tidy, second.tidy,\r
                    first.matchWins, second.matchWins,\r
                    appData.matchGames - (first.matchWins + second.matchWins));\r
@@ -6735,6 +8059,7 @@ GameEnds(result, resultDetails, whosays)
       ExitAnalyzeMode();\r
     gameMode = nextGameMode;\r
     ModeHighlight();\r
+    endingGame = 0;  /* [HGM] crash */\r
 }\r
 \r
 /* Assumes program was just initialized (initString sent).\r
@@ -6750,9 +8075,20 @@ 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
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "feedMoves\n");\r
+    }\r
     }\r
     for (i = backwardMostMove; i < upto; i++) {\r
        SendMoveToProgram(i, cps);\r
@@ -6769,7 +8105,7 @@ ResurrectChessProgram()
     if (appData.noChessProgram || first.pr != NoProc) return;\r
     \r
     StartChessProgram(&first);\r
-    InitChessProgram(&first);\r
+    InitChessProgram(&first, FALSE);\r
     FeedMovesToProgram(&first, currentMove);\r
 \r
     if (!first.sendTime) {\r
@@ -6780,8 +8116,8 @@ ResurrectChessProgram()
        timeRemaining[1][currentMove] = blackTimeRemaining;\r
     }\r
 \r
-    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&\r
-       first.analysisSupport) {\r
+    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||\r
+                appData.icsEngineAnalyze) && first.analysisSupport) {\r
       SendToProgram("analyze\n", &first);\r
       first.analyzing = TRUE;\r
     }\r
@@ -6800,7 +8136,6 @@ Reset(redraw, init)
        fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",\r
                redraw, init, gameMode);\r
     }\r
-\r
     pausing = pauseExamInvalid = FALSE;\r
     startedFromSetupPosition = blackPlaysFirst = FALSE;\r
     firstMove = TRUE;\r
@@ -6809,6 +8144,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
@@ -6818,6 +8155,7 @@ Reset(redraw, init)
     ics_gamenum = -1;\r
     white_holding[0] = black_holding[0] = NULLCHAR;\r
     ClearProgramStats();\r
+    opponentKibitzes = FALSE; // [HGM] kibitz: do not reserve space in engine-output window in zippy mode\r
     \r
     ResetFrontEnd();\r
     ClearHighlights();\r
@@ -6827,9 +8165,23 @@ Reset(redraw, init)
     alarmSounded = FALSE;\r
 \r
     GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+    if(appData.serverMovesName != NULL) {\r
+        /* [HGM] prepare to make moves file for broadcasting */\r
+        clock_t t = clock();\r
+        if(serverMoves != NULL) fclose(serverMoves);\r
+        serverMoves = fopen(appData.serverMovesName, "r");\r
+        if(serverMoves != NULL) {\r
+            fclose(serverMoves);\r
+            /* delay 15 sec before overwriting, so all clients can see end */\r
+            while(clock()-t < appData.serverPause*CLOCKS_PER_SEC);\r
+        }\r
+        serverMoves = fopen(appData.serverMovesName, "w");\r
+    }\r
+\r
     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
@@ -6843,7 +8195,9 @@ Reset(redraw, init)
     if (first.pr == NULL) {\r
        StartChessProgram(&first);\r
     }\r
-    if (init) InitChessProgram(&first);\r
+    if (init) {\r
+           InitChessProgram(&first, startedFromSetupPosition);\r
+    }\r
     DisplayTitle("");\r
     DisplayMessage("", "");\r
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
@@ -6910,9 +8264,8 @@ AutoPlayOneMove()
     SendMoveToProgram(currentMove++, &first);\r
     DisplayBothClocks();\r
     DrawPosition(FALSE, boards[currentMove]);\r
-    if (commentList[currentMove] != NULL) {\r
-       DisplayComment(currentMove - 1, commentList[currentMove]);\r
-    }\r
+    // [HGM] PV info: always display, routine tests if empty\r
+    DisplayComment(currentMove - 1, commentList[currentMove]);\r
     return TRUE;\r
 }\r
 \r
@@ -6964,6 +8317,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
@@ -7106,7 +8461,7 @@ LoadGameOneMove(readAhead)
        if (appData.testLegality) {\r
            if (appData.debugMode)\r
              fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);\r
-           sprintf(move, "Illegal move: %d.%s%s",\r
+           sprintf(move, _("Illegal move: %d.%s%s"),\r
                    (forwardMostMove / 2) + 1,\r
                    WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
            DisplayError(move, 0);\r
@@ -7126,7 +8481,7 @@ LoadGameOneMove(readAhead)
       case AmbiguousMove:\r
        if (appData.debugMode)\r
          fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);\r
-       sprintf(move, "Ambiguous move: %d.%s%s",\r
+       sprintf(move, _("Ambiguous move: %d.%s%s"),\r
                (forwardMostMove / 2) + 1,\r
                WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
        DisplayError(move, 0);\r
@@ -7136,8 +8491,8 @@ LoadGameOneMove(readAhead)
       default:\r
       case ImpossibleMove:\r
        if (appData.debugMode)\r
-         fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);\r
-       sprintf(move, "Illegal move: %d.%s%s",\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
        DisplayError(move, 0);\r
@@ -7149,7 +8504,7 @@ LoadGameOneMove(readAhead)
        if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {\r
            DrawPosition(FALSE, boards[currentMove]);\r
            DisplayBothClocks();\r
-           if (!appData.matchMode && commentList[currentMove] != NULL)\r
+            if (!appData.matchMode) // [HGM] PV info: routine tests if empty\r
              DisplayComment(currentMove - 1, commentList[currentMove]);\r
        }\r
        (void) StopLoadGameTimer();\r
@@ -7185,7 +8540,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
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        }\r
@@ -7197,7 +8552,7 @@ LoadGameFromFile(filename, n, title, useList)
     if (useList && n == 0) {\r
        int error = GameListBuild(f);\r
        if (error) {\r
-           DisplayError("Cannot build game list", error);\r
+           DisplayError(_("Cannot build game list"), error);\r
        } else if (!ListEmpty(&gameList) &&\r
                   ((ListGame *) gameList.tailPred)->number > 1) {\r
            GameListPopUp(f, title);\r
@@ -7286,7 +8641,7 @@ CmailLoadGame(f, gameNumber, title, useList)
     int retVal;\r
 \r
     if (gameNumber > nCmailGames) {\r
-       DisplayError("No more games in this message", 0);\r
+       DisplayError(_("No more games in this message"), 0);\r
        return FALSE;\r
     }\r
     if (f == lastLoadGameFP) {\r
@@ -7327,11 +8682,11 @@ ReloadGame(offset)
 {\r
     int gameNumber = lastLoadGameNumber + offset;\r
     if (lastLoadGameFP == NULL) {\r
-       DisplayError("No game has been loaded yet", 0);\r
+       DisplayError(_("No game has been loaded yet"), 0);\r
        return FALSE;\r
     }\r
     if (gameNumber <= 0) {\r
-       DisplayError("Can't back up any further", 0);\r
+       DisplayError(_("Can't back up any further"), 0);\r
        return FALSE;\r
     }\r
     if (cmailMsgLoaded) {\r
@@ -7360,6 +8715,7 @@ LoadGame(f, gameNumber, title, useList)
     int numPGNTags = 0;\r
     int err;\r
     GameMode oldGameMode;\r
+    VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */\r
 \r
     if (appData.debugMode) \r
        fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);\r
@@ -7386,7 +8742,7 @@ LoadGame(f, gameNumber, title, useList)
            gn = 1;\r
        }\r
        else {\r
-           DisplayError("Game number out of range", 0);\r
+           DisplayError(_("Game number out of range"), 0);\r
            return FALSE;\r
        }\r
     } else {\r
@@ -7397,7 +8753,7 @@ LoadGame(f, gameNumber, title, useList)
                gameNumber == 1) {\r
                gn = 1;\r
            } else {\r
-               DisplayError("Can't seek on game file", 0);\r
+               DisplayError(_("Can't seek on game file"), 0);\r
                return FALSE;\r
            }\r
        }\r
@@ -7409,7 +8765,6 @@ LoadGame(f, gameNumber, title, useList)
 \r
     yynewfile(f);\r
 \r
-\r
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
        sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
                lg->gameInfo.black);\r
@@ -7456,7 +8811,7 @@ LoadGame(f, gameNumber, title, useList)
                nCmailGames = CMAIL_MAX_GAMES - gn;\r
            } else {\r
                Reset(TRUE, TRUE);\r
-               DisplayError("Game not found in file", 0);\r
+               DisplayError(_("Game not found in file"), 0);\r
            }\r
            return FALSE;\r
 \r
@@ -7568,22 +8923,25 @@ LoadGame(f, gameNumber, title, useList)
        err = ParsePGNTag(yy_text, &gameInfo);\r
        if (!err) numPGNTags++;\r
 \r
+        /* [HGM] PGNvariant: automatically switch to variant given in PGN tag */\r
+        if(gameInfo.variant != oldVariant) {\r
+            startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */\r
+           InitPosition(TRUE);\r
+            oldVariant = gameInfo.variant;\r
+           if (appData.debugMode) \r
+             fprintf(debugFP, "New variant %d\n", (int) oldVariant);\r
+        }\r
+\r
+\r
        if (gameInfo.fen != NULL) {\r
          Board initial_position;\r
          startedFromSetupPosition = TRUE;\r
          if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {\r
            Reset(TRUE, TRUE);\r
-           DisplayError("Bad FEN position in file", 0);\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
            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
@@ -7598,6 +8956,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
@@ -7700,13 +9065,19 @@ LoadGame(f, gameNumber, title, useList)
     if (first.pr == NoProc) {\r
        StartChessProgram(&first);\r
     }\r
-    InitChessProgram(&first);\r
+    InitChessProgram(&first, FALSE);\r
     SendToProgram("force\n", &first);\r
     if (startedFromSetupPosition) {\r
        SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Load Game\n");\r
+    }\r
        DisplayBothClocks();\r
     }      \r
 \r
+    /* [HGM] server: flag to write setup moves in broadcast file as one */\r
+    loadFlag = appData.suppressLoadMoves;\r
+\r
     while (cm == Comment) {\r
        char *p;\r
        if (appData.debugMode) \r
@@ -7725,7 +9096,7 @@ LoadGame(f, gameNumber, title, useList)
     if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||\r
        cm == WhiteWins || cm == BlackWins ||\r
        cm == GameIsDrawn || cm == GameUnfinished) {\r
-       DisplayMessage("", "No moves in game");\r
+       DisplayMessage("", _("No moves in game"));\r
        if (cmailMsgLoaded) {\r
            if (appData.debugMode)\r
              fprintf(debugFP, "Setting flipView to %d.\n", FALSE);\r
@@ -7741,10 +9112,9 @@ LoadGame(f, gameNumber, title, useList)
        return TRUE;\r
     }\r
 \r
-    if (commentList[currentMove] != NULL) {\r
-      if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
+    // [HGM] PV info: routine tests if comment empty\r
+    if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
        DisplayComment(currentMove - 1, commentList[currentMove]);\r
-      }\r
     }\r
     if (!matchMode && appData.timeDelay != 0) \r
       DrawPosition(FALSE, boards[currentMove]);\r
@@ -7785,6 +9155,8 @@ LoadGame(f, gameNumber, title, useList)
 \r
     if (appData.debugMode) \r
        fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);\r
+\r
+    loadFlag = 0; /* [HGM] true game starts */\r
     return TRUE;\r
 }\r
 \r
@@ -7795,11 +9167,11 @@ ReloadPosition(offset)
 {\r
     int positionNumber = lastLoadPositionNumber + offset;\r
     if (lastLoadPositionFP == NULL) {\r
-       DisplayError("No position has been loaded yet", 0);\r
+       DisplayError(_("No position has been loaded yet"), 0);\r
        return FALSE;\r
     }\r
     if (positionNumber <= 0) {\r
-       DisplayError("Can't back up any further", 0);\r
+       DisplayError(_("Can't back up any further"), 0);\r
        return FALSE;\r
     }\r
     return LoadPosition(lastLoadPositionFP, positionNumber,\r
@@ -7821,7 +9193,7 @@ LoadPositionFromFile(filename, n, title)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -7856,13 +9228,13 @@ LoadPosition(f, positionNumber, title)
     strcpy(lastLoadPositionTitle, title);\r
     if (first.pr == NoProc) {\r
       StartChessProgram(&first);\r
-      InitChessProgram(&first);\r
+      InitChessProgram(&first, FALSE);\r
     }    \r
     pn = positionNumber;\r
     if (positionNumber < 0) {\r
        /* Negative position number means to seek to that byte offset */\r
        if (fseek(f, -positionNumber, 0) == -1) {\r
-           DisplayError("Can't seek on position file", 0);\r
+           DisplayError(_("Can't seek on position file"), 0);\r
            return FALSE;\r
        };\r
        pn = 1;\r
@@ -7873,16 +9245,17 @@ LoadPosition(f, positionNumber, title)
                positionNumber == 1) {\r
                pn = 1;\r
            } else {\r
-               DisplayError("Can't seek on position file", 0);\r
+               DisplayError(_("Can't seek on position file"), 0);\r
                return FALSE;\r
            }\r
        }\r
     }\r
     /* See if this file is FEN or old-style xboard */\r
     if (fgets(line, MSG_SIZ, f) == NULL) {\r
-       DisplayError("Position not found in file", 0);\r
+       DisplayError(_("Position not found in file"), 0);\r
        return FALSE;\r
     }\r
+#if 0\r
     switch (line[0]) {\r
       case '#':  case 'x':\r
       default:\r
@@ -7892,22 +9265,24 @@ LoadPosition(f, positionNumber, title)
       case 'P':  case 'N':  case 'B':  case 'R':  case 'Q':  case 'K':\r
       case '1':  case '2':  case '3':  case '4':  case '5':  case '6':\r
       case '7':  case '8':  case '9':\r
-#ifdef FAIRY\r
       case 'H':  case 'A':  case 'M':  case 'h':  case 'a':  case 'm':\r
       case 'E':  case 'F':  case 'G':  case 'e':  case 'f':  case 'g':\r
       case 'C':  case 'W':             case 'c':  case 'w': \r
-#endif\r
        fenMode = TRUE;\r
        break;\r
     }\r
+#else\r
+    // [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces\r
+    fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || CharToPiece(line[0]) != EmptySquare;\r
+#endif\r
 \r
     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
+               DisplayError(_("Position not found in file"), 0);\r
                return FALSE;\r
            }\r
            if (fenMode || line[0] == '#') pn--;\r
@@ -7916,7 +9291,7 @@ LoadPosition(f, positionNumber, title)
 \r
     if (fenMode) {\r
        if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {\r
-           DisplayError("Bad FEN position in file", 0);\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
            return FALSE;\r
        }\r
     } else {\r
@@ -7943,24 +9318,30 @@ 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
        strcpy(parseList[0], "");\r
        CopyBoard(boards[1], initial_position);\r
-       DisplayMessage("", "Black to play");\r
+       DisplayMessage("", _("Black to play"));\r
     } else {\r
        currentMove = forwardMostMove = backwardMostMove = 0;\r
-       DisplayMessage("", "White to play");\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
     if (positionNumber > 1) {\r
        sprintf(line, "%s %d", title, positionNumber);\r
@@ -8026,7 +9407,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
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -8210,55 +9591,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
-    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
@@ -8376,7 +9778,7 @@ SavePositionToFile(filename)
     } else {\r
        f = fopen(filename, "a");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -8506,17 +9908,17 @@ RegisterMove()
     }\r
 \r
     if (cmailOldMove == -1) {\r
-       DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);\r
+       DisplayError(_("You have edited the game history.\nUse Reload Same Game and make your move again."), 0);\r
        return FALSE;\r
     }\r
 \r
     if (currentMove > cmailOldMove + 1) {\r
-       DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);\r
+       DisplayError(_("You have entered too many moves.\nBack up to the correct position and try again."), 0);\r
        return FALSE;\r
     }\r
 \r
     if (currentMove < cmailOldMove) {\r
-       DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);\r
+       DisplayError(_("Displayed position is not current.\nStep forward to the correct position and try again."), 0);\r
        return FALSE;\r
     }\r
 \r
@@ -8564,7 +9966,7 @@ RegisterMove()
        cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;\r
        nCmailMovesRegistered ++;\r
     } else if (nCmailGames == 1) {\r
-       DisplayError("You have not made a move yet", 0);\r
+       DisplayError(_("You have not made a move yet"), 0);\r
        return FALSE;\r
     }\r
 \r
@@ -8585,18 +9987,18 @@ MailMoveEvent()
     char *arcDir;\r
 \r
     if (! cmailMsgLoaded) {\r
-       DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);\r
+       DisplayError(_("The cmail message is not loaded.\nUse Reload CMail Message and make your move again."), 0);\r
        return;\r
     }\r
 \r
     if (nCmailGames == nCmailResults) {\r
-       DisplayError("No unfinished games", 0);\r
+       DisplayError(_("No unfinished games"), 0);\r
        return;\r
     }\r
 \r
 #if CMAIL_PROHIBIT_REMAIL\r
     if (cmailMailedMove) {\r
-       sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName);\r
+       sprintf(msg, _("You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line."), appData.cmailGameName);\r
        DisplayError(msg, 0);\r
        return;\r
     }\r
@@ -8608,10 +10010,10 @@ MailMoveEvent()
        || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {\r
        sprintf(string, partCommandString,\r
                appData.debugMode ? " -v" : "", appData.cmailGameName);\r
-       commandOutput = popen(string, "rb");\r
+       commandOutput = popen(string, "r");\r
 \r
        if (commandOutput == NULL) {\r
-           DisplayError("Failed to invoke cmail", 0);\r
+           DisplayError(_("Failed to invoke cmail"), 0);\r
        } else {\r
            for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {\r
                nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);\r
@@ -8673,7 +10075,7 @@ CmailMsg()
     if (!cmailMsgLoaded) return "";\r
 \r
     if (cmailMailedMove) {\r
-       sprintf(cmailMsg, "Waiting for reply from opponent\n");\r
+       sprintf(cmailMsg, _("Waiting for reply from opponent\n"));\r
     } else {\r
        /* Create a list of games left */\r
        sprintf(string, "[");\r
@@ -8696,17 +10098,17 @@ CmailMsg()
            switch (nCmailGames) {\r
              case 1:\r
                sprintf(cmailMsg,\r
-                       "Still need to make move for game\n");\r
+                       _("Still need to make move for game\n"));\r
                break;\r
                \r
              case 2:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for both games\n");\r
+                       _("Still need to make moves for both games\n"));\r
                break;\r
                \r
              default:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for all %d games\n",\r
+                       _("Still need to make moves for all %d games\n"),\r
                        nCmailGames);\r
                break;\r
            }\r
@@ -8714,21 +10116,21 @@ CmailMsg()
            switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {\r
              case 1:\r
                sprintf(cmailMsg,\r
-                       "Still need to make a move for game %s\n",\r
+                       _("Still need to make a move for game %s\n"),\r
                        string);\r
                break;\r
                \r
              case 0:\r
                if (nCmailResults == nCmailGames) {\r
-                   sprintf(cmailMsg, "No unfinished games\n");\r
+                   sprintf(cmailMsg, _("No unfinished games\n"));\r
                } else {\r
-                   sprintf(cmailMsg, "Ready to send mail\n");\r
+                   sprintf(cmailMsg, _("Ready to send mail\n"));\r
                }\r
                break;\r
                \r
              default:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for games %s\n",\r
+                       _("Still need to make moves for games %s\n"),\r
                        string);\r
            }\r
        }\r
@@ -8751,8 +10153,6 @@ ResetGameEvent()
     }\r
 }\r
 \r
-static int exiting = 0;\r
-\r
 void\r
 ExitEvent(status)\r
      int status;\r
@@ -8775,8 +10175,10 @@ ExitEvent(status)
     if (icsPR != NoProc) {\r
       DestroyChildProcess(icsPR, TRUE);\r
     }\r
+#if 0\r
     /* Save game if resource set and not already saved by GameEnds() */\r
-    if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {\r
+    if ((gameInfo.resultDetails == NULL || errorExitFlag )\r
+                             && forwardMostMove > 0) {\r
       if (*appData.saveGameFile != NULLCHAR) {\r
        SaveGameToFile(appData.saveGameFile, TRUE);\r
       } else if (appData.autoSaveGames) {\r
@@ -8787,6 +10189,17 @@ ExitEvent(status)
       }\r
     }\r
     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 ? "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
+    if(endingGame) { int count = 0;\r
+        if(appData.debugMode) fprintf(debugFP, "ExitEvent() during GameEnds(), wait\n");\r
+        while(endingGame && count++ < 10) DoSleep(1);\r
+        if(appData.debugMode && endingGame) fprintf(debugFP, "GameEnds() seems stuck, proceed exiting\n");\r
+    }\r
 \r
     /* Kill off chess programs */\r
     if (first.pr != NoProc) {\r
@@ -8885,9 +10298,9 @@ EditCommentEvent()
     char title[MSG_SIZ];\r
 \r
     if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {\r
-       strcpy(title, "Edit comment");\r
+       strcpy(title, _("Edit comment"));\r
     } else {\r
-       sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,\r
+       sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,\r
                WhiteOnMove(currentMove - 1) ? " " : ".. ",\r
                parseList[currentMove - 1]);\r
     }\r
@@ -8911,17 +10324,19 @@ AnalyzeModeEvent()
       return;\r
 \r
     if (gameMode != AnalyzeFile) {\r
-       EditGameEvent();\r
-       if (gameMode != EditGame) return;\r
+        if (!appData.icsEngineAnalyze) {\r
+               EditGameEvent();\r
+               if (gameMode != EditGame) return;\r
+        }\r
        ResurrectChessProgram();\r
        SendToProgram("analyze\n", &first);\r
        first.analyzing = TRUE;\r
        /*first.maybeThinking = TRUE;*/\r
        first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
-       AnalysisPopUp("Analysis",\r
-                     "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
     }\r
-    gameMode = AnalyzeMode;\r
+    if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode;\r
     pausing = FALSE;\r
     ModeHighlight();\r
     SetGameInfo();\r
@@ -8945,8 +10360,8 @@ AnalyzeFileEvent()
        first.analyzing = TRUE;\r
        /*first.maybeThinking = TRUE;*/\r
        first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
-       AnalysisPopUp("Analysis",\r
-                     "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
     }\r
     gameMode = AnalyzeFile;\r
     pausing = FALSE;\r
@@ -8962,6 +10377,7 @@ void
 MachineWhiteEvent()\r
 {\r
     char buf[MSG_SIZ];\r
+    char *bookHit = NULL;\r
 \r
     if (appData.noChessProgram || (gameMode == MachinePlaysWhite))\r
       return;\r
@@ -8978,7 +10394,7 @@ MachineWhiteEvent()
         EditPositionDone();\r
 \r
     if (!WhiteOnMove(currentMove)) {\r
-       DisplayError("It is not White's turn", 0);\r
+       DisplayError(_("It is not White's turn"), 0);\r
        return;\r
     }\r
   \r
@@ -8990,6 +10406,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
@@ -9007,10 +10427,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
@@ -9018,6 +10437,19 @@ MachineWhiteEvent()
     if (appData.autoFlipView && !flipView) {\r
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\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)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
     }\r
 }\r
 \r
@@ -9025,6 +10457,7 @@ void
 MachineBlackEvent()\r
 {\r
     char buf[MSG_SIZ];\r
+   char *bookHit = NULL;\r
 \r
     if (appData.noChessProgram || (gameMode == MachinePlaysBlack))\r
        return;\r
@@ -9041,7 +10474,7 @@ MachineBlackEvent()
         EditPositionDone();\r
 \r
     if (WhiteOnMove(currentMove)) {\r
-       DisplayError("It is not Black's turn", 0);\r
+       DisplayError(_("It is not Black's turn"), 0);\r
        return;\r
     }\r
     \r
@@ -9070,10 +10503,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
@@ -9081,6 +10513,18 @@ MachineBlackEvent()
     if (appData.autoFlipView && flipView) {\r
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\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)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
     }\r
 }\r
 \r
@@ -9113,6 +10557,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
@@ -9122,7 +10567,7 @@ TwoMachinesEvent P((void))
       case MachinePlaysWhite:\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
-           DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
            return;\r
        }\r
        /* fall through */\r
@@ -9154,16 +10599,19 @@ TwoMachinesEvent P((void))
        } else {\r
          /* kludge: allow timeout for initial "feature" command */\r
          FreezeUI();\r
-         DisplayMessage("", "Starting second chess program");\r
+         DisplayMessage("", _("Starting second chess program"));\r
          ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);\r
        }\r
        return;\r
     }\r
     DisplayMessage("", "");\r
-    InitChessProgram(&second);\r
+    InitChessProgram(&second, FALSE);\r
     SendToProgram("force\n", &second);\r
     if (startedFromSetupPosition) {\r
        SendBoard(&second, backwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Two Machines\n");\r
+    }\r
     }\r
     for (i = backwardMostMove; i < forwardMostMove; i++) {\r
        SendMoveToProgram(i, &second);\r
@@ -9192,8 +10640,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
@@ -9206,11 +10654,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)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
 }\r
 \r
 void\r
@@ -9219,7 +10680,7 @@ TrainingEvent()
     if (gameMode == Training) {\r
       SetTrainingModeOff();\r
       gameMode = PlayFromGameFile;\r
-      DisplayMessage("", "Training mode off");\r
+      DisplayMessage("", _("Training mode off"));\r
     } else {\r
       gameMode = Training;\r
       animateTraining = appData.animate;\r
@@ -9227,10 +10688,10 @@ TrainingEvent()
       /* make sure we are not already at the end of the game */\r
       if (currentMove < forwardMostMove) {\r
        SetTrainingModeOn();\r
-       DisplayMessage("", "Training mode on");\r
+       DisplayMessage("", _("Training mode on"));\r
       } else {\r
        gameMode = PlayFromGameFile;\r
-       DisplayError("Already at end of game", 0);\r
+       DisplayError(_("Already at end of game"), 0);\r
       }\r
     }\r
     ModeHighlight();\r
@@ -9311,13 +10772,13 @@ EditGameEvent()
        break;\r
       case IcsPlayingBlack:\r
       case IcsPlayingWhite:\r
-       DisplayError("Warning: You are still playing a game", 0);\r
+       DisplayError(_("Warning: You are still playing a game"), 0);\r
        break;\r
       case IcsObserving:\r
-       DisplayError("Warning: You are still observing a game", 0);\r
+       DisplayError(_("Warning: You are still observing a game"), 0);\r
        break;\r
       case IcsExamining:\r
-       DisplayError("Warning: You are still examining a game", 0);\r
+       DisplayError(_("Warning: You are still examining a game"), 0);\r
        break;\r
       case IcsIdle:\r
        break;\r
@@ -9387,6 +10848,12 @@ EditPositionEvent()
 void\r
 ExitAnalyzeMode()\r
 {\r
+    /* [DM] icsEngineAnalyze - possible call from other functions */\r
+    if (appData.icsEngineAnalyze) {\r
+        appData.icsEngineAnalyze = FALSE;\r
+\r
+        DisplayMessage("",_("Close ICS engine analyze..."));\r
+    }\r
     if (first.analysisSupport && first.analyzing) {\r
       SendToProgram("exit\n", &first);\r
       first.analyzing = FALSE;\r
@@ -9399,17 +10866,25 @@ void
 EditPositionDone()\r
 {\r
     startedFromSetupPosition = TRUE;\r
-    InitChessProgram(&first);\r
+    InitChessProgram(&first, FALSE);\r
     SendToProgram("force\n", &first);\r
     if (blackPlaysFirst) {\r
        strcpy(moveList[0], "");\r
        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
     SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "EditPosDone\n");\r
+    }\r
     DisplayTitle("");\r
     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
     timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
@@ -9505,7 +10980,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
@@ -9514,7 +10990,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
@@ -9543,20 +11019,18 @@ EditPositionMenuEvent(selection, x, y)
        break;\r
 \r
       case PromotePiece:\r
-        if(piece >= (int)WhitePawn && piece < (int)WhiteWazir ||\r
-           piece >= (int)BlackPawn && piece < (int)BlackWazir   ) {\r
+        if(piece >= (int)WhitePawn && piece < (int)WhiteMan ||\r
+           piece >= (int)BlackPawn && piece < (int)BlackMan   ) {\r
             selection = (ChessSquare) (PROMOTED piece);\r
-        } else if(piece == EmptySquare) selection = WhiteWazir;\r
+        } else if(piece == EmptySquare) selection = WhiteSilver;\r
         else selection = (ChessSquare)((int)piece - 1);\r
         goto defaultlabel;\r
 \r
       case DemotePiece:\r
-        if(piece >= (int)WhiteUnicorn && piece < (int)WhiteKing ||\r
-           piece >= (int)BlackUnicorn && piece < (int)BlackKing   ) {\r
+        if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||\r
+           piece > (int)BlackMan && piece <= (int)BlackKing   ) {\r
             selection = (ChessSquare) (DEMOTED piece);\r
-        } else if( piece == WhiteKing || piece == BlackKing )\r
-            selection = (ChessSquare)((int)piece - (int)WhiteKing + (int)WhiteMan);\r
-        else if(piece == EmptySquare) selection = BlackWazir;\r
+        } else if(piece == EmptySquare) selection = BlackSilver;\r
         else selection = (ChessSquare)((int)piece + 1);       \r
         goto defaultlabel;\r
 \r
@@ -9600,7 +11074,7 @@ DropMenuEvent(selection, x, y)
       case IcsPlayingWhite:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return;\r
        }\r
        moveType = WhiteDrop;\r
@@ -9608,7 +11082,7 @@ DropMenuEvent(selection, x, y)
       case IcsPlayingBlack:\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return;\r
        }\r
        moveType = BlackDrop;\r
@@ -9625,7 +11099,7 @@ DropMenuEvent(selection, x, y)
                                 + (int) BlackPawn - (int) WhitePawn);\r
     }\r
     if (boards[currentMove][y][x] != EmptySquare) {\r
-       DisplayMoveError("That square is occupied");\r
+       DisplayMoveError(_("That square is occupied"));\r
        return;\r
     }\r
 \r
@@ -9649,7 +11123,7 @@ AcceptEvent()
            GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
        } else {\r
-           DisplayError("There is no pending offer on this move", 0);\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
        }\r
     } else {\r
@@ -9675,7 +11149,7 @@ DeclineEvent()
            DisplayComment(cmailOldMove - 1, "Draw declined");\r
 #endif /*NOTDEF*/\r
        } else {\r
-           DisplayError("There is no pending offer on this move", 0);\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
        }\r
     } else {\r
        /* Not used for offers from chess program */\r
@@ -9711,7 +11185,7 @@ CallFlagEvent()
                else\r
                  GameEnds(BlackWins, "Black wins on time", GE_PLAYER);\r
            } else {\r
-               DisplayError("Your opponent is not out of time", 0);\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
            }\r
            break;\r
          case MachinePlaysBlack:\r
@@ -9722,7 +11196,7 @@ CallFlagEvent()
                else\r
                  GameEnds(WhiteWins, "White wins on time", GE_PLAYER);\r
            } else {\r
-               DisplayError("Your opponent is not out of time", 0);\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
            }\r
            break;\r
        }\r
@@ -9757,7 +11231,7 @@ DrawEvent()
            DisplayComment(currentMove - 1, offer);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;\r
        } else {\r
-           DisplayError("You must make your move before offering a draw", 0);\r
+           DisplayError(_("You must make your move before offering a draw"), 0);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
        }\r
     } else if (first.offeredDraw) {\r
@@ -9907,7 +11381,7 @@ ForwardInner(target)
     DisplayMove(currentMove - 1);\r
     DrawPosition(FALSE, boards[currentMove]);\r
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
-    if (commentList[currentMove] && !matchMode && gameMode != Training) {\r
+    if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty\r
        DisplayComment(currentMove - 1, commentList[currentMove]);\r
     }\r
 }\r
@@ -10010,9 +11484,8 @@ BackwardInner(target)
     DisplayMove(currentMove - 1);\r
     DrawPosition(full_redraw, boards[currentMove]);\r
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
-    if (commentList[currentMove] != NULL) {\r
-       DisplayComment(currentMove - 1, commentList[currentMove]);\r
-    }\r
+    // [HGM] PV info: routine tests if comment empty\r
+    DisplayComment(currentMove - 1, commentList[currentMove]);\r
 }\r
 \r
 void\r
@@ -10072,11 +11545,11 @@ void
 RevertEvent()\r
 {\r
     if (gameMode != IcsExamining) {\r
-       DisplayError("You are not examining a game", 0);\r
+       DisplayError(_("You are not examining a game"), 0);\r
        return;\r
     }\r
     if (pausing) {\r
-       DisplayError("You can't revert while pausing", 0);\r
+       DisplayError(_("You can't revert while pausing"), 0);\r
        return;\r
     }\r
     SendToICS(ics_prefix);\r
@@ -10090,7 +11563,7 @@ RetractMoveEvent()
       case MachinePlaysWhite:\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
-           DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
            return;\r
        }\r
        if (forwardMostMove < 2) return;\r
@@ -10130,14 +11603,14 @@ MoveNowEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("It is your turn", 0);\r
+           DisplayError(_("It is your turn"), 0);\r
            return;\r
        }\r
        cps = &first;\r
        break;\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("It is your turn", 0);\r
+           DisplayError(_("It is your turn"), 0);\r
            return;\r
        }\r
        cps = &first;\r
@@ -10187,19 +11660,19 @@ HintEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       case BeginningOfGame:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       default:\r
-       DisplayError("No hint available", 0);\r
+       DisplayError(_("No hint available"), 0);\r
        return;\r
     }\r
     SendToProgram("hint\n", &first);\r
@@ -10213,14 +11686,14 @@ BookEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       case BeginningOfGame:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
@@ -10299,6 +11772,7 @@ TidyProgramName(prog, host, buf)
     p = q;\r
     while (p >= prog && *p != '/' && *p != '\\') p--;\r
     p++;\r
+    if(p == prog && *p == '"') p++;\r
     if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;\r
     memcpy(buf, p, q - p);\r
     buf[q - p] = NULLCHAR;\r
@@ -10459,7 +11933,7 @@ AppendComment(index, text)
     int oldlen, len;\r
     char *old;\r
 \r
-    GetInfoFromComment( index, text );\r
+    text = GetInfoFromComment( index, text ); /* [HGM] PV time: strip PV info from comment */\r
 \r
     CrushCRs(text);\r
     while (*text == '\n') text++;\r
@@ -10497,12 +11971,15 @@ static char * FindStr( char * text, char * sub_text )
 }\r
 \r
 /* [AS] Try to extract PV info from PGN comment */\r
-void GetInfoFromComment( int index, char * text )\r
+/* [HGM] PV time: and then remove it, to prevent it appearing twice */\r
+char *GetInfoFromComment( int index, char * text )\r
 {\r
+    char * sep = text;\r
+\r
     if( text != NULL && index > 0 ) {\r
         int score = 0;\r
         int depth = 0;\r
-        int time = -1;\r
+        int time = -1, sec = 0, deci;\r
         char * s_eval = FindStr( text, "[%eval " );\r
         char * s_emt = FindStr( text, "[%emt " );\r
 \r
@@ -10512,11 +11989,11 @@ void GetInfoFromComment( int index, char * text )
 \r
             if( s_eval != NULL ) {\r
                 if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {\r
-                    return;\r
+                    return text;\r
                 }\r
 \r
                 if( delim != ']' ) {\r
-                    return;\r
+                    return text;\r
                 }\r
             }\r
 \r
@@ -10525,26 +12002,43 @@ void GetInfoFromComment( int index, char * text )
         }\r
         else {\r
             /* We expect something like: [+|-]nnn.nn/dd */\r
-            char * sep = strchr( text, '/' );\r
             int score_lo = 0;\r
 \r
+            sep = strchr( text, '/' );\r
             if( sep == NULL || sep < (text+4) ) {\r
-                return;\r
+                return text;\r
             }\r
 \r
-            if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {\r
-                return;\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
             }\r
 \r
             if( score_lo < 0 || score_lo >= 100 ) {\r
-                return;\r
+                return text;\r
             }\r
 \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
+            while( *++sep >= '0' && *sep <= '9'); // strip depth\r
+            if(time >= 0)\r
+            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
         if( depth <= 0 ) {\r
-            return;\r
+            return text;\r
         }\r
 \r
         if( time < 0 ) {\r
@@ -10553,8 +12047,9 @@ void 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
 \r
 void\r
@@ -10578,9 +12073,19 @@ SendToProgram(message, cps)
     \r
     count = strlen(message);\r
     outCount = OutputToProcess(cps->pr, message, count, &error);\r
-    if (outCount < count && !exiting) {\r
-       sprintf(buf, "Error writing to %s chess program", cps->which);\r
-       DisplayFatalError(buf, error, 1);\r
+    if (outCount < count && !exiting \r
+                         && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */\r
+       sprintf(buf, _("Error writing to %s chess program"), cps->which);\r
+        if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+            if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+                gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+                sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);\r
+            } else {\r
+                gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+            }\r
+            gameInfo.resultDetails = buf;\r
+        }\r
+        DisplayFatalError(buf, error, 1);\r
     }\r
 }\r
 \r
@@ -10600,13 +12105,22 @@ ReceiveFromProgram(isr, closure, message, count, error)
     if (count <= 0) {\r
        if (count == 0) {\r
            sprintf(buf,\r
-                   "Error: %s chess program (%s) exited unexpectedly",\r
+                   _("Error: %s chess program (%s) exited unexpectedly"),\r
                    cps->which, cps->program);\r
+        if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+                if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+                    gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+                    sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program);\r
+                } else {\r
+                    gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+                }\r
+                gameInfo.resultDetails = buf;\r
+            }\r
            RemoveInputSource(cps->isr);\r
            DisplayFatalError(buf, 0, 1);\r
        } else {\r
            sprintf(buf,\r
-                   "Error reading from %s chess program (%s)",\r
+                   _("Error reading from %s chess program (%s)"),\r
                    cps->which, cps->program);\r
            RemoveInputSource(cps->isr);\r
 \r
@@ -10618,7 +12132,6 @@ ReceiveFromProgram(isr, closure, message, count, error)
 \r
             DisplayFatalError(buf, error, 1);\r
        }\r
-       GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
        return;\r
     }\r
     \r
@@ -10628,12 +12141,37 @@ 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
+\r
+    /* [DM] if icsEngineAnalyze is active we block all whisper and kibitz output, because nobody want to see this */\r
+    if (appData.icsEngineAnalyze) {\r
+        if (strstr(message, "whisper") != NULL ||\r
+             strstr(message, "kibitz") != NULL || \r
+            strstr(message, "tellics") != NULL) return;\r
     }\r
+\r
     HandleMachineMove(message, cps);\r
 }\r
 \r
@@ -10645,13 +12183,18 @@ SendTimeControl(cps, mps, tc, inc, sd, st)
      long tc;\r
 {\r
     char buf[MSG_SIZ];\r
-    int seconds = (tc / 1000) % 60;\r
+    int seconds;\r
 \r
     if( timeControl_2 > 0 ) {\r
         if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {\r
             tc = timeControl_2;\r
         }\r
     }\r
+    tc  /= cps->timeOdds; /* [HGM] time odds: apply before telling engine */\r
+    inc /= cps->timeOdds;\r
+    st  /= cps->timeOdds;\r
+\r
+    seconds = (tc / 1000) % 60; /* [HGM] displaced to after applying odds */\r
 \r
     if (st > 0) {\r
       /* Set exact time per move, normally using st command */\r
@@ -10690,6 +12233,23 @@ 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
+       gameMode == BeginningOfGame || gameMode == MachinePlaysBlack)\r
+        return &second;\r
+    return &first;\r
 }\r
 \r
 void\r
@@ -10711,6 +12271,12 @@ SendTimeRemaining(cps, machineWhite)
        time = blackTimeRemaining / 10;\r
        otime = whiteTimeRemaining / 10;\r
     }\r
+    /* [HGM] translate opponent's time by time-odds factor */\r
+    otime = (otime * cps->other->timeOdds) / cps->timeOdds;\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);\r
+    }\r
+\r
     if (time <= 0) time = 1;\r
     if (otime <= 0) otime = 1;\r
     \r
@@ -10785,6 +12351,63 @@ StringFeature(p, name, loc, cps)
   return FALSE;\r
 }\r
 \r
+int \r
+ParseOption(Option *opt, ChessProgramState *cps)\r
+// [HGM] options: process the string that defines an engine option, and determine\r
+// name, type, default value, and allowed value range\r
+{\r
+       char *p, *q, buf[MSG_SIZ];\r
+       int n, min = (-1)<<31, max = 1<<31, def;\r
+\r
+       if(p = strstr(opt->name, " -spin ")) {\r
+           if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;\r
+           if(max < min) max = min; // enforce consistency\r
+           if(def < min) def = min;\r
+           if(def > max) def = max;\r
+           opt->value = def;\r
+           opt->min = min;\r
+           opt->max = max;\r
+           opt->type = Spin;\r
+       } else if(p = strstr(opt->name, " -string ")) {\r
+           opt->textValue = p+9;\r
+           opt->type = TextBox;\r
+       } else if(p = strstr(opt->name, " -check ")) {\r
+           if(sscanf(p, " -check %d", &def) < 1) return FALSE;\r
+           opt->value = (def != 0);\r
+           opt->type = CheckBox;\r
+       } else if(p = strstr(opt->name, " -combo ")) {\r
+           opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type\r
+           cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices\r
+           opt->value = n = 0;\r
+           while(q = StrStr(q, " /// ")) {\r
+               n++; *q = 0;    // count choices, and null-terminate each of them\r
+               q += 5;\r
+               if(*q == '*') { // remember default, which is marked with * prefix\r
+                   q++;\r
+                   opt->value = n;\r
+               }\r
+               cps->comboList[cps->comboCnt++] = q;\r
+           }\r
+           cps->comboList[cps->comboCnt++] = NULL;\r
+           opt->max = n + 1;\r
+           opt->type = ComboBox;\r
+       } else if(p = strstr(opt->name, " -button")) {\r
+           opt->type = Button;\r
+       } else if(p = strstr(opt->name, " -save")) {\r
+           opt->type = SaveButton;\r
+       } 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
+               sprintf(buf, "option %s", p);\r
+               if(p = strstr(buf, ",")) *p = 0;\r
+               strcat(buf, "\n");\r
+               SendToProgram(buf, cps);\r
+       }\r
+       return TRUE;\r
+}\r
+\r
 void\r
 FeatureDone(cps, val)\r
      ChessProgramState* cps;\r
@@ -10851,6 +12474,25 @@ 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
+    if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {\r
+       ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature\r
+       if(cps->nrOptions >= MAX_OPTIONS) {\r
+           cps->nrOptions--;\r
+           sprintf(buf, "%s engine has too many options\n", cps->which);\r
+           DisplayError(buf, 0);\r
+       }\r
+       continue;\r
+    }\r
+    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
+    /* End of additions by HGM */\r
+\r
     /* unknown feature: complain and skip */\r
     q = p;\r
     while (*q && *q != '=') q++;\r
@@ -10912,12 +12554,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
@@ -10929,7 +12592,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
@@ -10949,6 +12612,8 @@ DisplayMove(moveNumber)
     char res[MSG_SIZ];\r
     char cpThinkOutput[MSG_SIZ];\r
 \r
+    if(appData.noGUI) return; // [HGM] fast: suppress display of moves\r
+    \r
     if (moveNumber == forwardMostMove - 1 || \r
        gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
 \r
@@ -10986,7 +12651,7 @@ DisplayMove(moveNumber)
     } else {\r
        res[0] = NULLCHAR;\r
     }\r
-    \r
+\r
     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
        DisplayMessage(res, cpThinkOutput);\r
     } else {\r
@@ -11003,7 +12668,8 @@ DisplayAnalysisText(text)
 {\r
     char buf[MSG_SIZ];\r
 \r
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile \r
+               || appData.icsEngineAnalyze) {\r
        sprintf(buf, "Analysis (%s)", first.tidy);\r
        AnalysisPopUp(buf, text);\r
     }\r
@@ -11039,8 +12705,8 @@ DisplayAnalysis()
     } else {\r
         safeStrCpy( lst, programStats.movelist, sizeof(lst));\r
 \r
-       nps = (((double)programStats.nodes) /\r
-              (((double)programStats.time)/100.0));\r
+        nps = (u64ToDouble(programStats.nodes) /\r
+             ((double)programStats.time /100.0));\r
 \r
        cs = programStats.time % 100;\r
        s = programStats.time / 100;\r
@@ -11051,32 +12717,32 @@ DisplayAnalysis()
 \r
        if (programStats.moves_left > 0 && appData.periodicUpdates) {\r
          if (programStats.move_name[0] != NULLCHAR) {\r
-           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    programStats.nr_moves-programStats.moves_left,\r
                    programStats.nr_moves, programStats.move_name,\r
                    ((float)programStats.score)/100.0, lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
          } else {\r
-           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    programStats.nr_moves-programStats.moves_left,\r
                    programStats.nr_moves, ((float)programStats.score)/100.0,\r
                    lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
          }\r
        } else {\r
-           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    ((float)programStats.score)/100.0,\r
                    lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
        }\r
     }\r
     DisplayAnalysisText(buf);\r
@@ -11088,6 +12754,8 @@ DisplayComment(moveNumber, text)
      char *text;\r
 {\r
     char title[MSG_SIZ];\r
+    char buf[8000]; // comment can be long!\r
+    int score, depth;\r
 \r
     if( appData.autoDisplayComment ) {\r
         if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
@@ -11097,9 +12765,18 @@ DisplayComment(moveNumber, text)
                    WhiteOnMove(moveNumber) ? " " : ".. ",\r
                    parseList[moveNumber]);\r
         }\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
 }\r
 \r
 /* This routine sends a ^C interrupt to gnuchess, to awaken it if it\r
@@ -11150,9 +12827,9 @@ CheckFlags()
                }\r
            } else {\r
                if (blackFlag) {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
                } else {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("White's flag fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("White's flag fell"));\r
                    if (appData.autoCallFlag) {\r
                        GameEnds(BlackWins, "Black wins on time", GE_XBOARD);\r
                        return TRUE;\r
@@ -11172,9 +12849,9 @@ CheckFlags()
                }\r
            } else {\r
                if (whiteFlag) {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
                } else {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Black's flag fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Black's flag fell"));\r
                    if (appData.autoCallFlag) {\r
                        GameEnds(WhiteWins, "White wins on time", GE_XBOARD);\r
                        return TRUE;\r
@@ -11192,30 +12869,18 @@ CheckTimeControl()
     if (!appData.clockMode || appData.icsActive ||\r
        gameMode == PlayFromGameFile || forwardMostMove == 0) return;\r
 \r
-    if (timeIncrement >= 0) {\r
-       if (WhiteOnMove(forwardMostMove)) {\r
-           blackTimeRemaining += timeIncrement;\r
-       } else {\r
-           whiteTimeRemaining += timeIncrement;\r
-       }\r
-    }\r
     /*\r
-     * add time to clocks when time control is achieved\r
+     * add time to clocks when time control is achieved ([HGM] now also used for increment)\r
      */\r
-    if (movesPerSession) {\r
-      switch ((forwardMostMove + 1) % (movesPerSession * 2)) {\r
-      case 0:\r
+    if ( !WhiteOnMove(forwardMostMove) )\r
        /* White made time control */\r
-       whiteTimeRemaining += GetTimeControlForWhite();\r
-       break;\r
-      case 1:\r
+        whiteTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+        /* [HGM] time odds: correct new time quota for time odds! */\r
+                                            / WhitePlayer()->timeOdds;\r
+      else\r
        /* Black made time control */\r
-       blackTimeRemaining += GetTimeControlForBlack();\r
-       break;\r
-      default:\r
-       break;\r
-      }\r
-    }\r
+        blackTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+                                            / WhitePlayer()->other->timeOdds;\r
 }\r
 \r
 void\r
@@ -11322,9 +12987,9 @@ ResetClocks()
     (void) StopClockTimer();\r
     if (appData.icsActive) {\r
        whiteTimeRemaining = blackTimeRemaining = 0;\r
-    } else {\r
-       whiteTimeRemaining = GetTimeControlForWhite();\r
-        blackTimeRemaining = GetTimeControlForBlack();\r
+    } else { /* [HGM] correct new time quote for time odds */\r
+        whiteTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->timeOdds;\r
+        blackTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->other->timeOdds;\r
     }\r
     if (whiteFlag || blackFlag) {\r
        DisplayTitle("");\r
@@ -11355,10 +13020,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
@@ -11413,13 +13080,20 @@ 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 =               // 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
+                 pvInfoList[forwardMostMove-1].time = \r
+                      (timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;\r
        }\r
-        /* [HGM] save time for PGN file if engine did not give it */\r
-        if(pvInfoList[forwardMostMove-1].time == -1)\r
-             pvInfoList[forwardMostMove-1].time = lastTickLength/100;\r
        flagged = CheckFlags();\r
     }\r
     CheckTimeControl();\r
@@ -11463,9 +13137,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
@@ -11487,6 +13163,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
@@ -11674,22 +13365,16 @@ PositionToFEN(move, useFEN960)
                     else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
                    emptycount = 0;\r
                }\r
-                if(gameInfo.variant == VariantShogi) {\r
-                    /* [HGM] write Shogi promoted pieces as +<unpromoted> */\r
-                    if( (int)piece > (int) WhiteCannon && (int)piece < (int) WhiteKing ||\r
-                        (int)piece > (int) BlackCannon && (int)piece < (int) BlackKing ) {\r
-                        *p++ = '+';\r
-                        piece = (ChessSquare)(DEMOTED piece);\r
-                    }\r
+                if(PieceToChar(piece) == '+') {\r
+                    /* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */\r
+                    *p++ = '+';\r
+                    piece = (ChessSquare)(DEMOTED piece);\r
                 } \r
                 *p++ = PieceToChar(piece);\r
-                if(gameInfo.variant == VariantCrazyhouse || gameInfo.variant == VariantBughouse) {\r
-                    /* [HGM] flag Crazyhouse promoted pieces */\r
-                    if( (int)piece > (int) WhiteQueen && (int)piece < (int) WhiteKing ||\r
-                        (int)piece > (int) BlackQueen && (int)piece < (int) BlackKing ) {\r
-                        p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
-                        *p++ = '~';\r
-                    }\r
+                if(p[-1] == '~') {\r
+                    /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */\r
+                    p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
+                    *p++ = '~';\r
                 }\r
            }\r
        }\r
@@ -11703,83 +13388,50 @@ PositionToFEN(move, useFEN960)
     }\r
     *(p - 1) = ' ';\r
 \r
+    /* [HGM] print Crazyhouse or Shogi holdings */\r
+    if( gameInfo.holdingsWidth ) {\r
+        *(p-1) = '['; /* if we wanted to support BFEN, this could be '/' */\r
+        q = p;\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
+            piece = boards[move][i][BOARD_WIDTH-1];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
+            piece = boards[move][BOARD_HEIGHT-i-1][0];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+\r
+        if( q == p ) *p++ = '-';\r
+        *p++ = ']';\r
+        *p++ = ' ';\r
+    }\r
+\r
     /* Active color */\r
     *p++ = whiteToPlay ? 'w' : 'b';\r
     *p++ = ' ';\r
 \r
-    /* HACK: we don't keep track of castling availability, so fake it! */\r
-    /* Tord! please fix with the aid of castlingRights[move][...] */\r
-\r
-    /* PUSH Fabien & Tord */\r
-\r
-    /* Declare all potential FRC castling rights (conservative) */\r
-    /* outermost rook on each side of the king */\r
-\r
-    if( gameInfo.variant == VariantFischeRandom ) {\r
-       int fk, fr;\r
-\r
-       q = p;\r
-\r
-       /* White castling rights */\r
-\r
-       for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) {\r
-\r
-          if (boards[move][0][fk] == WhiteKing) {\r
-\r
-             for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
-                if (boards[move][0][fr] == WhiteRook) {\r
-                   *p++ = useFEN960 ? 'A' + fr : 'K';\r
-                   break;\r
-                }\r
-             }\r
-\r
-             for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
-                if (boards[move][0][fr] == WhiteRook) {\r
-                   *p++ = useFEN960 ? 'A' + fr : 'Q';\r
-                   break;\r
-                }\r
-             }\r
-          }\r
-       }\r
-\r
-       /* Black castling rights */\r
-\r
-       for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) {\r
-\r
-          if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) {\r
+  if(nrCastlingRights) {\r
+     q = p;\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
+                *p++ = castlingRights[move][0] + AAA + 'A' - 'a';\r
+           if(castlingRights[move][2] >= 0 &&\r
+              castlingRights[move][1] >= 0   )\r
+                *p++ = castlingRights[move][1] + AAA + 'A' - 'a';\r
+           if(castlingRights[move][5] >= 0 &&\r
+              castlingRights[move][3] >= 0   )\r
+                *p++ = castlingRights[move][3] + AAA;\r
+           if(castlingRights[move][5] >= 0 &&\r
+              castlingRights[move][4] >= 0   )\r
+                *p++ = castlingRights[move][4] + AAA;\r
+     } else {\r
 \r
-             for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
-                if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
-                   *p++ = useFEN960 ? 'a' + fr : 'k';\r
-                   break;\r
-                }\r
-             }\r
-\r
-             for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
-                if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
-                   *p++ = useFEN960 ? 'a' + fr : 'q';\r
-                   break;\r
-                }\r
-             }\r
-          }\r
-       }\r
-\r
-       if (q == p) *p++ = '-'; /* No castling rights */\r
-       *p++ = ' ';\r
-    }\r
-    else {\r
-        q = p;\r
-\r
-#ifdef OLDCASTLINGCODE\r
-        if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) {\r
-            if (boards[move][0][BOARD_RGHT-1] == WhiteRook) *p++ = 'K';\r
-            if (boards[move][0][BOARD_LEFT] == WhiteRook) *p++ = 'Q';\r
-        }\r
-        if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) {\r
-           if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k';\r
-            if (boards[move][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook) *p++ = 'q';\r
-        }          \r
-#else\r
         /* [HGM] write true castling rights */\r
         if( nrCastlingRights == 6 ) {\r
             if(castlingRights[move][0] == BOARD_RGHT-1 &&\r
@@ -11791,13 +13443,13 @@ PositionToFEN(move, useFEN960)
             if(castlingRights[move][4] == BOARD_LEFT &&\r
                castlingRights[move][5] >= 0  ) *p++ = 'q';\r
         }\r
-#endif\r
-        if (q == p) *p++ = '-';\r
-        *p++ = ' ';\r
-    }\r
-\r
-    /* POP Fabien & Tord */\r
+     }\r
+     if (q == p) *p++ = '-'; /* No castling rights */\r
+     *p++ = ' ';\r
+  }\r
 \r
+  if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+     gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
     /* En passant target square */\r
     if (move > backwardMostMove) {\r
         fromX = moveList[move - 1][0] - AAA;\r
@@ -11817,44 +13469,26 @@ PositionToFEN(move, useFEN960)
     } else {\r
        *p++ = '-';\r
     }\r
-\r
-    /* [HGM] print Crazyhouse holdings */\r
-    if( gameInfo.variant == VariantCrazyhouse ) {\r
-        *p++ = ' '; q = p;\r
-        for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
-            piece = boards[move][i][BOARD_WIDTH-1];\r
-            if( piece != EmptySquare )\r
-              for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
-                  *p++ = PieceToChar(piece);\r
-        }\r
-        for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
-            piece = boards[move][BOARD_HEIGHT-i-1][0];\r
-            if( piece != EmptySquare )\r
-              for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
-                  *p++ = PieceToChar(piece);\r
-        }\r
-\r
-        if( q == p ) *p++ = '-';\r
-        *p++ = ' ';\r
-    }\r
+    *p++ = ' ';\r
+  }\r
 \r
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r
 \r
-    if (appData.debugMode) { int k;\r
-        fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
-        for(k=backwardMostMove; k<=forwardMostMove; k++)\r
-            fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
+        if (appData.debugMode) { int k;\r
+            fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
+            for(k=backwardMostMove; k<=forwardMostMove; k++)\r
+                fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
 \r
-    }\r
+        }\r
 \r
         while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++;\r
         if( j == backwardMostMove ) i += initialRulePlies;\r
-        sprintf(p, " %d", i);\r
-      p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
+        sprintf(p, "%d ", i);\r
+        p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
     }\r
     /* Fullmove number */\r
-    sprintf(p, " %d", (move / 2) + 1);\r
+    sprintf(p, "%d", (move / 2) + 1);\r
     \r
     return StrSave(buf);\r
 }\r
@@ -11873,21 +13507,21 @@ ParseFEN(board, blackPlaysFirst, fen)
     p = fen;\r
 \r
     /* [HGM] by default clear Crazyhouse holdings, if present */\r
-   if(gameInfo.holdingsWidth) {\r
+    if(gameInfo.holdingsWidth) {\r
        for(i=0; i<BOARD_HEIGHT; i++) {\r
            board[i][0]             = EmptySquare; /* black holdings */\r
            board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */\r
            board[i][1]             = (ChessSquare) 0; /* black counts */\r
            board[i][BOARD_WIDTH-2] = (ChessSquare) 0; /* white counts */\r
        }\r
-   }\r
+    }\r
 \r
     /* Piece placement data */\r
     for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
        j = 0;\r
        for (;;) {\r
-           if (*p == '/' || *p == ' ') {\r
-               if (*p == '/') p++;\r
+            if (*p == '/' || *p == ' ' || (*p == '[' && i == 0) ) {\r
+                if (*p == '/') p++;\r
                 emptycount = gameInfo.boardWidth - j;\r
                 while (emptycount--)\r
                         board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
@@ -11907,10 +13541,17 @@ ParseFEN(board, blackPlaysFirst, fen)
                         board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
             } else if (*p == '+' || isalpha(*p)) {\r
                 if (j >= gameInfo.boardWidth) return FALSE;\r
-                if(*p=='+') { piece = (ChessSquare) (PROMOTED CharToPiece(*++p) ); p++; }\r
-                else piece = CharToPiece(*p++);\r
+                if(*p=='+') {\r
+                    piece = CharToPiece(*++p);\r
+                    if(piece == EmptySquare) return FALSE; /* unknown piece */\r
+                    piece = (ChessSquare) (PROMOTED piece ); p++;\r
+                    if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */\r
+                } else piece = CharToPiece(*p++);\r
+\r
+                if(piece==EmptySquare) return FALSE; /* unknown piece */\r
                 if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */\r
                     piece = (ChessSquare) (PROMOTED piece);\r
+                    if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */\r
                     p++;\r
                 }\r
                 board[i][(j++)+gameInfo.holdingsWidth] = piece;\r
@@ -11921,6 +13562,36 @@ ParseFEN(board, blackPlaysFirst, fen)
     }\r
     while (*p == '/' || *p == ' ') p++;\r
 \r
+    /* [HGM] look for Crazyhouse holdings here */\r
+    while(*p==' ') p++;\r
+    if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') {\r
+        if(*p == '[') p++;\r
+        if(*p == '-' ) *p++; /* empty holdings */ else {\r
+            if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
+            /* if we would allow FEN reading to set board size, we would   */\r
+            /* have to add holdings and shift the board read so far here   */\r
+            while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
+                *p++;\r
+                if((int) piece >= (int) BlackPawn ) {\r
+                    i = (int)piece - (int)BlackPawn;\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
+                   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
+            }\r
+        }\r
+        if(*p == ']') *p++;\r
+    }\r
+\r
+    while(*p == ' ') p++;\r
+\r
     /* Active color */\r
     switch (*p++) {\r
       case 'w':\r
@@ -11935,11 +13606,12 @@ ParseFEN(board, blackPlaysFirst, fen)
 \r
     /* [HGM] We NO LONGER ignore the rest of the FEN notation */\r
     /* return the extra info in global variiables             */\r
-  {\r
+\r
     /* set defaults in case FEN is incomplete */\r
     FENepStatus = EP_UNKNOWN;\r
     for(i=0; i<nrCastlingRights; i++ ) {\r
-        FENcastlingRights[i] = initialRights[i];\r
+        FENcastlingRights[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
@@ -11950,79 +13622,102 @@ ParseFEN(board, blackPlaysFirst, fen)
     FENrulePlies = 0;\r
 \r
     while(*p==' ') p++;\r
-\r
-    if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
-              /* castling indicator present, so default is no castlings */\r
-              for(i=0; i<nrCastlingRights; i++ ) {\r
-                     FENcastlingRights[i] = -1;\r
-              }\r
-    }\r
-    while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
-        switch(*p++) {\r
+    if(nrCastlingRights) {\r
+      if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
+          /* castling indicator present, so default becomes no castlings */\r
+          for(i=0; i<nrCastlingRights; i++ ) {\r
+                 FENcastlingRights[i] = -1;\r
+          }\r
+      }\r
+      while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' ||\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
+\r
+        for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
+            if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;\r
+            if(board[0             ][i] == WhiteKing) whiteKingFile = i;\r
+        }\r
+        switch(c) {\r
           case'K':\r
-              FENcastlingRights[0] = BOARD_RGHT-1;\r
-              FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+              for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);\r
+              FENcastlingRights[0] = i != whiteKingFile ? i : -1;\r
+              FENcastlingRights[2] = whiteKingFile;\r
               break;\r
           case'Q':\r
-              FENcastlingRights[1] = BOARD_LEFT;\r
-              FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+              for(i=BOARD_LEFT; board[0][i]!=WhiteRook && i<whiteKingFile; i++);\r
+              FENcastlingRights[1] = i != whiteKingFile ? i : -1;\r
+              FENcastlingRights[2] = whiteKingFile;\r
               break;\r
           case'k':\r
-              FENcastlingRights[3] = BOARD_RGHT-1;\r
-              FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+              for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);\r
+              FENcastlingRights[3] = i != blackKingFile ? i : -1;\r
+              FENcastlingRights[5] = blackKingFile;\r
               break;\r
           case'q':\r
-              FENcastlingRights[4] = BOARD_LEFT;\r
-              FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+              for(i=BOARD_LEFT; board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);\r
+              FENcastlingRights[4] = i != blackKingFile ? i : -1;\r
+              FENcastlingRights[5] = blackKingFile;\r
+          case '-':\r
               break;\r
-          /* Tord! FRC! */\r
+          default: /* FRC castlings */\r
+              if(c >= 'a') { /* black rights */\r
+                  for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+                    if(board[BOARD_HEIGHT-1][i] == BlackKing) break;\r
+                  if(i == BOARD_RGHT) break;\r
+                  FENcastlingRights[5] = i;\r
+                  c -= AAA;\r
+                  if(board[BOARD_HEIGHT-1][c] <  BlackPawn ||\r
+                     board[BOARD_HEIGHT-1][c] >= BlackKing   ) break;\r
+                  if(c > i)\r
+                      FENcastlingRights[3] = c;\r
+                  else\r
+                      FENcastlingRights[4] = c;\r
+              } else { /* white rights */\r
+                  for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+                    if(board[0][i] == WhiteKing) break;\r
+                  if(i == BOARD_RGHT) break;\r
+                  FENcastlingRights[2] = i;\r
+                  c -= AAA - 'a' + 'A';\r
+                  if(board[0][c] >= WhiteKing) break;\r
+                  if(c > i)\r
+                      FENcastlingRights[0] = c;\r
+                  else\r
+                      FENcastlingRights[1] = c;\r
+              }\r
         }\r
+      }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "FEN castling rights:");\r
+        for(i=0; i<nrCastlingRights; i++)\r
+        fprintf(debugFP, " %d", FENcastlingRights[i]);\r
+        fprintf(debugFP, "\n");\r
     }\r
 \r
-    while(*p==' ') p++;\r
-\r
+      while(*p==' ') p++;\r
+    }\r
 \r
-    if(*p=='-') {\r
+    /* read e.p. field in games that know e.p. capture */\r
+    if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+       gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
+      if(*p=='-') {\r
         p++; FENepStatus = EP_NONE;\r
-    } else {\r
-       char c = *p++ - AAA;\r
-\r
-       if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
-       if(*p >= '0' && *p <='9') *p++;\r
-       FENepStatus = c;\r
-    }\r
+      } else {\r
+         char c = *p++ - AAA;\r
 \r
-    /* [HGM] look for Crazyhouse holdings here */\r
-    while(*p==' ') p++;\r
-    if( !isdigit(*p) ) {\r
-        if(*p == '-' ) *p++; /* empty holdings */ else {\r
-            if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
-            /* if we would allow FEN reading to set board size, we would   */\r
-            /* have to add holdings and shift the board read so far here   */\r
-            while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
-                *p++;\r
-                if((int) piece >= (int) BlackPawn ) {\r
-                    i = (int)piece - (int)BlackPawn;\r
-                    if( i >= BOARD_HEIGHT ) 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
-                    board[i][BOARD_WIDTH-1] = piece;    /* white holdings */\r
-                    board[i][BOARD_WIDTH-2]++;          /* black holdings */\r
-                }\r
-            }\r
-        }\r
+         if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
+         if(*p >= '0' && *p <='9') *p++;\r
+         FENepStatus = c;\r
+      }\r
     }\r
 \r
 \r
-\r
     if(sscanf(p, "%d", &i) == 1) {\r
         FENrulePlies = i; /* 50-move ply counter */\r
         /* (The move number is still ignored)    */\r
     }\r
- }\r
+\r
     return TRUE;\r
 }\r
       \r
@@ -12033,7 +13728,7 @@ EditPositionPasteFEN(char *fen)
     Board initial_position;\r
 \r
     if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {\r
-      DisplayError("Bad FEN position in clipboard", 0);\r
+      DisplayError(_("Bad FEN position in clipboard"), 0);\r
       return ;\r
     } else {\r
       int savedBlackPlaysFirst = blackPlaysFirst;\r