Add Swiss tourneys through pairing engine
authorH.G. Muller <h.g.muller@hccnet.nl>
Mon, 23 May 2011 17:00:00 +0000 (19:00 +0200)
committerH.G. Muller <h.g.muller@hccnet.nl>
Mon, 23 May 2011 17:00:00 +0000 (19:00 +0200)
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
backend.c
common.h
winboard/wsettings.c
xoptions.c

diff --git a/args.h b/args.h
index bc82aa7..69c1b12 100644 (file)
--- 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 },
index a520792..2ed15a3 100644 (file)
--- 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);
 }
index 35c38fb..c52cd9f 100644 (file)
--- 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   */
index 159c0a1..47cc1a3 100644 (file)
@@ -686,11 +686,12 @@ void LoadEnginePopUp(HWND hwnd)
     GenericPopup(hwnd, installOptions);\r
 }\r
 \r
-Boolean autoinc, twice;\r
+Boolean autoinc, twice, swiss;\r
 \r
 int MatchOK()\r
 {\r
     if(autoinc) appData.loadGameIndex = appData.loadPositionIndex = -(twice + 1);\r
+    if(swiss) { appData.defaultMatchGames = 1; appData.tourneyType = -1; }\r
     if(CreateTourney(appData.tourneyFile)) MatchEvent(2); else return 0;\r
     return 1;\r
 }\r
@@ -702,6 +703,7 @@ Option tourneyOptions[] = {
   { 0,  1,          0, NULL, (void*) &engineChoice, (char*) (engineMnemonic+1), (engineMnemonic+1), ComboBox, N_("Select Engine:") },\r
   { 0xD, 7,         0, NULL, (void*) &appData.participants, "", NULL, TextBox, "Tourney participants:" },\r
   { 0,  0,         10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0=RR, 1=gauntlet):") },\r
+  { 0,  0,          0, NULL, (void*) &swiss, "", NULL, CheckBox, N_("Use Swiss pairing engine (cycles = rounds)") },\r
   { 0,  0,          0, NULL, (void*) &appData.cycleSync, "", NULL, CheckBox, N_("Sync after cycle") },\r
   { 0,  1, 1000000000, NULL, (void*) &appData.tourneyCycles, "", NULL, Spin, N_("Number of tourney cycles:") },\r
   { 0,  0,          0, NULL, (void*) &appData.roundSync, "", NULL, CheckBox, N_("Sync after round") },\r
@@ -737,9 +739,10 @@ void TourneyPopup(HWND hwnd)
     NamesToList(firstChessProgramNames, engineList, engineMnemonic);\r
     comboCallback = &AddToTourney;\r
     autoinc = appData.loadGameIndex < 0 || appData.loadPositionIndex < 0;\r
-    twice = TRUE;\r
+    twice = TRUE; swiss = appData.tourneyType < 0;\r
     while(engineList[n]) n++; tourneyOptions[3].max = n-1;\r
     snprintf(title, MSG_SIZ, _("Tournament and Match Options"));\r
+    if(appData.pairingEngine[0]) tourneyOptions[5].min = -19;\r
 \r
     GenericPopup(hwnd, tourneyOptions);\r
 }\r
index 2acc5e2..5cadaf9 100644 (file)
@@ -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);
 }