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));
+void DisplayTwoMachinesTitle P(());
#ifdef WIN32
extern void ConsoleCreate();
int initialRulePlies, FENrulePlies;
FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
int loadFlag = 0;
-int shuffleOpenings;
+Boolean shuffleOpenings;
int mute; // mute all sounds
// [HGM] vari: next 12 to save and restore variations
appData.noChessProgram = FALSE;
appData.clockMode = TRUE;
InitEngine(cps, n);
+ UpdateLogos(TRUE);
if(n) return; // only startup first engine immediately; second can wait
savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-(
LoadEngine();
void
Load(ChessProgramState *cps, int i)
{
- char *p, *q, buf[MSG_SIZ], command[MSG_SIZ];
+ char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ];
if(engineLine[0]) { // an engine was selected from the combo box
snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
appData.directory[i] = strdup(engineName);
p[-1] = SLASH;
} else appData.directory[i] = ".";
+ if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces
if(params[0]) {
snprintf(command, MSG_SIZ, "%s %s", p, params);
p = command;
if(useNick) ASSIGN(appData.pgnName[i], nickName);
if(addToList) {
int len;
+ char quote;
q = firstChessProgramNames;
if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "\"%s\" -fd \"%s\"%s%s%s%s%s%s%s%s\n", p, appData.directory[i],
+ quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
+ quote, p, quote, appData.directory[i],
useNick ? " -fn \"" : "",
useNick ? nickName : "",
useNick ? "\"" : "",
safeStrCpy(q, p, strlen(p) + 2);
if(gameNr >= 0) q[gameNr] = resChar; // replace '*' with result
if(appData.debugMode) fprintf(debugFP, "pick next game from '%s': %d\n", q, nextGame);
- if(nextGame <= appData.matchGames && resChar != ' ') { // already reserve next game, if tourney not yet done
+ if(nextGame <= appData.matchGames && resChar != ' ' && !abortMatch) { // reserve next game if tourney not yet done
if(q[nextGame] == NULLCHAR) q[nextGame+1] = NULLCHAR; // append one char
q[nextGame] = '*';
}
fprintf(tf, "%s\"\n", q); fclose(tf); // update, and flush by closing
DisplayMessage(buf, "");
free(p); appData.results = q;
- if(nextGame <= appData.matchGames && resChar != ' ' &&
+ if(nextGame <= appData.matchGames && resChar != ' ' && !abortMatch &&
(gameNr < 0 || nextGame / appData.defaultMatchGames != gameNr / appData.defaultMatchGames)) {
UnloadEngine(&first); // next game belongs to other pairing;
UnloadEngine(&second); // already unload the engines, so TwoMachinesEvent will load new ones.
int dummy;
if(matchMode) { // already in match mode: switch it off
abortMatch = TRUE;
- appData.matchGames = appData.tourneyFile[0] ? nextGame: matchGame; // kludge to let match terminate after next game.
- ModeHighlight(); // kludgey way to remove checkmark...
+ if(!appData.tourneyFile[0]) appData.matchGames = matchGame; // kludge to let match terminate after next game.
return;
}
// if(gameMode != BeginningOfGame) {
}
matchMode = mode;
matchGame = roundNr = 1;
- first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches\r
+ first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches
NextMatchGame();
}
if(f = fopen(appData.tourneyFile, "r")) {
ParseArgsFromFile(f); // make sure tourney parmeters re known
fclose(f);
+ appData.clockMode = TRUE;
+ SetGNUMode();
} else appData.tourneyFile[0] = NULLCHAR; // for now ignore bad tourney file
}
MatchEvent(TRUE);
else SendToProgram("O-O-O\n", cps);
}
else SendToProgram(moveList[moveNum], cps);
+ } else
+ if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
+ snprintf(buf, MSG_SIZ, "%c%d%c%d%s", moveList[moveNum][0], moveList[moveNum][1] - '0',
+ moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
+ SendToProgram(buf, cps);
}
else SendToProgram(moveList[moveNum], cps);
/* End of additions by Tord */
}
Boolean pushed = FALSE;
+char *lastParseAttempt;
void
ParsePV(char *pv, Boolean storeComments, Boolean atEnd)
do {
while(*pv == ' ' || *pv == '\n' || *pv == '\t') pv++; // must still read away whitespace
if(nr == 0 && !storeComments && *pv == '(') pv++; // first (ponder) move can be in parentheses
+ lastParseAttempt = pv;
valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
if(appData.debugMode){
fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, yy_textstr, pv);
fromY, fromX, toY, toX, promoChar,
parseList[endPV - 1]);
} while(valid);
+ if(atEnd == 2) return; // used hidden, for PV conversion
currentMove = (atEnd || endPV == forwardMostMove) ? endPV : forwardMostMove + 1;
if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
return TRUE;
}
+char *
+PvToSAN(char *pv)
+{
+ static char buf[10*MSG_SIZ];
+ int i, k=0, savedEnd=endPV;
+ *buf = NULLCHAR;
+ if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
+ ParsePV(pv, FALSE, 2); // this appends PV to game, suppressing any display of it
+ for(i = forwardMostMove; i<endPV; i++){
+ if(i&1) snprintf(buf+k, 10*MSG_SIZ-k, "%s ", parseList[i]);
+ else snprintf(buf+k, 10*MSG_SIZ-k, "%d. %s ", i/2 + 1, parseList[i]);
+ k += strlen(buf+k);
+ }
+ snprintf(buf+k, 10*MSG_SIZ-k, "%s", lastParseAttempt); // if we ran into stuff that could not be parsed, print it verbatim
+ if(forwardMostMove < savedEnd) PopInner(0);
+ endPV = savedEnd;
+ return buf;
+}
+
Boolean
LoadPV(int x, int y)
{ // called on right mouse click to load PV
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
} else {
int capt = 0;
- GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker);
+ GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker, EmptySquare);
if(PosFlags(0) & F_MANDATORY_CAPTURE) {
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
if(capt)
if (action != Press) return -2; // return code to be ignored
switch (gameMode) {
case IcsExamining:
- if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1;\r
+ if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1;
case EditPosition:
- if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;\r
+ if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
if (xSqr < 0 || ySqr < 0) return -1;
if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
pieceSweep = shiftKey ? BlackPawn : WhitePawn; // [HGM] sweep: prepare selecting piece by mouse sweep
toX = xSqr; toY = ySqr; lastX = x, lastY = y;
if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
NextPiece(0);
- return -2;\r
+ return -2;
case IcsObserving:
if(!appData.icsEngineAnalyze) return -1;
case IcsPlayingWhite:
SetProgramStats( &stats );
}
+void
+ClearEngineOutputPane(int which)
+{
+ static FrontEndProgramStats dummyStats;
+ dummyStats.which = which;
+ dummyStats.pv = "#";
+ SetProgramStats( &dummyStats );
+}
+
#define MAXPLAYERS 500
char *
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
-
+ if(appData.tourneyType < 0 && !strchr(appData.results, '*'))
+ 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
int fromX, fromY, toX, toY;
ChessMove moveType;
char promoChar;
- char *p;
+ char *p, *pv=buf1;
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;
+ if(savedWhitePlayer == 0 || savedBlackPlayer == 0) {
+ DisplayError(_("Invalid pairing from pairing engine"), 0);
+ return;
+ }
pairingReceived = 1;
NextMatchGame();
return; // Skim the pairing messages here.
if (!strncmp(message, "telluser ", 9)) {
if(message[9] == '\\' && message[10] == '\\')
EscapeExpand(message+9, message+11); // [HGM] esc: allow escape sequences in popup box
+ PlayTellSound();
DisplayNote(message + 9);
return;
}
cps->userError = 1;
if(message[14] == '\\' && message[15] == '\\')
EscapeExpand(message+14, message+16); // [HGM] esc: allow escape sequences in popup box
+ PlayTellSound();
DisplayError(message + 14, 0);
return;
}
curscore = -curscore;
}
+ if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1);
tempStats.depth = plylev;
tempStats.nodes = nodes;
}
/* Buffer overflow protection */
- if (buf1[0] != NULLCHAR) {
- if (strlen(buf1) >= sizeof(tempStats.movelist)
+ if (pv[0] != NULLCHAR) {
+ if (strlen(pv) >= sizeof(tempStats.movelist)
&& appData.debugMode) {
fprintf(debugFP,
"PV is too long; using the first %u bytes.\n",
(unsigned) sizeof(tempStats.movelist) - 1);
}
- safeStrCpy( tempStats.movelist, buf1, sizeof(tempStats.movelist)/sizeof(tempStats.movelist[0]) );
+ safeStrCpy( tempStats.movelist, pv, sizeof(tempStats.movelist)/sizeof(tempStats.movelist[0]) );
} else {
sprintf(tempStats.movelist, " no PV\n");
}
if( buf1[0] != NULLCHAR ) {
unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;
- if( strlen(buf1) > max_len ) {
+ if( strlen(pv) > max_len ) {
if( appData.debugMode) {
fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");
}
- buf1[max_len+1] = '\0';
+ pv[max_len+1] = '\0';
}
- strcat( thinkOutput, buf1 );
+ strcat( thinkOutput, pv);
}
if (currentMove == forwardMostMove || gameMode == AnalyzeMode
SendToProgram(buf, cps);
}
cps->initDone = TRUE;
+ ClearEngineOutputPane(cps == &second);
}
return 0;
}
ASSIGN(appData.tourneyFile, name);
+ if(appData.tourneyType < 0) appData.defaultMatchGames = 1; // Swiss forces games/pairing = 1
if((f = WriteTourneyFile("")) == NULL) return 0;
}
fclose(f);
SWAP(timeOdds, h)
SWAP(logo, p)
SWAP(pgnName, p)
+ SWAP(pvSAN, h)
}
void
*whitePlayer = curRound;
*blackPlayer = nPlayers - 1; // this is the 'bye' when nPlayer is odd
} else {
- *whitePlayer = curRound - pairingsPerRound + curPairing;
+ *whitePlayer = curRound - (nPlayers-1)/2 + curPairing;
if(*whitePlayer < 0) *whitePlayer += nPlayers-1+(nPlayers&1);
- *blackPlayer = curRound + pairingsPerRound - curPairing;
+ *blackPlayer = curRound + (nPlayers-1)/2 - curPairing;
if(*blackPlayer >= nPlayers-1+(nPlayers&1)) *blackPlayer -= nPlayers-1+(nPlayers&1);
}
} else if(appData.tourneyType > 0) {
InitTimeControls(); // TC might be altered from tourney file
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
+ if(appData.tourneyType < 0) syncInterval = nPlayers/2; else
*swapColors = Pairing(nr<0 ? 0 : nr, nPlayers, &whitePlayer, &blackPlayer, &syncInterval);
if(syncInterval) {
waitingForGame = FALSE;
}
+ if(appData.tourneyType < 0) {
+ if(nr>=0 && !pairingReceived) {
+ char buf[1<<16];
+ if(pairing.pr == NoProc) {
+ if(!appData.pairingEngine[0]) {
+ DisplayFatalError(_("No pairing engine specified"), 0, 1);
+ return 0;
+ }
+ StartChessProgram(&pairing); // starts the pairing engine
+ }
+ 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
+ *swapColors = 0;
+ appData.matchGames = appData.tourneyCycles * syncInterval - 1;
+ whitePlayer = savedWhitePlayer-1; blackPlayer = savedBlackPlayer-1;
+ matchGame = 1; roundNr = nr / syncInterval + 1;
+ }
+
if(first.pr != NoProc) return 1; // engines already loaded
// redefine engines, engine dir, etc.
InitEngine(&first, 0); // initialize ChessProgramStates based on new settings.
InitEngine(&second, 1);
CommonEngineInit(); // after this TwoMachinesEvent will create correct engine processes
+ UpdateLogos(FALSE); // leave display to ModeHiglight()
return 1;
}
}
if(waitingForGame) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
- if(appData.tourneyFile[0] && !abortMatch){ // [HGM] we are in a tourney; update tourney file with game result
+ if(appData.tourneyFile[0]){ // [HGM] we are in a tourney; update tourney file with game result
ReserveGame(nextGame, resChar); // sets nextGame
if(nextGame > appData.matchGames) appData.tourneyFile[0] = 0, ranking = TourneyStandings(3); // tourney is done
+ else ranking = strdup("busy"); //suppress popup when aborted but not finished
} else roundNr = nextGame = matchGame + 1; // normal match, just increment; round equals matchGame
if (nextGame <= appData.matchGames && !abortMatch) {
first.tidy, second.tidy,
first.matchWins, second.matchWins,
appData.matchGames - (first.matchWins + second.matchWins));
+ if(!appData.tourneyFile[0]) matchGame++, DisplayTwoMachinesTitle(); // [HGM] update result in window title
popupRequested++; // [HGM] crash: postpone to after resetting endingGame
if (appData.firstPlaysBlack) { // [HGM] match: back to original for next match
first.twoMachinesColor = "black\n";
if(popupRequested) { // [HGM] crash: this calls GameEnds recursively through ExitEvent! Make it a harmless tail recursion.
if(matchMode == TRUE) { // match through command line: exit with or without popup
if(ranking) {
+ ToNrEvent(forwardMostMove);
if(strcmp(ranking, "busy")) DisplayFatalError(ranking, 0, 0);
else ExitEvent(0);
} else DisplayFatalError(buf, 0, 0);
} else { // match through menu; just stop, with or without popup
matchMode = FALSE; appData.matchGames = matchGame = roundNr = 0;
+ ModeHighlight();
if(ranking){
if(strcmp(ranking, "busy")) DisplayNote(ranking);
} else DisplayNote(buf);
gameMode = TwoMachinesPlay;
pausing = FALSE;
- ModeHighlight();
+ ModeHighlight(); // [HGM] logo: this triggers display update of logos
SetGameInfo();
DisplayTwoMachinesTitle();
firstMove = TRUE;
void
TypeInEvent(char firstChar)
{
- if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
- gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
- gameMode == AnalyzeMode || gameMode == EditGame || \r
- gameMode == EditPosition || gameMode == IcsExamining ||\r
- gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
- isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
- ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
- gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||\r
+ if ((gameMode == BeginningOfGame && !appData.icsActive) ||
+ gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
+ gameMode == AnalyzeMode || gameMode == EditGame ||
+ gameMode == EditPosition || gameMode == IcsExamining ||
+ gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
+ isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
+ ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
+ gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
gameMode == Training) PopUpMoveDialog(firstChar);
}
Board board;
int n, fromX, fromY, toX, toY;
char promoChar;
- ChessMove moveType;\r
-
- // [HGM] FENedit\r
- if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
- EditPositionPasteFEN(move);\r
- return;\r
- }\r
- // [HGM] movenum: allow move number to be typed in any mode\r
- if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
- ToNrEvent(2*n-1);\r
- return;\r
- }\r
-
- if (gameMode != EditGame && currentMove != forwardMostMove && \r
- gameMode != Training) {\r
- DisplayMoveError(_("Displayed move is not current"));\r
- } else {\r
- int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
- &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
- if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
- if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
- &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
- UserMoveEvent(fromX, fromY, toX, toY, promoChar); \r
- } else {\r
- DisplayMoveError(_("Could not parse move"));\r
- }
- }\r
-}\r
+ ChessMove moveType;
+
+ // [HGM] FENedit
+ if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
+ EditPositionPasteFEN(move);
+ return;
+ }
+ // [HGM] movenum: allow move number to be typed in any mode
+ if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
+ ToNrEvent(2*n-1);
+ return;
+ }
+
+ if (gameMode != EditGame && currentMove != forwardMostMove &&
+ gameMode != Training) {
+ DisplayMoveError(_("Displayed move is not current"));
+ } else {
+ int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
+ &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
+ if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
+ if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
+ &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
+ UserMoveEvent(fromX, fromY, toX, toY, promoChar);
+ } else {
+ DisplayMoveError(_("Could not parse move"));
+ }
+ }
+}
void
DisplayMove(moveNumber)