X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=51f348e1da46af00d4e0cbb804dbed5551c09784;hb=4b4ee0f5a685c7cca0c5f5b791f05ee157b4a219;hp=fa467736c64596e6d128ca8a39726f08d1533472;hpb=7a5a8ea11998209db6c9a3184633b3ad4ca9b7cf;p=xboard.git diff --git a/backend.c b/backend.c index fa46773..51f348e 100644 --- a/backend.c +++ b/backend.c @@ -233,6 +233,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)); #ifdef WIN32 extern void ConsoleCreate(); @@ -417,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; @@ -796,7 +797,11 @@ InitEngine(ChessProgramState *cps, int n) /* [HGM] debug */ cps->debug = FALSE; + cps->supportsNPS = UNKNOWN; + cps->memSize = FALSE; + cps->maxCores = FALSE; + cps->egtFormats[0] = NULLCHAR; /* [HGM] options */ cps->optionSettings = appData.engOptions[n]; @@ -911,7 +916,7 @@ Load(ChessProgramState *cps, int i) useNick ? "\"" : "", v1 ? " -firstProtocolVersion 1" : "", hasBook ? "" : " -fNoOwnBookUCI", - isUCI ? " -fUCI" : "", + isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "", storeVariant ? " -variant " : "", storeVariant ? VariantName(gameInfo.variant) : ""); firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1); @@ -1014,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 @@ -1398,14 +1410,29 @@ MatchEvent(int mode) // return; // } abortMatch = FALSE; - appData.matchGames = appData.defaultMatchGames; + if(mode == 2) 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) { char buf[MSG_SIZ]; + if(strchr(appData.results, '*') == NULL) { + FILE *f; + appData.tourneyCycles++; + if(f = WriteTourneyFile(appData.results)) { // make a tourney file with increased number of cycles + fclose(f); + NextTourneyGame(-1, &dummy); + ReserveGame(-1, 0); + if(nextGame <= appData.matchGames) { + DisplayNote(_("You restarted an already completed tourney\nOne more cycle will now be added to it\nGames commence in 10 sec")); + matchMode = mode; + ScheduleDelayedEvent(NextMatchGame, 10000); + return; + } + } + } snprintf(buf, MSG_SIZ, _("All games in tourney '%s' are already played or playing"), appData.tourneyFile); DisplayError(buf, 0); appData.tourneyFile[0] = 0; @@ -5293,20 +5320,12 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, f endPV++; CopyBoard(boards[endPV], boards[endPV-1]); ApplyMove(fromX, fromY, toX, toY, promoChar, boards[endPV]); - moveList[endPV-1][0] = fromX + AAA; - moveList[endPV-1][1] = fromY + ONE; - moveList[endPV-1][2] = toX + AAA; - moveList[endPV-1][3] = toY + ONE; - moveList[endPV-1][4] = promoChar; - moveList[endPV-1][5] = NULLCHAR; + CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, moveList[endPV - 1]); strncat(moveList[endPV-1], "\n", MOVE_LEN); - if(storeComments) - CoordsToAlgebraic(boards[endPV - 1], + CoordsToAlgebraic(boards[endPV - 1], PosFlags(endPV - 1), fromY, fromX, toY, toX, promoChar, parseList[endPV - 1]); - else - parseList[endPV-1][0] = NULLCHAR; } while(valid); currentMove = (atEnd || endPV == forwardMostMove) ? endPV : forwardMostMove + 1; if(currentMove == forwardMostMove) ClearPremoveHighlights(); else @@ -5317,7 +5336,7 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, f int MultiPV(ChessProgramState *cps) -{ // check if engine supports MultiPV, and if so, return the nmber of the option that sets it +{ // check if engine supports MultiPV, and if so, return the number of the option that sets it int i; for(i=0; inrOptions; i++) if(!strcmp(cps->option[i].name, "MultiPV") && cps->option[i].type == Spin) @@ -5343,14 +5362,14 @@ LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end) buf[index] = 0; if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(&first)) >= 0) { int n = first.option[multi].value; - if(origIndex < 10) { if(n>1) n--; } else if(origIndex > index - 6) n++; + if(origIndex > 17 && origIndex < 24) { if(n>1) n--; } else if(origIndex > index - 6) n++; snprintf(buf2, MSG_SIZ, "option MultiPV=%d\n", n); if(first.option[multi].value != n) SendToProgram(buf2, &first); first.option[multi].value = n; *start = *end = 0; - return TRUE; + return FALSE; } - ParsePV(buf+startPV, FALSE, !shiftKey); + ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode); *start = startPV; *end = index-1; return TRUE; } @@ -5370,15 +5389,22 @@ UnLoadPV() int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded! if(endPV < 0) return; endPV = -1; - if(shiftKey && gameMode == AnalyzeMode) { - if(pushed) storedGames--; // abandon shelved tail of original game + if(gameMode == AnalyzeMode && currentMove > forwardMostMove) { + Boolean saveAnimate = appData.animate; + if(pushed) { + if(shiftKey && storedGames < MAX_VARIATIONS-2) { // wants to start variation, and there is space + if(storedGames == 1) GreyRevert(FALSE); // we already pushed the tail, so just make it official + } else storedGames--; // abandon shelved tail of original game + } pushed = FALSE; forwardMostMove = currentMove; currentMove = oldFMM; + appData.animate = FALSE; ToNrEvent(forwardMostMove); + appData.animate = saveAnimate; } currentMove = forwardMostMove; - if(pushed) { PopInner(0); pushed = FALSE; } // restore shelved game contnuation + if(pushed) { PopInner(0); pushed = FALSE; } // restore shelved game continuation ClearPremoveHighlights(); DrawPosition(TRUE, boards[currentMove]); } @@ -7113,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 @@ -7571,8 +7599,23 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial) if(bookHit) { // after a book hit we never send 'go', and the code after the call to this routine // has '&& !bookHit' added to suppress potential sending there (based on 'firstMove'). - char buf[MSG_SIZ]; - snprintf(buf, MSG_SIZ, "%s%s\n", (cps->useUsermove ? "usermove " : ""), bookHit); // force book move into program supposed to play it + char buf[MSG_SIZ], *move = bookHit; + if(cps->useSAN) { + int fromX, fromY, toX, toY; + char promoChar; + ChessMove moveType; + move = buf + 30; + if (ParseOneMove(bookHit, forwardMostMove, &moveType, + &fromX, &fromY, &toX, &toY, &promoChar)) { + (void) CoordsToAlgebraic(boards[forwardMostMove], + PosFlags(forwardMostMove), + fromY, fromX, toY, toX, promoChar, move); + } else { + if(appData.debugMode) fprintf(debugFP, "Book move could not be parsed\n"); + bookHit = NULL; + } + } + snprintf(buf, MSG_SIZ, "%s%s\n", (cps->useUsermove ? "usermove " : ""), move); // force book move into program supposed to play it SendToProgram(buf, cps); if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go' } else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine @@ -7596,6 +7639,8 @@ void DeferredBookMove(void) HandleMachineMove(savedMessage, savedState); } +static int savedWhitePlayer, savedBlackPlayer, pairingReceived; + void HandleMachineMove(message, cps) char *message; @@ -7610,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 @@ -9459,9 +9512,14 @@ StartChessProgram(cps) if (err != 0) { snprintf(buf, MSG_SIZ, _("Startup failure on '%s'"), cps->program); - DisplayFatalError(buf, err, 1); - cps->pr = NoProc; - cps->isr = NULL; + DisplayError(buf, err); // [HGM] bit of a rough kludge: ignore failure, (which XBoard would do anyway), and let I/O discover it + if(cps != &first) return; + appData.noChessProgram = TRUE; + ThawUI(); + SetNCPMode(); +// DisplayFatalError(buf, err, 1); +// cps->pr = NoProc; +// cps->isr = NULL; return; } @@ -9496,38 +9554,60 @@ TwoMachinesEventIfReady P((void)) } int +CountPlayers(char *p) +{ + int n = 0; + while(p = strchr(p, '\n')) p++, n++; // count participants + return n; +} + +FILE * +WriteTourneyFile(char *results) +{ // write tournament parameters on tourneyFile; on success return the stream pointer for closing + FILE *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); + fprintf(f, "-tourneyType %d\n", appData.tourneyType); + fprintf(f, "-tourneyCycles %d\n", appData.tourneyCycles); + fprintf(f, "-defaultMatchGames %d\n", appData.defaultMatchGames); + fprintf(f, "-syncAfterRound %s\n", appData.roundSync ? "true" : "false"); + fprintf(f, "-syncAfterCycle %s\n", appData.cycleSync ? "true" : "false"); + fprintf(f, "-saveGameFile \"%s\"\n", appData.saveGameFile); + fprintf(f, "-loadGameFile \"%s\"\n", appData.loadGameFile); + fprintf(f, "-loadGameIndex %d\n", appData.loadGameIndex); + fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile); + fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex); + fprintf(f, "-rewindIndex %d\n", appData.rewindIndex); + if(searchTime > 0) + fprintf(f, "-searchTime \"%s\"\n", appData.searchTime); + else { + fprintf(f, "-mps %d\n", appData.movesPerSession); + fprintf(f, "-tc %s\n", appData.timeControl); + fprintf(f, "-inc %.2f\n", appData.timeIncrement); + } + fprintf(f, "-results \"%s\"\n", results); + } + return f; +} + +int CreateTourney(char *name) { FILE *f; - if(name[0] == NULLCHAR) return 0; + if(name[0] == NULLCHAR) { + DisplayError(_("You must supply a tournament file,\nfor storing the tourney progress"), 0); + return 0; + } f = fopen(appData.tourneyFile, "r"); if(f) { // file exists ParseArgsFromFile(f); // parse it } else { - f = fopen(appData.tourneyFile, "w"); - if(f == NULL) { DisplayError("Could not write on tourney file", 0); return 0; } else { - // create a file with tournament description - fprintf(f, "-participants {%s}\n", appData.participants); - fprintf(f, "-tourneyType %d\n", appData.tourneyType); - fprintf(f, "-tourneyCycles %d\n", appData.tourneyCycles); - fprintf(f, "-defaultMatchGames %d\n", appData.defaultMatchGames); - fprintf(f, "-syncAfterRound %s\n", appData.roundSync ? "true" : "false"); - fprintf(f, "-syncAfterCycle %s\n", appData.cycleSync ? "true" : "false"); - fprintf(f, "-saveGameFile \"%s\"\n", appData.saveGameFile); - fprintf(f, "-loadGameFile \"%s\"\n", appData.loadGameFile); - fprintf(f, "-loadGameIndex %d\n", appData.loadGameIndex); - fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile); - fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex); - fprintf(f, "-rewindIndex %d\n", appData.rewindIndex); - if(searchTime > 0) - fprintf(f, "-searchTime \"%s\"\n", appData.searchTime); - else { - fprintf(f, "-mps %d\n", appData.movesPerSession); - fprintf(f, "-tc %s\n", appData.timeControl); - fprintf(f, "-inc %.2f\n", appData.timeIncrement); - } - fprintf(f, "-results \"\"\n"); + if(CountPlayers(appData.participants) < appData.tourneyType + (!appData.tourneyType) + 1) { + DisplayError(_("Not enough participants"), 0); + return 0; } + if((f = WriteTourneyFile("")) == NULL) return 0; } fclose(f); appData.noChessProgram = FALSE; @@ -9603,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; @@ -9650,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"); @@ -9658,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; @@ -12249,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); } @@ -14310,6 +14408,7 @@ ReceiveFromProgram(isr, closure, message, count, error) if (count <= 0) { if (count == 0) { RemoveInputSource(cps->isr); + if(!cps->initDone) return; // [HGM] should not generate fatal error during engine load snprintf(buf, MSG_SIZ, _("Error: %s chess program (%s) exited unexpectedly"), _(cps->which), cps->program); if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */