X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=54f337d00de2de799d70409534434d1a9a7f6689;hb=3243b1f4122f27ef0360ee3f7b51de8088b6eba0;hp=a112d57e86d38f1408dbb6a0bdd25e0a45aa10f7;hpb=ca87afb87619efbaa94fe045670bc916e9ffe3ec;p=xboard.git diff --git a/backend.c b/backend.c old mode 100644 new mode 100755 index a112d57..54f337d --- a/backend.c +++ b/backend.c @@ -77,6 +77,7 @@ #if STDC_HEADERS # include # include +# include #else /* not STDC_HEADERS */ # if HAVE_STRING_H # include @@ -183,7 +184,6 @@ void FeedMovesToProgram P((ChessProgramState *cps, int upto)); void ResurrectChessProgram P((void)); void DisplayComment P((int moveNumber, char *text)); void DisplayMove P((int moveNumber)); -void DisplayAnalysis P((void)); void ParseGameHistory P((char *game)); void ParseBoard12 P((char *string)); @@ -607,7 +607,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; @@ -1042,6 +1042,7 @@ InitBackEnd2() fprintf(debugFP, "%s\n", programVersion); } + set_cont_sequence(appData.wrapContSeq); if (appData.matchGames > 0) { appData.matchMode = TRUE; } else if (appData.matchMode) { @@ -1423,12 +1424,14 @@ KeepAlive() /* added routine for printf style output to ics */ void ics_printf(char *format, ...) { - char buffer[MSG_SIZ], *args; - - args = (char *)&format + sizeof(format); - vsnprintf(buffer, sizeof(buffer), format, args); - buffer[sizeof(buffer)-1] = '\0'; - SendToICS(buffer); + char buffer[MSG_SIZ]; + va_list args; + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + buffer[sizeof(buffer)-1] = '\0'; + SendToICS(buffer); + va_end(args); } void @@ -1901,6 +1904,8 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) ChessSquare piece; if(gameInfo.holdingsWidth < 2) return; + if(gameInfo.variant != VariantBughouse && board[BOARD_SIZE-1][BOARD_SIZE-2]) + return; // prevent overwriting by pre-board holdings if( (int)lowestPiece >= BlackPawn ) { holdingsColumn = 0; @@ -1929,7 +1934,6 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } - } @@ -1937,8 +1941,6 @@ void VariantSwitch(Board board, VariantClass newVariant) { int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; - int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove; -// Board tempBoard; int saveCastling[BOARD_SIZE], saveEP; startedFromPositionFile = FALSE; if(gameInfo.variant == newVariant) return; @@ -1955,59 +1957,64 @@ VariantSwitch(Board board, VariantClass newVariant) * case we want to add those holdings to the already received position. */ - - if (appData.debugMode) { - fprintf(debugFP, "Switch board from %s to %s\n", - VariantName(gameInfo.variant), VariantName(newVariant)); - setbuf(debugFP, NULL); - } - shuffleOpenings = 0; /* [HGM] shuffle */ - gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */ - switch(newVariant) { - case VariantShogi: - newWidth = 9; newHeight = 9; - gameInfo.holdingsSize = 7; - case VariantBughouse: - case VariantCrazyhouse: - newHoldingsWidth = 2; break; - default: - newHoldingsWidth = gameInfo.holdingsSize = 0; - } - - if(newWidth != gameInfo.boardWidth || - newHeight != gameInfo.boardHeight || - newHoldingsWidth != gameInfo.holdingsWidth ) { - - /* shift position to new playing area, if needed */ - if(newHoldingsWidth > gameInfo.holdingsWidth) { - for(i=0; i=BOARD_LEFT; j--) - board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] = - board[i][j]; - for(i=0; i gameInfo.holdingsWidth) { + for(i=0; i=BOARD_LEFT; j--) + board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] = + board[i][j]; + for(i=0; i= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' && - buf[buf_len-3]==' ' && buf[buf_len-2]==' ' && buf[buf_len-1]==' ') { - buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous - if(buf_len == 0 || buf[buf_len-1] != ' ') - buf[buf_len++] = ' '; // add space (assumes ICS does not break lines within word) - } - } + /* copy new characters into the buffer */ + bp = buf + leftover_len; + buf_len=leftover_len; + for (i=0; i forwardMostMove; - /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */ - if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) { - takeback = forwardMostMove - moveNum; - for (i = 0; i < takeback; i++) { - if (appData.debugMode) fprintf(debugFP, "take back move\n"); - SendToProgram("undo\n", &first); - } - } - if (newGame) { forwardMostMove = backwardMostMove = currentMove = moveNum; if (gameMode == IcsExamining && moveNum == 0) { @@ -3660,6 +3734,20 @@ ParseBoard12(string) } } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) { +#if ZIPPY + /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */ + /* [HGM] applied this also to an engine that is silently watching */ + if (appData.zippyPlay && moveNum < forwardMostMove && first.initDone && + (gameMode == IcsObserving || gameMode == IcsExamining) && + gameInfo.variant == currentlyInitializedVariant) { + takeback = forwardMostMove - moveNum; + for (i = 0; i < takeback; i++) { + if (appData.debugMode) fprintf(debugFP, "take back move\n"); + SendToProgram("undo\n", &first); + } + } +#endif + forwardMostMove = moveNum; if (!pausing || currentMove > forwardMostMove) currentMove = forwardMostMove; @@ -3670,12 +3758,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 */ @@ -4830,24 +4926,29 @@ SendBoard(cps, moveNum) } int -IsPromotion(fromX, fromY, toX, toY) - int fromX, fromY, toX, toY; +HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) { + /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */ /* [HGM] add Shogi promotions */ int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn; ChessSquare piece; + ChessMove moveType; + Boolean premove; + + if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) return FALSE; // drop + if(toX < BOARD_LEFT || toX >= BOARD_RGHT) return FALSE; // move into holdings + + if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi || // no promotions + !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) // invalid move + return FALSE; - if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi || - !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE; - /* [HGM] Note to self: line above also weeds out drops */ piece = boards[currentMove][fromY][fromX]; if(gameInfo.variant == VariantShogi) { promotionZoneSize = 3; - highestPromotingPiece = (int)WhiteKing; - /* [HGM] Should be Silver = Ferz, really, but legality testing is off, - and if in normal chess we then allow promotion to King, why not - allow promotion of other piece in Shogi? */ + highestPromotingPiece = (int)WhiteFerz; } + + // next weed out all moves that do not touch the promotion zone at all if((int)piece >= BlackPawn) { if(toY >= promotionZoneSize && fromY >= promotionZoneSize) return FALSE; @@ -4856,7 +4957,62 @@ IsPromotion(fromX, fromY, toX, toY) if( toY < BOARD_HEIGHT - promotionZoneSize && fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE; } - return ( (int)piece <= highestPromotingPiece ); + + if( (int)piece > highestPromotingPiece ) return FALSE; // non-promoting piece + + // weed out mandatory Shogi promotions + if(gameInfo.variant == VariantShogi) { + if(piece >= BlackPawn) { + if(toY == 0 && piece == BlackPawn || + toY == 0 && piece == BlackQueen || + toY <= 1 && piece == BlackKnight) { + *promoChoice = '+'; + return FALSE; + } + } else { + if(toY == BOARD_HEIGHT-1 && piece == WhitePawn || + toY == BOARD_HEIGHT-1 && piece == WhiteQueen || + toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) { + *promoChoice = '+'; + return FALSE; + } + } + } + + // weed out obviously illegal Pawn moves + if(appData.testLegality && (piece == WhitePawn || piece == BlackPawn) ) { + if(toX > fromX+1 || toX < fromX-1) return FALSE; // wide + if(piece == WhitePawn && toY != fromY+1) return FALSE; // deep + if(piece == BlackPawn && toY != fromY-1) return FALSE; // deep + if(fromX != toX && gameInfo.variant == VariantShogi) return FALSE; + // note we are not allowed to test for valid (non-)capture, due to premove + } + + // we either have a choice what to promote to, or (in Shogi) whether to promote + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) { + *promoChoice = PieceToChar(BlackFerz); // no choice + return FALSE; + } + if(appData.alwaysPromoteToQueen) { // predetermined + if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers) + *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want + else *promoChoice = PieceToChar(BlackQueen); + return FALSE; + } + + // suppress promotion popup on illegal moves that are not premoves + premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) || + gameMode == IcsPlayingBlack && WhiteOnMove(currentMove); + if(appData.testLegality && !premove) { + moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), + epStatus[currentMove], castlingRights[currentMove], + fromY, fromX, toY, toX, NULLCHAR); + if(moveType != WhitePromotionQueen && moveType != BlackPromotionQueen && + moveType != WhitePromotionKnight && moveType != BlackPromotionKnight) + return FALSE; + } + + return TRUE; } int @@ -4999,29 +5155,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) ChessMove moveType; ChessSquare pdown, pup; - if (fromX < 0 || fromY < 0) return ImpossibleMove; - - /* [HGM] suppress all moves into holdings area and guard band */ - if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 ) - return ImpossibleMove; - - /* [HGM] moved to here from winboard.c */ - /* note: capture of own piece can be legal as drag-drop premove. For click-click it is selection of new piece. */ - pdown = boards[currentMove][fromY][fromX]; - pup = boards[currentMove][toY][toX]; - if ( gameMode != EditPosition && !captureOwn && - (WhitePawn <= pdown && pdown < BlackPawn && - WhitePawn <= pup && pup < BlackPawn || - BlackPawn <= pdown && pdown < EmptySquare && - BlackPawn <= pup && pup < EmptySquare - ) && !((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) && - (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0|| - pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 || - pup == WhiteKing && pdown == WhiteRook && fromY == 0 && toY == 0|| // also allow RxK - pup == BlackKing && pdown == BlackRook && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 ) - ) ) - return Comment; - /* Check if the user is playing in turn. This is complicated because we let the user "pick up" a piece before it is his turn. So the piece he tried to pick up may have been captured by the time he puts it down! @@ -5140,6 +5273,10 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return ImpossibleMove; } + if(toX < 0 || toY < 0) return ImpossibleMove; + pdown = boards[currentMove][fromY][fromX]; + pup = boards[currentMove][toY][toX]; + /* [HGM] If move started in holdings, it means a drop */ if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { if( pup != EmptySquare ) return ImpossibleMove; @@ -5384,6 +5521,190 @@ if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", move FinishMove(moveType, fromX, fromY, toX, toY, promoChar); } +void LeftClick(ClickType clickType, int xPix, int yPix) +{ + int x, y; + Boolean saveAnimate; + static int second = 0, promotionChoice = 0; + char promoChoice = NULLCHAR; + + if (clickType == Press) ErrorPopDown(); + + x = EventToSquare(xPix, BOARD_WIDTH); + y = EventToSquare(yPix, BOARD_HEIGHT); + if (!flipView && y >= 0) { + y = BOARD_HEIGHT - 1 - y; + } + if (flipView && x >= 0) { + x = BOARD_WIDTH - 1 - x; + } + + if(promotionChoice) { // we are waiting for a click to indicate promotion piece + if(clickType == Release) return; // ignore upclick of click-click destination + promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel + if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y); + if(gameInfo.holdingsWidth && + (WhiteOnMove(currentMove) + ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0 + : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) { + // click in right holdings, for determining promotion piece + ChessSquare p = boards[currentMove][y][x]; + if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p); + if(p != EmptySquare) { + FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p))); + fromX = fromY = -1; + return; + } + } + DrawPosition(FALSE, boards[currentMove]); + return; + } + + /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */ + if(clickType == Press + && ( x == BOARD_LEFT-1 || x == BOARD_RGHT + || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize + || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) ) + return; + + if (fromX == -1) { + if (clickType == Press) { + /* First square */ + if (OKToStartUserMove(x, y)) { + fromX = x; + fromY = y; + second = 0; + DragPieceBegin(xPix, yPix); + if (appData.highlightDragging) { + SetHighlights(x, y, -1, -1); + } + } + } + return; + } + + /* fromX != -1 */ + if (clickType == Press && gameMode != EditPosition) { + ChessSquare fromP; + ChessSquare toP; + int frc; + + // ignore off-board to clicks + if(y < 0 || x < 0) return; + + /* Check if clicking again on the same color piece */ + fromP = boards[currentMove][fromY][fromX]; + toP = boards[currentMove][y][x]; + frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom; + if ((WhitePawn <= fromP && fromP <= WhiteKing && + WhitePawn <= toP && toP <= WhiteKing && + !(fromP == WhiteKing && toP == WhiteRook && frc) && + !(fromP == WhiteRook && toP == WhiteKing && frc)) || + (BlackPawn <= fromP && fromP <= BlackKing && + BlackPawn <= toP && toP <= BlackKing && + !(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling + !(fromP == BlackKing && toP == BlackRook && frc))) { + /* Clicked again on same color piece -- changed his mind */ + second = (x == fromX && y == fromY); + if (appData.highlightDragging) { + SetHighlights(x, y, -1, -1); + } else { + ClearHighlights(); + } + if (OKToStartUserMove(x, y)) { + fromX = x; + fromY = y; + DragPieceBegin(xPix, yPix); + } + return; + } + // ignore clicks on holdings + if(x < BOARD_LEFT || x >= BOARD_RGHT) return; + } + + if (clickType == Release && x == fromX && y == fromY) { + DragPieceEnd(xPix, yPix); + if (appData.animateDragging) { + /* Undo animation damage if any */ + DrawPosition(FALSE, NULL); + } + if (second) { + /* Second up/down in same square; just abort move */ + second = 0; + fromX = fromY = -1; + ClearHighlights(); + gotPremove = 0; + ClearPremoveHighlights(); + } else { + /* First upclick in same square; start click-click mode */ + SetHighlights(x, y, -1, -1); + } + return; + } + + /* we now have a different from- and (possibly off-board) to-square */ + /* Completed move */ + toX = x; + toY = y; + saveAnimate = appData.animate; + if (clickType == Press) { + /* Finish clickclick move */ + if (appData.animate || appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } + } else { + /* Finish drag move */ + if (appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } + DragPieceEnd(xPix, yPix); + /* Don't animate move and drag both */ + appData.animate = FALSE; + } + + // moves into holding are invalid for now (later perhaps allow in EditPosition) + if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) { + ClearHighlights(); + fromX = fromY = -1; + return; + } + + // off-board moves should not be highlighted + if(x < 0 || x < 0) ClearHighlights(); + + if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) { + SetHighlights(fromX, fromY, toX, toY); + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + // [HGM] super: promotion to captured piece selected from holdings + ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; + promotionChoice = TRUE; + // kludge follows to temporarily execute move on display, without promoting yet + boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank + boards[currentMove][toY][toX] = p; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][fromY][fromX] = p; // take back, but display stays + boards[currentMove][toY][toX] = q; + DisplayMessage("Click in holdings to choose piece", ""); + return; + } + PromotionPopUp(); + } else { + UserMoveEvent(fromX, fromY, toX, toY, promoChoice); + if (!appData.highlightLastMove || gotPremove) ClearHighlights(); + if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); + fromX = fromY = -1; + } + appData.animate = saveAnimate; + if (appData.animate || appData.animateDragging) { + /* Undo animation damage if needed */ + DrawPosition(FALSE, NULL); + } +} + void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats ) { // char * hint = lastHint; @@ -6123,7 +6444,7 @@ 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; @@ -6685,7 +7006,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); - DisplayAnalysis(); } return; @@ -6713,7 +7033,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); - DisplayAnalysis(); } return; } else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s", @@ -6738,7 +7057,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. SendProgramStatsToFrontend( cps, &programStats ); - DisplayAnalysis(); return; } else if (strncmp(message,"++",2) == 0) { @@ -6774,7 +7092,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); - DisplayAnalysis(); } return; } @@ -7246,14 +7563,20 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) p = (int) fromX; if(p < (int) BlackPawn) { /* white drop */ p -= (int)WhitePawn; + p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) p = 0; - if(--board[p][BOARD_WIDTH-2] == 0) + if(--board[p][BOARD_WIDTH-2] <= 0) board[p][BOARD_WIDTH-1] = EmptySquare; + if((int)board[p][BOARD_WIDTH-2] < 0) + board[p][BOARD_WIDTH-2] = 0; } else { /* black drop */ p -= (int)BlackPawn; + p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) p = 0; - if(--board[BOARD_HEIGHT-1-p][1] == 0) + if(--board[BOARD_HEIGHT-1-p][1] <= 0) board[BOARD_HEIGHT-1-p][0] = EmptySquare; + if((int)board[BOARD_HEIGHT-1-p][1] < 0) + board[BOARD_HEIGHT-1-p][1] = 0; } } if (captured != EmptySquare && gameInfo.holdingsSize > 0 @@ -7287,7 +7610,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; @@ -7665,7 +7987,7 @@ TwoMachinesEventIfReady P((void)) void NextMatchGame P((void)) { - int index; /* [HGM] autoinc: step lod index during match */ + int index; /* [HGM] autoinc: step load index during match */ Reset(FALSE, TRUE); if (*appData.loadGameFile != NULLCHAR) { index = appData.loadGameIndex; @@ -9596,7 +9918,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, numtext); + fprintf(f, "%s", numtext); linelen += numlen; /* Get move */ @@ -9614,7 +9936,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, move_buffer); + fprintf(f, "%s", move_buffer); linelen += movelen; /* [AS] Add PV info if present */ @@ -9658,7 +9980,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, move_buffer); + fprintf(f, "%s", move_buffer); linelen += movelen; } @@ -10321,8 +10643,7 @@ AnalyzeModeEvent() first.analyzing = TRUE; /*first.maybeThinking = TRUE;*/ first.maybeThinking = FALSE; /* avoid killing GNU Chess */ - AnalysisPopUp(_("Analysis"), - _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.")); + EngineOutputPopUp(); } if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode; pausing = FALSE; @@ -10348,8 +10669,7 @@ AnalyzeFileEvent() first.analyzing = TRUE; /*first.maybeThinking = TRUE;*/ first.maybeThinking = FALSE; /* avoid killing GNU Chess */ - AnalysisPopUp(_("Analysis"), - _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.")); + EngineOutputPopUp(); } gameMode = AnalyzeFile; pausing = FALSE; @@ -10659,7 +10979,9 @@ TwoMachinesEvent P((void)) strcpy(bookMove, "move "); strcat(bookMove, bookHit); - HandleMachineMove(bookMove, &first); + savedMessage = bookMove; // args for deferred call + savedState = onmove; + ScheduleDelayedEvent(DeferredBookMove, 1); } } @@ -10847,7 +11169,6 @@ ExitAnalyzeMode() SendToProgram("exit\n", &first); first.analyzing = FALSE; } - AnalysisPopDown(); thinkOutput[0] = NULLCHAR; } @@ -12548,7 +12869,7 @@ PeriodicUpdatesEvent(newState) appData.periodicUpdates=newState; /* Display type changes, so update it now */ - DisplayAnalysis(); +// DisplayAnalysis(); /* Get the ball rolling again... */ if (newState) { @@ -12688,92 +13009,6 @@ DisplayMove(moveNumber) } void -DisplayAnalysisText(text) - char *text; -{ - char buf[MSG_SIZ]; - - if (gameMode == AnalyzeMode || gameMode == AnalyzeFile - || appData.icsEngineAnalyze) { - sprintf(buf, "Analysis (%s)", first.tidy); - AnalysisPopUp(buf, text); - } -} - -static int -only_one_move(str) - char *str; -{ - while (*str && isspace(*str)) ++str; - while (*str && !isspace(*str)) ++str; - if (!*str) return 1; - while (*str && isspace(*str)) ++str; - if (!*str) return 1; - return 0; -} - -void -DisplayAnalysis() -{ - char buf[MSG_SIZ]; - char lst[MSG_SIZ / 2]; - double nps; - static char *xtra[] = { "", " (--)", " (++)" }; - int h, m, s, cs; - - if (programStats.time == 0) { - programStats.time = 1; - } - - if (programStats.got_only_move) { - safeStrCpy(buf, programStats.movelist, sizeof(buf)); - } else { - safeStrCpy( lst, programStats.movelist, sizeof(lst)); - - nps = (u64ToDouble(programStats.nodes) / - ((double)programStats.time /100.0)); - - cs = programStats.time % 100; - s = programStats.time / 100; - h = (s / (60*60)); - s = s - h*60*60; - m = (s/60); - s = s - m*60; - - if (programStats.moves_left > 0 && appData.periodicUpdates) { - if (programStats.move_name[0] != NULLCHAR) { - sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", - programStats.depth, - programStats.nr_moves-programStats.moves_left, - programStats.nr_moves, programStats.move_name, - ((float)programStats.score)/100.0, lst, - only_one_move(lst)? - xtra[programStats.got_fail] : "", - (u64)programStats.nodes, (int)nps, h, m, s, cs); - } else { - sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", - programStats.depth, - programStats.nr_moves-programStats.moves_left, - programStats.nr_moves, ((float)programStats.score)/100.0, - lst, - only_one_move(lst)? - xtra[programStats.got_fail] : "", - (u64)programStats.nodes, (int)nps, h, m, s, cs); - } - } else { - sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", - programStats.depth, - ((float)programStats.score)/100.0, - lst, - only_one_move(lst)? - xtra[programStats.got_fail] : "", - (u64)programStats.nodes, (int)nps, h, m, s, cs); - } - } - DisplayAnalysisText(buf); -} - -void DisplayComment(moveNumber, text) int moveNumber; char *text; @@ -13790,3 +14025,110 @@ EditPositionPasteFEN(char *fen) } } } + +static char cseq[12] = "\\ "; + +Boolean set_cont_sequence(char *new_seq) +{ + int len; + Boolean ret; + + // handle bad attempts to set the sequence + if (!new_seq) + return 0; // acceptable error - no debug + + len = strlen(new_seq); + ret = (len > 0) && (len < sizeof(cseq)); + if (ret) + strcpy(cseq, new_seq); + else if (appData.debugMode) + fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %d)\n", new_seq, sizeof(cseq)-1); + return ret; +} + +/* + reformat a source message so words don't cross the width boundary. internal + newlines are not removed. returns the wrapped size (no null character unless + included in source message). If dest is NULL, only calculate the size required + for the dest buffer. lp argument indicats line position upon entry, and it's + passed back upon exit. +*/ +int wrap(char *dest, char *src, int count, int width, int *lp) +{ + int len, i, ansi, cseq_len, line, old_line, old_i, old_len, clen; + + cseq_len = strlen(cseq); + old_line = line = *lp; + ansi = len = clen = 0; + + for (i=0; i < count; i++) + { + if (src[i] == '\033') + ansi = 1; + + // if we hit the width, back up + if (!ansi && (line >= width) && src[i] != '\n' && src[i] != ' ') + { + // store i & len in case the word is too long + old_i = i, old_len = len; + + // find the end of the last word + while (i && src[i] != ' ' && src[i] != '\n') + { + i--; + len--; + } + + // word too long? restore i & len before splitting it + if ((old_i-i+clen) >= width) + { + i = old_i; + len = old_len; + } + + // extra space? + if (i && src[i-1] == ' ') + len--; + + if (src[i] != ' ' && src[i] != '\n') + { + i--; + if (len) + len--; + } + + // now append the newline and continuation sequence + if (dest) + dest[len] = '\n'; + len++; + if (dest) + strncpy(dest+len, cseq, cseq_len); + len += cseq_len; + line = cseq_len; + clen = cseq_len; + continue; + } + + if (dest) + dest[len] = src[i]; + len++; + if (!ansi) + line++; + if (src[i] == '\n') + line = 0; + if (src[i] == 'm') + ansi = 0; + } + if (dest && appData.debugMode) + { + fprintf(debugFP, "wrap(count:%d,width:%d,line:%d,len:%d,*lp:%d,src: ", + count, width, line, len, *lp); + show_bytes(debugFP, src, count); + fprintf(debugFP, "\ndest: "); + show_bytes(debugFP, dest, len); + fprintf(debugFP, "\n"); + } + *lp = dest ? line : old_line; + + return len; +}