X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=bbc39be43013acf85de4ef1679d859be476a3dd0;hb=21fe43e32fb967e85a405843bb45cc4a4fc8ab02;hp=acb912319a7999355e64aef352d33f4f2247e499;hpb=8f1c9fee822580af366e6e3624ba7354e1126ce8;p=xboard.git diff --git a/backend.c b/backend.c old mode 100755 new mode 100644 index acb9123..bbc39be --- a/backend.c +++ b/backend.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -168,7 +168,7 @@ int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, void BackwardInner P((int target)); void ForwardInner P((int target)); void GameEnds P((ChessMove result, char *resultDetails, int whosays)); -void EditPositionDone P((void)); +void EditPositionDone P((Boolean fakeRights)); void PrintOpponents P((FILE *fp)); void PrintPosition P((FILE *fp, int move)); void StartChessProgram P((ChessProgramState *cps)); @@ -187,8 +187,9 @@ void DisplayMove P((int moveNumber)); void ParseGameHistory P((char *game)); void ParseBoard12 P((char *string)); +void KeepAlive P((void)); void StartClocks P((void)); -void SwitchClocks P((void)); +void SwitchClocks P((int nr)); void StopClocks P((void)); void ResetClocks P((void)); char *PGNDate P((void)); @@ -242,6 +243,7 @@ char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */ VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */ int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */ +Boolean connectionAlive;/* [HGM] alive: ICS connection status from probing */ int opponentKibitzes; int lastSavedGame; /* [HGM] save: ID of game */ char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */ @@ -353,6 +355,7 @@ PosFlags(index) case VariantNoCastle: case VariantShatranj: case VariantCourier: + case VariantMakruk: flags &= ~F_ALL_CASTLE_OK; break; default: @@ -474,11 +477,11 @@ ChessSquare KnightmateArray[2][BOARD_SIZE] = { BlackUnicorn, BlackBishop, BlackMan, BlackRook } }; -ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */ - { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen, +ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] white and black different armies! */ + { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, - { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen, - BlackKing, BlackBishop, BlackKnight, BlackRook } + { BlackLance, BlackAlfil, BlackMarshall, BlackAngel, + BlackKing, BlackMarshall, BlackAlfil, BlackLance } }; ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */ @@ -488,6 +491,13 @@ ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatra BlackFerz, BlackAlfil, BlackKnight, BlackRook } }; +ChessSquare makrukArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */ + { WhiteRook, WhiteKnight, WhiteMan, WhiteKing, + WhiteFerz, WhiteMan, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackMan, BlackFerz, + BlackKing, BlackMan, BlackKnight, BlackRook } +}; + #if (BOARD_SIZE>=10) ChessSquare ShogiArray[2][BOARD_SIZE] = { @@ -607,7 +617,7 @@ InitBackEnd1() ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options GetTimeMark(&programStartTime); - srandom(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level + srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level ClearProgramStats(); programStats.ok_to_send = 1; @@ -743,7 +753,7 @@ InitBackEnd1() /* [HGM] time odds: set factor for each machine */ first.timeOdds = appData.firstTimeOdds; second.timeOdds = appData.secondTimeOdds; - { int norm = 1; + { float norm = 1; if(appData.timeOddsMode) { norm = first.timeOdds; if(norm > second.timeOdds) norm = second.timeOdds; @@ -873,6 +883,7 @@ 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 VariantBerolina: /* might work if TestLegality is off */ case VariantCapaRandom: /* should work */ case VariantJanus: /* should work */ @@ -1097,6 +1108,8 @@ InitBackEnd3 P((void)) AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR); fromUserISR = AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR); + if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command + ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000); } else if (appData.noChessProgram) { SetNCPMode(); } else { @@ -1417,6 +1430,8 @@ read_from_player(isr, closure, message, count, error) void KeepAlive() { // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out + if(!connectionAlive) DisplayFatalError("No response from ICS", 0, 1); + connectionAlive = FALSE; // only sticks if no response to 'date' command. SendToICS("date\n"); if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000); } @@ -1904,6 +1919,8 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) ChessSquare piece; if(gameInfo.holdingsWidth < 2) return; + if(gameInfo.variant != VariantBughouse && board[BOARD_SIZE-1][BOARD_SIZE-2]) + return; // prevent overwriting by pre-board holdings if( (int)lowestPiece >= BlackPawn ) { holdingsColumn = 0; @@ -1932,7 +1949,6 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } - } @@ -1940,6 +1956,7 @@ void VariantSwitch(Board board, VariantClass newVariant) { int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; + Board oldBoard; startedFromPositionFile = FALSE; if(gameInfo.variant == newVariant) return; @@ -2011,8 +2028,10 @@ VariantSwitch(Board board, VariantClass newVariant) gameInfo.holdingsWidth = newHoldingsWidth; gameInfo.variant = newVariant; InitDrawingSizes(-2, 0); - InitPosition(TRUE); /* this sets up board[0], but also other stuff */ - } else { gameInfo.variant = newVariant; InitPosition(TRUE); } + } else gameInfo.variant = newVariant; + CopyBoard(oldBoard, board); // remember correctly formatted board + InitPosition(FALSE); /* this sets up board[0], but also other stuff */ + DrawPosition(TRUE, currentMove ? boards[currentMove] : oldBoard); } static int loggedOn = FALSE; @@ -2038,7 +2057,7 @@ read_from_ics(isr, closure, data, count, error) int count; int error; { -#define BUF_SIZE 8192 +#define BUF_SIZE (16*1024) /* overflowed at 8K with "inchannel 1" on FICS? */ #define STARTED_NONE 0 #define STARTED_MOVES 1 #define STARTED_BOARD 2 @@ -2067,6 +2086,8 @@ read_from_ics(isr, closure, data, count, error) char talker[MSG_SIZ]; // [HGM] chat int channel; + connectionAlive = TRUE; // [HGM] alive: I think, therefore I am... + if (appData.debugMode) { if (!error) { fprintf(debugFP, " MSG_SIZ - 30) // defuse unreasonably long input { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; } parse[parse_pos] = NULLCHAR; // try to be smart: if it does not look like search info, it should go to // ICS interaction window after all, not to engine-output window. - for(i=0; i= '0' && parse[i] <= '9'); - nrAlph += (parse[i] >= 'a' && parse[i] <= 'z'); - nrAlph += (parse[i] >= 'A' && parse[i] <= 'Z'); + for(j=0; j= '0' && parse[j] <= '9'); + nrAlph += (parse[j] >= 'a' && parse[j] <= 'z'); + nrAlph += (parse[j] >= 'A' && parse[j] <= 'Z'); } if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info int depth=0; float score; @@ -2323,6 +2345,7 @@ read_from_ics(isr, closure, data, count, error) pvInfoList[forwardMostMove-1].score = 100*score; } OutputKibitz(suppressKibitz, parse); + next_out = i+1; // [HGM] suppress printing in ICS window } else { char tmp[MSG_SIZ]; sprintf(tmp, _("your opponent kibitzes: %s"), parse); @@ -2331,7 +2354,7 @@ read_from_ics(isr, closure, data, count, error) } started = STARTED_NONE; } else { - /* Don't match patterns against characters in chatter */ + /* Don't match patterns against characters in comment */ i++; continue; } @@ -2431,9 +2454,13 @@ read_from_ics(isr, closure, data, count, error) } continue; } else - if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz - started = STARTED_CHATTER; - suppressKibitz = TRUE; + if(looking_at(buf, &i, "kibitzed to *\n") && atoi(star_match[0])) { + // suppress the acknowledgements of our own autoKibitz + char *p; + if(p = strchr(star_match[0], ' ')) p[1] = NULLCHAR; // clip off "players)" on FICS + SendToPlayer(star_match[0], strlen(star_match[0])); + looking_at(buf, &i, "*% "); // eat prompt + next_out = i; } } // [HGM] kibitz: end of patch @@ -2444,7 +2471,9 @@ read_from_ics(isr, closure, data, count, error) if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") || looking_at(buf, &i, "* whispers:") || looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) || - looking_at(buf, &i, "*(*)(*):") && sscanf(star_match[2], "%d", &channel) == 1 )) { + looking_at(buf, &i, "*(*)(*):") && (sscanf(star_match[2], "%d", &channel),1) || + looking_at(buf, &i, "*(*)(*)(*):") && (sscanf(star_match[3], "%d", &channel),1) || + looking_at(buf, &i, "*(*)(*)(*)(*):") && sscanf(star_match[4], "%d", &channel) == 1 )) { int p; sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle chattingPartner = -1; @@ -2452,14 +2481,14 @@ read_from_ics(isr, closure, data, count, error) if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel for(p=0; p= 3) // [HGM] chat: continuation of line for chat box + chattingPartner = savingComment - 3; // kludge to remember the box } else { started = STARTED_CHATTER; } @@ -2729,7 +2760,7 @@ read_from_ics(isr, closure, data, count, error) moves and soak them up so user can step through them and/or save them. */ - Reset(TRUE, TRUE); + Reset(FALSE, TRUE); gameMode = IcsObserving; ModeHighlight(); ics_gamenum = -1; @@ -2833,7 +2864,9 @@ read_from_ics(isr, closure, data, count, error) if (looking_at(buf, &i, "% ") || ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE) && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book + if(suppressKibitz) next_out = i; savingComment = FALSE; + suppressKibitz = 0; switch (started) { case STARTED_MOVES: case STARTED_MOVES_NOHIDE: @@ -2901,7 +2934,7 @@ read_from_ics(isr, closure, data, count, error) currentMove = forwardMostMove; ClearHighlights();/*!!could figure this out*/ flipView = appData.flipView; - DrawPosition(FALSE, boards[currentMove]); + DrawPosition(TRUE, boards[currentMove]); DisplayBothClocks(); sprintf(str, "%s vs. %s", gameInfo.white, gameInfo.black); @@ -3026,10 +3059,10 @@ read_from_ics(isr, closure, data, count, error) looking_at(buf, &i, "It is not your move")) { /* Illegal move */ if (ics_user_moved && forwardMostMove > backwardMostMove) { // only backup if we already moved - currentMove = --forwardMostMove; + currentMove = forwardMostMove-1; DisplayMove(currentMove - 1); /* before DMError */ DrawPosition(FALSE, boards[currentMove]); - SwitchClocks(); + SwitchClocks(forwardMostMove-1); // [HGM] race DisplayBothClocks(); } DisplayMoveError(_("Illegal move (rejected by ICS)")); // [HGM] but always relay error msg @@ -3130,6 +3163,7 @@ read_from_ics(isr, closure, data, count, error) strncmp(why, "Continuing ", 11) == 0) { gs_gamenum = gamenum; strcpy(gs_kind, strchr(why, ' ') + 1); + VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board #if ZIPPY if (appData.zippyPlay) { ZippyGameStart(whitename, blackname); @@ -3232,16 +3266,14 @@ read_from_ics(isr, closure, data, count, error) if (currentMove == 0 && gameMode == IcsPlayingWhite && appData.premoveWhite) { - sprintf(str, "%s%s\n", ics_prefix, - appData.premoveWhiteText); + sprintf(str, "%s\n", appData.premoveWhiteText); if (appData.debugMode) fprintf(debugFP, "Sending premove:\n"); SendToICS(str); } else if (currentMove == 1 && gameMode == IcsPlayingBlack && appData.premoveBlack) { - sprintf(str, "%s%s\n", ics_prefix, - appData.premoveBlackText); + sprintf(str, "%s\n", appData.premoveBlackText); if (appData.debugMode) fprintf(debugFP, "Sending premove:\n"); SendToICS(str); @@ -3258,8 +3290,10 @@ read_from_ics(isr, closure, data, count, error) /* Usually suppress following prompt */ if (!(forwardMostMove == 0 && gameMode == IcsExamining)) { + while(looking_at(buf, &i, "\n")); // [HGM] skip empty lines if (looking_at(buf, &i, "*% ")) { savingComment = FALSE; + suppressKibitz = 0; } } next_out = i; @@ -3303,6 +3337,7 @@ read_from_ics(isr, closure, data, count, error) /* [HGM] copy holdings to board holdings area */ CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn); CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn); + boards[forwardMostMove][BOARD_SIZE-1][BOARD_SIZE-2] = 1; // flag holdings as set #if ZIPPY if (appData.zippyPlay && first.initDone) { ZippyHoldings(white_holding, black_holding, @@ -3326,7 +3361,9 @@ read_from_ics(isr, closure, data, count, error) } /* Suppress following prompt */ if (looking_at(buf, &i, "*% ")) { + if(strchr(star_match[0], 7)) SendToPlayer("\007", 1); // Bell(); // FICS fuses bell for next board with prompt in zh captures savingComment = FALSE; + suppressKibitz = 0; } next_out = i; } @@ -3336,12 +3373,13 @@ read_from_ics(isr, closure, data, count, error) i++; /* skip unparsed character and loop back */ } - if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window - started != STARTED_HOLDINGS && i > next_out) { - SendToPlayer(&buf[next_out], i - next_out); + if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz +// started != STARTED_HOLDINGS && i > next_out) { // [HGM] should we compare to leftover_start in stead of i? +// SendToPlayer(&buf[next_out], i - next_out); + started != STARTED_HOLDINGS && leftover_start > next_out) { + SendToPlayer(&buf[next_out], leftover_start - next_out); next_out = i; } - suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above leftover_len = buf_len - leftover_start; /* if buffer ends with something we couldn't parse, @@ -3395,7 +3433,7 @@ ParseBoard12(string) char promoChar; int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */ char *bookHit = NULL; // [HGM] book - Boolean weird = FALSE; + Boolean weird = FALSE, reqFlag = FALSE; fromX = fromY = toX = toY = -1; @@ -3425,27 +3463,6 @@ ParseBoard12(string) &moveNum, str, elapsed_time, move_str, &ics_flip, &ticking); - if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files || - weird && (int)gameInfo.variant <= (int)VariantShogi) { - /* [HGM] We seem to switch variant during a game! - * Try to guess new variant from board size - */ - VariantClass newVariant = VariantFairy; // if 8x8, but fairies present - if(ranks == 8 && files == 10) newVariant = VariantCapablanca; else - if(ranks == 10 && files == 9) newVariant = VariantXiangqi; else - if(ranks == 8 && files == 12) newVariant = VariantCourier; else - if(ranks == 9 && files == 9) newVariant = VariantShogi; else - if(!weird) newVariant = VariantNormal; - VariantSwitch(boards[currentMove], newVariant); /* temp guess */ - /* 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) { - ics_getting_history = H_REQUESTED; - sprintf(str, "%smoves %d\n", ics_prefix, gamenum); - SendToICS(str); - } - } - if (n < 21) { snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string); DisplayError(str, 0); @@ -3522,6 +3539,27 @@ ParseBoard12(string) ics_getting_history = H_FALSE; return; } + + if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files || + weird && (int)gameInfo.variant <= (int)VariantShogi) { + /* [HGM] We seem to have switched variant unexpectedly + * Try to guess new variant from board size + */ + VariantClass newVariant = VariantFairy; // if 8x8, but fairies present + if(ranks == 8 && files == 10) newVariant = VariantCapablanca; else + if(ranks == 10 && files == 9) newVariant = VariantXiangqi; else + if(ranks == 8 && files == 12) newVariant = VariantCourier; else + if(ranks == 9 && files == 9) newVariant = VariantShogi; else + if(!weird) newVariant = VariantNormal; + VariantSwitch(boards[currentMove], newVariant); /* temp guess */ + /* 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) { + ics_getting_history = H_REQUESTED; reqFlag = TRUE; + sprintf(str, "%smoves %d\n", ics_prefix, gamenum); + SendToICS(str); + } + } /* Take action if this is the first board of a new game, or of a different game than is currently being displayed. */ @@ -3537,8 +3575,8 @@ ParseBoard12(string) prevMove = -3; if (gamenum == -1) { newGameMode = IcsIdle; - } else if (moveNum > 0 && newGameMode != IcsIdle && - appData.getMoveList) { + } else if ((moveNum > 0 || newGameMode == IcsObserving) && newGameMode != IcsIdle && + appData.getMoveList && !reqFlag) { /* Need to get game history */ ics_getting_history = H_REQUESTED; sprintf(str, "%smoves %d\n", ics_prefix, gamenum); @@ -3574,7 +3612,7 @@ ParseBoard12(string) timeIncrement = increment * 1000; movesPerSession = 0; gameInfo.timeControl = TimeControlTagValue(); - VariantSwitch(board, StringToVariant(gameInfo.event) ); + VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event) ); if (appData.debugMode) { fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event); fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant)); @@ -3654,6 +3692,7 @@ ParseBoard12(string) } } CopyBoard(boards[moveNum], board); + boards[moveNum][BOARD_SIZE-1][BOARD_SIZE-2] = 0; // [HGM] indicate holdings not set if (moveNum == 0) { startedFromSetupPosition = !CompareBoards(board, initialPosition); @@ -3693,6 +3732,11 @@ ParseBoard12(string) for(k=BOARD_LEFT; k forwardMostMove; - /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */ - if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) { - takeback = forwardMostMove - moveNum; - for (i = 0; i < takeback; i++) { - if (appData.debugMode) fprintf(debugFP, "take back move\n"); - SendToProgram("undo\n", &first); - } - } - if (newGame) { forwardMostMove = backwardMostMove = currentMove = moveNum; if (gameMode == IcsExamining && moveNum == 0) { @@ -3741,6 +3776,20 @@ ParseBoard12(string) } } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) { +#if ZIPPY + /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */ + /* [HGM] applied this also to an engine that is silently watching */ + if (appData.zippyPlay && moveNum < forwardMostMove && first.initDone && + (gameMode == IcsObserving || gameMode == IcsExamining) && + gameInfo.variant == currentlyInitializedVariant) { + takeback = forwardMostMove - moveNum; + for (i = 0; i < takeback; i++) { + if (appData.debugMode) fprintf(debugFP, "take back move\n"); + SendToProgram("undo\n", &first); + } + } +#endif + forwardMostMove = moveNum; if (!pausing || currentMove > forwardMostMove) currentMove = forwardMostMove; @@ -3751,12 +3800,20 @@ ParseBoard12(string) forwardMostMove = pauseExamForwardMostMove; return; } - forwardMostMove = backwardMostMove = currentMove = moveNum; if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) { +#if ZIPPY + if(appData.zippyPlay && forwardMostMove > 0 && first.initDone) { + // [HGM] when we will receive the move list we now request, it will be + // fed to the engine from the first move on. So if the engine is not + // in the initial position now, bring it there. + InitChessProgram(&first, 0); + } +#endif ics_getting_history = H_REQUESTED; sprintf(str, "%smoves %d\n", ics_prefix, gamenum); SendToICS(str); } + forwardMostMove = backwardMostMove = currentMove = moveNum; } /* Update the clocks */ @@ -4150,7 +4207,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY) case BlackPromotionChancellor: case WhitePromotionArchbishop: case BlackPromotionArchbishop: - if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) sprintf(user_move, "%c%c%c%c=%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, PieceToChar(WhiteFerz)); @@ -4329,7 +4386,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) if (appData.testLegality) { return (*moveType != IllegalMove); } else { - return !(fromX == fromY && toX == toY); + return !(*fromX == *toX && *fromY == *toY); } case WhiteDrop: @@ -4633,6 +4690,12 @@ InitPosition(redraw) nrCastlingRights = 0; SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k"); break; + case VariantMakruk: + pieces = makrukArray; + nrCastlingRights = 0; + startedFromSetupPosition = TRUE; + SetCharTable(pieceToChar, "PN.R.M....SKpn.r.m....sk"); + break; case VariantTwoKings: pieces = twoKingsArray; break; @@ -4693,7 +4756,7 @@ InitPosition(redraw) break; case VariantFairy: pieces = fairyArray; - SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); + SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); break; case VariantGreat: pieces = GreatArray; @@ -4746,6 +4809,7 @@ InitPosition(redraw) pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ if(pawnRow < 1) pawnRow = 1; + if(gameInfo.variant == VariantMakruk) pawnRow = 2; /* User pieceToChar list overrules defaults */ if(appData.pieceToCharTable != NULL) @@ -4931,6 +4995,8 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) if(gameInfo.variant == VariantShogi) { promotionZoneSize = 3; highestPromotingPiece = (int)WhiteFerz; + } else if(gameInfo.variant == VariantMakruk) { + promotionZoneSize = 3; } // next weed out all moves that do not touch the promotion zone at all @@ -4974,7 +5040,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice) } // we either have a choice what to promote to, or (in Shogi) whether to promote - if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) { + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) { *promoChoice = PieceToChar(BlackFerz); // no choice return FALSE; } @@ -5258,6 +5324,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return ImpossibleMove; } + if(toX < 0 || toY < 0) return ImpossibleMove; pdown = boards[currentMove][fromY][fromX]; pup = boards[currentMove][toY][toX]; @@ -5289,7 +5356,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn) return ImpossibleMove; } } -if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar); + return moveType; /* [HGM] in stead of calling FinishMove directly, this function is made into one that returns an OK move type if FinishMove @@ -5308,7 +5375,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) /*char*/int promoChar; { char *bookHit = 0; -if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar); + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { // [HGM] superchess: suppress promotions to non-available piece int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); @@ -5323,13 +5390,12 @@ if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", move move type in caller when we know the move is a legal promotion */ if(moveType == NormalMove && promoChar) moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar); -if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar); + /* [HGM] convert drag-and-drop piece drops to standard form */ - if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { + if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ){ moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop; if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]); -// fromX = boards[currentMove][fromY][fromX]; // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings @@ -5338,7 +5404,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move } /* [HGM] The following if has been moved here from - UserMoveEvent(). Because it seemed to belon here (why not allow + UserMoveEvent(). Because it seemed to belong 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) { @@ -5412,7 +5478,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move } ModeHighlight(); } -if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar); + /* Relay move to ICS or chess engine */ if (appData.icsActive) { if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || @@ -5602,16 +5668,11 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } return; } - // ignore to-clicks in holdings + // ignore clicks on holdings if(x < BOARD_LEFT || x >= BOARD_RGHT) return; } - if (clickType == Release && (x == fromX && y == fromY || - x < BOARD_LEFT || x >= BOARD_RGHT)) { - - // treat drags into holding as click on start square - x = fromX; y = fromY; - + if (clickType == Release && x == fromX && y == fromY) { DragPieceEnd(xPix, yPix); if (appData.animateDragging) { /* Undo animation damage if any */ @@ -5631,7 +5692,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) return; } - /* we now have a different from- and to-square */ + /* we now have a different from- and (possibly off-board) to-square */ /* Completed move */ toX = x; toY = y; @@ -5654,6 +5715,18 @@ void LeftClick(ClickType clickType, int xPix, int yPix) /* Don't animate move and drag both */ appData.animate = FALSE; } + + // moves into holding are invalid for now (later perhaps allow in EditPosition) + if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) { + ClearHighlights(); + fromX = fromY = -1; + DrawPosition(TRUE, NULL); + return; + } + + // off-board moves should not be highlighted + if(x < 0 || x < 0) ClearHighlights(); + if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) { SetHighlights(fromX, fromY, toX, toY); if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { @@ -6197,15 +6270,6 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } else moveCount = 6; } } - - if (appData.debugMode) { int i; - fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n", - forwardMostMove, backwardMostMove, epStatus[backwardMostMove], - appData.drawRepeats); - for( i=forwardMostMove; i>=backwardMostMove; i-- ) - fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]); - - } /* Check for rep-draws */ count = 0; @@ -6613,9 +6677,9 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. gameMode = EditGame; ModeHighlight(); } - currentMove = --forwardMostMove; + currentMove = forwardMostMove-1; DisplayMove(currentMove-1); /* before DisplayMoveError */ - SwitchClocks(); + SwitchClocks(forwardMostMove-1); // [HGM] race DisplayBothClocks(); sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"), parseList[currentMove], cps->which); @@ -6906,7 +6970,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* [AS] Negate score if machine is playing black and reporting absolute scores */ if( cps->scoreIsAbsolute && - ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) ) + ( gameMode == MachinePlaysBlack || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b' || + gameMode == IcsPlayingBlack || // [HGM] also add other situations where engine should report black POV + (gameMode == AnalyzeMode || gameMode == AnalyzeFile || gameMode == IcsObserving && appData.icsEngineAnalyze) && + !WhiteOnMove(currentMove) + ) ) { curscore = -curscore; } @@ -6923,9 +6992,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if(cps->nps == 0) ticklen = 10*time; // use engine reported time else ticklen = (1000. * u64ToDouble(nodes)) / cps->nps; // convert node count to time - if(WhiteOnMove(forwardMostMove)) + if(WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w')) whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen; - else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen; + if(!WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack || + gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) + blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen; } /* Buffer overflow protection */ @@ -6933,8 +7005,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (strlen(buf1) >= sizeof(programStats.movelist) && appData.debugMode) { fprintf(debugFP, - "PV is too long; using the first %d bytes.\n", - sizeof(programStats.movelist) - 1); + "PV is too long; using the first %u bytes.\n", + (unsigned) sizeof(programStats.movelist) - 1); } safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) ); @@ -7323,6 +7395,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) char *ep; { ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0; + int promoRank = gameInfo.variant == VariantMakruk ? 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 */ @@ -7369,7 +7442,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) } /* [HGM] In Shatranj and Courier all promotions are to Ferz */ - if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier) + if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk) && promoChar != 0) promoChar = PieceToChar(WhiteFerz); if (fromX == toX && fromY == toY) return; @@ -7420,7 +7493,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) board[toY][toX+1] = board[fromY][BOARD_LEFT]; board[fromY][BOARD_LEFT] = EmptySquare; } else if (board[fromY][fromX] == WhitePawn - && toY == BOARD_HEIGHT-1 + && toY >= BOARD_HEIGHT-promoRank && gameInfo.variant != VariantXiangqi ) { /* white pawn promotion */ @@ -7484,13 +7557,13 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep) board[fromY][0] = EmptySquare; board[toY][2] = BlackRook; } else if (board[fromY][fromX] == BlackPawn - && toY == 0 + && toY < promoRank && gameInfo.variant != VariantXiangqi ) { /* black pawn promotion */ - board[0][toX] = CharToPiece(ToLower(promoChar)); - if (board[0][toX] == EmptySquare) { - board[0][toX] = BlackQueen; + 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 */ @@ -7685,8 +7758,8 @@ MakeMove(fromX, fromY, toX, toY, promoChar) {int i; for(i=0; i= GE_ENGINE1)) { /* If we are playing on ICS, the server decides when the game is over, but the engine can offer to draw, claim @@ -9920,17 +9995,9 @@ SaveGamePGN(f) /* [AS] Add PV info if present */ if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) { /* [HGM] add time */ - char buf[MSG_SIZ]; int seconds = 0; + char buf[MSG_SIZ]; int seconds; - if(i >= backwardMostMove) { - if(WhiteOnMove(i)) - seconds = timeRemaining[0][i] - timeRemaining[0][i+1] - + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds); - else - seconds = timeRemaining[1][i] - timeRemaining[1][i+1] - + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds); - } - seconds = (seconds+50)/100; // deci-seconds, rounded to nearest + seconds = (pvInfoList[i].time+5)/10; // deci-seconds, rounded to nearest if( seconds <= 0) buf[0] = 0; else if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else { @@ -10059,7 +10126,7 @@ SaveGame(f, dummy, dummy2) int dummy; char *dummy2; { - if (gameMode == EditPosition) EditPositionDone(); + if (gameMode == EditPosition) EditPositionDone(TRUE); lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving if (appData.oldSaveStyle) return SaveGameOldStyle(f); @@ -10099,7 +10166,8 @@ SavePosition(f, dummy, dummy2) { time_t tm; char *fen; - + + if (gameMode == EditPosition) EditPositionDone(TRUE); if (appData.oldSaveStyle) { tm = time((time_t *) NULL); @@ -10677,7 +10745,7 @@ MachineWhiteEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (!WhiteOnMove(currentMove)) { DisplayError(_("It is not White's turn"), 0); @@ -10758,7 +10826,7 @@ MachineBlackEvent() EditGameEvent(); if (gameMode == EditPosition) - EditPositionDone(); + EditPositionDone(TRUE); if (WhiteOnMove(currentMove)) { DisplayError(_("It is not Black's turn"), 0); @@ -10865,7 +10933,7 @@ TwoMachinesEvent P((void)) if (gameMode != EditGame) return; break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -11003,7 +11071,7 @@ IcsClientEvent() break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: @@ -11044,7 +11112,7 @@ EditGameEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case AnalyzeMode: case AnalyzeFile: @@ -11151,21 +11219,30 @@ ExitAnalyzeMode() } void -EditPositionDone() +EditPositionDone(Boolean fakeRights) { int king = gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing; startedFromSetupPosition = TRUE; InitChessProgram(&first, FALSE); - castlingRights[0][2] = castlingRights[0][5] = BOARD_WIDTH>>1; - if(boards[0][0][BOARD_WIDTH>>1] == king) { - castlingRights[0][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : -1; - castlingRights[0][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : -1; - } else castlingRights[0][2] = -1; - if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king) { - castlingRights[0][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : -1; - castlingRights[0][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : -1; - } else castlingRights[0][5] = -1; + if(fakeRights) + { /* don't do this if we just pasted FEN */ + castlingRights[0][2] = castlingRights[0][5] = BOARD_WIDTH>>1; + if(boards[0][0][BOARD_WIDTH>>1] == king) + { + castlingRights[0][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : -1; + castlingRights[0][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : -1; + } + else + castlingRights[0][2] = -1; + if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king) + { + castlingRights[0][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : -1; + castlingRights[0][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : -1; + } + else + castlingRights[0][5] = -1; + } SendToProgram("force\n", &first); if (blackPlaysFirst) { strcpy(moveList[0], ""); @@ -11337,7 +11414,8 @@ EditPositionMenuEvent(selection, x, y) case BlackQueen: if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi || - gameInfo.variant == VariantCourier ) + gameInfo.variant == VariantCourier || + gameInfo.variant == VariantMakruk ) selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz); goto defaultlabel; @@ -11802,7 +11880,7 @@ void ToStartEvent() { if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) { - /* to optimze, we temporarily turn off analysis mode while we undo + /* to optimize, we temporarily turn off analysis mode while we undo * all the moves. Otherwise we get analysis output after each undo. */ if (first.analysisSupport) { @@ -11997,7 +12075,7 @@ BookEvent() } break; case EditPosition: - EditPositionDone(); + EditPositionDone(TRUE); break; case TwoMachinesPlay: return; @@ -12382,7 +12460,7 @@ SendToProgram(message, cps) } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } DisplayFatalError(buf, error, 1); } @@ -12413,7 +12491,7 @@ ReceiveFromProgram(isr, closure, message, count, error) } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } - gameInfo.resultDetails = buf; + gameInfo.resultDetails = StrSave(buf); } RemoveInputSource(cps->isr); DisplayFatalError(buf, 0, 1); @@ -12574,7 +12652,7 @@ SendTimeRemaining(cps, machineWhite) /* [HGM] translate opponent's time by time-odds factor */ otime = (otime * cps->other->timeOdds) / cps->timeOdds; if (appData.debugMode) { - fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds); + fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds); } if (time <= 0) time = 1; @@ -12820,7 +12898,7 @@ ParseFeatures(args, cps) /* unknown feature: complain and skip */ q = p; while (*q && *q != '=') q++; - sprintf(buf, "rejected %.*s\n", q-p, p); + sprintf(buf, "rejected %.*s\n", (int)(q-p), p); SendToProgram(buf, cps); p = q; if (*p == '=') { @@ -12861,7 +12939,7 @@ PonderNextMoveEvent(newState) int newState; { if (newState == appData.ponderNextMove) return; - if (gameMode == EditPosition) EditPositionDone(); + if (gameMode == EditPosition) EditPositionDone(TRUE); if (newState) { SendToProgram("hard\n", &first); if (gameMode == TwoMachinesPlay) { @@ -12884,7 +12962,7 @@ NewSettingEvent(option, command, value) { char buf[MSG_SIZ]; - if (gameMode == EditPosition) EditPositionDone(); + if (gameMode == EditPosition) EditPositionDone(TRUE); sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value); SendToProgram(buf, &first); if (gameMode == TwoMachinesPlay) { @@ -12903,7 +12981,7 @@ ShowThinkingEvent() if (oldState == newState) return; oldState = newState; - if (gameMode == EditPosition) EditPositionDone(); + if (gameMode == EditPosition) EditPositionDone(TRUE); if (oldState) { SendToProgram("post\n", &first); if (gameMode == TwoMachinesPlay) { @@ -12994,26 +13072,23 @@ DisplayComment(moveNumber, text) char title[MSG_SIZ]; char buf[8000]; // comment can be long! int score, depth; - - if( appData.autoDisplayComment ) { - if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) { - strcpy(title, "Comment"); - } else { - sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1, - WhiteOnMove(moveNumber) ? " " : ".. ", - parseList[moveNumber]); - } - // [HGM] PV info: display PV info together with (or as) comment - if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) { - if(text == NULL) text = ""; - score = pvInfoList[moveNumber].score; - sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100., - depth, (pvInfoList[moveNumber].time+50)/100, text); - text = buf; - } - } else title[0] = 0; - - if (text != NULL) + + if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) { + strcpy(title, "Comment"); + } else { + sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1, + WhiteOnMove(moveNumber) ? " " : ".. ", + parseList[moveNumber]); + } + // [HGM] PV info: display PV info together with (or as) comment + if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) { + if(text == NULL) text = ""; + score = pvInfoList[moveNumber].score; + sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100., + depth, (pvInfoList[moveNumber].time+50)/100, text); + text = buf; + } + if (text != NULL && (appData.autoDisplayComment || commentUp)) CommentPopUp(title, text); } @@ -13312,7 +13387,7 @@ DecrementClocks() from the color that is *not* on move now. */ void -SwitchClocks() +SwitchClocks(int newMoveNr) { long lastTickLength; TimeMark now; @@ -13322,7 +13397,7 @@ SwitchClocks() if (StopClockTimer() && appData.clockMode) { lastTickLength = SubtractTimeMarks(&now, &tickStartTM); - if (WhiteOnMove(forwardMostMove)) { + if (!WhiteOnMove(forwardMostMove)) { if(blackNPS >= 0) lastTickLength = 0; blackTimeRemaining -= lastTickLength; /* [HGM] PGNtime: save time for PGN file if engine did not give it */ @@ -13339,6 +13414,7 @@ SwitchClocks() } flagged = CheckFlags(); } + forwardMostMove = newMoveNr; // [HGM] race: change stm when no timer interrupt scheduled CheckTimeControl(); if (flagged || !appData.clockMode) return; @@ -13658,7 +13734,7 @@ PositionToFEN(move, overrideCastling) *p++ = ' '; if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines - while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; + while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p; } else { if(nrCastlingRights) { q = p; @@ -13695,7 +13771,7 @@ PositionToFEN(move, overrideCastling) } if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi && - gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { + gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) { /* En passant target square */ if (move > backwardMostMove) { fromX = moveList[move - 1][0] - AAA; @@ -13870,10 +13946,12 @@ ParseFEN(board, blackPlaysFirst, fen) } /* assume possible unless obviously impossible */ if(initialRights[0]>=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[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteUnicorn + && 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; + if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackUnicorn + && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1; FENrulePlies = 0; while(*p==' ') p++; @@ -13894,9 +13972,15 @@ ParseFEN(board, blackPlaysFirst, fen) if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i; if(board[0 ][i] == WhiteKing) whiteKingFile = i; } + if(gameInfo.variant == VariantTwoKings || gameInfo.variant == VariantKnightmate) + whiteKingFile = blackKingFile = BOARD_WIDTH >> 1; // scanning fails in these variants + if(whiteKingFile<0 || board[0][whiteKingFile]!=WhiteUnicorn + && board[0][whiteKingFile]!=WhiteKing) whiteKingFile = -1; + if(blackKingFile<0 || board[BOARD_HEIGHT-1][blackKingFile]!=BlackUnicorn + && board[BOARD_HEIGHT-1][blackKingFile]!=BlackKing) blackKingFile = -1; switch(c) { case'K': - for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--); + for(i=BOARD_RGHT-1; i>whiteKingFile && board[0][i]!=WhiteRook; i--); FENcastlingRights[0] = i != whiteKingFile ? i : -1; FENcastlingRights[2] = whiteKingFile; break; @@ -13906,7 +13990,7 @@ ParseFEN(board, blackPlaysFirst, fen) FENcastlingRights[2] = whiteKingFile; break; case'k': - for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--); + for(i=BOARD_RGHT-1; i>blackKingFile && board[BOARD_HEIGHT-1][i]!=BlackRook; i--); FENcastlingRights[3] = i != blackKingFile ? i : -1; FENcastlingRights[5] = blackKingFile; break; @@ -13943,6 +14027,8 @@ ParseFEN(board, blackPlaysFirst, fen) } } } + for(i=0; i= 0) initialRights[i] = FENcastlingRights[i]; if (appData.debugMode) { fprintf(debugFP, "FEN castling rights:"); for(i=0; i