From: H.G. Muller Date: Wed, 20 Apr 2011 12:35:50 +0000 (+0200) Subject: Make engine loadable during session X-Git-Url: http://winboard.nl/cgi-bin?p=xboard.git;a=commitdiff_plain;h=b9641a5478e4828554c5a82cb11681281491db4c Make engine loadable during session A "Load Engine ..." menu item is added to the Engine menu, which pops up a dialog where one can replace the first or second engine by a new one, without destroying the game state. Switching from -ncp to engine mode needed activation of some menu items in SetGNUMode, and re-enabling clockMode. It was also important that noChessProgram is cleared before re-initializing the ChessProgramState. The Load-Engine dialog can select from the list of installed engines, and also add a newly specified engine to this list. In this case it saves the directory, hasBook, and isUCI info with the engine, and optionally the current variant. When loading an engine with a specified variant, it resets the game and switches to the new variant. --- diff --git a/args.h b/args.h index 0e596f5..1b43c3e 100644 --- a/args.h +++ b/args.h @@ -1043,6 +1043,12 @@ ParseArgs(GetFunc get, void *cl) } void +ParseArgsFromString(char *p) +{ + ParseArgs(StringGet, &p); +} + +void ParseIcsTextMenu(char *icsTextMenuString) { // int flags = 0; diff --git a/backend.c b/backend.c index bcdef50..7b5d2fc 100644 --- a/backend.c +++ b/backend.c @@ -811,6 +811,40 @@ InitEngine(ChessProgramState *cps, int n) InitEngineUCI( installDir, cps ); // [HGM] moved here from winboard.c, to make available in xboard } +ChessProgramState *savCps; + +void +LoadEngine() +{ + int i; + if(WaitForEngine(savCps, LoadEngine)) return; + CommonEngineInit(); // recalculate time odds + if(gameInfo.variant != StringToVariant(appData.variant)) { + // we changed variant when loading the engine; this forces us to reset + Reset(TRUE, savCps != &first); + EditGameEvent(); // for consistency with other path, as Reset changes mode + } + InitChessProgram(savCps, FALSE); + SendToProgram("force\n", savCps); + DisplayMessage("", ""); + if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove); + for (i = backwardMostMove; i < forwardMostMove; i++) SendMoveToProgram(i, savCps); + ThawUI(); + SetGNUMode(); +} + +void +ReplaceEngine(ChessProgramState *cps, int n) +{ + EditGameEvent(); + UnloadEngine(cps); + appData.noChessProgram = False; + appData.clockMode = True; + InitEngine(cps, n); + savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-( + LoadEngine(); +} + void InitBackEnd1() { @@ -14133,6 +14167,7 @@ FeatureDone(cps, val) DelayedEventCallback cb = GetDelayedEvent(); if ((cb == InitBackEnd3 && cps == &first) || (cb == SettingsMenuIfReady && cps == &second) || + (cb == LoadEngine) || (cb == TwoMachinesEventIfReady && cps == &second)) { CancelDelayedEvent(); ScheduleDelayedEvent(cb, val ? 1 : 3600000); diff --git a/frontend.h b/frontend.h index 1eba120..69e431e 100644 --- a/frontend.h +++ b/frontend.h @@ -83,6 +83,7 @@ void DisplayNote P((String message)); void AskQuestion P((String title, String question, String replyPrefix, ProcRef pr)); void DisplayIcsInteractionTitle P((String title)); +void ParseArgsFromString P((char *p)); void DrawPosition P((int fullRedraw, Board board)); void ResetFrontEnd P((void)); void NotifyFrontendLogin P((void)); diff --git a/xboard.c b/xboard.c index c721e52..1ec1cd2 100644 --- a/xboard.c +++ b/xboard.c @@ -454,6 +454,7 @@ void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)) void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); +void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms)); @@ -699,6 +700,8 @@ MenuItem actionMenu[] = { }; MenuItem engineMenu[] = { + {N_("Load New Engine ..."), "Load Engine", LoadEngineProc}, + {"----", NULL, NothingProc}, {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc}, {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc}, {"----", NULL, NothingProc}, @@ -2793,6 +2796,7 @@ Enables icsEnables[] = { #endif { "menuEngine.Engine #1 Settings", False }, { "menuEngine.Engine #2 Settings", False }, + { "menuEngine.Load Engine", False }, { "menuEdit.Annotate", False }, { NULL, False } }; @@ -2852,6 +2856,20 @@ Enables gnuEnables[] = { { "menuFile.Mail Move", False }, { "menuFile.Reload CMail Message", False }, + // [HGM] The following have been added to make a switch from ncp to GNU mode possible + { "menuMode.Machine White", True }, + { "menuMode.Machine Black", True }, + { "menuMode.Analysis Mode", True }, + { "menuMode.Analyze File", True }, + { "menuMode.Two Machines", True }, + { "menuMode.Machine Match", True }, + { "menuEngine.Engine #1 Settings", True }, + { "menuEngine.Engine #2 Settings", True }, + { "menuEngine.Hint", True }, + { "menuEngine.Book", True }, + { "menuEngine.Move Now", True }, + { "menuEngine.Retract Move", True }, + { "Action", True }, { NULL, False } }; diff --git a/xoptions.c b/xoptions.c index 688e185..14ab5d7 100644 --- a/xoptions.c +++ b/xoptions.c @@ -2006,6 +2006,132 @@ void SecondSettingsProc(w, event, prms, nprms) SettingsPopUp(&second); } +char *engineName, *engineDir, *engineChoice, *engineLine; +Boolean isUCI, hasBook, storeVariant, v1, addToList; +extern Option installOptions[]; +extern char *firstChessProgramNames; +char *engineNr[] = { N_("First Engine"), N_("Second Engine"), NULL }; +char *engineList[100] = {" "}, *engineMnemonic[100] = {""}; + +void NamesToList(char *names) +{ + char buf[MSG_SIZ], *p, *q; + int i=1; + while(*names) { + p = names; q = buf; + while(*p && *p != '\n') *q++ = *p++; + *q = 0; + if(engineList[i]) free(engineList[i]); + engineList[i] = strdup(buf); + if(*p == '\n') p++; + TidyProgramName(engineList[i], "localhost", buf); + if(engineMnemonic[i]) free(engineMnemonic[i]); + if(q = strstr(engineList[i], " -variant ")) { + strcat(buf, "("); + sscanf(q + 10, "%s", buf + strlen(buf)); + strcat(buf, ")"); + } + engineMnemonic[i] = strdup(buf); + names = p; i++; + } + engineList[i] = NULL; +} + +// following implemented as macro to avoid type limitations +#define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp; + +void SwapEngines(int n) +{ // swap settings for first engine and other engine (so far only some selected options) + int h; + char *p; + if(n == 0) return; + SWAP(directory, p) + SWAP(chessProgram, p) + SWAP(isUCI, h) + SWAP(hasOwnBookUCI, h) + SWAP(protocolVersion, h) + SWAP(reuse, h) + SWAP(scoreIsAbsolute, h) + SWAP(timeOdds, h) +} + +void Load(ChessProgramState *cps, int i) +{ + char *p, *q, buf[MSG_SIZ]; + if(engineLine[0]) { // an engine was selected from the combo box + snprintf(buf, MSG_SIZ, "-fcp %s", engineLine); + SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second* + ParseArgsFromString(buf); + SwapEngines(i); + ReplaceEngine(cps, i); + return; + } + p = engineName; + while(q = strchr(p, '/')) p = q+1; + if(*p== NULLCHAR) return; + appData.chessProgram[i] = strdup(p); + if(engineDir[0] != NULLCHAR) + appData.directory[i] = engineDir; + else if(p != engineName) { // derive directory from engine path, when not given + p[-1] = 0; + appData.directory[i] = strdup(engineName); + p[-1] = '/'; + } else appData.directory[i] = "."; + appData.isUCI[i] = isUCI; + appData.protocolVersion[i] = v1 ? 1 : PROTOVER; + appData.hasOwnBookUCI[i] = hasBook; + if(addToList) { + int len; + q = firstChessProgramNames; + snprintf(buf, MSG_SIZ, "\"%s\" -fd \"%s\"%s%s%s%s%s\n", p, appData.directory[i], + v1 ? " -firstProtocolVersion 1" : "", + hasBook ? "" : " -fNoOwnBookUCI", + isUCI ? " -fUCI" : "", + storeVariant ? " -variant " : "", + storeVariant ? VariantName(gameInfo.variant) : ""); +printf("new line: %s", buf); + firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1); + snprintf(firstChessProgramNames, len, "%s%s", q, buf); + if(q) free(q); + } + ReplaceEngine(cps, i); +} + +void InstallOK(int n) +{ + PopDown(0); // early popdown, to allow FreezeUI to instate grab + if(engineChoice[0] == engineNr[0][0]) Load(&first, 0); else Load(&second, 1); +} + +Option installOptions[] = { +{ 0, 0, 0, NULL, (void*) &engineLine, (char*) engineMnemonic, engineList, ComboBox, N_("Select engine from list:") }, +{ 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("or specify one below:") }, +{ 0, 0, 0, NULL, (void*) &engineDir, NULL, NULL, PathName, N_("Engine Directory:") }, +{ 0, 0, 0, NULL, (void*) &engineName, NULL, NULL, FileName, N_("Engine Command:") }, +{ 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("(Directory will be derived from engine path when empty)") }, +{ 0, 0, 0, NULL, (void*) &isUCI, NULL, NULL, CheckBox, N_("UCI") }, +{ 0, 0, 0, NULL, (void*) &v1, NULL, NULL, CheckBox, N_("WB protocol v1 (do not wait for engine features)") }, +{ 0, 0, 0, NULL, (void*) &hasBook, NULL, NULL, CheckBox, N_("Must not use GUI book") }, +{ 0, 0, 0, NULL, (void*) &addToList, NULL, NULL, CheckBox, N_("Add this engine to the list") }, +{ 0, 0, 0, NULL, (void*) &storeVariant, NULL, NULL, CheckBox, N_("Force current variant with this engine") }, +{ 0, 0, 0, NULL, (void*) &engineChoice, (char*) engineNr, engineNr, ComboBox, N_("Load mentioned engine as") }, +{ 0, 1, 0, NULL, (void*) &InstallOK, "", NULL, EndMark , "" } +}; + +void LoadEngineProc(w, event, prms, nprms) + Widget w; + XEvent *event; + String *prms; + Cardinal *nprms; +{ + isUCI = addToList = storeVariant = v1 = False; hasBook = True; // defaults + engineDir = ""; + if(engineChoice) free(engineChoice); engineChoice = strdup(engineNr[0]); + if(engineLine) free(engineLine); engineLine = strdup(""); + NamesToList(firstChessProgramNames); + GenericPopUp(installOptions, _("Load engine"), 0); +} + //---------------------------- Chat Windows ---------------------------------------------- void OutputChatMessage(int partner, char *mess)