X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c1a08c998253d23ea05860d56124868727245426;hb=9a9bae3af89e8e0c983d963d1bfbff111adc117f;hp=5b8dcfa6c2105a2b570cbc441d27295243eb5897;hpb=e1edcbabb63e65d5d47c07a0836dd51d7e01de0e;p=xboard.git diff --git a/backend.c b/backend.c old mode 100755 new mode 100644 index 5b8dcfa..c1a08c9 --- a/backend.c +++ b/backend.c @@ -160,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, @@ -168,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)); @@ -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= BlackPawn ) { holdingsColumn = 0; @@ -1932,7 +1945,6 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } - } @@ -1940,6 +1952,7 @@ void VariantSwitch(Board board, VariantClass newVariant) { int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; + Board oldBoard; startedFromPositionFile = FALSE; if(gameInfo.variant == newVariant) return; @@ -1977,7 +1990,7 @@ VariantSwitch(Board board, VariantClass newVariant) case VariantSuper: newHoldingsWidth = 2; gameInfo.holdingsSize = 8; - return; + break; case VariantGothic: case VariantCapablanca: case VariantCapaRandom: @@ -2011,10 +2024,10 @@ VariantSwitch(Board board, VariantClass newVariant) gameInfo.holdingsWidth = newHoldingsWidth; gameInfo.variant = newVariant; InitDrawingSizes(-2, 0); + } else gameInfo.variant = newVariant; + CopyBoard(oldBoard, board); // remember correctly formatted board InitPosition(FALSE); /* this sets up board[0], but also other stuff */ - } else { gameInfo.variant = newVariant; InitPosition(FALSE); } - - DrawPosition(TRUE, boards[currentMove]); + DrawPosition(TRUE, currentMove ? boards[currentMove] : oldBoard); } static int loggedOn = FALSE; @@ -2040,7 +2053,7 @@ read_from_ics(isr, closure, data, count, error) int count; int error; { -#define BUF_SIZE 8192 +#define BUF_SIZE (16*1024) /* overflowed at 8K with "inchannel 1" on FICS? */ #define STARTED_NONE 0 #define STARTED_MOVES 1 #define STARTED_BOARD 2 @@ -2079,7 +2092,8 @@ read_from_ics(isr, closure, data, count, error) if (appData.debugMode) { int f = forwardMostMove; fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f, - castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]); + boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2], + boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]); } if (count > 0) { /* If last read ended with a partial line that we couldn't parse, @@ -2304,7 +2318,7 @@ read_from_ics(isr, closure, data, count, error) chattingPartner = -1; } else if(!suppressKibitz) // [HGM] kibitz - AppendComment(forwardMostMove, StripHighlight(parse)); + AppendComment(forwardMostMove, StripHighlight(parse), TRUE); else { // [HGM kibitz: divert memorized engine kibitz to engine-output window int nrDigit = 0, nrAlph = 0, i; if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input @@ -2903,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); @@ -3234,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); @@ -3260,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; } @@ -3281,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) { @@ -3297,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, @@ -3322,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; @@ -3391,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; @@ -3406,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; @@ -3495,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. */ @@ -3503,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); @@ -3547,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)); @@ -3627,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); @@ -3649,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) { @@ -3714,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; @@ -3724,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 */ @@ -3757,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); @@ -3803,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: @@ -4460,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; } @@ -4480,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; @@ -4658,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; @@ -4763,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); @@ -4789,7 +4847,7 @@ InitPosition(redraw) /* [HGM] loadPos: use PositionFile for every new game */ CopyBoard(initialPosition, filePosition); for(i=0; i in stead of calling FinishMove directly, this function is made into one that returns an OK move type if FinishMove @@ -5281,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))); @@ -5296,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 @@ -5311,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) { @@ -5320,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); @@ -5351,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 */ @@ -5385,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 || @@ -5413,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; @@ -5575,16 +5626,11 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } return; } - // ignore to-clicks in holdings + // ignore clicks on holdings if(x < BOARD_LEFT || x >= BOARD_RGHT) return; } - if (clickType == Release && (x == fromX && y == fromY || - x < BOARD_LEFT || x >= BOARD_RGHT)) { - - // treat drags into holding as click on start square - x = fromX; y = fromY; - + if (clickType == Release && x == fromX && y == fromY) { DragPieceEnd(xPix, yPix); if (appData.animateDragging) { /* Undo animation damage if any */ @@ -5604,7 +5650,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) return; } - /* we now have a different from- and to-square */ + /* we now have a different from- and (possibly off-board) to-square */ /* Completed move */ toX = x; toY = y; @@ -5627,6 +5673,18 @@ void LeftClick(ClickType clickType, int xPix, int 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) { @@ -5843,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, @@ -5871,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) { @@ -5976,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) { @@ -6038,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*/ @@ -6051,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*/ @@ -6062,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 @@ -6076,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; } } @@ -6097,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: @@ -6140,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 */ @@ -6173,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]); } @@ -6184,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 @@ -6216,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", @@ -6250,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 @@ -6281,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 @@ -6387,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); @@ -6395,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; } @@ -6896,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 */ @@ -6906,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) ); @@ -7257,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 @@ -7555,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; @@ -7640,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; @@ -7650,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 */ @@ -8114,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) { @@ -8426,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; @@ -8593,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: @@ -8687,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; @@ -8723,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; @@ -8883,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; @@ -9254,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); @@ -9272,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(); } @@ -9378,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(); } @@ -9605,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"); } @@ -9835,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; } @@ -9888,17 +9920,9 @@ SaveGamePGN(f) /* [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 { @@ -9938,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 */ @@ -10027,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); @@ -10068,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); @@ -10645,7 +10670,7 @@ MachineWhiteEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (!WhiteOnMove(currentMove)) { DisplayError(_("It is not White's turn"), 0); @@ -10726,7 +10751,7 @@ MachineBlackEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (WhiteOnMove(currentMove)) { DisplayError(_("It is not Black's turn"), 0); @@ -10833,7 +10858,7 @@ TwoMachinesEvent P((void)) if (gameMode != EditGame) return; break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10844,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) { @@ -10925,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); } } @@ -10969,7 +10997,7 @@ IcsClientEvent() break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: @@ -11010,7 +11038,7 @@ EditGameEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -11117,32 +11145,30 @@ ExitAnalyzeMode() } 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); @@ -11963,7 +11993,7 @@ BookEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case TwoMachinesPlay: return; @@ -12068,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; @@ -12120,6 +12158,8 @@ SetGameInfo() gameInfo.round = StrSave("-"); gameInfo.white = StrSave("-"); gameInfo.black = StrSave("-"); + gameInfo.result = r; + gameInfo.resultDetails = p; break; case EditPosition: @@ -12169,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 @@ -12191,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); @@ -12210,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"); } } @@ -12264,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; } @@ -12313,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; } @@ -12342,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); } @@ -12373,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); @@ -12786,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 == '=') { @@ -12827,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) { @@ -12850,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) { @@ -12869,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) { @@ -12960,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); } @@ -13070,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; /* @@ -13196,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; @@ -13325,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); @@ -13624,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 */ @@ -13680,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++ = '-'; @@ -13699,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; @@ -13747,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; @@ -13829,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++; @@ -13847,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; } } @@ -13956,14 +14034,8 @@ 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= 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