Allow ICS nickname as positional argument
[xboard.git] / backend.c
index a463fa7..6977595 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -270,6 +270,7 @@ char lastMsg[MSG_SIZ];
 ChessSquare pieceSweep = EmptySquare;
 ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
 int promoDefaultAltered;
+int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
 
 /* States for ics_getting_history */
 #define H_FALSE 0
@@ -4898,6 +4899,13 @@ GetMoveListEvent ()
 }
 
 void
+SendToBoth (char *msg)
+{   // to make it easy to keep two engines in step in dual analysis
+    SendToProgram(msg, &first);
+    if(second.analyzing) SendToProgram(msg, &second);
+}
+
+void
 AnalysisPeriodicEvent (int force)
 {
     if (((programStats.ok_to_send == 0 || programStats.line_is_book)
@@ -4905,8 +4913,7 @@ AnalysisPeriodicEvent (int force)
       return;
 
     /* Send . command to Crafty to collect stats */
-    SendToProgram(".\n", &first);
-    if(second.analyzing) SendToProgram(".\n", &second);
+    SendToBoth(".\n");
 
     /* Don't send another until we get a response (this makes
        us stop sending to old Crafty's which don't understand
@@ -6222,7 +6229,7 @@ ExcludeOneMove (int fromY, int fromX, int toY, int toX, char promoChar, char sta
     // inform engine
     snprintf(buf, MSG_SIZ, "%sclude ", state == '+' ? "in" : "ex");
     CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, buf+8);
-    SendToProgram(buf, &first);
+    SendToBoth(buf);
     return (state == '+');
 }
 
@@ -6235,16 +6242,16 @@ ExcludeClick (int index)
        if(index < 13) { // none: include all
            WriteMap(0); // clear map
            for(i=0; i<exCnt; i++) exclusionHeader[excluTab[i].mark] = '+'; // and moves
-           SendToProgram("include all\n", &first); // and inform engine
+           SendToBoth("include all\n"); // and inform engine
        } else if(index > 18) { // tail
            if(exclusionHeader[19] == '-') { // tail was excluded
-               SendToProgram("include all\n", &first);
+               SendToBoth("include all\n");
                WriteMap(0); // clear map completely
                // now re-exclude selected moves
                for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '-')
                    ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '-');
            } else { // tail was included or in mixed state
-               SendToProgram("exclude all\n", &first);
+               SendToBoth("exclude all\n");
                WriteMap(0xFF); // fill map completely
                // now re-include selected moves
                j = 0; // count them
@@ -6855,7 +6862,6 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom
   }
 
   /* Relay move to ICS or chess engine */
-  if(second.analyzing) ToggleSecond(); // for now, we just stop second analyzing engine
   if (appData.icsActive) {
     if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
        gameMode == IcsExamining) {
@@ -6874,14 +6880,21 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom
                           gameMode == MachinePlaysBlack)) {
       SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
     }
-    if (gameMode != EditGame && gameMode != PlayFromGameFile) {
+    if (gameMode != EditGame && gameMode != PlayFromGameFile && gameMode != AnalyzeMode) {
         // [HGM] book: if program might be playing, let it use book
        bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE);
        first.maybeThinking = TRUE;
     } else if(fromY == DROP_RANK && fromX == EmptySquare) {
        if(!first.useSetboard) SendToProgram("undo\n", &first); // kludge to change stm in engines that do not support setboard
        SendBoard(&first, currentMove+1);
-    } else SendMoveToProgram(forwardMostMove-1, &first);
+       if(second.analyzing) {
+           if(!second.useSetboard) SendToProgram("undo\n", &second);
+           SendBoard(&second, currentMove+1);
+       }
+    } else {
+       SendMoveToProgram(forwardMostMove-1, &first);
+       if(second.analyzing) SendMoveToProgram(forwardMostMove-1, &second);
+    }
     if (currentMove == cmailOldMove + 1) {
       cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
     }
@@ -10190,9 +10203,10 @@ GetEngineLine (char *s, int n)
     char buf[MSG_SIZ];
     extern char *icsNames;
     if(!s || !*s) return 0;
-    NamesToList(n == 10 ? icsNames : firstChessProgramNames, command, mnemonic, "all");
+    NamesToList(n >= 10 ? icsNames : firstChessProgramNames, command, mnemonic, "all");
     for(i=1; mnemonic[i]; i++) if(!strcmp(s, mnemonic[i])) break;
     if(!mnemonic[i]) return 0;
+    if(n == 11) return 1; // just testing if there was a match
     snprintf(buf, MSG_SIZ, "-%s %s", n == 10 ? "icshost" : "fcp", command[i]);
     if(n == 1) SwapEngines(n);
     ParseArgsFromString(buf);
@@ -10995,6 +11009,11 @@ AutoPlayGameLoop ()
     }
 }
 
+void
+AnalyzeNextGame()
+{
+    ReloadGame(1); // next game
+}
 
 int
 AutoPlayOneMove ()
@@ -11016,7 +11035,14 @@ AutoPlayOneMove ()
     }
 
     if (currentMove >= forwardMostMove) {
-      if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); }
+      if(gameMode == AnalyzeFile) {
+         if(appData.loadGameIndex == -1) {
+           GameEnds(EndOfFile, NULL, GE_FILE);
+          ScheduleDelayedEvent(AnalyzeNextGame, 10);
+         } else {
+          ExitAnalyzeMode(); SendToProgram("force\n", &first);
+        }
+      }
 //      gameMode = EndOfGame;
 //      ModeHighlight();
 
@@ -11860,6 +11886,9 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
            gn = 1;
        }
        else {
+           if(gameMode == AnalyzeFile && appData.loadGameIndex == -1)
+             appData.loadGameIndex = 0; // [HGM] suppress error message if we reach file end after auto-stepping analysis
+           else
            DisplayError(_("Game number out of range"), 0);
            return FALSE;
        }
@@ -12253,7 +12282,10 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
 
     if (oldGameMode == AnalyzeFile ||
        oldGameMode == AnalyzeMode) {
+      appData.loadGameIndex = -1; // [HGM] order auto-stepping through games
+      keepInfo = 1;
       AnalyzeFileEvent();
+      keepInfo = 0;
     }
 
     if (!matchMode && pos > 0) {
@@ -13997,12 +14029,8 @@ ExitAnalyzeMode ()
         DisplayMessage("",_("Close ICS engine analyze..."));
     }
     if (first.analysisSupport && first.analyzing) {
-      SendToProgram("exit\n", &first);
-      first.analyzing = FALSE;
-    }
-    if (second.analyzing) {
-      SendToProgram("exit\n", &second);
-      second.analyzing = FALSE;
+      SendToBoth("exit\n");
+      first.analyzing = second.analyzing = FALSE;
     }
     thinkOutput[0] = NULLCHAR;
 }
@@ -14586,8 +14614,8 @@ ForwardInner (int target)
     if (gameMode == EditGame || gameMode == AnalyzeMode ||
        gameMode == Training || gameMode == PlayFromGameFile ||
        gameMode == AnalyzeFile) {
-      if(target != currentMove && second.analyzing) ToggleSecond(); // for now, we just stop second analyzing engine
        while (currentMove < target) {
+           if(second.analyzing) SendMoveToProgram(currentMove, &second);
            SendMoveToProgram(currentMove++, &first);
        }
     } else {
@@ -14691,7 +14719,6 @@ BackwardInner (int target)
     }
     if (gameMode == EditGame || gameMode==AnalyzeMode ||
        gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
-      if(target != currentMove && second.analyzing) ToggleSecond(); // for now, we just stop second analyzing engine
        while (currentMove > target) {
            if(moveList[currentMove-1][1] == '@' && moveList[currentMove-1][0] == '@') {
                // null move cannot be undone. Reload program with move history before it.
@@ -14700,10 +14727,14 @@ BackwardInner (int target)
                    if(moveList[i-1][1] == '@' && moveList[i-1][0] == '@') break;
                }
                SendBoard(&first, i); 
-               for(currentMove=i; currentMove<target; currentMove++) SendMoveToProgram(currentMove, &first);
+             if(second.analyzing) SendBoard(&second, i);
+               for(currentMove=i; currentMove<target; currentMove++) {
+                   SendMoveToProgram(currentMove, &first);
+                   if(second.analyzing) SendMoveToProgram(currentMove, &second);
+               }
                break;
            }
-           SendToProgram("undo\n", &first);
+           SendToBoth("undo\n");
            currentMove--;
        }
     } else {
@@ -15042,6 +15073,8 @@ SetGameInfo ()
     ChessMove r = GameUnfinished;
     char *p = NULL;
 
+    if(keepInfo) return;
+
     if(gameMode == EditGame) { // [HGM] vari: do not erase result on EditGame
        r = gameInfo.result;
        p = gameInfo.resultDetails;