changes from H.G. Muller; version 4.3.12
[xboard.git] / backend.c
index 6c79b03..e9e1368 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1367,9 +1367,8 @@ StringToVariant(e)
     if (!e) return v;\r
 \r
     /* [HGM] skip over optional board-size prefixes */\r
-    if( sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
-        while( *e++ != '_');\r
-    } else if( sscanf(e, "%dx%d_", &i, &i) == 2 ) {\r
+    if( sscanf(e, "%dx%d_", &i, &i) == 2 ||\r
+        sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
         while( *e++ != '_');\r
     }\r
 \r
@@ -1734,6 +1733,68 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
 \r
 }\r
 \r
+void\r
+VariantSwitch(Board board, VariantClass newVariant)\r
+{\r
+   int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\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
+    */\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
+            case VariantShowgi:\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
+        if(board != boards[0]) InitPosition(FALSE);\r
+\r
+    } else gameInfo.variant = newVariant;\r
+}\r
+\r
 static int loggedOn = FALSE;\r
 \r
 /*-- Game start info cache: --*/\r
@@ -2332,12 +2393,11 @@ read_from_ics(isr, closure, data, count, error)
              "* * match, initial time: * minute*, increment: * second")) {\r
                /* Header for a move list -- second line */\r
                /* Initial board will follow if this is a wild game */\r
-\r
                if (gameInfo.event != NULL) free(gameInfo.event);\r
                sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);\r
                gameInfo.event = StrSave(str);\r
-               gameInfo.variant = StringToVariant(gameInfo.event);\r
-                Reset(TRUE,TRUE); /* [HGM] possibly change board or holdings size */\r
+                /* [HGM] we switched variant. Translate boards if needed. */\r
+                VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));\r
                continue;\r
            }\r
 \r
@@ -2802,7 +2862,8 @@ read_from_ics(isr, closure, data, count, error)
                    started = STARTED_NONE;\r
                    parse[parse_pos] = NULLCHAR;\r
                    if (appData.debugMode)\r
-                     fprintf(debugFP, "Parsing holdings: %s\n", parse);\r
+                      fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",\r
+                                                        parse, currentMove);\r
                    if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
                        gamenum == ics_gamenum) {\r
                        if (gameInfo.variant == VariantNormal) {\r
@@ -2811,22 +2872,7 @@ read_from_ics(isr, closure, data, count, error)
                            * to move the position two files to the right to\r
                            * create room for them!\r
                            */\r
-                          int i, j;\r
-                          if(gameInfo.holdingsWidth == 0) /* to be sure */\r
-                          for(i=0; i<BOARD_HEIGHT; i++)\r
-                            for(j=BOARD_RGHT-1; j>=0; j--)\r
-                              boards[currentMove][i][j+2] = boards[currentMove][i][j];\r
-\r
-  if (appData.debugMode) {\r
-    fprintf(debugFP, "Switch board to Crazy\n");\r
-    setbuf(debugFP, NULL);\r
-  }\r
-                         gameInfo.variant = VariantCrazyhouse; /*temp guess*/\r
-                          gameInfo.boardWidth  = 8;  /* [HGM] guess board size as well */\r
-                          gameInfo.boardHeight = 8;\r
-                          gameInfo.holdingsSize = 5;\r
-                          gameInfo.holdingsWidth = 2;\r
-                          InitDrawingSizes(-2, 0);\r
+                          VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */\r
                          /* Get a move list just to see the header, which\r
                             will tell us whether this is really bug or zh */\r
                          if (ics_getting_history == H_FALSE) {\r
@@ -2862,7 +2908,7 @@ read_from_ics(isr, closure, data, count, error)
                                    gameInfo.black, black_holding);\r
                        }\r
 \r
-                       DrawPosition(FALSE, NULL);\r
+                        DrawPosition(FALSE, boards[currentMove]);\r
                        DisplayTitle(str);\r
                    }\r
                    /* Suppress following prompt */\r
@@ -3078,27 +3124,13 @@ ParseBoard12(string)
        timeIncrement = increment * 1000;\r
        movesPerSession = 0;\r
        gameInfo.timeControl = TimeControlTagValue();\r
-       gameInfo.variant = StringToVariant(gameInfo.event);\r
+        VariantSwitch(board, StringToVariant(gameInfo.event) );\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);\r
     fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));\r
     setbuf(debugFP, NULL);\r
   }\r
 \r
-        gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
-        gameInfo.boardWidth = gameInfo.boardHeight = 8;\r
-        switch(gameInfo.variant) {\r
-            case VariantShogi:\r
-            case VariantShowgi:\r
-              gameInfo.boardWidth = 9;  gameInfo.boardHeight = 9;\r
-              gameInfo.holdingsSize = 7;\r
-            case VariantBughouse:\r
-            case VariantCrazyhouse:\r
-              gameInfo.holdingsWidth = 2; break;\r
-            default:\r
-              gameInfo.holdingsWidth = gameInfo.holdingsSize = 0;\r
-        }\r
-        InitDrawingSizes(-2, 0);\r
         gameInfo.outOfBook = NULL;\r
        \r
        /* Do we have the ratings? */\r
@@ -3237,7 +3269,7 @@ ParseBoard12(string)
     if (moveNum > 0) {\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);\r
-    fprintf(debugFP, "board = %d-d x%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
+    fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
     setbuf(debugFP, NULL);\r
   }\r
        if (moveNum <= backwardMostMove) {\r
@@ -3282,6 +3314,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
@@ -3449,11 +3485,6 @@ SendMoveToProgram(moveNum, cps)
       } else {\r
        sprintf(buf, "%s\n", parseList[moveNum]);\r
       }\r
-      /* [HGM] decrement all digits to code ranks starting from 0 */\r
-      if(BOARD_HEIGHT>9) {\r
-          char *p = buf;\r
-          while(*p) { if(*p < 'A') (*p)--; p++; }\r
-      }\r
       SendToProgram(buf, cps);\r
     } else {\r
       /* Added by Tord: Send castle moves in "O-O" in FRC games if required by\r
@@ -3587,15 +3618,62 @@ ProcessICSInitScript(f)
 void\r
 AlphaRank(char *move, int n)\r
 {\r
-    char *p = move, c;\r
+    char *p = move, c; int x, y;\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
+    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] = (move[2]-'1')+BOARD_LEFT + AAA;\r
+        move[3] = (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
+/*\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
+*/\r
+        move[0] = (move[0]-'1')+BOARD_LEFT + AAA;\r
+        move[1] = (move[1]-'a') + ONE;\r
+        move[2] = (move[2]-'1')+BOARD_LEFT + AAA;\r
+        move[3] = (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] = (move[2]-AAA)-BOARD_LEFT + '1';\r
+        move[3] = (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
+/*\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
+*/\r
+        move[0] = (move[0]-AAA)-BOARD_LEFT + '1';\r
+        move[1] = (move[1]-ONE) + 'a';\r
+        move[2] = (move[2]-AAA)-BOARD_LEFT + '1';\r
+        move[3] = (move[3]-ONE) + 'a';\r
+        if(move[4] == PieceToChar(BlackQueen)) move[4] = '+';\r
+    }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "   out = '%s'\n", move);\r
     }\r
 }\r
 \r
@@ -3611,16 +3689,13 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     if (appData.debugMode) {\r
         fprintf(debugFP, "move to parse: %s\n", move);\r
     }\r
-    AlphaRank(move, 10);\r
     *moveType = yylexstr(moveNum, move);\r
 \r
     switch (*moveType) {\r
-#ifdef FAIRY\r
       case WhitePromotionChancellor:\r
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
-#endif\r
       case WhitePromotionQueen:\r
       case BlackPromotionQueen:\r
       case WhitePromotionRook:\r
@@ -3656,6 +3731,9 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
        *promoChar = currentMoveString[4];\r
         if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT ||\r
             *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) {\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Off-board move (%d,%d)-(%d,%d)%c, type = %d\n", *fromX, *fromY, *toX, *toY, *promoChar, *moveType);\r
+    }\r
            *fromX = *fromY = *toX = *toY = 0;\r
            return FALSE;\r
        }\r
@@ -3687,6 +3765,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
@@ -3826,6 +3907,9 @@ InitPosition(redraw)
         }\r
 \r
         initialRulePlies = 0; /* 50-move counter start */\r
+\r
+        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
+        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
     }\r
 \r
     \r
@@ -3858,7 +3942,7 @@ 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
+      castlingRank[7] = BOARD_HEIGHT-1;\r
       startedFromSetupPosition = TRUE;\r
       break;\r
     case VariantCapablanca:\r
@@ -3884,7 +3968,7 @@ InitPosition(redraw)
       gameInfo.boardHeight = 9;\r
       gameInfo.holdingsSize = 7;\r
       nrCastlingRights = 0;\r
-      SetCharTable(pieceToChar, "PNBRLSG.........Kpnbrlsg.........k"); \r
+      SetCharTable(pieceToChar, "PNBRLSG...++++++Kpnbrlsg...++++++k"); \r
       break;\r
     case VariantShowgi:\r
       pieces = ShogiArray;\r
@@ -3904,7 +3988,7 @@ InitPosition(redraw)
       break;\r
     case VariantKnightmate:\r
       pieces = KnightmateArray;\r
-      strcpy(pieceToChar, "P.BRQ...M.K......p.brq...m.k......"); \r
+      SetCharTable(pieceToChar, "P.BRQ...M.K......p.brq...m.k......"); \r
       break;\r
     case VariantFairy:\r
       pieces = fairyArray;\r
@@ -3914,6 +3998,7 @@ InitPosition(redraw)
     case VariantCrazyhouse:\r
     case VariantBughouse:\r
       pieces = FIDEArray;\r
+      SetCharTable(pieceToChar, "PNBRQ......~~~~.Kpnbrq......~~~~.k"); \r
       gameInfo.holdingsSize = 5;\r
       break;\r
     case VariantWildCastle:\r
@@ -3998,9 +4083,6 @@ InitPosition(redraw)
         castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
         castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
         castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
-\r
-        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
-        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
      }\r
 \r
     if(gameInfo.variant == VariantFischeRandom) {\r
@@ -4254,6 +4336,21 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
         return ImpossibleMove;\r
     }\r
 \r
+    /* [HGM] suppress all moves into holdings area and guard band */\r
+    if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )\r
+            return ImpossibleMove;\r
+\r
+    /* [HGM] <sameColor> moved to here from winboard.c */\r
+    /* note: this code seems to exist for filtering out some obviously illegal premoves */\r
+    pdown = boards[currentMove][fromY][fromX];\r
+    pup = boards[currentMove][toY][toX];\r
+    if (    gameMode != EditPosition &&\r
+            (WhitePawn <= pdown && pdown < BlackPawn &&\r
+             WhitePawn <= pup && pup < BlackPawn  ||\r
+             BlackPawn <= pdown && pdown < EmptySquare &&\r
+             BlackPawn <= pup && pup < EmptySquare)      )\r
+         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
@@ -4372,21 +4469,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
         return ImpossibleMove;\r
     }\r
 \r
-    /* [HGM] suppress all moves into holdings area and guard band */\r
-    if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )\r
-            return ImpossibleMove;\r
-\r
-    /* [HGM] <sameColor> moved to here from winboard.c */\r
-    /* note: EditPosition already filtered out and performed! */\r
-    pdown = boards[currentMove][fromY][fromX];\r
-    pup = boards[currentMove][toY][toX];\r
-    if ( \r
-            (WhitePawn <= pdown && pdown < BlackPawn &&\r
-             WhitePawn <= pup && pup < BlackPawn) ||\r
-            (BlackPawn <= pdown && pdown < EmptySquare &&\r
-             BlackPawn <= pup && pup < EmptySquare)      )\r
-         return ImpossibleMove;\r
-\r
     /* [HGM] If move started in holdings, it means a drop */\r
     if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { \r
          if( pup != EmptySquare ) return ImpossibleMove;\r
@@ -4729,6 +4811,7 @@ HandleMachineMove(message, cps)
            return;\r
        }\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
@@ -4950,7 +5033,8 @@ HandleMachineMove(message, cps)
                 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 (appData.debugMode) {\r
@@ -5075,7 +5159,7 @@ HandleMachineMove(message, cps)
     if (!strncmp(message, "setboard ", 9)) {\r
         Board initial_position; int i;\r
 \r
-        GameEnds(GameIsDrawn, "Engine aborts game", GE_XBOARD);\r
+        GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);\r
 \r
         if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {\r
             DisplayError("Bad FEN received from engine", 0);\r
@@ -5389,6 +5473,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
@@ -5789,12 +5877,16 @@ ParseGameHistory(game)
        yyboardindex = boardIndex;\r
        moveType = (ChessMove) yylex();\r
        switch (moveType) {\r
-#ifdef FAIRY\r
+         case IllegalMove:             /* maybe suicide chess, etc. */\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Illegal move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
           case WhitePromotionChancellor:\r
           case BlackPromotionChancellor:\r
           case WhitePromotionArchbishop:\r
           case BlackPromotionArchbishop:\r
-#endif\r
          case WhitePromotionQueen:\r
          case BlackPromotionQueen:\r
          case WhitePromotionRook:\r
@@ -5822,7 +5914,6 @@ ParseGameHistory(game)
           case BlackHSideCastleFR:\r
           case BlackASideCastleFR:\r
           /* POP Fabien */\r
-         case IllegalMove:             /* maybe suicide chess, etc. */\r
             fromX = currentMoveString[0] - AAA;\r
             fromY = currentMoveString[1] - ONE;\r
             toX = currentMoveString[2] - AAA;\r
@@ -5842,11 +5933,21 @@ ParseGameHistory(game)
          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
@@ -6186,14 +6287,14 @@ 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>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
+               (toX>BOARD_LEFT   && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
                 toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == BlackPawn ) )\r
               epStatus[forwardMostMove] = toX;\r
       } else \r
       if( boards[forwardMostMove][fromY][fromX] == BlackPawn ) {\r
            epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
            if( toY-fromY== -2 &&\r
-               (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
+               (toX>BOARD_LEFT   && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
                 toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
               epStatus[forwardMostMove] = toX;\r
        }\r
@@ -6204,6 +6305,8 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
               castlingRights[forwardMostMove][i] == toX   && castlingRank[i] == toY   \r
              ) castlingRights[forwardMostMove][i] = -1; // revoke for moved or captured piece\r
 \r
+\r
+\r
        }\r
 \r
     }\r
@@ -6514,8 +6617,9 @@ 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
@@ -11674,22 +11778,16 @@ PositionToFEN(move, useFEN960)
                     else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
                    emptycount = 0;\r
                }\r
-                if(gameInfo.variant == VariantShogi) {\r
-                    /* [HGM] write Shogi promoted pieces as +<unpromoted> */\r
-                    if( (int)piece > (int) WhiteCannon && (int)piece < (int) WhiteKing ||\r
-                        (int)piece > (int) BlackCannon && (int)piece < (int) BlackKing ) {\r
-                        *p++ = '+';\r
-                        piece = (ChessSquare)(DEMOTED piece);\r
-                    }\r
+                if(PieceToChar(piece) == '+') {\r
+                    /* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */\r
+                    *p++ = '+';\r
+                    piece = (ChessSquare)(DEMOTED piece);\r
                 } \r
                 *p++ = PieceToChar(piece);\r
-                if(gameInfo.variant == VariantCrazyhouse || gameInfo.variant == VariantBughouse) {\r
-                    /* [HGM] flag Crazyhouse promoted pieces */\r
-                    if( (int)piece > (int) WhiteQueen && (int)piece < (int) WhiteKing ||\r
-                        (int)piece > (int) BlackQueen && (int)piece < (int) BlackKing ) {\r
-                        p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
-                        *p++ = '~';\r
-                    }\r
+                if(p[-1] == '~') {\r
+                    /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */\r
+                    p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
+                    *p++ = '~';\r
                 }\r
            }\r
        }\r
@@ -11707,6 +11805,7 @@ PositionToFEN(move, useFEN960)
     *p++ = whiteToPlay ? 'w' : 'b';\r
     *p++ = ' ';\r
 \r
+  if(nrCastlingRights) {\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
@@ -11797,7 +11896,10 @@ PositionToFEN(move, useFEN960)
     }\r
 \r
     /* POP Fabien & Tord */\r
+  }\r
 \r
+  if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+     gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
     /* En passant target square */\r
     if (move > backwardMostMove) {\r
         fromX = moveList[move - 1][0] - AAA;\r
@@ -11817,10 +11919,12 @@ PositionToFEN(move, useFEN960)
     } else {\r
        *p++ = '-';\r
     }\r
+    *p++ = ' ';\r
+  }\r
 \r
-    /* [HGM] print Crazyhouse holdings */\r
-    if( gameInfo.variant == VariantCrazyhouse ) {\r
-        *p++ = ' '; q = p;\r
+    /* [HGM] print Crazyhouse or Shogi holdings */\r
+    if( gameInfo.holdingsWidth ) {\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
@@ -11841,20 +11945,20 @@ PositionToFEN(move, useFEN960)
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r
 \r
-    if (appData.debugMode) { int k;\r
-        fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
-        for(k=backwardMostMove; k<=forwardMostMove; k++)\r
-            fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
+        if (appData.debugMode) { int k;\r
+            fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
+            for(k=backwardMostMove; k<=forwardMostMove; k++)\r
+                fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
 \r
-    }\r
+        }\r
 \r
         while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++;\r
         if( j == backwardMostMove ) i += initialRulePlies;\r
-        sprintf(p, " %d", i);\r
-      p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
+        sprintf(p, "%d ", i);\r
+        p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
     }\r
     /* Fullmove number */\r
-    sprintf(p, " %d", (move / 2) + 1);\r
+    sprintf(p, "%d", (move / 2) + 1);\r
     \r
     return StrSave(buf);\r
 }\r
@@ -11873,14 +11977,14 @@ ParseFEN(board, blackPlaysFirst, fen)
     p = fen;\r
 \r
     /* [HGM] by default clear Crazyhouse holdings, if present */\r
-   if(gameInfo.holdingsWidth) {\r
+    if(gameInfo.holdingsWidth) {\r
        for(i=0; i<BOARD_HEIGHT; i++) {\r
            board[i][0]             = EmptySquare; /* black holdings */\r
            board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */\r
            board[i][1]             = (ChessSquare) 0; /* black counts */\r
            board[i][BOARD_WIDTH-2] = (ChessSquare) 0; /* white counts */\r
        }\r
-   }\r
+    }\r
 \r
     /* Piece placement data */\r
     for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
@@ -11907,10 +12011,17 @@ ParseFEN(board, blackPlaysFirst, fen)
                         board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
             } else if (*p == '+' || isalpha(*p)) {\r
                 if (j >= gameInfo.boardWidth) return FALSE;\r
-                if(*p=='+') { piece = (ChessSquare) (PROMOTED CharToPiece(*++p) ); p++; }\r
-                else piece = CharToPiece(*p++);\r
+                if(*p=='+') {\r
+                    piece = CharToPiece(*++p);\r
+                    if(piece == EmptySquare) return FALSE; /* unknown piece */\r
+                    piece = (ChessSquare) (PROMOTED piece ); p++;\r
+                    if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */\r
+                } else piece = CharToPiece(*p++);\r
+\r
+                if(piece==EmptySquare) return FALSE; /* unknown piece */\r
                 if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */\r
                     piece = (ChessSquare) (PROMOTED piece);\r
+                    if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */\r
                     p++;\r
                 }\r
                 board[i][(j++)+gameInfo.holdingsWidth] = piece;\r
@@ -11935,7 +12046,7 @@ 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
@@ -11950,14 +12061,14 @@ ParseFEN(board, blackPlaysFirst, fen)
     FENrulePlies = 0;\r
 \r
     while(*p==' ') p++;\r
-\r
-    if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
-              /* castling indicator present, so default is no castlings */\r
-              for(i=0; i<nrCastlingRights; i++ ) {\r
-                     FENcastlingRights[i] = -1;\r
-              }\r
-    }\r
-    while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
+    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
         switch(*p++) {\r
           case'K':\r
               FENcastlingRights[0] = BOARD_RGHT-1;\r
@@ -11977,24 +12088,28 @@ ParseFEN(board, blackPlaysFirst, fen)
               break;\r
           /* Tord! FRC! */\r
         }\r
-    }\r
-\r
-    while(*p==' ') p++;\r
+      }\r
 \r
+      while(*p==' ') p++;\r
+    }\r
 \r
-    if(*p=='-') {\r
+    /* read e.p. field in games that know e.p. capture */\r
+    if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+       gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
+      if(*p=='-') {\r
         p++; FENepStatus = EP_NONE;\r
-    } else {\r
-       char c = *p++ - AAA;\r
+      } else {\r
+         char c = *p++ - AAA;\r
 \r
-       if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
-       if(*p >= '0' && *p <='9') *p++;\r
-       FENepStatus = c;\r
+         if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
+         if(*p >= '0' && *p <='9') *p++;\r
+         FENepStatus = c;\r
+      }\r
     }\r
 \r
     /* [HGM] look for Crazyhouse holdings here */\r
     while(*p==' ') p++;\r
-    if( !isdigit(*p) ) {\r
+    if( gameInfo.holdingsWidth ) {\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
@@ -12017,12 +12132,11 @@ ParseFEN(board, blackPlaysFirst, fen)
     }\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