#ifdef WIN32
#include <windows.h>
-#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
-
int flock(int f, int code);
#define LOCK_EX 2
#define SLASH '\\'
#else
#include <sys/file.h>
-#define DoSleep( n ) if( (n) >= 0) sleep(n)
#define SLASH '/'
#endif
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
Load(ChessProgramState *cps, int i)
{
char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ];
- if(engineLine[0]) { // an engine was selected from the combo box
+ if(engineLine && 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*
- ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
+ ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
ParseArgsFromString(buf);
SwapEngines(i);
ReplaceEngine(cps, i);
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]) {
+ if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces
snprintf(command, MSG_SIZ, "%s %s", p, params);
p = command;
}
GetTimeMark(&programStartTime);
srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
+ appData.seedBase = random() + (random()<<15);
pauseStart = programStartTime; pauseStart.sec -= 100; // [HGM] matchpause: fake a pause that has long since ended
ClearProgramStats();
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);
}
}
+void
+HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
+{
+ MoveHistorySet( movelist, first, last, current, pvInfoList );
+
+ EvalGraphSet( first, last, current, pvInfoList );
+
+ MakeEngineOutputTitle();
+}
+
/*
* Establish will establish a contact to a remote host.port.
* Sets icsPR to a ProcRef for a process (or pseudo-process)
int step = 0;
if(promoSweep == EmptySquare || !appData.sweepSelect) return FALSE;
- if(abs(x - lastX) < 15 && abs(y - lastY) < 15) return FALSE;
+ if(abs(x - lastX) < 25 && abs(y - lastY) < 25) return FALSE;
if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
if(!step) return FALSE;
lastX = x; lastY = y;
void
MovePV(int x, int y, int h)
{ // step through PV based on mouse coordinates (called on mouse move)
- int margin = h>>3, step = 0;
+ int margin = h>>3, step = 0, threshold = (pieceSweep == EmptySquare ? 10 : 15);
// we must somehow check if right button is still down (might be released off board!)
if(endPV < 0 && pieceSweep == EmptySquare) return; // needed in XBoard because lastX/Y is shared :-(
- if(abs(x - lastX) < 7 && abs(y - lastY) < 7) return;
+ if(abs(x - lastX) < threshold && abs(y - lastY) < threshold) return;
if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
if(!step) return;
lastX = x; lastY = y;
if (StrStr(message, "analyze")) {
cps->analysisSupport = FALSE;
cps->analyzing = FALSE;
- Reset(FALSE, TRUE);
+// Reset(FALSE, TRUE); // [HGM] this caused discrepancy between display and internal state!
+ EditGameEvent(); // [HGM] try to preserve loaded game
snprintf(buf2,MSG_SIZ, _("%s does not support analysis"), cps->tidy);
DisplayError(buf2, 0);
return;
if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1);
+ if(serverMoves && (time > 100 || time == 0 && plylev > 7)) {
+ char buf[MSG_SIZ];
+ FILE *f;
+ snprintf(buf, MSG_SIZ, "%s", appData.serverMovesName);
+ buf[strlen(buf)-1] = gameMode == MachinePlaysWhite ? 'w' :
+ gameMode == MachinePlaysBlack ? 'b' : cps->twoMachinesColor[0];
+ if(appData.debugMode) fprintf(debugFP, "write PV on file '%s'\n", buf);
+ if(f = fopen(buf, "w")) { // export PV to applicable PV file
+ fprintf(f, "%5.2f/%-2d %s", curscore/100., plylev, pv);
+ fclose(f);
+ } else DisplayError("failed writing PV", 0);
+ }
+
tempStats.depth = plylev;
tempStats.nodes = nodes;
tempStats.time = time;
break;
case WhiteDrop:
case BlackDrop:
+ if(currentMoveString[0] == '@') continue; // no null moves in ICS mode!
fromX = moveType == WhiteDrop ?
(int) CharToPiece(ToUpper(currentMoveString[0])) :
(int) CharToPiece(ToLower(currentMoveString[0]));
{
// forwardMostMove++; // [HGM] bare: moved downstream
+ (void) CoordsToAlgebraic(boards[forwardMostMove],
+ PosFlags(forwardMostMove),
+ fromY, fromX, toY, toX, promoChar,
+ parseList[forwardMostMove]);
+
if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */
int timeLeft; static int lastLoadFlag=0; int king, piece;
piece = boards[forwardMostMove][fromY][fromX];
if(gameInfo.variant == VariantKnightmate)
king += (int) WhiteUnicorn - (int) WhiteKing;
if(forwardMostMove == 0) {
- if(blackPlaysFirst)
+ if(gameMode == MachinePlaysBlack || gameMode == BeginningOfGame)
+ fprintf(serverMoves, "%s;", UserName());
+ else if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b')
fprintf(serverMoves, "%s;", second.tidy);
fprintf(serverMoves, "%s;", first.tidy);
- if(!blackPlaysFirst)
+ if(gameMode == MachinePlaysWhite)
+ fprintf(serverMoves, "%s;", UserName());
+ else if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w')
fprintf(serverMoves, "%s;", second.tidy);
} else fprintf(serverMoves, loadFlag|lastLoadFlag ? ":" : ";");
lastLoadFlag = loadFlag;
fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);
// promotion suffix
if(promoChar != NULLCHAR)
- fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY);
+ fprintf(serverMoves, ":%c:%c%c", ToLower(promoChar), AAA+toX, ONE+toY);
if(!loadFlag) {
+ char buf[MOVE_LEN*2], *p; int len;
fprintf(serverMoves, "/%d/%d",
pvInfoList[forwardMostMove].depth, pvInfoList[forwardMostMove].score);
if(forwardMostMove+1 & 1) timeLeft = whiteTimeRemaining/1000;
else timeLeft = blackTimeRemaining/1000;
fprintf(serverMoves, "/%d", timeLeft);
+ strncpy(buf, parseList[forwardMostMove], MOVE_LEN*2);
+ if(p = strchr(buf, '=')) *p = NULLCHAR;
+ len = strlen(buf); if(len > 1 && buf[len-2] != '-') buf[len-2] = NULLCHAR; // strip to-square
+ fprintf(serverMoves, "/%s", buf);
}
fflush(serverMoves);
}
}
CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
moveList[forwardMostMove - 1]);
- (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
- PosFlags(forwardMostMove - 1),
- fromY, fromX, toY, toX, promoChar,
- parseList[forwardMostMove - 1]);
switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
case MT_NONE:
case MT_STALEMATE:
}
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);
+ fprintf(f, "-seedBase %d\n", appData.seedBase);
fprintf(f, "-tourneyType %d\n", appData.tourneyType);
fprintf(f, "-tourneyCycles %d\n", appData.tourneyCycles);
fprintf(f, "-defaultMatchGames %d\n", appData.defaultMatchGames);
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<len; i++) {
+ Pairing(i, nPlayers, &w, &b, &dummy);
+ if((w == changed || b == changed) && appData.results[i] == '*') {
+ DisplayError(_("You cannot replace an engine while it is engaged!\nTerminate its game first."), 0);
+ fclose(f);
+ return;
+ }
+ }
+ for(i=0; i<len; i++) {
+ Pairing(i, nPlayers, &w, &b, &dummy);
+ if(w == changed || b == changed) appData.results[i] = ' '; // mark as not played
+ }
+ }
+ WriteTourneyFile(appData.results, f);
+ fclose(f); // release lock
+ return;
+ }
+ } else DisplayError(_("No engine with the name you gave is installed"), 0);
+ }
+ if(changes == 0) DisplayError(_("First change an engine by editing the participants list\nof the Tournament Options dialog"), 0);
+ if(changes > 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);
}
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;
return 1;
}
-#define MAXENGINES 1000
-char *command[MAXENGINES], *mnemonic[MAXENGINES];
-
void NamesToList(char *names, char **engineList, char **engineMnemonic)
{
char buf[MSG_SIZ], *p, *q;
names = p; i++;
if(i > MAXENGINES - 2) break;
}
- engineList[i] = NULL;
+ engineList[i] = engineMnemonic[i] = NULL;
}
// following implemented as macro to avoid type limitations
first.twoMachinesColor = firstWhite ? "white\n" : "black\n"; // perform actual color assignement
second.twoMachinesColor = firstWhite ? "black\n" : "white\n";
appData.noChessProgram = (first.pr == NoProc); // kludge to prevent Reset from starting up chess program
+ if(appData.loadGameIndex == -2) srandom(appData.seedBase + 68163*(nextGame & ~1)); // deterministic seed to force same opening
Reset(FALSE, first.pr != NoProc);
appData.noChessProgram = FALSE;
if(!LoadGameOrPosition(matchGame)) return; // setup game; abort when bad game/pos file
if(result==WhiteWins) c = '+';
if(result==BlackWins) c = '-';
if(resultDetails != NULL)
- fprintf(serverMoves, ";%c;%s\n", c, resultDetails);
+ fprintf(serverMoves, ";%c;%s\n", c, resultDetails), fflush(serverMoves);
}
if (resultDetails != NULL) {
gameInfo.result = result;
if(waitingForGame) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
if(appData.tourneyFile[0]){ // [HGM] we are in a tourney; update tourney file with game result
+ if(appData.afterGame && appData.afterGame[0]) RunCommand(appData.afterGame);
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
static int initDone=FALSE;
if(!initDone) {
- for(next = WhitePawn; next<EmptySquare; next++) keys[next] = rand()>>8 ^ rand()<<6 ^rand()<<20;
+ for(next = WhitePawn; next<EmptySquare; next++) keys[next] = random()>>8 ^ random()<<6 ^random()<<20;
initDone = TRUE;
}
dummyInfo.variant = VariantNormal;
switch(next) {
case PGNTag:
if(plyNr) return -1; // after we have seen moves, any tags will be start of next game
- ParsePGNTag(yy_text, &dummyInfo);
- if(dummyInfo.fen) ParseFEN(boards[scratch], &btm, dummyInfo.fen), free(dummyInfo.fen), dummyInfo.fen = NULL;
+#if 0
+ ParsePGNTag(yy_text, &dummyInfo); // this has a bad memory leak...
+ if(dummyInfo.fen) ParseFEN(boards[scratch], &btm, dummyInfo.fen), free(dummyInfo.fen), dummyInfo.fen = NULL;
+#else
+ // do it ourselves avoiding malloc
+ { char *p = yy_text+1, *q;
+ while(!isdigit(*p) && !isalpha(*p)) p++;
+ q = p; while(*p != ' ' && *p != '\t' && *p != '\n') p++;
+ *p = NULLCHAR;
+ if(!StrCaseCmp(q, "Date") && (p = strchr(p+1, '"'))) { if(atoi(p+1) < appData.dateThreshold) return -1; } else
+ if(!StrCaseCmp(q, "Variant") && (p = strchr(p+1, '"'))) dummyInfo.variant = StringToVariant(p+1); else
+ if(!StrCaseCmp(q, "WhiteElo") && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else
+ if(!StrCaseCmp(q, "BlackElo") && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else
+ if(!StrCaseCmp(q, "WhiteUSCF") && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else
+ if(!StrCaseCmp(q, "BlackUSCF") && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else
+ if(!StrCaseCmp(q, "FEN") && (p = strchr(p+1, '"'))) ParseFEN(boards[scratch], &btm, p+1);
+ }
+#endif
default:
continue;
{
FILE *f;
char buf[MSG_SIZ];
- int result;
+ int result, i, t,tot=0;
if (strcmp(filename, "-") == 0) {
return SaveGame(stdout, 0, NULL);
} else {
- f = fopen(filename, append ? "a" : "w");
+ for(i=0; i<10; i++) { // upto 10 tries
+ f = fopen(filename, append ? "a" : "w");
+ if(f && i) fprintf(f, "[Delay \"%d retries, %d msec\"]\n",i,tot);
+ if(f || errno != 13) break;
+ DoSleep(t = 5 + random()%11); // wait 5-15 msec
+ tot += t;
+ }
if (f == NULL) {
snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);
DisplayError(buf, errno);
StartAnalysisClock();
GetTimeMark(&lastNodeCountTime);
lastNodeCount = 0;
+ if(appData.timeDelay > 0) StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
}
void
if (len == 0) return;
if (commentList[index] != NULL) {
+ Boolean addClosingBrace = addBraces;
old = commentList[index];
oldlen = strlen(old);
while(commentList[index][oldlen-1] == '\n')
if(addBraces) strcat(commentList[index], addBraces == 2 ? "\n(" : "\n{\n");
else strcat(commentList[index], "\n");
strcat(commentList[index], text);
- if(addBraces) strcat(commentList[index], addBraces == 2 ? ")\n" : "\n}\n");
+ if(addClosingBrace) strcat(commentList[index], addClosingBrace == 2 ? ")\n" : "\n}\n");
else strcat(commentList[index], "\n");
} else {
commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4...
while( *++sep >= '0' && *sep <= '9'); // strip seconds
if(deci >= 0)
while( *++sep >= '0' && *sep <= '9'); // strip fractional seconds
- while(*sep == ' ') sep++;
+ while(*sep == ' ' || *sep == '\n' || *sep == '\r') sep++;
}
if( depth <= 0 ) {