static void ExcludeClick P((int index));
void ToggleSecond P((void));
void PauseEngine P((ChessProgramState *cps));
+static int NonStandardBoardSize P((void));
#ifdef WIN32
extern void ConsoleCreate();
{
dst[ count-1 ] = '\0'; // make sure incomplete copy still null-terminated
if(appData.debugMode)
- fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst, (int)count);
+ fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst, (int)count);
}
return dst;
char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC, activePartner; /* [HGM] secondary TC: merge of MPS, TC and inc */
long timeRemaining[2][MAX_MOVES];
int matchGame = 0, nextGame = 0, roundNr = 0;
-Boolean waitingForGame = FALSE;
+Boolean waitingForGame = FALSE, startingEngine = FALSE;
TimeMark programStartTime, pauseStart;
char ics_handle[MSG_SIZ];
int have_set_title = 0;
cps->sendName = appData.icsActive;
cps->sdKludge = FALSE;
cps->stKludge = FALSE;
+ if(cps->tidy == NULL) cps->tidy = (char*) malloc(MSG_SIZ);
TidyProgramName(cps->program, cps->host, cps->tidy);
cps->matchWins = 0;
- safeStrCpy(cps->variants, appData.variant, MSG_SIZ);
+ ASSIGN(cps->variants, appData.variant);
cps->analysisSupport = 2; /* detect */
cps->analyzing = FALSE;
cps->initDone = FALSE;
+ cps->reload = FALSE;
/* New features added by Tord: */
cps->useFEN960 = FALSE;
cps->supportsNPS = UNKNOWN;
cps->memSize = FALSE;
cps->maxCores = FALSE;
- cps->egtFormats[0] = NULLCHAR;
+ ASSIGN(cps->egtFormats, "");
/* [HGM] options */
cps->optionSettings = appData.engOptions[n];
ChessProgramState *savCps;
+GameMode oldMode;
+
void
LoadEngine ()
{
if(gameInfo.variant != StringToVariant(appData.variant)) {
// we changed variant when loading the engine; this forces us to reset
Reset(TRUE, savCps != &first);
- EditGameEvent(); // for consistency with other path, as Reset changes mode
+ oldMode = BeginningOfGame; // to prevent restoring old mode
}
InitChessProgram(savCps, FALSE);
- SendToProgram("force\n", savCps);
+ if(gameMode == EditGame) SendToProgram("force\n", savCps); // in EditGame mode engine must be in force mode
DisplayMessage("", "");
if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove);
for (i = backwardMostMove; i < currentMove; i++) SendMoveToProgram(i, savCps);
ThawUI();
SetGNUMode();
+ if(oldMode == AnalyzeMode) AnalyzeModeEvent();
}
void
ReplaceEngine (ChessProgramState *cps, int n)
{
- EditGameEvent();
+ oldMode = gameMode; // remember mode, so it can be restored after loading sequence is complete
+ keepInfo = 1;
+ if(oldMode != BeginningOfGame) EditGameEvent();
+ keepInfo = 0;
UnloadEngine(cps);
appData.noChessProgram = FALSE;
appData.clockMode = TRUE;
InitBackEnd2 ()
{
if (appData.debugMode) {
- fprintf(debugFP, "%s\n", programVersion);
+# ifdef __GIT_VERSION
+ fprintf(debugFP, "Version: %s (%s)\n", programVersion, __GIT_VERSION);
+# else
+ fprintf(debugFP, "Version: %s\n", programVersion);
+# endif
}
ASSIGN(currentDebugFile, appData.nameOfDebugFile); // [HGM] debug split: remember initial name in use
}
}
if (appData.debugMode) {
- fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),
+ fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
e, wnum, VariantName(v));
}
return v;
gameInfo.whiteRating = string_to_rating(star_match[1]);
gameInfo.blackRating = string_to_rating(star_match[3]);
if (appData.debugMode)
- fprintf(debugFP, _("Ratings from header: W %d, B %d\n"),
+ fprintf(debugFP, "Ratings from header: W %d, B %d\n",
gameInfo.whiteRating, gameInfo.blackRating);
}
continue;
void
ParseBoard12 (char *string)
{
+#if ZIPPY
+ int i, takeback;
+ char *bookHit = NULL; // [HGM] book
+#endif
GameMode newGameMode;
- int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
- int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
+ int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
+ int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
char to_play, board_chars[200];
char move_str[MSG_SIZ], str[MSG_SIZ], elapsed_time[MSG_SIZ];
int fromX, fromY, toX, toY;
char promoChar;
int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
- char *bookHit = NULL; // [HGM] book
Boolean weird = FALSE, reqFlag = FALSE;
fromX = fromY = toX = toY = -1;
newGame = FALSE;
if (appData.debugMode)
- fprintf(debugFP, _("Parsing board: %s\n"), string);
+ fprintf(debugFP, "Parsing board: %s\n", string);
move_str[0] = NULLCHAR;
elapsed_time[0] = NULLCHAR;
partnerUp = 0; flipView = !flipView; } // [HGM] dual
snprintf(partnerStatus, MSG_SIZ,"W: %d:%02d B: %d:%02d (%d-%d) %c", white_time*fac/60000, (white_time*fac%60000)/1000,
(black_time*fac/60000), (black_time*fac%60000)/1000, white_stren, black_stren, to_play);
- DisplayMessage(partnerStatus, "");
+ if(!twoBoards) DisplayMessage(partnerStatus, "");
partnerBoardValid = TRUE;
return;
}
to canonical algebraic form. */
if (moveNum > 0) {
if (appData.debugMode) {
- if (appData.debugMode) { int f = forwardMostMove;
- fprintf(debugFP, "parseboard %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]);
- }
+ int f = forwardMostMove;
+ fprintf(debugFP, "parseboard %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]);
fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);
fprintf(debugFP, "moveNum = %d\n", moveNum);
fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);
return -1;
}
+Boolean extendGame; // signals to UnLoadPV() if walked part of PV has to be appended to game
+
Boolean
LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
{
}
ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
*start = startPV; *end = index-1;
+ extendGame = (gameMode == AnalyzeMode && appData.autoExtend);
return TRUE;
}
int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
lastX = x; lastY = y;
ParsePV(lastPV[which], FALSE, TRUE); // load the PV of the thinking engine in the boards array.
+ extendGame = FALSE;
return TRUE;
}
if(endPV < 0) return;
if(appData.autoCopyPV) CopyFENToClipboard();
endPV = -1;
- if(gameMode == AnalyzeMode && currentMove > forwardMostMove) {
+ if(extendGame && currentMove > forwardMostMove) {
Boolean saveAnimate = appData.animate;
if(pushed) {
if(shiftKey && storedGames < MAX_VARIATIONS-2) { // wants to start variation, and there is space
boards[forwardMostMove][EP_STATUS] = nrW == nrB ? EP_STALEMATE :
((nrW < nrB) != WhiteOnMove(forwardMostMove) ?
EP_CHECKMATE : EP_WINS);
- else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)
+ else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShogi)
boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses
}
break;
case MT_CHECKMATE:
reason = "Xboard adjudication: Checkmate";
boards[forwardMostMove][EP_STATUS] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);
+ if(gameInfo.variant == VariantShogi) {
+ if(forwardMostMove > backwardMostMove
+ && moveList[forwardMostMove-1][1] == '@'
+ && CharToPiece(ToUpper(moveList[forwardMostMove-1][0])) == WhitePawn) {
+ reason = "XBoard adjudication: pawn-drop mate";
+ boards[forwardMostMove][EP_STATUS] = EP_WINS;
+ }
+ }
break;
}
/* adjudicate after user-specified nr of repeats */
int result = GameIsDrawn;
char *details = "XBoard adjudication: repetition draw";
- if(gameInfo.variant == VariantXiangqi && appData.testLegality) {
+ if((gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShogi) && appData.testLegality) {
// [HGM] xiangqi: check for forbidden perpetuals
int m, ourPerpetual = 1, hisPerpetual = 1;
for(m=forwardMostMove; m>k; m-=2) {
if(hisPerpetual && !ourPerpetual) { // he is checking us, but did not repeat yet
break; // (or we would have caught him before). Abort repetition-checking loop.
} else
+ if(gameInfo.variant == VariantShogi) { // in Shogi other repetitions are draws
+ if(BOARD_HEIGHT == 5 && BOARD_RGHT - BOARD_LEFT == 5) { // but in mini-Shogi gote wins!
+ result = BlackWins;
+ details = "Xboard adjudication: repetition";
+ }
+ } else // it must be XQ
// Now check for perpetual chases
if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase
hisPerpetual = PerpetualChase(k, forwardMostMove);
/* Machine move could not be parsed; ignore it. */
snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
machineMove, _(cps->which));
- DisplayError(buf1, 0);
+ DisplayMoveError(buf1);
snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);
if (gameMode == TwoMachinesPlay) {
return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
}
- if ((!appData.testLegality || gameInfo.variant == VariantFairy) &&
- !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
+ if (!strncmp(message, "setup ", 6) &&
+ (!appData.testLegality || gameInfo.variant == VariantFairy || NonStandardBoardSize())
+ ) { // [HGM] allow first engine to define opening position
int dummy, s=6; char buf[MSG_SIZ];
if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11);
SendToICS(buf1);
}
- }
+ } else if(appData.autoComment) AppendComment (forwardMostMove, message + 11, 1); // in local mode, add as move comment
return;
}
if (!strncmp(message, "tellall ", 8)) {
) {
/* white pawn promotion */
board[toY][toX] = CharToPiece(ToUpper(promoChar));
- if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
- && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
+ if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
} else if ((fromY >= BOARD_HEIGHT>>1)
+ && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
) {
/* black pawn promotion */
board[toY][toX] = CharToPiece(ToLower(promoChar));
- if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
- && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
+ if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
} else if ((fromY < BOARD_HEIGHT>>1)
+ && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
}
}
+static int
+NonStandardBoardSize ()
+{
+ /* [HGM] Awkward testing. Should really be a table */
+ int overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
+ if( gameInfo.variant == VariantXiangqi )
+ overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0;
+ if( gameInfo.variant == VariantShogi )
+ overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;
+ if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )
+ overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;
+ if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom ||
+ gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon || gameInfo.variant == VariantJanus )
+ overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
+ if( gameInfo.variant == VariantCourier )
+ overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
+ if( gameInfo.variant == VariantSuper )
+ overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
+ if( gameInfo.variant == VariantGreat )
+ overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
+ if( gameInfo.variant == VariantSChess )
+ overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7;
+ if( gameInfo.variant == VariantGrand )
+ overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7;
+ return overruled;
+}
+
void
InitChessProgram (ChessProgramState *cps, int setup)
/* setup needed to setup FRC opening position */
{
- char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;
+ char buf[MSG_SIZ], b[MSG_SIZ];
if (appData.noChessProgram) return;
hintRequested = FALSE;
bookRequested = FALSE;
return;
}
- /* [HGM] make prefix for non-standard board size. Awkward testing... */
- overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
- if( gameInfo.variant == VariantXiangqi )
- overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0;
- if( gameInfo.variant == VariantShogi )
- overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;
- if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )
- overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;
- if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom ||
- gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon || gameInfo.variant == VariantJanus )
- overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
- if( gameInfo.variant == VariantCourier )
- overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
- if( gameInfo.variant == VariantSuper )
- overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
- if( gameInfo.variant == VariantGreat )
- overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
- if( gameInfo.variant == VariantSChess )
- overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7;
- if( gameInfo.variant == VariantGrand )
- overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7;
-
- if(overruled) {
+ if(NonStandardBoardSize()) { /* [HGM] make prefix for non-standard board size. */
snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight,
gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name
/* [HGM] varsize: try first if this defiant size variant is specifically known */
void
+ResendOptions (ChessProgramState *cps)
+{ // send the stored value of the options
+ int i;
+ char buf[MSG_SIZ];
+ Option *opt = cps->option;
+ for(i=0; i<cps->nrOptions; i++, opt++) {
+ switch(opt->type) {
+ case Spin:
+ case Slider:
+ case CheckBox:
+ snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value);
+ break;
+ case ComboBox:
+ snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]);
+ break;
+ default:
+ snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue);
+ break;
+ case Button:
+ case SaveButton:
+ continue;
+ }
+ SendToProgram(buf, cps);
+ }
+}
+
+void
StartChessProgram (ChessProgramState *cps)
{
char buf[MSG_SIZ];
cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
if (cps->protocolVersion > 1) {
snprintf(buf, MSG_SIZ, "xboard\nprotover %d\n", cps->protocolVersion);
- cps->nrOptions = 0; // [HGM] options: clear all engine-specific options
- cps->comboCnt = 0; // and values of combo boxes
+ if(!cps->reload) { // do not clear options when reloading because of -xreuse
+ cps->nrOptions = 0; // [HGM] options: clear all engine-specific options
+ cps->comboCnt = 0; // and values of combo boxes
+ }
SendToProgram(buf, cps);
+ if(cps->reload) ResendOptions(cps);
} else {
SendToProgram("xboard\n", cps);
}
return;
}
DisplayMessage("", ""); curMess = 0;
- ThawUI();
TwoMachinesEvent();
}
ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
appData.firstHasOwnBookUCI = !appData.defNoBook; appData.protocolVersion[0] = PROTOVER;
ParseArgsFromString(buf);
+ } else { // no engine with this nickname is installed!
+ snprintf(buf, MSG_SIZ, _("No engine %s is installed"), engineName);
+ ReserveGame(nextGame, ' '); // unreserve game and drop out of match mode with error
+ matchMode = FALSE; appData.matchGames = matchGame = roundNr = 0;
+ ModeHighlight();
+ DisplayError(buf, 0);
+ return 0;
}
free(engineName);
return i;
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;
+ int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers, OK = 1;
FILE *tf;
if(appData.tourneyFile[0] == NULLCHAR) return 1; // no tourney, always allow next game
tf = fopen(appData.tourneyFile, "r");
// redefine engines, engine dir, etc.
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
+ if(!SetPlayer(whitePlayer, appData.participants)) OK = 0; // 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, appData.participants); // find black player amongst it, and parse its engine line
+ if(!SetPlayer(blackPlayer, appData.participants)) OK = 0; // find black player amongst it, and parse its engine line
SwapEngines(1); // and make that valid for second engine by swapping
InitEngine(&second, 1);
}
CommonEngineInit(); // after this TwoMachinesEvent will create correct engine processes
UpdateLogos(FALSE); // leave display to ModeHiglight()
- return 1;
+ return OK;
}
void
else
SaveGameToFile(appData.saveGameFile, TRUE);
} else if (appData.autoSaveGames) {
- AutoSaveGame();
+ if(gameMode != IcsObserving || !appData.onlyOwn) AutoSaveGame();
}
if (*appData.savePositionFile != NULLCHAR) {
SavePositionToFile(appData.savePositionFile);
SendToProgram("quit\n", &first);
DoSleep( appData.delayAfterQuit );
DestroyChildProcess(first.pr, first.useSigterm);
+ first.reload = TRUE;
}
first.pr = NoProc;
}
SendToProgram("quit\n", &second);
DoSleep( appData.delayAfterQuit );
DestroyChildProcess(second.pr, second.useSigterm);
+ second.reload = TRUE;
}
second.pr = NoProc;
}
- if (matchMode && (gameMode == TwoMachinesPlay || waitingForGame && exiting)) {
+ if (matchMode && (gameMode == TwoMachinesPlay || (waitingForGame || startingEngine) && exiting)) {
char resChar = '=';
switch (result) {
case WhiteWins:
break;
}
- if(waitingForGame) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
+ if(exiting) 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 (appData.noChessProgram) return 1;
- if(matchMode && appData.tourneyFile[0]) { // [HGM] tourney: make sure we get features after engine replacement. (Should we always do this?)
- if(WaitForEngine(&first, TwoMachinesEventIfReady)) { doInit = 1; return 0; } // request to do init on next visit
+ if(matchMode /*&& appData.tourneyFile[0]*/) { // [HGM] tourney: make sure we get features after engine replacement. (Should we always do this?)
+ if(WaitForEngine(&first, TwoMachinesEventIfReady)) { doInit = 1; return 0; } // request to do init on next visit, because we started engine
if(!doInit) return 1; // this replaces testing first.pr != NoProc, which is true when we get here, but first time no reason to abort
doInit = 0; // we fell through (first time after starting the engine); make sure it doesn't happen again
} else {
/* secure check */
if (appData.icsEngineAnalyze) {
if (appData.debugMode)
- fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
+ fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
ExitAnalyzeMode();
ModeHighlight();
}
}
appData.icsEngineAnalyze = TRUE;
if (appData.debugMode)
- fprintf(debugFP, _("ICS engine analyze starting... \n"));
+ fprintf(debugFP, "ICS engine analyze starting... \n");
}
if (gameMode == AnalyzeMode) { ToggleSecond(); return 0; }
StartChessProgram(cps);
if (cps->protocolVersion == 1) {
retry();
+ ScheduleDelayedEvent(retry, 1); // Do this also through timeout to avoid recursive calling of 'retry'
} else {
/* kludge: allow timeout for initial "feature" command */
- FreezeUI();
+ if(retry != TwoMachinesEventIfReady) FreezeUI();
snprintf(buf, MSG_SIZ, _("Starting %s chess program"), _(cps->which));
DisplayMessage("", buf);
ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
// forwardMostMove = currentMove;
TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
+ startingEngine = TRUE;
if(!ResurrectChessProgram()) return; /* in case first program isn't running (unbalances its ping due to InitChessProgram!) */
- if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
+ if(!first.initDone && GetDelayedEvent() == TwoMachinesEventIfReady) return; // [HGM] engine #1 still waiting for feature timeout
if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
return;
}
+ if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) {
+ startingEngine = FALSE;
DisplayError("second engine does not play this", 0);
return;
}
}
gameMode = TwoMachinesPlay;
- pausing = FALSE;
+ pausing = startingEngine = FALSE;
ModeHighlight(); // [HGM] logo: this triggers display update of logos
SetGameInfo();
DisplayTwoMachinesTitle();
}
int
-StringFeature (char **p, char *name, char loc[], ChessProgramState *cps)
+StringFeature (char **p, char *name, char **loc, ChessProgramState *cps)
{
char buf[MSG_SIZ];
int len = strlen(name);
if (strncmp((*p), name, len) == 0
&& (*p)[len] == '=' && (*p)[len+1] == '\"') {
(*p) += len + 2;
- sscanf(*p, "%[^\"]", loc);
+ ASSIGN(*loc, *p); // kludge alert: assign rest of line just to be sure allocation is large enough so that sscanf below always fits
+ sscanf(*p, "%[^\"]", *loc);
while (**p && **p != '\"') (*p)++;
if (**p == '\"') (*p)++;
snprintf(buf, MSG_SIZ, "accepted %s\n", name);
ScheduleDelayedEvent(cb, val ? 1 : 3600000);
}
cps->initDone = val;
+ if(val) cps->reload = FALSE;
}
/* Parse feature command from engine */
ParseFeatures (char *args, ChessProgramState *cps)
{
char *p = args;
- char *q;
+ char *q = NULL;
int val;
char buf[MSG_SIZ];
continue;
}
if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
- if (StringFeature(&p, "myname", cps->tidy, cps)) {
+ if (StringFeature(&p, "myname", &cps->tidy, cps)) {
if (gameMode == TwoMachinesPlay) {
DisplayTwoMachinesTitle();
} else {
}
continue;
}
- if (StringFeature(&p, "variants", cps->variants, cps)) continue;
+ if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;
if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;
if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
- if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue;
- if (StringFeature(&p, "option", buf, cps)) {
+ if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
+ if (StringFeature(&p, "option", &q, cps)) { // read to freshly allocated temp buffer first
+ if(cps->reload) { FREE(q); q = NULL; continue; } // we are reloading because of xreuse
FREE(cps->option[cps->nrOptions].name);
- cps->option[cps->nrOptions].name = malloc(MSG_SIZ);
- safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ);
+ cps->option[cps->nrOptions].name = q; q = NULL;
if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name);
SendToProgram(buf, cps);