X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=677e21cfb47bcb97df4eb98217e59cf5d5237f56;hb=a7db862b2f44d23b358a1319cd8a374cf88c24fd;hp=b5f24a4c1e89dd0e3c8446b4967d39a6dc842210;hpb=8a375395a4cc09b1febaf64c4b2e7ce66c59a69b;p=xboard.git diff --git a/backend.c b/backend.c index b5f24a4..677e21c 100644 --- a/backend.c +++ b/backend.c @@ -268,7 +268,8 @@ extern int chatCount; int chattingPartner; char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */ ChessSquare pieceSweep = EmptySquare; -ChessSquare promoSweep = EmptySquare; +ChessSquare promoSweep = EmptySquare, defaultPromoChoice; +int promoDefaultAltered; /* States for ics_getting_history */ #define H_FALSE 0 @@ -1175,6 +1176,40 @@ InitBackEnd2() } void +MatchEvent(int mode) +{ // [HGM] moved out of InitBackend3, to make it callable when match starts through menu + /* Set up machine vs. machine match */ + if (appData.noChessProgram) { + DisplayFatalError(_("Can't have a match with no chess programs"), + 0, 2); + return; + } + matchMode = mode; + matchGame = 1; + if (*appData.loadGameFile != NULLCHAR) { + int index = appData.loadGameIndex; // [HGM] autoinc + if(index<0) lastIndex = index = 1; + if (!LoadGameFromFile(appData.loadGameFile, + index, + appData.loadGameFile, FALSE)) { + DisplayFatalError(_("Bad game file"), 0, 1); + return; + } + } else if (*appData.loadPositionFile != NULLCHAR) { + int index = appData.loadPositionIndex; // [HGM] autoinc + if(index<0) lastIndex = index = 1; + if (!LoadPositionFromFile(appData.loadPositionFile, + index, + appData.loadPositionFile)) { + DisplayFatalError(_("Bad position file"), 0, 1); + return; + } + } + first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches + TwoMachinesEvent(); +} + +void InitBackEnd3 P((void)) { GameMode initialMode; @@ -1260,34 +1295,7 @@ InitBackEnd3 P((void)) } if (appData.matchMode) { - /* Set up machine vs. machine match */ - if (appData.noChessProgram) { - DisplayFatalError(_("Can't have a match with no chess programs"), - 0, 2); - return; - } - matchMode = TRUE; - matchGame = 1; - if (*appData.loadGameFile != NULLCHAR) { - int index = appData.loadGameIndex; // [HGM] autoinc - if(index<0) lastIndex = index = 1; - if (!LoadGameFromFile(appData.loadGameFile, - index, - appData.loadGameFile, FALSE)) { - DisplayFatalError(_("Bad game file"), 0, 1); - return; - } - } else if (*appData.loadPositionFile != NULLCHAR) { - int index = appData.loadPositionIndex; // [HGM] autoinc - if(index<0) lastIndex = index = 1; - if (!LoadPositionFromFile(appData.loadPositionFile, - index, - appData.loadPositionFile)) { - DisplayFatalError(_("Bad position file"), 0, 1); - return; - } - } - TwoMachinesEvent(); + MatchEvent(TRUE); } else if (*appData.cmailGameName != NULLCHAR) { /* Set up cmail mode */ ReloadCmailMsgEvent(TRUE); @@ -4813,16 +4821,17 @@ ProcessICSInitScript(f) } +static int lastX, lastY, selectFlag, dragging; + void Sweep(int step) { - ChessSquare piece = boards[currentMove][toY][toX]; ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep; if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn; if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) king = EmptySquare; if(promoSweep >= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn; if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare; - if(toY != BOARD_HEIGHT-1 && toY != 0) pawn = EmptySquare; + if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare; do { promoSweep -= step; if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap @@ -4833,23 +4842,22 @@ Sweep(int step) } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || appData.testLegality && (promoSweep == king || gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep)); - boards[currentMove][toY][toX] = promoSweep; - DrawPosition(FALSE, boards[currentMove]); - boards[currentMove][toY][toX] = piece; + ChangeDragPiece(promoSweep); } -static int lastX, lastY; - -void PromoScroll(int x, int y) +int PromoScroll(int x, int y) { int step = 0; - if(abs(x - lastX) < 7 && abs(y - lastY) < 7) return; + + 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; + if(!step) return FALSE; lastX = x; lastY = y; - - if(promoSweep == EmptySquare) return; - Sweep(step); + if((promoSweep < BlackPawn) == flipView) step = -step; + if(step > 0) selectFlag = 1; + if(!selectFlag) Sweep(step); + return FALSE; } void @@ -5116,9 +5124,10 @@ UnLoadPV() void MovePV(int x, int y, int h) { // step through PV based on mouse coordinates (called on mouse move) - int margin = h>>3, step = 0, dist; + int margin = h>>3, step = 0; // we must somehow check if right button is still down (might be released off board!) + 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; @@ -5704,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 @@ -5788,14 +5812,11 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) return FALSE; } // give caller the default choice even if we will not make it - if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) - *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want - else if(gameInfo.variant == VariantSpartan) - *promoChoice = ToLower(PieceToChar(toY ? WhiteQueen : BlackAngel)); - else if(gameInfo.variant == VariantShogi) - *promoChoice = '+'; - else *promoChoice = ToLower(PieceToChar(toY ? WhiteQueen : BlackQueen)); - if(*promoChoice == '.') *promoChoice = ToLower(PieceToChar(piece)); // safety catch, to make sure promoChoice is a defined piece + *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 @@ -6337,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? @@ -6405,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)) { @@ -6430,16 +6467,11 @@ void LeftClick(ClickType clickType, int xPix, int yPix) x = BOARD_WIDTH - 1 - x; } - if(promoSweep != EmptySquare) { - char promoChar = ToLower(PieceToChar(promoSweep)); - if(gameInfo.variant == VariantShogi) promoChar = promoSweep == boards[currentMove][fromY][fromX] ? '=' : '+'; - saveAnimate = appData.animate; appData.animate = FALSE; - UserMoveEvent(fromX, fromY, toX, toY, promoChar); - appData.animate = saveAnimate; - promoSweep = EmptySquare; - DrawPosition(FALSE, boards[currentMove]); - fromX = fromY = -1; - return; + 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 @@ -6470,9 +6502,19 @@ 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 (clickType != Press) { if(dragging) { // [HGM] from-square must have been reset due to game end since last press @@ -6481,18 +6523,25 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } return; } - if(!appData.oneClick || !OnlyMove(&x, &y, FALSE)) { + 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 fromX = fromY = -1; return; } } @@ -6520,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); @@ -6536,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 @@ -6547,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); @@ -6566,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); @@ -6637,13 +6708,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) DisplayMessage("Click in holdings to choose piece", ""); return; } - if(appData.sweepSelect && clickType == Press) { - lastX = xPix; lastY = yPix; - ChessSquare piece = boards[currentMove][fromY][fromX]; - promoSweep = CharToPiece((piece >= BlackPawn ? ToLower : ToUpper)(promoChoice)); - if(promoChoice == '+') promoSweep = PROMOTED piece; - Sweep(0); - } else PromotionPopUp(); + PromotionPopUp(); } else { int oldMove = currentMove; UserMoveEvent(fromX, fromY, toX, toY, promoChoice); @@ -12019,6 +12084,7 @@ TwoMachinesEvent P((void)) char buf[MSG_SIZ]; ChessProgramState *onmove; char *bookHit = NULL; + static int stalling = 0; if (appData.noChessProgram) return; @@ -12055,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) { @@ -14231,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; {