exchanged some sprintf with snprintf
[xboard.git] / backend.c
index 0b1b144..3205cde 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -157,7 +157,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList));
 int LoadPositionFromFile P((char *filename, int n, char *title));\r
 int SavePositionToFile P((char *filename));\r
 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
-                 Board board));\r
+                 Board board, char *castle, char *ep));\r
 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
 void ShowMove P((int fromX, int fromY, int toX, int toY));\r
 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
@@ -669,7 +669,7 @@ InitBackEnd1()
     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
                          appData.movesPerSession)) {\r
        char buf[MSG_SIZ];\r
-       sprintf(buf, _("bad timeControl option %s"), appData.timeControl);\r
+       snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);\r
        DisplayFatalError(buf, 0, 2);\r
     }\r
 \r
@@ -684,7 +684,7 @@ InitBackEnd1()
            searchTime = min * 60 + sec;\r
        } else {\r
            char buf[MSG_SIZ];\r
-           sprintf(buf, _("bad searchTime option %s"), appData.searchTime);\r
+           snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);\r
            DisplayFatalError(buf, 0, 2);\r
        }\r
     }\r
@@ -831,7 +831,7 @@ InitBackEnd1()
        q = first.program;\r
        while (*q != ' ' && *q != NULLCHAR) q++;\r
        p = q;\r
-       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
+       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] backslash added */\r
        programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
                                        + strlen(PATCHLEVEL) + (q - p));\r
        sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
@@ -1124,7 +1124,7 @@ InitBackEnd3 P((void))
                sprintf(buf, _("Could not open comm port %s"),  \r
                        appData.icsCommPort);\r
            } else {\r
-               sprintf(buf, _("Could not connect to host %s, port %s"),  \r
+               snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),  \r
                        appData.icsHost, appData.icsPort);\r
            }\r
            DisplayFatalError(buf, err, 1);\r
@@ -1319,18 +1319,18 @@ establish()
     } else if (*appData.gateway != NULLCHAR) {\r
        if (*appData.remoteShell == NULLCHAR) {\r
            /* Use the rcmd protocol to run telnet program on a gateway host */\r
-           sprintf(buf, "%s %s %s",\r
+           snprintf(buf, sizeof(buf), "%s %s %s",\r
                    appData.telnetProgram, appData.icsHost, appData.icsPort);\r
            return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
 \r
        } else {\r
            /* Use the rsh program to run telnet program on a gateway host */\r
            if (*appData.remoteUser == NULLCHAR) {\r
-               sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
+               snprintf(buf, sizeof(buf), "%s %s %s %s %s", appData.remoteShell,\r
                        appData.gateway, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
            } else {\r
-               sprintf(buf, "%s %s -l %s %s %s %s",\r
+               snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s",\r
                        appData.remoteShell, appData.gateway, \r
                        appData.remoteUser, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
@@ -2277,7 +2277,7 @@ read_from_ics(isr, closure, data, count, error)
                        }\r
                        if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
                            int depth=0; float score;\r
-                           if(sscanf(parse, "%f/%d", &score, &depth) == 2 && depth>0) {\r
+                           if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {\r
                                // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph\r
                                pvInfoList[forwardMostMove-1].depth = depth;\r
                                pvInfoList[forwardMostMove-1].score = 100*score;\r
@@ -2345,7 +2345,7 @@ read_from_ics(isr, closure, data, count, error)
 \r
            if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
              char buf[MSG_SIZ];\r
-             sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
+             snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost);\r
              DisplayIcsInteractionTitle(buf);\r
              have_set_title = TRUE;\r
            }\r
@@ -3353,7 +3353,7 @@ ParseBoard12(string)
               &ticking);\r
 \r
     if (n < 21) {\r
-       sprintf(str, _("Failed to parse board string:\n\"%s\""), string);\r
+        snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);\r
        DisplayError(str, 0);\r
        return;\r
     }\r
@@ -3724,7 +3724,7 @@ ParseBoard12(string)
          else  strcpy(buf, str); // might be castling\r
          if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) \r
                strcat(buf, prom); // long move lacks promo specification!\r
-         if(!appData.testLegality) {\r
+         if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)\r
                if(appData.debugMode) \r
                        fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
                strcpy(move_str, buf);\r
@@ -3750,6 +3750,7 @@ ParseBoard12(string)
                     strcat(parseList[moveNum - 1], "+");\r
                break;\r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate\r
                strcat(parseList[moveNum - 1], "#");\r
                break;\r
            }\r
@@ -5216,9 +5217,9 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move
        * If they don't match, display an error message.\r
        */\r
       int saveAnimate;\r
-      Board testBoard;\r
+      Board testBoard; char testRights[BOARD_SIZE]; char testStatus;\r
       CopyBoard(testBoard, boards[currentMove]);\r
-      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus);\r
 \r
       if (CompareBoards(testBoard, boards[currentMove+1])) {\r
        ForwardInner(currentMove+1);\r
@@ -5262,19 +5263,6 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move
 \r
   MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
 \r
-    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
-               && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
-       // [HGM] superchess: take promotion piece out of holdings\r
-       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
-       if(WhiteOnMove(forwardMostMove-1)) {\r
-           if(!--boards[forwardMostMove][k][BOARD_WIDTH-2])\r
-               boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare;\r
-       } else {\r
-           if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1])\r
-               boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare;\r
-       }\r
-    }\r
-\r
   if (gameMode == BeginningOfGame) {\r
     if (appData.noChessProgram) {\r
       gameMode = EditGame;\r
@@ -5328,6 +5316,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", move
     case MT_CHECK:\r
       break;\r
     case MT_CHECKMATE:\r
+    case MT_STAINMATE:\r
       if (WhiteOnMove(currentMove)) {\r
        GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
       } else {\r
@@ -5646,7 +5635,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
          if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
                char buf[3*MSG_SIZ];\r
 \r
-               sprintf(buf, "kibitz %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
+               sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
                        programStats.score / 100.,\r
                        programStats.depth,\r
                        programStats.time / 100.,\r
@@ -5713,6 +5702,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                     NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,\r
                     NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;\r
                static int moveCount = 6;\r
+               ChessMove result;\r
+               char *reason = NULL;\r
 \r
                 /* Count what is on board. */\r
                for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
@@ -5776,9 +5767,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                /* Bare King in Shatranj (loses) or Losers (wins) */\r
                 if( NrW == 1 || NrPieces - NrW == 1) {\r
                   if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)\r
-                    epStatus[forwardMostMove] = EP_STALEMATE; // kludge to make position claimable as win\r
+                    epStatus[forwardMostMove] = EP_WINS;  // mark as win, so it becomes claimable\r
                     if(appData.checkMates) {\r
-                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
                                                        "Xboard adjudication: Bare king", GE_XBOARD );\r
@@ -5787,7 +5778,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                  } else\r
                   if( gameInfo.variant == VariantShatranj && --bare < 0)\r
                   {    /* bare King */\r
-                       epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as win for stm\r
+                       epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm\r
                        if(appData.checkMates) {\r
                            /* but only adjudicate if adjudication enabled */\r
                            SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
@@ -5801,40 +5792,61 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 \r
 \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
+            switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile,\r
                                        castlingRights[forwardMostMove]) ) {\r
-             case MT_NONE:\r
              case MT_CHECK:\r
+               if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time\r
+                   int i, checkCnt = 0;    // (should really be done by making nr of checks part of game state)\r
+                   for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) {\r
+                       if(MateTest(boards[i], PosFlags(i), epStatus[i], castlingRights[i]) == MT_CHECK)\r
+                           checkCnt++;\r
+                       if(checkCnt >= 2) {\r
+                           reason = "Xboard adjudication: 3rd check";\r
+                           epStatus[forwardMostMove] = EP_CHECKMATE;\r
+                           break;\r
+                       }\r
+                   }\r
+               }\r
+             case MT_NONE:\r
              default:\r
                break;\r
              case MT_STALEMATE:\r
-               if(epStatus[forwardMostMove] != EP_CHECKMATE) // [HGM] spare win through baring or K-capt\r
-                   epStatus[forwardMostMove] = EP_STALEMATE;\r
-                if(appData.checkMates) {\r
-                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
-                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantSuicide\r
-                                                        || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
-                       GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, // stalemated side wins!\r
-                               "Xboard adjudication: Stalemate", GE_XBOARD );\r
-                   else\r
-                       GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", GE_XBOARD );\r
-                   return;\r
+             case MT_STAINMATE:\r
+               reason = "Xboard adjudication: Stalemate";\r
+               if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt\r
+                   epStatus[forwardMostMove] = EP_STALEMATE;   // default result for stalemate is draw\r
+                   if(gameInfo.variant == VariantLosers  || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
+                       epStatus[forwardMostMove] = EP_WINS;    // in these variants stalemated is always a win\r
+                   else if(gameInfo.variant == VariantSuicide) // in suicide it depends\r
+                       epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE :\r
+                                                  ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?\r
+                                                                       EP_CHECKMATE : EP_WINS);\r
+                   else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)\r
+                       epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses\r
                }\r
                break;\r
              case MT_CHECKMATE:\r
-               epStatus[forwardMostMove] = EP_CHECKMATE;\r
-                if(appData.checkMates) {\r
+               reason = "Xboard adjudication: Checkmate";\r
+               epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);\r
+               break;\r
+           }\r
+\r
+               switch(i = epStatus[forwardMostMove]) {\r
+                   case EP_STALEMATE:\r
+                       result = GameIsDrawn; break;\r
+                   case EP_CHECKMATE:\r
+                       result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break;\r
+                   case EP_WINS:\r
+                       result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break;\r
+                   default:\r
+                       result = (ChessMove) 0;\r
+               }\r
+                if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested\r
                    SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                    ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   GameEnds( WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers) // [HGM] losers:\r
-                            ? BlackWins : WhiteWins,            // reverse the result ( A!=1 is !A for a boolean)\r
-                            "Xboard adjudication: Checkmate", GE_XBOARD );\r
+                   GameEnds( result, reason, GE_XBOARD );\r
                    return;\r
                }\r
-               break;\r
-           }\r
 \r
                 /* Next absolutely insufficient mating material. */\r
                 if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && \r
@@ -5960,7 +5972,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                                if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase\r
                                    hisPerpetual = PerpetualChase(k, forwardMostMove);\r
                                    ourPerpetual = PerpetualChase(k+1, forwardMostMove);\r
-                                   if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
+                                   if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit\r
                                        GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
                                                      "Xboard adjudication: perpetual chasing", GE_XBOARD );\r
                                        return;\r
@@ -6142,7 +6154,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellopponent ", 13)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);\r
+         snprintf(buf1, sizeof(buf1), "%ssay %s\n", ics_prefix, message + 13);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -6153,7 +6165,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellothers ", 11)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);\r
+         snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11);\r
          SendToICS(buf1);\r
        }\r
       }\r
@@ -6162,7 +6174,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellall ", 8)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);\r
+         snprintf(buf1, sizeof(buf1), "%skibitz %s\n", ics_prefix, message + 8);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -6332,7 +6344,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        || (StrStr(message, "Permission denied") != NULL)) {\r
 \r
        cps->maybeThinking = FALSE;\r
-       sprintf(buf1, _("Failed to start %s chess program %s on %s: %s\n"),\r
+       snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),\r
                cps->which, cps->program, cps->host, message);\r
        RemoveInputSource(cps->isr);\r
        DisplayFatalError(buf1, 0, 1);\r
@@ -6350,11 +6362,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                (void) CoordsToAlgebraic(boards[forwardMostMove],\r
                                    PosFlags(forwardMostMove), EP_UNKNOWN,\r
                                    fromY, fromX, toY, toX, promoChar, buf1);\r
-               sprintf(buf2, _("Hint: %s"), buf1);\r
+               snprintf(buf2, sizeof(buf2), _("Hint: %s"), buf1);\r
                DisplayInformation(buf2);\r
            } else {\r
                /* Hint move could not be parsed!? */\r
-               sprintf(buf2,\r
+             snprintf(buf2, sizeof(buf2),\r
                        _("Illegal hint move \"%s\"\nfrom %s chess program"),\r
                        buf1, cps->which);\r
                DisplayError(buf2, 0);\r
@@ -6981,11 +6993,13 @@ ParseGameHistory(game)
                                 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
                                 parseList[boardIndex]);\r
        CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+        {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[boardIndex+1][i] = castlingRights[boardIndex][i];}\r
        /* currentMoveString is set as a side-effect of yylex */\r
        strcpy(moveList[boardIndex], currentMoveString);\r
        strcat(moveList[boardIndex], "\n");\r
        boardIndex++;\r
-       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex], \r
+                                       castlingRights[boardIndex], &epStatus[boardIndex]);\r
         switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
                                  EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
          case MT_NONE:\r
@@ -6997,6 +7011,7 @@ ParseGameHistory(game)
                 strcat(parseList[boardIndex - 1], "+");\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            strcat(parseList[boardIndex - 1], "#");\r
            break;\r
        }\r
@@ -7006,55 +7021,55 @@ ParseGameHistory(game)
 \r
 /* Apply a move to the given board  */\r
 void\r
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep)\r
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
      Board board;\r
+     char *castling;\r
+     char *ep;\r
 {\r
   ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;\r
 \r
     /* [HGM] compute & store e.p. status and castling rights for new position */\r
-    /* if we are updating a board for which those exist (i.e. in boards[])    */\r
-    if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
+    /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */\r
     { int i;\r
 \r
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
-      oldEP = epStatus[p-1];\r
-      epStatus[p] = EP_NONE;\r
+      oldEP = *ep;\r
+      *ep = EP_NONE;\r
 \r
       if( board[toY][toX] != EmptySquare ) \r
-           epStatus[p] = EP_CAPTURE;  \r
+           *ep = EP_CAPTURE;  \r
 \r
       if( board[fromY][fromX] == WhitePawn ) {\r
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
-              epStatus[p] = EP_PAWN_MOVE;\r
+              *ep = EP_PAWN_MOVE;\r
            if( toY-fromY==2) {\r
                if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&\r
                        gameInfo.variant != VariantBerolina || toX < fromX)\r
-                     epStatus[p] = toX | berolina;\r
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
           }\r
       } else \r
       if( board[fromY][fromX] == BlackPawn ) {\r
            if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
-              epStatus[p] = EP_PAWN_MOVE; \r
+              *ep = EP_PAWN_MOVE; \r
            if( toY-fromY== -2) {\r
                if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&\r
                        gameInfo.variant != VariantBerolina || toX < fromX)\r
-                     epStatus[p] = toX | berolina;\r
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
           }\r
        }\r
 \r
        for(i=0; i<nrCastlingRights; i++) {\r
-           castlingRights[p][i] = castlingRights[p-1][i];\r
-           if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
-              castlingRights[p][i] == toX   && castlingRank[i] == toY   \r
-             ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+           if(castling[i] == fromX && castlingRank[i] == fromY ||\r
+              castling[i] == toX   && castlingRank[i] == toY   \r
+             ) castling[i] = -1; // revoke for moved or captured piece\r
        }\r
 \r
     }\r
@@ -7293,6 +7308,19 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
     }\r
 \r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
+               && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+       // [HGM] superchess: take promotion piece out of holdings\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if((int)piece < (int)BlackPawn) { // determine stm from piece color\r
+           if(!--board[k][BOARD_WIDTH-2])\r
+               board[k][BOARD_WIDTH-1] = EmptySquare;\r
+       } else {\r
+           if(!--board[BOARD_HEIGHT-1-k][1])\r
+               board[BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+       }\r
+    }\r
+\r
 }\r
 \r
 /* Updates forwardMostMove */\r
@@ -7358,7 +7386,9 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
        commentList[forwardMostMove+1] = NULL;\r
     }\r
     CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);\r
-    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1]);\r
+    {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[forwardMostMove+1][i] = castlingRights[forwardMostMove][i];}\r
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1], \r
+                               castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);\r
     forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board\r
     gameInfo.result = GameUnfinished;\r
     if (gameInfo.resultDetails != NULL) {\r
@@ -7383,6 +7413,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
             strcat(parseList[forwardMostMove - 1], "+");\r
        break;\r
       case MT_CHECKMATE:\r
+      case MT_STAINMATE:\r
        strcat(parseList[forwardMostMove - 1], "#");\r
        break;\r
     }\r
@@ -7538,7 +7569,7 @@ InitChessProgram(cps, setup)
     }\r
 \r
     if (cps->sendICS) {\r
-      sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
+      snprintf(buf, sizeof(buf), "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
       SendToProgram(buf, cps);\r
     }\r
     cps->maybeThinking = FALSE;\r
@@ -7586,10 +7617,10 @@ StartChessProgram(cps)
        err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);\r
     } else {\r
        if (*appData.remoteUser == NULLCHAR) {\r
-           sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,\r
+         snprintf(buf, sizeof(buf), "%s %s %s", appData.remoteShell, cps->host,\r
                    cps->program);\r
        } else {\r
-           sprintf(buf, "%s %s -l %s %s", appData.remoteShell,\r
+         snprintf(buf, sizeof(buf), "%s %s -l %s %s", appData.remoteShell,\r
                    cps->host, appData.remoteUser, cps->program);\r
        }\r
        err = StartChildProcess(buf, "", &cps->pr);\r
@@ -7750,14 +7781,14 @@ GameEnds(result, resultDetails, whosays)
                // [HGM] losers: because the logic is becoming a bit hairy, determine true result first\r
                if(epStatus[forwardMostMove] == EP_CHECKMATE) {\r
                    /* [HGM] verify: engine mate claims accepted if they were flagged */\r
-                   trueResult = WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers)\r
-                       ? BlackWins : WhiteWins; // [HGM] losers: reverse the result in VariantLosers!\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins;\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
                } else\r
-               if(epStatus[forwardMostMove] == EP_STALEMATE) {\r
+               if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now\r
                    trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE\r
-                   if(gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuicide || \r
-                      gameInfo.variant == VariantLosers)  // [HGM] losers: in giveaway variants stalemate wins\r
-                       trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
                }\r
 \r
                // now verify win claims, but not in drop games, as we don't understand those yet\r
@@ -8369,6 +8400,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8404,6 +8436,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8508,7 +8541,7 @@ LoadGameFromFile(filename, n, title, useList)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+         snprintf(buf, sizeof(buf),  _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        }\r
@@ -8564,6 +8597,7 @@ MakeRegisteredMove()
                break;\r
                \r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE:\r
                if (WhiteOnMove(currentMove)) {\r
                    GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
                } else {\r
@@ -8734,7 +8768,7 @@ LoadGame(f, gameNumber, title, useList)
     yynewfile(f);\r
 \r
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
-       sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
+      snprintf(buf, sizeof(buf), "%s vs. %s", lg->gameInfo.white,\r
                lg->gameInfo.black);\r
            DisplayTitle(buf);\r
     } else if (*title != NULLCHAR) {\r
@@ -9161,7 +9195,7 @@ LoadPositionFromFile(filename, n, title)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+            snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9375,7 +9409,7 @@ SaveGameToFile(filename, append)
     } else {\r
        f = fopen(filename, append ? "a" : "w");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9749,7 +9783,7 @@ SavePositionToFile(filename)
     } else {\r
        f = fopen(filename, "a");\r
        if (f == NULL) {\r
-           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -12737,16 +12771,16 @@ DisplayComment(moveNumber, text)
                    WhiteOnMove(moveNumber) ? " " : ".. ",\r
                    parseList[moveNumber]);\r
         }\r
+       // [HGM] PV info: display PV info together with (or as) comment\r
+       if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+           if(text == NULL) text = "";                                           \r
+           score = pvInfoList[moveNumber].score;\r
+           sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
+                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
+           text = buf;\r
+       }\r
     } else title[0] = 0;\r
 \r
-    // [HGM] PV info: display PV info together with (or as) comment\r
-    if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
-        if(text == NULL) text = "";                                           \r
-        score = pvInfoList[moveNumber].score;\r
-        sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
-                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
-        CommentPopUp(title, buf);\r
-    } else\r
     if (text != NULL)\r
         CommentPopUp(title, text);\r
 }\r