X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=b5f24a4c1e89dd0e3c8446b4967d39a6dc842210;hb=8a375395a4cc09b1febaf64c4b2e7ce66c59a69b;hp=89089f1dce04f33f23a4eaacf855fe9bcbba50de;hpb=c8a5fc25c8c93671385ab3662214ce66c33ebf8d;p=xboard.git diff --git a/backend.c b/backend.c index 89089f1..b5f24a4 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,8 @@ 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; /* States for ics_getting_history */ #define H_FALSE 0 @@ -737,8 +739,8 @@ InitBackEnd1() /* [AS] Adjudication threshold */ adjudicateLossThreshold = appData.adjudicateLossThreshold; - first.which = _("first"); - second.which = _("second"); + first.which = "first"; + second.which = "second"; first.maybeThinking = second.maybeThinking = FALSE; first.pr = second.pr = NoProc; first.isr = second.isr = NULL; @@ -2861,7 +2863,7 @@ read_from_ics(isr, closure, data, count, error) if(channel >= 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; @@ -4368,7 +4370,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(toY != BOARD_HEIGHT-1 && toY != 0) 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)); + boards[currentMove][toY][toX] = promoSweep; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][toY][toX] = piece; +} + +static int lastX, lastY; + +void PromoScroll(int x, int y) +{ + int step = 0; + 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(promoSweep == EmptySquare) return; + Sweep(step); +} + +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) @@ -5017,8 +5073,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) { @@ -5062,15 +5116,18 @@ 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; + int margin = h>>3, step = 0, dist; - 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(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 @@ -5730,12 +5787,16 @@ 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 + 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 + if(autoQueen) return FALSE; // predetermined // suppress promotion popup on illegal moves that are not premoves premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) || @@ -6188,7 +6249,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) { @@ -6366,6 +6430,18 @@ 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(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 @@ -6561,7 +6637,13 @@ void LeftClick(ClickType clickType, int xPix, int yPix) DisplayMessage("Click in holdings to choose piece", ""); return; } - PromotionPopUp(); + 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(); } else { int oldMove = currentMove; UserMoveEvent(fromX, fromY, toX, toY, promoChoice); @@ -6609,16 +6691,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: @@ -6812,7 +6903,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; @@ -6826,7 +6916,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; @@ -6839,7 +6928,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; @@ -6900,7 +6988,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; } @@ -6919,7 +7006,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; } @@ -6940,7 +7026,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; } @@ -7023,7 +7108,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; } @@ -7060,7 +7144,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; } @@ -7083,7 +7166,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; } @@ -7094,7 +7176,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; } @@ -7273,7 +7354,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); @@ -7367,7 +7448,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } if(Adjudicate(cps)) { - DrawPosition(FALSE, boards[currentMove = forwardMostMove-1]); ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ return; // [HGM] adjudicate: for all automatic game ends } @@ -7704,7 +7784,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. 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]); return; @@ -7729,7 +7809,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 +7832,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 { @@ -8903,7 +8983,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; @@ -10429,6 +10509,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 ' ': @@ -12616,6 +12697,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 */ @@ -13550,11 +13657,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; } @@ -13580,11 +13687,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; } @@ -13594,7 +13701,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 */ @@ -13712,7 +13819,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 { @@ -13865,11 +13972,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); @@ -14005,7 +14112,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;