X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c1a08c998253d23ea05860d56124868727245426;hb=9a9bae3af89e8e0c983d963d1bfbff111adc117f;hp=7cd8dd7e8d3d13b754a4212149830447d011c421;hpb=8826788c164344668b480dc439d6fb98a864ae0b;p=xboard.git diff --git a/backend.c b/backend.c old mode 100755 new mode 100644 index 7cd8dd7..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; @@ -644,10 +655,10 @@ InitBackEnd1() { int i, j; - for( i=0; i= BlackPawn ) { @@ -1941,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; @@ -2012,9 +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 @@ -3258,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; } @@ -3303,7 +3318,7 @@ read_from_ics(isr, closure, data, count, error) /* [HGM] copy holdings to board holdings area */ CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn); CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn); - boards[forwardMostMove][BOARD_SIZE-1][BOARD_SIZE-2] = 1; // flag holdings as set + boards[forwardMostMove][HOLDINGS_SET] = 1; // flag holdings as set #if ZIPPY if (appData.zippyPlay && first.initDone) { ZippyHoldings(white_holding, black_holding, @@ -3327,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; @@ -3396,7 +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; + Boolean weird = FALSE, reqFlag = FALSE; fromX = fromY = toX = toY = -1; @@ -3426,27 +3442,6 @@ ParseBoard12(string) &moveNum, str, elapsed_time, move_str, &ics_flip, &ticking); - if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files || - weird && (int)gameInfo.variant <= (int)VariantShogi) { - /* [HGM] We seem to switch variant during a game! - * 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; - sprintf(str, "%smoves %d\n", ics_prefix, gamenum); - SendToICS(str); - } - } - if (n < 21) { snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string); DisplayError(str, 0); @@ -3456,7 +3451,7 @@ ParseBoard12(string) /* Convert the move number to internal form */ moveNum = (moveNum - 1) * 2; if (to_play == 'B') moveNum++; - if (moveNum >= MAX_MOVES) { + if (moveNum > framePtr) { // [HGM] vari: do not run into saved variations DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"), 0, 1); return; @@ -3523,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. */ @@ -3538,8 +3554,8 @@ ParseBoard12(string) 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); @@ -3575,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)); @@ -3655,7 +3671,7 @@ ParseBoard12(string) } } CopyBoard(boards[moveNum], board); - boards[moveNum][BOARD_SIZE-1][BOARD_SIZE-2] = 0; // [HGM] indicate holdings not set + boards[moveNum][HOLDINGS_SET] = 0; // [HGM] indicate holdings not set if (moveNum == 0) { startedFromSetupPosition = !CompareBoards(board, initialPosition); @@ -3678,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>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; @@ -4700,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; @@ -4805,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); @@ -4831,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 @@ -5324,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))); @@ -5339,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 @@ -5354,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) { @@ -5363,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); @@ -5394,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 */ @@ -5428,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 || @@ -5456,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; @@ -5670,7 +5678,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) { ClearHighlights(); fromX = fromY = -1; - DrawPosition(FALSE, NULL); + DrawPosition(TRUE, NULL); return; } @@ -5893,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, @@ -5921,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) { @@ -6026,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) { @@ -6088,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*/ @@ -6101,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*/ @@ -6112,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 @@ -6126,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; } } @@ -6147,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: @@ -6190,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 */ @@ -6223,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]); } @@ -6234,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 @@ -6266,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", @@ -6300,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 @@ -6331,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 @@ -6437,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); @@ -6448,9 +6453,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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; } @@ -6959,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) ); @@ -7310,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= 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; @@ -7708,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 */ @@ -8172,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) { @@ -8484,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; @@ -8651,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: @@ -8745,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; @@ -8781,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; @@ -8941,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; @@ -9312,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); @@ -9330,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(); } @@ -9436,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(); } @@ -9663,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"); } @@ -9893,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; } @@ -9946,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 { @@ -9996,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 */ @@ -10085,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); @@ -10126,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); @@ -10703,7 +10670,7 @@ MachineWhiteEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (!WhiteOnMove(currentMove)) { DisplayError(_("It is not White's turn"), 0); @@ -10784,7 +10751,7 @@ MachineBlackEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (WhiteOnMove(currentMove)) { DisplayError(_("It is not Black's turn"), 0); @@ -10891,7 +10858,7 @@ TwoMachinesEvent P((void)) if (gameMode != EditGame) return; break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10902,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) { @@ -11029,7 +10997,7 @@ IcsClientEvent() break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: @@ -11070,7 +11038,7 @@ EditGameEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -11177,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); @@ -12023,7 +11993,7 @@ BookEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case TwoMachinesPlay: return; @@ -12128,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; @@ -12180,6 +12158,8 @@ SetGameInfo() gameInfo.round = StrSave("-"); gameInfo.white = StrSave("-"); gameInfo.black = StrSave("-"); + gameInfo.result = r; + gameInfo.resultDetails = p; break; case EditPosition: @@ -12229,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 @@ -12251,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); @@ -12270,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"); } } @@ -12324,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; } @@ -12373,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; } @@ -12402,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); } @@ -12433,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); @@ -12846,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 == '=') { @@ -12887,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) { @@ -12910,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) { @@ -12929,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) { @@ -13020,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); } @@ -13130,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; /* @@ -13256,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; @@ -13385,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); @@ -13684,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 */ @@ -13740,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++ = '-'; @@ -13759,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; @@ -13807,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; @@ -13889,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++; @@ -13907,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; } } @@ -14016,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