return flags;
}
-FILE *gameFileFP, *debugFP;
+FILE *gameFileFP, *debugFP, *serverFP;
+char *currentDebugFile; // [HGM] debug split: to remember name
/*
[AS] Note: sometimes, the sscanf() function is used to parse the input
}
char *engineNames[] = {
-"first",
-"second"
+ /* TRANSLATORS: "first" is the first of possible two chess engines. It is inserted into strings
+ such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */
+N_("first"),
+ /* TRANSLATORS: "second" is the second of possible two chess engines. It is inserted into strings
+ such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */
+N_("second")
};
void
"-firstOptions \"\" -firstNPS -1 -fn \"\"";
void
+FloatToFront(char **list, char *engineLine)
+{
+ char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
+ int i=0;
+ if(appData.recentEngines <= 0) return;
+ TidyProgramName(engineLine, "localhost", tidy+1);
+ tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
+ strncpy(buf+1, *list, MSG_SIZ-50);
+ if(p = strstr(buf, tidy)) { // tidy name appears in list
+ q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
+ while(*p++ = *++q); // squeeze out
+ }
+ strcat(tidy, buf+1); // put list behind tidy name
+ p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
+ if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
+ ASSIGN(*list, tidy+1);
+}
+
+void
Load (ChessProgramState *cps, int i)
{
char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ];
ParseArgsFromString(buf);
SwapEngines(i);
ReplaceEngine(cps, i);
+ FloatToFront(&appData.recentEngineList, engineLine);
return;
}
p = engineName;
p[-1] = 0;
appData.directory[i] = strdup(engineName);
p[-1] = SLASH;
+ if(SLASH == '/' && p - engineName > 1) *(p -= 2) = '.'; // for XBoard use ./exeName as command after split!
} else appData.directory[i] = ".";
if(params[0]) {
if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces
firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
snprintf(firstChessProgramNames, len, "%s%s", q, buf);
if(q) free(q);
+ FloatToFront(&appData.recentEngineList, buf);
}
ReplaceEngine(cps, i);
}
char *s = tcString;
if(!*s) return 0; // empty TC string means we ran out of the last sudden-death version
- if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", tcString);
do {
if(moves) NextSessionFromString(&s, &moves, &time, &increment, &incType);
nextSession = s; suddenDeath = moves == 0 && increment == 0;
- if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
if(movenr == -1) return time; /* last move before new session */
if(incType == '*') increment = 0; else // for sandclock, time is added while not thinking
if(incType == '!' && lastUsed < increment) increment = lastUsed;
if (appData.debugMode) {
fprintf(debugFP, "%s\n", programVersion);
}
+ ASSIGN(currentDebugFile, appData.nameOfDebugFile); // [HGM] debug split: remember initial name in use
set_cont_sequence(appData.wrapContSeq);
if (appData.matchGames > 0) {
UnloadEngine(&first); // next game belongs to other pairing;
UnloadEngine(&second); // already unload the engines, so TwoMachinesEvent will load new ones.
}
- if(appData.debugMode) fprintf(debugFP, "Reserved, next=%d, nr=%d, procs=(%x,%x)\n", nextGame, gameNr, first.pr, second.pr);
+ if(appData.debugMode) fprintf(debugFP, "Reserved, next=%d, nr=%d\n", nextGame, gameNr);
}
void
NextMatchGame();
}
+char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line
+
void
InitBackEnd3 P((void))
{
free(programVersion);
programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+ FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram);
}
if (appData.icsActive) {
{
static int lastDown = 0, displayed = 0, lastSecond;
if(y < 0) return FALSE;
+ if(!(appData.seekGraph && appData.icsActive && loggedOn &&
+ (gameMode == BeginningOfGame || gameMode == IcsIdle))) {
+ if(!seekGraphUp) return FALSE;
+ seekGraphUp = FALSE; // seek graph is up when it shouldn't be: take it down
+ DrawPosition(TRUE, NULL);
+ return TRUE;
+ }
if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
if(click == Release || moving) return FALSE;
nrOfSeekAds = 0;
if (looking_at(buf, &i, "% ") ||
((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
&& looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
- if(soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line
+ if(soughtPending && nrOfSeekAds) { // [HGM] seekgraph: on ICC sought-list has no termination line
soughtPending = FALSE;
seekGraphUp = TRUE;
DrawSeekGraph();
newGameMode =
((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
IcsPlayingWhite : IcsPlayingBlack;
+ soughtPending =FALSE; // [HGM] seekgraph: solve race condition
break;
case RELATION_EXAMINING:
newGameMode = IcsExamining;
r = boards[moveNum][CASTLING][5] = initialRights[5];
}
/* [HGM] e.p. rights. Assume that ICS sends file number here? */
- boards[moveNum][EP_STATUS] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
+ boards[moveNum][EP_STATUS] = EP_NONE;
+ if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
+ if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
+ if(double_push != -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
if (ics_getting_history == H_GOT_REQ_HEADER ||
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);
-}
if(!valid && nr == 0 &&
ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){
nr++; moveType = Comment; // First move has been played; kludge to make sure we continue
{
int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded!
if(endPV < 0) return;
+ if(appData.autoCopyPV) CopyFENToClipboard();
endPV = -1;
if(gameMode == AnalyzeMode && currentMove > forwardMostMove) {
Boolean saveAnimate = appData.animate;
char promoChoice = NULLCHAR;
ChessSquare piece;
- if(appData.seekGraph && appData.icsActive && loggedOn &&
- (gameMode == BeginningOfGame || gameMode == IcsIdle)) {
- SeekGraphClick(clickType, xPix, yPix, 0);
- return;
- }
+ if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
if (clickType == Press) ErrorPopDown();
|| x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
return;
+ if(gotPremove && x == premoveFromX && y == premoveFromY && clickType == Release) {
+ // could be static click on premove from-square: abort premove
+ gotPremove = 0;
+ ClearPremoveHighlights();
+ }
+
if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered)
fromX = fromY = -1; // second click on piece after altering default promo piece treated as first click
}
} else moveCount = 6;
}
- if (appData.debugMode) { int i;
- fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",
- forwardMostMove, backwardMostMove, boards[backwardMostMove][EP_STATUS],
- appData.drawRepeats);
- for( i=forwardMostMove; i>=backwardMostMove; i-- )
- fprintf(debugFP, "%d ep=%d\n", i, (signed char)boards[i][EP_STATUS]);
-
- }
// Repetition draws and 50-move rule can be applied independently of legality testing
return;
}
- if (appData.debugMode) { int f = forwardMostMove;
- fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,
- boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
- boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
- }
if(cps->alphaRank) AlphaRank(machineMove, 4);
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)) {
ChessMove moveType;
moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),
fromY, fromX, toY, toX, promoChar);
- if (appData.debugMode) {
- int i;
- for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",
- boards[forwardMostMove][CASTLING][i], castlingRank[i]);
- fprintf(debugFP, "castling rights\n");
- }
if(moveType == IllegalMove) {
snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",
machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);
/*
* If chess program startup fails, exit with an error message.
- * Attempts to recover here are futile.
+ * Attempts to recover here are futile. [HGM] Well, we try anyway
*/
if ((StrStr(message, "unknown host") != NULL)
|| (StrStr(message, "No remote directory") != NULL)
_(cps->which), cps->program, cps->host, message);
RemoveInputSource(cps->isr);
if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else {
- if(cps == &first) appData.noChessProgram = TRUE;
+ cps->isr = NULL;
+ DestroyChildProcess(cps->pr, 9 ); // just to be sure
+ cps->pr = NoProc;
+ if(cps == &first) {
+ appData.noChessProgram = TRUE;
+ gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu
+ gameMode = BeginningOfGame; ModeHighlight();
+ SetNCPMode();
+ }
+ if(GetDelayedEvent()) CancelDelayedEvent(), ThawUI(); // [HGM] cancel remaining loading effort scheduled after feature timeout
+ DisplayMessage("", ""); // erase waiting message
DisplayError(buf1, 0);
}
return;
strcat(parseList[forwardMostMove - 1], "#");
break;
}
- if (appData.debugMode) {
- fprintf(debugFP, "move: %s, parse: %s (%c)\n", moveList[forwardMostMove-1], parseList[forwardMostMove-1], moveList[forwardMostMove-1][4]);
- }
}
q = r; while(*q) nPlayers += (*q++ == '\n');
p = buf; while(*r && (*p = *r++) != '\n') p++;
*p = NULLCHAR;
- NamesToList(firstChessProgramNames, command, mnemonic);
+ NamesToList(firstChessProgramNames, command, mnemonic, "all");
for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break;
if(mnemonic[i]) { // The substitute is valid
FILE *f;
return 1;
}
-void
-NamesToList (char *names, char **engineList, char **engineMnemonic)
+int
+NamesToList (char *names, char **engineList, char **engineMnemonic, char *group)
{
char buf[MSG_SIZ], *p, *q;
- int i=1;
- while(*names) {
- p = names; q = buf;
+ int i=1, header, skip, all = !strcmp(group, "all"), depth = 0;
+ skip = !all && group[0]; // if group requested, we start in skip mode
+ for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) {
+ p = names; q = buf; header = 0;
while(*p && *p != '\n') *q++ = *p++;
*q = 0;
+ if(*p == '\n') p++;
+ if(buf[0] == '#') {
+ if(strstr(buf, "# end") == buf) { depth--; continue; } // leave group, and suppress printing label
+ depth++; // we must be entering a new group
+ if(all) continue; // suppress printing group headers when complete list requested
+ header = 1;
+ if(skip && !strcmp(group, buf)) { depth = 0; skip = FALSE; } // start when we reach requested group
+ }
+ if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header)
if(engineList[i]) free(engineList[i]);
engineList[i] = strdup(buf);
- if(*p == '\n') p++;
- TidyProgramName(engineList[i], "localhost", buf);
+ if(buf[0] != '#') TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied
if(engineMnemonic[i]) free(engineMnemonic[i]);
if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) {
strcat(buf, " (");
strcat(buf, ")");
}
engineMnemonic[i] = strdup(buf);
- names = p; i++;
- if(i > MAXENGINES - 2) break;
+ i++;
}
engineList[i] = engineMnemonic[i] = NULL;
+ return i;
}
// following implemented as macro to avoid type limitations
SWAP(engOptions, p)
}
-void
-SetPlayer (int player)
+int
+SetPlayer (int player, char *p)
{ // [HGM] find the engine line of the partcipant given by number, and parse its options.
int i;
- char buf[MSG_SIZ], *engineName, *p = appData.participants;
+ char buf[MSG_SIZ], *engineName;
for(i=0; i<player; i++) p = strchr(p, '\n') + 1;
engineName = strdup(p); if(p = strchr(engineName, '\n')) *p = NULLCHAR;
for(i=1; command[i]; i++) if(!strcmp(mnemonic[i], engineName)) break;
ParseArgsFromString(buf);
}
free(engineName);
+ return i;
+}
+
+char *recentEngines;
+
+void
+RecentEngineEvent (int nr)
+{
+ int n;
+// SwapEngines(1); // bump first to second
+// ReplaceEngine(&second, 1); // and load it there
+ NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
+ n = SetPlayer(nr, recentEngines); // select new (using original menu order!)
+ if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
+ ReplaceEngine(&first, 0);
+ FloatToFront(&appData.recentEngineList, command[n]);
+ }
}
int
matchGame = 1; roundNr = nr / syncInterval + 1;
}
- if(first.pr != NoProc && second.pr != NoProc) return 1; // engines already loaded
+ if(first.pr != NoProc && second.pr != NoProc || nr<0) return 1; // engines already loaded
// redefine engines, engine dir, etc.
- NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
- if(first.pr == NoProc || nr < 0) {
- SetPlayer(whitePlayer); // find white player amongst it, and parse its engine line
+ NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
+ if(first.pr == NoProc) {
+ SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
InitEngine(&first, 0); // initialize ChessProgramStates based on new settings.
}
if(second.pr == NoProc) {
SwapEngines(1);
- SetPlayer(blackPlayer); // find black player amongst it, and parse its engine line
+ SetPlayer(blackPlayer, appData.participants); // find black player amongst it, and parse its engine line
SwapEngines(1); // and make that valid for second engine by swapping
InitEngine(&second, 1);
}
{ // performs game initialization that does not invoke engines, and then tries to start the game
int res, firstWhite, swapColors = 0;
if(!NextTourneyGame(nextGame, &swapColors)) return; // this sets matchGame, -fcp / -scp and other options for next game, if needed
+ if(matchMode && appData.debugMode) { // [HGM] debug split: game is part of a match; we might have to create a debug file just for this game
+ char buf[MSG_SIZ];
+ snprintf(buf, MSG_SIZ, appData.nameOfDebugFile, nextGame+1); // expand name of debug file with %d in it
+ if(strcmp(buf, currentDebugFile)) { // name has changed
+ FILE *f = fopen(buf, "w");
+ if(f) { // if opening the new file failed, just keep using the old one
+ ASSIGN(currentDebugFile, buf);
+ fclose(debugFP);
+ debugFP = f;
+ }
+ if(appData.serverFileName) {
+ if(serverFP) fclose(serverFP);
+ serverFP = fopen(appData.serverFileName, "w");
+ if(serverFP && first.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", first.tidy);
+ if(serverFP && second.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", second.tidy);
+ }
+ }
+ }
firstWhite = appData.firstPlaysBlack ^ (matchGame & 1 | appData.sameColorGames > 1); // non-incremental default
firstWhite ^= swapColors; // reverses if NextTourneyGame says we are in an odd round
first.twoMachinesColor = firstWhite ? "white\n" : "black\n"; // perform actual color assignement
PrintPGNTags(f, &gameInfo);
+ if(appData.numberTag && matchMode) fprintf(f, "[Number \"%d\"]\n", nextGame+1); // [HGM] number tag
+
if (backwardMostMove > 0 || startedFromSetupPosition) {
char *fen = PositionToFEN(backwardMostMove, NULL);
fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
} else {
/* kludge: allow timeout for initial "feature" command */
FreezeUI();
- snprintf(buf, MSG_SIZ, _("Starting %s chess program"), cps->which);
+ snprintf(buf, MSG_SIZ, _("Starting %s chess program"), _(cps->which));
DisplayMessage("", buf);
ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
}
ScheduleDelayedEvent(TwoMachinesEventIfReady, appData.matchPause - wait);
return;
}
+ // we are now committed to starting the game
stalling = 0;
DisplayMessage("", "");
if (startedFromSetupPosition) {
currentMove = forwardMostMove = backwardMostMove = 0;
HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
DisplayMove(-1);
+ if(!appData.pieceMenu) DisplayMessage("Click clock to clear board", "");
}
void
} else
boards[0][y][x] = selection;
DrawPosition(TRUE, boards[0]);
+ ClearHighlights();
+ fromX = fromY = -1;
}
break;
}
void
ForwardInner (int target)
{
- int limit;
+ int limit; int oldSeekGraphUp = seekGraphUp;
if (appData.debugMode)
fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
}
DisplayBothClocks();
DisplayMove(currentMove - 1);
- DrawPosition(FALSE, boards[currentMove]);
+ DrawPosition(oldSeekGraphUp, boards[currentMove]);
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty
DisplayComment(currentMove - 1, commentList[currentMove]);
void
TidyProgramName (char *prog, char *host, char buf[MSG_SIZ])
{
- char *p, *q;
+ char *p, *q, c;
int local = (strcmp(host, "localhost") == 0);
while (!local && (p = strchr(prog, ';')) != NULL) {
p++;
while (p >= prog && *p != '/' && *p != '\\') p--;
p++;
if(p == prog && *p == '"') p++;
- if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
+ c = *q; *q = 0;
+ if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) *q = c, q -= 4; else *q = c;
memcpy(buf, p, q - p);
buf[q - p] = NULLCHAR;
if (!local) {
fprintf(debugFP, "%ld >%-6s: %s",
SubtractTimeMarks(&now, &programStartTime),
cps->which, message);
+ if(serverFP)
+ fprintf(serverFP, "%ld >%-6s: %s",
+ SubtractTimeMarks(&now, &programStartTime),
+ cps->which, message), fflush(serverFP);
}
count = strlen(message);
SubtractTimeMarks(&now, &programStartTime), cps->which,
quote,
message);
+ if(serverFP)
+ fprintf(serverFP, "%ld <%-6s: %s%s\n",
+ SubtractTimeMarks(&now, &programStartTime), cps->which,
+ quote,
+ message), fflush(serverFP);
}
}
}
/* [HGM] translate opponent's time by time-odds factor */
otime = (otime * cps->other->timeOdds) / cps->timeOdds;
- if (appData.debugMode) {
- fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds);
- }
if (time <= 0) time = 1;
if (otime <= 0) otime = 1;
ToNrEvent(2*n-1);
return;
}
+ // undocumented kludge: allow command-line option to be typed in!
+ // (potentially fatal, and does not implement the effect of the option.)
+ // should only be used for options that are values on which future decisions will be made,
+ // and definitely not on options that would be used during initialization.
+ if(strstr(move, "!!! -") == move) {
+ ParseArgsFromString(move+4);
+ return;
+ }
if (gameMode != EditGame && currentMove != forwardMostMove &&
gameMode != Training) {