X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=4c9968f2b45161abf74ff60b5ffc614699e235a6;hb=bd85ed7a2aac57e6afa8fdab7f3a13ce22910b18;hp=5bc1eaf9504abbbd267e86409dd34f1bbd092800;hpb=0b487f0b8319a23d5334c63b035f692bcbae6221;p=xboard.git diff --git a/backend.c b/backend.c index 5bc1eaf..4c9968f 100644 --- a/backend.c +++ b/backend.c @@ -566,6 +566,13 @@ ChessSquare aseanArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj BlackKing, BlackMan, BlackKnight, BlackRook } }; +ChessSquare lionArray[2][BOARD_FILES] = { + { WhiteRook, WhiteLion, WhiteBishop, WhiteQueen, + WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackLion, BlackBishop, BlackQueen, + BlackKing, BlackBishop, BlackKnight, BlackRook } +}; + #if (BOARD_FILES>=10) ChessSquare ShogiArray[2][BOARD_FILES] = { @@ -646,8 +653,23 @@ ChessSquare CourierArray[2][BOARD_FILES] = { { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing, BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook } }; +ChessSquare ChuArray[6][BOARD_FILES] = { + { WhiteLance, WhiteUnicorn, WhiteMan, WhiteFerz, WhiteWazir, WhiteKing, + WhiteAlfil, WhiteWazir, WhiteFerz, WhiteMan, WhiteUnicorn, WhiteLance }, + { BlackLance, BlackUnicorn, BlackMan, BlackFerz, BlackWazir, BlackAlfil, + BlackKing, BlackWazir, BlackFerz, BlackMan, BlackUnicorn, BlackLance }, + { WhiteCannon, EmptySquare, WhiteBishop, EmptySquare, WhiteNightrider, WhiteMarshall, + WhiteAngel, WhiteNightrider, EmptySquare, WhiteBishop, EmptySquare, WhiteCannon }, + { BlackCannon, EmptySquare, BlackBishop, EmptySquare, BlackNightrider, BlackAngel, + BlackMarshall, BlackNightrider, EmptySquare, BlackBishop, EmptySquare, BlackCannon }, + { WhiteFalcon, WhiteSilver, WhiteRook, WhiteCardinal, WhiteDragon, WhiteLion, + WhiteQueen, WhiteDragon, WhiteCardinal, WhiteRook, WhiteSilver, WhiteFalcon }, + { BlackFalcon, BlackSilver, BlackRook, BlackCardinal, BlackDragon, BlackQueen, + BlackLion, BlackDragon, BlackCardinal, BlackRook, BlackSilver, BlackFalcon } +}; #else // !(BOARD_FILES>=12) #define CourierArray CapablancaArray +#define ChuArray CapablancaArray #endif // !(BOARD_FILES>=12) @@ -1150,6 +1172,7 @@ InitBackEnd1 () case VariantCapablanca: /* [HGM] should work */ case VariantCourier: /* [HGM] initial forced moves not implemented */ case VariantShogi: /* [HGM] could still mate with pawn drop */ + case VariantChu: /* [HGM] experimental */ case VariantKnightmate: /* [HGM] should work */ case VariantCylinder: /* [HGM] untested */ case VariantFalcon: /* [HGM] untested */ @@ -1179,6 +1202,7 @@ InitBackEnd1 () case VariantSChess: /* S-Chess, should work */ case VariantGrand: /* should work */ case VariantSpartan: /* should work */ + case VariantLion: /* should work */ break; } } @@ -4992,6 +5016,11 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) char buf[MSG_SIZ]; if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') { + if(gameInfo.variant == VariantLion || gameInfo.variant == VariantChu) { + sprintf(buf, "%s@@@@\n", cps->useUsermove ? "usermove " : ""); + SendToProgram(buf, cps); + return; + } // null move in variant where engine does not understand it (for analysis purposes) SendBoard(cps, moveNum + 1); // send position after move in stead. return; @@ -5034,6 +5063,13 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) } else SendToProgram(moveList[moveNum], cps); } else + if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square + snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves + moveList[moveNum][5], moveList[moveNum][6] - '0', + moveList[moveNum][5], moveList[moveNum][6] - '0', + moveList[moveNum][2], moveList[moveNum][3] - '0'); + SendToProgram(buf, cps); + } else if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else @@ -5214,6 +5250,8 @@ UploadGameEvent () SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n"); } +int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove + void CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7]) { @@ -5225,6 +5263,7 @@ CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char if (promoChar == 'x' || promoChar == NULLCHAR) { sprintf(move, "%c%c%c%c\n", AAA + ff, ONE + rf, AAA + ft, ONE + rt); + if(killX >= 0 && killY >= 0) sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY); } else { sprintf(move, "%c%c%c%c%c\n", AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar); @@ -5245,7 +5284,8 @@ ProcessICSInitScript (FILE *f) } -static int lastX, lastY, lastLeftX, lastLeftY, selectFlag, dragging; +static int lastX, lastY, lastLeftX, lastLeftY, selectFlag; +int dragging; static ClickType lastClickType; void @@ -5265,8 +5305,8 @@ Sweep (int step) 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)); + appData.testLegality && (promoSweep == king || promoSweep == WhiteLion || promoSweep == BlackLion) || + IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep); if(toX >= 0) { int victim = boards[currentMove][toY][toX]; boards[currentMove][toY][toX] = promoSweep; @@ -5370,6 +5410,7 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -5403,7 +5444,8 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro if (appData.testLegality) { return (*moveType != IllegalMove); } else { - return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare && + return !(*fromX == *toX && *fromY == *toY && killX < 0) && boards[moveNum][*fromY][*fromX] != EmptySquare && + // [HGM] lion: if this is a double move we are less critical WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn); } @@ -5861,7 +5903,7 @@ void InitPosition (int redraw) { ChessSquare (* pieces)[BOARD_FILES]; - int i, j, pawnRow, overrule, + int i, j, pawnRow=1, pieceRows=1, overrule, oldx = gameInfo.boardWidth, oldy = gameInfo.boardHeight, oldh = gameInfo.holdingsWidth; @@ -5985,6 +6027,14 @@ InitPosition (int redraw) nrCastlingRights = 0; SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k"); break; + case VariantChu: + pieces = ChuArray; pieceRows = 3; + gameInfo.boardWidth = 12; + gameInfo.boardHeight = 12; + nrCastlingRights = 0; + SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.++.++++++++++.+++++K" + "p.brqsexogcathd.vmlifn+.++.++++++++++.+++++k"); + break; case VariantCourier: pieces = CourierArray; gameInfo.boardWidth = 12; @@ -5999,6 +6049,10 @@ InitPosition (int redraw) pieces = SpartanArray; SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k"); break; + case VariantLion: + pieces = lionArray; + SetCharTable(pieceToChar, "PNBRQ................LKpnbrq................lk"); + break; case VariantFairy: pieces = fairyArray; SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); @@ -6054,6 +6108,7 @@ InitPosition (int redraw) pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ if(pawnRow < 1) pawnRow = 1; if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN || gameInfo.variant == VariantGrand) pawnRow = 2; + if(gameInfo.variant == VariantChu) pawnRow = 3; /* User pieceToChar list overrules defaults */ if(appData.pieceToCharTable != NULL) @@ -6080,6 +6135,15 @@ InitPosition (int redraw) } } } + if(gameInfo.variant == VariantChu) { + if(j == (BOARD_WIDTH-2)/3 || j == BOARD_WIDTH - (BOARD_WIDTH+1)/3) + initialPosition[pawnRow+1][j] = WhiteCobra, + initialPosition[BOARD_HEIGHT-pawnRow-2][j] = BlackCobra; + for(i=1; i=BOARD_RGHT-1) { initialPosition[0][j] = WhiteRook; @@ -6377,9 +6441,13 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i return FALSE; piece = boards[currentMove][fromY][fromX]; - if(gameInfo.variant == VariantShogi) { + if(gameInfo.variant == VariantChu) { + int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece; + promotionZoneSize = BOARD_HEIGHT/3; + highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion; + } else if(gameInfo.variant == VariantShogi) { promotionZoneSize = BOARD_HEIGHT/3; - highestPromotingPiece = (int)WhiteFerz; + highestPromotingPiece = (int)WhiteAlfil; } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) { promotionZoneSize = 3; } @@ -6443,7 +6511,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i } // give caller the default choice even if we will not make it *promoChoice = ToLower(PieceToChar(defaultPromoChoice)); - if(gameInfo.variant == VariantShogi) *promoChoice = (defaultPromoChoice == piece ? '=' : '+'); + if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece ? '=' : '+'); if( sweepSelect && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand && gameInfo.variant != VariantSuper) return FALSE; @@ -6454,7 +6522,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i gameMode == IcsPlayingBlack && WhiteOnMove(currentMove); if(appData.testLegality && !premove) { moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), - fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR); + fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) ? '+' : NULLCHAR); if(moveType != WhitePromotion && moveType != BlackPromotion) return FALSE; } @@ -7055,15 +7123,17 @@ MarkByFEN(char *fen) DrawPosition(TRUE, NULL); } +static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES]; + void Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure) { typedef char Markers[BOARD_RANKS][BOARD_FILES]; Markers *m = (Markers *) closure; - if(rf == fromY && ff == fromX) + if(rf == fromY && ff == fromX && (killX < 0 && !(rt == rf && ft == ff) || abs(ft-killX) < 2 && abs(rt-killY) < 2)) (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant - || kind == BlackCapturesEnPassant); + || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0); else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3; } @@ -7072,7 +7142,7 @@ MarkTargetSquares (int clear) { int x, y, sum=0; if(clear) { // no reason to ever suppress clearing - for(x=0; x= BOARD_RGHT) return; } - if (clickType == Release && x == fromX && y == fromY) { + if (clickType == Release && x == fromX && y == fromY && killX < 0) { DragPieceEnd(xPix, yPix); dragging = 0; if(clearFlag) { // a deferred attempt to click-click move an empty square on top of a piece @@ -7365,7 +7441,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) clearFlag = 0; - if(gameMode != EditPosition && !appData.testLegality && !legal[y][x]) { + if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY) && !sweepSelecting) { if(dragging) DragPieceEnd(xPix, yPix), dragging = 0; DisplayMessage(_("only marked squares are legal"),""); DrawPosition(TRUE, NULL); @@ -7377,7 +7453,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) if(!sweepSelecting) { toX = x; toY = y; - } else sweepSelecting = 0; // this must be the up-click corresponding to the down-click that started the sweep + } saveAnimate = appData.animate; if (clickType == Press) { @@ -7387,11 +7463,18 @@ LeftClick (ClickType clickType, int xPix, int yPix) if(x >= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click return; } + if(dragging == 2) { // [HGM] lion: just turn buttonless drag into normal drag, and let release to the job + return; + } + if(x == killX && y == killY) { // second click on this square, which was selected as first-leg target + killX = killY = -1; // this informs us no second leg is coming, so treat as to-click without intermediate + } else + if(marker[y][x] == 5) return; // [HGM] lion: to-click on cyan square; defer action to release if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { if(appData.sweepSelect) { ChessSquare piece = boards[currentMove][fromY][fromX]; promoSweep = defaultPromoChoice; - if(PieceToChar(PROMOTED piece) == '+') promoSweep = PROMOTED piece; + if(PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece; selectFlag = 0; lastX = xPix; lastY = yPix; Sweep(0); // Pawn that is going to promote: preview promotion piece sweepSelecting = 1; @@ -7406,6 +7489,13 @@ LeftClick (ClickType clickType, int xPix, int yPix) } else { ClearHighlights(); } + } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep + sweepSelecting = 0; + if (appData.animate || appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } } else { #if 0 // [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square @@ -7416,6 +7506,17 @@ LeftClick (ClickType clickType, int xPix, int yPix) ClearHighlights(); } #endif + if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square + dragging *= 2; // flag button-less dragging if we are dragging + MarkTargetSquares(1); + if(x == killX && y == killY) killX = killY = -1; else { + killX = x; killY = y; //remeber this square as intermediate + ReportClick("put", x, y); // and inform engine + ReportClick("lift", x, y); + MarkTargetSquares(0); + return; + } + } DragPieceEnd(xPix, yPix); dragging = 0; /* Don't animate move and drag both */ appData.animate = FALSE; @@ -8190,11 +8291,12 @@ static char stashedInputMove[MSG_SIZ]; void HandleMachineMove (char *message, ChessProgramState *cps) { + static char firstLeg[20]; char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ]; char realname[MSG_SIZ]; int fromX, fromY, toX, toY; ChessMove moveType; - char promoChar; + char promoChar, roar; char *p, *pv=buf1; int machineWhite, oldError; char *bookHit; @@ -8332,14 +8434,32 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } if(cps->alphaRank) AlphaRank(machineMove, 4); + + // [HGM] lion: (some very limited) support for Alien protocol + killX = killY = -1; + if(machineMove[strlen(machineMove)-1] == ',') { // move ends in coma: non-final leg of composite move + safeStrCpy(firstLeg, machineMove, 20); // just remember it for processing when second leg arrives + return; + } else if(firstLeg[0]) { // there was a previous leg; + // only support case where same piece makes two step (and don't even test that!) + char buf[20], *p = machineMove+1, *q = buf+1, f; + safeStrCpy(buf, machineMove, 20); + while(isdigit(*q)) q++; // find start of to-square + safeStrCpy(machineMove, firstLeg, 20); + while(isdigit(*p)) p++; + safeStrCpy(p, q, 20); // glue to-square of second leg to from-square of first, to process over-all move + sscanf(buf, "%c%d", &f, &killY); killX = f - AAA; killY -= ONE - '0'; // pass intermediate square to MakeMove in global + firstLeg[0] = NULLCHAR; + } + if (!ParseOneMove(machineMove, forwardMostMove, &moveType, &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)); DisplayMoveError(buf1); - 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); + snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType); if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); @@ -8492,10 +8612,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. cps->other->maybeThinking = TRUE; } + roar = (killX >= 0 && IS_LION(boards[forwardMostMove][toY][toX])); + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ if (!pausing && appData.ringBellAfterMoves) { - RingBell(); + if(!roar) RingBell(); } /* @@ -8564,7 +8686,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if(*buf) SetCharTable(pieceToChar, buf); // do again, for it was spoiled by InitPosition } } - ParseFEN(boards[0], &dummy, message+s); + ParseFEN(boards[0], &dummy, message+s, FALSE); DrawPosition(TRUE, boards[0]); startedFromSetupPosition = TRUE; return; @@ -8577,7 +8699,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD); - if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) { + if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9, FALSE)) { DisplayError(_("Bad FEN received from engine"), 0); return ; } else { @@ -9386,6 +9508,7 @@ ParseGameHistory (char *game) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -9543,11 +9666,23 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } piece = board[toY][toX] = (ChessSquare) fromX; } else { + ChessSquare victim; int i; - if( board[toY][toX] != EmptySquare ) + if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something + victim = board[killY][killX], + board[killY][killX] = EmptySquare, board[EP_STATUS] = EP_CAPTURE; + if( board[toY][toX] != EmptySquare ) { + board[EP_STATUS] = EP_CAPTURE; + if( (fromX != toX || fromY != toY) && // not igui! + (captured == WhiteLion && board[fromY][fromX] != BlackLion || + captured == BlackLion && board[fromY][fromX] != WhiteLion ) ) { // [HGM] lion: Chu Lion-capture rules + board[EP_STATUS] = EP_IRON_LION; // non-Lion x Lion: no counter-strike allowed + } + } + 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 @@ -9728,8 +9863,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[fromY][fromX+1] = EmptySquare; } } else { - board[toY][toX] = board[fromY][fromX]; + ChessSquare piece = board[fromY][fromX]; // [HGM] lion: allow for igui (where from == to) board[fromY][fromX] = EmptySquare; + board[toY][toX] = piece; } } @@ -9812,7 +9948,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } else if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */ - board[toY][toX] = (ChessSquare) (PROMOTED piece); + board[toY][toX] = (ChessSquare) (CHUPROMOTED piece); } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified @@ -9831,19 +9967,24 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[BOARD_HEIGHT-1-k][0] = EmptySquare; } } - } /* Updates forwardMostMove */ void MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) { + int x = toX, y = toY; + char *s = parseList[forwardMostMove]; + ChessSquare p = boards[forwardMostMove][toY][toX]; // forwardMostMove++; // [HGM] bare: moved downstream + if(killX >= 0 && killY >= 0) x = killX, y = killY; // [HGM] lion: make SAN move to intermediate square, if there is one (void) CoordsToAlgebraic(boards[forwardMostMove], PosFlags(forwardMostMove), - fromY, fromX, toY, toX, promoChar, - parseList[forwardMostMove]); + fromY, fromX, y, x, promoChar, + s); + if(killX >= 0 && killY >= 0) + sprintf(s + strlen(s), "%c%c%d", p == EmptySquare || toX == fromX && toY == fromY ? '-' : 'x', toX + AAA, toY + ONE - '0'); if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */ int timeLeft; static int lastLoadFlag=0; int king, piece; @@ -9938,7 +10079,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) strcat(parseList[forwardMostMove - 1], "#"); break; } - } /* Updates currentMove if not pausing */ @@ -9958,6 +10098,8 @@ ShowMove (int fromX, int fromY, int toX, int toY) currentMove = forwardMostMove; } + killX = killY = -1; // [HGM] lion: used up + if (instant) return; DisplayMove(currentMove - 1); @@ -10030,6 +10172,8 @@ NonStandardBoardSize () overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7; if( gameInfo.variant == VariantGrand ) overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7; + if( gameInfo.variant == VariantChu ) + overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 12 || gameInfo.holdingsSize != 0; return overruled; } @@ -10765,7 +10909,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) result, resultDetails ? resultDetails : "(null)", whosays); } - fromX = fromY = -1; // [HGM] abort any move the user is entering. + fromX = fromY = killX = killY = -1; // [HGM] abort any move the user is entering. // [HGM] lion if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit) @@ -11261,6 +11405,7 @@ Reset (int redraw, int init) ClearPremoveHighlights(); gotPremove = FALSE; alarmSounded = FALSE; + killX = killY = -1; // [HGM] lion GameEnds(EndOfFile, NULL, GE_PLAYER); if(appData.serverMovesName != NULL) { @@ -11441,6 +11586,7 @@ LoadGameOneMove (ChessMove readAhead) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -11462,6 +11608,7 @@ LoadGameOneMove (ChessMove readAhead) toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; + if(promoChar == ';') promoChar = NULLCHAR; break; case WhiteDrop: @@ -11628,6 +11775,7 @@ LoadGameOneMove (ChessMove readAhead) thinkOutput[0] = NULLCHAR; MakeMove(fromX, fromY, toX, toY, promoChar); + killX = killY = -1; // [HGM] lion: used up currentMove = forwardMostMove; return TRUE; } @@ -12081,7 +12229,7 @@ GameContainsPosition (FILE *f, ListGame *lg) for(next = WhitePawn; next>8 ^ random()<<6 ^random()<<20; initDone = TRUE; } - if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen); + if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen, FALSE); else CopyBoard(boards[scratch], initialPosition); // default start position if(lg->moves) { turn = btm + 1; @@ -12125,6 +12273,7 @@ GameContainsPosition (FILE *f, ListGame *lg) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -12189,6 +12338,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) if (gameMode != BeginningOfGame) { Reset(FALSE, TRUE); } + killX = killY = -1; // [HGM] lion: in case we did not Reset gameFileFP = f; if (lastLoadGameFP != NULL && lastLoadGameFP != f) { @@ -12342,6 +12492,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) break; case NormalMove: + case FirstLeg: /* Only a NormalMove can be at the start of a game * without a position diagram. */ if (lastLoadGameStart == EndOfFile ) { @@ -12403,7 +12554,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) if (gameInfo.fen != NULL) { Board initial_position; startedFromSetupPosition = TRUE; - if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) { + if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen, TRUE)) { Reset(TRUE, TRUE); DisplayError(_("Bad FEN position in file"), 0); return FALSE; @@ -12740,7 +12891,7 @@ LoadPosition (FILE *f, int positionNumber, char *title) } if (fenMode) { - if (!ParseFEN(initial_position, &blackPlaysFirst, line)) { + if (!ParseFEN(initial_position, &blackPlaysFirst, line, TRUE)) { DisplayError(_("Bad FEN position in file"), 0); return FALSE; } @@ -13120,7 +13271,8 @@ SaveGamePGN (FILE *f) if (gameInfo.resultDetails != NULL && gameInfo.resultDetails[0] != NULLCHAR) { char buf[MSG_SIZ], *p = gameInfo.resultDetails; - if(gameInfo.result == GameUnfinished && appData.clockMode && !appData.icsActive) // [HGM] adjourn: save clock settings + if(gameInfo.result == GameUnfinished && appData.clockMode && + (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack || gameMode == TwoMachinesPlay)) // [HGM] adjourn: save clock settings snprintf(buf, MSG_SIZ, "%s (Clocks: %ld, %ld)", p, whiteTimeRemaining/1000, blackTimeRemaining/1000), p = buf; fprintf(f, "{%s} %s\n\n", p, PGNResult(gameInfo.result)); } else { @@ -14155,7 +14307,7 @@ TwoMachinesEvent P((void)) case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError(_("Wait until your turn,\nor select Move Now"), 0); + DisplayError(_("Wait until your turn,\nor select 'Move Now'."), 0); return; } /* fall through */ @@ -14592,11 +14744,15 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) { char buf[MSG_SIZ]; ChessSquare piece = boards[0][y][x]; + static Board erasedBoard, currentBoard, menuBoard, nullBoard; + static int lastVariant; if (gameMode != EditPosition && gameMode != IcsExamining) return; switch (selection) { case ClearBoard: + CopyBoard(currentBoard, boards[0]); + CopyBoard(menuBoard, initialPosition); if (gameMode == IcsExamining && ics_type == ICS_FICS) { SendToICS(ics_prefix); SendToICS("bsetup clear\n"); @@ -14604,6 +14760,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) SendToICS(ics_prefix); SendToICS("clearboard\n"); } else { + int nonEmpty = 0; for (x = 0; x < BOARD_WIDTH; x++) { ChessSquare p = EmptySquare; if(x == BOARD_LEFT-1 || x == BOARD_RGHT) p = (ChessSquare) 0; /* [HGM] holdings */ for (y = 0; y < BOARD_HEIGHT; y++) { @@ -14614,9 +14771,33 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) SendToICS(buf); } } else { + if(boards[0][y][x] != p) nonEmpty++; boards[0][y][x] = p; } } + menuBoard[1][x] = menuBoard[BOARD_HEIGHT-2][x] = p; + } + if(gameMode != IcsExamining) { // [HGM] editpos: cycle trough boards + for(x = BOARD_LEFT; x < BOARD_RGHT; x++) { // create 'menu board' by removing duplicates + ChessSquare p = menuBoard[0][x]; + for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[0][y] == p) menuBoard[0][y] = EmptySquare; + p = menuBoard[BOARD_HEIGHT-1][x]; + for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[BOARD_HEIGHT-1][y] == p) menuBoard[BOARD_HEIGHT-1][y] = EmptySquare; + } + DisplayMessage("Clicking clock again restores position", ""); + if(gameInfo.variant != lastVariant) lastVariant = gameInfo.variant, CopyBoard(erasedBoard, boards[0]); + if(!nonEmpty) { // asked to clear an empty board + CopyBoard(boards[0], menuBoard); + } else + if(CompareBoards(currentBoard, menuBoard)) { // asked to clear an empty board + CopyBoard(boards[0], initialPosition); + } else + if(CompareBoards(currentBoard, initialPosition) && !CompareBoards(currentBoard, erasedBoard) + && !CompareBoards(nullBoard, erasedBoard)) { + CopyBoard(boards[0], erasedBoard); + } else + CopyBoard(erasedBoard, currentBoard); + } } if (gameMode == EditPosition) { @@ -15270,7 +15451,7 @@ RetractMoveEvent () case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError(_("Wait until your turn,\nor select Move Now"), 0); + DisplayError(_("Wait until your turn,\nor select 'Move Now'."), 0); return; } if (forwardMostMove < 2) return; @@ -15368,14 +15549,14 @@ HintEvent () switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; @@ -15428,14 +15609,14 @@ BookEvent () switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; @@ -16488,7 +16669,7 @@ TypeInDoneEvent (char *move) ChessMove moveType; // [HGM] FENedit - if(gameMode == EditPosition && ParseFEN(board, &n, move) ) { + if(gameMode == EditPosition && ParseFEN(board, &n, move, TRUE) ) { EditPositionPasteFEN(move); return; } @@ -17377,34 +17558,31 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) } Boolean -ParseFEN (Board board, int *blackPlaysFirst, char *fen) +ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) { - int i, j; + int i, j, k, w=0; char *p, c; int emptycount, virgin[BOARD_FILES]; ChessSquare piece; p = fen; - /* [HGM] by default clear Crazyhouse holdings, if present */ - if(gameInfo.holdingsWidth) { - for(i=0; i= 0; i--) { j = 0; for (;;) { - if (*p == '/' || *p == ' ' || (*p == '[' && i == 0) ) { - if (*p == '/') p++; + if (*p == '/' || *p == ' ' || *p == '[' ) { + if(j > w) w = j; emptycount = gameInfo.boardWidth - j; while (emptycount--) board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; + if (*p == '/') p++; + else if(autoSize) { // we stumbled unexpectedly into end of board + for(k=i; k= 10) } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */ @@ -17426,7 +17604,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen) if(*p=='+') { piece = CharToPiece(*++p); if(piece == EmptySquare) return FALSE; /* unknown piece */ - piece = (ChessSquare) (PROMOTED piece ); p++; + piece = (ChessSquare) (CHUPROMOTED piece ); p++; if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */ } else piece = CharToPiece(*p++); @@ -17444,6 +17622,18 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen) } while (*p == '/' || *p == ' ') p++; + if(autoSize) appData.NrFiles = w, InitPosition(TRUE); + + /* [HGM] by default clear Crazyhouse holdings, if present */ + if(gameInfo.holdingsWidth) { + for(i=0; i