From: H.G. Muller Date: Thu, 10 Mar 2011 19:06:39 +0000 (+0100) Subject: Implement Grand Chess X-Git-Url: http://winboard.nl/cgi-bin?p=xboard.git;a=commitdiff_plain;h=f2344ce98b5950c7a047c3ee29959ad9f26ae8ae Implement Grand Chess Grand Chess requires a lot of exceptional handling similar to Superchess and Great Shatran, because it also allows only promotion to a captured piece. In addition it shares the enlarged promotion zone with Makruk and Shogi, as well as the fact that promotion can be deferred. The 10-rank board furthermore required a better generalization of double-pushes and e.p. captures. On such a board double puhes are now allowed from 2nd and 3rd rank, e.p. capture from 5th and 4th. --- diff --git a/backend.c b/backend.c index 2cbe2e0..b914fe4 100644 --- a/backend.c +++ b/backend.c @@ -396,6 +396,7 @@ PosFlags(index) case VariantShatranj: case VariantCourier: case VariantMakruk: + case VariantGrand: flags &= ~F_ALL_CASTLE_OK; break; default: @@ -599,6 +600,13 @@ ChessSquare JanusArray[2][BOARD_FILES] = { BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook } }; +ChessSquare GrandArray[2][BOARD_FILES] = { + { EmptySquare, WhiteKnight, WhiteBishop, WhiteQueen, WhiteKing, + WhiteMarshall, WhiteAngel, WhiteBishop, WhiteKnight, EmptySquare }, + { EmptySquare, BlackKnight, BlackBishop, BlackQueen, BlackKing, + BlackMarshall, BlackAngel, BlackBishop, BlackKnight, EmptySquare } +}; + #ifdef GOTHIC ChessSquare GothicArray[2][BOARD_FILES] = { { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, @@ -1112,13 +1120,14 @@ InitBackEnd1() case VariantAtomic: /* should work except for win condition */ case Variant3Check: /* should work except for win condition */ case VariantShatranj: /* should work except for all win conditions */ - case VariantMakruk: /* should work except for daw countdown */ + case VariantMakruk: /* should work except for draw countdown */ case VariantBerolina: /* might work if TestLegality is off */ case VariantCapaRandom: /* should work */ case VariantJanus: /* should work */ case VariantSuper: /* experimental */ case VariantGreat: /* experimental, requires legality testing to be off */ case VariantSChess: /* S-Chess, should work */ + case VariantGrand: /* should work */ case VariantSpartan: /* should work */ break; } @@ -4641,8 +4650,8 @@ ParseBoard12(string) safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0])); strcat(moveList[moveNum - 1], "\n"); - if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper - && gameInfo.variant != VariantGreat) // inherit info that ICS does not give from previous board + if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat + && gameInfo.variant != VariantGrand) // inherit info that ICS does not give from previous board for(k=0; k= BOARD_RGHT || overrule) continue; - initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth]; + initialPosition[gameInfo.variant == VariantGrand][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) { @@ -5894,7 +5911,13 @@ InitPosition(redraw) } } } - initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j-gameInfo.holdingsWidth]; + if(gameInfo.variant == VariantGrand) { + 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]; } if( (gameInfo.variant == VariantShogi) && !overrule ) { @@ -6073,7 +6096,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) if(gameInfo.variant == VariantShogi) { promotionZoneSize = BOARD_HEIGHT/3; highestPromotingPiece = (int)WhiteFerz; - } else if(gameInfo.variant == VariantMakruk) { + } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) { promotionZoneSize = 3; } @@ -6519,8 +6542,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) { char *bookHit = 0; - if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { - // [HGM] superchess: suppress promotions to non-available piece + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && promoChar != NULLCHAR) { + // [HGM] superchess: suppress promotions to non-available piece (but P always allowed) int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); if(WhiteOnMove(currentMove)) { if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0; @@ -6802,13 +6825,14 @@ void LeftClick(ClickType clickType, int xPix, int yPix) if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y); if(gameInfo.holdingsWidth && (WhiteOnMove(currentMove) - ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0 - : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) { + ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y >= 0 + : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT) ) { // click in right holdings, for determining promotion piece ChessSquare p = boards[currentMove][y][x]; if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p); - if(p != EmptySquare) { - FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p))); + if(p == WhitePawn || p == BlackPawn) p = EmptySquare; // [HGM] Pawns could be valid as deferral + if(p != EmptySquare || gameInfo.variant == VariantGrand && toY != 0 && toY != BOARD_HEIGHT-1) { // [HGM] grand: empty square means defer + FinishMove(NormalMove, fromX, fromY, toX, toY, p==EmptySquare ? NULLCHAR : ToLower(PieceToChar(p))); fromX = fromY = -1; return; } @@ -7017,7 +7041,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) { SetHighlights(fromX, fromY, toX, toY); - if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) { // [HGM] super: promotion to captured piece selected from holdings ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; promotionChoice = TRUE; @@ -8956,7 +8980,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) Board board; { ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0; - int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1; + int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 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 */ @@ -9054,18 +9078,15 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[fromY][BOARD_LEFT] = EmptySquare; } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi || board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) - && toY >= BOARD_HEIGHT-promoRank + && toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere ) { /* white pawn promotion */ board[toY][toX] = CharToPiece(ToUpper(promoChar)); - if (board[toY][toX] == EmptySquare) { - board[toY][toX] = WhiteQueen; - } if(gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */ board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]); board[fromY][fromX] = EmptySquare; - } else if ((fromY == BOARD_HEIGHT-4) + } else if ((fromY >= BOARD_HEIGHT>>1) && (toX != fromX) && gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantBerolina @@ -9118,18 +9139,15 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][2] = BlackRook; } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi || board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) - && toY < promoRank + && toY < promoRank && promoChar ) { /* black pawn promotion */ board[toY][toX] = CharToPiece(ToLower(promoChar)); - if (board[toY][toX] == EmptySquare) { - board[toY][toX] = BlackQueen; - } if(gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */ board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]); board[fromY][fromX] = EmptySquare; - } else if ((fromY == 3) + } else if ((fromY < BOARD_HEIGHT>>1) && (toX != fromX) && gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantBerolina @@ -9190,7 +9208,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) if (captured != EmptySquare && gameInfo.holdingsSize > 0 && gameInfo.variant != VariantBughouse && gameInfo.variant != VariantSChess ) { /* [HGM] holdings: Add to holdings, if holdings exist */ - if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) { // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured; } @@ -9241,8 +9259,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); } - if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) - && promoChar != NULLCHAR && gameInfo.holdingsSize) { + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) + && promoChar != NULLCHAR && gameInfo.holdingsSize) { // [HGM] superchess: take promotion piece out of holdings int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); if((int)piece < (int)BlackPawn) { // determine stm from piece color @@ -9470,6 +9488,8 @@ InitChessProgram(cps, setup) 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(overruled) { snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, @@ -10023,7 +10043,7 @@ GameEnds(result, resultDetails, whosays) // now verify win claims, but not in drop games, as we don't understand those yet if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper - || gameInfo.variant == VariantGreat) && + || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && (result == WhiteWins && claimer == 'w' || result == BlackWins && claimer == 'b' ) ) { // case to verify: engine claims own win if (appData.debugMode) { @@ -10049,7 +10069,8 @@ GameEnds(result, resultDetails, whosays) /* (Claiming a loss is accepted no questions asked!) */ } /* [HGM] bare: don't allow bare King to win */ - if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper + || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course... && result != GameIsDrawn) diff --git a/common.h b/common.h index fadc73d..9423537 100644 --- a/common.h +++ b/common.h @@ -320,6 +320,7 @@ typedef enum { VariantTwilight, VariantMakruk, VariantSChess, + VariantGrand, VariantSpartan, VariantUnknown /* Catchall for other unknown variants */ } VariantClass; @@ -365,6 +366,7 @@ typedef enum { "twilight",\ "makruk",\ "seirawan",\ + "grand",\ "spartan",\ "unknown" \ } diff --git a/moves.c b/moves.c index 3bbf526..89728e1 100644 --- a/moves.c +++ b/moves.c @@ -179,7 +179,7 @@ void GenPseudoLegal(board, flags, callback, closure, filter) int rf, ff; int i, j, d, s, fs, rs, rt, ft, m; int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board - int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1; + int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1; for (rf = 0; rf < BOARD_HEIGHT; rf++) for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) { @@ -228,12 +228,12 @@ void GenPseudoLegal(board, flags, callback, closure, filter) rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove, rf, ff, rf + 1, ff, closure); } - if (rf == 1 && board[2][ff] == EmptySquare && + if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board gameInfo.variant != VariantShatranj && /* [HGM] */ gameInfo.variant != VariantCourier && /* [HGM] */ - board[3][ff] == EmptySquare ) { + board[rf+2][ff] == EmptySquare ) { callback(board, flags, NormalMove, - rf, ff, 3, ff, closure); + rf, ff, rf+2, ff, closure); } for (s = -1; s <= 1; s += 2) { if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && @@ -243,13 +243,13 @@ void GenPseudoLegal(board, flags, callback, closure, filter) rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove, rf, ff, rf + 1, ff + s, closure); } - if (rf == BOARD_HEIGHT-4) { + if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && - (epfile == ff + s || epfile == EP_UNKNOWN) && - board[BOARD_HEIGHT-4][ff + s] == BlackPawn && - board[BOARD_HEIGHT-3][ff + s] == EmptySquare) { + (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 && + board[rf][ff + s] == BlackPawn && + board[rf+1][ff + s] == EmptySquare) { callback(board, flags, WhiteCapturesEnPassant, - rf, ff, 5, ff + s, closure); + rf, ff, rf+1, ff + s, closure); } } } @@ -278,12 +278,12 @@ void GenPseudoLegal(board, flags, callback, closure, filter) rf <= promoRank ? BlackPromotion : NormalMove, rf, ff, rf - 1, ff, closure); } - if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare && + if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand gameInfo.variant != VariantShatranj && /* [HGM] */ gameInfo.variant != VariantCourier && /* [HGM] */ - board[BOARD_HEIGHT-4][ff] == EmptySquare) { + board[rf-2][ff] == EmptySquare) { callback(board, flags, NormalMove, - rf, ff, BOARD_HEIGHT-4, ff, closure); + rf, ff, rf-2, ff, closure); } for (s = -1; s <= 1; s += 2) { if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && @@ -293,13 +293,13 @@ void GenPseudoLegal(board, flags, callback, closure, filter) rf <= promoRank ? BlackPromotion : NormalMove, rf, ff, rf - 1, ff + s, closure); } - if (rf == 3) { + if (rf < BOARD_HEIGHT>>1) { if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && - (epfile == ff + s || epfile == EP_UNKNOWN) && - board[3][ff + s] == WhitePawn && - board[2][ff + s] == EmptySquare) { + (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 && + board[rf][ff + s] == WhitePawn && + board[rf-1][ff + s] == EmptySquare) { callback(board, flags, BlackCapturesEnPassant, - rf, ff, 2, ff + s, closure); + rf, ff, rf-1, ff + s, closure); } } } @@ -1194,7 +1194,8 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo for(r=0; r 0) { return inCheck ? MT_CHECK : MT_NONE; } else { - if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // drop game + if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat + && gameInfo.variant != VariantGrand) { // drop game int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0; for(r=0; rkind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess else c = PieceToChar(BlackQueen); } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi diff --git a/winboard/resource.h b/winboard/resource.h index aa1cda7..666e76e 100644 --- a/winboard/resource.h +++ b/winboard/resource.h @@ -500,6 +500,7 @@ #define OPT_VariantMakruk 1529 #define OPT_VariantSChess 1530 #define OPT_VariantSpartan 1531 +#define OPT_VariantGrand 1534 #define IDC_Files 1550 #define IDC_Ranks 1551 #define IDC_Holdings 1552 diff --git a/winboard/winboard.rc b/winboard/winboard.rc index fcd368a..9644570 100644 --- a/winboard/winboard.rc +++ b/winboard/winboard.rc @@ -711,7 +711,9 @@ BEGIN 84,70,10 CONTROL "Seira&wan",OPT_VariantSChess,"Button",BS_AUTORADIOBUTTON,154,94,70, 10 - CONTROL "&xiangqi",OPT_VariantXiangqi,"Button",BS_AUTORADIOBUTTON,154,104,70, + CONTROL "&Grand",OPT_VariantGrand,"Button",BS_AUTORADIOBUTTON,154,104,70, + 10 + CONTROL "&xiangqi",OPT_VariantXiangqi,"Button",BS_AUTORADIOBUTTON,154,114,70, 10 GROUPBOX "Variant",GPB_Variant,4,4,215,125 LTEXT "Board size:",GPB_Board,10,165,40,8,WS_TABSTOP diff --git a/winboard/woptions.c b/winboard/woptions.c index ceea3df..00aca12 100644 --- a/winboard/woptions.c +++ b/winboard/woptions.c @@ -819,6 +819,7 @@ int radioButton[] = { -1, // Twilight, OPT_VariantMakruk, OPT_VariantSChess, + OPT_VariantGrand, OPT_VariantSpartan, // Spartan -2 // sentinel }; diff --git a/xoptions.c b/xoptions.c index db1dfe6..726dcc7 100644 --- a/xoptions.c +++ b/xoptions.c @@ -453,8 +453,9 @@ Option variantDescriptors[] = { { VariantSuicide, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("suicide")}, { VariantCapaRandom, 1, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("CRC (10x8)")}, { VariantGiveaway, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("give-away")}, -{ VariantSpartan, 1, 135, NULL, (void*) &Pick, "#FF0000", NULL, Button, N_("Spartan")}, +{ VariantGrand, 1, 135, NULL, (void*) &Pick, "#5070FF", NULL, Button, N_("grand (10x10)")}, { VariantLosers, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("losers")}, +{ VariantSpartan, 1, 135, NULL, (void*) &Pick, "#FF0000", NULL, Button, N_("Spartan")}, { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("Board size ( -1 = default for selected variant):")}, { 0, -1, BOARD_RANKS-1, NULL, (void*) &appData.NrRanks, "", NULL, Spin, N_("Number of Board Ranks:") }, { 0, -1, BOARD_FILES, NULL, (void*) &appData.NrFiles, "", NULL, Spin, N_("Number of Board Files:") },