X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=3205cdea754e33645f66b3154418d4ace5eb8953;hb=9384069316e400042076bd749ba0c6e0b07427fa;hp=54967970583092393999eca7cb0c37f304577eea;hpb=762e18baef5f815cc46019ba3e082bd5ec1980f6;p=xboard.git diff --git a/backend.c b/backend.c index 5496797..3205cde 100644 --- a/backend.c +++ b/backend.c @@ -2,8 +2,10 @@ * backend.c -- Common back end for X and Windows NT versions of * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $ * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-2001 Free Software Foundation, Inc. + * Copyright 1991 by Digital Equipment Corporation, Maynard, + * Massachusetts. Enhancements Copyright + * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software + * Foundation, Inc. * * The following terms apply to Digital Equipment Corporation's copyright * interest in XBoard: @@ -27,25 +29,25 @@ * SOFTWARE. * ------------------------------------------------------------------------ * - * The following terms apply to the enhanced version of XBoard distributed - * by the Free Software Foundation: + * The following terms apply to the enhanced version of XBoard + * distributed by the Free Software Foundation: * ------------------------------------------------------------------------ - * This program is free software; you can redistribute it and/or modify + * + * GNU XBoard is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU XBoard is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * ------------------------------------------------------------------------ + * along with this program. If not, see http://www.gnu.org/licenses/. * * - * See the file ChangeLog for a revision history. */ + *------------------------------------------------------------------------ + ** See the file ChangeLog for a revision history. */ /* [AS] Also useful here for debugging */ #ifdef WIN32 @@ -55,7 +57,7 @@ #else -#define DoSleep( n ) +#define DoSleep( n ) if( (n) >= 0) sleep(n) #endif @@ -68,6 +70,7 @@ #include #include #include +#include #if STDC_HEADERS # include @@ -120,6 +123,16 @@ extern int gettimeofday(struct timeval *, struct timezone *); # include "zippy.h" #endif #include "backendz.h" +#include "gettext.h" + +#ifdef ENABLE_NLS +# define _(s) gettext (s) +# define N_(s) gettext_noop (s) +#else +# define _(s) (s) +# define N_(s) s +#endif + /* A point in time */ typedef struct { @@ -127,23 +140,6 @@ typedef struct { int ms; /* Assuming this is >= 16 bits */ } TimeMark; -/* Search stats from chessprogram */ -typedef struct { - char movelist[2*MSG_SIZ]; /* Last PV we were sent */ - int depth; /* Current search depth */ - int nr_moves; /* Total nr of root moves */ - int moves_left; /* Moves remaining to be searched */ - char move_name[MOVE_LEN]; /* Current move being searched, if provided */ - unsigned long nodes; /* # of nodes searched */ - int time; /* Search time (centiseconds) */ - int score; /* Score (centipawns) */ - int got_only_move; /* If last msg was "(only move)" */ - int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */ - int ok_to_send; /* handshaking between send & recv */ - int line_is_book; /* 1 if movelist is book moves */ - int seen_stat; /* 1 if we've seen the stat01: line */ -} ChessProgramStats; - int establish P((void)); void read_from_player P((InputSourceRef isr, VOIDSTAR closure, char *buf, int count, int error)); @@ -161,7 +157,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList)); int LoadPositionFromFile P((char *filename, int n, char *title)); int SavePositionToFile P((char *filename)); void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar, - Board board)); + Board board, char *castle, char *ep)); void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar)); void ShowMove P((int fromX, int fromY, int toX, int toY)); int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, @@ -215,7 +211,18 @@ void ParseFeatures P((char* args, ChessProgramState *cps)); void InitBackEnd3 P((void)); void FeatureDone P((ChessProgramState* cps, int val)); void InitChessProgram P((ChessProgramState *cps, int setup)); +void OutputKibitz(int window, char *text); +int PerpetualChase(int first, int last); +int EngineOutputIsUp(); +void InitDrawingSizes(int x, int y); + +#ifdef WIN32 + extern void ConsoleCreate(); +#endif + ChessProgramState *WhitePlayer(); +void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c +int VerifyDisplayMode P(()); char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c @@ -224,15 +231,15 @@ char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [H extern char installDir[MSG_SIZ]; extern int tinyLayout, smallLayout; -static ChessProgramStats programStats; +ChessProgramStats programStats; static int exiting = 0; /* [HGM] moved to top */ -static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0; -extern int startedFromPositionFile; +static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/; int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */ 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 */ +int opponentKibitzes; /* States for ics_getting_history */ #define H_FALSE 0 @@ -287,6 +294,8 @@ static char * safeStrCpy( char * dst, const char * src, size_t count ) return dst; } +#if 0 +//[HGM] for future use? Conditioned out for now to suppress warning. static char * safeStrCat( char * dst, const char * src, size_t count ) { size_t dst_len; @@ -303,6 +312,27 @@ static char * safeStrCat( char * dst, const char * src, size_t count ) return dst; } +#endif + +/* Some compiler can't cast u64 to double + * This function do the job for us: + + * We use the highest bit for cast, this only + * works if the highest bit is not + * in use (This should not happen) + * + * We used this for all compiler + */ +double +u64ToDouble(u64 value) +{ + double r; + u64 tmp = value & u64Const(0x7fffffffffffffff); + r = (double)(s64)tmp; + if (value & u64Const(0x8000000000000000)) + r += 9.2233720368547758080e18; /* 2^63 */ + return r; +} /* Fake up flags for now, as we aren't keeping track of castling availability yet. [HGM] Change of logic: the flag now only @@ -318,9 +348,11 @@ PosFlags(index) if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE; switch (gameInfo.variant) { case VariantSuicide: - case VariantGiveaway: - flags |= F_IGNORE_CHECK; flags &= ~F_ALL_CASTLE_OK; + case VariantGiveaway: // [HGM] moved this case label one down: seems Giveaway does have castling on ICC! + flags |= F_IGNORE_CHECK; + case VariantLosers: + flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist break; case VariantAtomic: flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE; @@ -369,12 +401,8 @@ int gotPremove = 0; Boolean alarmSounded; /* end premove variables */ -#define ICS_GENERIC 0 -#define ICS_ICC 1 -#define ICS_FICS 2 -#define ICS_CHESSNET 3 /* not really supported */ -int ics_type = ICS_GENERIC; char *ics_prefix = "$"; +int ics_type = ICS_GENERIC; int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0; int pauseExamForwardMostMove = 0; @@ -495,6 +523,13 @@ ChessSquare CapablancaArray[2][BOARD_SIZE] = { BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook } }; +ChessSquare GreatArray[2][BOARD_SIZE] = { + { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, + WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon }, + { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, + BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon }, +}; + ChessSquare JanusArray[2][BOARD_SIZE] = { { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook }, @@ -528,6 +563,7 @@ ChessSquare FalconArray[2][BOARD_SIZE] = { #define XiangqiPosition FIDEArray #define CapablancaArray FIDEArray #define GothicArray FIDEArray +#define GreatArray FIDEArray #endif // !(BOARD_SIZE>=10) #if (BOARD_SIZE>=12) @@ -583,6 +619,7 @@ InitBackEnd1() ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options GetTimeMark(&programStartTime); + srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level ClearProgramStats(); programStats.ok_to_send = 1; @@ -632,7 +669,7 @@ InitBackEnd1() if (!ParseTimeControl(appData.timeControl, appData.timeIncrement, appData.movesPerSession)) { char buf[MSG_SIZ]; - sprintf(buf, "bad timeControl option %s", appData.timeControl); + snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl); DisplayFatalError(buf, 0, 2); } @@ -647,7 +684,7 @@ InitBackEnd1() searchTime = min * 60 + sec; } else { char buf[MSG_SIZ]; - sprintf(buf, "bad searchTime option %s", appData.searchTime); + snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime); DisplayFatalError(buf, 0, 2); } } @@ -712,6 +749,8 @@ InitBackEnd1() first.useFEN960 = FALSE; second.useFEN960 = FALSE; first.useOOCastle = TRUE; second.useOOCastle = TRUE; /* End of new features added by Tord. */ + first.fenOverride = appData.fenOverride1; + second.fenOverride = appData.fenOverride2; /* [HGM] time odds: set factor for each machine */ first.timeOdds = appData.firstTimeOdds; @@ -734,6 +773,10 @@ InitBackEnd1() first.debug = second.debug = FALSE; first.supportsNPS = second.supportsNPS = UNKNOWN; + /* [HGM] options */ + first.optionSettings = appData.firstOptions; + second.optionSettings = appData.secondOptions; + first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */ second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */ first.isUCI = appData.firstIsUCI; /* [AS] */ @@ -744,7 +787,7 @@ InitBackEnd1() if (appData.firstProtocolVersion > PROTOVER || appData.firstProtocolVersion < 1) { char buf[MSG_SIZ]; - sprintf(buf, "protocol version %d not supported", + sprintf(buf, _("protocol version %d not supported"), appData.firstProtocolVersion); DisplayFatalError(buf, 0, 2); } else { @@ -754,7 +797,7 @@ InitBackEnd1() if (appData.secondProtocolVersion > PROTOVER || appData.secondProtocolVersion < 1) { char buf[MSG_SIZ]; - sprintf(buf, "protocol version %d not supported", + sprintf(buf, _("protocol version %d not supported"), appData.secondProtocolVersion); DisplayFatalError(buf, 0, 2); } else { @@ -788,7 +831,7 @@ InitBackEnd1() q = first.program; while (*q != ' ' && *q != NULLCHAR) q++; p = q; - while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */ + while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] backslash added */ programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION) + strlen(PATCHLEVEL) + (q - p)); sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL); @@ -812,7 +855,7 @@ InitBackEnd1() case VariantBughouse: /* need four players and two boards */ case VariantKriegspiel: /* need to hide pieces and move details */ /* case VariantFischeRandom: (Fabien: moved below) */ - sprintf(buf, "Variant %s supported only in ICS mode", appData.variant); + sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant); DisplayFatalError(buf, 0, 2); return; @@ -827,7 +870,7 @@ InitBackEnd1() case Variant35: case Variant36: default: - sprintf(buf, "Unknown variant name %s", appData.variant); + sprintf(buf, _("Unknown variant name %s"), appData.variant); DisplayFatalError(buf, 0, 2); return; @@ -860,6 +903,7 @@ InitBackEnd1() case VariantCapaRandom: /* should work */ case VariantJanus: /* should work */ case VariantSuper: /* experimental */ + case VariantGreat: /* experimental, requires legality testing to be off */ break; } } @@ -1054,7 +1098,7 @@ InitBackEnd2() } else { /* kludge: allow timeout for initial "feature" commands */ FreezeUI(); - DisplayMessage("", "Starting chess program"); + DisplayMessage("", _("Starting chess program")); ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT); } } @@ -1068,14 +1112,19 @@ InitBackEnd3 P((void)) InitChessProgram(&first, startedFromSetupPosition); + if (appData.icsActive) { +#ifdef WIN32 + /* [DM] Make a console window if needed [HGM] merged ifs */ + ConsoleCreate(); +#endif err = establish(); if (err != 0) { if (*appData.icsCommPort != NULLCHAR) { - sprintf(buf, "Could not open comm port %s", + sprintf(buf, _("Could not open comm port %s"), appData.icsCommPort); } else { - sprintf(buf, "Could not connect to host %s, port %s", + snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"), appData.icsHost, appData.icsPort); } DisplayFatalError(buf, err, 1); @@ -1120,7 +1169,7 @@ InitBackEnd3 P((void)) } else if (StrCaseCmp(appData.initialMode, "Training") == 0) { initialMode = Training; } else { - sprintf(buf, "Unknown initialMode %s", appData.initialMode); + sprintf(buf, _("Unknown initialMode %s"), appData.initialMode); DisplayFatalError(buf, 0, 2); return; } @@ -1128,7 +1177,7 @@ InitBackEnd3 P((void)) if (appData.matchMode) { /* Set up machine vs. machine match */ if (appData.noChessProgram) { - DisplayFatalError("Can't have a match with no chess programs", + DisplayFatalError(_("Can't have a match with no chess programs"), 0, 2); return; } @@ -1140,7 +1189,7 @@ InitBackEnd3 P((void)) if (!LoadGameFromFile(appData.loadGameFile, index, appData.loadGameFile, FALSE)) { - DisplayFatalError("Bad game file", 0, 1); + DisplayFatalError(_("Bad game file"), 0, 1); return; } } else if (*appData.loadPositionFile != NULLCHAR) { @@ -1149,7 +1198,7 @@ InitBackEnd3 P((void)) if (!LoadPositionFromFile(appData.loadPositionFile, index, appData.loadPositionFile)) { - DisplayFatalError("Bad position file", 0, 1); + DisplayFatalError(_("Bad position file"), 0, 1); return; } } @@ -1161,7 +1210,7 @@ InitBackEnd3 P((void)) /* Set up other modes */ if (initialMode == AnalyzeFile) { if (*appData.loadGameFile == NULLCHAR) { - DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1); + DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1); return; } } @@ -1189,11 +1238,11 @@ InitBackEnd3 P((void)) } if (initialMode == AnalyzeMode) { if (appData.noChessProgram) { - DisplayFatalError("Analysis mode requires a chess engine", 0, 2); + DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2); return; } if (appData.icsActive) { - DisplayFatalError("Analysis mode does not work with ICS mode",0,2); + DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2); return; } AnalyzeModeEvent(); @@ -1204,36 +1253,36 @@ InitBackEnd3 P((void)) AnalysisPeriodicEvent(1); } else if (initialMode == MachinePlaysWhite) { if (appData.noChessProgram) { - DisplayFatalError("MachineWhite mode requires a chess engine", + DisplayFatalError(_("MachineWhite mode requires a chess engine"), 0, 2); return; } if (appData.icsActive) { - DisplayFatalError("MachineWhite mode does not work with ICS mode", + DisplayFatalError(_("MachineWhite mode does not work with ICS mode"), 0, 2); return; } MachineWhiteEvent(); } else if (initialMode == MachinePlaysBlack) { if (appData.noChessProgram) { - DisplayFatalError("MachineBlack mode requires a chess engine", + DisplayFatalError(_("MachineBlack mode requires a chess engine"), 0, 2); return; } if (appData.icsActive) { - DisplayFatalError("MachineBlack mode does not work with ICS mode", + DisplayFatalError(_("MachineBlack mode does not work with ICS mode"), 0, 2); return; } MachineBlackEvent(); } else if (initialMode == TwoMachinesPlay) { if (appData.noChessProgram) { - DisplayFatalError("TwoMachines mode requires a chess engine", + DisplayFatalError(_("TwoMachines mode requires a chess engine"), 0, 2); return; } if (appData.icsActive) { - DisplayFatalError("TwoMachines mode does not work with ICS mode", + DisplayFatalError(_("TwoMachines mode does not work with ICS mode"), 0, 2); return; } @@ -1244,7 +1293,7 @@ InitBackEnd3 P((void)) EditPositionEvent(); } else if (initialMode == Training) { if (*appData.loadGameFile == NULLCHAR) { - DisplayFatalError("Training mode requires a game file", 0, 2); + DisplayFatalError(_("Training mode requires a game file"), 0, 2); return; } TrainingEvent(); @@ -1270,18 +1319,18 @@ establish() } else if (*appData.gateway != NULLCHAR) { if (*appData.remoteShell == NULLCHAR) { /* Use the rcmd protocol to run telnet program on a gateway host */ - sprintf(buf, "%s %s %s", + snprintf(buf, sizeof(buf), "%s %s %s", appData.telnetProgram, appData.icsHost, appData.icsPort); return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR); } else { /* Use the rsh program to run telnet program on a gateway host */ if (*appData.remoteUser == NULLCHAR) { - sprintf(buf, "%s %s %s %s %s", appData.remoteShell, + snprintf(buf, sizeof(buf), "%s %s %s %s %s", appData.remoteShell, appData.gateway, appData.telnetProgram, appData.icsHost, appData.icsPort); } else { - sprintf(buf, "%s %s -l %s %s %s %s", + snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s", appData.remoteShell, appData.gateway, appData.remoteUser, appData.telnetProgram, appData.icsHost, appData.icsPort); @@ -1392,14 +1441,14 @@ read_from_player(isr, closure, message, count, error) gotEof = 0; outCount = OutputMaybeTelnet(icsPR, message, count, &outError); if (outCount < count) { - DisplayFatalError("Error writing to ICS", outError, 1); + DisplayFatalError(_("Error writing to ICS"), outError, 1); } } else if (count < 0) { RemoveInputSource(isr); - DisplayFatalError("Error reading from keyboard", error, 1); + DisplayFatalError(_("Error reading from keyboard"), error, 1); } else if (gotEof++ > 0) { RemoveInputSource(isr); - DisplayFatalError("Got end of file from keyboard", 0, 0); + DisplayFatalError(_("Got end of file from keyboard"), 0, 0); } } @@ -1414,7 +1463,7 @@ SendToICS(s) count = strlen(s); outCount = OutputMaybeTelnet(icsPR, s, count, &outError); if (outCount < count) { - DisplayFatalError("Error writing to ICS", outError, 1); + DisplayFatalError(_("Error writing to ICS"), outError, 1); } } @@ -1439,7 +1488,7 @@ SendToICSDelayed(s,msdelay) outCount = OutputToProcessDelayed(icsPR, s, count, &outError, msdelay); if (outCount < count) { - DisplayFatalError("Error writing to ICS", outError, 1); + DisplayFatalError(_("Error writing to ICS"), outError, 1); } } @@ -1531,7 +1580,8 @@ StringToVariant(e) if (!found) { if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random")) - || StrCaseStr(e, "wild/fr")) { + || StrCaseStr(e, "wild/fr") + || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) { v = VariantFischeRandom; } else if ((i = 4, p = StrCaseStr(e, "wild")) || (i = 1, p = StrCaseStr(e, "w"))) { @@ -1673,13 +1723,16 @@ StringToVariant(e) case 49: v = VariantSuper; break; + case 50: + v = VariantGreat; + break; case -1: /* Found "wild" or "w" in the string but no number; must assume it's normal chess. */ v = VariantNormal; break; default: - sprintf(buf, "Unknown wild type %d", wnum); + sprintf(buf, _("Unknown wild type %d"), wnum); DisplayError(buf, 0); v = VariantUnknown; break; @@ -1687,7 +1740,7 @@ StringToVariant(e) } } if (appData.debugMode) { - fprintf(debugFP, "recognized '%s' (%d) as variant %s\n", + fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"), e, wnum, VariantName(v)); } return v; @@ -1752,7 +1805,7 @@ SendToPlayer(data, length) int error, outCount; outCount = OutputToProcess(NoProc, data, length, &error); if (outCount < length) { - DisplayFatalError("Error writing to display", error, 1); + DisplayFatalError(_("Error writing to display"), error, 1); } } @@ -1838,7 +1891,7 @@ TelnetRequest(ddww, option) msg[2] = option; outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError); if (outCount < 3) { - DisplayFatalError("Error writing to ICS", outError, 1); + DisplayFatalError(_("Error writing to ICS"), outError, 1); } } @@ -1889,7 +1942,7 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece) j = PieceToNumber(piece); if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */ if(j < 0) continue; /* should not happen */ - piece = (ChessSquare) ( j + (int)lowestPiece ); + piece = (ChessSquare) ( (int)piece + (int)lowestPiece ); board[holdingsStartRow+j*direction][holdingsColumn] = piece; board[holdingsStartRow+j*direction][countsColumn]++; } @@ -1902,7 +1955,7 @@ VariantSwitch(Board board, VariantClass newVariant) { int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j; int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove; - Board tempBoard; int saveCastling[BOARD_SIZE], saveEP; +// Board tempBoard; int saveCastling[BOARD_SIZE], saveEP; startedFromPositionFile = FALSE; if(gameInfo.variant == newVariant) return; @@ -1996,6 +2049,7 @@ static int player2Rating = -1; /*----------------------------*/ ColorClass curColor = ColorNormal; +int suppressKibitz = 0; void read_from_ics(isr, closure, data, count, error) @@ -2027,9 +2081,9 @@ read_from_ics(isr, closure, data, count, error) int buf_len; int next_out; int tkind; + int backup; /* [DM] For zippy color lines */ char *p; -#ifdef WIN32 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'); + } + if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info + int depth=0; float score; + if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) { + // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph + pvInfoList[forwardMostMove-1].depth = depth; + pvInfoList[forwardMostMove-1].score = 100*score; + } + OutputKibitz(suppressKibitz, parse); + } else { + char tmp[MSG_SIZ]; + sprintf(tmp, _("your opponent kibitzes: %s"), parse); + SendToPlayer(tmp, strlen(tmp)); + } + } started = STARTED_NONE; } else { /* Don't match patterns against characters in chatter */ @@ -2265,7 +2345,7 @@ read_from_ics(isr, closure, data, count, error) if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) { char buf[MSG_SIZ]; - sprintf(buf, "%s@%s", ics_handle, appData.icsHost); + snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost); DisplayIcsInteractionTitle(buf); have_set_title = TRUE; } @@ -2289,17 +2369,53 @@ read_from_ics(isr, closure, data, count, error) } oldi = i; + // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window + if (appData.autoKibitz && started == STARTED_NONE && + !appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze + (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) { + if(looking_at(buf, &i, "* kibitzes: ") && + (StrStr(star_match[0], gameInfo.white) == star_match[0] || + StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent + suppressKibitz = TRUE; + if((StrStr(star_match[0], gameInfo.white) == star_match[0] + && (gameMode == IcsPlayingWhite)) || + (StrStr(star_match[0], gameInfo.black) == star_match[0] + && (gameMode == IcsPlayingBlack)) ) // opponent kibitz + started = STARTED_CHATTER; // own kibitz we simply discard + else { + started = STARTED_COMMENT; // make sure it will be collected in parse[] + parse_pos = 0; parse[0] = NULLCHAR; + savingComment = TRUE; + suppressKibitz = gameMode != IcsObserving ? 2 : + (StrStr(star_match[0], gameInfo.white) == NULL) + 1; + } + continue; + } else + if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz + started = STARTED_CHATTER; + suppressKibitz = TRUE; + } + } // [HGM] kibitz: end of patch + if (appData.zippyTalk || appData.zippyPlay) { + /* [DM] Backup address for color zippy lines */ + backup = i; #if ZIPPY - if (ZippyControl(buf, &i) || - ZippyConverse(buf, &i) || - (appData.zippyPlay && ZippyMatch(buf, &i))) { - loggedOn = TRUE; - continue; + #ifdef WIN32 + if (loggedOn == TRUE) + if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) || + (appData.zippyPlay && ZippyMatch(buf, &backup))); + #else + if (ZippyControl(buf, &i) || + ZippyConverse(buf, &i) || + (appData.zippyPlay && ZippyMatch(buf, &i))) { + loggedOn = TRUE; + if (!appData.colorize) continue; } + #endif #endif - } else { - if (/* Don't color "message" or "messages" output */ + } // [DM] 'else { ' deleted + if (/* Don't color "message" or "messages" output */ (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) || looking_at(buf, &i, "*. * at *:*: ") || looking_at(buf, &i, "--* (*:*): ") || @@ -2454,7 +2570,6 @@ read_from_ics(isr, closure, data, count, error) curColor = ColorSeek; } continue; - } } if (looking_at(buf, &i, "\\ ")) { @@ -2565,7 +2680,7 @@ read_from_ics(isr, closure, data, count, error) case H_GOT_UNWANTED_HEADER: case H_GETTING_MOVES: /* Should not happen */ - DisplayError("Error gathering move list: two headers", 0); + DisplayError(_("Error gathering move list: two headers"), 0); ics_getting_history = H_FALSE; break; } @@ -2579,7 +2694,7 @@ read_from_ics(isr, closure, data, count, error) gameInfo.whiteRating = string_to_rating(star_match[1]); gameInfo.blackRating = string_to_rating(star_match[3]); if (appData.debugMode) - fprintf(debugFP, "Ratings from header: W %d, B %d\n", + fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), gameInfo.whiteRating, gameInfo.blackRating); } continue; @@ -2612,7 +2727,7 @@ read_from_ics(isr, closure, data, count, error) break; case H_GETTING_MOVES: /* Should not happen */ - DisplayError("Error gathering move list: nested", 0); + DisplayError(_("Error gathering move list: nested"), 0); ics_getting_history = H_FALSE; break; case H_GOT_REQ_HEADER: @@ -2637,7 +2752,7 @@ read_from_ics(isr, closure, data, count, error) if (looking_at(buf, &i, "% ") || ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE) - && looking_at(buf, &i, "}*"))) { + && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book savingComment = FALSE; switch (started) { case STARTED_MOVES: @@ -2656,11 +2771,18 @@ read_from_ics(isr, closure, data, count, error) } SendTimeRemaining(&first, TRUE); } +#if 0 if (first.useColors) { SendToProgram("white\ngo\n", &first); } else { SendToProgram("go\n", &first); } +#else + if (first.useColors) { + SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent + } + bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos +#endif first.maybeThinking = TRUE; } else { if (first.usePlayother) { @@ -2681,11 +2803,18 @@ read_from_ics(isr, closure, data, count, error) } SendTimeRemaining(&first, FALSE); } +#if 0 if (first.useColors) { SendToProgram("black\ngo\n", &first); } else { SendToProgram("go\n", &first); } +#else + if (first.useColors) { + SendToProgram("black\n", &first); + } + bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); +#endif first.maybeThinking = TRUE; } else { if (first.usePlayother) { @@ -2738,6 +2867,17 @@ read_from_ics(isr, closure, data, count, error) default: break; } + if(bookHit) { // [HGM] book: simulate book reply + static char bookMove[MSG_SIZ]; // a bit generous? + + programStats.nodes = programStats.depth = programStats.time = + programStats.score = programStats.got_only_move = 0; + sprintf(programStats.movelist, "%s (xbook)", bookHit); + + strcpy(bookMove, "move "); + strcat(bookMove, bookHit); + HandleMachineMove(bookMove, &first); + } continue; } @@ -2824,7 +2964,7 @@ read_from_ics(isr, closure, data, count, error) if (forwardMostMove > backwardMostMove) { currentMove = --forwardMostMove; DisplayMove(currentMove - 1); /* before DMError */ - DisplayMoveError("Illegal move (rejected by ICS)"); + DisplayMoveError(_("Illegal move (rejected by ICS)")); DrawPosition(FALSE, boards[currentMove]); SwitchClocks(); DisplayBothClocks(); @@ -2979,6 +3119,11 @@ read_from_ics(isr, closure, data, count, error) if (gameMode == IcsObserving && atoi(star_match[0]) == ics_gamenum) { + /* icsEngineAnalyze */ + if (appData.icsEngineAnalyze) { + ExitAnalyzeMode(); + ModeHighlight(); + } StopClocks(); gameMode = IcsIdle; ics_gamenum = -1; @@ -3120,11 +3265,12 @@ read_from_ics(isr, closure, data, count, error) i++; /* skip unparsed character and loop back */ } - if (started != STARTED_MOVES && started != STARTED_BOARD && + 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); 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, @@ -3132,9 +3278,9 @@ read_from_ics(isr, closure, data, count, error) } else if (count == 0) { RemoveInputSource(isr); - DisplayFatalError("Connection closed by ICS", 0, 0); + DisplayFatalError(_("Connection closed by ICS"), 0, 0); } else { - DisplayFatalError("Error reading from ICS", error, 1); + DisplayFatalError(_("Error reading from ICS"), error, 1); } } @@ -3164,8 +3310,8 @@ ParseBoard12(string) char *string; { GameMode newGameMode; - int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0; - int j, k, n, moveNum, white_stren, black_stren, white_time, black_time; + int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i; + int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback; int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count; char to_play, board_chars[200]; char move_str[500], str[500], elapsed_time[500]; @@ -3184,7 +3330,7 @@ ParseBoard12(string) newGame = FALSE; if (appData.debugMode) - fprintf(debugFP, "Parsing board: %s\n", string); + fprintf(debugFP, _("Parsing board: %s\n"), string); move_str[0] = NULLCHAR; elapsed_time[0] = NULLCHAR; @@ -3207,7 +3353,7 @@ ParseBoard12(string) &ticking); if (n < 21) { - sprintf(str, "Failed to parse board string:\n\"%s\"", string); + snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string); DisplayError(str, 0); return; } @@ -3216,7 +3362,7 @@ ParseBoard12(string) moveNum = (moveNum - 1) * 2; if (to_play == 'B') moveNum++; if (moveNum >= MAX_MOVES) { - DisplayFatalError("Game too long; increase MAX_MOVES and recompile", + DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"), 0, 1); return; } @@ -3278,7 +3424,7 @@ ParseBoard12(string) return; case H_GETTING_MOVES: /* Should not happen */ - DisplayError("Error gathering move list: extra board", 0); + DisplayError(_("Error gathering move list: extra board"), 0); ics_getting_history = H_FALSE; return; } @@ -3479,6 +3625,16 @@ ParseBoard12(string) /* Update currentMove and known move number limits */ newMove = newGame || moveNum > 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) { @@ -3548,8 +3704,37 @@ ParseBoard12(string) strcat(parseList[moveNum - 1], " "); strcat(parseList[moveNum - 1], elapsed_time); moveList[moveNum - 1][0] = NULLCHAR; - } else if (ParseOneMove(move_str, moveNum - 1, &moveType, - &fromX, &fromY, &toX, &toY, &promoChar)) { + } else if (strcmp(move_str, "none") == 0) { + // [HGM] long SAN: swapped order; test for 'none' before parsing move + /* Again, we don't know what the board looked like; + this is really the start of the game. */ + parseList[moveNum - 1][0] = NULLCHAR; + moveList[moveNum - 1][0] = NULLCHAR; + backwardMostMove = moveNum; + startedFromSetupPosition = TRUE; + fromX = fromY = toX = toY = -1; + } else { + // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. + // So we parse the long-algebraic move string in stead of the SAN move + int valid; char buf[MSG_SIZ], *prom; + + // str looks something like "Q/a1-a2"; kill the slash + if(str[1] == '/') + sprintf(buf, "%c%s", str[0], str+2); + else strcpy(buf, str); // might be castling + if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) + strcat(buf, prom); // long move lacks promo specification! + if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!) + if(appData.debugMode) + fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf); + strcpy(move_str, buf); + } + valid = ParseOneMove(move_str, moveNum - 1, &moveType, + &fromX, &fromY, &toX, &toY, &promoChar) + || ParseOneMove(buf, moveNum - 1, &moveType, + &fromX, &fromY, &toX, &toY, &promoChar); + // end of long SAN patch + if (valid) { (void) CoordsToAlgebraic(boards[moveNum - 1], PosFlags(moveNum - 1), EP_UNKNOWN, fromY, fromX, toY, toX, promoChar, @@ -3565,6 +3750,7 @@ ParseBoard12(string) strcat(parseList[moveNum - 1], "+"); break; case MT_CHECKMATE: + case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate strcat(parseList[moveNum - 1], "#"); break; } @@ -3573,15 +3759,7 @@ ParseBoard12(string) /* currentMoveString is set as a side-effect of ParseOneMove */ strcpy(moveList[moveNum - 1], currentMoveString); strcat(moveList[moveNum - 1], "\n"); - } else if (strcmp(move_str, "none") == 0) { - /* Again, we don't know what the board looked like; - this is really the start of the game. */ - parseList[moveNum - 1][0] = NULLCHAR; - moveList[moveNum - 1][0] = NULLCHAR; - backwardMostMove = moveNum; - startedFromSetupPosition = TRUE; - fromX = fromY = toX = toY = -1; - } else { + } else { /* Move from ICS was illegal!? Punt. */ if (appData.debugMode) { fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str); @@ -3598,6 +3776,7 @@ ParseBoard12(string) strcat(parseList[moveNum - 1], elapsed_time); moveList[moveNum - 1][0] = NULLCHAR; fromX = fromY = toX = toY = -1; + } } if (appData.debugMode) { fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]); @@ -3612,7 +3791,7 @@ ParseBoard12(string) if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) || (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) { if (moveList[moveNum - 1][0] == NULLCHAR) { - sprintf(str, "Couldn't parse move \"%s\" from ICS", + sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str); DisplayError(str, 0); } else { @@ -3634,7 +3813,7 @@ ParseBoard12(string) } } else if (gameMode == IcsObserving || gameMode == IcsExamining) { if (moveList[moveNum - 1][0] == NULLCHAR) { - sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str); + sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str); DisplayError(str, 0); } else { if(gameInfo.variant == currentlyInitializedVariant) // [HGM] refrain sending moves engine can't understand! @@ -3722,9 +3901,9 @@ ParseBoard12(string) if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); strcpy(bookMove, "move "); strcat(bookMove, bookHit); @@ -3836,7 +4015,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY) switch (moveType) { default: - sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)", + sprintf(user_move, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"), (int)moveType, fromX, fromY, toX, toY); DisplayError(user_move + strlen("say "), 0); break; @@ -3874,10 +4053,14 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY) case BlackPromotionChancellor: case WhitePromotionArchbishop: case BlackPromotionArchbishop: - if(gameInfo.variant == VariantShatranj) + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) sprintf(user_move, "%c%c%c%c=%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, PieceToChar(WhiteFerz)); + else if(gameInfo.variant == VariantGreat) + sprintf(user_move, "%c%c%c%c=%c\n", + AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, + PieceToChar(WhiteMan)); else sprintf(user_move, "%c%c%c%c=%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, @@ -3938,7 +4121,7 @@ ProcessICSInitScript(f) void AlphaRank(char *move, int n) { - char *p = move, c; int x, y; +// char *p = move, c; int x, y; if (appData.debugMode) { fprintf(debugFP, "alphaRank(%s,%d)\n", move, n); @@ -4082,106 +4265,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar) } } -/* [AS] FRC game initialization */ -static int FindEmptySquare( Board board, int n ) -{ - int i = 0; - - while( 1 ) { - while( board[0][i] != EmptySquare ) i++; - if( n == 0 ) - break; - n--; - i++; - } - - return i; -} - -#if 0 -static void ShuffleFRC( Board board ) -{ - int i; - - srand( time(0) ); - - for( i=0; i<8; i++ ) { - board[0][i] = EmptySquare; - } - - board[0][(rand() % 4)*2 ] = WhiteBishop; /* On dark square */ - board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */ - board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen; - board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight; - board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight; - board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook; - initialRights[1] = initialRights[4] = - castlingRights[0][1] = castlingRights[0][4] = i; - board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing; - initialRights[2] = initialRights[5] = - castlingRights[0][2] = castlingRights[0][5] = i; - board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook; - initialRights[0] = initialRights[3] = - castlingRights[0][0] = castlingRights[0][3] = i; - - for( i=BOARD_LEFT; i> 8 ^ random() << 8; if(seed<0) seed = -seed; @@ -4211,9 +4294,9 @@ int put(Board board, int pieceType, int rank, int n, int shade) int i; for(i=BOARD_LEFT; i (int) WhitePawn; p--) { if(p != (int) WhiteBishop && p != (int) WhiteFerz && p != (int) WhiteAlfil) continue; @@ -4337,8 +4420,6 @@ void SetUpShuffle(Board board, int number) if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize } -#endif - int 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 */ @@ -4456,11 +4537,6 @@ InitPosition(redraw) break; case VariantTwoKings: pieces = twoKingsArray; - nrCastlingRights = 8; /* add rights for second King */ - castlingRights[0][6] = initialRights[2] = 5; - castlingRights[0][7] = initialRights[5] = 5; - castlingRank[6] = 0; - castlingRank[7] = BOARD_HEIGHT-1; break; case VariantCapaRandom: shuffleOpenings = TRUE; @@ -4481,10 +4557,10 @@ InitPosition(redraw) 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>>1; + castlingRights[0][2] = initialRights[2] =(BOARD_WIDTH-1)>>1; castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1; castlingRights[0][4] = initialRights[4] = BOARD_LEFT; - castlingRights[0][5] = initialRights[5] = BOARD_WIDTH-1>>1; + castlingRights[0][5] = initialRights[5] =(BOARD_WIDTH-1)>>1; break; case VariantFalcon: pieces = FalconArray; @@ -4521,6 +4597,12 @@ InitPosition(redraw) pieces = fairyArray; SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); break; + case VariantGreat: + pieces = GreatArray; + gameInfo.boardWidth = 10; + SetCharTable(pieceToChar, "PN....E...S..HWGMKpn....e...s..hwgmk"); + gameInfo.holdingsSize = 8; + break; case VariantSuper: pieces = FIDEArray; SetCharTable(pieceToChar, "PNBRQ..SE.......V.AKpnbrq..se.......v.ak"); @@ -4562,7 +4644,7 @@ InitPosition(redraw) } if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2; if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE) - DisplayFatalError("Recompile to support this BOARD_SIZE!", 0, 2); + 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; @@ -4619,6 +4701,12 @@ InitPosition(redraw) } if(gameInfo.variant == VariantSuper) Prelude(initialPosition); + if(gameInfo.variant == VariantGreat) { // promotion commoners + initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan; + initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9; + initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan; + initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9; + } #if 0 if(gameInfo.variant == VariantFischeRandom) { if( appData.defaultFrcPosition < 0 ) { @@ -4674,7 +4762,7 @@ SendBoard(cps, moveNum) char message[MSG_SIZ]; if (cps->useSetboard) { - char* fen = PositionToFEN(moveNum, cps->useFEN960); + char* fen = PositionToFEN(moveNum, cps->fenOverride); sprintf(message, "setboard %s\n", fen); SendToProgram(message, cps); free(fen); @@ -4822,7 +4910,7 @@ OKToStartUserMove(x, y) case IcsPlayingBlack: if (appData.zippyPlay) return FALSE; if (white_piece) { - DisplayMoveError("You are playing Black"); + DisplayMoveError(_("You are playing Black")); return FALSE; } break; @@ -4831,18 +4919,18 @@ OKToStartUserMove(x, y) case IcsPlayingWhite: if (appData.zippyPlay) return FALSE; if (!white_piece) { - DisplayMoveError("You are playing White"); + DisplayMoveError(_("You are playing White")); return FALSE; } break; case EditGame: if (!white_piece && WhiteOnMove(currentMove)) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); return FALSE; } if (white_piece && !WhiteOnMove(currentMove)) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); return FALSE; } if (cmailMsgLoaded && (currentMove < cmailOldMove)) { @@ -4862,7 +4950,7 @@ OKToStartUserMove(x, y) if (appData.icsActive) return FALSE; if (!appData.noChessProgram) { if (!white_piece) { - DisplayMoveError("You are playing White"); + DisplayMoveError(_("You are playing White")); return FALSE; } } @@ -4870,11 +4958,11 @@ OKToStartUserMove(x, y) case Training: if (!white_piece && WhiteOnMove(currentMove)) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); return FALSE; } if (white_piece && !WhiteOnMove(currentMove)) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); return FALSE; } break; @@ -4885,7 +4973,7 @@ OKToStartUserMove(x, y) } if (currentMove != forwardMostMove && gameMode != AnalyzeMode && gameMode != AnalyzeFile && gameMode != Training) { - DisplayMoveError("Displayed position is not current"); + DisplayMoveError(_("Displayed position is not current")); return FALSE; } return TRUE; @@ -4955,7 +5043,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) case MachinePlaysWhite: /* User is moving for Black */ if (WhiteOnMove(currentMove)) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); return ImpossibleMove; } break; @@ -4963,7 +5051,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) case MachinePlaysBlack: /* User is moving for White */ if (!WhiteOnMove(currentMove)) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); return ImpossibleMove; } break; @@ -4977,13 +5065,13 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) { /* User is moving for Black */ if (WhiteOnMove(currentMove)) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); return ImpossibleMove; } } else { /* User is moving for White */ if (!WhiteOnMove(currentMove)) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); return ImpossibleMove; } } @@ -4993,7 +5081,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) /* User is moving for Black */ if (WhiteOnMove(currentMove)) { if (!appData.premove) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); } else if (toX >= 0 && toY >= 0) { premoveToX = toX; premoveToY = toY; @@ -5014,7 +5102,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) /* User is moving for White */ if (!WhiteOnMove(currentMove)) { if (!appData.premove) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); } else if (toX >= 0 && toY >= 0) { premoveToX = toX; premoveToY = toY; @@ -5073,11 +5161,11 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) /* [HGM] but possibly ignore an IllegalMove result */ if (appData.testLegality) { if (moveType == IllegalMove || moveType == ImpossibleMove) { - DisplayMoveError("Illegal move"); + DisplayMoveError(_("Illegal move")); 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 @@ -5096,8 +5184,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) /*char*/int promoChar; { char *bookHit = 0; - - if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR) { +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))); if(WhiteOnMove(currentMove)) { @@ -5111,7 +5199,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) 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) { moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop; @@ -5129,9 +5217,9 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) * If they don't match, display an error message. */ int saveAnimate; - Board testBoard; + Board testBoard; char testRights[BOARD_SIZE]; char testStatus; CopyBoard(testBoard, boards[currentMove]); - ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard); + ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus); if (CompareBoards(testBoard, boards[currentMove+1])) { ForwardInner(currentMove+1); @@ -5150,10 +5238,10 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) gameMode = PlayFromGameFile; ModeHighlight(); SetTrainingModeOff(); - DisplayInformation("End of game"); + DisplayInformation(_("End of game")); } } else { - DisplayError("Incorrect move", 0); + DisplayError(_("Incorrect move"), 0); } return 1; } @@ -5175,18 +5263,6 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/ - if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR && gameInfo.holdingsSize) { - // [HGM] superchess: take promotion piece out of holdings - int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); - if(WhiteOnMove(forwardMostMove-1)) { - if(!--boards[forwardMostMove][k][BOARD_WIDTH-2]) - boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare; - } else { - if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1]) - boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare; - } - } - if (gameMode == BeginningOfGame) { if (appData.noChessProgram) { gameMode = EditGame; @@ -5206,7 +5282,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) } 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 || @@ -5240,6 +5316,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) case MT_CHECK: break; case MT_CHECKMATE: + case MT_STAINMATE: if (WhiteOnMove(currentMove)) { GameEnds(BlackWins, "Black mates", GE_PLAYER); } else { @@ -5265,9 +5342,9 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); strcpy(bookMove, "move "); strcat(bookMove, bookHit); @@ -5291,14 +5368,14 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) to do anything in between, can call this routine the old way. */ ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar); - +if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar); if(moveType != ImpossibleMove) FinishMove(moveType, fromX, fromY, toX, toY, promoChar); } void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats ) { - char * hint = lastHint; +// char * hint = lastHint; FrontEndProgramStats stats; stats.which = cps == &first ? 0 : 1; @@ -5327,7 +5404,7 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial) //first determine if the incoming move brings opponent into his book if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI)) bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move - if(appData.debugMode && bookHit) fprintf(debugFP, "book hit = %s\n", bookHit); + if(appData.debugMode) fprintf(debugFP, "book hit = %s\n", bookHit ? bookHit : "(NULL)"); if(bookHit != NULL && !cps->bookSuspend) { // make sure opponent is not going to reply after receiving move to book position SendToProgram("force\n", cps); @@ -5489,11 +5566,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h 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", + 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); + sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType); if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); @@ -5524,6 +5601,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0); GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); + return; } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom) /* [HGM] Kludge to handle engines that send FRC-style castling when they shouldn't (like TSCP-Gothic) */ @@ -5538,6 +5616,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h toX--; currentMoveString[2]--; break; + default: ; // nothing to do, but suppresses warning of pedantic compilers } } hintRequested = FALSE; @@ -5553,15 +5632,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h first.initDone) { SendMoveToICS(moveType, fromX, fromY, toX, toY); ics_user_moved = 1; - if(appData.autoKibitz) { /* [HGM] kibitz: send most-recent PV info to ICS */ + if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */ char buf[3*MSG_SIZ]; - sprintf(buf, "kibitz %d/%+.2f (%.2f sec, %.0f nodes, %1.0f knps) PV = %s\n", - programStats.depth, + sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n", programStats.score / 100., + programStats.depth, programStats.time / 100., - (double) programStats.nodes, - programStats.nodes / (10*abs(programStats.time) + 1.), + u64ToDouble(programStats.nodes), + u64ToDouble(programStats.nodes) / (10*abs(programStats.time) + 1.), programStats.movelist); SendToICS(buf); } @@ -5614,58 +5693,38 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if( gameMode == TwoMachinesPlay ) { // [HGM] some adjudications useful with buggy engines int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1; - if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper) { + if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { - if(appData.testLegality) - // don't wait for engine to announce game end if we can judge ourselves - switch (MateTest(boards[forwardMostMove], - PosFlags(forwardMostMove), epFile, - castlingRights[forwardMostMove]) ) { - case MT_NONE: - case MT_CHECK: - default: - break; - case MT_STALEMATE: - epStatus[forwardMostMove] = EP_STALEMATE; - if(appData.checkMates) { - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", - GE_XBOARD ); - } - break; - case MT_CHECKMATE: - epStatus[forwardMostMove] = EP_CHECKMATE; - if(appData.checkMates) { - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, - "Xboard adjudication: Checkmate", - GE_XBOARD ); - } - break; - } if( appData.testLegality ) { /* [HGM] Some more adjudications for obstinate engines */ int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0, - NrWQ=0, NrBQ=0, NrW=0, bishopsColor = 0, - NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k; + NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0, + NrPieces=0, NrPawns=0, PawnAdvance=0, i, j; static int moveCount = 6; + ChessMove result; + char *reason = NULL; - /* First absolutely insufficient mating material. Count what is on board. */ + /* Count what is on board. */ for(i=0; iother); // make sure opponent gets move + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, + "Xboard adjudication: King destroyed", GE_XBOARD ); + return; + } + } + + /* Bare King in Shatranj (loses) or Losers (wins) */ + if( NrW == 1 || NrPieces - NrW == 1) { + if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first) + epStatus[forwardMostMove] = EP_WINS; // mark as win, so it becomes claimable + if(appData.checkMates) { + SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication: Bare king", GE_XBOARD ); + return; + } + } else + if( gameInfo.variant == VariantShatranj && --bare < 0) + { /* bare King */ + epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm + if(appData.checkMates) { + /* but only adjudicate if adjudication enabled */ + SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, + "Xboard adjudication: Bare king", GE_XBOARD ); + return; + } + } + } else bare = 1; + + + // don't wait for engine to announce game end if we can judge ourselves + switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile, + castlingRights[forwardMostMove]) ) { + case MT_CHECK: + if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time + int i, checkCnt = 0; // (should really be done by making nr of checks part of game state) + for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) { + if(MateTest(boards[i], PosFlags(i), epStatus[i], castlingRights[i]) == MT_CHECK) + checkCnt++; + if(checkCnt >= 2) { + reason = "Xboard adjudication: 3rd check"; + epStatus[forwardMostMove] = EP_CHECKMATE; + break; + } + } + } + case MT_NONE: + default: + break; + case MT_STALEMATE: + case MT_STAINMATE: + reason = "Xboard adjudication: Stalemate"; + if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt + epStatus[forwardMostMove] = EP_STALEMATE; // default result for stalemate is draw + if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers: + epStatus[forwardMostMove] = EP_WINS; // in these variants stalemated is always a win + else if(gameInfo.variant == VariantSuicide) // in suicide it depends + epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE : + ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ? + EP_CHECKMATE : EP_WINS); + else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi) + epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses + } + break; + case MT_CHECKMATE: + reason = "Xboard adjudication: Checkmate"; + epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE); + break; + } + + switch(i = epStatus[forwardMostMove]) { + case EP_STALEMATE: + result = GameIsDrawn; break; + case EP_CHECKMATE: + result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break; + case EP_WINS: + result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break; + default: + result = (ChessMove) 0; + } + if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( result, reason, GE_XBOARD ); + return; + } + + /* Next absolutely insufficient mating material. */ + if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && + gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible + (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || + NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color { /* KBK, KNK, KK of KBKB with like Bishops */ /* always flag draws, for judging claims */ @@ -5696,25 +5860,14 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(appData.materialDraws) { /* but only adjudicate them if adjudication enabled */ + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD ); return; } } - /* Shatranj baring rule */ - if( gameInfo.variant == VariantShatranj && (NrW == 1 || NrPieces - NrW == 1) ) - { /* bare King */ - - if(--bare < 0 && appData.checkMates) { - /* but only adjudicate them if adjudication enabled */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, - "Xboard adjudication: Bare king", GE_XBOARD ); - return; - } - } else bare = 1; - /* Then some trivial draws (only adjudicate, cannot be claimed) */ if(NrPieces == 4 && ( NrWR == 1 && NrBR == 1 /* KRKR */ @@ -5724,6 +5877,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h ) ) { if(--moveCount < 0 && appData.trivialDraws) { /* if the first 3 moves do not show a tactical win, declare draw */ + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD ); return; @@ -5790,7 +5945,43 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if( rights == 0 && ++count > appData.drawRepeats-2 && appData.drawRepeats > 1) { /* adjudicate after user-specified nr of repeats */ + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + if(gameInfo.variant == VariantXiangqi && appData.testLegality) { + // [HGM] xiangqi: check for forbidden perpetuals + int m, ourPerpetual = 1, hisPerpetual = 1; + for(m=forwardMostMove; m>k; m-=2) { + if(MateTest(boards[m], PosFlags(m), + EP_NONE, castlingRights[m]) != MT_CHECK) + ourPerpetual = 0; // the current mover did not always check + if(MateTest(boards[m-1], PosFlags(m-1), + EP_NONE, castlingRights[m-1]) != MT_CHECK) + hisPerpetual = 0; // the opponent did not always check + } + if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n", + ourPerpetual, hisPerpetual); + if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication: perpetual checking", GE_XBOARD ); + return; + } + if(hisPerpetual && !ourPerpetual) // he is checking us, but did not repeat yet + break; // (or we would have caught him before). Abort repetition-checking loop. + // Now check for perpetual chases + if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase + hisPerpetual = PerpetualChase(k, forwardMostMove); + ourPerpetual = PerpetualChase(k+1, forwardMostMove); + if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit + GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, + "Xboard adjudication: perpetual chasing", GE_XBOARD ); + return; + } + if(hisPerpetual && !ourPerpetual) // he is chasing us, but did not repeat yet + break; // Abort repetition-checking loop. + } + // if neither of us is checking or chasing all the time, or both are, it is draw + } GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD ); return; } @@ -5812,6 +6003,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h epStatus[forwardMostMove] = EP_RULE_DRAW; /* this is used to judge if draw claims are legal */ if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) { + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); return; @@ -5831,6 +6024,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(epStatus[forwardMostMove] == EP_INSUF_DRAW) p = "Draw claim: insufficient mating material"; if( p != NULL ) { + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ GameEnds( GameIsDrawn, p, GE_XBOARD ); ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ return; @@ -5838,14 +6033,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } - } - - if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) { - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) { + SendToProgram("force\n", cps->other); // suppress reply + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD ); + GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD ); - return; + return; + } } bookHit = NULL; @@ -5893,9 +6089,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h strcat(bookMove, bookHit); message = bookMove; cps = cps->other; - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); if(cps->lastPing != cps->lastPong) { savedMessage = message; // args for deferred call @@ -5928,7 +6124,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD); if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) { - DisplayError("Bad FEN received from engine", 0); + DisplayError(_("Bad FEN received from engine"), 0); return ; } else { Reset(FALSE, FALSE); @@ -5958,7 +6154,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (!strncmp(message, "tellopponent ", 13)) { if (appData.icsActive) { if (loggedOn) { - sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13); + snprintf(buf1, sizeof(buf1), "%ssay %s\n", ics_prefix, message + 13); SendToICS(buf1); } } else { @@ -5969,7 +6165,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (!strncmp(message, "tellothers ", 11)) { if (appData.icsActive) { if (loggedOn) { - sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11); + snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11); SendToICS(buf1); } } @@ -5978,7 +6174,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (!strncmp(message, "tellall ", 8)) { if (appData.icsActive) { if (loggedOn) { - sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8); + snprintf(buf1, sizeof(buf1), "%skibitz %s\n", ics_prefix, message + 8); SendToICS(buf1); } } else { @@ -6070,7 +6266,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h cps->analysisSupport = FALSE; cps->analyzing = FALSE; Reset(FALSE, TRUE); - sprintf(buf2, "%s does not support analysis", cps->tidy); + sprintf(buf2, _("%s does not support analysis"), cps->tidy); DisplayError(buf2, 0); return; } @@ -6116,7 +6312,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h DisplayMove(currentMove-1); /* before DisplayMoveError */ SwitchClocks(); DisplayBothClocks(); - sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)", + sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"), parseList[currentMove], cps->which); DisplayMoveError(buf1); DrawPosition(FALSE, boards[currentMove]); @@ -6148,7 +6344,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h || (StrStr(message, "Permission denied") != NULL)) { cps->maybeThinking = FALSE; - sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n", + snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"), cps->which, cps->program, cps->host, message); RemoveInputSource(cps->isr); DisplayFatalError(buf1, 0, 1); @@ -6166,12 +6362,12 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h (void) CoordsToAlgebraic(boards[forwardMostMove], PosFlags(forwardMostMove), EP_UNKNOWN, fromY, fromX, toY, toX, promoChar, buf1); - sprintf(buf2, "Hint: %s", buf1); + snprintf(buf2, sizeof(buf2), _("Hint: %s"), buf1); DisplayInformation(buf2); } else { /* Hint move could not be parsed!? */ - sprintf(buf2, - "Illegal hint move \"%s\"\nfrom %s chess program", + snprintf(buf2, sizeof(buf2), + _("Illegal hint move \"%s\"\nfrom %s chess program"), buf1, cps->which); DisplayError(buf2, 0); } @@ -6353,10 +6549,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } else if (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) { if (userOfferedDraw) { - DisplayInformation("Machine accepts your draw offer"); + DisplayInformation(_("Machine accepts your draw offer")); GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD); } else { - DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree"); + DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree")); } } } @@ -6370,7 +6566,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h ) { int plylev, mvleft, mvtot, curscore, time; char mvname[MOVE_LEN]; - unsigned long nodes; + u64 nodes; // [DM] char plyext; int ignore = FALSE; int prefixHint = FALSE; @@ -6387,6 +6583,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h break; case AnalyzeMode: case AnalyzeFile: + break; + case IcsObserving: /* [DM] icsEngineAnalyze */ + if (!appData.icsEngineAnalyze) ignore = TRUE; break; case TwoMachinesPlay: if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) { @@ -6400,7 +6599,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if (!ignore) { buf1[0] = NULLCHAR; - if (sscanf(message, "%d%c %d %d %lu %[^\n]\n", + if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n", &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) { if (plyext != ' ' && plyext != '\t') { @@ -6424,8 +6623,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */ int ticklen; - if(cps->nps == 0) ticklen = 10*time; // use engine reported time - else ticklen = (1000. * nodes) / cps->nps; // convert node count to time + 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)) whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen; else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen; @@ -6484,7 +6683,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h strcat( thinkOutput, buf1 ); } - if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) { + if (currentMove == forwardMostMove || gameMode == AnalyzeMode + || gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); DisplayAnalysis(); } @@ -6511,12 +6711,13 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h SendProgramStatsToFrontend( cps, &programStats ); - if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) { + if (currentMove == forwardMostMove || gameMode==AnalyzeMode || + gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); DisplayAnalysis(); } return; - } else if (sscanf(message,"stat01: %d %lu %d %d %d %s", + } else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s", &time, &nodes, &plylev, &mvleft, &mvtot, mvname) >= 5) { /* The stat01: line is from Crafty (9.29+) in response @@ -6571,7 +6772,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h strcat(programStats.movelist, p); } - if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) { + if (currentMove == forwardMostMove || gameMode==AnalyzeMode || + gameMode == AnalyzeFile || appData.icsEngineAnalyze) { DisplayMove(currentMove - 1); DisplayAnalysis(); } @@ -6581,7 +6783,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h else { buf1[0] = NULLCHAR; - if (sscanf(message, "%d%c %d %d %lu %[^\n]\n", + if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n", &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) { ChessProgramStats cpstats; @@ -6715,7 +6917,7 @@ ParseGameHistory(game) break; case AmbiguousMove: /* bug? */ - sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text); + sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text); if (appData.debugMode) { fprintf(debugFP, "Ambiguous move from ICS: '%s'\n", yy_text); fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth); @@ -6725,7 +6927,7 @@ ParseGameHistory(game) return; case ImpossibleMove: /* bug? */ - sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text); + sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text); if (appData.debugMode) { fprintf(debugFP, "Impossible move from ICS: '%s'\n", yy_text); fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth); @@ -6736,7 +6938,7 @@ ParseGameHistory(game) case (ChessMove) 0: /* end of file */ if (boardIndex < backwardMostMove) { /* Oops, gap. How did that happen? */ - DisplayError("Gap in move list", 0); + DisplayError(_("Gap in move list"), 0); return; } backwardMostMove = blackPlaysFirst ? 1 : 0; @@ -6791,11 +6993,13 @@ ParseGameHistory(game) EP_UNKNOWN, fromY, fromX, toY, toX, promoChar, parseList[boardIndex]); CopyBoard(boards[boardIndex + 1], boards[boardIndex]); + {int i; for(i=0; i 0) - { int i, j; + /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */ + { int i; if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A; - oldEP = epStatus[p-1]; - epStatus[p] = EP_NONE; + oldEP = *ep; + *ep = EP_NONE; if( board[toY][toX] != EmptySquare ) - epStatus[p] = EP_CAPTURE; + *ep = EP_CAPTURE; if( board[fromY][fromX] == WhitePawn ) { - epStatus[p] = EP_PAWN_MOVE; - if( toY-fromY==2) + if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers + *ep = EP_PAWN_MOVE; + if( toY-fromY==2) { if(toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn && gameInfo.variant != VariantBerolina || toX < fromX) - epStatus[p] = toX | berolina; + *ep = toX | berolina; if(toX fromX) - epStatus[p] = toX; + *ep = toX; + } } else if( board[fromY][fromX] == BlackPawn ) { - epStatus[p] = EP_PAWN_MOVE; - if( toY-fromY== -2) + if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers + *ep = EP_PAWN_MOVE; + if( toY-fromY== -2) { if(toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn && gameInfo.variant != VariantBerolina || toX < fromX) - epStatus[p] = toX | berolina; + *ep = toX | berolina; if(toX fromX) - epStatus[p] = toX; + *ep = toX; + } } for(i=0; i 0 && gameInfo.variant != VariantBughouse ) { /* [HGM] holdings: Add to holdings, if holdings exist */ - if(gameInfo.variant == VariantSuper) { + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured; } @@ -7099,6 +7308,19 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = (ChessSquare) (PROMOTED piece); } + if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + && promoChar != NULLCHAR && gameInfo.holdingsSize) { + // [HGM] superchess: take promotion piece out of holdings + int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); + if((int)piece < (int)BlackPawn) { // determine stm from piece color + if(!--board[k][BOARD_WIDTH-2]) + board[k][BOARD_WIDTH-1] = EmptySquare; + } else { + if(!--board[BOARD_HEIGHT-1-k][1]) + board[BOARD_HEIGHT-1-k][0] = EmptySquare; + } + } + } /* Updates forwardMostMove */ @@ -7107,15 +7329,15 @@ MakeMove(fromX, fromY, toX, toY, promoChar) int fromX, fromY, toX, toY; int promoChar; { - forwardMostMove++; +// forwardMostMove++; // [HGM] bare: moved downstream - if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting */ + if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */ int timeLeft; static int lastLoadFlag=0; int king, piece; - piece = boards[forwardMostMove-1][fromY][fromX]; + piece = boards[forwardMostMove][fromY][fromX]; king = piece < (int) BlackPawn ? WhiteKing : BlackKing; if(gameInfo.variant == VariantKnightmate) king += (int) WhiteUnicorn - (int) WhiteKing; - if(forwardMostMove == 1) { + if(forwardMostMove == 0) { if(blackPlaysFirst) fprintf(serverMoves, "%s;", second.tidy); fprintf(serverMoves, "%s;", first.tidy); @@ -7133,9 +7355,9 @@ MakeMove(fromX, fromY, toX, toY, promoChar) fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_LEFT, ONE+fromY, AAA+toX+1,ONE+toY); } // e.p. suffix - if( (boards[forwardMostMove-1][fromY][fromX] == WhitePawn || - boards[forwardMostMove-1][fromY][fromX] == BlackPawn ) && - boards[forwardMostMove-1][toY][toX] == EmptySquare + if( (boards[forwardMostMove][fromY][fromX] == WhitePawn || + boards[forwardMostMove][fromY][fromX] == BlackPawn ) && + boards[forwardMostMove][toY][toX] == EmptySquare && fromX != toX ) fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY); // promotion suffix @@ -7143,28 +7365,31 @@ MakeMove(fromX, fromY, toX, toY, promoChar) fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY); if(!loadFlag) { fprintf(serverMoves, "/%d/%d", - pvInfoList[forwardMostMove-1].depth, pvInfoList[forwardMostMove-1].score); - if(forwardMostMove & 1) timeLeft = whiteTimeRemaining/1000; - else timeLeft = blackTimeRemaining/1000; + pvInfoList[forwardMostMove].depth, pvInfoList[forwardMostMove].score); + if(forwardMostMove+1 & 1) timeLeft = whiteTimeRemaining/1000; + else timeLeft = blackTimeRemaining/1000; fprintf(serverMoves, "/%d", timeLeft); } fflush(serverMoves); } - if (forwardMostMove >= MAX_MOVES) { - DisplayFatalError("Game too long; increase MAX_MOVES and recompile", + if (forwardMostMove+1 >= MAX_MOVES) { + DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"), 0, 1); return; } SwitchClocks(); - timeRemaining[0][forwardMostMove] = whiteTimeRemaining; - timeRemaining[1][forwardMostMove] = blackTimeRemaining; - if (commentList[forwardMostMove] != NULL) { - free(commentList[forwardMostMove]); - commentList[forwardMostMove] = NULL; - } - CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]); - ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]); + timeRemaining[0][forwardMostMove+1] = whiteTimeRemaining; + timeRemaining[1][forwardMostMove+1] = blackTimeRemaining; + if (commentList[forwardMostMove+1] != NULL) { + free(commentList[forwardMostMove+1]); + commentList[forwardMostMove+1] = NULL; + } + CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]); + {int i; for(i=0; iprotocolVersion != 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); + sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy); DisplayFatalError(buf, 0, 1); return; } @@ -7308,6 +7535,8 @@ InitChessProgram(cps, setup) overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; if( gameInfo.variant == VariantSuper ) overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; + if( gameInfo.variant == VariantGreat ) + overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; if(overruled) { sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, @@ -7340,7 +7569,7 @@ InitChessProgram(cps, setup) } if (cps->sendICS) { - sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-"); + snprintf(buf, sizeof(buf), "ics %s\n", appData.icsActive ? appData.icsHost : "-"); SendToProgram(buf, cps); } cps->maybeThinking = FALSE; @@ -7388,17 +7617,17 @@ StartChessProgram(cps) err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr); } else { if (*appData.remoteUser == NULLCHAR) { - sprintf(buf, "%s %s %s", appData.remoteShell, cps->host, + snprintf(buf, sizeof(buf), "%s %s %s", appData.remoteShell, cps->host, cps->program); } else { - sprintf(buf, "%s %s -l %s %s", appData.remoteShell, + snprintf(buf, sizeof(buf), "%s %s -l %s %s", appData.remoteShell, cps->host, appData.remoteUser, cps->program); } err = StartChildProcess(buf, "", &cps->pr); } if (err != 0) { - sprintf(buf, "Startup failure on '%s'", cps->program); + sprintf(buf, _("Startup failure on '%s'"), cps->program); DisplayFatalError(buf, err, 1); cps->pr = NoProc; cps->isr = NULL; @@ -7408,6 +7637,8 @@ StartChessProgram(cps) cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps); if (cps->protocolVersion > 1) { sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion); + cps->nrOptions = 0; // [HGM] options: clear all engine-specific options + cps->comboCnt = 0; // and values of combo boxes SendToProgram(buf, cps); } else { SendToProgram("xboard\n", cps); @@ -7419,13 +7650,13 @@ void TwoMachinesEventIfReady P((void)) { if (first.lastPing != first.lastPong) { - DisplayMessage("", "Waiting for first chess program"); - ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000); + DisplayMessage("", _("Waiting for first chess program")); + ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000 return; } if (second.lastPing != second.lastPong) { - DisplayMessage("", "Waiting for second chess program"); - ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000); + DisplayMessage("", _("Waiting for second chess program")); + ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000 return; } ThawUI(); @@ -7541,20 +7772,35 @@ GameEnds(result, resultDetails, whosays) if(gameMode == TwoMachinesPlay && appData.testClaims) { if(appData.testLegality && whosays >= GE_ENGINE1 ) { char claimer; + ChessMove trueResult = (ChessMove) -1; claimer = whosays == GE_ENGINE1 ? /* color of claimer */ first.twoMachinesColor[0] : second.twoMachinesColor[0] ; - if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) && - (result == WhiteWins && claimer == 'w' || - result == BlackWins && claimer == 'b' ) ) { - if (appData.debugMode) { - fprintf(debugFP, "result=%d sp=%d move=%d\n", - result, epStatus[forwardMostMove], forwardMostMove); + + // [HGM] losers: because the logic is becoming a bit hairy, determine true result first + if(epStatus[forwardMostMove] == EP_CHECKMATE) { + /* [HGM] verify: engine mate claims accepted if they were flagged */ + trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; + } else + if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win + /* [HGM] verify: engine mate claims accepted if they were flagged */ + trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; + } else + if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now + trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE } - /* [HGM] verify: engine mate claims accepted if they were flagged */ - if(epStatus[forwardMostMove] != EP_CHECKMATE && - result != (WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins)) { + + // now verify win claims, but not in drop games, as we don't understand those yet + if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper + || gameInfo.variant == VariantGreat) && + (result == WhiteWins && claimer == 'w' || + result == BlackWins && claimer == 'b' ) ) { // case to verify: engine claims own win + if (appData.debugMode) { + fprintf(debugFP, "result=%d sp=%d move=%d\n", + result, epStatus[forwardMostMove], forwardMostMove); + } + if(result != trueResult) { sprintf(buf, "False win claim: '%s'", resultDetails); result = claimer == 'w' ? BlackWins : WhiteWins; resultDetails = buf; @@ -7573,7 +7819,10 @@ GameEnds(result, resultDetails, whosays) /* (Claiming a loss is accepted no questions asked!) */ } /* [HGM] bare: don't allow bare King to win */ - if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) && result != GameIsDrawn) + if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway + && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course... + && result != GameIsDrawn) { int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn); for(j=BOARD_LEFT; jnumber > 1) { GameListPopUp(f, title); @@ -8345,6 +8597,7 @@ MakeRegisteredMove() break; case MT_CHECKMATE: + case MT_STAINMATE: if (WhiteOnMove(currentMove)) { GameEnds(BlackWins, "Black mates", GE_PLAYER); } else { @@ -8390,7 +8643,7 @@ CmailLoadGame(f, gameNumber, title, useList) int retVal; if (gameNumber > nCmailGames) { - DisplayError("No more games in this message", 0); + DisplayError(_("No more games in this message"), 0); return FALSE; } if (f == lastLoadGameFP) { @@ -8431,11 +8684,11 @@ ReloadGame(offset) { int gameNumber = lastLoadGameNumber + offset; if (lastLoadGameFP == NULL) { - DisplayError("No game has been loaded yet", 0); + DisplayError(_("No game has been loaded yet"), 0); return FALSE; } if (gameNumber <= 0) { - DisplayError("Can't back up any further", 0); + DisplayError(_("Can't back up any further"), 0); return FALSE; } if (cmailMsgLoaded) { @@ -8491,7 +8744,7 @@ LoadGame(f, gameNumber, title, useList) gn = 1; } else { - DisplayError("Game number out of range", 0); + DisplayError(_("Game number out of range"), 0); return FALSE; } } else { @@ -8502,7 +8755,7 @@ LoadGame(f, gameNumber, title, useList) gameNumber == 1) { gn = 1; } else { - DisplayError("Can't seek on game file", 0); + DisplayError(_("Can't seek on game file"), 0); return FALSE; } } @@ -8515,7 +8768,7 @@ LoadGame(f, gameNumber, title, useList) yynewfile(f); if (lg && lg->gameInfo.white && lg->gameInfo.black) { - sprintf(buf, "%s vs. %s", lg->gameInfo.white, + snprintf(buf, sizeof(buf), "%s vs. %s", lg->gameInfo.white, lg->gameInfo.black); DisplayTitle(buf); } else if (*title != NULLCHAR) { @@ -8560,7 +8813,7 @@ LoadGame(f, gameNumber, title, useList) nCmailGames = CMAIL_MAX_GAMES - gn; } else { Reset(TRUE, TRUE); - DisplayError("Game not found in file", 0); + DisplayError(_("Game not found in file"), 0); } return FALSE; @@ -8687,7 +8940,7 @@ LoadGame(f, gameNumber, title, useList) startedFromSetupPosition = TRUE; if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) { Reset(TRUE, TRUE); - DisplayError("Bad FEN position in file", 0); + DisplayError(_("Bad FEN position in file"), 0); return FALSE; } CopyBoard(boards[0], initial_position); @@ -8845,7 +9098,7 @@ LoadGame(f, gameNumber, title, useList) if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) || cm == WhiteWins || cm == BlackWins || cm == GameIsDrawn || cm == GameUnfinished) { - DisplayMessage("", "No moves in game"); + DisplayMessage("", _("No moves in game")); if (cmailMsgLoaded) { if (appData.debugMode) fprintf(debugFP, "Setting flipView to %d.\n", FALSE); @@ -8916,11 +9169,11 @@ ReloadPosition(offset) { int positionNumber = lastLoadPositionNumber + offset; if (lastLoadPositionFP == NULL) { - DisplayError("No position has been loaded yet", 0); + DisplayError(_("No position has been loaded yet"), 0); return FALSE; } if (positionNumber <= 0) { - DisplayError("Can't back up any further", 0); + DisplayError(_("Can't back up any further"), 0); return FALSE; } return LoadPosition(lastLoadPositionFP, positionNumber, @@ -8942,7 +9195,7 @@ LoadPositionFromFile(filename, n, title) } else { f = fopen(filename, "rb"); if (f == NULL) { - sprintf(buf, "Can't open \"%s\"", filename); + snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename); DisplayError(buf, errno); return FALSE; } else { @@ -8983,7 +9236,7 @@ LoadPosition(f, positionNumber, title) if (positionNumber < 0) { /* Negative position number means to seek to that byte offset */ if (fseek(f, -positionNumber, 0) == -1) { - DisplayError("Can't seek on position file", 0); + DisplayError(_("Can't seek on position file"), 0); return FALSE; }; pn = 1; @@ -8994,14 +9247,14 @@ LoadPosition(f, positionNumber, title) positionNumber == 1) { pn = 1; } else { - DisplayError("Can't seek on position file", 0); + DisplayError(_("Can't seek on position file"), 0); return FALSE; } } } /* See if this file is FEN or old-style xboard */ if (fgets(line, MSG_SIZ, f) == NULL) { - DisplayError("Position not found in file", 0); + DisplayError(_("Position not found in file"), 0); return FALSE; } #if 0 @@ -9031,7 +9284,7 @@ LoadPosition(f, positionNumber, title) /* skip positions before number pn */ if (fgets(line, MSG_SIZ, f) == NULL) { Reset(TRUE, TRUE); - DisplayError("Position not found in file", 0); + DisplayError(_("Position not found in file"), 0); return FALSE; } if (fenMode || line[0] == '#') pn--; @@ -9040,7 +9293,7 @@ LoadPosition(f, positionNumber, title) if (fenMode) { if (!ParseFEN(initial_position, &blackPlaysFirst, line)) { - DisplayError("Bad FEN position in file", 0); + DisplayError(_("Bad FEN position in file"), 0); return FALSE; } } else { @@ -9072,10 +9325,10 @@ LoadPosition(f, positionNumber, title) strcpy(moveList[0], ""); strcpy(parseList[0], ""); CopyBoard(boards[1], initial_position); - DisplayMessage("", "Black to play"); + DisplayMessage("", _("Black to play")); } else { currentMove = forwardMostMove = backwardMostMove = 0; - DisplayMessage("", "White to play"); + DisplayMessage("", _("White to play")); } /* [HGM] copy FEN attributes as well */ { int i; @@ -9156,7 +9409,7 @@ SaveGameToFile(filename, append) } else { f = fopen(filename, append ? "a" : "w"); if (f == NULL) { - sprintf(buf, "Can't open \"%s\"", filename); + snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename); DisplayError(buf, errno); return FALSE; } else { @@ -9265,7 +9518,7 @@ SaveGamePGN(f) { int i, offset, linelen, newblock; time_t tm; - char *movetext; +// char *movetext; char numtext[32]; int movelen, numlen, blank; char move_buffer[100]; /* [AS] Buffer for move+PV info */ @@ -9277,7 +9530,7 @@ SaveGamePGN(f) PrintPGNTags(f, &gameInfo); if (backwardMostMove > 0 || startedFromSetupPosition) { - char *fen = PositionToFEN(backwardMostMove, 1); + char *fen = PositionToFEN(backwardMostMove, NULL); fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen); fprintf(f, "\n{--------------\n"); PrintPosition(f, backwardMostMove); @@ -9340,7 +9593,16 @@ SaveGamePGN(f) linelen += numlen; /* Get move */ - movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */ + strcpy(move_buffer, parseList[i]); // [HGM] pgn: print move via buffer, so it can be edited + movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */ + if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) { + int p = movelen - 1; + if(move_buffer[p] == ' ') p--; + if(move_buffer[p] == ')') { // [HGM] pgn: strip off ICS time if we have extended info + while(p && move_buffer[--p] != '('); + if(p && move_buffer[p-1] == ' ') move_buffer[movelen=p-1] = 0; + } + } /* Print move */ blank = linelen > 0 && movelen > 0; @@ -9353,7 +9615,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, parseList[i]); + fprintf(f, move_buffer); linelen += movelen; /* [AS] Add PV info if present */ @@ -9361,25 +9623,19 @@ SaveGamePGN(f) /* [HGM] add time */ char buf[MSG_SIZ]; int seconds = 0; -#if 0 +#if 1 if(i >= backwardMostMove) { if(WhiteOnMove(i)) seconds = timeRemaining[0][i] - timeRemaining[0][i+1] - + GetTimeQuota(i/2) / WhitePlayer()->timeOdds; + + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds); else seconds = timeRemaining[1][i] - timeRemaining[1][i+1] - + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds; + + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds); } seconds = (seconds+50)/100; // deci-seconds, rounded to nearest #else seconds = (pvInfoList[i].time + 5)/10; // [HGM] PVtime: use engine time #endif - if (appData.debugMode,0) { - fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n", - timeRemaining[0][i+1], timeRemaining[0][i], - timeRemaining[1][i+1], timeRemaining[1][i], seconds - ); - } if( seconds <= 0) buf[0] = 0; else if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else { @@ -9527,7 +9783,7 @@ SavePositionToFile(filename) } else { f = fopen(filename, "a"); if (f == NULL) { - sprintf(buf, "Can't open \"%s\"", filename); + snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename); DisplayError(buf, errno); return FALSE; } else { @@ -9556,7 +9812,7 @@ SavePosition(f, dummy, dummy2) PrintPosition(f, currentMove); fprintf(f, "--------------]\n"); } else { - fen = PositionToFEN(currentMove, 1); + fen = PositionToFEN(currentMove, NULL); fprintf(f, "%s\n", fen); free(fen); } @@ -9657,17 +9913,17 @@ RegisterMove() } if (cmailOldMove == -1) { - DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0); + DisplayError(_("You have edited the game history.\nUse Reload Same Game and make your move again."), 0); return FALSE; } if (currentMove > cmailOldMove + 1) { - DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0); + DisplayError(_("You have entered too many moves.\nBack up to the correct position and try again."), 0); return FALSE; } if (currentMove < cmailOldMove) { - DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0); + DisplayError(_("Displayed position is not current.\nStep forward to the correct position and try again."), 0); return FALSE; } @@ -9715,7 +9971,7 @@ RegisterMove() cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE; nCmailMovesRegistered ++; } else if (nCmailGames == 1) { - DisplayError("You have not made a move yet", 0); + DisplayError(_("You have not made a move yet"), 0); return FALSE; } @@ -9736,18 +9992,18 @@ MailMoveEvent() char *arcDir; if (! cmailMsgLoaded) { - DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0); + DisplayError(_("The cmail message is not loaded.\nUse Reload CMail Message and make your move again."), 0); return; } if (nCmailGames == nCmailResults) { - DisplayError("No unfinished games", 0); + DisplayError(_("No unfinished games"), 0); return; } #if CMAIL_PROHIBIT_REMAIL if (cmailMailedMove) { - sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName); + sprintf(msg, _("You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line."), appData.cmailGameName); DisplayError(msg, 0); return; } @@ -9759,10 +10015,10 @@ MailMoveEvent() || (nCmailMovesRegistered + nCmailResults == nCmailGames)) { sprintf(string, partCommandString, appData.debugMode ? " -v" : "", appData.cmailGameName); - commandOutput = popen(string, "rb"); + commandOutput = popen(string, "r"); if (commandOutput == NULL) { - DisplayError("Failed to invoke cmail", 0); + DisplayError(_("Failed to invoke cmail"), 0); } else { for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) { nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput); @@ -9824,7 +10080,7 @@ CmailMsg() if (!cmailMsgLoaded) return ""; if (cmailMailedMove) { - sprintf(cmailMsg, "Waiting for reply from opponent\n"); + sprintf(cmailMsg, _("Waiting for reply from opponent\n")); } else { /* Create a list of games left */ sprintf(string, "["); @@ -9847,17 +10103,17 @@ CmailMsg() switch (nCmailGames) { case 1: sprintf(cmailMsg, - "Still need to make move for game\n"); + _("Still need to make move for game\n")); break; case 2: sprintf(cmailMsg, - "Still need to make moves for both games\n"); + _("Still need to make moves for both games\n")); break; default: sprintf(cmailMsg, - "Still need to make moves for all %d games\n", + _("Still need to make moves for all %d games\n"), nCmailGames); break; } @@ -9865,21 +10121,21 @@ CmailMsg() switch (nCmailGames - nCmailMovesRegistered - nCmailResults) { case 1: sprintf(cmailMsg, - "Still need to make a move for game %s\n", + _("Still need to make a move for game %s\n"), string); break; case 0: if (nCmailResults == nCmailGames) { - sprintf(cmailMsg, "No unfinished games\n"); + sprintf(cmailMsg, _("No unfinished games\n")); } else { - sprintf(cmailMsg, "Ready to send mail\n"); + sprintf(cmailMsg, _("Ready to send mail\n")); } break; default: sprintf(cmailMsg, - "Still need to make moves for games %s\n", + _("Still need to make moves for games %s\n"), string); } } @@ -10047,9 +10303,9 @@ EditCommentEvent() char title[MSG_SIZ]; if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) { - strcpy(title, "Edit comment"); + strcpy(title, _("Edit comment")); } else { - sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1, + sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1, WhiteOnMove(currentMove - 1) ? " " : ".. ", parseList[currentMove - 1]); } @@ -10073,17 +10329,19 @@ AnalyzeModeEvent() return; if (gameMode != AnalyzeFile) { - EditGameEvent(); - if (gameMode != EditGame) return; + if (!appData.icsEngineAnalyze) { + EditGameEvent(); + if (gameMode != EditGame) return; + } ResurrectChessProgram(); SendToProgram("analyze\n", &first); first.analyzing = TRUE; /*first.maybeThinking = TRUE;*/ first.maybeThinking = FALSE; /* avoid killing GNU Chess */ - AnalysisPopUp("Analysis", - "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."); + AnalysisPopUp(_("Analysis"), + _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.")); } - gameMode = AnalyzeMode; + if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode; pausing = FALSE; ModeHighlight(); SetGameInfo(); @@ -10107,8 +10365,8 @@ AnalyzeFileEvent() first.analyzing = TRUE; /*first.maybeThinking = TRUE;*/ first.maybeThinking = FALSE; /* avoid killing GNU Chess */ - AnalysisPopUp("Analysis", - "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."); + AnalysisPopUp(_("Analysis"), + _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.")); } gameMode = AnalyzeFile; pausing = FALSE; @@ -10141,7 +10399,7 @@ MachineWhiteEvent() EditPositionDone(); if (!WhiteOnMove(currentMove)) { - DisplayError("It is not White's turn", 0); + DisplayError(_("It is not White's turn"), 0); return; } @@ -10184,14 +10442,15 @@ MachineWhiteEvent() if (appData.autoFlipView && !flipView) { flipView = !flipView; DrawPosition(FALSE, NULL); + DisplayBothClocks(); // [HGM] logo: clocks might have to be exchanged; } if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); strcpy(bookMove, "move "); strcat(bookMove, bookHit); @@ -10220,7 +10479,7 @@ MachineBlackEvent() EditPositionDone(); if (WhiteOnMove(currentMove)) { - DisplayError("It is not Black's turn", 0); + DisplayError(_("It is not Black's turn"), 0); return; } @@ -10259,13 +10518,14 @@ MachineBlackEvent() if (appData.autoFlipView && flipView) { flipView = !flipView; DrawPosition(FALSE, NULL); + DisplayBothClocks(); // [HGM] logo: clocks might have to be exchanged; } if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); strcpy(bookMove, "move "); strcat(bookMove, bookHit); @@ -10312,7 +10572,7 @@ TwoMachinesEvent P((void)) case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError("Wait until your turn,\nor select Move Now", 0); + DisplayError(_("Wait until your turn,\nor select Move Now"), 0); return; } /* fall through */ @@ -10344,7 +10604,7 @@ TwoMachinesEvent P((void)) } else { /* kludge: allow timeout for initial "feature" command */ FreezeUI(); - DisplayMessage("", "Starting second chess program"); + DisplayMessage("", _("Starting second chess program")); ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT); } return; @@ -10409,9 +10669,9 @@ TwoMachinesEvent P((void)) if(bookHit) { // [HGM] book: simulate book reply static char bookMove[MSG_SIZ]; // a bit generous? - programStats.depth = programStats.nodes = programStats.time = + programStats.nodes = programStats.depth = programStats.time = programStats.score = programStats.got_only_move = 0; - sprintf(programStats.movelist, "%s (xbook)", bookMove); + sprintf(programStats.movelist, "%s (xbook)", bookHit); strcpy(bookMove, "move "); strcat(bookMove, bookHit); @@ -10425,7 +10685,7 @@ TrainingEvent() if (gameMode == Training) { SetTrainingModeOff(); gameMode = PlayFromGameFile; - DisplayMessage("", "Training mode off"); + DisplayMessage("", _("Training mode off")); } else { gameMode = Training; animateTraining = appData.animate; @@ -10433,10 +10693,10 @@ TrainingEvent() /* make sure we are not already at the end of the game */ if (currentMove < forwardMostMove) { SetTrainingModeOn(); - DisplayMessage("", "Training mode on"); + DisplayMessage("", _("Training mode on")); } else { gameMode = PlayFromGameFile; - DisplayError("Already at end of game", 0); + DisplayError(_("Already at end of game"), 0); } } ModeHighlight(); @@ -10517,13 +10777,13 @@ EditGameEvent() break; case IcsPlayingBlack: case IcsPlayingWhite: - DisplayError("Warning: You are still playing a game", 0); + DisplayError(_("Warning: You are still playing a game"), 0); break; case IcsObserving: - DisplayError("Warning: You are still observing a game", 0); + DisplayError(_("Warning: You are still observing a game"), 0); break; case IcsExamining: - DisplayError("Warning: You are still examining a game", 0); + DisplayError(_("Warning: You are still examining a game"), 0); break; case IcsIdle: break; @@ -10593,6 +10853,12 @@ EditPositionEvent() void ExitAnalyzeMode() { + /* [DM] icsEngineAnalyze - possible call from other functions */ + if (appData.icsEngineAnalyze) { + appData.icsEngineAnalyze = FALSE; + + DisplayMessage("",_("Close ICS engine analyze...")); + } if (first.analysisSupport && first.analyzing) { SendToProgram("exit\n", &first); first.analyzing = FALSE; @@ -10813,7 +11079,7 @@ DropMenuEvent(selection, x, y) case IcsPlayingWhite: case MachinePlaysBlack: if (!WhiteOnMove(currentMove)) { - DisplayMoveError("It is Black's turn"); + DisplayMoveError(_("It is Black's turn")); return; } moveType = WhiteDrop; @@ -10821,7 +11087,7 @@ DropMenuEvent(selection, x, y) case IcsPlayingBlack: case MachinePlaysWhite: if (WhiteOnMove(currentMove)) { - DisplayMoveError("It is White's turn"); + DisplayMoveError(_("It is White's turn")); return; } moveType = BlackDrop; @@ -10838,7 +11104,7 @@ DropMenuEvent(selection, x, y) + (int) BlackPawn - (int) WhitePawn); } if (boards[currentMove][y][x] != EmptySquare) { - DisplayMoveError("That square is occupied"); + DisplayMoveError(_("That square is occupied")); return; } @@ -10862,7 +11128,7 @@ AcceptEvent() GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER); cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT; } else { - DisplayError("There is no pending offer on this move", 0); + DisplayError(_("There is no pending offer on this move"), 0); cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE; } } else { @@ -10888,7 +11154,7 @@ DeclineEvent() DisplayComment(cmailOldMove - 1, "Draw declined"); #endif /*NOTDEF*/ } else { - DisplayError("There is no pending offer on this move", 0); + DisplayError(_("There is no pending offer on this move"), 0); } } else { /* Not used for offers from chess program */ @@ -10924,7 +11190,7 @@ CallFlagEvent() else GameEnds(BlackWins, "Black wins on time", GE_PLAYER); } else { - DisplayError("Your opponent is not out of time", 0); + DisplayError(_("Your opponent is not out of time"), 0); } break; case MachinePlaysBlack: @@ -10935,7 +11201,7 @@ CallFlagEvent() else GameEnds(WhiteWins, "White wins on time", GE_PLAYER); } else { - DisplayError("Your opponent is not out of time", 0); + DisplayError(_("Your opponent is not out of time"), 0); } break; } @@ -10970,7 +11236,7 @@ DrawEvent() DisplayComment(currentMove - 1, offer); cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW; } else { - DisplayError("You must make your move before offering a draw", 0); + DisplayError(_("You must make your move before offering a draw"), 0); cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE; } } else if (first.offeredDraw) { @@ -11284,11 +11550,11 @@ void RevertEvent() { if (gameMode != IcsExamining) { - DisplayError("You are not examining a game", 0); + DisplayError(_("You are not examining a game"), 0); return; } if (pausing) { - DisplayError("You can't revert while pausing", 0); + DisplayError(_("You can't revert while pausing"), 0); return; } SendToICS(ics_prefix); @@ -11302,7 +11568,7 @@ RetractMoveEvent() case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError("Wait until your turn,\nor select Move Now", 0); + DisplayError(_("Wait until your turn,\nor select Move Now"), 0); return; } if (forwardMostMove < 2) return; @@ -11342,14 +11608,14 @@ MoveNowEvent() switch (gameMode) { case MachinePlaysWhite: if (!WhiteOnMove(forwardMostMove)) { - DisplayError("It is your turn", 0); + DisplayError(_("It is your turn"), 0); return; } cps = &first; break; case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove)) { - DisplayError("It is your turn", 0); + DisplayError(_("It is your turn"), 0); return; } cps = &first; @@ -11399,19 +11665,19 @@ HintEvent() switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError("Wait until your turn", 0); + DisplayError(_("Wait until your turn"), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError("Wait until your turn", 0); + DisplayError(_("Wait until your turn"), 0); return; } break; default: - DisplayError("No hint available", 0); + DisplayError(_("No hint available"), 0); return; } SendToProgram("hint\n", &first); @@ -11425,14 +11691,14 @@ BookEvent() switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError("Wait until your turn", 0); + DisplayError(_("Wait until your turn"), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError("Wait until your turn", 0); + DisplayError(_("Wait until your turn"), 0); return; } break; @@ -11814,7 +12080,7 @@ SendToProgram(message, cps) outCount = OutputToProcess(cps->pr, message, count, &error); if (outCount < count && !exiting && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */ - sprintf(buf, "Error writing to %s chess program", cps->which); + sprintf(buf, _("Error writing to %s chess program"), cps->which); if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ if(epStatus[forwardMostMove] <= EP_DRAWS) { gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */ @@ -11844,12 +12110,12 @@ ReceiveFromProgram(isr, closure, message, count, error) if (count <= 0) { if (count == 0) { sprintf(buf, - "Error: %s chess program (%s) exited unexpectedly", + _("Error: %s chess program (%s) exited unexpectedly"), cps->which, cps->program); if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ if(epStatus[forwardMostMove] <= EP_DRAWS) { gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */ - sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program); + sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program); } else { gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins; } @@ -11859,7 +12125,7 @@ ReceiveFromProgram(isr, closure, message, count, error) DisplayFatalError(buf, 0, 1); } else { sprintf(buf, - "Error reading from %s chess program (%s)", + _("Error reading from %s chess program (%s)"), cps->which, cps->program); RemoveInputSource(cps->isr); @@ -11903,6 +12169,14 @@ ReceiveFromProgram(isr, closure, message, count, error) message); } } + + /* [DM] if icsEngineAnalyze is active we block all whisper and kibitz output, because nobody want to see this */ + if (appData.icsEngineAnalyze) { + if (strstr(message, "whisper") != NULL || + strstr(message, "kibitz") != NULL || + strstr(message, "tellics") != NULL) return; + } + HandleMachineMove(message, cps); } @@ -12082,6 +12356,64 @@ StringFeature(p, name, loc, cps) return FALSE; } +int +ParseOption(Option *opt, ChessProgramState *cps) +// [HGM] options: process the string that defines an engine option, and determine +// name, type, default value, and allowed value range +{ + char *p, *q, buf[MSG_SIZ]; + int n, min = (-1)<<31, max = 1<<31, def; + + if(p = strstr(opt->name, " -spin ")) { + if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE; + if(max < min) max = min; // enforce consistency + if(def < min) def = min; + if(def > max) def = max; + opt->value = def; + opt->min = min; + opt->max = max; + opt->type = Spin; + } else if(p = strstr(opt->name, " -string ")) { + opt->textValue = p+9; + opt->type = TextBox; + } else if(p = strstr(opt->name, " -check ")) { + if(sscanf(p, " -check %d", &def) < 1) return FALSE; + opt->value = (def != 0); + opt->type = CheckBox; + } else if(p = strstr(opt->name, " -combo ")) { + opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type + cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices + opt->value = n = 0; + while(q = StrStr(q, " /// ")) { + n++; *q = 0; // count choices, and null-terminate each of them + q += 5; + if(*q == '*') { // remember default, which is marked with * prefix + q++; + opt->value = n; + } + cps->comboList[cps->comboCnt++] = q; + } + cps->comboList[cps->comboCnt++] = NULL; + opt->max = n + 1; + opt->type = ComboBox; + } else if(p = strstr(opt->name, " -button")) { + opt->type = Button; + } else if(p = strstr(opt->name, " -save")) { + opt->type = SaveButton; + } else return FALSE; + *p = 0; // terminate option name + // now look if the command-line options define a setting for this engine option. + if(cps->optionSettings && cps->optionSettings[0]) + p = strstr(cps->optionSettings, opt->name); else p = NULL; + if(p && (p == cps->optionSettings || p[-1] == ',')) { + sprintf(buf, "option %s", p); + if(p = strstr(buf, ",")) *p = 0; + strcat(buf, "\n"); + SendToProgram(buf, cps); + } + return TRUE; +} + void FeatureDone(cps, val) ChessProgramState* cps; @@ -12155,6 +12487,16 @@ ParseFeatures(args, cps) if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue; if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue; if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue; + if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) { + ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature + if(cps->nrOptions >= MAX_OPTIONS) { + cps->nrOptions--; + sprintf(buf, "%s engine has too many options\n", cps->which); + DisplayError(buf, 0); + } + continue; + } + if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue; /* End of additions by HGM */ /* unknown feature: complain and skip */ @@ -12276,6 +12618,8 @@ DisplayMove(moveNumber) char res[MSG_SIZ]; char cpThinkOutput[MSG_SIZ]; + if(appData.noGUI) return; // [HGM] fast: suppress display of moves + if (moveNumber == forwardMostMove - 1 || gameMode == AnalyzeMode || gameMode == AnalyzeFile) { @@ -12313,7 +12657,7 @@ DisplayMove(moveNumber) } else { res[0] = NULLCHAR; } - + if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) { DisplayMessage(res, cpThinkOutput); } else { @@ -12330,7 +12674,8 @@ DisplayAnalysisText(text) { char buf[MSG_SIZ]; - if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) { + if (gameMode == AnalyzeMode || gameMode == AnalyzeFile + || appData.icsEngineAnalyze) { sprintf(buf, "Analysis (%s)", first.tidy); AnalysisPopUp(buf, text); } @@ -12366,8 +12711,8 @@ DisplayAnalysis() } else { safeStrCpy( lst, programStats.movelist, sizeof(lst)); - nps = (((double)programStats.nodes) / - (((double)programStats.time)/100.0)); + nps = (u64ToDouble(programStats.nodes) / + ((double)programStats.time /100.0)); cs = programStats.time % 100; s = programStats.time / 100; @@ -12378,32 +12723,32 @@ DisplayAnalysis() if (programStats.moves_left > 0 && appData.periodicUpdates) { if (programStats.move_name[0] != NULLCHAR) { - sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d", + sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", programStats.depth, programStats.nr_moves-programStats.moves_left, programStats.nr_moves, programStats.move_name, ((float)programStats.score)/100.0, lst, only_one_move(lst)? xtra[programStats.got_fail] : "", - programStats.nodes, (int)nps, h, m, s, cs); + (u64)programStats.nodes, (int)nps, h, m, s, cs); } else { - sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d", + sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", programStats.depth, programStats.nr_moves-programStats.moves_left, programStats.nr_moves, ((float)programStats.score)/100.0, lst, only_one_move(lst)? xtra[programStats.got_fail] : "", - programStats.nodes, (int)nps, h, m, s, cs); + (u64)programStats.nodes, (int)nps, h, m, s, cs); } } else { - sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d", + sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d", programStats.depth, ((float)programStats.score)/100.0, lst, only_one_move(lst)? xtra[programStats.got_fail] : "", - programStats.nodes, (int)nps, h, m, s, cs); + (u64)programStats.nodes, (int)nps, h, m, s, cs); } } DisplayAnalysisText(buf); @@ -12426,16 +12771,16 @@ DisplayComment(moveNumber, text) 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; - // [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); - CommentPopUp(title, buf); - } else if (text != NULL) CommentPopUp(title, text); } @@ -12488,9 +12833,9 @@ CheckFlags() } } else { if (blackFlag) { - if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell"); + if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell")); } else { - if(gameMode != TwoMachinesPlay) DisplayTitle("White's flag fell"); + if(gameMode != TwoMachinesPlay) DisplayTitle(_("White's flag fell")); if (appData.autoCallFlag) { GameEnds(BlackWins, "Black wins on time", GE_XBOARD); return TRUE; @@ -12510,9 +12855,9 @@ CheckFlags() } } else { if (whiteFlag) { - if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell"); + if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell")); } else { - if(gameMode != TwoMachinesPlay) DisplayTitle("Black's flag fell"); + if(gameMode != TwoMachinesPlay) DisplayTitle(_("Black's flag fell")); if (appData.autoCallFlag) { GameEnds(WhiteWins, "White wins on time", GE_XBOARD); return TRUE; @@ -12560,6 +12905,11 @@ DisplayBothClocks() you have neither ftime nor gettimeofday. */ +/* VS 2008 requires the #include outside of the function */ +#if !HAVE_GETTIMEOFDAY && HAVE_FTIME +#include +#endif + /* Get the current time as a TimeMark */ void GetTimeMark(tm) @@ -12577,7 +12927,7 @@ GetTimeMark(tm) #else /*!HAVE_GETTIMEOFDAY*/ #if HAVE_FTIME -#include +// include / moved to just above start of function struct timeb timeB; ftime(&timeB); @@ -12998,9 +13348,9 @@ PGNDate() char * -PositionToFEN(move, useFEN960) +PositionToFEN(move, overrideCastling) int move; - int useFEN960; + char *overrideCastling; { int i, j, fromX, fromY, toX, toY; int whiteToPlay; @@ -13075,6 +13425,9 @@ PositionToFEN(move, useFEN960) *p++ = whiteToPlay ? 'w' : 'b'; *p++ = ' '; + if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines + while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; + } else { if(nrCastlingRights) { q = p; if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) { @@ -13132,6 +13485,7 @@ PositionToFEN(move, useFEN960) } *p++ = ' '; } + } /* [HGM] find reversible plies */ { int i = 0, j=move; @@ -13389,7 +13743,7 @@ EditPositionPasteFEN(char *fen) Board initial_position; if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) { - DisplayError("Bad FEN position in clipboard", 0); + DisplayError(_("Bad FEN position in clipboard"), 0); return ; } else { int savedBlackPlaysFirst = blackPlaysFirst;