#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
+# include <stdarg.h>
#else /* not STDC_HEADERS */
# if HAVE_STRING_H
# include <string.h>
char *buf, int count, int error));
void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
char *buf, int count, int error));
+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 InitPosition P((int redraw));
void HandleMachineMove P((char *message, ChessProgramState *cps));
int AutoPlayOneMove P((void));
int LoadGameOneMove P((ChessMove readAhead));
int LoadPositionFromFile P((char *filename, int n, char *title));
int SavePositionToFile P((char *filename));
void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
- Board board, char *castle, char *ep));
+ Board board));
void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
void ShowMove P((int fromX, int fromY, int toX, int toY));
int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
void BackwardInner P((int target));
void ForwardInner P((int target));
void GameEnds P((ChessMove result, char *resultDetails, int whosays));
-void EditPositionDone P((void));
+void EditPositionDone P((Boolean fakeRights));
void PrintOpponents P((FILE *fp));
void PrintPosition P((FILE *fp, int move));
void StartChessProgram P((ChessProgramState *cps));
void ResurrectChessProgram P((void));
void DisplayComment P((int moveNumber, char *text));
void DisplayMove P((int moveNumber));
-void DisplayAnalysis P((void));
void ParseGameHistory P((char *game));
void ParseBoard12 P((char *string));
void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c
char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
+void ics_update_width P((int new_width));
extern char installDir[MSG_SIZ];
extern int tinyLayout, smallLayout;
int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
int opponentKibitzes;
int lastSavedGame; /* [HGM] save: ID of game */
+char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
+extern int chatCount;
+int chattingPartner;
/* States for ics_getting_history */
#define H_FALSE 0
return dst;
}
-#if 0
-//[HGM] for future use? Conditioned out for now to suppress warning.
-static char * safeStrCat( char * dst, const char * src, size_t count )
-{
- size_t dst_len;
-
- assert( dst != NULL );
- assert( src != NULL );
- assert( count > 0 );
-
- dst_len = strlen(dst);
-
- assert( count > dst_len ); /* Buffer size must be greater than current length */
-
- safeStrCpy( dst + dst_len, src, count - dst_len );
-
- return dst;
-}
-#endif
-
/* Some compiler can't cast u64 to double
* This function do the job for us:
Board boards[MAX_MOVES];
/* [HGM] Following 7 needed for accurate legality tests: */
-char epStatus[MAX_MOVES];
-char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
-char castlingRank[BOARD_SIZE]; // and corresponding ranks
-char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
+signed char castlingRank[BOARD_FILES]; // and corresponding ranks
+signed char initialRights[BOARD_FILES];
int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
int initialRulePlies, FENrulePlies;
-char FENepStatus;
FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
int loadFlag = 0;
int shuffleOpenings;
-
-ChessSquare FIDEArray[2][BOARD_SIZE] = {
+int mute; // mute all sounds
+
+// [HGM] vari: next 12 to save and restore variations
+#define MAX_VARIATIONS 10
+int framePtr = MAX_MOVES-1; // points to free stack entry
+int storedGames = 0;
+int savedFirst[MAX_VARIATIONS];
+int savedLast[MAX_VARIATIONS];
+int savedFramePtr[MAX_VARIATIONS];
+char *savedDetails[MAX_VARIATIONS];
+ChessMove savedResult[MAX_VARIATIONS];
+
+void PushTail P((int firstMove, int lastMove));
+Boolean PopTail P((void));
+void CleanupTail P((void));
+
+ChessSquare FIDEArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackBishop, BlackQueen,
BlackKing, BlackBishop, BlackKnight, BlackRook }
};
-ChessSquare twoKingsArray[2][BOARD_SIZE] = {
+ChessSquare twoKingsArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackBishop, BlackQueen,
BlackKing, BlackKing, BlackKnight, BlackRook }
};
-ChessSquare KnightmateArray[2][BOARD_SIZE] = {
+ChessSquare KnightmateArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,
WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },
{ BlackRook, BlackMan, BlackBishop, BlackQueen,
BlackUnicorn, BlackBishop, BlackMan, BlackRook }
};
-ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */
+ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
{ WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,
WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
{ BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,
BlackKing, BlackBishop, BlackKnight, BlackRook }
};
-ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */
+ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
{ WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,
WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackAlfil, BlackKing,
};
-#if (BOARD_SIZE>=10)
-ChessSquare ShogiArray[2][BOARD_SIZE] = {
+#if (BOARD_FILES>=10)
+ChessSquare ShogiArray[2][BOARD_FILES] = {
{ WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,
WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },
{ BlackQueen, BlackKnight, BlackFerz, BlackWazir,
BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }
};
-ChessSquare XiangqiArray[2][BOARD_SIZE] = {
+ChessSquare XiangqiArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,
WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackAlfil, BlackFerz,
BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }
};
-ChessSquare CapablancaArray[2][BOARD_SIZE] = {
+ChessSquare CapablancaArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
};
-ChessSquare GreatArray[2][BOARD_SIZE] = {
+ChessSquare GreatArray[2][BOARD_FILES] = {
{ WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
{ BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
};
-ChessSquare JanusArray[2][BOARD_SIZE] = {
+ChessSquare JanusArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
{ BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
};
#ifdef GOTHIC
-ChessSquare GothicArray[2][BOARD_SIZE] = {
+ChessSquare GothicArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
#endif // !GOTHIC
#ifdef FALCON
-ChessSquare FalconArray[2][BOARD_SIZE] = {
+ChessSquare FalconArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
#define FalconArray CapablancaArray
#endif // !FALCON
-#else // !(BOARD_SIZE>=10)
+#else // !(BOARD_FILES>=10)
#define XiangqiPosition FIDEArray
#define CapablancaArray FIDEArray
#define GothicArray FIDEArray
#define GreatArray FIDEArray
-#endif // !(BOARD_SIZE>=10)
+#endif // !(BOARD_FILES>=10)
-#if (BOARD_SIZE>=12)
-ChessSquare CourierArray[2][BOARD_SIZE] = {
+#if (BOARD_FILES>=12)
+ChessSquare CourierArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,
WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },
{ BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
};
-#else // !(BOARD_SIZE>=12)
+#else // !(BOARD_FILES>=12)
#define CourierArray CapablancaArray
-#endif // !(BOARD_SIZE>=12)
+#endif // !(BOARD_FILES>=12)
Board initialPosition;
ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
GetTimeMark(&programStartTime);
- srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level
+ srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
ClearProgramStats();
programStats.ok_to_send = 1;
{
int i, j;
- for( i=0; i<MAX_MOVES; i++ ) {
+ for( i=0; i<=framePtr; i++ ) {
pvInfoList[i].depth = -1;
- epStatus[i]=EP_NONE;
- for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;
+ boards[i][EP_STATUS] = EP_NONE;
+ for( j=0; j<BOARD_FILES-2; j++ ) boards[i][CASTLING][j] = NoRights;
}
}
if (appData.icsActive) {
appData.clockMode = TRUE; /* changes dynamically in ICS mode */
- } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
+// } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
+ } else if (appData.noChessProgram) { // [HGM] st: searchTime mode now also is clockMode
appData.clockMode = FALSE;
first.sendTime = second.sendTime = 0;
}
programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
sprintf(programVersion, "%s", PACKAGE_STRING);
} else {
-#if 0
- char *p, *q;
- q = first.program;
- while (*q != ' ' && *q != NULLCHAR) q++;
- p = q;
- while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] backslash added */
- programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING + (q - p));
- sprintf(programVersion, "%s + ", PACKAGE_STRING);
- strncat(programVersion, p, q - p);
-#else
- /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
- programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
- sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
-#endif
+ /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
+ programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+ sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
}
if (!appData.icsActive) {
int ti;
int mps;
{
-#if 0
- int matched, min, sec;
-
- matched = sscanf(tc, "%d:%d", &min, &sec);
- if (matched == 1) {
- timeControl = min * 60 * 1000;
- } else if (matched == 2) {
- timeControl = (min * 60 + sec) * 1000;
- } else {
- return FALSE;
- }
-#else
- long tc1;
- long tc2;
- char buf[MSG_SIZ];
-
- if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
- if(ti > 0) {
- if(mps)
- sprintf(buf, "+%d/%s+%d", mps, tc, ti);
- else sprintf(buf, "+%s+%d", tc, ti);
- } else {
- if(mps)
+ long tc1;
+ long tc2;
+ char buf[MSG_SIZ];
+
+ if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
+ if(ti > 0) {
+ if(mps)
+ sprintf(buf, "+%d/%s+%d", mps, tc, ti);
+ else sprintf(buf, "+%s+%d", tc, ti);
+ } else {
+ if(mps)
sprintf(buf, "+%d/%s", mps, tc);
- else sprintf(buf, "+%s", tc);
- }
- fullTimeControlString = StrSave(buf);
-
- 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;
+ else sprintf(buf, "+%s", tc);
+ }
+ fullTimeControlString = StrSave(buf);
+
+ if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
+ return FALSE;
+ }
+
+ if( *tc == '/' ) {
+ /* Parse second time control */
+ tc++;
+
+ if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
+ return FALSE;
}
-
- timeControl = tc1 * 1000;
-#endif
-
- if (ti >= 0) {
- timeIncrement = ti * 1000; /* convert to ms */
- movesPerSession = 0;
- } else {
- timeIncrement = 0;
- movesPerSession = mps;
+
+ if( tc2 == 0 ) {
+ return FALSE;
}
- return TRUE;
+
+ 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;
+ } else {
+ timeIncrement = 0;
+ movesPerSession = mps;
+ }
+ return TRUE;
}
void
fprintf(debugFP, "%s\n", programVersion);
}
+ set_cont_sequence(appData.wrapContSeq);
if (appData.matchGames > 0) {
appData.matchMode = TRUE;
} else if (appData.matchMode) {
}
/* [HGM] loadPos: make that every new game uses the setup */
/* from file as long as we do not switch variant */
- if(!blackPlaysFirst) { int i;
+ if(!blackPlaysFirst) {
startedFromPositionFile = TRUE;
CopyBoard(filePosition, boards[0]);
- for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];
}
}
if (initialMode == AnalyzeMode) {
}
void
+KeepAlive()
+{ // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out
+ SendToICS("date\n");
+ if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
+}
+
+/* added routine for printf style output to ics */
+void ics_printf(char *format, ...)
+{
+ char buffer[MSG_SIZ];
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ buffer[sizeof(buffer)-1] = '\0';
+ SendToICS(buffer);
+ va_end(args);
+}
+
+void
SendToICS(s)
char *s;
{
while( *e++ != '_');
}
+ if(StrCaseStr(e, "misc/")) { // [HGM] on FICS, misc/shogi is not shogi
+ v = VariantNormal;
+ found = TRUE;
+ } else
for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
if (StrCaseStr(e, variantNames[i])) {
v = (VariantClass) i;
ChessSquare piece;
if(gameInfo.holdingsWidth < 2) return;
+ if(gameInfo.variant != VariantBughouse && board[HOLDINGS_SET])
+ return; // prevent overwriting by pre-board holdings
if( (int)lowestPiece >= BlackPawn ) {
holdingsColumn = 0;
board[holdingsStartRow+j*direction][holdingsColumn] = piece;
board[holdingsStartRow+j*direction][countsColumn]++;
}
-
}
VariantSwitch(Board board, VariantClass newVariant)
{
int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
- int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;
-// Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;
+ 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));
- setbuf(debugFP, NULL);
- }
- shuffleOpenings = 0; /* [HGM] shuffle */
- gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
- switch(newVariant) {
- case VariantShogi:
- newWidth = 9; newHeight = 9;
- gameInfo.holdingsSize = 7;
- case VariantBughouse:
- case VariantCrazyhouse:
- newHoldingsWidth = 2; break;
- 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(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
- board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
- board[i][j];
- for(i=0; i<newHeight; i++) {
- board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
- board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
- }
- } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
- for(i=0; i<BOARD_HEIGHT; i++)
- for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
- board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
- board[i][j];
- }
-
- gameInfo.boardWidth = newWidth;
- gameInfo.boardHeight = newHeight;
- gameInfo.holdingsWidth = newHoldingsWidth;
- gameInfo.variant = newVariant;
- InitDrawingSizes(-2, 0);
-
- /* [HGM] The following should definitely be solved in a better way */
-#if 0
- CopyBoard(board, tempBoard); /* save position in case it is board[0] */
- for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];
- saveEP = epStatus[0];
-#endif
- InitPosition(FALSE); /* this sets up board[0], but also other stuff */
-#if 0
- epStatus[0] = saveEP;
- for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];
- CopyBoard(tempBoard, board); /* restore position received from ICS */
-#endif
- } else { gameInfo.variant = newVariant; InitPosition(FALSE); }
-
- forwardMostMove = oldForwardMostMove;
- backwardMostMove = oldBackwardMostMove;
- currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */
+
+ if (appData.debugMode) {
+ fprintf(debugFP, "Switch board from %s to %s\n",
+ VariantName(gameInfo.variant), VariantName(newVariant));
+ setbuf(debugFP, NULL);
+ }
+ shuffleOpenings = 0; /* [HGM] shuffle */
+ gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
+ switch(newVariant)
+ {
+ case VariantShogi:
+ newWidth = 9; newHeight = 9;
+ gameInfo.holdingsSize = 7;
+ case VariantBughouse:
+ case VariantCrazyhouse:
+ newHoldingsWidth = 2; break;
+ case VariantGreat:
+ newWidth = 10;
+ case VariantSuper:
+ newHoldingsWidth = 2;
+ gameInfo.holdingsSize = 8;
+ break;
+ case VariantGothic:
+ case VariantCapablanca:
+ case VariantCapaRandom:
+ newWidth = 10;
+ 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(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
+ board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
+ board[i][j];
+ for(i=0; i<newHeight; i++) {
+ board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
+ board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
+ }
+ } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
+ for(i=0; i<BOARD_HEIGHT; i++)
+ for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
+ board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
+ board[i][j];
+ }
+ gameInfo.boardWidth = newWidth;
+ gameInfo.boardHeight = newHeight;
+ gameInfo.holdingsWidth = newHoldingsWidth;
+ gameInfo.variant = newVariant;
+ InitDrawingSizes(-2, 0);
+ } else gameInfo.variant = newVariant;
+ CopyBoard(oldBoard, board); // remember correctly formatted board
+ InitPosition(FALSE); /* this sets up board[0], but also other stuff */
+ DrawPosition(TRUE, currentMove ? boards[currentMove] : oldBoard);
}
static int loggedOn = FALSE;
char gs_kind[MSG_SIZ];
static char player1Name[128] = "";
static char player2Name[128] = "";
+static char cont_seq[] = "\n\\ ";
static int player1Rating = -1;
static int player2Rating = -1;
/*----------------------------*/
static int firstTime = TRUE, intfSet = FALSE;
static ColorClass prevColor = ColorNormal;
static int savingComment = FALSE;
+ static int cmatch = 0; // continuation sequence match
+ char *bp;
char str[500];
int i, oldi;
int buf_len;
int tkind;
int backup; /* [DM] For zippy color lines */
char *p;
+ char talker[MSG_SIZ]; // [HGM] chat
+ int channel;
if (appData.debugMode) {
if (!error) {
if (appData.debugMode) { int f = forwardMostMove;
fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,
- castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
+ boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
+ boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
}
if (count > 0) {
/* If last read ended with a partial line that we couldn't parse,
buf[i] = buf[leftover_start + i];
}
- /* Copy in new characters, removing nulls and \r's */
- buf_len = leftover_len;
- for (i = 0; i < count; i++) {
- if (data[i] != NULLCHAR && data[i] != '\r')
- buf[buf_len++] = data[i];
- if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' &&
- buf[buf_len-3]==' ' && buf[buf_len-2]==' ' && buf[buf_len-1]==' ') {
- buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous
- buf[buf_len++] = ' '; // replace by space (assumes ICS does not break lines within word)
- }
- }
+ /* copy new characters into the buffer */
+ bp = buf + leftover_len;
+ buf_len=leftover_len;
+ for (i=0; i<count; i++)
+ {
+ // ignore these
+ if (data[i] == '\r')
+ continue;
+
+ // join lines split by ICS?
+ if (!appData.noJoin)
+ {
+ /*
+ Joining just consists of finding matches against the
+ continuation sequence, and discarding that sequence
+ if found instead of copying it. So, until a match
+ fails, there's nothing to do since it might be the
+ complete sequence, and thus, something we don't want
+ copied.
+ */
+ if (data[i] == cont_seq[cmatch])
+ {
+ cmatch++;
+ if (cmatch == strlen(cont_seq))
+ {
+ cmatch = 0; // complete match. just reset the counter
+
+ /*
+ it's possible for the ICS to not include the space
+ at the end of the last word, making our [correct]
+ join operation fuse two separate words. the server
+ does this when the space occurs at the width setting.
+ */
+ if (!buf_len || buf[buf_len-1] != ' ')
+ {
+ *bp++ = ' ';
+ buf_len++;
+ }
+ }
+ continue;
+ }
+ else if (cmatch)
+ {
+ /*
+ match failed, so we have to copy what matched before
+ falling through and copying this character. In reality,
+ this will only ever be just the newline character, but
+ it doesn't hurt to be precise.
+ */
+ strncpy(bp, cont_seq, cmatch);
+ bp += cmatch;
+ buf_len += cmatch;
+ cmatch = 0;
+ }
+ }
+
+ // copy this char
+ *bp++ = data[i];
+ buf_len++;
+ }
buf[buf_len] = NULLCHAR;
next_out = leftover_len;
sprintf(str,
"/set-quietly interface %s\n/set-quietly style 12\n",
programVersion);
-
} else if (ics_type == ICS_CHESSNET) {
sprintf(str, "/style 12\n");
} else {
strcat(str, "$iset lock 1\n$style 12\n");
}
SendToICS(str);
+ NotifyFrontendLogin();
intfSet = TRUE;
}
parse[parse_pos++] = buf[i];
if (buf[i] == '\n') {
parse[parse_pos] = NULLCHAR;
+ if(chattingPartner>=0) {
+ char mess[MSG_SIZ];
+ sprintf(mess, "%s%s", talker, parse);
+ OutputChatMessage(chattingPartner, mess);
+ chattingPartner = -1;
+ } else
if(!suppressKibitz) // [HGM] kibitz
- AppendComment(forwardMostMove, StripHighlight(parse));
+ AppendComment(forwardMostMove, StripHighlight(parse), TRUE);
else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
int nrDigit = 0, nrAlph = 0, i;
if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
}
} // [HGM] kibitz: end of patch
+//if(appData.debugMode) fprintf(debugFP, "hunt for tell, buf = %s\n", buf+i);
+
+ // [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:") ||
+ looking_at(buf, &i, "* whispers:") ||
+ looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) ||
+ looking_at(buf, &i, "*(*)(*):") && sscanf(star_match[2], "%d", &channel) == 1 )) {
+ int p;
+ sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
+ chattingPartner = -1;
+
+ if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
+ for(p=0; p<MAX_CHAT; p++) {
+ if(channel == atoi(chatPartner[p])) {
+ talker[0] = '['; strcat(talker, "]");
+ chattingPartner = p; break;
+ }
+ } else
+ if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
+ for(p=0; p<MAX_CHAT; p++) {
+ if(!strcmp("WHISPER", chatPartner[p])) {
+ talker[0] = '['; strcat(talker, "]");
+ chattingPartner = p; break;
+ }
+ }
+ if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
+ for(p=0; p<MAX_CHAT; p++) if(!StrCaseCmp(talker+1, chatPartner[p])) {
+ talker[0] = 0;
+ chattingPartner = p; break;
+ }
+ if(chattingPartner<0) i = oldi; else {
+ started = STARTED_COMMENT;
+ parse_pos = 0; parse[0] = NULLCHAR;
+ savingComment = TRUE;
+ suppressKibitz = TRUE;
+ }
+ } // [HGM] chat: end of patch
+
if (appData.zippyTalk || appData.zippyPlay) {
/* [DM] Backup address for color zippy lines */
backup = i;
#endif
#endif
} // [DM] 'else { ' deleted
- if (/* Don't color "message" or "messages" output */
- (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
- looking_at(buf, &i, "*. * at *:*: ") ||
- looking_at(buf, &i, "--* (*:*): ") ||
+ if (
/* Regular tells and says */
(tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
looking_at(buf, &i, "* (your partner) tells you: ") ||
looking_at(buf, &i, "* says: ") ||
+ /* Don't color "message" or "messages" output */
+ (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
+ looking_at(buf, &i, "*. * at *:*: ") ||
+ looking_at(buf, &i, "--* (*:*): ") ||
/* Message notifications (same color as tells) */
looking_at(buf, &i, "* has left a message ") ||
looking_at(buf, &i, "* just sent you a message:\n") ||
}
SendTimeRemaining(&first, TRUE);
}
-#if 0
- if (first.useColors) {
- SendToProgram("white\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
-#else
if (first.useColors) {
SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent
}
bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos
-#endif
first.maybeThinking = TRUE;
} else {
if (first.usePlayother) {
}
SendTimeRemaining(&first, FALSE);
}
-#if 0
- if (first.useColors) {
- SendToProgram("black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
-#else
if (first.useColors) {
SendToProgram("black\n", &first);
}
bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);
-#endif
first.maybeThinking = TRUE;
} else {
if (first.usePlayother) {
currentMove = forwardMostMove;
ClearHighlights();/*!!could figure this out*/
flipView = appData.flipView;
- DrawPosition(FALSE, boards[currentMove]);
+ DrawPosition(TRUE, boards[currentMove]);
DisplayBothClocks();
sprintf(str, "%s vs. %s",
gameInfo.white, gameInfo.black);
if (currentMove == 0 &&
gameMode == IcsPlayingWhite &&
appData.premoveWhite) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveWhiteText);
+ sprintf(str, "%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%s\n", ics_prefix,
- appData.premoveBlackText);
+ sprintf(str, "%s\n", appData.premoveBlackText);
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
/* Usually suppress following prompt */
if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
+ while(looking_at(buf, &i, "\n")); // [HGM] skip empty lines
if (looking_at(buf, &i, "*% ")) {
savingComment = FALSE;
}
* to move the position two files to the right to
* create room for them!
*/
- VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */
+ VariantClass newVariant;
+ switch(gameInfo.boardWidth) { // base guess on board width
+ case 9: newVariant = VariantShogi; break;
+ case 10: newVariant = VariantGreat; break;
+ default: newVariant = VariantCrazyhouse; break;
+ }
+ VariantSwitch(boards[currentMove], newVariant); /* temp guess */
/* Get a move list just to see the header, which
will tell us whether this is really bug or zh */
if (ics_getting_history == H_FALSE) {
white_holding[strlen(white_holding)-1] = NULLCHAR;
black_holding[strlen(black_holding)-1] = NULLCHAR;
/* [HGM] copy holdings to board holdings area */
- CopyHoldings(boards[currentMove], white_holding, WhitePawn);
- CopyHoldings(boards[currentMove], black_holding, BlackPawn);
+ CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn);
+ CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn);
+ boards[forwardMostMove][HOLDINGS_SET] = 1; // flag holdings as set
#if ZIPPY
if (appData.zippyPlay && first.initDone) {
ZippyHoldings(white_holding, black_holding,
}
/* Suppress following prompt */
if (looking_at(buf, &i, "*% ")) {
+ if(strchr(star_match[0], 7)) SendToPlayer("\007", 1); // Bell(); // FICS fuses bell for next board with prompt in zh captures
savingComment = FALSE;
}
next_out = i;
char promoChar;
int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
char *bookHit = NULL; // [HGM] book
+ Boolean weird = FALSE, reqFlag = FALSE;
fromX = fromY = toX = toY = -1;
while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {
if(string[i] == ' ') { ranks++; files = 0; }
else files++;
+ if(!strchr(" -pnbrqkPNBRQK" , string[i])) weird = TRUE; // test for fairies
i++;
}
for(j = 0; j <i; j++) board_chars[j] = string[j];
/* Convert the move number to internal form */
moveNum = (moveNum - 1) * 2;
if (to_play == 'B') moveNum++;
- if (moveNum >= MAX_MOVES) {
+ if (moveNum > framePtr) { // [HGM] vari: do not run into saved variations
DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
0, 1);
return;
ics_getting_history = H_FALSE;
return;
}
+
+ 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
+ */
+ VariantClass newVariant = VariantFairy; // if 8x8, but fairies present
+ if(ranks == 8 && files == 10) newVariant = VariantCapablanca; else
+ if(ranks == 10 && files == 9) newVariant = VariantXiangqi; else
+ if(ranks == 8 && files == 12) newVariant = VariantCourier; else
+ if(ranks == 9 && files == 9) newVariant = VariantShogi; else
+ if(!weird) newVariant = VariantNormal;
+ VariantSwitch(boards[currentMove], newVariant); /* temp guess */
+ /* Get a move list just to see the header, which
+ 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);
+ SendToICS(str);
+ }
+ }
/* Take action if this is the first board of a new game, or of a
different game than is currently being displayed. */
/* Forget the old game and get the history (if any) of the new one */
if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
+ Reset(TRUE, TRUE);
}
newGame = TRUE;
if (appData.autoRaiseBoard) BoardToTop();
prevMove = -3;
if (gamenum == -1) {
newGameMode = IcsIdle;
- } else if (moveNum > 0 && newGameMode != IcsIdle &&
- appData.getMoveList) {
+ } else if ((moveNum > 0 || newGameMode == IcsObserving) && newGameMode != IcsIdle &&
+ appData.getMoveList && !reqFlag) {
/* Need to get game history */
ics_getting_history = H_REQUESTED;
sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
timeIncrement = increment * 1000;
movesPerSession = 0;
gameInfo.timeControl = TimeControlTagValue();
- VariantSwitch(board, StringToVariant(gameInfo.event) );
+ VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event) );
if (appData.debugMode) {
fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);
fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));
}
}
CopyBoard(boards[moveNum], board);
+ boards[moveNum][HOLDINGS_SET] = 0; // [HGM] indicate holdings not set
if (moveNum == 0) {
startedFromSetupPosition =
!CompareBoards(board, initialPosition);
for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
if(board[0][i] == WhiteRook) j = i;
- initialRights[0] = castlingRights[moveNum][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
+ initialRights[0] = boards[moveNum][CASTLING][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
if(board[0][i] == WhiteRook) j = i;
- initialRights[1] = castlingRights[moveNum][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
+ initialRights[1] = boards[moveNum][CASTLING][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
- initialRights[3] = castlingRights[moveNum][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
+ initialRights[3] = boards[moveNum][CASTLING][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
- initialRights[4] = castlingRights[moveNum][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
+ initialRights[4] = boards[moveNum][CASTLING][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
if(gameInfo.variant == VariantKnightmate) { wKing = WhiteUnicorn; bKing = BlackUnicorn; }
for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
- if(board[0][k] == wKing) initialRights[2] = castlingRights[moveNum][2] = k;
+ if(board[0][k] == wKing) initialRights[2] = boards[moveNum][CASTLING][2] = k;
for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
if(board[BOARD_HEIGHT-1][k] == bKing)
- initialRights[5] = castlingRights[moveNum][5] = k;
+ initialRights[5] = boards[moveNum][CASTLING][5] = k;
} else { int r;
- r = castlingRights[moveNum][0] = initialRights[0];
- if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;
- r = castlingRights[moveNum][1] = initialRights[1];
- if(board[0][r] != WhiteRook) castlingRights[moveNum][1] = -1;
- r = castlingRights[moveNum][3] = initialRights[3];
- if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][3] = -1;
- r = castlingRights[moveNum][4] = initialRights[4];
- if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][4] = -1;
+ r = boards[moveNum][CASTLING][0] = initialRights[0];
+ if(board[0][r] != WhiteRook) boards[moveNum][CASTLING][0] = NoRights;
+ r = boards[moveNum][CASTLING][1] = initialRights[1];
+ if(board[0][r] != WhiteRook) boards[moveNum][CASTLING][1] = NoRights;
+ r = boards[moveNum][CASTLING][3] = initialRights[3];
+ if(board[BOARD_HEIGHT-1][r] != BlackRook) boards[moveNum][CASTLING][3] = NoRights;
+ r = boards[moveNum][CASTLING][4] = initialRights[4];
+ if(board[BOARD_HEIGHT-1][r] != BlackRook) boards[moveNum][CASTLING][4] = NoRights;
/* wildcastle kludge: always assume King has rights */
- r = castlingRights[moveNum][2] = initialRights[2];
- r = castlingRights[moveNum][5] = initialRights[5];
+ r = boards[moveNum][CASTLING][2] = initialRights[2];
+ r = boards[moveNum][CASTLING][5] = initialRights[5];
}
/* [HGM] e.p. rights. Assume that ICS sends file number here? */
- epStatus[moveNum] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
+ boards[moveNum][EP_STATUS] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
if (ics_getting_history == H_GOT_REQ_HEADER ||
/* Update currentMove and known move number limits */
newMove = newGame || moveNum > forwardMostMove;
- /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */
- if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {
- takeback = forwardMostMove - moveNum;
- for (i = 0; i < takeback; i++) {
- if (appData.debugMode) fprintf(debugFP, "take back move\n");
- SendToProgram("undo\n", &first);
- }
- }
-
if (newGame) {
forwardMostMove = backwardMostMove = currentMove = moveNum;
if (gameMode == IcsExamining && moveNum == 0) {
}
} else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
|| (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
+#if ZIPPY
+ /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */
+ /* [HGM] applied this also to an engine that is silently watching */
+ if (appData.zippyPlay && moveNum < forwardMostMove && first.initDone &&
+ (gameMode == IcsObserving || gameMode == IcsExamining) &&
+ gameInfo.variant == currentlyInitializedVariant) {
+ takeback = forwardMostMove - moveNum;
+ for (i = 0; i < takeback; i++) {
+ if (appData.debugMode) fprintf(debugFP, "take back move\n");
+ SendToProgram("undo\n", &first);
+ }
+ }
+#endif
+
forwardMostMove = moveNum;
if (!pausing || currentMove > forwardMostMove)
currentMove = forwardMostMove;
forwardMostMove = pauseExamForwardMostMove;
return;
}
- forwardMostMove = backwardMostMove = currentMove = moveNum;
if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
+#if ZIPPY
+ if(appData.zippyPlay && forwardMostMove > 0 && first.initDone) {
+ // [HGM] when we will receive the move list we now request, it will be
+ // fed to the engine from the first move on. So if the engine is not
+ // in the initial position now, bring it there.
+ InitChessProgram(&first, 0);
+ }
+#endif
ics_getting_history = H_REQUESTED;
sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
+ forwardMostMove = backwardMostMove = currentMove = moveNum;
}
/* Update the clocks */
if (appData.debugMode) {
if (appData.debugMode) { int f = forwardMostMove;
fprintf(debugFP, "parseboard %d, castling = %d %d %d %d %d %d\n", f,
- castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
+ boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
+ boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
}
fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);
fprintf(debugFP, "moveNum = %d\n", moveNum);
// end of long SAN patch
if (valid) {
(void) CoordsToAlgebraic(boards[moveNum - 1],
- PosFlags(moveNum - 1), EP_UNKNOWN,
+ PosFlags(moveNum - 1),
fromY, fromX, toY, toX, promoChar,
parseList[moveNum-1]);
- switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN,
- castlingRights[moveNum]) ) {
+ switch (MateTest(boards[moveNum], PosFlags(moveNum)) ) {
case MT_NONE:
case MT_STALEMATE:
default:
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);
}
-#if 0
- if (appData.testLegality && appData.debugMode) {
- sprintf(str, "Illegal move \"%s\" from ICS", move_str);
- DisplayError(str, 0);
- }
-#endif
strcpy(parseList[moveNum - 1], move_str);
strcat(parseList[moveNum - 1], " ");
strcat(parseList[moveNum - 1], elapsed_time);
programStats.ok_to_send = 0;
}
+void ics_update_width(new_width)
+ int new_width;
+{
+ ics_printf("set width %d\n", new_width);
+}
+
void
SendMoveToProgram(moveNum, cps)
int moveNum;
break;
}
SendToICS(user_move);
+ if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command
+ ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
}
void
// Last King gets castling rights
while(piecesLeft[(int)WhiteUnicorn]) {
i = put(board, WhiteUnicorn, 0, piecesLeft[(int)WhiteRook]/2, ANY);
- initialRights[2] = initialRights[5] = castlingRights[0][2] = castlingRights[0][5] = i;
+ initialRights[2] = initialRights[5] = boards[0][CASTLING][2] = boards[0][CASTLING][5] = i;
}
while(piecesLeft[(int)WhiteKing]) {
i = put(board, WhiteKing, 0, piecesLeft[(int)WhiteRook]/2, ANY);
- initialRights[2] = initialRights[5] = castlingRights[0][2] = castlingRights[0][5] = i;
+ initialRights[2] = initialRights[5] = boards[0][CASTLING][2] = boards[0][CASTLING][5] = i;
}
if(PosFlags(0) & F_FRC_TYPE_CASTLING) { // first and last Rook get FRC castling rights
if(first) {
first=0;
- initialRights[1] = initialRights[4] = castlingRights[0][1] = castlingRights[0][4] = i;
+ initialRights[1] = initialRights[4] = boards[0][CASTLING][1] = boards[0][CASTLING][4] = i;
}
- initialRights[0] = initialRights[3] = castlingRights[0][0] = castlingRights[0][3] = i;
+ initialRights[0] = initialRights[3] = boards[0][CASTLING][0] = boards[0][CASTLING][3] = i;
}
}
for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // copy black from white
InitPosition(redraw)
int redraw;
{
- ChessSquare (* pieces)[BOARD_SIZE];
+ ChessSquare (* pieces)[BOARD_FILES];
int i, j, pawnRow, overrule,
oldx = gameInfo.boardWidth,
oldy = gameInfo.boardHeight,
oldh = gameInfo.holdingsWidth,
oldv = gameInfo.variant;
- currentMove = forwardMostMove = backwardMostMove = 0;
if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request
/* [AS] Initialize pv info list [HGM] and game status */
{
- for( i=0; i<MAX_MOVES; i++ ) {
+ for( i=0; i<=framePtr; i++ ) { // [HGM] vari: spare saved variations
pvInfoList[i].depth = 0;
- epStatus[i]=EP_NONE;
- for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;
+ boards[i][EP_STATUS] = EP_NONE;
+ for( j=0; j<BOARD_FILES-2; j++ ) boards[i][CASTLING][j] = NoRights;
}
initialRulePlies = 0; /* 50-move counter start */
gameInfo.boardHeight = 8;
gameInfo.holdingsSize = 0;
nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */
- for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1; /* but no rights yet */
+ 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");
switch (gameInfo.variant) {
gameInfo.boardWidth = 10;
SetCharTable(pieceToChar, "PNBRQ..JKpnbrq..jk");
nrCastlingRights = 6;
- castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;
- castlingRights[0][1] = initialRights[1] = BOARD_LEFT;
- castlingRights[0][2] = initialRights[2] =(BOARD_WIDTH-1)>>1;
- castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;
- castlingRights[0][4] = initialRights[4] = BOARD_LEFT;
- castlingRights[0][5] = initialRights[5] =(BOARD_WIDTH-1)>>1;
+ initialPosition[CASTLING][0] = initialRights[0] = BOARD_RGHT-1;
+ initialPosition[CASTLING][1] = initialRights[1] = BOARD_LEFT;
+ initialPosition[CASTLING][2] = initialRights[2] =(BOARD_WIDTH-1)>>1;
+ initialPosition[CASTLING][3] = initialRights[3] = BOARD_RGHT-1;
+ initialPosition[CASTLING][4] = initialRights[4] = BOARD_LEFT;
+ initialPosition[CASTLING][5] = initialRights[5] =(BOARD_WIDTH-1)>>1;
break;
case VariantFalcon:
pieces = FalconArray;
gameInfo.boardWidth = 12;
nrCastlingRights = 0;
SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk");
- for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;
break;
case VariantKnightmate:
pieces = KnightmateArray;
case VariantNoCastle:
pieces = FIDEArray;
nrCastlingRights = 0;
- for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;
/* !!?unconstrained back-rank shuffle */
shuffleOpenings = 1;
break;
gameInfo.holdingsSize = i;
}
if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;
- if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE)
- DisplayFatalError(_("Recompile to support this BOARD_SIZE!"), 0, 2);
+ if(BOARD_HEIGHT > BOARD_RANKS || BOARD_WIDTH > BOARD_FILES)
+ DisplayFatalError(_("Recompile to support this BOARD_RANKS or BOARD_FILES!"), 0, 2);
pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
if(pawnRow < 1) pawnRow = 1;
/* Variants with other castling rights must set them themselves above */
nrCastlingRights = 6;
- castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;
- castlingRights[0][1] = initialRights[1] = BOARD_LEFT;
- castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;
- castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;
- castlingRights[0][4] = initialRights[4] = BOARD_LEFT;
- castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;
+ initialPosition[CASTLING][0] = initialRights[0] = BOARD_RGHT-1;
+ initialPosition[CASTLING][1] = initialRights[1] = BOARD_LEFT;
+ initialPosition[CASTLING][2] = initialRights[2] = BOARD_WIDTH>>1;
+ initialPosition[CASTLING][3] = initialRights[3] = BOARD_RGHT-1;
+ initialPosition[CASTLING][4] = initialRights[4] = BOARD_LEFT;
+ initialPosition[CASTLING][5] = initialRights[5] = BOARD_WIDTH>>1;
}
if(gameInfo.variant == VariantSuper) Prelude(initialPosition);
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;
}
-#if 0
- if(gameInfo.variant == VariantFischeRandom) {
- if( appData.defaultFrcPosition < 0 ) {
- ShuffleFRC( initialPosition );
- }
- else {
- SetupFRC( initialPosition, appData.defaultFrcPosition );
- }
- startedFromSetupPosition = TRUE;
- } else
-#else
if (appData.debugMode) {
fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
}
SetUpShuffle(initialPosition, appData.defaultFrcPosition);
startedFromSetupPosition = TRUE;
}
-#endif
if(startedFromPositionFile) {
/* [HGM] loadPos: use PositionFile for every new game */
CopyBoard(initialPosition, filePosition);
for(i=0; i<nrCastlingRights; i++)
- castlingRights[0][i] = initialRights[i] = fileRights[i];
+ initialRights[i] = filePosition[CASTLING][i];
startedFromSetupPosition = TRUE;
}
}
int
-IsPromotion(fromX, fromY, toX, toY)
- int fromX, fromY, toX, toY;
+HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
{
+ /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
/* [HGM] add Shogi promotions */
int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn;
ChessSquare piece;
+ ChessMove moveType;
+ Boolean premove;
+
+ if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) return FALSE; // drop
+ if(toX < BOARD_LEFT || toX >= BOARD_RGHT) return FALSE; // move into holdings
+
+ if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi || // no promotions
+ !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) // invalid move
+ return FALSE;
- if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi ||
- !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE;
- /* [HGM] Note to self: line above also weeds out drops */
piece = boards[currentMove][fromY][fromX];
if(gameInfo.variant == VariantShogi) {
promotionZoneSize = 3;
- highestPromotingPiece = (int)WhiteKing;
- /* [HGM] Should be Silver = Ferz, really, but legality testing is off,
- and if in normal chess we then allow promotion to King, why not
- allow promotion of other piece in Shogi? */
+ highestPromotingPiece = (int)WhiteFerz;
}
+
+ // next weed out all moves that do not touch the promotion zone at all
if((int)piece >= BlackPawn) {
if(toY >= promotionZoneSize && fromY >= promotionZoneSize)
return FALSE;
if( toY < BOARD_HEIGHT - promotionZoneSize &&
fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;
}
- return ( (int)piece <= highestPromotingPiece );
+
+ if( (int)piece > highestPromotingPiece ) return FALSE; // non-promoting piece
+
+ // weed out mandatory Shogi promotions
+ if(gameInfo.variant == VariantShogi) {
+ if(piece >= BlackPawn) {
+ if(toY == 0 && piece == BlackPawn ||
+ toY == 0 && piece == BlackQueen ||
+ toY <= 1 && piece == BlackKnight) {
+ *promoChoice = '+';
+ return FALSE;
+ }
+ } else {
+ if(toY == BOARD_HEIGHT-1 && piece == WhitePawn ||
+ toY == BOARD_HEIGHT-1 && piece == WhiteQueen ||
+ toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) {
+ *promoChoice = '+';
+ return FALSE;
+ }
+ }
+ }
+
+ // weed out obviously illegal Pawn moves
+ if(appData.testLegality && (piece == WhitePawn || piece == BlackPawn) ) {
+ if(toX > fromX+1 || toX < fromX-1) return FALSE; // wide
+ if(piece == WhitePawn && toY != fromY+1) return FALSE; // deep
+ if(piece == BlackPawn && toY != fromY-1) return FALSE; // deep
+ if(fromX != toX && gameInfo.variant == VariantShogi) return FALSE;
+ // note we are not allowed to test for valid (non-)capture, due to premove
+ }
+
+ // we either have a choice what to promote to, or (in Shogi) whether to promote
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) {
+ *promoChoice = PieceToChar(BlackFerz); // no choice
+ return FALSE;
+ }
+ if(appData.alwaysPromoteToQueen) { // predetermined
+ if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers)
+ *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want
+ else *promoChoice = PieceToChar(BlackQueen);
+ return FALSE;
+ }
+
+ // suppress promotion popup on illegal moves that are not premoves
+ premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) ||
+ gameMode == IcsPlayingBlack && WhiteOnMove(currentMove);
+ if(appData.testLegality && !premove) {
+ moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
+ fromY, fromX, toY, toX, NULLCHAR);
+ if(moveType != WhitePromotionQueen && moveType != BlackPromotionQueen &&
+ moveType != WhitePromotionKnight && moveType != BlackPromotionKnight)
+ return FALSE;
+ }
+
+ return TRUE;
}
int
/* Could disallow this or prompt for confirmation */
cmailOldMove = -1;
}
- if (currentMove < forwardMostMove) {
- /* Discarding moves */
- /* Could prompt for confirmation here,
- but I don't think that's such a good idea */
- forwardMostMove = currentMove;
- }
break;
case BeginningOfGame:
break;
}
if (currentMove != forwardMostMove && gameMode != AnalyzeMode
+ && gameMode != EditGame // [HGM] vari: treat as AnalyzeMode
&& gameMode != AnalyzeFile && gameMode != Training) {
DisplayMoveError(_("Displayed position is not current"));
return FALSE;
char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
ChessMove lastLoadGameStart = (ChessMove) 0;
-
ChessMove
-UserMoveTest(fromX, fromY, toX, toY, promoChar)
+UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
int fromX, fromY, toX, toY;
int promoChar;
+ Boolean captureOwn;
{
ChessMove moveType;
ChessSquare pdown, pup;
- if (fromX < 0 || fromY < 0) return ImpossibleMove;
- if ((fromX == toX) && (fromY == toY)) {
- return ImpossibleMove;
- }
-
- /* [HGM] suppress all moves into holdings area and guard band */
- if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )
- return ImpossibleMove;
-
- /* [HGM] <sameColor> moved to here from winboard.c */
- /* note: this code seems to exist for filtering out some obviously illegal premoves */
- pdown = boards[currentMove][fromY][fromX];
- pup = boards[currentMove][toY][toX];
- if ( gameMode != EditPosition &&
- (WhitePawn <= pdown && pdown < BlackPawn &&
- WhitePawn <= pup && pup < BlackPawn ||
- BlackPawn <= pdown && pdown < EmptySquare &&
- BlackPawn <= pup && pup < EmptySquare
- ) && !((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&
- (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0||
- pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 )
- ) )
- return ImpossibleMove;
-
/* Check if the user is playing in turn. This is complicated because we
let the user "pick up" a piece before it is his turn. So the piece he
tried to pick up may have been captured by the time he puts it down!
return ImpossibleMove;
}
+ if(toX < 0 || toY < 0) return ImpossibleMove;
+ 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;
/* [HGM] always test for legality, to get promotion info */
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- epStatus[currentMove], castlingRights[currentMove],
fromY, fromX, toY, toX, promoChar);
-
/* [HGM] but possibly ignore an IllegalMove result */
if (appData.testLegality) {
if (moveType == IllegalMove || moveType == ImpossibleMove) {
* If they don't match, display an error message.
*/
int saveAnimate;
- Board testBoard; char testRights[BOARD_SIZE]; char testStatus;
+ Board testBoard;
CopyBoard(testBoard, boards[currentMove]);
- ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus);
+ ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
if (CompareBoards(testBoard, boards[currentMove+1])) {
ForwardInner(currentMove+1);
/* Ok, now we know that the move is good, so we can kill
the previous line in Analysis Mode */
- if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
- forwardMostMove = currentMove;
+ if ((gameMode == AnalyzeMode || gameMode == EditGame)
+ && currentMove < forwardMostMove) {
+ PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game
}
/* If we need the chess program but it's dead, restart it */
switch (gameMode) {
case EditGame:
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, castlingRights[currentMove]) ) {
+ switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
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);
+ ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar, FALSE);
if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);
- if(moveType != ImpossibleMove)
+ if(moveType == AmbiguousMove)
+ DrawPosition(FALSE, boards[currentMove]);
+ else if(moveType != ImpossibleMove && moveType != Comment)
FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
}
+void LeftClick(ClickType clickType, int xPix, int yPix)
+{
+ int x, y;
+ Boolean saveAnimate;
+ static int second = 0, promotionChoice = 0;
+ char promoChoice = NULLCHAR;
+
+ if (clickType == Press) ErrorPopDown();
+
+ x = EventToSquare(xPix, BOARD_WIDTH);
+ y = EventToSquare(yPix, BOARD_HEIGHT);
+ if (!flipView && y >= 0) {
+ y = BOARD_HEIGHT - 1 - y;
+ }
+ if (flipView && x >= 0) {
+ x = BOARD_WIDTH - 1 - x;
+ }
+
+ if(promotionChoice) { // we are waiting for a click to indicate promotion piece
+ if(clickType == Release) return; // ignore upclick of click-click destination
+ promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
+ if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
+ if(gameInfo.holdingsWidth &&
+ (WhiteOnMove(currentMove)
+ ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
+ : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+ // click in right holdings, for determining promotion piece
+ ChessSquare p = boards[currentMove][y][x];
+ if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
+ if(p != EmptySquare) {
+ FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+ fromX = fromY = -1;
+ return;
+ }
+ }
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
+ }
+
+ /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
+ if(clickType == Press
+ && ( x == BOARD_LEFT-1 || x == BOARD_RGHT
+ || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize
+ || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
+ return;
+
+ if (fromX == -1) {
+ if (clickType == Press) {
+ /* First square */
+ if (OKToStartUserMove(x, y)) {
+ fromX = x;
+ fromY = y;
+ second = 0;
+ DragPieceBegin(xPix, yPix);
+ if (appData.highlightDragging) {
+ SetHighlights(x, y, -1, -1);
+ }
+ }
+ }
+ return;
+ }
+
+ /* fromX != -1 */
+ if (clickType == Press && gameMode != EditPosition) {
+ ChessSquare fromP;
+ ChessSquare toP;
+ int frc;
+
+ // ignore off-board to clicks
+ if(y < 0 || x < 0) return;
+
+ /* Check if clicking again on the same color piece */
+ fromP = boards[currentMove][fromY][fromX];
+ toP = boards[currentMove][y][x];
+ frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
+ if ((WhitePawn <= fromP && fromP <= WhiteKing &&
+ WhitePawn <= toP && toP <= WhiteKing &&
+ !(fromP == WhiteKing && toP == WhiteRook && frc) &&
+ !(fromP == WhiteRook && toP == WhiteKing && frc)) ||
+ (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 (appData.highlightDragging) {
+ SetHighlights(x, y, -1, -1);
+ } else {
+ ClearHighlights();
+ }
+ if (OKToStartUserMove(x, y)) {
+ fromX = x;
+ fromY = y;
+ DragPieceBegin(xPix, yPix);
+ }
+ return;
+ }
+ // ignore clicks on holdings
+ if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
+ }
+
+ if (clickType == Release && x == fromX && y == fromY) {
+ DragPieceEnd(xPix, yPix);
+ if (appData.animateDragging) {
+ /* Undo animation damage if any */
+ DrawPosition(FALSE, NULL);
+ }
+ if (second) {
+ /* Second up/down in same square; just abort move */
+ second = 0;
+ fromX = fromY = -1;
+ ClearHighlights();
+ gotPremove = 0;
+ ClearPremoveHighlights();
+ } else {
+ /* First upclick in same square; start click-click mode */
+ SetHighlights(x, y, -1, -1);
+ }
+ return;
+ }
+
+ /* we now have a different from- and (possibly off-board) to-square */
+ /* Completed move */
+ toX = x;
+ toY = y;
+ saveAnimate = appData.animate;
+ if (clickType == Press) {
+ /* Finish clickclick move */
+ if (appData.animate || appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ } else {
+ ClearHighlights();
+ }
+ } else {
+ /* Finish drag move */
+ if (appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ } else {
+ ClearHighlights();
+ }
+ DragPieceEnd(xPix, yPix);
+ /* Don't animate move and drag both */
+ appData.animate = FALSE;
+ }
+
+ // moves into holding are invalid for now (later perhaps allow in EditPosition)
+ if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
+ ClearHighlights();
+ fromX = fromY = -1;
+ DrawPosition(TRUE, NULL);
+ return;
+ }
+
+ // off-board moves should not be highlighted
+ if(x < 0 || x < 0) ClearHighlights();
+
+ if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
+ SetHighlights(fromX, fromY, toX, toY);
+ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ // [HGM] super: promotion to captured piece selected from holdings
+ ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
+ promotionChoice = TRUE;
+ // kludge follows to temporarily execute move on display, without promoting yet
+ boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
+ boards[currentMove][toY][toX] = p;
+ DrawPosition(FALSE, boards[currentMove]);
+ boards[currentMove][fromY][fromX] = p; // take back, but display stays
+ boards[currentMove][toY][toX] = q;
+ DisplayMessage("Click in holdings to choose piece", "");
+ return;
+ }
+ PromotionPopUp();
+ } else {
+ UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
+ if (!appData.highlightLastMove || gotPremove) ClearHighlights();
+ if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+ fromX = fromY = -1;
+ }
+ appData.animate = saveAnimate;
+ if (appData.animate || appData.animateDragging) {
+ /* Undo animation damage if needed */
+ DrawPosition(FALSE, NULL);
+ }
+}
+
void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )
{
// char * hint = lastHint;
if (appData.debugMode) { int f = forwardMostMove;
fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,
- castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
+ boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
+ boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
}
if(cps->alphaRank) AlphaRank(machineMove, 4);
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
) {
ChessMove moveType;
moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),
- epStatus[forwardMostMove], castlingRights[forwardMostMove],
fromY, fromX, toY, toX, promoChar);
if (appData.debugMode) {
int i;
for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",
- castlingRights[forwardMostMove][i], castlingRank[i]);
+ boards[forwardMostMove][CASTLING][i], castlingRank[i]);
fprintf(debugFP, "castling rights\n");
}
if(moveType == IllegalMove) {
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, %1.0f knps) PV=%s\n",
+ sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
programStats.score / 100.,
programStats.depth,
programStats.time / 100.,
(unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.),
programStats.movelist);
SendToICS(buf);
+if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes);
}
}
#endif
if( gameMode == TwoMachinesPlay ) {
// [HGM] some adjudications useful with buggy engines
- int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;
+ int k, count = 0; static int bare = 1;
if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
/* Some material-based adjudications that have to be made before stalemate test */
if(gameInfo.variant == VariantAtomic && NrK < 2) {
// [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal
- epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as if stm is checkmated
+ boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // make claimable as if stm is checkmated
if(appData.checkMates) {
SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
/* Bare King in Shatranj (loses) or Losers (wins) */
if( NrW == 1 || NrPieces - NrW == 1) {
if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)
- epStatus[forwardMostMove] = EP_WINS; // mark as win, so it becomes claimable
+ boards[forwardMostMove][EP_STATUS] = EP_WINS; // mark as win, so it becomes claimable
if(appData.checkMates) {
SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
} else
if( gameInfo.variant == VariantShatranj && --bare < 0)
{ /* bare King */
- epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm
+ boards[forwardMostMove][EP_STATUS] = EP_WINS; // make claimable as win for stm
if(appData.checkMates) {
/* but only adjudicate if adjudication enabled */
SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move
// don't wait for engine to announce game end if we can judge ourselves
- switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile,
- castlingRights[forwardMostMove]) ) {
+ switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
case MT_CHECK:
if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time
int i, checkCnt = 0; // (should really be done by making nr of checks part of game state)
for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) {
- if(MateTest(boards[i], PosFlags(i), epStatus[i], castlingRights[i]) == MT_CHECK)
+ if(MateTest(boards[i], PosFlags(i)) == MT_CHECK)
checkCnt++;
if(checkCnt >= 2) {
reason = "Xboard adjudication: 3rd check";
- epStatus[forwardMostMove] = EP_CHECKMATE;
+ boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE;
break;
}
}
case MT_STALEMATE:
case MT_STAINMATE:
reason = "Xboard adjudication: Stalemate";
- if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt
- epStatus[forwardMostMove] = EP_STALEMATE; // default result for stalemate is draw
+ if((signed char)boards[forwardMostMove][EP_STATUS] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt
+ boards[forwardMostMove][EP_STATUS] = EP_STALEMATE; // default result for stalemate is draw
if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers:
- epStatus[forwardMostMove] = EP_WINS; // in these variants stalemated is always a win
+ boards[forwardMostMove][EP_STATUS] = EP_WINS; // in these variants stalemated is always a win
else if(gameInfo.variant == VariantSuicide) // in suicide it depends
- epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE :
+ boards[forwardMostMove][EP_STATUS] = NrW == NrPieces-NrW ? EP_STALEMATE :
((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?
EP_CHECKMATE : EP_WINS);
else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)
- epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses
+ boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses
}
break;
case MT_CHECKMATE:
reason = "Xboard adjudication: Checkmate";
- epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);
+ boards[forwardMostMove][EP_STATUS] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);
break;
}
- switch(i = epStatus[forwardMostMove]) {
+ switch(i = (signed char)boards[forwardMostMove][EP_STATUS]) {
case EP_STALEMATE:
result = GameIsDrawn; break;
case EP_CHECKMATE:
{ /* KBK, KNK, KK of KBKB with like Bishops */
/* always flag draws, for judging claims */
- epStatus[forwardMostMove] = EP_INSUF_DRAW;
+ boards[forwardMostMove][EP_STATUS] = EP_INSUF_DRAW;
if(appData.materialDraws) {
/* but only adjudicate them if adjudication enabled */
} else moveCount = 6;
}
}
-#if 1
- if (appData.debugMode) { int i;
- fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",
- forwardMostMove, backwardMostMove, epStatus[backwardMostMove],
- appData.drawRepeats);
- for( i=forwardMostMove; i>=backwardMostMove; i-- )
- fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);
+
+ 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]);
+
+ }
- }
-#endif
/* Check for rep-draws */
count = 0;
for(k = forwardMostMove-2;
k>=backwardMostMove && k>=forwardMostMove-100 &&
- epStatus[k] < EP_UNKNOWN &&
- epStatus[k+2] <= EP_NONE && epStatus[k+1] <= EP_NONE;
+ (signed char)boards[k][EP_STATUS] < EP_UNKNOWN &&
+ (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE;
k-=2)
{ int rights=0;
-#if 0
- if (appData.debugMode) {
- fprintf(debugFP, " loop\n");
- }
-#endif
if(CompareBoards(boards[k], boards[forwardMostMove])) {
-#if 0
- if (appData.debugMode) {
- fprintf(debugFP, "match\n");
- }
-#endif
/* compare castling rights */
- if( castlingRights[forwardMostMove][2] != castlingRights[k][2] &&
- (castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) )
+ if( boards[forwardMostMove][CASTLING][2] != boards[k][CASTLING][2] &&
+ (boards[k][CASTLING][0] != NoRights || boards[k][CASTLING][1] != NoRights) )
rights++; /* King lost rights, while rook still had them */
- if( castlingRights[forwardMostMove][2] >= 0 ) { /* king has rights */
- if( castlingRights[forwardMostMove][0] != castlingRights[k][0] ||
- castlingRights[forwardMostMove][1] != castlingRights[k][1] )
+ if( boards[forwardMostMove][CASTLING][2] != NoRights ) { /* king has rights */
+ if( boards[forwardMostMove][CASTLING][0] != boards[k][CASTLING][0] ||
+ boards[forwardMostMove][CASTLING][1] != boards[k][CASTLING][1] )
rights++; /* but at least one rook lost them */
}
- if( castlingRights[forwardMostMove][5] != castlingRights[k][5] &&
- (castlingRights[k][3] >= 0 || castlingRights[k][4] >= 0) )
+ if( boards[forwardMostMove][CASTLING][5] != boards[k][CASTLING][5] &&
+ (boards[k][CASTLING][3] != NoRights || boards[k][CASTLING][4] != NoRights) )
rights++;
- if( castlingRights[forwardMostMove][5] >= 0 ) {
- if( castlingRights[forwardMostMove][3] != castlingRights[k][3] ||
- castlingRights[forwardMostMove][4] != castlingRights[k][4] )
+ 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 0
- if (appData.debugMode) {
- for(i=0; i<nrCastlingRights; i++)
- fprintf(debugFP, " (%d,%d)", castlingRights[forwardMostMove][i], castlingRights[k][i]);
- }
-
- if (appData.debugMode) {
- fprintf(debugFP, " %d %d\n", rights, k);
- }
-#endif
if( rights == 0 && ++count > appData.drawRepeats-2
&& appData.drawRepeats > 1) {
/* adjudicate after user-specified nr of repeats */
// [HGM] xiangqi: check for forbidden perpetuals
int m, ourPerpetual = 1, hisPerpetual = 1;
for(m=forwardMostMove; m>k; m-=2) {
- if(MateTest(boards[m], PosFlags(m),
- EP_NONE, castlingRights[m]) != MT_CHECK)
+ if(MateTest(boards[m], PosFlags(m)) != MT_CHECK)
ourPerpetual = 0; // the current mover did not always check
- if(MateTest(boards[m-1], PosFlags(m-1),
- EP_NONE, castlingRights[m-1]) != MT_CHECK)
+ if(MateTest(boards[m-1], PosFlags(m-1)) != MT_CHECK)
hisPerpetual = 0; // the opponent did not always check
}
if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",
return;
}
if( rights == 0 && count > 1 ) /* occurred 2 or more times before */
- epStatus[forwardMostMove] = EP_REP_DRAW;
+ boards[forwardMostMove][EP_STATUS] = EP_REP_DRAW;
}
}
/* Now we test for 50-move draws. Determine ply count */
count = forwardMostMove;
/* look for last irreversble move */
- while( epStatus[count] <= EP_NONE && count > backwardMostMove )
+ while( (signed char)boards[count][EP_STATUS] <= EP_NONE && count > backwardMostMove )
count--;
/* if we hit starting position, add initial plies */
if( count == backwardMostMove )
count -= initialRulePlies;
count = forwardMostMove - count;
if( count >= 100)
- epStatus[forwardMostMove] = EP_RULE_DRAW;
+ boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
/* this is used to judge if draw claims are legal */
if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {
SendToProgram("force\n", cps->other); // suppress reply
*/
if( cps->other->offeredDraw || cps->offeredDraw ) {
char *p = NULL;
- if(epStatus[forwardMostMove] == EP_RULE_DRAW)
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_RULE_DRAW)
p = "Draw claim: 50-move rule";
- if(epStatus[forwardMostMove] == EP_REP_DRAW)
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_REP_DRAW)
p = "Draw claim: 3-fold repetition";
- if(epStatus[forwardMostMove] == EP_INSUF_DRAW)
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_INSUF_DRAW)
p = "Draw claim: insufficient mating material";
if( p != NULL ) {
SendToProgram("force\n", cps->other); // suppress reply
* want this, I was asked to put it in, and obliged.
*/
if (!strncmp(message, "setboard ", 9)) {
- Board initial_position; int i;
+ Board initial_position;
GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);
DisplayError(_("Bad FEN received from engine"), 0);
return ;
} else {
- Reset(FALSE, FALSE);
+ Reset(TRUE, FALSE);
CopyBoard(boards[0], initial_position);
initialRulePlies = FENrulePlies;
- epStatus[0] = FENepStatus;
- for( i=0; i<nrCastlingRights; i++ )
- castlingRights[0][i] = FENcastlingRights[i];
if(blackPlaysFirst) gameMode = MachinePlaysWhite;
else gameMode = MachinePlaysBlack;
DrawPosition(FALSE, boards[currentMove]);
if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
gameMode == IcsIdle) return;
if (forwardMostMove <= backwardMostMove) return;
-#if 0
- /* Following removed: it caused a bug where a real illegal move
- message in analyze mored would be ignored. */
- if (cps == &first && programStats.ok_to_send == 0) {
- /* Bogus message from Crafty responding to "." This filtering
- can miss some of the bad messages, but fortunately the bug
- is fixed in current Crafty versions, so it doesn't matter. */
- return;
- }
-#endif
if (pausing) PauseEvent();
+ if(appData.forceIllegal) {
+ // [HGM] illegal: machine refused move; force position after move into it
+ SendToProgram("force\n", cps);
+ if(!cps->useSetboard) { // hideous kludge on kludge, because SendBoard sucks.
+ // we have a real problem now, as SendBoard will use the a2a3 kludge
+ // when black is to move, while there might be nothing on a2 or black
+ // might already have the move. So send the board as if white has the move.
+ // But first we must change the stm of the engine, as it refused the last move
+ SendBoard(cps, 0); // always kludgeless, as white is to move on boards[0]
+ if(WhiteOnMove(forwardMostMove)) {
+ SendToProgram("a7a6\n", cps); // for the engine black still had the move
+ SendBoard(cps, forwardMostMove); // kludgeless board
+ } else {
+ SendToProgram("a2a3\n", cps); // for the engine white still had the move
+ CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);
+ SendBoard(cps, forwardMostMove+1); // kludgeless board
+ }
+ } else SendBoard(cps, forwardMostMove); // FEN case, also sets stm properly
+ if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay)
+ SendToProgram("go\n", cps);
+ return;
+ } else
if (gameMode == PlayFromGameFile) {
/* Stop reading this game file */
gameMode = EditGame;
if (ParseOneMove(buf1, forwardMostMove, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)) {
(void) CoordsToAlgebraic(boards[forwardMostMove],
- PosFlags(forwardMostMove), EP_UNKNOWN,
+ PosFlags(forwardMostMove),
fromY, fromX, toY, toX, promoChar, buf1);
snprintf(buf2, sizeof(buf2), _("Hint: %s"), buf1);
DisplayInformation(buf2);
/* [HGM] in two-machine mode we delay relaying draw offer */
/* until after we also have move, to see if it is really claim */
}
-#if 0
- else {
- if (cps->other->sendDrawOffers) {
- SendToProgram("draw\n", cps->other);
- }
- }
-#endif
} else if (gameMode == MachinePlaysWhite ||
gameMode == MachinePlaysBlack) {
if (userOfferedDraw) {
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))
+ if(WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite ||
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w'))
whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;
- else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;
+ if(!WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b'))
+ blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;
}
/* Buffer overflow protection */
if (strlen(buf1) >= sizeof(programStats.movelist)
&& appData.debugMode) {
fprintf(debugFP,
- "PV is too long; using the first %d bytes.\n",
- sizeof(programStats.movelist) - 1);
+ "PV is too long; using the first %u bytes.\n",
+ (unsigned) sizeof(programStats.movelist) - 1);
}
safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
if (currentMove == forwardMostMove || gameMode == AnalyzeMode
|| gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
} else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s",
SendProgramStatsToFrontend( cps, &programStats );
- DisplayAnalysis();
return;
} else if (strncmp(message,"++",2) == 0) {
if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
}
return;
}
(void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
- EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
+ fromY, fromX, toY, toX, promoChar,
parseList[boardIndex]);
CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
- {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[boardIndex+1][i] = castlingRights[boardIndex][i];}
/* currentMoveString is set as a side-effect of yylex */
strcpy(moveList[boardIndex], currentMoveString);
strcat(moveList[boardIndex], "\n");
boardIndex++;
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex],
- castlingRights[boardIndex], &epStatus[boardIndex]);
- switch (MateTest(boards[boardIndex], PosFlags(boardIndex),
- EP_UNKNOWN, castlingRights[boardIndex]) ) {
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
+ switch (MateTest(boards[boardIndex], PosFlags(boardIndex)) ) {
case MT_NONE:
case MT_STALEMATE:
default:
/* Apply a move to the given board */
void
-ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep)
+ApplyMove(fromX, fromY, toX, toY, promoChar, board)
int fromX, fromY, toX, toY;
int promoChar;
Board board;
- char *castling;
- char *ep;
{
ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
{ int i;
if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
- oldEP = *ep;
- *ep = EP_NONE;
+ oldEP = (signed char)board[EP_STATUS];
+ board[EP_STATUS] = EP_NONE;
if( board[toY][toX] != EmptySquare )
- *ep = EP_CAPTURE;
+ board[EP_STATUS] = EP_CAPTURE;
if( board[fromY][fromX] == WhitePawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
- *ep = EP_PAWN_MOVE;
+ board[EP_STATUS] = EP_PAWN_MOVE;
if( toY-fromY==2) {
if(toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn &&
gameInfo.variant != VariantBerolina || toX < fromX)
- *ep = toX | berolina;
+ board[EP_STATUS] = toX | berolina;
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
- *ep = toX;
+ board[EP_STATUS] = toX;
}
} else
if( board[fromY][fromX] == BlackPawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
- *ep = 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)
- *ep = toX | berolina;
+ board[EP_STATUS] = toX | berolina;
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
- *ep = toX;
+ board[EP_STATUS] = toX;
}
}
for(i=0; i<nrCastlingRights; i++) {
- if(castling[i] == fromX && castlingRank[i] == fromY ||
- castling[i] == toX && castlingRank[i] == toY
- ) castling[i] = -1; // revoke for moved or captured piece
+ if(board[CASTLING][i] == fromX && castlingRank[i] == fromY ||
+ board[CASTLING][i] == toX && castlingRank[i] == toY
+ ) board[CASTLING][i] = NoRights; // revoke for moved or captured piece
}
}
p = (int) fromX;
if(p < (int) BlackPawn) { /* white drop */
p -= (int)WhitePawn;
+ p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) p = 0;
- if(--board[p][BOARD_WIDTH-2] == 0)
+ if(--board[p][BOARD_WIDTH-2] <= 0)
board[p][BOARD_WIDTH-1] = EmptySquare;
+ if((int)board[p][BOARD_WIDTH-2] < 0)
+ board[p][BOARD_WIDTH-2] = 0;
} else { /* black drop */
p -= (int)BlackPawn;
+ p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) p = 0;
- if(--board[BOARD_HEIGHT-1-p][1] == 0)
+ if(--board[BOARD_HEIGHT-1-p][1] <= 0)
board[BOARD_HEIGHT-1-p][0] = EmptySquare;
+ if((int)board[BOARD_HEIGHT-1-p][1] < 0)
+ board[BOARD_HEIGHT-1-p][1] = 0;
}
}
if (captured != EmptySquare && gameInfo.holdingsSize > 0
board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;
}
}
-
} else if (gameInfo.variant == VariantAtomic) {
if (captured != EmptySquare) {
int y, x;
fflush(serverMoves);
}
- if (forwardMostMove+1 >= MAX_MOVES) {
+ if (forwardMostMove+1 > framePtr) { // [HGM] vari: do not run into saved variations
DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
0, 1);
return;
}
- SwitchClocks();
- timeRemaining[0][forwardMostMove+1] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove+1] = blackTimeRemaining;
if (commentList[forwardMostMove+1] != NULL) {
free(commentList[forwardMostMove+1]);
commentList[forwardMostMove+1] = NULL;
}
CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);
- {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[forwardMostMove+1][i] = castlingRights[forwardMostMove][i];}
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1],
- castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1]);
forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board
+ SwitchClocks(); // uses forwardMostMove, so must be done after incrementing it !
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;
gameInfo.result = GameUnfinished;
if (gameInfo.resultDetails != NULL) {
free(gameInfo.resultDetails);
CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
moveList[forwardMostMove - 1]);
(void) CoordsToAlgebraic(boards[forwardMostMove - 1],
- PosFlags(forwardMostMove - 1), EP_UNKNOWN,
+ PosFlags(forwardMostMove - 1),
fromY, fromX, toY, toX, promoChar,
parseList[forwardMostMove - 1]);
- switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove),
- epStatus[forwardMostMove], /* [HGM] use true e.p. */
- castlingRights[forwardMostMove]) ) {
+ switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
case MT_NONE:
case MT_STALEMATE:
default:
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, "egtbpath %s %s\n", name+1, s);
+ sprintf(buf, "egtpath %s %s\n", name+1, s);
*r = c;
SendToProgram(buf,cps); // send egtbpath command for this format
}
void
NextMatchGame P((void))
{
- int index; /* [HGM] autoinc: step lod index during match */
+ int index; /* [HGM] autoinc: step load index during match */
Reset(FALSE, TRUE);
if (*appData.loadGameFile != NULLCHAR) {
index = appData.loadGameIndex;
second.twoMachinesColor[0] ;
// [HGM] losers: because the logic is becoming a bit hairy, determine true result first
- if(epStatus[forwardMostMove] == EP_CHECKMATE) {
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_CHECKMATE) {
/* [HGM] verify: engine mate claims accepted if they were flagged */
trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins;
} else
- if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_WINS) { // added code for games where being mated is a win
/* [HGM] verify: engine mate claims accepted if they were flagged */
trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
} else
- if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now
+ if((signed char)boards[forwardMostMove][EP_STATUS] == EP_STALEMATE) { // only used to indicate draws now
trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE
}
result == BlackWins && claimer == 'b' ) ) { // case to verify: engine claims own win
if (appData.debugMode) {
fprintf(debugFP, "result=%d sp=%d move=%d\n",
- result, epStatus[forwardMostMove], forwardMostMove);
+ result, (signed char)boards[forwardMostMove][EP_STATUS], forwardMostMove);
}
if(result != trueResult) {
sprintf(buf, "False win claim: '%s'", resultDetails);
resultDetails = buf;
}
} else
- if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS
+ if( result == GameIsDrawn && (signed char)boards[forwardMostMove][EP_STATUS] > EP_DRAWS
&& (forwardMostMove <= backwardMostMove ||
- epStatus[forwardMostMove-1] > EP_DRAWS ||
+ (signed char)boards[forwardMostMove-1][EP_STATUS] > EP_DRAWS ||
(claimer=='b')==(forwardMostMove&1))
) {
/* [HGM] verify: draws that were not flagged are false claims */
&& result != GameIsDrawn)
{ int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {
- int p = (int)boards[forwardMostMove][i][j] - color;
+ int p = (signed char)boards[forwardMostMove][i][j] - color;
if(p >= 0 && p <= (int)WhiteKing) k++;
}
if (appData.debugMode) {
fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
redraw, init, gameMode);
}
+ CleanupTail(); // [HGM] vari: delete any stored variations
pausing = pauseExamInvalid = FALSE;
startedFromSetupPosition = blackPlaysFirst = FALSE;
firstMove = TRUE;
gameMode = BeginningOfGame;
ModeHighlight();
if(appData.icsActive) gameInfo.variant = VariantNormal;
+ currentMove = forwardMostMove = backwardMostMove = 0;
InitPosition(redraw);
for (i = 0; i < MAX_MOVES; i++) {
if (commentList[i] != NULL) {
if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
/* append the comment but don't display it */
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
+ AppendComment(currentMove, p, FALSE);
return TRUE;
case WhiteCapturesEnPassant:
case (ChessMove) 0: /* end of file */
if (appData.debugMode)
fprintf(debugFP, "Parser hit end of file\n");
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, castlingRights[currentMove]) ) {
+ switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
/* Reached start of next game in file */
if (appData.debugMode)
fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, castlingRights[currentMove]) ) {
+ switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
MakeMove(fromX, fromY, toX, toY, promoChar);
ShowMove(fromX, fromY, toX, toY);
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, castlingRights[currentMove]) ) {
+ switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
break;
/* [HGM] copy FEN attributes as well. Bugfix 4.3.14m and 4.3.15e: moved to after 'blackPlaysFirst' */
{ int i;
initialRulePlies = FENrulePlies;
- epStatus[forwardMostMove] = FENepStatus;
for( i=0; i< nrCastlingRights; i++ )
- initialRights[i] = castlingRights[forwardMostMove][i] = FENcastlingRights[i];
+ initialRights[i] = initial_position[CASTLING][i];
}
yyboardindex = forwardMostMove;
free(gameInfo.fen);
if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
+ AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
cm = (ChessMove) yylex();
}
if (appData.debugMode)
fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
+ AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
cm = (ChessMove) yylex();
}
DisplayError(_("Position not found in file"), 0);
return FALSE;
}
-#if 0
- switch (line[0]) {
- case '#': case 'x':
- default:
- fenMode = FALSE;
- break;
- case 'p': case 'n': case 'b': case 'r': case 'q': case 'k':
- case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':
- case '1': case '2': case '3': case '4': case '5': case '6':
- case '7': case '8': case '9':
- case 'H': case 'A': case 'M': case 'h': case 'a': case 'm':
- case 'E': case 'F': case 'G': case 'e': case 'f': case 'g':
- case 'C': case 'W': case 'c': case 'w':
- fenMode = TRUE;
- break;
- }
-#else
// [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces
fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || CharToPiece(line[0]) != EmptySquare;
-#endif
if (pn >= 2) {
if (fenMode || line[0] == '#') pn--;
currentMove = forwardMostMove = backwardMostMove = 0;
DisplayMessage("", _("White to play"));
}
- /* [HGM] copy FEN attributes as well */
- { int i;
- initialRulePlies = FENrulePlies;
- epStatus[forwardMostMove] = FENepStatus;
- for( i=0; i< nrCastlingRights; i++ )
- castlingRights[forwardMostMove][i] = FENcastlingRights[i];
- }
+ initialRulePlies = FENrulePlies; /* [HGM] copy FEN attributes as well */
SendBoard(&first, forwardMostMove);
if (appData.debugMode) {
int i, j;
- for(i=0;i<2;i++){for(j=0;j<6;j++)fprintf(debugFP, " %d", castlingRights[i][j]);fprintf(debugFP,"\n");}
+ for(i=0;i<2;i++){for(j=0;j<6;j++)fprintf(debugFP, " %d", boards[i][CASTLING][j]);fprintf(debugFP,"\n");}
for(j=0;j<6;j++)fprintf(debugFP, " %d", initialRights[j]);fprintf(debugFP,"\n");
fprintf(debugFP, "Load Position\n");
}
/* Print comments preceding this move */
if (commentList[i] != NULL) {
if (linelen > 0) fprintf(f, "\n");
- fprintf(f, "{\n%s}\n", commentList[i]);
+ fprintf(f, "%s\n", commentList[i]);
linelen = 0;
newblock = TRUE;
}
fprintf(f, " ");
linelen++;
}
- fprintf(f, numtext);
+ fprintf(f, "%s", numtext);
linelen += numlen;
/* Get move */
strcpy(move_buffer, SavePart(parseList[i])); // [HGM] pgn: print move via buffer, so it can be edited
movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */
-#if 0
- // SavePart already does this!
- if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {
- int p = movelen - 1;
- if(move_buffer[p] == ' ') p--;
- if(move_buffer[p] == ')') { // [HGM] pgn: strip off ICS time if we have extended info
- while(p && move_buffer[--p] != '(');
- if(p && move_buffer[p-1] == ' ') move_buffer[movelen=p-1] = 0;
- }
- }
-#endif
+
/* Print move */
blank = linelen > 0 && movelen > 0;
if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
fprintf(f, " ");
linelen++;
}
- fprintf(f, move_buffer);
+ fprintf(f, "%s", move_buffer);
linelen += movelen;
/* [AS] Add PV info if present */
/* [HGM] add time */
char buf[MSG_SIZ]; int seconds = 0;
-#if 1
if(i >= backwardMostMove) {
if(WhiteOnMove(i))
seconds = timeRemaining[0][i] - timeRemaining[0][i+1]
+ GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds);
}
seconds = (seconds+50)/100; // deci-seconds, rounded to nearest
-#else
- seconds = (pvInfoList[i].time + 5)/10; // [HGM] PVtime: use engine time
-#endif
if( seconds <= 0) buf[0] = 0; else
if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {
fprintf(f, " ");
linelen++;
}
- fprintf(f, move_buffer);
+ fprintf(f, "%s", move_buffer);
linelen += movelen;
}
/* Print comments after last move */
if (commentList[i] != NULL) {
- fprintf(f, "{\n%s}\n", commentList[i]);
+ fprintf(f, "%s\n", commentList[i]);
}
/* Print result */
int dummy;
char *dummy2;
{
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving
if (appData.oldSaveStyle)
return SaveGameOldStyle(f);
if (icsPR != NoProc) {
DestroyChildProcess(icsPR, TRUE);
}
-#if 0
- /* Save game if resource set and not already saved by GameEnds() */
- if ((gameInfo.resultDetails == NULL || errorExitFlag )
- && forwardMostMove > 0) {
- if (*appData.saveGameFile != NULLCHAR) {
- SaveGameToFile(appData.saveGameFile, TRUE);
- } else if (appData.autoSaveGames) {
- AutoSaveGame();
- }
- if (*appData.savePositionFile != NULLCHAR) {
- SavePositionToFile(appData.savePositionFile);
- }
- }
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-#else
+
/* [HGM] crash: leave writing PGN and position entirely to GameEnds() */
GameEnds(gameInfo.result, gameInfo.resultDetails==NULL ? "xboard exit" : gameInfo.resultDetails, GE_PLAYER);
-#endif
+
/* [HGM] crash: the above GameEnds() is a dud if another one was running */
/* make sure this other one finishes before killing it! */
if(endingGame) { int count = 0;
first.analyzing = TRUE;
/*first.maybeThinking = TRUE;*/
first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- AnalysisPopUp(_("Analysis"),
- _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));
+ EngineOutputPopUp();
}
if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode;
pausing = FALSE;
first.analyzing = TRUE;
/*first.maybeThinking = TRUE;*/
first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- AnalysisPopUp(_("Analysis"),
- _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));
+ EngineOutputPopUp();
}
gameMode = AnalyzeFile;
pausing = FALSE;
EditGameEvent();
if (gameMode == EditPosition)
- EditPositionDone();
+ EditPositionDone(TRUE);
if (!WhiteOnMove(currentMove)) {
DisplayError(_("It is not White's turn"), 0);
SetMachineThinkingEnables();
first.maybeThinking = TRUE;
StartClocks();
+ firstMove = FALSE;
if (appData.autoFlipView && !flipView) {
flipView = !flipView;
EditGameEvent();
if (gameMode == EditPosition)
- EditPositionDone();
+ EditPositionDone(TRUE);
if (WhiteOnMove(currentMove)) {
DisplayError(_("It is not Black's turn"), 0);
if (gameMode != EditGame) return;
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
case AnalyzeFile:
break;
}
- forwardMostMove = currentMove;
+// forwardMostMove = currentMove;
+ TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
ResurrectChessProgram(); /* in case first program isn't running */
if (second.pr == NULL) {
strcpy(bookMove, "move ");
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = onmove;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
case AnalyzeFile:
SendToProgram("exit\n", &first);
first.analyzing = FALSE;
}
- AnalysisPopDown();
thinkOutput[0] = NULLCHAR;
}
void
-EditPositionDone()
+EditPositionDone(Boolean fakeRights)
{
int king = gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing;
startedFromSetupPosition = TRUE;
InitChessProgram(&first, FALSE);
- castlingRights[0][2] = castlingRights[0][5] = BOARD_WIDTH>>1;
+ if(fakeRights) { // [HGM] suppress this if we just pasted a FEN.
+ boards[0][EP_STATUS] = EP_NONE;
+ boards[0][CASTLING][2] = boards[0][CASTLING][5] = BOARD_WIDTH>>1;
if(boards[0][0][BOARD_WIDTH>>1] == king) {
- castlingRights[0][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : -1;
- castlingRights[0][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : -1;
- } else castlingRights[0][2] = -1;
+ boards[0][CASTLING][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : NoRights;
+ boards[0][CASTLING][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : NoRights;
+ } else boards[0][CASTLING][2] = NoRights;
if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king) {
- castlingRights[0][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : -1;
- castlingRights[0][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : -1;
- } else castlingRights[0][5] = -1;
+ boards[0][CASTLING][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : NoRights;
+ boards[0][CASTLING][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : NoRights;
+ } else boards[0][CASTLING][5] = NoRights;
+ }
SendToProgram("force\n", &first);
if (blackPlaysFirst) {
strcpy(moveList[0], "");
strcpy(parseList[0], "");
currentMove = forwardMostMove = backwardMostMove = 1;
CopyBoard(boards[1], boards[0]);
- /* [HGM] copy rights as well, as this code is also used after pasting a FEN */
- { int i;
- epStatus[1] = epStatus[0];
- for(i=0; i<nrCastlingRights; i++) castlingRights[1][i] = castlingRights[0][i];
- }
} else {
currentMove = forwardMostMove = backwardMostMove = 0;
}
StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
"Black offers a draw" : "White offers a draw")) {
#ifdef NOTDEF
- AppendComment(cmailOldMove, "Draw declined");
+ AppendComment(cmailOldMove, "Draw declined", TRUE);
DisplayComment(cmailOldMove - 1, "Draw declined");
#endif /*NOTDEF*/
} else {
} else if (currentMove == cmailOldMove + 1) {
char *offer = WhiteOnMove(cmailOldMove) ?
"White offers a draw" : "Black offers a draw";
- AppendComment(currentMove, offer);
+ AppendComment(currentMove, offer, TRUE);
DisplayComment(currentMove - 1, offer);
cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;
} else {
void
RevertEvent()
{
+ if(PopTail()) { // [HGM] vari: restore old game tail
+ return;
+ }
if (gameMode != IcsExamining) {
DisplayError(_("You are not examining a game"), 0);
return;
void
TruncateGame()
{
+ CleanupTail(); // [HGM] vari: only keep current variation if we explicitly truncate
if (forwardMostMove > currentMove) {
if (gameInfo.resultDetails != NULL) {
free(gameInfo.resultDetails);
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case TwoMachinesPlay:
return;
commentList[index] = NULL;
return;
}
+ if(*text == '{' || *text == '(' || *text == '[') {
commentList[index] = (char *) malloc(len + 2);
strncpy(commentList[index], text, len);
commentList[index][len] = '\n';
commentList[index][len + 1] = NULLCHAR;
+ } 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");
+ strcat(commentList[index], text);
+ while(p = strchr(commentList[index], '}')) *p = ')'; // kill all } to make it one comment
+ strcat(commentList[index], "\n}");
+ }
}
void
}
void
-AppendComment(index, text)
+AppendComment(index, text, addBraces)
int index;
char *text;
+ Boolean addBraces; // [HGM] braces: tells if we should add {}
{
int oldlen, len;
char *old;
+if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces); fflush(debugFP);
text = GetInfoFromComment( index, text ); /* [HGM] PV time: strip PV info from comment */
CrushCRs(text);
if (commentList[index] != NULL) {
old = commentList[index];
oldlen = strlen(old);
- commentList[index] = (char *) malloc(oldlen + len + 2);
+ commentList[index] = (char *) malloc(oldlen + len + 4); // might waste 2
strcpy(commentList[index], old);
free(old);
+ // [HGM] braces: join "{A\n}" + "{B}" as "{A\nB\n}"
+ if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces)) {
+ if(addBraces) addBraces = FALSE; else { text++; len--; }
+ while (*text == '\n') { text++; len--; }
+ commentList[index][oldlen-1] = NULLCHAR;
+ oldlen--;
+ }
strncpy(&commentList[index][oldlen], text, len);
- commentList[index][oldlen + len] = '\n';
- commentList[index][oldlen + len + 1] = NULLCHAR;
+ if(addBraces) strcpy(&commentList[index][oldlen + len], "\n}");
+ else strcpy(&commentList[index][oldlen + len], "\n");
} else {
- commentList[index] = (char *) malloc(len + 2);
- strncpy(commentList[index], text, len);
- commentList[index][len] = '\n';
- commentList[index][len + 1] = NULLCHAR;
+ commentList[index] = (char *) malloc(len + 4); // perhaps wastes 2...
+ if(addBraces) commentList[index][0] = '{';
+ strcpy(commentList[index] + addBraces, text);
+ strcat(commentList[index], "\n");
+ if(addBraces) strcat(commentList[index], "}");
}
}
if( s_emt != NULL ) {
}
+ return text;
}
else {
/* We expect something like: [+|-]nnn.nn/dd */
int score_lo = 0;
+ if(*text != '{') return text; // [HGM] braces: must be normal comment
+
sep = strchr( text, '/' );
if( sep == NULL || sep < (text+4) ) {
return text;
}
time = -1; sec = -1; deci = -1;
- if( sscanf( text, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
- sscanf( text, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
- sscanf( text, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
- sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
+ if( sscanf( text+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
+ sscanf( text+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
+ sscanf( text+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
+ sscanf( text+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
return text;
}
pvInfoList[index-1].depth = depth;
pvInfoList[index-1].score = score;
pvInfoList[index-1].time = 10*time; // centi-sec
+ if(*sep == '}') *sep = 0; else *--sep = '{';
}
return sep;
}
&& !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */
sprintf(buf, _("Error writing to %s chess program"), cps->which);
if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
- if(epStatus[forwardMostMove] <= EP_DRAWS) {
+ 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);
} else {
_("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(epStatus[forwardMostMove] <= EP_DRAWS) {
+ 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);
} else {
sscanf(message, "resign%c", &c)!=1 && sscanf(message, "feature %c", &c)!=1 &&
sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
sscanf(message, "tell%c", &c)!=1 && sscanf(message, "0-1 %c", &c)!=1 &&
- sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 && start != '#')
+ sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
+ sscanf(message, "pong %c", &c)!=1 && start != '#')
{ quote = "# "; print = (appData.engineComments == 2); }
message[0] = start; // restore original message
}
/* unknown feature: complain and skip */
q = p;
while (*q && *q != '=') q++;
- sprintf(buf, "rejected %.*s\n", q-p, p);
+ sprintf(buf, "rejected %.*s\n", (int)(q-p), p);
SendToProgram(buf, cps);
p = q;
if (*p == '=') {
appData.periodicUpdates=newState;
/* Display type changes, so update it now */
- DisplayAnalysis();
+// DisplayAnalysis();
/* Get the ball rolling again... */
if (newState) {
int newState;
{
if (newState == appData.ponderNextMove) return;
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
if (newState) {
SendToProgram("hard\n", &first);
if (gameMode == TwoMachinesPlay) {
{
char buf[MSG_SIZ];
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);
SendToProgram(buf, &first);
if (gameMode == TwoMachinesPlay) {
if (oldState == newState) return;
oldState = newState;
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
if (oldState) {
SendToProgram("post\n", &first);
if (gameMode == TwoMachinesPlay) {
}
void
-DisplayAnalysisText(text)
- char *text;
-{
- char buf[MSG_SIZ];
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
- || appData.icsEngineAnalyze) {
- sprintf(buf, "Analysis (%s)", first.tidy);
- AnalysisPopUp(buf, text);
- }
-}
-
-static int
-only_one_move(str)
- char *str;
-{
- while (*str && isspace(*str)) ++str;
- while (*str && !isspace(*str)) ++str;
- if (!*str) return 1;
- while (*str && isspace(*str)) ++str;
- if (!*str) return 1;
- return 0;
-}
-
-void
-DisplayAnalysis()
-{
- char buf[MSG_SIZ];
- char lst[MSG_SIZ / 2];
- double nps;
- static char *xtra[] = { "", " (--)", " (++)" };
- int h, m, s, cs;
-
- if (programStats.time == 0) {
- programStats.time = 1;
- }
-
- if (programStats.got_only_move) {
- safeStrCpy(buf, programStats.movelist, sizeof(buf));
- } else {
- safeStrCpy( lst, programStats.movelist, sizeof(lst));
-
- nps = (u64ToDouble(programStats.nodes) /
- ((double)programStats.time /100.0));
-
- cs = programStats.time % 100;
- s = programStats.time / 100;
- h = (s / (60*60));
- s = s - h*60*60;
- m = (s/60);
- s = s - m*60;
-
- if (programStats.moves_left > 0 && appData.periodicUpdates) {
- if (programStats.move_name[0] != NULLCHAR) {
- sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, programStats.move_name,
- ((float)programStats.score)/100.0, lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- } else {
- sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- }
- } else {
- sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- }
- }
- DisplayAnalysisText(buf);
-}
-
-void
DisplayComment(moveNumber, text)
int moveNumber;
char *text;
char title[MSG_SIZ];
char buf[8000]; // comment can be long!
int score, depth;
-
- if( appData.autoDisplayComment ) {
- if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
- strcpy(title, "Comment");
- } else {
- sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
- WhiteOnMove(moveNumber) ? " " : ".. ",
- parseList[moveNumber]);
- }
- // [HGM] PV info: display PV info together with (or as) comment
- if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {
- if(text == NULL) text = "";
- score = pvInfoList[moveNumber].score;
- sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
- depth, (pvInfoList[moveNumber].time+50)/100, text);
- text = buf;
- }
- } else title[0] = 0;
-
- if (text != NULL)
+
+ if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
+ strcpy(title, "Comment");
+ } else {
+ sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
+ WhiteOnMove(moveNumber) ? " " : ".. ",
+ parseList[moveNumber]);
+ }
+ // [HGM] PV info: display PV info together with (or as) comment
+ if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {
+ if(text == NULL) text = "";
+ score = pvInfoList[moveNumber].score;
+ sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
+ depth, (pvInfoList[moveNumber].time+50)/100, text);
+ text = buf;
+ }
+ if (text != NULL && (appData.autoDisplayComment || commentUp))
CommentPopUp(title, text);
}
void
CheckTimeControl()
{
- if (!appData.clockMode || appData.icsActive ||
+ if (!appData.clockMode || appData.icsActive || searchTime || // [HGM] st: no inc in st mode
gameMode == PlayFromGameFile || forwardMostMove == 0) return;
/*
(void) StopClockTimer();
if (appData.icsActive) {
whiteTimeRemaining = blackTimeRemaining = 0;
+ } else if (searchTime) {
+ 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;
break;
}
+ if (searchTime) { // [HGM] st: set clock of player that has to move to max time
+ if(WhiteOnMove(forwardMostMove))
+ whiteTimeRemaining = 1000*searchTime / WhitePlayer()->timeOdds;
+ else blackTimeRemaining = 1000*searchTime / WhitePlayer()->other->timeOdds;
+ }
+
tickStartTM = now;
intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
whiteTimeRemaining : blackTimeRemaining);
q = p;
if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {
/* [HGM] write directly from rights */
- if(castlingRights[move][2] >= 0 &&
- castlingRights[move][0] >= 0 )
- *p++ = castlingRights[move][0] + AAA + 'A' - 'a';
- if(castlingRights[move][2] >= 0 &&
- castlingRights[move][1] >= 0 )
- *p++ = castlingRights[move][1] + AAA + 'A' - 'a';
- if(castlingRights[move][5] >= 0 &&
- castlingRights[move][3] >= 0 )
- *p++ = castlingRights[move][3] + AAA;
- if(castlingRights[move][5] >= 0 &&
- castlingRights[move][4] >= 0 )
- *p++ = castlingRights[move][4] + AAA;
+ if(boards[move][CASTLING][2] != NoRights &&
+ boards[move][CASTLING][0] != NoRights )
+ *p++ = boards[move][CASTLING][0] + AAA + 'A' - 'a';
+ if(boards[move][CASTLING][2] != NoRights &&
+ boards[move][CASTLING][1] != NoRights )
+ *p++ = boards[move][CASTLING][1] + AAA + 'A' - 'a';
+ if(boards[move][CASTLING][5] != NoRights &&
+ boards[move][CASTLING][3] != NoRights )
+ *p++ = boards[move][CASTLING][3] + AAA;
+ if(boards[move][CASTLING][5] != NoRights &&
+ boards[move][CASTLING][4] != NoRights )
+ *p++ = boards[move][CASTLING][4] + AAA;
} else {
/* [HGM] write true castling rights */
if( nrCastlingRights == 6 ) {
- if(castlingRights[move][0] == BOARD_RGHT-1 &&
- castlingRights[move][2] >= 0 ) *p++ = 'K';
- if(castlingRights[move][1] == BOARD_LEFT &&
- castlingRights[move][2] >= 0 ) *p++ = 'Q';
- if(castlingRights[move][3] == BOARD_RGHT-1 &&
- castlingRights[move][5] >= 0 ) *p++ = 'k';
- if(castlingRights[move][4] == BOARD_LEFT &&
- castlingRights[move][5] >= 0 ) *p++ = 'q';
+ if(boards[move][CASTLING][0] == BOARD_RGHT-1 &&
+ boards[move][CASTLING][2] != NoRights ) *p++ = 'K';
+ if(boards[move][CASTLING][1] == BOARD_LEFT &&
+ boards[move][CASTLING][2] != NoRights ) *p++ = 'Q';
+ if(boards[move][CASTLING][3] == BOARD_RGHT-1 &&
+ boards[move][CASTLING][5] != NoRights ) *p++ = 'k';
+ if(boards[move][CASTLING][4] == BOARD_LEFT &&
+ boards[move][CASTLING][5] != NoRights ) *p++ = 'q';
}
}
if (q == p) *p++ = '-'; /* No castling rights */
} else {
*p++ = '-';
}
+ } else if(move == backwardMostMove) {
+ // [HGM] perhaps we should always do it like this, and forget the above?
+ if((signed char)boards[move][EP_STATUS] >= 0) {
+ *p++ = boards[move][EP_STATUS] + AAA;
+ *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';
+ } else {
+ *p++ = '-';
+ }
} else {
*p++ = '-';
}
if (appData.debugMode) { int k;
fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);
for(k=backwardMostMove; k<=forwardMostMove; k++)
- fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);
+ fprintf(debugFP, "e%d. p=%d\n", k, (signed char)boards[k][EP_STATUS]);
}
- while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++;
+ while(j > backwardMostMove && (signed char)boards[j][EP_STATUS] <= EP_NONE) j--,i++;
if( j == backwardMostMove ) i += initialRulePlies;
sprintf(p, "%d ", i);
p += i>=100 ? 4 : i >= 10 ? 3 : 2;
while (emptycount--)
board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;
break;
-#if(BOARD_SIZE >= 10)
+#if(BOARD_FILES >= 10)
} else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */
p++; emptycount=10;
if (j + emptycount > gameInfo.boardWidth) return FALSE;
/* return the extra info in global variiables */
/* set defaults in case FEN is incomplete */
- FENepStatus = EP_UNKNOWN;
+ board[EP_STATUS] = EP_UNKNOWN;
for(i=0; i<nrCastlingRights; i++ ) {
- FENcastlingRights[i] =
- gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom ? -1 : initialRights[i];
+ board[CASTLING][i] =
+ gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom ? NoRights : initialRights[i];
} /* assume possible unless obviously impossible */
- if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;
- if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;
- if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;
- if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;
- if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;
- if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;
+ if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) board[CASTLING][0] = NoRights;
+ if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) board[CASTLING][1] = NoRights;
+ if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) board[CASTLING][2] = NoRights;
+ if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) board[CASTLING][3] = NoRights;
+ if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) board[CASTLING][4] = NoRights;
+ if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) board[CASTLING][5] = NoRights;
FENrulePlies = 0;
while(*p==' ') p++;
if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {
/* castling indicator present, so default becomes no castlings */
for(i=0; i<nrCastlingRights; i++ ) {
- FENcastlingRights[i] = -1;
+ board[CASTLING][i] = NoRights;
}
}
while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' ||
switch(c) {
case'K':
for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);
- FENcastlingRights[0] = i != whiteKingFile ? i : -1;
- FENcastlingRights[2] = whiteKingFile;
+ board[CASTLING][0] = i != whiteKingFile ? i : NoRights;
+ board[CASTLING][2] = whiteKingFile;
break;
case'Q':
for(i=BOARD_LEFT; board[0][i]!=WhiteRook && i<whiteKingFile; i++);
- FENcastlingRights[1] = i != whiteKingFile ? i : -1;
- FENcastlingRights[2] = whiteKingFile;
+ board[CASTLING][1] = i != whiteKingFile ? i : NoRights;
+ board[CASTLING][2] = whiteKingFile;
break;
case'k':
for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);
- FENcastlingRights[3] = i != blackKingFile ? i : -1;
- FENcastlingRights[5] = blackKingFile;
+ board[CASTLING][3] = i != blackKingFile ? i : NoRights;
+ board[CASTLING][5] = blackKingFile;
break;
case'q':
for(i=BOARD_LEFT; board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);
- FENcastlingRights[4] = i != blackKingFile ? i : -1;
- FENcastlingRights[5] = blackKingFile;
+ board[CASTLING][4] = i != blackKingFile ? i : NoRights;
+ board[CASTLING][5] = blackKingFile;
case '-':
break;
default: /* FRC castlings */
for(i=BOARD_LEFT; i<BOARD_RGHT; i++)
if(board[BOARD_HEIGHT-1][i] == BlackKing) break;
if(i == BOARD_RGHT) break;
- FENcastlingRights[5] = i;
+ board[CASTLING][5] = i;
c -= AAA;
if(board[BOARD_HEIGHT-1][c] < BlackPawn ||
board[BOARD_HEIGHT-1][c] >= BlackKing ) break;
if(c > i)
- FENcastlingRights[3] = c;
+ board[CASTLING][3] = c;
else
- FENcastlingRights[4] = c;
+ board[CASTLING][4] = c;
} else { /* white rights */
for(i=BOARD_LEFT; i<BOARD_RGHT; i++)
if(board[0][i] == WhiteKing) break;
if(i == BOARD_RGHT) break;
- FENcastlingRights[2] = i;
+ board[CASTLING][2] = i;
c -= AAA - 'a' + 'A';
if(board[0][c] >= WhiteKing) break;
if(c > i)
- FENcastlingRights[0] = c;
+ board[CASTLING][0] = c;
else
- FENcastlingRights[1] = c;
+ board[CASTLING][1] = c;
}
}
}
if (appData.debugMode) {
fprintf(debugFP, "FEN castling rights:");
for(i=0; i<nrCastlingRights; i++)
- fprintf(debugFP, " %d", FENcastlingRights[i]);
+ fprintf(debugFP, " %d", board[CASTLING][i]);
fprintf(debugFP, "\n");
}
if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi &&
gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) {
if(*p=='-') {
- p++; FENepStatus = EP_NONE;
+ 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++;
- FENepStatus = c;
+ board[EP_STATUS] = c;
}
}
EditPositionEvent();
blackPlaysFirst = savedBlackPlaysFirst;
CopyBoard(boards[0], initial_position);
- /* [HGM] copy FEN attributes as well */
- { int i;
- initialRulePlies = FENrulePlies;
- epStatus[0] = FENepStatus;
- for( i=0; i<nrCastlingRights; i++ )
- castlingRights[0][i] = FENcastlingRights[i];
- }
- EditPositionDone();
+ initialRulePlies = FENrulePlies; /* [HGM] copy FEN attributes as well */
+ EditPositionDone(FALSE); // [HGM] fake: do not fake rights if we had FEN
DisplayBothClocks();
DrawPosition(FALSE, boards[currentMove]);
}
}
}
+
+static char cseq[12] = "\\ ";
+
+Boolean set_cont_sequence(char *new_seq)
+{
+ int len;
+ Boolean ret;
+
+ // handle bad attempts to set the sequence
+ if (!new_seq)
+ return 0; // acceptable error - no debug
+
+ len = strlen(new_seq);
+ ret = (len > 0) && (len < sizeof(cseq));
+ if (ret)
+ strcpy(cseq, new_seq);
+ else if (appData.debugMode)
+ fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %u)\n", new_seq, (unsigned) sizeof(cseq)-1);
+ return ret;
+}
+
+/*
+ reformat a source message so words don't cross the width boundary. internal
+ newlines are not removed. returns the wrapped size (no null character unless
+ included in source message). If dest is NULL, only calculate the size required
+ for the dest buffer. lp argument indicats line position upon entry, and it's
+ passed back upon exit.
+*/
+int wrap(char *dest, char *src, int count, int width, int *lp)
+{
+ int len, i, ansi, cseq_len, line, old_line, old_i, old_len, clen;
+
+ cseq_len = strlen(cseq);
+ old_line = line = *lp;
+ ansi = len = clen = 0;
+
+ for (i=0; i < count; i++)
+ {
+ if (src[i] == '\033')
+ ansi = 1;
+
+ // if we hit the width, back up
+ if (!ansi && (line >= width) && src[i] != '\n' && src[i] != ' ')
+ {
+ // store i & len in case the word is too long
+ old_i = i, old_len = len;
+
+ // find the end of the last word
+ while (i && src[i] != ' ' && src[i] != '\n')
+ {
+ i--;
+ len--;
+ }
+
+ // word too long? restore i & len before splitting it
+ if ((old_i-i+clen) >= width)
+ {
+ i = old_i;
+ len = old_len;
+ }
+
+ // extra space?
+ if (i && src[i-1] == ' ')
+ len--;
+
+ if (src[i] != ' ' && src[i] != '\n')
+ {
+ i--;
+ if (len)
+ len--;
+ }
+
+ // now append the newline and continuation sequence
+ if (dest)
+ dest[len] = '\n';
+ len++;
+ if (dest)
+ strncpy(dest+len, cseq, cseq_len);
+ len += cseq_len;
+ line = cseq_len;
+ clen = cseq_len;
+ continue;
+ }
+
+ if (dest)
+ dest[len] = src[i];
+ len++;
+ if (!ansi)
+ line++;
+ if (src[i] == '\n')
+ line = 0;
+ if (src[i] == 'm')
+ ansi = 0;
+ }
+ if (dest && appData.debugMode)
+ {
+ fprintf(debugFP, "wrap(count:%d,width:%d,line:%d,len:%d,*lp:%d,src: ",
+ count, width, line, len, *lp);
+ show_bytes(debugFP, src, count);
+ fprintf(debugFP, "\ndest: ");
+ show_bytes(debugFP, dest, len);
+ fprintf(debugFP, "\n");
+ }
+ *lp = dest ? line : old_line;
+
+ return len;
+}
+
+// [HGM] vari: routines for shelving variations
+
+void
+PushTail(int firstMove, int lastMove)
+{
+ int i, j, nrMoves = lastMove - firstMove;
+
+ if(appData.icsActive) { // only in local mode
+ forwardMostMove = currentMove; // mimic old ICS behavior
+ return;
+ }
+ if(storedGames >= MAX_VARIATIONS-1) return;
+
+ // push current tail of game on stack
+ savedResult[storedGames] = gameInfo.result;
+ savedDetails[storedGames] = gameInfo.resultDetails;
+ gameInfo.resultDetails = NULL;
+ savedFirst[storedGames] = firstMove;
+ savedLast [storedGames] = lastMove;
+ savedFramePtr[storedGames] = framePtr;
+ framePtr -= nrMoves; // reserve space for the boards
+ for(i=nrMoves; i>=1; i--) { // copy boards to stack, working downwards, in case of overlap
+ CopyBoard(boards[framePtr+i], boards[firstMove+i]);
+ for(j=0; j<MOVE_LEN; j++)
+ moveList[framePtr+i][j] = moveList[firstMove+i-1][j];
+ for(j=0; j<2*MOVE_LEN; j++)
+ parseList[framePtr+i][j] = parseList[firstMove+i-1][j];
+ timeRemaining[0][framePtr+i] = timeRemaining[0][firstMove+i];
+ timeRemaining[1][framePtr+i] = timeRemaining[1][firstMove+i];
+ pvInfoList[framePtr+i] = pvInfoList[firstMove+i-1];
+ pvInfoList[firstMove+i-1].depth = 0;
+ commentList[framePtr+i] = commentList[firstMove+i];
+ commentList[firstMove+i] = NULL;
+ }
+
+ storedGames++;
+ forwardMostMove = currentMove; // truncte game so we can start variation
+ if(storedGames == 1) GreyRevert(FALSE);
+}
+
+Boolean
+PopTail()
+{
+ int i, j, nrMoves;
+
+ if(appData.icsActive) return FALSE; // only in local mode
+ if(!storedGames) return FALSE; // sanity
+
+ storedGames--;
+ ToNrEvent(savedFirst[storedGames]); // sets currentMove
+ nrMoves = savedLast[storedGames] - currentMove;
+ for(i=1; i<nrMoves; i++) { // copy last variation back
+ CopyBoard(boards[currentMove+i], boards[framePtr+i]);
+ for(j=0; j<MOVE_LEN; j++)
+ moveList[currentMove+i-1][j] = moveList[framePtr+i][j];
+ for(j=0; j<2*MOVE_LEN; j++)
+ parseList[currentMove+i-1][j] = parseList[framePtr+i][j];
+ timeRemaining[0][currentMove+i] = timeRemaining[0][framePtr+i];
+ timeRemaining[1][currentMove+i] = timeRemaining[1][framePtr+i];
+ pvInfoList[currentMove+i-1] = pvInfoList[framePtr+i];
+ if(commentList[currentMove+i]) free(commentList[currentMove+i]);
+ commentList[currentMove+i] = commentList[framePtr+i];
+ commentList[framePtr+i] = NULL;
+ }
+ framePtr = savedFramePtr[storedGames];
+ gameInfo.result = savedResult[storedGames];
+ if(gameInfo.resultDetails != NULL) {
+ free(gameInfo.resultDetails);
+ }
+ gameInfo.resultDetails = savedDetails[storedGames];
+ forwardMostMove = currentMove + nrMoves;
+ if(storedGames == 0) GreyRevert(TRUE);
+ return TRUE;
+}
+
+void
+CleanupTail()
+{ // remove all shelved variations
+ int i;
+ for(i=0; i<storedGames; i++) {
+ if(savedDetails[i])
+ free(savedDetails[i]);
+ savedDetails[i] = NULL;
+ }
+ for(i=framePtr; i<MAX_MOVES; i++) {
+ if(commentList[i]) free(commentList[i]);
+ commentList[i] = NULL;
+ }
+ framePtr = MAX_MOVES-1;
+ storedGames = 0;
+}