# 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 *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 */
extern int tinyLayout, smallLayout;
ChessProgramStats programStats;
static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
Board partnerBoard; /* [HGM] bughouse: for peeking at partner game */
+int partnerHighlight[2];
Boolean partnerBoardValid = 0;
char partnerStatus[MSG_SIZ];
Boolean partnerUp;
#define TN_SGA 0003
#define TN_PORT 23
-/* [AS] */
-static char * safeStrCpy( char * dst, const char * src, size_t count )
+char*
+safeStrCpy( char *dst, const char *src, size_t count )
{
- assert( dst != NULL );
- assert( src != NULL );
- assert( count > 0 );
+ /* see for example: https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/854-BSI.html
+ *
+ * usage: safeStrCpy( stringA, stringB, sizeof(stringA)/sizeof(stringA[0]);
+ */
- strncpy( dst, src, count );
- dst[ count-1 ] = '\0';
- return dst;
+ assert( dst != NULL );
+ assert( src != NULL );
+ assert( count > 0 );
+
+ strncpy( dst, src, count );
+ if( dst[ count-1 ] != '\0' )
+ {
+ if(appData.debugMode)
+ printf("safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst,count);
+ }
+ dst[ count-1 ] = '\0';
+
+ 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:
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 white_holding[64], black_holding[64];
TimeMark lastNodeCountTime;
long lastNodeCount=0;
+
int have_sent_ICS_logon = 0;
+int sending_ICS_login = 0;
+int sending_ICS_password = 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 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 loadFlag = 0;
int shuffleOpenings;
int mute; // mute all sounds
};
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 }
};
#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
int matched, min, sec;
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
if (appData.icsActive) {
appData.matchMode = FALSE;
appData.matchGames = 0;
-#if ZIPPY
+#if ZIPPY
appData.noChessProgram = !appData.zippyPlay;
#else
appData.zippyPlay = FALSE;
/* [AS] Adjudication threshold */
adjudicateLossThreshold = appData.adjudicateLossThreshold;
-
- first.which = "first";
- second.which = "second";
+
+ first.which = _("first");
+ second.which = _("second");
first.maybeThinking = second.maybeThinking = FALSE;
first.pr = second.pr = NoProc;
first.isr = second.isr = NULL;
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);
+ safeStrCpy(first.variants, appData.variant, sizeof(first.variants)/sizeof(first.variants[0]));
+ safeStrCpy(second.variants, appData.variant,sizeof(second.variants)/sizeof(second.variants[0]));
first.analysisSupport = second.analysisSupport = 2; /* detect */
first.analyzing = second.analyzing = FALSE;
first.initDone = second.initDone = FALSE;
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.firstProtocolVersion > PROTOVER
+ || appData.firstProtocolVersion < 1)
+ {
+ char buf[MSG_SIZ];
+ int len;
- 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;
- }
+ len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
+ appData.firstProtocolVersion);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
+ DisplayFatalError(buf, 0, 2);
+ }
+ else
+ {
+ first.protocolVersion = appData.firstProtocolVersion;
+ }
+
+ if (appData.secondProtocolVersion > PROTOVER
+ || appData.secondProtocolVersion < 1)
+ {
+ char buf[MSG_SIZ];
+ int len;
+
+ len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
+ appData.secondProtocolVersion);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
+ DisplayFatalError(buf, 0, 2);
+ }
+ else
+ {
+ second.protocolVersion = appData.secondProtocolVersion;
+ }
if (appData.icsActive) {
appData.clockMode = TRUE; /* changes dynamically in ICS mode */
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);
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 */
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;
+ int len;
+
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;
{
GameMode initialMode;
char buf[MSG_SIZ];
- int err;
+ int err, len;
InitChessProgram(&first, startedFromSetupPosition);
+ if(!appData.noChessProgram) { /* [HGM] tidy: redo program version to use name from myname feature */
+ free(programVersion);
+ programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+ sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+ }
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) {
} 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;
}
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:") ||
/* [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 (
SendToICS("refresh\n");
continue;
}
-
+
if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
ICSInitScript();
have_sent_ICS_logon = 1;
+ /* if we don't send the login/password via icsLogon, use special readline
+ code for it */
+ if (strlen(appData.icsLogon)==0)
+ {
+ sending_ICS_password = 0; // in case we come back to login
+ sending_ICS_login = 1;
+ };
continue;
}
-
- if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
+ /* need to shadow the password */
+ if (!sending_ICS_password && looking_at(buf, &i, "password:")) {
+ /* if we don't send the login/password via icsLogon, use special readline
+ code for it */
+ if (strlen(appData.icsLogon)==0)
+ sending_ICS_password = 1;
+ continue;
+ }
+
+ 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;
char promoChar;
int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
char *bookHit = NULL; // [HGM] book
- Boolean weird = FALSE, reqFlag = FALSE, repaint = FALSE;
+ Boolean weird = FALSE, reqFlag = FALSE;
fromX = fromY = toX = toY = -1;
-
+
newGame = FALSE;
if (appData.debugMode)
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++) {
for (j = 0; j < files; j++)
board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);
}
}
CopyBoard(partnerBoard, board);
- if(appData.dualBoard && !twoBoards) { twoBoards = repaint = 1; InitDrawingSizes(-2,0); }
+ if(toSqr = strchr(str, '/')) { // extract highlights from long move
+ partnerBoard[EP_STATUS-3] = toSqr[1] - AAA; // kludge: hide highlighting info in board
+ partnerBoard[EP_STATUS-4] = toSqr[2] - ONE;
+ } else partnerBoard[EP_STATUS-4] = partnerBoard[EP_STATUS-3] = -1;
+ if(toSqr = strchr(str, '-')) {
+ partnerBoard[EP_STATUS-1] = toSqr[1] - AAA;
+ partnerBoard[EP_STATUS-2] = toSqr[2] - ONE;
+ } else partnerBoard[EP_STATUS-1] = partnerBoard[EP_STATUS-2] = -1;
+ if(appData.dualBoard && !twoBoards) { twoBoards = 1; InitDrawingSizes(-2,0); }
if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
- if(partnerUp) DrawPosition(repaint, partnerBoard);
+ 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, sizeof(move_str)/sizeof(move_str[0]));
}
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) // 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);
/* 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",
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:
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
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]++;
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, "............");
switch (gameInfo.variant) {
case VariantFischeRandom:
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;
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 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 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:
initialPosition[BOARD_HEIGHT-pawnRow-1][j] = 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;
/* 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;
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);
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 */
piece = boards[currentMove][fromY][fromX];
if(gameInfo.variant == VariantShogi) {
- promotionZoneSize = 3;
+ promotionZoneSize = BOARD_HEIGHT/3;
highestPromotingPiece = (int)WhiteFerz;
} else if(gameInfo.variant == VariantMakruk) {
promotionZoneSize = 3;
if(appData.testLegality && !premove) {
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
fromY, fromX, toY, toX, NULLCHAR);
- if(moveType != WhitePromotionQueen && moveType != BlackPromotionQueen &&
- moveType != WhitePromotionKnight && moveType != BlackPromotionKnight)
+ 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:
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;
/* 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) {
+ 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) {
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) {
// [HGM] superchess: suppress promotions to non-available piece
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
if(WhiteOnMove(currentMove)) {
/* [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) PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game
+ else forwardMostMove = currentMove;
}
/* If we need the chess program but it's dead, restart it */
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 {
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
-
+
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;
{
int x, y;
Boolean saveAnimate;
- static int second = 0, promotionChoice = 0;
+ static int second = 0, promotionChoice = 0, dragging = 0;
char promoChoice = NULLCHAR;
if(appData.seekGraph && appData.icsActive && loggedOn &&
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)
+ 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) ) {
// click in right holdings, for determining promotion piece
fromY = y;
second = 0;
MarkTargetSquares(0);
- DragPieceBegin(xPix, yPix);
+ DragPieceBegin(xPix, yPix); dragging = 1;
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
}
}
+ } else 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;
}
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)) {
+ if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
} else {
}
if (OKToStartUserMove(x, y)) {
fromX = x;
- fromY = y;
+ fromY = y; dragging = 1;
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix);
}
}
if (clickType == Release && x == fromX && y == fromY) {
- DragPieceEnd(xPix, yPix);
+ DragPieceEnd(xPix, yPix); dragging = 0;
if (appData.animateDragging) {
/* Undo animation damage if any */
DrawPosition(FALSE, NULL);
} 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; }
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
+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;
}
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)
}
/* 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;
}
/* 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
}
} 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 */
+ }
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ 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 */
// 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
+ snprintf(buf, MSG_SIZ, "%s%s\n", (cps->useUsermove ? "usermove " : ""), bookHit); // 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
* 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"),
+ 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->offeredDraw) cps->offeredDraw--;
/* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(machineMove, currentMoveString);
+ safeStrCpy(machineMove, currentMoveString, sizeof(machineMove)/sizeof(machineMove[0]));
strcat(machineMove, "\n");
- strcpy(moveList[forwardMostMove], machineMove);
+ safeStrCpy(moveList[forwardMostMove], machineMove, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0]));
+
+ /* [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(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);
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)) {
+ EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box
DisplayNote(message + 9);
return;
}
if (!strncmp(message, "tellusererror ", 14)) {
cps->userError = 1;
+ EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box
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;
}
DisplayMove(currentMove-1); /* before DisplayMoveError */
SwitchClocks(forwardMostMove-1); // [HGM] race
DisplayBothClocks();
- sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),
+ snprintf(buf1, 10*MSG_SIZ, _("Illegal move \"%s\" (rejected by %s chess program)"),
parseList[currentMove], cps->which);
DisplayMoveError(buf1);
DrawPosition(FALSE, boards[currentMove]);
Don't use it. */
cps->sendTime = 0;
}
-
+
/*
* If chess program startup fails, exit with an error message.
* Attempts to recover here are futile.
DisplayFatalError(buf1, 0, 1);
return;
}
-
- /*
+
+ /*
* Look for hint output
*/
if (sscanf(message, "Hint: %s", buf1) == 1) {
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
}
- 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 (strlen(buf1) >= 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, buf1, 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 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
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);
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]);
/* [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;
+
+ /* [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 (fromY == DROP_RANK) {
+ /* must be first */
+ piece = board[toY][toX] = (ChessSquare) fromX;
+ } else {
+ int i;
if( board[fromY][fromX] == WhitePawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
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
}
- }
+ if (fromX == toX && fromY == toY) return;
- /* [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 (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)
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) {
if (captured != EmptySquare && gameInfo.holdingsSize > 0
&& gameInfo.variant != VariantBughouse ) {
/* [HGM] holdings: Add to holdings, if holdings exist */
- if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
// [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(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) { // without legality testing, unconditionally believe promoChar
+ board[toY][toX] = CharToPiece(promoChar);
}
-
- if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
- && promoChar != NULLCHAR && gameInfo.holdingsSize) {
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
+ && 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)
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 ||
+ if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom ||
gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon )
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
if( gameInfo.variant == VariantCourier )
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
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;
-}
+}
void
}
err = StartChildProcess(buf, "", &cps->pr);
}
-
+
if (err != 0) {
- sprintf(buf, _("Startup failure on '%s'"), cps->program);
+ snprintf(buf, MSG_SIZ, _("Startup failure on '%s'"), cps->program);
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);
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);
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);
{
GameMode nextGameMode;
int isIcsGame;
- char buf[MSG_SIZ];
+ char buf[MSG_SIZ], popupRequested = 0;
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 ) {
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;
}
}
/* [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
+ && 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);
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));
+ popupRequested++; // [HGM] crash: postpone to after resetting endingGame
}
}
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) DisplayFatalError(buf, 0, 0); else {
+ matchMode = FALSE; appData.matchGames = matchGame = 0;
+ DisplayNote(buf);
+ }
+ }
}
/* 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;
}
If so, restart it and feed it all the moves made so far. */
if (appData.noChessProgram || first.pr != NoProc) return;
-
+
StartChessProgram(&first);
InitChessProgram(&first, FALSE);
FeedMovesToProgram(&first, currentMove);
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();
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();
}
-
+
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:
}
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);
} else {
/* currentMoveString is set as a side-effect of yylex */
strcat(currentMoveString, "\n");
- strcpy(moveList[forwardMostMove], currentMoveString);
-
+ safeStrCpy(moveList[forwardMostMove], currentMoveString, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0]));
+
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();
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;
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);
yyboardindex = forwardMostMove;
cm = (ChessMove) yylex();
- 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) {
/* 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);
}
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) {
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);
cm = (ChessMove) yylex();
}
- 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;
}
{
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]);
}
{
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 );
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]);
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 (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);
}
char buf[MSG_SIZ];
ChessProgramState *onmove;
char *bookHit = NULL;
-
+
if (appData.noChessProgram) return;
switch (gameMode) {
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");
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];
/* 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];
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.date = PGNDate();
if (matchGame > 0) {
char buf[MSG_SIZ];
- sprintf(buf, "%d", matchGame);
+ snprintf(buf, MSG_SIZ, "%d", matchGame);
gameInfo.round = StrSave(buf);
} else {
gameInfo.round = StrSave("-");
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);
free(old);
// [HGM] braces: join "{A\n}\n" + "{\nB}" as "{A\nB\n}"
if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces)) {
} else {
commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4...
if(addBraces)
- strcpy(commentList[index], "{\n");
+ safeStrCpy(commentList[index], "{\n", sizeof(commentList[index])/sizeof(commentList[index][0]));
else commentList[index][0] = NULLCHAR;
strcat(commentList[index], text);
strcat(commentList[index], "\n");
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);
+ 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) {
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);
+ snprintf(buf, MSG_SIZ, "%s program exits in draw position (%s)", cps->which, cps->program);
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
if (isr != cps->isr) return; /* Killed intentionally */
if (count <= 0) {
if (count == 0) {
- sprintf(buf,
- _("Error: %s chess program (%s) exited unexpectedly"),
+ 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) {
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program);
+ snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), cps->which, cps->program);
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
RemoveInputSource(cps->isr);
if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, 0, 1); else errorExitStatus = 1;
} else {
- sprintf(buf,
- _("Error reading from %s chess program (%s)"),
+ snprintf(buf, MSG_SIZ, _("Error reading from %s chess program (%s)"),
cps->which, cps->program);
RemoveInputSource(cps->isr);
}
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 &&
}
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->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
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;
strcat(buf, "\n");
SendToProgram(buf, cps);
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);
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 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 = "";
+ if(text == NULL) text = "";
score = pvInfoList[moveNumber].score;
- sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
+ snprintf(buf,sizeof(buf)/sizeof(buf[0]), "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
depth, (pvInfoList[moveNumber].time+50)/100, text);
text = buf;
}
/*
* 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;
}
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 =
+ pvInfoList[forwardMostMove-1].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);
}
/* [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
+void
PushTail(int firstMove, int lastMove)
{
int i, j, nrMoves = lastMove - firstMove;
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; }
return TRUE;
}
-void
+void
CleanupTail()
{ // remove all shelved variations
int i;
CommentPopDown();
ToNrEvent(currentMove+1);
}
+