X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c19666281f2aa2d11b7147c7d26119c1060a1d59;hb=d3c211c5c2ebfb2e77a6dc37c973ce7d69f301fd;hp=f2aae99a2a7b30be464699f86f3d5a110b62c0af;hpb=278e742af91281ce50be123980253bc35b1143a1;p=xboard.git diff --git a/backend.c b/backend.c index f2aae99..c196662 100644 --- a/backend.c +++ b/backend.c @@ -393,6 +393,7 @@ PosFlags (index) } FILE *gameFileFP, *debugFP; +char *currentDebugFile; // [HGM] debug split: to remember name /* [AS] Note: sometimes, the sscanf() function is used to parse the input @@ -873,6 +874,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 +900,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 +947,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); } @@ -1303,6 +1326,7 @@ InitBackEnd2 () if (appData.debugMode) { fprintf(debugFP, "%s\n", programVersion); } + ASSIGN(currentDebugFile, appData.nameOfDebugFile); // [HGM] debug split: remember initial name in use set_cont_sequence(appData.wrapContSeq); if (appData.matchGames > 0) { @@ -1396,9 +1420,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 +1496,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 +2611,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 +2959,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 +3095,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; @@ -9752,7 +9786,6 @@ WriteTourneyFile (char *results, FILE *f) return f; } -#define MAXENGINES 1000 char *command[MAXENGINES], *mnemonic[MAXENGINES]; void @@ -9777,7 +9810,7 @@ Substitute (char *participants, int expunge) q = r; while(*q) nPlayers += (*q++ == '\n'); p = buf; while(*r && (*p = *r++) != '\n') p++; *p = NULLCHAR; - NamesToList(firstChessProgramNames, command, mnemonic); + NamesToList(firstChessProgramNames, command, mnemonic, "all"); for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break; if(mnemonic[i]) { // The substitute is valid FILE *f; @@ -9846,19 +9879,28 @@ CreateTourney (char *name) return 1; } -void -NamesToList (char *names, char **engineList, char **engineMnemonic) +int +NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) { char buf[MSG_SIZ], *p, *q; - int i=1; - while(*names) { - p = names; q = buf; + int i=1, header, skip, all = !strcmp(group, "all"), depth = 0; + skip = !all && group[0]; // if group requested, we start in skip mode + for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) { + p = names; q = buf; header = 0; while(*p && *p != '\n') *q++ = *p++; *q = 0; + if(*p == '\n') p++; + if(buf[0] == '#') { + if(strstr(buf, "# end") == buf) { depth--; continue; } // leave group, and suppress printing label + depth++; // we must be entering a new group + if(all) continue; // suppress printing group headers when complete list requested + header = 1; + if(skip && !strcmp(group, buf)) { depth = 0; skip = FALSE; } // start when we reach requested group + } + if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header) if(engineList[i]) free(engineList[i]); engineList[i] = strdup(buf); - if(*p == '\n') p++; - TidyProgramName(engineList[i], "localhost", buf); + if(buf[0] != '#') TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied if(engineMnemonic[i]) free(engineMnemonic[i]); if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) { strcat(buf, " ("); @@ -9866,10 +9908,10 @@ NamesToList (char *names, char **engineList, char **engineMnemonic) strcat(buf, ")"); } engineMnemonic[i] = strdup(buf); - names = p; i++; - if(i > MAXENGINES - 2) break; + i++; } engineList[i] = engineMnemonic[i] = NULL; + return i; } // following implemented as macro to avoid type limitations @@ -9895,21 +9937,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= 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 +10071,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); + NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines + 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; @@ -12264,6 +12330,8 @@ SaveGamePGN (FILE *f) PrintPGNTags(f, &gameInfo); + if(appData.numberTag && matchMode) fprintf(f, "[Number \"%d\"]\n", nextGame+1); // [HGM] number tag + if (backwardMostMove > 0 || startedFromSetupPosition) { char *fen = PositionToFEN(backwardMostMove, NULL); fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen); @@ -13340,6 +13408,17 @@ TwoMachinesEvent P((void)) break; } + if(matchMode && appData.debugMode) { // [HGM] debug split: game is part of a match; we might have to create a debug file just for this game + snprintf(buf, MSG_SIZ, appData.nameOfDebugFile, nextGame+1); // expand name of debug file with %d in it + if(strcmp(buf, currentDebugFile)) { // name has changed + FILE *f = fopen(buf, "w"); + if(f) { // if opening the new file failed, just keep using the old one + ASSIGN(currentDebugFile, buf); + fclose(debugFP); + debugFP = f; + } + } + } // forwardMostMove = currentMove; TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this... @@ -13365,6 +13444,7 @@ TwoMachinesEvent P((void)) ScheduleDelayedEvent(TwoMachinesEventIfReady, appData.matchPause - wait); return; } + // we are now committed to starting the game stalling = 0; DisplayMessage("", ""); if (startedFromSetupPosition) { @@ -14157,6 +14237,7 @@ ForwardInner (int target) if (gameMode == EditPosition) return; + seekGraphUp = FALSE; MarkTargetSquares(1); if (gameMode == PlayFromGameFile && !pausing) @@ -14263,6 +14344,7 @@ BackwardInner (int target) target, currentMove, forwardMostMove); if (gameMode == EditPosition) return; + seekGraphUp = FALSE; MarkTargetSquares(1); if (currentMove <= backwardMostMove) { ClearHighlights(); @@ -14591,7 +14673,7 @@ PrintOpponents (FILE *fp) void TidyProgramName (char *prog, char *host, char buf[MSG_SIZ]) { - char *p, *q; + char *p, *q, c; int local = (strcmp(host, "localhost") == 0); while (!local && (p = strchr(prog, ';')) != NULL) { p++; @@ -14608,7 +14690,8 @@ TidyProgramName (char *prog, char *host, char buf[MSG_SIZ]) while (p >= prog && *p != '/' && *p != '\\') p--; p++; if(p == prog && *p == '"') p++; - if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4; + c = *q; *q = 0; + if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) *q = c, q -= 4; else *q = c; memcpy(buf, p, q - p); buf[q - p] = NULLCHAR; if (!local) { @@ -15552,6 +15635,14 @@ TypeInDoneEvent (char *move) ToNrEvent(2*n-1); return; } + // undocumented kludge: allow command-line option to be typed in! + // (potentially fatal, and does not implement the effect of the option.) + // should only be used for options that are values on which future decisions will be made, + // and definitely not on options that would be used during initialization. + if(strstr(move, "!!! -") == move) { + ParseArgsFromString(move+4); + return; + } if (gameMode != EditGame && currentMove != forwardMostMove && gameMode != Training) {