Start implementing rights control in Edit Position mode
[xboard.git] / backend.c
index 9b81c39..a89311b 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -296,7 +296,7 @@ int promoDefaultAltered;
 int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
 static int initPing = -1;
 int border;       /* [HGM] width of board rim, needed to size seek graph  */
-char bestMove[MSG_SIZ];
+char bestMove[MSG_SIZ], avoidMove[MSG_SIZ];
 int solvingTime, totalTime;
 
 /* States for ics_getting_history */
@@ -996,6 +996,7 @@ Load (ChessProgramState *cps, int i)
        SwapEngines(i);
        ReplaceEngine(cps, i);
        FloatToFront(&appData.recentEngineList, engineLine);
+       if(gameMode == BeginningOfGame) Reset(TRUE, TRUE);
        return;
     }
     p = engineName;
@@ -1585,7 +1586,7 @@ MatchEvent (int mode)
        }
        matchMode = mode;
        matchGame = roundNr = 1;
-       first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches
+       first.matchWins = second.matchWins = totalTime = 0; // [HGM] match: needed in later matches
        NextMatchGame();
 }
 
@@ -1743,6 +1744,11 @@ InitBackEnd3 P((void))
                 CopyBoard(filePosition, boards[0]);
                 CopyBoard(initialPosition, boards[0]);
             }
+       } else if(*appData.fen != NULLCHAR) {
+           if(ParseFEN(filePosition, &blackPlaysFirst, appData.fen, TRUE) && !blackPlaysFirst) {
+                startedFromPositionFile = TRUE;
+               Reset(TRUE, TRUE);
+           }
        }
        if (initialMode == AnalyzeMode) {
          if (appData.noChessProgram) {
@@ -6454,6 +6460,7 @@ InitPosition (int redraw)
           initialRights[i] = filePosition[CASTLING][i];
       startedFromSetupPosition = TRUE;
     }
+    if(*appData.men) LoadPieceDesc(appData.men);
 
     CopyBoard(boards[0], initialPosition);
 
@@ -6975,6 +6982,7 @@ char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
 ChessMove lastLoadGameStart = EndOfFile;
 int doubleClick;
 Boolean addToBookFlag;
+static Board rightsBoard, nullBoard;
 
 void
 UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
@@ -7102,9 +7110,15 @@ UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
            if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
                ChessSquare p = boards[0][rf][ff];
                if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
-               if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); 
+               if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); else
+               if(p == WhiteKing || p == BlackKing || p == WhiteRook || p == BlackRook) {
+                   int n = rightsBoard[toY][toX] ^= 1; // toggle virginity of K or R
+                   DisplayMessage("", n ? _("rights granted") : _("rights revoked"));
+                   gatingPiece = p;
+               }
            }
            boards[0][toY][toX] = boards[0][fromY][fromX];
+           rightsBoard[toY][toX] = 0;  // revoke rights on moving
            if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
                if(boards[0][fromY][0] != EmptySquare) {
                    if(boards[0][fromY][1]) boards[0][fromY][1]--;
@@ -7161,6 +7175,7 @@ UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
         if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
             ClearPremoveHighlights(); // was included
        else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated  by premove highlights
+       DrawPosition(FALSE, NULL);
        return;
     }
 
@@ -7904,7 +7919,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        flashing = 1; // prevent recursive calling (by release of to-click) while flashing piece
        UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
        if (!appData.highlightLastMove || gotPremove) ClearHighlights();
-       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY), DrawPosition(FALSE, NULL);
        if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed
           Explode(boards[currentMove-1], fromX, fromY, toX, toY))
            DrawPosition(TRUE, boards[currentMove]);
@@ -8050,6 +8065,9 @@ SendProgramStatsToFrontend (ChessProgramState * cps, ChessProgramStats * cpstats
 
     if(stats.pv && stats.pv[0]) safeStrCpy(lastPV[stats.which], stats.pv, sizeof(lastPV[stats.which])/sizeof(lastPV[stats.which][0])); // [HGM] pv: remember last PV of each
 
+    if( gameMode == AnalyzeMode && stats.pv && stats.pv[0]
+        && appData.analysisBell && stats.time >= 100*appData.analysisBell ) RingBell();
+
     SetProgramStats( &stats );
 }
 
@@ -8942,11 +8960,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         }
         if(appData.epd) {
            if(solvingTime >= 0) {
-              snprintf(buf1, MSG_SIZ, "%d. %4.2fs\n", matchGame, solvingTime/100.);
-              totalTime += solvingTime; first.matchWins++;
+              snprintf(buf1, MSG_SIZ, "%d. %4.2fs: %s ", matchGame, solvingTime/100., parseList[backwardMostMove]);
+              totalTime += solvingTime; first.matchWins++; solvingTime = -1;
            } else {
-              snprintf(buf1, MSG_SIZ, "%d. wrong (%s)\n", matchGame, parseList[backwardMostMove]);
-              second.matchWins++;
+              snprintf(buf1, MSG_SIZ, "%d. %s?%s ", matchGame, parseList[backwardMostMove], solvingTime == -2 ? " ???" : "");
+              if(solvingTime == -2) second.matchWins++;
            }
            OutputKibitz(2, buf1);
            GameEnds(GameUnfinished, NULL, GE_XBOARD);
@@ -9685,6 +9703,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 
        if (!ignore) {
            ChessProgramStats tempStats = programStats; // [HGM] info: filter out info lines
+           int solved = 0;
            buf1[0] = NULLCHAR;
            if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
@@ -9713,10 +9732,33 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 
                if(*bestMove) { // rememer time best EPD move was first found
                    int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2;
-                   ChessMove mt;
-                   int ok = ParseOneMove(bestMove, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1);
-                   ok    &= ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
-                   solvingTime = (ok && ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2 ? time : -1);
+                   ChessMove mt; char *p = bestMove;
+                   int ok = ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
+                   solved = 0;
+                   while(ok && *p && ParseOneMove(p, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1)) {
+                       if(ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2) {
+                           solvingTime = (solvingTime < 0 ? time : solvingTime);
+                           solved = 1;
+                           break;
+                       }
+                       while(*p && *p != ' ') p++;
+                       while(*p == ' ') p++;
+                   }
+                   if(!solved) solvingTime = -1;
+               }
+               if(*avoidMove && !solved) {
+                   int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2;
+                   ChessMove mt; char *p = avoidMove, solved = 1;
+                   int ok = ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
+                   while(ok && *p && ParseOneMove(p, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1)) {
+                       if(ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2) {
+                           solved = 0; solvingTime = -2;
+                           break;
+                       }
+                       while(*p && *p != ' ') p++;
+                       while(*p == ' ') p++;
+                   }
+                   if(solved && !*bestMove) solvingTime = (solvingTime < 0 ? time : solvingTime);
                }
 
                if(serverMoves && (time > 100 || time == 0 && plylev > 7)) {
@@ -10783,6 +10825,7 @@ InitChessProgram (ChessProgramState *cps, int setup)
 
       b = SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth,
                            gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, cps->tidy);
+
       if (b == NULL) {
        VariantClass v;
        char c, *q = cps->variants, *p = strchr(q, ',');
@@ -11418,6 +11461,11 @@ NextMatchGame ()
     res = LoadGameOrPosition(matchGame); // setup game
     appData.noChessProgram = FALSE; // LoadGameOrPosition might call Reset too!
     if(!res) return; // abort when bad game/pos file
+    if(appData.epd) {// in EPD mode we make sure first engine is to move
+       firstWhite = !(forwardMostMove & 1);
+       first.twoMachinesColor =  firstWhite ? "white\n" : "black\n";   // perform actual color assignement
+       second.twoMachinesColor = firstWhite ? "black\n" : "white\n";
+    }
     TwoMachinesEvent();
 }
 
@@ -11825,6 +11873,16 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
            return;
        } else {
            gameMode = nextGameMode;
+           if(appData.epd) {
+               snprintf(buf, MSG_SIZ, "-------------------------------------- ");
+               OutputKibitz(2, buf);
+               snprintf(buf, MSG_SIZ, _("Average solving time %4.2f sec (total time %4.2f sec) "), totalTime/(100.*first.matchWins), totalTime/100.);
+               OutputKibitz(2, buf);
+               snprintf(buf, MSG_SIZ, _("%d avoid-moves played "), second.matchWins);
+               if(second.matchWins) OutputKibitz(2, buf);
+               snprintf(buf, MSG_SIZ, _("Solved %d out of %d (%3.1f%%) "), first.matchWins, nextGame-1, first.matchWins*100./(nextGame-1));
+               OutputKibitz(2, buf);
+           }
            snprintf(buf, MSG_SIZ, _("Match %s vs. %s: final score %d-%d-%d"),
                     first.tidy, second.tidy,
                     first.matchWins, second.matchWins,
@@ -11965,7 +12023,10 @@ Reset (int redraw, int init)
     lastHint[0] = NULLCHAR;
     ClearGameInfo(&gameInfo);
     gameInfo.variant = StringToVariant(appData.variant);
-    if(gameInfo.variant == VariantNormal && strcmp(appData.variant, "normal")) gameInfo.variant = VariantUnknown;
+    if(gameInfo.variant == VariantNormal && strcmp(appData.variant, "normal")) {
+       gameInfo.variant = VariantUnknown;
+       strncpy(engineVariant, appData.variant, MSG_SIZ);
+    }
     ics_user_moved = ics_clock_paused = FALSE;
     ics_getting_history = H_FALSE;
     ics_gamenum = -1;
@@ -12097,21 +12158,17 @@ AutoPlayOneMove ()
            SetHighlights(-1, -1, toX, toY);
        }
     } else {
-        int viaX = moveList[currentMove][5] - AAA;
-        int viaY = moveList[currentMove][6] - ONE;
         fromX = moveList[currentMove][0] - AAA;
         fromY = moveList[currentMove][1] - ONE;
 
         HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
 
         if(moveList[currentMove][4] == ';') { // multi-leg
-            ChessSquare piece = boards[currentMove][viaY][viaX];
-           AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
-            boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
-            AnimateMove(boards[currentMove], fromX=viaX, fromY=viaY, toX, toY);
-            boards[currentMove][viaY][viaX] = piece;
-        } else
+            killX = moveList[currentMove][5] - AAA;
+            killY = moveList[currentMove][6] - ONE;
+        }
        AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
+       killX = killY = -1;
 
        if (appData.highlightLastMove) {
            SetHighlights(fromX, fromY, toX, toY);
@@ -12932,7 +12989,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     char buf[MSG_SIZ];
     int gn = gameNumber;
     ListGame *lg = NULL;
-    int numPGNTags = 0;
+    int numPGNTags = 0, i;
     int err, pos = -1;
     GameMode oldGameMode;
     VariantClass v, oldVariant = gameInfo.variant; /* [HGM] PGNvariant */
@@ -13121,6 +13178,8 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     if (appData.debugMode)
       fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
 
+    for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; } // reset VariantMen
+
     if (cm == XBoardGame) {
        /* Skip any header junk before position diagram and/or move 1 */
        for (;;) {
@@ -13514,9 +13573,12 @@ LoadPosition (FILE *f, int positionNumber, char *title)
            DisplayError(_("Bad FEN position in file"), 0);
            return FALSE;
        }
-       if((p = strstr(line, ";")) && (p = strstr(p+1, "bm "))) { // EPD with best move
-           sscanf(p+3, "%s", bestMove);
+       if((strchr(line, ';')) && (p = strstr(line, " bm "))) { // EPD with best move
+           sscanf(p+4, "%[^;]", bestMove);
        } else *bestMove = NULLCHAR;
+       if((strchr(line, ';')) && (p = strstr(line, " am "))) { // EPD with avoid move
+           sscanf(p+4, "%[^;]", avoidMove);
+       } else *avoidMove = NULLCHAR;
     } else {
        (void) fgets(line, MSG_SIZ, f);
        (void) fgets(line, MSG_SIZ, f);
@@ -14970,6 +15032,7 @@ TwoMachinesEvent P((void))
       ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
       return;
     }
+  if(!appData.epd) {
     if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
 
     if(!SupportedVariant(second.variants, gameInfo.variant, gameInfo.boardWidth,
@@ -14988,6 +15051,7 @@ TwoMachinesEvent P((void))
       ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
       return;
     }
+  }
     GetTimeMark(&now); // [HGM] matchpause: implement match pause after engine load
     if(appData.matchPause>10000 || appData.matchPause<10)
                 appData.matchPause = 10000; /* [HGM] make pause adjustable */
@@ -14999,6 +15063,7 @@ TwoMachinesEvent P((void))
     // we are now committed to starting the game
     stalling = 0;
     DisplayMessage("", "");
+  if(!appData.epd) {
     if (startedFromSetupPosition) {
        SendBoard(&second, backwardMostMove);
     if (appData.debugMode) {
@@ -15008,6 +15073,7 @@ TwoMachinesEvent P((void))
     for (i = backwardMostMove; i < forwardMostMove; i++) {
        SendMoveToProgram(i, &second);
     }
+  }
 
     gameMode = TwoMachinesPlay;
     pausing = startingEngine = FALSE;
@@ -15026,11 +15092,13 @@ TwoMachinesEvent P((void))
       snprintf(buf, MSG_SIZ, "name %s\n", second.tidy);
       SendToProgram(buf, &first);
     }
+  if(!appData.epd) {
     SendToProgram(second.computerString, &second);
     if (second.sendName) {
       snprintf(buf, MSG_SIZ, "name %s\n", first.tidy);
       SendToProgram(buf, &second);
     }
+  }
 
     ResetClocks();
     if (!first.sendTime || !second.sendTime) {
@@ -15225,10 +15293,10 @@ EditGameEvent ()
     SetGameInfo();
 }
 
-
 void
 EditPositionEvent ()
 {
+    int i;
     if (gameMode == EditPosition) {
        EditGameEvent();
        return;
@@ -15240,8 +15308,11 @@ EditPositionEvent ()
     gameMode = EditPosition;
     ModeHighlight();
     SetGameInfo();
+    CopyBoard(rightsBoard, nullBoard);
     if (currentMove > 0)
       CopyBoard(boards[0], boards[currentMove]);
+    for(i=0; i<nrCastlingRights; i++) if(boards[0][CASTLING][i] != NoRights)
+      rightsBoard[castlingRank[i]][boards[0][CASTLING][i]] = 1; // copy remaining rights
 
     blackPlaysFirst = !WhiteOnMove(currentMove);
     ResetClocks();
@@ -15389,6 +15460,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
     ChessSquare piece = boards[0][y][x];
     static Board erasedBoard, currentBoard, menuBoard, nullBoard;
     static int lastVariant;
+    int baseRank = BOARD_HEIGHT-1, hasRights = 0;
 
     if (gameMode != EditPosition && gameMode != IcsExamining) return;
 
@@ -15505,12 +15577,20 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
             selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);
         goto defaultlabel;
 
+      case WhiteRook:
+        baseRank = 0;
+      case BlackRook:
+        if(y == baseRank && (x == BOARD_LEFT || x == BOARD_RGHT-1 || appData.fischerCastling)) hasRights = 1;
+        goto defaultlabel;
+
       case WhiteKing:
+        baseRank = 0;
       case BlackKing:
         if(gameInfo.variant == VariantXiangqi)
             selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);
         if(gameInfo.variant == VariantKnightmate)
             selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);
+        if(y == baseRank && (x == BOARD_WIDTH>>1 || appData.fischerCastling)) hasRights = 1;
       default:
         defaultlabel:
        if (gameMode == IcsExamining) {
@@ -15519,6 +15599,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
                     PieceToChar(selection), AAA + x, ONE + y);
            SendToICS(buf);
        } else {
+            rightsBoard[y][x] = hasRights;
             if(x < BOARD_LEFT || x >= BOARD_RGHT) {
                 int n;
                 if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
@@ -15868,19 +15949,15 @@ ForwardInner (int target)
                SetHighlights(-1, -1, toX, toY);
            }
        } else {
-            int viaX = moveList[target - 1][5] - AAA;
-            int viaY = moveList[target - 1][6] - ONE;
             fromX = moveList[target - 1][0] - AAA;
             fromY = moveList[target - 1][1] - ONE;
            if (target == currentMove + 1) {
                if(moveList[target - 1][4] == ';') { // multi-leg
-                   ChessSquare piece = boards[currentMove][viaY][viaX];
-                   AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
-                   boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
-                   AnimateMove(boards[currentMove], viaX, viaY, toX, toY);
-                   boards[currentMove][viaY][viaX] = piece;
-               } else
+                   killX = moveList[target - 1][5] - AAA;
+                   killY = moveList[target - 1][6] - ONE;
+               }
                AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
+               killX = killY = -1;
            }
            if (appData.highlightLastMove) {
                SetHighlights(fromX, fromY, toX, toY);