From 00d7f7869e045cbed5037d39e1e56224692eb766 Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Mon, 23 May 2011 19:00:00 +0200 Subject: [PATCH] Add Swiss tourneys through pairing engine An external program can be designated pairing engine through the new persistent option -pairingEngine. Defining the option will allow tourneytype -1 to be chosen for Swiss. In this case the pairing engine will be queried for a pairing before every new match game, by sending it the -results string, plus the command 'pairing' + game number. --- args.h | 1 + backend.c | 51 +++++++++++++++++++++++++++++++++++++++++++------ common.h | 1 + winboard/wsettings.c | 7 ++++- xoptions.c | 3 +- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/args.h b/args.h index bc82aa7..69c1b12 100644 --- a/args.h +++ b/args.h @@ -621,6 +621,7 @@ ArgDescriptor argDescriptors[] = { { "secondPgnName", ArgString, (void *) &appData.pgnName[1], FALSE, (ArgIniType) "" }, { "sn", ArgString, (void *) &appData.pgnName[1], FALSE, INVALID }, { "absoluteAnalysisScores", ArgBoolean, (void *) &appData.whitePOV, TRUE, FALSE }, + { "pairingEngine", ArgFilename, (void *) &appData.pairingEngine, TRUE, "" }, #if ZIPPY { "zippyTalk", ArgBoolean, (void *) &appData.zippyTalk, FALSE, (ArgIniType) ZIPPY_TALK }, diff --git a/backend.c b/backend.c index a520792..2ed15a3 100644 --- a/backend.c +++ b/backend.c @@ -418,7 +418,7 @@ char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ]; char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ]; char thinkOutput1[MSG_SIZ*10]; -ChessProgramState first, second; +ChessProgramState first, second, pairing; /* premove variables */ int premoveToX = 0; @@ -1019,6 +1019,13 @@ InitBackEnd1() InitEngine(&second, 1); CommonEngineInit(); + pairing.which = "pairing"; // pairing engine + pairing.pr = NoProc; + pairing.isr = NULL; + pairing.program = appData.pairingEngine; + pairing.host = "localhost"; + pairing.dir = "."; + if (appData.icsActive) { appData.clockMode = TRUE; /* changes dynamically in ICS mode */ } else if (appData.noChessProgram) { // [HGM] st: searchTime mode now also is clockMode @@ -1406,7 +1413,7 @@ MatchEvent(int mode) appData.matchGames = appData.defaultMatchGames; /* Set up machine vs. machine match */ nextGame = 0; - NextTourneyGame(0, &dummy); // sets appData.matchGames if this is tourney, to make sure ReserveGame knows it + NextTourneyGame(-1, &dummy); // sets appData.matchGames if this is tourney, to make sure ReserveGame knows it if(appData.tourneyFile[0]) { ReserveGame(-1, 0); if(nextGame > appData.matchGames) { @@ -7132,6 +7139,8 @@ TourneyStandings(int display) int score[MAXPLAYERS], ranking[MAXPLAYERS], points[MAXPLAYERS], games[MAXPLAYERS]; char result, *p, *names[MAXPLAYERS]; + if(appData.tourneyType < 0) return strdup("Swiss tourney finished"); // standings of Swiss yet TODO + names[0] = p = strdup(appData.participants); while(p = strchr(p, '\n')) *p++ = NULLCHAR, names[++nPlayers] = p; // count participants @@ -7630,6 +7639,8 @@ void DeferredBookMove(void) HandleMachineMove(savedMessage, savedState); } +static int savedWhitePlayer, savedBlackPlayer, pairingReceived; + void HandleMachineMove(message, cps) char *message; @@ -7644,6 +7655,14 @@ HandleMachineMove(message, cps) int machineWhite; char *bookHit; + if(cps == &pairing && sscanf(message, "%d-%d", &savedWhitePlayer, &savedBlackPlayer) == 2) { + // [HGM] pairing: Mega-hack! Pairing engine also uses this routine (so it could give other WB commands). + if(savedWhitePlayer == 0 || savedBlackPlayer == 0) return; + pairingReceived = 1; + NextMatchGame(); + return; // Skim the pairing messages here. + } + cps->userError = 0; FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit @@ -9664,7 +9683,7 @@ SetPlayer(int player) int Pairing(int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInterval) { // determine players from game number - int curCycle, curRound, curPairing, gamesPerCycle, gamesPerRound, roundsPerCycle, pairingsPerRound; + int curCycle, curRound, curPairing, gamesPerCycle, gamesPerRound, roundsPerCycle=1, pairingsPerRound=1; if(appData.tourneyType == 0) { roundsPerCycle = (nPlayers - 1) | 1; @@ -9711,7 +9730,7 @@ int NextTourneyGame(int nr, int *swapColors) { // !!!major kludge!!! fiddle appData settings to get everything in order for next tourney game char *p, *q; - int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers=0; + int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers; FILE *tf; if(appData.tourneyFile[0] == NULLCHAR) return 1; // no tourney, always allow next game tf = fopen(appData.tourneyFile, "r"); @@ -9719,9 +9738,24 @@ NextTourneyGame(int nr, int *swapColors) ParseArgsFromFile(tf); fclose(tf); InitTimeControls(); // TC might be altered from tourney file - p = appData.participants; - while(p = strchr(p, '\n')) p++, nPlayers++; // count participants - *swapColors = Pairing(nr, nPlayers, &whitePlayer, &blackPlayer, &syncInterval); + nPlayers = CountPlayers(appData.participants); // count participants + if(appData.tourneyType < 0 && appData.pairingEngine[0]) { + if(nr>=0 && !pairingReceived) { + char buf[1<<16]; + if(pairing.pr == NoProc) StartChessProgram(&pairing); + snprintf(buf, 1<<16, "results %d %s\n", nPlayers, appData.results); + SendToProgram(buf, &pairing); + snprintf(buf, 1<<16, "pairing %d\n", nr+1); + SendToProgram(buf, &pairing); + return 0; // wait for pairing engine to answer (which causes NextTourneyGame to be called again... + } + pairingReceived = 0; // ... so we continue here + syncInterval = nPlayers/2; *swapColors = 0; + appData.matchGames = appData.tourneyCycles * syncInterval - 1; + whitePlayer = savedWhitePlayer-1; blackPlayer = savedBlackPlayer-1; + matchGame = 1; roundNr = nr / syncInterval + 1; + } else + *swapColors = Pairing(nr<0 ? 0 : nr, nPlayers, &whitePlayer, &blackPlayer, &syncInterval); if(syncInterval) { p = q = appData.results; @@ -12310,6 +12344,9 @@ ExitEvent(status) RemoveInputSource(second.isr); } + if (pairing.pr != NoProc) SendToProgram("quit\n", &pairing); + if (pairing.isr != NULL) RemoveInputSource(pairing.isr); + ShutDownFrontEnd(); exit(status); } diff --git a/common.h b/common.h index 35c38fb..c52cd9f 100644 --- a/common.h +++ b/common.h @@ -631,6 +631,7 @@ typedef struct { char *egtFormats; int niceEngines; /* [HGM] nice */ char *logo[ENGINES];/* [HGM] logo */ + char *pairingEngine;/* [HGM] pairing */ Boolean autoLogo; Boolean noGUI; /* [HGM] fast: suppress all display updates */ char *engOptions[ENGINES]; /* [HGM] options */ diff --git a/winboard/wsettings.c b/winboard/wsettings.c index 159c0a1..47cc1a3 100644 --- a/winboard/wsettings.c +++ b/winboard/wsettings.c @@ -686,11 +686,12 @@ void LoadEnginePopUp(HWND hwnd) GenericPopup(hwnd, installOptions); } -Boolean autoinc, twice; +Boolean autoinc, twice, swiss; int MatchOK() { if(autoinc) appData.loadGameIndex = appData.loadPositionIndex = -(twice + 1); + if(swiss) { appData.defaultMatchGames = 1; appData.tourneyType = -1; } if(CreateTourney(appData.tourneyFile)) MatchEvent(2); else return 0; return 1; } @@ -702,6 +703,7 @@ Option tourneyOptions[] = { { 0, 1, 0, NULL, (void*) &engineChoice, (char*) (engineMnemonic+1), (engineMnemonic+1), ComboBox, N_("Select Engine:") }, { 0xD, 7, 0, NULL, (void*) &appData.participants, "", NULL, TextBox, "Tourney participants:" }, { 0, 0, 10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0=RR, 1=gauntlet):") }, + { 0, 0, 0, NULL, (void*) &swiss, "", NULL, CheckBox, N_("Use Swiss pairing engine (cycles = rounds)") }, { 0, 0, 0, NULL, (void*) &appData.cycleSync, "", NULL, CheckBox, N_("Sync after cycle") }, { 0, 1, 1000000000, NULL, (void*) &appData.tourneyCycles, "", NULL, Spin, N_("Number of tourney cycles:") }, { 0, 0, 0, NULL, (void*) &appData.roundSync, "", NULL, CheckBox, N_("Sync after round") }, @@ -737,9 +739,10 @@ void TourneyPopup(HWND hwnd) NamesToList(firstChessProgramNames, engineList, engineMnemonic); comboCallback = &AddToTourney; autoinc = appData.loadGameIndex < 0 || appData.loadPositionIndex < 0; - twice = TRUE; + twice = TRUE; swiss = appData.tourneyType < 0; while(engineList[n]) n++; tourneyOptions[3].max = n-1; snprintf(title, MSG_SIZ, _("Tournament and Match Options")); + if(appData.pairingEngine[0]) tourneyOptions[5].min = -19; GenericPopup(hwnd, tourneyOptions); } diff --git a/xoptions.c b/xoptions.c index 2acc5e2..5cadaf9 100644 --- a/xoptions.c +++ b/xoptions.c @@ -816,7 +816,7 @@ Option matchOptions[] = { { 0xD, 150, 0, NULL, (void*) &engineName, "", NULL, TextBox, "Tourney participants:" }, { 0, 1, 0, NULL, (void*) &engineChoice, (char*) (engineMnemonic+1), (engineMnemonic+1), ComboBox, N_("Select Engine:") }, { 0, 0, 10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0 = round-robin, 1 = gauntlet):") }, -{ 0, 1, 1000000000, NULL, (void*) &appData.tourneyCycles, "", NULL, Spin, N_("Number of tourney cycles:") }, +{ 0, 1, 1000000000, NULL, (void*) &appData.tourneyCycles, "", NULL, Spin, N_("Number of tourney cycles (or Swiss rounds):") }, { 0, 1, 1000000000, NULL, (void*) &appData.defaultMatchGames, "", NULL, Spin, N_("Default Number of Games in Match (or Pairing):") }, { 0, 0, 1000000000, NULL, (void*) &appData.matchPause, "", NULL, Spin, N_("Pause between Match Games (msec):") }, { 0, 0, 0, NULL, (void*) &appData.saveGameFile, ".pgn", NULL, FileName, N_("Save Tourney Games on:") }, @@ -1770,6 +1770,7 @@ void MatchOptionsProc(w, event, prms, nprms) { NamesToList(firstChessProgramNames, engineList, engineMnemonic); comboCallback = &AddToTourney; + matchOptions[5].min = -(appData.pairingEngine[0] != NULLCHAR); // with pairing engine, allow Swiss GenericPopUp(matchOptions, _("Match Options"), 0); } -- 1.7.0.4