Implement check-count field in FENs
[xboard.git] / backend.c
index 929b30f..9973c37 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1031,7 +1031,7 @@ Load (ChessProgramState *cps, int i)
        q = firstChessProgramNames;
        if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
        quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
-       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s",
                        quote, p, quote, appData.directory[i],
                        useNick ? " -fn \"" : "",
                        useNick ? nickName : "",
@@ -1041,10 +1041,10 @@ Load (ChessProgramState *cps, int i)
                        isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
                        storeVariant ? " -variant " : "",
                        storeVariant ? VariantName(gameInfo.variant) : "");
-       if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions);
-       firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
+       if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions);
+       firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2);
        if(insert != q) insert[-1] = NULLCHAR;
-       snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert);
+       snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert);
        if(q)   free(q);
        FloatToFront(&appData.recentEngineList, buf);
        ASSIGN(currentEngine[i], buf);
@@ -1593,8 +1593,6 @@ MatchEvent (int mode)
        NextMatchGame();
 }
 
-char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line
-
 void
 InitBackEnd3 P((void))
 {
@@ -1626,7 +1624,7 @@ InitBackEnd3 P((void))
        free(programVersion);
        programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
        sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
-       FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram);
+       FloatToFront(&appData.recentEngineList, currentEngine[0] ? currentEngine[0] : appData.firstChessProgram);
     }
 
     if (appData.icsActive) {
@@ -6453,6 +6451,7 @@ InitPosition (int redraw)
       initialPosition[1][1] = initialPosition[2][1] =
       initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
      }
+     initialPosition[CHECK_COUNT] = (gameInfo.variant == Variant3Check ? 0x303 : 0);
   if (appData.debugMode) {
     fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
   }
@@ -7464,7 +7463,7 @@ MarkTargetSquares (int clear)
     if(PosFlags(0) & F_MANDATORY_CAPTURE) {
       for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
       if(capt)
-      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = 0;
+      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = legal[y][x] = 0;
     }
   }
   DrawPosition(FALSE, NULL);
@@ -10032,7 +10031,7 @@ void
 ParseGameHistory (char *game)
 {
     ChessMove moveType;
-    int fromX, fromY, toX, toY, boardIndex;
+    int fromX, fromY, toX, toY, boardIndex, mask;
     char promoChar;
     char *p, *q;
     char buf[MSG_SIZ];
@@ -10193,15 +10192,18 @@ ParseGameHistory (char *game)
        strcat(moveList[boardIndex], "\n");
        boardIndex++;
        ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
+       mask = (WhiteOnMove(boardIndex) ? 0xFF : 0xFF00);
         switch (MateTest(boards[boardIndex], PosFlags(boardIndex)) ) {
          case MT_NONE:
          case MT_STALEMATE:
          default:
            break;
          case MT_CHECK:
-            if(!IS_SHOGI(gameInfo.variant))
-                strcat(parseList[boardIndex - 1], "+");
-           break;
+            if(boards[boardIndex][CHECK_COUNT]) boards[boardIndex][CHECK_COUNT] -= mask & 0x101;
+            if(!boards[boardIndex][CHECK_COUNT] || boards[boardIndex][CHECK_COUNT] & mask) {
+                if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[boardIndex - 1], "+");
+                break;
+           }
          case MT_CHECKMATE:
          case MT_STAINMATE:
            strcat(parseList[boardIndex - 1], "#");
@@ -10222,9 +10224,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
     /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
 
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
-      oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_FILE],lastRank = board[LAST_RANK];
+      oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_TO] & 255,lastRank = board[LAST_TO] >> 8;
       board[EP_STATUS] = EP_NONE;
-      board[EP_FILE] = board[EP_RANK] = board[LAST_FILE] = board[LAST_RANK] = 100;
+      board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = 0x4040;
 
   if (fromY == DROP_RANK) {
        /* must be first */
@@ -10280,7 +10282,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
+              board[LAST_TO] = toX + 256*toY;
           }
       } else
       if( pawn == BlackPawn ) {
@@ -10294,7 +10296,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
                        gameInfo.variant != VariantBerolina || toX > fromX)
                      board[EP_STATUS] = toX;
-              board[LAST_FILE] = toX; board[LAST_RANK] = toY;
+              board[LAST_TO] = toX + 256*toY;
           }
        }
 
@@ -10591,7 +10593,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
 void
 MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
 {
-    int x = toX, y = toY;
+    int x = toX, y = toY, mask;
     char *s = parseList[forwardMostMove];
     ChessSquare p = boards[forwardMostMove][toY][toX];
 //    forwardMostMove++; // [HGM] bare: moved downstream
@@ -10687,15 +10689,18 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
     }
     CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
                              moveList[forwardMostMove - 1]);
+    mask = (WhiteOnMove(forwardMostMove) ? 0xFF : 0xFF00);
     switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
       case MT_NONE:
       case MT_STALEMATE:
       default:
        break;
       case MT_CHECK:
-        if(!IS_SHOGI(gameInfo.variant))
-            strcat(parseList[forwardMostMove - 1], "+");
-       break;
+        if(boards[forwardMostMove][CHECK_COUNT]) boards[forwardMostMove][CHECK_COUNT] -= mask & 0x101;
+       if(!boards[forwardMostMove][CHECK_COUNT] || boards[forwardMostMove][CHECK_COUNT] & mask) {
+            if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[forwardMostMove - 1], "+");
+            break;
+        }
       case MT_CHECKMATE:
       case MT_STAINMATE:
        strcat(parseList[forwardMostMove - 1], "#");
@@ -11276,9 +11281,9 @@ void
 SaveEngineSettings (int n)
 {
     int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings;
-    if(!currentEngine[n] || !currentEngine[n][0]) return; // no engine from list is loaded
+    if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded
     p = strstr(firstChessProgramNames, currentEngine[n]);
-    if(!p) return; // sanity check; engine could be deleted from list after loading
+    if(!p) { DisplayMessage("saving failed: engine not found in list", ""); return; } // sanity check; engine could be deleted from list after loading
     optionSettings = ResendOptions(n ? &second : &first, FALSE);
     len = strlen(currentEngine[n]);
     q = p + len; *p = 0; // cut list into head and tail piece
@@ -11345,6 +11350,7 @@ GetEngineLine (char *s, int n)
     if(n == 1) SwapEngines(n);
     ParseArgsFromString(buf);
     if(n == 1) SwapEngines(n);
+    if(n < 2) { ASSIGN(currentEngine[n], command[i]); }
     if(n == 0 && *appData.secondChessProgram == NULLCHAR) {
        SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog)
        ParseArgsFromString(buf);
@@ -18461,6 +18467,12 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
   }
   }
 
+    i = boards[move][CHECK_COUNT];
+    if(i) {
+       sprintf(p, "%d+%d ", i&255, i>>8);
+       while(*p) p++;
+    }
+
     if(moveCounts)
     {   int i = 0, j=move;
 
@@ -18821,10 +18833,19 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
       }
     }
 
+    while(*p == ' ') p++;
+
+    board[CHECK_COUNT] = 0; // [HGM] 3check: check-count field
+    if(sscanf(p, "%d+%d", &i, &j) == 2) {
+       board[CHECK_COUNT] = i + 256*j;
+       while(*p && *p != ' ') p++;
+    }
 
-    if(sscanf(p, "%d", &i) == 1) {
+    c = sscanf(p, "%d%*d +%d+%d", &i, &j, &k);
+    if(c > 0) {
         FENrulePlies = i; /* 50-move ply counter */
         /* (The move number is still ignored)    */
+       if(c == 3 && !board[CHECK_COUNT]) board[CHECK_COUNT] = (3 - j) + 256*(3 - k); // SCIDB-style check count
     }
 
     return TRUE;