X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=6c79b0368c9a25c9de770da0a7490980c24f972d;hb=062fb2bd92324706eed1fba8d5f7b4015ef2b058;hp=29ebca685c9172a5af0db0a045a20675489a6f4a;hpb=056614196635a4730170261fe0e638191f14c620;p=xboard.git diff --git a/backend.c b/backend.c index 29ebca6..6c79b03 100644 --- a/backend.c +++ b/backend.c @@ -421,36 +421,47 @@ ChessSquare twoKingsArray[2][BOARD_SIZE] = { { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteKing, WhiteKing, WhiteKnight, WhiteRook }, { BlackRook, BlackKnight, BlackBishop, BlackQueen, - BlackKing, BlackKing, BlackKnight, BlackRook } + BlackKing, BlackKing, BlackKnight, BlackRook } }; #ifdef FAIRY +ChessSquare KnightmateArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen, + WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook }, + { BlackRook, BlackMan, BlackBishop, BlackQueen, + BlackUnicorn, BlackBishop, BlackMan, BlackRook } +}; + ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */ - { WhiteFairyRook, WhiteFairyKnight, WhiteFairyBishop, WhiteQueen, + { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen, WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, - { BlackFairyRook, BlackFairyKnight, BlackFairyBishop, BlackQueen, + { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen, BlackKing, BlackBishop, BlackKnight, BlackRook } }; ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */ - { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn, - WhiteKing, WhiteFairyBishop, WhiteKnight, WhiteRook }, - { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn, - BlackKing, BlackFairyBishop, BlackKnight, BlackRook } + { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz, + WhiteKing, WhiteAlfil, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackAlfil, BlackFerz, + BlackKing, BlackAlfil, BlackKnight, BlackRook } +}; + + +#if (BOARD_SIZE>=10) +ChessSquare ShogiArray[2][BOARD_SIZE] = { + { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir, + WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen }, + { BlackQueen, BlackKnight, BlackFerz, BlackWazir, + BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen } }; -#if (BOARD_SIZE>=9) ChessSquare XiangqiArray[2][BOARD_SIZE] = { - { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn, - WhiteKing, WhiteFairyPawn, WhiteFairyBishop, WhiteKnight, WhiteRook }, - { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn, - BlackKing, BlackFairyPawn, BlackFairyBishop, BlackKnight, BlackRook } + { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz, + WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackAlfil, BlackFerz, + BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook } }; -#else -#define XiangqiPosition FIDEPosition -#endif -#if (BOARD_SIZE>=10) ChessSquare CapablancaArray[2][BOARD_SIZE] = { { WhiteRook, WhiteKnight, WhiteCardinal, WhiteBishop, WhiteQueen, WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook }, @@ -470,16 +481,17 @@ ChessSquare GothicArray[2][BOARD_SIZE] = { #endif // !GOTHIC #else // !(BOARD_SIZE>=10) +#define XiangqiPosition FIDEArray #define CapablancaArray FIDEArray #define GothicArray FIDEArray #endif // !(BOARD_SIZE>=10) #if (BOARD_SIZE>=12) ChessSquare CourierArray[2][BOARD_SIZE] = { - { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteBishop, WhiteFairyKing, WhiteKing, - WhiteFairyPawn, WhiteFairyRook, WhiteBishop, WhiteFairyBishop, WhiteKnight, WhiteRook }, - { BlackRook, BlackKnight, BlackFairyBishop, BlackBishop, BlackFairyKing, BlackKing, - BlackFairyPawn, BlackFairyRook, BlackBishop, BlackFairyBishop, BlackKnight, BlackRook } + { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing, + WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing, + BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook } }; #else // !(BOARD_SIZE>=12) #define CourierArray CapablancaArray @@ -733,25 +745,31 @@ InitBackEnd1() case VariantLoadable: case Variant29: case Variant30: -#ifndef FAIRY case Variant31: case Variant32: case Variant33: case Variant34: case Variant35: case Variant36: -#endif default: sprintf(buf, "Unknown variant name %s", appData.variant); DisplayFatalError(buf, 0, 2); return; + case VariantXiangqi: /* [HGM] repetition rules not implemented */ + case VariantFairy: /* [HGM] TestLegality definitely off! */ + case VariantGothic: /* [HGM] should work */ + case VariantCapablanca: /* [HGM] should work */ + case VariantCourier: /* [HGM] initial forced moves not implemented */ + case VariantShogi: /* [HGM] drops not tested for legality */ + case VariantShowgi: /* [HGM] not a valid variant */ + case VariantKnightmate: /* [HGM] should work */ + case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!) + offboard interposition not understood */ case VariantNormal: /* definitely works! */ case VariantWildCastle: /* pieces not automatically shuffled */ case VariantNoCastle: /* pieces not automatically shuffled */ case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */ - case VariantCrazyhouse: /* holdings not shown, - offboard interposition not understood */ case VariantLosers: /* should work except for win condition, and doesn't know captures are mandatory */ case VariantSuicide: /* should work except for win condition, @@ -762,14 +780,6 @@ InitBackEnd1() case VariantAtomic: /* should work except for win condition */ case Variant3Check: /* should work except for win condition */ case VariantShatranj: /* might work if TestLegality is off */ -#ifdef FAIRY - case VariantShogi: - case VariantXiangqi: - case VariantFairy: /* [HGM] TestLegality definitely off! */ - case VariantGothic: - case VariantCapablanca: - case VariantCourier: -#endif break; } } @@ -1355,7 +1365,14 @@ StringToVariant(e) char buf[MSG_SIZ]; if (!e) return v; - + + /* [HGM] skip over optional board-size prefixes */ + if( sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) { + while( *e++ != '_'); + } else if( sscanf(e, "%dx%d_", &i, &i) == 2 ) { + while( *e++ != '_'); + } + for (i=0; i= BlackPawn ) { + holdingsColumn = 0; + countsColumn = 1; + holdingsStartRow = BOARD_HEIGHT-1; + direction = -1; + } else { + holdingsColumn = BOARD_WIDTH-1; + countsColumn = BOARD_WIDTH-2; + holdingsStartRow = 0; + direction = 1; + } + + for(i=0; i= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */ + if(j < 0) continue; /* should not happen */ + piece = (ChessSquare) ( j + (int)lowestPiece ); + board[holdingsStartRow+j*direction][holdingsColumn] = piece; + board[holdingsStartRow+j*direction][countsColumn]++; + } + +} + static int loggedOn = FALSE; /*-- Game start info cache: --*/ @@ -2280,6 +2337,7 @@ read_from_ics(isr, closure, data, count, error) sprintf(str, "ICS %s %s match", star_match[0], star_match[1]); gameInfo.event = StrSave(str); gameInfo.variant = StringToVariant(gameInfo.event); + Reset(TRUE,TRUE); /* [HGM] possibly change board or holdings size */ continue; } @@ -2725,9 +2783,9 @@ read_from_ics(isr, closure, data, count, error) ClearPremoveHighlights(); if (appData.debugMode) fprintf(debugFP, "Sending premove:\n"); - UserMoveEvent(premoveFromX, premoveFromY, + UserMoveEvent(premoveFromX, premoveFromY, premoveToX, premoveToY, - premovePromoChar); + premovePromoChar); } } @@ -2748,7 +2806,27 @@ read_from_ics(isr, closure, data, count, error) if (sscanf(parse, " game %d", &gamenum) == 1 && gamenum == ics_gamenum) { if (gameInfo.variant == VariantNormal) { + /* [HGM] We seem to switch variant during a game! + * Presumably no holdings were displayed, so we have + * to move the position two files to the right to + * create room for them! + */ + int i, j; + if(gameInfo.holdingsWidth == 0) /* to be sure */ + for(i=0; i=0; j--) + boards[currentMove][i][j+2] = boards[currentMove][i][j]; + + if (appData.debugMode) { + fprintf(debugFP, "Switch board to Crazy\n"); + setbuf(debugFP, NULL); + } gameInfo.variant = VariantCrazyhouse; /*temp guess*/ + gameInfo.boardWidth = 8; /* [HGM] guess board size as well */ + gameInfo.boardHeight = 8; + gameInfo.holdingsSize = 5; + gameInfo.holdingsWidth = 2; + InitDrawingSizes(-2, 0); /* Get a move list just to see the header, which will tell us whether this is really bug or zh */ if (ics_getting_history == H_FALSE) { @@ -2763,6 +2841,9 @@ read_from_ics(isr, closure, data, count, error) new_piece); white_holding[strlen(white_holding)-1] = NULLCHAR; black_holding[strlen(black_holding)-1] = NULLCHAR; + /* [HGM] copy holdings to board holdings area */ + CopyHoldings(boards[currentMove], white_holding, WhitePawn); + CopyHoldings(boards[currentMove], black_holding, BlackPawn); #if ZIPPY if (appData.zippyPlay && first.initDone) { ZippyHoldings(white_holding, black_holding, @@ -2780,6 +2861,7 @@ read_from_ics(isr, closure, data, count, error) gameInfo.white, white_holding, gameInfo.black, black_holding); } + DrawPosition(FALSE, NULL); DisplayTitle(str); } @@ -2997,6 +3079,26 @@ ParseBoard12(string) movesPerSession = 0; gameInfo.timeControl = TimeControlTagValue(); gameInfo.variant = StringToVariant(gameInfo.event); + if (appData.debugMode) { + fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event); + fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant)); + setbuf(debugFP, NULL); + } + + gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */ + gameInfo.boardWidth = gameInfo.boardHeight = 8; + switch(gameInfo.variant) { + case VariantShogi: + case VariantShowgi: + gameInfo.boardWidth = 9; gameInfo.boardHeight = 9; + gameInfo.holdingsSize = 7; + case VariantBughouse: + case VariantCrazyhouse: + gameInfo.holdingsWidth = 2; break; + default: + gameInfo.holdingsWidth = gameInfo.holdingsSize = 0; + } + InitDrawingSizes(-2, 0); gameInfo.outOfBook = NULL; /* Do we have the ratings? */ @@ -3058,9 +3160,14 @@ ParseBoard12(string) } /* Parse the board */ - for (k = 0; k < 8; k++) + for (k = 0; k < 8; k++) { for (j = 0; j < 8; j++) - board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]); + board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(7-k)*9 + j]); + if(gameInfo.holdingsWidth > 1) { + board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare; + board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;; + } + } CopyBoard(boards[moveNum], board); if (moveNum == 0) { startedFromSetupPosition = @@ -3128,6 +3235,11 @@ ParseBoard12(string) /* Put the move on the move list, first converting to canonical algebraic form. */ if (moveNum > 0) { + if (appData.debugMode) { + fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str); + fprintf(debugFP, "board = %d-d x%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT); + setbuf(debugFP, NULL); + } if (moveNum <= backwardMostMove) { /* We don't know what the board looked like before this move. Punt. */ @@ -3148,7 +3260,8 @@ ParseBoard12(string) default: break; case MT_CHECK: - strcat(parseList[moveNum - 1], "+"); + if(gameInfo.variant != VariantShogi) + strcat(parseList[moveNum - 1], "+"); break; case MT_CHECKMATE: strcat(parseList[moveNum - 1], "#"); @@ -3181,6 +3294,10 @@ ParseBoard12(string) moveList[moveNum - 1][0] = NULLCHAR; fromX = fromY = toX = toY = -1; } + if (appData.debugMode) { + fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]); + setbuf(debugFP, NULL); + } #if ZIPPY /* Send move to chess program (BEFORE animating it). */ @@ -3333,7 +3450,7 @@ SendMoveToProgram(moveNum, cps) sprintf(buf, "%s\n", parseList[moveNum]); } /* [HGM] decrement all digits to code ranks starting from 0 */ - if(BOARD_HEIGHT>8) { + if(BOARD_HEIGHT>9) { char *p = buf; while(*p) { if(*p < 'A') (*p)--; p++; } } @@ -3343,9 +3460,9 @@ SendMoveToProgram(moveNum, cps) * the engine. It would be nice to have a better way to identify castle * moves here. */ if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) { - int fromX = moveList[moveNum][0] - 'a'; + int fromX = moveList[moveNum][0] - AAA; int fromY = moveList[moveNum][1] - ONE; - int toX = moveList[moveNum][2] - 'a'; + int toX = moveList[moveNum][2] - AAA; int toY = moveList[moveNum][3] - ONE; if((boards[currentMove][fromY][fromX] == WhiteKing && boards[currentMove][toY][toX] == WhiteRook) @@ -3411,21 +3528,21 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY) case BlackPromotionArchbishop: #endif sprintf(user_move, "%c%c%c%c=%c\n", - 'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY, + AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, PieceToChar(PromoPiece(moveType))); break; case WhiteDrop: case BlackDrop: sprintf(user_move, "%c@%c%c\n", ToUpper(PieceToChar((ChessSquare) fromX)), - 'a' + toX, ONE + toY); + AAA + toX, ONE + toY); break; case NormalMove: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case IllegalMove: /* could be a variant we don't quite understand */ sprintf(user_move, "%c%c%c%c\n", - 'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY); + AAA + fromX, ONE + fromY, AAA + toX, ONE + toY); break; } SendToICS(user_move); @@ -3439,16 +3556,17 @@ CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move) { if (rf == DROP_RANK) { sprintf(move, "%c@%c%c\n", - ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, ONE + rt); + ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt); } else { if (promoChar == 'x' || promoChar == NULLCHAR) { sprintf(move, "%c%c%c%c\n", - 'a' + ff, ONE + rf, 'a' + ft, ONE + rt); + AAA + ff, ONE + rf, AAA + ft, ONE + rt); } else { sprintf(move, "%c%c%c%c%c\n", - 'a' + ff, ONE + rf, 'a' + ft, ONE + rt, promoChar); + AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar); } } + AlphaRank(move, 4); } void @@ -3465,6 +3583,22 @@ ProcessICSInitScript(f) } +/* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */ +void +AlphaRank(char *move, int n) +{ + char *p = move, c; + + if( !appData.alphaRank ) return; + + while(c = *p) { + if(c>='0' && c<='9') *p += 'a'-'0'; else + if(c>='a' && c<='z') *p -= 'a'-'0'; + p++; + if(--n < 1) break; + } +} + /* Parser for moves from gnuchess, ICS, or user typein box */ Boolean ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) @@ -3474,6 +3608,10 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) int *fromX, *fromY, *toX, *toY; char *promoChar; { + if (appData.debugMode) { + fprintf(debugFP, "move to parse: %s\n", move); + } + AlphaRank(move, 10); *moveType = yylexstr(moveNum, move); switch (*moveType) { @@ -3511,13 +3649,13 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) case BlackASideCastleFR: /* End of code added by Tord */ case IllegalMove: /* bug or odd chess variant */ - *fromX = currentMoveString[0] - 'a'; + *fromX = currentMoveString[0] - AAA; *fromY = currentMoveString[1] - ONE; - *toX = currentMoveString[2] - 'a'; + *toX = currentMoveString[2] - AAA; *toY = currentMoveString[3] - ONE; *promoChar = currentMoveString[4]; - if (*fromX < 0 || *fromX >= BOARD_WIDTH || *fromY < 0 || *fromY >= BOARD_HEIGHT || - *toX < 0 || *toX >= BOARD_WIDTH || *toY < 0 || *toY >= BOARD_HEIGHT) { + if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT || + *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) { *fromX = *fromY = *toX = *toY = 0; return FALSE; } @@ -3533,7 +3671,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) (int) CharToPiece(ToUpper(currentMoveString[0])) : (int) CharToPiece(ToLower(currentMoveString[0])); *fromY = DROP_RANK; - *toX = currentMoveString[2] - 'a'; + *toX = currentMoveString[2] - AAA; *toY = currentMoveString[3] - ONE; *promoChar = NULLCHAR; return TRUE; @@ -3591,7 +3729,7 @@ static void ShuffleFRC( Board board ) board[0][FindEmptySquare(board, 0)] = WhiteKing; board[0][FindEmptySquare(board, 0)] = WhiteRook; - for( i=0; i= 12 && !(NrPieces&1)) { + int i; /* [HGM] Accept even length from 12 to 34 */ + + for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.'; + for( i=0; i>1; - castlingRights[0][3] = initialRights[3] = BOARD_WIDTH-1; - castlingRights[0][4] = initialRights[4] = 0; - castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1; - - castlingRank[0] = castlingRank[1] = castlingRank[2] = 0; - castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1; - initialRulePlies = 0; /* 50-move counter start */ } @@ -3683,17 +3836,21 @@ InitPosition(redraw) /* empty squares. This initial position is then copied to boards[0], */ /* possibly after shuffling, so that it remains available. */ + gameInfo.holdingsWidth = 0; /* default board sizes */ + gameInfo.boardWidth = 8; + gameInfo.boardHeight = 8; + gameInfo.holdingsSize = 0; + nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */ + for(i=0; i= 0) { + if(gameInfo.boardWidth != appData.NrFiles) overrule++; + gameInfo.boardWidth = appData.NrFiles; + } + if(appData.NrRanks >= 0) { + gameInfo.boardHeight = appData.NrRanks; + } + if(appData.holdingsSize >= 0) { + i = appData.holdingsSize; + if(i > gameInfo.boardHeight) i = gameInfo.boardHeight; + gameInfo.holdingsSize = i; + } + if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2; + if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE) + DisplayFatalError("Recompile to support this BOARD_SIZE!", 0, 2); + + pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ + if(pawnRow < 1) pawnRow = 1; + + /* User pieceToChar list overrules defaults */ + if(appData.pieceToCharTable != NULL) + SetCharTable(pieceToChar, appData.pieceToCharTable); + + for( j=0; j= BOARD_RGHT || overrule) continue; + initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth]; + initialPosition[pawnRow][j] = WhitePawn; + initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn; if(gameInfo.variant == VariantXiangqi) { if(j&1) { - initialPosition[3][j] = - initialPosition[BOARD_HEIGHT-4][j] = EmptySquare; - if(j==1 || j>=BOARD_WIDTH-2) { - initialPosition[2][j] = WhiteFairyMarshall; - initialPosition[BOARD_HEIGHT-3][j] = BlackFairyMarshall; + initialPosition[pawnRow][j] = + initialPosition[BOARD_HEIGHT-pawnRow-1][j] = EmptySquare; + if(j==BOARD_LEFT+1 || j>=BOARD_RGHT-2) { + initialPosition[2][j] = WhiteCannon; + initialPosition[BOARD_HEIGHT-3][j] = BlackCannon; } - } else { - initialPosition[3][j] = WhitePawn; - initialPosition[BOARD_HEIGHT-4][j] = BlackPawn; } - } else { - initialPosition[1][j] = WhitePawn; - initialPosition[BOARD_HEIGHT-2][j] = BlackPawn; } - initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j]; - } + initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j-gameInfo.holdingsWidth]; + } + if( (gameInfo.variant == VariantShogi + ||gameInfo.variant == VariantShowgi + ) && !overrule ) { + j=BOARD_LEFT+1; + initialPosition[1][j] = WhiteBishop; + initialPosition[BOARD_HEIGHT-2][j] = BlackRook; + j=BOARD_RGHT-2; + initialPosition[1][j] = WhiteRook; + initialPosition[BOARD_HEIGHT-2][j] = BlackBishop; + } + + if( nrCastlingRights == -1) { + /* [HGM] Build normal castling rights (must be done after board sizing!) */ + /* This sets default castling rights from none to normal corners */ + /* Variants with other castling rights must set them themselves above */ + nrCastlingRights = 6; + + castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1; + castlingRights[0][1] = initialRights[1] = BOARD_LEFT; + castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1; + castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1; + castlingRights[0][4] = initialRights[4] = BOARD_LEFT; + castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1; + + castlingRank[0] = castlingRank[1] = castlingRank[2] = 0; + castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1; + } if(gameInfo.variant == VariantFischeRandom) { if( appData.defaultFrcPosition < 0 ) { @@ -3770,6 +4014,16 @@ InitPosition(redraw) CopyBoard(boards[0], initialPosition); + if(oldx != gameInfo.boardWidth || + oldy != gameInfo.boardHeight || + oldh != gameInfo.holdingsWidth +#ifdef GOTHIC + || oldv == VariantGothic || + gameInfo.variant == VariantGothic +#endif + ) + InitDrawingSizes(-2 ,0); + if (redraw) DrawPosition(TRUE, boards[currentMove]); } @@ -3799,10 +4053,10 @@ SendBoard(cps, moveNum) SendToProgram("#\n", cps); for (i = BOARD_HEIGHT - 1; i >= 0; i--) { bp = &boards[moveNum][i][0]; - for (j = 0; j < BOARD_WIDTH; j++, bp++) { + for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) { if ((int) *bp < (int) BlackPawn) { sprintf(message, "%c%c%c\n", PieceToChar(*bp), - 'a' + j, ONE + i); + AAA + j, ONE + i); SendToProgram(message, cps); } } @@ -3811,11 +4065,11 @@ SendBoard(cps, moveNum) SendToProgram("c\n", cps); for (i = BOARD_HEIGHT - 1; i >= 0; i--) { bp = &boards[moveNum][i][0]; - for (j = 0; j < BOARD_WIDTH; j++, bp++) { + for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) { if (((int) *bp != (int) EmptySquare) && ((int) *bp >= (int) BlackPawn)) { sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)), - 'a' + j, ONE + i); + AAA + j, ONE + i); SendToProgram(message, cps); } } @@ -3829,19 +4083,48 @@ int IsPromotion(fromX, fromY, toX, toY) int fromX, fromY, toX, toY; { - return gameMode != EditPosition && gameInfo.variant != VariantXiangqi && - fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 && - ((boards[currentMove][fromY][fromX] == WhitePawn && toY == BOARD_HEIGHT-1) || - (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0)); + /* [HGM] add Shogi promotions */ + int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn; + ChessSquare piece; + + if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi || + !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE; + /* [HGM] Note to self: line above also weeds out drops */ + piece = boards[currentMove][fromY][fromX]; + if(gameInfo.variant == VariantShogi) { + promotionZoneSize = 3; + highestPromotingPiece = (int)WhiteKing; + /* [HGM] Should be Silver = Ferz, really, but legality testing is off, + and if in normal chess we then allow promotion to King, why not + allow promotion of other piece in Shogi? */ + } + if((int)piece >= BlackPawn) { + if(toY >= promotionZoneSize && fromY >= promotionZoneSize) + return FALSE; + highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece; + } else { + if( toY < BOARD_HEIGHT - promotionZoneSize && + fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE; + } + return ( (int)piece <= highestPromotingPiece ); } +int +InPalace(row, column) + int row, column; +{ /* [HGM] for Xiangqi */ + if( (row < 3 || row > BOARD_HEIGHT-4) && + column < (BOARD_WIDTH + 4)/2 && + column > (BOARD_WIDTH - 5)/2 ) return TRUE; + return FALSE; +} int PieceForSquare (x, y) int x; int y; { - if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) + if (x < BOARD_LEFT || x >= BOARD_RGHT || y < 0 || y >= BOARD_HEIGHT) return -1; else return boards[currentMove][y][x]; @@ -3958,18 +4241,19 @@ char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ]; ChessMove lastLoadGameStart = (ChessMove) 0; -void -UserMoveEvent(fromX, fromY, toX, toY, promoChar) +ChessMove +UserMoveTest(fromX, fromY, toX, toY, promoChar) int fromX, fromY, toX, toY; int promoChar; { ChessMove moveType; + ChessSquare pdown, pup; - if (fromX < 0 || fromY < 0) return; + if (fromX < 0 || fromY < 0) return ImpossibleMove; if ((fromX == toX) && (fromY == toY)) { - return; + return ImpossibleMove; } - + /* Check if the user is playing in turn. This is complicated because we let the user "pick up" a piece before it is his turn. So the piece he tried to pick up may have been captured by the time he puts it down! @@ -3990,13 +4274,13 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) case IcsIdle: /* We switched into a game mode where moves are not accepted, perhaps while the mouse button was down. */ - return; + return ImpossibleMove; case MachinePlaysWhite: /* User is moving for Black */ if (WhiteOnMove(currentMove)) { DisplayMoveError("It is White's turn"); - return; + return ImpossibleMove; } break; @@ -4004,7 +4288,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) /* User is moving for White */ if (!WhiteOnMove(currentMove)) { DisplayMoveError("It is Black's turn"); - return; + return ImpossibleMove; } break; @@ -4018,13 +4302,13 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) /* User is moving for Black */ if (WhiteOnMove(currentMove)) { DisplayMoveError("It is White's turn"); - return; + return ImpossibleMove; } } else { /* User is moving for White */ if (!WhiteOnMove(currentMove)) { DisplayMoveError("It is Black's turn"); - return; + return ImpossibleMove; } } break; @@ -4046,7 +4330,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) "fromY %d, toX %d, toY %d\n", fromX, fromY, toX, toY); } - return; + return ImpossibleMove; } break; @@ -4067,7 +4351,7 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) "fromY %d, toX %d, toY %d\n", fromX, fromY, toX, toY); } - return; + return ImpossibleMove; } break; @@ -4075,6 +4359,8 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) break; case EditPosition: + /* EditPosition, empty square, or different color piece; + click-click move is possible */ if (toX == -2 || toY == -2) { boards[0][fromY][fromX] = EmptySquare; DrawPosition(FALSE, boards[currentMove]); @@ -4083,24 +4369,87 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) boards[0][fromY][fromX] = EmptySquare; DrawPosition(FALSE, boards[currentMove]); } - return; + return ImpossibleMove; + } + + /* [HGM] suppress all moves into holdings area and guard band */ + if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 ) + return ImpossibleMove; + + /* [HGM] moved to here from winboard.c */ + /* note: EditPosition already filtered out and performed! */ + pdown = boards[currentMove][fromY][fromX]; + pup = boards[currentMove][toY][toX]; + if ( + (WhitePawn <= pdown && pdown < BlackPawn && + WhitePawn <= pup && pup < BlackPawn) || + (BlackPawn <= pdown && pdown < EmptySquare && + BlackPawn <= pup && pup < EmptySquare) ) + return ImpossibleMove; + + /* [HGM] If move started in holdings, it means a drop */ + if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { + if( pup != EmptySquare ) return ImpossibleMove; + if(appData.testLegality) { + /* it would be more logical if LegalityTest() also figured out + * which drops are legal. For now we forbid pawns on back rank. + * Shogi is on its own here... + */ + if( (pdown == WhitePawn || pdown == BlackPawn) && + (toY == 0 || toY == BOARD_HEIGHT -1 ) ) + return(ImpossibleMove); /* no pawn drops on 1st/8th */ + } + return WhiteDrop; /* Not needed to specify white or black yet */ } - if (toX < 0 || toY < 0) return; userOfferedDraw = FALSE; - if (appData.testLegality) { - moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), - EP_UNKNOWN, castlingRights[currentMove], + /* [HGM] always test for legality, to get promotion info */ + moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), + epStatus[currentMove], castlingRights[currentMove], fromY, fromX, toY, toX, promoChar); + + /* [HGM] but possibly ignore an IllegalMove result */ + if (appData.testLegality) { if (moveType == IllegalMove || moveType == ImpossibleMove) { DisplayMoveError("Illegal move"); - return; + return ImpossibleMove; } - } else { - moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar); } + return moveType; + /* [HGM] in stead of calling FinishMove directly, this + function is made into one that returns an OK move type if FinishMove + should be called. This to give the calling driver routine the + opportunity to finish the userMove input with a promotion popup, + without bothering the user with this for invalid or illegal moves */ + +/* FinishMove(moveType, fromX, fromY, toX, toY, promoChar); */ +} + +/* Common tail of UserMoveEvent and DropMenuEvent */ +void +FinishMove(moveType, fromX, fromY, toX, toY, promoChar) + ChessMove moveType; + int fromX, fromY, toX, toY; + /*char*/int promoChar; +{ + /* [HGM] kludge to avoid having know the exact promotion + move type in caller when we know the move is a legal promotion */ + if(moveType == NormalMove) + moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar); + + /* [HGM] convert drag-and-drop piece drops to standard form */ + if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { + moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop; + fromX = boards[currentMove][fromY][fromX]; + fromY = DROP_RANK; + } + + /* [HGM] The following if has been moved here from + UserMoveEnevt(). Because it seemed to belon here (why not allow + piece drops in training games?), and because it can only be + performed after it is known to what we promote. */ if (gameMode == Training) { /* compare the move played on the board to the next move in the * game. If they match, display the move and the opponent's response. @@ -4136,16 +4485,6 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) return; } - FinishMove(moveType, fromX, fromY, toX, toY, promoChar); -} - -/* Common tail of UserMoveEvent and DropMenuEvent */ -void -FinishMove(moveType, fromX, fromY, toX, toY, promoChar) - ChessMove moveType; - int fromX, fromY, toX, toY; - /*char*/int promoChar; -{ /* Ok, now we know that the move is good, so we can kill the previous line in Analysis Mode */ if (gameMode == AnalyzeMode && currentMove < forwardMostMove) { @@ -4236,6 +4575,26 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) } } +void +UserMoveEvent(fromX, fromY, toX, toY, promoChar) + int fromX, fromY, toX, toY; + int promoChar; +{ + /* [HGM] This routine was added to allow calling of its two logical + parts from other modules in the old way. Before, UserMoveEvent() + automatically called FinishMove() if the move was OK, and returned + otherwise. I separated the two, in order to make it possible to + slip a promotion popup in between. But that it always needs two + calls, to the first part, (now called UserMoveTest() ), and to + FinishMove if the first part succeeded. Calls that do not need + to do anything in between, can call this routine the old way. + */ + ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar); + + if(moveType != ImpossibleMove) + FinishMove(moveType, fromX, fromY, toX, toY, promoChar); +} + void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats ) { char * hint = lastHint; @@ -4370,15 +4729,17 @@ HandleMachineMove(message, cps) return; } - if (!ParseOneMove(machineMove, forwardMostMove, &moveType, - &fromX, &fromY, &toX, &toY, &promoChar)) { + if (!ParseOneMove(machineMove, forwardMostMove, &moveType, + &fromX, &fromY, &toX, &toY, &promoChar)) { /* Machine move could not be parsed; ignore it. */ sprintf(buf1, "Illegal move \"%s\" from %s machine", machineMove, cps->which); DisplayError(buf1, 0); + sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d%c", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0); if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, - "Forfeit due to illegal move", GE_XBOARD); + buf1, GE_XBOARD); } return; } @@ -4388,21 +4749,40 @@ HandleMachineMove(message, cps) /* to make sure an illegal e.p. capture does not slip through, */ /* to cause a forfeit on a justified illegal-move complaint */ /* of the opponent. */ - if(gameMode==TwoMachinesPlay && appData.testLegality && - LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove), + if( gameMode==TwoMachinesPlay && appData.testLegality + && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */ + ) { + ChessMove moveType; + moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove), epStatus[forwardMostMove], castlingRights[forwardMostMove], - fromY, fromX, toY, toX, promoChar) == IllegalMove) - { static char buf[MSG_SIZ]; + fromY, fromX, toY, toX, promoChar); if (appData.debugMode) { int i; for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ", castlingRights[forwardMostMove][i], castlingRank[i]); fprintf(debugFP, "castling rights\n"); } - sprintf(buf, "Xboard: Forfeit due to illegal move %s (%c%c%c%c)%c", - machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0); - GameEnds(machineWhite ? BlackWins : WhiteWins, buf, GE_XBOARD); + if(moveType == IllegalMove) { + sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0); + GameEnds(machineWhite ? BlackWins : WhiteWins, + buf1, GE_XBOARD); + } else if(gameInfo.variant != VariantFischeRandom) + /* [HGM] Kludge to handle engines that send FRC-style castling + when they shouldn't (like TSCP-Gothic) */ + switch(moveType) { + case WhiteASideCastleFR: + case BlackASideCastleFR: + toY++; + currentMoveString[2]++; + break; + case WhiteHSideCastleFR: + case BlackHSideCastleFR: + toY--; + currentMoveString[2]--; + break; } + } hintRequested = FALSE; lastHint[0] = NULLCHAR; bookRequested = FALSE; @@ -4464,10 +4844,11 @@ HandleMachineMove(message, cps) #ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines - if( gameMode == TwoMachinesPlay ) { + if( gameMode == TwoMachinesPlay && gameInfo.holdingsSize == 0) { int count = 0, epFile = epStatus[forwardMostMove]; - if(appData.testLegality) // don't wait for engine to announce game end if we can judge ourselves + if(appData.testLegality && appData.checkMates) + // don't wait for engine to announce game end if we can judge ourselves switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile, castlingRights[forwardMostMove]) ) { @@ -4496,7 +4877,7 @@ HandleMachineMove(message, cps) static int moveCount; /* First absolutely insufficient mating material. Count what is on board. */ - for(i=0; i appData.drawRepeats-2 - && adjudicateLossThreshold != 0) { + && appData.drawRepeats > 1) { /* adjudicate after user-specified nr of repeats */ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD ); @@ -4628,7 +5009,7 @@ HandleMachineMove(message, cps) if( count >= 100) epStatus[forwardMostMove] = EP_RULE_DRAW; /* this is used to judge if draw claims are legal */ - if(adjudicateLossThreshold != 0 && count >= 2*appData.ruleMoves) { + if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) { ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); return; @@ -4688,6 +5069,31 @@ HandleMachineMove(message, cps) cps->useSigterm = FALSE; } + /* [HGM] Allow engine to set up a position. Don't ask me why one would + * want this, I was asked to put it in, and obliged. + */ + if (!strncmp(message, "setboard ", 9)) { + Board initial_position; int i; + + GameEnds(GameIsDrawn, "Engine aborts game", GE_XBOARD); + + if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) { + DisplayError("Bad FEN received from engine", 0); + return ; + } else { + Reset(FALSE, FALSE); + CopyBoard(boards[0], initial_position); + initialRulePlies = FENrulePlies; + epStatus[0] = FENepStatus; + for( i=0; itwoMachinesColor[0] == 'w' ? BlackWins : WhiteWins, - buf, GE_XBOARD ); + "False illegal-move claim", GE_XBOARD ); } return; } @@ -5420,9 +5823,9 @@ ParseGameHistory(game) case BlackASideCastleFR: /* POP Fabien */ case IllegalMove: /* maybe suicide chess, etc. */ - fromX = currentMoveString[0] - 'a'; + fromX = currentMoveString[0] - AAA; fromY = currentMoveString[1] - ONE; - toX = currentMoveString[2] - 'a'; + toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; break; @@ -5432,7 +5835,7 @@ ParseGameHistory(game) (int) CharToPiece(ToUpper(currentMoveString[0])) : (int) CharToPiece(ToLower(currentMoveString[0])); fromY = DROP_RANK; - toX = currentMoveString[2] - 'a'; + toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = NULLCHAR; break; @@ -5516,7 +5919,8 @@ ParseGameHistory(game) default: break; case MT_CHECK: - strcat(parseList[boardIndex - 1], "+"); + if(gameInfo.variant != VariantShogi) + strcat(parseList[boardIndex - 1], "+"); break; case MT_CHECKMATE: strcat(parseList[boardIndex - 1], "#"); @@ -5533,37 +5937,39 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) int promoChar; Board board; { - /* [HGM] In Shatranj and Courier all promotions are to Ferz */ - if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier) - && promoChar != 0) promoChar = 'f'; + ChessSquare captured = board[toY][toX], piece; int p; + + /* [HGM] In Shatranj and Courier all promotions are to Ferz */ + if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier) + && promoChar != 0) promoChar = 'F'; - ChessSquare captured = board[toY][toX]; - if (fromY == DROP_RANK) { + if (fromX == toX && fromY == toY) return; + + if (fromY == DROP_RANK) { /* must be first */ board[toY][toX] = (ChessSquare) fromX; - } else if (fromX == toX && fromY == toY) { - return; - } + } else { + piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */ /* Code added by Tord: */ /* FRC castling assumed when king captures friendly rook. */ - else if (board[fromY][fromX] == WhiteKing && + if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook) { board[fromY][fromX] = EmptySquare; board[toY][toX] = EmptySquare; if(toX > fromX) { - board[0][BOARD_WIDTH-2] = WhiteKing; board[0][BOARD_WIDTH-3] = WhiteRook; + board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook; } else { - board[0][2] = WhiteKing; board[0][3] = WhiteRook; + board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook; } } else if (board[fromY][fromX] == BlackKing && board[toY][toX] == BlackRook) { board[fromY][fromX] = EmptySquare; board[toY][toX] = EmptySquare; if(toX > fromX) { - board[BOARD_HEIGHT-1][BOARD_WIDTH-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_WIDTH-3] = BlackRook; + board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook; } else { - board[BOARD_HEIGHT-1][2] = BlackKing; board[BOARD_HEIGHT-1][3] = BlackRook; + board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook; } /* End of code added by Tord */ @@ -5572,14 +5978,14 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) && toY == fromY && toX > fromX+1) { board[fromY][fromX] = EmptySquare; board[toY][toX] = WhiteKing; - board[fromY][BOARD_WIDTH-1] = EmptySquare; + board[fromY][BOARD_RGHT-1] = EmptySquare; board[toY][toX-1] = WhiteRook; } else if (initialPosition[fromY][fromX] == WhiteKing && board[fromY][fromX] == WhiteKing && toY == fromY && toX < fromX-1) { board[fromY][fromX] = EmptySquare; board[toY][toX] = WhiteKing; - board[fromY][0] = EmptySquare; + board[fromY][BOARD_LEFT] = EmptySquare; board[toY][toX+1] = WhiteRook; } else if (fromY == 0 && fromX == 3 && board[fromY][fromX] == WhiteKing @@ -5597,15 +6003,16 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][2] = WhiteRook; } else if (board[fromY][fromX] == WhitePawn && toY == BOARD_HEIGHT-1 -#ifdef FAIRY && gameInfo.variant != VariantXiangqi -#endif ) { /* 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) && (toX != fromX) @@ -5620,14 +6027,14 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) && toY == fromY && toX > fromX+1) { board[fromY][fromX] = EmptySquare; board[toY][toX] = BlackKing; - board[fromY][BOARD_WIDTH-1] = EmptySquare; + board[fromY][BOARD_RGHT-1] = EmptySquare; board[toY][toX-1] = BlackRook; } else if (initialPosition[fromY][fromX] == BlackKing && board[fromY][fromX] == BlackKing && toY == fromY && toX < fromX-1) { board[fromY][fromX] = EmptySquare; board[toY][toX] = BlackKing; - board[fromY][0] = EmptySquare; + board[fromY][BOARD_LEFT] = EmptySquare; board[toY][toX+1] = BlackRook; } else if (fromY == 7 && fromX == 3 && board[fromY][fromX] == BlackKing @@ -5645,15 +6052,16 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][2] = BlackRook; } else if (board[fromY][fromX] == BlackPawn && toY == 0 -#ifdef FAIRY && gameInfo.variant != VariantXiangqi -#endif ) { /* black pawn promotion */ board[0][toX] = CharToPiece(ToLower(promoChar)); if (board[0][toX] == EmptySquare) { board[0][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) && (toX != fromX) @@ -5667,28 +6075,70 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = board[fromY][fromX]; board[fromY][fromX] = EmptySquare; } - if (gameInfo.variant == VariantCrazyhouse) { -#if 0 - /* !!A lot more code needs to be written to support holdings */ + + /* [HGM] now we promote for Shogi, if needed */ + if(gameInfo.variant == VariantShogi && promoChar == 'q') + board[toY][toX] = (ChessSquare) (PROMOTED piece); + } + + if (gameInfo.holdingsWidth != 0) { + + /* !!A lot more code needs to be written to support holdings */ + /* [HGM] OK, so I have written it. Holdings are stored in the */ + /* penultimate board files, so they are automaticlly stored */ + /* in the game history. */ if (fromY == DROP_RANK) { - /* Delete from holdings */ - if (holdings[(int) fromX] > 0) holdings[(int) fromX]--; + /* Delete from holdings, by decreasing count */ + /* and erasing image if necessary */ + p = (int) fromX; + if(p < (int) BlackPawn) { /* white drop */ + p -= (int)WhitePawn; + if(p >= gameInfo.holdingsSize) p = 0; + if(--board[p][BOARD_WIDTH-2] == 0) + board[p][BOARD_WIDTH-1] = EmptySquare; + } else { /* black drop */ + p -= (int)BlackPawn; + if(p >= gameInfo.holdingsSize) p = 0; + if(--board[BOARD_HEIGHT-1-p][1] == 0) + board[BOARD_HEIGHT-1-p][0] = EmptySquare; + } } - if (captured != EmptySquare) { - /* Add to holdings */ - if (captured < BlackPawn) { - holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++; + if (captured != EmptySquare && gameInfo.holdingsSize > 0 + && gameInfo.variant != VariantBughouse ) { + /* Add to holdings, if holdings exist */ + p = (int) captured; + if (p >= (int) BlackPawn) { + p -= (int)BlackPawn; + if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) { + /* in Shogi restore piece to its original first */ + captured = (ChessSquare) (DEMOTED captured); + p = DEMOTED p; + } + p = PieceToNumber((ChessSquare)p); + if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; } + board[p][BOARD_WIDTH-2]++; + board[p][BOARD_WIDTH-1] = + BLACK_TO_WHITE captured; } else { - holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++; + p -= (int)WhitePawn; + if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) { + captured = (ChessSquare) (DEMOTED captured); + p = DEMOTED p; + } + p = PieceToNumber((ChessSquare)p); + if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; } + board[BOARD_HEIGHT-1-p][1]++; + board[BOARD_HEIGHT-1-p][0] = + WHITE_TO_BLACK captured; } } -#endif + } else if (gameInfo.variant == VariantAtomic) { if (captured != EmptySquare) { int y, x; for (y = toY-1; y <= toY+1; y++) { for (x = toX-1; x <= toX+1; x++) { - if (y >= 0 && y < BOARD_WIDTH && x >= 0 && x < BOARD_WIDTH && + if (y >= 0 && y < BOARD_HEIGHT && x >= BOARD_LEFT && x < BOARD_RGHT && board[y][x] != WhitePawn && board[y][x] != BlackPawn) { board[y][x] = EmptySquare; } @@ -5697,6 +6147,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = EmptySquare; } } + if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') { + /* [HGM] Shogi promotions */ + board[toY][toX] = (ChessSquare) (PROMOTED piece); + } + } /* Updates forwardMostMove */ @@ -5731,15 +6186,15 @@ MakeMove(fromX, fromY, toX, toY, promoChar) if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) { epStatus[forwardMostMove] = EP_PAWN_MOVE; if( toY-fromY==2 && - (toX>1 && boards[forwardMostMove][toY][toX-1] == BlackPawn || - toXBOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == BlackPawn || + toX1 && boards[forwardMostMove][toY][toX-1] == WhitePawn || - toXBOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == WhitePawn || + toXinitString, cps); if (gameInfo.variant != VariantNormal && - gameInfo.variant != VariantLoadable) { + gameInfo.variant != VariantLoadable + /* [HGM] also send variant if board size non-standard */ + || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 + ) { char *v = VariantName(gameInfo.variant); - if (StrStr(cps->variants, v) == NULL) { + if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) { + /* [HGM] in protocol 1 we have to assume all variants valid */ sprintf(buf, "Variant %s not supported by %s", v, cps->tidy); DisplayFatalError(buf, 0, 1); return; } - sprintf(buf, "variant %s\n", VariantName(gameInfo.variant)); + b = buf; + /* [HGM] make prefix for non-standard board size. Awkward testing... */ + overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; + 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 == VariantGothic ) + 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(overruled) { + if (cps->protocolVersion != 1 && StrStr(cps->variants, "boardsize") == NULL) { + sprintf(buf, "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 */ + sprintf(buf, "%dx%d+%d_", gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize ); + while(*b++ != '_'); + } + sprintf(b, "variant %s\n", VariantName(gameInfo.variant)); SendToProgram(buf, cps); } if (cps->sendICS) { @@ -6397,7 +6887,7 @@ AutoPlayOneMove() return FALSE; } - toX = moveList[currentMove][2] - 'a'; + toX = moveList[currentMove][2] - AAA; toY = moveList[currentMove][3] - ONE; if (moveList[currentMove][1] == '@') { @@ -6405,7 +6895,7 @@ AutoPlayOneMove() SetHighlights(-1, -1, toX, toY); } } else { - fromX = moveList[currentMove][0] - 'a'; + fromX = moveList[currentMove][0] - AAA; fromY = moveList[currentMove][1] - ONE; HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */ @@ -6470,12 +6960,10 @@ LoadGameOneMove(readAhead) case WhiteCapturesEnPassant: case BlackCapturesEnPassant: -#ifdef FAIRY case WhitePromotionChancellor: case BlackPromotionChancellor: case WhitePromotionArchbishop: case BlackPromotionArchbishop: -#endif case WhitePromotionQueen: case BlackPromotionQueen: case WhitePromotionRook: @@ -6503,9 +6991,9 @@ LoadGameOneMove(readAhead) /* POP Fabien */ if (appData.debugMode) fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString); - fromX = currentMoveString[0] - 'a'; + fromX = currentMoveString[0] - AAA; fromY = currentMoveString[1] - ONE; - toX = currentMoveString[2] - 'a'; + toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; break; @@ -6518,7 +7006,7 @@ LoadGameOneMove(readAhead) (int) CharToPiece(ToUpper(currentMoveString[0])) : (int) CharToPiece(ToLower(currentMoveString[0])); fromY = DROP_RANK; - toX = currentMoveString[2] - 'a'; + toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; break; @@ -6627,9 +7115,9 @@ LoadGameOneMove(readAhead) if (appData.debugMode) fprintf(debugFP, "Parsed %s into IllegalMove %s\n", yy_text, currentMoveString); - fromX = currentMoveString[0] - 'a'; + fromX = currentMoveString[0] - AAA; fromY = currentMoveString[1] - ONE; - toX = currentMoveString[2] - 'a'; + toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; } @@ -6738,9 +7226,9 @@ MakeRegisteredMove() thinkOutput[0] = NULLCHAR; strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]); - fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a'; + fromX = cmailMove[lastLoadGameNumber - 1][0] - AAA; fromY = cmailMove[lastLoadGameNumber - 1][1] - ONE; - toX = cmailMove[lastLoadGameNumber - 1][2] - 'a'; + toX = cmailMove[lastLoadGameNumber - 1][2] - AAA; toY = cmailMove[lastLoadGameNumber - 1][3] - ONE; promoChar = cmailMove[lastLoadGameNumber - 1][4]; MakeMove(fromX, fromY, toX, toY, promoChar); @@ -7167,7 +7655,7 @@ LoadGame(f, gameNumber, title, useList) if (!startedFromSetupPosition) { p = yy_text; for (i = BOARD_HEIGHT - 1; i >= 0; i--) - for (j = 0; j < BOARD_WIDTH; p++) + for (j = BOARD_LEFT; j < BOARD_RGHT; p++) switch (*p) { case '[': case '-': @@ -7437,7 +7925,7 @@ LoadPosition(f, positionNumber, title) for (i = BOARD_HEIGHT - 1; i >= 0; i--) { (void) fgets(line, MSG_SIZ, f); - for (p = line, j = 0; j < BOARD_WIDTH; p++) { + for (p = line, j = BOARD_LEFT; j < BOARD_RGHT; p++) { if (*p == ' ') continue; initial_position[i][j++] = CharToPiece(*p); @@ -9004,6 +9492,7 @@ EditPositionMenuEvent(selection, x, y) int x, y; { char buf[MSG_SIZ]; + ChessSquare piece = boards[0][y][x]; if (gameMode != EditPosition && gameMode != IcsExamining) return; @@ -9021,7 +9510,7 @@ EditPositionMenuEvent(selection, x, y) if (gameMode == IcsExamining) { if (boards[currentMove][y][x] != EmptySquare) { sprintf(buf, "%sx@%c%c\n", ics_prefix, - 'a' + x, ONE + y); + AAA + x, ONE + y); SendToICS(buf); } } else { @@ -9045,7 +9534,7 @@ EditPositionMenuEvent(selection, x, y) case EmptySquare: if (gameMode == IcsExamining) { - sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, ONE + y); + sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y); SendToICS(buf); } else { boards[0][y][x] = EmptySquare; @@ -9053,10 +9542,43 @@ EditPositionMenuEvent(selection, x, y) } break; + case PromotePiece: + if(piece >= (int)WhitePawn && piece < (int)WhiteWazir || + piece >= (int)BlackPawn && piece < (int)BlackWazir ) { + selection = (ChessSquare) (PROMOTED piece); + } else if(piece == EmptySquare) selection = WhiteWazir; + else selection = (ChessSquare)((int)piece - 1); + goto defaultlabel; + + case DemotePiece: + if(piece >= (int)WhiteUnicorn && piece < (int)WhiteKing || + piece >= (int)BlackUnicorn && piece < (int)BlackKing ) { + selection = (ChessSquare) (DEMOTED piece); + } else if( piece == WhiteKing || piece == BlackKing ) + selection = (ChessSquare)((int)piece - (int)WhiteKing + (int)WhiteMan); + else if(piece == EmptySquare) selection = BlackWazir; + else selection = (ChessSquare)((int)piece + 1); + goto defaultlabel; + + case WhiteQueen: + case BlackQueen: + if(gameInfo.variant == VariantShatranj || + gameInfo.variant == VariantXiangqi || + gameInfo.variant == VariantCourier ) + selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz); + goto defaultlabel; + + case WhiteKing: + case BlackKing: + if(gameInfo.variant == VariantXiangqi) + selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir); + if(gameInfo.variant == VariantKnightmate) + selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn); default: + defaultlabel: if (gameMode == IcsExamining) { sprintf(buf, "%s%c@%c%c\n", ics_prefix, - PieceToChar(selection), 'a' + x, ONE + y); + PieceToChar(selection), AAA + x, ONE + y); SendToICS(buf); } else { boards[0][y][x] = selection; @@ -9350,14 +9872,14 @@ ForwardInner(target) if (target > 0 && moveList[target - 1][0]) { int fromX, fromY, toX, toY; - toX = moveList[target - 1][2] - 'a'; + toX = moveList[target - 1][2] - AAA; toY = moveList[target - 1][3] - ONE; if (moveList[target - 1][1] == '@') { if (appData.highlightLastMove) { SetHighlights(-1, -1, toX, toY); } } else { - fromX = moveList[target - 1][0] - 'a'; + fromX = moveList[target - 1][0] - AAA; fromY = moveList[target - 1][1] - ONE; if (target == currentMove + 1) { AnimateMove(boards[currentMove], fromX, fromY, toX, toY); @@ -9453,14 +9975,14 @@ BackwardInner(target) if (moveList[target][0]) { int fromX, fromY, toX, toY; - toX = moveList[target][2] - 'a'; + toX = moveList[target][2] - AAA; toY = moveList[target][3] - ONE; if (moveList[target][1] == '@') { if (appData.highlightLastMove) { SetHighlights(-1, -1, toX, toY); } } else { - fromX = moveList[target][0] - 'a'; + fromX = moveList[target][0] - AAA; fromY = moveList[target][1] - ONE; if (target == currentMove - 1) { AnimateMove(boards[currentMove], toX, toY, fromX, fromY); @@ -9733,10 +10255,10 @@ PrintPosition(fp, move) int i, j; for (i = BOARD_HEIGHT - 1; i >= 0; i--) { - for (j = 0; j < BOARD_WIDTH; j++) { + for (j = BOARD_LEFT; j < BOARD_RGHT; j++) { char c = PieceToChar(boards[move][i][j]); fputc(c == 'x' ? '.' : c, fp); - fputc(j == BOARD_WIDTH - 1 ? '\n' : ' ', fp); + fputc(j == BOARD_RGHT - 1 ? '\n' : ' ', fp); } } if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0)) @@ -10784,6 +11306,15 @@ NextTickLength(timeRemaining) return nextTickLength; } +/* Adjust clock one minute up or down */ +void +AdjustClock(Boolean which, int dir) +{ + if(which) blackTimeRemaining += 60000*dir; + else whiteTimeRemaining += 60000*dir; + DisplayBothClocks(); +} + /* Stop clocks and reset to a fresh time control */ void ResetClocks() @@ -11124,6 +11655,7 @@ PositionToFEN(move, useFEN960) char buf[128]; char *p, *q; int emptycount; + ChessSquare piece; whiteToPlay = (gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0); @@ -11132,17 +11664,33 @@ PositionToFEN(move, useFEN960) /* Piece placement data */ for (i = BOARD_HEIGHT - 1; i >= 0; i--) { emptycount = 0; - for (j = 0; j < BOARD_WIDTH; j++) { + for (j = BOARD_LEFT; j < BOARD_RGHT; j++) { if (boards[move][i][j] == EmptySquare) { emptycount++; - } else { + } else { ChessSquare piece = boards[move][i][j]; if (emptycount > 0) { if(emptycount<10) /* [HGM] can be >= 10 */ *p++ = '0' + emptycount; else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; } emptycount = 0; } - *p++ = PieceToChar(boards[move][i][j]); + if(gameInfo.variant == VariantShogi) { + /* [HGM] write Shogi promoted pieces as + */ + if( (int)piece > (int) WhiteCannon && (int)piece < (int) WhiteKing || + (int)piece > (int) BlackCannon && (int)piece < (int) BlackKing ) { + *p++ = '+'; + piece = (ChessSquare)(DEMOTED piece); + } + } + *p++ = PieceToChar(piece); + if(gameInfo.variant == VariantCrazyhouse || gameInfo.variant == VariantBughouse) { + /* [HGM] flag Crazyhouse promoted pieces */ + if( (int)piece > (int) WhiteQueen && (int)piece < (int) WhiteKing || + (int)piece > (int) BlackQueen && (int)piece < (int) BlackKing ) { + p[-1] = PieceToChar((ChessSquare)(DEMOTED piece)); + *p++ = '~'; + } + } } } if (emptycount > 0) { @@ -11174,18 +11722,18 @@ PositionToFEN(move, useFEN960) /* White castling rights */ - for (fk = 1; fk < BOARD_WIDTH-1; fk++) { + for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) { if (boards[move][0][fk] == WhiteKing) { - for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */ + for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */ if (boards[move][0][fr] == WhiteRook) { *p++ = useFEN960 ? 'A' + fr : 'K'; break; } } - for (fr = 0; fr < fk; fr++) { /* A side */ + for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */ if (boards[move][0][fr] == WhiteRook) { *p++ = useFEN960 ? 'A' + fr : 'Q'; break; @@ -11196,18 +11744,18 @@ PositionToFEN(move, useFEN960) /* Black castling rights */ - for (fk = 1; fk < BOARD_WIDTH-1; fk++) { + for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) { if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) { - for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */ + for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */ if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) { *p++ = useFEN960 ? 'a' + fr : 'k'; break; } } - for (fr = 0; fr < fk; fr++) { /* A side */ + for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */ if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) { *p++ = useFEN960 ? 'a' + fr : 'q'; break; @@ -11224,23 +11772,23 @@ PositionToFEN(move, useFEN960) #ifdef OLDCASTLINGCODE if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) { - if (boards[move][0][BOARD_WIDTH-1] == WhiteRook) *p++ = 'K'; - if (boards[move][0][0] == WhiteRook) *p++ = 'Q'; + if (boards[move][0][BOARD_RGHT-1] == WhiteRook) *p++ = 'K'; + if (boards[move][0][BOARD_LEFT] == WhiteRook) *p++ = 'Q'; } if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) { if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k'; - if (boards[move][BOARD_HEIGHT-1][0] == BlackRook) *p++ = 'q'; + if (boards[move][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook) *p++ = 'q'; } #else /* [HGM] write true castling rights */ if( nrCastlingRights == 6 ) { - if(castlingRights[move][0] == BOARD_WIDTH-1 && + if(castlingRights[move][0] == BOARD_RGHT-1 && castlingRights[move][2] >= 0 ) *p++ = 'K'; - if(castlingRights[move][1] == 0 && + if(castlingRights[move][1] == BOARD_LEFT && castlingRights[move][2] >= 0 ) *p++ = 'Q'; - if(castlingRights[move][3] == BOARD_WIDTH-1 && + if(castlingRights[move][3] == BOARD_RGHT-1 && castlingRights[move][5] >= 0 ) *p++ = 'k'; - if(castlingRights[move][4] == 0 && + if(castlingRights[move][4] == BOARD_LEFT && castlingRights[move][5] >= 0 ) *p++ = 'q'; } #endif @@ -11252,16 +11800,16 @@ PositionToFEN(move, useFEN960) /* En passant target square */ if (move > backwardMostMove) { - fromX = moveList[move - 1][0] - 'a'; + fromX = moveList[move - 1][0] - AAA; fromY = moveList[move - 1][1] - ONE; - toX = moveList[move - 1][2] - 'a'; + toX = moveList[move - 1][2] - AAA; toY = moveList[move - 1][3] - ONE; if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) && toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) && boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) && fromX == toX) { /* 2-square pawn move just happened */ - *p++ = toX + 'a'; + *p++ = toX + AAA; *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3'; } else { *p++ = '-'; @@ -11270,6 +11818,26 @@ PositionToFEN(move, useFEN960) *p++ = '-'; } + /* [HGM] print Crazyhouse holdings */ + if( gameInfo.variant == VariantCrazyhouse ) { + *p++ = ' '; q = p; + for(i=0; i= 0; i--) { j = 0; for (;;) { if (*p == '/' || *p == ' ') { if (*p == '/') p++; - emptycount = BOARD_WIDTH - j; - while (emptycount--) board[i][j++] = EmptySquare; + emptycount = gameInfo.boardWidth - j; + while (emptycount--) + board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; break; #if(BOARD_SIZE >= 10) } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */ p++; emptycount=10; - if (j + emptycount > BOARD_WIDTH) return FALSE; - while (emptycount--) board[i][j++] = EmptySquare; + if (j + emptycount > gameInfo.boardWidth) return FALSE; + while (emptycount--) + board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; #endif } else if (isdigit(*p)) { emptycount = *p++ - '0'; while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */ - if (j + emptycount > BOARD_WIDTH) return FALSE; - while (emptycount--) board[i][j++] = EmptySquare; - } else if (isalpha(*p)) { - if (j >= BOARD_WIDTH) return FALSE; - board[i][j++] = CharToPiece(*p++); + if (j + emptycount > gameInfo.boardWidth) return FALSE; + while (emptycount--) + board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; + } else if (*p == '+' || isalpha(*p)) { + if (j >= gameInfo.boardWidth) return FALSE; + if(*p=='+') { piece = (ChessSquare) (PROMOTED CharToPiece(*++p) ); p++; } + else piece = CharToPiece(*p++); + if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */ + piece = (ChessSquare) (PROMOTED piece); + p++; + } + board[i][(j++)+gameInfo.holdingsWidth] = piece; } else { return FALSE; } @@ -11353,18 +11941,18 @@ ParseFEN(board, blackPlaysFirst, fen) for(i=0; i=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1; + if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1; + if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1; + if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1; + if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1; + if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1; FENrulePlies = 0; while(*p==' ') p++; if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') { - /* castling indicator present, so default is impossible */ + /* castling indicator present, so default is no castlings */ for(i=0; i>1; break; case'Q': - FENcastlingRights[1] = 0; + FENcastlingRights[1] = BOARD_LEFT; FENcastlingRights[2] = BOARD_WIDTH>>1; break; case'k': - FENcastlingRights[3] = BOARD_WIDTH-1; + FENcastlingRights[3] = BOARD_RGHT-1; FENcastlingRights[5] = BOARD_WIDTH>>1; break; case'q': - FENcastlingRights[4] = 0; + FENcastlingRights[4] = BOARD_LEFT; FENcastlingRights[5] = BOARD_WIDTH>>1; break; /* Tord! FRC! */ @@ -11397,13 +11985,39 @@ ParseFEN(board, blackPlaysFirst, fen) if(*p=='-') { p++; FENepStatus = EP_NONE; } else { - char c = *p++ - 'a'; + char c = *p++ - AAA; - if(c < 0 || c >= BOARD_WIDTH) return TRUE; + if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE; if(*p >= '0' && *p <='9') *p++; FENepStatus = c; } + /* [HGM] look for Crazyhouse holdings here */ + while(*p==' ') p++; + if( !isdigit(*p) ) { + if(*p == '-' ) *p++; /* empty holdings */ else { + if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */ + /* if we would allow FEN reading to set board size, we would */ + /* have to add holdings and shift the board read so far here */ + while( (piece = CharToPiece(*p) ) != EmptySquare ) { + *p++; + if((int) piece >= (int) BlackPawn ) { + i = (int)piece - (int)BlackPawn; + if( i >= BOARD_HEIGHT ) return FALSE; + board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */ + board[BOARD_HEIGHT-1-i][1]++; /* black counts */ + } else { + i = (int)piece - (int)WhitePawn; + if( i >= BOARD_HEIGHT ) return FALSE; + board[i][BOARD_WIDTH-1] = piece; /* white holdings */ + board[i][BOARD_WIDTH-2]++; /* black holdings */ + } + } + } + } + + + if(sscanf(p, "%d", &i) == 1) { FENrulePlies = i; /* 50-move ply counter */ /* (The move number is still ignored) */