X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=7c96808e68adb63c3f940d75f7dad2e31fe6c28a;hb=4aed6e1fe3f9c75dbb37ab41e81aed204bba602f;hp=4633377bf5b26ad51427b0edf9a4b29adfc15891;hpb=f75d0a5c5ff9825398ac4b11644d4cb8c14252a6;p=xboard.git diff --git a/backend.c b/backend.c index 4633377..7c96808 100644 --- a/backend.c +++ b/backend.c @@ -225,7 +225,7 @@ void DisplayTwoMachinesTitle P(()); static void ExcludeClick P((int index)); void ToggleSecond P((void)); void PauseEngine P((ChessProgramState *cps)); -static int NonStandardBoardSize P((void)); +static int NonStandardBoardSize P((VariantClass v, int w, int h, int s)); #ifdef WIN32 extern void ConsoleCreate(); @@ -275,6 +275,7 @@ ChessSquare pieceSweep = EmptySquare; ChessSquare promoSweep = EmptySquare, defaultPromoChoice; int promoDefaultAltered; int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */ +static int initPing = -1; /* States for ics_getting_history */ #define H_FALSE 0 @@ -566,6 +567,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] = { @@ -610,6 +618,13 @@ ChessSquare GrandArray[2][BOARD_FILES] = { BlackMarshall, BlackAngel, BlackBishop, BlackKnight, EmptySquare } }; +ChessSquare ChuChessArray[2][BOARD_FILES] = { + { WhiteMan, WhiteKnight, WhiteBishop, WhiteCardinal, WhiteLion, + WhiteQueen, WhiteDragon, WhiteBishop, WhiteKnight, WhiteMan }, + { BlackMan, BlackKnight, BlackBishop, BlackDragon, BlackQueen, + BlackLion, BlackCardinal, BlackBishop, BlackKnight, BlackMan } +}; + #ifdef GOTHIC ChessSquare GothicArray[2][BOARD_FILES] = { { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, @@ -646,8 +661,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 +1180,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 +1210,8 @@ InitBackEnd1 () case VariantSChess: /* S-Chess, should work */ case VariantGrand: /* should work */ case VariantSpartan: /* should work */ + case VariantLion: /* should work */ + case VariantChuChess: /* should work */ break; } } @@ -2043,7 +2076,8 @@ StringToVariant (char *e) found = TRUE; } else for (i=0; i= VariantShogi && isalpha(p[strlen(variantNames[i])])) continue; v = (VariantClass) i; found = TRUE; break; @@ -4992,6 +5026,11 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) char buf[MSG_SIZ]; if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') { + if(gameInfo.variant == VariantLion || gameInfo.variant == VariantChuChess || 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 +5073,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 +5260,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 +5273,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 +5294,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 +5315,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 +5420,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 +5454,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 +5913,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 +6037,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 +6059,16 @@ 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 VariantChuChess: + pieces = ChuChessArray; + gameInfo.boardWidth = 10; + gameInfo.boardHeight = 10; + SetCharTable(pieceToChar, "PNBRQ.....M.+++......LKpnbrq.....m.+++......lk"); + break; case VariantFairy: pieces = fairyArray; SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); @@ -6053,7 +6123,9 @@ 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 == VariantMakruk || gameInfo.variant == VariantASEAN || + gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) pawnRow = 2; + if(gameInfo.variant == VariantChu) pawnRow = 3; /* User pieceToChar list overrules defaults */ if(appData.pieceToCharTable != NULL) @@ -6067,7 +6139,7 @@ InitPosition (int redraw) initialPosition[i][j] = s; if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue; - initialPosition[gameInfo.variant == VariantGrand][j] = pieces[0][j-gameInfo.holdingsWidth]; + initialPosition[gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess][j] = pieces[0][j-gameInfo.holdingsWidth]; initialPosition[pawnRow][j] = WhitePawn; initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn; if(gameInfo.variant == VariantXiangqi) { @@ -6080,14 +6152,24 @@ InitPosition (int redraw) } } } - if(gameInfo.variant == VariantGrand) { + 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; initialPosition[BOARD_HEIGHT-1][j] = BlackRook; } } - initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand)][j] = pieces[1][j-gameInfo.holdingsWidth]; + initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess)][j] = pieces[1][j-gameInfo.holdingsWidth]; } + if(gameInfo.variant == VariantChuChess) initialPosition[0][BOARD_WIDTH/2] = WhiteKing, initialPosition[BOARD_HEIGHT-1][BOARD_WIDTH/2-1] = BlackKing; if( (gameInfo.variant == VariantShogi) && !overrule ) { j=BOARD_LEFT+1; @@ -6377,10 +6459,14 @@ 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; - } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) { + highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion; + } else if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantChuChess) { + promotionZoneSize = BOARD_HEIGHT/3; + highestPromotingPiece = (int)WhiteAlfil; + } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { promotionZoneSize = 3; } @@ -6394,10 +6480,13 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if((int)piece >= BlackPawn) { if(toY >= promotionZoneSize && fromY >= promotionZoneSize) return FALSE; + if(fromY < promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE; highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece; } else { if( toY < BOARD_HEIGHT - promotionZoneSize && fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE; + if(fromY >= BOARD_HEIGHT - promotionZoneSize && gameInfo.variant == VariantChuChess) + return FALSE; } if( (int)piece > highestPromotingPiece ) return FALSE; // non-promoting piece @@ -6443,7 +6532,8 @@ 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(gameInfo.variant == VariantChuChess) *promoChoice = (piece == WhitePawn || piece == BlackPawn ? 'q' : '+'); if( sweepSelect && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand && gameInfo.variant != VariantSuper) return FALSE; @@ -6454,7 +6544,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) || gameInfo.variant == VariantChuChess ? '+' : NULLCHAR); if(moveType != WhitePromotion && moveType != BlackPromotion) return FALSE; } @@ -7055,15 +7145,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 +7164,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 +7463,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 +7475,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 +7485,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 +7511,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 +7528,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 +8313,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 +8456,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 +8634,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(); } /* @@ -8546,7 +8690,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } if (!strncmp(message, "setup ", 6) && - (!appData.testLegality || gameInfo.variant == VariantFairy || gameInfo.variant == VariantUnknown || NonStandardBoardSize()) + (!appData.testLegality || gameInfo.variant == VariantFairy || gameInfo.variant == VariantUnknown || + NonStandardBoardSize(gameInfo.variant, gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize)) ) { // [HGM] allow first engine to define opening position int dummy, w, h, hand, s=6; char buf[MSG_SIZ], varName[MSG_SIZ]; if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; @@ -8677,6 +8822,14 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } } if (sscanf(message, "pong %d", &cps->lastPong) == 1) { + if(initPing == cps->lastPong) { + if(gameInfo.variant == VariantUnknown) { + DisplayError(_("Engine did not send setup for non-standard variant"), 0); + *engineVariant = NULLCHAR; appData.variant = VariantNormal; // back to normal as error recovery? + GameEnds(GameUnfinished, NULL, GE_XBOARD); + } + initPing = -1; + } return; } if(!strncmp(message, "highlight ", 10)) { @@ -9386,6 +9539,7 @@ ParseGameHistory (char *game) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -9526,7 +9680,7 @@ void ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) { ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0; - int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1; + int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1; /* [HGM] compute & store e.p. status and castling rights for new position */ /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */ @@ -9543,10 +9697,22 @@ 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 ) @@ -9728,8 +9894,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 +9979,9 @@ 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); + if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight)) + board[toY][toX] = piece + WhiteLion - WhiteKnight; // adjust Knight promotions to Lion } 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 +10000,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 +10112,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) strcat(parseList[forwardMostMove - 1], "#"); break; } - } /* Updates currentMove if not pausing */ @@ -9958,6 +10131,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); @@ -10006,38 +10181,75 @@ SendEgtPath (ChessProgramState *cps) } static int -NonStandardBoardSize () -{ - /* [HGM] Awkward testing. Should really be a table */ - int overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantUnknown || *engineVariant) return 0; // engine-defined name never needs prefix - if( gameInfo.variant == VariantXiangqi ) - overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantShogi ) - overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7; - 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 == 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; - if( gameInfo.variant == VariantSuper ) - 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( gameInfo.variant == VariantGrand ) - overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7; - return overruled; +NonStandardBoardSize (VariantClass v, int boardWidth, int boardHeight, int holdingsSize) +{ + int width = 8, height = 8, holdings = 0; // most common sizes + if( v == VariantUnknown || *engineVariant) return 0; // engine-defined name never needs prefix + // correct the deviations default for each variant + if( v == VariantXiangqi ) width = 9, height = 10; + if( v == VariantShogi ) width = 9, height = 9, holdings = 7; + if( v == VariantBughouse || v == VariantCrazyhouse) holdings = 5; + if( v == VariantCapablanca || v == VariantCapaRandom || + v == VariantGothic || v == VariantFalcon || v == VariantJanus ) + width = 10; + if( v == VariantCourier ) width = 12; + if( v == VariantSuper ) holdings = 8; + if( v == VariantGreat ) width = 10, holdings = 8; + if( v == VariantSChess ) holdings = 7; + if( v == VariantGrand ) width = 10, height = 10, holdings = 7; + if( v == VariantChuChess) width = 10, height = 10; + if( v == VariantChu ) width = 12, height = 12; + return boardWidth >= 0 && boardWidth != width || // -1 is default, + boardHeight >= 0 && boardHeight != height || // and thus by definition OK + holdingsSize >= 0 && holdingsSize != holdings; +} + +char variantError[MSG_SIZ]; + +char * +SupportedVariant (char *list, VariantClass v, int boardWidth, int boardHeight, int holdingsSize, int proto, char *engine) +{ // returns error message (recognizable by upper-case) if engine does not support the variant + char *p, *variant = VariantName(v); + static char b[MSG_SIZ]; + if(NonStandardBoardSize(v, boardWidth, boardHeight, holdingsSize)) { /* [HGM] make prefix for non-standard board size. */ + snprintf(b, MSG_SIZ, "%dx%d+%d_%s", boardWidth, boardHeight, + holdingsSize, variant); // cook up sized variant name + /* [HGM] varsize: try first if this deviant size variant is specifically known */ + if(StrStr(list, b) == NULL) { + // specific sized variant not known, check if general sizing allowed + if(proto != 1 && StrStr(list, "boardsize") == NULL) { + snprintf(variantError, MSG_SIZ, "Board size %dx%d+%d not supported by %s", + boardWidth, boardHeight, holdingsSize, engine); + return NULL; + } + /* [HGM] here we really should compare with the maximum supported board size */ + } + } else snprintf(b, MSG_SIZ,"%s", variant); + if(proto == 1) return b; // for protocol 1 we cannot check and hope for the best + p = StrStr(list, b); + while(p && (p != list && p[-1] != ',' || p[strlen(b)] && p[strlen(b)] != ',') ) p = StrStr(p+1, b); + if(p == NULL) { + // occurs not at all in list, or only as sub-string + snprintf(variantError, MSG_SIZ, _("Variant %s not supported by %s"), b, engine); + if(p = StrStr(list, b)) { // handle requesting parent variant when only size-overridden is supported + int l = strlen(variantError); + char *q; + while(p != list && p[-1] != ',') p--; + q = strchr(p, ','); + if(q) *q = NULLCHAR; + snprintf(variantError + l, MSG_SIZ - l, _(", but %s is"), p); + if(q) *q= ','; + } + return NULL; + } + return b; } void InitChessProgram (ChessProgramState *cps, int setup) /* setup needed to setup FRC opening position */ { - char buf[MSG_SIZ], b[MSG_SIZ]; + char buf[MSG_SIZ], *b; if (appData.noChessProgram) return; hintRequested = FALSE; bookRequested = FALSE; @@ -10059,33 +10271,15 @@ InitChessProgram (ChessProgramState *cps, int setup) if (gameInfo.variant != VariantNormal && gameInfo.variant != VariantLoadable /* [HGM] also send variant if board size non-standard */ - || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0 - ) { - char *v = VariantName(gameInfo.variant); - if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) { - /* [HGM] in protocol 1 we have to assume all variants valid */ - snprintf(buf, MSG_SIZ, _("Variant %s not supported by %s"), v, cps->tidy); - DisplayFatalError(buf, 0, 1); + || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0) { + + b = SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, cps->tidy); + if (b == NULL) { + DisplayFatalError(variantError, 0, 1); return; } - if(NonStandardBoardSize()) { /* [HGM] make prefix for non-standard board size. */ - snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, - gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name - /* [HGM] varsize: try first if this defiant size variant is specifically known */ - if(StrStr(cps->variants, b) == NULL) { - // specific sized variant not known, check if general sizing allowed - if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best - if(StrStr(cps->variants, "boardsize") == NULL) { - snprintf(buf, MSG_SIZ, "Board size %dx%d+%d not supported by %s", - gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy); - DisplayFatalError(buf, 0, 1); - return; - } - /* [HGM] here we really should compare with the maximum supported board size */ - } - } - } else snprintf(b, MSG_SIZ,"%s", VariantName(gameInfo.variant)); snprintf(buf, MSG_SIZ, "variant %s\n", b); SendToProgram(buf, cps); } @@ -10125,7 +10319,7 @@ InitChessProgram (ChessProgramState *cps, int setup) SendToProgram("easy\n", cps); } if (cps->usePing) { - snprintf(buf, MSG_SIZ, "ping %d\n", ++cps->lastPing); + snprintf(buf, MSG_SIZ, "ping %d\n", initPing = ++cps->lastPing); SendToProgram(buf, cps); } cps->initDone = TRUE; @@ -10765,7 +10959,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) @@ -11164,7 +11358,8 @@ FeedMovesToProgram (ChessProgramState *cps, int upto) if(currentlyInitializedVariant != gameInfo.variant) { char buf[MSG_SIZ]; // [HGM] variantswitch: make engine aware of new variant - if(cps->protocolVersion > 1 && StrStr(cps->variants, VariantName(gameInfo.variant)) == NULL) + if(!SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, "")) return; // [HGM] refrain from feeding moves altogether if variant is unsupported! snprintf(buf, MSG_SIZ, "variant %s\n", VariantName(gameInfo.variant)); SendToProgram(buf, cps); @@ -11261,6 +11456,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 +11637,7 @@ LoadGameOneMove (ChessMove readAhead) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -11462,6 +11659,7 @@ LoadGameOneMove (ChessMove readAhead) toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; + if(promoChar == ';') promoChar = NULLCHAR; break; case WhiteDrop: @@ -11628,6 +11826,7 @@ LoadGameOneMove (ChessMove readAhead) thinkOutput[0] = NULLCHAR; MakeMove(fromX, fromY, toX, toY, promoChar); + killX = killY = -1; // [HGM] lion: used up currentMove = forwardMostMove; return TRUE; } @@ -12125,6 +12324,7 @@ GameContainsPosition (FILE *f, ListGame *lg) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -12189,6 +12389,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 +12543,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 ) { @@ -14191,7 +14393,8 @@ TwoMachinesEvent P((void)) } if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features - if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) { + if(!SupportedVariant(second.variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, second.protocolVersion, second.tidy)) { startingEngine = FALSE; DisplayError("second engine does not play this", 0); return; @@ -17453,7 +17656,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++);