* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
# include "zippy.h"
#endif
#include "backendz.h"
+#include "evalgraph.h"
#include "gettext.h"
#ifdef ENABLE_NLS
FILE *WriteTourneyFile P((char *results, FILE *f));
void DisplayTwoMachinesTitle P(());
static void ExcludeClick P((int index));
+void ToggleSecond P((void));
+void PauseEngine P((ChessProgramState *cps));
#ifdef WIN32
extern void ConsoleCreate();
ChessSquare pieceSweep = EmptySquare;
ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
int promoDefaultAltered;
+int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
/* States for ics_getting_history */
#define H_FALSE 0
/* end premove variables */
char *ics_prefix = "$";
-int ics_type = ICS_GENERIC;
+enum ICS_TYPE ics_type = ICS_GENERIC;
int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
int pauseExamForwardMostMove = 0;
{
int outError, outCount;
static int gotEof = 0;
+ static FILE *ini;
/* Pass data read from player on to ICS */
if (count > 0) {
if (outCount < count) {
DisplayFatalError(_("Error writing to ICS"), outError, 1);
}
+ if(have_sent_ICS_logon == 2) {
+ if(ini = fopen(appData.icsLogon, "w")) { // save first two lines (presumably username & password) on init script file
+ fprintf(ini, "%s", message);
+ have_sent_ICS_logon = 3;
+ } else
+ have_sent_ICS_logon = 1;
+ } else if(have_sent_ICS_logon == 3) {
+ fprintf(ini, "%s", message);
+ fclose(ini);
+ have_sent_ICS_logon = 1;
+ }
} else if (count < 0) {
RemoveInputSource(isr);
DisplayFatalError(_("Error reading from keyboard"), error, 1);
}
void
+PlotSingleSeekAd (int i)
+{
+ PlotSeekAd(i);
+}
+
+void
AddAd (char *handle, char *rating, int base, int inc, char rated, char *type, int nr, Boolean plot)
{
char buf[MSG_SIZ], *ext = "";
seekNrList[nrOfSeekAds] = nr;
zList[nrOfSeekAds] = 0;
seekAdList[nrOfSeekAds++] = StrSave(buf);
- if(plot) PlotSeekAd(nrOfSeekAds-1);
+ if(plot) PlotSingleSeekAd(nrOfSeekAds-1);
}
}
for(i=0; i<4000; i+= 100) if(i>=minRating && i<maxRating) {
int yy =((double)i - minRating)/(maxRating - minRating)*(h-vMargin-squareSize/8-1) + vMargin;
yy = h-1-yy;
- DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
+ DrawSeekAxis(hMargin-5, yy, hMargin+5*(i%500==0), yy); // rating ticks
if(i%500 == 0) {
char buf[MSG_SIZ];
snprintf(buf, MSG_SIZ, "%d", i);
continue;
}
- if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
- ICSInitScript();
- have_sent_ICS_logon = 1;
+ if (looking_at(buf, &i, "login:")) {
+ if (!have_sent_ICS_logon) {
+ if(ICSInitScript())
+ have_sent_ICS_logon = 1;
+ else // no init script was found
+ have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // flag that we should capture username + password
+ } else { // we have sent (or created) the InitScript, but apparently the ICS rejected it
+ have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // request creation of a new script
+ }
continue;
}
}
void
+SendToBoth (char *msg)
+{ // to make it easy to keep two engines in step in dual analysis
+ SendToProgram(msg, &first);
+ if(second.analyzing) SendToProgram(msg, &second);
+}
+
+void
AnalysisPeriodicEvent (int force)
{
if (((programStats.ok_to_send == 0 || programStats.line_is_book)
return;
/* Send . command to Crafty to collect stats */
- SendToProgram(".\n", &first);
+ SendToBoth(".\n");
/* Don't send another until we get a response (this makes
us stop sending to old Crafty's which don't understand
for(i = backwardMostMove; i<last; i++) {
char buf[20];
snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%s\n", parseList[i]);
+ if((*buf == 'b' || *buf == 'B') && buf[1] == 'x') { // work-around for stupid FICS bug, which thinks bxc3 can be a Bishop move
+ int len = strlen(moveList[i]);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%s", moveList[i]); // use long algebraic
+ if(!isdigit(buf[len-2])) snprintf(buf+len-2, 20-len, "=%c\n", ToUpper(buf[len-2])); // promotion must have '=' in ICS format
+ }
SendToICS(buf);
}
SendToICS(ics_prefix);
Boolean valid;
int nr = 0;
- if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+ lastParseAttempt = pv; if(!*pv) return; // turns out we crash when we parse an empty PV
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && currentMove < forwardMostMove) {
PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
pushed = TRUE;
}
}
Boolean
-LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end)
+LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
{
int startPV, multi, lineStart, origIndex = index;
char *p, buf2[MSG_SIZ];
+ ChessProgramState *cps = (pane ? &second : &first);
if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
lastX = x; lastY = y;
do{ while(buf[index] && buf[index] != '\n') index++;
} while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line
buf[index] = 0;
- if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(&first)) >= 0) {
- int n = first.option[multi].value;
+ if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(cps)) >= 0) {
+ int n = cps->option[multi].value;
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;
+ if(cps->option[multi].value != n) SendToProgram(buf2, cps);
+ cps->option[multi].value = n;
*start = *end = 0;
return FALSE;
} else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked
static char buf[10*MSG_SIZ];
int i, k=0, savedEnd=endPV, saveFMM = forwardMostMove;
*buf = NULLCHAR;
- if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
+ if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV); // shelve PV of PV-walk
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]);
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(pushed) { PopInner(0); pushed = FALSE; } // restore game continuation shelved by ParsePV
if(forwardMostMove < savedEnd) { PopInner(0); forwardMostMove = saveFMM; } // PopInner would set fmm to endPV!
endPV = savedEnd;
return buf;
// inform engine
snprintf(buf, MSG_SIZ, "%sclude ", state == '+' ? "in" : "ex");
CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, buf+8);
- SendToProgram(buf, &first);
+ SendToBoth(buf);
return (state == '+');
}
if(index < 13) { // none: include all
WriteMap(0); // clear map
for(i=0; i<exCnt; i++) exclusionHeader[excluTab[i].mark] = '+'; // and moves
- SendToProgram("include all\n", &first); // and inform engine
+ SendToBoth("include all\n"); // and inform engine
} else if(index > 18) { // tail
if(exclusionHeader[19] == '-') { // tail was excluded
- SendToProgram("include all\n", &first);
+ SendToBoth("include all\n");
WriteMap(0); // clear map completely
// now re-exclude selected moves
for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '-')
ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '-');
} else { // tail was included or in mixed state
- SendToProgram("exclude all\n", &first);
+ SendToBoth("exclude all\n");
WriteMap(0xFF); // fill map completely
// now re-include selected moves
j = 0; // count them
}
}
- if(doubleClick) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
+ if(doubleClick && gameMode == AnalyzeMode) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
ClearPremoveHighlights(); // was included
else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated by premove highlights
gameMode == MachinePlaysBlack)) {
SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
}
- if (gameMode != EditGame && gameMode != PlayFromGameFile) {
+ if (gameMode != EditGame && gameMode != PlayFromGameFile && gameMode != AnalyzeMode) {
// [HGM] book: if program might be playing, let it use book
bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE);
first.maybeThinking = TRUE;
} else if(fromY == DROP_RANK && fromX == EmptySquare) {
if(!first.useSetboard) SendToProgram("undo\n", &first); // kludge to change stm in engines that do not support setboard
SendBoard(&first, currentMove+1);
- } else SendMoveToProgram(forwardMostMove-1, &first);
+ if(second.analyzing) {
+ if(!second.useSetboard) SendToProgram("undo\n", &second);
+ SendBoard(&second, currentMove+1);
+ }
+ } else {
+ SendMoveToProgram(forwardMostMove-1, &first);
+ if(second.analyzing) SendMoveToProgram(forwardMostMove-1, &second);
+ }
if (currentMove == cmailOldMove + 1) {
cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
}
return;
}
doubleClick = FALSE;
+ if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
+ doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
+ }
fromX = x; fromY = y; toX = toY = -1;
if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
// even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
ClearHighlights();
}
} else {
+#if 0
+// [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square
/* Finish drag move */
if (appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
} else {
ClearHighlights();
}
+#endif
DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
}
- MarkTargetSquares(1);
// moves into holding are invalid for now (except in EditPosition, adapting to-square)
if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
}
ClearHighlights();
fromX = fromY = -1;
+ MarkTargetSquares(1);
DrawPosition(TRUE, boards[currentMove]);
return;
}
if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
SetHighlights(fromX, fromY, toX, toY);
+ MarkTargetSquares(1);
if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
// [HGM] super: promotion to captured piece selected from holdings
ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed
Explode(boards[currentMove-1], fromX, fromY, toX, toY))
DrawPosition(TRUE, boards[currentMove]);
+ MarkTargetSquares(1);
fromX = fromY = -1;
}
appData.animate = saveAnimate;
|| majors + (12*pCnt[BlackFerz-side] | 6*pCnt[BlackAlfil-side]) > 16; // KCKAA, KCKAX, KCKEEX, KCKEXX (XX!=HH), KCKXXX
// TO DO: cases wih an unpromoted f-Pawn acting as platform for an opponent Cannon
+ } else if(v == VariantKnightmate) {
+ if(nMine == 1) return FALSE;
+ if(nMine == 2 && nHis == 1 && pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side] + pCnt[WhiteKnight+side]) return FALSE; // KBK is only draw
} else if(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings
int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side];
// In any case it determnes if the game is a claimable draw (filling in EP_STATUS).
// Actually ending the game is now based on the additional internal condition canAdjudicate.
// Only when the game is ended, and the opponent is a computer, this opponent gets the move relayed.
- int k, count = 0; static int bare = 1;
+ int k, drop, count = 0; static int bare = 1;
ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first));
Boolean canAdjudicate = !appData.icsActive;
/* Check for rep-draws */
count = 0;
+ drop = gameInfo.holdingsSize && (gameInfo.variant != VariantSuper && gameInfo.variant != VariantSChess
+ && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand);
for(k = forwardMostMove-2;
- k>=backwardMostMove && k>=forwardMostMove-100 &&
+ k>=backwardMostMove && k>=forwardMostMove-100 && (drop ||
(signed char)boards[k][EP_STATUS] < EP_UNKNOWN &&
- (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE;
+ (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE);
k-=2)
{ int rights=0;
if(CompareBoards(boards[k], boards[forwardMostMove])) {
}
static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+static ChessProgramState *stalledEngine;
+static char stashedInputMove[MSG_SIZ];
void
HandleMachineMove (char *message, ChessProgramState *cps)
if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
(sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
{
+ if(pausing && !cps->pause) { // for pausing engine that does not support 'pause', we stash its move for processing when we resume.
+ if(appData.debugMode) fprintf(debugFP, "pause %s engine after move\n", cps->which);
+ safeStrCpy(stashedInputMove, message, MSG_SIZ);
+ stalledEngine = cps;
+ if(appData.ponderNextMove) { // bring opponent out of ponder
+ if(gameMode == TwoMachinesPlay) {
+ if(cps->other->pause)
+ PauseEngine(cps->other);
+ else
+ SendToProgram("easy\n", cps->other);
+ }
+ }
+ StopClocks();
+ return;
+ }
+
/* This method is only useful on engines that support ping */
if (cps->lastPing != cps->lastPong) {
if (gameMode == BeginningOfGame) {
) {
/* white pawn promotion */
board[toY][toX] = CharToPiece(ToUpper(promoChar));
- if(gameInfo.variant==VariantBughouse ||
- gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+ if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+ && 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)
) {
/* black pawn promotion */
board[toY][toX] = CharToPiece(ToLower(promoChar));
- if(gameInfo.variant==VariantBughouse ||
- gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+ if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+ && 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)
&& fromX != toX && fromY != toY)
fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);
// promotion suffix
- if(promoChar != NULLCHAR)
- fprintf(serverMoves, ":%c:%c%c", ToLower(promoChar), AAA+toX, ONE+toY);
+ if(promoChar != NULLCHAR) {
+ if(fromY == 0 || fromY == BOARD_HEIGHT-1)
+ fprintf(serverMoves, ":%c%c:%c%c", WhiteOnMove(forwardMostMove) ? 'w' : 'b',
+ ToLower(promoChar), AAA+fromX, ONE+fromY); // Seirawan gating
+ else 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",
else timeLeft = blackTimeRemaining/1000;
fprintf(serverMoves, "/%d", timeLeft);
strncpy(buf, parseList[forwardMostMove], MOVE_LEN*2);
+ if(p = strchr(buf, '/')) *p = NULLCHAR; else
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);
AnimateMove(boards[forwardMostMove - 1],
fromX, fromY, toX, toY);
}
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
}
currentMove = forwardMostMove;
}
if (instant) return;
DisplayMove(currentMove - 1);
+ if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
+ if (appData.highlightLastMove) { // [HGM] moved to after DrawPosition, as with arrow it could redraw old board
+ SetHighlights(fromX, fromY, toX, toY);
+ }
+ }
DrawPosition(FALSE, boards[currentMove]);
DisplayBothClocks();
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
}
int
+CheckPlayers (char *participants)
+{
+ int i;
+ char buf[MSG_SIZ], *p;
+ NamesToList(firstChessProgramNames, command, mnemonic, "all");
+ while(p = strchr(participants, '\n')) {
+ *p = NULLCHAR;
+ for(i=1; mnemonic[i]; i++) if(!strcmp(participants, mnemonic[i])) break;
+ if(!mnemonic[i]) {
+ snprintf(buf, MSG_SIZ, _("No engine %s is installed"), participants);
+ *p = '\n';
+ DisplayError(buf, 0);
+ return 1;
+ }
+ *p = '\n';
+ participants = p + 1;
+ }
+ return 0;
+}
+
+int
CreateTourney (char *name)
{
FILE *f;
DisplayError(_("Not enough participants"), 0);
return 0;
}
+ if(CheckPlayers(appData.participants)) return 0;
ASSIGN(appData.tourneyFile, name);
if(appData.tourneyType < 0) appData.defaultMatchGames = 1; // Swiss forces games/pairing = 1
if((f = WriteTourneyFile("", NULL)) == NULL) return 0;
}
int
+GetEngineLine (char *s, int n)
+{
+ int i;
+ char buf[MSG_SIZ];
+ extern char *icsNames;
+ if(!s || !*s) return 0;
+ NamesToList(n >= 10 ? icsNames : firstChessProgramNames, command, mnemonic, "all");
+ for(i=1; mnemonic[i]; i++) if(!strcmp(s, mnemonic[i])) break;
+ if(!mnemonic[i]) return 0;
+ if(n == 11) return 1; // just testing if there was a match
+ snprintf(buf, MSG_SIZ, "-%s %s", n == 10 ? "icshost" : "fcp", command[i]);
+ if(n == 1) SwapEngines(n);
+ ParseArgsFromString(buf);
+ if(n == 1) SwapEngines(n);
+ if(n == 0 && *appData.secondChessProgram == NULLCHAR) {
+ SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog)
+ ParseArgsFromString(buf);
+ }
+ return 1;
+}
+
+int
SetPlayer (int player, char *p)
{ // [HGM] find the engine line of the partcipant given by number, and parse its options.
int i;
fromX = fromY = -1; // [HGM] abort any move the user is entering.
+ if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
+
if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {
/* If we are playing on ICS, the server decides when the
game is over, but the engine can offer to draw, claim
if (*appData.savePositionFile != NULLCHAR) {
SavePositionToFile(appData.savePositionFile);
}
+ AddGameToBook(FALSE); // Only does something during Monte-Carlo book building
}
}
}
}
+void
+AnalyzeNextGame()
+{
+ ReloadGame(1); // next game
+}
int
AutoPlayOneMove ()
}
if (currentMove >= forwardMostMove) {
- if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); }
+ if(gameMode == AnalyzeFile) {
+ if(appData.loadGameIndex == -1) {
+ GameEnds(EndOfFile, NULL, GE_FILE);
+ ScheduleDelayedEvent(AnalyzeNextGame, 10);
+ } else {
+ ExitAnalyzeMode(); SendToProgram("force\n", &first);
+ }
+ }
// gameMode = EndOfGame;
// ModeHighlight();
}
GameInfo dummyInfo;
+static int creatingBook;
int
GameContainsPosition (FILE *f, ListGame *lg)
gn = 1;
}
else {
+ if(gameMode == AnalyzeFile && appData.loadGameIndex == -1)
+ appData.loadGameIndex = 0; // [HGM] suppress error message if we reach file end after auto-stepping analysis
+ else
DisplayError(_("Game number out of range"), 0);
return FALSE;
}
cm = (ChessMove) Myylex();
}
+ if(!creatingBook) {
if (first.pr == NoProc) {
StartChessProgram(&first);
}
}
DisplayBothClocks();
}
+ }
/* [HGM] server: flag to write setup moves in broadcast file as one */
loadFlag = appData.suppressLoadMoves;
if (oldGameMode == AnalyzeFile ||
oldGameMode == AnalyzeMode) {
+ appData.loadGameIndex = -1; // [HGM] order auto-stepping through games
AnalyzeFileEvent();
}
+ if(creatingBook) return TRUE;
if (!matchMode && pos > 0) {
ToNrEvent(pos); // [HGM] no autoplay if selected on position
} else
}
void
+PauseEngine (ChessProgramState *cps)
+{
+ SendToProgram("pause\n", cps);
+ cps->pause = 2;
+}
+
+void
+UnPauseEngine (ChessProgramState *cps)
+{
+ SendToProgram("resume\n", cps);
+ cps->pause = 1;
+}
+
+void
PauseEvent ()
{
if (appData.debugMode)
if (pausing) {
pausing = FALSE;
ModeHighlight();
+ if(stalledEngine) { // [HGM] pause: resume game by releasing withheld move
+ StartClocks();
+ if(gameMode == TwoMachinesPlay) { // we might have to make the opponent resume pondering
+ if(stalledEngine->other->pause == 2) UnPauseEngine(stalledEngine->other);
+ else if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine->other);
+ }
+ if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine);
+ HandleMachineMove(stashedInputMove, stalledEngine);
+ stalledEngine = NULL;
+ return;
+ }
if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack) {
+ gameMode == TwoMachinesPlay ||
+ gameMode == MachinePlaysBlack) { // the thinking engine must have used pause mode, or it would have been stalledEngine
+ if(first.pause) UnPauseEngine(&first);
+ else if(appData.ponderNextMove) SendToProgram("hard\n", &first);
+ if(second.pause) UnPauseEngine(&second);
+ else if(gameMode == TwoMachinesPlay && appData.ponderNextMove) SendToProgram("hard\n", &second);
StartClocks();
} else {
DisplayBothClocks();
Reset(FALSE, TRUE);
SendToICS(ics_prefix);
SendToICS("refresh\n");
- } else if (currentMove < forwardMostMove) {
+ } else if (currentMove < forwardMostMove && gameMode != AnalyzeMode) {
ForwardInner(forwardMostMove);
}
pauseExamInvalid = FALSE;
case TwoMachinesPlay:
if (forwardMostMove == 0)
return; /* don't pause if no one has moved */
- if ((gameMode == MachinePlaysWhite &&
- !WhiteOnMove(forwardMostMove)) ||
- (gameMode == MachinePlaysBlack &&
- WhiteOnMove(forwardMostMove))) {
+ if(gameMode == TwoMachinesPlay) { // [HGM] pause: stop clocks if engine can be paused immediately
+ ChessProgramState *onMove = (WhiteOnMove(forwardMostMove) == (first.twoMachinesColor[0] == 'w') ? &first : &second);
+ if(onMove->pause) { // thinking engine can be paused
+ PauseEngine(onMove); // do it
+ if(onMove->other->pause) // pondering opponent can always be paused immediately
+ PauseEngine(onMove->other);
+ else
+ SendToProgram("easy\n", onMove->other);
+ StopClocks();
+ } else if(appData.ponderNextMove) SendToProgram("easy\n", onMove); // pre-emptively bring out of ponder
+ } else if(gameMode == (WhiteOnMove(forwardMostMove) ? MachinePlaysWhite : MachinePlaysBlack)) { // engine on move
+ if(first.pause) {
+ PauseEngine(&first);
+ StopClocks();
+ } else if(appData.ponderNextMove) SendToProgram("easy\n", &first); // pre-emptively bring out of ponder
+ } else { // human on move, pause pondering by either method
+ if(first.pause)
+ PauseEngine(&first);
+ else if(appData.ponderNextMove)
+ SendToProgram("easy\n", &first);
StopClocks();
}
+ // if no immediate pausing is possible, wait for engine to move, and stop clocks then
+ case AnalyzeMode:
pausing = TRUE;
ModeHighlight();
break;
}
void
+ToggleSecond ()
+{
+ if(second.analyzing) {
+ SendToProgram("exit\n", &second);
+ second.analyzing = FALSE;
+ } else {
+ if (second.pr == NoProc) StartChessProgram(&second);
+ InitChessProgram(&second, FALSE);
+ FeedMovesToProgram(&second, currentMove);
+
+ SendToProgram("analyze\n", &second);
+ second.analyzing = TRUE;
+ }
+}
+
+/* Toggle ShowThinking */
+void
+ToggleShowThinking()
+{
+ appData.showThinking = !appData.showThinking;
+ ShowThinkingEvent();
+}
+
+int
AnalyzeModeEvent ()
{
+ char buf[MSG_SIZ];
+
+ if (!first.analysisSupport) {
+ snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
+ DisplayError(buf, 0);
+ return 0;
+ }
+ /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
+ if (appData.icsActive) {
+ if (gameMode != IcsObserving) {
+ snprintf(buf, MSG_SIZ, _("You are not observing a game"));
+ DisplayError(buf, 0);
+ /* secure check */
+ if (appData.icsEngineAnalyze) {
+ if (appData.debugMode)
+ fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
+ ExitAnalyzeMode();
+ ModeHighlight();
+ }
+ return 0;
+ }
+ /* if enable, user wants to disable icsEngineAnalyze */
+ if (appData.icsEngineAnalyze) {
+ ExitAnalyzeMode();
+ ModeHighlight();
+ return 0;
+ }
+ appData.icsEngineAnalyze = TRUE;
+ if (appData.debugMode)
+ fprintf(debugFP, _("ICS engine analyze starting... \n"));
+ }
+
+ if (gameMode == AnalyzeMode) { ToggleSecond(); return 0; }
if (appData.noChessProgram || gameMode == AnalyzeMode)
- return;
+ return 0;
if (gameMode != AnalyzeFile) {
if (!appData.icsEngineAnalyze) {
EditGameEvent();
- if (gameMode != EditGame) return;
+ if (gameMode != EditGame) return 0;
}
+ if (!appData.showThinking) ToggleShowThinking();
ResurrectChessProgram();
SendToProgram("analyze\n", &first);
first.analyzing = TRUE;
StartAnalysisClock();
GetTimeMark(&lastNodeCountTime);
lastNodeCount = 0;
+ return 1;
}
void
if (appData.noChessProgram || gameMode == AnalyzeFile)
return;
+ if (!first.analysisSupport) {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
+ DisplayError(buf, 0);
+ return;
+ }
+
if (gameMode != AnalyzeMode) {
+ keepInfo = 1; // mere annotating should not alter PGN tags
EditGameEvent();
+ keepInfo = 0;
if (gameMode != EditGame) return;
+ if (!appData.showThinking) ToggleShowThinking();
ResurrectChessProgram();
SendToProgram("analyze\n", &first);
first.analyzing = TRUE;
gameMode = AnalyzeFile;
pausing = FALSE;
ModeHighlight();
- SetGameInfo();
StartAnalysisClock();
GetTimeMark(&lastNodeCountTime);
lastNodeCount = 0;
if(appData.timeDelay > 0) StartLoadGameTimer((long)(1000.0f * appData.timeDelay));
+ AnalysisPeriodicEvent(1);
}
void
DisplayMessage("",_("Close ICS engine analyze..."));
}
if (first.analysisSupport && first.analyzing) {
- SendToProgram("exit\n", &first);
- first.analyzing = FALSE;
+ SendToBoth("exit\n");
+ first.analyzing = second.analyzing = FALSE;
}
thinkOutput[0] = NULLCHAR;
}
boards[0][CASTLING][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? BOARD_LEFT : NoRights;
boards[0][CASTLING][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : NoRights;
} else boards[0][CASTLING][5] = NoRights;
- if(gameInfo.variant = VariantSChess) {
+ if(gameInfo.variant == VariantSChess) {
int i;
for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // pieces in their original position are assumed virgin
boards[0][VIRGIN][i] = 0;
gameMode == Training || gameMode == PlayFromGameFile ||
gameMode == AnalyzeFile) {
while (currentMove < target) {
+ if(second.analyzing) SendMoveToProgram(currentMove, &second);
SendMoveToProgram(currentMove++, &first);
}
} else {
if(moveList[i-1][1] == '@' && moveList[i-1][0] == '@') break;
}
SendBoard(&first, i);
- for(currentMove=i; currentMove<target; currentMove++) SendMoveToProgram(currentMove, &first);
+ if(second.analyzing) SendBoard(&second, i);
+ for(currentMove=i; currentMove<target; currentMove++) {
+ SendMoveToProgram(currentMove, &first);
+ if(second.analyzing) SendMoveToProgram(currentMove, &second);
+ }
break;
}
- SendToProgram("undo\n", &first);
+ SendToBoth("undo\n");
currentMove--;
}
} else {
}
void
+CreateBookEvent ()
+{
+ ListGame * lg = (ListGame *) gameList.head;
+ FILE *f;
+ int nItem;
+ static int secondTime = FALSE;
+
+ if( !(f = GameFile()) || ((ListGame *) gameList.tailPred)->number <= 0 ) {
+ DisplayError(_("Game list not loaded or empty"), 0);
+ return;
+ }
+
+ if(!secondTime && (f = fopen(appData.polyglotBook, "r"))) {
+ fclose(f);
+ secondTime++;
+ DisplayNote(_("Book file exists! Try again for overwrite."));
+ return;
+ }
+
+ creatingBook = TRUE;
+ secondTime = FALSE;
+
+ /* Get list size */
+ for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){
+ LoadGame(f, nItem, "", TRUE);
+ AddGameToBook(TRUE);
+ lg = (ListGame *) lg->node.succ;
+ }
+
+ creatingBook = FALSE;
+ FlushBook();
+}
+
+void
BookEvent ()
{
if (appData.noChessProgram) return;
ChessMove r = GameUnfinished;
char *p = NULL;
+ if(keepInfo) return;
+
if(gameMode == EditGame) { // [HGM] vari: do not erase result on EditGame
r = gameInfo.result;
p = gameInfo.resultDetails;
if (BoolFeature(&p, "exclude", &cps->excludeMoves, cps)) continue;
if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
- if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
+ if (BoolFeature(&p, "pause", &cps->pause, cps)) continue; // [HGM] pause
if (IntFeature(&p, "done", &val, cps)) {
FeatureDone(cps, val);
continue;
boards[move][CASTLING][2] != NoRights ) k = 1, *p++ = 'K';
q = (boards[move][CASTLING][1] == BOARD_LEFT &&
boards[move][CASTLING][2] != NoRights );
- if(gameInfo.variant = VariantSChess) { // for S-Chess, indicate all vrgin backrank pieces
+ if(gameInfo.variant == VariantSChess) { // for S-Chess, indicate all vrgin backrank pieces
for(i=j=0; i<BOARD_HEIGHT; i++) j += boards[move][i][BOARD_RGHT]; // count white held pieces
for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q && j; i--)
if((boards[move][0][i] != WhiteKing || k+q == 0) &&
boards[move][CASTLING][5] != NoRights ) k = 1, *p++ = 'k';
q = (boards[move][CASTLING][4] == BOARD_LEFT &&
boards[move][CASTLING][5] != NoRights );
- if(gameInfo.variant = VariantSChess) {
+ if(gameInfo.variant == VariantSChess) {
for(i=j=0; i<BOARD_HEIGHT; i++) j += boards[move][i][BOARD_LEFT-1]; // count black held pieces
for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q && j; i--)
if((boards[move][BOARD_HEIGHT-1][i] != BlackKing || k+q == 0) &&
ToNrEvent(currentMove+1);
}
+void
+LoadTheme ()
+{
+ char *p, *q, buf[MSG_SIZ];
+ if(engineLine && engineLine[0]) { // a theme was selected from the listbox
+ snprintf(buf, MSG_SIZ, "-theme %s", engineLine);
+ ParseArgsFromString(buf);
+ ActivateTheme(TRUE); // also redo colors
+ return;
+ }
+ p = nickName;
+ if(*p && !strchr(p, '"')) // theme name specified and well-formed; add settings to theme list
+ {
+ int len;
+ q = appData.themeNames;
+ snprintf(buf, MSG_SIZ, "\"%s\"", nickName);
+ if(appData.useBitmaps) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt true -lbtf \"%s\" -dbtf \"%s\" -lbtm %d -dbtm %d",
+ appData.liteBackTextureFile, appData.darkBackTextureFile,
+ appData.liteBackTextureMode,
+ appData.darkBackTextureMode );
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt false -lsc %s -dsc %s",
+ Col2Text(2), // lightSquareColor
+ Col2Text(3) ); // darkSquareColor
+ }
+ if(appData.useBorder) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub true -border \"%s\"",
+ appData.border);
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub false");
+ }
+ if(appData.useFont) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf true -pf \"%s\" -fptc \"%s\" -fpfcw %s -fpbcb %s",
+ appData.renderPiecesWithFont,
+ appData.fontToPieceTable,
+ Col2Text(9), // appData.fontBackColorWhite
+ Col2Text(10) ); // appData.fontForeColorBlack
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf false -pid \"%s\"",
+ appData.pieceDirectory);
+ if(!appData.pieceDirectory[0])
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -wpc %s -bpc %s",
+ Col2Text(0), // whitePieceColor
+ Col2Text(1) ); // blackPieceColor
+ }
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -hsc %s -phc %s\n",
+ Col2Text(4), // highlightSquareColor
+ Col2Text(5) ); // premoveHighlightColor
+ appData.themeNames = malloc(len = strlen(q) + strlen(buf) + 1);
+ if(insert != q) insert[-1] = NULLCHAR;
+ snprintf(appData.themeNames, len, "%s\n%s%s", q, buf, insert);
+ if(q) free(q);
+ }
+ ActivateTheme(FALSE);
+}