changes from H.G. Muller; version 4.3.13
[xboard.git] / backend.c
index 29ebca6..1f0af61 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -214,12 +214,17 @@ 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
-void GetInfoFromComment( int, char * );\r
+char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
 \r
 extern int tinyLayout, smallLayout;\r
 static ChessProgramStats programStats;\r
+static int exiting = 0; /* [HGM] moved to top */\r
+static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
+extern int startedFromPositionFile;\r
+int startedFromPositionFile = FALSE; Board filePosition;    /* [HGM] loadPos */\r
+char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
 \r
 /* States for ics_getting_history */\r
 #define H_FALSE 0\r
@@ -292,7 +297,12 @@ static char * safeStrCat( char * dst, const char * src, size_t count )
 }\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
@@ -310,6 +320,9 @@ 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
     flags &= ~F_ALL_CASTLE_OK;\r
     break;\r
@@ -383,6 +396,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 +419,12 @@ 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
 \r
 ChessSquare  FIDEArray[2][BOARD_SIZE] = {\r
     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
@@ -421,70 +437,91 @@ ChessSquare twoKingsArray[2][BOARD_SIZE] = {
     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
        WhiteKing, WhiteKing, WhiteKnight, WhiteRook },\r
     { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
-       BlackKing, BlackKing, BlackKnight, BlackRook }\r
+        BlackKing, BlackKing, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare  KnightmateArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,\r
+        WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },\r
+    { BlackRook, BlackMan, BlackBishop, BlackQueen,\r
+        BlackUnicorn, BlackBishop, BlackMan, BlackRook }\r
 };\r
 \r
-#ifdef FAIRY\r
 ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */\r
-    { WhiteFairyRook, WhiteFairyKnight, WhiteFairyBishop, WhiteQueen,\r
+    { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,\r
         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
-    { BlackFairyRook, BlackFairyKnight, BlackFairyBishop, BlackQueen,\r
+    { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,\r
        BlackKing, BlackBishop, BlackKnight, BlackRook }\r
 };\r
 \r
 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
-    { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn,\r
-        WhiteKing, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
-    { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn,\r
-        BlackKing, BlackFairyBishop, 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
+#if (BOARD_SIZE>=10)\r
+ChessSquare ShogiArray[2][BOARD_SIZE] = {\r
+    { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,\r
+        WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },\r
+    { BlackQueen, BlackKnight, BlackFerz, BlackWazir,\r
+        BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }\r
 };\r
 \r
-#if (BOARD_SIZE>=9)\r
 ChessSquare XiangqiArray[2][BOARD_SIZE] = {\r
-    { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn,\r
-        WhiteKing, WhiteFairyPawn, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
-    { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn,\r
-        BlackKing, BlackFairyPawn, BlackFairyBishop, BlackKnight, BlackRook }\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,\r
+        WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
+        BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
 };\r
-#else\r
-#define XiangqiPosition FIDEPosition\r
-#endif\r
 \r
-#if (BOARD_SIZE>=10)\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
 #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
 #endif // !(BOARD_SIZE>=10)\r
 \r
 #if (BOARD_SIZE>=12)\r
 ChessSquare CourierArray[2][BOARD_SIZE] = {\r
-    { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteBishop, WhiteFairyKing, WhiteKing,\r
-        WhiteFairyPawn, WhiteFairyRook, WhiteBishop, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
-    { BlackRook, BlackKnight, BlackFairyBishop, BlackBishop, BlackFairyKing, BlackKing,\r
-        BlackFairyPawn, BlackFairyRook, BlackBishop, BlackFairyBishop, BlackKnight, BlackRook }\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,\r
+        WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,\r
+        BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }\r
 };\r
 #else // !(BOARD_SIZE>=12)\r
 #define CourierArray CapablancaArray\r
 #endif // !(BOARD_SIZE>=12)\r
-#endif // FAIRY\r
 \r
 \r
 Board initialPosition;\r
@@ -513,7 +550,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
@@ -654,6 +691,26 @@ 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
+\r
     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
     first.isUCI = appData.firstIsUCI; /* [AS] */\r
@@ -733,25 +790,32 @@ InitBackEnd1()
       case VariantLoadable:\r
       case Variant29:\r
       case Variant30:\r
-#ifndef FAIRY\r
       case Variant31:\r
       case Variant32:\r
       case Variant33:\r
       case Variant34:\r
       case Variant35:\r
       case Variant36:\r
-#endif\r
       default:\r
        sprintf(buf, "Unknown variant name %s", appData.variant);\r
        DisplayFatalError(buf, 0, 2);\r
        return;\r
 \r
+      case VariantXiangqi:    /* [HGM] repetition rules not implemented */\r
+      case VariantFairy:      /* [HGM] TestLegality definitely off! */\r
+      case VariantGothic:     /* [HGM] should work */\r
+      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 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 VariantCrazyhouse: /* holdings not shown,\r
-                                offboard interposition not understood */\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
@@ -762,14 +826,6 @@ InitBackEnd1()
       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
-#ifdef FAIRY\r
-      case VariantShogi:\r
-      case VariantXiangqi:\r
-      case VariantFairy:      /* [HGM] TestLegality definitely off! */\r
-      case VariantGothic:\r
-      case VariantCapablanca:\r
-      case VariantCourier:\r
-#endif\r
        break;\r
       }\r
     }\r
@@ -817,22 +873,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
@@ -855,6 +940,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
@@ -925,7 +1023,10 @@ InitBackEnd3 P((void))
     char buf[MSG_SIZ];\r
     int err;\r
 \r
-    InitChessProgram(&first);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From InitBackend3\n");\r
+    }\r
+    InitChessProgram(&first, startedFromSetupPosition);\r
 \r
     if (appData.icsActive) {\r
        err = establish();\r
@@ -1028,6 +1129,19 @@ 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
@@ -1233,7 +1347,7 @@ 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
@@ -1355,7 +1469,13 @@ StringToVariant(e)
     char buf[MSG_SIZ];\r
 \r
     if (!e) return v;\r
-    \r
+\r
+    /* [HGM] skip over optional board-size prefixes */\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
     for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {\r
       if (StrCaseStr(e, variantNames[i])) {\r
        v = (VariantClass) i;\r
@@ -1452,46 +1572,49 @@ StringToVariant(e)
          v = Variant30;\r
          break;\r
        case 31:\r
-#ifdef FAIRY\r
-          v = VariantShogi;\r
-#else\r
          v = Variant31;\r
-#endif\r
          break;\r
        case 32:\r
-#ifdef FAIRY\r
-          v = VariantXiangqi;\r
-#else\r
          v = Variant32;\r
-#endif\r
          break;\r
        case 33:\r
-#ifdef FAIRY\r
-          v = VariantCourier;\r
-#else\r
          v = Variant33;\r
-#endif\r
          break;\r
        case 34:\r
-#ifdef FAIRY\r
-          v = VariantGothic;\r
-#else\r
          v = Variant34;\r
-#endif\r
          break;\r
        case 35:\r
-#ifdef FAIRY\r
-          v = VariantCapablanca;\r
-#else\r
          v = Variant35;\r
-#endif\r
          break;\r
        case 36:\r
-#ifdef FAIRY\r
-          v = VariantFairy;\r
-#else\r
          v = Variant36;\r
-#endif\r
+         break;\r
+        case 37:\r
+          v = VariantShogi;\r
+         break;\r
+        case 38:\r
+          v = VariantXiangqi;\r
+         break;\r
+        case 39:\r
+          v = VariantCourier;\r
+         break;\r
+        case 40:\r
+          v = VariantGothic;\r
+         break;\r
+        case 41:\r
+          v = VariantCapablanca;\r
+         break;\r
+        case 42:\r
+          v = VariantKnightmate;\r
+         break;\r
+        case 43:\r
+          v = VariantFairy;\r
+          break;\r
+        case 44:\r
+          v = VariantCylinder;\r
+         break;\r
+        case 45:\r
+          v = VariantFalcon;\r
          break;\r
 \r
        case -1:\r
@@ -1677,6 +1800,131 @@ DontEcho()
     TelnetRequest(TN_DONT, TN_ECHO);\r
 }\r
 \r
+void\r
+CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)\r
+{\r
+    /* put the holdings sent to us by the server on the board holdings area */\r
+    int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;\r
+    char p;\r
+    ChessSquare piece;\r
+\r
+    if(gameInfo.holdingsWidth < 2)  return;\r
+\r
+    if( (int)lowestPiece >= BlackPawn ) {\r
+        holdingsColumn = 0;\r
+        countsColumn = 1;\r
+        holdingsStartRow = BOARD_HEIGHT-1;\r
+        direction = -1;\r
+    } else {\r
+        holdingsColumn = BOARD_WIDTH-1;\r
+        countsColumn = BOARD_WIDTH-2;\r
+        holdingsStartRow = 0;\r
+        direction = 1;\r
+    }\r
+\r
+    for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */\r
+        board[i][holdingsColumn] = EmptySquare;\r
+        board[i][countsColumn]   = (ChessSquare) 0;\r
+    }\r
+    while( (p=*holdings++) != NULLCHAR ) {\r
+        piece = CharToPiece( ToUpper(p) );\r
+        if(piece == EmptySquare) continue;\r
+        /*j = (int) piece - (int) WhitePawn;*/\r
+        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
+        board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
+        board[holdingsStartRow+j*direction][countsColumn]++;\r
+    }\r
+\r
+}\r
+\r
+char startBoard[MSG_SIZ]; /* [HGM] variantswitch */\r
+\r
+void\r
+VariantSwitch(Board board, VariantClass newVariant)\r
+{\r
+   int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j, oldCurrentMove = currentMove;\r
+   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
+    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
+        forwardMostMove = backwardMostMove =\r
+        currentMove = oldCurrentMove; /* InitPos reset this, but we need still to redraw it */\r
+#if 0\r
+        epStatus[0] = saveEP;\r
+        for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];\r
+        CopyBoard(tempBoard, board); /* restore position received from ICS   */\r
+#endif\r
+    } else { gameInfo.variant = newVariant; InitPosition(FALSE); }\r
+}\r
+\r
 static int loggedOn = FALSE;\r
 \r
 /*-- Game start info cache: --*/\r
@@ -1732,6 +1980,10 @@ read_from_ics(isr, closure, data, count, error)
     }\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
@@ -2275,11 +2527,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
+                /* [HGM] we switched variant. Translate boards if needed. */\r
+                VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));\r
                continue;\r
            }\r
 \r
@@ -2725,9 +2977,9 @@ read_from_ics(isr, closure, data, count, error)
                        ClearPremoveHighlights();\r
                        if (appData.debugMode)\r
                          fprintf(debugFP, "Sending premove:\n");\r
-                         UserMoveEvent(premoveFromX, premoveFromY, \r
+                          UserMoveEvent(premoveFromX, premoveFromY, \r
                                        premoveToX, premoveToY, \r
-                                       premovePromoChar);\r
+                                        premovePromoChar);\r
                      }\r
                    }\r
 \r
@@ -2744,11 +2996,17 @@ 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
-                         gameInfo.variant = VariantCrazyhouse; /*temp guess*/\r
+                          /* [HGM] We seem to switch variant during a game!\r
+                           * Presumably no holdings were displayed, so we have\r
+                           * to move the position two files to the right to\r
+                           * create room for them!\r
+                           */\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
@@ -2763,6 +3021,9 @@ read_from_ics(isr, closure, data, count, error)
                               new_piece);\r
                         white_holding[strlen(white_holding)-1] = NULLCHAR;\r
                         black_holding[strlen(black_holding)-1] = NULLCHAR;\r
+                        /* [HGM] copy holdings to board holdings area */\r
+                        CopyHoldings(boards[currentMove], white_holding, WhitePawn);\r
+                        CopyHoldings(boards[currentMove], black_holding, BlackPawn);\r
 #if ZIPPY\r
                        if (appData.zippyPlay && first.initDone) {\r
                            ZippyHoldings(white_holding, black_holding,\r
@@ -2780,7 +3041,8 @@ read_from_ics(isr, closure, data, count, error)
                                    gameInfo.white, white_holding,\r
                                    gameInfo.black, black_holding);\r
                        }\r
-                       DrawPosition(FALSE, NULL);\r
+\r
+                        DrawPosition(FALSE, boards[currentMove]);\r
                        DisplayTitle(str);\r
                    }\r
                    /* Suppress following prompt */\r
@@ -2996,7 +3258,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.outOfBook = NULL;\r
        \r
        /* Do we have the ratings? */\r
@@ -3058,14 +3326,74 @@ ParseBoard12(string)
     }\r
     \r
     /* Parse the board */\r
-    for (k = 0; k < 8; k++)\r
+    for (k = 0; k < 8; k++) {\r
       for (j = 0; j < 8; j++)\r
-       board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);\r
+        board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(7-k)*9 + 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
+      }\r
+    }\r
     CopyBoard(boards[moveNum], board);\r
     if (moveNum == 0) {\r
        startedFromSetupPosition =\r
          !CompareBoards(board, initialPosition);\r
-    }\r
+        if(startedFromSetupPosition)\r
+            initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */\r
+    }\r
+    /* [HGM] variantswitch: remember the last initial position parsed, */\r
+    /* because it might have been parsed in the wrong variant, so that */\r
+    /* we can re-parse it once we know the proper variant (which might */\r
+    /* have different piece assignments for the same letters).         */\r
+    if(moveNum == 0) strcpy(startBoard, string);\r
+\r
+    /* [HGM] Set castling rights. Take the outermost Rooks,\r
+       to make it also work for FRC opening positions. Note that board12\r
+       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;\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
+        for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+            if(board[0][k] == WhiteKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
+        for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+            if(board[BOARD_HEIGHT-1][k] == BlackKing)\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
@@ -3128,6 +3456,16 @@ ParseBoard12(string)
     /* Put the move on the move list, first converting\r
        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, "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
            /* We don't know what the board looked like before\r
               this move.  Punt. */\r
@@ -3148,7 +3486,8 @@ ParseBoard12(string)
              default:\r
                break;\r
              case MT_CHECK:\r
-               strcat(parseList[moveNum - 1], "+");\r
+                if(gameInfo.variant != VariantShogi)\r
+                    strcat(parseList[moveNum - 1], "+");\r
                break;\r
              case MT_CHECKMATE:\r
                strcat(parseList[moveNum - 1], "#");\r
@@ -3169,6 +3508,10 @@ ParseBoard12(string)
            fromX = fromY = toX = toY = -1;\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
@@ -3181,6 +3524,10 @@ ParseBoard12(string)
            moveList[moveNum - 1][0] = NULLCHAR;\r
            fromX = fromY = toX = toY = -1;\r
        }\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
+    setbuf(debugFP, NULL);\r
+  }\r
 \r
 #if ZIPPY\r
        /* Send move to chess program (BEFORE animating it). */\r
@@ -3319,6 +3666,7 @@ SendMoveToProgram(moveNum, cps)
      ChessProgramState *cps;\r
 {\r
     char buf[MSG_SIZ];\r
+\r
     if (cps->useUsermove) {\r
       SendToProgram("usermove ", cps);\r
     }\r
@@ -3332,25 +3680,23 @@ 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>8) {\r
-          char *p = buf;\r
-          while(*p) { if(*p < 'A') (*p)--; p++; }\r
-      }\r
       SendToProgram(buf, cps);\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
-       int fromX = moveList[moveNum][0] - 'a'; \r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Tord's FRC castling code\n");\r
+  }\r
+        int fromX = moveList[moveNum][0] - AAA; \r
         int fromY = moveList[moveNum][1] - ONE;\r
-       int toX = moveList[moveNum][2] - 'a'; \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
@@ -3359,6 +3705,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
@@ -3404,28 +3765,31 @@ 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
-                'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY,\r
+        if(gameInfo.variant == VariantShatranj)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteFerz));\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
       case WhiteDrop:\r
       case BlackDrop:\r
        sprintf(user_move, "%c@%c%c\n",\r
                ToUpper(PieceToChar((ChessSquare) fromX)),\r
-                'a' + toX, ONE + toY);\r
+                AAA + toX, ONE + toY);\r
        break;\r
       case NormalMove:\r
       case WhiteCapturesEnPassant:\r
       case BlackCapturesEnPassant:\r
       case IllegalMove:  /* could be a variant we don't quite understand */\r
        sprintf(user_move, "%c%c%c%c\n",\r
-                'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY);\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY);\r
        break;\r
     }\r
     SendToICS(user_move);\r
@@ -3439,16 +3803,17 @@ CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
 {\r
     if (rf == DROP_RANK) {\r
        sprintf(move, "%c@%c%c\n",\r
-                ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, ONE + rt);\r
+                ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt);\r
     } else {\r
        if (promoChar == 'x' || promoChar == NULLCHAR) {\r
            sprintf(move, "%c%c%c%c\n",\r
-                    'a' + ff, ONE + rf, 'a' + ft, ONE + rt);\r
+                    AAA + ff, ONE + rf, AAA + ft, ONE + rt);\r
        } else {\r
            sprintf(move, "%c%c%c%c%c\n",\r
-                    'a' + ff, ONE + rf, 'a' + ft, ONE + rt, promoChar);\r
+                    AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);\r
        }\r
     }\r
+    AlphaRank(move, 4);\r
 }\r
 \r
 void\r
@@ -3465,6 +3830,57 @@ ProcessICSInitScript(f)
 }\r
 \r
 \r
+/* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */\r
+void\r
+AlphaRank(char *move, int n)\r
+{\r
+    char *p = move, c; int x, y;\r
+\r
+    if( !appData.alphaRank ) return;\r
+\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
+    }\r
+\r
+    if(move[1]=='*' && \r
+       move[2]>='0' && move[2]<='9' &&\r
+       move[3]>='a' && move[3]<='x'    ) {\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
 /* Parser for moves from gnuchess, ICS, or user typein box */\r
 Boolean\r
 ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)\r
@@ -3474,15 +3890,16 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
      int *fromX, *fromY, *toX, *toY;\r
      char *promoChar;\r
 {       \r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "move to parse: %s\n", move);\r
+    }\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
@@ -3511,13 +3928,16 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
       case BlackASideCastleFR:\r
       /* End of code added by Tord */\r
       case IllegalMove:                /* bug or odd chess variant */\r
-        *fromX = currentMoveString[0] - 'a';\r
+        *fromX = currentMoveString[0] - AAA;\r
         *fromY = currentMoveString[1] - ONE;\r
-       *toX = currentMoveString[2] - 'a';\r
+        *toX = currentMoveString[2] - AAA;\r
         *toY = currentMoveString[3] - ONE;\r
        *promoChar = currentMoveString[4];\r
-        if (*fromX < 0 || *fromX >= BOARD_WIDTH || *fromY < 0 || *fromY >= BOARD_HEIGHT ||\r
-            *toX < 0 || *toX >= BOARD_WIDTH || *toY < 0 || *toY >= BOARD_HEIGHT) {\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
@@ -3533,7 +3953,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
          (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
        (int) CharToPiece(ToLower(currentMoveString[0]));\r
        *fromY = DROP_RANK;\r
-       *toX = currentMoveString[2] - 'a';\r
+        *toX = currentMoveString[2] - AAA;\r
         *toY = currentMoveString[3] - ONE;\r
        *promoChar = NULLCHAR;\r
        return TRUE;\r
@@ -3549,6 +3969,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
@@ -3587,11 +4010,17 @@ 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
-\r
-    for( i=0; i<BOARD_WIDTH; i++ ) {\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[1]  = initialRights[4]  =\r
+    castlingRights[0][1] = castlingRights[0][4] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
+    initialRights[2]  = initialRights[5]  =\r
+    castlingRights[0][2] = castlingRights[0][5] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[0]  = initialRights[3]  =\r
+    castlingRights[0][0] = castlingRights[0][3] = i;\r
+\r
+    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
         board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
     }\r
 }\r
@@ -3632,22 +4061,56 @@ 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=0; i<BOARD_WIDTH; i++ ) {\r
+    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
         board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
     }\r
 }\r
 \r
+BOOL 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
+\r
+    if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
+                    && NrPieces >= 12 && !(NrPieces&1)) {\r
+        int i; /* [HGM] Accept even length from 12 to 34 */\r
+\r
+        for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';\r
+        for( i=0; i<NrPieces/2-1; i++ ) {\r
+            table[i] = map[i];\r
+            table[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
+        }\r
+        table[(int) WhiteKing]  = map[NrPieces/2-1];\r
+        table[(int) BlackKing]  = map[NrPieces-1];\r
+\r
+        result = TRUE;\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
 void\r
 InitPosition(redraw)\r
      int redraw;\r
 {\r
     ChessSquare (* pieces)[BOARD_SIZE];\r
-    int i, j;\r
+    int i, j, pawnRow, overrule,\r
+    oldx = gameInfo.boardWidth,\r
+    oldy = gameInfo.boardHeight,\r
+    oldh = gameInfo.holdingsWidth,\r
+    oldv = gameInfo.variant;\r
 \r
     currentMove = forwardMostMove = backwardMostMove = 0;\r
 \r
@@ -3659,20 +4122,10 @@ InitPosition(redraw)
             for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
         }\r
 \r
-        /* [HGM] Build normal castling rights */\r
-        for( j=0; j<BOARD_SIZE; j++ ) initialRights[j] = -1;\r
-        nrCastlingRights = 6;\r
-        castlingRights[0][0] = initialRights[0] = BOARD_WIDTH-1;\r
-        castlingRights[0][1] = initialRights[1] = 0;\r
-        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;\r
-        castlingRights[0][3] = initialRights[3] = BOARD_WIDTH-1;\r
-        castlingRights[0][4] = initialRights[4] = 0;\r
-        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\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
-        initialRulePlies = 0; /* 50-move counter start */\r
     }\r
 \r
     \r
@@ -3683,17 +4136,22 @@ InitPosition(redraw)
     /* empty squares. This initial position is then copied to boards[0],  */\r
     /* possibly after shuffling, so that it remains available.            */\r
 \r
+    gameInfo.holdingsWidth = 0; /* default board sizes */\r
+    gameInfo.boardWidth    = 8;\r
+    gameInfo.boardHeight   = 8;\r
+    gameInfo.holdingsSize  = 0;\r
+    nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */\r
+    for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1; /* but no rights yet */\r
+    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k"); \r
+\r
     switch (gameInfo.variant) {\r
     default:\r
-      pieces = BOARD_WIDTH <= 8  ? FIDEArray :\r
-               BOARD_WIDTH <= 10 ? CapablancaArray : CourierArray;\r
+      pieces = FIDEArray;\r
       break;\r
     case VariantShatranj:\r
       pieces = ShatranjArray;\r
-      CharToPiece('E'); /* associate PGN/FEN letter with internal piece type */\r
-      CharToPiece('e');\r
       nrCastlingRights = 0;\r
-      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k"); \r
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
@@ -3701,29 +4159,59 @@ 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
-#ifdef FAIRY\r
     case VariantCapablanca:\r
       pieces = CapablancaArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
       break;\r
     case VariantGothic:\r
       pieces = GothicArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \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.AE..K.C.ph.r.ae..k.c."); \r
+      break;\r
+    case VariantShogi:\r
+      pieces = ShogiArray;\r
+      gameInfo.boardWidth  = 9;\r
+      gameInfo.boardHeight = 9;\r
+      gameInfo.holdingsSize = 7;\r
+      nrCastlingRights = 0;\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.FE..WMKpnbr.fe..wmk"); \r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       break;\r
+    case VariantKnightmate:\r
+      pieces = KnightmateArray;\r
+      SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k."); \r
+      break;\r
     case VariantFairy:\r
       pieces = fairyArray;\r
-      startedFromSetupPosition = TRUE;\r
+      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
+      break;\r
+    case VariantCrazyhouse:\r
+    case VariantBughouse:\r
+      pieces = FIDEArray;\r
+      SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k"); \r
+      gameInfo.holdingsSize = 5;\r
       break;\r
-#endif\r
     case VariantWildCastle:\r
       pieces = FIDEArray;\r
       /* !!?shuffle with kings guaranteed to be on d or e file */\r
@@ -3736,28 +4224,76 @@ InitPosition(redraw)
       break;\r
     }\r
 \r
-    for( j=0; j<BOARD_WIDTH; j++ ) {\r
+    overrule = 0;\r
+    if(appData.NrFiles >= 0) {\r
+        if(gameInfo.boardWidth != appData.NrFiles) overrule++;\r
+        gameInfo.boardWidth = appData.NrFiles;\r
+    }\r
+    if(appData.NrRanks >= 0) {\r
+        gameInfo.boardHeight = appData.NrRanks;\r
+    }\r
+    if(appData.holdingsSize >= 0) {\r
+        i = appData.holdingsSize;\r
+        if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;\r
+        gameInfo.holdingsSize = i;\r
+    }\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
+\r
+    pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */\r
+    if(pawnRow < 1) pawnRow = 1;\r
+\r
+    /* User pieceToChar list overrules defaults */\r
+    if(appData.pieceToCharTable != NULL)\r
+        SetCharTable(pieceToChar, appData.pieceToCharTable);\r
+\r
+    for( j=0; j<BOARD_WIDTH; j++ ) { ChessSquare s = EmptySquare;\r
+\r
+        if(j==BOARD_LEFT-1 || j==BOARD_RGHT)\r
+            s = (ChessSquare) 0; /* account holding counts in guard band */\r
         for( i=0; i<BOARD_HEIGHT; i++ )\r
-            initialPosition[i][j] = EmptySquare;\r
-        initialPosition[0][j] = pieces[0][j];\r
+            initialPosition[i][j] = s;\r
+\r
+        if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue;\r
+        initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth];\r
+        initialPosition[pawnRow][j] = WhitePawn;\r
+        initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn;\r
         if(gameInfo.variant == VariantXiangqi) {\r
             if(j&1) {\r
-                initialPosition[3][j] = \r
-                initialPosition[BOARD_HEIGHT-4][j] = EmptySquare;\r
-                if(j==1 || j>=BOARD_WIDTH-2) {\r
-                    initialPosition[2][j] = WhiteFairyMarshall;\r
-                    initialPosition[BOARD_HEIGHT-3][j] = BlackFairyMarshall;\r
+                initialPosition[pawnRow][j] = \r
+                initialPosition[BOARD_HEIGHT-pawnRow-1][j] = EmptySquare;\r
+                if(j==BOARD_LEFT+1 || j>=BOARD_RGHT-2) {\r
+                   initialPosition[2][j] = WhiteCannon;\r
+                   initialPosition[BOARD_HEIGHT-3][j] = BlackCannon;\r
                 }\r
-            } else {\r
-                initialPosition[3][j] = WhitePawn;\r
-                initialPosition[BOARD_HEIGHT-4][j] = BlackPawn;\r
             }\r
-        } else {\r
-            initialPosition[1][j] = WhitePawn;\r
-            initialPosition[BOARD_HEIGHT-2][j] = BlackPawn;\r
         }\r
-        initialPosition[BOARD_HEIGHT-1][j] =  pieces[1][j];\r
+        initialPosition[BOARD_HEIGHT-1][j] =  pieces[1][j-gameInfo.holdingsWidth];\r
     }\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
+            j=BOARD_RGHT-2;\r
+            initialPosition[1][j] = WhiteRook;\r
+            initialPosition[BOARD_HEIGHT-2][j] = BlackBishop;\r
+    }\r
+\r
+    if( nrCastlingRights == -1) {\r
+        /* [HGM] Build normal castling rights (must be done after board sizing!) */\r
+        /*       This sets default castling rights from none to normal corners   */\r
+        /* Variants with other castling rights must set them themselves above    */\r
+        nrCastlingRights = 6;\r
+       \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;\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;\r
+     }\r
 \r
     if(gameInfo.variant == VariantFischeRandom) {\r
       if( appData.defaultFrcPosition < 0 ) {\r
@@ -3766,10 +4302,31 @@ InitPosition(redraw)
       else {\r
         SetupFRC( initialPosition, appData.defaultFrcPosition );\r
       }\r
+      startedFromSetupPosition = TRUE;\r
+    } else 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
 \r
+    if(oldx != gameInfo.boardWidth ||\r
+       oldy != gameInfo.boardHeight ||\r
+       oldh != gameInfo.holdingsWidth\r
+#ifdef GOTHIC\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
     if (redraw)\r
       DrawPosition(TRUE, boards[currentMove]);\r
 }\r
@@ -3799,10 +4356,19 @@ SendBoard(cps, moveNum)
       SendToProgram("#\n", cps);\r
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
        bp = &boards[moveNum][i][0];\r
-        for (j = 0; j < BOARD_WIDTH; j++, bp++) {\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
-                    'a' + j, ONE + i);\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(appData.alphaRank) {\r
+                message[1] = BOARD_RGHT   - 1 - j + '1';\r
+                message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+            }\r
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -3811,11 +4377,20 @@ SendBoard(cps, moveNum)
       SendToProgram("c\n", cps);\r
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
        bp = &boards[moveNum][i][0];\r
-        for (j = 0; j < BOARD_WIDTH; j++, bp++) {\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
-                    'a' + j, ONE + i);\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(appData.alphaRank) {\r
+                message[1] = BOARD_RGHT   - 1 - j + '1';\r
+                message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+            }\r
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -3823,25 +4398,55 @@ 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
 IsPromotion(fromX, fromY, toX, toY)\r
      int fromX, fromY, toX, toY;\r
 {\r
-    return gameMode != EditPosition && gameInfo.variant != VariantXiangqi &&\r
-      fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&\r
-       ((boards[currentMove][fromY][fromX] == WhitePawn && toY == BOARD_HEIGHT-1) ||\r
-        (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));\r
+    /* [HGM] add Shogi promotions */\r
+    int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn;\r
+    ChessSquare piece;\r
+\r
+    if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi ||\r
+      !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE;\r
+   /* [HGM] Note to self: line above also weeds out drops */\r
+    piece = boards[currentMove][fromY][fromX];\r
+    if(gameInfo.variant == VariantShogi) {\r
+        promotionZoneSize = 3;\r
+        highestPromotingPiece = (int)WhiteKing;\r
+        /* [HGM] Should be Silver = Ferz, really, but legality testing is off,\r
+           and if in normal chess we then allow promotion to King, why not\r
+           allow promotion of other piece in Shogi?                         */\r
+    }\r
+    if((int)piece >= BlackPawn) {\r
+        if(toY >= promotionZoneSize && fromY >= promotionZoneSize)\r
+             return FALSE;\r
+        highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece;\r
+    } else {\r
+        if(  toY < BOARD_HEIGHT - promotionZoneSize &&\r
+           fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;\r
+    }\r
+    return ( (int)piece <= highestPromotingPiece );\r
 }\r
 \r
+int\r
+InPalace(row, column)\r
+     int row, column;\r
+{   /* [HGM] for Xiangqi */\r
+    if( (row < 3 || row > BOARD_HEIGHT-4) &&\r
+         column < (BOARD_WIDTH + 4)/2 &&\r
+         column > (BOARD_WIDTH - 5)/2 ) return TRUE;\r
+    return FALSE;\r
+}\r
 \r
 int\r
 PieceForSquare (x, y)\r
      int x;\r
      int y;\r
 {\r
-  if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)\r
+  if (x < BOARD_LEFT || x >= BOARD_RGHT || y < 0 || y >= BOARD_HEIGHT)\r
      return -1;\r
   else\r
      return boards[currentMove][y][x];\r
@@ -3958,18 +4563,38 @@ char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
 ChessMove lastLoadGameStart = (ChessMove) 0;\r
 \r
 \r
-void\r
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)\r
+ChessMove\r
+UserMoveTest(fromX, fromY, toX, toY, promoChar)\r
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
 {\r
     ChessMove moveType;\r
+    ChessSquare pdown, pup;\r
 \r
-    if (fromX < 0 || fromY < 0) return;\r
+    if (fromX < 0 || fromY < 0) return ImpossibleMove;\r
     if ((fromX == toX) && (fromY == toY)) {\r
-       return;\r
-    }\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: 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 &&\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
@@ -3990,13 +4615,13 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
       case IcsIdle:\r
        /* We switched into a game mode where moves are not accepted,\r
            perhaps while the mouse button was down. */\r
-       return;\r
+        return ImpossibleMove;\r
 \r
       case MachinePlaysWhite:\r
        /* User is moving for Black */\r
        if (WhiteOnMove(currentMove)) {\r
            DisplayMoveError("It is White's turn");\r
-           return;\r
+            return ImpossibleMove;\r
        }\r
        break;\r
 \r
@@ -4004,7 +4629,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
        /* User is moving for White */\r
        if (!WhiteOnMove(currentMove)) {\r
            DisplayMoveError("It is Black's turn");\r
-           return;\r
+            return ImpossibleMove;\r
        }\r
        break;\r
 \r
@@ -4018,13 +4643,13 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
            /* User is moving for Black */\r
            if (WhiteOnMove(currentMove)) {\r
                DisplayMoveError("It is White's turn");\r
-               return;\r
+                return ImpossibleMove;\r
            }\r
        } else {\r
            /* User is moving for White */\r
            if (!WhiteOnMove(currentMove)) {\r
                DisplayMoveError("It is Black's turn");\r
-               return;\r
+                return ImpossibleMove;\r
            }\r
        }\r
        break;\r
@@ -4046,7 +4671,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
                            "fromY %d, toX %d, toY %d\n",\r
                            fromX, fromY, toX, toY);\r
            }\r
-           return;\r
+            return ImpossibleMove;\r
        }\r
        break;\r
 \r
@@ -4067,7 +4692,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
                            "fromY %d, toX %d, toY %d\n",\r
                            fromX, fromY, toX, toY);\r
            }\r
-           return;\r
+            return ImpossibleMove;\r
        }\r
        break;\r
 \r
@@ -4075,6 +4700,8 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
        break;\r
 \r
       case EditPosition:\r
+       /* EditPosition, empty square, or different color piece;\r
+          click-click move is possible */\r
        if (toX == -2 || toY == -2) {\r
            boards[0][fromY][fromX] = EmptySquare;\r
            DrawPosition(FALSE, boards[currentMove]);\r
@@ -4083,24 +4710,72 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
            boards[0][fromY][fromX] = EmptySquare;\r
            DrawPosition(FALSE, boards[currentMove]);\r
        }\r
-       return;\r
+        return ImpossibleMove;\r
+    }\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
+         if(appData.testLegality) {\r
+             /* it would be more logical if LegalityTest() also figured out\r
+              * which drops are legal. For now we forbid pawns on back rank.\r
+              * Shogi is on its own here...\r
+              */\r
+             if( (pdown == WhitePawn || pdown == BlackPawn) &&\r
+                 (toY == 0 || toY == BOARD_HEIGHT -1 ) )\r
+                 return(ImpossibleMove); /* no pawn drops on 1st/8th */\r
+         }\r
+         return WhiteDrop; /* Not needed to specify white or black yet */\r
     }\r
 \r
-    if (toX < 0 || toY < 0) return;\r
     userOfferedDraw = FALSE;\r
        \r
-    if (appData.testLegality) {\r
-       moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
-                                EP_UNKNOWN, castlingRights[currentMove],\r
+    /* [HGM] always test for legality, to get promotion info */\r
+    moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
+                          epStatus[currentMove], castlingRights[currentMove],\r
                                          fromY, fromX, toY, toX, promoChar);\r
+\r
+    /* [HGM] but possibly ignore an IllegalMove result */\r
+    if (appData.testLegality) {\r
        if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
            DisplayMoveError("Illegal move");\r
-           return;\r
+            return ImpossibleMove;\r
        }\r
-    } else {\r
-       moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
     }\r
 \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
+       should be called. This to give the calling driver routine the\r
+       opportunity to finish the userMove input with a promotion popup,\r
+       without bothering the user with this for invalid or illegal moves */\r
+\r
+/*    FinishMove(moveType, fromX, fromY, toX, toY, promoChar); */\r
+}\r
+\r
+/* Common tail of UserMoveEvent and DropMenuEvent */\r
+void\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
+       move type in caller when we know the move is a legal promotion */\r
+    if(moveType == NormalMove)\r
+        moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
+\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
+         fromX = boards[currentMove][fromY][fromX];\r
+         fromY = DROP_RANK;\r
+    }\r
+\r
+    /* [HGM] <popupFix> The following if has been moved here from\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
       /* compare the move played on the board to the next move in the\r
        * game. If they match, display the move and the opponent's response. \r
@@ -4136,16 +4811,6 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
       return;\r
     }\r
 \r
-    FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
-}\r
-\r
-/* Common tail of UserMoveEvent and DropMenuEvent */\r
-void\r
-FinishMove(moveType, fromX, fromY, toX, toY, promoChar)\r
-     ChessMove moveType;\r
-     int fromX, fromY, toX, toY;\r
-     /*char*/int promoChar;\r
-{\r
   /* Ok, now we know that the move is good, so we can kill\r
      the previous line in Analysis Mode */\r
   if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {\r
@@ -4236,6 +4901,26 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
   }\r
 }\r
 \r
+void\r
+UserMoveEvent(fromX, fromY, toX, toY, promoChar)\r
+     int fromX, fromY, toX, toY;\r
+     int promoChar;\r
+{\r
+    /* [HGM] This routine was added to allow calling of its two logical\r
+       parts from other modules in the old way. Before, UserMoveEvent()\r
+       automatically called FinishMove() if the move was OK, and returned\r
+       otherwise. I separated the two, in order to make it possible to\r
+       slip a promotion popup in between. But that it always needs two\r
+       calls, to the first part, (now called UserMoveTest() ), and to\r
+       FinishMove if the first part succeeded. Calls that do not need\r
+       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(moveType != ImpossibleMove)\r
+        FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
+}\r
+\r
 void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )\r
 {\r
     char * hint = lastHint;\r
@@ -4279,6 +4964,11 @@ HandleMachineMove(message, cps)
     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
@@ -4370,15 +5060,22 @@ HandleMachineMove(message, cps)
            return;\r
        }\r
 \r
-       if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
-                             &fromX, &fromY, &toX, &toY, &promoChar)) {\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
+        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
                    machineMove, cps->which);\r
            DisplayError(buf1, 0);\r
+            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d%c",\r
+                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
            if (gameMode == TwoMachinesPlay) {\r
              GameEnds(machineWhite ? BlackWins : WhiteWins,\r
-                      "Forfeit due to illegal move", GE_XBOARD);\r
+                       buf1, GE_XBOARD);\r
            }\r
            return;\r
        }\r
@@ -4388,21 +5085,40 @@ HandleMachineMove(message, cps)
         /* to make sure an illegal e.p. capture does not slip through,   */\r
         /* to cause a forfeit on a justified illegal-move complaint      */\r
         /* of the opponent.                                              */\r
-        if(gameMode==TwoMachinesPlay && appData.testLegality &&\r
-           LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+        if( gameMode==TwoMachinesPlay && appData.testLegality\r
+            && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */\r
+                                                              ) {\r
+           ChessMove moveType;\r
+           moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
                         epStatus[forwardMostMove], castlingRights[forwardMostMove],\r
-                             fromY, fromX, toY, toX, promoChar) == IllegalMove)\r
-           { static char buf[MSG_SIZ];\r
+                             fromY, fromX, toY, toX, promoChar);\r
            if (appData.debugMode) {\r
                 int i;\r
                 for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",\r
                     castlingRights[forwardMostMove][i], castlingRank[i]);\r
                 fprintf(debugFP, "castling rights\n");\r
            }\r
-              sprintf(buf, "Xboard: Forfeit due to illegal move %s (%c%c%c%c)%c",\r
-                            machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0);\r
-              GameEnds(machineWhite ? BlackWins : WhiteWins, buf, GE_XBOARD);\r
+            if(moveType == IllegalMove) {\r
+                sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",\r
+                        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
+           /* [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
+               toX+=2;\r
+               currentMoveString[2]++;\r
+               break;\r
+             case WhiteHSideCastleFR:\r
+             case BlackHSideCastleFR:\r
+               toX--;\r
+               currentMoveString[2]--;\r
+               break;\r
            }\r
+        }\r
        hintRequested = FALSE;\r
        lastHint[0] = NULLCHAR;\r
        bookRequested = FALSE;\r
@@ -4426,7 +5142,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
@@ -4464,10 +5180,11 @@ HandleMachineMove(message, cps)
 \r
 #ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines\r
 \r
-        if( gameMode == TwoMachinesPlay ) {\r
+        if( gameMode == TwoMachinesPlay && gameInfo.holdingsSize == 0) {\r
             int count = 0, epFile = epStatus[forwardMostMove];\r
 \r
-            if(appData.testLegality) // don't wait for engine to announce game end if we can judge ourselves\r
+            if(appData.testLegality && appData.checkMates) \r
+            // don't wait for engine to announce game end if we can judge ourselves\r
             switch (MateTest(boards[forwardMostMove],\r
                                  PosFlags(forwardMostMove), epFile,\r
                                        castlingRights[forwardMostMove]) ) {\r
@@ -4491,12 +5208,12 @@ HandleMachineMove(message, cps)
             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
+                    NrWQ=0, NrBQ=0, bishopsColor = 0,\r
                     NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
                 static int moveCount;\r
 \r
                 /* First absolutely insufficient mating material. Count what is on board. */\r
-                for(i=0; i<BOARD_HEIGHT; i++) for(j=0; j<BOARD_WIDTH; j++)\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
@@ -4505,19 +5222,21 @@ HandleMachineMove(message, cps)
                         case WhiteKnight:\r
                              NrWN++; break;\r
                         case WhiteBishop:\r
+                             bishopsColor |= 1 << ((i^j)&1);\r
                              NrWB++; break;\r
                         case BlackKnight:\r
-                             NrWN++; break;\r
+                             NrBN++; break;\r
                         case BlackBishop:\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
@@ -4528,13 +5247,14 @@ HandleMachineMove(message, cps)
                     NrPieces += (p != EmptySquare);\r
                 }\r
 \r
-                if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2 )\r
-                {    /* KBK, KNK or KK */\r
+                if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2\r
+                 || NrPieces == 4 && NrBB+NrWB==2 && bishopsColor != 3)\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(adjudicateLossThreshold != 0) {\r
+                     if(appData.materialDraws) {\r
                          /* but only adjudicate them if adjudication enabled */\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
@@ -4549,14 +5269,14 @@ HandleMachineMove(message, cps)
                    || NrWN==2 || NrBN==2     /* KNNK */\r
                    || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */\r
                   ) ) {\r
-                     if(--moveCount < 0 && adjudicateLossThreshold != 0)\r
+                     if(--moveCount < 0 && appData.trivialDraws)\r
                      {    /* if the first 3 moves do not show a tactical win, declare draw */\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
+#if 0\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
@@ -4565,20 +5285,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
@@ -4596,6 +5322,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
@@ -4604,8 +5331,9 @@ 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
-                            && adjudicateLossThreshold != 0) {\r
+                            && appData.drawRepeats > 1) {\r
                              /* adjudicate after user-specified nr of repeats */\r
                              ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                              GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
@@ -4628,11 +5356,32 @@ HandleMachineMove(message, cps)
                 if( count >= 100)\r
                          epStatus[forwardMostMove] = EP_RULE_DRAW;\r
                          /* this is used to judge if draw claims are legal */\r
-                if(adjudicateLossThreshold != 0 && count >= 2*appData.ruleMoves) {\r
+                if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
                          return;\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
+                             GameEnds( GameIsDrawn, p, GE_XBOARD );\r
+                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                             return;\r
+                         }\r
+                }\r
+\r
             }\r
 \r
 \r
@@ -4647,6 +5396,11 @@ HandleMachineMove(message, cps)
         }\r
 \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
@@ -4688,6 +5442,31 @@ HandleMachineMove(message, cps)
        cps->useSigterm = FALSE;\r
     }\r
 \r
+    /* [HGM] Allow engine to set up a position. Don't ask me why one would\r
+     * want this, I was asked to put it in, and obliged.\r
+     */\r
+    if (!strncmp(message, "setboard ", 9)) {\r
+        Board initial_position; int i;\r
+\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
+            return ;\r
+        } else {\r
+           Reset(FALSE, FALSE);\r
+           CopyBoard(boards[0], initial_position);\r
+           initialRulePlies = FENrulePlies;\r
+           epStatus[0] = FENepStatus;\r
+           for( i=0; i<nrCastlingRights; i++ )\r
+                castlingRights[0][i] = FENcastlingRights[i];\r
+           if(blackPlaysFirst) gameMode = MachinePlaysWhite;\r
+           else gameMode = MachinePlaysBlack;                 \r
+           DrawPosition(FALSE, boards[currentMove]);\r
+        }\r
+       return;\r
+    }\r
+\r
     /*\r
      * Look for communication commands\r
      */\r
@@ -4868,11 +5647,8 @@ HandleMachineMove(message, cps)
         /* [HGM] illegal-move claim should forfeit game when Xboard */\r
         /* only passes fully legal moves                            */\r
         if( appData.testLegality && gameMode == TwoMachinesPlay ) {\r
-            static char buf[MSG_SIZ];\r
-            sprintf(buf, "False illegal-move claim on %s (%c%c%c%c)%c",\r
-                    machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0);\r
             GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,\r
-                                buf, GE_XBOARD );\r
+                                "False illegal-move claim", GE_XBOARD );\r
         }\r
        return;\r
     }\r
@@ -4986,6 +5762,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
@@ -5083,11 +5863,16 @@ 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
@@ -5386,12 +6171,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
@@ -5419,10 +6208,9 @@ ParseGameHistory(game)
           case BlackHSideCastleFR:\r
           case BlackASideCastleFR:\r
           /* POP Fabien */\r
-         case IllegalMove:             /* maybe suicide chess, etc. */\r
-           fromX = currentMoveString[0] - 'a';\r
+            fromX = currentMoveString[0] - AAA;\r
             fromY = currentMoveString[1] - ONE;\r
-           toX = currentMoveString[2] - 'a';\r
+            toX = currentMoveString[2] - AAA;\r
             toY = currentMoveString[3] - ONE;\r
            promoChar = currentMoveString[4];\r
            break;\r
@@ -5432,18 +6220,28 @@ ParseGameHistory(game)
              (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
            (int) CharToPiece(ToLower(currentMoveString[0]));\r
            fromY = DROP_RANK;\r
-           toX = currentMoveString[2] - 'a';\r
+            toX = currentMoveString[2] - AAA;\r
             toY = currentMoveString[3] - ONE;\r
            promoChar = NULLCHAR;\r
            break;\r
          case AmbiguousMove:\r
            /* bug? */\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
+  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
@@ -5516,7 +6314,8 @@ ParseGameHistory(game)
          default:\r
            break;\r
          case MT_CHECK:\r
-           strcat(parseList[boardIndex - 1], "+");\r
+            if(gameInfo.variant != VariantShogi)\r
+                strcat(parseList[boardIndex - 1], "+");\r
            break;\r
          case MT_CHECKMATE:\r
            strcat(parseList[boardIndex - 1], "#");\r
@@ -5533,102 +6332,129 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      int promoChar;\r
      Board board;\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
+  ChessSquare captured = board[toY][toX], piece, king; int p;\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
+      epStatus[p] = EP_NONE;\r
+\r
+      if( board[toY][toX] != EmptySquare ) \r
+           epStatus[p] = EP_CAPTURE;  \r
+\r
+      if( board[fromY][fromX] == WhitePawn ) {\r
+           epStatus[p] = EP_PAWN_MOVE; \r
+           if( toY-fromY==2 &&\r
+               (toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn ||\r
+                toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn ) )\r
+              epStatus[p] = toX;\r
+      } else \r
+      if( board[fromY][fromX] == BlackPawn ) {\r
+           epStatus[p] = EP_PAWN_MOVE; \r
+           if( toY-fromY== -2 &&\r
+               (toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn ||\r
+                toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn ) )\r
+              epStatus[p] = toX;\r
+       }\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 = PieceToChar(WhiteFerz);\r
          \r
-    ChessSquare captured = board[toY][toX];\r
-    if (fromY == DROP_RANK) {\r
+  if (fromX == toX && fromY == toY) return;\r
+\r
+  if (fromY == DROP_RANK) {\r
        /* must be first */\r
-       board[toY][toX] = (ChessSquare) fromX;\r
-    } else if (fromX == toX && fromY == toY) {\r
-       return;\r
-    }\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
-    else if (board[fromY][fromX] == WhiteKing &&\r
+    if (board[fromY][fromX] == WhiteKing &&\r
             board[toY][toX] == WhiteRook) {\r
       board[fromY][fromX] = EmptySquare;\r
       board[toY][toX] = EmptySquare;\r
       if(toX > fromX) {\r
-       board[0][BOARD_WIDTH-2] = WhiteKing; board[0][BOARD_WIDTH-3] = WhiteRook;\r
+        board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook;\r
       } else {\r
-       board[0][2] = WhiteKing; board[0][3] = WhiteRook;\r
+        board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook;\r
       }\r
     } else if (board[fromY][fromX] == BlackKing &&\r
               board[toY][toX] == BlackRook) {\r
       board[fromY][fromX] = EmptySquare;\r
       board[toY][toX] = EmptySquare;\r
       if(toX > fromX) {\r
-       board[BOARD_HEIGHT-1][BOARD_WIDTH-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_WIDTH-3] = BlackRook;\r
+        board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook;\r
       } else {\r
-       board[BOARD_HEIGHT-1][2] = BlackKing; board[BOARD_HEIGHT-1][3] = BlackRook;\r
+        board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook;\r
       }\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[fromY][BOARD_WIDTH-1] = EmptySquare;\r
-        board[toY][toX-1] = WhiteRook;\r
-    } else if (initialPosition[fromY][fromX] == WhiteKing\r
-              && board[fromY][fromX] == WhiteKing\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\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[fromY][0] = 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
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
     } else if (board[fromY][fromX] == WhitePawn\r
                && toY == BOARD_HEIGHT-1\r
-#ifdef FAIRY\r
                && gameInfo.variant != VariantXiangqi\r
-#endif\r
                ) {\r
        /* white pawn promotion */\r
         board[toY][toX] = CharToPiece(ToUpper(promoChar));\r
         if (board[toY][toX] == EmptySquare) {\r
             board[toY][toX] = WhiteQueen;\r
        }\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == BOARD_HEIGHT-4)\r
               && (toX != fromX)\r
+               && gameInfo.variant != VariantXiangqi\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 (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[fromY][BOARD_WIDTH-1] = EmptySquare;\r
-        board[toY][toX-1] = BlackRook;\r
-    } else if (initialPosition[fromY][fromX] == BlackKing\r
-              && board[fromY][fromX] == BlackKing\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\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[fromY][0] = EmptySquare;\r
-        board[toY][toX+1] = BlackRook;\r
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
     } else if (fromY == 7 && fromX == 3\r
               && board[fromY][fromX] == BlackKing\r
               && toY == 7 && toX == 5) {\r
@@ -5645,18 +6471,20 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][2] = BlackRook;\r
     } else if (board[fromY][fromX] == BlackPawn\r
               && toY == 0\r
-#ifdef FAIRY\r
                && gameInfo.variant != VariantXiangqi\r
-#endif\r
                ) {\r
        /* black pawn promotion */\r
        board[0][toX] = CharToPiece(ToLower(promoChar));\r
        if (board[0][toX] == EmptySquare) {\r
            board[0][toX] = BlackQueen;\r
        }\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == 3)\r
               && (toX != fromX)\r
+               && gameInfo.variant != VariantXiangqi\r
               && (board[fromY][fromX] == BlackPawn)\r
               && (board[toY][toX] == EmptySquare)) {\r
        board[fromY][fromX] = EmptySquare;\r
@@ -5667,28 +6495,70 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = board[fromY][fromX];\r
        board[fromY][fromX] = EmptySquare;\r
     }\r
-    if (gameInfo.variant == VariantCrazyhouse) {\r
-#if 0\r
-      /* !!A lot more code needs to be written to support holdings */\r
+\r
+    /* [HGM] now we promote for Shogi, if needed */\r
+    if(gameInfo.variant == VariantShogi && promoChar == 'q')\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+  }\r
+\r
+    if (gameInfo.holdingsWidth != 0) {\r
+\r
+      /* !!A lot more code needs to be written to support holdings  */\r
+      /* [HGM] OK, so I have written it. Holdings are stored in the */\r
+      /* penultimate board files, so they are automaticlly stored   */\r
+      /* in the game history.                                       */\r
       if (fromY == DROP_RANK) {\r
-       /* Delete from holdings */\r
-       if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;\r
+        /* Delete from holdings, by decreasing count */\r
+        /* and erasing image if necessary            */\r
+        p = (int) fromX;\r
+        if(p < (int) BlackPawn) { /* white drop */\r
+             p -= (int)WhitePawn;\r
+             if(p >= gameInfo.holdingsSize) p = 0;\r
+             if(--board[p][BOARD_WIDTH-2] == 0)\r
+                  board[p][BOARD_WIDTH-1] = EmptySquare;\r
+        } else {                  /* black drop */\r
+             p -= (int)BlackPawn;\r
+             if(p >= gameInfo.holdingsSize) p = 0;\r
+             if(--board[BOARD_HEIGHT-1-p][1] == 0)\r
+                  board[BOARD_HEIGHT-1-p][0] = EmptySquare;\r
+        }\r
       }\r
-      if (captured != EmptySquare) {\r
-       /* Add to holdings */\r
-       if (captured < BlackPawn) {\r
-         holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;\r
+      if (captured != EmptySquare && gameInfo.holdingsSize > 0\r
+          && gameInfo.variant != VariantBughouse        ) {\r
+        /* Add to holdings, if holdings exist */\r
+        p = (int) captured;\r
+        if (p >= (int) BlackPawn) {\r
+          p -= (int)BlackPawn;\r
+          if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {\r
+                  /* in Shogi restore piece to its original  first */\r
+                  captured = (ChessSquare) (DEMOTED captured);\r
+                  p = DEMOTED p;\r
+          }\r
+          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
        } else {\r
-         holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;\r
+          p -= (int)WhitePawn;\r
+          if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {\r
+                  captured = (ChessSquare) (DEMOTED captured);\r
+                  p = DEMOTED p;\r
+          }\r
+          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
        }\r
       }\r
-#endif\r
+\r
     } else if (gameInfo.variant == VariantAtomic) {\r
       if (captured != EmptySquare) {\r
        int y, x;\r
        for (y = toY-1; y <= toY+1; y++) {\r
          for (x = toX-1; x <= toX+1; x++) {\r
-           if (y >= 0 && y < BOARD_WIDTH && x >= 0 && x < BOARD_WIDTH &&\r
+            if (y >= 0 && y < BOARD_HEIGHT && x >= BOARD_LEFT && x < BOARD_RGHT &&\r
                board[y][x] != WhitePawn && board[y][x] != BlackPawn) {\r
              board[y][x] = EmptySquare;\r
            }\r
@@ -5697,6 +6567,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = EmptySquare;\r
       }\r
     }\r
+    if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') {\r
+        /* [HGM] Shogi promotions */\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+    }\r
+\r
 }\r
 \r
 /* Updates forwardMostMove */\r
@@ -5707,6 +6582,48 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
 {\r
     forwardMostMove++;\r
 \r
+    if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting */\r
+        int timeLeft; static int lastLoadFlag=0; int king, piece;\r
+        piece = boards[forwardMostMove-1][fromY][fromX];\r
+        king = piece < (int) BlackPawn ? WhiteKing : BlackKing;\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            king += (int) WhiteUnicorn - (int) WhiteKing;\r
+        if(forwardMostMove == 1) {\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-1][fromY][fromX] == WhitePawn ||\r
+             boards[forwardMostMove-1][fromY][fromX] == BlackPawn   ) &&\r
+             boards[forwardMostMove-1][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-1].depth, pvInfoList[forwardMostMove-1].score);\r
+            if(forwardMostMove & 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
                        0, 1);\r
@@ -5720,38 +6637,6 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
        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>1 && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
-                toX<BOARD_WIDTH-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>1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
-                toX<BOARD_WIDTH-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
     gameInfo.result = GameUnfinished;\r
     if (gameInfo.resultDetails != NULL) {\r
@@ -5772,12 +6657,17 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
       default:\r
        break;\r
       case MT_CHECK:\r
-       strcat(parseList[forwardMostMove - 1], "+");\r
+        if(gameInfo.variant != VariantShogi)\r
+            strcat(parseList[forwardMostMove - 1], "+");\r
        break;\r
       case MT_CHECKMATE:\r
        strcat(parseList[forwardMostMove - 1], "#");\r
        break;\r
     }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "move: %s, parse: %s (%c)\n", moveList[forwardMostMove-1], parseList[forwardMostMove-1], moveList[forwardMostMove-1][4]);\r
+    }\r
+\r
 }\r
 \r
 /* Updates currentMove if not pausing */\r
@@ -5809,24 +6699,67 @@ ShowMove(fromX, fromY, toX, toY)
 \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];\r
+    char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;\r
     if (appData.noChessProgram) return;\r
     hintRequested = FALSE;\r
     bookRequested = FALSE;\r
     SendToProgram(cps->initString, cps);\r
     if (gameInfo.variant != VariantNormal &&\r
-       gameInfo.variant != VariantLoadable) {\r
+       gameInfo.variant != VariantLoadable\r
+        /* [HGM] also send variant if board size non-standard */\r
+        || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8\r
+                                            ) {\r
       char *v = VariantName(gameInfo.variant);\r
-      if (StrStr(cps->variants, v) == NULL) {\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
        DisplayFatalError(buf, 0, 1);\r
        return;\r
       }\r
-      sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));\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
+           overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantShogi )\r
+           overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;\r
+      if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )\r
+           overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;\r
+      if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantGothic  || gameInfo.variant == VariantFalcon )\r
+           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
+\r
+      if(overruled) {\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
+      } else sprintf(b, "%s", VariantName(gameInfo.variant));\r
+      sprintf(buf, "variant %s\n", b);\r
       SendToProgram(buf, cps);\r
+      /* [HGM] send opening position in FRC to first engine */\r
+      if(setup /* cps == &first && gameInfo.variant == VariantFischeRandom */) {\r
+          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
@@ -5961,12 +6894,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
@@ -5983,7 +6919,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
@@ -5993,7 +6930,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
@@ -6024,14 +6961,19 @@ GameEnds(result, resultDetails, whosays)
                 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
+                if( gameInfo.holdingsWidth == 0 &&\r
+                    (result == WhiteWins && claimer == 'w' ||\r
+                     result == BlackWins && claimer == 'b'   ) ) {\r
                       /* Xboard immediately adjudicates all mates, so win claims must be false */\r
                       sprintf(buf, "False win claim: '%s'", resultDetails);\r
                       result = claimer == 'w' ? BlackWins : WhiteWins;\r
                       resultDetails = buf;\r
                 } else\r
-                if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS ) {\r
+                if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS\r
+                    && (forwardMostMove <= backwardMostMove ||\r
+                        epStatus[forwardMostMove-1] > EP_DRAWS ||\r
+                        (claimer=='b')==(forwardMostMove&1))\r
+                                                                                  ) {\r
                       /* Draw that was not flagged by Xboard is false */\r
                       sprintf(buf, "False draw claim: '%s'", resultDetails);\r
                       result = claimer == 'w' ? BlackWins : WhiteWins;\r
@@ -6040,6 +6982,12 @@ GameEnds(result, resultDetails, whosays)
                 /* (Claiming a loss is accepted no questions asked!) */\r
         }\r
 \r
+        if(serverMoves != NULL && !loadFlag) { char c = '=';\r
+            if(result==WhiteWins) c = '+';\r
+            if(result==BlackWins) c = '-';\r
+            if(resultDetails != NULL)\r
+                fprintf(serverMoves, ";%c;%s\n", c, resultDetails);\r
+        }\r
     if (appData.debugMode) {\r
       fprintf(debugFP, "GameEnds(%d, %s, %d) after test\n",\r
              result, resultDetails ? resultDetails : "(null)", whosays);\r
@@ -6048,7 +6996,30 @@ GameEnds(result, resultDetails, whosays)
            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
@@ -6066,23 +7037,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
@@ -6140,7 +7094,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
@@ -6229,6 +7184,7 @@ GameEnds(result, resultDetails, whosays)
             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
@@ -6245,6 +7201,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
@@ -6263,6 +7220,9 @@ FeedMovesToProgram(cps, upto)
     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
@@ -6279,7 +7239,10 @@ ResurrectChessProgram()
     if (appData.noChessProgram || first.pr != NoProc) return;\r
     \r
     StartChessProgram(&first);\r
-    InitChessProgram(&first);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From ResurrectChessProgram\n");\r
+    }\r
+    InitChessProgram(&first, FALSE);\r
     FeedMovesToProgram(&first, currentMove);\r
 \r
     if (!first.sendTime) {\r
@@ -6310,7 +7273,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
@@ -6337,6 +7299,19 @@ 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
@@ -6353,7 +7328,11 @@ Reset(redraw, init)
     if (first.pr == NULL) {\r
        StartChessProgram(&first);\r
     }\r
-    if (init) InitChessProgram(&first);\r
+    if (init) {\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From Reset\n");\r
+    }\r
+InitChessProgram(&first, startedFromSetupPosition);}\r
     DisplayTitle("");\r
     DisplayMessage("", "");\r
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
@@ -6397,7 +7376,7 @@ AutoPlayOneMove()
       return FALSE;\r
     }\r
     \r
-    toX = moveList[currentMove][2] - 'a';\r
+    toX = moveList[currentMove][2] - AAA;\r
     toY = moveList[currentMove][3] - ONE;\r
 \r
     if (moveList[currentMove][1] == '@') {\r
@@ -6405,7 +7384,7 @@ AutoPlayOneMove()
            SetHighlights(-1, -1, toX, toY);\r
        }\r
     } else {\r
-       fromX = moveList[currentMove][0] - 'a';\r
+        fromX = moveList[currentMove][0] - AAA;\r
         fromY = moveList[currentMove][1] - ONE;\r
 \r
         HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */\r
@@ -6420,9 +7399,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
@@ -6470,12 +7448,10 @@ LoadGameOneMove(readAhead)
 \r
       case WhiteCapturesEnPassant:\r
       case BlackCapturesEnPassant:\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
@@ -6503,9 +7479,9 @@ LoadGameOneMove(readAhead)
       /* POP Fabien */\r
        if (appData.debugMode)\r
          fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);\r
-       fromX = currentMoveString[0] - 'a';\r
+        fromX = currentMoveString[0] - AAA;\r
         fromY = currentMoveString[1] - ONE;\r
-       toX = currentMoveString[2] - 'a';\r
+        toX = currentMoveString[2] - AAA;\r
         toY = currentMoveString[3] - ONE;\r
        promoChar = currentMoveString[4];\r
        break;\r
@@ -6518,7 +7494,7 @@ LoadGameOneMove(readAhead)
          (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
        (int) CharToPiece(ToLower(currentMoveString[0]));\r
        fromY = DROP_RANK;\r
-       toX = currentMoveString[2] - 'a';\r
+        toX = currentMoveString[2] - AAA;\r
         toY = currentMoveString[3] - ONE;\r
        break;\r
 \r
@@ -6627,9 +7603,9 @@ LoadGameOneMove(readAhead)
            if (appData.debugMode)\r
              fprintf(debugFP, "Parsed %s into IllegalMove %s\n",\r
                      yy_text, currentMoveString);\r
-           fromX = currentMoveString[0] - 'a';\r
+            fromX = currentMoveString[0] - AAA;\r
             fromY = currentMoveString[1] - ONE;\r
-           toX = currentMoveString[2] - 'a';\r
+            toX = currentMoveString[2] - AAA;\r
             toY = currentMoveString[3] - ONE;\r
            promoChar = currentMoveString[4];\r
        }\r
@@ -6661,7 +7637,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
@@ -6738,9 +7714,9 @@ MakeRegisteredMove()
     \r
            thinkOutput[0] = NULLCHAR;\r
            strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);\r
-           fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';\r
+            fromX = cmailMove[lastLoadGameNumber - 1][0] - AAA;\r
             fromY = cmailMove[lastLoadGameNumber - 1][1] - ONE;\r
-           toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';\r
+            toX = cmailMove[lastLoadGameNumber - 1][2] - AAA;\r
             toY = cmailMove[lastLoadGameNumber - 1][3] - ONE;\r
            promoChar = cmailMove[lastLoadGameNumber - 1][4];\r
            MakeMove(fromX, fromY, toX, toY, promoChar);\r
@@ -6872,6 +7848,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
@@ -6921,7 +7898,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
@@ -7080,6 +8056,16 @@ 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
@@ -7167,7 +8153,7 @@ LoadGame(f, gameNumber, title, useList)
        if (!startedFromSetupPosition) {\r
            p = yy_text;\r
             for (i = BOARD_HEIGHT - 1; i >= 0; i--)\r
-              for (j = 0; j < BOARD_WIDTH; p++)\r
+              for (j = BOARD_LEFT; j < BOARD_RGHT; p++)\r
                switch (*p) {\r
                  case '[':\r
                  case '-':\r
@@ -7212,13 +8198,22 @@ LoadGame(f, gameNumber, title, useList)
     if (first.pr == NoProc) {\r
        StartChessProgram(&first);\r
     }\r
-    InitChessProgram(&first);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From LoadGame\n");\r
+    }\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
@@ -7253,10 +8248,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
@@ -7297,6 +8291,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
@@ -7368,7 +8364,10 @@ LoadPosition(f, positionNumber, title)
     strcpy(lastLoadPositionTitle, title);\r
     if (first.pr == NoProc) {\r
       StartChessProgram(&first);\r
-      InitChessProgram(&first);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From LoadPosition\n");\r
+    }\r
+      InitChessProgram(&first, FALSE);\r
     }    \r
     pn = positionNumber;\r
     if (positionNumber < 0) {\r
@@ -7395,6 +8394,7 @@ LoadPosition(f, positionNumber, title)
        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
@@ -7404,14 +8404,16 @@ 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
@@ -7437,7 +8439,7 @@ LoadPosition(f, positionNumber, title)
     \r
         for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
            (void) fgets(line, MSG_SIZ, f);\r
-            for (p = line, j = 0; j < BOARD_WIDTH; p++) {\r
+            for (p = line, j = BOARD_LEFT; j < BOARD_RGHT; p++) {\r
                if (*p == ' ')\r
                  continue;\r
                initial_position[i][j++] = CharToPiece(*p);\r
@@ -7473,6 +8475,9 @@ LoadPosition(f, positionNumber, title)
        DisplayMessage("", "White to play");\r
     }\r
     SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Load Position\n");\r
+    }\r
 \r
     if (positionNumber > 1) {\r
        sprintf(line, "%s %d", title, positionNumber);\r
@@ -7732,10 +8737,11 @@ SaveGamePGN(f)
             if(i >= backwardMostMove) {\r
                 /* take the time that changed */\r
                 seconds = timeRemaining[0][i] - timeRemaining[0][i+1];\r
-                if(seconds <= 0)\r
+              if(seconds <= 0)\r
                     seconds = timeRemaining[1][i] - timeRemaining[1][i+1];\r
             }\r
             seconds /= 1000;\r
+            seconds = pvInfoList[i].time/100;\r
     if (appData.debugMode) {\r
         fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
                 timeRemaining[0][i+1], timeRemaining[0][i],\r
@@ -8263,8 +9269,6 @@ ResetGameEvent()
     }\r
 }\r
 \r
-static int exiting = 0;\r
-\r
 void\r
 ExitEvent(status)\r
      int status;\r
@@ -8287,8 +9291,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
@@ -8299,6 +9305,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 ? "aborted" : 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
@@ -8672,10 +9689,16 @@ TwoMachinesEvent P((void))
        return;\r
     }\r
     DisplayMessage("", "");\r
-    InitChessProgram(&second);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From TwoMachines\n");\r
+    }\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
@@ -8911,7 +9934,10 @@ void
 EditPositionDone()\r
 {\r
     startedFromSetupPosition = TRUE;\r
-    InitChessProgram(&first);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "From EditPosition\n");\r
+    }\r
+    InitChessProgram(&first, FALSE);\r
     SendToProgram("force\n", &first);\r
     if (blackPlaysFirst) {\r
        strcpy(moveList[0], "");\r
@@ -8922,6 +9948,9 @@ EditPositionDone()
        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
@@ -9004,6 +10033,7 @@ EditPositionMenuEvent(selection, x, y)
      int x, y;\r
 {\r
     char buf[MSG_SIZ];\r
+    ChessSquare piece = boards[0][y][x];\r
 \r
     if (gameMode != EditPosition && gameMode != IcsExamining) return;\r
 \r
@@ -9021,7 +10051,7 @@ EditPositionMenuEvent(selection, x, y)
                    if (gameMode == IcsExamining) {\r
                        if (boards[currentMove][y][x] != EmptySquare) {\r
                            sprintf(buf, "%sx@%c%c\n", ics_prefix,\r
-                                    'a' + x, ONE + y);\r
+                                    AAA + x, ONE + y);\r
                            SendToICS(buf);\r
                        }\r
                    } else {\r
@@ -9045,7 +10075,7 @@ EditPositionMenuEvent(selection, x, y)
 \r
       case EmptySquare:\r
        if (gameMode == IcsExamining) {\r
-            sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, ONE + y);\r
+            sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);\r
            SendToICS(buf);\r
        } else {\r
            boards[0][y][x] = EmptySquare;\r
@@ -9053,10 +10083,41 @@ EditPositionMenuEvent(selection, x, y)
        }\r
        break;\r
 \r
+      case PromotePiece:\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 = WhiteSilver;\r
+        else selection = (ChessSquare)((int)piece - 1);\r
+        goto defaultlabel;\r
+\r
+      case DemotePiece:\r
+        if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||\r
+           piece > (int)BlackMan && piece <= (int)BlackKing   ) {\r
+            selection = (ChessSquare) (DEMOTED piece);\r
+        } else if(piece == EmptySquare) selection = BlackSilver;\r
+        else selection = (ChessSquare)((int)piece + 1);       \r
+        goto defaultlabel;\r
+\r
+      case WhiteQueen:\r
+      case BlackQueen:\r
+        if(gameInfo.variant == VariantShatranj ||\r
+           gameInfo.variant == VariantXiangqi  ||\r
+           gameInfo.variant == VariantCourier    )\r
+            selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);\r
+        goto defaultlabel;\r
+\r
+      case WhiteKing:\r
+      case BlackKing:\r
+        if(gameInfo.variant == VariantXiangqi)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);\r
       default:\r
+        defaultlabel:\r
        if (gameMode == IcsExamining) {\r
            sprintf(buf, "%s%c@%c%c\n", ics_prefix,\r
-                    PieceToChar(selection), 'a' + x, ONE + y);\r
+                    PieceToChar(selection), AAA + x, ONE + y);\r
            SendToICS(buf);\r
        } else {\r
            boards[0][y][x] = selection;\r
@@ -9350,14 +10411,14 @@ ForwardInner(target)
 \r
     if (target > 0 && moveList[target - 1][0]) {\r
        int fromX, fromY, toX, toY;\r
-       toX = moveList[target - 1][2] - 'a';\r
+        toX = moveList[target - 1][2] - AAA;\r
         toY = moveList[target - 1][3] - ONE;\r
        if (moveList[target - 1][1] == '@') {\r
            if (appData.highlightLastMove) {\r
                SetHighlights(-1, -1, toX, toY);\r
            }\r
        } else {\r
-           fromX = moveList[target - 1][0] - 'a';\r
+            fromX = moveList[target - 1][0] - AAA;\r
             fromY = moveList[target - 1][1] - ONE;\r
            if (target == currentMove + 1) {\r
                AnimateMove(boards[currentMove], fromX, fromY, toX, toY);\r
@@ -9385,7 +10446,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
@@ -9453,14 +10514,14 @@ BackwardInner(target)
     \r
     if (moveList[target][0]) {\r
        int fromX, fromY, toX, toY;\r
-       toX = moveList[target][2] - 'a';\r
+        toX = moveList[target][2] - AAA;\r
         toY = moveList[target][3] - ONE;\r
        if (moveList[target][1] == '@') {\r
            if (appData.highlightLastMove) {\r
                SetHighlights(-1, -1, toX, toY);\r
            }\r
        } else {\r
-           fromX = moveList[target][0] - 'a';\r
+            fromX = moveList[target][0] - AAA;\r
             fromY = moveList[target][1] - ONE;\r
            if (target == currentMove - 1) {\r
                AnimateMove(boards[currentMove], toX, toY, fromX, fromY);\r
@@ -9488,9 +10549,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
@@ -9733,10 +10793,10 @@ PrintPosition(fp, move)
     int i, j;\r
     \r
     for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
-        for (j = 0; j < BOARD_WIDTH; j++) {\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {\r
            char c = PieceToChar(boards[move][i][j]);\r
            fputc(c == 'x' ? '.' : c, fp);\r
-            fputc(j == BOARD_WIDTH - 1 ? '\n' : ' ', fp);\r
+            fputc(j == BOARD_RGHT - 1 ? '\n' : ' ', fp);\r
        }\r
     }\r
     if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))\r
@@ -9777,6 +10837,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
@@ -9937,7 +10998,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
@@ -9975,12 +11036,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;\r
         char * s_eval = FindStr( text, "[%eval " );\r
         char * s_emt = FindStr( text, "[%emt " );\r
 \r
@@ -9990,11 +11054,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
@@ -10003,26 +11067,38 @@ 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;\r
+            if( sscanf( text, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 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 = 60*time + sec;\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
+            while(*sep == ' ') sep++;\r
         }\r
 \r
         if( depth <= 0 ) {\r
-            return;\r
+            return text;\r
         }\r
 \r
         if( time < 0 ) {\r
@@ -10031,8 +11107,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  = time;\r
     }\r
+    return sep;\r
 }\r
 \r
 void\r
@@ -10056,9 +11133,19 @@ SendToProgram(message, cps)
     \r
     count = strlen(message);\r
     outCount = OutputToProcess(cps->pr, message, count, &error);\r
-    if (outCount < count && !exiting) {\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
-       DisplayFatalError(buf, error, 1);\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
@@ -10080,6 +11167,15 @@ ReceiveFromProgram(isr, closure, message, count, error)
            sprintf(buf,\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
@@ -10096,7 +11192,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
@@ -10123,13 +11218,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
@@ -10170,6 +11270,14 @@ SendTimeControl(cps, mps, tc, inc, sd, st)
     }\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
+        return &second;\r
+    return &first;\r
+}\r
+\r
 void\r
 SendTimeRemaining(cps, machineWhite)\r
      ChessProgramState *cps;\r
@@ -10189,6 +11297,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
@@ -10320,6 +11434,8 @@ ParseFeatures(args, cps)
     if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;\r
     if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;\r
     if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */\r
+    if (BoolFeature(&p, "debug", &cps->debug, cps)) continue;\r
+    if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;\r
     if (IntFeature(&p, "done", &val, cps)) {\r
       FeatureDone(cps, val);\r
       continue;\r
@@ -10566,6 +11682,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
@@ -10575,9 +11693,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, 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
@@ -10670,30 +11797,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 fot 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
@@ -10784,6 +11899,15 @@ NextTickLength(timeRemaining)
     return nextTickLength;\r
 }\r
 \r
+/* Adjust clock one minute up or down */\r
+void\r
+AdjustClock(Boolean which, int dir)\r
+{\r
+    if(which) blackTimeRemaining += 60000*dir;\r
+    else      whiteTimeRemaining += 60000*dir;\r
+    DisplayBothClocks();\r
+}\r
+\r
 /* Stop clocks and reset to a fresh time control */\r
 void\r
 ResetClocks() \r
@@ -10791,9 +11915,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
@@ -10883,12 +12007,17 @@ SwitchClocks()
        lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
        if (WhiteOnMove(forwardMostMove)) {\r
            blackTimeRemaining -= lastTickLength;\r
+           /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
+            if(pvInfoList[forwardMostMove-1].time == -1)\r
+                 pvInfoList[forwardMostMove-1].time = \r
+                      (timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;\r
        } else {\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
@@ -11124,6 +12253,7 @@ PositionToFEN(move, useFEN960)
     char buf[128];\r
     char *p, *q;\r
     int emptycount;\r
+    ChessSquare piece;\r
 \r
     whiteToPlay = (gameMode == EditPosition) ?\r
       !blackPlaysFirst : (move % 2 == 0);\r
@@ -11132,17 +12262,27 @@ PositionToFEN(move, useFEN960)
     /* Piece placement data */\r
     for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
        emptycount = 0;\r
-        for (j = 0; j < BOARD_WIDTH; j++) {\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {\r
            if (boards[move][i][j] == EmptySquare) {\r
                emptycount++;\r
-           } else {\r
+            } else { ChessSquare piece = boards[move][i][j];\r
                if (emptycount > 0) {\r
                     if(emptycount<10) /* [HGM] can be >= 10 */\r
                         *p++ = '0' + emptycount;\r
                     else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
                    emptycount = 0;\r
                }\r
-               *p++ = PieceToChar(boards[move][i][j]);\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(p[-1] == '~') {\r
+                    /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */\r
+                    p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
+                    *p++ = '~';\r
+                }\r
            }\r
        }\r
        if (emptycount > 0) {\r
@@ -11155,113 +12295,80 @@ 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 = 1; fk < BOARD_WIDTH-1; fk++) {\r
-\r
-          if (boards[move][0][fk] == WhiteKing) {\r
-\r
-             for (fr = BOARD_WIDTH-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 = 0; 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 = 1; fk < BOARD_WIDTH-1; fk++) {\r
-\r
-          if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) {\r
-\r
-             for (fr = BOARD_WIDTH-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 = 0; 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
+  if(nrCastlingRights) {\r
+     q = p;\r
+     if(gameInfo.variant == VariantFischeRandom) {\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
-#ifdef OLDCASTLINGCODE\r
-        if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) {\r
-           if (boards[move][0][BOARD_WIDTH-1] == WhiteRook) *p++ = 'K';\r
-           if (boards[move][0][0] == 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][0] == BlackRook) *p++ = 'q';\r
-        }          \r
-#else\r
         /* [HGM] write true castling rights */\r
         if( nrCastlingRights == 6 ) {\r
-            if(castlingRights[move][0] == BOARD_WIDTH-1 &&\r
+            if(castlingRights[move][0] == BOARD_RGHT-1 &&\r
                castlingRights[move][2] >= 0  ) *p++ = 'K';\r
-            if(castlingRights[move][1] == 0 &&\r
+            if(castlingRights[move][1] == BOARD_LEFT &&\r
                castlingRights[move][2] >= 0  ) *p++ = 'Q';\r
-            if(castlingRights[move][3] == BOARD_WIDTH-1 &&\r
+            if(castlingRights[move][3] == BOARD_RGHT-1 &&\r
                castlingRights[move][5] >= 0  ) *p++ = 'k';\r
-            if(castlingRights[move][4] == 0 &&\r
+            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] - 'a';\r
+        fromX = moveList[move - 1][0] - AAA;\r
         fromY = moveList[move - 1][1] - ONE;\r
-       toX = moveList[move - 1][2] - 'a';\r
+        toX = moveList[move - 1][2] - AAA;\r
         toY = moveList[move - 1][3] - ONE;\r
        if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) &&\r
            toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) &&\r
            boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&\r
            fromX == toX) {\r
            /* 2-square pawn move just happened */\r
-           *p++ = toX + 'a';\r
+            *p++ = toX + AAA;\r
            *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';\r
        } else {\r
            *p++ = '-';\r
@@ -11269,24 +12376,26 @@ PositionToFEN(move, useFEN960)
     } else {\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
@@ -11300,32 +12409,59 @@ ParseFEN(board, blackPlaysFirst, fen)
     int i, j;\r
     char *p;\r
     int emptycount;\r
+    ChessSquare piece;\r
 \r
     p = fen;\r
 \r
+    /* [HGM] by default clear Crazyhouse holdings, if present */\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
     /* 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
-                emptycount = BOARD_WIDTH - j;\r
-               while (emptycount--) board[i][j++] = EmptySquare;\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
                break;\r
 #if(BOARD_SIZE >= 10)\r
             } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */\r
                 p++; emptycount=10;\r
-                if (j + emptycount > BOARD_WIDTH) return FALSE;\r
-               while (emptycount--) board[i][j++] = EmptySquare;\r
+                if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
 #endif\r
             } else if (isdigit(*p)) {\r
                emptycount = *p++ - '0';\r
                 while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */\r
-                if (j + emptycount > BOARD_WIDTH) return FALSE;\r
-               while (emptycount--) board[i][j++] = EmptySquare;\r
-           } else if (isalpha(*p)) {\r
-                if (j >= BOARD_WIDTH) return FALSE;\r
-               board[i][j++] = CharToPiece(*p++);\r
+                if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
+            } else if (*p == '+' || isalpha(*p)) {\r
+                if (j >= gameInfo.boardWidth) return FALSE;\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
            } else {\r
                return FALSE;\r
            }\r
@@ -11333,6 +12469,34 @@ 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
+                    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(*p == ']') *p++;\r
+    }\r
+\r
+    while(*p == ' ') p++;\r
+\r
     /* Active color */\r
     switch (*p++) {\r
       case 'w':\r
@@ -11347,68 +12511,118 @@ 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 ? -1 : initialRights[i];\r
     }   /* assume possible unless obviously impossible */\r
-    if(board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;\r
-    if(board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;\r
-    if(board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;\r
-    if(board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;\r
-    if(board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;\r
-    if(board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;\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
+    if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;\r
+    if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;\r
+    if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;\r
+    if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;\r
     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 impossible */\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 &&\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_WIDTH-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] = 0;\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_WIDTH-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] = 0;\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++ - 'a';\r
+      } else {\r
+         char c = *p++ - AAA;\r
 \r
-       if(c < 0 || c >= BOARD_WIDTH) return TRUE;\r
-       if(*p >= '0' && *p <='9') *p++;\r
-       FENepStatus = c;\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
     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