X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=07df9ce10b050c624ee1d933b6e6013841dabc00;hb=42747fc80391054f0b2c4794645a1db73984b842;hp=67a1c1a0770e2c7a87029ef4ad62f36ab10109db;hpb=e80c98c04e951e5026a24531cd6316be962636b9;p=xboard.git diff --git a/backend.c b/backend.c index 67a1c1a..07df9ce 100644 --- a/backend.c +++ b/backend.c @@ -449,7 +449,12 @@ int adjudicateLossPlies = 6; char white_holding[64], black_holding[64]; TimeMark lastNodeCountTime; long lastNodeCount=0; +int shiftKey; // [HGM] set by mouse handler + int have_sent_ICS_logon = 0; +int sending_ICS_login = 0; +int sending_ICS_password = 0; + int movesPerSession; int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */ long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack; @@ -1023,6 +1028,13 @@ int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *i if(**str == '!') type = *(*str)++; // Bronstein TC if(result = NextIntegerFromString( str, &temp2)) return -1; *inc = temp2 * 1000; + if(**str == '.') { // read fraction of increment + char *start = ++(*str); + if(result = NextIntegerFromString( str, &temp2)) return -1; + temp2 *= 1000; + while(start++ < *str) temp2 /= 10; + *inc += temp2; + } } else *inc = 0; *moves = 0; *tc = temp * 1000; *incType = type; return 0; @@ -1065,14 +1077,13 @@ int GetTimeQuota(int movenr, int lastUsed, char *tcString) int ParseTimeControl(tc, ti, mps) char *tc; - int ti; + float ti; int mps; { long tc1; long tc2; char buf[MSG_SIZ], buf2[MSG_SIZ], *mytc = tc; int min, sec=0; - int len; if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0; if(!strchr(tc, '+') && !strchr(tc, '/') && sscanf(tc, "%d:%d", &min, &sec) >= 1) @@ -1080,17 +1091,17 @@ ParseTimeControl(tc, ti, mps) if(ti > 0) { if(mps) - snprintf(buf, MSG_SIZ, ":%d/%s+%d", mps, mytc, ti); - else - snprintf(buf, MSG_SIZ, ":%s+%d", mytc, ti); + snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti); + else + snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti); } else { if(mps) snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc); - else + else snprintf(buf, MSG_SIZ, ":%s", mytc); } fullTimeControlString = StrSave(buf); // this should now be in PGN format - + if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) { return FALSE; } @@ -3108,8 +3119,23 @@ read_from_ics(isr, closure, data, count, error) if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) { ICSInitScript(); have_sent_ICS_logon = 1; + /* if we don't send the login/password via icsLogon, use special readline + code for it */ + if (strlen(appData.icsLogon)==0) + { + sending_ICS_password = 0; // in case we come back to login + sending_ICS_login = 1; + }; continue; } + /* need to shadow the password */ + if (!sending_ICS_password && looking_at(buf, &i, "password:")) { + /* if we don't send the login/password via icsLogon, use special readline + code for it */ + if (strlen(appData.icsLogon)==0) + sending_ICS_password = 1; + continue; + } if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && (looking_at(buf, &i, "\n<12> ") || @@ -3538,7 +3564,7 @@ read_from_ics(isr, closure, data, count, error) /* [4] is " *" or empty (don't care). */ int gamenum = atoi(star_match[0]); char *whitename, *blackname, *why, *endtoken; - ChessMove endtype = (ChessMove) 0; + ChessMove endtype = EndOfFile; if (tkind == 0) { whitename = star_match[1]; @@ -3982,7 +4008,7 @@ ParseBoard12(string) } if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files || - weird && (int)gameInfo.variant <= (int)VariantShogi) { + weird && (int)gameInfo.variant < (int)VariantShogi) { /* [HGM] We seem to have switched variant unexpectedly * Try to guess new variant from board size */ @@ -4311,6 +4337,8 @@ ParseBoard12(string) // So we parse the long-algebraic move string in stead of the SAN move int valid; char buf[MSG_SIZ], *prom; + if(gameInfo.variant == VariantShogi && !strchr(move_str, '=') && !strchr(move_str, '@')) + strcat(move_str, "="); // if ICS does not say 'promote' on non-drop, we defer. // str looks something like "Q/a1-a2"; kill the slash if(str[1] == '/') snprintf(buf, MSG_SIZ,"%c%s", str[0], str+2); @@ -4349,8 +4377,24 @@ ParseBoard12(string) strcat(parseList[moveNum - 1], " "); strcat(parseList[moveNum - 1], elapsed_time); /* currentMoveString is set as a side-effect of ParseOneMove */ + if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '^'; safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0])); strcat(moveList[moveNum - 1], "\n"); + + if(gameInfo.holdingsWidth && !appData.disguise) // inherit info that ICS does not give from previous board + for(k=0; k= BlackPawn && old < BlackCannon) + boards[moveNum][k][j] = PROMOTED old; // choose correct type of Gold in promotion + else boards[moveNum][k][j] = old; // preserve type of Gold + } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!) + boards[moveNum][k][j] = PROMOTED new; // use non-primordial representation of chosen piece + } } else { /* Move from ICS was illegal!? Punt. */ if (appData.debugMode) { @@ -4640,7 +4684,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar) break; case WhiteNonPromotion: case BlackNonPromotion: - sprintf(user_move, "%c%c%c%c=\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY); + sprintf(user_move, "%c%c%c%c==\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY); break; case WhitePromotion: case BlackPromotion: @@ -4659,14 +4703,16 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar) break; case WhiteDrop: case BlackDrop: + drop: snprintf(user_move, MSG_SIZ, "%c@%c%c\n", - ToUpper(PieceToChar((ChessSquare) fromX)), - AAA + toX, ONE + toY); + ToUpper(PieceToChar((ChessSquare) fromX)), + AAA + toX, ONE + toY); break; + case IllegalMove: /* could be a variant we don't quite understand */ + if(fromY == DROP_RANK) goto drop; // We need 'IllegalDrop' move type? case NormalMove: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: - case IllegalMove: /* could be a variant we don't quite understand */ snprintf(user_move, MSG_SIZ,"%c%c%c%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY); break; @@ -4838,9 +4884,6 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) int *fromX, *fromY, *toX, *toY; char *promoChar; { - if (appData.debugMode) { - fprintf(debugFP, "move to parse: %s\n", move); - } *moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr); switch (*moveType) { @@ -4899,7 +4942,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) case AmbiguousMove: case ImpossibleMove: - case (ChessMove) 0: /* end of file */ + case EndOfFile: case ElapsedTime: case Comment: case PGNTag: @@ -5675,6 +5718,11 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) *promoChoice = PieceToChar(BlackFerz); // no choice return FALSE; } + // no sense asking what we must promote to if it is going to explode... + if(gameInfo.variant == VariantAtomic && boards[currentMove][toY][toX] != EmptySquare) { + *promoChoice = PieceToChar(BlackQueen); // Queen as good as any + return FALSE; + } if(autoQueen) { // predetermined if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers) *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want @@ -5687,7 +5735,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) gameMode == IcsPlayingBlack && WhiteOnMove(currentMove); if(appData.testLegality && !premove) { moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), - fromY, fromX, toY, toX, NULLCHAR); + fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR); if(moveType != WhitePromotion && moveType != BlackPromotion) return FALSE; } @@ -5829,6 +5877,8 @@ OnlyMove(int *x, int *y, Boolean captures) { case IcsPlayingBlack: if(WhiteOnMove(currentMove)) return FALSE; break; + case EditGame: + break; default: return FALSE; } @@ -5875,7 +5925,7 @@ FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL; int lastLoadGameNumber = 0, lastLoadPositionNumber = 0; int lastLoadGameUseList = FALSE; char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ]; -ChessMove lastLoadGameStart = (ChessMove) 0; +ChessMove lastLoadGameStart = EndOfFile; void UserMoveEvent(fromX, fromY, toX, toY, promoChar) @@ -6022,15 +6072,15 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) pup = boards[currentMove][toY][toX]; /* [HGM] If move started in holdings, it means a drop. Convert to standard form */ - if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { + if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { if( pup != EmptySquare ) return; moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop; - if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", + if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]); // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings - while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++; + while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++; fromY = DROP_RANK; } @@ -6115,7 +6165,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) the previous line in Analysis Mode */ if ((gameMode == AnalyzeMode || gameMode == EditGame) && currentMove < forwardMostMove) { - PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game + if(appData.variations && shiftKey) PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game + else forwardMostMove = currentMove; } /* If we need the chess program but it's dead, restart it */ @@ -6265,6 +6316,20 @@ MarkTargetSquares(int clear) DrawPosition(TRUE, NULL); } +int +Explode(Board board, int fromX, int fromY, int toX, int toY) +{ + if(gameInfo.variant == VariantAtomic && + (board[toY][toX] != EmptySquare || // capture? + toX != fromX && (board[fromY][fromX] == WhitePawn || // e.p. ? + board[fromY][fromX] == BlackPawn ) + )) { + AnimateAtomicCapture(board, fromX, fromY, toX, toY); + return TRUE; + } + return FALSE; +} + void LeftClick(ClickType clickType, int xPix, int yPix) { int x, y; @@ -6475,9 +6540,13 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } PromotionPopUp(); } else { + int oldMove = currentMove; UserMoveEvent(fromX, fromY, toX, toY, promoChoice); if (!appData.highlightLastMove || gotPremove) ClearHighlights(); if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); + if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed + Explode(boards[currentMove-1], fromX, fromY, toX, toY)) + DrawPosition(TRUE, boards[currentMove]); fromX = fromY = -1; } appData.animate = saveAnimate; @@ -6803,7 +6872,7 @@ Adjudicate(ChessProgramState *cps) case EP_WINS: result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break; default: - result = (ChessMove) 0; + result = EndOfFile; } if(canAdjudicate && appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested if(engineOpponent) @@ -8208,7 +8277,7 @@ ParseGameHistory(game) } DisplayError(buf, 0); return; - case (ChessMove) 0: /* end of file */ + case EndOfFile: if (boardIndex < backwardMostMove) { /* Oops, gap. How did that happen? */ DisplayError(_("Gap in move list"), 0); @@ -8309,10 +8378,6 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) if( board[toY][toX] != EmptySquare ) board[EP_STATUS] = EP_CAPTURE; - /* [HGM] In Shatranj and Courier all promotions are to Ferz */ - if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk) - && promoChar != 0) promoChar = PieceToChar(WhiteFerz); - if (fromY == DROP_RANK) { /* must be first */ piece = board[toY][toX] = (ChessSquare) fromX; @@ -8575,11 +8640,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */ board[toY][toX] = (ChessSquare) (PROMOTED piece); - } else if(!appData.testLegality) { // without legality testing, unconditionally believe promoChar - board[toY][toX] = CharToPiece(promoChar); + } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar + board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); } - if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) - && promoChar != NULLCHAR && gameInfo.holdingsSize) { + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + && promoChar != NULLCHAR && gameInfo.holdingsSize) { // [HGM] superchess: take promotion piece out of holdings int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); if((int)piece < (int)BlackPawn) { // determine stm from piece color @@ -9466,7 +9531,7 @@ Reset(redraw, init) gotPremove = FALSE; alarmSounded = FALSE; - GameEnds((ChessMove) 0, NULL, GE_PLAYER); + GameEnds(EndOfFile, NULL, GE_PLAYER); if(appData.serverMovesName != NULL) { /* [HGM] prepare to make moves file for broadcasting */ clock_t t = clock(); @@ -9591,7 +9656,7 @@ LoadGameOneMove(readAhead) } yyboardindex = forwardMostMove; - if (readAhead != (ChessMove)0) { + if (readAhead != EndOfFile) { moveType = readAhead; } else { if (gameFileFP == NULL) @@ -9679,7 +9744,7 @@ LoadGameOneMove(readAhead) } break; - case (ChessMove) 0: /* end of file */ + case EndOfFile: if (appData.debugMode) fprintf(debugFP, "Parser hit end of file\n"); switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) { @@ -9707,7 +9772,7 @@ LoadGameOneMove(readAhead) if (appData.debugMode) fprintf(debugFP, "Parser ignoring: '%s' (%d)\n", yy_text, (int) moveType); - return LoadGameOneMove((ChessMove)0); /* tail recursion */ + return LoadGameOneMove(EndOfFile); /* tail recursion */ } /* else fall thru */ @@ -9742,7 +9807,7 @@ LoadGameOneMove(readAhead) if (appData.debugMode) fprintf(debugFP, "Parser ignoring: '%s' (%d)\n", yy_text, (int) moveType); - return LoadGameOneMove((ChessMove)0); /* tail recursion */ + return LoadGameOneMove(EndOfFile); /* tail recursion */ case IllegalMove: if (appData.testLegality) { @@ -10088,12 +10153,12 @@ LoadGame(f, gameNumber, title, useList) * 5-4-02: Let's try being more lenient and allowing a game to * start with an unnumbered move. Does that break anything? */ - cm = lastLoadGameStart = (ChessMove) 0; + cm = lastLoadGameStart = EndOfFile; while (gn > 0) { yyboardindex = forwardMostMove; cm = (ChessMove) yylex(); switch (cm) { - case (ChessMove) 0: + case EndOfFile: if (cmailMsgLoaded) { nCmailGames = CMAIL_MAX_GAMES - gn; } else { @@ -10115,7 +10180,7 @@ LoadGame(f, gameNumber, title, useList) case PGNTag: break; case MoveNumberOne: - case (ChessMove) 0: + case EndOfFile: gn--; /* count this game */ lastLoadGameStart = cm; break; @@ -10130,7 +10195,7 @@ LoadGame(f, gameNumber, title, useList) case GNUChessGame: case PGNTag: case MoveNumberOne: - case (ChessMove) 0: + case EndOfFile: gn--; /* count this game */ lastLoadGameStart = cm; break; @@ -10165,7 +10230,7 @@ LoadGame(f, gameNumber, title, useList) case NormalMove: /* Only a NormalMove can be at the start of a game * without a position diagram. */ - if (lastLoadGameStart == (ChessMove) 0) { + if (lastLoadGameStart == EndOfFile ) { gn--; lastLoadGameStart = MoveNumberOne; } @@ -10185,10 +10250,10 @@ LoadGame(f, gameNumber, title, useList) yyboardindex = forwardMostMove; cm = (ChessMove) yylex(); - if (cm == (ChessMove) 0 || + if (cm == EndOfFile || cm == GNUChessGame || cm == XBoardGame) { /* Empty game; pretend end-of-file and handle later */ - cm = (ChessMove) 0; + cm = EndOfFile; break; } @@ -10372,7 +10437,7 @@ LoadGame(f, gameNumber, title, useList) cm = (ChessMove) yylex(); } - if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) || + if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) || cm == WhiteWins || cm == BlackWins || cm == GameIsDrawn || cm == GameUnfinished) { DisplayMessage("", _("No moves in game")); @@ -10409,7 +10474,7 @@ LoadGame(f, gameNumber, title, useList) LoadGameOneMove(cm); /* load the remaining moves from the file */ - while (LoadGameOneMove((ChessMove)0)) { + while (LoadGameOneMove(EndOfFile)) { timeRemaining[0][forwardMostMove] = whiteTimeRemaining; timeRemaining[1][forwardMostMove] = blackTimeRemaining; } @@ -11992,7 +12057,7 @@ EditGameEvent() SendToProgram("force\n", &first); break; case TwoMachinesPlay: - GameEnds((ChessMove) 0, NULL, GE_PLAYER); + GameEnds(EndOfFile, NULL, GE_PLAYER); ResurrectChessProgram(); SetUserThinkingEnables(); break; @@ -13259,7 +13324,7 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces); } else { commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4... if(addBraces) - safeStrCpy(commentList[index], "{\n", sizeof(commentList[index])/sizeof(commentList[index][0])); + safeStrCpy(commentList[index], "{\n", 3); else commentList[index][0] = NULLCHAR; strcat(commentList[index], text); strcat(commentList[index], "\n"); @@ -13528,10 +13593,10 @@ SendTimeControl(cps, mps, tc, inc, sd, st) /* Note old gnuchess bug -- minutes:seconds used to not work. Fixed in later versions, but still avoid :seconds when seconds is 0. */ - snprintf(buf, MSG_SIZ, "level %d %ld %d\n", mps, tc/60000, inc/1000); + snprintf(buf, MSG_SIZ, "level %d %ld %g\n", mps, tc/60000, inc/1000.); } else { - snprintf(buf, MSG_SIZ, "level %d %ld:%02d %d\n", mps, tc/60000, - seconds, inc/1000); + snprintf(buf, MSG_SIZ, "level %d %ld:%02d %g\n", mps, tc/60000, + seconds, inc/1000.); } } SendToProgram(buf, cps); @@ -14287,7 +14352,7 @@ DecrementClocks() if (WhiteOnMove(forwardMostMove)) { if(whiteNPS >= 0) lastTickLength = 0; timeRemaining = whiteTimeRemaining -= lastTickLength; - if(timeRemaining < 0) { + if(timeRemaining < 0 && !appData.icsActive) { GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, 0, whiteTC); // sets suddenDeath & nextSession; if(suddenDeath) { // [HGM] if we run out of a non-last incremental session, go to the next whiteStartMove = forwardMostMove; whiteTC = nextSession; @@ -14299,7 +14364,7 @@ DecrementClocks() } else { if(blackNPS >= 0) lastTickLength = 0; timeRemaining = blackTimeRemaining -= lastTickLength; - if(timeRemaining < 0) { // [HGM] if we run out of a non-last incremental session, go to the next + if(timeRemaining < 0 && !appData.icsActive) { // [HGM] if we run out of a non-last incremental session, go to the next GetTimeQuota((forwardMostMove-blackStartMove-1)/2, 0, blackTC); if(suddenDeath) { blackStartMove = forwardMostMove; @@ -14864,12 +14929,12 @@ ParseFEN(board, blackPlaysFirst, fen) while(*p==' ') p++; if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') { if(*p == '[') p++; - if(*p == '-' ) *p++; /* empty holdings */ else { + if(*p == '-' ) p++; /* empty holdings */ else { if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */ /* if we would allow FEN reading to set board size, we would */ /* have to add holdings and shift the board read so far here */ while( (piece = CharToPiece(*p) ) != EmptySquare ) { - *p++; + p++; if((int) piece >= (int) BlackPawn ) { i = (int)piece - (int)BlackPawn; i = PieceToNumber((ChessSquare)i); @@ -14885,7 +14950,7 @@ ParseFEN(board, blackPlaysFirst, fen) } } } - if(*p == ']') *p++; + if(*p == ']') p++; } while(*p == ' ') p++; @@ -15020,7 +15085,7 @@ ParseFEN(board, blackPlaysFirst, fen) char c = *p++ - AAA; if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE; - if(*p >= '0' && *p <='9') *p++; + if(*p >= '0' && *p <='9') p++; board[EP_STATUS] = c; } } @@ -15219,11 +15284,11 @@ PopTail(Boolean annotate) if(annotate) { int cnt = 10; if(!WhiteOnMove(currentMove)) - snprintf(buf, sizeof(buf)/sizeof(buf[0]),"(%d...", currentMove+2>>1); + snprintf(buf, sizeof(buf)/sizeof(buf[0]),"(%d...", (currentMove+2)>>1); else safeStrCpy(buf, "(", sizeof(buf)/sizeof(buf[0])); for(i=currentMove; i>1, SavePart(parseList[i])); + snprintf(moveBuf, sizeof(moveBuf)/sizeof(moveBuf[0]), " %d. %s", (i+2)>>1, SavePart(parseList[i])); else snprintf(moveBuf, sizeof(moveBuf)/sizeof(moveBuf[0])," %s", SavePart(parseList[i])); strcat(buf, moveBuf); if(commentList[i]) { strcat(buf, " "); strcat(buf, commentList[i]); }