Put recently used engines in WB menu
[xboard.git] / backend.c
index 3e91099..0a736e7 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -873,6 +873,25 @@ static char resetOptions[] =
        "-firstOptions \"\" -firstNPS -1 -fn \"\"";
 
 void
+FloatToFront(char **list, char *engineLine)
+{
+    char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
+    int i=0;
+    if(appData.recentEngines <= 0) return;
+    TidyProgramName(engineLine, "localhost", tidy+1);
+    tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
+    strncpy(buf+1, *list, MSG_SIZ-50);
+    if(p = strstr(buf, tidy)) { // tidy name appears in list
+       q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
+       while(*p++ = *++q); // squeeze out
+    }
+    strcat(tidy, buf+1); // put list behind tidy name
+    p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
+    if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
+    ASSIGN(*list, tidy+1);
+}
+
+void
 Load (ChessProgramState *cps, int i)
 {
     char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ];
@@ -880,9 +899,11 @@ Load (ChessProgramState *cps, int i)
        snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
        SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
        ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
+       appData.firstProtocolVersion = PROTOVER;
        ParseArgsFromString(buf);
        SwapEngines(i);
        ReplaceEngine(cps, i);
+       FloatToFront(&appData.recentEngineList, engineLine);
        return;
     }
     p = engineName;
@@ -925,6 +946,7 @@ Load (ChessProgramState *cps, int i)
        firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
        snprintf(firstChessProgramNames, len, "%s%s", q, buf);
        if(q)   free(q);
+       FloatToFront(&appData.recentEngineList, buf);
     }
     ReplaceEngine(cps, i);
 }
@@ -1396,9 +1418,13 @@ ReserveGame (int gameNr, char resChar)
     free(p); appData.results = q;
     if(nextGame <= appData.matchGames && resChar != ' ' && !abortMatch &&
        (gameNr < 0 || nextGame / appData.defaultMatchGames != gameNr / appData.defaultMatchGames)) {
+      int round = appData.defaultMatchGames * appData.tourneyType;
+      if(gameNr < 0 || appData.tourneyType < 1 ||  // gauntlet engine can always stay loaded as first engine
+        appData.tourneyType > 1 && nextGame/round != gameNr/round) // in multi-gauntlet change only after round
        UnloadEngine(&first);  // next game belongs to other pairing;
        UnloadEngine(&second); // already unload the engines, so TwoMachinesEvent will load new ones.
     }
+    if(appData.debugMode) fprintf(debugFP, "Reserved, next=%d, nr=%d, procs=(%x,%x)\n", nextGame, gameNr, first.pr, second.pr);
 }
 
 void
@@ -1468,6 +1494,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, appData.firstChessProgram);
     }
 
     if (appData.icsActive) {
@@ -2582,6 +2609,7 @@ int
 SeekGraphClick (ClickType click, int x, int y, int moving)
 {
     static int lastDown = 0, displayed = 0, lastSecond;
+    if(y < 0) return FALSE;
     if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
        if(click == Release || moving) return FALSE;
        nrOfSeekAds = 0;
@@ -2929,6 +2957,9 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                            OutputKibitz(suppressKibitz, parse);
                        } else {
                            char tmp[MSG_SIZ];
+                           if(gameMode == IcsObserving) // restore original ICS messages
+                             snprintf(tmp, MSG_SIZ, "%s kibitzes: %s", star_match[0], parse);
+                           else
                            snprintf(tmp, MSG_SIZ, _("your opponent kibitzes: %s"), parse);
                            SendToPlayer(tmp, strlen(tmp));
                        }
@@ -3062,7 +3093,8 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
            if (appData.autoKibitz && started == STARTED_NONE &&
                 !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze
                (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
-               if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
+               if((looking_at(buf, &i, "\n* kibitzes: ") || looking_at(buf, &i, "\n* whispers: ") ||
+                   looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
                   (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
                    StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent
                        suppressKibitz = TRUE;
@@ -3524,7 +3556,7 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
            if (looking_at(buf, &i, "% ") ||
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
                 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
-               if(ics_type == ICS_ICC && soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line
+               if(soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line
                    soughtPending = FALSE;
                    seekGraphUp = TRUE;
                    DrawSeekGraph();
@@ -3601,8 +3633,8 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                        flipView = appData.flipView;
                        DrawPosition(TRUE, boards[currentMove]);
                        DisplayBothClocks();
-                       snprintf(str, MSG_SIZ, _("%s vs. %s"),
-                               gameInfo.white, gameInfo.black);
+                       snprintf(str, MSG_SIZ, "%s %s %s",
+                               gameInfo.white, _("vs."),  gameInfo.black);
                        DisplayTitle(str);
                        gameMode = IcsIdle;
                    } else {
@@ -4019,8 +4051,8 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                            snprintf(str, MSG_SIZ, "[%s-%s] %s-%s", wh, bh,
                                    gameInfo.white, gameInfo.black);
                        } else {
-                         snprintf(str, MSG_SIZ, _("%s [%s] vs. %s [%s]"),
-                                   gameInfo.white, white_holding,
+                         snprintf(str, MSG_SIZ, "%s [%s] %s %s [%s]",
+                                   gameInfo.white, white_holding, _("vs."),
                                    gameInfo.black, black_holding);
                        }
                        if(!partnerUp) // [HGM] bughouse: when peeking at partner game we already know what he captured...
@@ -4635,7 +4667,7 @@ ParseBoard12 (char *string)
            strcat(moveList[moveNum - 1], "\n");
 
             if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
-                                 && gameInfo.variant != VariantGrand) // inherit info that ICS does not give from previous board
+               && gameInfo.variant != VariantGrand&& gameInfo.variant != VariantSChess) // inherit info that ICS does not give from previous board
               for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
                 ChessSquare old, new = boards[moveNum][k][j];
                   if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
@@ -4750,12 +4782,12 @@ ParseBoard12 (char *string)
                    basetime, increment, (int) gameInfo.variant);
        } else {
            if(gameInfo.variant == VariantNormal)
-             snprintf(str, MSG_SIZ, _("%s (%d) vs. %s (%d) {%d %d}"),
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
+             snprintf(str, MSG_SIZ, "%s (%d) %s %s (%d) {%d %d}",
+                   gameInfo.white, white_stren, _("vs."), gameInfo.black, black_stren,
                    basetime, increment);
            else
-             snprintf(str, MSG_SIZ, _("%s (%d) vs. %s (%d) {%d %d %s}"),
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
+             snprintf(str, MSG_SIZ, "%s (%d) %s %s (%d) {%d %d %s}",
+                   gameInfo.white, white_stren, _("vs."), gameInfo.black, black_stren,
                    basetime, increment, VariantName(gameInfo.variant));
        }
        DisplayTitle(str);
@@ -6625,7 +6657,7 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom
       gameMode = MachinePlaysBlack;
       StartClocks();
       SetGameInfo();
-      snprintf(buf, MSG_SIZ, _("%s vs. %s"), gameInfo.white, gameInfo.black);
+      snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
       DisplayTitle(buf);
       if (first.sendName) {
        snprintf(buf, MSG_SIZ,"name %s\n", gameInfo.white);
@@ -9752,7 +9784,6 @@ WriteTourneyFile (char *results, FILE *f)
     return f;
 }
 
-#define MAXENGINES 1000
 char *command[MAXENGINES], *mnemonic[MAXENGINES];
 
 void
@@ -9895,21 +9926,38 @@ SwapEngines (int n)
     SWAP(engOptions, p)
 }
 
-void
-SetPlayer (int player)
+int
+SetPlayer (int player, char *p)
 {   // [HGM] find the engine line of the partcipant given by number, and parse its options.
     int i;
-    char buf[MSG_SIZ], *engineName, *p = appData.participants;
+    char buf[MSG_SIZ], *engineName;
     for(i=0; i<player; i++) p = strchr(p, '\n') + 1;
     engineName = strdup(p); if(p = strchr(engineName, '\n')) *p = NULLCHAR;
     for(i=1; command[i]; i++) if(!strcmp(mnemonic[i], engineName)) break;
     if(mnemonic[i]) {
        snprintf(buf, MSG_SIZ, "-fcp %s", command[i]);
        ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
-       appData.firstHasOwnBookUCI = !appData.defNoBook;
+       appData.firstHasOwnBookUCI = !appData.defNoBook; appData.protocolVersion[0] = PROTOVER;
        ParseArgsFromString(buf);
     }
     free(engineName);
+    return i;
+}
+
+char *recentEngines;
+
+void
+RecentEngineEvent (int nr)
+{
+    int n;
+//    SwapEngines(1); // bump first to second
+//    ReplaceEngine(&second, 1); // and load it there
+    NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
+    n = SetPlayer(nr, recentEngines); // select new (using original menu order!)
+    if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
+       ReplaceEngine(&first, 0);
+       FloatToFront(&appData.recentEngineList, command[n]);
+    }
 }
 
 int
@@ -9948,6 +9996,9 @@ Pairing (int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInte
            *blackPlayer = curRound + (nPlayers-1)/2 - curPairing;
            if(*blackPlayer >= nPlayers-1+(nPlayers&1)) *blackPlayer -= nPlayers-1+(nPlayers&1);
        }
+    } else if(appData.tourneyType > 1) {
+       *blackPlayer = curPairing; // in multi-gauntlet, assign gauntlet engines to second, so first an be kept loaded during round
+       *whitePlayer = curRound + appData.tourneyType;
     } else if(appData.tourneyType > 0) {
        *whitePlayer = curPairing;
        *blackPlayer = curRound + appData.tourneyType;
@@ -10009,16 +10060,20 @@ NextTourneyGame (int nr, int *swapColors)
        matchGame = 1; roundNr = nr / syncInterval + 1;
     }
 
-    if(first.pr != NoProc || second.pr != NoProc) return 1; // engines already loaded
+    if(first.pr != NoProc && second.pr != NoProc) return 1; // engines already loaded
 
     // redefine engines, engine dir, etc.
     NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
-    SetPlayer(whitePlayer); // find white player amongst it, and parse its engine line
-    SwapEngines(1);
-    SetPlayer(blackPlayer); // find black player amongst it, and parse its engine line
-    SwapEngines(1);         // and make that valid for second engine by swapping
-    InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
-    InitEngine(&second, 1);
+    if(first.pr == NoProc || nr < 0) {
+      SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
+      InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
+    }
+    if(second.pr == NoProc) {
+      SwapEngines(1);
+      SetPlayer(blackPlayer, appData.participants); // find black player amongst it, and parse its engine line
+      SwapEngines(1);         // and make that valid for second engine by swapping
+      InitEngine(&second, 1);
+    }
     CommonEngineInit();     // after this TwoMachinesEvent will create correct engine processes
     UpdateLogos(FALSE);     // leave display to ModeHiglight()
     return 1;
@@ -11218,7 +11273,7 @@ PackGame (Board board)
     if(movePtr > dataSize) {
        if(appData.debugMode) fprintf(debugFP, "move-cache overflow, enlarge to %d MB\n", dataSize/128);
        dataSize *= 8; // increase size by factor 8 (512KB -> 4MB -> 32MB -> 256MB -> 2GB)
-       if(dataSize) newSpace = (Move*) calloc(8*dataSize + 1000, sizeof(Move));
+       if(dataSize) newSpace = (Move*) calloc(dataSize + 1000, sizeof(Move));
        if(newSpace) {
            int i;
            Move *p = moveDatabase, *q = newSpace;
@@ -11529,7 +11584,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     yynewfile(f);
 
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {
-      snprintf(buf, sizeof(buf), _("%s vs. %s"), lg->gameInfo.white,
+      snprintf(buf, sizeof(buf), "%s %s %s", lg->gameInfo.white, _("vs."),
                lg->gameInfo.black);
            DisplayTitle(buf);
     } else if (*title != NULLCHAR) {
@@ -13122,7 +13177,7 @@ MachineWhiteEvent ()
     pausing = FALSE;
     ModeHighlight();
     SetGameInfo();
-    snprintf(buf, MSG_SIZ, _("%s vs. %s"), gameInfo.white, gameInfo.black);
+    snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     DisplayTitle(buf);
     if (first.sendName) {
       snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.black);
@@ -13199,7 +13254,7 @@ MachineBlackEvent ()
     pausing = FALSE;
     ModeHighlight();
     SetGameInfo();
-    snprintf(buf, MSG_SIZ, _("%s vs. %s"), gameInfo.white, gameInfo.black);
+    snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     DisplayTitle(buf);
     if (first.sendName) {
       snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.white);
@@ -13244,24 +13299,24 @@ DisplayTwoMachinesTitle ()
     char buf[MSG_SIZ];
     if (appData.matchGames > 0) {
         if(appData.tourneyFile[0]) {
-         snprintf(buf, MSG_SIZ, _("%s vs. %s (%d/%d%s)"),
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d/%d%s)",
+                  gameInfo.white, _("vs."), gameInfo.black,
                   nextGame+1, appData.matchGames+1,
                   appData.tourneyType>0 ? "gt" : appData.tourneyType<0 ? "sw" : "rr");
         } else 
         if (first.twoMachinesColor[0] == 'w') {
-         snprintf(buf, MSG_SIZ, _("%s vs. %s (%d-%d-%d)"),
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
+                  gameInfo.white, _("vs."),  gameInfo.black,
                   first.matchWins, second.matchWins,
                   matchGame - 1 - (first.matchWins + second.matchWins));
        } else {
-         snprintf(buf, MSG_SIZ, _("%s vs. %s (%d-%d-%d)"),
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
+                  gameInfo.white, _("vs."), gameInfo.black,
                   second.matchWins, first.matchWins,
                   matchGame - 1 - (first.matchWins + second.matchWins));
        }
     } else {
-      snprintf(buf, MSG_SIZ, _("%s vs. %s"), gameInfo.white, gameInfo.black);
+      snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     }
     DisplayTitle(buf);
 }
@@ -14157,6 +14212,7 @@ ForwardInner (int target)
     if (gameMode == EditPosition)
       return;
 
+    seekGraphUp = FALSE;
     MarkTargetSquares(1);
 
     if (gameMode == PlayFromGameFile && !pausing)
@@ -14263,6 +14319,7 @@ BackwardInner (int target)
                target, currentMove, forwardMostMove);
 
     if (gameMode == EditPosition) return;
+    seekGraphUp = FALSE;
     MarkTargetSquares(1);
     if (currentMove <= backwardMostMove) {
        ClearHighlights();
@@ -15366,7 +15423,7 @@ ParseFeatures (char *args, ChessProgramState *cps)
       continue;
     }
     if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
-    if (StringFeature(&p, "myname", &cps->tidy, cps)) {
+    if (StringFeature(&p, "myname", cps->tidy, cps)) {
       if (gameMode == TwoMachinesPlay) {
        DisplayTwoMachinesTitle();
       } else {
@@ -15374,7 +15431,7 @@ ParseFeatures (char *args, ChessProgramState *cps)
       }
       continue;
     }
-    if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
+    if (StringFeature(&p, "variants", cps->variants, cps)) continue;
     if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
     if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
     if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
@@ -15398,8 +15455,8 @@ ParseFeatures (char *args, ChessProgramState *cps)
     if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;
     if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;
     if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
-    if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
-    if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {
+    if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue;
+    if (StringFeature(&p, "option", cps->option[cps->nrOptions].name, cps)) {
        if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
          snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name);
            SendToProgram(buf, cps);