X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c1a08c998253d23ea05860d56124868727245426;hb=9a9bae3af89e8e0c983d963d1bfbff111adc117f;hp=a112d57e86d38f1408dbb6a0bdd25e0a45aa10f7;hpb=ca87afb87619efbaa94fe045670bc916e9ffe3ec;p=xboard.git diff --git a/backend.c b/backend.c index a112d57..c1a08c9 100644 --- a/backend.c +++ b/backend.c @@ -77,6 +77,7 @@ #if STDC_HEADERS # include # include +# include #else /* not STDC_HEADERS */ # if HAVE_STRING_H # include @@ -159,7 +160,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList)); 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, @@ -167,7 +168,7 @@ 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)); @@ -183,7 +184,6 @@ void FeedMovesToProgram P((ChessProgramState *cps, int upto)); 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)); @@ -441,47 +441,58 @@ AppData appData; Board boards[MAX_MOVES]; /* [HGM] Following 7 needed for accurate legality tests: */ -signed char epStatus[MAX_MOVES]; -signed char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1 -signed char castlingRank[BOARD_SIZE]; // and corresponding ranks -signed 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; int mute; // mute all sounds -ChessSquare FIDEArray[2][BOARD_SIZE] = { +// [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((Boolean annotate)); +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, @@ -489,36 +500,36 @@ ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatra }; -#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, @@ -526,7 +537,7 @@ ChessSquare JanusArray[2][BOARD_SIZE] = { }; #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, @@ -537,7 +548,7 @@ ChessSquare GothicArray[2][BOARD_SIZE] = { #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, @@ -547,23 +558,23 @@ ChessSquare FalconArray[2][BOARD_SIZE] = { #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; @@ -607,7 +618,7 @@ InitBackEnd1() 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; @@ -644,10 +655,10 @@ InitBackEnd1() { int i, j; - for( i=0; i 0) { appData.matchMode = TRUE; } else if (appData.matchMode) { @@ -1191,10 +1204,9 @@ InitBackEnd3 P((void)) } /* [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= BlackPawn ) { holdingsColumn = 0; @@ -1929,7 +1945,6 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } - } @@ -1937,8 +1952,7 @@ void 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; @@ -1955,59 +1969,65 @@ VariantSwitch(Board board, VariantClass newVariant) * 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_LEFT; j--) - board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] = - board[i][j]; - for(i=0; i gameInfo.holdingsWidth) { + for(i=0; i=BOARD_LEFT; j--) + board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] = + board[i][j]; + for(i=0; i 0) { /* If last read ended with a partial line that we couldn't parse, @@ -2079,18 +2103,66 @@ read_from_ics(isr, closure, data, count, error) 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(!appData.noJoin && 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 - if(buf_len == 0 || buf[buf_len-1] != ' ') - buf[buf_len++] = ' '; // add 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 MSG_SIZ - 30) // defuse unreasonably long input @@ -2849,7 +2917,7 @@ read_from_ics(isr, closure, data, count, error) 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); @@ -3180,16 +3248,14 @@ read_from_ics(isr, closure, data, count, error) 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); @@ -3206,6 +3272,7 @@ read_from_ics(isr, closure, data, count, error) /* 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; } @@ -3227,7 +3294,13 @@ read_from_ics(isr, closure, data, count, error) * 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) { @@ -3243,8 +3316,9 @@ read_from_ics(isr, closure, data, count, error) 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, @@ -3268,6 +3342,7 @@ read_from_ics(isr, closure, data, count, error) } /* 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; @@ -3337,6 +3412,7 @@ ParseBoard12(string) 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; @@ -3352,6 +3428,7 @@ ParseBoard12(string) 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 = 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; @@ -3441,6 +3518,27 @@ ParseBoard12(string) 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. */ @@ -3449,15 +3547,15 @@ ParseBoard12(string) /* 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); @@ -3493,7 +3591,7 @@ ParseBoard12(string) 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)); @@ -3573,6 +3671,7 @@ ParseBoard12(string) } } CopyBoard(boards[moveNum], board); + boards[moveNum][HOLDINGS_SET] = 0; // [HGM] indicate holdings not set if (moveNum == 0) { startedFromSetupPosition = !CompareBoards(board, initialPosition); @@ -3595,38 +3694,38 @@ ParseBoard12(string) for(i=BOARD_LEFT, 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_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 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) { @@ -3660,6 +3750,20 @@ ParseBoard12(string) } } 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; @@ -3670,12 +3774,20 @@ ParseBoard12(string) 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 */ @@ -3703,7 +3815,8 @@ ParseBoard12(string) 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); @@ -3749,11 +3862,10 @@ ParseBoard12(string) // 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: @@ -4406,12 +4518,12 @@ void SetUpShuffle(Board board, int number) // 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; } @@ -4426,9 +4538,9 @@ void SetUpShuffle(Board board, int number) 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>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; @@ -4604,7 +4718,6 @@ InitPosition(redraw) gameInfo.boardWidth = 12; nrCastlingRights = 0; SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk"); - for(i=0; i 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; @@ -4709,12 +4821,12 @@ InitPosition(redraw) /* 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); @@ -4735,7 +4847,7 @@ InitPosition(redraw) /* [HGM] loadPos: use PositionFile for every new game */ CopyBoard(initialPosition, filePosition); for(i=0; i= 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; @@ -4856,7 +4973,61 @@ IsPromotion(fromX, fromY, toX, toY) 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 @@ -4943,12 +5114,6 @@ OKToStartUserMove(x, y) /* 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: @@ -4977,6 +5142,7 @@ OKToStartUserMove(x, y) 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; @@ -4999,29 +5165,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) ChessMove moveType; ChessSquare pdown, pup; - if (fromX < 0 || fromY < 0) return ImpossibleMove; - - /* [HGM] suppress all moves into holdings area and guard band */ - if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 ) - return ImpossibleMove; - - /* [HGM] moved to here from winboard.c */ - /* note: capture of own piece can be legal as drag-drop premove. For click-click it is selection of new piece. */ - pdown = boards[currentMove][fromY][fromX]; - pup = boards[currentMove][toY][toX]; - if ( gameMode != EditPosition && !captureOwn && - (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 || - pup == WhiteKing && pdown == WhiteRook && fromY == 0 && toY == 0|| // also allow RxK - pup == BlackKing && pdown == BlackRook && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 ) - ) ) - return Comment; - /* 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! @@ -5140,6 +5283,10 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) 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; @@ -5159,7 +5306,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) /* [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) { @@ -5168,7 +5314,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return ImpossibleMove; } } -if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar); + return moveType; /* [HGM] in stead of calling FinishMove directly, this function is made into one that returns an OK move type if FinishMove @@ -5187,7 +5333,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) /*char*/int promoChar; { char *bookHit = 0; -if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar); + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { // [HGM] superchess: suppress promotions to non-available piece int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); @@ -5202,13 +5348,12 @@ if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", move move type in caller when we know the move is a legal promotion */ if(moveType == NormalMove && promoChar) moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar); -if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar); + /* [HGM] convert drag-and-drop piece drops to standard form */ - if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { + if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ){ moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop; if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]); -// fromX = boards[currentMove][fromY][fromX]; // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings @@ -5217,7 +5362,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move } /* [HGM] The following if has been moved here from - UserMoveEvent(). Because it seemed to belon here (why not allow + UserMoveEvent(). Because it seemed to belong here (why not allow piece drops in training games?), and because it can only be performed after it is known to what we promote. */ if (gameMode == Training) { @@ -5226,9 +5371,9 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move * 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); @@ -5257,8 +5402,9 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move /* 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 */ @@ -5291,7 +5437,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move } ModeHighlight(); } -if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar); + /* Relay move to ICS or chess engine */ if (appData.icsActive) { if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || @@ -5319,8 +5465,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", move 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; @@ -5384,6 +5529,191 @@ if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", move 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; @@ -5571,7 +5901,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h 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, @@ -5599,12 +5930,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h ) { 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) { @@ -5704,7 +6034,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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) { @@ -5766,7 +6096,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* 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*/ @@ -5779,7 +6109,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* 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*/ @@ -5790,7 +6120,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } 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 @@ -5804,17 +6134,16 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. // 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; } } @@ -5825,25 +6154,25 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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: @@ -5868,7 +6197,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. { /* 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 */ @@ -5901,10 +6230,10 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (appData.debugMode) { int i; fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n", - forwardMostMove, backwardMostMove, epStatus[backwardMostMove], + forwardMostMove, backwardMostMove, boards[backwardMostMove][EP_STATUS], appData.drawRepeats); for( i=forwardMostMove; i>=backwardMostMove; i-- ) - fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]); + fprintf(debugFP, "%d ep=%d\n", i, (signed char)boards[i][EP_STATUS]); } @@ -5912,26 +6241,26 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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(CompareBoards(boards[k], boards[forwardMostMove])) { /* 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( rights == 0 && ++count > appData.drawRepeats-2 @@ -5944,11 +6273,9 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. // [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", @@ -5978,21 +6305,21 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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 @@ -6009,11 +6336,11 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. */ 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 @@ -6115,7 +6442,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. * 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); @@ -6123,12 +6450,9 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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; iscoreIsAbsolute && - ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) ) + ( gameMode == MachinePlaysBlack || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b' || + gameMode == IcsPlayingBlack || // [HGM] also add other situations where engine should report black POV + (gameMode == AnalyzeMode || gameMode == AnalyzeFile || gameMode == IcsObserving && appData.icsEngineAnalyze) && + !WhiteOnMove(currentMove) + ) ) { curscore = -curscore; } @@ -6624,9 +6953,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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 */ @@ -6634,8 +6966,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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) ); @@ -6685,7 +7017,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); - DisplayAnalysis(); } return; @@ -6713,7 +7044,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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", @@ -6738,7 +7068,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. SendProgramStatsToFrontend( cps, &programStats ); - DisplayAnalysis(); return; } else if (strncmp(message,"++",2) == 0) { @@ -6774,7 +7103,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); - DisplayAnalysis(); } return; } @@ -6989,18 +7317,15 @@ ParseGameHistory(game) 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; iBOARD_LEFT && board[toY][toX-1] == BlackPawn && gameInfo.variant != VariantBerolina || toX < fromX) - *ep = toX | berolina; + board[EP_STATUS] = toX | berolina; if(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 fromX) - *ep = toX; + board[EP_STATUS] = toX; } } for(i=0; i= 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 @@ -7287,7 +7616,6 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured; } } - } else if (gameInfo.variant == VariantAtomic) { if (captured != EmptySquare) { int y, x; @@ -7372,7 +7700,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar) 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; @@ -7382,9 +7710,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar) commentList[forwardMostMove+1] = NULL; } CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]); - {int i; for(i=0; i 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 */ @@ -7846,7 +8170,7 @@ GameEnds(result, resultDetails, whosays) && result != GameIsDrawn) { int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn); for(j=BOARD_LEFT; j= 0 && p <= (int)WhiteKing) k++; } if (appData.debugMode) { @@ -8158,6 +8482,7 @@ Reset(redraw, init) 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; @@ -8325,14 +8650,9 @@ LoadGameOneMove(readAhead) 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: @@ -8419,8 +8739,7 @@ LoadGameOneMove(readAhead) 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; @@ -8455,8 +8774,7 @@ LoadGameOneMove(readAhead) /* 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; @@ -8615,8 +8933,7 @@ MakeRegisteredMove() 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; @@ -8986,9 +9303,8 @@ LoadGame(f, gameNumber, title, useList) /* [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); @@ -9004,12 +9320,7 @@ LoadGame(f, gameNumber, title, useList) 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(); } @@ -9110,12 +9421,7 @@ LoadGame(f, gameNumber, title, useList) 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(); } @@ -9337,17 +9643,11 @@ LoadPosition(f, positionNumber, title) 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"); } @@ -9567,7 +9867,7 @@ SaveGamePGN(f) /* 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", commentList[i]); linelen = 0; newblock = TRUE; } @@ -9596,7 +9896,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, numtext); + fprintf(f, "%s", numtext); linelen += numlen; /* Get move */ @@ -9614,23 +9914,15 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, move_buffer); + fprintf(f, "%s", move_buffer); linelen += movelen; /* [AS] Add PV info if present */ if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) { /* [HGM] add time */ - char buf[MSG_SIZ]; int seconds = 0; + char buf[MSG_SIZ]; int seconds; - if(i >= backwardMostMove) { - if(WhiteOnMove(i)) - seconds = timeRemaining[0][i] - timeRemaining[0][i+1] - + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds); - else - seconds = timeRemaining[1][i] - timeRemaining[1][i+1] - + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds); - } - seconds = (seconds+50)/100; // deci-seconds, rounded to nearest + seconds = (pvInfoList[i].time+5)/10; // deci-seconds, rounded to nearest if( seconds <= 0) buf[0] = 0; else if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else { @@ -9658,7 +9950,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, move_buffer); + fprintf(f, "%s", move_buffer); linelen += movelen; } @@ -9670,7 +9962,7 @@ SaveGamePGN(f) /* Print comments after last move */ if (commentList[i] != NULL) { - fprintf(f, "{\n%s}\n", commentList[i]); + fprintf(f, "%s\n", commentList[i]); } /* Print result */ @@ -9759,7 +10051,7 @@ SaveGame(f, dummy, dummy2) 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); @@ -9800,6 +10092,7 @@ SavePosition(f, dummy, dummy2) time_t tm; char *fen; + if (gameMode == EditPosition) EditPositionDone(TRUE); if (appData.oldSaveStyle) { tm = time((time_t *) NULL); @@ -10321,8 +10614,7 @@ AnalyzeModeEvent() 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; @@ -10348,8 +10640,7 @@ AnalyzeFileEvent() 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; @@ -10379,7 +10670,7 @@ MachineWhiteEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (!WhiteOnMove(currentMove)) { DisplayError(_("It is not White's turn"), 0); @@ -10460,7 +10751,7 @@ MachineBlackEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (WhiteOnMove(currentMove)) { DisplayError(_("It is not Black's turn"), 0); @@ -10567,7 +10858,7 @@ TwoMachinesEvent P((void)) if (gameMode != EditGame) return; break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10578,7 +10869,8 @@ TwoMachinesEvent P((void)) 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) { @@ -10659,7 +10951,9 @@ TwoMachinesEvent P((void)) strcpy(bookMove, "move "); strcat(bookMove, bookHit); - HandleMachineMove(bookMove, &first); + savedMessage = bookMove; // args for deferred call + savedState = onmove; + ScheduleDelayedEvent(DeferredBookMove, 1); } } @@ -10703,7 +10997,7 @@ IcsClientEvent() break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: @@ -10744,7 +11038,7 @@ EditGameEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10847,37 +11141,34 @@ ExitAnalyzeMode() 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 currentMove) { if (gameInfo.resultDetails != NULL) { free(gameInfo.resultDetails); @@ -11698,7 +11993,7 @@ BookEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case TwoMachinesPlay: return; @@ -11803,6 +12098,14 @@ SetGameInfo() { /* This routine is used only for certain modes */ VariantClass v = gameInfo.variant; + ChessMove r = GameUnfinished; + char *p = NULL; + + if(gameMode == EditGame) { // [HGM] vari: do not erase result on EditGame + r = gameInfo.result; + p = gameInfo.resultDetails; + gameInfo.resultDetails = NULL; + } ClearGameInfo(&gameInfo); gameInfo.variant = v; @@ -11855,6 +12158,8 @@ SetGameInfo() gameInfo.round = StrSave("-"); gameInfo.white = StrSave("-"); gameInfo.black = StrSave("-"); + gameInfo.result = r; + gameInfo.resultDetails = p; break; case EditPosition: @@ -11904,10 +12209,23 @@ ReplaceComment(index, text) commentList[index] = NULL; return; } + if( *text == '{' && strchr(text, '}') || // [HGM] braces: if certainy malformed, put braces + *text == '[' && strchr(text, ']') || // otherwise hope the user knows what he is doing + *text == '(' && strchr(text, ')')) { // (perhaps check if this parses as comment-only?) 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"); + strncpy(commentList[index]+2, text, len); + commentList[index][len+2] = NULLCHAR; + while(p = strchr(commentList[index], '}')) *p = ')'; // kill all } to make it one comment + strcat(commentList[index], "\n}\n"); + } } void @@ -11926,13 +12244,15 @@ CrushCRs(text) } 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); @@ -11945,17 +12265,30 @@ AppendComment(index, text) if (commentList[index] != NULL) { old = commentList[index]; oldlen = strlen(old); - commentList[index] = (char *) malloc(oldlen + len + 2); + while(commentList[index][oldlen-1] == '\n') + commentList[index][--oldlen] = NULLCHAR; + commentList[index] = (char *) malloc(oldlen + len + 6); // might waste 4 strcpy(commentList[index], old); free(old); - strncpy(&commentList[index][oldlen], text, len); - commentList[index][oldlen + len] = '\n'; - commentList[index][oldlen + len + 1] = NULLCHAR; + // [HGM] braces: join "{A\n}\n" + "{\nB}" as "{A\nB\n}" + if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces)) { + if(addBraces) addBraces = FALSE; else { text++; len--; } + while (*text == '\n') { text++; len--; } + commentList[index][--oldlen] = NULLCHAR; + } + if(addBraces) strcat(commentList[index], "\n{\n"); + else strcat(commentList[index], "\n"); + strcat(commentList[index], text); + if(addBraces) strcat(commentList[index], "\n}\n"); + else strcat(commentList[index], "\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 + 6); // perhaps wastes 4... + if(addBraces) + strcpy(commentList[index], "{\n"); + else commentList[index][0] = NULLCHAR; + strcat(commentList[index], text); + strcat(commentList[index], "\n"); + if(addBraces) strcat(commentList[index], "}\n"); } } @@ -11999,21 +12332,24 @@ char *GetInfoFromComment( int index, char * text ) 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; } @@ -12048,6 +12384,7 @@ char *GetInfoFromComment( int index, char * 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; } @@ -12077,13 +12414,13 @@ SendToProgram(message, cps) && !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 { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } DisplayFatalError(buf, error, 1); } @@ -12108,13 +12445,13 @@ ReceiveFromProgram(isr, closure, message, count, error) _("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 { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } RemoveInputSource(cps->isr); DisplayFatalError(buf, 0, 1); @@ -12521,7 +12858,7 @@ ParseFeatures(args, cps) /* 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 == '=') { @@ -12548,7 +12885,7 @@ PeriodicUpdatesEvent(newState) appData.periodicUpdates=newState; /* Display type changes, so update it now */ - DisplayAnalysis(); +// DisplayAnalysis(); /* Get the ball rolling again... */ if (newState) { @@ -12562,7 +12899,7 @@ PonderNextMoveEvent(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) { @@ -12585,7 +12922,7 @@ NewSettingEvent(option, command, value) { 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) { @@ -12604,7 +12941,7 @@ ShowThinkingEvent() if (oldState == newState) return; oldState = newState; - if (gameMode == EditPosition) EditPositionDone(); + if (gameMode == EditPosition) EditPositionDone(TRUE); if (oldState) { SendToProgram("post\n", &first); if (gameMode == TwoMachinesPlay) { @@ -12688,92 +13025,6 @@ DisplayMove(moveNumber) } 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; @@ -12781,26 +13032,23 @@ DisplayComment(moveNumber, 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); } @@ -12891,7 +13139,7 @@ CheckFlags() 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; /* @@ -13017,6 +13265,9 @@ ResetClocks() (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; @@ -13146,6 +13397,12 @@ SwitchClocks() 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); @@ -13445,36 +13702,36 @@ PositionToFEN(move, overrideCastling) *p++ = ' '; if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines - while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; + while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p; } else { if(nrCastlingRights) { 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 */ @@ -13501,8 +13758,8 @@ PositionToFEN(move, overrideCastling) } } else if(move == backwardMostMove) { // [HGM] perhaps we should always do it like this, and forget the above? - if(epStatus[move] >= 0) { - *p++ = epStatus[move] + AAA; + if((signed char)boards[move][EP_STATUS] >= 0) { + *p++ = boards[move][EP_STATUS] + AAA; *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3'; } else { *p++ = '-'; @@ -13520,11 +13777,11 @@ PositionToFEN(move, overrideCastling) 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; @@ -13568,7 +13825,7 @@ ParseFEN(board, blackPlaysFirst, fen) 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; @@ -13650,17 +13907,17 @@ ParseFEN(board, blackPlaysFirst, fen) /* 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=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++; @@ -13668,7 +13925,7 @@ ParseFEN(board, blackPlaysFirst, fen) if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') { /* castling indicator present, so default becomes no castlings */ for(i=0; iwhiteKingFile; 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 && iblackKingFile; 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= 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= 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= BOARD_RGHT) return TRUE; if(*p >= '0' && *p <='9') *p++; - FENepStatus = c; + board[EP_STATUS] = c; } } @@ -13777,16 +14034,224 @@ EditPositionPasteFEN(char *fen) 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 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>1); + else strcpy(buf, "("); + for(i=currentMove; i>1, SavePart(parseList[i])); + else sprintf(moveBuf, " %s", SavePart(parseList[i])); + strcat(buf, moveBuf); + if(!--cnt) { strcat(buf, "\n"); cnt = 10; } + } + strcat(buf, ")"); + } + for(i=1; i