changes from H.G. Muller; version 4.3.7
[xboard.git] / backend.c
index f3cffc5..6c79b03 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1702,13 +1702,13 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
     char p;\r
     ChessSquare piece;\r
 \r
-    if(gameInfo.holdingsWidth < 1)  return;\r
+    if(gameInfo.holdingsWidth < 2)  return;\r
 \r
     if( (int)lowestPiece >= BlackPawn ) {\r
         holdingsColumn = 0;\r
         countsColumn = 1;\r
         holdingsStartRow = BOARD_HEIGHT-1;\r
-        direction = 1;\r
+        direction = -1;\r
     } else {\r
         holdingsColumn = BOARD_WIDTH-1;\r
         countsColumn = BOARD_WIDTH-2;\r
@@ -1723,12 +1723,13 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
     while( (p=*holdings++) != NULLCHAR ) {\r
         piece = CharToPiece( ToUpper(p) );\r
         if(piece == EmptySquare) continue;\r
-        j = (int) piece - (int) WhitePawn;\r
+        /*j = (int) piece - (int) WhitePawn;*/\r
+        j = PieceToNumber(piece);\r
         if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
         if(j < 0) continue;               /* should not happen */\r
-        piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
-        board[holdingsStartRow+i*direction][holdingsColumn] = piece;\r
-        board[holdingsStartRow+i*direction][countsColumn]++;\r
+        piece = (ChessSquare) ( j + (int)lowestPiece );\r
+        board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
+        board[holdingsStartRow+j*direction][countsColumn]++;\r
     }\r
 \r
 }\r
@@ -2336,6 +2337,7 @@ read_from_ics(isr, closure, data, count, error)
                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
                continue;\r
            }\r
 \r
@@ -2804,7 +2806,27 @@ read_from_ics(isr, closure, data, count, error)
                    if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
                        gamenum == ics_gamenum) {\r
                        if (gameInfo.variant == VariantNormal) {\r
+                          /* [HGM] We seem to switch variant during a game!\r
+                           * Presumably no holdings were displayed, so we have\r
+                           * to move the position two files to the right to\r
+                           * create room for them!\r
+                           */\r
+                          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
                          /* 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
@@ -2819,6 +2841,9 @@ read_from_ics(isr, closure, data, count, error)
                               new_piece);\r
                         white_holding[strlen(white_holding)-1] = NULLCHAR;\r
                         black_holding[strlen(black_holding)-1] = NULLCHAR;\r
+                        /* [HGM] copy holdings to board holdings area */\r
+                        CopyHoldings(boards[currentMove], white_holding, WhitePawn);\r
+                        CopyHoldings(boards[currentMove], black_holding, BlackPawn);\r
 #if ZIPPY\r
                        if (appData.zippyPlay && first.initDone) {\r
                            ZippyHoldings(white_holding, black_holding,\r
@@ -2837,9 +2862,6 @@ read_from_ics(isr, closure, data, count, error)
                                    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
@@ -3057,20 +3079,26 @@ ParseBoard12(string)
        movesPerSession = 0;\r
        gameInfo.timeControl = TimeControlTagValue();\r
        gameInfo.variant = 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 = gameInfo.boardHeight = 9;\r
-              gameInfo.holdingsSize += 2;\r
+              gameInfo.boardWidth = 9;  gameInfo.boardHeight = 9;\r
+              gameInfo.holdingsSize = 7;\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
+              gameInfo.holdingsWidth = gameInfo.holdingsSize = 0;\r
         }\r
+        InitDrawingSizes(-2, 0);\r
         gameInfo.outOfBook = NULL;\r
        \r
        /* Do we have the ratings? */\r
@@ -3132,9 +3160,14 @@ ParseBoard12(string)
     }\r
     \r
     /* Parse the board */\r
-    for (k = 0; k < 8; k++)\r
+    for (k = 0; k < 8; k++) {\r
       for (j = 0; j < 8; j++)\r
-       board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);\r
+        board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(7-k)*9 + j]);\r
+      if(gameInfo.holdingsWidth > 1) {\r
+           board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare;\r
+           board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;\r
+      }\r
+    }\r
     CopyBoard(boards[moveNum], board);\r
     if (moveNum == 0) {\r
        startedFromSetupPosition =\r
@@ -3202,6 +3235,11 @@ ParseBoard12(string)
     /* Put the move on the move list, first converting\r
        to canonical algebraic form. */\r
     if (moveNum > 0) {\r
+  if (appData.debugMode) {\r
+    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
+    setbuf(debugFP, NULL);\r
+  }\r
        if (moveNum <= backwardMostMove) {\r
            /* We don't know what the board looked like before\r
               this move.  Punt. */\r
@@ -3222,7 +3260,8 @@ ParseBoard12(string)
              default:\r
                break;\r
              case MT_CHECK:\r
-               strcat(parseList[moveNum - 1], "+");\r
+                if(gameInfo.variant != VariantShogi)\r
+                    strcat(parseList[moveNum - 1], "+");\r
                break;\r
              case MT_CHECKMATE:\r
                strcat(parseList[moveNum - 1], "#");\r
@@ -3255,6 +3294,10 @@ ParseBoard12(string)
            moveList[moveNum - 1][0] = NULLCHAR;\r
            fromX = fromY = toX = toY = -1;\r
        }\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
+    setbuf(debugFP, NULL);\r
+  }\r
 \r
 #if ZIPPY\r
        /* Send move to chess program (BEFORE animating it). */\r
@@ -3407,7 +3450,7 @@ SendMoveToProgram(moveNum, cps)
        sprintf(buf, "%s\n", parseList[moveNum]);\r
       }\r
       /* [HGM] decrement all digits to code ranks starting from 0 */\r
-      if(BOARD_HEIGHT>8) {\r
+      if(BOARD_HEIGHT>9) {\r
           char *p = buf;\r
           while(*p) { if(*p < 'A') (*p)--; p++; }\r
       }\r
@@ -3737,6 +3780,30 @@ static void SetupFRC( Board board, int pos_index )
     }\r
 }\r
 \r
+BOOL SetCharTable( char *table, const char * map )\r
+/* [HGM] moved here from winboard.c because of its general usefulness */\r
+/*       Basically a safe strcpy that uses the last character as King */\r
+{\r
+    BOOL result = FALSE; int NrPieces;\r
+\r
+    if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
+                    && NrPieces >= 12 && !(NrPieces&1)) {\r
+        int i; /* [HGM] Accept even length from 12 to 34 */\r
+\r
+        for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';\r
+        for( i=0; i<NrPieces/2-1; i++ ) {\r
+            table[i] = map[i];\r
+            table[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
+        }\r
+        table[(int) WhiteKing]  = map[NrPieces/2-1];\r
+        table[(int) BlackKing]  = map[NrPieces-1];\r
+\r
+        result = TRUE;\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
 void\r
 InitPosition(redraw)\r
      int redraw;\r
@@ -3745,7 +3812,8 @@ InitPosition(redraw)
     int i, j, pawnRow, overrule,\r
     oldx = gameInfo.boardWidth,\r
     oldy = gameInfo.boardHeight,\r
-    oldh = gameInfo.holdingsWidth;\r
+    oldh = gameInfo.holdingsWidth,\r
+    oldv = gameInfo.variant;\r
 \r
     currentMove = forwardMostMove = backwardMostMove = 0;\r
 \r
@@ -3757,19 +3825,6 @@ InitPosition(redraw)
             for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
         }\r
 \r
-        /* [HGM] Build normal castling rights */\r
-        for( j=0; j<BOARD_SIZE; j++ ) initialRights[j] = -1;\r
-        nrCastlingRights = 6;\r
-        castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
-        castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
-        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;\r
-        castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
-        castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
-        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
-\r
-        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
-        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
-\r
         initialRulePlies = 0; /* 50-move counter start */\r
     }\r
 \r
@@ -3785,6 +3840,9 @@ InitPosition(redraw)
     gameInfo.boardWidth    = 8;\r
     gameInfo.boardHeight   = 8;\r
     gameInfo.holdingsSize  = 0;\r
+    nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */\r
+    for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1; /* but no rights yet */\r
+    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k"); \r
 \r
     switch (gameInfo.variant) {\r
     default:\r
@@ -3793,7 +3851,6 @@ InitPosition(redraw)
     case VariantShatranj:\r
       pieces = ShatranjArray;\r
       nrCastlingRights = 0;\r
-      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
@@ -3807,18 +3864,19 @@ InitPosition(redraw)
     case VariantCapablanca:\r
       pieces = CapablancaArray;\r
       gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \r
       break;\r
     case VariantGothic:\r
       pieces = GothicArray;\r
       gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \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
+      SetCharTable(pieceToChar, "PH.R.AKE.C.......ph.r.ake.c......."); \r
       break;\r
     case VariantShogi:\r
       pieces = ShogiArray;\r
@@ -3826,8 +3884,7 @@ InitPosition(redraw)
       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
+      SetCharTable(pieceToChar, "PNBRLSG.........Kpnbrlsg.........k"); \r
       break;\r
     case VariantShowgi:\r
       pieces = ShogiArray;\r
@@ -3836,28 +3893,28 @@ InitPosition(redraw)
       gameInfo.holdingsSize = 7;\r
       nrCastlingRights = 0;\r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
-      strcpy(pieceToChar, "PNBRQFWEHACGOUMKpnbrlsgpnbrls..k"); \r
+      SetCharTable(pieceToChar, "PNBRQFWEMOUHACG.Kpnbrlsgpnbrls...k"); \r
       break;\r
     case VariantCourier:\r
       pieces = CourierArray;\r
       gameInfo.boardWidth  = 12;\r
       nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PNBR.FWEM.......Kpnbr.fwem.......k"); \r
       for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
       break;\r
     case VariantKnightmate:\r
       pieces = KnightmateArray;\r
-      strcpy(pieceToChar, "PNBRQFWEHACGOMK.pnbrqfwehacgomK."); \r
+      strcpy(pieceToChar, "P.BRQ...M.K......p.brq...m.k......"); \r
       break;\r
     case VariantFairy:\r
       pieces = fairyArray;\r
-      strcpy(pieceToChar, "PNBRQFWEHACGOMUKpnbrqfwehacgomuk"); \r
+      SetCharTable(pieceToChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk"); \r
       startedFromSetupPosition = TRUE;\r
       break;\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
@@ -3877,7 +3934,6 @@ InitPosition(redraw)
         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
@@ -3889,11 +3945,12 @@ InitPosition(redraw)
     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
+    pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */\r
+    if(pawnRow < 1) pawnRow = 1;\r
 \r
     /* User pieceToChar list overrules defaults */\r
     if(appData.pieceToCharTable != NULL)\r
-        strcpy(pieceToChar, appData.pieceToCharTable);\r
+        SetCharTable(pieceToChar, appData.pieceToCharTable);\r
 \r
     for( j=0; j<BOARD_WIDTH; j++ ) { ChessSquare s = EmptySquare;\r
 \r
@@ -3929,6 +3986,23 @@ InitPosition(redraw)
             initialPosition[BOARD_HEIGHT-2][j] = BlackBishop;\r
     }\r
 \r
+    if( nrCastlingRights == -1) {\r
+        /* [HGM] Build normal castling rights (must be done after board sizing!) */\r
+        /*       This sets default castling rights from none to normal corners   */\r
+        /* Variants with other castling rights must set them themselves above    */\r
+        nrCastlingRights = 6;\r
+       \r
+        castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
+        castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
+        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;\r
+        castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
+        castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
+        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
+\r
+        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
       if( appData.defaultFrcPosition < 0 ) {\r
         ShuffleFRC( initialPosition );\r
@@ -3942,8 +4016,13 @@ InitPosition(redraw)
 \r
     if(oldx != gameInfo.boardWidth ||\r
        oldy != gameInfo.boardHeight ||\r
-       oldh != gameInfo.holdingsWidth )\r
-            InitDrawingSizes(-1 ,0);\r
+       oldh != gameInfo.holdingsWidth\r
+#ifdef GOTHIC\r
+       || oldv == VariantGothic ||\r
+       gameInfo.variant == VariantGothic\r
+#endif\r
+                                         )\r
+            InitDrawingSizes(-2 ,0);\r
 \r
     if (redraw)\r
       DrawPosition(TRUE, boards[currentMove]);\r
@@ -4014,7 +4093,10 @@ IsPromotion(fromX, fromY, toX, toY)
     piece = boards[currentMove][fromY][fromX];\r
     if(gameInfo.variant == VariantShogi) {\r
         promotionZoneSize = 3;\r
-        highestPromotingPiece = (int)WhiteFerz; /* Silver */\r
+        highestPromotingPiece = (int)WhiteKing;\r
+        /* [HGM] Should be Silver = Ferz, really, but legality testing is off,\r
+           and if in normal chess we then allow promotion to King, why not\r
+           allow promotion of other piece in Shogi?                         */\r
     }\r
     if((int)piece >= BlackPawn) {\r
         if(toY >= promotionZoneSize && fromY >= promotionZoneSize)\r
@@ -4165,14 +4247,13 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
      int promoChar;\r
 {\r
     ChessMove moveType;\r
+    ChessSquare pdown, pup;\r
 \r
     if (fromX < 0 || fromY < 0) return ImpossibleMove;\r
     if ((fromX == toX) && (fromY == toY)) {\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
+\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
@@ -4278,6 +4359,8 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
        break;\r
 \r
       case EditPosition:\r
+       /* EditPosition, empty square, or different color piece;\r
+          click-click move is possible */\r
        if (toX == -2 || toY == -2) {\r
            boards[0][fromY][fromX] = EmptySquare;\r
            DrawPosition(FALSE, boards[currentMove]);\r
@@ -4289,26 +4372,49 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
         return ImpossibleMove;\r
     }\r
 \r
-    if (toX < 0 || toY < 0) return ImpossibleMove;\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( boards[currentMove][toY][toX] != EmptySquare ) return ImpossibleMove;\r
+    if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { \r
+         if( pup != EmptySquare ) return ImpossibleMove;\r
+         if(appData.testLegality) {\r
+             /* it would be more logical if LegalityTest() also figured out\r
+              * which drops are legal. For now we forbid pawns on back rank.\r
+              * Shogi is on its own here...\r
+              */\r
+             if( (pdown == WhitePawn || pdown == BlackPawn) &&\r
+                 (toY == 0 || toY == BOARD_HEIGHT -1 ) )\r
+                 return(ImpossibleMove); /* no pawn drops on 1st/8th */\r
+         }\r
          return WhiteDrop; /* Not needed to specify white or black yet */\r
     }\r
 \r
     userOfferedDraw = FALSE;\r
        \r
-    if (appData.testLegality) {\r
-       moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
-                                EP_UNKNOWN, castlingRights[currentMove],\r
+    /* [HGM] always test for legality, to get promotion info */\r
+    moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
+                          epStatus[currentMove], castlingRights[currentMove],\r
                                          fromY, fromX, toY, toX, promoChar);\r
+\r
+    /* [HGM] but possibly ignore an IllegalMove result */\r
+    if (appData.testLegality) {\r
        if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
            DisplayMoveError("Illegal move");\r
             return ImpossibleMove;\r
        }\r
-    } else {\r
-       moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
     }\r
 \r
     return moveType;\r
@@ -4643,23 +4749,40 @@ HandleMachineMove(message, cps)
         /* to make sure an illegal e.p. capture does not slip through,   */\r
         /* to cause a forfeit on a justified illegal-move complaint      */\r
         /* of the opponent.                                              */\r
-        if(gameMode==TwoMachinesPlay && appData.testLegality &&\r
-           fromY != DROP_RANK && /* [HGM] temporary; should still add legality test for drops */\r
-           LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+        if( gameMode==TwoMachinesPlay && appData.testLegality\r
+            && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */\r
+                                                              ) {\r
+           ChessMove moveType;\r
+           moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
                         epStatus[forwardMostMove], castlingRights[forwardMostMove],\r
-                             fromY, fromX, toY, toX, promoChar) == IllegalMove)\r
-           {\r
+                             fromY, fromX, toY, toX, promoChar);\r
            if (appData.debugMode) {\r
                 int i;\r
                 for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",\r
                     castlingRights[forwardMostMove][i], castlingRank[i]);\r
                 fprintf(debugFP, "castling rights\n");\r
            }\r
-            sprintf(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
+            if(moveType == IllegalMove) {\r
+                sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",\r
+                        machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
+                GameEnds(machineWhite ? BlackWins : WhiteWins,\r
+                           buf1, GE_XBOARD);\r
+           } else if(gameInfo.variant != VariantFischeRandom)\r
+           /* [HGM] Kludge to handle engines that send FRC-style castling\r
+              when they shouldn't (like TSCP-Gothic) */\r
+           switch(moveType) {\r
+             case WhiteASideCastleFR:\r
+             case BlackASideCastleFR:\r
+               toY++;\r
+               currentMoveString[2]++;\r
+               break;\r
+             case WhiteHSideCastleFR:\r
+             case BlackHSideCastleFR:\r
+               toY--;\r
+               currentMoveString[2]--;\r
+               break;\r
            }\r
+        }\r
        hintRequested = FALSE;\r
        lastHint[0] = NULLCHAR;\r
        bookRequested = FALSE;\r
@@ -4946,6 +5069,31 @@ HandleMachineMove(message, cps)
        cps->useSigterm = FALSE;\r
     }\r
 \r
+    /* [HGM] Allow engine to set up a position. Don't ask me why one would\r
+     * want this, I was asked to put it in, and obliged.\r
+     */\r
+    if (!strncmp(message, "setboard ", 9)) {\r
+        Board initial_position; int i;\r
+\r
+        GameEnds(GameIsDrawn, "Engine aborts game", GE_XBOARD);\r
+\r
+        if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {\r
+            DisplayError("Bad FEN received from engine", 0);\r
+            return ;\r
+        } else {\r
+           Reset(FALSE, FALSE);\r
+           CopyBoard(boards[0], initial_position);\r
+           initialRulePlies = FENrulePlies;\r
+           epStatus[0] = FENepStatus;\r
+           for( i=0; i<nrCastlingRights; i++ )\r
+                castlingRights[0][i] = FENcastlingRights[i];\r
+           if(blackPlaysFirst) gameMode = MachinePlaysWhite;\r
+           else gameMode = MachinePlaysBlack;                 \r
+           DrawPosition(FALSE, boards[currentMove]);\r
+        }\r
+       return;\r
+    }\r
+\r
     /*\r
      * Look for communication commands\r
      */\r
@@ -5771,7 +5919,8 @@ ParseGameHistory(game)
          default:\r
            break;\r
          case MT_CHECK:\r
-           strcat(parseList[boardIndex - 1], "+");\r
+            if(gameInfo.variant != VariantShogi)\r
+                strcat(parseList[boardIndex - 1], "+");\r
            break;\r
          case MT_CHECKMATE:\r
            strcat(parseList[boardIndex - 1], "#");\r
@@ -5788,24 +5937,23 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      int promoChar;\r
      Board board;\r
 {\r
-    ChessSquare captured = board[toY][toX], piece; int p;\r
+  ChessSquare captured = board[toY][toX], piece; int p;\r
 \r
-    /* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
-    if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\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
          \r
-    if (fromY == DROP_RANK) {\r
+  if (fromX == toX && fromY == toY) return;\r
+\r
+  if (fromY == DROP_RANK) {\r
        /* must be first */\r
        board[toY][toX] = (ChessSquare) fromX;\r
-    } else if (fromX == toX && fromY == toY) {\r
-       return;\r
-    }\r
+  } else {\r
+     piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */\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
+    if (board[fromY][fromX] == WhiteKing &&\r
             board[toY][toX] == WhiteRook) {\r
       board[fromY][fromX] = EmptySquare;\r
       board[toY][toX] = EmptySquare;\r
@@ -5855,17 +6003,16 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][2] = WhiteRook;\r
     } else if (board[fromY][fromX] == WhitePawn\r
                && toY == BOARD_HEIGHT-1\r
-#ifdef FAIRY\r
                && gameInfo.variant != VariantXiangqi\r
-#endif\r
                ) {\r
        /* white pawn promotion */\r
         board[toY][toX] = CharToPiece(ToUpper(promoChar));\r
         if (board[toY][toX] == EmptySquare) {\r
             board[toY][toX] = WhiteQueen;\r
        }\r
-        if(gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
-            board[toY][toX] += (int) WhiteAlfil - (int) WhitePawn;\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == BOARD_HEIGHT-4)\r
               && (toX != fromX)\r
@@ -5905,17 +6052,16 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][2] = BlackRook;\r
     } else if (board[fromY][fromX] == BlackPawn\r
               && toY == 0\r
-#ifdef FAIRY\r
                && gameInfo.variant != VariantXiangqi\r
-#endif\r
                ) {\r
        /* black pawn promotion */\r
        board[0][toX] = CharToPiece(ToLower(promoChar));\r
        if (board[0][toX] == EmptySquare) {\r
            board[0][toX] = BlackQueen;\r
        }\r
-        if(gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
-            board[toY][toX] += (int) WhiteAlfil - (int) WhitePawn;\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
        board[fromY][fromX] = EmptySquare;\r
     } else if ((fromY == 3)\r
               && (toX != fromX)\r
@@ -5929,6 +6075,12 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = board[fromY][fromX];\r
        board[fromY][fromX] = EmptySquare;\r
     }\r
+\r
+    /* [HGM] now we promote for Shogi, if needed */\r
+    if(gameInfo.variant == VariantShogi && promoChar == 'q')\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+  }\r
+\r
     if (gameInfo.holdingsWidth != 0) {\r
 \r
       /* !!A lot more code needs to be written to support holdings  */\r
@@ -5962,6 +6114,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
                   captured = (ChessSquare) (DEMOTED captured);\r
                   p = DEMOTED p;\r
           }\r
+          p = PieceToNumber((ChessSquare)p);\r
           if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; }\r
           board[p][BOARD_WIDTH-2]++;\r
           board[p][BOARD_WIDTH-1] =\r
@@ -5972,6 +6125,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
                   captured = (ChessSquare) (DEMOTED captured);\r
                   p = DEMOTED p;\r
           }\r
+          p = PieceToNumber((ChessSquare)p);\r
           if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }\r
           board[BOARD_HEIGHT-1-p][1]++;\r
           board[BOARD_HEIGHT-1-p][0] =\r
@@ -5993,7 +6147,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = EmptySquare;\r
       }\r
     }\r
-    if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR) {\r
+    if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') {\r
         /* [HGM] Shogi promotions */\r
         board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
     }\r
@@ -6073,12 +6227,17 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
       default:\r
        break;\r
       case MT_CHECK:\r
-       strcat(parseList[forwardMostMove - 1], "+");\r
+        if(gameInfo.variant != VariantShogi)\r
+            strcat(parseList[forwardMostMove - 1], "+");\r
        break;\r
       case MT_CHECKMATE:\r
        strcat(parseList[forwardMostMove - 1], "#");\r
        break;\r
     }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "move: %s, parse: %s (%c)\n", moveList[forwardMostMove-1], parseList[forwardMostMove-1], moveList[forwardMostMove-1][4]);\r
+    }\r
+\r
 }\r
 \r
 /* Updates currentMove if not pausing */\r
@@ -6124,7 +6283,8 @@ InitChessProgram(cps)
         || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8\r
                                             ) {\r
       char *v = VariantName(gameInfo.variant);\r
-      if (StrStr(cps->variants, v) == NULL) {\r
+      if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) {\r
+        /* [HGM] in protocol 1 we have to assume all variants valid */\r
        sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);\r
        DisplayFatalError(buf, 0, 1);\r
        return;\r
@@ -6144,15 +6304,13 @@ InitChessProgram(cps)
            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
+           if (cps->protocolVersion != 1 && StrStr(cps->variants, "boardsize") == NULL) {\r
              sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
                   gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
              DisplayFatalError(buf, 0, 1);\r
              return;\r
            }\r
-#endif\r
+           /* [HGM] here we really should compare with the maximum supported board size */\r
            sprintf(buf, "%dx%d+%d_", gameInfo.boardWidth,\r
                               gameInfo.boardHeight, gameInfo.holdingsSize );\r
            while(*b++ != '_');\r
@@ -6802,12 +6960,10 @@ LoadGameOneMove(readAhead)
 \r
       case WhiteCapturesEnPassant:\r
       case BlackCapturesEnPassant:\r
-#ifdef FAIRY\r
       case WhitePromotionChancellor:\r
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
-#endif\r
       case WhitePromotionQueen:\r
       case BlackPromotionQueen:\r
       case WhitePromotionRook:\r
@@ -9336,6 +9492,7 @@ EditPositionMenuEvent(selection, x, y)
      int x, y;\r
 {\r
     char buf[MSG_SIZ];\r
+    ChessSquare piece = boards[0][y][x];\r
 \r
     if (gameMode != EditPosition && gameMode != IcsExamining) return;\r
 \r
@@ -9385,7 +9542,40 @@ EditPositionMenuEvent(selection, x, y)
        }\r
        break;\r
 \r
+      case PromotePiece:\r
+        if(piece >= (int)WhitePawn && piece < (int)WhiteWazir ||\r
+           piece >= (int)BlackPawn && piece < (int)BlackWazir   ) {\r
+            selection = (ChessSquare) (PROMOTED piece);\r
+        } else if(piece == EmptySquare) selection = WhiteWazir;\r
+        else selection = (ChessSquare)((int)piece - 1);\r
+        goto defaultlabel;\r
+\r
+      case DemotePiece:\r
+        if(piece >= (int)WhiteUnicorn && piece < (int)WhiteKing ||\r
+           piece >= (int)BlackUnicorn && piece < (int)BlackKing   ) {\r
+            selection = (ChessSquare) (DEMOTED piece);\r
+        } else if( piece == WhiteKing || piece == BlackKing )\r
+            selection = (ChessSquare)((int)piece - (int)WhiteKing + (int)WhiteMan);\r
+        else if(piece == EmptySquare) selection = BlackWazir;\r
+        else selection = (ChessSquare)((int)piece + 1);       \r
+        goto defaultlabel;\r
+\r
+      case WhiteQueen:\r
+      case BlackQueen:\r
+        if(gameInfo.variant == VariantShatranj ||\r
+           gameInfo.variant == VariantXiangqi  ||\r
+           gameInfo.variant == VariantCourier    )\r
+            selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);\r
+        goto defaultlabel;\r
+\r
+      case WhiteKing:\r
+      case BlackKing:\r
+        if(gameInfo.variant == VariantXiangqi)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);\r
       default:\r
+        defaultlabel:\r
        if (gameMode == IcsExamining) {\r
            sprintf(buf, "%s%c@%c%c\n", ics_prefix,\r
                     PieceToChar(selection), AAA + x, ONE + y);\r
@@ -11116,6 +11306,15 @@ NextTickLength(timeRemaining)
     return nextTickLength;\r
 }\r
 \r
+/* Adjust clock one minute up or down */\r
+void\r
+AdjustClock(Boolean which, int dir)\r
+{\r
+    if(which) blackTimeRemaining += 60000*dir;\r
+    else      whiteTimeRemaining += 60000*dir;\r
+    DisplayBothClocks();\r
+}\r
+\r
 /* Stop clocks and reset to a fresh time control */\r
 void\r
 ResetClocks() \r
@@ -11468,19 +11667,27 @@ PositionToFEN(move, useFEN960)
         for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {\r
            if (boards[move][i][j] == EmptySquare) {\r
                emptycount++;\r
-           } else {\r
+            } else { ChessSquare piece = boards[move][i][j];\r
                if (emptycount > 0) {\r
                     if(emptycount<10) /* [HGM] can be >= 10 */\r
                         *p++ = '0' + emptycount;\r
                     else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
                    emptycount = 0;\r
                }\r
-                *p++ = PieceToChar(boards[move][i][j]);\r
-                if(gameInfo.variant == VariantCrazyhouse) {\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
+                } \r
+                *p++ = PieceToChar(piece);\r
+                if(gameInfo.variant == VariantCrazyhouse || gameInfo.variant == VariantBughouse) {\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
+                    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
                 }\r
@@ -11698,11 +11905,12 @@ ParseFEN(board, blackPlaysFirst, fen)
                 if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
                 while (emptycount--)\r
                         board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
-           } else if (isalpha(*p)) {\r
+            } else if (*p == '+' || isalpha(*p)) {\r
                 if (j >= gameInfo.boardWidth) return FALSE;\r
-                piece = CharToPiece(*p++);\r
+                if(*p=='+') { piece = (ChessSquare) (PROMOTED CharToPiece(*++p) ); p++; }\r
+                else piece = CharToPiece(*p++);\r
                 if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */\r
-                    piece = (ChessSquare) ((int)piece + (int)WhiteAlfil - (int)WhitePawn);\r
+                    piece = (ChessSquare) (PROMOTED piece);\r
                     p++;\r
                 }\r
                 board[i][(j++)+gameInfo.holdingsWidth] = piece;\r