From 01bccb194cfca30703d9b49b05a6f2837d67f0f0 Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Wed, 10 Aug 2011 11:27:22 +0200 Subject: [PATCH] Allow substitution of engines during tournament Two buttons are added in the tournament options dialog, for upgrading and for replacing a tourney participant. Lots of tests on the validity of the request are done, and if all are passed, the tourney file is written with the new participants (and in case of replace) with the results of the replaced engines erased from the -results string. --- backend.c | 78 ++++++++++++++++++++++++++++++++++++++++++++------ backend.h | 1 + winboard/wsettings.c | 34 ++++++++++++++++++++- xoptions.c | 22 +++++++++++-- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/backend.c b/backend.c index b313fa5..e4a9031 100644 --- a/backend.c +++ b/backend.c @@ -234,7 +234,7 @@ void InitDrawingSizes(int x, int y); void NextMatchGame P((void)); int NextTourneyGame P((int nr, int *swap)); int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync)); -FILE *WriteTourneyFile P((char *results)); +FILE *WriteTourneyFile P((char *results, FILE *f)); void DisplayTwoMachinesTitle P(()); #ifdef WIN32 @@ -1436,7 +1436,7 @@ MatchEvent(int mode) if(strchr(appData.results, '*') == NULL) { FILE *f; appData.tourneyCycles++; - if(f = WriteTourneyFile(appData.results)) { // make a tourney file with increased number of cycles + if(f = WriteTourneyFile(appData.results, NULL)) { // make a tourney file with increased number of cycles fclose(f); NextTourneyGame(-1, &dummy); ReserveGame(-1, 0); @@ -9720,9 +9720,9 @@ CountPlayers(char *p) } FILE * -WriteTourneyFile(char *results) +WriteTourneyFile(char *results, FILE *f) { // write tournament parameters on tourneyFile; on success return the stream pointer for closing - FILE *f = fopen(appData.tourneyFile, "w"); + if(f == NULL) f = fopen(appData.tourneyFile, "w"); if(f == NULL) DisplayError(_("Could not write on tourney file"), 0); else { // create a file with tournament description fprintf(f, "-participants {%s}\n", appData.participants); @@ -9749,10 +9749,73 @@ WriteTourneyFile(char *results) return f; } +#define MAXENGINES 1000 +char *command[MAXENGINES], *mnemonic[MAXENGINES]; + +void Substitute(char *participants, int expunge) +{ + int i, changed, changes=0, nPlayers=0; + char *p, *q, *r, buf[MSG_SIZ]; + if(participants == NULL) return; + if(appData.tourneyFile[0] == NULLCHAR) { free(participants); return; } + r = p = participants; q = appData.participants; + while(*p && *p == *q) { + if(*p == '\n') r = p+1, nPlayers++; + p++; q++; + } + if(*p) { // difference + while(*p && *p++ != '\n'); + while(*q && *q++ != '\n'); + changed = nPlayers; + changes = 1 + (strcmp(p, q) != 0); + } + if(changes == 1) { // a single engine mnemonic was changed + q = r; while(*q) nPlayers += (*q++ == '\n'); + p = buf; while(*r && (*p = *r++) != '\n') p++; + *p = NULLCHAR; + NamesToList(firstChessProgramNames, command, mnemonic); + for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break; + if(mnemonic[i]) { // The substitute is valid + FILE *f; + if(appData.tourneyFile[0] && (f = fopen(appData.tourneyFile, "r+")) ) { + flock(fileno(f), LOCK_EX); + ParseArgsFromFile(f); + fseek(f, 0, SEEK_SET); + FREE(appData.participants); appData.participants = participants; + if(expunge) { // erase results of replaced engine + int len = strlen(appData.results), w, b, dummy; + for(i=0; i 1) DisplayError(_("You can only change one engine at the time"), 0); + free(participants); + return; +} + int CreateTourney(char *name) { FILE *f; + if(matchMode && strcmp(name, appData.tourneyFile)) { + ASSIGN(name, appData.tourneyFile); //do not allow change of tourneyfile while playing + } if(name[0] == NULLCHAR) { if(appData.participants[0]) DisplayError(_("You must supply a tournament file,\nfor storing the tourney progress"), 0); @@ -9770,7 +9833,7 @@ CreateTourney(char *name) } ASSIGN(appData.tourneyFile, name); if(appData.tourneyType < 0) appData.defaultMatchGames = 1; // Swiss forces games/pairing = 1 - if((f = WriteTourneyFile("")) == NULL) return 0; + if((f = WriteTourneyFile("", NULL)) == NULL) return 0; } fclose(f); appData.noChessProgram = FALSE; @@ -9779,9 +9842,6 @@ CreateTourney(char *name) return 1; } -#define MAXENGINES 1000 -char *command[MAXENGINES], *mnemonic[MAXENGINES]; - void NamesToList(char *names, char **engineList, char **engineMnemonic) { char buf[MSG_SIZ], *p, *q; @@ -9804,7 +9864,7 @@ void NamesToList(char *names, char **engineList, char **engineMnemonic) names = p; i++; if(i > MAXENGINES - 2) break; } - engineList[i] = NULL; + engineList[i] = engineMnemonic[i] = NULL; } // following implemented as macro to avoid type limitations diff --git a/backend.h b/backend.h index 4055ba4..b1f7500 100644 --- a/backend.h +++ b/backend.h @@ -300,6 +300,7 @@ void NamesToList P((char *name, char **engines, char **mnemonics)); int CreateTourney P((char *name)); char *MakeName P((char *templ)); void SwapEngines P((int n)); +void Substitute P((char *participants, int expunge)); extern char* StripHighlight P((char *)); /* returns static data */ extern char* StripHighlightAndTitle P((char *)); /* returns static data */ diff --git a/winboard/wsettings.c b/winboard/wsettings.c index 11de72b..bfad588 100644 --- a/winboard/wsettings.c +++ b/winboard/wsettings.c @@ -486,6 +486,7 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa if( activeList[j].type == SaveButton) GetOptionValues(hDlg, activeCps, activeList); else if( activeList[j].type != Button) break; + else if( !activeCps ) { (*(ButtonCallback*) activeList[j].target)(hDlg); break; } snprintf(buf, MSG_SIZ, "option %s\n", activeList[j].name); SendToProgram(buf, activeCps); } @@ -694,8 +695,35 @@ int MatchOK() { if(autoinc) appData.loadGameIndex = appData.loadPositionIndex = -(twice + 1); if(swiss) { appData.defaultMatchGames = 1; appData.tourneyType = -1; } - if(CreateTourney(tfName)) MatchEvent(2); else return !appData.participants[0]; - return 1; + if(CreateTourney(tfName) && !matchMode) { // CreateTourney reloads original settings if file already existed + MatchEvent(2); + return 1; // close dialog + } + return matchMode || !appData.participants[0]; // if we failed to create and are not in playing, forbid popdown if there are participants +} + +char *GetParticipants(HWND hDlg) +{ + int len = GetWindowTextLength(GetDlgItem(hDlg, 2001+2*9)) + 1; + char *participants,*p, *q; + if(len < 4) return NULL; // box is empty (enough) + participants = (char*) malloc(len); + GetDlgItemText(hDlg, 2001+2*9, participants, len ); + p = q = participants; + while(*p++ = *q++) if(p[-1] == '\r') p--; + return participants; +} + +void ReplaceParticipant(HWND hDlg) +{ + char *participants = GetParticipants(hDlg); + Substitute(participants, TRUE); +} + +void UpgradeParticipant(HWND hDlg) +{ + char *participants = GetParticipants(hDlg); + Substitute(participants, FALSE); } Option tourneyOptions[] = { @@ -719,6 +747,8 @@ Option tourneyOptions[] = { { 0, 0, 1000000000, NULL, (void*) &appData.rewindIndex, "", NULL, Spin, N_("Rewind after (0 = never):") }, { 0, 0, 0, NULL, (void*) &twice, "", NULL, CheckBox, N_("Use each line/position twice") }, { 0, 0, 1000000000, NULL, (void*) &appData.matchPause, "", NULL, Spin, N_("Pause between Games (ms):") }, + { 0, 0, 0, NULL, (void*) &ReplaceParticipant, "", NULL, Button, N_("Replace Engine") }, + { 0, 0, 0, NULL, (void*) &UpgradeParticipant, "", NULL, Button, N_("Upgrade Engine") }, { 0, 0, 0, NULL, (void*) &MatchOK, "", NULL, EndMark , "" } }; diff --git a/xoptions.c b/xoptions.c index 31750ba..d6b5b92 100644 --- a/xoptions.c +++ b/xoptions.c @@ -329,14 +329,25 @@ void AddToTourney(int n) int MatchOK(int n) { - if(appData.participants && appData.participants[0]) free(appData.participants); - appData.participants = strdup(engineName); - if(!CreateTourney(tfName)) return !appData.participants[0]; + ASSIGN(appData.participants, engineName); + if(!CreateTourney(tfName) || matchMode) return matchMode || !appData.participants[0]; PopDown(0); // early popdown to prevent FreezeUI called through MatchEvent from causing XtGrab warning MatchEvent(2); // start tourney return 1; } +void ReplaceParticipant() +{ + GenericReadout(3); + Substitute(strdup(engineName), True); +} + +void UpgradeParticipant() +{ + GenericReadout(3); + Substitute(strdup(engineName), False); +} + Option matchOptions[] = { { 0, 0, 0, NULL, (void*) &tfName, ".trn", NULL, FileName, N_("Tournament file:") }, { 0, 0, 0, NULL, (void*) &appData.roundSync, "", NULL, CheckBox, N_("Sync after round (for concurrent playing of a single") }, @@ -353,7 +364,9 @@ Option matchOptions[] = { { 0, 0, 0, NULL, (void*) &appData.loadPositionFile, ".fen", NULL, FileName, N_("File with Start Positions:") }, { 0, -2, 1000000000, NULL, (void*) &appData.loadPositionIndex, "", NULL, Spin, N_("Position Number (-1 or -2 = Auto-Increment):") }, { 0, 0, 1000000000, NULL, (void*) &appData.rewindIndex, "", NULL, Spin, N_("Rewind Index after this many Games (0 = never):") }, -{ 0, 0, 0, NULL, (void*) &MatchOK, "", NULL, EndMark , "" } +{ 0, 0, 0, NULL, (void*) &ReplaceParticipant, NULL, NULL, Button, N_("Replace Engine") }, +{ 0, 1, 0, NULL, (void*) &UpgradeParticipant, NULL, NULL, Button, N_("Upgrade Engine") }, +{ 0, 1, 0, NULL, (void*) &MatchOK, "", NULL, EndMark , "" } }; int GeneralOptionsOK(int n) @@ -1315,6 +1328,7 @@ void MatchOptionsProc(w, event, prms, nprms) comboCallback = &AddToTourney; matchOptions[5].min = -(appData.pairingEngine[0] != NULLCHAR); // with pairing engine, allow Swiss ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName)); + ASSIGN(engineName, appData.participants); GenericPopUp(matchOptions, _("Match Options"), 0); } -- 1.7.0.4