changes by H.G. Muller; version 4.3.4
[xboard.git] / backend.c
index 29ebca6..f3cffc5 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -421,36 +421,47 @@ 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
 #ifdef FAIRY\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
 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, WhiteFerz,\r
+        WhiteKing, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
+        BlackKing, 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
         WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
@@ -470,16 +481,17 @@ ChessSquare GothicArray[2][BOARD_SIZE] = {
 #endif // !GOTHIC\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
@@ -733,25 +745,31 @@ 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 VariantShowgi:     /* [HGM] not a valid variant */\r
+      case VariantKnightmate: /* [HGM] should work */\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 +780,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
@@ -1355,7 +1365,14 @@ 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+%d_", &i, &i, &i) == 3 ) {\r
+        while( *e++ != '_');\r
+    } else if( sscanf(e, "%dx%d_", &i, &i) == 2 ) {\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 +1469,46 @@ 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 = VariantShowgi;\r
          break;\r
 \r
        case -1:\r
@@ -1677,6 +1694,45 @@ 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 < 1)  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
+        if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
+        if(j < 0) continue;               /* should not happen */\r
+        piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
+        board[holdingsStartRow+i*direction][holdingsColumn] = piece;\r
+        board[holdingsStartRow+i*direction][countsColumn]++;\r
+    }\r
+\r
+}\r
+\r
 static int loggedOn = FALSE;\r
 \r
 /*-- Game start info cache: --*/\r
@@ -2725,9 +2781,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
@@ -2780,6 +2836,10 @@ read_from_ics(isr, closure, data, count, error)
                                    gameInfo.white, white_holding,\r
                                    gameInfo.black, black_holding);\r
                        }\r
+\r
+                        /* [HGM] copy holdings to board holdings area */\r
+                        CopyHoldings(boards[currentMove], white_holding, WhitePawn);\r
+                        CopyHoldings(boards[currentMove], black_holding, BlackPawn);\r
                        DrawPosition(FALSE, NULL);\r
                        DisplayTitle(str);\r
                    }\r
@@ -2997,6 +3057,20 @@ ParseBoard12(string)
        movesPerSession = 0;\r
        gameInfo.timeControl = TimeControlTagValue();\r
        gameInfo.variant = StringToVariant(gameInfo.event);\r
+        gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
+        switch(gameInfo.variant) {\r
+            case VariantShogi:\r
+            case VariantShowgi:\r
+              gameInfo.boardWidth = gameInfo.boardHeight = 9;\r
+              gameInfo.holdingsSize += 2;\r
+            case VariantBughouse:\r
+            case VariantCrazyhouse:\r
+              gameInfo.boardWidth = gameInfo.boardHeight = 8;\r
+              gameInfo.holdingsWidth = 2; break;\r
+            default:\r
+              gameInfo.boardWidth = gameInfo.boardHeight = 8;\r
+              gameInfo.holdingsWidth = 0;\r
+        }\r
         gameInfo.outOfBook = NULL;\r
        \r
        /* Do we have the ratings? */\r
@@ -3343,9 +3417,9 @@ SendMoveToProgram(moveNum, cps)
        * 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
+        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
@@ -3411,21 +3485,21 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       case BlackPromotionArchbishop:\r
 #endif\r
        sprintf(user_move, "%c%c%c%c=%c\n",\r
-                'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY,\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 +3513,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 +3540,22 @@ 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;\r
+\r
+    if( !appData.alphaRank ) return;\r
+\r
+    while(c = *p) {\r
+        if(c>='0' && c<='9') *p += 'a'-'0'; else\r
+        if(c>='a' && c<='z') *p -= 'a'-'0';\r
+        p++;\r
+        if(--n < 1) break;\r
+    }\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,6 +3565,10 @@ 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
+    AlphaRank(move, 10);\r
     *moveType = yylexstr(moveNum, move);\r
 \r
     switch (*moveType) {\r
@@ -3511,13 +3606,13 @@ 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
            *fromX = *fromY = *toX = *toY = 0;\r
            return FALSE;\r
        }\r
@@ -3533,7 +3628,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
@@ -3591,7 +3686,7 @@ static void ShuffleFRC( Board board )
     board[0][FindEmptySquare(board, 0)] = WhiteKing;\r
     board[0][FindEmptySquare(board, 0)] = WhiteRook;\r
 \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
@@ -3637,7 +3732,7 @@ static void SetupFRC( Board board, int pos_index )
     board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\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
@@ -3647,7 +3742,10 @@ InitPosition(redraw)
      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
 \r
     currentMove = forwardMostMove = backwardMostMove = 0;\r
 \r
@@ -3662,11 +3760,11 @@ InitPosition(redraw)
         /* [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][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_WIDTH-1;\r
-        castlingRights[0][4] = initialRights[4] = 0;\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
         castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
@@ -3683,15 +3781,17 @@ 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
+\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
       break;\r
@@ -3704,26 +3804,61 @@ InitPosition(redraw)
       castlingRank[6] = BOARD_HEIGHT-1;\r
       startedFromSetupPosition = TRUE;\r
       break;\r
-#ifdef FAIRY\r
     case VariantCapablanca:\r
       pieces = CapablancaArray;\r
+      gameInfo.boardWidth = 10;\r
       break;\r
     case VariantGothic:\r
       pieces = GothicArray;\r
+      gameInfo.boardWidth = 10;\r
       break;\r
     case VariantXiangqi:\r
       pieces = XiangqiArray;\r
+      gameInfo.boardWidth  = 9;\r
+      gameInfo.boardHeight = 10;\r
+      nrCastlingRights = 0;\r
+      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      strcpy(pieceToChar, "PN.R.MKE...C....pn.r.mke...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
+      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      strcpy(pieceToChar, "PNBRLSGPNBRLS..Kpnbrlsgpnbrls..k"); \r
+      break;\r
+    case VariantShowgi:\r
+      pieces = ShogiArray;\r
+      gameInfo.boardWidth  = 9;\r
+      gameInfo.boardHeight = 9;\r
+      gameInfo.holdingsSize = 7;\r
+      nrCastlingRights = 0;\r
+      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      strcpy(pieceToChar, "PNBRQFWEHACGOUMKpnbrlsgpnbrls..k"); \r
       break;\r
     case VariantCourier:\r
       pieces = CourierArray;\r
+      gameInfo.boardWidth  = 12;\r
       nrCastlingRights = 0;\r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       break;\r
+    case VariantKnightmate:\r
+      pieces = KnightmateArray;\r
+      strcpy(pieceToChar, "PNBRQFWEHACGOMK.pnbrqfwehacgomK."); \r
+      break;\r
     case VariantFairy:\r
       pieces = fairyArray;\r
+      strcpy(pieceToChar, "PNBRQFWEHACGOMUKpnbrqfwehacgomuk"); \r
       startedFromSetupPosition = TRUE;\r
       break;\r
-#endif\r
+    case VariantCrazyhouse:\r
+    case VariantBughouse:\r
+      pieces = FIDEArray;\r
+      gameInfo.holdingsSize = 5;\r
+      strcpy(pieceToChar, "PNBRQ...NBRQ...Kpnbrq...nbrq...k"); \r
+      break;\r
     case VariantWildCastle:\r
       pieces = FIDEArray;\r
       /* !!?shuffle with kings guaranteed to be on d or e file */\r
@@ -3736,27 +3871,62 @@ 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
+        if(gameInfo.boardHeight != appData.NrRanks) overrule++;\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 variants */\r
+\r
+    /* User pieceToChar list overrules defaults */\r
+    if(appData.pieceToCharTable != NULL)\r
+        strcpy(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\r
+       ||gameInfo.variant == VariantShowgi\r
+                                         ) && !overrule ) {\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(gameInfo.variant == VariantFischeRandom) {\r
@@ -3770,6 +3940,11 @@ InitPosition(redraw)
 \r
     CopyBoard(boards[0], initialPosition);\r
 \r
+    if(oldx != gameInfo.boardWidth ||\r
+       oldy != gameInfo.boardHeight ||\r
+       oldh != gameInfo.holdingsWidth )\r
+            InitDrawingSizes(-1 ,0);\r
+\r
     if (redraw)\r
       DrawPosition(TRUE, boards[currentMove]);\r
 }\r
@@ -3799,10 +3974,10 @@ 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
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -3811,11 +3986,11 @@ 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
            SendToProgram(message, cps);\r
          }\r
        }\r
@@ -3829,19 +4004,45 @@ int
 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)WhiteFerz; /* Silver */\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,17 +4159,19 @@ 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
 \r
-    if (fromX < 0 || fromY < 0) return;\r
+    if (fromX < 0 || fromY < 0) return ImpossibleMove;\r
     if ((fromX == toX) && (fromY == toY)) {\r
-       return;\r
+        return ImpossibleMove;\r
     }\r
+    /* [HGM] suppress all moves into holdings area and guard band */\r
+    if( toX < BOARD_LEFT || toX >= BOARD_RGHT ) 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
@@ -3990,13 +4193,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 +4207,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 +4221,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 +4249,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 +4270,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
@@ -4083,10 +4286,17 @@ 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
+    if (toX < 0 || toY < 0) return ImpossibleMove;\r
+\r
+    /* [HGM] If move started in holdings, it means a drop */\r
+    if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {\r
+         if( boards[currentMove][toY][toX] != EmptySquare ) return ImpossibleMove;\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
@@ -4095,12 +4305,45 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
                                          fromY, fromX, toY, toX, promoChar);\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
+       UserMoveEnevt(). 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 +4379,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 +4469,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
@@ -4370,15 +4623,17 @@ HandleMachineMove(message, cps)
            return;\r
        }\r
 \r
-       if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
-                             &fromX, &fromY, &toX, &toY, &promoChar)) {\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
@@ -4389,19 +4644,21 @@ HandleMachineMove(message, cps)
         /* to cause a forfeit on a justified illegal-move complaint      */\r
         /* of the opponent.                                              */\r
         if(gameMode==TwoMachinesPlay && appData.testLegality &&\r
+           fromY != DROP_RANK && /* [HGM] temporary; should still add legality test for drops */\r
            LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
                         epStatus[forwardMostMove], castlingRights[forwardMostMove],\r
                              fromY, fromX, toY, toX, promoChar) == IllegalMove)\r
-           { static char buf[MSG_SIZ];\r
+           {\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
+            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
            }\r
        hintRequested = FALSE;\r
        lastHint[0] = NULLCHAR;\r
@@ -4464,10 +4721,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
@@ -4496,7 +4754,7 @@ HandleMachineMove(message, cps)
                 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
@@ -4534,7 +4792,7 @@ HandleMachineMove(message, cps)
                      /* 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,7 +4807,7 @@ 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
@@ -4605,7 +4863,7 @@ HandleMachineMove(message, cps)
       fprintf(debugFP, " %d %d\n", rights, k);\r
     }\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,7 +4886,7 @@ 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
@@ -4868,11 +5126,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
@@ -5420,9 +5675,9 @@ ParseGameHistory(game)
           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,7 +5687,7 @@ 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
@@ -5533,11 +5788,12 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      int promoChar;\r
      Board board;\r
 {\r
+    ChessSquare captured = board[toY][toX], piece; int p;\r
+\r
     /* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
     if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\r
-       && promoChar != 0) promoChar = 'f';\r
+       && promoChar != 0) promoChar = 'F';\r
          \r
-    ChessSquare captured = board[toY][toX];\r
     if (fromY == DROP_RANK) {\r
        /* must be first */\r
        board[toY][toX] = (ChessSquare) fromX;\r
@@ -5545,6 +5801,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        return;\r
     }\r
 \r
+    piece = board[fromY][fromX];\r
+    \r
     /* Code added by Tord: */\r
     /* FRC castling assumed when king captures friendly rook. */\r
     else if (board[fromY][fromX] == WhiteKing &&\r
@@ -5552,18 +5810,18 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       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
@@ -5572,14 +5830,14 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         && toY == fromY && toX > fromX+1) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = WhiteKing;\r
-        board[fromY][BOARD_WIDTH-1] = EmptySquare;\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\r
         board[toY][toX-1] = WhiteRook;\r
     } else if (initialPosition[fromY][fromX] == WhiteKing\r
               && board[fromY][fromX] == WhiteKing\r
                && toY == fromY && toX < fromX-1) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = WhiteKing;\r
-       board[fromY][0] = EmptySquare;\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
         board[toY][toX+1] = WhiteRook;\r
     } else if (fromY == 0 && fromX == 3\r
               && board[fromY][fromX] == WhiteKing\r
@@ -5606,6 +5864,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         if (board[toY][toX] == EmptySquare) {\r
             board[toY][toX] = WhiteQueen;\r
        }\r
+        if(gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] += (int) WhiteAlfil - (int) WhitePawn;\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == BOARD_HEIGHT-4)\r
               && (toX != fromX)\r
@@ -5620,14 +5880,14 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
                && toY == fromY && toX > fromX+1) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = BlackKing;\r
-        board[fromY][BOARD_WIDTH-1] = EmptySquare;\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\r
         board[toY][toX-1] = BlackRook;\r
     } else if (initialPosition[fromY][fromX] == BlackKing\r
               && board[fromY][fromX] == BlackKing\r
                && toY == fromY && toX < fromX-1) {\r
        board[fromY][fromX] = EmptySquare;\r
        board[toY][toX] = BlackKing;\r
-       board[fromY][0] = EmptySquare;\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
         board[toY][toX+1] = BlackRook;\r
     } else if (fromY == 7 && fromX == 3\r
               && board[fromY][fromX] == BlackKing\r
@@ -5654,6 +5914,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        if (board[0][toX] == EmptySquare) {\r
            board[0][toX] = BlackQueen;\r
        }\r
+        if(gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] += (int) WhiteAlfil - (int) WhitePawn;\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == 3)\r
               && (toX != fromX)\r
@@ -5667,28 +5929,62 @@ 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
+    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
+          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
+          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 +5993,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = EmptySquare;\r
       }\r
     }\r
+    if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR) {\r
+        /* [HGM] Shogi promotions */\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+    }\r
+\r
 }\r
 \r
 /* Updates forwardMostMove */\r
@@ -5731,15 +6032,15 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
       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
+               (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
+                toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == BlackPawn ) )\r
               epStatus[forwardMostMove] = toX;\r
       } else \r
       if( boards[forwardMostMove][fromY][fromX] == BlackPawn ) {\r
            epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
            if( toY-fromY== -2 &&\r
-               (toX>1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
-                toX<BOARD_WIDTH-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
+               (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
+                toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
               epStatus[forwardMostMove] = toX;\r
        }\r
 \r
@@ -5812,20 +6113,51 @@ void
 InitChessProgram(cps)\r
      ChessProgramState *cps;\r
 {\r
-    char buf[MSG_SIZ];\r
+    char buf[MSG_SIZ], *b; 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
        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
+      b = buf;\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 )\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
+#if 0\r
+           // doesn't work in protocol 1\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
+#endif\r
+           sprintf(buf, "%dx%d+%d_", gameInfo.boardWidth,\r
+                              gameInfo.boardHeight, gameInfo.holdingsSize );\r
+           while(*b++ != '_');\r
+      }\r
+      sprintf(b, "variant %s\n", VariantName(gameInfo.variant));\r
       SendToProgram(buf, cps);\r
     }\r
     if (cps->sendICS) {\r
@@ -6397,7 +6729,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 +6737,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
@@ -6503,9 +6835,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 +6850,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 +6959,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
@@ -6738,9 +7070,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
@@ -7167,7 +7499,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
@@ -7437,7 +7769,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
@@ -9021,7 +9353,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 +9377,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
@@ -9056,7 +9388,7 @@ EditPositionMenuEvent(selection, x, y)
       default:\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 +9682,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
@@ -9453,14 +9785,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
@@ -9733,10 +10065,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
@@ -11124,6 +11456,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,7 +11465,7 @@ 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
@@ -11142,7 +11475,15 @@ PositionToFEN(move, useFEN960)
                     else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
                    emptycount = 0;\r
                }\r
-               *p++ = PieceToChar(boards[move][i][j]);\r
+                *p++ = PieceToChar(boards[move][i][j]);\r
+                if(gameInfo.variant == VariantCrazyhouse) {\r
+                    /* [HGM] flag Crazyhouse promoted pieces */\r
+                    if( (int)boards[move][i][j] > (int) WhiteQueen && (int)boards[move][i][j] < (int) WhiteKing ||\r
+                        (int)boards[move][i][j] > (int) BlackQueen && (int)boards[move][i][j] < (int) BlackKing ) {\r
+                        p[-1] = PieceToChar((ChessSquare)((int)boards[move][i][j]-(int)WhiteAlfil+(int)WhitePawn));\r
+                        *p++ = '~';\r
+                    }\r
+                }\r
            }\r
        }\r
        if (emptycount > 0) {\r
@@ -11174,18 +11515,18 @@ PositionToFEN(move, useFEN960)
 \r
        /* White castling rights */\r
 \r
-       for (fk = 1; fk < BOARD_WIDTH-1; fk++) {\r
+       for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) {\r
 \r
           if (boards[move][0][fk] == WhiteKing) {\r
 \r
-             for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */\r
+             for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
                 if (boards[move][0][fr] == WhiteRook) {\r
                    *p++ = useFEN960 ? 'A' + fr : 'K';\r
                    break;\r
                 }\r
              }\r
 \r
-             for (fr = 0; fr < fk; fr++) { /* A side */\r
+             for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
                 if (boards[move][0][fr] == WhiteRook) {\r
                    *p++ = useFEN960 ? 'A' + fr : 'Q';\r
                    break;\r
@@ -11196,18 +11537,18 @@ PositionToFEN(move, useFEN960)
 \r
        /* Black castling rights */\r
 \r
-       for (fk = 1; fk < BOARD_WIDTH-1; fk++) {\r
+       for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-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
+             for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
                 if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
                    *p++ = useFEN960 ? 'a' + fr : 'k';\r
                    break;\r
                 }\r
              }\r
 \r
-             for (fr = 0; fr < fk; fr++) { /* A side */\r
+             for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
                 if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
                    *p++ = useFEN960 ? 'a' + fr : 'q';\r
                    break;\r
@@ -11224,23 +11565,23 @@ PositionToFEN(move, useFEN960)
 \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
+            if (boards[move][0][BOARD_RGHT-1] == WhiteRook) *p++ = 'K';\r
+            if (boards[move][0][BOARD_LEFT] == WhiteRook) *p++ = 'Q';\r
         }\r
         if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) {\r
            if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k';\r
-           if (boards[move][BOARD_HEIGHT-1][0] == BlackRook) *p++ = 'q';\r
+            if (boards[move][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook) *p++ = 'q';\r
         }          \r
 #else\r
         /* [HGM] write true castling rights */\r
         if( nrCastlingRights == 6 ) {\r
-            if(castlingRights[move][0] == BOARD_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
@@ -11252,16 +11593,16 @@ PositionToFEN(move, useFEN960)
 \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
@@ -11270,6 +11611,26 @@ PositionToFEN(move, useFEN960)
        *p++ = '-';\r
     }\r
 \r
+    /* [HGM] print Crazyhouse holdings */\r
+    if( gameInfo.variant == VariantCrazyhouse ) {\r
+        *p++ = ' '; q = p;\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
+            piece = boards[move][i][BOARD_WIDTH-1];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
+            piece = boards[move][BOARD_HEIGHT-i-1][0];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+\r
+        if( q == p ) *p++ = '-';\r
+        *p++ = ' ';\r
+    }\r
+\r
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r
 \r
@@ -11300,32 +11661,51 @@ 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
+                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
+                if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
            } else if (isalpha(*p)) {\r
-                if (j >= BOARD_WIDTH) return FALSE;\r
-               board[i][j++] = CharToPiece(*p++);\r
+                if (j >= gameInfo.boardWidth) return FALSE;\r
+                piece = CharToPiece(*p++);\r
+                if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */\r
+                    piece = (ChessSquare) ((int)piece + (int)WhiteAlfil - (int)WhitePawn);\r
+                    p++;\r
+                }\r
+                board[i][(j++)+gameInfo.holdingsWidth] = piece;\r
            } else {\r
                return FALSE;\r
            }\r
@@ -11353,18 +11733,18 @@ ParseFEN(board, blackPlaysFirst, fen)
     for(i=0; i<nrCastlingRights; i++ ) {\r
         FENcastlingRights[i] = 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
+              /* castling indicator present, so default is no castlings */\r
               for(i=0; i<nrCastlingRights; i++ ) {\r
                      FENcastlingRights[i] = -1;\r
               }\r
@@ -11372,19 +11752,19 @@ ParseFEN(board, blackPlaysFirst, fen)
     while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
         switch(*p++) {\r
           case'K':\r
-              FENcastlingRights[0] = BOARD_WIDTH-1;\r
+              FENcastlingRights[0] = BOARD_RGHT-1;\r
               FENcastlingRights[2] = BOARD_WIDTH>>1;\r
               break;\r
           case'Q':\r
-              FENcastlingRights[1] = 0;\r
+              FENcastlingRights[1] = BOARD_LEFT;\r
               FENcastlingRights[2] = BOARD_WIDTH>>1;\r
               break;\r
           case'k':\r
-              FENcastlingRights[3] = BOARD_WIDTH-1;\r
+              FENcastlingRights[3] = BOARD_RGHT-1;\r
               FENcastlingRights[5] = BOARD_WIDTH>>1;\r
               break;\r
           case'q':\r
-              FENcastlingRights[4] = 0;\r
+              FENcastlingRights[4] = BOARD_LEFT;\r
               FENcastlingRights[5] = BOARD_WIDTH>>1;\r
               break;\r
           /* Tord! FRC! */\r
@@ -11397,13 +11777,39 @@ ParseFEN(board, blackPlaysFirst, fen)
     if(*p=='-') {\r
         p++; FENepStatus = EP_NONE;\r
     } else {\r
-       char c = *p++ - 'a';\r
+       char c = *p++ - AAA;\r
 \r
-       if(c < 0 || c >= BOARD_WIDTH) return TRUE;\r
+       if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
        if(*p >= '0' && *p <='9') *p++;\r
        FENepStatus = c;\r
     }\r
 \r
+    /* [HGM] look for Crazyhouse holdings here */\r
+    while(*p==' ') p++;\r
+    if( !isdigit(*p) ) {\r
+        if(*p == '-' ) *p++; /* empty holdings */ else {\r
+            if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
+            /* if we would allow FEN reading to set board size, we would   */\r
+            /* have to add holdings and shift the board read so far here   */\r
+            while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
+                *p++;\r
+                if((int) piece >= (int) BlackPawn ) {\r
+                    i = (int)piece - (int)BlackPawn;\r
+                    if( i >= BOARD_HEIGHT ) return FALSE;\r
+                    board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */\r
+                    board[BOARD_HEIGHT-1-i][1]++;       /* black counts   */\r
+                } else {\r
+                    i = (int)piece - (int)WhitePawn;\r
+                    if( i >= BOARD_HEIGHT ) return FALSE;\r
+                    board[i][BOARD_WIDTH-1] = piece;    /* white holdings */\r
+                    board[i][BOARD_WIDTH-2]++;          /* black holdings */\r
+                }\r
+            }\r
+        }\r
+    }\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