--- /dev/null
+/*
+ * backend.c -- Common back end for X and Windows NT versions of
+ * XBoard $Id$
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
+ * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * 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
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ * ------------------------------------------------------------------------
+ *
+ * See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <math.h>
+/* daniel */
+#include <windows.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#else /* not HAVE_SYS_FCNTL_H */
+# if HAVE_FCNTL_H
+# include <fcntl.h>
+# endif /* HAVE_FCNTL_H */
+#endif /* not HAVE_SYS_FCNTL_H */
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if defined(_amigados) && !defined(__GNUC__)
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+extern int gettimeofday(struct timeval *, struct timezone *);
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "parser.h"
+#include "moves.h"
+#if ZIPPY
+# include "zippy.h"
+#endif
+#include "backendz.h"
+#include "winboard.h"
+
+/* A point in time */
+typedef struct {
+ long sec; /* Assuming this is >= 32 bits */
+ int ms; /* Assuming this is >= 16 bits */
+} TimeMark;
+
+int establish P((void));
+void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
+ char *buf, int count, int error));
+void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
+ char *buf, int count, int error));
+void SendToICS P((char *s));
+void SendToICSDelayed P((char *s, long msdelay));
+void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
+ int toX, int toY));
+void InitPosition P((int redraw));
+void HandleMachineMove P((char *message, ChessProgramState *cps));
+int AutoPlayOneMove P((void));
+int LoadGameOneMove P((ChessMove readAhead));
+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));
+void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
+void ShowMove P((int fromX, int fromY, int toX, int toY));
+void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
+ /*char*/int promoChar));
+void BackwardInner P((int target));
+void ForwardInner P((int target));
+void GameEnds P((ChessMove result, char *resultDetails, int whosays));
+void EditPositionDone P((void));
+void PrintOpponents P((FILE *fp));
+void PrintPosition P((FILE *fp, int move));
+void StartChessProgram P((ChessProgramState *cps));
+void GuiCommand P((int command, int param));
+void IcsAnalyzeOutPut P((ChessProgramState *cps, int endThink));
+int SwitchGames P((void));
+void SendToProgram P((char *message, ChessProgramState *cps));
+void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
+void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
+ char *buf, int count, int error));
+void SendTimeControl P((ChessProgramState *cps,
+ int mps, long tc, int inc, int sd, int st));
+char *TimeControlTagValue P((void));
+void Attention P((ChessProgramState *cps));
+void FeedMovesToProgram P((ChessProgramState *cps, int upto));
+void ResurrectChessProgram P((void));
+void DisplayComment P((int moveNumber, char *text));
+void DisplayMove P((int moveNumber));
+void GetTimeMark P((TimeMark *));
+void ParseGameHistory P((char *game));
+void ParseBoard12 P((char *string));
+void StartClocks P((void));
+void SwitchClocks P((void));
+void StopClocks P((void));
+void ResetClocks P((void));
+char *PGNDate P((void));
+void SetGameInfo P((void));
+Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
+int RegisterMove P((void));
+void MakeRegisteredMove P((void));
+void TruncateGame P((void));
+int looking_at P((char *, int *, char *));
+void CopyPlayerNameIntoFileName P((char **, char *));
+char *SavePart P((char *));
+int SaveGameOldStyle P((FILE *));
+int SaveGamePGN P((FILE *));
+
+long SubtractTimeMarks P((TimeMark *, TimeMark *));
+int CheckFlags P((void));
+long NextTickLength P((long));
+void CheckTimeControl P((void));
+void show_bytes P((FILE *, char *, int));
+int string_to_rating P((char *str));
+void ParseFeatures P((char* args, ChessProgramState *cps));
+void InitBackEnd3 P((void));
+void FeatureDone P((ChessProgramState* cps, int val));
+void InitChessProgram P((ChessProgramState *cps));
+extern int tinyLayout, smallLayout;
+
+void ParseZippyP3 P((char* data, char* player));
+
+/* States for ics_getting_history */
+#define H_FALSE 0
+#define H_REQUESTED 1
+#define H_GOT_REQ_HEADER 2
+#define H_GOT_UNREQ_HEADER 3
+#define H_GETTING_MOVES 4
+#define H_GOT_UNWANTED_HEADER 5
+
+/* whosays values for GameEnds */
+#define GE_ICS 0
+#define GE_ENGINE 1
+#define GE_PLAYER 2
+#define GE_FILE 3
+#define GE_XBOARD 4
+
+/* Maximum number of games in a cmail message */
+#define CMAIL_MAX_GAMES 20
+
+/* Different types of move when calling RegisterMove */
+#define CMAIL_MOVE 0
+#define CMAIL_RESIGN 1
+#define CMAIL_DRAW 2
+#define CMAIL_ACCEPT 3
+
+/* Different types of result to remember for each game */
+#define CMAIL_NOT_RESULT 0
+#define CMAIL_OLD_RESULT 1
+#define CMAIL_NEW_RESULT 2
+
+/* Telnet protocol constants */
+#define TN_WILL 0373
+#define TN_WONT 0374
+#define TN_DO 0375
+#define TN_DONT 0376
+#define TN_IAC 0377
+#define TN_ECHO 0001
+#define TN_SGA 0003
+#define TN_PORT 23
+
+/* Fake up flags for now, as we aren't keeping track of castling
+ availability yet */
+int
+PosFlags(index)
+{
+ int flags = F_ALL_CASTLE_OK;
+ 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;
+ break;
+ case VariantAtomic:
+ flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
+ break;
+ case VariantKriegspiel:
+ flags |= F_KRIEGSPIEL_CAPTURE;
+ break;
+ case VariantNoCastle:
+ flags &= ~F_ALL_CASTLE_OK;
+ break;
+ default:
+ break;
+ }
+ return flags;
+}
+
+FILE *gameFileFP, *debugFP;
+
+char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
+char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
+char thinkOutput1[MSG_SIZ*10];
+
+/*ChessProgramState first, second; */
+
+/* premove variables */
+int premoveToX = 0;
+int premoveToY = 0;
+int premoveFromX = 0;
+int premoveFromY = 0;
+int premovePromoChar = 0;
+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 currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
+int pauseExamForwardMostMove = 0;
+int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
+int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
+int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
+int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
+int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
+int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
+int whiteFlag = FALSE, blackFlag = FALSE;
+int userOfferedDraw = FALSE;
+int ics_user_moved = 0, ics_getting_history = H_FALSE, ics_gamenum = -1;
+int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
+int cmailMoveType[CMAIL_MAX_GAMES];
+prefixHint = 0; /* PonderMove true/false */
+long ics_clock_paused = 0;
+ProcRef icsPR = NoProc, cmailPR = NoProc;
+InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
+gameMode = BeginningOfGame;
+char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
+char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
+char white_holding[64], black_holding[64];
+TimeMark lastNodeCountTime;
+long lastNodeCount = 0;
+int have_sent_ICS_logon = 0;
+int movesPerSession;
+long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
+long timeRemaining[2][MAX_MOVES];
+int matchGame = 0;
+TimeMark programStartTime;
+char ics_handle[MSG_SIZ];
+int have_set_title = 0;
+smartQueue icsQueue[max_gamenum];
+
+/* zippypassword3 */
+typedef struct {
+ char string[512];
+} value[6];
+value command;
+
+/* Search stats from chessprogram */
+typedef struct {
+ char movelist[MSG_SIZ]; /* Last PV we were sent */
+ char ponderMove[12]; /* Current ponder move */
+ 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 */
+ int GUI_time; /* Time from EngineRoom */
+} ChessProgramStats;
+
+static ChessProgramStats programStats;
+
+/* animateTraining preserves the state of appData.animate
+ * when Training mode is activated. This allows the
+ * response to be animated when appData.animate == TRUE and
+ * appData.animateDragging == TRUE.
+ */
+Boolean animateTraining;
+
+GameInfo gameInfo;
+
+AppData appData;
+
+Board boards[MAX_MOVES];
+Board initialPosition = {
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+ { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
+ WhitePawn, WhitePawn, WhitePawn, WhitePawn },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
+ BlackPawn, BlackPawn, BlackPawn, BlackPawn },
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen,
+ BlackKing, BlackBishop, BlackKnight, BlackRook }
+};
+Board twoKingsPosition = {
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
+ WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
+ { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
+ WhitePawn, WhitePawn, WhitePawn, WhitePawn },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
+ EmptySquare, EmptySquare, EmptySquare, EmptySquare },
+ { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
+ BlackPawn, BlackPawn, BlackPawn, BlackPawn },
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen,
+ BlackKing, BlackKing, BlackKnight, BlackRook }
+};
+
+/* Convert str to a rating. Checks for special cases of "----",
+ "++++", etc. Also strips ()'s */
+int
+string_to_rating(str)
+ char *str;
+{
+ while(*str && !isdigit(*str)) ++str;
+ if (!*str)
+ return 0; /* One of the special "no rating" cases */
+ else
+ return atoi(str);
+}
+
+void
+ClearProgramStats()
+{
+ /* Init programStats */
+ programStats.movelist[0] = NULLCHAR;
+ programStats.ponderMove[0] = 0;
+ programStats.depth = 0;
+ programStats.nr_moves = 0;
+ programStats.moves_left = 0;
+ programStats.nodes = 0;
+ programStats.time = 100;
+ programStats.score = 0;
+ programStats.got_only_move = 0;
+ programStats.got_fail = 0;
+ programStats.line_is_book = 0;
+}
+
+void
+InitBackEnd1()
+{
+ int matched, min, sec;
+
+ GetTimeMark(&programStartTime);
+
+ ClearProgramStats();
+ programStats.ok_to_send = 1;
+ programStats.seen_stat = 0;
+ supportStat = 0;
+
+ /*
+ * Initialize game list
+ */
+ ListNew(&gameList);
+
+
+ /*
+ * Internet chess server status
+ */
+ if (appData.icsActive) {
+ appData.matchMode = FALSE;
+ appData.matchGames = 0;
+#if ZIPPY
+ appData.noChessProgram = !appData.zippyPlay;
+#else
+ appData.zippyPlay = FALSE;
+ appData.zippyTalk = FALSE;
+ appData.noChessProgram = TRUE;
+#endif
+ if (*appData.icsHelper != NULLCHAR) {
+ appData.useTelnet = TRUE;
+ appData.telnetProgram = appData.icsHelper;
+ }
+ } else {
+ appData.zippyTalk = appData.zippyPlay = FALSE;
+ }
+
+ /*
+ * Parse timeControl resource
+ */
+ if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
+ appData.movesPerSession)) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "bad timeControl option %s", appData.timeControl);
+ DisplayFatalError(buf, 0, 2);
+ }
+
+ /*
+ * Parse searchTime resource
+ */
+ if (*appData.searchTime != NULLCHAR) {
+ matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
+ if (matched == 1) {
+ searchTime = min * 60;
+ } else if (matched == 2) {
+ searchTime = min * 60 + sec;
+ } else {
+ char buf[MSG_SIZ];
+ sprintf(buf, "bad searchTime option %s", appData.searchTime);
+ DisplayFatalError(buf, 0, 2);
+ }
+ }
+
+ first.which = "first";
+ second.which = "second";
+ first.maybeThinking = second.maybeThinking = FALSE;
+ first.pr = second.pr = NoProc;
+ first.isr = second.isr = NULL;
+ first.sendTime = second.sendTime = 2;
+ first.sendDrawOffers = 1;
+ if (appData.firstPlaysBlack) {
+ first.twoMachinesColor = "black\n";
+ second.twoMachinesColor = "white\n";
+ } else {
+ first.twoMachinesColor = "white\n";
+ second.twoMachinesColor = "black\n";
+ }
+ first.program = appData.firstChessProgram;
+ second.program = appData.secondChessProgram;
+ first.host = appData.firstHost;
+ second.host = appData.secondHost;
+ first.dir = appData.firstDirectory;
+ second.dir = appData.secondDirectory;
+ first.other = &second;
+ second.other = &first;
+ first.initString = appData.initString;
+ second.initString = appData.secondInitString;
+ first.computerString = appData.firstComputerString;
+ second.computerString = appData.secondComputerString;
+ first.useSigint = second.useSigint = TRUE;
+ first.useSigterm = second.useSigterm = TRUE;
+ first.reuse = appData.reuseFirst;
+ second.reuse = appData.reuseSecond;
+ first.useSetboard = second.useSetboard = FALSE;
+ first.useSAN = second.useSAN = FALSE;
+ first.usePing = second.usePing = FALSE;
+ first.lastPing = second.lastPing = 0;
+ first.lastPong = second.lastPong = 0;
+ first.usePlayother = second.usePlayother = FALSE;
+ first.useColors = second.useColors = TRUE;
+ first.useUsermove = second.useUsermove = FALSE;
+ first.sendICS = second.sendICS = FALSE;
+ first.sendName = second.sendName = appData.icsActive;
+ first.sdKludge = second.sdKludge = FALSE;
+ first.stKludge = second.stKludge = FALSE;
+ TidyProgramName(first.program, first.host, first.tidy);
+ TidyProgramName(second.program, second.host, second.tidy);
+ first.matchWins = second.matchWins = 0;
+ strcpy(first.variants, appData.variant);
+ strcpy(second.variants, appData.variant);
+ first.analysisSupport = second.analysisSupport = 2; /* detect */
+ first.analyzing = second.analyzing = FALSE;
+ first.initDone = second.initDone = FALSE;
+
+ if (appData.firstProtocolVersion > PROTOVER ||
+ appData.firstProtocolVersion < 1) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "protocol version %d not supported",
+ appData.firstProtocolVersion);
+ DisplayFatalError(buf, 0, 2);
+ } else {
+ first.protocolVersion = appData.firstProtocolVersion;
+ }
+
+ if (appData.secondProtocolVersion > PROTOVER ||
+ appData.secondProtocolVersion < 1) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "protocol version %d not supported",
+ appData.secondProtocolVersion);
+ DisplayFatalError(buf, 0, 2);
+ } else {
+ second.protocolVersion = appData.secondProtocolVersion;
+ }
+
+ if (appData.icsActive) {
+ appData.clockMode = TRUE; /* changes dynamically in ICS mode */
+ } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
+ appData.clockMode = FALSE;
+ first.sendTime = second.sendTime = 0;
+ }
+
+#if ZIPPY
+ /* Override some settings from environment variables, for backward
+ compatibility. Unfortunately it's not feasible to have the env
+ vars just set defaults, at least in xboard. Ugh.
+ */
+ if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
+ ZippyInit();
+ }
+#endif
+
+ if (appData.noChessProgram) {
+ programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
+ + strlen(PATCHLEVEL));
+ sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
+ } else {
+ char *p, *q;
+ q = first.program;
+ while (*q != ' ' && *q != NULLCHAR) q++;
+ p = q;
+ while (p > first.program && *(p-1) != '/') p--;
+ programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
+ + strlen(PATCHLEVEL) + (q - p));
+ sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
+ strncat(programVersion, p, q - p);
+ }
+
+ if (!appData.icsActive) {
+ char buf[MSG_SIZ];
+ /* Check for variants that are supported only in ICS mode,
+ or not at all. Some that are accepted here nevertheless
+ have bugs; see comments below.
+ */
+ VariantClass variant = StringToVariant(appData.variant);
+ switch (variant) {
+ case VariantBughouse: /* need four players and two boards */
+ case VariantKriegspiel: /* need to hide pieces and move details */
+ case VariantFischeRandom: /* castling doesn't work, shuffle not done */
+ sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
+ DisplayFatalError(buf, 0, 2);
+ return;
+
+ case VariantUnknown:
+ case VariantLoadable:
+ case Variant29:
+ case Variant30:
+ case Variant31:
+ case Variant32:
+ case Variant33:
+ case Variant34:
+ case Variant35:
+ case Variant36:
+ default:
+ sprintf(buf, "Unknown variant name %s", appData.variant);
+ DisplayFatalError(buf, 0, 2);
+ return;
+
+ case VariantNormal: /* definitely works! */
+ case VariantWildCastle: /* pieces not automatically shuffled */
+ case VariantNoCastle: /* pieces not automatically shuffled */
+ case VariantCrazyhouse: /* holdings not shown,
+ offboard interposition not understood */
+ case VariantLosers: /* should work except for win condition,
+ and doesn't know captures are mandatory */
+ case VariantSuicide: /* should work except for win condition,
+ and doesn't know captures are mandatory */
+ case VariantGiveaway: /* should work except for win condition,
+ and doesn't know captures are mandatory */
+ case VariantTwoKings: /* should work */
+ case VariantAtomic: /* should work except for win condition */
+ case Variant3Check: /* should work except for win condition */
+ case VariantShatranj: /* might work if TestLegality is off */
+ break;
+ }
+ }
+}
+
+int
+ParseTimeControl(tc, ti, mps)
+ char *tc;
+ int ti;
+ int mps;
+{
+ int matched, min, sec;
+
+ matched = sscanf(tc, "%d:%d", &min, &sec);
+ if (matched == 1) {
+ timeControl = min * 60 * 1000;
+ } else if (matched == 2) {
+ timeControl = (min * 60 + sec) * 1000;
+ } else {
+ return FALSE;
+ }
+
+ if (ti >= 0) {
+ timeIncrement = ti * 1000; /* convert to ms */
+ movesPerSession = 0;
+ } else {
+ timeIncrement = 0;
+ movesPerSession = mps;
+ }
+ return TRUE;
+}
+
+void
+InitBackEnd2()
+{
+ if (appData.debugMode) {
+ fprintf(debugFP, "%s\n", programVersion);
+ }
+
+ if (appData.matchGames > 0) {
+ appData.matchMode = TRUE;
+ } else if (appData.matchMode) {
+ appData.matchGames = 1;
+ }
+ Reset(TRUE, FALSE);
+ if (appData.noChessProgram || first.protocolVersion == 1) {
+ InitBackEnd3();
+ } else {
+ /* kludge: allow timeout for initial "feature" commands */
+ FreezeUI();
+ if (appData.icsActive) {
+ DisplayMessage("", "ICS-Mode: Waiting for chessprogram...");
+ } else {
+ DisplayMessage("", "Starting chessprogram");
+ }
+ ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
+ }
+}
+
+void
+InitBackEnd3 P((void))
+{
+ GameMode initialMode;
+ char buf[MSG_SIZ];
+ int err;
+
+ InitChessProgram(&first);
+
+ /* Make a console window if needed */
+ if (appData.icsActive) {
+ ConsoleCreate();
+ }
+ /* engine Room */
+ supportStat = 0;
+
+ if (appData.icsActive) {
+ err = establish();
+ if (err != 0) {
+ if (*appData.icsCommPort != NULLCHAR) {
+ sprintf(buf, "Could not open comm port %s",
+ appData.icsCommPort);
+ } else {
+ sprintf(buf, "Could not connect to host %s, port %s",
+ appData.icsHost, appData.icsPort);
+ }
+ DisplayFatalError(buf, err, 1);
+ return;
+ }
+ SetICSMode();
+ telnetISR =
+ AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
+ fromUserISR =
+ AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
+ } else if (appData.noChessProgram) {
+ SetNCPMode();
+ } else {
+ SetGNUMode();
+ }
+
+ if (*appData.cmailGameName != NULLCHAR) {
+ SetCmailMode();
+ OpenLoopback(&cmailPR);
+ cmailISR =
+ AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
+ }
+
+ ThawUI();
+ DisplayMessage("", "");
+ if (StrCaseCmp(appData.initialMode, "") == 0) {
+ initialMode = BeginningOfGame;
+ } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
+ initialMode = TwoMachinesPlay;
+ } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
+ initialMode = AnalyzeFile;
+ } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
+ initialMode = AnalyzeMode;
+ } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
+ initialMode = MachinePlaysWhite;
+ } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
+ initialMode = MachinePlaysBlack;
+ } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
+ initialMode = EditGame;
+ } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
+ initialMode = EditPosition;
+ } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
+ initialMode = Training;
+ } else {
+ sprintf(buf, "Unknown initialMode %s", appData.initialMode);
+ DisplayFatalError(buf, 0, 2);
+ return;
+ }
+
+ if (appData.matchMode) {
+ /* Set up machine vs. machine match */
+ if (appData.noChessProgram) {
+ DisplayFatalError("Can't have a match with no chess programs",
+ 0, 2);
+ return;
+ }
+ matchMode = TRUE;
+ matchGame = 1;
+ if (*appData.loadGameFile != NULLCHAR) {
+ if (!LoadGameFromFile(appData.loadGameFile,
+ appData.loadGameIndex,
+ appData.loadGameFile, FALSE)) {
+ DisplayFatalError("Bad game file", 0, 1);
+ return;
+ }
+ } else if (*appData.loadPositionFile != NULLCHAR) {
+ if (!LoadPositionFromFile(appData.loadPositionFile,
+ appData.loadPositionIndex,
+ appData.loadPositionFile)) {
+ DisplayFatalError("Bad position file", 0, 1);
+ return;
+ }
+ }
+ TwoMachinesEvent();
+ } else if (*appData.cmailGameName != NULLCHAR) {
+ /* Set up cmail mode */
+ ReloadCmailMsgEvent(TRUE);
+ } else {
+ /* Set up other modes */
+ if (initialMode == AnalyzeFile) {
+ if (*appData.loadGameFile == NULLCHAR) {
+ DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
+ return;
+ }
+ }
+ if (*appData.loadGameFile != NULLCHAR) {
+ (void) LoadGameFromFile(appData.loadGameFile,
+ appData.loadGameIndex,
+ appData.loadGameFile, TRUE);
+ } else if (*appData.loadPositionFile != NULLCHAR) {
+ (void) LoadPositionFromFile(appData.loadPositionFile,
+ appData.loadPositionIndex,
+ appData.loadPositionFile);
+ }
+ if (initialMode == AnalyzeMode) {
+ if (appData.noChessProgram) {
+ DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
+ return;
+ }
+ if (appData.icsActive) {
+ DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
+ return;
+ }
+ AnalyzeModeEvent();
+ } else if (initialMode == AnalyzeFile) {
+ ShowThinkingEvent(TRUE);
+ AnalyzeFileEvent();
+ AnalysisPeriodicEvent(1);
+ } else if (initialMode == MachinePlaysWhite) {
+ if (appData.noChessProgram) {
+ DisplayFatalError("MachineWhite mode requires a chess engine",
+ 0, 2);
+ return;
+ }
+ if (appData.icsActive) {
+ 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",
+ 0, 2);
+ return;
+ }
+ if (appData.icsActive) {
+ 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",
+ 0, 2);
+ return;
+ }
+ if (appData.icsActive) {
+ DisplayFatalError("TwoMachines mode does not work with ICS mode",
+ 0, 2);
+ return;
+ }
+ TwoMachinesEvent();
+ } else if (initialMode == EditGame) {
+ EditGameEvent();
+ } else if (initialMode == EditPosition) {
+ EditPositionEvent();
+ } else if (initialMode == Training) {
+ if (*appData.loadGameFile == NULLCHAR) {
+ DisplayFatalError("Training mode requires a game file", 0, 2);
+ return;
+ }
+ TrainingEvent();
+ }
+ }
+ /* If EngineRoom TRUE start */
+ if (appData.AnalysisWindow && appData.showThinking &&
+ !appData.noChessProgram) {
+ if (appData.icsActive && appData.zippyPlay) {
+ AnalysisPopUp(0,0,0,0,0,0,0,5);
+ }
+ if (!appData.icsActive) AnalysisPopUp(0,0,0,0,0,0,0,5);
+ StartAnalysisClock();
+ }
+}
+
+/*
+ * Establish will establish a contact to a remote host.port.
+ * Sets icsPR to a ProcRef for a process (or pseudo-process)
+ * used to talk to the host.
+ * Returns 0 if okay, error code if not.
+ */
+int
+establish()
+{
+ char buf[MSG_SIZ];
+
+ if (*appData.icsCommPort != NULLCHAR) {
+ /* Talk to the host through a serial comm port */
+ return OpenCommPort(appData.icsCommPort, &icsPR);
+
+ } 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",
+ 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,
+ appData.gateway, appData.telnetProgram,
+ appData.icsHost, appData.icsPort);
+ } else {
+ sprintf(buf, "%s %s -l %s %s %s %s",
+ appData.remoteShell, appData.gateway,
+ appData.remoteUser, appData.telnetProgram,
+ appData.icsHost, appData.icsPort);
+ }
+ return StartChildProcess(buf, "", &icsPR);
+
+ }
+ } else if (appData.useTelnet) {
+ return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
+
+ } else {
+ /* TCP socket interface differs somewhat between
+ Unix and NT; handle details in the front end.
+ */
+ return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
+ }
+}
+
+void
+show_bytes(fp, buf, count)
+ FILE *fp;
+ char *buf;
+ int count;
+{
+ while (count--) {
+ if (*buf < 040 || *(unsigned char *) buf > 0177) {
+ fprintf(fp, "\\%03o", *buf & 0xff);
+ } else {
+ putc(*buf, fp);
+ }
+ buf++;
+ }
+ fflush(fp);
+}
+
+/* Returns an errno value */
+int
+OutputMaybeTelnet(pr, message, count, outError)
+ ProcRef pr;
+ char *message;
+ int count;
+ int *outError;
+{
+ char buf[8192], *p, *q, *buflim;
+ int left, newcount, outcount;
+
+ if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
+ *appData.gateway != NULLCHAR) {
+ if (appData.debugMode) {
+ fprintf(debugFP, ">ICS: ");
+ show_bytes(debugFP, message, count);
+ fprintf(debugFP, "\n");
+ }
+ return OutputToProcess(pr, message, count, outError);
+ }
+
+ buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
+ p = message;
+ q = buf;
+ left = count;
+ newcount = 0;
+ while (left) {
+ if (q >= buflim) {
+ if (appData.debugMode) {
+ fprintf(debugFP, ">ICS: ");
+ show_bytes(debugFP, buf, newcount);
+ fprintf(debugFP, "\n");
+ }
+ outcount = OutputToProcess(pr, buf, newcount, outError);
+ if (outcount < newcount) return -1; /* to be sure */
+ q = buf;
+ newcount = 0;
+ }
+ if (*p == '\n') {
+ *q++ = '\r';
+ newcount++;
+ } else if (((unsigned char) *p) == TN_IAC) {
+ *q++ = (char) TN_IAC;
+ newcount ++;
+ }
+ *q++ = *p++;
+ newcount++;
+ left--;
+ }
+ if (appData.debugMode) {
+ fprintf(debugFP, ">ICS: ");
+ show_bytes(debugFP, buf, newcount);
+ fprintf(debugFP, "\n");
+ }
+ outcount = OutputToProcess(pr, buf, newcount, outError);
+ if (outcount < newcount) return -1; /* to be sure */
+ return count;
+}
+
+void
+read_from_player(isr, closure, message, count, error)
+ InputSourceRef isr;
+ VOIDSTAR closure;
+ char *message;
+ int count;
+ int error;
+{
+ int outError, outCount;
+ static int gotEof = 0;
+
+ /* Pass data read from player on to ICS */
+ if (count > 0) {
+ gotEof = 0;
+ outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
+ if (outCount < count) {
+ DisplayFatalError("Error writing to ICS", outError, 1);
+ }
+ } else if (count < 0) {
+ RemoveInputSource(isr);
+ DisplayFatalError("Error reading from keyboard", error, 1);
+ } else if (gotEof++ > 0) {
+ RemoveInputSource(isr);
+ DisplayFatalError("Got end of file from keyboard", 0, 0);
+ }
+}
+
+void
+SendToICS(s)
+ char *s;
+{
+ int count, outCount, outError;
+
+ if (icsPR == NULL) return;
+
+ count = strlen(s);
+ outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
+ if (outCount < count) {
+ DisplayFatalError("Error writing to ICS", outError, 1);
+ }
+}
+
+/* This is used for sending logon scripts to the ICS. Sending
+ without a delay causes problems when using timestamp on ICC
+ (at least on my machine). */
+void
+SendToICSDelayed(s,msdelay)
+ char *s;
+ long msdelay;
+{
+ int count, outCount, outError;
+
+ if (icsPR == NULL) return;
+
+ count = strlen(s);
+ if (appData.debugMode) {
+ fprintf(debugFP, ">ICS: ");
+ show_bytes(debugFP, s, count);
+ fprintf(debugFP, "\n");
+ }
+ outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
+ msdelay);
+ if (outCount < count) {
+ DisplayFatalError("Error writing to ICS", outError, 1);
+ }
+}
+
+
+/* Remove all highlighting escape sequences in s
+ Also deletes any suffix starting with '('
+ */
+char *
+StripHighlightAndTitle(s)
+ char *s;
+{
+ static char retbuf[MSG_SIZ];
+ char *p = retbuf;
+
+ while (*s != NULLCHAR) {
+ while (*s == '\033') {
+ while (*s != NULLCHAR && !isalpha(*s)) s++;
+ if (*s != NULLCHAR) s++;
+ }
+ while (*s != NULLCHAR && *s != '\033') {
+ if (*s == '(' || *s == '[') {
+ *p = NULLCHAR;
+ return retbuf;
+ }
+ *p++ = *s++;
+ }
+ }
+ *p = NULLCHAR;
+ return retbuf;
+}
+
+/* Remove all highlighting escape sequences in s */
+char *
+StripHighlight(s)
+ char *s;
+{
+ static char retbuf[MSG_SIZ];
+ char *p = retbuf;
+
+ while (*s != NULLCHAR) {
+ while (*s == '\033') {
+ while (*s != NULLCHAR && !isalpha(*s)) s++;
+ if (*s != NULLCHAR) s++;
+ }
+ while (*s != NULLCHAR && *s != '\033') {
+ *p++ = *s++;
+ }
+ }
+ *p = NULLCHAR;
+ return retbuf;
+}
+
+char *variantNames[] = VARIANT_NAMES;
+char *
+VariantName(v)
+ VariantClass v;
+{
+ return variantNames[v];
+}
+
+
+/* Identify a variant from the strings the chess servers use or the
+ PGN Variant tag names we use. */
+VariantClass
+StringToVariant(e)
+ char *e;
+{
+ char *p;
+ int wnum = -1;
+ VariantClass v = VariantNormal;
+ int i, found = FALSE;
+ char buf[MSG_SIZ];
+
+ if (!e) return v;
+
+ for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
+ if (StrCaseStr(e, variantNames[i])) {
+ v = (VariantClass) i;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
+ || StrCaseStr(e, "wild/fr")) {
+ v = VariantFischeRandom;
+ } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
+ (i = 1, p = StrCaseStr(e, "w"))) {
+ p += i;
+ while (*p && (isspace(*p) || *p == '(')) p++;
+ if (isdigit(*p)) {
+ wnum = atoi(p);
+ } else {
+ wnum = -1;
+ }
+ switch (wnum) {
+ case 0: /* FICS only, actually */
+ case 1:
+ /* Castling legal even if K starts on d-file */
+ v = VariantWildCastle;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ /* Castling illegal even if K & R happen to start in
+ normal positions. */
+ v = VariantNoCastle;
+ break;
+ case 5:
+ case 7:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 18:
+ case 19:
+ /* Castling legal iff K & R start in normal positions */
+ v = VariantNormal;
+ break;
+ case 6:
+ case 20:
+ case 21:
+ /* Special wilds for position setup; unclear what to do here */
+ v = VariantLoadable;
+ break;
+ case 9:
+ /* Bizarre ICC game */
+ v = VariantTwoKings;
+ break;
+ case 16:
+ v = VariantKriegspiel;
+ break;
+ case 17:
+ v = VariantLosers;
+ break;
+ case 22:
+ v = VariantFischeRandom;
+ break;
+ case 23:
+ v = VariantCrazyhouse;
+ break;
+ case 24:
+ v = VariantBughouse;
+ break;
+ case 25:
+ v = Variant3Check;
+ break;
+ case 26:
+ /* Not quite the same as FICS suicide! */
+ v = VariantGiveaway;
+ break;
+ case 27:
+ v = VariantAtomic;
+ break;
+ case 28:
+ v = VariantShatranj;
+ break;
+
+ /* Temporary names for future ICC types. The name *will* change in
+ the next xboard/WinBoard release after ICC defines it. */
+ case 29:
+ v = Variant29;
+ break;
+ case 30:
+ v = Variant30;
+ break;
+ case 31:
+ v = Variant31;
+ break;
+ case 32:
+ v = Variant32;
+ break;
+ case 33:
+ v = Variant33;
+ break;
+ case 34:
+ v = Variant34;
+ break;
+ case 35:
+ v = Variant35;
+ break;
+ case 36:
+ v = Variant36;
+ 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);
+ DisplayError(buf, 0);
+ v = VariantUnknown;
+ break;
+ }
+ }
+ }
+ if (appData.debugMode) {
+ fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
+ e, wnum, VariantName(v));
+ }
+ return v;
+}
+
+static int leftover_start = 0, leftover_len = 0;
+char star_match[STAR_MATCH_N][MSG_SIZ];
+
+/* Test whether pattern is present at &buf[*index]; if so, return TRUE,
+ advance *index beyond it, and set leftover_start to the new value of
+ *index; else return FALSE. If pattern contains the character '*', it
+ matches any sequence of characters not containing '\r', '\n', or the
+ character following the '*' (if any), and the matched sequence(s) are
+ copied into star_match.
+ */
+int
+looking_at(buf, index, pattern)
+ char *buf;
+ int *index;
+ char *pattern;
+{
+ char *bufp = &buf[*index], *patternp = pattern;
+ int star_count = 0;
+ char *matchp = star_match[0];
+
+ for (;;) {
+ if (*patternp == NULLCHAR) {
+ *index = leftover_start = bufp - buf;
+ *matchp = NULLCHAR;
+ return TRUE;
+ }
+ if (*bufp == NULLCHAR) return FALSE;
+ if (*patternp == '*') {
+ if (*bufp == *(patternp + 1)) {
+ *matchp = NULLCHAR;
+ matchp = star_match[++star_count];
+ patternp += 2;
+ bufp++;
+ continue;
+ } else if (*bufp == '\n' || *bufp == '\r') {
+ patternp++;
+ if (*patternp == NULLCHAR)
+ continue;
+ else
+ return FALSE;
+ } else {
+ *matchp++ = *bufp++;
+ continue;
+ }
+ }
+ if (*patternp != *bufp) return FALSE;
+ patternp++;
+ bufp++;
+ }
+}
+
+void
+SendToPlayer(data, length)
+ char *data;
+ int length;
+{
+ int error, outCount;
+ outCount = OutputToProcess(NoProc, data, length, &error);
+ if (outCount < length) {
+ DisplayFatalError("Error writing to display", error, 1);
+ }
+}
+
+void
+PackHolding(packed, holding)
+ char packed[];
+ char *holding;
+{
+ char *p = holding;
+ char *q = packed;
+ int runlength = 0;
+ int curr = 9999;
+ do {
+ if (*p == curr) {
+ runlength++;
+ } else {
+ switch (runlength) {
+ case 0:
+ break;
+ case 1:
+ *q++ = curr;
+ break;
+ case 2:
+ *q++ = curr;
+ *q++ = curr;
+ break;
+ default:
+ sprintf(q, "%d", runlength);
+ while (*q) q++;
+ *q++ = curr;
+ break;
+ }
+ runlength = 1;
+ curr = *p;
+ }
+ } while (*p++);
+ *q = NULLCHAR;
+}
+
+/* Telnet protocol requests from the front end */
+void
+TelnetRequest(ddww, option)
+ unsigned char ddww, option;
+{
+ unsigned char msg[3];
+ int outCount, outError;
+
+ if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
+
+ if (appData.debugMode) {
+ char buf1[8], buf2[8], *ddwwStr, *optionStr;
+ switch (ddww) {
+ case TN_DO:
+ ddwwStr = "DO";
+ break;
+ case TN_DONT:
+ ddwwStr = "DONT";
+ break;
+ case TN_WILL:
+ ddwwStr = "WILL";
+ break;
+ case TN_WONT:
+ ddwwStr = "WONT";
+ break;
+ default:
+ ddwwStr = buf1;
+ sprintf(buf1, "%d", ddww);
+ break;
+ }
+ switch (option) {
+ case TN_ECHO:
+ optionStr = "ECHO";
+ break;
+ default:
+ optionStr = buf2;
+ sprintf(buf2, "%d", option);
+ break;
+ }
+ fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
+ }
+ msg[0] = TN_IAC;
+ msg[1] = ddww;
+ msg[2] = option;
+ outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
+ if (outCount < 3) {
+ DisplayFatalError("Error writing to ICS", outError, 1);
+ }
+}
+
+void
+DoEcho()
+{
+ if (!appData.icsActive) return;
+ TelnetRequest(TN_DO, TN_ECHO);
+}
+
+void
+DontEcho()
+{
+ if (!appData.icsActive) return;
+ TelnetRequest(TN_DONT, TN_ECHO);
+}
+
+static int loggedOn = FALSE;
+
+/*-- Game start info cache: --*/
+int gs_gamenum;
+char gs_kind[MSG_SIZ];
+static char player1Name[128] = "";
+static char player2Name[128] = "";
+static int player1Rating = -1;
+static int player2Rating = -1;
+/*----------------------------*/
+
+
+/* ICC user send showinfo <game number> und we send latest pv */
+/* If username beginn with guest* send advertisement ;-) */
+void
+showInfo(data, player)
+char* data;
+char* player;
+{
+ char *ptr = data;
+ char temp[MSG_SIZ];
+ char buf[MSG_SIZ];
+ int i;
+
+ /* this feature is only for ICC admins or chessprogramer */
+ /* You must set commandline /icc to use this */
+ if (appData.userVersion == TRUE) return;
+ if (appData.ICC_feature == FALSE || ics_type != ICS_ICC) return;
+ while (*ptr == ' ') ptr++;
+ if (*ptr == 0 || (sizeof(ptr) > MSG_SIZ)) return;
+ sscanf(ptr, "%s", buf);
+
+ /* showgames command */
+ if (strncmp(buf, "showgames", 9) == 0) {
+ {
+ int counter;
+ for (counter = 1; counter <= max_gamenum; counter++) {
+ if (icsQueue[counter].killPv > 0) {
+ sprintf(temp, "tell %s I analyze game %d (%s/%s)\n", player, counter,
+ icsQueue[counter].white, icsQueue[counter].black);
+ SendToICS(temp);
+ }
+ }
+ return;
+ }
+ }
+ i = atoi(buf); /* security first - user could try to buffer overflow data ! */
+ if (i > max_gamenum || icsQueue[i].killPv == 0) {
+ sprintf(temp, "tell %s I don't analysis this game\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s \"tell %s showgames\" to see which games are currently analyzed.\n", player, ics_handle);
+ SendToICS(temp);
+ } else if (icsQueue[i].killPv > 0) {
+ sprintf(temp, "tell %s Hello %s from Winboard-DM\n", player, player);
+ SendToICS(temp);
+ /* pos after "Depth " */
+ if (icsQueue[i].lastpv[0]) {
+ sprintf(temp, "tell %s Last analyze of game %d (%s/%s): (%s) Depth=%s\n",
+ player, i, icsQueue[i].white, icsQueue[i].black, first.tidy, icsQueue[i].lastpv);
+ } else {
+ sprintf(temp, "tell %s Sorry, no analysis avaible from game %d at the moment. Please try it later again.\n",
+ player, i);
+ }
+ SendToICS(temp);
+ /* for ICC guest send member and register info */
+ if (strncmp(player, "guest", 5) == 0) {
+ sprintf(temp, "tell %s Be member of ICC. Get a free 7-days trial membership: \"http://www.chessclub.com/register/english.html\" For more information type: \"help reg\"\n", player);
+ SendToICS(temp);
+ }
+ }
+}
+
+/* ICS-Analyze:
+ Call this to see if we have a game where we not send a message
+ for the current move
+ */
+
+int
+SwitchGames()
+{
+ int counter;
+ char buf[32];
+
+ for (counter = 1; counter <= max_gamenum; counter++) {
+ if (icsQueue[counter].killPv > 0 && icsQueue[counter].CurrentMove >=
+ icsQueue[counter].MessageMove && icsQueue[counter].flag == 0) {
+ if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: We switch to game %d \n", counter);
+ sprintf(buf, "refresh %d \n", counter);
+ SendToICS(buf);
+ return 0;
+ break;
+ }
+ }
+ return 1;
+}
+
+/* parse and action from zippy password3 */
+void
+ParseZippyP3(data, player)
+char* data;
+char* player;
+{
+ int counter = 0;
+ int forward = 0;
+ int i, j;
+ char *ptr = data;
+ char temp[MSG_SIZ];
+
+ if (appData.userVersion == TRUE) return;
+
+ for (;;) {
+ if (counter > 6) {
+ sprintf(temp, "tell %s Sorry, to many values. Max 6 values each line. Try again.\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s Or try send me \"help\"\n", player);
+ SendToICS(temp);
+ return;
+ }
+ while (*ptr == ' ') ptr++;
+ if (*ptr == 0) break;
+ sscanf(ptr, "%s", command[counter].string);
+ if (appData.debugMode) fprintf(debugFP, "zp3: %s\n", command[counter].string);
+ ptr = ptr + strlen(command[counter].string);
+ counter++;
+ }
+ if(strncmp(command[forward].string, "help", 4) == 0) {
+ /* icc don't accept text after new line :((( */
+ /* So, we must write every line */
+ sprintf(temp, "tell %s Hello %s from %s %s%s\n", player, player, PRODUCT, VERSION, PATCHLEVEL);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze <observe|follow|unobserve|unfollow> <Playername|Gamenumber>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze <start|stop>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze output <whisper|kibitz|tell|none> if tell <value>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze killpv <value> <Gamenumber>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s if you are setup killpv you enable it automaticly\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze smartqueue <1|0>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze show <Gamenumber>\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s engine reset\n", player);
+ SendToICS(temp);
+ sprintf(temp, "tell %s <whisper|kibitz>\n", player);
+ SendToICS(temp);
+ return;
+ }
+
+ if (strncmp(command[forward].string, "engine", 6) == 0) {
+ if (strncmp(command[forward+1].string, "reset", 5) == 0) {
+ sprintf(temp, "tell %s reset engine...\n", player);
+ SendToICS(temp);
+ ResurrectChessProgram();
+ }
+ return;
+ }
+ /*
+ if (strncmp(command[forward].string, "whisper", 7) == 0) {
+ sprintf(temp, "tell %s GUI = whisper\n", player);
+ SendToICS(temp);
+ appData.SendOutPutToICS = 1;
+ return;
+ } else if (trncmp(command[forward].string, "kibitz", 6) == 0) {
+ sprintf(temp, "tell %s GUI = kibitz\n", player);
+ SendToICS(temp);
+ appData.SendOutPutToICS = 2;
+ return;
+ }
+ */
+ if(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
+ sprintf(temp, "tell %s Sorry, i'm playing a game!\n", player);
+ SendToICS(temp);
+ return;
+ }
+ if (strncmp(command[forward].string, "analyze", 10) == 0) {
+ if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
+ strncmp(command[forward+1].string, "follow", 6) == 0 ||
+ strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
+ strncmp(command[forward+1].string, "unfollow", 8) == 0) {
+
+ /* reset Queue if leave */
+ if (strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
+ strncmp(command[forward+1].string, "unfollow", 8) == 0) ResetIcsQueue(ics_gamenum);
+
+ /* secure */
+ if(gameMode != IcsPlayingWhite || gameMode != IcsPlayingBlack) {
+ sprintf(temp, "%s %s\n", command[forward+1].string,
+ command[forward+2].string);
+ SendToICS(temp);
+ sprintf(temp, "tell %s action: %s %s\n", player, command[forward+1].string,
+ command[forward+2].string);
+ if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
+ strncmp(command[forward+1].string, "follow", 6) == 0) {
+
+ if (ics_gamenum > max_gamenum) {
+ sprintf(temp, "tell %s gamenumber to high\n", player);
+ SendToICS(temp);
+ return;
+ }
+ }
+ } else {
+ sprintf(temp, "tell %s Sorry, i'm playing\n", player);
+ }
+ SendToICS(temp);
+ return;
+ }
+
+ if (strncmp(command[forward+1].string, "start", 5) == 0) {
+ if (gameMode != IcsObserving) {
+ sprintf(temp, "tell %s I must observe a game\n", player);
+ } else if (gameMode == IcsObserving) {
+ sprintf(temp, "tell %s Starting ICS analyze...\n", player);
+ appData.icsAnalyze = TRUE;
+ IcsAnalyze(TRUE);
+ }
+ SendToICS(temp);
+ return;
+ }
+ if (strncmp(command[forward+1].string, "stop", 4) == 0) {
+ if (gameMode != IcsObserving) {
+ sprintf(temp, "tell %s I must observe a game\n", player);
+ } else if (gameMode == IcsObserving) {
+ sprintf(temp, "tell %s Stopping ICS analyze...\n", player);
+ IcsAnalyze(FALSE);
+ }
+ SendToICS(temp);
+ return;
+ }
+ if (strncmp(command[forward+1].string, "output", 6) == 0) {
+ if(strncmp(command[forward+2].string, "whisper", 7) == 0) {
+ appData.icsAnalyzeOutPut = 1;
+ sprintf(temp, "tell %s analyze output is whisper\n", player);
+ } else if (strncmp(command[forward+2].string, "kibitz", 6) == 0) {
+ appData.icsAnalyzeOutPut = 2;
+ sprintf(temp, "tell %s analyze output is kibitz\n", player);
+ } else if (strncmp(command[forward+2].string, "tell", 4) == 0) {
+ appData.icsAnalyzeOutPut = 3;
+ if (command[forward+3].string[0] != NULLCHAR) {
+ if (strncmp(command[forward+3].string, "none", 4) == 0) {
+ appData.icsAnalyzeOutPut = 4;
+ sprintf(temp, "tell %s analyze output is: none\n", player);
+ SendToICS(temp);
+ return;
+ }
+ strcpy(appData.icsTells, command[forward+3].string);
+ sprintf(temp, "tell %s analyze output is tell: %s\n", player,
+ appData.icsTells);
+ } else {
+ sprintf(temp, "tell %s Error: I don't know where i should send my analysis\n", player);
+ appData.icsAnalyzeOutPut = 4;
+ }
+ }
+ SendToICS(temp);
+ }
+ if (strncmp(command[forward+1].string, "killpv", 6) == 0) {
+ i = atoi(command[forward+3].string);
+ j = atoi(command[forward+2].string);
+ if (i <= max_gamenum && i != 0) {
+ if (icsQueue[i].killPv == 0) {
+ sprintf(temp, "tell %s Error: Mh, killpv is zero. Wrong gamenumber?\n", player);
+ SendToICS(temp);
+ } else {
+ if ( j > 20 || j < 1) {
+ sprintf(temp, "tell %s Error: killpv must be 1-20\n", player);
+ } else {
+ sprintf(temp, "tell %s killpv for game %d is now %d\n", player, i, j);
+ icsQueue[i].killPv = j;
+ appData.icsEngineKillPV = TRUE;
+ }
+ SendToICS(temp);
+ }
+ } else {
+ sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
+ SendToICS(temp);
+ }
+ return;
+ }
+ if (strncmp(command[forward+1].string, "smartqueue", 10) == 0) {
+ j = atoi(command[forward+2].string);
+ if (j == 0 || j == 1) {
+ appData.smartQueue = j;
+ sprintf(temp, "tell %s smartqueue is now: %d", player, appData.smartQueue);
+ } else {
+ sprintf(temp, "tell %s Error: not possible smartquere value\n", player);
+ }
+ SendToICS(temp);
+ return;
+ }
+ if (strncmp(command[forward+1].string, "show", 4) == 0) {
+ i = atoi(command[forward+2].string);
+ if (i <= max_gamenum && i != 0) {
+ if (icsQueue[i].killPv == 0) {
+ sprintf(temp, "tell %s Error: emtpy game slot. Wrong gamenumber?\n", player);
+ SendToICS(temp);
+ } else {
+ sprintf(temp, "tell %s summary information about game: %d\n", player, i);
+ SendToICS(temp);
+ sprintf(temp, "tell %s enable killpv: %d, killpv value = %d\n", player,
+ appData.icsEngineKillPV, icsQueue[i].killPv);
+ SendToICS(temp);
+ sprintf(temp, "tell %s enable smartqueue (all games): %d\n", player, appData.smartQueue);
+ SendToICS(temp);
+ sprintf(temp, "tell %s analyze output (all games) is %d\n", player,
+ appData.icsAnalyzeOutPut);
+ SendToICS(temp);
+ }
+ } else {
+ sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
+ SendToICS(temp);
+ }
+ return;
+ }
+ }
+}
+
+void
+read_from_ics(isr, closure, data, count, error)
+ InputSourceRef isr;
+ VOIDSTAR closure;
+ char *data;
+ int count;
+ int error;
+{
+#define BUF_SIZE 8192
+#define STARTED_NONE 0
+#define STARTED_MOVES 1
+#define STARTED_BOARD 2
+#define STARTED_OBSERVE 3
+#define STARTED_HOLDINGS 4
+#define STARTED_CHATTER 5
+#define STARTED_COMMENT 6
+#define STARTED_MOVES_NOHIDE 7
+
+ static int started = STARTED_NONE;
+ static char parse[20000];
+ static int parse_pos = 0;
+ static char buf[BUF_SIZE + 1];
+ static int firstTime = TRUE, intfSet = FALSE;
+ static ColorClass curColor = ColorNormal;
+ static ColorClass prevColor = ColorNormal;
+ static int savingComment = FALSE;
+ char str[512];
+ int i, oldi;
+ int buf_len;
+ int next_out;
+ int tkind;
+ /* extra zippy vars */
+ int save;
+ char *q;
+ char *p = 0;
+ char *player;
+ char reply[MSG_SIZ];
+
+ if (count > 0) {
+ /* If last read ended with a partial line that we couldn't parse,
+ prepend it to the new read and try again. */
+ if (leftover_len > 0) {
+ for (i=0; i<leftover_len; i++)
+ buf[i] = buf[leftover_start + i];
+ }
+
+ /* Copy in new characters, removing nulls and \r's */
+ buf_len = leftover_len;
+ for (i = 0; i < count; i++) {
+ if (data[i] != NULLCHAR && data[i] != '\r')
+ buf[buf_len++] = data[i];
+ }
+
+ buf[buf_len] = NULLCHAR;
+ next_out = leftover_len;
+ leftover_start = 0;
+
+ i = 0;
+ while (i < buf_len) {
+ /* Deal with part of the TELNET option negotiation
+ protocol. We refuse to do anything beyond the
+ defaults, except that we allow the WILL ECHO option,
+ which ICS uses to turn off password echoing when we are
+ directly connected to it. We reject this option
+ if localLineEditing mode is on (always on in xboard)
+ and we are talking to port 23, which might be a real
+ telnet server that will try to keep WILL ECHO on permanently.
+ */
+ if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
+ static int remoteEchoOption = FALSE; /* telnet ECHO option */
+ unsigned char option;
+ oldi = i;
+ switch ((unsigned char) buf[++i]) {
+ case TN_WILL:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<WILL ");
+ switch (option = (unsigned char) buf[++i]) {
+ case TN_ECHO:
+ if (appData.debugMode)
+ fprintf(debugFP, "ECHO ");
+ /* Reply only if this is a change, according
+ to the protocol rules. */
+ if (remoteEchoOption) break;
+ if (appData.localLineEditing &&
+ atoi(appData.icsPort) == TN_PORT) {
+ TelnetRequest(TN_DONT, TN_ECHO);
+ } else {
+ EchoOff();
+ TelnetRequest(TN_DO, TN_ECHO);
+ remoteEchoOption = TRUE;
+ }
+ break;
+ default:
+ if (appData.debugMode)
+ fprintf(debugFP, "%d ", option);
+ /* Whatever this is, we don't want it. */
+ TelnetRequest(TN_DONT, option);
+ break;
+ }
+ break;
+ case TN_WONT:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<WONT ");
+ switch (option = (unsigned char) buf[++i]) {
+ case TN_ECHO:
+ if (appData.debugMode)
+ fprintf(debugFP, "ECHO ");
+ /* Reply only if this is a change, according
+ to the protocol rules. */
+ if (!remoteEchoOption) break;
+ EchoOn();
+ TelnetRequest(TN_DONT, TN_ECHO);
+ remoteEchoOption = FALSE;
+ break;
+ default:
+ if (appData.debugMode)
+ fprintf(debugFP, "%d ", (unsigned char) option);
+ /* Whatever this is, it must already be turned
+ off, because we never agree to turn on
+ anything non-default, so according to the
+ protocol rules, we don't reply. */
+ break;
+ }
+ break;
+ case TN_DO:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<DO ");
+ switch (option = (unsigned char) buf[++i]) {
+ default:
+ /* Whatever this is, we refuse to do it. */
+ if (appData.debugMode)
+ fprintf(debugFP, "%d ", option);
+ TelnetRequest(TN_WONT, option);
+ break;
+ }
+ break;
+ case TN_DONT:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<DONT ");
+ switch (option = (unsigned char) buf[++i]) {
+ default:
+ if (appData.debugMode)
+ fprintf(debugFP, "%d ", option);
+ /* Whatever this is, we are already not doing
+ it, because we never agree to do anything
+ non-default, so according to the protocol
+ rules, we don't reply. */
+ break;
+ }
+ break;
+ case TN_IAC:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<IAC ");
+ /* Doubled IAC; pass it through */
+ i--;
+ break;
+ default:
+ if (appData.debugMode)
+ fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
+ /* Drop all other telnet commands on the floor */
+ break;
+ }
+ if (oldi > next_out)
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ if (++i > next_out)
+ next_out = i;
+ continue;
+ }
+
+ /* OK, this at least will *usually* work */
+ if (!loggedOn && looking_at(buf, &i, "ics%")) {
+ loggedOn = TRUE;
+ }
+
+ if (loggedOn && !intfSet) {
+ if (ics_type == ICS_ICC) {
+ sprintf(str,
+ "/set-quietly interface %s\n/set-quietly style 12\n",
+ programVersion);
+
+ } else if (ics_type == ICS_CHESSNET) {
+ sprintf(str, "/style 12\n");
+ } else {
+ strcpy(str, "alias $ @\n$set interface ");
+ strcat(str, programVersion);
+ strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
+#ifdef WIN32
+ strcat(str, "$iset nohighlight 1\n");
+#endif
+ strcat(str, "$iset lock 1\n$style 12\n");
+ }
+ SendToICS(str);
+ intfSet = TRUE;
+ }
+
+ if (started == STARTED_COMMENT) {
+ /* Accumulate characters in comment */
+ parse[parse_pos++] = buf[i];
+ if (buf[i] == '\n') {
+ parse[parse_pos] = NULLCHAR;
+ AppendComment(forwardMostMove, StripHighlight(parse));
+ started = STARTED_NONE;
+ } else {
+ /* Don't match patterns against characters in chatter */
+ i++;
+ continue;
+ }
+ }
+ if (started == STARTED_CHATTER) {
+ if (buf[i] != '\n') {
+ /* Don't match patterns against characters in chatter */
+ i++;
+ continue;
+ }
+ started = STARTED_NONE;
+ }
+
+ /* Kludge to deal with rcmd protocol */
+ if (firstTime && looking_at(buf, &i, "\001*")) {
+ DisplayFatalError(&buf[1], 0, 1);
+ continue;
+ } else {
+ firstTime = FALSE;
+ }
+
+ if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
+ ics_type = ICS_ICC;
+ ics_prefix = "/";
+ if (appData.debugMode)
+ fprintf(debugFP, "ics_type %d\n", ics_type);
+ continue;
+ }
+ if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
+ ics_type = ICS_FICS;
+ ics_prefix = "$";
+ if (appData.debugMode)
+ fprintf(debugFP, "ics_type %d\n", ics_type);
+ continue;
+ }
+ if (!loggedOn && looking_at(buf, &i, "chess.net")) {
+ ics_type = ICS_CHESSNET;
+ ics_prefix = "/";
+ if (appData.debugMode)
+ fprintf(debugFP, "ics_type %d\n", ics_type);
+ continue;
+ }
+
+ if (!loggedOn &&
+ (looking_at(buf, &i, "\"*\" is *a registered name") ||
+ looking_at(buf, &i, "Logging you in as \"*\"") ||
+ looking_at(buf, &i, "will be \"*\""))) {
+ strcpy(ics_handle, star_match[0]);
+ continue;
+ }
+
+ if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
+ DisplayIcsInteractionTitle(buf);
+ have_set_title = TRUE;
+ }
+
+ /* skip finger notes */
+ if (started == STARTED_NONE &&
+ ((buf[i] == ' ' && isdigit(buf[i+1])) ||
+ (buf[i] == '1' && buf[i+1] == '0')) &&
+ buf[i+2] == ':' && buf[i+3] == ' ') {
+ started = STARTED_CHATTER;
+ i += 3;
+ continue;
+ }
+
+ /* skip formula vars */
+ if (started == STARTED_NONE &&
+ buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
+ started = STARTED_CHATTER;
+ i += 3;
+ continue;
+ }
+ oldi = i;
+
+ /* save position of char array pointer for zippy */
+ save = i;
+ /* Try found special things that never works with color */
+ /* I really don't know why - code is ok. */
+ q = p;
+
+ if (appData.zippyTalk || appData.zippyPlay) {
+ if (looking_at(buf, &save, "* tells you: *") ||
+ looking_at(buf, &save, "* says: *")) {
+ player = StripHighlightAndTitle(star_match[0]);
+ if (appData.zippyPassword[0] != NULLCHAR &&
+ strncmp(star_match[1], appData.zippyPassword,
+ strlen(appData.zippyPassword)) == 0) {
+ q = star_match[1] + strlen(appData.zippyPassword);
+ while (*q == ' ') q++;
+ fprintf(debugFP, "zippy 1 %s \n", q);
+ SendToICS(q);
+ SendToICS("\n");
+ } else if(appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
+ strncmp(star_match[1], appData.zippyPassword2,
+ strlen(appData.zippyPassword2)) == 0) {
+ fprintf(debugFP, "zippy2vor: %s \n", q);
+ q = star_match[1] + strlen(appData.zippyPassword2);
+ while (*q == ' ') q++;
+ SendToProgram(q, &first);
+ SendToProgram("\n", &first);
+
+ } else if (appData.zippyPassword3[0] != NULLCHAR && first.initDone &&
+ strncmp(star_match[1], appData.zippyPassword3,
+ strlen(appData.zippyPassword3)) == 0 &&
+ appData.userVersion == FALSE) {
+ q = star_match[1] + strlen(appData.zippyPassword3);
+ ParseZippyP3(q, player);
+
+ } else if (strncmp(star_match[1], "showinfo", 8) == 0 &&
+ appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
+ appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
+ q = star_match[1] + strlen("showinfo");
+ showInfo(q, player);
+ } else if (strncmp(star_match[1], "showgames", 9) == 0 &&
+ appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
+ appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
+ q = star_match[1];
+ showInfo(q, player);
+
+ } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
+ strncmp(star_match[1], appData.zippyWrongPassword,
+ strlen(appData.zippyWrongPassword)) == 0) {
+ q = star_match[1] + strlen(appData.zippyWrongPassword);
+ while (*q == ' ') q++;
+ sprintf(reply, "wrong %s\n", player);
+ SendToICS(reply);
+ }
+ }
+ /* workaround for icc and freechess.org */
+ if (looking_at(buf, &save, "Your opponent offers you a draw") ||
+ looking_at(buf, &save, "offers you a draw") ||
+ looking_at(buf, &save, "* offers you a draw")) {
+ if (first.sendDrawOffers && first.initDone) {
+ SendToProgram("draw\n", &first);
+ /* Handling zippy Draw */
+ } else if (appData.zippyDraw && first.initDone) {
+ //ZippyDraw(1, programStats.score, programStats.depth);
+ }
+ }
+ if (appData.zippyPlay && first.initDone && loggedOn == TRUE) ZippyMatch(buf, &save, player);
+ }
+
+ /* Make color for all and for zippy */
+ if (/* Don't color "message" or "messages" output */
+ (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
+ looking_at(buf, &i, "*. * at *:*: ") ||
+ looking_at(buf, &i, "--* (*:*): ") ||
+ /* Regular tells and says */
+ (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
+ looking_at(buf, &i, "* (your partner) tells you: ") ||
+ looking_at(buf, &i, "* says: ") ||
+ /* Message notifications (same color as tells) */
+ looking_at(buf, &i, "* has left a message ") ||
+ looking_at(buf, &i, "* just sent you a message:\n") ||
+ /* Whispers and kibitzes */
+ (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
+ looking_at(buf, &i, "* kibitzes: ") ||
+ /* Channel tells */
+ (tkind = 3, looking_at(buf, &i, "*(*: "))) {
+
+ if (tkind == 1 && strchr(star_match[0], ':')) {
+ /* Avoid "tells you:" spoofs in channels */
+ tkind = 3;
+ }
+ if (star_match[0][0] == NULLCHAR ||
+ strchr(star_match[0], ' ') ||
+ (tkind == 3 && strchr(star_match[1], ' '))) {
+ /* Reject bogus matches */
+ i = oldi;
+ } else {
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ switch (tkind) {
+ case 1:
+ Colorize(ColorTell, FALSE);
+ curColor = ColorTell;
+ break;
+ case 2:
+ Colorize(ColorKibitz, FALSE);
+ curColor = ColorKibitz;
+ break;
+ case 3:
+ p = strrchr(star_match[1], '(');
+ if (p == NULL) {
+ p = star_match[1];
+ } else {
+ p++;
+ }
+ if (atoi(p) == 1) {
+ Colorize(ColorChannel1, FALSE);
+ curColor = ColorChannel1;
+ } else {
+ Colorize(ColorChannel, FALSE);
+ curColor = ColorChannel;
+ }
+ break;
+ case 5:
+ curColor = ColorNormal;
+ break;
+ }
+ }
+ if (started == STARTED_NONE && appData.autoComment &&
+ (gameMode == IcsObserving ||
+ gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack)) {
+ parse_pos = i - oldi;
+ memcpy(parse, &buf[oldi], parse_pos);
+ parse[parse_pos] = NULLCHAR;
+ started = STARTED_COMMENT;
+ savingComment = TRUE;
+ } else {
+ started = STARTED_CHATTER;
+ savingComment = FALSE;
+ }
+ loggedOn = TRUE;
+ continue;
+ }
+ }
+
+ if (looking_at(buf, &i, "* s-shouts: ") ||
+ looking_at(buf, &i, "* c-shouts: ")) {
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorSShout, FALSE);
+ curColor = ColorSShout;
+ }
+ loggedOn = TRUE;
+ started = STARTED_CHATTER;
+ continue;
+ }
+
+ if (looking_at(buf, &i, "--->")) {
+ loggedOn = TRUE;
+ continue;
+ }
+
+ if (looking_at(buf, &i, "* shouts: ") ||
+ looking_at(buf, &i, "--> ")) {
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorShout, FALSE);
+ curColor = ColorShout;
+ }
+ loggedOn = TRUE;
+ started = STARTED_CHATTER;
+ continue;
+ }
+
+ if (looking_at( buf, &i, "Challenge:")) {
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorChallenge, FALSE);
+ curColor = ColorChallenge;
+ }
+ loggedOn = TRUE;
+ continue;
+ }
+
+ if (looking_at(buf, &i, "* offers you") ||
+ looking_at(buf, &i, "* offers to be") ||
+ looking_at(buf, &i, "* would like to") ||
+ looking_at(buf, &i, "* requests to") ||
+ looking_at(buf, &i, "Your opponent offers") ||
+ looking_at(buf, &i, "Your opponent requests")) {
+
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorRequest, FALSE);
+ curColor = ColorRequest;
+ }
+ continue;
+ }
+
+ if (looking_at(buf, &i, "* (*) seeking")) {
+ if (appData.colorize) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorSeek, FALSE);
+ curColor = ColorSeek;
+ }
+ continue;
+ }
+
+
+ if (looking_at(buf, &i, "\\ ")) {
+ if (prevColor != ColorNormal) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(prevColor, TRUE);
+ curColor = prevColor;
+ }
+ if (savingComment) {
+ parse_pos = i - oldi;
+ memcpy(parse, &buf[oldi], parse_pos);
+ parse[parse_pos] = NULLCHAR;
+ started = STARTED_COMMENT;
+ } else {
+ started = STARTED_CHATTER;
+ }
+ continue;
+ }
+
+ if (looking_at(buf, &i, "Black Strength :") ||
+ looking_at(buf, &i, "<<< style 10 board >>>") ||
+ looking_at(buf, &i, "<10>") ||
+ looking_at(buf, &i, "#@#")) {
+ /* Wrong board style */
+ loggedOn = TRUE;
+ SendToICS(ics_prefix);
+ SendToICS("set style 12\n");
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ continue;
+ }
+
+ if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
+ ICSInitScript();
+ have_sent_ICS_logon = 1;
+ continue;
+ }
+
+ if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
+ (looking_at(buf, &i, "\n<12> ") ||
+ looking_at(buf, &i, "<12> "))) {
+ loggedOn = TRUE;
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ }
+ next_out = i;
+ started = STARTED_BOARD;
+ parse_pos = 0;
+ continue;
+ }
+
+ if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
+ looking_at(buf, &i, "<b1> ")) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ }
+ next_out = i;
+ started = STARTED_HOLDINGS;
+ parse_pos = 0;
+ continue;
+ }
+
+ /* Send buf now to zippy */
+ if (appData.zippyTalk || appData.zippyPlay) {
+#if ZIPPY
+ if (ZippyControl(buf, &save) || ZippyConverse(buf, &save)) {
+ loggedOn = TRUE;
+ continue;
+ }
+#endif
+ }
+
+ /* ICS: init icsQueue */
+ if (appData.zippyPlay && first.initDone && gameMode == IcsObserving) {
+ if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
+ if (appData.debugMode) fprintf(debugFP, "To high gamenumber or gamenumber -1 !\n");
+ return;
+ }
+ if (icsQueue[ics_gamenum].killPv == 0) {
+ icsQueue[ics_gamenum].move = currentMove;
+ icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
+ icsQueue[ics_gamenum].counter = 0;
+ strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
+ strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
+ }
+ }
+
+ if (looking_at(buf, &i, "* *vs. * *--- *")) {
+ loggedOn = TRUE;
+ /* Header for a move list -- first line */
+ switch (ics_getting_history) {
+ case H_FALSE:
+ switch (gameMode) {
+ case IcsIdle:
+ case BeginningOfGame:
+ /* User typed "moves" or "oldmoves" while we
+ were idle. Pretend we asked for these
+ moves and soak them up so user can step
+ through them and/or save them.
+ */
+ Reset(FALSE, TRUE);
+ gameMode = IcsObserving;
+ ModeHighlight();
+ ics_gamenum = -1;
+ ics_getting_history = H_GOT_UNREQ_HEADER;
+ break;
+ case EditGame: /*?*/
+ case EditPosition: /*?*/
+ /* Should above feature work in these modes too? */
+ /* For now it doesn't */
+ ics_getting_history = H_GOT_UNWANTED_HEADER;
+ break;
+ default:
+ ics_getting_history = H_GOT_UNWANTED_HEADER;
+ break;
+ }
+ break;
+ case H_REQUESTED:
+ /* Is this the right one? */
+ if (gameInfo.white && gameInfo.black &&
+ strcmp(gameInfo.white, star_match[0]) == 0 &&
+ strcmp(gameInfo.black, star_match[2]) == 0) {
+ /* All is well */
+ ics_getting_history = H_GOT_REQ_HEADER;
+ }
+ break;
+ case H_GOT_REQ_HEADER:
+ case H_GOT_UNREQ_HEADER:
+ case H_GOT_UNWANTED_HEADER:
+ case H_GETTING_MOVES:
+ /* Should not happen */
+ DisplayError("Error gathering move list: two headers", 0);
+ ics_getting_history = H_FALSE;
+ break;
+ }
+
+ /* Save player ratings into gameInfo if needed */
+ if ((ics_getting_history == H_GOT_REQ_HEADER ||
+ ics_getting_history == H_GOT_UNREQ_HEADER) &&
+ (gameInfo.whiteRating == -1 ||
+ gameInfo.blackRating == -1)) {
+
+ 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",
+ gameInfo.whiteRating, gameInfo.blackRating);
+ }
+ continue;
+ }
+
+ if (looking_at(buf, &i,
+ "* * match, initial time: * minute*, increment: * second")) {
+ /* Header for a move list -- second line */
+ /* Initial board will follow if this is a wild game */
+
+ if (gameInfo.event != NULL) free(gameInfo.event);
+ sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
+ gameInfo.event = StrSave(str);
+ gameInfo.variant = StringToVariant(gameInfo.event);
+ continue;
+ }
+
+ if (looking_at(buf, &i, "Move ")) {
+ /* Beginning of a move list */
+ switch (ics_getting_history) {
+ case H_FALSE:
+ /* Normally should not happen */
+ /* Maybe user hit reset while we were parsing */
+ break;
+ case H_REQUESTED:
+ /* Happens if we are ignoring a move list that is not
+ * the one we just requested. Common if the user
+ * tries to observe two games without turning off
+ * getMoveList */
+ break;
+ case H_GETTING_MOVES:
+ /* Should not happen */
+ DisplayError("Error gathering move list: nested", 0);
+ ics_getting_history = H_FALSE;
+ break;
+ case H_GOT_REQ_HEADER:
+ ics_getting_history = H_GETTING_MOVES;
+ started = STARTED_MOVES;
+ parse_pos = 0;
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ }
+ break;
+ case H_GOT_UNREQ_HEADER:
+ ics_getting_history = H_GETTING_MOVES;
+ started = STARTED_MOVES_NOHIDE;
+ parse_pos = 0;
+ break;
+ case H_GOT_UNWANTED_HEADER:
+ ics_getting_history = H_FALSE;
+ break;
+ }
+ continue;
+ }
+
+ if (looking_at(buf, &i, "% ") ||
+ ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
+ && looking_at(buf, &i, "}*"))) {
+ savingComment = FALSE;
+ switch (started) {
+ case STARTED_MOVES:
+ case STARTED_MOVES_NOHIDE:
+ memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
+ parse[parse_pos + i - oldi] = NULLCHAR;
+ ParseGameHistory(parse);
+#if ZIPPY
+ if (appData.zippyPlay && first.initDone) {
+ if (appData.icsAnalyze && gameMode == IcsObserving) {
+ IcsAnalyze(TRUE);
+ }
+ /* icsAnalyze send the moves to engine if we start new */
+ if (!appData.icsAnalyze) FeedMovesToProgram(&first, forwardMostMove);
+ /* Bug 4.2.6: Engine want play, skip that */
+ if (gameMode == IcsExamining) {
+ /* set idle mode */
+ SendToProgram("force\n", &first);
+ }
+ if (gameMode == IcsPlayingWhite) {
+ if (appData.icsAnalyze) {
+ IcsAnalyze(FALSE);
+ appData.icsAnalyze = FALSE;
+
+ }
+ if (WhiteOnMove(forwardMostMove)) {
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("black\n", &first);
+ }
+ SendTimeRemaining(&first, TRUE);
+ }
+ if (first.useColors) {
+ SendToProgram("white\ngo\n", &first);
+ } else {
+ SendToProgram("go\n", &first);
+ }
+ first.maybeThinking = TRUE;
+ } else {
+ if (first.usePlayother) {
+ if (first.sendTime) {
+ SendTimeRemaining(&first, TRUE);
+ }
+ SendToProgram("playother\n", &first);
+ firstMove = FALSE;
+ } else {
+ firstMove = TRUE;
+ }
+ }
+ } else if (gameMode == IcsPlayingBlack) {
+ if (appData.icsAnalyze) {
+ IcsAnalyze(FALSE);
+ appData.icsAnalyze = FALSE;
+ SendToICS("refresh\n");
+ }
+ /* engineRoom stay forever */
+ if (!WhiteOnMove(forwardMostMove)) {
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("white\n", &first);
+ }
+ SendTimeRemaining(&first, FALSE);
+ }
+ if (first.useColors) {
+ SendToProgram("black\ngo\n", &first);
+ } else {
+ SendToProgram("go\n", &first);
+ }
+ first.maybeThinking = TRUE;
+ } else {
+ if (first.usePlayother) {
+ if (first.sendTime) {
+ SendTimeRemaining(&first, FALSE);
+ }
+ SendToProgram("playother\n", &first);
+ firstMove = FALSE;
+ } else {
+ firstMove = TRUE;
+ }
+ }
+ }
+
+#endif ZIPPY
+
+ }
+ if (gameMode == IcsObserving && ics_gamenum == -1) {
+ /* Moves came from oldmoves or moves command
+ while we weren't doing anything else.
+ */
+ currentMove = forwardMostMove;
+ ClearHighlights();/*!!could figure this out*/
+ flipView = appData.flipView;
+ DrawPosition(FALSE, boards[currentMove]);
+ DisplayBothClocks();
+ sprintf(str, "%s vs. %s",
+ gameInfo.white, gameInfo.black);
+ DisplayTitle(str);
+ gameMode = IcsIdle;
+ } else {
+ /* Moves were history of an active game */
+ if (gameInfo.resultDetails != NULL) {
+ free(gameInfo.resultDetails);
+ gameInfo.resultDetails = NULL;
+ }
+ }
+ HistorySet(parseList, backwardMostMove,
+ forwardMostMove, currentMove-1);
+ DisplayMove(currentMove - 1);
+ if (started == STARTED_MOVES) next_out = i;
+ started = STARTED_NONE;
+ ics_getting_history = H_FALSE;
+ break;
+
+ case STARTED_OBSERVE:
+ started = STARTED_NONE;
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ break;
+
+ default:
+ break;
+ }
+ continue;
+ }
+
+ if ((started == STARTED_MOVES || started == STARTED_BOARD ||
+ started == STARTED_HOLDINGS ||
+ started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
+ /* Accumulate characters in move list or board */
+ parse[parse_pos++] = buf[i];
+ }
+
+ /* Start of game messages. Mostly we detect start of game
+ when the first board image arrives. On some versions
+ of the ICS, though, we need to do a "refresh" after starting
+ to observe in order to get the current board right away. */
+ if (looking_at(buf, &i, "Adding game * to observation list")) {
+ started = STARTED_OBSERVE;
+ continue;
+ }
+
+ /* Handle auto-observe */
+ if (appData.autoObserve &&
+ (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
+ looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
+ char *player;
+ /* Choose the player that was highlighted, if any. */
+ if (star_match[0][0] == '\033' ||
+ star_match[1][0] != '\033') {
+ player = star_match[0];
+ } else {
+ player = star_match[2];
+ }
+ sprintf(str, "%sobserve %s\n",
+ ics_prefix, StripHighlightAndTitle(player));
+ SendToICS(str);
+
+ /* Save ratings from notify string */
+ strcpy(player1Name, star_match[0]);
+ player1Rating = string_to_rating(star_match[1]);
+ strcpy(player2Name, star_match[2]);
+ player2Rating = string_to_rating(star_match[3]);
+
+ if (appData.debugMode)
+ fprintf(debugFP,
+ "Ratings from 'Game notification:' %s %d, %s %d\n",
+ player1Name, player1Rating,
+ player2Name, player2Rating);
+
+ continue;
+ }
+
+ /* Deal with automatic examine mode after a game,
+ and with IcsObserving -> IcsExamining transition */
+ if (looking_at(buf, &i, "Entering examine mode for game *") ||
+ looking_at(buf, &i, "has made you an examiner of game *")) {
+
+ int gamenum = atoi(star_match[0]);
+ if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
+ gamenum == ics_gamenum) {
+ /* We were already playing or observing this game;
+ no need to refetch history */
+ gameMode = IcsExamining;
+ if (pausing) {
+ pauseExamForwardMostMove = forwardMostMove;
+ } else if (currentMove < forwardMostMove) {
+ ForwardInner(forwardMostMove);
+ }
+ } else {
+ /* I don't think this case really can happen */
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ }
+ continue;
+ }
+
+ /* Error messages */
+ if (ics_user_moved) {
+ if (looking_at(buf, &i, "Illegal move") ||
+ looking_at(buf, &i, "Not a legal move") ||
+ looking_at(buf, &i, "Your king is in check") ||
+ looking_at(buf, &i, "It isn't your turn") ||
+ looking_at(buf, &i, "It is not your move")) {
+ /* Illegal move */
+ ics_user_moved = 0;
+ if (forwardMostMove > backwardMostMove) {
+ currentMove = --forwardMostMove;
+ DisplayMove(currentMove - 1); /* before DMError */
+ DisplayMoveError("Illegal move (rejected by ICS)");
+ DrawPosition(FALSE, boards[currentMove]);
+ SwitchClocks();
+ DisplayBothClocks();
+ }
+ continue;
+ }
+ }
+
+ if (looking_at(buf, &i, "still have time") ||
+ looking_at(buf, &i, "not out of time") ||
+ looking_at(buf, &i, "either player is out of time") ||
+ looking_at(buf, &i, "has timeseal; checking")) {
+ /* We must have called his flag a little too soon */
+ whiteFlag = blackFlag = FALSE;
+ continue;
+ }
+
+ if (looking_at(buf, &i, "added * seconds to") ||
+ looking_at(buf, &i, "seconds were added to")) {
+ /* Update the clocks */
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ continue;
+ }
+
+ if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
+ ics_clock_paused = TRUE;
+ StopClocks();
+ continue;
+ }
+
+ if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
+ ics_clock_paused = FALSE;
+ StartClocks();
+ continue;
+ }
+
+ /* Grab player ratings from the Creating: message.
+ Note we have to check for the special case when
+ the ICS inserts things like [white] or [black]. */
+ if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
+ looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
+ /* star_matches:
+ 0 player 1 name (not necessarily white)
+ 1 player 1 rating
+ 2 empty, white, or black (IGNORED)
+ 3 player 2 name (not necessarily black)
+ 4 player 2 rating
+
+ The names/ratings are sorted out when the game
+ actually starts (below).
+ */
+ strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
+ player1Rating = string_to_rating(star_match[1]);
+ strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
+ player2Rating = string_to_rating(star_match[4]);
+
+ if (appData.debugMode)
+ fprintf(debugFP,
+ "Ratings from 'Creating:' %s %d, %s %d\n",
+ player1Name, player1Rating,
+ player2Name, player2Rating);
+
+ continue;
+ }
+
+ /* Improved generic start/end-of-game messages */
+ if (looking_at(buf, &i, "{Game * (* vs. *) *}*")) {
+ /* star_match[0] is the game number */
+ /* [1] is the white player's name */
+ /* [2] is the black player's name */
+ /* For end-of-game: */
+ /* [3] is the reason for the game end */
+ /* [4] is a PGN end game-token, preceded by " " */
+ /* For start-of-game: */
+ /* [3] begins with "Creating" or "Continuing" */
+ /* [4] is " *" or empty (don't care). */
+ int gamenum = atoi(star_match[0]);
+ char *why = star_match[3];
+ char *endtoken = star_match[4];
+ ChessMove endtype = (ChessMove) 0;
+
+ /* Game start messages */
+ if (strncmp(why, "Creating ", 9) == 0 ||
+ strncmp(why, "Continuing ", 11) == 0) {
+ gs_gamenum = gamenum;
+ strcpy(gs_kind, strchr(why, ' ') + 1);
+#if ZIPPY
+ if (appData.zippyPlay) {
+ ZippyGameStart(star_match[1], star_match[2]);
+ }
+#endif /*ZIPPY*/
+ continue;
+ }
+
+ /* Game end messages */
+ if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
+ ics_gamenum != gamenum) {
+ continue;
+ }
+ while (endtoken[0] == ' ') endtoken++;
+ switch (endtoken[0]) {
+ case '*':
+ default:
+ endtype = GameUnfinished;
+ break;
+ case '0':
+ endtype = BlackWins;
+ break;
+ case '1':
+ if (endtoken[1] == '/')
+ endtype = GameIsDrawn;
+ else
+ endtype = WhiteWins;
+ break;
+ }
+ GameEnds(endtype, why, GE_ICS);
+#if ZIPPY
+ if (appData.zippyPlay && first.initDone) {
+ ZippyGameEnd(endtype, why);
+ if (first.pr == NULL) {
+ /* Start the next process early so that we'll
+ be ready for the next challenge */
+ StartChessProgram(&first);
+ }
+ /* Send "new" early, in case this command takes
+ a long time to finish, so that we'll be ready
+ for the next challenge. */
+ Reset(TRUE, TRUE);
+ }
+#endif /*ZIPPY*/
+ continue;
+ }
+
+ if (looking_at(buf, &i, "Removing game * from observation") ||
+ looking_at(buf, &i, "no longer observing game *") ||
+ looking_at(buf, &i, "Game * (*) has no examiners")) {
+ if (gameMode == IcsObserving &&
+ atoi(star_match[0]) == ics_gamenum)
+ {
+ if (appData.icsAnalyze) IcsAnalyze(FALSE);
+ ResetIcsQueue(ics_gamenum);
+ StopClocks();
+ gameMode = IcsIdle;
+ ics_gamenum = -1;
+ ics_user_moved = FALSE;
+ }
+ continue;
+ }
+
+ if (looking_at(buf, &i, "no longer examining game *")) {
+ if (gameMode == IcsExamining &&
+ atoi(star_match[0]) == ics_gamenum)
+ {
+ if (appData.icsAnalyze) IcsAnalyze(FALSE);
+ ResetIcsQueue(ics_gamenum);
+ gameMode = IcsIdle;
+ ics_gamenum = -1;
+ ics_user_moved = FALSE;
+ }
+ continue;
+ }
+
+ /* Advance leftover_start past any newlines we find,
+ so only partial lines can get reparsed */
+ if (looking_at(buf, &i, "\n")) {
+ prevColor = curColor;
+ if (curColor != ColorNormal) {
+ if (oldi > next_out) {
+ SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = oldi;
+ }
+ Colorize(ColorNormal, FALSE);
+ curColor = ColorNormal;
+ }
+ if (started == STARTED_BOARD) {
+ started = STARTED_NONE;
+ parse[parse_pos] = NULLCHAR;
+ ParseBoard12(parse);
+ ics_user_moved = 0;
+
+ /* Send premove here */
+ if (appData.premove) {
+ char str[MSG_SIZ];
+ if (currentMove == 0 &&
+ gameMode == IcsPlayingWhite &&
+ appData.premoveWhite) {
+ sprintf(str, "%s%s\n", ics_prefix,
+ appData.premoveWhiteText);
+ if (appData.debugMode)
+ fprintf(debugFP, "Sending premove:\n");
+ SendToICS(str);
+ } else if (currentMove == 1 &&
+ gameMode == IcsPlayingBlack &&
+ appData.premoveBlack) {
+ sprintf(str, "%s%s\n", ics_prefix,
+ appData.premoveBlackText);
+ if (appData.debugMode)
+ fprintf(debugFP, "Sending premove:\n");
+ SendToICS(str);
+ } else if (gotPremove) {
+ gotPremove = 0;
+ ClearPremoveHighlights();
+ if (appData.debugMode)
+ fprintf(debugFP, "Sending premove:\n");
+ UserMoveEvent(premoveFromX, premoveFromY,
+ premoveToX, premoveToY,
+ premovePromoChar);
+ }
+ }
+
+ /* Usually suppress following prompt */
+ if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
+ if (looking_at(buf, &i, "*% ")) {
+ savingComment = FALSE;
+ }
+ }
+ next_out = i;
+ } else if (started == STARTED_HOLDINGS) {
+ int gamenum;
+ char new_piece[MSG_SIZ];
+ started = STARTED_NONE;
+ parse[parse_pos] = NULLCHAR;
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsing holdings: %s\n", parse);
+ if (sscanf(parse, " game %d", &gamenum) == 1 &&
+ gamenum == ics_gamenum) {
+ if (gameInfo.variant == VariantNormal) {
+ gameInfo.variant = VariantCrazyhouse; /*temp guess*/
+ /* Get a move list just to see the header, which
+ will tell us whether this is really bug or zh */
+ if (ics_getting_history == H_FALSE) {
+ ics_getting_history = H_REQUESTED;
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ SendToICS(str);
+ }
+ }
+ new_piece[0] = NULLCHAR;
+ sscanf(parse, "game %d white [%s black [%s <- %s",
+ &gamenum, white_holding, black_holding,
+ new_piece);
+ white_holding[strlen(white_holding)-1] = NULLCHAR;
+ black_holding[strlen(black_holding)-1] = NULLCHAR;
+#if ZIPPY
+ if (appData.zippyPlay && first.initDone) {
+ ZippyHoldings(white_holding, black_holding,
+ new_piece);
+ }
+#endif /*ZIPPY*/
+ if (tinyLayout || smallLayout) {
+ char wh[16], bh[16];
+ PackHolding(wh, white_holding);
+ PackHolding(bh, black_holding);
+ sprintf(str, "[%s-%s] %s-%s", wh, bh,
+ gameInfo.white, gameInfo.black);
+ } else {
+ sprintf(str, "%s [%s] vs. %s [%s]",
+ gameInfo.white, white_holding,
+ gameInfo.black, black_holding);
+ }
+ DrawPosition(FALSE, NULL);
+ DisplayTitle(str);
+ }
+ /* Suppress following prompt */
+ if (looking_at(buf, &i, "*% ")) {
+ savingComment = FALSE;
+ }
+ next_out = i;
+ }
+ continue;
+ }
+
+ i++; /* skip unparsed character and loop back */
+ }
+
+ if (started != STARTED_MOVES && started != STARTED_BOARD &&
+ started != STARTED_HOLDINGS && i > next_out) {
+ SendToPlayer(&buf[next_out], i - next_out);
+ next_out = i;
+ }
+
+ leftover_len = buf_len - leftover_start;
+ /* if buffer ends with something we couldn't parse,
+ reparse it after appending the next read */
+
+ } else if (count == 0) {
+ RemoveInputSource(isr);
+ DisplayFatalError("Connection closed by ICS", 0, 0);
+ } else {
+ DisplayFatalError("Error reading from ICS", error, 1);
+ }
+}
+
+
+/* Board style 12 looks like this:
+
+ <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0
+
+ * The "<12> " is stripped before it gets to this routine. The two
+ * trailing 0's (flip state and clock ticking) are later addition, and
+ * some chess servers may not have them, or may have only the first.
+ * Additional trailing fields may be added in the future.
+ */
+
+#define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
+
+#define RELATION_OBSERVING_PLAYED 0
+#define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
+#define RELATION_PLAYING_MYMOVE 1
+#define RELATION_PLAYING_NOTMYMOVE -1
+#define RELATION_EXAMINING 2
+#define RELATION_ISOLATED_BOARD -3
+#define RELATION_STARTING_POSITION -4 /* FICS only */
+
+void
+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 double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
+ int takeback, i;
+ char to_play, board_chars[72];
+ char move_str[500], str[500], elapsed_time[500];
+ char black[32], white[32];
+ Board board;
+ int prevMove = currentMove;
+ int ticking = 2;
+ ChessMove moveType;
+ int fromX, fromY, toX, toY;
+ char promoChar;
+
+ fromX = fromY = toX = toY = -1;
+
+ newGame = FALSE;
+
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsing board: %s\n", string);
+
+ move_str[0] = NULLCHAR;
+ elapsed_time[0] = NULLCHAR;
+ n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
+ &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
+ &gamenum, white, black, &relation, &basetime, &increment,
+ &white_stren, &black_stren, &white_time, &black_time,
+ &moveNum, str, elapsed_time, move_str, &ics_flip,
+ &ticking);
+
+ if (n < 22) {
+ sprintf(str, "Failed to parse board string:\n\"%s\"", string);
+ DisplayError(str, 0);
+ return;
+ }
+
+ /* Convert the move number to internal form */
+ moveNum = (moveNum - 1) * 2;
+ if (to_play == 'B') moveNum++;
+ if (moveNum >= MAX_MOVES) {
+ DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
+ 0, 1);
+ return;
+ }
+
+ switch (relation) {
+ case RELATION_OBSERVING_PLAYED:
+ case RELATION_OBSERVING_STATIC:
+ if (gamenum == -1) {
+ /* Old ICC buglet */
+ relation = RELATION_OBSERVING_STATIC;
+ }
+ newGameMode = IcsObserving;
+ break;
+ case RELATION_PLAYING_MYMOVE:
+ case RELATION_PLAYING_NOTMYMOVE:
+ newGameMode =
+ ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
+ IcsPlayingWhite : IcsPlayingBlack;
+ break;
+ case RELATION_EXAMINING:
+ newGameMode = IcsExamining;
+ break;
+ case RELATION_ISOLATED_BOARD:
+ default:
+ /* Just display this board. If user was doing something else,
+ we will forget about it until the next board comes. */
+ newGameMode = IcsIdle;
+ break;
+ case RELATION_STARTING_POSITION:
+ newGameMode = gameMode;
+ /* if we switch to a new board start IcsAnalyze */
+ if(appData.icsAnalyze) IcsAnalyze(TRUE);
+ break;
+ }
+
+ /* Modify behavior for initial board display on move listing
+ of wild games.
+ */
+ switch (ics_getting_history) {
+ case H_FALSE:
+ case H_REQUESTED:
+ break;
+ case H_GOT_REQ_HEADER:
+ case H_GOT_UNREQ_HEADER:
+ /* This is the initial position of the current game */
+ gamenum = ics_gamenum;
+ moveNum = 0; /* old ICS bug workaround */
+ if (to_play == 'B') {
+ startedFromSetupPosition = TRUE;
+ blackPlaysFirst = TRUE;
+ moveNum = 1;
+ if (forwardMostMove == 0) forwardMostMove = 1;
+ if (backwardMostMove == 0) backwardMostMove = 1;
+ if (currentMove == 0) currentMove = 1;
+ }
+ newGameMode = gameMode;
+ relation = RELATION_STARTING_POSITION; /* ICC needs this */
+ break;
+ case H_GOT_UNWANTED_HEADER:
+ /* This is an initial board that we don't want */
+ return;
+ case H_GETTING_MOVES:
+ /* Should not happen */
+ DisplayError("Error gathering move list: extra board", 0);
+ ics_getting_history = H_FALSE;
+ return;
+ }
+
+ /* Take action if this is the first board of a new game, or of a
+ different game than is currently being displayed. */
+ if (gamenum != ics_gamenum || newGameMode != gameMode ||
+ relation == RELATION_ISOLATED_BOARD) {
+
+ /* Forget the old game and get the history (if any) of the new one */
+ if (gameMode != BeginningOfGame) {
+ Reset(FALSE, TRUE);
+ }
+ newGame = TRUE;
+ if (appData.autoRaiseBoard) BoardToTop();
+ prevMove = -3;
+ if (gamenum == -1) {
+ newGameMode = IcsIdle;
+ } else if (moveNum > 0 && newGameMode != IcsIdle &&
+ appData.getMoveList) {
+ /* Need to get game history */
+ ics_getting_history = H_REQUESTED;
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ SendToICS(str);
+ }
+
+
+ /* Initially flip the board to have black on the bottom if playing
+ black or if the ICS flip flag is set, but let the user change
+ it with the Flip View button. */
+ flipView = appData.autoFlipView ?
+ (newGameMode == IcsPlayingBlack) || ics_flip :
+ appData.flipView;
+
+ /* Done with values from previous mode; copy in new ones */
+ gameMode = newGameMode;
+ ModeHighlight();
+ ics_gamenum = gamenum;
+ if (gamenum == gs_gamenum) {
+ int klen = strlen(gs_kind);
+ if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
+ sprintf(str, "ICS %s", gs_kind);
+ gameInfo.event = StrSave(str);
+ } else {
+ gameInfo.event = StrSave("ICS game");
+ }
+ gameInfo.site = StrSave(appData.icsHost);
+ gameInfo.date = PGNDate();
+ gameInfo.round = StrSave("-");
+ gameInfo.white = StrSave(white);
+ gameInfo.black = StrSave(black);
+ timeControl = basetime * 60 * 1000;
+ timeIncrement = increment * 1000;
+ movesPerSession = 0;
+ gameInfo.timeControl = TimeControlTagValue();
+ gameInfo.variant = StringToVariant(gameInfo.event);
+
+ /* Do we have the ratings? */
+ if (strcmp(player1Name, white) == 0 &&
+ strcmp(player2Name, black) == 0) {
+ if (appData.debugMode)
+ fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
+ player1Rating, player2Rating);
+ gameInfo.whiteRating = player1Rating;
+ gameInfo.blackRating = player2Rating;
+ } else if (strcmp(player2Name, white) == 0 &&
+ strcmp(player1Name, black) == 0) {
+ if (appData.debugMode)
+ fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
+ player2Rating, player1Rating);
+ gameInfo.whiteRating = player2Rating;
+ gameInfo.blackRating = player1Rating;
+ }
+ player1Name[0] = player2Name[0] = NULLCHAR;
+
+ /* Silence shouts if requested */
+ if (appData.quietPlay &&
+ (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
+ SendToICS(ics_prefix);
+ SendToICS("set shout 0\n");
+ }
+ }
+
+ /* Deal with midgame name changes */
+ if (!newGame) {
+ if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
+ if (gameInfo.white) free(gameInfo.white);
+ gameInfo.white = StrSave(white);
+ }
+ if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
+ if (gameInfo.black) free(gameInfo.black);
+ gameInfo.black = StrSave(black);
+ }
+ }
+
+ /* Throw away game result if anything actually changes in examine mode */
+ if (gameMode == IcsExamining && !newGame) {
+ gameInfo.result = GameUnfinished;
+ if (gameInfo.resultDetails != NULL) {
+ free(gameInfo.resultDetails);
+ gameInfo.resultDetails = NULL;
+ }
+ }
+
+ /* In pausing && IcsExamining mode, we ignore boards coming
+ in if they are in a different variation than we are. */
+ if (pauseExamInvalid) return;
+ if (pausing && gameMode == IcsExamining) {
+ if (moveNum <= pauseExamForwardMostMove) {
+ pauseExamInvalid = TRUE;
+ forwardMostMove = pauseExamForwardMostMove;
+ return;
+ }
+ }
+
+ /* Parse the board */
+ for (k = 0; k < 8; k++)
+ for (j = 0; j < 8; j++)
+ board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
+ CopyBoard(boards[moveNum], board);
+ if (moveNum == 0) {
+ startedFromSetupPosition =
+ !CompareBoards(board, initialPosition);
+ }
+
+ if (ics_getting_history == H_GOT_REQ_HEADER ||
+ ics_getting_history == H_GOT_UNREQ_HEADER) {
+ /* This was an initial position from a move list, not
+ the current position */
+ return;
+ }
+
+ /* Update currentMove and known move number limits */
+ newMove = newGame || moveNum > forwardMostMove;
+
+
+ /* If we found takebacks during IcsAnalyze try send to engine */
+ if (!newGame && appData.icsAnalyze && first.analyzing &&
+ moveNum < forwardMostMove) {
+ if (appData.debugMode) fprintf(debugFP, "take back move\n");
+ takeback = forwardMostMove - moveNum;
+ if (!appData.icsWBprotoAgr) {
+ /* safty first */
+ SendToProgram("exit\n", &first);
+ SendToProgram("force\n", &first);
+ for (i = 0; i < takeback; i++) {
+ SendToProgram("undo\n", &first);
+ /* Some engine need analyze and stat again */
+ }
+ SendToProgram("analyze\n", &first);
+ } else {
+ /* hard */
+ IcsAnalyze(FALSE);
+ IcsAnalyze(TRUE);
+ }
+ }
+ if (newGame) {
+ forwardMostMove = backwardMostMove = currentMove = moveNum;
+ if (gameMode == IcsExamining && moveNum == 0) {
+ /* Workaround for ICS limitation: we are not told the wild
+ type when starting to examine a game. But if we ask for
+ the move list, the move list header will tell us */
+ ics_getting_history = H_REQUESTED;
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ SendToICS(str);
+ }
+ } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
+ || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
+ forwardMostMove = moveNum;
+ if (!pausing || currentMove > forwardMostMove)
+ currentMove = forwardMostMove;
+ } else {
+ /* New part of history that is not contiguous with old part */
+ if (pausing && gameMode == IcsExamining) {
+ pauseExamInvalid = TRUE;
+ forwardMostMove = pauseExamForwardMostMove;
+ return;
+ }
+ forwardMostMove = backwardMostMove = currentMove = moveNum;
+ if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
+ ics_getting_history = H_REQUESTED;
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ SendToICS(str);
+ }
+ }
+
+ {
+ int i = 0;
+ /* Update the clocks */
+ if (strchr(elapsed_time, '.')) {
+ /* Time is in ms */
+ timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
+ timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
+ } else {
+ /* Time is in seconds */
+ i = 1;
+ timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
+ timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
+ }
+ }
+
+ /* Time workaround for ICC - send only sec :( */
+ if (ics_type == ICS_ICC) {
+ if (timeRemaining[0][moveNum] >= 1500) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 1500;
+ if (timeRemaining[1][moveNum] >= 1500) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 1500;
+
+ if (timeRemaining[0][moveNum] == 1000) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 950;
+ if (timeRemaining[0][moveNum] == 1000) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 950;
+
+ }
+
+#if ZIPPY
+ if (appData.zippyPlay && newGame &&
+ gameMode != IcsObserving && gameMode != IcsIdle &&
+ gameMode != IcsExamining)
+ ZippyFirstBoard(moveNum, basetime, increment);
+#endif
+
+ /* Put the move on the move list, first converting
+ to canonical algebraic form. */
+ if (moveNum > 0) {
+ if (moveNum <= backwardMostMove) {
+ /* We don't know what the board looked like before
+ this move. Punt. */
+ strcpy(parseList[moveNum - 1], move_str);
+ 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)) {
+ (void) CoordsToAlgebraic(boards[moveNum - 1],
+ PosFlags(moveNum - 1), EP_UNKNOWN,
+ fromY, fromX, toY, toX, promoChar,
+ parseList[moveNum-1]);
+ switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
+ case MT_NONE:
+ case MT_STALEMATE:
+ default:
+ break;
+ case MT_CHECK:
+ strcat(parseList[moveNum - 1], "+");
+ break;
+ case MT_CHECKMATE:
+ strcat(parseList[moveNum - 1], "#");
+ break;
+ }
+ strcat(parseList[moveNum - 1], " ");
+ strcat(parseList[moveNum - 1], elapsed_time);
+ /* 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 {
+ /* Move from ICS was illegal!? Punt. */
+#if 0
+ if (appData.testLegality && appData.debugMode) {
+ sprintf(str, "Illegal move \"%s\" from ICS", move_str);
+ DisplayError(str, 0);
+ }
+#endif
+ strcpy(parseList[moveNum - 1], move_str);
+ strcat(parseList[moveNum - 1], " ");
+ strcat(parseList[moveNum - 1], elapsed_time);
+ moveList[moveNum - 1][0] = NULLCHAR;
+ fromX = fromY = toX = toY = -1;
+ }
+
+#if ZIPPY
+ /* Send move to chess program (BEFORE animating it). */
+ if (appData.zippyPlay && !newGame && newMove &&
+ (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
+
+ if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
+ (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
+ if (moveList[moveNum - 1][0] == NULLCHAR) {
+ sprintf(str, "Couldn't parse move \"%s\" from ICS",
+ move_str);
+ DisplayError(str, 0);
+ } else {
+ if (first.sendTime) {
+ SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
+ }
+ SendMoveToProgram(moveNum - 1, &first);
+ if (firstMove) {
+ firstMove = FALSE;
+ if (first.useColors) {
+ SendToProgram(gameMode == IcsPlayingWhite ?
+ "white\ngo\n" :
+ "black\ngo\n", &first);
+ } else {
+ SendToProgram("go\n", &first);
+ }
+ first.maybeThinking = TRUE;
+ }
+ }
+ } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
+ if (moveList[moveNum - 1][0] == NULLCHAR) {
+ sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
+ DisplayError(str, 0);
+ } else {
+ SendMoveToProgram(moveNum - 1, &first);
+ }
+ }
+ }
+#endif
+ }
+
+ if (moveNum > 0 && !gotPremove) {
+ /* If move comes from a remote source, animate it. If it
+ isn't remote, it will have already been animated. */
+ if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
+ AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
+ }
+ if (!pausing && appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ }
+ }
+
+ /* Start the clocks */
+ whiteFlag = blackFlag = FALSE;
+ appData.clockMode = !(basetime == 0 && increment == 0);
+ if (ticking == 0) {
+ ics_clock_paused = TRUE;
+ StopClocks();
+ } else if (ticking == 1) {
+ ics_clock_paused = FALSE;
+ }
+ if (gameMode == IcsIdle ||
+ relation == RELATION_OBSERVING_STATIC ||
+ relation == RELATION_EXAMINING ||
+ ics_clock_paused)
+ DisplayBothClocks();
+ else
+ StartClocks();
+
+ /* Display opponents and material strengths */
+ if (gameInfo.variant != VariantBughouse &&
+ gameInfo.variant != VariantCrazyhouse) {
+ if (tinyLayout || smallLayout) {
+ sprintf(str, "%s(%d) %s(%d) {%d %d}",
+ gameInfo.white, white_stren, gameInfo.black, black_stren,
+ basetime, increment);
+ } else {
+ sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
+ gameInfo.white, white_stren, gameInfo.black, black_stren,
+ basetime, increment);
+ }
+ DisplayTitle(str);
+ }
+
+
+ /* Display the board */
+ if (!pausing) {
+
+ if (appData.premove)
+ if (!gotPremove ||
+ ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
+ ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
+ ClearPremoveHighlights();
+
+ DrawPosition(FALSE, boards[currentMove]);
+ DisplayMove(moveNum - 1);
+ if (appData.ringBellAfterMoves && !ics_user_moved)
+ RingBell();
+ }
+
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+}
+
+void
+GetMoveListEvent()
+{
+ char buf[MSG_SIZ];
+ if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
+ ics_getting_history = H_REQUESTED;
+ sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
+ SendToICS(buf);
+ }
+}
+
+void
+AnalysisPeriodicEvent(force)
+ int force;
+{
+ /* WB engine room */
+ if (appData.AnalysisWindow && programStats.depth > 2) {
+ /* GUI disable send stat ? */
+ if (!appData.engineStatLine) SendToProgram(".\n", &first);
+ /* don't support Stats on game ?*/
+ if (supportStat == 0) {
+ /* call Display every sec for time and nodes */
+ DisplayAnalysis(1,0);
+ } else {
+ /* GUI makes time ???? */
+ /* at the moment: yes! */
+ DisplayAnalysis(1,0);
+ }
+ } else if (appData.icsAnalyze && programStats.depth > 2) {
+ SendToProgram(".\n", &first);
+ DisplayAnalysis(1,0);
+ } else if (((programStats.ok_to_send == 0 || programStats.line_is_book)
+ && !force) || !appData.periodicUpdates)
+ return;
+
+ /* Send . command to Crafty to collect stats */
+ if (!appData.AnalysisWindow && (gameMode == AnalyzeMode ||
+ gameMode == AnalyzeFile)) SendToProgram(".\n", &first);
+
+ /* Don't send another until we get a response (this makes
+ us stop sending to old Crafty's which don't understand
+ the "." command (sending illegal cmds resets node count & time,
+ which looks bad)) */
+ programStats.ok_to_send = 0;
+}
+
+void
+SendMoveToProgram(moveNum, cps)
+ int moveNum;
+ ChessProgramState *cps;
+{
+ char buf[MSG_SIZ];
+ if (cps->useUsermove) {
+ SendToProgram("usermove ", cps);
+ }
+ if (cps->useSAN) {
+ char *space;
+ if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
+ int len = space - parseList[moveNum];
+ memcpy(buf, parseList[moveNum], len);
+ buf[len++] = '\n';
+ buf[len] = NULLCHAR;
+ } else {
+ sprintf(buf, "%s\n", parseList[moveNum]);
+ }
+ SendToProgram(buf, cps);
+ } else {
+ SendToProgram(moveList[moveNum], cps);
+ }
+}
+
+void
+SendMoveToICS(moveType, fromX, fromY, toX, toY)
+ ChessMove moveType;
+ int fromX, fromY, toX, toY;
+{
+ char user_move[MSG_SIZ];
+
+ switch (moveType) {
+ default:
+ 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;
+ case WhiteKingSideCastle:
+ case BlackKingSideCastle:
+ case WhiteQueenSideCastleWild:
+ case BlackQueenSideCastleWild:
+ sprintf(user_move, "o-o\n");
+ break;
+ case WhiteQueenSideCastle:
+ case BlackQueenSideCastle:
+ case WhiteKingSideCastleWild:
+ case BlackKingSideCastleWild:
+ sprintf(user_move, "o-o-o\n");
+ break;
+ case WhitePromotionQueen:
+ case BlackPromotionQueen:
+ case WhitePromotionRook:
+ case BlackPromotionRook:
+ case WhitePromotionBishop:
+ case BlackPromotionBishop:
+ case WhitePromotionKnight:
+ case BlackPromotionKnight:
+ case WhitePromotionKing:
+ case BlackPromotionKing:
+ sprintf(user_move, "%c%c%c%c=%c\n",
+ 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
+ PieceToChar(PromoPiece(moveType)));
+ break;
+ case WhiteDrop:
+ case BlackDrop:
+ sprintf(user_move, "%c@%c%c\n",
+ ToUpper(PieceToChar((ChessSquare) fromX)),
+ 'a' + toX, '1' + toY);
+ break;
+ case NormalMove:
+ case WhiteCapturesEnPassant:
+ case BlackCapturesEnPassant:
+ case IllegalMove: /* could be a variant we don't quite understand */
+ sprintf(user_move, "%c%c%c%c\n",
+ 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
+ break;
+ }
+ SendToICS(user_move);
+}
+
+void
+CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
+ int rf, ff, rt, ft;
+ char promoChar;
+ char move[7];
+{
+ if (rf == DROP_RANK) {
+ sprintf(move, "%c@%c%c\n",
+ ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
+ } else {
+ if (promoChar == 'x' || promoChar == NULLCHAR) {
+ sprintf(move, "%c%c%c%c\n",
+ 'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
+ } else {
+ sprintf(move, "%c%c%c%c%c\n",
+ 'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
+ }
+ }
+}
+
+void
+ProcessICSInitScript(f)
+ FILE *f;
+{
+ char buf[MSG_SIZ];
+
+ while (fgets(buf, MSG_SIZ, f)) {
+ SendToICSDelayed(buf,(long)appData.msLoginDelay);
+ }
+
+ fclose(f);
+}
+
+
+/* Parser for moves from gnuchess, ICS, or user typein box */
+Boolean
+ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
+ char *move;
+ int moveNum;
+ ChessMove *moveType;
+ int *fromX, *fromY, *toX, *toY;
+ char *promoChar;
+{
+ *moveType = yylexstr(moveNum, move);
+ switch (*moveType) {
+ case WhitePromotionQueen:
+ case BlackPromotionQueen:
+ case WhitePromotionRook:
+ case BlackPromotionRook:
+ case WhitePromotionBishop:
+ case BlackPromotionBishop:
+ case WhitePromotionKnight:
+ case BlackPromotionKnight:
+ case WhitePromotionKing:
+ case BlackPromotionKing:
+ case NormalMove:
+ case WhiteCapturesEnPassant:
+ case BlackCapturesEnPassant:
+ case WhiteKingSideCastle:
+ case WhiteQueenSideCastle:
+ case BlackKingSideCastle:
+ case BlackQueenSideCastle:
+ case WhiteKingSideCastleWild:
+ case WhiteQueenSideCastleWild:
+ case BlackKingSideCastleWild:
+ case BlackQueenSideCastleWild:
+ case IllegalMove: /* bug or odd chess variant */
+ *fromX = currentMoveString[0] - 'a';
+ *fromY = currentMoveString[1] - '1';
+ *toX = currentMoveString[2] - 'a';
+ *toY = currentMoveString[3] - '1';
+ *promoChar = currentMoveString[4];
+ if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
+ *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
+ *fromX = *fromY = *toX = *toY = 0;
+ return FALSE;
+ }
+ if (appData.testLegality) {
+ return (*moveType != IllegalMove);
+ } else {
+ return !(fromX == fromY && toX == toY);
+ }
+
+ case WhiteDrop:
+ case BlackDrop:
+ *fromX = *moveType == WhiteDrop ?
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :
+ (int) CharToPiece(ToLower(currentMoveString[0]));
+ *fromY = DROP_RANK;
+ *toX = currentMoveString[2] - 'a';
+ *toY = currentMoveString[3] - '1';
+ *promoChar = NULLCHAR;
+ return TRUE;
+
+ case AmbiguousMove:
+ case ImpossibleMove:
+ case (ChessMove) 0: /* end of file */
+ case ElapsedTime:
+ case Comment:
+ case PGNTag:
+ case NAG:
+ case WhiteWins:
+ case BlackWins:
+ case GameIsDrawn:
+ default:
+ /* bug? */
+ *fromX = *fromY = *toX = *toY = 0;
+ *promoChar = NULLCHAR;
+ return FALSE;
+ }
+}
+
+
+void
+InitPosition(redraw)
+ int redraw;
+{
+ currentMove = forwardMostMove = backwardMostMove = 0;
+ switch (gameInfo.variant) {
+ default:
+ CopyBoard(boards[0], initialPosition);
+ break;
+ case VariantTwoKings:
+ CopyBoard(boards[0], twoKingsPosition);
+ startedFromSetupPosition = TRUE;
+ break;
+ case VariantWildCastle:
+ CopyBoard(boards[0], initialPosition);
+ /* !!?shuffle with kings guaranteed to be on d or e file */
+ break;
+ case VariantNoCastle:
+ CopyBoard(boards[0], initialPosition);
+ /* !!?unconstrained back-rank shuffle */
+ break;
+ case VariantFischeRandom:
+ CopyBoard(boards[0], initialPosition);
+ /* !!shuffle according to FR rules */
+ break;
+ }
+ if (redraw)
+ DrawPosition(FALSE, boards[currentMove]);
+}
+
+void
+SendBoard(cps, moveNum)
+ ChessProgramState *cps;
+ int moveNum;
+{
+ char message[MSG_SIZ];
+
+ if (cps->useSetboard) {
+ char* fen = PositionToFEN(moveNum);
+ sprintf(message, "setboard %s\n", fen);
+ SendToProgram(message, cps);
+ free(fen);
+
+ } else {
+ ChessSquare *bp;
+ int i, j;
+ /* Kludge to set black to move, avoiding the troublesome and now
+ * deprecated "black" command.
+ */
+ if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
+
+ SendToProgram("edit\n", cps);
+ SendToProgram("#\n", cps);
+ for (i = BOARD_SIZE - 1; i >= 0; i--) {
+ bp = &boards[moveNum][i][0];
+ for (j = 0; j < BOARD_SIZE; j++, bp++) {
+ if ((int) *bp < (int) BlackPawn) {
+ sprintf(message, "%c%c%c\n", PieceToChar(*bp),
+ 'a' + j, '1' + i);
+ SendToProgram(message, cps);
+ }
+ }
+ }
+
+ SendToProgram("c\n", cps);
+ for (i = BOARD_SIZE - 1; i >= 0; i--) {
+ bp = &boards[moveNum][i][0];
+ for (j = 0; j < BOARD_SIZE; j++, bp++) {
+ if (((int) *bp != (int) EmptySquare)
+ && ((int) *bp >= (int) BlackPawn)) {
+ sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
+ 'a' + j, '1' + i);
+ SendToProgram(message, cps);
+ }
+ }
+ }
+
+ SendToProgram(".\n", cps);
+ }
+}
+
+int
+IsPromotion(fromX, fromY, toX, toY)
+ int fromX, fromY, toX, toY;
+{
+ return gameMode != EditPosition &&
+ fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
+ ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
+ (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
+}
+
+
+int
+PieceForSquare (x, y)
+ int x;
+ int y;
+{
+ if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
+ return -1;
+ else
+ return boards[currentMove][y][x];
+}
+
+int
+OKToStartUserMove(x, y)
+ int x, y;
+{
+ ChessSquare from_piece;
+ int white_piece;
+
+ if (matchMode) return FALSE;
+ if (gameMode == EditPosition) return TRUE;
+
+ if (x >= 0 && y >= 0)
+ from_piece = boards[currentMove][y][x];
+ else
+ from_piece = EmptySquare;
+
+ if (from_piece == EmptySquare) return FALSE;
+
+ white_piece = (int)from_piece >= (int)WhitePawn &&
+ (int)from_piece <= (int)WhiteKing;
+
+ switch (gameMode) {
+ case PlayFromGameFile:
+ case AnalyzeFile:
+ case TwoMachinesPlay:
+ case EndOfGame:
+ return FALSE;
+
+ case IcsObserving:
+ case IcsIdle:
+ return FALSE;
+
+ case MachinePlaysWhite:
+ case IcsPlayingBlack:
+ if (appData.zippyPlay) return FALSE;
+ if (white_piece) {
+ DisplayMoveError("You are playing Black");
+ return FALSE;
+ }
+ break;
+
+ case MachinePlaysBlack:
+ case IcsPlayingWhite:
+ if (appData.zippyPlay) return FALSE;
+ if (!white_piece) {
+ DisplayMoveError("You are playing White");
+ return FALSE;
+ }
+ break;
+
+ case EditGame:
+ if (!white_piece && WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is White's turn");
+ return FALSE;
+ }
+ if (white_piece && !WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is Black's turn");
+ return FALSE;
+ }
+ if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
+ /* Editing correspondence game history */
+ /* Could disallow this or prompt for confirmation */
+ cmailOldMove = -1;
+ }
+ if (currentMove < forwardMostMove) {
+ /* Discarding moves */
+ /* Could prompt for confirmation here,
+ but I don't think that's such a good idea */
+ forwardMostMove = currentMove;
+ }
+ break;
+
+ case BeginningOfGame:
+ if (appData.icsActive) return FALSE;
+ if (!appData.noChessProgram) {
+ if (!white_piece) {
+ DisplayMoveError("You are playing White");
+ return FALSE;
+ }
+ }
+ break;
+
+ case Training:
+ if (!white_piece && WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is White's turn");
+ return FALSE;
+ }
+ if (white_piece && !WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is Black's turn");
+ return FALSE;
+ }
+ break;
+
+ default:
+ case IcsExamining:
+ break;
+ }
+ if (currentMove != forwardMostMove && gameMode != AnalyzeMode
+ && gameMode != AnalyzeFile && gameMode != Training) {
+ DisplayMoveError("Displayed position is not current");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
+int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
+int lastLoadGameUseList = FALSE;
+char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
+ChessMove lastLoadGameStart = (ChessMove) 0;
+
+
+void
+UserMoveEvent(fromX, fromY, toX, toY, promoChar)
+ int fromX, fromY, toX, toY;
+ int promoChar;
+{
+ ChessMove moveType;
+
+ if (fromX < 0 || fromY < 0) return;
+ if ((fromX == toX) && (fromY == toY)) {
+ return;
+ }
+
+ /* Check if the user is playing in turn. This is complicated because we
+ let the user "pick up" a piece before it is his turn. So the piece he
+ tried to pick up may have been captured by the time he puts it down!
+ Therefore we use the color the user is supposed to be playing in this
+ test, not the color of the piece that is currently on the starting
+ square---except in EditGame mode, where the user is playing both
+ sides; fortunately there the capture race can't happen. (It can
+ now happen in IcsExamining mode, but that's just too bad. The user
+ will get a somewhat confusing message in that case.)
+ */
+
+ switch (gameMode) {
+ case PlayFromGameFile:
+ case AnalyzeFile:
+ case TwoMachinesPlay:
+ case EndOfGame:
+ case IcsObserving:
+ case IcsIdle:
+ /* We switched into a game mode where moves are not accepted,
+ perhaps while the mouse button was down. */
+ return;
+
+ case MachinePlaysWhite:
+ /* User is moving for Black */
+ if (WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is White's turn");
+ return;
+ }
+ break;
+
+ case MachinePlaysBlack:
+ /* User is moving for White */
+ if (!WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is Black's turn");
+ return;
+ }
+ break;
+
+ case EditGame:
+ case IcsExamining:
+ case BeginningOfGame:
+ case AnalyzeMode:
+ case Training:
+ if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
+ (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
+ /* User is moving for Black */
+ if (WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is White's turn");
+ return;
+ }
+ } else {
+ /* User is moving for White */
+ if (!WhiteOnMove(currentMove)) {
+ DisplayMoveError("It is Black's turn");
+ return;
+ }
+ }
+ break;
+
+ case IcsPlayingBlack:
+ /* User is moving for Black */
+ if (WhiteOnMove(currentMove)) {
+ if (!appData.premove) {
+ DisplayMoveError("It is White's turn");
+ } else if (toX >= 0 && toY >= 0) {
+ premoveToX = toX;
+ premoveToY = toY;
+ premoveFromX = fromX;
+ premoveFromY = fromY;
+ premovePromoChar = promoChar;
+ gotPremove = 1;
+ if (appData.debugMode)
+ fprintf(debugFP, "Got premove: fromX %d,"
+ "fromY %d, toX %d, toY %d\n",
+ fromX, fromY, toX, toY);
+ }
+ return;
+ }
+ break;
+
+ case IcsPlayingWhite:
+ /* User is moving for White */
+ if (!WhiteOnMove(currentMove)) {
+ if (!appData.premove) {
+ DisplayMoveError("It is Black's turn");
+ } else if (toX >= 0 && toY >= 0) {
+ premoveToX = toX;
+ premoveToY = toY;
+ premoveFromX = fromX;
+ premoveFromY = fromY;
+ premovePromoChar = promoChar;
+ gotPremove = 1;
+ if (appData.debugMode)
+ fprintf(debugFP, "Got premove: fromX %d,"
+ "fromY %d, toX %d, toY %d\n",
+ fromX, fromY, toX, toY);
+ }
+ return;
+ }
+ break;
+
+ default:
+ break;
+
+ case EditPosition:
+ if (toX == -2 || toY == -2) {
+ boards[0][fromY][fromX] = EmptySquare;
+ DrawPosition(FALSE, boards[currentMove]);
+ } else if (toX >= 0 && toY >= 0) {
+ boards[0][toY][toX] = boards[0][fromY][fromX];
+ boards[0][fromY][fromX] = EmptySquare;
+ DrawPosition(FALSE, boards[currentMove]);
+ }
+ return;
+ }
+
+ if (toX < 0 || toY < 0) return;
+ userOfferedDraw = FALSE;
+
+ if (appData.testLegality) {
+ moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
+ EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
+ if (moveType == IllegalMove || moveType == ImpossibleMove) {
+ DisplayMoveError("Illegal move");
+ return;
+ }
+ } else {
+ moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
+ }
+
+ if (gameMode == Training) {
+ /* compare the move played on the board to the next move in the
+ * game. If they match, display the move and the opponent's response.
+ * If they don't match, display an error message.
+ */
+ int saveAnimate;
+ Board testBoard;
+ CopyBoard(testBoard, boards[currentMove]);
+ ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
+
+ if (CompareBoards(testBoard, boards[currentMove+1])) {
+ ForwardInner(currentMove+1);
+
+ /* Autoplay the opponent's response.
+ * if appData.animate was TRUE when Training mode was entered,
+ * the response will be animated.
+ */
+ saveAnimate = appData.animate;
+ appData.animate = animateTraining;
+ ForwardInner(currentMove+1);
+ appData.animate = saveAnimate;
+
+ /* check for the end of the game */
+ if (currentMove >= forwardMostMove) {
+ gameMode = PlayFromGameFile;
+ ModeHighlight();
+ SetTrainingModeOff();
+ DisplayInformation("End of game");
+ }
+ } else {
+ DisplayError("Incorrect move", 0);
+ }
+ return;
+ }
+
+ FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
+}
+
+/* Common tail of UserMoveEvent and DropMenuEvent */
+void
+FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
+ ChessMove moveType;
+ int fromX, fromY, toX, toY;
+ /*char*/int promoChar;
+{
+ /* Ok, now we know that the move is good, so we can kill
+ the previous line in Analysis Mode */
+ if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+ forwardMostMove = currentMove;
+ }
+
+ /* If we need the chess program but it's dead, restart it */
+ ResurrectChessProgram();
+
+ /* A user move restarts a paused game*/
+ if (pausing)
+ PauseEvent();
+
+ thinkOutput[0] = NULLCHAR;
+
+ MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
+
+ if (gameMode == BeginningOfGame) {
+ if (appData.noChessProgram) {
+ gameMode = EditGame;
+ SetGameInfo();
+ } else {
+ char buf[MSG_SIZ];
+ gameMode = MachinePlaysBlack;
+ SetGameInfo();
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ DisplayTitle(buf);
+ if (first.sendName) {
+ sprintf(buf, "name %s\n", gameInfo.white);
+ SendToProgram(buf, &first);
+ }
+ }
+ ModeHighlight();
+ }
+
+ /* Relay move to ICS or chess engine */
+ if (appData.icsActive) {
+ if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
+ gameMode == IcsExamining) {
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ ics_user_moved = 1;
+ }
+ } else {
+ if (first.sendTime && (gameMode == BeginningOfGame ||
+ gameMode == MachinePlaysWhite ||
+ gameMode == MachinePlaysBlack)) {
+ SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
+ }
+
+ SendMoveToProgram(forwardMostMove-1, &first);
+ if (gameMode != EditGame && gameMode != PlayFromGameFile) {
+ first.maybeThinking = TRUE;
+ }
+ if (currentMove == cmailOldMove + 1) {
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
+ }
+ }
+
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
+ switch (gameMode) {
+ case EditGame:
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),
+ EP_UNKNOWN)) {
+ case MT_NONE:
+ case MT_CHECK:
+ break;
+ case MT_CHECKMATE:
+ if (WhiteOnMove(currentMove)) {
+ GameEnds(BlackWins, "Black mates", GE_PLAYER);
+ } else {
+ GameEnds(WhiteWins, "White mates", GE_PLAYER);
+ }
+ break;
+ case MT_STALEMATE:
+ GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
+ break;
+ }
+ break;
+
+ case MachinePlaysBlack:
+ case MachinePlaysWhite:
+ /* disable certain menu options while machine is thinking */
+ SetMachineThinkingEnables();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+HandleMachineMove(message, cps)
+ char *message;
+ ChessProgramState *cps;
+{
+ char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
+ char realname[MSG_SIZ];
+ int fromX, fromY, toX, toY;
+ ChessMove moveType;
+ char promoChar;
+ char *p;
+ int machineWhite;
+
+ /*
+ * Kludge to ignore BEL characters
+ */
+ while (*message == '\007') message++;
+
+ /*
+ * Look for book output
+ */
+ if (cps == &first && bookRequested) {
+ if (message[0] == '\t' || message[0] == ' ') {
+ /* Part of the book output is here; append it */
+ strcat(bookOutput, message);
+ strcat(bookOutput, " \n");
+ return;
+ } else if (bookOutput[0] != NULLCHAR) {
+ /* All of book output has arrived; display it */
+ char *p = bookOutput;
+ while (*p != NULLCHAR) {
+ if (*p == '\t') *p = ' ';
+ p++;
+ }
+ DisplayInformation(bookOutput);
+ bookRequested = FALSE;
+ /* Fall through to parse the current output */
+ }
+ }
+
+ /*
+ * Look for machine move.
+ */
+ if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
+ strcmp(buf2, "...") == 0) ||
+ (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
+ strcmp(buf1, "move") == 0)) {
+ /* Save last score befor move for zippy draw handling */
+ if (appData.icsActive && appData.zippyDraw) {
+ // ZippyDraw(0, programStats.score, programStats.depth);
+ }
+ /* This method is only useful on engines that support ping */
+ if (cps->lastPing != cps->lastPong) {
+ if (gameMode == BeginningOfGame) {
+ /* Extra move from before last new; ignore */
+ if (appData.debugMode) {
+ fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
+ }
+ } else {
+ if (appData.debugMode) {
+ fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
+ cps->which, gameMode);
+ }
+ SendToProgram("undo\n", cps);
+ }
+ return;
+ }
+
+ switch (gameMode) {
+ case BeginningOfGame:
+ /* Extra move from before last reset; ignore */
+ if (appData.debugMode) {
+ fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
+ }
+ return;
+
+ case EndOfGame:
+ case IcsIdle:
+ default:
+ /* Extra move after we tried to stop. The mode test is
+ not a reliable way of detecting this problem, but it's
+ the best we can do on engines that don't support ping.
+ */
+ if (appData.debugMode) {
+ fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
+ cps->which, gameMode);
+ }
+ SendToProgram("undo\n", cps);
+ return;
+
+ case MachinePlaysWhite:
+ case IcsPlayingWhite:
+ machineWhite = TRUE;
+ break;
+
+ case MachinePlaysBlack:
+ case IcsPlayingBlack:
+ machineWhite = FALSE;
+ break;
+
+ case TwoMachinesPlay:
+ machineWhite = (cps->twoMachinesColor[0] == 'w');
+ break;
+ }
+ if (WhiteOnMove(forwardMostMove) != machineWhite) {
+ if (appData.debugMode) {
+ fprintf(debugFP,
+ "Ignoring move out of turn by %s, gameMode %d"
+ ", forwardMost %d\n",
+ cps->which, gameMode, forwardMostMove);
+ }
+ return;
+ }
+
+ if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
+ &fromX, &fromY, &toX, &toY, &promoChar)) {
+ /* Machine move could not be parsed; ignore it. */
+ sprintf(buf1, "Illegal move \"%s\" from %s machine",
+ machineMove, cps->which);
+ /*!!if (appData.debugMode)*/ DisplayError(buf1, 0);
+ return;
+ }
+
+ hintRequested = FALSE;
+ lastHint[0] = NULLCHAR;
+ bookRequested = FALSE;
+ /* Program may be pondering now */
+ cps->maybeThinking = TRUE;
+ if (cps->sendTime == 2) cps->sendTime = 1;
+ if (cps->offeredDraw) cps->offeredDraw--;
+
+#if ZIPPY
+ if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
+ first.initDone) {
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ ics_user_moved = 1;
+ }
+#endif
+ /* currentMoveString is set as a side-effect of ParseOneMove */
+ strcpy(machineMove, currentMoveString);
+ strcat(machineMove, "\n");
+ strcpy(moveList[forwardMostMove], machineMove);
+
+ MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
+
+ if (gameMode == TwoMachinesPlay) {
+ if (cps->other->sendTime) {
+ SendTimeRemaining(cps->other,
+ cps->other->twoMachinesColor[0] == 'w');
+ }
+ SendMoveToProgram(forwardMostMove-1, cps->other);
+ if (firstMove) {
+ firstMove = FALSE;
+ if (cps->other->useColors) {
+ SendToProgram(cps->other->twoMachinesColor, cps->other);
+ }
+ SendToProgram("go\n", cps->other);
+ }
+ cps->other->maybeThinking = TRUE;
+ }
+
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ if (!pausing && appData.ringBellAfterMoves) {
+ RingBell();
+ }
+
+ /*
+ * Reenable menu items that were disabled while
+ * machine was thinking
+ */
+ if (gameMode != TwoMachinesPlay)
+ SetUserThinkingEnables();
+ return;
+ }
+
+
+ /* Set special modes for chess engines. Later something general
+ * could be added here; for now there is just one kludge feature,
+ * needed because Crafty 15.10 and earlier don't ignore SIGINT
+ * when "xboard" is given as an interactive command.
+ */
+ if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
+ cps->useSigint = FALSE;
+ cps->useSigterm = FALSE;
+ }
+
+ /*
+ * Look for communication commands
+ */
+ if (!strncmp(message, "telluser ", 9)) {
+ DisplayInformation(message + 9);
+ return;
+ }
+ if (!strncmp(message, "tellusererror ", 14)) {
+ DisplayError(message + 14, 0);
+ return;
+ }
+ if (!strncmp(message, "tellopponent ", 13)) {
+ if (appData.icsActive) {
+ if (loggedOn) {
+ sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
+ SendToICS(buf1);
+ }
+ } else {
+ DisplayInformation(message + 13);
+ }
+ return;
+ }
+ if (!strncmp(message, "tellothers ", 11)) {
+ if (appData.icsActive) {
+ if (loggedOn) {
+ sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
+ SendToICS(buf1);
+ }
+ }
+ return;
+ }
+ if (!strncmp(message, "tellall ", 8)) {
+ if (appData.icsActive) {
+ /* daniel*/
+ if (loggedOn && !appData.icsAnalyze) {
+ sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
+ SendToICS(buf1);
+ }
+ } else {
+ DisplayInformation(message + 8);
+ }
+ return;
+ }
+ if (strncmp(message, "warning", 7) == 0) {
+ /* Undocumented feature, use tellusererror in new code */
+ DisplayError(message, 0);
+ return;
+ }
+ if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
+ strcpy(realname, cps->tidy);
+ strcat(realname, " query");
+ AskQuestion(realname, buf2, buf1, cps->pr);
+ return;
+ }
+ /* Commands from the engine directly to ICS. We don't allow these to be
+ * sent until we are logged on. Crafty kibitzes have been known to
+ * interfere with the login process.
+ */
+ if (loggedOn) {
+ if (!strncmp(message, "tellics ", 8)) {
+ SendToICS(message + 8);
+ SendToICS("\n");
+ return;
+ }
+ if (!strncmp(message, "tellicsnoalias ", 15)) {
+ SendToICS(ics_prefix);
+ SendToICS(message + 15);
+ SendToICS("\n");
+ return;
+ }
+ /* The following are for backward compatibility only */
+ if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
+ !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
+ SendToICS(ics_prefix);
+ SendToICS(message);
+ SendToICS("\n");
+ return;
+ }
+ }
+ if (strncmp(message, "feature ", 8) == 0) {
+ ParseFeatures(message+8, cps);
+ }
+ if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
+ return;
+ }
+ /*
+ * If the move is illegal, cancel it and redraw the board.
+ * Also deal with other error cases. Matching is rather loose
+ * here to accommodate engines written before the spec.
+ */
+ if (strncmp(message + 1, "llegal move", 11) == 0 ||
+ strncmp(message, "Error", 5) == 0) {
+ if (StrStr(message, "name") ||
+ StrStr(message, "rating") || StrStr(message, "?") ||
+ StrStr(message, "result") || StrStr(message, "board") ||
+ StrStr(message, "bk") || StrStr(message, "computer") ||
+ StrStr(message, "variant") || StrStr(message, "hint") ||
+ StrStr(message, "random") || StrStr(message, "depth") ||
+ StrStr(message, "accepted")) {
+ return;
+ }
+ if (StrStr(message, "protover")) {
+ /* Program is responding to input, so it's apparently done
+ initializing, and this error message indicates it is
+ protocol version 1. So we don't need to wait any longer
+ for it to initialize and send feature commands. */
+ FeatureDone(cps, 1);
+ cps->protocolVersion = 1;
+ return;
+ }
+ cps->maybeThinking = FALSE;
+
+ if (StrStr(message, "draw")) {
+ /* Program doesn't have "draw" command */
+ cps->sendDrawOffers = 0;
+ return;
+ }
+ if (cps->sendTime != 1 &&
+ (StrStr(message, "time") || StrStr(message, "otim"))) {
+ /* Program apparently doesn't have "time" or "otim" command */
+ cps->sendTime = 0;
+ return;
+ }
+ if (StrStr(message, "analyze")) {
+ cps->analysisSupport = FALSE;
+ cps->analyzing = FALSE;
+ Reset(FALSE, TRUE);
+ sprintf(buf2, "%s does not support analysis", cps->tidy);
+ DisplayError(buf2, 0);
+ return;
+ }
+ if (StrStr(message, "st")) {
+ cps->stKludge = TRUE;
+ SendTimeControl(cps, movesPerSession, timeControl,
+ timeIncrement, appData.searchDepth,
+ searchTime);
+ return;
+ }
+ if (StrStr(message, "sd")) {
+ cps->sdKludge = TRUE;
+ SendTimeControl(cps, movesPerSession, timeControl,
+ timeIncrement, appData.searchDepth,
+ searchTime);
+ return;
+ }
+ if (!StrStr(message, "llegal")) return;
+ if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
+ gameMode == IcsIdle) return;
+ if (forwardMostMove <= backwardMostMove) return;
+ if (cps == &first && programStats.ok_to_send == 0) {
+ /* Bogus message from Crafty responding to "." This filtering
+ can miss some of the bad messages, but fortunately the bug
+ is fixed in current Crafty versions, so it doesn't matter. */
+ return;
+ }
+ if (pausing) PauseEvent();
+ if (gameMode == PlayFromGameFile) {
+ /* Stop reading this game file */
+ gameMode = EditGame;
+ ModeHighlight();
+ }
+
+
+ currentMove = --forwardMostMove;
+ DisplayMove(currentMove-1); /* before DisplayMoveError */
+ SwitchClocks();
+ DisplayBothClocks();
+ sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",
+ parseList[currentMove], cps->which);
+ DisplayMoveError(buf1);
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
+ }
+ if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
+ /* Program has a broken "time" command that
+ outputs a string not ending in newline.
+ Don't use it. */
+ cps->sendTime = 0;
+ }
+
+ /*
+ * If chess program startup fails, exit with an error message.
+ * Attempts to recover here are futile.
+ */
+ if ((StrStr(message, "unknown host") != NULL)
+ || (StrStr(message, "No remote directory") != NULL)
+ || (StrStr(message, "not found") != NULL)
+ || (StrStr(message, "No such file") != NULL)
+ || (StrStr(message, "can't alloc") != NULL)
+ || (StrStr(message, "Permission denied") != NULL)) {
+
+ cps->maybeThinking = FALSE;
+ sprintf(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);
+ return;
+ }
+
+ /*
+ * Look for hint output
+ */
+ if (sscanf(message, "Hint: %s", buf1) == 1) {
+ switch (gameMode) {
+ case IcsPlayingWhite:
+ case IcsPlayingBlack:
+ case MachinePlaysWhite:
+ case MachinePlaysBlack:
+ strcpy(programStats.ponderMove, buf1);
+ break;
+ default:
+ programStats.ponderMove[0] = NULLCHAR;
+ break;
+ }
+ if (cps == &first && hintRequested) {
+ hintRequested = FALSE;
+ if (ParseOneMove(buf1, forwardMostMove, &moveType,
+ &fromX, &fromY, &toX, &toY, &promoChar)) {
+ (void) CoordsToAlgebraic(boards[forwardMostMove],
+ PosFlags(forwardMostMove), EP_UNKNOWN,
+ fromY, fromX, toY, toX, promoChar, buf1);
+ sprintf(buf2, "Hint: %s", buf1);
+ DisplayInformation(buf2);
+ } else {
+ /* Hint move could not be parsed!? */
+ sprintf(buf2,
+ "Illegal hint move \"%s\"\nfrom %s chess program",
+ buf1, cps->which);
+ DisplayError(buf2, 0);
+ }
+ } else {
+ /* Copy ponder move */
+ strcpy(lastHint, buf1);
+ }
+ return;
+ }
+
+ /*
+ * Ignore other messages if game is not in progress
+ */
+ if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
+ gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
+
+ /*
+ * look for win, lose, draw, or draw offer
+ */
+ if (strncmp(message, "1-0", 3) == 0) {
+ char *p, *q, *r = "";
+ p = strchr(message, '{');
+ if (p) {
+ q = strchr(p, '}');
+ if (q) {
+ *q = NULLCHAR;
+ r = p + 1;
+ }
+ }
+ GameEnds(WhiteWins, r, GE_ENGINE);
+ return;
+ } else if (strncmp(message, "0-1", 3) == 0) {
+ char *p, *q, *r = "";
+ p = strchr(message, '{');
+ if (p) {
+ q = strchr(p, '}');
+ if (q) {
+ *q = NULLCHAR;
+ r = p + 1;
+ }
+ }
+ /* Kludge for Arasan 4.1 bug */
+ if (strcmp(r, "Black resigns") == 0) {
+ GameEnds(WhiteWins, r, GE_ENGINE);
+ return;
+ }
+ GameEnds(BlackWins, r, GE_ENGINE);
+ return;
+ } else if (strncmp(message, "1/2", 3) == 0) {
+ char *p, *q, *r = "";
+ p = strchr(message, '{');
+ if (p) {
+ q = strchr(p, '}');
+ if (q) {
+ *q = NULLCHAR;
+ r = p + 1;
+ }
+ }
+ GameEnds(GameIsDrawn, r, GE_ENGINE);
+ return;
+
+ } else if (strncmp(message, "White resign", 12) == 0) {
+ GameEnds(BlackWins, "White resigns", GE_ENGINE);
+ return;
+ } else if (strncmp(message, "Black resign", 12) == 0) {
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
+ return;
+ } else if (strncmp(message, "White", 5) == 0 &&
+ message[5] != '(' &&
+ StrStr(message, "Black") == NULL) {
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ return;
+ } else if (strncmp(message, "Black", 5) == 0 &&
+ message[5] != '(') {
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ return;
+ } else if (strcmp(message, "resign") == 0 ||
+ strcmp(message, "computer resigns") == 0) {
+ switch (gameMode) {
+ case MachinePlaysBlack:
+ case IcsPlayingBlack:
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
+ break;
+ case MachinePlaysWhite:
+ case IcsPlayingWhite:
+ GameEnds(BlackWins, "White resigns", GE_ENGINE);
+ break;
+ case TwoMachinesPlay:
+ if (cps->twoMachinesColor[0] == 'w')
+ GameEnds(BlackWins, "White resigns", GE_ENGINE);
+ else
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
+ break;
+ default:
+ /* can't happen */
+ break;
+ }
+ return;
+ } else if (strncmp(message, "opponent mates", 14) == 0) {
+ switch (gameMode) {
+ case MachinePlaysBlack:
+ case IcsPlayingBlack:
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ break;
+ case MachinePlaysWhite:
+ case IcsPlayingWhite:
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ break;
+ case TwoMachinesPlay:
+ if (cps->twoMachinesColor[0] == 'w')
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ else
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ break;
+ default:
+ /* can't happen */
+ break;
+ }
+ return;
+ } else if (strncmp(message, "computer mates", 14) == 0) {
+ switch (gameMode) {
+ case MachinePlaysBlack:
+ case IcsPlayingBlack:
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ break;
+ case MachinePlaysWhite:
+ case IcsPlayingWhite:
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ break;
+ case TwoMachinesPlay:
+ if (cps->twoMachinesColor[0] == 'w')
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ else
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ break;
+ default:
+ /* can't happen */
+ break;
+ }
+ return;
+ } else if (strncmp(message, "checkmate", 9) == 0) {
+ if (WhiteOnMove(forwardMostMove)) {
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);
+ } else {
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);
+ }
+ return;
+ } else if (strstr(message, "Draw") != NULL ||
+ strstr(message, "game is a draw") != NULL) {
+ GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
+ return;
+ } else if (strstr(message, "offer") != NULL &&
+ strstr(message, "draw") != NULL) {
+#if ZIPPY
+ if (appData.zippyPlay && first.initDone) {
+ /* Relay offer to ICS */
+ SendToICS(ics_prefix);
+ SendToICS("draw\n");
+ }
+#endif
+ cps->offeredDraw = 2; /* valid until this engine moves twice */
+ if (gameMode == TwoMachinesPlay) {
+ if (cps->other->offeredDraw) {
+ GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
+ } else {
+ if (cps->other->sendDrawOffers) {
+ SendToProgram("draw\n", cps->other);
+ }
+ }
+ } else if (gameMode == MachinePlaysWhite ||
+ gameMode == MachinePlaysBlack) {
+ if (userOfferedDraw) {
+ DisplayInformation("Machine accepts your draw offer");
+ GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
+ } else {
+ DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");
+ }
+ }
+ }
+
+
+ /*
+ * Look for thinking output
+ */
+ /* Using icsAnalyze for future function */
+ if (appData.showThinking || appData.icsAnalyze) {
+ int plylev, mvleft, mvtot, curscore, time;
+ char mvname[MOVE_LEN];
+ unsigned long nodes;
+ char plyext;
+ int ignore = FALSE;
+ prefixHint = FALSE;
+ mvname[0] = NULLCHAR;
+
+ /* reset thinkoutput */
+ thinkOutput[0] = NULLCHAR;
+
+ switch (gameMode) {
+ case MachinePlaysBlack:
+ case IcsPlayingBlack:
+ if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
+ break;
+ case MachinePlaysWhite:
+ case IcsPlayingWhite:
+ if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
+ break;
+ case AnalyzeMode:
+ case AnalyzeFile:
+ break;
+ case IcsObserving:
+ ignore = FALSE;
+ break;
+ case TwoMachinesPlay:
+ if ((cps->twoMachinesColor[0] == 'w') !=
+ WhiteOnMove(forwardMostMove)) {
+ ignore = TRUE;
+ }
+ break;
+ default:
+ ignore = TRUE;
+ break;
+ }
+
+ if (!ignore) {
+ /* reset ponder move display */
+ buf1[0] = NULLCHAR;
+ if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
+ &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
+
+ if (plyext != ' ' && plyext != '\t') {
+ time *= 100;
+ }
+ programStats.depth = plylev;
+ programStats.nodes = nodes;
+ programStats.time = time;
+ programStats.score = curscore;
+ programStats.got_only_move = 0;
+
+ /* Buffer overflow protection */
+ if (buf1[0] != NULLCHAR) {
+ if (strlen(buf1) >= MSG_SIZ) {
+ if (appData.debugMode) fprintf(debugFP, "PV is to long. I use the first %d bytes. \n", MSG_SIZ);
+ strncpy(programStats.movelist, buf1, MSG_SIZ);
+ } else {
+ strcpy(programStats.movelist, buf1);
+ }
+ } else {
+ sprintf(programStats.movelist, " no PV \n");
+ if (appData.debugMode) fprintf(debugFP, "I found no PV \n");
+ }
+
+ if (programStats.seen_stat) {
+ programStats.ok_to_send = 1;
+ }
+
+ if (strchr(programStats.movelist, '(') != NULL) {
+ programStats.line_is_book = 1;
+ programStats.nr_moves = 0;
+ programStats.moves_left = 0;
+ } else {
+ programStats.line_is_book = 0;
+ }
+
+ sprintf(thinkOutput, "%d %c%+.2f %s%s%s",
+ plylev,
+ (gameMode == TwoMachinesPlay ?
+ ToUpper(cps->twoMachinesColor[0]) : ' '),
+ ((double) programStats.score) / 100.0,
+ prefixHint ? lastHint : "",
+ prefixHint ? " " : "", buf1);
+ /* Using icsAnalyze for future function */
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
+ appData.icsAnalyzeWindow || appData.AnalysisWindow ||
+ gameMode == AnalyzeFile) {
+ if (appData.icsAnalyzeWindow ||
+ appData.AnalysisWindow) DisplayAnalysis(0,1);
+ DisplayMove(currentMove - 1);
+ } else {
+ if (appData.showThinking) DisplayMove(currentMove - 1);
+ }
+ return;
+
+ } else if ((p=StrStr(message, "(only move)")) != NULL) {
+ /* crafty (9.25+) says "(only move) <move>"
+ * if there is only 1 legal move
+ */
+ sscanf(p, "(only move) %s", buf1);
+ sprintf(thinkOutput, "%s (only move)", buf1);
+ sprintf(programStats.movelist, "%s (only move)", buf1);
+ programStats.depth = 2; /* don't use 0 or 1 it's book depth */
+ programStats.nr_moves = 1;
+ programStats.moves_left = 1;
+ programStats.nodes = 1;
+ programStats.time = 1;
+ programStats.got_only_move = 1;
+
+ /* Not really, but we also use this member to
+ mean "line isn't going to change" (Crafty
+ isn't searching, so stats won't change) */
+ programStats.line_is_book = 1;
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
+ appData.icsAnalyzeWindow || appData.AnalysisWindow ||
+ gameMode == AnalyzeFile) {
+ if (appData.icsAnalyzeWindow ||
+ appData.AnalysisWindow) DisplayAnalysis(0,1);
+ DisplayMove(currentMove - 1);
+ } else {
+ if (appData.showThinking) DisplayMove(currentMove - 1);
+ }
+ return;
+
+ } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",
+ &time, &nodes, &plylev, &mvleft,
+ &mvtot, mvname) >= 5) {
+ /* The stat01: line is from Crafty (9.29+) in response
+ to the "." command */
+ programStats.seen_stat = 1;
+ /* for display engine room */
+ supportStat = 1;
+ cps->maybeThinking = TRUE;
+
+ if (programStats.got_only_move || !appData.periodicUpdates) return;
+
+ programStats.depth = plylev;
+ programStats.time = time;
+ programStats.nodes = nodes;
+ programStats.moves_left = mvleft;
+ programStats.nr_moves = mvtot;
+ strcpy(programStats.move_name, mvname);
+ programStats.ok_to_send = 1;
+ if (appData.icsAnalyzeWindow ||
+ appData.AnalysisWindow) DisplayAnalysis(0,0);
+ return;
+
+ } else if (strncmp(message,"++",2) == 0) {
+ /* Crafty 9.29+ outputs this */
+ programStats.got_fail = 2;
+ return;
+
+ } else if (strncmp(message,"--",2) == 0) {
+ /* Crafty 9.29+ outputs this */
+ programStats.got_fail = 1;
+ return;
+
+ } else if (thinkOutput[0] != NULLCHAR &&
+ strncmp(message, " ", 4) == 0) {
+ p = message;
+ while (*p && *p == ' ') p++;
+ strcat(thinkOutput, " ");
+ strcat(thinkOutput, p);
+ strcat(programStats.movelist, " ");
+ strcat(programStats.movelist, p);
+
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
+ appData.icsAnalyzeWindow || appData.AnalysisWindow ||
+ gameMode == AnalyzeFile) {
+ if (appData.icsAnalyzeWindow ||
+ appData.AnalysisWindow) DisplayAnalysis(0,1);
+ DisplayMove(currentMove - 1);
+ } else {
+ if (appData.showThinking) DisplayMove(currentMove - 1);
+ }
+ return;
+
+ }
+ }
+ }
+}
+
+
+/* Parse a game score from the character string "game", and
+ record it as the history of the current game. The game
+ score is NOT assumed to start from the standard position.
+ The display is not updated in any way.
+ */
+void
+ParseGameHistory(game)
+ char *game;
+{
+ ChessMove moveType;
+ int fromX, fromY, toX, toY, boardIndex;
+ char promoChar;
+ char *p, *q;
+ char buf[MSG_SIZ];
+
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsing game history: %s\n", game);
+
+ if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
+ gameInfo.site = StrSave(appData.icsHost);
+ gameInfo.date = PGNDate();
+ gameInfo.round = StrSave("-");
+
+ /* Parse out names of players */
+ while (*game == ' ') game++;
+ p = buf;
+ while (*game != ' ') *p++ = *game++;
+ *p = NULLCHAR;
+ gameInfo.white = StrSave(buf);
+ while (*game == ' ') game++;
+ p = buf;
+ while (*game != ' ' && *game != '\n') *p++ = *game++;
+ *p = NULLCHAR;
+ gameInfo.black = StrSave(buf);
+
+ /* Parse moves */
+ boardIndex = blackPlaysFirst ? 1 : 0;
+ yynewstr(game);
+ for (;;) {
+ yyboardindex = boardIndex;
+ moveType = (ChessMove) yylex();
+ switch (moveType) {
+ case WhitePromotionQueen:
+ case BlackPromotionQueen:
+ case WhitePromotionRook:
+ case BlackPromotionRook:
+ case WhitePromotionBishop:
+ case BlackPromotionBishop:
+ case WhitePromotionKnight:
+ case BlackPromotionKnight:
+ case WhitePromotionKing:
+ case BlackPromotionKing:
+ case NormalMove:
+ case WhiteCapturesEnPassant:
+ case BlackCapturesEnPassant:
+ case WhiteKingSideCastle:
+ case WhiteQueenSideCastle:
+ case BlackKingSideCastle:
+ case BlackQueenSideCastle:
+ case WhiteKingSideCastleWild:
+ case WhiteQueenSideCastleWild:
+ case BlackKingSideCastleWild:
+ case BlackQueenSideCastleWild:
+ case IllegalMove: /* maybe suicide chess, etc. */
+ fromX = currentMoveString[0] - 'a';
+ fromY = currentMoveString[1] - '1';
+ toX = currentMoveString[2] - 'a';
+ toY = currentMoveString[3] - '1';
+ promoChar = currentMoveString[4];
+ break;
+ case WhiteDrop:
+ case BlackDrop:
+ fromX = moveType == WhiteDrop ?
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :
+ (int) CharToPiece(ToLower(currentMoveString[0]));
+ fromY = DROP_RANK;
+ toX = currentMoveString[2] - 'a';
+ toY = currentMoveString[3] - '1';
+ promoChar = NULLCHAR;
+ break;
+ case AmbiguousMove:
+ /* bug? */
+ sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);
+ DisplayError(buf, 0);
+ return;
+ case ImpossibleMove:
+ /* bug? */
+ sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);
+ DisplayError(buf, 0);
+ return;
+ case (ChessMove) 0: /* end of file */
+ if (boardIndex < backwardMostMove) {
+ /* Oops, gap. How did that happen? */
+ DisplayError("Gap in move list", 0);
+ return;
+ }
+ backwardMostMove = blackPlaysFirst ? 1 : 0;
+ if (boardIndex > forwardMostMove) {
+ forwardMostMove = boardIndex;
+ }
+ return;
+ case ElapsedTime:
+ if (boardIndex > 0) {
+ strcat(parseList[boardIndex-1], " ");
+ strcat(parseList[boardIndex-1], yy_text);
+ }
+ continue;
+ case Comment:
+ case PGNTag:
+ case NAG:
+ default:
+ /* ignore */
+ continue;
+ case WhiteWins:
+ case BlackWins:
+ case GameIsDrawn:
+ case GameUnfinished:
+ if (gameMode == IcsExamining) {
+ if (boardIndex < backwardMostMove) {
+ /* Oops, gap. How did that happen? */
+ return;
+ }
+ backwardMostMove = blackPlaysFirst ? 1 : 0;
+ return;
+ }
+ gameInfo.result = moveType;
+ p = strchr(yy_text, '{');
+ if (p == NULL) p = strchr(yy_text, '(');
+ if (p == NULL) {
+ p = yy_text;
+ if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
+ } else {
+ q = strchr(p, *p == '{' ? '}' : ')');
+ if (q != NULL) *q = NULLCHAR;
+ p++;
+ }
+ gameInfo.resultDetails = StrSave(p);
+ continue;
+ }
+ if (boardIndex >= forwardMostMove &&
+ !(gameMode == IcsObserving && ics_gamenum == -1)) {
+ backwardMostMove = blackPlaysFirst ? 1 : 0;
+ return;
+ }
+ (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
+ EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
+ parseList[boardIndex]);
+ CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
+ /* currentMoveString is set as a side-effect of yylex */
+ strcpy(moveList[boardIndex], currentMoveString);
+ strcat(moveList[boardIndex], "\n");
+ boardIndex++;
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
+ switch (MateTest(boards[boardIndex],
+ PosFlags(boardIndex), EP_UNKNOWN)) {
+ case MT_NONE:
+ case MT_STALEMATE:
+ default:
+ break;
+ case MT_CHECK:
+ strcat(parseList[boardIndex - 1], "+");
+ break;
+ case MT_CHECKMATE:
+ strcat(parseList[boardIndex - 1], "#");
+ break;
+ }
+ }
+}
+
+
+/* Apply a move to the given board */
+void
+ApplyMove(fromX, fromY, toX, toY, promoChar, board)
+ int fromX, fromY, toX, toY;
+ int promoChar;
+ Board board;
+{
+ ChessSquare captured = board[toY][toX];
+ if (fromY == DROP_RANK) {
+ /* must be first */
+ board[toY][toX] = (ChessSquare) fromX;
+ } else if (fromX == toX && fromY == toY) {
+ return;
+ } else if (fromY == 0 && fromX == 4
+ && board[fromY][fromX] == WhiteKing
+ && toY == 0 && toX == 6) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = WhiteKing;
+ board[fromY][7] = EmptySquare;
+ board[toY][5] = WhiteRook;
+ } else if (fromY == 0 && fromX == 4
+ && board[fromY][fromX] == WhiteKing
+ && toY == 0 && toX == 2) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = WhiteKing;
+ board[fromY][0] = EmptySquare;
+ board[toY][3] = WhiteRook;
+ } else if (fromY == 0 && fromX == 3
+ && board[fromY][fromX] == WhiteKing
+ && toY == 0 && toX == 5) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = WhiteKing;
+ board[fromY][7] = EmptySquare;
+ board[toY][4] = WhiteRook;
+ } else if (fromY == 0 && fromX == 3
+ && board[fromY][fromX] == WhiteKing
+ && toY == 0 && toX == 1) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = WhiteKing;
+ board[fromY][0] = EmptySquare;
+ board[toY][2] = WhiteRook;
+ } else if (board[fromY][fromX] == WhitePawn
+ && toY == 7) {
+ /* white pawn promotion */
+ board[7][toX] = CharToPiece(ToUpper(promoChar));
+ if (board[7][toX] == EmptySquare) {
+ board[7][toX] = WhiteQueen;
+ }
+ board[fromY][fromX] = EmptySquare;
+ } else if ((fromY == 4)
+ && (toX != fromX)
+ && (board[fromY][fromX] == WhitePawn)
+ && (board[toY][toX] == EmptySquare)) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = WhitePawn;
+ captured = board[toY - 1][toX];
+ board[toY - 1][toX] = EmptySquare;
+ } else if (fromY == 7 && fromX == 4
+ && board[fromY][fromX] == BlackKing
+ && toY == 7 && toX == 6) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = BlackKing;
+ board[fromY][7] = EmptySquare;
+ board[toY][5] = BlackRook;
+ } else if (fromY == 7 && fromX == 4
+ && board[fromY][fromX] == BlackKing
+ && toY == 7 && toX == 2) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = BlackKing;
+ board[fromY][0] = EmptySquare;
+ board[toY][3] = BlackRook;
+ } else if (fromY == 7 && fromX == 3
+ && board[fromY][fromX] == BlackKing
+ && toY == 7 && toX == 5) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = BlackKing;
+ board[fromY][7] = EmptySquare;
+ board[toY][4] = BlackRook;
+ } else if (fromY == 7 && fromX == 3
+ && board[fromY][fromX] == BlackKing
+ && toY == 7 && toX == 1) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = BlackKing;
+ board[fromY][0] = EmptySquare;
+ board[toY][2] = BlackRook;
+ } else if (board[fromY][fromX] == BlackPawn
+ && toY == 0) {
+ /* black pawn promotion */
+ board[0][toX] = CharToPiece(ToLower(promoChar));
+ if (board[0][toX] == EmptySquare) {
+ board[0][toX] = BlackQueen;
+ }
+ board[fromY][fromX] = EmptySquare;
+ } else if ((fromY == 3)
+ && (toX != fromX)
+ && (board[fromY][fromX] == BlackPawn)
+ && (board[toY][toX] == EmptySquare)) {
+ board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = BlackPawn;
+ captured = board[toY + 1][toX];
+ board[toY + 1][toX] = EmptySquare;
+ } else {
+ board[toY][toX] = board[fromY][fromX];
+ board[fromY][fromX] = EmptySquare;
+ }
+ if (gameInfo.variant == VariantCrazyhouse) {
+#if 0
+ /* !!A lot more code needs to be written to support holdings */
+ if (fromY == DROP_RANK) {
+ /* Delete from holdings */
+ if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
+ }
+ if (captured != EmptySquare) {
+ /* Add to holdings */
+ if (captured < BlackPawn) {
+ holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
+ } else {
+ holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
+ }
+ }
+#endif
+ } else if (gameInfo.variant == VariantAtomic) {
+ if (captured != EmptySquare) {
+ int y, x;
+ for (y = toY-1; y <= toY+1; y++) {
+ for (x = toX-1; x <= toX+1; x++) {
+ if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
+ board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
+ board[y][x] = EmptySquare;
+ }
+ }
+ }
+ board[toY][toX] = EmptySquare;
+ }
+ }
+}
+
+/* Updates forwardMostMove */
+void
+MakeMove(fromX, fromY, toX, toY, promoChar)
+ int fromX, fromY, toX, toY;
+ int promoChar;
+{
+ forwardMostMove++;
+ if (forwardMostMove >= 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]);
+ gameInfo.result = GameUnfinished;
+ if (gameInfo.resultDetails != NULL) {
+ free(gameInfo.resultDetails);
+ gameInfo.resultDetails = NULL;
+ }
+ CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
+ moveList[forwardMostMove - 1]);
+ (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
+ PosFlags(forwardMostMove - 1), EP_UNKNOWN,
+ fromY, fromX, toY, toX, promoChar,
+ parseList[forwardMostMove - 1]);
+ switch (MateTest(boards[forwardMostMove],
+ PosFlags(forwardMostMove), EP_UNKNOWN)){
+ case MT_NONE:
+ case MT_STALEMATE:
+ default:
+ break;
+ case MT_CHECK:
+ strcat(parseList[forwardMostMove - 1], "+");
+ break;
+ case MT_CHECKMATE:
+ strcat(parseList[forwardMostMove - 1], "#");
+ break;
+ }
+}
+
+/* Updates currentMove if not pausing */
+void
+ShowMove(fromX, fromY, toX, toY)
+{
+ int instant = (gameMode == PlayFromGameFile) ?
+ (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
+ if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
+ if (!instant) {
+ if (forwardMostMove == currentMove + 1) {
+ AnimateMove(boards[forwardMostMove - 1],
+ fromX, fromY, toX, toY);
+ }
+ if (appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ }
+ }
+ currentMove = forwardMostMove;
+ }
+
+ if (instant) return;
+ DisplayMove(currentMove - 1);
+ DrawPosition(FALSE, boards[currentMove]);
+ DisplayBothClocks();
+ HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
+}
+
+
+void
+InitChessProgram(cps)
+ ChessProgramState *cps;
+{
+ char buf[MSG_SIZ];
+ if (appData.noChessProgram) return;
+ hintRequested = FALSE;
+ bookRequested = FALSE;
+ SendToProgram(cps->initString, cps);
+ if (gameInfo.variant != VariantNormal &&
+ gameInfo.variant != VariantLoadable) {
+ char *v = VariantName(gameInfo.variant);
+ if (StrStr(cps->variants, v) == NULL) {
+ sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);
+ DisplayFatalError(buf, 0, 1);
+ return;
+ }
+ sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
+ SendToProgram(buf, cps);
+ }
+ if (cps->sendICS) {
+ sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
+ SendToProgram(buf, cps);
+ }
+ cps->maybeThinking = FALSE;
+ cps->offeredDraw = 0;
+ if (!appData.icsActive) {
+ SendTimeControl(cps, movesPerSession, timeControl,
+ timeIncrement, appData.searchDepth,
+ searchTime);
+ }
+ if (appData.showThinking) {
+ SendToProgram("post\n", cps);
+ }
+ SendToProgram("hard\n", cps);
+ if (!appData.ponderNextMove) {
+ /* Warning: "easy" is a toggle in GNU Chess, so don't send
+ it without being sure what state we are in first. "hard"
+ is not a toggle, so that one is OK.
+ */
+ SendToProgram("easy\n", cps);
+ }
+ if (cps->usePing) {
+ sprintf(buf, "ping %d\n", ++cps->lastPing);
+ SendToProgram(buf, cps);
+ }
+ cps->initDone = TRUE;
+}
+
+
+void
+StartChessProgram(cps)
+ ChessProgramState *cps;
+{
+ char buf[MSG_SIZ];
+ int err;
+
+ if (appData.noChessProgram) return;
+ cps->initDone = FALSE;
+
+ if (strcmp(cps->host, "localhost") == 0) {
+ err = StartChildProcess(cps->program, cps->dir, &cps->pr);
+ } else if (*appData.remoteShell == NULLCHAR) {
+ err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
+ } else {
+ if (*appData.remoteUser == NULLCHAR) {
+ sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
+ cps->program);
+ } else {
+ sprintf(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);
+ DisplayFatalError(buf, err, 1);
+ cps->pr = NoProc;
+ cps->isr = NULL;
+ return;
+ }
+
+ cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
+ if (cps->protocolVersion > 1) {
+ sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
+ SendToProgram(buf, cps);
+ } else {
+ SendToProgram("xboard\n", cps);
+ }
+}
+
+
+void
+TwoMachinesEventIfReady P((void))
+{
+ if (first.lastPing != first.lastPong) {
+ DisplayMessage("", "Waiting for first chess program");
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
+ return;
+ }
+ if (second.lastPing != second.lastPong) {
+ DisplayMessage("", "Waiting for second chess program");
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
+ return;
+ }
+ ThawUI();
+ TwoMachinesEvent();
+}
+
+void
+NextMatchGame P((void))
+{
+ Reset(FALSE, TRUE);
+ if (*appData.loadGameFile != NULLCHAR) {
+ LoadGameFromFile(appData.loadGameFile,
+ appData.loadGameIndex,
+ appData.loadGameFile, FALSE);
+ } else if (*appData.loadPositionFile != NULLCHAR) {
+ LoadPositionFromFile(appData.loadPositionFile,
+ appData.loadPositionIndex,
+ appData.loadPositionFile);
+ }
+ TwoMachinesEventIfReady();
+}
+
+void
+GameEnds(result, resultDetails, whosays)
+ ChessMove result;
+ char *resultDetails;
+ int whosays;
+{
+ GameMode nextGameMode;
+ int isIcsGame;
+
+ if (appData.debugMode) {
+ fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
+ result, resultDetails ? resultDetails : "(null)", whosays);
+ }
+
+ if (appData.icsAnalyze && gameMode == IcsObserving) ResetIcsQueue(ics_gamenum);
+
+ if (appData.icsActive && whosays == GE_ENGINE) {
+ /* If we are playing on ICS, the server decides when the
+ game is over, but the engine can offer to draw, claim
+ a draw, or resign.
+ */
+#if ZIPPY
+ if (appData.zippyPlay && first.initDone) {
+ if (result == GameIsDrawn) {
+ /* In case draw still needs to be claimed */
+ SendToICS(ics_prefix);
+ SendToICS("draw\n");
+ } else if (StrCaseStr(resultDetails, "resign")) {
+ SendToICS(ics_prefix);
+ SendToICS("resign\n");
+ }
+ }
+#endif
+ return;
+ }
+
+ /* If we're loading the game from a file, stop */
+ if (whosays == GE_FILE) {
+ (void) StopLoadGameTimer();
+ gameFileFP = NULL;
+ }
+
+ /* Cancel draw offers */
+ first.offeredDraw = second.offeredDraw = 0;
+
+ /* If this is an ICS game, only ICS can really say it's done;
+ if not, anyone can. */
+ isIcsGame = (gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack ||
+ gameMode == IcsObserving ||
+ gameMode == IcsExamining);
+
+ if (!isIcsGame || whosays == GE_ICS) {
+ /* OK -- not an ICS game, or ICS said it was done */
+ StopClocks();
+ if (!isIcsGame && !appData.noChessProgram)
+ SetUserThinkingEnables();
+
+ if (resultDetails != NULL) {
+ gameInfo.result = result;
+ gameInfo.resultDetails = StrSave(resultDetails);
+
+ /* Tell program how game ended in case it is learning */
+ if (gameMode == MachinePlaysWhite ||
+ gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack ||
+ gameMode == BeginningOfGame) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "result %s {%s}\n", PGNResult(result),
+ resultDetails);
+ if (first.pr != NoProc) {
+ SendToProgram(buf, &first);
+ }
+ if (second.pr != NoProc &&
+ gameMode == TwoMachinesPlay) {
+ SendToProgram(buf, &second);
+ }
+ }
+
+ /* display last move only if game was not loaded from file */
+ if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
+ DisplayMove(currentMove - 1);
+
+ if (forwardMostMove != 0) {
+ if (gameMode != PlayFromGameFile && gameMode != EditGame) {
+ if (*appData.saveGameFile != NULLCHAR) {
+ SaveGameToFile(appData.saveGameFile, TRUE);
+ } else if (appData.autoSaveGames) {
+ AutoSaveGame();
+ }
+ if (*appData.savePositionFile != NULLCHAR) {
+ SavePositionToFile(appData.savePositionFile);
+ }
+ }
+ }
+ }
+
+ if (appData.icsActive) {
+ if (appData.quietPlay &&
+ (gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack)) {
+ SendToICS(ics_prefix);
+ SendToICS("set shout 1\n");
+ }
+ nextGameMode = IcsIdle;
+ ics_user_moved = FALSE;
+ /* clean up premove. It's ugly when the game has ended and the
+ * premove highlights are still on the board.
+ */
+ if (gotPremove) {
+ gotPremove = FALSE;
+ ClearPremoveHighlights();
+ DrawPosition(FALSE, boards[currentMove]);
+ }
+ if (whosays == GE_ICS) {
+ switch (result) {
+ case WhiteWins:
+ if (gameMode == IcsPlayingWhite)
+ PlayIcsWinSound();
+ else if(gameMode == IcsPlayingBlack)
+ PlayIcsLossSound();
+ break;
+ case BlackWins:
+ if (gameMode == IcsPlayingBlack)
+ PlayIcsWinSound();
+ else if(gameMode == IcsPlayingWhite)
+ PlayIcsLossSound();
+ break;
+ case GameIsDrawn:
+ PlayIcsDrawSound();
+ break;
+ default:
+ PlayIcsUnfinishedSound();
+ }
+ }
+ } else if (gameMode == EditGame ||
+ gameMode == PlayFromGameFile ||
+ gameMode == AnalyzeMode ||
+ gameMode == AnalyzeFile) {
+ nextGameMode = gameMode;
+ } else {
+ nextGameMode = EndOfGame;
+ }
+ pausing = FALSE;
+ ModeHighlight();
+ } else {
+ nextGameMode = gameMode;
+ }
+
+ if (appData.noChessProgram) {
+ gameMode = nextGameMode;
+ ModeHighlight();
+ return;
+ }
+
+ if (first.reuse) {
+ /* Put first chess program into idle state */
+ if (first.pr != NoProc &&
+ (gameMode == MachinePlaysWhite ||
+ gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == IcsPlayingWhite ||
+ gameMode == IcsPlayingBlack ||
+ gameMode == BeginningOfGame)) {
+ SendToProgram("force\n", &first);
+ if (first.usePing) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "ping %d\n", ++first.lastPing);
+ SendToProgram(buf, &first);
+ }
+ }
+ } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
+ /* Kill off first chess program */
+ if (first.isr != NULL)
+ RemoveInputSource(first.isr);
+ first.isr = NULL;
+
+ if (first.pr != NoProc) {
+ ExitAnalyzeMode();
+ SendToProgram("quit\n", &first);
+ DestroyChildProcess(first.pr, first.useSigterm);
+ }
+ first.pr = NoProc;
+ }
+ if (second.reuse) {
+ /* Put second chess program into idle state */
+ if (second.pr != NoProc &&
+ gameMode == TwoMachinesPlay) {
+ SendToProgram("force\n", &second);
+ if (second.usePing) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "ping %d\n", ++second.lastPing);
+ SendToProgram(buf, &second);
+ }
+ }
+ } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
+ /* Kill off second chess program */
+ if (second.isr != NULL)
+ RemoveInputSource(second.isr);
+ second.isr = NULL;
+
+ if (second.pr != NoProc) {
+ SendToProgram("quit\n", &second);
+ DestroyChildProcess(second.pr, second.useSigterm);
+ }
+ second.pr = NoProc;
+ }
+
+ if (matchMode && gameMode == TwoMachinesPlay) {
+ switch (result) {
+ case WhiteWins:
+ if (first.twoMachinesColor[0] == 'w') {
+ first.matchWins++;
+ } else {
+ second.matchWins++;
+ }
+ break;
+ case BlackWins:
+ if (first.twoMachinesColor[0] == 'b') {
+ first.matchWins++;
+ } else {
+ second.matchWins++;
+ }
+ break;
+ default:
+ break;
+ }
+ if (matchGame < appData.matchGames) {
+ char *tmp;
+ tmp = first.twoMachinesColor;
+ first.twoMachinesColor = second.twoMachinesColor;
+ second.twoMachinesColor = tmp;
+ gameMode = nextGameMode;
+ matchGame++;
+ ScheduleDelayedEvent(NextMatchGame, 10000);
+ return;
+ } else {
+ char buf[MSG_SIZ];
+ gameMode = nextGameMode;
+ sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",
+ first.tidy, second.tidy,
+ first.matchWins, second.matchWins,
+ appData.matchGames - (first.matchWins + second.matchWins));
+ DisplayFatalError(buf, 0, 0);
+ }
+ }
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
+ !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
+ ExitAnalyzeMode();
+ gameMode = nextGameMode;
+ ModeHighlight();
+}
+
+/* Assumes program was just initialized (initString sent).
+ Leaves program in force mode. */
+void
+FeedMovesToProgram(cps, upto)
+ ChessProgramState *cps;
+ int upto;
+
+{
+ int i;
+ if (appData.debugMode)
+ fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
+ startedFromSetupPosition ? "position and " : "",
+ backwardMostMove, upto, cps->which);
+ /* daniel */
+ if(!appData.icsAnalyze) SendToProgram("force\n", cps);
+ if (startedFromSetupPosition) {
+ SendBoard(cps, backwardMostMove);
+ }
+ for (i = backwardMostMove; i < upto; i++) {
+
+ SendMoveToProgram(i, cps);
+ }
+}
+
+
+void
+ResurrectChessProgram()
+{
+ /* The chess program may have exited.
+ If so, restart it and feed it all the moves made so far. */
+
+ if (appData.noChessProgram || first.pr != NoProc) return;
+
+ StartChessProgram(&first);
+ InitChessProgram(&first);
+ FeedMovesToProgram(&first, currentMove);
+
+ if (!first.sendTime) {
+ /* can't tell gnuchess what its clock should read,
+ so we bow to its notion. */
+ ResetClocks();
+ timeRemaining[0][currentMove] = whiteTimeRemaining;
+ timeRemaining[1][currentMove] = blackTimeRemaining;
+ }
+
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
+ first.analysisSupport) {
+ SendToProgram("analyze\n", &first);
+ first.analyzing = TRUE;
+ }
+}
+
+/*
+ * Button procedures
+ */
+void
+Reset(redraw, init)
+ int redraw, init;
+{
+ int i;
+
+ if (appData.debugMode) {
+ fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
+ redraw, init, gameMode);
+ }
+
+ /* reset send engine output to ics */
+ appData.SendOutPutToICS = 1;
+
+ pausing = pauseExamInvalid = FALSE;
+ startedFromSetupPosition = blackPlaysFirst = FALSE;
+ firstMove = TRUE;
+ whiteFlag = blackFlag = FALSE;
+ userOfferedDraw = FALSE;
+ hintRequested = bookRequested = FALSE;
+ first.maybeThinking = FALSE;
+ second.maybeThinking = FALSE;
+ thinkOutput[0] = NULLCHAR;
+ lastHint[0] = NULLCHAR;
+ ClearGameInfo(&gameInfo);
+ gameInfo.variant = StringToVariant(appData.variant);
+ ics_user_moved = ics_clock_paused = FALSE;
+ ics_getting_history = H_FALSE;
+ ics_gamenum = -1;
+ white_holding[0] = black_holding[0] = NULLCHAR;
+ ClearProgramStats();
+
+ ResetFrontEnd();
+ ClearHighlights();
+ flipView = appData.flipView;
+ ClearPremoveHighlights();
+ gotPremove = FALSE;
+ alarmSounded = FALSE;
+
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);
+ ExitAnalyzeMode();
+ gameMode = BeginningOfGame;
+ ModeHighlight();
+ InitPosition(redraw);
+ for (i = 0; i < MAX_MOVES; i++) {
+ if (commentList[i] != NULL) {
+ free(commentList[i]);
+ commentList[i] = NULL;
+ }
+ }
+ ResetClocks();
+ timeRemaining[0][0] = whiteTimeRemaining;
+ timeRemaining[1][0] = blackTimeRemaining;
+ if (first.pr == NULL) {
+ StartChessProgram(&first);
+ }
+ if (init) InitChessProgram(&first);
+ DisplayTitle("");
+ DisplayMessage("", "");
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+}
+
+void
+AutoPlayGameLoop()
+{
+ for (;;) {
+ if (!AutoPlayOneMove())
+ return;
+ if (matchMode || appData.timeDelay == 0)
+ continue;
+ if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
+ return;
+ StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
+ break;
+ }
+}
+
+
+int
+AutoPlayOneMove()
+{
+ int fromX, fromY, toX, toY;
+
+ if (appData.debugMode) {
+ fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
+ }
+
+ if (gameMode != PlayFromGameFile)
+ return FALSE;
+
+ if (currentMove >= forwardMostMove) {
+ gameMode = EditGame;
+ ModeHighlight();
+ return FALSE;
+ }
+
+ toX = moveList[currentMove][2] - 'a';
+ toY = moveList[currentMove][3] - '1';
+
+ if (moveList[currentMove][1] == '@') {
+ if (appData.highlightLastMove) {
+ SetHighlights(-1, -1, toX, toY);
+ }
+ } else {
+ fromX = moveList[currentMove][0] - 'a';
+ fromY = moveList[currentMove][1] - '1';
+ AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
+
+ if (appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ }
+ }
+ DisplayMove(currentMove);
+ SendMoveToProgram(currentMove++, &first);
+ DisplayBothClocks();
+ DrawPosition(FALSE, boards[currentMove]);
+ if (commentList[currentMove] != NULL) {
+ DisplayComment(currentMove - 1, commentList[currentMove]);
+ }
+ return TRUE;
+}
+
+
+int
+LoadGameOneMove(readAhead)
+ ChessMove readAhead;
+{
+ int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
+ char promoChar = NULLCHAR;
+ ChessMove moveType;
+ char move[MSG_SIZ];
+ char *p, *q;
+
+ if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
+ gameMode != AnalyzeMode && gameMode != Training) {
+ gameFileFP = NULL;
+ return FALSE;
+ }
+
+ yyboardindex = forwardMostMove;
+ if (readAhead != (ChessMove)0) {
+ moveType = readAhead;
+ } else {
+ if (gameFileFP == NULL)
+ return FALSE;
+ moveType = (ChessMove) yylex();
+ }
+
+ done = FALSE;
+ switch (moveType) {
+ case Comment:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
+ p = yy_text;
+ if (*p == '{' || *p == '[' || *p == '(') {
+ p[strlen(p) - 1] = NULLCHAR;
+ p++;
+ }
+
+ /* append the comment but don't display it */
+ while (*p == '\n') p++;
+ AppendComment(currentMove, p);
+ return TRUE;
+
+ case WhiteCapturesEnPassant:
+ case BlackCapturesEnPassant:
+ case WhitePromotionQueen:
+ case BlackPromotionQueen:
+ case WhitePromotionRook:
+ case BlackPromotionRook:
+ case WhitePromotionBishop:
+ case BlackPromotionBishop:
+ case WhitePromotionKnight:
+ case BlackPromotionKnight:
+ case WhitePromotionKing:
+ case BlackPromotionKing:
+ case NormalMove:
+ case WhiteKingSideCastle:
+ case WhiteQueenSideCastle:
+ case BlackKingSideCastle:
+ case BlackQueenSideCastle:
+ case WhiteKingSideCastleWild:
+ case WhiteQueenSideCastleWild:
+ case BlackKingSideCastleWild:
+ case BlackQueenSideCastleWild:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
+ fromX = currentMoveString[0] - 'a';
+ fromY = currentMoveString[1] - '1';
+ toX = currentMoveString[2] - 'a';
+ toY = currentMoveString[3] - '1';
+ promoChar = currentMoveString[4];
+ break;
+
+ case WhiteDrop:
+ case BlackDrop:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
+ fromX = moveType == WhiteDrop ?
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :
+ (int) CharToPiece(ToLower(currentMoveString[0]));
+ fromY = DROP_RANK;
+ toX = currentMoveString[2] - 'a';
+ toY = currentMoveString[3] - '1';
+ break;
+
+ case WhiteWins:
+ case BlackWins:
+ case GameIsDrawn:
+ case GameUnfinished:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed game end: %s\n", yy_text);
+ p = strchr(yy_text, '{');
+ if (p == NULL) p = strchr(yy_text, '(');
+ if (p == NULL) {
+ p = yy_text;
+ if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
+ } else {
+ q = strchr(p, *p == '{' ? '}' : ')');
+ if (q != NULL) *q = NULLCHAR;
+ p++;
+ }
+ GameEnds(moveType, p, GE_FILE);
+ done = TRUE;
+ if (cmailMsgLoaded) {
+ ClearHighlights();
+ flipView = WhiteOnMove(currentMove);
+ if (moveType == GameUnfinished) flipView = !flipView;
+ if (appData.debugMode)
+ fprintf(debugFP, "Setting flipView to %d\n", flipView) ;
+ }
+ break;
+
+ case (ChessMove) 0: /* end of file */
+ if (appData.debugMode)
+ fprintf(debugFP, "Parser hit end of file\n");
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),
+ EP_UNKNOWN)) {
+ case MT_NONE:
+ case MT_CHECK:
+ break;
+ case MT_CHECKMATE:
+ if (WhiteOnMove(currentMove)) {
+ GameEnds(BlackWins, "Black mates", GE_FILE);
+ } else {
+ GameEnds(WhiteWins, "White mates", GE_FILE);
+ }
+ break;
+ case MT_STALEMATE:
+ GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
+ break;
+ }
+ done = TRUE;
+ break;
+
+ case MoveNumberOne:
+ if (lastLoadGameStart == GNUChessGame) {
+ /* GNUChessGames have numbers, but they aren't move numbers */
+ if (appData.debugMode)
+ fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
+ yy_text, (int) moveType);
+ return LoadGameOneMove((ChessMove)0); /* tail recursion */
+ }
+ /* else fall thru */
+
+ case XBoardGame:
+ case GNUChessGame:
+ case PGNTag:
+ /* Reached start of next game in file */
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),
+ EP_UNKNOWN)) {
+ case MT_NONE:
+ case MT_CHECK:
+ break;
+ case MT_CHECKMATE:
+ if (WhiteOnMove(currentMove)) {
+ GameEnds(BlackWins, "Black mates", GE_FILE);
+ } else {
+ GameEnds(WhiteWins, "White mates", GE_FILE);
+ }
+ break;
+ case MT_STALEMATE:
+ GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
+ break;
+ }
+ done = TRUE;
+ break;
+
+ case PositionDiagram: /* should not happen; ignore */
+ case ElapsedTime: /* ignore */
+ case NAG: /* ignore */
+ if (appData.debugMode)
+ fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
+ yy_text, (int) moveType);
+ return LoadGameOneMove((ChessMove)0); /* tail recursion */
+
+ case IllegalMove:
+ if (appData.testLegality) {
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
+ sprintf(move, "Illegal move: %d.%s%s",
+ (forwardMostMove / 2) + 1,
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
+ DisplayError(move, 0);
+ done = TRUE;
+ } else {
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
+ yy_text, currentMoveString);
+ fromX = currentMoveString[0] - 'a';
+ fromY = currentMoveString[1] - '1';
+ toX = currentMoveString[2] - 'a';
+ toY = currentMoveString[3] - '1';
+ promoChar = currentMoveString[4];
+ }
+ break;
+
+ case AmbiguousMove:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
+ sprintf(move, "Ambiguous move: %d.%s%s",
+ (forwardMostMove / 2) + 1,
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
+ DisplayError(move, 0);
+ done = TRUE;
+ break;
+
+ default:
+ case ImpossibleMove:
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);
+ sprintf(move, "Illegal move: %d.%s%s",
+ (forwardMostMove / 2) + 1,
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
+ DisplayError(move, 0);
+ done = TRUE;
+ break;
+ }
+
+ if (done) {
+ if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
+ DrawPosition(FALSE, boards[currentMove]);
+ DisplayBothClocks();
+ if (!appData.matchMode && commentList[currentMove] != NULL)
+ DisplayComment(currentMove - 1, commentList[currentMove]);
+ }
+ (void) StopLoadGameTimer();
+ gameFileFP = NULL;
+ cmailOldMove = forwardMostMove;
+ return FALSE;
+ } else {
+ /* currentMoveString is set as a side-effect of yylex */
+ strcat(currentMoveString, "\n");
+ strcpy(moveList[forwardMostMove], currentMoveString);
+
+ thinkOutput[0] = NULLCHAR;
+ MakeMove(fromX, fromY, toX, toY, promoChar);
+ currentMove = forwardMostMove;
+ return TRUE;
+ }
+}
+
+/* Load the nth game from the given file */
+int
+LoadGameFromFile(filename, n, title, useList)
+ char *filename;
+ int n;
+ char *title;
+ /*Boolean*/ int useList;
+{
+ FILE *f;
+ char buf[MSG_SIZ];
+
+ if (strcmp(filename, "-") == 0) {
+ f = stdin;
+ title = "stdin";
+ } else {
+ f = fopen(filename, "rb");
+ if (f == NULL) {
+ sprintf(buf, "Can't open \"%s\"", filename);
+ DisplayError(buf, errno);
+ return FALSE;
+ }
+ }
+ if (fseek(f, 0, 0) == -1) {
+ /* f is not seekable; probably a pipe */
+ useList = FALSE;
+ }
+ if (useList && n == 0) {
+ int error = GameListBuild(f);
+ if (error) {
+ DisplayError("Cannot build game list", error);
+ } else if (!ListEmpty(&gameList) &&
+ ((ListGame *) gameList.tailPred)->number > 1) {
+ GameListPopUp(f, title);
+ return TRUE;
+ }
+ GameListDestroy();
+ n = 1;
+ }
+ if (n == 0) n = 1;
+ return LoadGame(f, n, title, FALSE);
+}
+
+
+void
+MakeRegisteredMove()
+{
+ int fromX, fromY, toX, toY;
+ char promoChar;
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
+ switch (cmailMoveType[lastLoadGameNumber - 1]) {
+ case CMAIL_MOVE:
+ case CMAIL_DRAW:
+ if (appData.debugMode)
+ fprintf(debugFP, "Restoring %s for game %d\n",
+ cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
+
+ thinkOutput[0] = NULLCHAR;
+ strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
+ fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';
+ fromY = cmailMove[lastLoadGameNumber - 1][1] - '1';
+ toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';
+ toY = cmailMove[lastLoadGameNumber - 1][3] - '1';
+ promoChar = cmailMove[lastLoadGameNumber - 1][4];
+ MakeMove(fromX, fromY, toX, toY, promoChar);
+ ShowMove(fromX, fromY, toX, toY);
+
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),
+ EP_UNKNOWN)) {
+ case MT_NONE:
+ case MT_CHECK:
+ break;
+
+ case MT_CHECKMATE:
+ if (WhiteOnMove(currentMove)) {
+ GameEnds(BlackWins, "Black mates", GE_PLAYER);
+ } else {
+ GameEnds(WhiteWins, "White mates", GE_PLAYER);
+ }
+ break;
+
+ case MT_STALEMATE:
+ GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
+ break;
+ }
+
+ break;
+
+ case CMAIL_RESIGN:
+ if (WhiteOnMove(currentMove)) {
+ GameEnds(BlackWins, "White resigns", GE_PLAYER);
+ } else {
+ GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
+ }
+ break;
+
+ case CMAIL_ACCEPT:
+ GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/* Wrapper around LoadGame for use when a Cmail message is loaded */
+int
+CmailLoadGame(f, gameNumber, title, useList)
+ FILE *f;
+ int gameNumber;
+ char *title;
+ int useList;
+{
+ int retVal;
+
+ if (gameNumber > nCmailGames) {
+ DisplayError("No more games in this message", 0);
+ return FALSE;
+ }
+ if (f == lastLoadGameFP) {
+ int offset = gameNumber - lastLoadGameNumber;
+ if (offset == 0) {
+ cmailMsg[0] = NULLCHAR;
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
+ cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
+ nCmailMovesRegistered--;
+ }
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
+ if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {
+ cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;
+ }
+ } else {
+ if (! RegisterMove()) return FALSE;
+ }
+ }
+
+ retVal = LoadGame(f, gameNumber, title, useList);
+
+ /* Make move registered during previous look at this game, if any */
+ MakeRegisteredMove();
+
+ if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {
+ commentList[currentMove]
+ = StrSave(cmailCommentList[lastLoadGameNumber - 1]);
+ DisplayComment(currentMove - 1, commentList[currentMove]);
+ }
+
+ return retVal;
+}
+
+/* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
+int
+ReloadGame(offset)
+ int offset;
+{
+ int gameNumber = lastLoadGameNumber + offset;
+ if (lastLoadGameFP == NULL) {
+ DisplayError("No game has been loaded yet", 0);
+ return FALSE;
+ }
+ if (gameNumber <= 0) {
+ DisplayError("Can't back up any further", 0);
+ return FALSE;
+ }
+ if (cmailMsgLoaded) {
+ return CmailLoadGame(lastLoadGameFP, gameNumber,
+ lastLoadGameTitle, lastLoadGameUseList);
+ } else {
+ return LoadGame(lastLoadGameFP, gameNumber,
+ lastLoadGameTitle, lastLoadGameUseList);
+ }
+}
+
+
+
+/* Load the nth game from open file f */
+int
+LoadGame(f, gameNumber, title, useList)
+ FILE *f;
+ int gameNumber;
+ char *title;
+ int useList;
+{
+ ChessMove cm;
+ char buf[MSG_SIZ];
+ int gn = gameNumber;
+ ListGame *lg = NULL;
+ int numPGNTags = 0;
+ int err;
+ GameMode oldGameMode;
+
+ if (appData.debugMode)
+ fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
+
+ if (gameMode == Training )
+ SetTrainingModeOff();
+
+ oldGameMode = gameMode;
+ if (gameMode != BeginningOfGame) {
+ Reset(FALSE, TRUE);
+ }
+
+ gameFileFP = f;
+ if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
+ fclose(lastLoadGameFP);
+ }
+
+ if (useList) {
+ lg = (ListGame *) ListElem(&gameList, gameNumber-1);
+
+ if (lg) {
+ fseek(f, lg->offset, 0);
+ GameListHighlight(gameNumber);
+ gn = 1;
+ }
+ else {
+ DisplayError("Game number out of range", 0);
+ return FALSE;
+ }
+ } else {
+ GameListDestroy();
+ if (fseek(f, 0, 0) == -1) {
+ if (f == lastLoadGameFP ?
+ gameNumber == lastLoadGameNumber + 1 :
+ gameNumber == 1) {
+ gn = 1;
+ } else {
+ DisplayError("Can't seek on game file", 0);
+ return FALSE;
+ }
+ }
+ }
+ lastLoadGameFP = f;
+ lastLoadGameNumber = gameNumber;
+ strcpy(lastLoadGameTitle, title);
+ lastLoadGameUseList = useList;
+
+ yynewfile(f);
+
+
+ if (lg && lg->gameInfo.white && lg->gameInfo.black) {
+ sprintf(buf, "%s vs. %s", lg->gameInfo.white,
+ lg->gameInfo.black);
+ DisplayTitle(buf);
+ } else if (*title != NULLCHAR) {
+ if (gameNumber > 1) {
+ sprintf(buf, "%s %d", title, gameNumber);
+ DisplayTitle(buf);
+ } else {
+ DisplayTitle(title);
+ }
+ }
+
+ if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {
+ gameMode = PlayFromGameFile;
+ ModeHighlight();
+ }
+
+ currentMove = forwardMostMove = backwardMostMove = 0;
+ CopyBoard(boards[0], initialPosition);
+ StopClocks();
+
+ /*
+ * Skip the first gn-1 games in the file.
+ * Also skip over anything that precedes an identifiable
+ * start of game marker, to avoid being confused by
+ * garbage at the start of the file. Currently
+ * recognized start of game markers are the move number "1",
+ * the pattern "gnuchess .* game", the pattern
+ * "^[#;%] [^ ]* game file", and a PGN tag block.
+ * A game that starts with one of the latter two patterns
+ * will also have a move number 1, possibly
+ * following a position diagram.
+ */
+ cm = lastLoadGameStart = (ChessMove) 0;
+ yyskipmoves = TRUE;
+ while (gn > 0) {
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+ yyskipmoves = FALSE;
+ switch (cm) {
+ case (ChessMove) 0:
+ if (cmailMsgLoaded) {
+ nCmailGames = CMAIL_MAX_GAMES - gn;
+ } else {
+ Reset(TRUE, TRUE);
+ DisplayError("Game not found in file", 0);
+ }
+ yyskipmoves = FALSE;
+ return FALSE;
+
+ case GNUChessGame:
+ case XBoardGame:
+ gn--;
+ lastLoadGameStart = cm;
+ break;
+
+ case MoveNumberOne:
+ switch (lastLoadGameStart) {
+ case GNUChessGame:
+ case XBoardGame:
+ case PGNTag:
+ break;
+ case MoveNumberOne:
+ case (ChessMove) 0:
+ gn--; /* count this game */
+ lastLoadGameStart = cm;
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+ break;
+
+ case PGNTag:
+ switch (lastLoadGameStart) {
+ case GNUChessGame:
+ case PGNTag:
+ case MoveNumberOne:
+ case (ChessMove) 0:
+ gn--; /* count this game */
+ lastLoadGameStart = cm;
+ break;
+ case XBoardGame:
+ lastLoadGameStart = cm; /* game counted already */
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+ if (gn > 0) {
+ do {
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+ } while (cm == PGNTag || cm == Comment);
+ }
+ break;
+
+ case WhiteWins:
+ case BlackWins:
+ case GameIsDrawn:
+ if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {
+ if ( cmailResult[CMAIL_MAX_GAMES - gn - 1]
+ != CMAIL_OLD_RESULT) {
+ nCmailResults ++ ;
+ cmailResult[ CMAIL_MAX_GAMES
+ - gn - 1] = CMAIL_OLD_RESULT;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ yyskipmoves = FALSE;
+
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
+
+ if (cm == XBoardGame) {
+ /* Skip any header junk before position diagram and/or move 1 */
+ for (;;) {
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+
+ if (cm == (ChessMove) 0 ||
+ cm == GNUChessGame || cm == XBoardGame) {
+ /* Empty game; pretend end-of-file and handle later */
+ cm = (ChessMove) 0;
+ break;
+ }
+
+ if (cm == MoveNumberOne || cm == PositionDiagram ||
+ cm == PGNTag || cm == Comment)
+ break;
+ }
+ } else if (cm == GNUChessGame) {
+ if (gameInfo.event != NULL) {
+ free(gameInfo.event);
+ }
+ gameInfo.event = StrSave(yy_text);
+ }
+
+ startedFromSetupPosition = FALSE;
+ while (cm == PGNTag) {
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
+ err = ParsePGNTag(yy_text, &gameInfo);
+ if (!err) numPGNTags++;
+
+ if (gameInfo.fen != NULL) {
+ Board initial_position;
+ startedFromSetupPosition = TRUE;
+ if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
+ Reset(TRUE, TRUE);
+ DisplayError("Bad FEN position in file", 0);
+ return FALSE;
+ }
+ CopyBoard(boards[0], initial_position);
+ if (blackPlaysFirst) {
+ currentMove = forwardMostMove = backwardMostMove = 1;
+ CopyBoard(boards[1], initial_position);
+ strcpy(moveList[0], "");
+ strcpy(parseList[0], "");
+ timeRemaining[0][1] = whiteTimeRemaining;
+ timeRemaining[1][1] = blackTimeRemaining;
+ if (commentList[0] != NULL) {
+ commentList[1] = commentList[0];
+ commentList[0] = NULL;
+ }
+ } else {
+ currentMove = forwardMostMove = backwardMostMove = 0;
+ }
+ yyboardindex = forwardMostMove;
+ free(gameInfo.fen);
+ gameInfo.fen = NULL;
+ }
+
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+
+ /* Handle comments interspersed among the tags */
+ while (cm == Comment) {
+ char *p;
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
+ p = yy_text;
+ if (*p == '{' || *p == '[' || *p == '(') {
+ p[strlen(p) - 1] = NULLCHAR;
+ p++;
+ }
+ while (*p == '\n') p++;
+ AppendComment(currentMove, p);
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+ }
+ }
+
+ /* don't rely on existence of Event tag since if game was
+ * pasted from clipboard the Event tag may not exist
+ */
+ if (numPGNTags > 0){
+ char *tags;
+ if (gameInfo.variant == VariantNormal) {
+ gameInfo.variant = StringToVariant(gameInfo.event);
+ }
+ if (!matchMode) {
+ tags = PGNTags(&gameInfo);
+ TagsPopUp(tags, CmailMsg());
+ free(tags);
+ }
+ } else {
+ /* Make something up, but don't display it now */
+ SetGameInfo();
+ TagsPopDown();
+ }
+
+ if (cm == PositionDiagram) {
+ int i, j;
+ char *p;
+ Board initial_position;
+
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);
+
+ if (!startedFromSetupPosition) {
+ p = yy_text;
+ for (i = BOARD_SIZE - 1; i >= 0; i--)
+ for (j = 0; j < BOARD_SIZE; p++)
+ switch (*p) {
+ case '[':
+ case '-':
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ initial_position[i][j++] = CharToPiece(*p);
+ break;
+ }
+ while (*p == ' ' || *p == '\t' ||
+ *p == '\n' || *p == '\r') p++;
+
+ if (strncmp(p, "black", strlen("black"))==0)
+ blackPlaysFirst = TRUE;
+ else
+ blackPlaysFirst = FALSE;
+ startedFromSetupPosition = TRUE;
+
+ CopyBoard(boards[0], initial_position);
+ if (blackPlaysFirst) {
+ currentMove = forwardMostMove = backwardMostMove = 1;
+ CopyBoard(boards[1], initial_position);
+ strcpy(moveList[0], "");
+ strcpy(parseList[0], "");
+ timeRemaining[0][1] = whiteTimeRemaining;
+ timeRemaining[1][1] = blackTimeRemaining;
+ if (commentList[0] != NULL) {
+ commentList[1] = commentList[0];
+ commentList[0] = NULL;
+ }
+ } else {
+ currentMove = forwardMostMove = backwardMostMove = 0;
+ }
+ }
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+ }
+
+ if (first.pr == NoProc) {
+ StartChessProgram(&first);
+ }
+ InitChessProgram(&first);
+ SendToProgram("force\n", &first);
+ if (startedFromSetupPosition) {
+ SendBoard(&first, forwardMostMove);
+ DisplayBothClocks();
+ }
+
+ while (cm == Comment) {
+ char *p;
+ if (appData.debugMode)
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
+ p = yy_text;
+ if (*p == '{' || *p == '[' || *p == '(') {
+ p[strlen(p) - 1] = NULLCHAR;
+ p++;
+ }
+ while (*p == '\n') p++;
+ AppendComment(currentMove, p);
+ yyboardindex = forwardMostMove;
+ cm = (ChessMove) yylex();
+ }
+
+ if (cm == (ChessMove) 0 || cm == WhiteWins || cm == BlackWins ||
+ cm == GameIsDrawn || cm == GameUnfinished) {
+ DisplayMessage("", "No moves in game");
+ if (cmailMsgLoaded) {
+ if (appData.debugMode)
+ fprintf(debugFP, "Setting flipView to %d.\n", FALSE);
+ ClearHighlights();
+ flipView = FALSE;
+ }
+ DrawPosition(FALSE, boards[currentMove]);
+ DisplayBothClocks();
+ gameMode = EditGame;
+ ModeHighlight();
+ gameFileFP = NULL;
+ cmailOldMove = 0;
+ return TRUE;
+ }
+
+ if (commentList[currentMove] != NULL) {
+ if (!matchMode && (pausing || appData.timeDelay != 0)) {
+ DisplayComment(currentMove - 1, commentList[currentMove]);
+ }
+ }
+ if (!matchMode && appData.timeDelay != 0)
+ DrawPosition(FALSE, boards[currentMove]);
+
+ if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
+ programStats.ok_to_send = 1;
+ }
+
+ /* if the first token after the PGN tags is a move
+ * and not move number 1, retrieve it from the parser
+ */
+ if (cm != MoveNumberOne)
+ LoadGameOneMove(cm);
+
+ /* load the remaining moves from the file */
+ while (LoadGameOneMove((ChessMove)0)) {
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;
+ }
+
+ /* rewind to the start of the game */
+ currentMove = backwardMostMove;
+
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+
+ if (oldGameMode == AnalyzeFile ||
+ oldGameMode == AnalyzeMode) {
+ AnalyzeFileEvent();
+ }
+
+ if (matchMode || appData.timeDelay == 0) {
+ ToEndEvent();
+ gameMode = EditGame;
+ ModeHighlight();
+ } else if (appData.timeDelay > 0) {
+ AutoPlayGameLoop();
+ }
+
+ if (appData.debugMode)
+ fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
+ return TRUE;
+}
+
+/* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
+int
+ReloadPosition(offset)
+ int offset;
+{
+ int positionNumber = lastLoadPositionNumber + offset;
+ if (lastLoadPositionFP == NULL) {
+ DisplayError("No position has been loaded yet", 0);
+ return FALSE;
+ }
+ if (positionNumber <= 0) {
+ DisplayError("Can't back up any further", 0);
+ return FALSE;
+ }
+ return LoadPosition(lastLoadPositionFP, positionNumber,
+ lastLoadPositionTitle);
+}
+
+/* Load the nth position from the given file */
+int
+LoadPositionFromFile(filename, n, title)
+ char *filename;
+ int n;
+ char *title;
+{
+ FILE *f;
+ char buf[MSG_SIZ];
+
+ if (strcmp(filename, "-") == 0) {
+ return LoadPosition(stdin, n, "stdin");
+ } else {
+ f = fopen(filename, "rb");
+ if (f == NULL) {
+ sprintf(buf, "Can't open \"%s\"", filename);
+ DisplayError(buf, errno);
+ return FALSE;
+ } else {
+ return LoadPosition(f, n, title);
+ }
+ }
+}
+
+/* Load the nth position from the given open file, and close it */
+int
+LoadPosition(f, positionNumber, title)
+ FILE *f;
+ int positionNumber;
+ char *title;
+{
+ char *p, line[MSG_SIZ];
+ Board initial_position;
+ int i, j, fenMode, pn;
+
+ if (gameMode == Training )
+ SetTrainingModeOff();
+
+ if (gameMode != BeginningOfGame) {
+ Reset(FALSE, TRUE);
+ }
+ if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {
+ fclose(lastLoadPositionFP);
+ }
+ if (positionNumber == 0) positionNumber = 1;
+ lastLoadPositionFP = f;
+ lastLoadPositionNumber = positionNumber;
+ strcpy(lastLoadPositionTitle, title);
+ if (first.pr == NoProc) {
+ StartChessProgram(&first);
+ InitChessProgram(&first);
+ }
+ pn = positionNumber;
+ 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);
+ return FALSE;
+ };
+ pn = 1;
+ } else {
+ if (fseek(f, 0, 0) == -1) {
+ if (f == lastLoadPositionFP ?
+ positionNumber == lastLoadPositionNumber + 1 :
+ positionNumber == 1) {
+ pn = 1;
+ } else {
+ 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);
+ return FALSE;
+ }
+ switch (line[0]) {
+ case '#': case 'x':
+ default:
+ fenMode = FALSE;
+ break;
+ case 'p': case 'n': case 'b': case 'r': case 'q': case 'k':
+ case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':
+ case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8':
+ fenMode = TRUE;
+ break;
+ }
+
+ if (pn >= 2) {
+ if (fenMode || line[0] == '#') pn--;
+ while (pn > 0) {
+ /* skip postions before number pn */
+ if (fgets(line, MSG_SIZ, f) == NULL) {
+ DisplayError("Position not found in file", 0);
+ return FALSE;
+ }
+ if (fenMode || line[0] == '#') pn--;
+ }
+ }
+
+ if (fenMode) {
+ if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
+ DisplayError("Bad FEN position in file", 0);
+ return FALSE;
+ }
+ } else {
+ (void) fgets(line, MSG_SIZ, f);
+ (void) fgets(line, MSG_SIZ, f);
+
+ for (i = BOARD_SIZE - 1; i >= 0; i--) {
+ (void) fgets(line, MSG_SIZ, f);
+ for (p = line, j = 0; j < BOARD_SIZE; p++) {
+ if (*p == ' ')
+ continue;
+ initial_position[i][j++] = CharToPiece(*p);
+ }
+ }
+
+ blackPlaysFirst = FALSE;
+ if (!feof(f)) {
+ (void) fgets(line, MSG_SIZ, f);
+ if (strncmp(line, "black", strlen("black"))==0)
+ blackPlaysFirst = TRUE;
+ }
+ }
+ startedFromSetupPosition = TRUE;
+
+ SendToProgram("force\n", &first);
+ CopyBoard(boards[0], initial_position);
+ if (blackPlaysFirst) {
+ currentMove = forwardMostMove = backwardMostMove = 1;
+ strcpy(moveList[0], "");
+ strcpy(parseList[0], "");
+ CopyBoard(boards[1], initial_position);
+ DisplayMessage("", "Black to play");
+ } else {
+ currentMove = forwardMostMove = backwardMostMove = 0;
+ DisplayMessage("", "White to play");
+ }
+ SendBoard(&first, forwardMostMove);
+
+ if (positionNumber > 1) {
+ sprintf(line, "%s %d", title, positionNumber);
+ DisplayTitle(line);
+ } else {
+ DisplayTitle(title);
+ }
+ gameMode = EditGame;
+ ModeHighlight();
+ ResetClocks();
+ timeRemaining[0][1] = whiteTimeRemaining;
+ timeRemaining[1][1] = blackTimeRemaining;
+ DrawPosition(FALSE, boards[currentMove]);
+
+ return TRUE;
+}
+
+
+void
+CopyPlayerNameIntoFileName(dest, src)
+ char **dest, *src;
+{
+ while (*src != NULLCHAR && *src != ',') {
+ if (*src == ' ') {
+ *(*dest)++ = '_';
+ src++;
+ } else {
+ *(*dest)++ = *src++;
+ }
+ }
+}
+
+char *DefaultFileName(ext)
+ char *ext;
+{
+ static char def[MSG_SIZ];
+ char *p;
+
+ if (gameInfo.white != NULL && gameInfo.white[0] != '-') {
+ p = def;
+ CopyPlayerNameIntoFileName(&p, gameInfo.white);
+ *p++ = '-';
+ CopyPlayerNameIntoFileName(&p, gameInfo.black);
+ *p++ = '.';
+ strcpy(p, ext);
+ } else {
+ def[0] = NULLCHAR;
+ }
+ return def;
+}
+
+/* Save the current game to the given file */
+int
+SaveGameToFile(filename, append)
+ char *filename;
+ int append;
+{
+ FILE *f;
+ char buf[MSG_SIZ];
+
+ if (strcmp(filename, "-") == 0) {
+ return SaveGame(stdout, 0, NULL);
+ } else {
+ f = fopen(filename, append ? "a" : "w");
+ if (f == NULL) {
+ sprintf(buf, "Can't open \"%s\"", filename);
+ DisplayError(buf, errno);
+ return FALSE;
+ } else {
+ return SaveGame(f, 0, NULL);
+ }
+ }
+}
+
+char *
+SavePart(str)
+ char *str;
+{
+ static char buf[MSG_SIZ];
+ char *p;
+
+ p = strchr(str, ' ');
+ if (p == NULL) return str;
+ strncpy(buf, str, p - str);
+ buf[p - str] = NULLCHAR;
+ return buf;
+}
+
+#define PGN_MAX_LINE 75
+
+/* Save game in PGN style and close the file */
+int
+SaveGamePGN(f)
+ FILE *f;
+{
+ int i, offset, linelen, newblock;
+ time_t tm;
+ char *movetext;
+ char numtext[32];
+ int movelen, numlen, blank;
+
+ tm = time((time_t *) NULL);
+
+ PrintPGNTags(f, &gameInfo);
+
+ if (backwardMostMove > 0 || startedFromSetupPosition) {
+ char *fen = PositionToFEN(backwardMostMove);
+ fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
+ fprintf(f, "\n{--------------\n");
+ PrintPosition(f, backwardMostMove);
+ fprintf(f, "--------------}\n");
+ free(fen);
+ } else {
+ fprintf(f, "\n");
+ }
+
+ i = backwardMostMove;
+ offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+ linelen = 0;
+ newblock = TRUE;
+
+ while (i < forwardMostMove) {
+ /* Print comments preceding this move */
+ if (commentList[i] != NULL) {
+ if (linelen > 0) fprintf(f, "\n");
+ fprintf(f, "{\n%s}\n", commentList[i]);
+ linelen = 0;
+ newblock = TRUE;
+ }
+
+ /* Format move number */
+ if ((i % 2) == 0) {
+ sprintf(numtext, "%d.", (i - offset)/2 + 1);
+ } else {
+ if (newblock) {
+ sprintf(numtext, "%d...", (i - offset)/2 + 1);
+ } else {
+ numtext[0] = NULLCHAR;
+ }
+ }
+ numlen = strlen(numtext);
+ newblock = FALSE;
+
+ /* Print move number */
+ blank = linelen > 0 && numlen > 0;
+ if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {
+ fprintf(f, "\n");
+ linelen = 0;
+ blank = 0;
+ }
+ if (blank) {
+ fprintf(f, " ");
+ linelen++;
+ }
+ fprintf(f, numtext);
+ linelen += numlen;
+
+ /* Get move */
+ movetext = SavePart(parseList[i]);
+ movelen = strlen(movetext);
+
+ /* Print move */
+ blank = linelen > 0 && movelen > 0;
+ if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
+ fprintf(f, "\n");
+ linelen = 0;
+ blank = 0;
+ }
+ if (blank) {
+ fprintf(f, " ");
+ linelen++;
+ }
+ fprintf(f, movetext);
+ linelen += movelen;
+
+ i++;
+ }
+
+ /* Start a new line */
+ if (linelen > 0) fprintf(f, "\n");
+
+ /* Print comments after last move */
+ if (commentList[i] != NULL) {
+ fprintf(f, "{\n%s}\n", commentList[i]);
+ }
+
+ /* Print result */
+ if (gameInfo.resultDetails != NULL &&
+ gameInfo.resultDetails[0] != NULLCHAR) {
+ fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,
+ PGNResult(gameInfo.result));
+ } else {
+ fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
+ }
+
+ fclose(f);
+ return TRUE;
+}
+
+/* Save game in old style and close the file */
+int
+SaveGameOldStyle(f)
+ FILE *f;
+{
+ int i, offset;
+ time_t tm;
+
+ tm = time((time_t *) NULL);
+
+ fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
+ PrintOpponents(f);
+
+ if (backwardMostMove > 0 || startedFromSetupPosition) {
+ fprintf(f, "\n[--------------\n");
+ PrintPosition(f, backwardMostMove);
+ fprintf(f, "--------------]\n");
+ } else {
+ fprintf(f, "\n");
+ }
+
+ i = backwardMostMove;
+ offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+
+ while (i < forwardMostMove) {
+ if (commentList[i] != NULL) {
+ fprintf(f, "[%s]\n", commentList[i]);
+ }
+
+ if ((i % 2) == 1) {
+ fprintf(f, "%d. ... %s\n", (i - offset)/2 + 1, parseList[i]);
+ i++;
+ } else {
+ fprintf(f, "%d. %s ", (i - offset)/2 + 1, parseList[i]);
+ i++;
+ if (commentList[i] != NULL) {
+ fprintf(f, "\n");
+ continue;
+ }
+ if (i >= forwardMostMove) {
+ fprintf(f, "\n");
+ break;
+ }
+ fprintf(f, "%s\n", parseList[i]);
+ i++;
+ }
+ }
+
+ if (commentList[i] != NULL) {
+ fprintf(f, "[%s]\n", commentList[i]);
+ }
+
+ /* This isn't really the old style, but it's close enough */
+ if (gameInfo.resultDetails != NULL &&
+ gameInfo.resultDetails[0] != NULLCHAR) {
+ fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),
+ gameInfo.resultDetails);
+ } else {
+ fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
+ }
+
+ fclose(f);
+ return TRUE;
+}
+
+/* Save the current game to open file f and close the file */
+int
+SaveGame(f, dummy, dummy2)
+ FILE *f;
+ int dummy;
+ char *dummy2;
+{
+ if (gameMode == EditPosition) EditPositionDone();
+ if (appData.oldSaveStyle)
+ return SaveGameOldStyle(f);
+ else
+ return SaveGamePGN(f);
+}
+
+/* Save the current position to the given file */
+int
+SavePositionToFile(filename)
+ char *filename;
+{
+ FILE *f;
+ char buf[MSG_SIZ];
+
+ if (strcmp(filename, "-") == 0) {
+ return SavePosition(stdout, 0, NULL);
+ } else {
+ f = fopen(filename, "a");
+ if (f == NULL) {
+ sprintf(buf, "Can't open \"%s\"", filename);
+ DisplayError(buf, errno);
+ return FALSE;
+ } else {
+ SavePosition(f, 0, NULL);
+ return TRUE;
+ }
+ }
+}
+
+/* Save the current position to the given open file and close the file */
+int
+SavePosition(f, dummy, dummy2)
+ FILE *f;
+ int dummy;
+ char *dummy2;
+{
+ time_t tm;
+ char *fen;
+
+ if (appData.oldSaveStyle) {
+ tm = time((time_t *) NULL);
+
+ fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
+ PrintOpponents(f);
+ fprintf(f, "[--------------\n");
+ PrintPosition(f, currentMove);
+ fprintf(f, "--------------]\n");
+ } else {
+ fen = PositionToFEN(currentMove);
+ fprintf(f, "%s\n", fen);
+ free(fen);
+ }
+ fclose(f);
+ return TRUE;
+}
+
+void
+ReloadCmailMsgEvent(unregister)
+ int unregister;
+{
+ static char *inFilename = NULL;
+ static char *outFilename;
+ int i;
+ struct stat inbuf, outbuf;
+ int status;
+
+ /* Any registered moves are unregistered if unregister is set, */
+ /* i.e. invoked by the signal handler */
+ if (unregister) {
+ for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
+ cmailMoveRegistered[i] = FALSE;
+ if (cmailCommentList[i] != NULL) {
+ free(cmailCommentList[i]);
+ cmailCommentList[i] = NULL;
+ }
+ }
+ nCmailMovesRegistered = 0;
+ }
+
+ for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
+ cmailResult[i] = CMAIL_NOT_RESULT;
+ }
+ nCmailResults = 0;
+
+ if (inFilename == NULL) {
+ /* Because the filenames are static they only get malloced once */
+ /* and they never get freed */
+ inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);
+ sprintf(inFilename, "%s.game.in", appData.cmailGameName);
+
+ outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
+ sprintf(outFilename, "%s.out", appData.cmailGameName);
+ }
+
+ status = stat(outFilename, &outbuf);
+ if (status < 0) {
+ cmailMailedMove = FALSE;
+ } else {
+ status = stat(inFilename, &inbuf);
+ cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
+ }
+
+ /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
+ counts the games, notes how each one terminated, etc.
+
+ It would be nice to remove this kludge and instead gather all
+ the information while building the game list. (And to keep it
+ in the game list nodes instead of having a bunch of fixed-size
+ parallel arrays.) Note this will require getting each game's
+ termination from the PGN tags, as the game list builder does
+ not process the game moves. --mann
+ */
+ cmailMsgLoaded = TRUE;
+ LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
+
+ /* Load first game in the file or popup game menu */
+ LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
+
+ return;
+}
+
+int
+RegisterMove()
+{
+ FILE *f;
+ char string[MSG_SIZ];
+
+ if ( cmailMailedMove
+ || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {
+ return TRUE; /* Allow free viewing */
+ }
+
+ /* Unregister move to ensure that we don't leave RegisterMove */
+ /* with the move registered when the conditions for registering no */
+ /* longer hold */
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
+ cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
+ nCmailMovesRegistered --;
+
+ if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
+ {
+ free(cmailCommentList[lastLoadGameNumber - 1]);
+ cmailCommentList[lastLoadGameNumber - 1] = NULL;
+ }
+ }
+
+ if (cmailOldMove == -1) {
+ 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);
+ return FALSE;
+ }
+
+ if (currentMove < cmailOldMove) {
+ DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);
+ return FALSE;
+ }
+
+ if (forwardMostMove > currentMove) {
+ /* Silently truncate extra moves */
+ TruncateGame();
+ }
+
+ if ( (currentMove == cmailOldMove + 1)
+ || ( (currentMove == cmailOldMove)
+ && ( (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)
+ || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {
+ if (gameInfo.result != GameUnfinished) {
+ cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;
+ }
+
+ if (commentList[currentMove] != NULL) {
+ cmailCommentList[lastLoadGameNumber - 1]
+ = StrSave(commentList[currentMove]);
+ }
+ strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
+
+ if (appData.debugMode)
+ fprintf(debugFP, "Saving %s for game %d\n",
+ cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
+
+ sprintf(string,
+ "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
+
+ f = fopen(string, "w");
+ if (appData.oldSaveStyle) {
+ SaveGameOldStyle(f); /* also closes the file */
+
+ sprintf(string, "%s.pos.out", appData.cmailGameName);
+ f = fopen(string, "w");
+ SavePosition(f, 0, NULL); /* also closes the file */
+ } else {
+ fprintf(f, "{--------------\n");
+ PrintPosition(f, currentMove);
+ fprintf(f, "--------------}\n\n");
+
+ SaveGame(f, 0, NULL); /* also closes the file*/
+ }
+
+ cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
+ nCmailMovesRegistered ++;
+ } else if (nCmailGames == 1) {
+ DisplayError("You have not made a move yet", 0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+MailMoveEvent()
+{
+ static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
+ FILE *commandOutput;
+ char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];
+ int nBytes = 0; /* Suppress warnings on uninitialized variables */
+ int nBuffers;
+ int i;
+ int archived;
+ char *arcDir;
+
+ if (! cmailMsgLoaded) {
+ 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);
+ 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);
+ DisplayError(msg, 0);
+ return;
+ }
+#endif
+
+ if (! (cmailMailedMove || RegisterMove())) return;
+
+ if ( cmailMailedMove
+ || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
+ sprintf(string, partCommandString,
+ appData.debugMode ? " -v" : "", appData.cmailGameName);
+ commandOutput = popen(string, "r");
+
+ if (commandOutput == NULL) {
+ DisplayError("Failed to invoke cmail", 0);
+ } else {
+ for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {
+ nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);
+ }
+ if (nBuffers > 1) {
+ (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);
+ (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);
+ nBytes = MSG_SIZ - 1;
+ } else {
+ (void) memcpy(msg, buffer, nBytes);
+ }
+ *(msg + nBytes) = '\0'; /* \0 for end-of-string*/
+
+ if(StrStr(msg, "Mailed cmail message to ") != NULL) {
+ cmailMailedMove = TRUE; /* Prevent >1 moves */
+
+ archived = TRUE;
+ for (i = 0; i < nCmailGames; i ++) {
+ if (cmailResult[i] == CMAIL_NOT_RESULT) {
+ archived = FALSE;
+ }
+ }
+ if ( archived
+ && ( (arcDir = (char *) getenv("CMAIL_ARCDIR"))
+ != NULL)) {
+ sprintf(buffer, "%s/%s.%s.archive",
+ arcDir,
+ appData.cmailGameName,
+ gameInfo.date);
+ LoadGameFromFile(buffer, 1, buffer, FALSE);
+ cmailMsgLoaded = FALSE;
+ }
+ }
+
+ DisplayInformation(msg);
+ pclose(commandOutput);
+ }
+ } else {
+ if ((*cmailMsg) != '\0') {
+ DisplayInformation(cmailMsg);
+ }
+ }
+
+ return;
+}
+
+char *
+CmailMsg()
+{
+ int prependComma = 0;
+ char number[5];
+ char string[MSG_SIZ]; /* Space for game-list */
+ int i;
+
+ if (!cmailMsgLoaded) return "";
+
+ if (cmailMailedMove) {
+ sprintf(cmailMsg, "Waiting for reply from opponent\n");
+ } else {
+ /* Create a list of games left */
+ sprintf(string, "[");
+ for (i = 0; i < nCmailGames; i ++) {
+ if (! ( cmailMoveRegistered[i]
+ || (cmailResult[i] == CMAIL_OLD_RESULT))) {
+ if (prependComma) {
+ sprintf(number, ",%d", i + 1);
+ } else {
+ sprintf(number, "%d", i + 1);
+ prependComma = 1;
+ }
+
+ strcat(string, number);
+ }
+ }
+ strcat(string, "]");
+
+ if (nCmailMovesRegistered + nCmailResults == 0) {
+ switch (nCmailGames) {
+ case 1:
+ sprintf(cmailMsg,
+ "Still need to make move for game\n");
+ break;
+
+ case 2:
+ sprintf(cmailMsg,
+ "Still need to make moves for both games\n");
+ break;
+
+ default:
+ sprintf(cmailMsg,
+ "Still need to make moves for all %d games\n",
+ nCmailGames);
+ break;
+ }
+ } else {
+ switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
+ case 1:
+ sprintf(cmailMsg,
+ "Still need to make a move for game %s\n",
+ string);
+ break;
+
+ case 0:
+ if (nCmailResults == nCmailGames) {
+ sprintf(cmailMsg, "No unfinished games\n");
+ } else {
+ sprintf(cmailMsg, "Ready to send mail\n");
+ }
+ break;
+
+ default:
+ sprintf(cmailMsg,
+ "Still need to make moves for games %s\n",
+ string);
+ }
+ }
+ }
+
+ return cmailMsg;
+}
+
+void
+ResetGameEvent()
+{
+ if (gameMode == Training)
+ SetTrainingModeOff();
+
+ Reset(TRUE, TRUE);
+ cmailMsgLoaded = FALSE;
+ if (appData.icsActive) {
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ }
+}
+
+static int exiting = 0;
+
+void
+ExitEvent(status)
+ int status;
+{
+ exiting++;
+ if (exiting > 2) {
+ /* Give up on clean exit */
+ exit(status);
+ }
+ if (exiting > 1) {
+ /* Keep trying for clean exit */
+ return;
+ }
+
+ if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);
+
+ if (telnetISR != NULL) {
+ RemoveInputSource(telnetISR);
+ }
+ if (icsPR != NoProc) {
+ DestroyChildProcess(icsPR, TRUE);
+ }
+ /* Save game if resource set and not already saved by GameEnds() */
+ if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {
+ if (*appData.saveGameFile != NULLCHAR) {
+ SaveGameToFile(appData.saveGameFile, TRUE);
+ } else if (appData.autoSaveGames) {
+ AutoSaveGame();
+ }
+ if (*appData.savePositionFile != NULLCHAR) {
+ SavePositionToFile(appData.savePositionFile);
+ }
+ }
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);
+
+ /* Kill off chess programs */
+ if (first.pr != NoProc) {
+ ExitAnalyzeMode();
+ SendToProgram("quit\n", &first);
+ DestroyChildProcess(first.pr, first.useSigterm);
+ }
+ if (second.pr != NoProc) {
+ SendToProgram("quit\n", &second);
+ DestroyChildProcess(second.pr, second.useSigterm);
+ }
+ if (first.isr != NULL) {
+ RemoveInputSource(first.isr);
+ }
+ if (second.isr != NULL) {
+ RemoveInputSource(second.isr);
+ }
+
+ ShutDownFrontEnd();
+ exit(status);
+}
+
+void
+PauseEvent()
+{
+ if (appData.debugMode)
+ fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
+ if (pausing) {
+ pausing = FALSE;
+ ModeHighlight();
+ if (gameMode == MachinePlaysWhite ||
+ gameMode == MachinePlaysBlack) {
+ StartClocks();
+ } else {
+ DisplayBothClocks();
+ }
+ if (gameMode == PlayFromGameFile) {
+ if (appData.timeDelay >= 0)
+ AutoPlayGameLoop();
+ } else if (gameMode == IcsExamining && pauseExamInvalid) {
+ Reset(FALSE, TRUE);
+ SendToICS(ics_prefix);
+ SendToICS("refresh\n");
+ } else if (currentMove < forwardMostMove) {
+ ForwardInner(forwardMostMove);
+ }
+ pauseExamInvalid = FALSE;
+ } else {
+ switch (gameMode) {
+ default:
+ return;
+ case IcsExamining:
+ pauseExamForwardMostMove = forwardMostMove;
+ pauseExamInvalid = FALSE;
+ /* fall through */
+ case IcsObserving:
+ case IcsPlayingWhite:
+ case IcsPlayingBlack:
+ pausing = TRUE;
+ ModeHighlight();
+ return;
+ case PlayFromGameFile:
+ (void) StopLoadGameTimer();
+ pausing = TRUE;
+ ModeHighlight();
+ break;
+ case BeginningOfGame:
+ if (appData.icsActive) return;
+ /* else fall through */
+ case MachinePlaysWhite:
+ case MachinePlaysBlack:
+ case TwoMachinesPlay:
+ if (forwardMostMove == 0)
+ return; /* don't pause if no one has moved */
+ if ((gameMode == MachinePlaysWhite &&
+ !WhiteOnMove(forwardMostMove)) ||
+ (gameMode == MachinePlaysBlack &&
+ WhiteOnMove(forwardMostMove))) {
+ StopClocks();
+ }
+ pausing = TRUE;
+ ModeHighlight();
+ break;
+ }
+ }
+}
+
+void
+EditCommentEvent()
+{
+ char title[MSG_SIZ];
+
+ if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
+ strcpy(title, "Edit comment");
+ } else {
+ sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,
+ WhiteOnMove(currentMove - 1) ? " " : ".. ",
+ parseList[currentMove - 1]);
+ }
+
+ EditCommentPopUp(currentMove, title, commentList[currentMove]);
+}
+
+
+void
+EditTagsEvent()
+{
+ char *tags = PGNTags(&gameInfo);
+ EditTagsPopUp(tags);
+ free(tags);
+}
+
+void
+AnalyzeModeEvent()
+{
+ if (appData.noChessProgram || gameMode == AnalyzeMode)
+ return;
+
+ /* Engine Room enable */
+ /* We need a AnalysisDisplay(TRUE) for timer */
+ appData.AnalysisWindow = TRUE;
+
+ if (gameMode != AnalyzeFile) {
+ EditGameEvent();
+ if (gameMode != EditGame) return;
+ ResurrectChessProgram();
+ SendToProgram("analyze\n", &first);
+ first.analyzing = TRUE;
+ /*first.maybeThinking = TRUE;*/
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */
+ }
+ gameMode = AnalyzeMode;
+ pausing = FALSE;
+ ModeHighlight();
+ SetGameInfo();
+ StartAnalysisClock();
+ GetTimeMark(&lastNodeCountTime);
+ lastNodeCount = 0;
+ PeriodicUpdatesEvent(TRUE);
+}
+
+/* daniel */
+void IcsAnalyze(newState)
+int newState;
+
+{
+ if (appData.noChessProgram) {
+ DisplayFatalError("ICS-Analysis requires a chess engine", 0, 1);
+ return;
+ }
+
+ if (!newState && first.analyzing) {
+ if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: Shutting down engine\n");
+ ExitAnalyzeMode();
+ DisplayMessage("","Close ICS engine analyse...");
+ return;
+ }
+
+ if (gameMode != IcsObserving) {
+ MessageBox(0,"You are not observing a game",
+ "ICS Engine Analyse", MB_OK);
+ appData.icsAnalyze = FALSE;
+ /* Set state for GUI */
+ appData.icsEngineNone = TRUE;
+ appData.icsEngineWhisper = FALSE;
+ appData.icsEngineKibitz = FALSE;
+ appData.icsEngineTell = FALSE;
+ appData.icsAnalyzeOutPut = 4;
+ return;
+ }
+
+ if (first.analysisSupport == FALSE) {
+ MessageBox(0,"Sorry, this engine does not support analyze",
+ "ICS Engine Analyse", MB_OK);
+ return;
+ }
+
+ if (newState) {
+ if (appData.debugMode) fprintf(debugFP, "Starting ICS analyze\n");
+ DisplayMessage("","Starting ICS engine analyse");
+ if (!appData.icsWBprotoAgr) {
+ /* safty */
+ /* Wo do that only if we start new or have a problem */
+ SendToProgram("new\n", &first);
+ SendToProgram("post\n", &first);
+ SendToProgram("force\n", &first);
+ FeedMovesToProgram(&first, currentMove);
+ SendToProgram("analyze\n", &first);
+ } else {
+ /* hard */
+ SendToProgram("exit\n", &first);
+ SendToProgram("new\n", &first);
+ SendToProgram("post\n", &first);
+ SendToProgram("hart\n", &first);
+ SendToProgram("force\n", &first);
+ FeedMovesToProgram(&first, currentMove);
+ SendToProgram("analyze\n", &first);
+ SendToProgram(".\n", &first);
+ }
+ PeriodicUpdatesEvent(TRUE); /* appData.periodicUpdates */
+ first.analyzing = TRUE; /* Used for GUI and other ways */
+ }
+}
+
+void
+IcsAnalyzeWindowUp()
+{
+ /* Using allso for startup Engine Room
+ * So function name looks like mismatch
+ * Change it for a better program style -
+ * but this is not crirical
+ */
+
+ if (appData.AnalysisWindow) {
+ ClearProgramStats();
+ DisplayAnalysis(0,0);
+ PeriodicUpdatesEvent(TRUE);
+ }
+ /* StartAnalysisClock check gameMode */
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
+ appData.icsAnalyze || appData.AnalysisWindow) {
+ StartAnalysisClock();
+ }
+ GetTimeMark(&lastNodeCountTime);
+ lastNodeCount = 0; /* importent */
+}
+
+void
+AnalyzeFileEvent()
+{
+ if (appData.noChessProgram || gameMode == AnalyzeFile)
+ return;
+
+ /* Engine Room */
+ /* We need a DisplayAnalysis(TRUE); */
+ appData.AnalysisWindow = TRUE;
+
+ if (gameMode != AnalyzeMode) {
+ EditGameEvent();
+ if (gameMode != EditGame) return;
+ ResurrectChessProgram();
+ SendToProgram("analyze\n", &first);
+ first.analyzing = TRUE;
+ /*first.maybeThinking = TRUE;*/
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */
+ }
+ gameMode = AnalyzeFile;
+ pausing = FALSE;
+ ModeHighlight();
+ SetGameInfo();
+
+ StartAnalysisClock();
+ GetTimeMark(&lastNodeCountTime);
+ lastNodeCount = 0;
+}
+
+void
+MachineWhiteEvent()
+{
+ char buf[MSG_SIZ];
+
+ if (appData.noChessProgram || (gameMode == MachinePlaysWhite))
+ return;
+
+
+ if (gameMode == PlayFromGameFile ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == Training ||
+ gameMode == AnalyzeMode ||
+ gameMode == EndOfGame)
+ EditGameEvent();
+
+ if (gameMode == EditPosition)
+ EditPositionDone();
+
+ if (!WhiteOnMove(currentMove)) {
+ DisplayError("It is not White's turn", 0);
+ return;
+ }
+
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
+ ExitAnalyzeMode();
+
+
+ if (gameMode == EditGame || gameMode == AnalyzeMode ||
+ gameMode == AnalyzeFile)
+ TruncateGame();
+
+ ResurrectChessProgram(); /* in case it isn't running */
+ gameMode = MachinePlaysWhite;
+ pausing = FALSE;
+ ModeHighlight();
+ SetGameInfo();
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ DisplayTitle(buf);
+ if (first.sendName) {
+ sprintf(buf, "name %s\n", gameInfo.black);
+ SendToProgram(buf, &first);
+ }
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("black\n", &first); /*gnu kludge*/
+ }
+ SendTimeRemaining(&first, TRUE);
+ }
+ if (first.useColors) {
+ SendToProgram("white\ngo\n", &first);
+ } else {
+ SendToProgram("go\n", &first);
+ }
+ SetMachineThinkingEnables();
+ first.maybeThinking = TRUE;
+ StartClocks();
+
+ if (appData.autoFlipView && !flipView) {
+ flipView = !flipView;
+ DrawPosition(FALSE, NULL);
+ }
+ if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
+}
+
+void
+MachineBlackEvent()
+{
+ char buf[MSG_SIZ];
+
+ if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
+ return;
+
+
+ if (gameMode == PlayFromGameFile ||
+ gameMode == TwoMachinesPlay ||
+ gameMode == Training ||
+ gameMode == AnalyzeMode ||
+ gameMode == EndOfGame)
+ EditGameEvent();
+
+ if (gameMode == EditPosition)
+ EditPositionDone();
+
+ if (WhiteOnMove(currentMove)) {
+ DisplayError("It is not Black's turn", 0);
+ return;
+ }
+
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
+ ExitAnalyzeMode();
+
+ if (gameMode == EditGame || gameMode == AnalyzeMode ||
+ gameMode == AnalyzeFile)
+ TruncateGame();
+
+ ResurrectChessProgram(); /* in case it isn't running */
+ gameMode = MachinePlaysBlack;
+ pausing = FALSE;
+ ModeHighlight();
+ SetGameInfo();
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ DisplayTitle(buf);
+ if (first.sendName) {
+ sprintf(buf, "name %s\n", gameInfo.white);
+ SendToProgram(buf, &first);
+ }
+ if (first.sendTime) {
+ if (first.useColors) {
+ SendToProgram("white\n", &first); /*gnu kludge*/
+ }
+ SendTimeRemaining(&first, FALSE);
+ }
+ if (first.useColors) {
+ SendToProgram("black\ngo\n", &first);
+ } else {
+ SendToProgram("go\n", &first);
+ }
+ SetMachineThinkingEnables();
+ first.maybeThinking = TRUE;
+ StartClocks();
+
+ if (appData.autoFlipView && flipView) {
+ flipView = !flipView;
+ DrawPosition(FALSE, NULL);
+ }
+ if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
+}
+
+
+void
+DisplayTwoMachinesTitle()
+{
+ char buf[MSG_SIZ];
+ if (appData.matchGames > 0) {
+ if (first.twoMachinesColor[0] == 'w') {
+ sprintf(buf, "%s vs. %s (%d-%d-%d)",
+ gameInfo.white, gameInfo.black,
+ first.matchWins, second.matchWins,
+ matchGame - 1 - (first.matchWins + second.matchWins));
+ } else {
+ sprintf(buf, "%s vs. %s (%d-%d-%d)",
+ gameInfo.white, gameInfo.black,
+ second.matchWins, first.matchWins,
+ matchGame - 1 - (first.matchWins + second.matchWins));
+ }
+ } else {
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+ }
+ DisplayTitle(buf);
+}
+
+void
+TwoMachinesEvent P((void))
+{
+ int i;
+ char buf[MSG_SIZ];
+ ChessProgramState *onmove;
+
+ if (appData.noChessProgram) return;
+
+ switch (gameMode) {
+ case TwoMachinesPlay:
+ return;
+ case MachinePlaysWhite:
+ case MachinePlaysBlack:
+ if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
+ DisplayError("Wait until your turn,\nor select Move Now", 0);
+ return;
+ }
+ /* fall through */
+ case BeginningOfGame:
+ case PlayFromGameFile:
+ case EndOfGame:
+ EditGameEvent();
+ if (gameMode != EditGame) return;
+ break;
+ case EditPosition:
+ EditPositionDone();
+ break;
+ case AnalyzeMode:
+ case AnalyzeFile:
+ ExitAnalyzeMode();
+ break;
+ case EditGame:
+ default:
+ break;
+ }
+
+ forwardMostMove = currentMove;
+ ResurrectChessProgram(); /* in case first program isn't running */
+
+ if (second.pr == NULL) {
+ StartChessProgram(&second);
+ if (second.protocolVersion == 1) {
+ TwoMachinesEventIfReady();
+ } else {
+ /* kludge: allow timeout for initial "feature" command */
+ FreezeUI();
+ DisplayMessage("", "Starting second chess program");
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
+ }
+ return;
+ }
+ DisplayMessage("", "");
+ InitChessProgram(&second);
+ SendToProgram("force\n", &second);
+ if (startedFromSetupPosition) {
+ SendBoard(&second, backwardMostMove);
+ }
+ for (i = backwardMostMove; i < forwardMostMove; i++) {
+ SendMoveToProgram(i, &second);
+ }
+
+ gameMode = TwoMachinesPlay;
+ pausing = FALSE;
+ ModeHighlight();
+ SetGameInfo();
+ DisplayTwoMachinesTitle();
+ firstMove = TRUE;
+ if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {
+ onmove = &first;
+ } else {
+ onmove = &second;
+ }
+
+ SendToProgram(first.computerString, &first);
+ if (first.sendName) {
+ sprintf(buf, "name %s\n", second.tidy);
+ SendToProgram(buf, &first);
+ }
+ SendToProgram(second.computerString, &second);
+ if (second.sendName) {
+ sprintf(buf, "name %s\n", first.tidy);
+ SendToProgram(buf, &second);