X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=e9e1368b16c126886555b971078030a54ea261ea;hb=93077be9f3009b8eb6ad64067f31d000aaf284c3;hp=f3cffc5fbc7279fb084ae5b68af95ac1f703750c;hpb=7e4f4f5718bf4efee8be4abfc981d18cfd6f8438;p=xboard.git diff --git a/backend.c b/backend.c index f3cffc5..e9e1368 100644 --- a/backend.c +++ b/backend.c @@ -1367,9 +1367,8 @@ StringToVariant(e) 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 ) { + if( sscanf(e, "%dx%d_", &i, &i) == 2 || + sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) { while( *e++ != '_'); } @@ -1702,13 +1701,13 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) char p; ChessSquare piece; - if(gameInfo.holdingsWidth < 1) return; + if(gameInfo.holdingsWidth < 2) return; if( (int)lowestPiece >= BlackPawn ) { holdingsColumn = 0; countsColumn = 1; holdingsStartRow = BOARD_HEIGHT-1; - direction = 1; + direction = -1; } else { holdingsColumn = BOARD_WIDTH-1; countsColumn = BOARD_WIDTH-2; @@ -1723,16 +1722,79 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) while( (p=*holdings++) != NULLCHAR ) { piece = CharToPiece( ToUpper(p) ); if(piece == EmptySquare) continue; - j = (int) piece - (int) WhitePawn; + /*j = (int) piece - (int) WhitePawn;*/ + j = PieceToNumber(piece); if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */ if(j < 0) continue; /* should not happen */ - piece = (ChessSquare) ( (int)piece + (int)lowestPiece ); - board[holdingsStartRow+i*direction][holdingsColumn] = piece; - board[holdingsStartRow+i*direction][countsColumn]++; + piece = (ChessSquare) ( j + (int)lowestPiece ); + board[holdingsStartRow+j*direction][holdingsColumn] = piece; + board[holdingsStartRow+j*direction][countsColumn]++; } } +void +VariantSwitch(Board board, VariantClass newVariant) +{ + int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; + if(gameInfo.variant == newVariant) return; + + /* [HGM] This routine is called each time an assignment is made to + * gameInfo.variant during a game, to make sure the board sizes + * are set to match the new variant. If that means adding or deleting + * holdings, we shift the playing board accordingly + */ + + if (appData.debugMode) { + fprintf(debugFP, "Switch board from %s to %s\n", + VariantName(gameInfo.variant), VariantName(newVariant)); + setbuf(debugFP, NULL); + } + gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */ + switch(newVariant) { + case VariantShogi: + case VariantShowgi: + newWidth = 9; newHeight = 9; + gameInfo.holdingsSize = 7; + case VariantBughouse: + case VariantCrazyhouse: + newHoldingsWidth = 2; break; + default: + newHoldingsWidth = gameInfo.holdingsSize = 0; + } + + if(newWidth != gameInfo.boardWidth || + newHeight != gameInfo.boardHeight || + newHoldingsWidth != gameInfo.holdingsWidth ) { + + /* shift position to new playing area, if needed */ + if(newHoldingsWidth > gameInfo.holdingsWidth) { + for(i=0; i=BOARD_LEFT; j--) + board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] = + board[i][j]; + for(i=0; i 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 = @@ -3202,6 +3267,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. */ @@ -3222,7 +3292,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], "#"); @@ -3243,6 +3314,10 @@ ParseBoard12(string) fromX = fromY = toX = toY = -1; } else { /* Move from ICS was illegal!? Punt. */ + if (appData.debugMode) { + fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str); + fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth); + } #if 0 if (appData.testLegality && appData.debugMode) { sprintf(str, "Illegal move \"%s\" from ICS", move_str); @@ -3255,6 +3330,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). */ @@ -3406,11 +3485,6 @@ SendMoveToProgram(moveNum, cps) } else { sprintf(buf, "%s\n", parseList[moveNum]); } - /* [HGM] decrement all digits to code ranks starting from 0 */ - if(BOARD_HEIGHT>8) { - char *p = buf; - while(*p) { if(*p < 'A') (*p)--; p++; } - } SendToProgram(buf, cps); } else { /* Added by Tord: Send castle moves in "O-O" in FRC games if required by @@ -3544,15 +3618,62 @@ ProcessICSInitScript(f) void AlphaRank(char *move, int n) { - char *p = move, c; + char *p = move, c; int x, y; 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; + if (appData.debugMode) { + fprintf(debugFP, "alphaRank(%s,%d)\n", move, n); + } + + if(move[1]=='*' && + move[2]>='0' && move[2]<='9' && + move[3]>='a' && move[3]<='x' ) { + move[2] = (move[2]-'1')+BOARD_LEFT + AAA; + move[3] = (move[3]-'a') + ONE; + } else + if(move[0]>='0' && move[0]<='9' && + move[1]>='a' && move[1]<='x' && + move[2]>='0' && move[2]<='9' && + move[3]>='a' && move[3]<='x' ) { + /* input move, Shogi -> normal */ +/* + move[0] = BOARD_RGHT -1-(move[0]-'1') + AAA; + move[1] = BOARD_HEIGHT-1-(move[1]-'a') + ONE; + move[2] = BOARD_RGHT -1-(move[2]-'1') + AAA; + move[3] = BOARD_HEIGHT-1-(move[3]-'a') + ONE; +*/ + move[0] = (move[0]-'1')+BOARD_LEFT + AAA; + move[1] = (move[1]-'a') + ONE; + move[2] = (move[2]-'1')+BOARD_LEFT + AAA; + move[3] = (move[3]-'a') + ONE; + } else + if(move[1]=='@' && + move[3]>='0' && move[3]<='9' && + move[2]>='a' && move[2]<='x' ) { + move[1] = '*'; + move[2] = (move[2]-AAA)-BOARD_LEFT + '1'; + move[3] = (move[3]-ONE) + 'a'; + } else + if( + move[0]>='a' && move[0]<='x' && + move[3]>='0' && move[3]<='9' && + move[2]>='a' && move[2]<='x' ) { + /* output move, normal -> Shogi */ +/* + move[0] = BOARD_RGHT -1-(move[0]-AAA) + '1'; + move[1] = BOARD_HEIGHT-1-(move[1]-ONE) + 'a'; + move[2] = BOARD_RGHT -1-(move[2]-AAA) + '1'; + move[3] = BOARD_HEIGHT-1-(move[3]-ONE) + 'a'; +*/ + move[0] = (move[0]-AAA)-BOARD_LEFT + '1'; + move[1] = (move[1]-ONE) + 'a'; + move[2] = (move[2]-AAA)-BOARD_LEFT + '1'; + move[3] = (move[3]-ONE) + 'a'; + if(move[4] == PieceToChar(BlackQueen)) move[4] = '+'; + } + if (appData.debugMode) { + fprintf(debugFP, " out = '%s'\n", move); } } @@ -3568,16 +3689,13 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) if (appData.debugMode) { fprintf(debugFP, "move to parse: %s\n", move); } - AlphaRank(move, 10); *moveType = yylexstr(moveNum, move); switch (*moveType) { -#ifdef FAIRY case WhitePromotionChancellor: case BlackPromotionChancellor: case WhitePromotionArchbishop: case BlackPromotionArchbishop: -#endif case WhitePromotionQueen: case BlackPromotionQueen: case WhitePromotionRook: @@ -3613,6 +3731,9 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) *promoChar = currentMoveString[4]; if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT || *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) { + if (appData.debugMode) { + fprintf(debugFP, "Off-board move (%d,%d)-(%d,%d)%c, type = %d\n", *fromX, *fromY, *toX, *toY, *promoChar, *moveType); + } *fromX = *fromY = *toX = *toY = 0; return FALSE; } @@ -3644,6 +3765,9 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) case BlackWins: case GameIsDrawn: default: + if (appData.debugMode) { + fprintf(debugFP, "Impossible move %s, type = %d\n", currentMoveString, *moveType); + } /* bug? */ *fromX = *fromY = *toX = *toY = 0; *promoChar = NULLCHAR; @@ -3737,6 +3861,30 @@ static void SetupFRC( Board board, int pos_index ) } } +BOOL SetCharTable( char *table, const char * map ) +/* [HGM] moved here from winboard.c because of its general usefulness */ +/* Basically a safe strcpy that uses the last character as King */ +{ + BOOL result = FALSE; int NrPieces; + + if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare + && NrPieces >= 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_RGHT-1; - castlingRights[0][4] = initialRights[4] = BOARD_LEFT; - castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1; + initialRulePlies = 0; /* 50-move counter start */ castlingRank[0] = castlingRank[1] = castlingRank[2] = 0; castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1; - - initialRulePlies = 0; /* 50-move counter start */ } @@ -3785,6 +3924,9 @@ InitPosition(redraw) 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.boardHeight != appData.NrRanks) overrule++; gameInfo.boardHeight = appData.NrRanks; } if(appData.holdingsSize >= 0) { @@ -3889,11 +4030,12 @@ InitPosition(redraw) 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 variants */ + 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) - strcpy(pieceToChar, appData.pieceToCharTable); + SetCharTable(pieceToChar, appData.pieceToCharTable); for( j=0; j>1; + castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1; + castlingRights[0][4] = initialRights[4] = BOARD_LEFT; + castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1; + } + if(gameInfo.variant == VariantFischeRandom) { if( appData.defaultFrcPosition < 0 ) { ShuffleFRC( initialPosition ); @@ -3942,8 +4098,13 @@ InitPosition(redraw) if(oldx != gameInfo.boardWidth || oldy != gameInfo.boardHeight || - oldh != gameInfo.holdingsWidth ) - InitDrawingSizes(-1 ,0); + oldh != gameInfo.holdingsWidth +#ifdef GOTHIC + || oldv == VariantGothic || + gameInfo.variant == VariantGothic +#endif + ) + InitDrawingSizes(-2 ,0); if (redraw) DrawPosition(TRUE, boards[currentMove]); @@ -4014,7 +4175,10 @@ IsPromotion(fromX, fromY, toX, toY) piece = boards[currentMove][fromY][fromX]; if(gameInfo.variant == VariantShogi) { promotionZoneSize = 3; - highestPromotingPiece = (int)WhiteFerz; /* Silver */ + 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) @@ -4165,14 +4329,28 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) int promoChar; { ChessMove moveType; + ChessSquare pdown, pup; if (fromX < 0 || fromY < 0) return ImpossibleMove; if ((fromX == toX) && (fromY == toY)) { return ImpossibleMove; } + /* [HGM] suppress all moves into holdings area and guard band */ - if( toX < BOARD_LEFT || toX >= BOARD_RGHT ) return ImpossibleMove; - + if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 ) + return ImpossibleMove; + + /* [HGM] moved to here from winboard.c */ + /* note: this code seems to exist for filtering out some obviously illegal premoves */ + pdown = boards[currentMove][fromY][fromX]; + pup = boards[currentMove][toY][toX]; + if ( gameMode != EditPosition && + (WhitePawn <= pdown && pdown < BlackPawn && + WhitePawn <= pup && pup < BlackPawn || + BlackPawn <= pdown && pdown < EmptySquare && + BlackPawn <= pup && pup < EmptySquare) ) + 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! @@ -4278,6 +4456,8 @@ UserMoveTest(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]); @@ -4289,26 +4469,34 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) return ImpossibleMove; } - if (toX < 0 || toY < 0) return ImpossibleMove; - /* [HGM] If move started in holdings, it means a drop */ - if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { - if( boards[currentMove][toY][toX] != EmptySquare ) return ImpossibleMove; + 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 */ } 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 ImpossibleMove; } - } else { - moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar); } return moveType; @@ -4623,6 +4811,7 @@ HandleMachineMove(message, cps) return; } + AlphaRank(machineMove, 4); if (!ParseOneMove(machineMove, forwardMostMove, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) { /* Machine move could not be parsed; ignore it. */ @@ -4643,23 +4832,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 && - fromY != DROP_RANK && /* [HGM] temporary; should still add legality test for drops */ - 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) - { + 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(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); + 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; @@ -4827,7 +5033,8 @@ HandleMachineMove(message, cps) count = 0; for(k = forwardMostMove-2; k>=backwardMostMove && k>=forwardMostMove-100 && - epStatus[k] <= EP_NONE && epStatus[k+1] <= EP_NONE; + epStatus[k] < EP_UNKNOWN && + epStatus[k+2] <= EP_NONE && epStatus[k+1] <= EP_NONE; k-=2) { int rights=0; if (appData.debugMode) { @@ -4946,6 +5153,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(GameUnfinished, "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; i= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; } board[p][BOARD_WIDTH-2]++; board[p][BOARD_WIDTH-1] = @@ -5972,6 +6226,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) 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] = @@ -5993,7 +6248,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = EmptySquare; } } - if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR) { + if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') { /* [HGM] Shogi promotions */ board[toY][toX] = (ChessSquare) (PROMOTED piece); } @@ -6032,14 +6287,14 @@ MakeMove(fromX, fromY, toX, toY, promoChar) if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) { epStatus[forwardMostMove] = EP_PAWN_MOVE; if( toY-fromY==2 && - (toX>BOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == BlackPawn || + (toX>BOARD_LEFT && boards[forwardMostMove][toY][toX-1] == BlackPawn || toXBOARD_LEFT+1 && boards[forwardMostMove][toY][toX-1] == WhitePawn || + (toX>BOARD_LEFT && boards[forwardMostMove][toY][toX-1] == WhitePawn || toXvariants, 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; @@ -6144,15 +6407,13 @@ InitChessProgram(cps) overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; if(overruled) { -#if 0 - // doesn't work in protocol 1 - if (StrStr(cps->variants, "boardsize") == NULL,) { + 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; } -#endif + /* [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++ != '_'); @@ -6356,8 +6617,9 @@ GameEnds(result, resultDetails, whosays) claimer = whosays == GE_ENGINE1 ? /* color of claimer */ first.twoMachinesColor[0] : second.twoMachinesColor[0] ; - if( result == WhiteWins && claimer == 'w' || - result == BlackWins && claimer == 'b' ) { + if( gameInfo.holdingsWidth == 0 && + (result == WhiteWins && claimer == 'w' || + result == BlackWins && claimer == 'b' ) ) { /* Xboard immediately adjudicates all mates, so win claims must be false */ sprintf(buf, "False win claim: '%s'", resultDetails); result = claimer == 'w' ? BlackWins : WhiteWins; @@ -6802,12 +7064,10 @@ LoadGameOneMove(readAhead) case WhiteCapturesEnPassant: case BlackCapturesEnPassant: -#ifdef FAIRY case WhitePromotionChancellor: case BlackPromotionChancellor: case WhitePromotionArchbishop: case BlackPromotionArchbishop: -#endif case WhitePromotionQueen: case BlackPromotionQueen: case WhitePromotionRook: @@ -9336,6 +9596,7 @@ EditPositionMenuEvent(selection, x, y) int x, y; { char buf[MSG_SIZ]; + ChessSquare piece = boards[0][y][x]; if (gameMode != EditPosition && gameMode != IcsExamining) return; @@ -9385,7 +9646,40 @@ 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), AAA + x, ONE + y); @@ -11116,6 +11410,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() @@ -11468,21 +11771,23 @@ PositionToFEN(move, useFEN960) 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 == VariantCrazyhouse) { - /* [HGM] flag Crazyhouse promoted pieces */ - if( (int)boards[move][i][j] > (int) WhiteQueen && (int)boards[move][i][j] < (int) WhiteKing || - (int)boards[move][i][j] > (int) BlackQueen && (int)boards[move][i][j] < (int) BlackKing ) { - p[-1] = PieceToChar((ChessSquare)((int)boards[move][i][j]-(int)WhiteAlfil+(int)WhitePawn)); - *p++ = '~'; - } + if(PieceToChar(piece) == '+') { + /* [HGM] write promoted pieces as '+' (Shogi) */ + *p++ = '+'; + piece = (ChessSquare)(DEMOTED piece); + } + *p++ = PieceToChar(piece); + if(p[-1] == '~') { + /* [HGM] flag promoted pieces as '~' (Crazyhouse) */ + p[-1] = PieceToChar((ChessSquare)(DEMOTED piece)); + *p++ = '~'; } } } @@ -11500,6 +11805,7 @@ PositionToFEN(move, useFEN960) *p++ = whiteToPlay ? 'w' : 'b'; *p++ = ' '; + if(nrCastlingRights) { /* HACK: we don't keep track of castling availability, so fake it! */ /* Tord! please fix with the aid of castlingRights[move][...] */ @@ -11590,7 +11896,10 @@ PositionToFEN(move, useFEN960) } /* POP Fabien & Tord */ + } + if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi && + gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { /* En passant target square */ if (move > backwardMostMove) { fromX = moveList[move - 1][0] - AAA; @@ -11610,10 +11919,12 @@ PositionToFEN(move, useFEN960) } else { *p++ = '-'; } + *p++ = ' '; + } - /* [HGM] print Crazyhouse holdings */ - if( gameInfo.variant == VariantCrazyhouse ) { - *p++ = ' '; q = p; + /* [HGM] print Crazyhouse or Shogi holdings */ + if( gameInfo.holdingsWidth ) { + q = p; for(i=0; i backwardMostMove && epStatus[j] <= EP_NONE) j--,i++; if( j == backwardMostMove ) i += initialRulePlies; - sprintf(p, " %d", i); - p += i>=100 ? 4 : i >= 10 ? 3 : 2; + sprintf(p, "%d ", i); + p += i>=100 ? 4 : i >= 10 ? 3 : 2; } /* Fullmove number */ - sprintf(p, " %d", (move / 2) + 1); + sprintf(p, "%d", (move / 2) + 1); return StrSave(buf); } @@ -11666,14 +11977,14 @@ ParseFEN(board, blackPlaysFirst, fen) p = fen; /* [HGM] by default clear Crazyhouse holdings, if present */ - if(gameInfo.holdingsWidth) { + if(gameInfo.holdingsWidth) { for(i=0; i= 0; i--) { @@ -11698,11 +12009,19 @@ ParseFEN(board, blackPlaysFirst, fen) if (j + emptycount > gameInfo.boardWidth) return FALSE; while (emptycount--) board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; - } else if (isalpha(*p)) { + } else if (*p == '+' || isalpha(*p)) { if (j >= gameInfo.boardWidth) return FALSE; - piece = CharToPiece(*p++); + if(*p=='+') { + piece = CharToPiece(*++p); + if(piece == EmptySquare) return FALSE; /* unknown piece */ + piece = (ChessSquare) (PROMOTED piece ); p++; + if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */ + } else piece = CharToPiece(*p++); + + if(piece==EmptySquare) return FALSE; /* unknown piece */ if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */ - piece = (ChessSquare) ((int)piece + (int)WhiteAlfil - (int)WhitePawn); + piece = (ChessSquare) (PROMOTED piece); + if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */ p++; } board[i][(j++)+gameInfo.holdingsWidth] = piece; @@ -11727,7 +12046,7 @@ ParseFEN(board, blackPlaysFirst, fen) /* [HGM] We NO LONGER ignore the rest of the FEN notation */ /* return the extra info in global variiables */ - { + /* set defaults in case FEN is incomplete */ FENepStatus = EP_UNKNOWN; for(i=0; i= BOARD_RGHT) return TRUE; - if(*p >= '0' && *p <='9') *p++; - FENepStatus = c; + 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( gameInfo.holdingsWidth ) { 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 */ @@ -11809,12 +12132,11 @@ ParseFEN(board, blackPlaysFirst, fen) } - if(sscanf(p, "%d", &i) == 1) { FENrulePlies = i; /* 50-move ply counter */ /* (The move number is still ignored) */ } - } + return TRUE; }