* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
+int flock(int f, int code);
+#define LOCK_EX 2
+#define SLASH '\\'
+
#else
#define DoSleep( n ) if( (n) >= 0) sleep(n)
+#define SLASH '/'
#endif
# include "zippy.h"
#endif
#include "backendz.h"
-#include "gettext.h"
-
-#ifdef ENABLE_NLS
-# define _(s) gettext (s)
-# define N_(s) gettext_noop (s)
-#else
-# define _(s) (s)
-# define N_(s) s
-#endif
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+# define T_(s) gettext(s)
+#else
+# ifdef WIN32
+# define _(s) T_(s)
+# define N_(s) s
+# else
+# define _(s) (s)
+# define N_(s) s
+# define T_(s) s
+# endif
+#endif
/* A point in time */
void ics_printf P((char *format, ...));
void SendToICS P((char *s));
void SendToICSDelayed P((char *s, long msdelay));
-void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
- int toX, int toY));
+void SendMoveToICS P((ChessMove moveType, int fromX, int fromY, int toX, int toY, char promoChar));
void HandleMachineMove P((char *message, ChessProgramState *cps));
int AutoPlayOneMove P((void));
int LoadGameOneMove P((ChessMove readAhead));
char *TimeControlTagValue P((void));
void Attention P((ChessProgramState *cps));
void FeedMovesToProgram P((ChessProgramState *cps, int upto));
-void ResurrectChessProgram P((void));
+int ResurrectChessProgram P((void));
void DisplayComment P((int moveNumber, char *text));
void DisplayMove P((int moveNumber));
int PerpetualChase(int first, int last);
int EngineOutputIsUp();
void InitDrawingSizes(int x, int y);
+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));
+void DisplayTwoMachinesTitle P(());
#ifdef WIN32
extern void ConsoleCreate();
char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
void ics_update_width P((int new_width));
extern char installDir[MSG_SIZ];
+VariantClass startVariant; /* [HGM] nicks: initial variant */
+Boolean abortMatch;
extern int tinyLayout, smallLayout;
ChessProgramStats programStats;
extern int chatCount;
int chattingPartner;
char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
+char lastMsg[MSG_SIZ];
+ChessSquare pieceSweep = EmptySquare;
+ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
+int promoDefaultAltered;
/* States for ics_getting_history */
#define H_FALSE 0
#define TN_SGA 0003
#define TN_PORT 23
-/* [AS] */
-static char * safeStrCpy( char * dst, const char * src, size_t count )
-{
- assert( dst != NULL );
- assert( src != NULL );
- assert( count > 0 );
+char*
+safeStrCpy( char *dst, const char *src, size_t count )
+{ // [HGM] made safe
+ int i;
+ assert( dst != NULL );
+ assert( src != NULL );
+ assert( count > 0 );
- strncpy( dst, src, count );
- dst[ count-1 ] = '\0';
- return dst;
+ for(i=0; i<count; i++) if((dst[i] = src[i]) == NULLCHAR) break;
+ if( i == count && dst[count-1] != NULLCHAR)
+ {
+ 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);
+ }
+
+ return dst;
}
/* Some compiler can't cast u64 to double
case VariantKriegspiel:
flags |= F_KRIEGSPIEL_CAPTURE;
break;
- case VariantCapaRandom:
+ case VariantCapaRandom:
case VariantFischeRandom:
flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */
case VariantNoCastle:
case VariantShatranj:
case VariantCourier:
case VariantMakruk:
+ case VariantGrand:
flags &= ~F_ALL_CASTLE_OK;
break;
default:
FILE *gameFileFP, *debugFP;
-/*
+/*
[AS] Note: sometimes, the sscanf() function is used to parse the input
into a fixed-size buffer. Because of this, we must be prepared to
receive strings as long as the size of the input buffer, which is currently
char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
char thinkOutput1[MSG_SIZ*10];
-ChessProgramState first, second;
+ChessProgramState first, second, pairing;
/* premove variables */
int premoveToX = 0;
char white_holding[64], black_holding[64];
TimeMark lastNodeCountTime;
long lastNodeCount=0;
+int shiftKey; // [HGM] set by mouse handler
+
int have_sent_ICS_logon = 0;
int movesPerSession;
-long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
+int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */
+long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack;
long timeControl_2; /* [AS] Allow separate time controls */
-char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */
+char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC; /* [HGM] secondary TC: merge of MPS, TC and inc */
long timeRemaining[2][MAX_MOVES];
-int matchGame = 0;
-TimeMark programStartTime;
+int matchGame = 0, nextGame = 0, roundNr = 0;
+Boolean waitingForGame = FALSE;
+TimeMark programStartTime, pauseStart;
char ics_handle[MSG_SIZ];
int have_set_title = 0;
int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
int initialRulePlies, FENrulePlies;
FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
-int loadFlag = 0;
-int shuffleOpenings;
+int loadFlag = 0;
+Boolean shuffleOpenings;
int mute; // mute all sounds
// [HGM] vari: next 12 to save and restore variations
void PushTail P((int firstMove, int lastMove));
Boolean PopTail P((Boolean annotate));
+void PushInner P((int firstMove, int lastMove));
+void PopInner P((Boolean annotate));
void CleanupTail P((void));
ChessSquare FIDEArray[2][BOARD_FILES] = {
BlackUnicorn, BlackBishop, BlackMan, BlackRook }
};
+ChessSquare SpartanArray[2][BOARD_FILES] = {
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+ { BlackAlfil, BlackMarshall, BlackKing, BlackDragon,
+ BlackDragon, BlackKing, BlackAngel, BlackAlfil }
+};
+
ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
- { BlackLance, BlackAlfil, BlackMarshall, BlackAngel,
- BlackKing, BlackMarshall, BlackAlfil, BlackLance }
+ { BlackCardinal, BlackAlfil, BlackMarshall, BlackAngel,
+ BlackKing, BlackMarshall, BlackAlfil, BlackCardinal }
};
ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
};
ChessSquare CapablancaArray[2][BOARD_FILES] = {
- { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
+ { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
- { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
+ { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
};
ChessSquare GreatArray[2][BOARD_FILES] = {
- { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
+ { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
- { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
+ { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
};
ChessSquare JanusArray[2][BOARD_FILES] = {
- { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
+ { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
- { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
+ { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
};
+ChessSquare GrandArray[2][BOARD_FILES] = {
+ { EmptySquare, WhiteKnight, WhiteBishop, WhiteQueen, WhiteKing,
+ WhiteMarshall, WhiteAngel, WhiteBishop, WhiteKnight, EmptySquare },
+ { EmptySquare, BlackKnight, BlackBishop, BlackQueen, BlackKing,
+ BlackMarshall, BlackAngel, BlackBishop, BlackKnight, EmptySquare }
+};
+
#ifdef GOTHIC
ChessSquare GothicArray[2][BOARD_FILES] = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
- { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }
};
#else // !GOTHIC
#ifdef FALCON
ChessSquare FalconArray[2][BOARD_FILES] = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
- { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
+ { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
};
#else // !FALCON
}
void
-InitBackEnd1()
+CommonEngineInit()
+{ // [HGM] moved some code here from InitBackend1 that has to be done after both engines have contributed their settings
+ if (appData.firstPlaysBlack) {
+ first.twoMachinesColor = "black\n";
+ second.twoMachinesColor = "white\n";
+ } else {
+ first.twoMachinesColor = "white\n";
+ second.twoMachinesColor = "black\n";
+ }
+
+ first.other = &second;
+ second.other = &first;
+
+ { float norm = 1;
+ if(appData.timeOddsMode) {
+ norm = appData.timeOdds[0];
+ if(norm > appData.timeOdds[1]) norm = appData.timeOdds[1];
+ }
+ first.timeOdds = appData.timeOdds[0]/norm;
+ second.timeOdds = appData.timeOdds[1]/norm;
+ }
+
+ if(programVersion) free(programVersion);
+ if (appData.noChessProgram) {
+ programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
+ sprintf(programVersion, "%s", PACKAGE_STRING);
+ } else {
+ /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
+ programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+ sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+ }
+}
+
+void
+UnloadEngine(ChessProgramState *cps)
+{
+ /* Kill off first chess program */
+ if (cps->isr != NULL)
+ RemoveInputSource(cps->isr);
+ cps->isr = NULL;
+
+ if (cps->pr != NoProc) {
+ ExitAnalyzeMode();
+ DoSleep( appData.delayBeforeQuit );
+ SendToProgram("quit\n", cps);
+ DoSleep( appData.delayAfterQuit );
+ DestroyChildProcess(cps->pr, cps->useSigterm);
+ }
+ cps->pr = NoProc;
+ if(appData.debugMode) fprintf(debugFP, "Unload %s\n", cps->which);
+}
+
+void
+ClearOptions(ChessProgramState *cps)
+{
+ int i;
+ cps->nrOptions = cps->comboCnt = 0;
+ for(i=0; i<MAX_OPTIONS; i++) {
+ cps->option[i].min = cps->option[i].max = cps->option[i].value = 0;
+ cps->option[i].textValue = 0;
+ }
+}
+
+char *engineNames[] = {
+"first",
+"second"
+};
+
+void
+InitEngine(ChessProgramState *cps, int n)
+{ // [HGM] all engine initialiation put in a function that does one engine
+
+ ClearOptions(cps);
+
+ cps->which = engineNames[n];
+ cps->maybeThinking = FALSE;
+ cps->pr = NoProc;
+ cps->isr = NULL;
+ cps->sendTime = 2;
+ cps->sendDrawOffers = 1;
+
+ cps->program = appData.chessProgram[n];
+ cps->host = appData.host[n];
+ cps->dir = appData.directory[n];
+ cps->initString = appData.engInitString[n];
+ cps->computerString = appData.computerString[n];
+ cps->useSigint = TRUE;
+ cps->useSigterm = TRUE;
+ cps->reuse = appData.reuse[n];
+ cps->nps = appData.NPS[n]; // [HGM] nps: copy nodes per second
+ cps->useSetboard = FALSE;
+ cps->useSAN = FALSE;
+ cps->usePing = FALSE;
+ cps->lastPing = 0;
+ cps->lastPong = 0;
+ cps->usePlayother = FALSE;
+ cps->useColors = TRUE;
+ cps->useUsermove = FALSE;
+ cps->sendICS = FALSE;
+ cps->sendName = appData.icsActive;
+ cps->sdKludge = FALSE;
+ cps->stKludge = FALSE;
+ TidyProgramName(cps->program, cps->host, cps->tidy);
+ cps->matchWins = 0;
+ safeStrCpy(cps->variants, appData.variant, MSG_SIZ);
+ cps->analysisSupport = 2; /* detect */
+ cps->analyzing = FALSE;
+ cps->initDone = FALSE;
+
+ /* New features added by Tord: */
+ cps->useFEN960 = FALSE;
+ cps->useOOCastle = TRUE;
+ /* End of new features added by Tord. */
+ cps->fenOverride = appData.fenOverride[n];
+
+ /* [HGM] time odds: set factor for each machine */
+ cps->timeOdds = appData.timeOdds[n];
+
+ /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
+ cps->accumulateTC = appData.accumulateTC[n];
+ cps->maxNrOfSessions = 1;
+
+ /* [HGM] debug */
+ cps->debug = FALSE;
+
+ cps->supportsNPS = UNKNOWN;
+ cps->memSize = FALSE;
+ cps->maxCores = FALSE;
+ cps->egtFormats[0] = NULLCHAR;
+
+ /* [HGM] options */
+ cps->optionSettings = appData.engOptions[n];
+
+ cps->scoreIsAbsolute = appData.scoreIsAbsolute[n]; /* [AS] */
+ cps->isUCI = appData.isUCI[n]; /* [AS] */
+ cps->hasOwnBookUCI = appData.hasOwnBookUCI[n]; /* [AS] */
+
+ if (appData.protocolVersion[n] > PROTOVER
+ || appData.protocolVersion[n] < 1)
+ {
+ char buf[MSG_SIZ];
+ int len;
+
+ len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
+ appData.protocolVersion[n]);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
+ DisplayFatalError(buf, 0, 2);
+ }
+ else
+ {
+ cps->protocolVersion = appData.protocolVersion[n];
+ }
+
+ InitEngineUCI( installDir, cps ); // [HGM] moved here from winboard.c, to make available in xboard
+}
+
+ChessProgramState *savCps;
+
+void
+LoadEngine()
+{
+ int i;
+ if(WaitForEngine(savCps, LoadEngine)) return;
+ CommonEngineInit(); // recalculate time odds
+ 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
+ }
+ InitChessProgram(savCps, FALSE);
+ SendToProgram("force\n", savCps);
+ DisplayMessage("", "");
+ if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove);
+ for (i = backwardMostMove; i < forwardMostMove; i++) SendMoveToProgram(i, savCps);
+ ThawUI();
+ SetGNUMode();
+}
+
+void
+ReplaceEngine(ChessProgramState *cps, int n)
+{
+ EditGameEvent();
+ UnloadEngine(cps);
+ 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();
+}
+
+extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
+extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
+
+static char resetOptions[] =
+ "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
+ "-firstOptions \"\" -firstNPS -1 -fn \"\"";
+
+void
+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
+ 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(buf);
+ SwapEngines(i);
+ ReplaceEngine(cps, i);
+ return;
+ }
+ p = engineName;
+ while(q = strchr(p, SLASH)) p = q+1;
+ if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; }
+ if(engineDir[0] != NULLCHAR)
+ appData.directory[i] = engineDir;
+ else if(p != engineName) { // derive directory from engine path, when not given
+ p[-1] = 0;
+ 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;
+ }
+ appData.chessProgram[i] = strdup(p);
+ appData.isUCI[i] = isUCI;
+ appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
+ appData.hasOwnBookUCI[i] = hasBook;
+ if(!nickName[0]) useNick = FALSE;
+ 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;
+ 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 ? "\"" : "",
+ v1 ? " -firstProtocolVersion 1" : "",
+ hasBook ? "" : " -fNoOwnBookUCI",
+ isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
+ storeVariant ? " -variant " : "",
+ storeVariant ? VariantName(gameInfo.variant) : "");
+ firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
+ snprintf(firstChessProgramNames, len, "%s%s", q, buf);
+ if(q) free(q);
+ }
+ ReplaceEngine(cps, i);
+}
+
+void
+InitTimeControls()
{
int matched, min, sec;
+ /*
+ * Parse timeControl resource
+ */
+ if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
+ appData.movesPerSession)) {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
+ DisplayFatalError(buf, 0, 2);
+ }
+
+ /*
+ * Parse searchTime resource
+ */
+ if (*appData.searchTime != NULLCHAR) {
+ matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
+ if (matched == 1) {
+ searchTime = min * 60;
+ } else if (matched == 2) {
+ searchTime = min * 60 + sec;
+ } else {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
+ DisplayFatalError(buf, 0, 2);
+ }
+ }
+}
+
+void
+InitBackEnd1()
+{
ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
+ startVariant = StringToVariant(appData.variant); // [HGM] nicks: remember original variant
GetTimeMark(&programStartTime);
srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
+ pauseStart = programStartTime; pauseStart.sec -= 100; // [HGM] matchpause: fake a pause that has long since ended
ClearProgramStats();
programStats.ok_to_send = 1;
if (appData.icsActive) {
appData.matchMode = FALSE;
appData.matchGames = 0;
-#if ZIPPY
+#if ZIPPY
appData.noChessProgram = !appData.zippyPlay;
#else
appData.zippyPlay = FALSE;
}
}
- /*
- * Parse timeControl resource
- */
- if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
- appData.movesPerSession)) {
- char buf[MSG_SIZ];
- snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
- DisplayFatalError(buf, 0, 2);
- }
-
- /*
- * Parse searchTime resource
- */
- if (*appData.searchTime != NULLCHAR) {
- matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
- if (matched == 1) {
- searchTime = min * 60;
- } else if (matched == 2) {
- searchTime = min * 60 + sec;
- } else {
- char buf[MSG_SIZ];
- snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
- DisplayFatalError(buf, 0, 2);
- }
- }
+ InitTimeControls();
/* [AS] Adjudication threshold */
adjudicateLossThreshold = appData.adjudicateLossThreshold;
-
- first.which = "first";
- second.which = "second";
- first.maybeThinking = second.maybeThinking = FALSE;
- first.pr = second.pr = NoProc;
- first.isr = second.isr = NULL;
- first.sendTime = second.sendTime = 2;
- first.sendDrawOffers = 1;
- if (appData.firstPlaysBlack) {
- first.twoMachinesColor = "black\n";
- second.twoMachinesColor = "white\n";
- } else {
- first.twoMachinesColor = "white\n";
- second.twoMachinesColor = "black\n";
- }
- first.program = appData.firstChessProgram;
- second.program = appData.secondChessProgram;
- first.host = appData.firstHost;
- second.host = appData.secondHost;
- first.dir = appData.firstDirectory;
- second.dir = appData.secondDirectory;
- first.other = &second;
- second.other = &first;
- first.initString = appData.initString;
- second.initString = appData.secondInitString;
- first.computerString = appData.firstComputerString;
- second.computerString = appData.secondComputerString;
- first.useSigint = second.useSigint = TRUE;
- first.useSigterm = second.useSigterm = TRUE;
- first.reuse = appData.reuseFirst;
- second.reuse = appData.reuseSecond;
- first.nps = appData.firstNPS; // [HGM] nps: copy nodes per second
- second.nps = appData.secondNPS;
- first.useSetboard = second.useSetboard = FALSE;
- first.useSAN = second.useSAN = FALSE;
- first.usePing = second.usePing = FALSE;
- first.lastPing = second.lastPing = 0;
- first.lastPong = second.lastPong = 0;
- first.usePlayother = second.usePlayother = FALSE;
- first.useColors = second.useColors = TRUE;
- first.useUsermove = second.useUsermove = FALSE;
- first.sendICS = second.sendICS = FALSE;
- first.sendName = second.sendName = appData.icsActive;
- first.sdKludge = second.sdKludge = FALSE;
- first.stKludge = second.stKludge = FALSE;
- TidyProgramName(first.program, first.host, first.tidy);
- TidyProgramName(second.program, second.host, second.tidy);
- first.matchWins = second.matchWins = 0;
- strcpy(first.variants, appData.variant);
- strcpy(second.variants, appData.variant);
- first.analysisSupport = second.analysisSupport = 2; /* detect */
- first.analyzing = second.analyzing = FALSE;
- first.initDone = second.initDone = FALSE;
- /* New features added by Tord: */
- first.useFEN960 = FALSE; second.useFEN960 = FALSE;
- first.useOOCastle = TRUE; second.useOOCastle = TRUE;
- /* End of new features added by Tord. */
- first.fenOverride = appData.fenOverride1;
- second.fenOverride = appData.fenOverride2;
+ InitEngine(&first, 0);
+ InitEngine(&second, 1);
+ CommonEngineInit();
- /* [HGM] time odds: set factor for each machine */
- first.timeOdds = appData.firstTimeOdds;
- second.timeOdds = appData.secondTimeOdds;
- { float norm = 1;
- if(appData.timeOddsMode) {
- norm = first.timeOdds;
- if(norm > second.timeOdds) norm = second.timeOdds;
- }
- first.timeOdds /= norm;
- second.timeOdds /= norm;
- }
-
- /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
- first.accumulateTC = appData.firstAccumulateTC;
- second.accumulateTC = appData.secondAccumulateTC;
- first.maxNrOfSessions = second.maxNrOfSessions = 1;
-
- /* [HGM] debug */
- first.debug = second.debug = FALSE;
- first.supportsNPS = second.supportsNPS = UNKNOWN;
-
- /* [HGM] options */
- first.optionSettings = appData.firstOptions;
- second.optionSettings = appData.secondOptions;
-
- first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
- second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
- first.isUCI = appData.firstIsUCI; /* [AS] */
- second.isUCI = appData.secondIsUCI; /* [AS] */
- first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
- second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
-
- if (appData.firstProtocolVersion > PROTOVER ||
- appData.firstProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, _("protocol version %d not supported"),
- appData.firstProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- first.protocolVersion = appData.firstProtocolVersion;
- }
-
- if (appData.secondProtocolVersion > PROTOVER ||
- appData.secondProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, _("protocol version %d not supported"),
- appData.secondProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- second.protocolVersion = appData.secondProtocolVersion;
- }
+ pairing.which = "pairing"; // pairing engine
+ pairing.pr = NoProc;
+ pairing.isr = NULL;
+ pairing.program = appData.pairingEngine;
+ pairing.host = "localhost";
+ pairing.dir = ".";
if (appData.icsActive) {
appData.clockMode = TRUE; /* changes dynamically in ICS mode */
-// } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
} else if (appData.noChessProgram) { // [HGM] st: searchTime mode now also is clockMode
appData.clockMode = FALSE;
first.sendTime = second.sendTime = 0;
}
-
+
#if ZIPPY
/* Override some settings from environment variables, for backward
compatibility. Unfortunately it's not feasible to have the env
ZippyInit();
}
#endif
-
- if (appData.noChessProgram) {
- programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
- sprintf(programVersion, "%s", PACKAGE_STRING);
- } else {
- /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
- programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
- sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
- }
if (!appData.icsActive) {
char buf[MSG_SIZ];
+ int len;
+
/* Check for variants that are supported only in ICS mode,
or not at all. Some that are accepted here nevertheless
have bugs; see comments below.
switch (variant) {
case VariantBughouse: /* need four players and two boards */
case VariantKriegspiel: /* need to hide pieces and move details */
- /* case VariantFischeRandom: (Fabien: moved below) */
- sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);
+ /* case VariantFischeRandom: (Fabien: moved below) */
+ len = snprintf(buf,MSG_SIZ, _("Variant %s supported only in ICS mode"), appData.variant);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
DisplayFatalError(buf, 0, 2);
return;
case Variant35:
case Variant36:
default:
- sprintf(buf, _("Unknown variant name %s"), appData.variant);
+ len = snprintf(buf, MSG_SIZ, _("Unknown variant name %s"), appData.variant);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
DisplayFatalError(buf, 0, 2);
return;
case VariantGothic: /* [HGM] should work */
case VariantCapablanca: /* [HGM] should work */
case VariantCourier: /* [HGM] initial forced moves not implemented */
- case VariantShogi: /* [HGM] drops not tested for legality */
+ case VariantShogi: /* [HGM] could still mate with pawn drop */
case VariantKnightmate: /* [HGM] should work */
case VariantCylinder: /* [HGM] untested */
case VariantFalcon: /* [HGM] untested */
case VariantAtomic: /* should work except for win condition */
case Variant3Check: /* should work except for win condition */
case VariantShatranj: /* should work except for all win conditions */
- case VariantMakruk: /* should work except for daw countdown */
+ case VariantMakruk: /* should work except for draw countdown */
case VariantBerolina: /* might work if TestLegality is off */
case VariantCapaRandom: /* should work */
case VariantJanus: /* should work */
case VariantSuper: /* experimental */
case VariantGreat: /* experimental, requires legality testing to be off */
+ case VariantSChess: /* S-Chess, should work */
+ case VariantGrand: /* should work */
+ case VariantSpartan: /* should work */
break;
}
}
- InitEngineUCI( installDir, &first ); // [HGM] moved here from winboard.c, to make available in xboard
- InitEngineUCI( installDir, &second );
}
int NextIntegerFromString( char ** str, long * value )
return result;
}
-int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)
-{ /* [HGM] routine added to read '+moves/time' for secondary time control */
- int result = -1; long temp, temp2;
+int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *incType)
+{ /* [HGM] routine added to read '+moves/time' for secondary time control. */
+ int result = -1, type = 0; long temp, temp2;
- if(**str != '+') return -1; // old params remain in force!
+ if(**str != ':') return -1; // old params remain in force!
(*str)++;
- if( NextTimeControlFromString( str, &temp ) ) return -1;
+ if(**str == '*') type = *(*str)++, temp = 0; // sandclock TC
+ if( NextIntegerFromString( str, &temp ) ) return -1;
+ if(type) { *moves = 0; *tc = temp * 500; *inc = temp * 1000; *incType = '*'; return 0; }
if(**str != '/') {
/* time only: incremental or sudden-death time control */
if(**str == '+') { /* increment follows; read it */
(*str)++;
+ if(**str == '!') type = *(*str)++; // Bronstein TC
if(result = NextIntegerFromString( str, &temp2)) return -1;
*inc = temp2 * 1000;
+ if(**str == '.') { // read fraction of increment
+ char *start = ++(*str);
+ if(result = NextIntegerFromString( str, &temp2)) return -1;
+ temp2 *= 1000;
+ while(start++ < *str) temp2 /= 10;
+ *inc += temp2;
+ }
} else *inc = 0;
- *moves = 0; *tc = temp * 1000;
+ *moves = 0; *tc = temp * 1000; *incType = type;
return 0;
- } else if(temp % 60 != 0) return -1; /* moves was given as min:sec */
+ }
(*str)++; /* classical time control */
- result = NextTimeControlFromString( str, &temp2);
+ result = NextIntegerFromString( str, &temp2); // NOTE: already converted to seconds by ParseTimeControl()
+
if(result == 0) {
- *moves = temp/60;
+ *moves = temp;
*tc = temp2 * 1000;
*inc = 0;
+ *incType = type;
}
return result;
}
-int GetTimeQuota(int movenr)
+int GetTimeQuota(int movenr, int lastUsed, char *tcString)
{ /* [HGM] get time to add from the multi-session time-control string */
- int moves=1; /* kludge to force reading of first session */
+ int incType, moves=1; /* kludge to force reading of first session */
long time, increment;
- char *s = fullTimeControlString;
+ char *s = tcString;
- if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);
+ 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);
+ 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(!moves) return increment; /* current session is incremental */
if(movenr >= 0) movenr -= moves; /* we already finished this session */
} while(movenr >= -1); /* try again for next session */
int
ParseTimeControl(tc, ti, mps)
char *tc;
- int ti;
+ float ti;
int mps;
{
long tc1;
long tc2;
- char buf[MSG_SIZ];
-
+ char buf[MSG_SIZ], buf2[MSG_SIZ], *mytc = tc;
+ int min, sec=0;
+
if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
+ if(!strchr(tc, '+') && !strchr(tc, '/') && sscanf(tc, "%d:%d", &min, &sec) >= 1)
+ sprintf(mytc=buf2, "%d", 60*min+sec); // convert 'classical' min:sec tc string to seconds
if(ti > 0) {
+
if(mps)
- sprintf(buf, "+%d/%s+%d", mps, tc, ti);
- else sprintf(buf, "+%s+%d", tc, ti);
+ snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti);
+ else
+ snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti);
} else {
if(mps)
- sprintf(buf, "+%d/%s", mps, tc);
- else sprintf(buf, "+%s", tc);
+ snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc);
+ else
+ snprintf(buf, MSG_SIZ, ":%s", mytc);
}
- fullTimeControlString = StrSave(buf);
+ fullTimeControlString = StrSave(buf); // this should now be in PGN format
if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
return FALSE;
}
-
+
if( *tc == '/' ) {
/* Parse second time control */
tc++;
-
+
if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
return FALSE;
}
-
+
if( tc2 == 0 ) {
return FALSE;
}
-
+
timeControl_2 = tc2 * 1000;
}
else {
timeControl_2 = 0;
}
-
+
if( tc1 == 0 ) {
return FALSE;
}
-
+
timeControl = tc1 * 1000;
-
+
if (ti >= 0) {
timeIncrement = ti * 1000; /* convert to ms */
movesPerSession = 0;
}
}
+int
+CalculateIndex(int index, int gameNr)
+{ // [HGM] autoinc: absolute way to determine load index from game number (taking auto-inc and rewind into account)
+ int res;
+ if(index > 0) return index; // fixed nmber
+ if(index == 0) return 1;
+ res = (index == -1 ? gameNr : (gameNr-1)/2 + 1); // autoinc
+ if(appData.rewindIndex > 0) res = (res-1) % appData.rewindIndex + 1; // rewind
+ return res;
+}
+
+int
+LoadGameOrPosition(int gameNr)
+{ // [HGM] taken out of MatchEvent and NextMatchGame (to combine it)
+ if (*appData.loadGameFile != NULLCHAR) {
+ if (!LoadGameFromFile(appData.loadGameFile,
+ CalculateIndex(appData.loadGameIndex, gameNr),
+ appData.loadGameFile, FALSE)) {
+ DisplayFatalError(_("Bad game file"), 0, 1);
+ return 0;
+ }
+ } else if (*appData.loadPositionFile != NULLCHAR) {
+ if (!LoadPositionFromFile(appData.loadPositionFile,
+ CalculateIndex(appData.loadPositionIndex, gameNr),
+ appData.loadPositionFile)) {
+ DisplayFatalError(_("Bad position file"), 0, 1);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+ReserveGame(int gameNr, char resChar)
+{
+ FILE *tf = fopen(appData.tourneyFile, "r+");
+ char *p, *q, c, buf[MSG_SIZ];
+ if(tf == NULL) { nextGame = appData.matchGames + 1; return; } // kludge to terminate match
+ safeStrCpy(buf, lastMsg, MSG_SIZ);
+ DisplayMessage(_("Pick new game"), "");
+ flock(fileno(tf), LOCK_EX); // lock the tourney file while we are messing with it
+ ParseArgsFromFile(tf);
+ p = q = appData.results;
+ if(appData.debugMode) {
+ char *r = appData.participants;
+ fprintf(debugFP, "results = '%s'\n", p);
+ while(*r) fprintf(debugFP, *r >= ' ' ? "%c" : "\\%03o", *r), r++;
+ fprintf(debugFP, "\n");
+ }
+ while(*q && *q != ' ') q++; // get first un-played game (could be beyond end!)
+ nextGame = q - p;
+ q = malloc(strlen(p) + 2); // could be arbitrary long, but allow to extend by one!
+ 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 != ' ' && !abortMatch) { // reserve next game if tourney not yet done
+ if(q[nextGame] == NULLCHAR) q[nextGame+1] = NULLCHAR; // append one char
+ q[nextGame] = '*';
+ }
+ fseek(tf, -(strlen(p)+4), SEEK_END);
+ c = fgetc(tf);
+ if(c != '"') // depending on DOS or Unix line endings we can be one off
+ fseek(tf, -(strlen(p)+2), SEEK_END);
+ else fseek(tf, -(strlen(p)+3), SEEK_END);
+ fprintf(tf, "%s\"\n", q); fclose(tf); // update, and flush by closing
+ DisplayMessage(buf, "");
+ free(p); appData.results = q;
+ 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.
+ }
+}
+
+void
+MatchEvent(int mode)
+{ // [HGM] moved out of InitBackend3, to make it callable when match starts through menu
+ int dummy;
+ if(matchMode) { // already in match mode: switch it off
+ abortMatch = TRUE;
+ if(!appData.tourneyFile[0]) appData.matchGames = matchGame; // kludge to let match terminate after next game.
+ return;
+ }
+// if(gameMode != BeginningOfGame) {
+// DisplayError(_("You can only start a match from the initial position."), 0);
+// return;
+// }
+ abortMatch = FALSE;
+ if(mode == 2) appData.matchGames = appData.defaultMatchGames;
+ /* Set up machine vs. machine match */
+ nextGame = 0;
+ NextTourneyGame(-1, &dummy); // sets appData.matchGames if this is tourney, to make sure ReserveGame knows it
+ if(appData.tourneyFile[0]) {
+ ReserveGame(-1, 0);
+ if(nextGame > appData.matchGames) {
+ char buf[MSG_SIZ];
+ if(strchr(appData.results, '*') == NULL) {
+ FILE *f;
+ appData.tourneyCycles++;
+ if(f = WriteTourneyFile(appData.results)) { // make a tourney file with increased number of cycles
+ fclose(f);
+ NextTourneyGame(-1, &dummy);
+ ReserveGame(-1, 0);
+ if(nextGame <= appData.matchGames) {
+ DisplayNote(_("You restarted an already completed tourney\nOne more cycle will now be added to it\nGames commence in 10 sec"));
+ matchMode = mode;
+ ScheduleDelayedEvent(NextMatchGame, 10000);
+ return;
+ }
+ }
+ }
+ snprintf(buf, MSG_SIZ, _("All games in tourney '%s' are already played or playing"), appData.tourneyFile);
+ DisplayError(buf, 0);
+ appData.tourneyFile[0] = 0;
+ return;
+ }
+ } else
+ if (appData.noChessProgram) { // [HGM] in tourney engines are loaded automatically
+ DisplayFatalError(_("Can't have a match with no chess programs"),
+ 0, 2);
+ return;
+ }
+ matchMode = mode;
+ matchGame = roundNr = 1;
+ first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches
+ NextMatchGame();
+}
+
void
InitBackEnd3 P((void))
{
GameMode initialMode;
char buf[MSG_SIZ];
- int err;
+ int err, len;
InitChessProgram(&first, startedFromSetupPosition);
if (appData.icsActive) {
#ifdef WIN32
/* [DM] Make a console window if needed [HGM] merged ifs */
- ConsoleCreate();
+ ConsoleCreate();
#endif
err = establish();
- if (err != 0) {
- if (*appData.icsCommPort != NULLCHAR) {
- sprintf(buf, _("Could not open comm port %s"),
- appData.icsCommPort);
- } else {
- snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),
+ if (err != 0)
+ {
+ if (*appData.icsCommPort != NULLCHAR)
+ len = snprintf(buf, MSG_SIZ, _("Could not open comm port %s"),
+ appData.icsCommPort);
+ else
+ len = snprintf(buf, MSG_SIZ, _("Could not connect to host %s, port %s"),
appData.icsHost, appData.icsPort);
- }
+
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
+
DisplayFatalError(buf, err, 1);
return;
}
cmailISR =
AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
}
-
+
ThawUI();
DisplayMessage("", "");
if (StrCaseCmp(appData.initialMode, "") == 0) {
initialMode = BeginningOfGame;
+ if(!appData.icsActive && appData.noChessProgram) { // [HGM] could be fall-back
+ gameMode = MachinePlaysBlack; // "Machine Black" might have been implicitly highlighted
+ ModeHighlight(); // make sure XBoard knows it is highlighted, so it will un-highlight it
+ gameMode = BeginningOfGame; // in case BeginningOfGame now means "Edit Position"
+ ModeHighlight();
+ }
} else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
initialMode = TwoMachinesPlay;
} else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
- initialMode = AnalyzeFile;
+ initialMode = AnalyzeFile;
} else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
initialMode = AnalyzeMode;
} else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
} else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
initialMode = Training;
} else {
- sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);
+ len = snprintf(buf, MSG_SIZ, _("Unknown initialMode %s"), appData.initialMode);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
+
DisplayFatalError(buf, 0, 2);
return;
}
if (appData.matchMode) {
- /* Set up machine vs. machine match */
- if (appData.noChessProgram) {
- DisplayFatalError(_("Can't have a match with no chess programs"),
- 0, 2);
- return;
- }
- matchMode = TRUE;
- matchGame = 1;
- if (*appData.loadGameFile != NULLCHAR) {
- int index = appData.loadGameIndex; // [HGM] autoinc
- if(index<0) lastIndex = index = 1;
- if (!LoadGameFromFile(appData.loadGameFile,
- index,
- appData.loadGameFile, FALSE)) {
- DisplayFatalError(_("Bad game file"), 0, 1);
- return;
- }
- } else if (*appData.loadPositionFile != NULLCHAR) {
- int index = appData.loadPositionIndex; // [HGM] autoinc
- if(index<0) lastIndex = index = 1;
- if (!LoadPositionFromFile(appData.loadPositionFile,
- index,
- appData.loadPositionFile)) {
- DisplayFatalError(_("Bad position file"), 0, 1);
- return;
- }
- }
- TwoMachinesEvent();
+ if(appData.tourneyFile[0]) { // start tourney from command line
+ FILE *f;
+ 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 if (*appData.cmailGameName != NULLCHAR) {
/* Set up cmail mode */
ReloadCmailMsgEvent(TRUE);
appData.icsHost, appData.icsPort);
} else {
snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s",
- appData.remoteShell, appData.gateway,
+ appData.remoteShell, appData.gateway,
appData.remoteUser, appData.telnetProgram,
appData.icsHost, appData.icsPort);
}
}
}
+void EscapeExpand(char *p, char *q)
+{ // [HGM] initstring: routine to shape up string arguments
+ while(*p++ = *q++) if(p[-1] == '\\')
+ switch(*q++) {
+ case 'n': p[-1] = '\n'; break;
+ case 'r': p[-1] = '\r'; break;
+ case 't': p[-1] = '\t'; break;
+ case '\\': p[-1] = '\\'; break;
+ case 0: *p = 0; return;
+ default: p[-1] = q[-1]; break;
+ }
+}
+
void
show_bytes(fp, buf, count)
FILE *fp;
/* Remove all highlighting escape sequences in s
- Also deletes any suffix starting with '('
+ Also deletes any suffix starting with '('
*/
char *
StripHighlightAndTitle(s)
VariantClass v = VariantNormal;
int i, found = FALSE;
char buf[MSG_SIZ];
+ int len;
if (!e) return v;
if (!found) {
if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
- || StrCaseStr(e, "wild/fr")
+ || StrCaseStr(e, "wild/fr")
|| StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {
v = VariantFischeRandom;
} else if ((i = 4, p = StrCaseStr(e, "wild")) ||
v = VariantShatranj;
break;
- /* Temporary names for future ICC types. The name *will* change in
+ /* Temporary names for future ICC types. The name *will* change in
the next xboard/WinBoard release after ICC defines it. */
case 29:
v = Variant29;
v = VariantNormal;
break;
default:
- sprintf(buf, _("Unknown wild type %d"), wnum);
+ len = snprintf(buf, MSG_SIZ, _("Unknown wild type %d"), wnum);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "StringToVariant: buffer truncated.\n");
+
DisplayError(buf, 0);
v = VariantUnknown;
break;
char *bufp = &buf[*index], *patternp = pattern;
int star_count = 0;
char *matchp = star_match[0];
-
+
for (;;) {
if (*patternp == NULLCHAR) {
*index = leftover_start = bufp - buf;
break;
default:
ddwwStr = buf1;
- sprintf(buf1, "%d", ddww);
+ snprintf(buf1,sizeof(buf1)/sizeof(buf1[0]), "%d", ddww);
break;
}
switch (option) {
break;
default:
optionStr = buf2;
- sprintf(buf2, "%d", option);
+ snprintf(buf2,sizeof(buf2)/sizeof(buf2[0]), "%d", option);
break;
}
fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
VariantSwitch(Board board, VariantClass newVariant)
{
int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
- Board oldBoard;
+ static Board oldBoard;
startedFromPositionFile = FALSE;
if(gameInfo.variant == newVariant) return;
* case we want to add those holdings to the already received position.
*/
-
+
if (appData.debugMode) {
fprintf(debugFP, "Switch board from %s to %s\n",
VariantName(gameInfo.variant), VariantName(newVariant));
}
shuffleOpenings = 0; /* [HGM] shuffle */
gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
- switch(newVariant)
+ switch(newVariant)
{
case VariantShogi:
newWidth = 9; newHeight = 9;
default:
newHoldingsWidth = gameInfo.holdingsSize = 0;
};
-
+
if(newWidth != gameInfo.boardWidth ||
newHeight != gameInfo.boardHeight ||
newHoldingsWidth != gameInfo.holdingsWidth ) {
-
+
/* shift position to new playing area, if needed */
if(newHoldingsWidth > gameInfo.holdingsWidth) {
- for(i=0; i<BOARD_HEIGHT; i++)
+ for(i=0; i<BOARD_HEIGHT; i++)
for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
board[i][j];
if(v == VariantLoadable) type = "setup"; else
type = VariantName(v);
}
- sprintf(buf, "%s (%s) %d %d %c %s%s", handle, rating, base, inc, rated, type, ext);
+ snprintf(buf, MSG_SIZ, "%s (%s) %d %d %c %s%s", handle, rating, base, inc, rated, type, ext);
if(nrOfSeekAds < MAX_SEEK_ADS-1) {
if(seekAdList[nrOfSeekAds]) free(seekAdList[nrOfSeekAds]);
ratingList[nrOfSeekAds] = -1; // for if seeker has no rating
int
DrawSeekGraph()
{
- if(!seekGraphUp) return FALSE;
int i;
+ if(!seekGraphUp) return FALSE;
h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
if(i%500 == 0) {
char buf[MSG_SIZ];
- sprintf(buf, "%d", i);
+ snprintf(buf, MSG_SIZ, "%d", i);
DrawSeekText(buf, hMargin+squareSize/8+7, yy);
}
}
DrawSeekAxis(xx, h-1-vMargin, xx, h-6-vMargin-3*(i%10==0)); // TC ticks
if(i<=5 || (i>40 ? i%20 : i%10) == 0) {
char buf[MSG_SIZ];
- sprintf(buf, "%d", i);
+ snprintf(buf, MSG_SIZ, "%d", i);
DrawSeekText(buf, xx-2-3*(i>9), h-1-vMargin/2);
}
}
return TRUE;
} // on press 'hit', only show info
if(moving == 2) return TRUE; // ignore right up-clicks on dot
- sprintf(buf, "play %d\n", seekNrList[closest]);
+ snprintf(buf, MSG_SIZ, "play %d\n", seekNrList[closest]);
SendToICS(ics_prefix);
SendToICS(buf);
return TRUE; // let incoming board of started game pop down the graph
#define STARTED_CHATTER 5
#define STARTED_COMMENT 6
#define STARTED_MOVES_NOHIDE 7
-
+
static int started = STARTED_NONE;
static char parse[20000];
static int parse_pos = 0;
static int savingComment = FALSE;
static int cmatch = 0; // continuation sequence match
char *bp;
- char str[500];
+ char str[MSG_SIZ];
int i, oldi;
int buf_len;
int next_out;
// next_out = leftover_len; // [HGM] should we set this to 0, and not print it in advance?
next_out = 0;
leftover_start = 0;
-
+
i = 0;
while (i < buf_len) {
/* Deal with part of the TELNET option negotiation
next_out = i;
continue;
}
-
+
/* OK, this at least will *usually* work */
if (!loggedOn && looking_at(buf, &i, "ics%")) {
loggedOn = TRUE;
}
-
+
if (loggedOn && !intfSet) {
if (ics_type == ICS_ICC) {
- sprintf(str,
+ snprintf(str, MSG_SIZ,
"/set-quietly interface %s\n/set-quietly style 12\n",
programVersion);
if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
strcat(str, "/set-2 51 1\n/set seek 1\n");
} else if (ics_type == ICS_CHESSNET) {
- sprintf(str, "/style 12\n");
+ snprintf(str, MSG_SIZ, "/style 12\n");
} else {
- strcpy(str, "alias $ @\n$set interface ");
+ safeStrCpy(str, "alias $ @\n$set interface ", sizeof(str)/sizeof(str[0]));
strcat(str, programVersion);
strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
parse[parse_pos] = NULLCHAR;
if(chattingPartner>=0) {
char mess[MSG_SIZ];
- sprintf(mess, "%s%s", talker, parse);
+ snprintf(mess, MSG_SIZ, "%s%s", talker, parse);
OutputChatMessage(chattingPartner, mess);
chattingPartner = -1;
next_out = i+1; // [HGM] suppress printing in ICS window
OutputKibitz(suppressKibitz, parse);
} else {
char tmp[MSG_SIZ];
- sprintf(tmp, _("your opponent kibitzes: %s"), parse);
+ snprintf(tmp, MSG_SIZ, _("your opponent kibitzes: %s"), parse);
SendToPlayer(tmp, strlen(tmp));
}
next_out = i+1; // [HGM] suppress printing in ICS window
(looking_at(buf, &i, "\"*\" is *a registered name") ||
looking_at(buf, &i, "Logging you in as \"*\"") ||
looking_at(buf, &i, "will be \"*\""))) {
- strcpy(ics_handle, star_match[0]);
+ safeStrCpy(ics_handle, star_match[0], sizeof(ics_handle)/sizeof(ics_handle[0]));
continue;
}
if(looking_at(buf, &i, "* (*) seeking * * * * *\"play *\" to respond)\n")) {
int s = (ics_type == ICS_ICC); // ICC format differs
if(seekGraphUp)
- AddAd(star_match[0], star_match[1], atoi(star_match[2+s]), atoi(star_match[3+s]),
+ AddAd(star_match[0], star_match[1], atoi(star_match[2+s]), atoi(star_match[3+s]),
star_match[4+s][0], star_match[5-3*s], atoi(star_match[7]), TRUE);
looking_at(buf, &i, "*% "); // eat prompt
if(oldi > 0 && buf[oldi-1] == '\n') oldi--; // suppress preceding LF, if any
}
// [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
- if (appData.autoKibitz && started == STARTED_NONE &&
+ if (appData.autoKibitz && started == STARTED_NONE &&
!appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
- (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
+ (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent
suppressKibitz = TRUE;
if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
savingComment = TRUE;
suppressKibitz = gameMode != IcsObserving ? 2 :
(StrStr(star_match[0], gameInfo.white) == NULL) + 1;
- }
+ }
continue;
} else
if((looking_at(buf, &i, "\nkibitzed to *\n") || looking_at(buf, &i, "kibitzed to *\n") ||
// [HGM] chat: intercept tells by users for which we have an open chat window
channel = -1;
- if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
+ if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
looking_at(buf, &i, "* whispers:") ||
looking_at(buf, &i, "* kibitzes:") ||
looking_at(buf, &i, "* shouts:") ||
if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
for(p=0; p<MAX_CHAT; p++) {
- if(channel == atoi(chatPartner[p])) {
+ if(chatPartner[p][0] >= '0' && chatPartner[p][0] <= '9' && channel == atoi(chatPartner[p])) {
talker[0] = '['; strcat(talker, "] ");
Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE);
chattingPartner = p; break;
}
} // [HGM] chat: end of patch
+ backup = i;
if (appData.zippyTalk || appData.zippyPlay) {
/* [DM] Backup address for color zippy lines */
- backup = i;
#if ZIPPY
- #ifdef WIN32
if (loggedOn == TRUE)
if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
(appData.zippyPlay && ZippyMatch(buf, &backup)));
- #else
- if (ZippyControl(buf, &i) ||
- ZippyConverse(buf, &i) ||
- (appData.zippyPlay && ZippyMatch(buf, &i))) {
- loggedOn = TRUE;
- if (!appData.colorize) continue;
- }
- #endif
#endif
} // [DM] 'else { ' deleted
if (
continue;
}
+ if(i < backup) { i = backup; continue; } // [HGM] for if ZippyControl matches, but the colorie code doesn't
+
if (looking_at(buf, &i, "\\ ")) {
if (prevColor != ColorNormal) {
if (oldi > next_out) {
SendToICS("refresh\n");
continue;
}
-
+
if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
ICSInitScript();
have_sent_ICS_logon = 1;
continue;
}
-
- if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
+
+ if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
(looking_at(buf, &i, "\n<12> ") ||
looking_at(buf, &i, "<12> "))) {
loggedOn = TRUE;
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;
/* Header for a move list -- second line */
/* Initial board will follow if this is a wild game */
if (gameInfo.event != NULL) free(gameInfo.event);
- sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
+ snprintf(str, MSG_SIZ, "ICS %s %s match", star_match[0], star_match[1]);
gameInfo.event = StrSave(str);
/* [HGM] we switched variant. Translate boards if needed. */
VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));
break;
}
continue;
- }
-
+ }
+
if (looking_at(buf, &i, "% ") ||
((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
&& looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
if (WhiteOnMove(forwardMostMove)) {
if (first.sendTime) {
if (first.useColors) {
- SendToProgram("black\n", &first);
+ SendToProgram("black\n", &first);
}
SendTimeRemaining(&first, TRUE);
}
firstMove = TRUE;
}
}
- }
+ }
}
#endif
if (gameMode == IcsObserving && ics_gamenum == -1) {
flipView = appData.flipView;
DrawPosition(TRUE, boards[currentMove]);
DisplayBothClocks();
- sprintf(str, "%s vs. %s",
+ snprintf(str, MSG_SIZ, "%s vs. %s",
gameInfo.white, gameInfo.black);
DisplayTitle(str);
gameMode = IcsIdle;
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
HandleMachineMove(bookMove, &first);
}
continue;
}
-
+
if ((started == STARTED_MOVES || started == STARTED_BOARD ||
started == STARTED_HOLDINGS ||
started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
/* Accumulate characters in move list or board */
parse[parse_pos++] = buf[i];
}
-
+
/* Start of game messages. Mostly we detect start of game
when the first board image arrives. On some versions
of the ICS, though, we need to do a "refresh" after starting
} else {
player = star_match[2];
}
- sprintf(str, "%sobserve %s\n",
+ snprintf(str, MSG_SIZ, "%sobserve %s\n",
ics_prefix, StripHighlightAndTitle(player));
SendToICS(str);
/* Save ratings from notify string */
- strcpy(player1Name, star_match[0]);
+ safeStrCpy(player1Name, star_match[0], sizeof(player1Name)/sizeof(player1Name[0]));
player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, star_match[2]);
+ safeStrCpy(player2Name, star_match[2], sizeof(player2Name)/sizeof(player2Name[0]));
player2Rating = string_to_rating(star_match[3]);
if (appData.debugMode)
- fprintf(debugFP,
+ fprintf(debugFP,
"Ratings from 'Game notification:' %s %d, %s %d\n",
player1Name, player1Rating,
player2Name, player2Rating);
SendToICS("refresh\n");
}
continue;
- }
-
+ }
+
/* Error messages */
// if (ics_user_moved) {
if (1) { // [HGM] old way ignored error after move type in; ics_user_moved is not set then!
2 empty, white, or black (IGNORED)
3 player 2 name (not necessarily black)
4 player 2 rating
-
+
The names/ratings are sorted out when the game
actually starts (below).
*/
- strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
- player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
- player2Rating = string_to_rating(star_match[4]);
+ safeStrCpy(player1Name, StripHighlightAndTitle(star_match[0]), sizeof(player1Name)/sizeof(player1Name[0]));
+ player1Rating = string_to_rating(star_match[1]);
+ safeStrCpy(player2Name, StripHighlightAndTitle(star_match[3]), sizeof(player2Name)/sizeof(player2Name[0]));
+ player2Rating = string_to_rating(star_match[4]);
if (appData.debugMode)
- fprintf(debugFP,
+ fprintf(debugFP,
"Ratings from 'Creating:' %s %d, %s %d\n",
player1Name, player1Rating,
player2Name, player2Rating);
continue;
}
-
+
/* Improved generic start/end-of-game messages */
if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
(tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
/* [4] is " *" or empty (don't care). */
int gamenum = atoi(star_match[0]);
char *whitename, *blackname, *why, *endtoken;
- ChessMove endtype = (ChessMove) 0;
+ ChessMove endtype = EndOfFile;
if (tkind == 0) {
whitename = star_match[1];
if (strncmp(why, "Creating ", 9) == 0 ||
strncmp(why, "Continuing ", 11) == 0) {
gs_gamenum = gamenum;
- strcpy(gs_kind, strchr(why, ' ') + 1);
+ safeStrCpy(gs_kind, strchr(why, ' ') + 1,sizeof(gs_kind)/sizeof(gs_kind[0]));
VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board
#if ZIPPY
if (appData.zippyPlay) {
if (currentMove == 0 &&
gameMode == IcsPlayingWhite &&
appData.premoveWhite) {
- sprintf(str, "%s\n", appData.premoveWhiteText);
+ snprintf(str, MSG_SIZ, "%s\n", appData.premoveWhiteText);
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
} else if (currentMove == 1 &&
gameMode == IcsPlayingBlack &&
appData.premoveBlack) {
- sprintf(str, "%s\n", appData.premoveBlackText);
+ snprintf(str, MSG_SIZ, "%s\n", appData.premoveBlackText);
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
ClearPremoveHighlights();
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
- UserMoveEvent(premoveFromX, premoveFromY,
- premoveToX, premoveToY,
+ UserMoveEvent(premoveFromX, premoveFromY,
+ premoveToX, premoveToY,
premovePromoChar);
}
}
will tell us whether this is really bug or zh */
if (ics_getting_history == H_FALSE) {
ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ snprintf(str, MSG_SIZ, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
}
char wh[16], bh[16];
PackHolding(wh, white_holding);
PackHolding(bh, black_holding);
- sprintf(str, "[%s-%s] %s-%s", wh, bh,
+ snprintf(str, MSG_SIZ,"[%s-%s] %s-%s", wh, bh,
gameInfo.white, gameInfo.black);
} else {
- sprintf(str, "%s [%s] vs. %s [%s]",
+ snprintf(str, MSG_SIZ, "%s [%s] vs. %s [%s]",
gameInfo.white, white_holding,
gameInfo.black, black_holding);
}
i++; /* skip unparsed character and loop back */
}
-
+
if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz
// started != STARTED_HOLDINGS && i > next_out) { // [HGM] should we compare to leftover_start in stead of i?
// SendToPlayer(&buf[next_out], i - next_out);
SendToPlayer(&buf[next_out], leftover_start - next_out);
next_out = i;
}
-
+
leftover_len = buf_len - leftover_start;
/* if buffer ends with something we couldn't parse,
reparse it after appending the next read */
-
+
} else if (count == 0) {
RemoveInputSource(isr);
DisplayFatalError(_("Connection closed by ICS"), 0, 0);
/* Board style 12 looks like this:
-
+
<12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0
-
+
* The "<12> " is stripped before it gets to this routine. The two
* trailing 0's (flip state and clock ticking) are later addition, and
* some chess servers may not have them, or may have only the first.
- * Additional trailing fields may be added in the future.
+ * Additional trailing fields may be added in the future.
*/
#define PATTERN "%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
void
ParseBoard12(string)
char *string;
-{
+{
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 double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
char to_play, board_chars[200];
- char move_str[500], str[500], elapsed_time[500];
+ char move_str[MSG_SIZ], str[MSG_SIZ], elapsed_time[MSG_SIZ];
char black[32], white[32];
Board board;
int prevMove = currentMove;
Boolean weird = FALSE, reqFlag = FALSE;
fromX = fromY = toX = toY = -1;
-
+
newGame = FALSE;
if (appData.debugMode)
&ticking);
if (n < 21) {
- snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);
+ snprintf(str, MSG_SIZ, _("Failed to parse board string:\n\"%s\""), string);
DisplayError(str, 0);
return;
}
0, 1);
return;
}
-
+
switch (relation) {
case RELATION_OBSERVING_PLAYED:
case RELATION_OBSERVING_STATIC:
case RELATION_ISOLATED_BOARD:
default:
/* Just display this board. If user was doing something else,
- we will forget about it until the next board comes. */
+ we will forget about it until the next board comes. */
newGameMode = IcsIdle;
break;
case RELATION_STARTING_POSITION:
newGameMode = gameMode;
break;
}
-
+
if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
- && newGameMode == IcsObserving && appData.bgObserve) {
+ && newGameMode == IcsObserving && gamenum != ics_gamenum && appData.bgObserve) {
// [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */
char *toSqr;
for (k = 0; k < ranks; k++) {
if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
if(partnerUp) DrawPosition(FALSE, partnerBoard);
if(twoBoards) { partnerUp = 0; flipView = !flipView; } // [HGM] dual
- sprintf(partnerStatus, "W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
+ snprintf(partnerStatus, MSG_SIZ,"W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
(black_time/60000), (black_time%60000)/1000, white_stren, black_stren, to_play);
DisplayMessage(partnerStatus, "");
partnerBoardValid = TRUE;
return;
}
- if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files ||
- weird && (int)gameInfo.variant <= (int)VariantShogi) {
+ if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files ||
+ weird && (int)gameInfo.variant < (int)VariantShogi) {
/* [HGM] We seem to have switched variant unexpectedly
* Try to guess new variant from board size
*/
will tell us whether this is really bug or zh */
if (ics_getting_history == H_FALSE) {
ics_getting_history = H_REQUESTED; reqFlag = TRUE;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ snprintf(str, MSG_SIZ, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
}
-
+
/* Take action if this is the first board of a new game, or of a
different game than is currently being displayed. */
if (gamenum != ics_gamenum || newGameMode != gameMode ||
relation == RELATION_ISOLATED_BOARD) {
-
+
/* Forget the old game and get the history (if any) of the new one */
if (gameMode != BeginningOfGame) {
Reset(TRUE, TRUE);
appData.getMoveList && !reqFlag) {
/* Need to get game history */
ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ snprintf(str, MSG_SIZ, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
-
+
/* Initially flip the board to have black on the bottom if playing
black or if the ICS flip flag is set, but let the user change
it with the Flip View button. */
- flipView = appData.autoFlipView ?
+ flipView = appData.autoFlipView ?
(newGameMode == IcsPlayingBlack) || ics_flip :
appData.flipView;
-
+
/* Done with values from previous mode; copy in new ones */
gameMode = newGameMode;
ModeHighlight();
if (gamenum == gs_gamenum) {
int klen = strlen(gs_kind);
if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
- sprintf(str, "ICS %s", gs_kind);
+ snprintf(str, MSG_SIZ, "ICS %s", gs_kind);
gameInfo.event = StrSave(str);
} else {
gameInfo.event = StrSave("ICS game");
}
gameInfo.outOfBook = NULL;
-
+
/* Do we have the ratings? */
if (strcmp(player1Name, white) == 0 &&
strcmp(player2Name, black) == 0) {
SendToICS("set shout 0\n");
}
}
-
+
/* Deal with midgame name changes */
if (!newGame) {
if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
gameInfo.black = StrSave(black);
}
}
-
+
/* Throw away game result if anything actually changes in examine mode */
if (gameMode == IcsExamining && !newGame) {
gameInfo.result = GameUnfinished;
gameInfo.resultDetails = NULL;
}
}
-
+
/* In pausing && IcsExamining mode, we ignore boards coming
in if they are in a different variation than we are. */
if (pauseExamInvalid) return;
return;
}
}
-
+
if (appData.debugMode) {
fprintf(debugFP, "load %dx%d board\n", files, ranks);
}
/* [HGM] e.p. rights. Assume that ICS sends file number here? */
boards[moveNum][EP_STATUS] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
-
+
if (ics_getting_history == H_GOT_REQ_HEADER ||
ics_getting_history == H_GOT_UNREQ_HEADER) {
/* This was an initial position from a move list, not
the current position */
return;
}
-
+
/* Update currentMove and known move number limits */
newMove = newGame || moveNum > forwardMostMove;
type when starting to examine a game. But if we ask for
the move list, the move list header will tell us */
ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ snprintf(str, MSG_SIZ, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
} else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
if (!pausing || currentMove > forwardMostMove)
currentMove = forwardMostMove;
} else {
- /* New part of history that is not contiguous with old part */
+ /* New part of history that is not contiguous with old part */
if (pausing && gameMode == IcsExamining) {
pauseExamInvalid = TRUE;
forwardMostMove = pauseExamForwardMostMove;
}
#endif
ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ snprintf(str, MSG_SIZ, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
forwardMostMove = backwardMostMove = currentMove = moveNum;
}
-
+
/* Update the clocks */
if (strchr(elapsed_time, '.')) {
/* Time is in ms */
timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
}
-
+
#if ZIPPY
if (appData.zippyPlay && newGame &&
gameMode != IcsExamining)
ZippyFirstBoard(moveNum, basetime, increment);
#endif
-
+
/* Put the move on the move list, first converting
to canonical algebraic form. */
if (moveNum > 0) {
if (moveNum <= backwardMostMove) {
/* We don't know what the board looked like before
this move. Punt. */
- strcpy(parseList[moveNum - 1], move_str);
+ safeStrCpy(parseList[moveNum - 1], move_str, sizeof(parseList[moveNum - 1])/sizeof(parseList[moveNum - 1][0]));
strcat(parseList[moveNum - 1], " ");
strcat(parseList[moveNum - 1], elapsed_time);
moveList[moveNum - 1][0] = NULLCHAR;
startedFromSetupPosition = TRUE;
fromX = fromY = toX = toY = -1;
} else {
- // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move.
+ // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move.
// So we parse the long-algebraic move string in stead of the SAN move
int valid; char buf[MSG_SIZ], *prom;
+ if(gameInfo.variant == VariantShogi && !strchr(move_str, '=') && !strchr(move_str, '@'))
+ strcat(move_str, "="); // if ICS does not say 'promote' on non-drop, we defer.
// str looks something like "Q/a1-a2"; kill the slash
- if(str[1] == '/')
- sprintf(buf, "%c%s", str[0], str+2);
- else strcpy(buf, str); // might be castling
- if((prom = strstr(move_str, "=")) && !strstr(buf, "="))
+ if(str[1] == '/')
+ snprintf(buf, MSG_SIZ,"%c%s", str[0], str+2);
+ else safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0])); // might be castling
+ if((prom = strstr(move_str, "=")) && !strstr(buf, "="))
strcat(buf, prom); // long move lacks promo specification!
if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)
- if(appData.debugMode)
+ if(appData.debugMode)
fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);
- strcpy(move_str, buf);
+ safeStrCpy(move_str, buf, MSG_SIZ);
}
valid = ParseOneMove(move_str, moveNum - 1, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)
strcat(parseList[moveNum - 1], " ");
strcat(parseList[moveNum - 1], elapsed_time);
/* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(moveList[moveNum - 1], currentMoveString);
+ if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '^';
+ safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0]));
strcat(moveList[moveNum - 1], "\n");
+
+ if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
+ && gameInfo.variant != VariantGrand) // inherit info that ICS does not give from previous board
+ for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
+ ChessSquare old, new = boards[moveNum][k][j];
+ if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
+ old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
+ if(old == new) continue;
+ if(old == PROMOTED new) boards[moveNum][k][j] = old; // prevent promoted pieces to revert to primordial ones
+ else if(new == WhiteWazir || new == BlackWazir) {
+ if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
+ boards[moveNum][k][j] = PROMOTED old; // choose correct type of Gold in promotion
+ else boards[moveNum][k][j] = old; // preserve type of Gold
+ } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
+ boards[moveNum][k][j] = PROMOTED new; // use non-primordial representation of chosen piece
+ }
} else {
/* Move from ICS was illegal!? Punt. */
- if (appData.debugMode) {
- fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);
- fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
- }
- strcpy(parseList[moveNum - 1], move_str);
+ if (appData.debugMode) {
+ fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);
+ fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
+ }
+ safeStrCpy(parseList[moveNum - 1], move_str, sizeof(parseList[moveNum - 1])/sizeof(parseList[moveNum - 1][0]));
strcat(parseList[moveNum - 1], " ");
strcat(parseList[moveNum - 1], elapsed_time);
moveList[moveNum - 1][0] = NULLCHAR;
#if ZIPPY
/* Send move to chess program (BEFORE animating it). */
- if (appData.zippyPlay && !newGame && newMove &&
+ if (appData.zippyPlay && !newGame && newMove &&
(!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
(gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, _("Couldn't parse move \"%s\" from ICS"),
+ snprintf(str, MSG_SIZ, _("Couldn't parse move \"%s\" from ICS"),
move_str);
DisplayError(str, 0);
} else {
}
} else if (gameMode == IcsObserving || gameMode == IcsExamining) {
if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str);
+ snprintf(str, MSG_SIZ, _("Couldn't parse move \"%s\" from ICS"), move_str);
DisplayError(str, 0);
} else {
if(gameInfo.variant == currentlyInitializedVariant) // [HGM] refrain sending moves engine can't understand!
SetHighlights(fromX, fromY, toX, toY);
}
}
-
+
/* Start the clocks */
whiteFlag = blackFlag = FALSE;
appData.clockMode = !(basetime == 0 && increment == 0);
DisplayBothClocks();
else
StartClocks();
-
+
/* Display opponents and material strengths */
if (gameInfo.variant != VariantBughouse &&
gameInfo.variant != VariantCrazyhouse && !appData.noGUI) {
if (tinyLayout || smallLayout) {
if(gameInfo.variant == VariantNormal)
- sprintf(str, "%s(%d) %s(%d) {%d %d}",
+ snprintf(str, MSG_SIZ, "%s(%d) %s(%d) {%d %d}",
gameInfo.white, white_stren, gameInfo.black, black_stren,
basetime, increment);
else
- sprintf(str, "%s(%d) %s(%d) {%d %d w%d}",
+ snprintf(str, MSG_SIZ, "%s(%d) %s(%d) {%d %d w%d}",
gameInfo.white, white_stren, gameInfo.black, black_stren,
basetime, increment, (int) gameInfo.variant);
} else {
if(gameInfo.variant == VariantNormal)
- sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
+ snprintf(str, MSG_SIZ, "%s (%d) vs. %s (%d) {%d %d}",
gameInfo.white, white_stren, gameInfo.black, black_stren,
basetime, increment);
else
- sprintf(str, "%s (%d) vs. %s (%d) {%d %d %s}",
+ snprintf(str, MSG_SIZ, "%s (%d) vs. %s (%d) {%d %d %s}",
gameInfo.white, white_stren, gameInfo.black, black_stren,
basetime, increment, VariantName(gameInfo.variant));
}
/* Display the board */
if (!pausing && !appData.noGUI) {
-
+
if (appData.premove)
- if (!gotPremove ||
+ if (!gotPremove ||
((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
ClearPremoveHighlights();
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
HandleMachineMove(bookMove, &first);
}
char buf[MSG_SIZ];
if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
ics_getting_history = H_REQUESTED;
- sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
+ snprintf(buf, MSG_SIZ, "%smoves %d\n", ics_prefix, ics_gamenum);
SendToICS(buf);
}
}
buf[len++] = '\n';
buf[len] = NULLCHAR;
} else {
- sprintf(buf, "%s\n", parseList[moveNum]);
+ snprintf(buf, MSG_SIZ,"%s\n", parseList[moveNum]);
}
SendToProgram(buf, cps);
} else {
AlphaRank(moveList[moveNum], 4); // and back
} else
/* Added by Tord: Send castle moves in "O-O" in FRC games if required by
- * the engine. It would be nice to have a better way to identify castle
+ * the engine. It would be nice to have a better way to identify castle
* moves here. */
if((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom)
&& cps->useOOCastle) {
- int fromX = moveList[moveNum][0] - AAA;
+ int fromX = moveList[moveNum][0] - AAA;
int fromY = moveList[moveNum][1] - ONE;
- int toX = moveList[moveNum][2] - AAA;
+ int toX = moveList[moveNum][2] - AAA;
int toY = moveList[moveNum][3] - ONE;
- if((boards[moveNum][fromY][fromX] == WhiteKing
+ if((boards[moveNum][fromY][fromX] == WhiteKing
&& boards[moveNum][toY][toX] == WhiteRook)
- || (boards[moveNum][fromY][fromX] == BlackKing
+ || (boards[moveNum][fromY][fromX] == BlackKing
&& boards[moveNum][toY][toX] == BlackRook)) {
if(toX > fromX) SendToProgram("O-O\n", cps);
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
+ if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move
+ snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0],
+ moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
+ } else
+ 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 */
/* Send 'go' if we are in a mode where machine should play. */
if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) &&
(gameMode == TwoMachinesPlay ||
-#ifdef ZIPPY
+#if ZIPPY
gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite ||
#endif
gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) {
}
void
-SendMoveToICS(moveType, fromX, fromY, toX, toY)
+SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar)
ChessMove moveType;
int fromX, fromY, toX, toY;
+ char promoChar;
{
char user_move[MSG_SIZ];
switch (moveType) {
default:
- sprintf(user_move, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"),
+ snprintf(user_move, MSG_SIZ, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"),
(int)moveType, fromX, fromY, toX, toY);
DisplayError(user_move + strlen("say "), 0);
break;
case WhiteHSideCastleFR:
case BlackHSideCastleFR:
/* POP Fabien */
- sprintf(user_move, "o-o\n");
+ snprintf(user_move, MSG_SIZ, "o-o\n");
break;
case WhiteQueenSideCastle:
case BlackQueenSideCastle:
case WhiteASideCastleFR:
case BlackASideCastleFR:
/* POP Fabien */
- sprintf(user_move, "o-o-o\n");
+ snprintf(user_move, MSG_SIZ, "o-o-o\n");
break;
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case WhitePromotionChancellor:
- case BlackPromotionChancellor:
- case WhitePromotionArchbishop:
- case BlackPromotionArchbishop:
+ case WhiteNonPromotion:
+ case BlackNonPromotion:
+ sprintf(user_move, "%c%c%c%c==\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY);
+ break;
+ case WhitePromotion:
+ case BlackPromotion:
if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
- sprintf(user_move, "%c%c%c%c=%c\n",
+ snprintf(user_move, MSG_SIZ, "%c%c%c%c=%c\n",
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,
PieceToChar(WhiteFerz));
else if(gameInfo.variant == VariantGreat)
- sprintf(user_move, "%c%c%c%c=%c\n",
+ snprintf(user_move, MSG_SIZ,"%c%c%c%c=%c\n",
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,
PieceToChar(WhiteMan));
else
- sprintf(user_move, "%c%c%c%c=%c\n",
+ snprintf(user_move, MSG_SIZ, "%c%c%c%c=%c\n",
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,
- PieceToChar(PromoPiece(moveType)));
+ promoChar);
break;
case WhiteDrop:
case BlackDrop:
- sprintf(user_move, "%c@%c%c\n",
- ToUpper(PieceToChar((ChessSquare) fromX)),
- AAA + toX, ONE + toY);
+ drop:
+ snprintf(user_move, MSG_SIZ, "%c@%c%c\n",
+ ToUpper(PieceToChar((ChessSquare) fromX)),
+ AAA + toX, ONE + toY);
break;
+ case IllegalMove: /* could be a variant we don't quite understand */
+ if(fromY == DROP_RANK) goto drop; // We need 'IllegalDrop' move type?
case NormalMove:
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
- case IllegalMove: /* could be a variant we don't quite understand */
- sprintf(user_move, "%c%c%c%c\n",
+ snprintf(user_move, MSG_SIZ,"%c%c%c%c\n",
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY);
break;
}
char buf[MSG_SIZ], *p, *fen, command[MSG_SIZ], bsetup = 0;
if(ics_type == ICS_ICC) { // on ICC match ourselves in applicable variant
- sprintf(command, "match %s", ics_handle);
+ snprintf(command,MSG_SIZ, "match %s", ics_handle);
} else { // on FICS we must first go to general examine mode
- strcpy(command, "examine\nbsetup"); // and specify variant within it with bsetups
+ safeStrCpy(command, "examine\nbsetup", sizeof(command)/sizeof(command[0])); // and specify variant within it with bsetups
}
if(gameInfo.variant != VariantNormal) {
// try figure out wild number, as xboard names are not always valid on ICS
for(i=1; i<=36; i++) {
- sprintf(buf, "wild/%d", i);
+ snprintf(buf, MSG_SIZ, "wild/%d", i);
if(StringToVariant(buf) == gameInfo.variant) break;
}
- if(i<=36 && ics_type == ICS_ICC) sprintf(buf, "%s w%d\n", command, i);
- else if(i == 22) sprintf(buf, "%s fr\n", command);
- else sprintf(buf, "%s %s\n", command, VariantName(gameInfo.variant));
- } else sprintf(buf, "%s\n", ics_type == ICS_ICC ? command : "examine\n"); // match yourself or examine
+ if(i<=36 && ics_type == ICS_ICC) snprintf(buf, MSG_SIZ,"%s w%d\n", command, i);
+ else if(i == 22) snprintf(buf,MSG_SIZ, "%s fr\n", command);
+ else snprintf(buf, MSG_SIZ,"%s %s\n", command, VariantName(gameInfo.variant));
+ } else snprintf(buf, MSG_SIZ,"%s\n", ics_type == ICS_ICC ? command : "examine\n"); // match yourself or examine
SendToICS(ics_prefix);
SendToICS(buf);
if(startedFromSetupPosition || backwardMostMove != 0) {
fen = PositionToFEN(backwardMostMove, NULL);
if(ics_type == ICS_ICC) { // on ICC we can simply send a complete FEN to set everything
- sprintf(buf, "loadfen %s\n", fen);
+ snprintf(buf, MSG_SIZ,"loadfen %s\n", fen);
SendToICS(buf);
} else { // FICS: everything has to set by separate bsetup commands
p = strchr(fen, ' '); p[0] = NULLCHAR; // cut after board
- sprintf(buf, "bsetup fen %s\n", fen);
+ snprintf(buf, MSG_SIZ,"bsetup fen %s\n", fen);
SendToICS(buf);
if(!WhiteOnMove(backwardMostMove)) {
SendToICS("bsetup tomove black\n");
}
i = (strchr(p+3, 'K') != NULL) + 2*(strchr(p+3, 'Q') != NULL);
- sprintf(buf, "bsetup wcastle %s\n", castlingStrings[i]);
+ snprintf(buf, MSG_SIZ,"bsetup wcastle %s\n", castlingStrings[i]);
SendToICS(buf);
i = (strchr(p+3, 'k') != NULL) + 2*(strchr(p+3, 'q') != NULL);
- sprintf(buf, "bsetup bcastle %s\n", castlingStrings[i]);
+ snprintf(buf, MSG_SIZ, "bsetup bcastle %s\n", castlingStrings[i]);
SendToICS(buf);
i = boards[backwardMostMove][EP_STATUS];
if(i >= 0) { // set e.p.
- sprintf(buf, "bsetup eppos %c\n", i+AAA);
+ snprintf(buf, MSG_SIZ,"bsetup eppos %c\n", i+AAA);
SendToICS(buf);
}
bsetup++;
}
for(i = backwardMostMove; i<last; i++) {
char buf[20];
- sprintf(buf, "%s\n", parseList[i]);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%s\n", parseList[i]);
SendToICS(buf);
}
SendToICS(ics_prefix);
char move[7];
{
if (rf == DROP_RANK) {
- sprintf(move, "%c@%c%c\n",
+ sprintf(move, "%c@%c%c\n",
ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt);
} else {
if (promoChar == 'x' || promoChar == NULLCHAR) {
- sprintf(move, "%c%c%c%c\n",
+ sprintf(move, "%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt);
} else {
sprintf(move, "%c%c%c%c%c\n",
}
+static int lastX, lastY, selectFlag, dragging;
+
+void
+Sweep(int step)
+{
+ ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep;
+ if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn;
+ if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) king = EmptySquare;
+ if(promoSweep >= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn;
+ if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare;
+ if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare;
+ do {
+ promoSweep -= step;
+ if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap
+ else if((int)promoSweep == -1) promoSweep = WhiteKing;
+ else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn;
+ else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
+ if(!step) step = 1;
+ } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
+ appData.testLegality && (promoSweep == king ||
+ gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep));
+ ChangeDragPiece(promoSweep);
+}
+
+int PromoScroll(int x, int y)
+{
+ int step = 0;
+
+ if(promoSweep == EmptySquare || !appData.sweepSelect) return FALSE;
+ if(abs(x - lastX) < 15 && abs(y - lastY) < 15) return FALSE;
+ if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
+ if(!step) return FALSE;
+ lastX = x; lastY = y;
+ if((promoSweep < BlackPawn) == flipView) step = -step;
+ if(step > 0) selectFlag = 1;
+ if(!selectFlag) Sweep(step);
+ return FALSE;
+}
+
+void
+NextPiece(int step)
+{
+ ChessSquare piece = boards[currentMove][toY][toX];
+ do {
+ pieceSweep -= step;
+ if(pieceSweep == EmptySquare) pieceSweep = WhitePawn; // wrap
+ if((int)pieceSweep == -1) pieceSweep = BlackKing;
+ if(!step) step = -1;
+ } while(PieceToChar(pieceSweep) == '.');
+ boards[currentMove][toY][toX] = pieceSweep;
+ DrawPosition(FALSE, boards[currentMove]);
+ boards[currentMove][toY][toX] = piece;
+}
/* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */
void
AlphaRank(char *move, int n)
fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);
}
- if(move[1]=='*' &&
+ if(move[1]=='*' &&
move[2]>='0' && move[2]<='9' &&
move[3]>='a' && move[3]<='x' ) {
move[1] = '@';
ChessMove *moveType;
int *fromX, *fromY, *toX, *toY;
char *promoChar;
-{
- if (appData.debugMode) {
- fprintf(debugFP, "move to parse: %s\n", move);
- }
+{
*moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
switch (*moveType) {
- case WhitePromotionChancellor:
- case BlackPromotionChancellor:
- case WhitePromotionArchbishop:
- case BlackPromotionArchbishop:
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
+ case WhitePromotion:
+ case BlackPromotion:
+ case WhiteNonPromotion:
+ case BlackNonPromotion:
case NormalMove:
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
if (appData.testLegality) {
return (*moveType != IllegalMove);
} else {
- return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
+ return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn);
}
case AmbiguousMove:
case ImpossibleMove:
- case (ChessMove) 0: /* end of file */
+ case EndOfFile:
case ElapsedTime:
case Comment:
case PGNTag:
}
}
+Boolean pushed = FALSE;
+char *lastParseAttempt;
void
-ParsePV(char *pv, Boolean storeComments)
+ParsePV(char *pv, Boolean storeComments, Boolean atEnd)
{ // Parse a string of PV moves, and append to current game, behind forwardMostMove
int fromX, fromY, toX, toY; char promoChar;
ChessMove moveType;
Boolean valid;
int nr = 0;
+ if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+ PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
+ pushed = TRUE;
+ }
endPV = forwardMostMove;
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);
}
if(!valid && nr == 0 &&
- ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){
+ ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){
nr++; moveType = Comment; // First move has been played; kludge to make sure we continue
// Hande case where played move is different from leading PV move
CopyBoard(boards[endPV+1], boards[endPV-1]); // tentatively unplay last game move
moveList[endPV-1][2] = toX + AAA;
moveList[endPV-1][3] = toY + ONE;
parseList[endPV-1][0] = NULLCHAR;
- strcpy(moveList[endPV-2], "_0_0"); // suppress premove highlight on takeback move
+ safeStrCpy(moveList[endPV-2], "_0_0", sizeof(moveList[endPV-2])/sizeof(moveList[endPV-2][0])); // suppress premove highlight on takeback move
}
}
pv = strstr(pv, yy_textstr) + strlen(yy_textstr); // skip what we parsed
endPV++;
CopyBoard(boards[endPV], boards[endPV-1]);
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[endPV]);
- moveList[endPV-1][0] = fromX + AAA;
- moveList[endPV-1][1] = fromY + ONE;
- moveList[endPV-1][2] = toX + AAA;
- moveList[endPV-1][3] = toY + ONE;
- if(storeComments)
- CoordsToAlgebraic(boards[endPV - 1],
+ CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, moveList[endPV - 1]);
+ strncat(moveList[endPV-1], "\n", MOVE_LEN);
+ CoordsToAlgebraic(boards[endPV - 1],
PosFlags(endPV - 1),
fromY, fromX, toY, toX, promoChar,
parseList[endPV - 1]);
- else
- parseList[endPV-1][0] = NULLCHAR;
} while(valid);
- currentMove = endPV;
+ 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,
moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
DrawPosition(TRUE, boards[currentMove]);
}
-static int lastX, lastY;
+int
+MultiPV(ChessProgramState *cps)
+{ // check if engine supports MultiPV, and if so, return the number of the option that sets it
+ int i;
+ for(i=0; i<cps->nrOptions; i++)
+ if(!strcmp(cps->option[i].name, "MultiPV") && cps->option[i].type == Spin)
+ return i;
+ return -1;
+}
Boolean
LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
{
- int startPV;
- char *p;
+ int startPV, multi, lineStart, origIndex = index;
+ char *p, buf2[MSG_SIZ];
if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
lastX = x; lastY = y;
while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
- startPV = index;
+ lineStart = startPV = index;
while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
if(index == startPV && (p = StrCaseStr(buf+index, "PV="))) startPV = p - buf + 3;
index = startPV;
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;
- ParsePV(buf+startPV, FALSE);
+ if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(&first)) >= 0) {
+ int n = first.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;
+ *start = *end = 0;
+ return FALSE;
+ }
+ ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
*start = startPV; *end = index-1;
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
int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
lastX = x; lastY = y;
- ParsePV(lastPV[which], FALSE); // load the PV of the thinking engine in the boards array.
+ ParsePV(lastPV[which], FALSE, TRUE); // load the PV of the thinking engine in the boards array.
return TRUE;
}
void
UnLoadPV()
{
+ int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded!
if(endPV < 0) return;
endPV = -1;
+ if(gameMode == AnalyzeMode && currentMove > forwardMostMove) {
+ Boolean saveAnimate = appData.animate;
+ if(pushed) {
+ if(shiftKey && storedGames < MAX_VARIATIONS-2) { // wants to start variation, and there is space
+ if(storedGames == 1) GreyRevert(FALSE); // we already pushed the tail, so just make it official
+ } else storedGames--; // abandon shelved tail of original game
+ }
+ pushed = FALSE;
+ forwardMostMove = currentMove;
+ currentMove = oldFMM;
+ appData.animate = FALSE;
+ ToNrEvent(forwardMostMove);
+ appData.animate = saveAnimate;
+ }
currentMove = forwardMostMove;
+ if(pushed) { PopInner(0); pushed = FALSE; } // restore shelved game continuation
ClearPremoveHighlights();
DrawPosition(TRUE, boards[currentMove]);
}
{ // step through PV based on mouse coordinates (called on mouse move)
int margin = h>>3, step = 0;
- if(endPV < 0) return;
// we must somehow check if right button is still down (might be released off board!)
- if(y < margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = 1; else
- if(y > h - margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = -1; else
- if( y > lastY + 6 ) step = -1; else if(y < lastY - 6) step = 1;
+ 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( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
if(!step) return;
lastX = x; lastY = y;
+
+ if(pieceSweep != EmptySquare) { NextPiece(step); return; }
+ if(endPV < 0) return;
+ if(y < margin) step = 1; else
+ if(y > h - margin) step = -1;
if(currentMove + step > endPV || currentMove + step < forwardMostMove) step = 0;
currentMove += step;
if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
board[rank][i] = (ChessSquare) pieceType;
squaresLeft[((i-BOARD_LEFT)&1) + 1]--;
squaresLeft[ANY]--;
- piecesLeft[pieceType]--;
+ piecesLeft[pieceType]--;
return i;
}
}
{
int result = FALSE; int NrPieces;
- if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare
+ if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare
&& NrPieces >= 12 && !(NrPieces&1)) {
int i; /* [HGM] Accept even length from 12 to 34 */
void Prelude(Board board)
{ // [HGM] superchess: random selection of exo-pieces
- int i, j, k; ChessSquare p;
+ int i, j, k; ChessSquare p;
static ChessSquare exoPieces[4] = { WhiteAngel, WhiteMarshall, WhiteSilver, WhiteLance };
GetPositionNumber(); // use FRC position number
if(appData.pieceToCharTable != NULL) { // select pieces to participate from given char table
SetCharTable(pieceToChar, appData.pieceToCharTable);
- for(i=(int)WhiteQueen+1, j=0; i<(int)WhiteKing && j<4; i++)
+ for(i=(int)WhiteQueen+1, j=0; i<(int)WhiteKing && j<4; i++)
if(PieceToChar((ChessSquare)i) != '.') exoPieces[j++] = (ChessSquare) i;
}
- j = seed%4; seed /= 4;
+ j = seed%4; seed /= 4;
p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
- j = seed%3 + (seed%3 >= j); seed /= 3;
+ j = seed%3 + (seed%3 >= j); seed /= 3;
p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
- j = seed%3; seed /= 3;
+ j = seed%3; seed /= 3;
p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
- j = seed%2 + (seed%2 >= j); seed /= 2;
+ j = seed%2 + (seed%2 >= j); seed /= 2;
p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
int i, j, pawnRow, overrule,
oldx = gameInfo.boardWidth,
oldy = gameInfo.boardHeight,
- oldh = gameInfo.holdingsWidth,
- oldv = gameInfo.variant;
+ oldh = gameInfo.holdingsWidth;
+ static int oldv;
if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request
castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;
}
-
+
/* [HGM] logic here is completely changed. In stead of full positions */
/* the initialized data only consist of the two backranks. The switch */
/* selects which one we will use, which is than copied to the Board */
for(i=0; i<BOARD_FILES-2; i++)
initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
initialPosition[EP_STATUS] = EP_NONE;
- SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
+ SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
+ if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
+ SetCharTable(pieceNickName, appData.pieceNickNames);
+ else SetCharTable(pieceNickName, "............");
+ pieces = FIDEArray;
switch (gameInfo.variant) {
case VariantFischeRandom:
shuffleOpenings = TRUE;
default:
- pieces = FIDEArray;
break;
case VariantShatranj:
pieces = ShatranjArray;
nrCastlingRights = 0;
- SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k");
+ SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k");
break;
case VariantMakruk:
pieces = makrukArray;
nrCastlingRights = 0;
startedFromSetupPosition = TRUE;
- SetCharTable(pieceToChar, "PN.R.M....SKpn.r.m....sk");
+ SetCharTable(pieceToChar, "PN.R.M....SKpn.r.m....sk");
break;
case VariantTwoKings:
pieces = twoKingsArray;
break;
+ case VariantGrand:
+ pieces = GrandArray;
+ nrCastlingRights = 0;
+ SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
+ gameInfo.boardWidth = 10;
+ gameInfo.boardHeight = 10;
+ gameInfo.holdingsSize = 7;
+ break;
case VariantCapaRandom:
shuffleOpenings = TRUE;
case VariantCapablanca:
pieces = CapablancaArray;
gameInfo.boardWidth = 10;
- SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
+ SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
break;
case VariantGothic:
pieces = GothicArray;
gameInfo.boardWidth = 10;
- SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
+ SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
+ break;
+ case VariantSChess:
+ SetCharTable(pieceToChar, "PNBRQ..HEKpnbrq..hek");
+ gameInfo.holdingsSize = 7;
break;
case VariantJanus:
pieces = JanusArray;
gameInfo.boardWidth = 10;
- SetCharTable(pieceToChar, "PNBRQ..JKpnbrq..jk");
+ SetCharTable(pieceToChar, "PNBRQ..JKpnbrq..jk");
nrCastlingRights = 6;
initialPosition[CASTLING][0] = initialRights[0] = BOARD_RGHT-1;
initialPosition[CASTLING][1] = initialRights[1] = BOARD_LEFT;
case VariantFalcon:
pieces = FalconArray;
gameInfo.boardWidth = 10;
- SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk");
+ SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk");
break;
case VariantXiangqi:
pieces = XiangqiArray;
gameInfo.boardWidth = 9;
gameInfo.boardHeight = 10;
nrCastlingRights = 0;
- SetCharTable(pieceToChar, "PH.R.AE..K.C.ph.r.ae..k.c.");
+ SetCharTable(pieceToChar, "PH.R.AE..K.C.ph.r.ae..k.c.");
break;
case VariantShogi:
pieces = ShogiArray;
gameInfo.boardHeight = 9;
gameInfo.holdingsSize = 7;
nrCastlingRights = 0;
- SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k");
+ SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k");
break;
case VariantCourier:
pieces = CourierArray;
gameInfo.boardWidth = 12;
nrCastlingRights = 0;
- SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk");
+ SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk");
break;
case VariantKnightmate:
pieces = KnightmateArray;
- SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k.");
+ SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k.");
+ break;
+ case VariantSpartan:
+ pieces = SpartanArray;
+ SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
break;
case VariantFairy:
pieces = fairyArray;
- SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
+ SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
break;
case VariantGreat:
pieces = GreatArray;
case VariantCrazyhouse:
case VariantBughouse:
pieces = FIDEArray;
- SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k");
+ SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k");
gameInfo.holdingsSize = 5;
break;
case VariantWildCastle:
pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
if(pawnRow < 1) pawnRow = 1;
- if(gameInfo.variant == VariantMakruk) pawnRow = 2;
+ if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) pawnRow = 2;
/* User pieceToChar list overrules defaults */
if(appData.pieceToCharTable != NULL)
initialPosition[i][j] = s;
if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue;
- initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth];
+ initialPosition[gameInfo.variant == VariantGrand][j] = pieces[0][j-gameInfo.holdingsWidth];
initialPosition[pawnRow][j] = WhitePawn;
- initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn;
+ initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn;
if(gameInfo.variant == VariantXiangqi) {
if(j&1) {
- initialPosition[pawnRow][j] =
+ initialPosition[pawnRow][j] =
initialPosition[BOARD_HEIGHT-pawnRow-1][j] = EmptySquare;
if(j==BOARD_LEFT+1 || j>=BOARD_RGHT-2) {
initialPosition[2][j] = WhiteCannon;
}
}
}
- initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j-gameInfo.holdingsWidth];
+ if(gameInfo.variant == VariantGrand) {
+ if(j==BOARD_LEFT || j>=BOARD_RGHT-1) {
+ initialPosition[0][j] = WhiteRook;
+ initialPosition[BOARD_HEIGHT-1][j] = BlackRook;
+ }
+ }
+ initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand)][j] = pieces[1][j-gameInfo.holdingsWidth];
}
if( (gameInfo.variant == VariantShogi) && !overrule ) {
/* This sets default castling rights from none to normal corners */
/* Variants with other castling rights must set them themselves above */
nrCastlingRights = 6;
-
+
initialPosition[CASTLING][0] = initialRights[0] = BOARD_RGHT-1;
initialPosition[CASTLING][1] = initialRights[1] = BOARD_LEFT;
initialPosition[CASTLING][2] = initialRights[2] = BOARD_WIDTH>>1;
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;
}
+ if( gameInfo.variant == VariantSChess ) {
+ initialPosition[1][0] = BlackMarshall;
+ initialPosition[2][0] = BlackAngel;
+ initialPosition[6][BOARD_WIDTH-1] = WhiteMarshall;
+ initialPosition[5][BOARD_WIDTH-1] = WhiteAngel;
+ initialPosition[1][1] = initialPosition[2][1] =
+ initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
+ }
if (appData.debugMode) {
fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
}
if(oldx != gameInfo.boardWidth ||
oldy != gameInfo.boardHeight ||
+ oldv != gameInfo.variant ||
oldh != gameInfo.holdingsWidth
-#ifdef GOTHIC
- || oldv == VariantGothic || // For licensing popups
- gameInfo.variant == VariantGothic
-#endif
-#ifdef FALCON
- || oldv == VariantFalcon ||
- gameInfo.variant == VariantFalcon
-#endif
)
InitDrawingSizes(-2 ,0);
+ oldv = gameInfo.variant;
if (redraw)
DrawPosition(TRUE, boards[currentMove]);
}
int moveNum;
{
char message[MSG_SIZ];
-
+
if (cps->useSetboard) {
char* fen = PositionToFEN(moveNum, cps->fenOverride);
- sprintf(message, "setboard %s\n", fen);
+ snprintf(message, MSG_SIZ,"setboard %s\n", fen);
SendToProgram(message, cps);
free(fen);
/* Kludge to set black to move, avoiding the troublesome and now
* deprecated "black" command.
*/
- if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
+ if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move...
+ SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps);
SendToProgram("edit\n", cps);
SendToProgram("#\n", cps);
bp = &boards[moveNum][i][BOARD_LEFT];
for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {
if ((int) *bp < (int) BlackPawn) {
- sprintf(message, "%c%c%c\n", PieceToChar(*bp),
+ snprintf(message, MSG_SIZ, "%c%c%c\n", PieceToChar(*bp),
AAA + j, ONE + i);
if(message[0] == '+' || message[0] == '~') {
- sprintf(message, "%c%c%c+\n",
+ snprintf(message, MSG_SIZ,"%c%c%c+\n",
PieceToChar((ChessSquare)(DEMOTED *bp)),
AAA + j, ONE + i);
}
}
}
}
-
+
SendToProgram("c\n", cps);
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
bp = &boards[moveNum][i][BOARD_LEFT];
for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {
if (((int) *bp != (int) EmptySquare)
&& ((int) *bp >= (int) BlackPawn)) {
- sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
+ snprintf(message,MSG_SIZ, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
AAA + j, ONE + i);
if(message[0] == '+' || message[0] == '~') {
- sprintf(message, "%c%c%c+\n",
+ snprintf(message, MSG_SIZ,"%c%c%c+\n",
PieceToChar((ChessSquare)(DEMOTED *bp)),
AAA + j, ONE + i);
}
}
}
}
-
+
SendToProgram(".\n", cps);
}
setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */
}
+ChessSquare
+DefaultPromoChoice(int white)
+{
+ ChessSquare result;
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
+ result = WhiteFerz; // no choice
+ else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway)
+ result= WhiteKing; // in Suicide Q is the last thing we want
+ else if(gameInfo.variant == VariantSpartan)
+ result = white ? WhiteQueen : WhiteAngel;
+ else result = WhiteQueen;
+ if(!white) result = WHITE_TO_BLACK result;
+ return result;
+}
+
static int autoQueen; // [HGM] oneclick
int
-HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
+HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice, int sweepSelect)
{
/* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
/* [HGM] add Shogi promotions */
piece = boards[currentMove][fromY][fromX];
if(gameInfo.variant == VariantShogi) {
- promotionZoneSize = 3;
+ promotionZoneSize = BOARD_HEIGHT/3;
highestPromotingPiece = (int)WhiteFerz;
- } else if(gameInfo.variant == VariantMakruk) {
+ } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) {
promotionZoneSize = 3;
}
+ // Treat Lance as Pawn when it is not representing Amazon
+ if(gameInfo.variant != VariantSuper) {
+ if(piece == WhiteLance) piece = WhitePawn; else
+ if(piece == BlackLance) piece = BlackPawn;
+ }
+
// next weed out all moves that do not touch the promotion zone at all
if((int)piece >= BlackPawn) {
if(toY >= promotionZoneSize && fromY >= promotionZoneSize)
*promoChoice = PieceToChar(BlackFerz); // no choice
return FALSE;
}
- if(autoQueen) { // predetermined
- if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers)
- *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want
- else *promoChoice = PieceToChar(BlackQueen);
+ // no sense asking what we must promote to if it is going to explode...
+ if(gameInfo.variant == VariantAtomic && boards[currentMove][toY][toX] != EmptySquare) {
+ *promoChoice = PieceToChar(BlackQueen); // Queen as good as any
return FALSE;
}
+ // give caller the default choice even if we will not make it
+ *promoChoice = ToLower(PieceToChar(defaultPromoChoice));
+ if(gameInfo.variant == VariantShogi) *promoChoice = (defaultPromoChoice == piece ? '=' : '+');
+ if( sweepSelect && gameInfo.variant != VariantGreat
+ && gameInfo.variant != VariantGrand
+ && gameInfo.variant != VariantSuper) return FALSE;
+ if(autoQueen) return FALSE; // predetermined
// suppress promotion popup on illegal moves that are not premoves
premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) ||
gameMode == IcsPlayingBlack && WhiteOnMove(currentMove);
if(appData.testLegality && !premove) {
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- fromY, fromX, toY, toX, NULLCHAR);
- if(moveType != WhitePromotionQueen && moveType != BlackPromotionQueen &&
- moveType != WhitePromotionKnight && moveType != BlackPromotionKnight)
+ fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR);
+ if(moveType != WhitePromotion && moveType != BlackPromotion)
return FALSE;
}
if (!white_piece && WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
return FALSE;
- }
+ }
if (white_piece && !WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is Black's turn"));
return FALSE;
- }
+ }
if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
/* Editing correspondence game history */
/* Could disallow this or prompt for confirmation */
}
}
break;
-
+
case Training:
if (!white_piece && WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
return FALSE;
- }
+ }
if (white_piece && !WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is Black's turn"));
return FALSE;
- }
+ }
break;
default:
Boolean
OnlyMove(int *x, int *y, Boolean captures) {
DisambiguateClosure cl;
- if (appData.zippyPlay) return FALSE;
+ if (appData.zippyPlay || !appData.testLegality) return FALSE;
switch(gameMode) {
case MachinePlaysBlack:
case IcsPlayingWhite:
case IcsPlayingBlack:
if(WhiteOnMove(currentMove)) return FALSE;
break;
+ case EditGame:
+ break;
default:
return FALSE;
}
- cl.pieceIn = EmptySquare;
+ cl.pieceIn = EmptySquare;
cl.rfIn = *y;
cl.ffIn = *x;
cl.rtIn = -1;
Disambiguate(boards[currentMove], PosFlags(currentMove), &cl);
if( cl.kind == NormalMove ||
cl.kind == AmbiguousMove && captures && cl.captures == 1 ||
- cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen ||
- cl.kind == WhitePromotionKnight || cl.kind == BlackPromotionKnight ||
+ cl.kind == WhitePromotion || cl.kind == BlackPromotion ||
cl.kind == WhiteCapturesEnPassant || cl.kind == BlackCapturesEnPassant) {
fromX = cl.ff;
fromY = cl.rf;
Disambiguate(boards[currentMove], PosFlags(currentMove), &cl);
if( cl.kind == NormalMove ||
cl.kind == AmbiguousMove && captures && cl.captures == 1 ||
- cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen ||
- cl.kind == WhitePromotionKnight || cl.kind == BlackPromotionKnight ||
+ cl.kind == WhitePromotion || cl.kind == BlackPromotion ||
cl.kind == WhiteCapturesEnPassant || cl.kind == BlackCapturesEnPassant) {
fromX = cl.ff;
fromY = cl.rf;
int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
int lastLoadGameUseList = FALSE;
char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
-ChessMove lastLoadGameStart = (ChessMove) 0;
+ChessMove lastLoadGameStart = EndOfFile;
-ChessMove
-UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
+void
+UserMoveEvent(fromX, fromY, toX, toY, promoChar)
int fromX, fromY, toX, toY;
int promoChar;
- Boolean captureOwn;
{
ChessMove moveType;
ChessSquare pdown, pup;
case IcsIdle:
/* We switched into a game mode where moves are not accepted,
perhaps while the mouse button was down. */
- return ImpossibleMove;
+ return;
case MachinePlaysWhite:
/* User is moving for Black */
if (WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
- return ImpossibleMove;
+ return;
}
break;
/* User is moving for White */
if (!WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is Black's turn"));
- return ImpossibleMove;
+ return;
}
break;
case BeginningOfGame:
case AnalyzeMode:
case Training:
+ if(fromY == DROP_RANK) break; // [HGM] drop moves (entered through move type-in) are automatically assigned to side-to-move
if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
(int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {
/* User is moving for Black */
if (WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
- return ImpossibleMove;
+ return;
}
} else {
/* User is moving for White */
if (!WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is Black's turn"));
- return ImpossibleMove;
+ return;
}
}
break;
premoveFromY = fromY;
premovePromoChar = promoChar;
gotPremove = 1;
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Got premove: fromX %d,"
"fromY %d, toX %d, toY %d\n",
fromX, fromY, toX, toY);
}
- return ImpossibleMove;
+ return;
}
break;
premoveFromY = fromY;
premovePromoChar = promoChar;
gotPremove = 1;
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Got premove: fromX %d,"
"fromY %d, toX %d, toY %d\n",
fromX, fromY, toX, toY);
}
- return ImpossibleMove;
+ return;
}
break;
click-click move is possible */
if (toX == -2 || toY == -2) {
boards[0][fromY][fromX] = EmptySquare;
- return AmbiguousMove;
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
} else if (toX >= 0 && toY >= 0) {
boards[0][toY][toX] = boards[0][fromY][fromX];
if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
if(boards[0][fromY][0] != EmptySquare) {
if(boards[0][fromY][1]) boards[0][fromY][1]--;
- if(boards[0][fromY][1] == 0) boards[0][fromY][0] = EmptySquare;
+ if(boards[0][fromY][1] == 0) boards[0][fromY][0] = EmptySquare;
}
} else
if(fromX == BOARD_RGHT+1) {
if(boards[0][fromY][BOARD_WIDTH-1] != EmptySquare) {
if(boards[0][fromY][BOARD_WIDTH-2]) boards[0][fromY][BOARD_WIDTH-2]--;
- if(boards[0][fromY][BOARD_WIDTH-2] == 0) boards[0][fromY][BOARD_WIDTH-1] = EmptySquare;
+ if(boards[0][fromY][BOARD_WIDTH-2] == 0) boards[0][fromY][BOARD_WIDTH-1] = EmptySquare;
}
} else
boards[0][fromY][fromX] = EmptySquare;
- return AmbiguousMove;
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
}
- return ImpossibleMove;
+ return;
}
- if(toX < 0 || toY < 0) return ImpossibleMove;
+ if(toX < 0 || toY < 0) return;
pdown = boards[currentMove][fromY][fromX];
pup = boards[currentMove][toY][toX];
- /* [HGM] If move started in holdings, it means a drop */
- if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {
- if( pup != EmptySquare ) return ImpossibleMove;
- if(appData.testLegality) {
- /* it would be more logical if LegalityTest() also figured out
- * which drops are legal. For now we forbid pawns on back rank.
- * Shogi is on its own here...
- */
- if( (pdown == WhitePawn || pdown == BlackPawn) &&
- (toY == 0 || toY == BOARD_HEIGHT -1 ) )
- return(ImpossibleMove); /* no pawn drops on 1st/8th */
- }
- return WhiteDrop; /* Not needed to specify white or black yet */
+ /* [HGM] If move started in holdings, it means a drop. Convert to standard form */
+ if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ) {
+ if( pup != EmptySquare ) return;
+ moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
+ if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
+ moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
+ // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
+ if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
+ fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
+ while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
+ fromY = DROP_RANK;
}
/* [HGM] always test for legality, to get promotion info */
if (appData.testLegality) {
if (moveType == IllegalMove || moveType == ImpossibleMove) {
DisplayMoveError(_("Illegal move"));
- return ImpossibleMove;
+ return;
}
}
- return moveType;
- /* [HGM] <popupFix> in stead of calling FinishMove directly, this
- function is made into one that returns an OK move type if FinishMove
- should be called. This to give the calling driver routine the
- opportunity to finish the userMove input with a promotion popup,
- without bothering the user with this for invalid or illegal moves */
-
-/* FinishMove(moveType, fromX, fromY, toX, toY, promoChar); */
+ FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
}
/* Common tail of UserMoveEvent and DropMenuEvent */
{
char *bookHit = 0;
- if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) {
- // [HGM] superchess: suppress promotions to non-available piece
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && promoChar != NULLCHAR) {
+ // [HGM] superchess: suppress promotions to non-available piece (but P always allowed)
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
if(WhiteOnMove(currentMove)) {
if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;
/* [HGM] <popupFix> kludge to avoid having to know the exact promotion
move type in caller when we know the move is a legal promotion */
if(moveType == NormalMove && promoChar)
- moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
-
- /* [HGM] convert drag-and-drop piece drops to standard form */
- if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ){
- moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
- if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
- moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
- // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
- if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
- fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
- while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
- fromY = DROP_RANK;
- }
+ moveType = WhiteOnMove(currentMove) ? WhitePromotion : BlackPromotion;
/* [HGM] <popupFix> The following if has been moved here from
UserMoveEvent(). Because it seemed to belong here (why not allow
performed after it is known to what we promote. */
if (gameMode == Training) {
/* compare the move played on the board to the next move in the
- * game. If they match, display the move and the opponent's response.
+ * game. If they match, display the move and the opponent's response.
* If they don't match, display an error message.
*/
int saveAnimate;
/* Ok, now we know that the move is good, so we can kill
the previous line in Analysis Mode */
- if ((gameMode == AnalyzeMode || gameMode == EditGame)
+ if ((gameMode == AnalyzeMode || gameMode == EditGame)
&& currentMove < forwardMostMove) {
- PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game
+ if(appData.variations && shiftKey) PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game
+ else forwardMostMove = currentMove;
}
/* If we need the chess program but it's dead, restart it */
MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
- if(Adjudicate(NULL)) return 1; // [HGM] adjudicate: take care of automtic game end
+ if(Adjudicate(NULL)) { // [HGM] adjudicate: take care of automatic game end
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ return 1;
+ }
if (gameMode == BeginningOfGame) {
if (appData.noChessProgram) {
gameMode = MachinePlaysBlack;
StartClocks();
SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
DisplayTitle(buf);
if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.white);
+ snprintf(buf, MSG_SIZ,"name %s\n", gameInfo.white);
SendToProgram(buf, &first);
}
StartClocks();
if(userOfferedDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS
SendToICS("draw ");
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar);
}
// also send plain move, in case ICS does not understand atomic claims
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar);
ics_user_moved = 1;
}
} else {
switch (gameMode) {
case EditGame:
+ if(appData.testLegality)
switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
}
break;
-
+
case MachinePlaysBlack:
case MachinePlaysWhite:
/* disable certain menu options while machine is thinking */
}
userOfferedDraw = FALSE; // [HGM] drawclaim: after move made, and tested for claimable draw
-
+ promoDefaultAltered = FALSE; // [HGM] fall back on default choice
+
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
HandleMachineMove(bookMove, &first);
}
}
void
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)
- int fromX, fromY, toX, toY;
- int promoChar;
-{
- /* [HGM] This routine was added to allow calling of its two logical
- parts from other modules in the old way. Before, UserMoveEvent()
- automatically called FinishMove() if the move was OK, and returned
- otherwise. I separated the two, in order to make it possible to
- slip a promotion popup in between. But that it always needs two
- calls, to the first part, (now called UserMoveTest() ), and to
- FinishMove if the first part succeeded. Calls that do not need
- to do anything in between, can call this routine the old way.
- */
- ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar, FALSE);
-if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);
- if(moveType == AmbiguousMove)
- DrawPosition(FALSE, boards[currentMove]);
- else if(moveType != ImpossibleMove && moveType != Comment)
- FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
-}
-
-void
Mark(board, flags, kind, rf, ff, rt, ft, closure)
Board board;
int flags;
MarkTargetSquares(int clear)
{
int x, y;
- if(!appData.markers || !appData.highlightDragging ||
+ if(!appData.markers || !appData.highlightDragging ||
!appData.testLegality || gameMode == EditPosition) return;
if(clear) {
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)
DrawPosition(TRUE, NULL);
}
+int
+Explode(Board board, int fromX, int fromY, int toX, int toY)
+{
+ if(gameInfo.variant == VariantAtomic &&
+ (board[toY][toX] != EmptySquare || // capture?
+ toX != fromX && (board[fromY][fromX] == WhitePawn || // e.p. ?
+ board[fromY][fromX] == BlackPawn )
+ )) {
+ AnimateAtomicCapture(board, fromX, fromY, toX, toY);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+ChessSquare gatingPiece = EmptySquare; // exported to front-end, for dragging
+
+int CanPromote(ChessSquare piece, int y)
+{
+ if(gameMode == EditPosition) return FALSE; // no promotions when editing position
+ // some variants have fixed promotion piece, no promotion at all, or another selection mechanism
+ if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantXiangqi ||
+ gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat ||
+ gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+ gameInfo.variant == VariantMakruk) return FALSE;
+ return (piece == BlackPawn && y == 1 ||
+ piece == WhitePawn && y == BOARD_HEIGHT-2 ||
+ piece == BlackLance && y == 1 ||
+ piece == WhiteLance && y == BOARD_HEIGHT-2 );
+}
+
void LeftClick(ClickType clickType, int xPix, int yPix)
{
int x, y;
Boolean saveAnimate;
- static int second = 0, promotionChoice = 0;
+ static int second = 0, promotionChoice = 0, clearFlag = 0;
char promoChoice = NULLCHAR;
+ ChessSquare piece;
if(appData.seekGraph && appData.icsActive && loggedOn &&
(gameMode == BeginningOfGame || gameMode == IcsIdle)) {
}
if (clickType == Press) ErrorPopDown();
- MarkTargetSquares(1);
x = EventToSquare(xPix, BOARD_WIDTH);
y = EventToSquare(yPix, BOARD_HEIGHT);
x = BOARD_WIDTH - 1 - x;
}
+ if(promoSweep != EmptySquare) { // up-click during sweep-select of promo-piece
+ defaultPromoChoice = promoSweep;
+ promoSweep = EmptySquare; // terminate sweep
+ promoDefaultAltered = TRUE;
+ if(!selectFlag && (x != toX || y != toY)) x = fromX, y = fromY; // and fake up-click on same square if we were still selecting
+ }
+
if(promotionChoice) { // we are waiting for a click to indicate promotion piece
if(clickType == Release) return; // ignore upclick of click-click destination
promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
- if(gameInfo.holdingsWidth &&
- (WhiteOnMove(currentMove)
- ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
- : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+ if(gameInfo.holdingsWidth &&
+ (WhiteOnMove(currentMove)
+ ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y >= 0
+ : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT) ) {
// click in right holdings, for determining promotion piece
ChessSquare p = boards[currentMove][y][x];
if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
- if(p != EmptySquare) {
- FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+ if(p == WhitePawn || p == BlackPawn) p = EmptySquare; // [HGM] Pawns could be valid as deferral
+ if(p != EmptySquare || gameInfo.variant == VariantGrand && toY != 0 && toY != BOARD_HEIGHT-1) { // [HGM] grand: empty square means defer
+ FinishMove(NormalMove, fromX, fromY, toX, toY, p==EmptySquare ? NULLCHAR : ToLower(PieceToChar(p)));
fromX = fromY = -1;
return;
}
|| x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
return;
+ if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered)
+ fromX = fromY = -1; // second click on piece after altering default promo piece treated as first click
+
+ if(!promoDefaultAltered) { // determine default promotion piece, based on the side the user is moving for
+ int side = (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack ||
+ gameMode != MachinePlaysWhite && gameMode != IcsPlayingBlack && WhiteOnMove(currentMove));
+ defaultPromoChoice = DefaultPromoChoice(side);
+ }
+
autoQueen = appData.alwaysPromoteToQueen;
if (fromX == -1) {
- if(!appData.oneClick || !OnlyMove(&x, &y, FALSE)) {
- if (clickType == Press) {
+ int originalY = y;
+ gatingPiece = EmptySquare;
+ if (clickType != Press) {
+ if(dragging) { // [HGM] from-square must have been reset due to game end since last press
+ DragPieceEnd(xPix, yPix); dragging = 0;
+ DrawPosition(FALSE, NULL);
+ }
+ return;
+ }
+ 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
+ appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) {
/* First square */
- if (OKToStartUserMove(x, y)) {
- fromX = x;
- fromY = y;
+ if (OKToStartUserMove(fromX, fromY)) {
second = 0;
MarkTargetSquares(0);
- DragPieceBegin(xPix, yPix);
+ DragPieceBegin(xPix, yPix, FALSE); dragging = 1;
+ if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) {
+ promoSweep = defaultPromoChoice;
+ selectFlag = 0; lastX = xPix; lastY = yPix;
+ Sweep(0); // Pawn that is going to promote: preview promotion piece
+ DisplayMessage("", _("Pull pawn backwards to under-promote"));
+ }
if (appData.highlightDragging) {
- SetHighlights(x, y, -1, -1);
+ SetHighlights(fromX, fromY, -1, -1);
}
- }
+ } else fromX = fromY = -1;
+ return;
}
- return;
- }
}
/* fromX != -1 */
/* Check if clicking again on the same color piece */
fromP = boards[currentMove][fromY][fromX];
toP = boards[currentMove][y][x];
- frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
+ frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom || gameInfo.variant == VariantSChess;
if ((WhitePawn <= fromP && fromP <= WhiteKing &&
WhitePawn <= toP && toP <= WhiteKing &&
!(fromP == WhiteKing && toP == WhiteRook && frc) &&
!(fromP == WhiteRook && toP == WhiteKing && frc)) ||
- (BlackPawn <= fromP && fromP <= BlackKing &&
+ (BlackPawn <= fromP && fromP <= BlackKing &&
BlackPawn <= toP && toP <= BlackKing &&
!(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling
!(fromP == BlackKing && toP == BlackRook && frc))) {
/* Clicked again on same color piece -- changed his mind */
second = (x == fromX && y == fromY);
- if(!second || !OnlyMove(&x, &y, TRUE)) {
+ promoDefaultAltered = FALSE;
+ MarkTargetSquares(1);
+ if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
} else {
ClearHighlights();
}
if (OKToStartUserMove(x, y)) {
+ if(gameInfo.variant == VariantSChess && // S-Chess: back-rank piece selected after holdings means gating
+ (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) &&
+ y == (toP < BlackPawn ? 0 : BOARD_HEIGHT-1))
+ gatingPiece = boards[currentMove][fromY][fromX];
+ else gatingPiece = EmptySquare;
fromX = x;
- fromY = y;
+ fromY = y; dragging = 1;
MarkTargetSquares(0);
- DragPieceBegin(xPix, yPix);
+ DragPieceBegin(xPix, yPix, FALSE);
+ if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
+ promoSweep = defaultPromoChoice;
+ selectFlag = 0; lastX = xPix; lastY = yPix;
+ Sweep(0); // Pawn that is going to promote: preview promotion piece
+ }
}
- return;
}
+ if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on
+ second = FALSE;
}
// ignore clicks on holdings
if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
}
if (clickType == Release && x == fromX && y == fromY) {
- DragPieceEnd(xPix, yPix);
+ DragPieceEnd(xPix, yPix); dragging = 0;
+ if(clearFlag) {
+ // a deferred attempt to click-click move an empty square on top of a piece
+ boards[currentMove][y][x] = EmptySquare;
+ ClearHighlights();
+ DrawPosition(FALSE, boards[currentMove]);
+ fromX = fromY = -1; clearFlag = 0;
+ return;
+ }
if (appData.animateDragging) {
/* Undo animation damage if any */
DrawPosition(FALSE, NULL);
/* Second up/down in same square; just abort move */
second = 0;
fromX = fromY = -1;
+ gatingPiece = EmptySquare;
ClearHighlights();
gotPremove = 0;
ClearPremoveHighlights();
return;
}
+ clearFlag = 0;
+
/* we now have a different from- and (possibly off-board) to-square */
/* Completed move */
toX = x;
toY = y;
saveAnimate = appData.animate;
+ MarkTargetSquares(1);
if (clickType == Press) {
+ if(gameMode == EditPosition && boards[currentMove][fromY][fromX] == EmptySquare) {
+ // must be Edit Position mode with empty-square selected
+ fromX = x; fromY = y; DragPieceBegin(xPix, yPix, FALSE); dragging = 1; // consider this a new attempt to drag
+ if(x >= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click
+ return;
+ }
+ if(appData.sweepSelect && HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
+ ChessSquare piece = boards[currentMove][fromY][fromX];
+ DragPieceBegin(xPix, yPix, TRUE); dragging = 1;
+ promoSweep = defaultPromoChoice;
+ if(PieceToChar(PROMOTED piece) == '+') promoSweep = PROMOTED piece;
+ selectFlag = 0; lastX = xPix; lastY = yPix;
+ Sweep(0); // Pawn that is going to promote: preview promotion piece
+ DisplayMessage("", _("Pull pawn backwards to under-promote"));
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
+ }
/* Finish clickclick move */
if (appData.animate || appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
} else {
ClearHighlights();
}
- DragPieceEnd(xPix, yPix);
+ DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
}
if(gameMode == EditPosition && piece != EmptySquare &&
fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {
int n;
-
+
if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
n = PieceToNumber(piece - (int)BlackPawn);
if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
}
// off-board moves should not be highlighted
- if(x < 0 || x < 0) ClearHighlights();
+ if(x < 0 || y < 0) ClearHighlights();
+
+ if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece));
- if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
+ if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
SetHighlights(fromX, fromY, toX, toY);
- if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ 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];
promotionChoice = TRUE;
}
PromotionPopUp();
} else {
+ int oldMove = currentMove;
UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
if (!appData.highlightLastMove || gotPremove) ClearHighlights();
if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+ if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed
+ Explode(boards[currentMove-1], fromX, fromY, toX, toY))
+ DrawPosition(TRUE, boards[currentMove]);
fromX = fromY = -1;
}
appData.animate = saveAnimate;
xSqr = EventToSquare(x, BOARD_WIDTH);
ySqr = EventToSquare(y, BOARD_HEIGHT);
- if (action == Release) UnLoadPV(); // [HGM] pv
+ if (action == Release) {
+ if(pieceSweep != EmptySquare) {
+ EditPositionMenuEvent(pieceSweep, toX, toY);
+ pieceSweep = EmptySquare;
+ } else UnLoadPV(); // [HGM] pv
+ }
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 < 0 || ySqr < 0) return -1;\r
- whichMenu = 0; // edit-position menu
- break;
+ 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;
case IcsObserving:
if(!appData.icsEngineAnalyze) return -1;
case IcsPlayingWhite:
stats.an_move_count = cpstats->nr_moves;
}
- if(stats.pv && stats.pv[0]) strcpy(lastPV[stats.which], stats.pv); // [HGM] pv: remember last PV of each
+ if(stats.pv && stats.pv[0]) safeStrCpy(lastPV[stats.which], stats.pv, sizeof(lastPV[stats.which])/sizeof(lastPV[stats.which][0])); // [HGM] pv: remember last PV of each
SetProgramStats( &stats );
}
+void
+ClearEngineOutputPane(int which)
+{
+ static FrontEndProgramStats dummyStats;
+ dummyStats.which = which;
+ dummyStats.pv = "#";
+ SetProgramStats( &dummyStats );
+}
+
+#define MAXPLAYERS 500
+
+char *
+TourneyStandings(int display)
+{
+ int i, w, b, color, wScore, bScore, dummy, nr=0, nPlayers=0;
+ int score[MAXPLAYERS], ranking[MAXPLAYERS], points[MAXPLAYERS], games[MAXPLAYERS];
+ char result, *p, *names[MAXPLAYERS];
+
+ 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
+
+ for(i=0; i<nPlayers; i++) score[i] = games[i] = 0;
+
+ while(result = appData.results[nr]) {
+ color = Pairing(nr, nPlayers, &w, &b, &dummy);
+ if(!(color ^ matchGame & 1)) { dummy = w; w = b; b = dummy; }
+ wScore = bScore = 0;
+ switch(result) {
+ case '+': wScore = 2; break;
+ case '-': bScore = 2; break;
+ case '=': wScore = bScore = 1; break;
+ case ' ':
+ case '*': return strdup("busy"); // tourney not finished
+ }
+ score[w] += wScore;
+ score[b] += bScore;
+ games[w]++;
+ games[b]++;
+ nr++;
+ }
+ if(appData.tourneyType > 0) nPlayers = appData.tourneyType; // in gauntlet, list only gauntlet engine(s)
+ for(w=0; w<nPlayers; w++) {
+ bScore = -1;
+ for(i=0; i<nPlayers; i++) if(score[i] > bScore) bScore = score[i], b = i;
+ ranking[w] = b; points[w] = bScore; score[b] = -2;
+ }
+ p = malloc(nPlayers*34+1);
+ for(w=0; w<nPlayers && w<display; w++)
+ sprintf(p+34*w, "%2d. %5.1f/%-3d %-19.19s\n", w+1, points[w]/2., games[ranking[w]], names[ranking[w]]);
+ free(names[0]);
+ return p;
+}
+
+void
+Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *bishopColor)
+{ // count all piece types
+ int p, f, r;
+ *nB = *nW = *wStale = *bStale = *bishopColor = 0;
+ for(p=WhitePawn; p<=EmptySquare; p++) pCnt[p] = 0;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+ p = board[r][f];
+ pCnt[p]++;
+ if(p == WhitePawn && r == BOARD_HEIGHT-1) (*wStale)++; else
+ if(p == BlackPawn && r == 0) (*bStale)++; // count last-Rank Pawns (XQ) separately
+ if(p <= WhiteKing) (*nW)++; else if(p <= BlackKing) (*nB)++;
+ if(p == WhiteBishop || p == WhiteFerz || p == WhiteAlfil ||
+ p == BlackBishop || p == BlackFerz || p == BlackAlfil )
+ *bishopColor |= 1 << ((f^r)&1); // track square color of color-bound pieces
+ }
+}
+
+int
+SufficientDefence(int pCnt[], int side, int nMine, int nHis)
+{
+ int myPawns = pCnt[WhitePawn+side]; // my total Pawn count;
+ int majorDefense = pCnt[BlackRook-side] + pCnt[BlackCannon-side] + pCnt[BlackKnight-side];
+
+ nMine -= pCnt[WhiteFerz+side] + pCnt[WhiteAlfil+side]; // discount defenders
+ if(nMine - myPawns > 2) return FALSE; // no trivial draws with more than 1 major
+ if(myPawns == 2 && nMine == 3) // KPP
+ return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 3;
+ if(myPawns == 1 && nMine == 2) // KP
+ return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] + pCnt[BlackPawn-side] >= 1;
+ if(myPawns == 1 && nMine == 3 && pCnt[WhiteKnight+side]) // KHP
+ return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side]*2 >= 5;
+ if(myPawns) return FALSE;
+ if(pCnt[WhiteRook+side])
+ return pCnt[BlackRook-side] ||
+ pCnt[BlackCannon-side] && (pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] >= 2) ||
+ pCnt[BlackKnight-side] && pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] > 2 ||
+ pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 4;
+ if(pCnt[WhiteCannon+side]) {
+ if(pCnt[WhiteFerz+side] + myPawns == 0) return TRUE; // Cannon needs platform
+ return majorDefense || pCnt[BlackAlfil-side] >= 2;
+ }
+ if(pCnt[WhiteKnight+side])
+ return majorDefense || pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] + pCnt[BlackPawn-side] >= 1;
+ return FALSE;
+}
+
+int
+MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisColor)
+{
+ VariantClass v = gameInfo.variant;
+
+ if(v == VariantShogi || v == VariantCrazyhouse || v == VariantBughouse) return TRUE; // drop games always winnable
+ if(v == VariantShatranj) return TRUE; // always winnable through baring
+ if(v == VariantLosers || v == VariantSuicide || v == VariantGiveaway) return TRUE;
+ if(v == Variant3Check || v == VariantAtomic) return nMine > 1; // can win through checking / exploding King
+
+ if(v == VariantXiangqi) {
+ int majors = 5*pCnt[BlackKnight-side] + 7*pCnt[BlackCannon-side] + 7*pCnt[BlackRook-side];
+
+ nMine -= pCnt[WhiteFerz+side] + pCnt[WhiteAlfil+side] + stale; // discount defensive pieces and back-rank Pawns
+ if(nMine + stale == 1) return (pCnt[BlackFerz-side] > 1 && pCnt[BlackKnight-side] > 0); // bare K can stalemate KHAA (!)
+ if(nMine > 2) return TRUE; // if we don't have P, H or R, we must have CC
+ if(nMine == 2 && pCnt[WhiteCannon+side] == 0) return TRUE; // We have at least one P, H or R
+ // if we get here, we must have KC... or KP..., possibly with additional A, E or last-rank P
+ if(stale) // we have at least one last-rank P plus perhaps C
+ return majors // KPKX
+ || pCnt[BlackFerz-side] && pCnt[BlackFerz-side] + pCnt[WhiteCannon+side] + stale > 2; // KPKAA, KPPKA and KCPKA
+ else // KCA*E*
+ return pCnt[WhiteFerz+side] // KCAK
+ || pCnt[WhiteAlfil+side] && pCnt[BlackRook-side] + pCnt[BlackCannon-side] + pCnt[BlackFerz-side] // KCEKA, KCEKX (X!=H)
+ || 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(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings
+ int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side];
+
+ if(nMine == 1) return FALSE; // bare King
+ if(nBishops && bisColor == 3) return TRUE; // There must be a second B/A/F, which can either block (his) or attack (mine) the escape square
+ nMine += (nBishops > 0) - nBishops; // By now all Bishops (and Ferz) on like-colored squares, so count as one
+ if(nMine > 2 && nMine != pCnt[WhiteAlfil+side] + 1) return TRUE; // At least two pieces, not all Alfils
+ // by now we have King + 1 piece (or multiple Bishops on the same color)
+ if(pCnt[WhiteKnight+side])
+ return (pCnt[BlackKnight-side] + pCnt[BlackBishop-side] + pCnt[BlackMan-side] +
+ pCnt[BlackWazir-side] + pCnt[BlackSilver-side] + bisColor // KNKN, KNKB, KNKF, KNKE, KNKW, KNKM, KNKS
+ || nHis > 3); // be sure to cover suffocation mates in corner (e.g. KNKQCA)
+ if(nBishops)
+ return (pCnt[BlackKnight-side]); // KBKN, KFKN
+ if(pCnt[WhiteAlfil+side])
+ return (nHis > 2); // Alfils can in general not reach a corner square, but there might be edge (suffocation) mates
+ if(pCnt[WhiteWazir+side])
+ return (pCnt[BlackKnight-side] + pCnt[BlackWazir-side] + pCnt[BlackAlfil-side]); // KWKN, KWKW, KWKE
+ }
+
+ return TRUE;
+}
+
int
Adjudicate(ChessProgramState *cps)
{ // [HGM] some adjudications useful with buggy engines
ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first));
Boolean canAdjudicate = !appData.icsActive;
- // most tests only when we understand the game, i.e. legality-checking on, and (for the time being) no piece drops
- if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ // most tests only when we understand the game, i.e. legality-checking on
if( appData.testLegality )
{ /* [HGM] Some more adjudications for obstinate engines */
- int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,
- NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,
- NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;
+ int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+1], i;
static int moveCount = 6;
ChessMove result;
char *reason = NULL;
/* Count what is on board. */
- for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
- { ChessSquare p = boards[forwardMostMove][i][j];
- int m=i;
-
- switch((int) p)
- { /* count B,N,R and other of each side */
- case WhiteKing:
- case BlackKing:
- NrK++; break; // [HGM] atomic: count Kings
- case WhiteKnight:
- NrWN++; break;
- case WhiteBishop:
- case WhiteFerz: // [HGM] shatranj: kludge to mke it work in shatranj
- bishopsColor |= 1 << ((i^j)&1);
- NrWB++; break;
- case BlackKnight:
- NrBN++; break;
- case BlackBishop:
- case BlackFerz: // [HGM] shatranj: kludge to mke it work in shatranj
- bishopsColor |= 1 << ((i^j)&1);
- NrBB++; break;
- case WhiteRook:
- NrWR++; break;
- case BlackRook:
- NrBR++; break;
- case WhiteQueen:
- NrWQ++; break;
- case BlackQueen:
- NrBQ++; break;
- case EmptySquare:
- break;
- case BlackPawn:
- m = 7-i;
- case WhitePawn:
- PawnAdvance += m; NrPawns++;
- }
- NrPieces += (p != EmptySquare);
- NrW += ((int)p < (int)BlackPawn);
- if(gameInfo.variant == VariantXiangqi &&
- (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {
- NrPieces--; // [HGM] XQ: do not count purely defensive pieces
- NrW -= ((int)p < (int)BlackPawn);
- }
- }
+ Count(boards[forwardMostMove], nr, &nrW, &nrB, &staleW, &staleB, &bishopColor);
/* Some material-based adjudications that have to be made before stalemate test */
- if(gameInfo.variant == VariantAtomic && NrK < 2) {
+ if(gameInfo.variant == VariantAtomic && nr[WhiteKing] + nr[BlackKing] < 2) {
// [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal
boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // make claimable as if stm is checkmated
if(canAdjudicate && appData.checkMates) {
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins,
+ GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins,
"Xboard adjudication: King destroyed", GE_XBOARD );
return 1;
}
}
/* Bare King in Shatranj (loses) or Losers (wins) */
- if( NrW == 1 || NrPieces - NrW == 1) {
+ if( nrW == 1 || nrB == 1) {
if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)
boards[forwardMostMove][EP_STATUS] = EP_WINS; // mark as win, so it becomes claimable
if(canAdjudicate && appData.checkMates) {
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets to see move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
"Xboard adjudication: Bare king", GE_XBOARD );
return 1;
}
/* but only adjudicate if adjudication enabled */
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn,
+ GameEnds( nrW > 1 ? WhiteWins : nrB > 1 ? BlackWins : GameIsDrawn,
"Xboard adjudication: Bare king", GE_XBOARD );
return 1;
}
if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers:
boards[forwardMostMove][EP_STATUS] = EP_WINS; // in these variants stalemated is always a win
else if(gameInfo.variant == VariantSuicide) // in suicide it depends
- boards[forwardMostMove][EP_STATUS] = NrW == NrPieces-NrW ? EP_STALEMATE :
- ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?
+ boards[forwardMostMove][EP_STATUS] = nrW == nrB ? EP_STALEMATE :
+ ((nrW < nrB) != WhiteOnMove(forwardMostMove) ?
EP_CHECKMATE : EP_WINS);
else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)
boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses
case EP_WINS:
result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break;
default:
- result = (ChessMove) 0;
+ result = EndOfFile;
}
if(canAdjudicate && appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( result, reason, GE_XBOARD );
return 1;
}
/* Next absolutely insufficient mating material. */
- if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&
- gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible
- (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||
- NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color
- { /* KBK, KNK, KK of KBKB with like Bishops */
+ if(!MatingPotential(nr, WhitePawn, nrW, nrB, staleW, bishopColor) &&
+ !MatingPotential(nr, BlackPawn, nrB, nrW, staleB, bishopColor))
+ { /* includes KBK, KNK, KK of KBKB with like Bishops */
/* always flag draws, for judging claims */
boards[forwardMostMove][EP_STATUS] = EP_INSUF_DRAW;
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see last move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );
return 1;
}
}
/* Then some trivial draws (only adjudicate, cannot be claimed) */
- if(NrPieces == 4 &&
- ( NrWR == 1 && NrBR == 1 /* KRKR */
- || NrWQ==1 && NrBQ==1 /* KQKQ */
- || NrWN==2 || NrBN==2 /* KNNK */
- || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */
- ) ) {
- if(canAdjudicate && --moveCount < 0 && appData.trivialDraws)
+ if(gameInfo.variant == VariantXiangqi ?
+ SufficientDefence(nr, WhitePawn, nrW, nrB) && SufficientDefence(nr, BlackPawn, nrB, nrW)
+ : nrW + nrB == 4 &&
+ ( nr[WhiteRook] == 1 && nr[BlackRook] == 1 /* KRKR */
+ || nr[WhiteQueen] && nr[BlackQueen]==1 /* KQKQ */
+ || nr[WhiteKnight]==2 || nr[BlackKnight]==2 /* KNNK */
+ || nr[WhiteKnight]+nr[WhiteBishop] == 1 && nr[BlackKnight]+nr[BlackBishop] == 1 /* KBKN, KBKB, KNKN */
+ ) ) {
+ if(--moveCount < 0 && appData.trivialDraws && canAdjudicate)
{ /* if the first 3 moves do not show a tactical win, declare draw */
if(engineOpponent) {
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );
return 1;
}
} 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
}
if( boards[forwardMostMove][CASTLING][5] != boards[k][CASTLING][5] &&
(boards[k][CASTLING][3] != NoRights || boards[k][CASTLING][4] != NoRights) )
- rights++;
+ rights++;
if( boards[forwardMostMove][CASTLING][5] != NoRights ) {
if( boards[forwardMostMove][CASTLING][3] != boards[k][CASTLING][3] ||
boards[forwardMostMove][CASTLING][4] != boards[k][CASTLING][4] )
rights++;
}
- if( canAdjudicate && rights == 0 && ++count > appData.drawRepeats-2
+ if( rights == 0 && ++count > appData.drawRepeats-2 && canAdjudicate
&& appData.drawRepeats > 1) {
/* adjudicate after user-specified nr of repeats */
- if(engineOpponent) {
- SendToProgram("force\n", engineOpponent); // suppress reply
- SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
- }
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- if(gameInfo.variant == VariantXiangqi && appData.testLegality) {
+ int result = GameIsDrawn;
+ char *details = "XBoard adjudication: repetition draw";
+ if(gameInfo.variant == VariantXiangqi && appData.testLegality) {
// [HGM] xiangqi: check for forbidden perpetuals
int m, ourPerpetual = 1, hisPerpetual = 1;
for(m=forwardMostMove; m>k; m-=2) {
if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",
ourPerpetual, hisPerpetual);
if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication: perpetual checking", GE_XBOARD );
- return 1;
- }
- if(hisPerpetual && !ourPerpetual) // he is checking us, but did not repeat yet
+ result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+ details = "Xboard adjudication: perpetual checking";
+ } else
+ 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
// Now check for perpetual chases
if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase
hisPerpetual = PerpetualChase(k, forwardMostMove);
ourPerpetual = PerpetualChase(k+1, forwardMostMove);
if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication: perpetual chasing", GE_XBOARD );
- return 1;
- }
+ result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+ details = "Xboard adjudication: perpetual chasing";
+ } else
if(hisPerpetual && !ourPerpetual) // he is chasing us, but did not repeat yet
break; // Abort repetition-checking loop.
}
// if neither of us is checking or chasing all the time, or both are, it is draw
}
- GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );
+ if(engineOpponent) {
+ SendToProgram("force\n", engineOpponent); // suppress reply
+ SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
+ }
+ GameEnds( result, details, GE_XBOARD );
return 1;
}
if( rights == 0 && count > 1 ) /* occurred 2 or more times before */
/* if we hit starting position, add initial plies */
if( count == backwardMostMove )
count -= initialRulePlies;
- count = forwardMostMove - count;
+ count = forwardMostMove - count;
+ if(gameInfo.variant == VariantXiangqi && ( count >= 100 || count >= 2*appData.ruleMoves ) ) {
+ // adjust reversible move counter for checks in Xiangqi
+ int i = forwardMostMove - count, inCheck = 0, lastCheck;
+ if(i < backwardMostMove) i = backwardMostMove;
+ while(i <= forwardMostMove) {
+ lastCheck = inCheck; // check evasion does not count
+ inCheck = (MateTest(boards[i], PosFlags(i)) == MT_CHECK);
+ if(inCheck || lastCheck) count--; // check does not count
+ i++;
+ }
+ }
if( count >= 100)
boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
/* this is used to judge if draw claims are legal */
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, p, GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );
return 1;
}
if(bookHit) {
// after a book hit we never send 'go', and the code after the call to this routine
// has '&& !bookHit' added to suppress potential sending there (based on 'firstMove').
- char buf[MSG_SIZ];
- if (cps->useUsermove) sprintf(buf, "usermove "); // sorry, no SAN yet :(
- sprintf(buf, "%s\n", bookHit); // force book move into program supposed to play it
+ char buf[MSG_SIZ], *move = bookHit;
+ if(cps->useSAN) {
+ int fromX, fromY, toX, toY;
+ char promoChar;
+ ChessMove moveType;
+ move = buf + 30;
+ if (ParseOneMove(bookHit, forwardMostMove, &moveType,
+ &fromX, &fromY, &toX, &toY, &promoChar)) {
+ (void) CoordsToAlgebraic(boards[forwardMostMove],
+ PosFlags(forwardMostMove),
+ fromY, fromX, toY, toX, promoChar, move);
+ } else {
+ if(appData.debugMode) fprintf(debugFP, "Book move could not be parsed\n");
+ bookHit = NULL;
+ }
+ }
+ snprintf(buf, MSG_SIZ, "%s%s\n", (cps->useUsermove ? "usermove " : ""), move); // force book move into program supposed to play it
SendToProgram(buf, cps);
if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go'
} else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine
cps->bookSuspend = FALSE; // after a 'go' we are never suspended
} else { // 'go' might be sent based on 'firstMove' after this routine returns
if(cps->bookSuspend && !firstMove) // 'go' needed, and it will not be done after we return
- SendToProgram("go\n", cps);
+ SendToProgram("go\n", cps);
cps->bookSuspend = FALSE; // anyhow, we will not be suspended after a miss
}
return bookHit; // notify caller of hit, so it can take action to send move to opponent
HandleMachineMove(savedMessage, savedState);
}
+static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+
void
HandleMachineMove(message, cps)
char *message;
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) {
+ DisplayError(_("Invalid pairing from pairing engine"), 0);
+ return;
+ }
+ pairingReceived = 1;
+ NextMatchGame();
+ return; // Skim the pairing messages here.
+ }
+
cps->userError = 0;
FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit
* Look for machine move.
*/
if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
- (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
+ (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
{
/* This method is only useful on engines that support ping */
if (cps->lastPing != cps->lastPong) {
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)) {
/* Machine move could not be parsed; ignore it. */
- sprintf(buf1, _("Illegal move \"%s\" from %s machine"),
- machineMove, cps->which);
+ snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
+ machineMove, _(cps->which));
DisplayError(buf1, 0);
- sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
+ 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) {
GameEnds(machineWhite ? BlackWins : WhiteWins,
/* to make sure an illegal e.p. capture does not slip through, */
/* to cause a forfeit on a justified illegal-move complaint */
/* of the opponent. */
- if( gameMode==TwoMachinesPlay && appData.testLegality
- && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */
- ) {
+ if( gameMode==TwoMachinesPlay && appData.testLegality ) {
ChessMove moveType;
moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),
fromY, fromX, toY, toX, promoChar);
fprintf(debugFP, "castling rights\n");
}
if(moveType == IllegalMove) {
- sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",
+ 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);
GameEnds(machineWhite ? BlackWins : WhiteWins,
buf1, GE_XBOARD);
if (cps->sendTime == 2) cps->sendTime = 1;
if (cps->offeredDraw) cps->offeredDraw--;
- /* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(machineMove, currentMoveString);
- strcat(machineMove, "\n");
- strcpy(moveList[forwardMostMove], machineMove);
+ /* [AS] Save move info*/
+ pvInfoList[ forwardMostMove ].score = programStats.score;
+ pvInfoList[ forwardMostMove ].depth = programStats.depth;
+ pvInfoList[ forwardMostMove ].time = programStats.time; // [HGM] PGNtime: take time from engine stats
MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
if( count >= adjudicateLossPlies ) {
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication",
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
+ "Xboard adjudication",
GE_XBOARD );
return;
}
}
- if(Adjudicate(cps)) return; // [HGM] adjudicate: for all automatic game ends
+ if(Adjudicate(cps)) {
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ return; // [HGM] adjudicate: for all automatic game ends
+ }
#if ZIPPY
if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
if(cps->offeredDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS
SendToICS("draw ");
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar);
}
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar);
ics_user_moved = 1;
if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */
char buf[3*MSG_SIZ];
- sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
+ snprintf(buf, 3*MSG_SIZ, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
programStats.score / 100.,
programStats.depth,
programStats.time / 100.,
}
#endif
- /* [AS] Save move info and clear stats for next move */
- pvInfoList[ forwardMostMove-1 ].score = programStats.score;
- pvInfoList[ forwardMostMove-1 ].depth = programStats.depth;
- pvInfoList[ forwardMostMove-1 ].time = programStats.time; // [HGM] PGNtime: take time from engine stats
+ /* [AS] Clear stats for next move */
ClearProgramStats();
thinkOutput[0] = NULLCHAR;
hiddenThinkOutputState = 0;
}
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
+
if (!pausing && appData.ringBellAfterMoves) {
RingBell();
}
- /*
+ /*
* Reenable menu items that were disabled while
* machine was thinking
*/
if(bookHit) {
static char bookMove[MSG_SIZ]; // a bit generous?
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
message = bookMove;
cps = cps->other;
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
}
+ if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
+ int dummy, s=6; char buf[MSG_SIZ];
+ if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return;
+ if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+ ParseFEN(boards[0], &dummy, message+s);
+ DrawPosition(TRUE, boards[0]);
+ startedFromSetupPosition = TRUE;
+ return;
+ }
/* [HGM] Allow engine to set up a position. Don't ask me why one would
* want this, I was asked to put it in, and obliged.
*/
CopyBoard(boards[0], initial_position);
initialRulePlies = FENrulePlies;
if(blackPlaysFirst) gameMode = MachinePlaysWhite;
- else gameMode = MachinePlaysBlack;
+ else gameMode = MachinePlaysBlack;
DrawPosition(FALSE, boards[currentMove]);
}
return;
* Look for communication commands
*/
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;
}
if (!strncmp(message, "tellusererror ", 14)) {
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;
}
return;
}
if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
- strcpy(realname, cps->tidy);
+ safeStrCpy(realname, cps->tidy, sizeof(realname)/sizeof(realname[0]));
strcat(realname, " query");
AskQuestion(realname, buf2, buf1, cps->pr);
return;
}
- /* Commands from the engine directly to ICS. We don't allow these to be
- * sent until we are logged on. Crafty kibitzes have been known to
+ /* Commands from the engine directly to ICS. We don't allow these to be
+ * sent until we are logged on. Crafty kibitzes have been known to
* interfere with the login process.
*/
if (loggedOn) {
*/
if (strncmp(message + 1, "llegal move", 11) == 0 ||
strncmp(message, "Error", 5) == 0) {
- if (StrStr(message, "name") ||
+ if (StrStr(message, "name") ||
StrStr(message, "rating") || StrStr(message, "?") ||
StrStr(message, "result") || StrStr(message, "board") ||
StrStr(message, "bk") || StrStr(message, "computer") ||
cps->analysisSupport = FALSE;
cps->analyzing = FALSE;
Reset(FALSE, TRUE);
- sprintf(buf2, _("%s does not support analysis"), cps->tidy);
+ snprintf(buf2,MSG_SIZ, _("%s does not support analysis"), cps->tidy);
DisplayError(buf2, 0);
return;
}
gameMode = EditGame;
ModeHighlight();
}
- currentMove = forwardMostMove-1;
- DisplayMove(currentMove-1); /* before DisplayMoveError */
- SwitchClocks(forwardMostMove-1); // [HGM] race
- DisplayBothClocks();
- sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),
- parseList[currentMove], cps->which);
- DisplayMoveError(buf1);
- DrawPosition(FALSE, boards[currentMove]);
-
/* [HGM] illegal-move claim should forfeit game when Xboard */
/* only passes fully legal moves */
if( appData.testLegality && gameMode == TwoMachinesPlay ) {
GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
"False illegal-move claim", GE_XBOARD );
+ return; // do not take back move we tested as valid
}
+ currentMove = forwardMostMove-1;
+ DisplayMove(currentMove-1); /* before DisplayMoveError */
+ SwitchClocks(forwardMostMove-1); // [HGM] race
+ DisplayBothClocks();
+ snprintf(buf1, 10*MSG_SIZ, _("Illegal move \"%s\" (rejected by %s chess program)"),
+ parseList[currentMove], _(cps->which));
+ DisplayMoveError(buf1);
+ DrawPosition(FALSE, boards[currentMove]);
return;
}
if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
Don't use it. */
cps->sendTime = 0;
}
-
+
/*
* If chess program startup fails, exit with an error message.
* Attempts to recover here are futile.
cps->maybeThinking = FALSE;
snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),
- cps->which, cps->program, cps->host, message);
+ _(cps->which), cps->program, cps->host, message);
RemoveInputSource(cps->isr);
- DisplayFatalError(buf1, 0, 1);
+ if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else {
+ if(cps == &first) appData.noChessProgram = TRUE;
+ DisplayError(buf1, 0);
+ }
return;
}
-
- /*
+
+ /*
* Look for hint output
*/
if (sscanf(message, "Hint: %s", buf1) == 1) {
/* Hint move could not be parsed!? */
snprintf(buf2, sizeof(buf2),
_("Illegal hint move \"%s\"\nfrom %s chess program"),
- buf1, cps->which);
+ buf1, _(cps->which));
DisplayError(buf2, 0);
}
} else {
- strcpy(lastHint, buf1);
+ safeStrCpy(lastHint, buf1, sizeof(lastHint)/sizeof(lastHint[0]));
}
return;
}
r = p + 1;
}
}
-
+
GameEnds(GameIsDrawn, r, GE_ENGINE1 + (cps != &first));
return;
}
}
-
+
/*
* Look for thinking output
*/
}
if (!ignore) {
+ ChessProgramStats tempStats = programStats; // [HGM] info: filter out info lines
buf1[0] = NULLCHAR;
if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
&plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
}
/* [AS] Negate score if machine is playing black and reporting absolute scores */
- if( cps->scoreIsAbsolute &&
+ if( cps->scoreIsAbsolute &&
( gameMode == MachinePlaysBlack ||
gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b' ||
gameMode == IcsPlayingBlack || // [HGM] also add other situations where engine should report black POV
curscore = -curscore;
}
+ if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1);
- programStats.depth = plylev;
- programStats.nodes = nodes;
- programStats.time = time;
- programStats.score = curscore;
- programStats.got_only_move = 0;
+ tempStats.depth = plylev;
+ tempStats.nodes = nodes;
+ tempStats.time = time;
+ tempStats.score = curscore;
+ tempStats.got_only_move = 0;
if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */
int ticklen;
if(cps->nps == 0) ticklen = 10*time; // use engine reported time
else ticklen = (1000. * u64ToDouble(nodes)) / cps->nps; // convert node count to time
if(WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite ||
- gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w'))
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w'))
whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;
if(!WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b'))
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b'))
blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;
}
/* Buffer overflow protection */
- if (buf1[0] != NULLCHAR) {
- if (strlen(buf1) >= sizeof(programStats.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(programStats.movelist) - 1);
+ (unsigned) sizeof(tempStats.movelist) - 1);
}
- safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
+ safeStrCpy( tempStats.movelist, pv, sizeof(tempStats.movelist)/sizeof(tempStats.movelist[0]) );
} else {
- sprintf(programStats.movelist, " no PV\n");
+ sprintf(tempStats.movelist, " no PV\n");
}
- if (programStats.seen_stat) {
- programStats.ok_to_send = 1;
+ if (tempStats.seen_stat) {
+ tempStats.ok_to_send = 1;
}
- if (strchr(programStats.movelist, '(') != NULL) {
- programStats.line_is_book = 1;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
+ if (strchr(tempStats.movelist, '(') != NULL) {
+ tempStats.line_is_book = 1;
+ tempStats.nr_moves = 0;
+ tempStats.moves_left = 0;
} else {
- programStats.line_is_book = 0;
+ tempStats.line_is_book = 0;
}
- SendProgramStatsToFrontend( cps, &programStats );
+ if(tempStats.score != 0 || tempStats.nodes != 0 || tempStats.time != 0)
+ programStats = tempStats; // [HGM] info: only set stats if genuine PV and not an info line
+
+ SendProgramStatsToFrontend( cps, &tempStats );
- /*
+ /*
[AS] Protect the thinkOutput buffer from overflow... this
is only useful if buf1 hasn't overflowed first!
*/
- sprintf(thinkOutput, "[%d]%c%+.2f %s%s",
- plylev,
- (gameMode == TwoMachinesPlay ?
- ToUpper(cps->twoMachinesColor[0]) : ' '),
- ((double) curscore) / 100.0,
- prefixHint ? lastHint : "",
- prefixHint ? " " : "" );
+ snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%+.2f %s%s",
+ plylev,
+ (gameMode == TwoMachinesPlay ?
+ ToUpper(cps->twoMachinesColor[0]) : ' '),
+ ((double) curscore) / 100.0,
+ prefixHint ? lastHint : "",
+ prefixHint ? " " : "" );
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
* if there is only 1 legal move
*/
sscanf(p, "(only move) %s", buf1);
- sprintf(thinkOutput, "%s (only move)", buf1);
+ snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "%s (only move)", buf1);
sprintf(programStats.movelist, "%s (only move)", buf1);
programStats.depth = 1;
programStats.nr_moves = 1;
programStats.line_is_book = 1;
SendProgramStatsToFrontend( cps, &programStats );
-
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
+
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
}
programStats.nodes = nodes;
programStats.moves_left = mvleft;
programStats.nr_moves = mvtot;
- strcpy(programStats.move_name, mvname);
+ safeStrCpy(programStats.move_name, mvname, sizeof(programStats.move_name)/sizeof(programStats.move_name[0]));
programStats.ok_to_send = 1;
programStats.movelist[0] = '\0';
buf1[0] = NULLCHAR;
if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
- &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5)
+ &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5)
{
ChessProgramStats cpstats;
cpstats.movelist[0] = '\0';
if (buf1[0] != NULLCHAR) {
- safeStrCpy( cpstats.movelist, buf1, sizeof(cpstats.movelist) );
+ safeStrCpy( cpstats.movelist, buf1, sizeof(cpstats.movelist)/sizeof(cpstats.movelist[0]) );
}
cpstats.ok_to_send = 0;
/* Parse a game score from the character string "game", and
record it as the history of the current game. The game
- score is NOT assumed to start from the standard position.
+ score is NOT assumed to start from the standard position.
The display is not updated in any way.
*/
void
yynewstr(game);
for (;;) {
yyboardindex = boardIndex;
- moveType = (ChessMove) yylex();
+ moveType = (ChessMove) Myylex();
switch (moveType) {
case IllegalMove: /* maybe suicide chess, etc. */
if (appData.debugMode) {
fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
setbuf(debugFP, NULL);
}
- case WhitePromotionChancellor:
- case BlackPromotionChancellor:
- case WhitePromotionArchbishop:
- case BlackPromotionArchbishop:
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
+ case WhitePromotion:
+ case BlackPromotion:
+ case WhiteNonPromotion:
+ case BlackNonPromotion:
case NormalMove:
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
break;
case AmbiguousMove:
/* bug? */
- sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text);
+ snprintf(buf, MSG_SIZ, _("Ambiguous move in ICS output: \"%s\""), yy_text);
if (appData.debugMode) {
fprintf(debugFP, "Ambiguous move from ICS: '%s'\n", yy_text);
fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
return;
case ImpossibleMove:
/* bug? */
- sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text);
+ snprintf(buf, MSG_SIZ, _("Illegal move in ICS output: \"%s\""), yy_text);
if (appData.debugMode) {
fprintf(debugFP, "Impossible move from ICS: '%s'\n", yy_text);
fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
}
DisplayError(buf, 0);
return;
- case (ChessMove) 0: /* end of file */
+ case EndOfFile:
if (boardIndex < backwardMostMove) {
/* Oops, gap. How did that happen? */
DisplayError(_("Gap in move list"), 0);
if (q != NULL) *q = NULLCHAR;
p++;
}
+ while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message
gameInfo.resultDetails = StrSave(p);
continue;
}
parseList[boardIndex]);
CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
/* currentMoveString is set as a side-effect of yylex */
- strcpy(moveList[boardIndex], currentMoveString);
+ safeStrCpy(moveList[boardIndex], currentMoveString, sizeof(moveList[boardIndex])/sizeof(moveList[boardIndex][0]));
strcat(moveList[boardIndex], "\n");
boardIndex++;
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
Board board;
{
ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
- int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
+ int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
/* [HGM] compute & store e.p. status and castling rights for new position */
/* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
- { int i;
if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
oldEP = (signed char)board[EP_STATUS];
board[EP_STATUS] = EP_NONE;
- if( board[toY][toX] != EmptySquare )
- board[EP_STATUS] = EP_CAPTURE;
+ if( board[toY][toX] != EmptySquare )
+ board[EP_STATUS] = EP_CAPTURE;
+
+ if (fromY == DROP_RANK) {
+ /* must be first */
+ piece = board[toY][toX] = (ChessSquare) fromX;
+ } else {
+ int i;
+ if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
+ if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
+ board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants
+ } else
if( board[fromY][fromX] == WhitePawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
board[EP_STATUS] = EP_PAWN_MOVE;
gameInfo.variant != VariantBerolina || toX < fromX)
board[EP_STATUS] = toX | berolina;
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
- gameInfo.variant != VariantBerolina || toX > fromX)
+ gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
}
- } else
+ } else
if( board[fromY][fromX] == BlackPawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
- board[EP_STATUS] = EP_PAWN_MOVE;
+ board[EP_STATUS] = EP_PAWN_MOVE;
if( toY-fromY== -2) {
if(toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn &&
gameInfo.variant != VariantBerolina || toX < fromX)
board[EP_STATUS] = toX | berolina;
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
- gameInfo.variant != VariantBerolina || toX > fromX)
+ gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
}
}
for(i=0; i<nrCastlingRights; i++) {
if(board[CASTLING][i] == fromX && castlingRank[i] == fromY ||
- board[CASTLING][i] == toX && castlingRank[i] == toY
+ board[CASTLING][i] == toX && castlingRank[i] == toY
) board[CASTLING][i] = NoRights; // revoke for moved or captured piece
}
- }
-
- /* [HGM] In Shatranj and Courier all promotions are to Ferz */
- if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk)
- && promoChar != 0) promoChar = PieceToChar(WhiteFerz);
-
- if (fromX == toX && fromY == toY) return;
+ if (fromX == toX && fromY == toY) return;
- if (fromY == DROP_RANK) {
- /* must be first */
- piece = board[toY][toX] = (ChessSquare) fromX;
- } else {
piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */
king = piece < (int) BlackPawn ? WhiteKing : BlackKing; /* [HGM] Knightmate simplify testing for castling */
if(gameInfo.variant == VariantKnightmate)
king += (int) WhiteUnicorn - (int) WhiteKing;
/* Code added by Tord: */
- /* FRC castling assumed when king captures friendly rook. */
- if (board[fromY][fromX] == WhiteKing &&
- board[toY][toX] == WhiteRook) {
+ /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */
+ if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook ||
+ board[fromY][fromX] == WhiteRook && board[toY][toX] == WhiteKing) {
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
- if(toX > fromX) {
+ if((toX > fromX) != (piece == WhiteRook)) {
board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook;
} else {
board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook;
}
- } else if (board[fromY][fromX] == BlackKing &&
- board[toY][toX] == BlackRook) {
+ } else if (board[fromY][fromX] == BlackKing && board[toY][toX] == BlackRook ||
+ board[fromY][fromX] == BlackRook && board[toY][toX] == BlackKing) {
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
- if(toX > fromX) {
+ if((toX > fromX) != (piece == BlackRook)) {
board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook;
} else {
board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook;
board[toY][toX] = king;
board[toY][toX+1] = board[fromY][BOARD_LEFT];
board[fromY][BOARD_LEFT] = EmptySquare;
- } else if (board[fromY][fromX] == WhitePawn
- && toY >= BOARD_HEIGHT-promoRank
- && gameInfo.variant != VariantXiangqi
+ } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi ||
+ board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
+ && toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere
) {
/* white pawn promotion */
board[toY][toX] = CharToPiece(ToUpper(promoChar));
- if (board[toY][toX] == EmptySquare) {
- board[toY][toX] = WhiteQueen;
- }
if(gameInfo.variant==VariantBughouse ||
gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
- } else if ((fromY == BOARD_HEIGHT-4)
+ } else if ((fromY >= BOARD_HEIGHT>>1)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
board[toY][toX] = BlackKing;
board[fromY][0] = EmptySquare;
board[toY][2] = BlackRook;
- } else if (board[fromY][fromX] == BlackPawn
- && toY < promoRank
- && gameInfo.variant != VariantXiangqi
+ } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi ||
+ board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
+ && toY < promoRank && promoChar
) {
/* black pawn promotion */
board[toY][toX] = CharToPiece(ToLower(promoChar));
- if (board[toY][toX] == EmptySquare) {
- board[toY][toX] = BlackQueen;
- }
if(gameInfo.variant==VariantBughouse ||
gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
- } else if ((fromY == 3)
+ } else if ((fromY < BOARD_HEIGHT>>1)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
board[toY][toX] = board[fromY][fromX];
board[fromY][fromX] = EmptySquare;
}
-
- /* [HGM] now we promote for Shogi, if needed */
- if(gameInfo.variant == VariantShogi && promoChar == 'q')
- board[toY][toX] = (ChessSquare) (PROMOTED piece);
}
if (gameInfo.holdingsWidth != 0) {
/* [HGM] OK, so I have written it. Holdings are stored in the */
/* penultimate board files, so they are automaticlly stored */
/* in the game history. */
- if (fromY == DROP_RANK) {
+ if (fromY == DROP_RANK || gameInfo.variant == VariantSChess
+ && promoChar && piece != WhitePawn && piece != BlackPawn) {
/* Delete from holdings, by decreasing count */
/* and erasing image if necessary */
- p = (int) fromX;
+ p = fromY == DROP_RANK ? (int) fromX : CharToPiece(piece > BlackPawn ? ToLower(promoChar) : ToUpper(promoChar));
if(p < (int) BlackPawn) { /* white drop */
p -= (int)WhitePawn;
p = PieceToNumber((ChessSquare)p);
}
}
if (captured != EmptySquare && gameInfo.holdingsSize > 0
- && gameInfo.variant != VariantBughouse ) {
+ && gameInfo.variant != VariantBughouse && gameInfo.variant != VariantSChess ) {
/* [HGM] holdings: Add to holdings, if holdings exist */
- if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
// [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip
captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;
}
board[toY][toX] = EmptySquare;
}
}
- if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') {
- /* [HGM] Shogi promotions */
+ if(gameInfo.variant == VariantSChess && promoChar != NULLCHAR && promoChar != '=' && piece != WhitePawn && piece != BlackPawn) {
+ board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating
+ } else
+ if(promoChar == '+') {
+ /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */
board[toY][toX] = (ChessSquare) (PROMOTED piece);
+ } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
+ board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
}
-
- if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
- && promoChar != NULLCHAR && gameInfo.holdingsSize) {
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
+ && promoChar != NULLCHAR && gameInfo.holdingsSize) {
// [HGM] superchess: take promotion piece out of holdings
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
if((int)piece < (int)BlackPawn) { // determine stm from piece color
if(gameInfo.variant == VariantKnightmate)
king += (int) WhiteUnicorn - (int) WhiteKing;
if(forwardMostMove == 0) {
- if(blackPlaysFirst)
+ if(blackPlaysFirst)
fprintf(serverMoves, "%s;", second.tidy);
fprintf(serverMoves, "%s;", first.tidy);
- if(!blackPlaysFirst)
+ if(!blackPlaysFirst)
fprintf(serverMoves, "%s;", second.tidy);
} else fprintf(serverMoves, loadFlag|lastLoadFlag ? ":" : ";");
lastLoadFlag = loadFlag;
if( (boards[forwardMostMove][fromY][fromX] == WhitePawn ||
boards[forwardMostMove][fromY][fromX] == BlackPawn ) &&
boards[forwardMostMove][toY][toX] == EmptySquare
- && fromX != toX )
+ && fromX != toX && fromY != toY)
fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);
// promotion suffix
if(promoChar != NULLCHAR)
DrawPosition(FALSE, boards[currentMove]);
DisplayBothClocks();
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
+ DisplayBook(currentMove);
}
void SendEgtPath(ChessProgramState *cps)
name[0] = ','; // extract next format name from feature and copy with prefixed ','
while(*p && *p != ',') *q++ = *p++;
*q++ = ':'; *q = 0;
- if( appData.defaultPathEGTB && appData.defaultPathEGTB[0] &&
+ if( appData.defaultPathEGTB && appData.defaultPathEGTB[0] &&
strcmp(name, ",nalimov:") == 0 ) {
// take nalimov path from the menu-changeable option first, if it is defined
- sprintf(buf, "egtpath nalimov %s\n", appData.defaultPathEGTB);
+ snprintf(buf, MSG_SIZ, "egtpath nalimov %s\n", appData.defaultPathEGTB);
SendToProgram(buf,cps); // send egtbpath command for nalimov
} else
if( (s = StrStr(appData.egtFormats, name+1)) == appData.egtFormats ||
while(*r && *r != ',') r++; // path info is everything upto next ';' or end of string
c = *r; *r = 0; // temporarily null-terminate path info
*--q = 0; // strip of trailig ':' from name
- sprintf(buf, "egtpath %s %s\n", name+1, s);
+ snprintf(buf, MSG_SIZ, "egtpath %s %s\n", name+1, s);
*r = c;
SendToProgram(buf,cps); // send egtbpath command for this format
}
/* [HGM] some new WB protocol commands to configure engine are sent now, if engine supports them */
/* moved to before sending initstring in 4.3.15, so Polyglot can delay UCI 'isready' to recepton of 'new' */
if(cps->memSize) { /* [HGM] memory */
- sprintf(buf, "memory %d\n", appData.defaultHashSize + appData.defaultCacheSizeEGTB);
+ snprintf(buf, MSG_SIZ, "memory %d\n", appData.defaultHashSize + appData.defaultCacheSizeEGTB);
SendToProgram(buf, cps);
}
SendEgtPath(cps); /* [HGM] EGT */
if(cps->maxCores) { /* [HGM] SMP: (protocol specified must be last settings command before new!) */
- sprintf(buf, "cores %d\n", appData.smpCores);
+ snprintf(buf, MSG_SIZ, "cores %d\n", appData.smpCores);
SendToProgram(buf, cps);
}
char *v = VariantName(gameInfo.variant);
if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) {
/* [HGM] in protocol 1 we have to assume all variants valid */
- sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy);
+ snprintf(buf, MSG_SIZ, _("Variant %s not supported by %s"), v, cps->tidy);
DisplayFatalError(buf, 0, 1);
return;
}
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 )
+ 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;
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) {
- sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight,
- gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name
+ 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 */
- if(StrStr(cps->variants, b) == NULL) {
+ if(StrStr(cps->variants, b) == NULL) {
// specific sized variant not known, check if general sizing allowed
if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best
if(StrStr(cps->variants, "boardsize") == NULL) {
- sprintf(buf, "Board size %dx%d+%d not supported by %s",
+ snprintf(buf, MSG_SIZ, "Board size %dx%d+%d not supported by %s",
gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);
DisplayFatalError(buf, 0, 1);
return;
/* [HGM] here we really should compare with the maximum supported board size */
}
}
- } else sprintf(b, "%s", VariantName(gameInfo.variant));
- sprintf(buf, "variant %s\n", b);
+ } else snprintf(b, MSG_SIZ,"%s", VariantName(gameInfo.variant));
+ snprintf(buf, MSG_SIZ, "variant %s\n", b);
SendToProgram(buf, cps);
}
currentlyInitializedVariant = gameInfo.variant;
timeIncrement, appData.searchDepth,
searchTime);
}
- if (appData.showThinking
+ if (appData.showThinking
// [HGM] thinking: four options require thinking output to be sent
|| !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp()
) {
SendToProgram("easy\n", cps);
}
if (cps->usePing) {
- sprintf(buf, "ping %d\n", ++cps->lastPing);
+ snprintf(buf, MSG_SIZ, "ping %d\n", ++cps->lastPing);
SendToProgram(buf, cps);
}
cps->initDone = TRUE;
-}
+ ClearEngineOutputPane(cps == &second);
+}
void
}
err = StartChildProcess(buf, "", &cps->pr);
}
-
+
if (err != 0) {
- sprintf(buf, _("Startup failure on '%s'"), cps->program);
- DisplayFatalError(buf, err, 1);
- cps->pr = NoProc;
- cps->isr = NULL;
+ snprintf(buf, MSG_SIZ, _("Startup failure on '%s'"), cps->program);
+ DisplayError(buf, err); // [HGM] bit of a rough kludge: ignore failure, (which XBoard would do anyway), and let I/O discover it
+ if(cps != &first) return;
+ appData.noChessProgram = TRUE;
+ ThawUI();
+ SetNCPMode();
+// DisplayFatalError(buf, err, 1);
+// cps->pr = NoProc;
+// cps->isr = NULL;
return;
}
-
+
cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
if (cps->protocolVersion > 1) {
- sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
+ 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
SendToProgram(buf, cps);
}
}
-
void
TwoMachinesEventIfReady P((void))
{
+ static int curMess = 0;
if (first.lastPing != first.lastPong) {
- DisplayMessage("", _("Waiting for first chess program"));
+ if(curMess != 1) DisplayMessage("", _("Waiting for first chess program")); curMess = 1;
ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000
return;
}
if (second.lastPing != second.lastPong) {
- DisplayMessage("", _("Waiting for second chess program"));
+ if(curMess != 2) DisplayMessage("", _("Waiting for second chess program")); curMess = 2;
ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000
return;
}
+ DisplayMessage("", ""); curMess = 0;
ThawUI();
TwoMachinesEvent();
}
-void
-NextMatchGame P((void))
+char *
+MakeName(char *template)
{
- int index; /* [HGM] autoinc: step load index during match */
- Reset(FALSE, TRUE);
- if (*appData.loadGameFile != NULLCHAR) {
- index = appData.loadGameIndex;
- if(index < 0) { // [HGM] autoinc
- lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;
- if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;
- }
- LoadGameFromFile(appData.loadGameFile,
- index,
- appData.loadGameFile, FALSE);
- } else if (*appData.loadPositionFile != NULLCHAR) {
- index = appData.loadPositionIndex;
- if(index < 0) { // [HGM] autoinc
- lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;
- if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;
- }
- LoadPositionFromFile(appData.loadPositionFile,
- index,
- appData.loadPositionFile);
+ time_t clock;
+ struct tm *tm;
+ static char buf[MSG_SIZ];
+ char *p = buf;
+ int i;
+
+ clock = time((time_t *)NULL);
+ tm = localtime(&clock);
+
+ while(*p++ = *template++) if(p[-1] == '%') {
+ switch(*template++) {
+ case 0: *p = 0; return buf;
+ case 'Y': i = tm->tm_year+1900; break;
+ case 'y': i = tm->tm_year-100; break;
+ case 'M': i = tm->tm_mon+1; break;
+ case 'd': i = tm->tm_mday; break;
+ case 'h': i = tm->tm_hour; break;
+ case 'm': i = tm->tm_min; break;
+ case 's': i = tm->tm_sec; break;
+ default: i = 0;
+ }
+ snprintf(p-1, MSG_SIZ-10 - (p - buf), "%02d", i); p += strlen(p);
}
- TwoMachinesEventIfReady();
+ return buf;
+}
+
+int
+CountPlayers(char *p)
+{
+ int n = 0;
+ while(p = strchr(p, '\n')) p++, n++; // count participants
+ return n;
+}
+
+FILE *
+WriteTourneyFile(char *results)
+{ // write tournament parameters on tourneyFile; on success return the stream pointer for closing
+ FILE *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, "-tourneyType %d\n", appData.tourneyType);
+ fprintf(f, "-tourneyCycles %d\n", appData.tourneyCycles);
+ fprintf(f, "-defaultMatchGames %d\n", appData.defaultMatchGames);
+ fprintf(f, "-syncAfterRound %s\n", appData.roundSync ? "true" : "false");
+ fprintf(f, "-syncAfterCycle %s\n", appData.cycleSync ? "true" : "false");
+ fprintf(f, "-saveGameFile \"%s\"\n", appData.saveGameFile);
+ fprintf(f, "-loadGameFile \"%s\"\n", appData.loadGameFile);
+ fprintf(f, "-loadGameIndex %d\n", appData.loadGameIndex);
+ fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile);
+ fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex);
+ fprintf(f, "-rewindIndex %d\n", appData.rewindIndex);
+ if(searchTime > 0)
+ fprintf(f, "-searchTime \"%d:%02d\"\n", searchTime/60, searchTime%60);
+ else {
+ fprintf(f, "-mps %d\n", appData.movesPerSession);
+ fprintf(f, "-tc %s\n", appData.timeControl);
+ fprintf(f, "-inc %.2f\n", appData.timeIncrement);
+ }
+ fprintf(f, "-results \"%s\"\n", results);
+ }
+ return f;
+}
+
+int
+CreateTourney(char *name)
+{
+ FILE *f;
+ if(name[0] == NULLCHAR) {
+ if(appData.participants[0])
+ DisplayError(_("You must supply a tournament file,\nfor storing the tourney progress"), 0);
+ return 0;
+ }
+ f = fopen(name, "r");
+ if(f) { // file exists
+ ASSIGN(appData.tourneyFile, name);
+ ParseArgsFromFile(f); // parse it
+ } else {
+ if(!appData.participants[0]) return 0; // ignore tourney file if non-existing & no participants
+ if(CountPlayers(appData.participants) < (appData.tourneyType>0 ? appData.tourneyType+1 : 2)) {
+ DisplayError(_("Not enough participants"), 0);
+ 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);
+ appData.noChessProgram = FALSE;
+ appData.clockMode = TRUE;
+ SetGNUMode();
+ return 1;
+}
+
+#define MAXENGINES 1000
+char *command[MAXENGINES], *mnemonic[MAXENGINES];
+
+void NamesToList(char *names, char **engineList, char **engineMnemonic)
+{
+ char buf[MSG_SIZ], *p, *q;
+ int i=1;
+ while(*names) {
+ p = names; q = buf;
+ while(*p && *p != '\n') *q++ = *p++;
+ *q = 0;
+ if(engineList[i]) free(engineList[i]);
+ engineList[i] = strdup(buf);
+ if(*p == '\n') p++;
+ TidyProgramName(engineList[i], "localhost", buf);
+ 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, " (");
+ sscanf(q + 8, "%s", buf + strlen(buf));
+ strcat(buf, ")");
+ }
+ engineMnemonic[i] = strdup(buf);
+ names = p; i++;
+ if(i > MAXENGINES - 2) break;
+ }
+ engineList[i] = NULL;
+}
+
+// following implemented as macro to avoid type limitations
+#define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp;
+
+void SwapEngines(int n)
+{ // swap settings for first engine and other engine (so far only some selected options)
+ int h;
+ char *p;
+ if(n == 0) return;
+ SWAP(directory, p)
+ SWAP(chessProgram, p)
+ SWAP(isUCI, h)
+ SWAP(hasOwnBookUCI, h)
+ SWAP(protocolVersion, h)
+ SWAP(reuse, h)
+ SWAP(scoreIsAbsolute, h)
+ SWAP(timeOdds, h)
+ SWAP(logo, p)
+ SWAP(pgnName, p)
+ SWAP(pvSAN, h)
+}
+
+void
+SetPlayer(int player)
+{ // [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;
+ 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;
+ if(mnemonic[i]) {
+ snprintf(buf, MSG_SIZ, "-fcp %s", command[i]);
+ ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
+ ParseArgsFromString(buf);
+ }
+ free(engineName);
+}
+
+int
+Pairing(int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInterval)
+{ // determine players from game number
+ int curCycle, curRound, curPairing, gamesPerCycle, gamesPerRound, roundsPerCycle=1, pairingsPerRound=1;
+
+ if(appData.tourneyType == 0) {
+ roundsPerCycle = (nPlayers - 1) | 1;
+ pairingsPerRound = nPlayers / 2;
+ } else if(appData.tourneyType > 0) {
+ roundsPerCycle = nPlayers - appData.tourneyType;
+ pairingsPerRound = appData.tourneyType;
+ }
+ gamesPerRound = pairingsPerRound * appData.defaultMatchGames;
+ gamesPerCycle = gamesPerRound * roundsPerCycle;
+ appData.matchGames = gamesPerCycle * appData.tourneyCycles - 1; // fake like all games are one big match
+ curCycle = nr / gamesPerCycle; nr %= gamesPerCycle;
+ curRound = nr / gamesPerRound; nr %= gamesPerRound;
+ curPairing = nr / appData.defaultMatchGames; nr %= appData.defaultMatchGames;
+ matchGame = nr + curCycle * appData.defaultMatchGames + 1; // fake game nr that loads correct game or position from file
+ roundNr = (curCycle * roundsPerCycle + curRound) * appData.defaultMatchGames + nr + 1;
+
+ if(appData.cycleSync) *syncInterval = gamesPerCycle;
+ if(appData.roundSync) *syncInterval = gamesPerRound;
+
+ if(appData.debugMode) fprintf(debugFP, "cycle=%d, round=%d, pairing=%d curGame=%d\n", curCycle, curRound, curPairing, matchGame);
+
+ if(appData.tourneyType == 0) {
+ if(curPairing == (nPlayers-1)/2 ) {
+ *whitePlayer = curRound;
+ *blackPlayer = nPlayers - 1; // this is the 'bye' when nPlayer is odd
+ } else {
+ *whitePlayer = curRound - (nPlayers-1)/2 + curPairing;
+ if(*whitePlayer < 0) *whitePlayer += nPlayers-1+(nPlayers&1);
+ *blackPlayer = curRound + (nPlayers-1)/2 - curPairing;
+ if(*blackPlayer >= nPlayers-1+(nPlayers&1)) *blackPlayer -= nPlayers-1+(nPlayers&1);
+ }
+ } else if(appData.tourneyType > 0) {
+ *whitePlayer = curPairing;
+ *blackPlayer = curRound + appData.tourneyType;
+ }
+
+ // take care of white/black alternation per round.
+ // For cycles and games this is already taken care of by default, derived from matchGame!
+ return curRound & 1;
+}
+
+int
+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;
+ FILE *tf;
+ if(appData.tourneyFile[0] == NULLCHAR) return 1; // no tourney, always allow next game
+ tf = fopen(appData.tourneyFile, "r");
+ if(tf == NULL) { DisplayFatalError(_("Bad tournament file"), 0, 1); return 0; }
+ ParseArgsFromFile(tf); fclose(tf);
+ InitTimeControls(); // TC might be altered from tourney file
+
+ nPlayers = CountPlayers(appData.participants); // count participants
+ if(appData.tourneyType < 0) syncInterval = nPlayers/2; else
+ *swapColors = Pairing(nr<0 ? 0 : nr, nPlayers, &whitePlayer, &blackPlayer, &syncInterval);
+
+ if(syncInterval) {
+ p = q = appData.results;
+ while(*q) if(*q++ == '*' || q[-1] == ' ') { firstBusy = q - p - 1; break; }
+ if(firstBusy/syncInterval < (nextGame/syncInterval)) {
+ DisplayMessage(_("Waiting for other game(s)"),"");
+ waitingForGame = TRUE;
+ ScheduleDelayedEvent(NextMatchGame, 1000); // wait for all games of previous round to finish
+ return 0;
+ }
+ 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.
+ NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
+ SetPlayer(whitePlayer); // find white player amongst it, and parse its engine line
+ SwapEngines(1);
+ SetPlayer(blackPlayer); // find black player amongst it, and parse its engine line
+ SwapEngines(1); // and make that valid for second engine by swapping
+ 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;
+}
+
+void
+NextMatchGame()
+{ // performs game initialization that does not invoke engines, and then tries to start the game
+ int firstWhite, swapColors = 0;
+ if(!NextTourneyGame(nextGame, &swapColors)) return; // this sets matchGame, -fcp / -scp and other options for next game, if needed
+ 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
+ second.twoMachinesColor = firstWhite ? "black\n" : "white\n";
+ appData.noChessProgram = (first.pr == NoProc); // kludge to prevent Reset from starting up chess program
+ Reset(FALSE, first.pr != NoProc);
+ appData.noChessProgram = FALSE;
+ if(!LoadGameOrPosition(matchGame)) return; // setup game; abort when bad game/pos file
+ TwoMachinesEvent();
}
void UserAdjudicationEvent( int result )
{
GameMode nextGameMode;
int isIcsGame;
- char buf[MSG_SIZ];
+ char buf[MSG_SIZ], popupRequested = 0, *ranking = NULL;
if(endingGame) return; /* [HGM] crash: forbid recursion */
endingGame = 1;
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
- a draw, or resign.
+ game is over, but the engine can offer to draw, claim
+ a draw, or resign.
*/
#if ZIPPY
if (appData.zippyPlay && first.initDone) {
/* If this is an ICS game, only ICS can really say it's done;
if not, anyone can. */
- isIcsGame = (gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == IcsObserving ||
+ isIcsGame = (gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack ||
+ gameMode == IcsObserving ||
gameMode == IcsExamining);
if (!isIcsGame || whosays == GE_ICS) {
/* OK -- not an ICS game, or ICS said it was done */
StopClocks();
- if (!isIcsGame && !appData.noChessProgram)
+ if (!isIcsGame && !appData.noChessProgram)
SetUserThinkingEnables();
-
+
/* [HGM] if a machine claims the game end we verify this claim */
if(gameMode == TwoMachinesPlay && appData.testClaims) {
if(appData.testLegality && whosays >= GE_ENGINE1 ) {
// now verify win claims, but not in drop games, as we don't understand those yet
if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper
- || gameInfo.variant == VariantGreat) &&
+ || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) &&
(result == WhiteWins && claimer == 'w' ||
result == BlackWins && claimer == 'b' ) ) { // case to verify: engine claims own win
if (appData.debugMode) {
result, (signed char)boards[forwardMostMove][EP_STATUS], forwardMostMove);
}
if(result != trueResult) {
- sprintf(buf, "False win claim: '%s'", resultDetails);
+ snprintf(buf, MSG_SIZ, "False win claim: '%s'", resultDetails);
result = claimer == 'w' ? BlackWins : WhiteWins;
resultDetails = buf;
}
(claimer=='b')==(forwardMostMove&1))
) {
/* [HGM] verify: draws that were not flagged are false claims */
- sprintf(buf, "False draw claim: '%s'", resultDetails);
+ snprintf(buf, MSG_SIZ, "False draw claim: '%s'", resultDetails);
result = claimer == 'w' ? BlackWins : WhiteWins;
resultDetails = buf;
}
/* (Claiming a loss is accepted no questions asked!) */
}
/* [HGM] bare: don't allow bare King to win */
- if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
- && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway
+ if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper
+ || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
+ && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway
&& gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...
&& result != GameIsDrawn)
{ int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
}
if(k <= 1) {
result = GameIsDrawn;
- sprintf(buf, "%s but bare king", resultDetails);
+ snprintf(buf, MSG_SIZ, "%s but bare king", resultDetails);
resultDetails = buf;
}
}
/* display last move only if game was not loaded from file */
if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
DisplayMove(currentMove - 1);
-
+
if (forwardMostMove != 0) {
if (gameMode != PlayFromGameFile && gameMode != EditGame
&& lastSavedGame != GameCheckSum() // [HGM] save: suppress duplicates
gameMode == IcsPlayingBlack ||
gameMode == BeginningOfGame) {
char buf[MSG_SIZ];
- sprintf(buf, "result %s {%s}\n", PGNResult(result),
+ snprintf(buf, MSG_SIZ, "result %s {%s}\n", PGNResult(result),
resultDetails);
if (first.pr != NoProc) {
SendToProgram(buf, &first);
}
}
} else if (gameMode == EditGame ||
- gameMode == PlayFromGameFile ||
- gameMode == AnalyzeMode ||
+ gameMode == PlayFromGameFile ||
+ gameMode == AnalyzeMode ||
gameMode == AnalyzeFile) {
nextGameMode = gameMode;
} else {
SendToProgram("force\n", &first);
if (first.usePing) {
char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++first.lastPing);
+ snprintf(buf, MSG_SIZ, "ping %d\n", ++first.lastPing);
SendToProgram(buf, &first);
}
}
if (first.isr != NULL)
RemoveInputSource(first.isr);
first.isr = NULL;
-
+
if (first.pr != NoProc) {
ExitAnalyzeMode();
DoSleep( appData.delayBeforeQuit );
SendToProgram("force\n", &second);
if (second.usePing) {
char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++second.lastPing);
+ snprintf(buf, MSG_SIZ, "ping %d\n", ++second.lastPing);
SendToProgram(buf, &second);
}
}
if (second.isr != NULL)
RemoveInputSource(second.isr);
second.isr = NULL;
-
+
if (second.pr != NoProc) {
DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &second);
second.pr = NoProc;
}
- if (matchMode && gameMode == TwoMachinesPlay) {
+ if (matchMode && (gameMode == TwoMachinesPlay || waitingForGame && exiting)) {
+ char resChar = '=';
switch (result) {
case WhiteWins:
+ resChar = '+';
if (first.twoMachinesColor[0] == 'w') {
first.matchWins++;
} else {
}
break;
case BlackWins:
+ resChar = '-';
if (first.twoMachinesColor[0] == 'b') {
first.matchWins++;
} else {
second.matchWins++;
}
break;
+ case GameUnfinished:
+ resChar = ' ';
default:
break;
}
- if (matchGame < appData.matchGames) {
- char *tmp;
- if(appData.sameColorGames <= 1) { /* [HGM] alternate: suppress color swap */
- tmp = first.twoMachinesColor;
- first.twoMachinesColor = second.twoMachinesColor;
- second.twoMachinesColor = tmp;
- }
+
+ 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
+ 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) {
gameMode = nextGameMode;
- matchGame++;
- if(appData.matchPause>10000 || appData.matchPause<10)
- appData.matchPause = 10000; /* [HGM] make pause adjustable */
- ScheduleDelayedEvent(NextMatchGame, appData.matchPause);
+ matchGame = nextGame; // this will be overruled in tourney mode!
+ GetTimeMark(&pauseStart); // [HGM] matchpause: stipulate a pause
+ ScheduleDelayedEvent(NextMatchGame, 10); // but start game immediately (as it will wait out the pause itself)
endingGame = 0; /* [HGM] crash */
return;
} else {
- char buf[MSG_SIZ];
gameMode = nextGameMode;
- sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),
- first.tidy, second.tidy,
- first.matchWins, second.matchWins,
- appData.matchGames - (first.matchWins + second.matchWins));
- DisplayFatalError(buf, 0, 0);
+ snprintf(buf, MSG_SIZ, _("Match %s vs. %s: final score %d-%d-%d"),
+ 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";
+ second.twoMachinesColor = "white\n";
+ } else {
+ first.twoMachinesColor = "white\n";
+ second.twoMachinesColor = "black\n";
+ }
}
}
if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
gameMode = nextGameMode;
ModeHighlight();
endingGame = 0; /* [HGM] crash */
+ 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);
+ }
+ if(ranking) free(ranking);
+ }
}
/* Assumes program was just initialized (initString sent).
Leaves program in force mode. */
void
-FeedMovesToProgram(cps, upto)
+FeedMovesToProgram(cps, upto)
ChessProgramState *cps;
int upto;
{
int i;
-
+
if (appData.debugMode)
fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
startedFromSetupPosition ? "position and " : "",
backwardMostMove, upto, cps->which);
- if(currentlyInitializedVariant != gameInfo.variant) { char buf[MSG_SIZ];
+ if(currentlyInitializedVariant != gameInfo.variant) {
+ char buf[MSG_SIZ];
// [HGM] variantswitch: make engine aware of new variant
if(cps->protocolVersion > 1 && StrStr(cps->variants, VariantName(gameInfo.variant)) == NULL)
return; // [HGM] refrain from feeding moves altogether if variant is unsupported!
- sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
+ snprintf(buf, MSG_SIZ, "variant %s\n", VariantName(gameInfo.variant));
SendToProgram(buf, cps);
currentlyInitializedVariant = gameInfo.variant;
}
}
-void
+int
ResurrectChessProgram()
{
/* The chess program may have exited.
If so, restart it and feed it all the moves made so far. */
+ static int doInit = 0;
+
+ if (appData.noChessProgram) return 1;
- if (appData.noChessProgram || first.pr != NoProc) return;
-
- StartChessProgram(&first);
+ 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(!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 {
+ if (first.pr != NoProc) return 1;
+ StartChessProgram(&first);
+ }
InitChessProgram(&first, FALSE);
FeedMovesToProgram(&first, currentMove);
SendToProgram("analyze\n", &first);
first.analyzing = TRUE;
}
+ return 1;
}
/*
white_holding[0] = black_holding[0] = NULLCHAR;
ClearProgramStats();
opponentKibitzes = FALSE; // [HGM] kibitz: do not reserve space in engine-output window in zippy mode
-
+
ResetFrontEnd();
ClearHighlights();
flipView = appData.flipView;
gotPremove = FALSE;
alarmSounded = FALSE;
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
+ GameEnds(EndOfFile, NULL, GE_PLAYER);
if(appData.serverMovesName != NULL) {
/* [HGM] prepare to make moves file for broadcasting */
clock_t t = clock();
ResetClocks();
timeRemaining[0][0] = whiteTimeRemaining;
timeRemaining[1][0] = blackTimeRemaining;
+
if (first.pr == NULL) {
StartChessProgram(&first);
}
return;
if (matchMode || appData.timeDelay == 0)
continue;
- if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
+ if (appData.timeDelay < 0)
return;
StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
break;
fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
}
- if (gameMode != PlayFromGameFile)
+ if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile)
return FALSE;
+ if (gameMode == AnalyzeFile && currentMove > backwardMostMove) {
+ pvInfoList[currentMove].depth = programStats.depth;
+ pvInfoList[currentMove].score = programStats.score;
+ pvInfoList[currentMove].time = 0;
+ if(currentMove < forwardMostMove) AppendComment(currentMove+1, lastPV[0], 2);
+ }
+
if (currentMove >= forwardMostMove) {
+ if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); }
gameMode = EditGame;
ModeHighlight();
return FALSE;
}
-
+
toX = moveList[currentMove][2] - AAA;
toY = moveList[currentMove][3] - ONE;
ChessMove moveType;
char move[MSG_SIZ];
char *p, *q;
-
- if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
+
+ if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
gameMode != AnalyzeMode && gameMode != Training) {
gameFileFP = NULL;
return FALSE;
}
-
+
yyboardindex = forwardMostMove;
- if (readAhead != (ChessMove)0) {
+ if (readAhead != EndOfFile) {
moveType = readAhead;
} else {
if (gameFileFP == NULL)
return FALSE;
- moveType = (ChessMove) yylex();
+ moveType = (ChessMove) Myylex();
}
-
+
done = FALSE;
switch (moveType) {
case Comment:
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
- case WhitePromotionChancellor:
- case BlackPromotionChancellor:
- case WhitePromotionArchbishop:
- case BlackPromotionArchbishop:
- case WhitePromotionCentaur:
- case BlackPromotionCentaur:
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
+ case WhitePromotion:
+ case BlackPromotion:
+ case WhiteNonPromotion:
+ case BlackNonPromotion:
case NormalMove:
case WhiteKingSideCastle:
case WhiteQueenSideCastle:
if (q != NULL) *q = NULLCHAR;
p++;
}
+ while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message
GameEnds(moveType, p, GE_FILE);
done = TRUE;
if (cmailMsgLoaded) {
}
break;
- case (ChessMove) 0: /* end of file */
+ case EndOfFile:
if (appData.debugMode)
fprintf(debugFP, "Parser hit end of file\n");
switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
if (appData.debugMode)
fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
yy_text, (int) moveType);
- return LoadGameOneMove((ChessMove)0); /* tail recursion */
+ return LoadGameOneMove(EndOfFile); /* tail recursion */
}
/* else fall thru */
if (appData.debugMode)
fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
yy_text, (int) moveType);
- return LoadGameOneMove((ChessMove)0); /* tail recursion */
+ return LoadGameOneMove(EndOfFile); /* tail recursion */
case IllegalMove:
if (appData.testLegality) {
if (appData.debugMode)
fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
- sprintf(move, _("Illegal move: %d.%s%s"),
+ snprintf(move, MSG_SIZ, _("Illegal move: %d.%s%s"),
(forwardMostMove / 2) + 1,
WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
DisplayError(move, 0);
case AmbiguousMove:
if (appData.debugMode)
fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
- sprintf(move, _("Ambiguous move: %d.%s%s"),
+ snprintf(move, MSG_SIZ, _("Ambiguous move: %d.%s%s"),
(forwardMostMove / 2) + 1,
WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
DisplayError(move, 0);
case ImpossibleMove:
if (appData.debugMode)
fprintf(debugFP, "Parsed ImpossibleMove (type = %d): %s\n", moveType, yy_text);
- sprintf(move, _("Illegal move: %d.%s%s"),
+ snprintf(move, MSG_SIZ, _("Illegal move: %d.%s%s"),
(forwardMostMove / 2) + 1,
WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
DisplayError(move, 0);
return FALSE;
} else {
/* currentMoveString is set as a side-effect of yylex */
- strcat(currentMoveString, "\n");
- strcpy(moveList[forwardMostMove], currentMoveString);
-
+
thinkOutput[0] = NULLCHAR;
MakeMove(fromX, fromY, toX, toY, promoChar);
currentMove = forwardMostMove;
if (appData.debugMode)
fprintf(debugFP, "Restoring %s for game %d\n",
cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
-
+
thinkOutput[0] = NULLCHAR;
- strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
+ safeStrCpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1], sizeof(moveList[currentMove])/sizeof(moveList[currentMove][0]));
fromX = cmailMove[lastLoadGameNumber - 1][0] - AAA;
fromY = cmailMove[lastLoadGameNumber - 1][1] - ONE;
toX = cmailMove[lastLoadGameNumber - 1][2] - AAA;
promoChar = cmailMove[lastLoadGameNumber - 1][4];
MakeMove(fromX, fromY, toX, toY, promoChar);
ShowMove(fromX, fromY, toX, toY);
-
+
switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
-
+
case MT_CHECKMATE:
case MT_STAINMATE:
if (WhiteOnMove(currentMove)) {
GameEnds(WhiteWins, "White mates", GE_PLAYER);
}
break;
-
+
case MT_STALEMATE:
GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
break;
}
break;
-
+
case CMAIL_RESIGN:
if (WhiteOnMove(currentMove)) {
GameEnds(BlackWins, "White resigns", GE_PLAYER);
GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
}
break;
-
+
case CMAIL_ACCEPT:
GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
break;
-
+
default:
break;
}
GameMode oldGameMode;
VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
if (gameMode == Training )
if (useList) {
lg = (ListGame *) ListElem(&gameList, gameNumber-1);
-
+
if (lg) {
fseek(f, lg->offset, 0);
GameListHighlight(gameNumber);
}
lastLoadGameFP = f;
lastLoadGameNumber = gameNumber;
- strcpy(lastLoadGameTitle, title);
+ safeStrCpy(lastLoadGameTitle, title, sizeof(lastLoadGameTitle)/sizeof(lastLoadGameTitle[0]));
lastLoadGameUseList = useList;
yynewfile(f);
DisplayTitle(buf);
} else if (*title != NULLCHAR) {
if (gameNumber > 1) {
- sprintf(buf, "%s %d", title, gameNumber);
+ snprintf(buf, MSG_SIZ, "%s %d", title, gameNumber);
DisplayTitle(buf);
} else {
DisplayTitle(title);
/*
* Skip the first gn-1 games in the file.
- * Also skip over anything that precedes an identifiable
- * start of game marker, to avoid being confused by
- * garbage at the start of the file. Currently
+ * Also skip over anything that precedes an identifiable
+ * start of game marker, to avoid being confused by
+ * garbage at the start of the file. Currently
* recognized start of game markers are the move number "1",
* the pattern "gnuchess .* game", the pattern
- * "^[#;%] [^ ]* game file", and a PGN tag block.
+ * "^[#;%] [^ ]* game file", and a PGN tag block.
* A game that starts with one of the latter two patterns
* will also have a move number 1, possibly
* following a position diagram.
* 5-4-02: Let's try being more lenient and allowing a game to
* start with an unnumbered move. Does that break anything?
*/
- cm = lastLoadGameStart = (ChessMove) 0;
+ cm = lastLoadGameStart = EndOfFile;
while (gn > 0) {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
switch (cm) {
- case (ChessMove) 0:
+ case EndOfFile:
if (cmailMsgLoaded) {
nCmailGames = CMAIL_MAX_GAMES - gn;
} else {
gn--;
lastLoadGameStart = cm;
break;
-
+
case MoveNumberOne:
switch (lastLoadGameStart) {
case GNUChessGame:
case PGNTag:
break;
case MoveNumberOne:
- case (ChessMove) 0:
+ case EndOfFile:
gn--; /* count this game */
lastLoadGameStart = cm;
break;
case GNUChessGame:
case PGNTag:
case MoveNumberOne:
- case (ChessMove) 0:
+ case EndOfFile:
gn--; /* count this game */
lastLoadGameStart = cm;
break;
if (gn > 0) {
do {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
} while (cm == PGNTag || cm == Comment);
}
break;
case NormalMove:
/* Only a NormalMove can be at the start of a game
* without a position diagram. */
- if (lastLoadGameStart == (ChessMove) 0) {
+ if (lastLoadGameStart == EndOfFile ) {
gn--;
lastLoadGameStart = MoveNumberOne;
}
break;
}
}
-
+
if (appData.debugMode)
fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
/* Skip any header junk before position diagram and/or move 1 */
for (;;) {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
- if (cm == (ChessMove) 0 ||
+ if (cm == EndOfFile ||
cm == GNUChessGame || cm == XBoardGame) {
/* Empty game; pretend end-of-file and handle later */
- cm = (ChessMove) 0;
+ cm = EndOfFile;
break;
}
free(gameInfo.event);
}
gameInfo.event = StrSave(yy_text);
- }
+ }
startedFromSetupPosition = FALSE;
while (cm == PGNTag) {
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
err = ParsePGNTag(yy_text, &gameInfo);
if (!err) numPGNTags++;
/* [HGM] PGNvariant: automatically switch to variant given in PGN tag */
if(gameInfo.variant != oldVariant) {
startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */
+ ResetFrontEnd(); // [HGM] might need other bitmaps. Cannot use Reset() because it clears gameInfo :-(
InitPosition(TRUE);
oldVariant = gameInfo.variant;
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "New variant %d\n", (int) oldVariant);
}
if (blackPlaysFirst) {
currentMove = forwardMostMove = backwardMostMove = 1;
CopyBoard(boards[1], initial_position);
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
+ safeStrCpy(moveList[0], "", sizeof(moveList[0])/sizeof(moveList[0][0]));
+ safeStrCpy(parseList[0], "", sizeof(parseList[0])/sizeof(parseList[0][0]));
timeRemaining[0][1] = whiteTimeRemaining;
timeRemaining[1][1] = blackTimeRemaining;
if (commentList[0] != NULL) {
}
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
/* Handle comments interspersed among the tags */
while (cm == Comment) {
char *p;
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
}
for (i = BOARD_HEIGHT - 1; i >= 0; i--)
for (j = BOARD_LEFT; j < BOARD_RGHT; p++)
switch (*p) {
+ case '{':
case '[':
case '-':
case ' ':
}
while (*p == ' ' || *p == '\t' ||
*p == '\n' || *p == '\r') p++;
-
+
if (strncmp(p, "black", strlen("black"))==0)
blackPlaysFirst = TRUE;
else
blackPlaysFirst = FALSE;
startedFromSetupPosition = TRUE;
-
+
CopyBoard(boards[0], initial_position);
if (blackPlaysFirst) {
currentMove = forwardMostMove = backwardMostMove = 1;
CopyBoard(boards[1], initial_position);
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
+ safeStrCpy(moveList[0], "", sizeof(moveList[0])/sizeof(moveList[0][0]));
+ safeStrCpy(parseList[0], "", sizeof(parseList[0])/sizeof(parseList[0][0]));
timeRemaining[0][1] = whiteTimeRemaining;
timeRemaining[1][1] = blackTimeRemaining;
if (commentList[0] != NULL) {
}
}
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
if (first.pr == NoProc) {
fprintf(debugFP, "Load Game\n");
}
DisplayBothClocks();
- }
+ }
/* [HGM] server: flag to write setup moves in broadcast file as one */
loadFlag = appData.suppressLoadMoves;
while (cm == Comment) {
char *p;
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
- if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||
+ if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) ||
cm == WhiteWins || cm == BlackWins ||
cm == GameIsDrawn || cm == GameUnfinished) {
DisplayMessage("", _("No moves in game"));
if (!matchMode && (pausing || appData.timeDelay != 0)) {
DisplayComment(currentMove - 1, commentList[currentMove]);
}
- if (!matchMode && appData.timeDelay != 0)
+ if (!matchMode && appData.timeDelay != 0)
DrawPosition(FALSE, boards[currentMove]);
if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
}
/* if the first token after the PGN tags is a move
- * and not move number 1, retrieve it from the parser
+ * and not move number 1, retrieve it from the parser
*/
if (cm != MoveNumberOne)
LoadGameOneMove(cm);
/* load the remaining moves from the file */
- while (LoadGameOneMove((ChessMove)0)) {
+ while (LoadGameOneMove(EndOfFile)) {
timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
timeRemaining[1][forwardMostMove] = blackTimeRemaining;
}
AutoPlayGameLoop();
}
- if (appData.debugMode)
+ if (appData.debugMode)
fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
loadFlag = 0; /* [HGM] true game starts */
char *p, line[MSG_SIZ];
Board initial_position;
int i, j, fenMode, pn;
-
+
if (gameMode == Training )
SetTrainingModeOff();
if (positionNumber == 0) positionNumber = 1;
lastLoadPositionFP = f;
lastLoadPositionNumber = positionNumber;
- strcpy(lastLoadPositionTitle, title);
+ safeStrCpy(lastLoadPositionTitle, title, sizeof(lastLoadPositionTitle)/sizeof(lastLoadPositionTitle[0]));
if (first.pr == NoProc) {
StartChessProgram(&first);
InitChessProgram(&first, FALSE);
- }
+ }
pn = positionNumber;
if (positionNumber < 0) {
/* Negative position number means to seek to that byte offset */
} else {
(void) fgets(line, MSG_SIZ, f);
(void) fgets(line, MSG_SIZ, f);
-
+
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
(void) fgets(line, MSG_SIZ, f);
for (p = line, j = BOARD_LEFT; j < BOARD_RGHT; p++) {
initial_position[i][j++] = CharToPiece(*p);
}
}
-
+
blackPlaysFirst = FALSE;
if (!feof(f)) {
(void) fgets(line, MSG_SIZ, f);
}
}
startedFromSetupPosition = TRUE;
-
+
SendToProgram("force\n", &first);
CopyBoard(boards[0], initial_position);
if (blackPlaysFirst) {
currentMove = forwardMostMove = backwardMostMove = 1;
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
+ safeStrCpy(moveList[0], "", sizeof(moveList[0])/sizeof(moveList[0][0]));
+ safeStrCpy(parseList[0], "", sizeof(parseList[0])/sizeof(parseList[0][0]));
CopyBoard(boards[1], initial_position);
DisplayMessage("", _("Black to play"));
} else {
}
if (positionNumber > 1) {
- sprintf(line, "%s %d", title, positionNumber);
+ snprintf(line, MSG_SIZ, "%s %d", title, positionNumber);
DisplayTitle(line);
} else {
DisplayTitle(title);
timeRemaining[0][1] = whiteTimeRemaining;
timeRemaining[1][1] = blackTimeRemaining;
DrawPosition(FALSE, boards[currentMove]);
-
+
return TRUE;
}
*p++ = '-';
CopyPlayerNameIntoFileName(&p, gameInfo.black);
*p++ = '.';
- strcpy(p, ext);
+ safeStrCpy(p, ext, MSG_SIZ-2-strlen(gameInfo.white)-strlen(gameInfo.black));
} else {
def[0] = NULLCHAR;
}
{
FILE *f;
char buf[MSG_SIZ];
+ int result;
if (strcmp(filename, "-") == 0) {
return SaveGame(stdout, 0, NULL);
DisplayError(buf, errno);
return FALSE;
} else {
- return SaveGame(f, 0, NULL);
+ safeStrCpy(buf, lastMsg, MSG_SIZ);
+ DisplayMessage(_("Waiting for access to save file"), "");
+ flock(fileno(f), LOCK_EX); // [HGM] lock: lock file while we are writing
+ DisplayMessage(_("Saving game"), "");
+ if(lseek(fileno(f), 0, SEEK_END) == -1) DisplayError("Bad Seek", errno); // better safe than sorry...
+ result = SaveGame(f, 0, NULL);
+ DisplayMessage(buf, "");
+ return result;
}
}
}
{
static char buf[MSG_SIZ];
char *p;
-
+
p = strchr(str, ' ');
if (p == NULL) return str;
strncpy(buf, str, p - str);
}
sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );
- sprintf( buf+strlen(buf), "%s%.2f",
+ sprintf( buf+strlen(buf), "%s%.2f",
pvInfoList[idx].score >= 0 ? "+" : "",
pvInfoList[idx].score / 100.0 );
}
char move_buffer[100]; /* [AS] Buffer for move+PV info */
offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-
+
tm = time((time_t *) NULL);
-
+
PrintPGNTags(f, &gameInfo);
-
+
if (backwardMostMove > 0 || startedFromSetupPosition) {
char *fen = PositionToFEN(backwardMostMove, NULL);
fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
GetOutOfBookInfo( buf );
if( buf[0] != '\0' ) {
- fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf );
+ fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf );
}
}
}
/* Format move number */
- if ((i % 2) == 0) {
- sprintf(numtext, "%d.", (i - offset)/2 + 1);
- } else {
- if (newblock) {
- sprintf(numtext, "%d...", (i - offset)/2 + 1);
- } else {
- numtext[0] = NULLCHAR;
- }
- }
+ if ((i % 2) == 0)
+ snprintf(numtext, sizeof(numtext)/sizeof(numtext[0]),"%d.", (i - offset)/2 + 1);
+ else
+ if (newblock)
+ snprintf(numtext, sizeof(numtext)/sizeof(numtext[0]), "%d...", (i - offset)/2 + 1);
+ else
+ numtext[0] = NULLCHAR;
+
numlen = strlen(numtext);
newblock = FALSE;
linelen += numlen;
/* Get move */
- strcpy(move_buffer, SavePart(parseList[i])); // [HGM] pgn: print move via buffer, so it can be edited
+ safeStrCpy(move_buffer, SavePart(parseList[i]), sizeof(move_buffer)/sizeof(move_buffer[0])); // [HGM] pgn: print move via buffer, so it can be edited
movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */
/* Print move */
seconds = (pvInfoList[i].time+5)/10; // deci-seconds, rounded to nearest
- if( seconds <= 0) buf[0] = 0; else
- if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {
- seconds = (seconds + 4)/10; // round to full seconds
- if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0); else
- sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0);
- }
+ if( seconds <= 0)
+ buf[0] = 0;
+ else
+ if( seconds < 30 )
+ snprintf(buf, MSG_SIZ, " %3.1f%c", seconds/10., 0);
+ else
+ {
+ seconds = (seconds + 4)/10; // round to full seconds
+ if( seconds < 60 )
+ snprintf(buf, MSG_SIZ, " %d%c", seconds, 0);
+ else
+ snprintf(buf, MSG_SIZ, " %d:%02d%c", seconds/60, seconds%60, 0);
+ }
- sprintf( move_buffer, "{%s%.2f/%d%s}",
- pvInfoList[i].score >= 0 ? "+" : "",
- pvInfoList[i].score / 100.0,
- pvInfoList[i].depth,
- buf );
+ snprintf( move_buffer, sizeof(move_buffer)/sizeof(move_buffer[0]),"{%s%.2f/%d%s}",
+ pvInfoList[i].score >= 0 ? "+" : "",
+ pvInfoList[i].score / 100.0,
+ pvInfoList[i].depth,
+ buf );
movelen = strlen(move_buffer); /* [HGM] pgn: line-break point after move */
i++;
}
-
+
/* Start a new line */
if (linelen > 0) fprintf(f, "\n");
{
int i, offset;
time_t tm;
-
+
tm = time((time_t *) NULL);
-
+
fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
PrintOpponents(f);
-
+
if (backwardMostMove > 0 || startedFromSetupPosition) {
fprintf(f, "\n[--------------\n");
PrintPosition(f, backwardMostMove);
i++;
}
}
-
+
if (commentList[i] != NULL) {
fprintf(f, "[%s]\n", commentList[i]);
}
DisplayError(buf, errno);
return FALSE;
} else {
+ safeStrCpy(buf, lastMsg, MSG_SIZ);
+ DisplayMessage(_("Waiting for access to save file"), "");
+ flock(fileno(f), LOCK_EX); // [HGM] lock
+ DisplayMessage(_("Saving position"), "");
+ lseek(fileno(f), 0, SEEK_END); // better safe than sorry...
SavePosition(f, 0, NULL);
+ DisplayMessage(buf, "");
return TRUE;
}
}
{
time_t tm;
char *fen;
-
+
if (gameMode == EditPosition) EditPositionDone(TRUE);
if (appData.oldSaveStyle) {
tm = time((time_t *) NULL);
-
+
fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
PrintOpponents(f);
fprintf(f, "[--------------\n");
int i;
struct stat inbuf, outbuf;
int status;
-
+
/* Any registered moves are unregistered if unregister is set, */
/* i.e. invoked by the signal handler */
if (unregister) {
outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
sprintf(outFilename, "%s.out", appData.cmailGameName);
}
-
+
status = stat(outFilename, &outbuf);
if (status < 0) {
cmailMailedMove = FALSE;
status = stat(inFilename, &inbuf);
cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
}
-
+
/* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
counts the games, notes how each one terminated, etc.
-
+
It would be nice to remove this kludge and instead gather all
the information while building the game list. (And to keep it
in the game list nodes instead of having a bunch of fixed-size
*/
cmailMsgLoaded = TRUE;
LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
-
+
/* Load first game in the file or popup game menu */
LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
nCmailMovesRegistered --;
- if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
+ if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
{
free(cmailCommentList[lastLoadGameNumber - 1]);
cmailCommentList[lastLoadGameNumber - 1] = NULL;
cmailCommentList[lastLoadGameNumber - 1]
= StrSave(commentList[currentMove]);
}
- strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
+ safeStrCpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1], sizeof(cmailMove[lastLoadGameNumber - 1])/sizeof(cmailMove[lastLoadGameNumber - 1][0]));
if (appData.debugMode)
fprintf(debugFP, "Saving %s for game %d\n",
cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
- sprintf(string,
- "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
-
+ snprintf(string, MSG_SIZ, "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
+
f = fopen(string, "w");
if (appData.oldSaveStyle) {
SaveGameOldStyle(f); /* also closes the file */
-
- sprintf(string, "%s.pos.out", appData.cmailGameName);
+
+ snprintf(string, MSG_SIZ, "%s.pos.out", appData.cmailGameName);
f = fopen(string, "w");
SavePosition(f, 0, NULL); /* also closes the file */
} else {
fprintf(f, "{--------------\n");
PrintPosition(f, currentMove);
fprintf(f, "--------------}\n\n");
-
+
SaveGame(f, 0, NULL); /* also closes the file*/
}
-
+
cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
nCmailMovesRegistered ++;
} else if (nCmailGames == 1) {
#if CMAIL_PROHIBIT_REMAIL
if (cmailMailedMove) {
- sprintf(msg, _("You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line."), appData.cmailGameName);
+ snprintf(msg, MSG_SIZ, _("You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line."), appData.cmailGameName);
DisplayError(msg, 0);
return;
}
#endif
if (! (cmailMailedMove || RegisterMove())) return;
-
+
if ( cmailMailedMove
|| (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
- sprintf(string, partCommandString,
- appData.debugMode ? " -v" : "", appData.cmailGameName);
+ snprintf(string, MSG_SIZ, partCommandString,
+ appData.debugMode ? " -v" : "", appData.cmailGameName);
commandOutput = popen(string, "r");
if (commandOutput == NULL) {
if ( archived
&& ( (arcDir = (char *) getenv("CMAIL_ARCDIR"))
!= NULL)) {
- sprintf(buffer, "%s/%s.%s.archive",
- arcDir,
- appData.cmailGameName,
- gameInfo.date);
+ snprintf(buffer, MSG_SIZ, "%s/%s.%s.archive",
+ arcDir,
+ appData.cmailGameName,
+ gameInfo.date);
LoadGameFromFile(buffer, 1, buffer, FALSE);
cmailMsgLoaded = FALSE;
}
char number[5];
char string[MSG_SIZ]; /* Space for game-list */
int i;
-
+
if (!cmailMsgLoaded) return "";
if (cmailMailedMove) {
- sprintf(cmailMsg, _("Waiting for reply from opponent\n"));
+ snprintf(cmailMsg, MSG_SIZ, _("Waiting for reply from opponent\n"));
} else {
/* Create a list of games left */
- sprintf(string, "[");
+ snprintf(string, MSG_SIZ, "[");
for (i = 0; i < nCmailGames; i ++) {
if (! ( cmailMoveRegistered[i]
|| (cmailResult[i] == CMAIL_OLD_RESULT))) {
if (prependComma) {
- sprintf(number, ",%d", i + 1);
+ snprintf(number, sizeof(number)/sizeof(number[0]), ",%d", i + 1);
} else {
- sprintf(number, "%d", i + 1);
+ snprintf(number, sizeof(number)/sizeof(number[0]), "%d", i + 1);
prependComma = 1;
}
-
+
strcat(string, number);
}
}
if (nCmailMovesRegistered + nCmailResults == 0) {
switch (nCmailGames) {
case 1:
- sprintf(cmailMsg,
- _("Still need to make move for game\n"));
+ snprintf(cmailMsg, MSG_SIZ, _("Still need to make move for game\n"));
break;
-
+
case 2:
- sprintf(cmailMsg,
- _("Still need to make moves for both games\n"));
+ snprintf(cmailMsg, MSG_SIZ, _("Still need to make moves for both games\n"));
break;
-
+
default:
- sprintf(cmailMsg,
- _("Still need to make moves for all %d games\n"),
- nCmailGames);
+ snprintf(cmailMsg, MSG_SIZ, _("Still need to make moves for all %d games\n"),
+ nCmailGames);
break;
}
} else {
switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
case 1:
- sprintf(cmailMsg,
- _("Still need to make a move for game %s\n"),
- string);
+ snprintf(cmailMsg, MSG_SIZ, _("Still need to make a move for game %s\n"),
+ string);
break;
-
+
case 0:
if (nCmailResults == nCmailGames) {
- sprintf(cmailMsg, _("No unfinished games\n"));
+ snprintf(cmailMsg, MSG_SIZ, _("No unfinished games\n"));
} else {
- sprintf(cmailMsg, _("Ready to send mail\n"));
+ snprintf(cmailMsg, MSG_SIZ, _("Ready to send mail\n"));
}
break;
-
+
default:
- sprintf(cmailMsg,
- _("Still need to make moves for games %s\n"),
- string);
+ snprintf(cmailMsg, MSG_SIZ, _("Still need to make moves for games %s\n"),
+ string);
}
}
}
/* Kill off chess programs */
if (first.pr != NoProc) {
ExitAnalyzeMode();
-
+
DoSleep( appData.delayBeforeQuit );
SendToProgram("quit\n", &first);
DoSleep( appData.delayAfterQuit );
RemoveInputSource(second.isr);
}
+ if (pairing.pr != NoProc) SendToProgram("quit\n", &pairing);
+ if (pairing.isr != NULL) RemoveInputSource(pairing.isr);
+
ShutDownFrontEnd();
exit(status);
}
DisplayBothClocks();
}
if (gameMode == PlayFromGameFile) {
- if (appData.timeDelay >= 0)
+ if (appData.timeDelay >= 0)
AutoPlayGameLoop();
} else if (gameMode == IcsExamining && pauseExamInvalid) {
Reset(FALSE, TRUE);
char title[MSG_SIZ];
if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
- strcpy(title, _("Edit comment"));
+ safeStrCpy(title, _("Edit comment"), sizeof(title)/sizeof(title[0]));
} else {
- sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,
- WhiteOnMove(currentMove - 1) ? " " : ".. ",
- parseList[currentMove - 1]);
+ snprintf(title, MSG_SIZ, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,
+ WhiteOnMove(currentMove - 1) ? " " : ".. ",
+ parseList[currentMove - 1]);
}
EditCommentPopUp(currentMove, title, commentList[currentMove]);
EditTagsEvent()
{
char *tags = PGNTags(&gameInfo);
- EditTagsPopUp(tags);
+ bookUp = FALSE;
+ EditTagsPopUp(tags, NULL);
free(tags);
}
return;
- if (gameMode == PlayFromGameFile ||
- gameMode == TwoMachinesPlay ||
- gameMode == Training ||
- gameMode == AnalyzeMode ||
+ if (gameMode == PlayFromGameFile ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == Training ||
+ gameMode == AnalyzeMode ||
gameMode == EndOfGame)
EditGameEvent();
- if (gameMode == EditPosition)
+ if (gameMode == EditPosition)
EditPositionDone(TRUE);
if (!WhiteOnMove(currentMove)) {
DisplayError(_("It is not White's turn"), 0);
return;
}
-
+
if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
ExitAnalyzeMode();
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
+ if (gameMode == EditGame || gameMode == AnalyzeMode ||
gameMode == AnalyzeFile)
TruncateGame();
pausing = FALSE;
ModeHighlight();
SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
DisplayTitle(buf);
if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.black);
+ snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.black);
SendToProgram(buf, &first);
}
if (first.sendTime) {
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
HandleMachineMove(bookMove, &first);
}
void
MachineBlackEvent()
{
- char buf[MSG_SIZ];
- char *bookHit = NULL;
+ char buf[MSG_SIZ];
+ char *bookHit = NULL;
if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
return;
- if (gameMode == PlayFromGameFile ||
- gameMode == TwoMachinesPlay ||
- gameMode == Training ||
- gameMode == AnalyzeMode ||
+ if (gameMode == PlayFromGameFile ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == Training ||
+ gameMode == AnalyzeMode ||
gameMode == EndOfGame)
EditGameEvent();
- if (gameMode == EditPosition)
+ if (gameMode == EditPosition)
EditPositionDone(TRUE);
if (WhiteOnMove(currentMove)) {
DisplayError(_("It is not Black's turn"), 0);
return;
}
-
+
if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
ExitAnalyzeMode();
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
+ if (gameMode == EditGame || gameMode == AnalyzeMode ||
gameMode == AnalyzeFile)
TruncateGame();
pausing = FALSE;
ModeHighlight();
SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
DisplayTitle(buf);
if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.white);
+ snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.white);
SendToProgram(buf, &first);
}
if (first.sendTime) {
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
HandleMachineMove(bookMove, &first);
}
{
char buf[MSG_SIZ];
if (appData.matchGames > 0) {
+ if(appData.tourneyFile[0]) {
+ snprintf(buf, MSG_SIZ, "%s vs. %s (%d/%d%s)",
+ gameInfo.white, gameInfo.black,
+ nextGame+1, appData.matchGames+1,
+ appData.tourneyType>0 ? "gt" : appData.tourneyType<0 ? "sw" : "rr");
+ } else
if (first.twoMachinesColor[0] == 'w') {
- sprintf(buf, "%s vs. %s (%d-%d-%d)",
- gameInfo.white, gameInfo.black,
- first.matchWins, second.matchWins,
- matchGame - 1 - (first.matchWins + second.matchWins));
+ snprintf(buf, MSG_SIZ, "%s vs. %s (%d-%d-%d)",
+ gameInfo.white, gameInfo.black,
+ first.matchWins, second.matchWins,
+ matchGame - 1 - (first.matchWins + second.matchWins));
} else {
- sprintf(buf, "%s vs. %s (%d-%d-%d)",
- gameInfo.white, gameInfo.black,
- second.matchWins, first.matchWins,
- matchGame - 1 - (first.matchWins + second.matchWins));
+ snprintf(buf, MSG_SIZ, "%s vs. %s (%d-%d-%d)",
+ gameInfo.white, gameInfo.black,
+ second.matchWins, first.matchWins,
+ matchGame - 1 - (first.matchWins + second.matchWins));
}
} else {
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
}
DisplayTitle(buf);
}
void
+SettingsMenuIfReady()
+{
+ if (second.lastPing != second.lastPong) {
+ DisplayMessage("", _("Waiting for second chess program"));
+ ScheduleDelayedEvent(SettingsMenuIfReady, 10); // [HGM] fast: lowered from 1000
+ return;
+ }
+ ThawUI();
+ DisplayMessage("", "");
+ SettingsPopUp(&second);
+}
+
+int
+WaitForEngine(ChessProgramState *cps, DelayedEventCallback retry)
+{
+ char buf[MSG_SIZ];
+ if (cps->pr == NULL) {
+ StartChessProgram(cps);
+ if (cps->protocolVersion == 1) {
+ retry();
+ } else {
+ /* kludge: allow timeout for initial "feature" command */
+ FreezeUI();
+ snprintf(buf, MSG_SIZ, _("Starting %s chess program"), cps->which);
+ DisplayMessage("", buf);
+ ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void
TwoMachinesEvent P((void))
{
int i;
char buf[MSG_SIZ];
ChessProgramState *onmove;
char *bookHit = NULL;
-
+ static int stalling = 0;
+ TimeMark now;
+ long wait;
+
if (appData.noChessProgram) return;
switch (gameMode) {
// forwardMostMove = currentMove;
TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
- ResurrectChessProgram(); /* in case first program isn't running */
- if (second.pr == NULL) {
- StartChessProgram(&second);
- if (second.protocolVersion == 1) {
- TwoMachinesEventIfReady();
- } else {
- /* kludge: allow timeout for initial "feature" command */
- FreezeUI();
- DisplayMessage("", _("Starting second chess program"));
- ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
- }
+ 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.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
+ return;
+ }
+ if(!stalling) {
+ InitChessProgram(&second, FALSE); // unbalances ping of second engine
+ SendToProgram("force\n", &second);
+ stalling = 1;
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
+ return;
+ }
+ GetTimeMark(&now); // [HGM] matchpause: implement match pause after engine load
+ if(appData.matchPause>10000 || appData.matchPause<10)
+ appData.matchPause = 10000; /* [HGM] make pause adjustable */
+ wait = SubtractTimeMarks(&now, &pauseStart);
+ if(wait < appData.matchPause) {
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, appData.matchPause - wait);
return;
}
+ stalling = 0;
DisplayMessage("", "");
- InitChessProgram(&second, FALSE);
- SendToProgram("force\n", &second);
if (startedFromSetupPosition) {
SendBoard(&second, backwardMostMove);
if (appData.debugMode) {
gameMode = TwoMachinesPlay;
pausing = FALSE;
- ModeHighlight();
+ ModeHighlight(); // [HGM] logo: this triggers display update of logos
SetGameInfo();
DisplayTwoMachinesTitle();
firstMove = TRUE;
} else {
onmove = &second;
}
-
+ if(appData.debugMode) fprintf(debugFP, "New game (%d): %s-%s (%c)\n", matchGame, first.tidy, second.tidy, first.twoMachinesColor[0]);
SendToProgram(first.computerString, &first);
if (first.sendName) {
- sprintf(buf, "name %s\n", second.tidy);
+ snprintf(buf, MSG_SIZ, "name %s\n", second.tidy);
SendToProgram(buf, &first);
}
SendToProgram(second.computerString, &second);
if (second.sendName) {
- sprintf(buf, "name %s\n", first.tidy);
+ snprintf(buf, MSG_SIZ, "name %s\n", first.tidy);
SendToProgram(buf, &second);
}
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
- programStats.nodes = programStats.depth = programStats.time =
+ programStats.nodes = programStats.depth = programStats.time =
programStats.score = programStats.got_only_move = 0;
sprintf(programStats.movelist, "%s (xbook)", bookHit);
- strcpy(bookMove, "move ");
+ safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
savedMessage = bookMove; // args for deferred call
savedState = onmove;
case AnalyzeFile:
ExitAnalyzeMode();
break;
-
+
default:
EditGameEvent();
break;
SendToProgram("force\n", &first);
break;
case TwoMachinesPlay:
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
+ GameEnds(EndOfFile, NULL, GE_PLAYER);
ResurrectChessProgram();
SetUserThinkingEnables();
break;
default:
return;
}
-
+
pausing = FALSE;
StopClocks();
first.offeredDraw = second.offeredDraw = 0;
whiteFlag = blackFlag = 0;
}
DisplayTitle("");
- }
-
+ }
+
gameMode = EditGame;
ModeHighlight();
SetGameInfo();
EditGameEvent();
return;
}
-
+
EditGameEvent();
if (gameMode != EditGame) return;
-
+
gameMode = EditPosition;
ModeHighlight();
SetGameInfo();
if (currentMove > 0)
CopyBoard(boards[0], boards[currentMove]);
-
+
blackPlaysFirst = !WhiteOnMove(currentMove);
ResetClocks();
currentMove = forwardMostMove = backwardMostMove = 0;
}
SendToProgram("force\n", &first);
if (blackPlaysFirst) {
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
+ safeStrCpy(moveList[0], "", sizeof(moveList[0])/sizeof(moveList[0][0]));
+ safeStrCpy(parseList[0], "", sizeof(parseList[0])/sizeof(parseList[0][0]));
currentMove = forwardMostMove = backwardMostMove = 1;
CopyBoard(boards[1], boards[0]);
} else {
len = strlen(buf);
if (len > MSG_SIZ)
len = MSG_SIZ;
-
+
strncpy(temp, buf, len);
temp[len] = 0;
for (y = 0; y < BOARD_HEIGHT; y++) {
if (gameMode == IcsExamining) {
if (boards[currentMove][y][x] != EmptySquare) {
- sprintf(buf, "%sx@%c%c\n", ics_prefix,
+ snprintf(buf, MSG_SIZ, "%sx@%c%c\n", ics_prefix,
AAA + x, ONE + y);
SendToICS(buf);
}
case EmptySquare:
if (gameMode == IcsExamining) {
if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
- sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);
+ snprintf(buf, MSG_SIZ, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);
SendToICS(buf);
} else {
if(x < BOARD_LEFT || x >= BOARD_RGHT) {
piece > (int)BlackMan && piece <= (int)BlackKing ) {
selection = (ChessSquare) (DEMOTED piece);
} else if(piece == EmptySquare) selection = BlackSilver;
- else selection = (ChessSquare)((int)piece + 1);
+ else selection = (ChessSquare)((int)piece + 1);
goto defaultlabel;
case WhiteQueen:
defaultlabel:
if (gameMode == IcsExamining) {
if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
- sprintf(buf, "%s%c@%c%c\n", ics_prefix,
- PieceToChar(selection), AAA + x, ONE + y);
+ snprintf(buf, MSG_SIZ, "%s%c@%c%c\n", ics_prefix,
+ PieceToChar(selection), AAA + x, ONE + y);
SendToICS(buf);
} else {
if(x < BOARD_LEFT || x >= BOARD_RGHT) {
AcceptEvent()
{
/* Accept a pending offer of any kind from opponent */
-
+
if (appData.icsActive) {
SendToICS(ics_prefix);
SendToICS("accept\n");
DeclineEvent()
{
/* Decline a pending offer of any kind from opponent */
-
+
if (appData.icsActive) {
SendToICS(ics_prefix);
SendToICS("decline\n");
}
void
+ClockClick(int which)
+{ // [HGM] code moved to back-end from winboard.c
+ if(which) { // black clock
+ if (gameMode == EditPosition || gameMode == IcsExamining) {
+ if(!appData.pieceMenu && blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
+ SetBlackToPlayEvent();
+ } else if (gameMode == EditGame || shiftKey) {
+ AdjustClock(which, -1);
+ } else if (gameMode == IcsPlayingWhite ||
+ gameMode == MachinePlaysBlack) {
+ CallFlagEvent();
+ }
+ } else { // white clock
+ if (gameMode == EditPosition || gameMode == IcsExamining) {
+ if(!appData.pieceMenu && !blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
+ SetWhiteToPlayEvent();
+ } else if (gameMode == EditGame || shiftKey) {
+ AdjustClock(which, -1);
+ } else if (gameMode == IcsPlayingBlack ||
+ gameMode == MachinePlaysWhite) {
+ CallFlagEvent();
+ }
+ }
+}
+
+void
DrawEvent()
{
/* Offer draw or accept pending draw offer from opponent */
-
+
if (appData.icsActive) {
/* Note: tournament rules require draw offers to be
made after you make your move but before you punch
your clock. Currently ICS doesn't let you do that;
instead, you immediately punch your clock after making
a move, but you can offer a draw at any time. */
-
+
SendToICS(ics_prefix);
SendToICS("draw\n");
userOfferedDraw = TRUE; // [HGM] drawclaim: also set flag in ICS play
AdjournEvent()
{
/* Offer Adjourn or accept pending Adjourn offer from opponent */
-
+
if (appData.icsActive) {
SendToICS(ics_prefix);
SendToICS("adjourn\n");
AbortEvent()
{
/* Offer Abort or accept pending Abort offer from opponent */
-
+
if (appData.icsActive) {
SendToICS(ics_prefix);
SendToICS("abort\n");
ResignEvent()
{
/* Resign. You can do this even if it's not your turn. */
-
+
if (appData.icsActive) {
SendToICS(ics_prefix);
SendToICS("resign\n");
if (gameMode == PlayFromGameFile && !pausing)
PauseEvent();
-
+
if (gameMode == IcsExamining && pausing)
limit = pauseExamForwardMostMove;
else
limit = forwardMostMove;
-
+
if (target > limit) target = limit;
if (target > 0 && moveList[target - 1][0]) {
}
}
}
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
- gameMode == Training || gameMode == PlayFromGameFile ||
+ if (gameMode == EditGame || gameMode == AnalyzeMode ||
+ gameMode == Training || gameMode == PlayFromGameFile ||
gameMode == AnalyzeFile) {
while (currentMove < target) {
SendMoveToProgram(currentMove++, &first);
} else {
currentMove = target;
}
-
+
if (gameMode == EditGame || gameMode == EndOfGame) {
whiteTimeRemaining = timeRemaining[0][currentMove];
blackTimeRemaining = timeRemaining[1][currentMove];
if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty
DisplayComment(currentMove - 1, commentList[currentMove]);
}
+ DisplayBook(currentMove);
}
/* to optimze, we temporarily turn off analysis mode while we feed
* the remaining moves to the engine. Otherwise we get analysis output
* after each move.
- */
+ */
if (first.analysisSupport) {
SendToProgram("exit\nforce\n", &first);
first.analyzing = FALSE;
}
}
-
+
if (gameMode == IcsExamining && !pausing) {
SendToICS(ics_prefix);
SendToICS("forward 999999\n");
}
if (gameMode == PlayFromGameFile && !pausing)
PauseEvent();
-
+
if (moveList[target][0]) {
int fromX, fromY, toX, toY;
toX = moveList[target][2] - AAA;
} else {
currentMove = target;
}
-
+
if (gameMode == EditGame || gameMode == EndOfGame) {
whiteTimeRemaining = timeRemaining[0][currentMove];
blackTimeRemaining = timeRemaining[1][currentMove];
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
// [HGM] PV info: routine tests if comment empty
DisplayComment(currentMove - 1, commentList[currentMove]);
+ DisplayBook(currentMove);
}
void
if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
/* to optimize, we temporarily turn off analysis mode while we undo
* all the moves. Otherwise we get analysis output after each undo.
- */
+ */
if (first.analysisSupport) {
SendToProgram("exit\nforce\n", &first);
first.analyzing = FALSE;
int move;
{
int i, j;
-
+
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
char c = PieceToChar(boards[move][i][j]);
{
char buf[MSG_SIZ];
if (!appData.clockMode) {
- strcpy(buf, "-");
+ safeStrCpy(buf, "-", sizeof(buf)/sizeof(buf[0]));
} else if (movesPerSession > 0) {
- sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);
+ snprintf(buf, MSG_SIZ, "%d/%ld", movesPerSession, timeControl/1000);
} else if (timeIncrement == 0) {
- sprintf(buf, "%ld", timeControl/1000);
+ snprintf(buf, MSG_SIZ, "%ld", timeControl/1000);
} else {
- sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);
+ snprintf(buf, MSG_SIZ, "%ld+%ld", timeControl/1000, timeIncrement/1000);
}
return StrSave(buf);
}
char *p = NULL;
if(gameMode == EditGame) { // [HGM] vari: do not erase result on EditGame
- r = gameInfo.result;
- p = gameInfo.resultDetails;
+ r = gameInfo.result;
+ p = gameInfo.resultDetails;
gameInfo.resultDetails = NULL;
}
ClearGameInfo(&gameInfo);
gameInfo.event = StrSave( appData.pgnEventHeader );
gameInfo.site = StrSave(HostName());
gameInfo.date = PGNDate();
- if (matchGame > 0) {
+ if (roundNr > 0) {
char buf[MSG_SIZ];
- sprintf(buf, "%d", matchGame);
+ snprintf(buf, MSG_SIZ, "%d", roundNr);
gameInfo.round = StrSave(buf);
} else {
gameInfo.round = StrSave("-");
}
if (first.twoMachinesColor[0] == 'w') {
- gameInfo.white = StrSave(first.tidy);
- gameInfo.black = StrSave(second.tidy);
+ gameInfo.white = StrSave(appData.pgnName[0][0] ? appData.pgnName[0] : first.tidy);
+ gameInfo.black = StrSave(appData.pgnName[1][0] ? appData.pgnName[1] : second.tidy);
} else {
- gameInfo.white = StrSave(second.tidy);
- gameInfo.black = StrSave(first.tidy);
+ gameInfo.white = StrSave(appData.pgnName[1][0] ? appData.pgnName[1] : second.tidy);
+ gameInfo.black = StrSave(appData.pgnName[0][0] ? appData.pgnName[0] : first.tidy);
}
gameInfo.timeControl = TimeControlTagValue();
break;
char *text;
{
int len;
+ char *p;
+ float score;
+ if(index && sscanf(text, "%f/%d", &score, &len) == 2 &&
+ pvInfoList[index-1].depth == len &&
+ fabs(pvInfoList[index-1].score - score*100.) < 0.5 &&
+ (p = strchr(text, '\n'))) text = p; // [HGM] strip off first line with PV info, if any
while (*text == '\n') text++;
len = strlen(text);
while (len > 0 && text[len - 1] == '\n') len--;
strncpy(commentList[index], text, len);
commentList[index][len] = '\n';
commentList[index][len + 1] = NULLCHAR;
- } else {
+ } else {
// [HGM] braces: if text does not start with known OK delimiter, put braces around it.
char *p;
- commentList[index] = (char *) malloc(len + 6);
- strcpy(commentList[index], "{\n");
- strncpy(commentList[index]+2, text, len);
+ commentList[index] = (char *) malloc(len + 7);
+ safeStrCpy(commentList[index], "{\n", 3);
+ safeStrCpy(commentList[index]+2, text, len+1);
commentList[index][len+2] = NULLCHAR;
while(p = strchr(commentList[index], '}')) *p = ')'; // kill all } to make it one comment
strcat(commentList[index], "\n}\n");
while(commentList[index][oldlen-1] == '\n')
commentList[index][--oldlen] = NULLCHAR;
commentList[index] = (char *) malloc(oldlen + len + 6); // might waste 4
- strcpy(commentList[index], old);
+ safeStrCpy(commentList[index], old, oldlen + len + 6);
free(old);
// [HGM] braces: join "{A\n}\n" + "{\nB}" as "{A\nB\n}"
- if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces)) {
- if(addBraces) addBraces = FALSE; else { text++; len--; }
+ if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces == TRUE)) {
+ if(addBraces == TRUE) addBraces = FALSE; else { text++; len--; }
while (*text == '\n') { text++; len--; }
commentList[index][--oldlen] = NULLCHAR;
}
- if(addBraces) strcat(commentList[index], "\n{\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], "\n}\n");
+ if(addBraces) strcat(commentList[index], addBraces == 2 ? ")\n" : "\n}\n");
else strcat(commentList[index], "\n");
} else {
commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4...
if(addBraces)
- strcpy(commentList[index], "{\n");
+ safeStrCpy(commentList[index], addBraces == 2 ? "(" : "{\n", 3);
else commentList[index][0] = NULLCHAR;
strcat(commentList[index], text);
- strcat(commentList[index], "\n");
- if(addBraces) strcat(commentList[index], "}\n");
+ strcat(commentList[index], addBraces == 2 ? ")\n" : "\n");
+ if(addBraces == TRUE) strcat(commentList[index], "}\n");
}
}
/* [HGM] PV time: and then remove it, to prevent it appearing twice */
char *GetInfoFromComment( int index, char * text )
{
- char * sep = text;
+ char * sep = text, *p;
if( text != NULL && index > 0 ) {
int score = 0;
return text;
}
+ p = text;
+ if(p[1] == '(') { // comment starts with PV
+ p = strchr(p, ')'); // locate end of PV
+ if(p == NULL || sep < p+5) return text;
+ // at this point we have something like "{(.*) +0.23/6 ..."
+ p = text; while(*++p != ')') p[-1] = *p; p[-1] = ')';
+ *p = '\n'; while(*p == ' ' || *p == '\n') p++; *--p = '{';
+ // we now moved the brace to behind the PV: "(.*) {+0.23/6 ..."
+ }
time = -1; sec = -1; deci = -1;
- if( sscanf( text+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
- sscanf( text+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
- sscanf( text+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
- sscanf( text+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
+ if( sscanf( p+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
+ sscanf( p+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
+ sscanf( p+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
+ sscanf( p+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
return text;
}
/* [HGM] PV time: now locate end of PV info */
while( *++sep >= '0' && *sep <= '9'); // strip depth
if(time >= 0)
- while( *++sep >= '0' && *sep <= '9'); // strip time
+ while( *++sep >= '0' && *sep <= '9' || *sep == '\n'); // strip time
if(sec >= 0)
while( *++sep >= '0' && *sep <= '9'); // strip seconds
if(deci >= 0)
pvInfoList[index-1].score = score;
pvInfoList[index-1].time = 10*time; // centi-sec
if(*sep == '}') *sep = 0; else *--sep = '{';
+ if(p != text) { while(*p++ = *sep++); sep = text; } // squeeze out space between PV and comment, and return both
}
return sep;
}
if (cps->pr == NULL) return;
Attention(cps);
-
+
if (appData.debugMode) {
TimeMark now;
GetTimeMark(&now);
- fprintf(debugFP, "%ld >%-6s: %s",
+ fprintf(debugFP, "%ld >%-6s: %s",
SubtractTimeMarks(&now, &programStartTime),
cps->which, message);
}
-
+
count = strlen(message);
outCount = OutputToProcess(cps->pr, message, count, &error);
- if (outCount < count && !exiting
+ if (outCount < count && !exiting
&& !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */
- sprintf(buf, _("Error writing to %s chess program"), cps->which);
+ if(!cps->initDone) return; // [HGM] should not generate fatal error during engine load
+ snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), _(cps->which));
if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
+ snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program);
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; GameEnds(GameIsDrawn, buf, GE_XBOARD); return; }
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);
} else {
- gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
+ ChessMove res = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; GameEnds(res, buf, GE_XBOARD); return; }
+ gameInfo.result = res;
}
gameInfo.resultDetails = StrSave(buf);
}
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; return; }
if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, error, 1); else errorExitStatus = 1;
}
}
if (isr != cps->isr) return; /* Killed intentionally */
if (count <= 0) {
if (count == 0) {
- sprintf(buf,
- _("Error: %s chess program (%s) exited unexpectedly"),
- cps->which, cps->program);
+ RemoveInputSource(cps->isr);
+ if(!cps->initDone) return; // [HGM] should not generate fatal error during engine load
+ snprintf(buf, MSG_SIZ, _("Error: %s chess program (%s) exited unexpectedly"),
+ _(cps->which), cps->program);
if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
+ snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program);
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; GameEnds(GameIsDrawn, buf, GE_XBOARD); return; }
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program);
} else {
- gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
+ ChessMove res = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; GameEnds(res, buf, GE_XBOARD); return; }
+ gameInfo.result = res;
}
gameInfo.resultDetails = StrSave(buf);
}
- RemoveInputSource(cps->isr);
+ if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; return; }
if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, 0, 1); else errorExitStatus = 1;
} else {
- sprintf(buf,
- _("Error reading from %s chess program (%s)"),
- cps->which, cps->program);
+ snprintf(buf, MSG_SIZ, _("Error reading from %s chess program (%s)"),
+ _(cps->which), cps->program);
RemoveInputSource(cps->isr);
/* [AS] Program is misbehaving badly... kill it */
}
return;
}
-
+
if ((end_str = strchr(message, '\r')) != NULL)
*end_str = NULLCHAR;
if ((end_str = strchr(message, '\n')) != NULL)
*end_str = NULLCHAR;
-
+
if (appData.debugMode) {
TimeMark now; int print = 1;
char *quote = ""; char c; int i;
if(appData.engineComments != 1) { /* [HGM] debug: decide if protocol-violating output is written */
char start = message[0];
if(start >='A' && start <= 'Z') start += 'a' - 'A'; // be tolerant to capitalizing
- if(sscanf(message, "%d%c%d%d%d", &i, &c, &i, &i, &i) != 5 &&
+ if(sscanf(message, "%d%c%d%d%d", &i, &c, &i, &i, &i) != 5 &&
sscanf(message, "move %c", &c)!=1 && sscanf(message, "offer%c", &c)!=1 &&
sscanf(message, "resign%c", &c)!=1 && sscanf(message, "feature %c", &c)!=1 &&
sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
sscanf(message, "tell%c", &c)!=1 && sscanf(message, "0-1 %c", &c)!=1 &&
sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
+ sscanf(message, "setboard %c", &c)!=1 && sscanf(message, "setup %c", &c)!=1 &&
+ sscanf(message, "hint: %c", &c)!=1 &&
sscanf(message, "pong %c", &c)!=1 && start != '#') {
quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
print = (appData.engineComments >= 2);
}
if(print) {
GetTimeMark(&now);
- fprintf(debugFP, "%ld <%-6s: %s%s\n",
- SubtractTimeMarks(&now, &programStartTime), cps->which,
+ fprintf(debugFP, "%ld <%-6s: %s%s\n",
+ SubtractTimeMarks(&now, &programStartTime), cps->which,
quote,
message);
}
/* [DM] if icsEngineAnalyze is active we block all whisper and kibitz output, because nobody want to see this */
if (appData.icsEngineAnalyze) {
if (strstr(message, "whisper") != NULL ||
- strstr(message, "kibitz") != NULL ||
+ strstr(message, "kibitz") != NULL ||
strstr(message, "tellics") != NULL) return;
}
/* GNU Chess 4 has no st command; uses level in a nonstandard way */
seconds = st % 60;
if (seconds == 0) {
- sprintf(buf, "level 1 %d\n", st/60);
+ snprintf(buf, MSG_SIZ, "level 1 %d\n", st/60);
} else {
- sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);
+ snprintf(buf, MSG_SIZ, "level 1 %d:%02d\n", st/60, seconds);
}
} else {
- sprintf(buf, "st %d\n", st);
+ snprintf(buf, MSG_SIZ, "st %d\n", st);
}
} else {
/* Set conventional or incremental time control, using level command */
/* Note old gnuchess bug -- minutes:seconds used to not work.
Fixed in later versions, but still avoid :seconds
when seconds is 0. */
- sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);
+ snprintf(buf, MSG_SIZ, "level %d %ld %g\n", mps, tc/60000, inc/1000.);
} else {
- sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,
- seconds, inc/1000);
+ snprintf(buf, MSG_SIZ, "level %d %ld:%02d %g\n", mps, tc/60000,
+ seconds, inc/1000.);
}
}
SendToProgram(buf, cps);
/* Orthogonally, limit search to given depth */
if (sd > 0) {
if (cps->sdKludge) {
- sprintf(buf, "depth\n%d\n", sd);
+ snprintf(buf, MSG_SIZ, "depth\n%d\n", sd);
} else {
- sprintf(buf, "sd %d\n", sd);
+ snprintf(buf, MSG_SIZ, "sd %d\n", sd);
}
SendToProgram(buf, cps);
}
- if(cps->nps > 0) { /* [HGM] nps */
- if(cps->supportsNPS == FALSE) cps->nps = -1; // don't use if engine explicitly says not supported!
+ if(cps->nps >= 0) { /* [HGM] nps */
+ if(cps->supportsNPS == FALSE)
+ cps->nps = -1; // don't use if engine explicitly says not supported!
else {
- sprintf(buf, "nps %d\n", cps->nps);
- SendToProgram(buf, cps);
+ snprintf(buf, MSG_SIZ, "nps %d\n", cps->nps);
+ SendToProgram(buf, cps);
}
}
}
ChessProgramState *WhitePlayer()
/* [HGM] return pointer to 'first' or 'second', depending on who plays white */
{
- if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b' ||
+ if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b' ||
gameMode == BeginningOfGame || gameMode == MachinePlaysBlack)
return &second;
return &first;
if (time <= 0) time = 1;
if (otime <= 0) otime = 1;
-
- sprintf(message, "time %ld\n", time);
+
+ snprintf(message, MSG_SIZ, "time %ld\n", time);
SendToProgram(message, cps);
- sprintf(message, "otim %ld\n", otime);
+ snprintf(message, MSG_SIZ, "otim %ld\n", otime);
SendToProgram(message, cps);
}
char buf[MSG_SIZ];
int len = strlen(name);
int val;
+
if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
(*p) += len + 1;
sscanf(*p, "%d", &val);
*loc = (val != 0);
- while (**p && **p != ' ') (*p)++;
- sprintf(buf, "accepted %s\n", name);
+ while (**p && **p != ' ')
+ (*p)++;
+ snprintf(buf, MSG_SIZ, "accepted %s\n", name);
SendToProgram(buf, cps);
return TRUE;
}
(*p) += len + 1;
sscanf(*p, "%d", loc);
while (**p && **p != ' ') (*p)++;
- sprintf(buf, "accepted %s\n", name);
+ snprintf(buf, MSG_SIZ, "accepted %s\n", name);
SendToProgram(buf, cps);
return TRUE;
}
sscanf(*p, "%[^\"]", loc);
while (**p && **p != '\"') (*p)++;
if (**p == '\"') (*p)++;
- sprintf(buf, "accepted %s\n", name);
+ snprintf(buf, MSG_SIZ, "accepted %s\n", name);
SendToProgram(buf, cps);
return TRUE;
}
return FALSE;
}
-int
+int
ParseOption(Option *opt, ChessProgramState *cps)
// [HGM] options: process the string that defines an engine option, and determine
// name, type, default value, and allowed value range
} else if((p = strstr(opt->name, " -file "))) {
// for now -file is a synonym for -string, to already provide compatibility with future polyglots
opt->textValue = p+7;
- opt->type = TextBox; // FileName;
+ opt->type = FileName; // FileName;
} else if((p = strstr(opt->name, " -path "))) {
// for now -file is a synonym for -string, to already provide compatibility with future polyglots
opt->textValue = p+7;
- opt->type = TextBox; // PathName;
+ opt->type = PathName; // PathName;
} else if(p = strstr(opt->name, " -check ")) {
if(sscanf(p, " -check %d", &def) < 1) return FALSE;
opt->value = (def != 0);
if(cps->optionSettings && cps->optionSettings[0])
p = strstr(cps->optionSettings, opt->name); else p = NULL;
if(p && (p == cps->optionSettings || p[-1] == ',')) {
- sprintf(buf, "option %s", p);
+ snprintf(buf, MSG_SIZ, "option %s", p);
if(p = strstr(buf, ",")) *p = 0;
+ if(q = strchr(buf, '=')) switch(opt->type) {
+ case ComboBox:
+ for(n=0; n<opt->max; n++)
+ if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
+ break;
+ case TextBox:
+ safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
+ break;
+ case Spin:
+ case CheckBox:
+ opt->value = atoi(q+1);
+ default:
+ break;
+ }
strcat(buf, "\n");
SendToProgram(buf, cps);
}
{
DelayedEventCallback cb = GetDelayedEvent();
if ((cb == InitBackEnd3 && cps == &first) ||
- (cb == TwoMachinesEventIfReady && cps == &second)) {
+ (cb == SettingsMenuIfReady && cps == &second) ||
+ (cb == LoadEngine) ||
+ (cb == TwoMachinesEventIfReady)) {
CancelDelayedEvent();
ScheduleDelayedEvent(cb, val ? 1 : 3600000);
}
void
ParseFeatures(args, cps)
char* args;
- ChessProgramState *cps;
+ ChessProgramState *cps;
{
char *p = args;
char *q;
if (*p == NULLCHAR) return;
if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
- if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;
- if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;
- if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;
- if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;
+ if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;
+ if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;
+ if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;
+ if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;
if (BoolFeature(&p, "reuse", &val, cps)) {
/* Engine can disable reuse, but can't enable it if user said no */
if (!val) cps->reuse = FALSE;
if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {
if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
- sprintf(buf, "rejected option %s\n", cps->option[--cps->nrOptions].name);
+ snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name);
SendToProgram(buf, cps);
continue;
}
if(cps->nrOptions >= MAX_OPTIONS) {
cps->nrOptions--;
- sprintf(buf, "%s engine has too many options\n", cps->which);
+ snprintf(buf, MSG_SIZ, _("%s engine has too many options\n"), _(cps->which));
DisplayError(buf, 0);
}
continue;
}
- if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
/* End of additions by HGM */
/* unknown feature: complain and skip */
q = p;
while (*q && *q != '=') q++;
- sprintf(buf, "rejected %.*s\n", (int)(q-p), p);
+ snprintf(buf, MSG_SIZ,"rejected %.*s\n", (int)(q-p), p);
SendToProgram(buf, cps);
p = q;
if (*p == '=') {
}
void
-NewSettingEvent(option, command, value)
+NewSettingEvent(option, feature, command, value)
char *command;
- int option, value;
+ int option, value, *feature;
{
char buf[MSG_SIZ];
if (gameMode == EditPosition) EditPositionDone(TRUE);
- sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);
- SendToProgram(buf, &first);
+ snprintf(buf, MSG_SIZ,"%s%s %d\n", (option ? "option ": ""), command, value);
+ if(feature == NULL || *feature) SendToProgram(buf, &first);
if (gameMode == TwoMachinesPlay) {
- SendToProgram(buf, &second);
+ if(feature == NULL || feature[(int*)&second - (int*)&first]) SendToProgram(buf, &second);
}
}
int newState = appData.showThinking
// [HGM] thinking: other features now need thinking output as well
|| !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp();
-
+
if (oldState == newState) return;
oldState = newState;
if (gameMode == EditPosition) EditPositionDone(TRUE);
}
void
+TypeInEvent(char firstChar)
+{
+ 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);
+}
+
+void
+TypeInDoneEvent(char *move)
+{
+ Board board;
+ int n, fromX, fromY, toX, toY;
+ char promoChar;
+ 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)
int moveNumber;
{
char cpThinkOutput[MSG_SIZ];
if(appData.noGUI) return; // [HGM] fast: suppress display of moves
-
- if (moveNumber == forwardMostMove - 1 ||
+
+ if (moveNumber == forwardMostMove - 1 ||
gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));
+ safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput)/sizeof(cpThinkOutput[0]));
if (strchr(cpThinkOutput, '\n')) {
*strchr(cpThinkOutput, '\n') = NULLCHAR;
if (moveNumber == forwardMostMove - 1 &&
gameInfo.resultDetails != NULL) {
if (gameInfo.resultDetails[0] == NULLCHAR) {
- sprintf(res, " %s", PGNResult(gameInfo.result));
+ snprintf(res, MSG_SIZ, " %s", PGNResult(gameInfo.result));
} else {
- sprintf(res, " {%s} %s",
- gameInfo.resultDetails, PGNResult(gameInfo.result));
+ snprintf(res, MSG_SIZ, " {%s} %s",
+ T_(gameInfo.resultDetails), PGNResult(gameInfo.result));
}
} else {
res[0] = NULLCHAR;
if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
DisplayMessage(res, cpThinkOutput);
} else {
- sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,
+ snprintf(message, MSG_SIZ, "%d.%s%s%s", moveNumber / 2 + 1,
WhiteOnMove(moveNumber) ? " " : ".. ",
parseList[moveNumber], res);
DisplayMessage(message, cpThinkOutput);
char *text;
{
char title[MSG_SIZ];
- char buf[8000]; // comment can be long!
- int score, depth;
-
+
if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
- strcpy(title, "Comment");
+ safeStrCpy(title, "Comment", sizeof(title)/sizeof(title[0]));
} else {
- sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
+ snprintf(title,MSG_SIZ, "Comment on %d.%s%s", moveNumber / 2 + 1,
WhiteOnMove(moveNumber) ? " " : ".. ",
parseList[moveNumber]);
}
- // [HGM] PV info: display PV info together with (or as) comment
- if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {
- if(text == NULL) text = "";
- score = pvInfoList[moveNumber].score;
- sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
- depth, (pvInfoList[moveNumber].time+50)/100, text);
- text = buf;
- }
if (text != NULL && (appData.autoDisplayComment || commentUp))
CommentPopUp(title, text);
}
/*
* add time to clocks when time control is achieved ([HGM] now also used for increment)
*/
- if ( !WhiteOnMove(forwardMostMove) )
+ if ( !WhiteOnMove(forwardMostMove) ) {
/* White made time control */
- whiteTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)
+ lastWhite -= whiteTimeRemaining; // [HGM] contains start time, socalculate thinking time
+ whiteTimeRemaining += GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, lastWhite, whiteTC)
/* [HGM] time odds: correct new time quota for time odds! */
/ WhitePlayer()->timeOdds;
- else
+ lastBlack = blackTimeRemaining; // [HGM] leave absolute time (after quota), so next switch we can us it to calculate thinking time
+ } else {
+ lastBlack -= blackTimeRemaining;
/* Black made time control */
- blackTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)
+ blackTimeRemaining += GetTimeQuota((forwardMostMove-blackStartMove-1)/2, lastBlack, blackTC)
/ WhitePlayer()->other->timeOdds;
+ lastWhite = whiteTimeRemaining;
+ }
}
void
struct timezone timeZone;
gettimeofday(&timeVal, &timeZone);
- tm->sec = (long) timeVal.tv_sec;
+ tm->sec = (long) timeVal.tv_sec;
tm->ms = (int) (timeVal.tv_usec / 1000L);
#else /*!HAVE_GETTIMEOFDAY*/
* We give the human user a slight advantage if he is playing white---the
* clocks don't run until he makes his first move, so it takes zero time.
* Also, we don't account for network lag, so we could get out of sync
- * with GNU Chess's clock -- but then, referees are always right.
+ * with GNU Chess's clock -- but then, referees are always right.
*/
static TimeMark tickStartTM;
/* Stop clocks and reset to a fresh time control */
void
-ResetClocks()
+ResetClocks()
{
(void) StopClockTimer();
if (appData.icsActive) {
whiteTimeRemaining = 1000*searchTime / WhitePlayer()->timeOdds;
blackTimeRemaining = 1000*searchTime / WhitePlayer()->other->timeOdds;
} else { /* [HGM] correct new time quote for time odds */
- whiteTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->timeOdds;
- blackTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->other->timeOdds;
+ whiteTC = blackTC = fullTimeControlString;
+ whiteTimeRemaining = GetTimeQuota(-1, 0, whiteTC) / WhitePlayer()->timeOdds;
+ blackTimeRemaining = GetTimeQuota(-1, 0, blackTC) / WhitePlayer()->other->timeOdds;
}
if (whiteFlag || blackFlag) {
DisplayTitle("");
whiteFlag = blackFlag = FALSE;
}
+ lastWhite = lastBlack = whiteStartMove = blackStartMove = 0;
DisplayBothClocks();
}
if (!appData.clockMode) return;
if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;
-
+
GetTimeMark(&now);
lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
if (WhiteOnMove(forwardMostMove)) {
if(whiteNPS >= 0) lastTickLength = 0;
timeRemaining = whiteTimeRemaining -= lastTickLength;
+ if(timeRemaining < 0 && !appData.icsActive) {
+ GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, 0, whiteTC); // sets suddenDeath & nextSession;
+ if(suddenDeath) { // [HGM] if we run out of a non-last incremental session, go to the next
+ whiteStartMove = forwardMostMove; whiteTC = nextSession;
+ lastWhite= timeRemaining = whiteTimeRemaining += GetTimeQuota(-1, 0, whiteTC);
+ }
+ }
DisplayWhiteClock(whiteTimeRemaining - fudge,
WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
} else {
if(blackNPS >= 0) lastTickLength = 0;
timeRemaining = blackTimeRemaining -= lastTickLength;
+ if(timeRemaining < 0 && !appData.icsActive) { // [HGM] if we run out of a non-last incremental session, go to the next
+ GetTimeQuota((forwardMostMove-blackStartMove-1)/2, 0, blackTC);
+ if(suddenDeath) {
+ blackStartMove = forwardMostMove;
+ lastBlack = timeRemaining = blackTimeRemaining += GetTimeQuota(-1, 0, blackTC=nextSession);
+ }
+ }
DisplayBlackClock(blackTimeRemaining - fudge,
!WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
}
-
if (CheckFlags()) return;
-
+
tickStartTM = now;
intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
StartClockTimer(intendedTickLength);
/* if the time remaining has fallen below the alarm threshold, sound the
* alarm. if the alarm has sounded and (due to a takeback or time control
* with increment) the time remaining has increased to a level above the
- * threshold, reset the alarm so it can sound again.
+ * threshold, reset the alarm so it can sound again.
*/
-
+
if (appData.icsActive && appData.icsAlarm) {
/* make sure we are dealing with the user's clock */
if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
alarmSounded = FALSE;
- } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
+ } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
PlayAlarmSound();
alarmSounded = TRUE;
}
if(blackNPS >= 0) lastTickLength = 0;
blackTimeRemaining -= lastTickLength;
/* [HGM] PGNtime: save time for PGN file if engine did not give it */
-// if(pvInfoList[forwardMostMove-1].time == -1)
- pvInfoList[forwardMostMove-1].time = // use GUI time
+// if(pvInfoList[forwardMostMove].time == -1)
+ pvInfoList[forwardMostMove].time = // use GUI time
(timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;
} else {
if(whiteNPS >= 0) lastTickLength = 0;
whiteTimeRemaining -= lastTickLength;
/* [HGM] PGNtime: save time for PGN file if engine did not give it */
-// if(pvInfoList[forwardMostMove-1].time == -1)
- pvInfoList[forwardMostMove-1].time =
+// if(pvInfoList[forwardMostMove].time == -1)
+ pvInfoList[forwardMostMove].time =
(timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;
}
flagged = CheckFlags();
whiteTimeRemaining : blackTimeRemaining);
StartClockTimer(intendedTickLength);
}
-
+
/* Stop both clocks */
void
StopClocks()
-{
+{
long lastTickLength;
TimeMark now;
}
CheckFlags();
}
-
+
/* Start clock of player on move. Time may have been reset, so
if clock is already running, stop and restart it. */
void
whiteTimeRemaining : blackTimeRemaining);
/* [HGM] nps: figure out nps factors, by determining which engine plays white and/or black once and for all */
- whiteNPS = blackNPS = -1;
+ whiteNPS = blackNPS = -1;
if(gameMode == MachinePlaysWhite || gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w'
|| appData.zippyPlay && gameMode == IcsPlayingBlack) // first (perhaps only) engine has white
whiteNPS = first.nps;
long second, minute, hour, day;
char *sign = "";
static char buf[32];
-
+
if (ms > 0 && ms <= 9900) {
/* convert milliseconds to tenths, rounding up */
double tenths = floor( ((double)(ms + 99L)) / 100.00 );
- sprintf(buf, " %03.1f ", tenths/10.0);
+ snprintf(buf,sizeof(buf)/sizeof(buf[0]), " %03.1f ", tenths/10.0);
return buf;
}
sign = "-";
second = -second;
}
-
+
day = second / (60 * 60 * 24);
second = second % (60 * 60 * 24);
hour = second / (60 * 60);
second = second % (60 * 60);
minute = second / 60;
second = second % 60;
-
+
if (day > 0)
- sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld:%02ld ",
sign, day, hour, minute, second);
else if (hour > 0)
- sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
else
- sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);
-
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%2ld:%02ld ", sign, minute, second);
+
return buf;
}
char *string, *match;
{
int i, length;
-
+
length = strlen(match);
-
+
for (i = strlen(string) - length; i >= 0; i--, string++)
if (!strncmp(match, string, length))
return string;
-
+
return NULL;
}
char *string, *match;
{
int i, j, length;
-
+
length = strlen(match);
-
+
for (i = strlen(string) - length; i >= 0; i--, string++) {
for (j = 0; j < length; j++) {
if (ToLower(match[j]) != ToLower(string[j]))
char *s1, *s2;
{
char c1, c2;
-
+
for (;;) {
c1 = ToLower(*s1++);
c2 = ToLower(*s2++);
StrSave(s)
char *s;
{
- char *ret;
+ char *ret;
- if ((ret = (char *) malloc(strlen(s) + 1))) {
- strcpy(ret, s);
+ if ((ret = (char *) malloc(strlen(s) + 1)))
+ {
+ safeStrCpy(ret, s, strlen(s)+1);
}
- return ret;
+ return ret;
}
char *
free(*savePtr);
}
if ((*savePtr = (char *) malloc(strlen(s) + 1))) {
- strcpy(*savePtr, s);
+ safeStrCpy(*savePtr, s, strlen(s)+1);
}
return(*savePtr);
}
clock = time((time_t *)NULL);
tm = localtime(&clock);
- sprintf(buf, "%04d.%02d.%02d",
+ snprintf(buf, MSG_SIZ, "%04d.%02d.%02d",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
return StrSave(buf);
}
{
int i, j, fromX, fromY, toX, toY;
int whiteToPlay;
- char buf[128];
+ char buf[MSG_SIZ];
char *p, *q;
int emptycount;
ChessSquare piece;
/* Piece placement data */
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
+ if(MSG_SIZ - (p - buf) < BOARD_RGHT - BOARD_LEFT + 20) { *p = 0; return StrSave(buf); }
emptycount = 0;
for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
if (boards[move][i][j] == EmptySquare) {
/* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */
*p++ = '+';
piece = (ChessSquare)(DEMOTED piece);
- }
+ }
*p++ = PieceToChar(piece);
if(p[-1] == '~') {
/* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
}
if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi &&
- gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) {
+ gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) {
/* En passant target square */
if (move > backwardMostMove) {
fromX = moveList[move - 1][0] - AAA;
}
/* Fullmove number */
sprintf(p, "%d", (move / 2) + 1);
-
+
return StrSave(buf);
}
char *fen;
{
int i, j;
- char *p;
+ char *p, c;
int emptycount;
ChessSquare piece;
while(*p==' ') p++;
if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') {
if(*p == '[') p++;
- if(*p == '-' ) *p++; /* empty holdings */ else {
+ if(*p == '-' ) p++; /* empty holdings */ else {
if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */
/* if we would allow FEN reading to set board size, we would */
/* have to add holdings and shift the board read so far here */
while( (piece = CharToPiece(*p) ) != EmptySquare ) {
- *p++;
+ p++;
if((int) piece >= (int) BlackPawn ) {
i = (int)piece - (int)BlackPawn;
i = PieceToNumber((ChessSquare)i);
}
}
}
- if(*p == ']') *p++;
+ if(*p == ']') p++;
}
while(*p == ' ') p++;
/* Active color */
- switch (*p++) {
+ c = *p++;
+ if(appData.colorNickNames) {
+ if( c == appData.colorNickNames[0] ) c = 'w'; else
+ if( c == appData.colorNickNames[1] ) c = 'b';
+ }
+ switch (c) {
case 'w':
*blackPlaysFirst = FALSE;
break;
- case 'b':
+ case 'b':
*blackPlaysFirst = TRUE;
break;
default:
/* read e.p. field in games that know e.p. capture */
if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi &&
- gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) {
+ gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) {
if(*p=='-') {
p++; board[EP_STATUS] = EP_NONE;
} else {
char c = *p++ - AAA;
if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;
- if(*p >= '0' && *p <='9') *p++;
+ if(*p >= '0' && *p <='9') p++;
board[EP_STATUS] = c;
}
}
return TRUE;
}
-
+
void
EditPositionPasteFEN(char *fen)
{
len = strlen(new_seq);
ret = (len > 0) && (len < sizeof(cseq));
if (ret)
- strcpy(cseq, new_seq);
+ safeStrCpy(cseq, new_seq, sizeof(cseq)/sizeof(cseq[0]));
else if (appData.debugMode)
- fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %u)\n", new_seq, (unsigned) sizeof(cseq)-1);
+ fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %u)\n", new_seq, (unsigned) sizeof(cseq)-1);
return ret;
}
// [HGM] vari: routines for shelving variations
-void
-PushTail(int firstMove, int lastMove)
+void
+PushInner(int firstMove, int lastMove)
{
int i, j, nrMoves = lastMove - firstMove;
- if(appData.icsActive) { // only in local mode
- forwardMostMove = currentMove; // mimic old ICS behavior
- return;
- }
- if(storedGames >= MAX_VARIATIONS-1) return;
-
// push current tail of game on stack
savedResult[storedGames] = gameInfo.result;
savedDetails[storedGames] = gameInfo.resultDetails;
storedGames++;
forwardMostMove = firstMove; // truncate game so we can start variation
+}
+
+void
+PushTail(int firstMove, int lastMove)
+{
+ if(appData.icsActive) { // only in local mode
+ forwardMostMove = currentMove; // mimic old ICS behavior
+ return;
+ }
+ if(storedGames >= MAX_VARIATIONS-2) return; // leave one for PV-walk
+
+ PushInner(firstMove, lastMove);
if(storedGames == 1) GreyRevert(FALSE);
}
-Boolean
-PopTail(Boolean annotate)
+void
+PopInner(Boolean annotate)
{
int i, j, nrMoves;
char buf[8000], moveBuf[20];
- if(appData.icsActive) return FALSE; // only in local mode
- if(!storedGames) return FALSE; // sanity
- CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
-
storedGames--;
ToNrEvent(savedFirst[storedGames]); // sets currentMove
nrMoves = savedLast[storedGames] - currentMove;
if(annotate) {
int cnt = 10;
- if(!WhiteOnMove(currentMove)) sprintf(buf, "(%d...", currentMove+2>>1);
- else strcpy(buf, "(");
+ if(!WhiteOnMove(currentMove))
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]),"(%d...", (currentMove+2)>>1);
+ else safeStrCpy(buf, "(", sizeof(buf)/sizeof(buf[0]));
for(i=currentMove; i<forwardMostMove; i++) {
if(WhiteOnMove(i))
- sprintf(moveBuf, " %d. %s", i+2>>1, SavePart(parseList[i]));
- else sprintf(moveBuf, " %s", SavePart(parseList[i]));
+ snprintf(moveBuf, sizeof(moveBuf)/sizeof(moveBuf[0]), " %d. %s", (i+2)>>1, SavePart(parseList[i]));
+ else snprintf(moveBuf, sizeof(moveBuf)/sizeof(moveBuf[0])," %s", SavePart(parseList[i]));
strcat(buf, moveBuf);
if(commentList[i]) { strcat(buf, " "); strcat(buf, commentList[i]); }
if(!--cnt) { strcat(buf, "\n"); cnt = 10; }
}
gameInfo.resultDetails = savedDetails[storedGames];
forwardMostMove = currentMove + nrMoves;
+}
+
+Boolean
+PopTail(Boolean annotate)
+{
+ if(appData.icsActive) return FALSE; // only in local mode
+ if(!storedGames) return FALSE; // sanity
+ CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
+
+ PopInner(annotate);
+
if(storedGames == 0) GreyRevert(TRUE);
return TRUE;
}
-void
+void
CleanupTail()
{ // remove all shelved variations
int i;
PushTail(currentMove, forwardMostMove); // shelve main variation. This truncates game
// kludge: use ParsePV() to append variation to game
move = currentMove;
- ParsePV(start, TRUE);
+ ParsePV(start, TRUE, TRUE);
forwardMostMove = endPV; endPV = -1; currentMove = move; // cleanup what ParsePV did
ClearPremoveHighlights();
CommentPopDown();