Refined stalemate adjudication in Suicide, some cleanup
[xboard.git] / backend.c
index 0b1b144..a49464a 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -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
@@ -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
@@ -5328,6 +5329,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 +5648,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 +5715,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 +5780,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 +5791,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 +5805,49 @@ 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
              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 +5973,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
@@ -6997,6 +7010,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
@@ -7383,6 +7397,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
@@ -7750,14 +7765,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 +8384,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 +8420,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
@@ -8564,6 +8581,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