X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=7c96808e68adb63c3f940d75f7dad2e31fe6c28a;hb=4aed6e1fe3f9c75dbb37ab41e81aed204bba602f;hp=0e0f80eec18f95d78e9a46defdb1b336a71bd612;hpb=2cbef99e7202b7af01c5fef277501fc5baa7b50a;p=xboard.git diff --git a/backend.c b/backend.c index 0e0f80e..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, @@ -1195,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; } } @@ -2059,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; @@ -5008,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; @@ -5050,16 +5073,18 @@ 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 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); @@ -5235,7 +5260,7 @@ UploadGameEvent () SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n"); } -static int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove +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]) @@ -5269,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 @@ -5289,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 || - IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED 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; @@ -5394,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: @@ -5427,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); } @@ -6031,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"); @@ -6085,7 +6123,8 @@ 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 */ @@ -6100,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) { @@ -6122,14 +6161,15 @@ InitPosition (int redraw) initialPosition[BOARD_HEIGHT-1-i][j] = pieces[2*i+1][j-gameInfo.holdingsWidth]; } } - if(gameInfo.variant == VariantGrand) { + if(gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { if(j==BOARD_LEFT || j>=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; @@ -6422,11 +6462,11 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if(gameInfo.variant == VariantChu) { int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece; promotionZoneSize = BOARD_HEIGHT/3; - highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteKing; - } else if(gameInfo.variant == VariantShogi) { + 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) { + } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { promotionZoneSize = 3; } @@ -6440,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 @@ -6490,6 +6533,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(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; @@ -6500,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, IS_SHOGI(gameInfo.variant) ? '+' : NULLCHAR); + fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantChuChess ? '+' : NULLCHAR); if(moveType != WhitePromotion && moveType != BlackPromotion) return FALSE; } @@ -7101,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; } @@ -7118,7 +7164,7 @@ MarkTargetSquares (int clear) { int x, y, sum=0; if(clear) { // no reason to ever suppress clearing - for(x=0; xwhich)); 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); @@ -8581,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(); } /* @@ -8635,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; @@ -8766,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)) { @@ -9475,6 +9539,7 @@ ParseGameHistory (char *game) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -9615,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 */ @@ -9632,14 +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( 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 ) + 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 ) @@ -9907,6 +9980,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */ 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 @@ -9925,7 +10000,6 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[BOARD_HEIGHT-1-k][0] = EmptySquare; } } - } /* Updates forwardMostMove */ @@ -10038,8 +10112,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) strcat(parseList[forwardMostMove - 1], "#"); break; } - - killX = killY = -1; // [HGM] lion: used up } /* Updates currentMove if not pausing */ @@ -10059,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); @@ -10107,40 +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; - if( gameInfo.variant == VariantChu ) - overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 12 || gameInfo.holdingsSize != 0; - 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; @@ -10162,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); } @@ -10228,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; @@ -10868,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) @@ -11267,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); @@ -11364,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) { @@ -11544,6 +11637,7 @@ LoadGameOneMove (ChessMove readAhead) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -11565,6 +11659,7 @@ LoadGameOneMove (ChessMove readAhead) toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; + if(promoChar == ';') promoChar = NULLCHAR; break; case WhiteDrop: @@ -11731,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; } @@ -12228,6 +12324,7 @@ GameContainsPosition (FILE *f, ListGame *lg) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -12292,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) { @@ -12445,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 ) { @@ -14294,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;