X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=677e21cfb47bcb97df4eb98217e59cf5d5237f56;hb=a7db862b2f44d23b358a1319cd8a374cf88c24fd;hp=958c92eb10f8bb0e3199f3dbe00ac7427ebb035c;hpb=9c7cb89d6ae88edc97bc99e51fb6779ddf6e33b8;p=xboard.git diff --git a/backend.c b/backend.c index 958c92e..677e21c 100644 --- a/backend.c +++ b/backend.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -267,6 +267,9 @@ char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */ extern int chatCount; int chattingPartner; char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */ +ChessSquare pieceSweep = EmptySquare; +ChessSquare promoSweep = EmptySquare, defaultPromoChoice; +int promoDefaultAltered; /* States for ics_getting_history */ #define H_FALSE 0 @@ -318,11 +321,11 @@ safeStrCpy( char *dst, const char *src, size_t count ) assert( count > 0 ); for(i=0; i= 0) // channel broadcast; look if there is a chatbox for this channel for(p=0; p= '0' && chatPartner[p][0] <= '9' && channel == atoi(chatPartner[p])) { talker[0] = '['; strcat(talker, "] "); Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE); chattingPartner = p; break; @@ -3116,23 +3131,8 @@ 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> ") || @@ -3855,7 +3855,7 @@ ParseBoard12(string) int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback; int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count; char to_play, board_chars[200]; - char move_str[500], str[500], elapsed_time[500]; + char move_str[MSG_SIZ], str[MSG_SIZ], elapsed_time[MSG_SIZ]; char black[32], white[32]; Board board; int prevMove = currentMove; @@ -3896,7 +3896,7 @@ ParseBoard12(string) &ticking); if (n < 21) { - snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string); + snprintf(str, MSG_SIZ, _("Failed to parse board string:\n\"%s\""), string); DisplayError(str, 0); return; } @@ -4345,7 +4345,7 @@ ParseBoard12(string) if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!) if(appData.debugMode) fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf); - safeStrCpy(move_str, buf, sizeof(move_str)/sizeof(move_str[0])); + safeStrCpy(move_str, buf, MSG_SIZ); } valid = ParseOneMove(move_str, moveNum - 1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar) @@ -4378,7 +4378,8 @@ ParseBoard12(string) 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 + if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper + && gameInfo.variant != VariantGreat) // inherit info that ICS does not give from previous board for(k=0; k= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn; + if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare; + if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare; + do { + promoSweep -= step; + if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap + else if((int)promoSweep == -1) promoSweep = WhiteKing; + else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn; + else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing; + if(!step) step = 1; + } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || + appData.testLegality && (promoSweep == king || + gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep)); + ChangeDragPiece(promoSweep); +} + +int PromoScroll(int x, int y) +{ + int step = 0; + + if(promoSweep == EmptySquare || !appData.sweepSelect) return FALSE; + if(abs(x - lastX) < 15 && abs(y - lastY) < 15) return FALSE; + if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1; + if(!step) return FALSE; + lastX = x; lastY = y; + if((promoSweep < BlackPawn) == flipView) step = -step; + if(step > 0) selectFlag = 1; + if(!selectFlag) Sweep(step); + return FALSE; +} + +void +NextPiece(int step) +{ + ChessSquare piece = boards[currentMove][toY][toX]; + do { + pieceSweep -= step; + if(pieceSweep == EmptySquare) pieceSweep = WhitePawn; // wrap + if((int)pieceSweep == -1) pieceSweep = BlackKing; + if(!step) step = -1; + } while(PieceToChar(pieceSweep) == '.'); + boards[currentMove][toY][toX] = pieceSweep; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][toY][toX] = piece; +} /* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */ void AlphaRank(char *move, int n) @@ -5009,6 +5063,9 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, f moveList[endPV-1][1] = fromY + ONE; moveList[endPV-1][2] = toX + AAA; moveList[endPV-1][3] = toY + ONE; + moveList[endPV-1][4] = promoChar; + moveList[endPV-1][5] = NULLCHAR; + strncat(moveList[endPV-1], "\n", MOVE_LEN); if(storeComments) CoordsToAlgebraic(boards[endPV - 1], PosFlags(endPV - 1), @@ -5024,8 +5081,6 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, f DrawPosition(TRUE, boards[currentMove]); } -static int lastX, lastY; - Boolean LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end) { @@ -5071,13 +5126,17 @@ MovePV(int x, int y, int h) { // step through PV based on mouse coordinates (called on mouse move) int margin = h>>3, step = 0; - if(endPV < 0) return; // we must somehow check if right button is still down (might be released off board!) - if(y < margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = 1; else - if(y > h - margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = -1; else - if( y > lastY + 6 ) step = -1; else if(y < lastY - 6) step = 1; + if(endPV < 0 && pieceSweep == EmptySquare) return; // needed in XBoard because lastX/Y is shared :-( + if(abs(x - lastX) < 7 && abs(y - lastY) < 7) return; + if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1; if(!step) return; lastX = x; lastY = y; + + if(pieceSweep != EmptySquare) { NextPiece(step); return; } + if(endPV < 0) return; + if(y < margin) step = 1; else + if(y > h - margin) step = -1; if(currentMove + step > endPV || currentMove + step < forwardMostMove) step = 0; currentMove += step; if(currentMove == forwardMostMove) ClearPremoveHighlights(); else @@ -5310,8 +5369,8 @@ InitPosition(redraw) int i, j, pawnRow, overrule, oldx = gameInfo.boardWidth, oldy = gameInfo.boardHeight, - oldh = gameInfo.holdingsWidth, - oldv = gameInfo.variant; + oldh = gameInfo.holdingsWidth; + static int oldv; if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request @@ -5428,6 +5487,10 @@ InitPosition(redraw) pieces = KnightmateArray; SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k."); break; + case VariantSpartan: + pieces = SpartanArray; + SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k"); + break; case VariantFairy: pieces = fairyArray; SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); @@ -5498,7 +5561,7 @@ InitPosition(redraw) if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue; initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth]; initialPosition[pawnRow][j] = WhitePawn; - initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn; + initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn; if(gameInfo.variant == VariantXiangqi) { if(j&1) { initialPosition[pawnRow][j] = @@ -5569,18 +5632,12 @@ InitPosition(redraw) if(oldx != gameInfo.boardWidth || oldy != gameInfo.boardHeight || + oldv != gameInfo.variant || oldh != gameInfo.holdingsWidth -#ifdef GOTHIC - || oldv == VariantGothic || // For licensing popups - gameInfo.variant == VariantGothic -#endif -#ifdef FALCON - || oldv == VariantFalcon || - gameInfo.variant == VariantFalcon -#endif ) InitDrawingSizes(-2 ,0); + oldv = gameInfo.variant; if (redraw) DrawPosition(TRUE, boards[currentMove]); } @@ -5656,6 +5713,21 @@ SendBoard(cps, moveNum) setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */ } +ChessSquare +DefaultPromoChoice(int white) +{ + ChessSquare result; + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) + result = WhiteFerz; // no choice + else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) + result= WhiteKing; // in Suicide Q is the last thing we want + else if(gameInfo.variant == VariantSpartan) + result = white ? WhiteQueen : WhiteAngel; + else result = WhiteQueen; + if(!white) result = WHITE_TO_BLACK result; + return result; +} + static int autoQueen; // [HGM] oneclick int @@ -5683,6 +5755,12 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) promotionZoneSize = 3; } + // Treat Lance as Pawn when it is not representing Amazon + if(gameInfo.variant != VariantSuper) { + if(piece == WhiteLance) piece = WhitePawn; else + if(piece == BlackLance) piece = BlackPawn; + } + // next weed out all moves that do not touch the promotion zone at all if((int)piece >= BlackPawn) { if(toY >= promotionZoneSize && fromY >= promotionZoneSize) @@ -5733,12 +5811,13 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) *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 - else *promoChoice = PieceToChar(BlackQueen); - return FALSE; - } + // give caller the default choice even if we will not make it + *promoChoice = ToLower(PieceToChar(defaultPromoChoice)); + if(gameInfo.variant == VariantShogi) *promoChoice = '+'; + if(appData.sweepSelect && gameInfo.variant != VariantGreat + && gameInfo.variant != VariantShogi + && gameInfo.variant != VariantSuper) return FALSE; + if(autoQueen) return FALSE; // predetermined // suppress promotion popup on illegal moves that are not premoves premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) || @@ -5988,6 +6067,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) case BeginningOfGame: case AnalyzeMode: case Training: + if(fromY == DROP_RANK) break; // [HGM] drop moves (entered through move type-in) are automatically assigned to side-to-move if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn && (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) { /* User is moving for Black */ @@ -6082,15 +6162,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) && fromY != DROP_RANK ) { 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; } @@ -6190,7 +6270,10 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/ - if(Adjudicate(NULL)) return 1; // [HGM] adjudicate: take care of automtic game end + if(Adjudicate(NULL)) { // [HGM] adjudicate: take care of automatic game end + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + return 1; + } if (gameMode == BeginningOfGame) { if (appData.noChessProgram) { @@ -6245,6 +6328,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) switch (gameMode) { case EditGame: + if(appData.testLegality) switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) { case MT_NONE: case MT_CHECK: @@ -6274,6 +6358,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) } userOfferedDraw = FALSE; // [HGM] drawclaim: after move made, and tested for claimable draw + promoDefaultAltered = FALSE; // [HGM] fall back on default choice if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? @@ -6342,12 +6427,27 @@ Explode(Board board, int fromX, int fromY, int toX, int toY) ChessSquare gatingPiece = EmptySquare; // exported to front-end, for dragging +int CanPromote(ChessSquare piece, int y) +{ + if(gameMode == EditPosition) return FALSE; // no promotions when editing position + // some variants have fixed promotion piece, no promotion at all, or another selection mechanism + if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantXiangqi || + gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || + gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || + gameInfo.variant == VariantMakruk) return FALSE; + return (piece == BlackPawn && y == 1 || + piece == WhitePawn && y == BOARD_HEIGHT-2 || + piece == BlackLance && y == 1 || + piece == WhiteLance && y == BOARD_HEIGHT-2 ); +} + void LeftClick(ClickType clickType, int xPix, int yPix) { int x, y; Boolean saveAnimate; - static int second = 0, promotionChoice = 0, dragging = 0; + static int second = 0, promotionChoice = 0, clearFlag = 0; char promoChoice = NULLCHAR; + ChessSquare piece; if(appData.seekGraph && appData.icsActive && loggedOn && (gameMode == BeginningOfGame || gameMode == IcsIdle)) { @@ -6367,6 +6467,13 @@ void LeftClick(ClickType clickType, int xPix, int yPix) x = BOARD_WIDTH - 1 - x; } + if(promoSweep != EmptySquare) { // up-click during sweep-select of promo-piece + defaultPromoChoice = promoSweep; + promoSweep = EmptySquare; // terminate sweep + promoDefaultAltered = TRUE; + if(!selectFlag) x = fromX, y = fromY; // and fake up-click on same square if we were still selecting + } + 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 @@ -6395,29 +6502,48 @@ void LeftClick(ClickType clickType, int xPix, int yPix) || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) ) return; + if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered) + fromX = fromY = -1; // second click on piece after altering default promo piece treated as first click + + if(!promoDefaultAltered) { // determine default promotion piece, based on the side the user is moving for + int side = (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack || + gameMode != MachinePlaysWhite && gameMode != IcsPlayingBlack && WhiteOnMove(currentMove)); + defaultPromoChoice = DefaultPromoChoice(side); + } + autoQueen = appData.alwaysPromoteToQueen; if (fromX == -1) { + int originalY = y; gatingPiece = EmptySquare; - if(!appData.oneClick || !OnlyMove(&x, &y, FALSE)) { - if (clickType == Press) { + if (clickType != Press) { + if(dragging) { // [HGM] from-square must have been reset due to game end since last press + DragPieceEnd(xPix, yPix); dragging = 0; + DrawPosition(FALSE, NULL); + } + return; + } + fromX = x; fromY = y; + if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) || + // even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection + appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) { /* First square */ - if (OKToStartUserMove(x, y)) { - fromX = x; - fromY = y; + if (OKToStartUserMove(fromX, fromY)) { second = 0; MarkTargetSquares(0); DragPieceBegin(xPix, yPix); dragging = 1; + if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) { + promoSweep = defaultPromoChoice; + selectFlag = 0; lastX = xPix; lastY = yPix; + Sweep(0); // Pawn that is going to promote: preview promotion piece + DisplayMessage("", _("Pull pawn backwards to under-promote")); + } if (appData.highlightDragging) { - SetHighlights(x, y, -1, -1); + SetHighlights(fromX, fromY, -1, -1); } - } - } else if(dragging) { // [HGM] from-square must have been reset due to game end since last press - DragPieceEnd(xPix, yPix); dragging = 0; - DrawPosition(FALSE, NULL); + } else fromX = fromY = -1; + return; } - return; - } } /* fromX != -1 */ @@ -6443,6 +6569,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) !(fromP == BlackKing && toP == BlackRook && frc))) { /* Clicked again on same color piece -- changed his mind */ second = (x == fromX && y == fromY); + promoDefaultAltered = FALSE; if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) { if (appData.highlightDragging) { SetHighlights(x, y, -1, -1); @@ -6459,6 +6586,11 @@ void LeftClick(ClickType clickType, int xPix, int yPix) fromY = y; dragging = 1; MarkTargetSquares(0); DragPieceBegin(xPix, yPix); + if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) { + promoSweep = defaultPromoChoice; + selectFlag = 0; lastX = xPix; lastY = yPix; + Sweep(0); // Pawn that is going to promote: preview promotion piece + } } } if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on @@ -6470,6 +6602,14 @@ void LeftClick(ClickType clickType, int xPix, int yPix) if (clickType == Release && x == fromX && y == fromY) { DragPieceEnd(xPix, yPix); dragging = 0; + if(clearFlag) { + // a deferred attempt to click-click move an empty square on top of a piece + boards[currentMove][y][x] = EmptySquare; + ClearHighlights(); + DrawPosition(FALSE, boards[currentMove]); + fromX = fromY = -1; clearFlag = 0; + return; + } if (appData.animateDragging) { /* Undo animation damage if any */ DrawPosition(FALSE, NULL); @@ -6489,12 +6629,20 @@ void LeftClick(ClickType clickType, int xPix, int yPix) return; } + clearFlag = 0; + /* we now have a different from- and (possibly off-board) to-square */ /* Completed move */ toX = x; toY = y; saveAnimate = appData.animate; if (clickType == Press) { + if(gameMode == EditPosition && boards[currentMove][fromY][fromX] == EmptySquare) { + // must be Edit Position mode with empty-square selected + fromX = x; fromY = y; DragPieceBegin(xPix, yPix); dragging = 1; // consider this a new attempt to drag + if(x >= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click + return; + } /* Finish clickclick move */ if (appData.animate || appData.highlightLastMove) { SetHighlights(fromX, fromY, toX, toY); @@ -6608,16 +6756,25 @@ int RightClick(ClickType action, int x, int y, int *fromX, int *fromY) xSqr = EventToSquare(x, BOARD_WIDTH); ySqr = EventToSquare(y, BOARD_HEIGHT); - if (action == Release) UnLoadPV(); // [HGM] pv + if (action == Release) { + if(pieceSweep != EmptySquare) { + EditPositionMenuEvent(pieceSweep, toX, toY); + pieceSweep = EmptySquare; + } else UnLoadPV(); // [HGM] pv + } if (action != Press) return -2; // return code to be ignored switch (gameMode) { case IcsExamining: if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1; case EditPosition: if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1; - if (xSqr < 0 || ySqr < 0) return -1; - whichMenu = 0; // edit-position menu - break; + if (xSqr < 0 || ySqr < 0) return -1; + if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu + pieceSweep = shiftKey ? BlackPawn : WhitePawn; // [HGM] sweep: prepare selecting piece by mouse sweep + toX = xSqr; toY = ySqr; lastX = x, lastY = y; + if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY; + NextPiece(0); + return -2; case IcsObserving: if(!appData.icsEngineAnalyze) return -1; case IcsPlayingWhite: @@ -6811,7 +6968,6 @@ Adjudicate(ChessProgramState *cps) if(canAdjudicate && appData.checkMates) { if(engineOpponent) SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, "Xboard adjudication: King destroyed", GE_XBOARD ); return 1; @@ -6825,7 +6981,6 @@ Adjudicate(ChessProgramState *cps) if(canAdjudicate && appData.checkMates) { if(engineOpponent) SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets to see move - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, "Xboard adjudication: Bare king", GE_XBOARD ); return 1; @@ -6838,7 +6993,6 @@ Adjudicate(ChessProgramState *cps) /* but only adjudicate if adjudication enabled */ if(engineOpponent) SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( nrW > 1 ? WhiteWins : nrB > 1 ? BlackWins : GameIsDrawn, "Xboard adjudication: Bare king", GE_XBOARD ); return 1; @@ -6899,7 +7053,6 @@ Adjudicate(ChessProgramState *cps) if(canAdjudicate && appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested if(engineOpponent) SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( result, reason, GE_XBOARD ); return 1; } @@ -6918,7 +7071,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see last move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD ); return 1; } @@ -6939,7 +7091,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD ); return 1; } @@ -7022,7 +7173,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( result, details, GE_XBOARD ); return 1; } @@ -7059,7 +7209,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); return 1; } @@ -7082,7 +7231,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, p, GE_XBOARD ); return 1; } @@ -7093,7 +7241,6 @@ Adjudicate(ChessProgramState *cps) SendToProgram("force\n", engineOpponent); // suppress reply SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */ } - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD ); return 1; } @@ -7272,7 +7419,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h &fromX, &fromY, &toX, &toY, &promoChar)) { /* Machine move could not be parsed; ignore it. */ snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"), - machineMove, cps->which); + machineMove, _(cps->which)); DisplayError(buf1, 0); snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d", machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType); @@ -7329,11 +7476,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (cps->sendTime == 2) cps->sendTime = 1; if (cps->offeredDraw) cps->offeredDraw--; - /* currentMoveString is set as a side-effect of ParseOneMove */ - safeStrCpy(machineMove, currentMoveString, sizeof(machineMove)/sizeof(machineMove[0])); - strcat(machineMove, "\n"); - safeStrCpy(moveList[forwardMostMove], machineMove, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0])); - /* [AS] Save move info*/ pvInfoList[ forwardMostMove ].score = programStats.score; pvInfoList[ forwardMostMove ].depth = programStats.depth; @@ -7370,7 +7512,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } } - if(Adjudicate(cps)) return; // [HGM] adjudicate: for all automatic game ends + if(Adjudicate(cps)) { + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + return; // [HGM] adjudicate: for all automatic game ends + } #if ZIPPY if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) && @@ -7692,21 +7837,21 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. gameMode = EditGame; ModeHighlight(); } + /* [HGM] illegal-move claim should forfeit game when Xboard */ + /* only passes fully legal moves */ + if( appData.testLegality && gameMode == TwoMachinesPlay ) { + GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins, + "False illegal-move claim", GE_XBOARD ); + return; // do not take back move we tested as valid + } currentMove = forwardMostMove-1; DisplayMove(currentMove-1); /* before DisplayMoveError */ SwitchClocks(forwardMostMove-1); // [HGM] race DisplayBothClocks(); snprintf(buf1, 10*MSG_SIZ, _("Illegal move \"%s\" (rejected by %s chess program)"), - parseList[currentMove], cps->which); + parseList[currentMove], _(cps->which)); DisplayMoveError(buf1); DrawPosition(FALSE, boards[currentMove]); - - /* [HGM] illegal-move claim should forfeit game when Xboard */ - /* only passes fully legal moves */ - if( appData.testLegality && gameMode == TwoMachinesPlay ) { - GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins, - "False illegal-move claim", GE_XBOARD ); - } return; } if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) { @@ -7729,7 +7874,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. cps->maybeThinking = FALSE; snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"), - cps->which, cps->program, cps->host, message); + _(cps->which), cps->program, cps->host, message); RemoveInputSource(cps->isr); DisplayFatalError(buf1, 0, 1); return; @@ -7752,7 +7897,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* Hint move could not be parsed!? */ snprintf(buf2, sizeof(buf2), _("Illegal hint move \"%s\"\nfrom %s chess program"), - buf1, cps->which); + buf1, _(cps->which)); DisplayError(buf2, 0); } } else { @@ -8356,6 +8501,7 @@ ParseGameHistory(game) if (q != NULL) *q = NULLCHAR; p++; } + while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message gameInfo.resultDetails = StrSave(p); continue; } @@ -8417,6 +8563,10 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) } else { int i; + if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) { + if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi ) + board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants + } else if( board[fromY][fromX] == WhitePawn ) { if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers board[EP_STATUS] = EP_PAWN_MOVE; @@ -8491,9 +8641,9 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = king; board[toY][toX+1] = board[fromY][BOARD_LEFT]; board[fromY][BOARD_LEFT] = EmptySquare; - } else if (board[fromY][fromX] == WhitePawn + } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi || + board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) && toY >= BOARD_HEIGHT-promoRank - && gameInfo.variant != VariantXiangqi ) { /* white pawn promotion */ board[toY][toX] = CharToPiece(ToUpper(promoChar)); @@ -8555,9 +8705,9 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = BlackKing; board[fromY][0] = EmptySquare; board[toY][2] = BlackRook; - } else if (board[fromY][fromX] == BlackPawn + } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi || + board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) && toY < promoRank - && gameInfo.variant != VariantXiangqi ) { /* black pawn promotion */ board[toY][toX] = CharToPiece(ToLower(promoChar)); @@ -8680,8 +8830,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) } 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 @@ -8898,7 +9048,7 @@ InitChessProgram(cps, setup) if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse ) overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5; if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom || - gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon ) + gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon || gameInfo.variant == VariantJanus ) overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; if( gameInfo.variant == VariantCourier ) overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; @@ -8906,6 +9056,8 @@ InitChessProgram(cps, setup) overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; if( gameInfo.variant == VariantGreat ) overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; + if( gameInfo.variant == VariantSChess ) + overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7; if(overruled) { snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, @@ -9448,6 +9600,13 @@ GameEnds(result, resultDetails, whosays) first.matchWins, second.matchWins, appData.matchGames - (first.matchWins + second.matchWins)); popupRequested++; // [HGM] crash: postpone to after resetting endingGame + if (appData.firstPlaysBlack) { // [HGM] match: back to original for next match + first.twoMachinesColor = "black\n"; + second.twoMachinesColor = "white\n"; + } else { + first.twoMachinesColor = "white\n"; + second.twoMachinesColor = "black\n"; + } } } if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && @@ -9617,7 +9776,7 @@ AutoPlayGameLoop() return; if (matchMode || appData.timeDelay == 0) continue; - if (appData.timeDelay < 0 || gameMode == AnalyzeFile) + if (appData.timeDelay < 0) return; StartLoadGameTimer((long)(1000.0 * appData.timeDelay)); break; @@ -9634,10 +9793,18 @@ AutoPlayOneMove() fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove); } - if (gameMode != PlayFromGameFile) + if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile) return FALSE; + if (gameMode == AnalyzeFile && currentMove > backwardMostMove) { + pvInfoList[currentMove].depth = programStats.depth; + pvInfoList[currentMove].score = programStats.score; + pvInfoList[currentMove].time = 0; + if(currentMove < forwardMostMove) AppendComment(currentMove+1, lastPV[0], 2); + } + if (currentMove >= forwardMostMove) { + if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); } gameMode = EditGame; ModeHighlight(); @@ -9770,6 +9937,7 @@ LoadGameOneMove(readAhead) if (q != NULL) *q = NULLCHAR; p++; } + while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message GameEnds(moveType, p, GE_FILE); done = TRUE; if (cmailMsgLoaded) { @@ -9902,8 +10070,6 @@ LoadGameOneMove(readAhead) return FALSE; } else { /* currentMoveString is set as a side-effect of yylex */ - strcat(currentMoveString, "\n"); - safeStrCpy(moveList[forwardMostMove], currentMoveString, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0])); thinkOutput[0] = NULLCHAR; MakeMove(fromX, fromY, toX, toY, promoChar); @@ -10408,6 +10574,7 @@ LoadGame(f, gameNumber, title, useList) for (i = BOARD_HEIGHT - 1; i >= 0; i--) for (j = BOARD_LEFT; j < BOARD_RGHT; p++) switch (*p) { + case '{': case '[': case '-': case ' ': @@ -11642,7 +11809,7 @@ void EditTagsEvent() { char *tags = PGNTags(&gameInfo); - EditTagsPopUp(tags); + EditTagsPopUp(tags, NULL); free(tags); } @@ -11917,6 +12084,7 @@ TwoMachinesEvent P((void)) char buf[MSG_SIZ]; ChessProgramState *onmove; char *bookHit = NULL; + static int stalling = 0; if (appData.noChessProgram) return; @@ -11953,13 +12121,23 @@ TwoMachinesEvent P((void)) ResurrectChessProgram(); /* in case first program isn't running */ if(WaitForSecond(TwoMachinesEventIfReady)) return; - DisplayMessage("", ""); - InitChessProgram(&second, FALSE); - SendToProgram("force\n", &second); if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position + DisplayMessage("", _("Waiting for first chess program")); ScheduleDelayedEvent(TwoMachinesEvent, 10); return; } + if(!stalling) { + InitChessProgram(&second, FALSE); + SendToProgram("force\n", &second); + } + if(second.lastPing != second.lastPong) { // [HGM] second engine might have to reallocate hash + if(!stalling) DisplayMessage("", _("Waiting for second chess program")); + stalling = 1; + ScheduleDelayedEvent(TwoMachinesEvent, 10); + return; + } + stalling = 0; + DisplayMessage("", ""); if (startedFromSetupPosition) { SendBoard(&second, backwardMostMove); if (appData.debugMode) { @@ -12595,6 +12773,32 @@ CallFlagEvent() } void +ClockClick(int which) +{ // [HGM] code moved to back-end from winboard.c + if(which) { // black clock + if (gameMode == EditPosition || gameMode == IcsExamining) { + if(!appData.pieceMenu && blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0); + SetBlackToPlayEvent(); + } else if (gameMode == EditGame || shiftKey) { + AdjustClock(which, -1); + } else if (gameMode == IcsPlayingWhite || + gameMode == MachinePlaysBlack) { + CallFlagEvent(); + } + } else { // white clock + if (gameMode == EditPosition || gameMode == IcsExamining) { + if(!appData.pieceMenu && !blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0); + SetWhiteToPlayEvent(); + } else if (gameMode == EditGame || shiftKey) { + AdjustClock(which, -1); + } else if (gameMode == IcsPlayingBlack || + gameMode == MachinePlaysWhite) { + CallFlagEvent(); + } + } +} + +void DrawEvent() { /* Offer draw or accept pending draw offer from opponent */ @@ -13298,7 +13502,13 @@ ReplaceComment(index, text) char *text; { int len; + char *p; + float score; + if(index && sscanf(text, "%f/%d", &score, &len) == 2 && + pvInfoList[index-1].depth == len && + fabs(pvInfoList[index-1].score - score*100.) < 0.5 && + (p = strchr(text, '\n'))) text = p; // [HGM] strip off first line with PV info, if any while (*text == '\n') text++; len = strlen(text); while (len > 0 && text[len - 1] == '\n') len--; @@ -13369,27 +13579,27 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces); while(commentList[index][oldlen-1] == '\n') commentList[index][--oldlen] = NULLCHAR; commentList[index] = (char *) malloc(oldlen + len + 6); // might waste 4 - safeStrCpy(commentList[index], old, oldlen); + safeStrCpy(commentList[index], old, oldlen + len + 6); free(old); // [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--; } + if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces == TRUE)) { + if(addBraces == TRUE) addBraces = FALSE; else { text++; len--; } while (*text == '\n') { text++; len--; } commentList[index][--oldlen] = NULLCHAR; } - if(addBraces) strcat(commentList[index], "\n{\n"); + if(addBraces) strcat(commentList[index], addBraces == 2 ? "\n(" : "\n{\n"); else strcat(commentList[index], "\n"); strcat(commentList[index], text); - if(addBraces) strcat(commentList[index], "\n}\n"); + if(addBraces) strcat(commentList[index], addBraces == 2 ? ")\n" : "\n}\n"); else strcat(commentList[index], "\n"); } else { commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4... if(addBraces) - safeStrCpy(commentList[index], "{\n", 3); + safeStrCpy(commentList[index], addBraces == 2 ? "(" : "{\n", 3); else commentList[index][0] = NULLCHAR; strcat(commentList[index], text); - strcat(commentList[index], "\n"); - if(addBraces) strcat(commentList[index], "}\n"); + strcat(commentList[index], addBraces == 2 ? ")\n" : "\n"); + if(addBraces == TRUE) strcat(commentList[index], "}\n"); } } @@ -13408,7 +13618,7 @@ static char * FindStr( char * text, char * sub_text ) /* [HGM] PV time: and then remove it, to prevent it appearing twice */ char *GetInfoFromComment( int index, char * text ) { - char * sep = text; + char * sep = text, *p; if( text != NULL && index > 0 ) { int score = 0; @@ -13446,11 +13656,20 @@ char *GetInfoFromComment( int index, char * text ) return text; } + p = text; + if(p[1] == '(') { // comment starts with PV + p = strchr(p, ')'); // locate end of PV + if(p == NULL || sep < p+5) return text; + // at this point we have something like "{(.*) +0.23/6 ..." + p = text; while(*++p != ')') p[-1] = *p; p[-1] = ')'; + *p = '\n'; while(*p == ' ' || *p == '\n') p++; *--p = '{'; + // we now moved the brace to behind the PV: "(.*) {+0.23/6 ..." + } time = -1; sec = -1; deci = -1; - 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 ) { + if( sscanf( p+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 && + sscanf( p+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 && + sscanf( p+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 && + sscanf( p+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) { return text; } @@ -13466,7 +13685,7 @@ char *GetInfoFromComment( int index, char * text ) /* [HGM] PV time: now locate end of PV info */ while( *++sep >= '0' && *sep <= '9'); // strip depth if(time >= 0) - while( *++sep >= '0' && *sep <= '9'); // strip time + while( *++sep >= '0' && *sep <= '9' || *sep == '\n'); // strip time if(sec >= 0) while( *++sep >= '0' && *sep <= '9'); // strip seconds if(deci >= 0) @@ -13486,6 +13705,7 @@ char *GetInfoFromComment( int index, char * text ) pvInfoList[index-1].score = score; pvInfoList[index-1].time = 10*time; // centi-sec if(*sep == '}') *sep = 0; else *--sep = '{'; + if(p != text) { while(*p++ = *sep++); sep = text; } // squeeze out space between PV and comment, and return both } return sep; } @@ -13513,11 +13733,11 @@ SendToProgram(message, cps) outCount = OutputToProcess(cps->pr, message, count, &error); if (outCount < count && !exiting && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */ - snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), cps->which); + snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), _(cps->which)); if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) { gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */ - snprintf(buf, MSG_SIZ, "%s program exits in draw position (%s)", cps->which, cps->program); + snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program); } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } @@ -13543,11 +13763,11 @@ ReceiveFromProgram(isr, closure, message, count, error) if (count <= 0) { if (count == 0) { snprintf(buf, MSG_SIZ, _("Error: %s chess program (%s) exited unexpectedly"), - cps->which, cps->program); + _(cps->which), cps->program); if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) { gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */ - snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), cps->which, cps->program); + snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program); } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } @@ -13557,7 +13777,7 @@ ReceiveFromProgram(isr, closure, message, count, error) if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, 0, 1); else errorExitStatus = 1; } else { snprintf(buf, MSG_SIZ, _("Error reading from %s chess program (%s)"), - cps->which, cps->program); + _(cps->which), cps->program); RemoveInputSource(cps->isr); /* [AS] Program is misbehaving badly... kill it */ @@ -13675,7 +13895,7 @@ SendTimeControl(cps, mps, tc, inc, sd, st) SendToProgram(buf, cps); } - if(cps->nps > 0) { /* [HGM] nps */ + if(cps->nps >= 0) { /* [HGM] nps */ if(cps->supportsNPS == FALSE) cps->nps = -1; // don't use if engine explicitly says not supported! else { @@ -13828,11 +14048,11 @@ ParseOption(Option *opt, ChessProgramState *cps) } else if((p = strstr(opt->name, " -file "))) { // for now -file is a synonym for -string, to already provide compatibility with future polyglots opt->textValue = p+7; - opt->type = TextBox; // FileName; + opt->type = FileName; // FileName; } else if((p = strstr(opt->name, " -path "))) { // for now -file is a synonym for -string, to already provide compatibility with future polyglots opt->textValue = p+7; - opt->type = TextBox; // PathName; + opt->type = PathName; // PathName; } else if(p = strstr(opt->name, " -check ")) { if(sscanf(p, " -check %d", &def) < 1) return FALSE; opt->value = (def != 0); @@ -13968,7 +14188,7 @@ ParseFeatures(args, cps) } if(cps->nrOptions >= MAX_OPTIONS) { cps->nrOptions--; - snprintf(buf, MSG_SIZ, "%s engine has too many options\n", cps->which); + snprintf(buf, MSG_SIZ, _("%s engine has too many options\n"), _(cps->which)); DisplayError(buf, 0); } continue; @@ -14087,6 +14307,55 @@ AskQuestionEvent(title, question, replyPrefix, which) } void +TypeInEvent(char firstChar) +{ + if ((gameMode == BeginningOfGame && !appData.icsActive) || + gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack || + gameMode == AnalyzeMode || gameMode == EditGame || + gameMode == EditPosition || gameMode == IcsExamining || + gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || + isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes + ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile || + gameMode == IcsObserving || gameMode == TwoMachinesPlay ) || + gameMode == Training) PopUpMoveDialog(firstChar); +} + +void +TypeInDoneEvent(char *move) +{ + Board board; + int n, fromX, fromY, toX, toY; + char promoChar; + ChessMove moveType; + + // [HGM] FENedit + if(gameMode == EditPosition && ParseFEN(board, &n, move) ) { + EditPositionPasteFEN(move); + return; + } + // [HGM] movenum: allow move number to be typed in any mode + if(sscanf(move, "%d", &n) == 1 && n != 0 ) { + ToNrEvent(2*n-1); + return; + } + + if (gameMode != EditGame && currentMove != forwardMostMove && + gameMode != Training) { + DisplayMoveError(_("Displayed move is not current")); + } else { + int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, + &moveType, &fromX, &fromY, &toX, &toY, &promoChar); + if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized + if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, + &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) { + UserMoveEvent(fromX, fromY, toX, toY, promoChar); + } else { + DisplayMoveError(_("Could not parse move")); + } + } +} + +void DisplayMove(moveNumber) int moveNumber; { @@ -14504,15 +14773,15 @@ SwitchClocks(int newMoveNr) if(blackNPS >= 0) lastTickLength = 0; blackTimeRemaining -= lastTickLength; /* [HGM] PGNtime: save time for PGN file if engine did not give it */ -// if(pvInfoList[forwardMostMove-1].time == -1) - pvInfoList[forwardMostMove-1].time = // use GUI time +// if(pvInfoList[forwardMostMove].time == -1) + pvInfoList[forwardMostMove].time = // use GUI time (timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10; } else { if(whiteNPS >= 0) lastTickLength = 0; whiteTimeRemaining -= lastTickLength; /* [HGM] PGNtime: save time for PGN file if engine did not give it */ -// if(pvInfoList[forwardMostMove-1].time == -1) - pvInfoList[forwardMostMove-1].time = +// if(pvInfoList[forwardMostMove].time == -1) + pvInfoList[forwardMostMove].time = (timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10; } flagged = CheckFlags();