X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=fac9abdc871ff1251cc353f5d84c1e76c350faf4;hb=da8802ac4d06115296e0f8ba955ecf5570741d08;hp=4249d7c7a70b5753c781c1dab1bcd2ebaba1d360;hpb=fa1ce90bbcd27d332afebd9ecbd4e0ed662c4940;p=xboard.git diff --git a/backend.c b/backend.c index 4249d7c..fac9abd 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] = { @@ -1195,6 +1202,7 @@ InitBackEnd1 () case VariantSChess: /* S-Chess, should work */ case VariantGrand: /* should work */ case VariantSpartan: /* should work */ + case VariantLion: /* should work */ break; } } @@ -5055,6 +5063,11 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0], moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4); + } 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'); } else snprintf(buf, MSG_SIZ, "%c%d%c%d%s", moveList[moveNum][0], moveList[moveNum][1] - '0', moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4); @@ -5230,6 +5243,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]) { @@ -5241,6 +5256,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); @@ -5281,8 +5297,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; @@ -5386,6 +5402,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: @@ -6006,8 +6023,8 @@ InitPosition (int redraw) gameInfo.boardWidth = 12; gameInfo.boardHeight = 12; nrCastlingRights = 0; - SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.+++++++++++++.+++++K" - "p.brqsexogcathd.vmlifn+.+++++++++++++.+++++k"); + SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.++.++++++++++.+++++K" + "p.brqsexogcathd.vmlifn+.++.++++++++++.+++++k"); break; case VariantCourier: pieces = CourierArray; @@ -6023,6 +6040,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"); @@ -6411,9 +6432,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 = (int)WhiteFerz; + highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion; + } else if(gameInfo.variant == VariantShogi) { + promotionZoneSize = BOARD_HEIGHT/3; + highestPromotingPiece = (int)WhiteAlfil; } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) { promotionZoneSize = 3; } @@ -6477,7 +6502,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; @@ -6488,7 +6513,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; } @@ -7094,10 +7119,10 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO { 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; } @@ -7143,7 +7168,7 @@ 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 || + if(IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) return FALSE; @@ -7154,15 +7179,20 @@ CanPromote (ChessSquare piece, int y) } void -HoverEvent (int hiX, int hiY, int x, int y) +HoverEvent (int xPix, int yPix, int x, int y) { static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES]; + static int oldX = -1, oldY = -1, oldFromX = -1, oldFromY = -1; int r, f; + if(dragging == 2) DragPieceMove(xPix, yPix); // [HGM] lion: drag without button for second leg if(!first.highlight) return; - if(hiX == -1 && hiY == -1 && x == fromX && y == fromY) // record markings + if(fromX != oldFromX || fromY != oldFromY) oldX = oldY = -1; // kludge to fake entry on from-click + if(x == oldX && y == oldY) return; // only do something if we enter new square + oldFromX = fromX; oldFromY = fromY; + if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) // record markings after from-change for(r=0; r= 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 @@ -7399,7 +7432,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); @@ -7411,7 +7444,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) { @@ -7421,11 +7454,19 @@ 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 + dragging = 1; + 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; @@ -7440,6 +7481,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 @@ -7450,6 +7498,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 + MarkTargetSquares(0); + ReportClick("put", x, y); // and inform engine + ReportClick("lift", x, y); + return; + } + } DragPieceEnd(xPix, yPix); dragging = 0; /* Don't animate move and drag both */ appData.animate = FALSE; @@ -8224,11 +8283,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; @@ -8366,6 +8426,24 @@ 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. */ @@ -8526,10 +8604,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(); } /* @@ -9420,6 +9500,7 @@ ParseGameHistory (char *game) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -9577,11 +9658,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 @@ -9762,8 +9855,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; } } @@ -9846,7 +9940,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 @@ -9865,19 +9959,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; @@ -9972,7 +10071,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) strcat(parseList[forwardMostMove - 1], "#"); break; } - } /* Updates currentMove if not pausing */ @@ -9992,6 +10090,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); @@ -10801,7 +10901,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) @@ -11297,6 +11397,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) { @@ -11477,6 +11578,7 @@ LoadGameOneMove (ChessMove readAhead) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -11498,6 +11600,7 @@ LoadGameOneMove (ChessMove readAhead) toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; + if(promoChar == ';') promoChar = NULLCHAR; break; case WhiteDrop: @@ -11664,6 +11767,7 @@ LoadGameOneMove (ChessMove readAhead) thinkOutput[0] = NULLCHAR; MakeMove(fromX, fromY, toX, toY, promoChar); + killX = killY = -1; // [HGM] lion: used up currentMove = forwardMostMove; return TRUE; } @@ -12161,6 +12265,7 @@ GameContainsPosition (FILE *f, ListGame *lg) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -12225,6 +12330,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) { @@ -12378,6 +12484,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 ) { @@ -17489,7 +17596,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) 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++);