X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c2dea7c0355f5575b8fd6505981a5e0051612755;hb=24f3259ce1a69c28c67f8e29eb32a18e43ab7f8c;hp=0bbe876c9aeb5c2ba47a835fa3de4e6210c8cf9b;hpb=b62cf5e6c96ca7a64c184b6218c18eb045142a7b;p=xboard.git diff --git a/backend.c b/backend.c index 0bbe876..c2dea7c 100755 --- a/backend.c +++ b/backend.c @@ -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)); @@ -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) { @@ -1903,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; @@ -1931,7 +1934,6 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } - } @@ -1939,6 +1941,7 @@ void VariantSwitch(Board board, VariantClass newVariant) { int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; + Board oldBoard; startedFromPositionFile = FALSE; if(gameInfo.variant == newVariant) return; @@ -1976,7 +1979,7 @@ VariantSwitch(Board board, VariantClass newVariant) case VariantSuper: newHoldingsWidth = 2; gameInfo.holdingsSize = 8; - return; + break; case VariantGothic: case VariantCapablanca: case VariantCapaRandom: @@ -2010,10 +2013,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; @@ -2022,7 +2025,7 @@ static int loggedOn = FALSE; int gs_gamenum; char gs_kind[MSG_SIZ]; static char player1Name[128] = ""; -static char player2Name[128] = ""; +static char player2Name[128] = ""; static char cont_seq[] = "\n\\ "; static int player1Rating = -1; static int player2Rating = -1; @@ -2055,9 +2058,9 @@ read_from_ics(isr, closure, data, count, error) static char buf[BUF_SIZE + 1]; static int firstTime = TRUE, intfSet = FALSE; static ColorClass prevColor = ColorNormal; - static int savingComment = FALSE; - static int cmatch = 0; // continuation sequence match - char *bp; + static int savingComment = FALSE; + static int cmatch = 0; // continuation sequence match + char *bp; char str[500]; int i, oldi; int buf_len; @@ -2088,53 +2091,67 @@ read_from_ics(isr, closure, data, count, error) buf[i] = buf[leftover_start + i]; } - /* copy new characters into the buffer */ - bp = buf + leftover_len; - buf_len=leftover_len; - for (i=0; i 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); @@ -3532,7 +3580,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)); @@ -3612,6 +3660,7 @@ ParseBoard12(string) } } CopyBoard(boards[moveNum], board); + boards[moveNum][BOARD_SIZE-1][BOARD_SIZE-2] = 0; // [HGM] indicate holdings not set if (moveNum == 0) { startedFromSetupPosition = !CompareBoards(board, initialPosition); @@ -3678,15 +3727,6 @@ ParseBoard12(string) /* Update currentMove and known move number limits */ newMove = newGame || moveNum > 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) { @@ -3699,6 +3739,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; @@ -3709,12 +3763,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 */ @@ -4869,24 +4931,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; @@ -4895,7 +4962,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 @@ -5038,29 +5160,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! @@ -5179,6 +5278,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; @@ -5207,7 +5310,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return ImpossibleMove; } } -if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar); + return moveType; /* [HGM] in stead of calling FinishMove directly, this function is made into one that returns an OK move type if FinishMove @@ -5226,7 +5329,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))); @@ -5241,13 +5344,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 @@ -5256,7 +5358,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) { @@ -5330,7 +5432,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 || @@ -5423,6 +5525,191 @@ if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", move FinishMove(moveType, fromX, fromY, toX, toY, promoChar); } +void LeftClick(ClickType clickType, int xPix, int yPix) +{ + int x, y; + Boolean saveAnimate; + static int second = 0, promotionChoice = 0; + char promoChoice = NULLCHAR; + + if (clickType == Press) ErrorPopDown(); + + x = EventToSquare(xPix, BOARD_WIDTH); + y = EventToSquare(yPix, BOARD_HEIGHT); + if (!flipView && y >= 0) { + y = BOARD_HEIGHT - 1 - y; + } + if (flipView && x >= 0) { + x = BOARD_WIDTH - 1 - x; + } + + if(promotionChoice) { // we are waiting for a click to indicate promotion piece + if(clickType == Release) return; // ignore upclick of click-click destination + promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel + if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y); + if(gameInfo.holdingsWidth && + (WhiteOnMove(currentMove) + ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0 + : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) { + // click in right holdings, for determining promotion piece + ChessSquare p = boards[currentMove][y][x]; + if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p); + if(p != EmptySquare) { + FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p))); + fromX = fromY = -1; + return; + } + } + DrawPosition(FALSE, boards[currentMove]); + return; + } + + /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */ + if(clickType == Press + && ( x == BOARD_LEFT-1 || x == BOARD_RGHT + || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize + || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) ) + return; + + if (fromX == -1) { + if (clickType == Press) { + /* First square */ + if (OKToStartUserMove(x, y)) { + fromX = x; + fromY = y; + second = 0; + DragPieceBegin(xPix, yPix); + if (appData.highlightDragging) { + SetHighlights(x, y, -1, -1); + } + } + } + return; + } + + /* fromX != -1 */ + if (clickType == Press && gameMode != EditPosition) { + ChessSquare fromP; + ChessSquare toP; + int frc; + + // ignore off-board to clicks + if(y < 0 || x < 0) return; + + /* Check if clicking again on the same color piece */ + fromP = boards[currentMove][fromY][fromX]; + toP = boards[currentMove][y][x]; + frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom; + if ((WhitePawn <= fromP && fromP <= WhiteKing && + WhitePawn <= toP && toP <= WhiteKing && + !(fromP == WhiteKing && toP == WhiteRook && frc) && + !(fromP == WhiteRook && toP == WhiteKing && frc)) || + (BlackPawn <= fromP && fromP <= BlackKing && + BlackPawn <= toP && toP <= BlackKing && + !(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling + !(fromP == BlackKing && toP == BlackRook && frc))) { + /* Clicked again on same color piece -- changed his mind */ + second = (x == fromX && y == fromY); + if (appData.highlightDragging) { + SetHighlights(x, y, -1, -1); + } else { + ClearHighlights(); + } + if (OKToStartUserMove(x, y)) { + fromX = x; + fromY = y; + DragPieceBegin(xPix, yPix); + } + return; + } + // ignore clicks on holdings + if(x < BOARD_LEFT || x >= BOARD_RGHT) return; + } + + if (clickType == Release && x == fromX && y == fromY) { + DragPieceEnd(xPix, yPix); + if (appData.animateDragging) { + /* Undo animation damage if any */ + DrawPosition(FALSE, NULL); + } + if (second) { + /* Second up/down in same square; just abort move */ + second = 0; + fromX = fromY = -1; + ClearHighlights(); + gotPremove = 0; + ClearPremoveHighlights(); + } else { + /* First upclick in same square; start click-click mode */ + SetHighlights(x, y, -1, -1); + } + return; + } + + /* we now have a different from- and (possibly off-board) to-square */ + /* Completed move */ + toX = x; + toY = y; + saveAnimate = appData.animate; + if (clickType == Press) { + /* Finish clickclick move */ + if (appData.animate || appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } + } else { + /* Finish drag move */ + if (appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } + DragPieceEnd(xPix, yPix); + /* Don't animate move and drag both */ + appData.animate = FALSE; + } + + // moves into holding are invalid for now (later perhaps allow in EditPosition) + if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) { + ClearHighlights(); + fromX = fromY = -1; + DrawPosition(TRUE, NULL); + return; + } + + // off-board moves should not be highlighted + if(x < 0 || x < 0) ClearHighlights(); + + if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) { + SetHighlights(fromX, fromY, toX, toY); + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + // [HGM] super: promotion to captured piece selected from holdings + ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; + promotionChoice = TRUE; + // kludge follows to temporarily execute move on display, without promoting yet + boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank + boards[currentMove][toY][toX] = p; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][fromY][fromX] = p; // take back, but display stays + boards[currentMove][toY][toX] = q; + DisplayMessage("Click in holdings to choose piece", ""); + return; + } + PromotionPopUp(); + } else { + UserMoveEvent(fromX, fromY, toX, toY, promoChoice); + if (!appData.highlightLastMove || gotPremove) ClearHighlights(); + if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); + fromX = fromY = -1; + } + appData.animate = saveAnimate; + if (appData.animate || appData.animateDragging) { + /* Undo animation damage if needed */ + DrawPosition(FALSE, NULL); + } +} + void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats ) { // char * hint = lastHint; @@ -5937,15 +6224,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } else moveCount = 6; } } - - if (appData.debugMode) { int i; - fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n", - forwardMostMove, backwardMostMove, epStatus[backwardMostMove], - appData.drawRepeats); - for( i=forwardMostMove; i>=backwardMostMove; i-- ) - fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]); - - } /* Check for rep-draws */ count = 0; @@ -6162,7 +6440,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; @@ -6646,7 +6924,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* [AS] Negate score if machine is playing black and reporting absolute scores */ if( cps->scoreIsAbsolute && - ((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; } @@ -6663,9 +6946,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if(cps->nps == 0) ticklen = 10*time; // use engine reported time else ticklen = (1000. * u64ToDouble(nodes)) / cps->nps; // convert node count to time - if(WhiteOnMove(forwardMostMove)) + if(WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w')) whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen; - else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen; + if(!WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) + blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen; } /* Buffer overflow protection */ @@ -6673,8 +6959,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) ); @@ -7281,14 +7567,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 @@ -7322,7 +7614,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; @@ -7700,7 +7991,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; @@ -9655,17 +9946,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 { @@ -9794,7 +10077,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); @@ -9834,7 +10117,8 @@ SavePosition(f, dummy, dummy2) { time_t tm; char *fen; - + + if (gameMode == EditPosition) EditPositionDone(TRUE); if (appData.oldSaveStyle) { tm = time((time_t *) NULL); @@ -10412,7 +10696,7 @@ MachineWhiteEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (!WhiteOnMove(currentMove)) { DisplayError(_("It is not White's turn"), 0); @@ -10493,7 +10777,7 @@ MachineBlackEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (WhiteOnMove(currentMove)) { DisplayError(_("It is not Black's turn"), 0); @@ -10600,7 +10884,7 @@ TwoMachinesEvent P((void)) if (gameMode != EditGame) return; break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10692,7 +10976,9 @@ TwoMachinesEvent P((void)) strcpy(bookMove, "move "); strcat(bookMove, bookHit); - HandleMachineMove(bookMove, &first); + savedMessage = bookMove; // args for deferred call + savedState = onmove; + ScheduleDelayedEvent(DeferredBookMove, 1); } } @@ -10736,7 +11022,7 @@ IcsClientEvent() break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: @@ -10777,7 +11063,7 @@ EditGameEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -10884,21 +11170,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(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; - 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; + if(fakeRights) + { /* don't do this if we just pasted FEN */ + castlingRights[0][2] = castlingRights[0][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; + 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; + } SendToProgram("force\n", &first); if (blackPlaysFirst) { strcpy(moveList[0], ""); @@ -11535,7 +11830,7 @@ void ToStartEvent() { if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) { - /* to optimze, we temporarily turn off analysis mode while we undo + /* to optimize, we temporarily turn off analysis mode while we undo * all the moves. Otherwise we get analysis output after each undo. */ if (first.analysisSupport) { @@ -11730,7 +12025,7 @@ BookEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case TwoMachinesPlay: return; @@ -12115,7 +12410,7 @@ SendToProgram(message, cps) } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } DisplayFatalError(buf, error, 1); } @@ -12146,7 +12441,7 @@ ReceiveFromProgram(isr, closure, message, count, error) } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } RemoveInputSource(cps->isr); DisplayFatalError(buf, 0, 1); @@ -12553,7 +12848,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 == '=') { @@ -12594,7 +12889,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) { @@ -12617,7 +12912,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) { @@ -12636,7 +12931,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) { @@ -12727,26 +13022,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); } @@ -13730,9 +14022,116 @@ EditPositionPasteFEN(char *fen) for( i=0; i 0) && (len < sizeof(cseq)); + if (ret) + strcpy(cseq, new_seq); + else if (appData.debugMode) + fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %u)\n", new_seq, (unsigned) sizeof(cseq)-1); + return ret; +} + +/* + reformat a source message so words don't cross the width boundary. internal + newlines are not removed. returns the wrapped size (no null character unless + included in source message). If dest is NULL, only calculate the size required + for the dest buffer. lp argument indicats line position upon entry, and it's + passed back upon exit. +*/ +int wrap(char *dest, char *src, int count, int width, int *lp) +{ + int len, i, ansi, cseq_len, line, old_line, old_i, old_len, clen; + + cseq_len = strlen(cseq); + old_line = line = *lp; + ansi = len = clen = 0; + + for (i=0; i < count; i++) + { + if (src[i] == '\033') + ansi = 1; + + // if we hit the width, back up + if (!ansi && (line >= width) && src[i] != '\n' && src[i] != ' ') + { + // store i & len in case the word is too long + old_i = i, old_len = len; + + // find the end of the last word + while (i && src[i] != ' ' && src[i] != '\n') + { + i--; + len--; + } + + // word too long? restore i & len before splitting it + if ((old_i-i+clen) >= width) + { + i = old_i; + len = old_len; + } + + // extra space? + if (i && src[i-1] == ' ') + len--; + + if (src[i] != ' ' && src[i] != '\n') + { + i--; + if (len) + len--; + } + + // now append the newline and continuation sequence + if (dest) + dest[len] = '\n'; + len++; + if (dest) + strncpy(dest+len, cseq, cseq_len); + len += cseq_len; + line = cseq_len; + clen = cseq_len; + continue; + } + + if (dest) + dest[len] = src[i]; + len++; + if (!ansi) + line++; + if (src[i] == '\n') + line = 0; + if (src[i] == 'm') + ansi = 0; + } + if (dest && appData.debugMode) + { + fprintf(debugFP, "wrap(count:%d,width:%d,line:%d,len:%d,*lp:%d,src: ", + count, width, line, len, *lp); + show_bytes(debugFP, src, count); + fprintf(debugFP, "\ndest: "); + show_bytes(debugFP, dest, len); + fprintf(debugFP, "\n"); + } + *lp = dest ? line : old_line; + + return len; +}