2 * backend.c -- Common back end for X and Windows NT versions of
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
8 * The following terms apply to Digital Equipment Corporation's copyright
10 * ------------------------------------------------------------------------
13 * Permission to use, copy, modify, and distribute this software and its
14 * documentation for any purpose and without fee is hereby granted,
15 * provided that the above copyright notice appear in all copies and that
16 * both that copyright notice and this permission notice appear in
17 * supporting documentation, and that the name of Digital not be
18 * used in advertising or publicity pertaining to distribution of the
19 * software without specific, written prior permission.
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 * ------------------------------------------------------------------------
30 * The following terms apply to the enhanced version of XBoard distributed
31 * by the Free Software Foundation:
32 * ------------------------------------------------------------------------
33 * This program is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation; either version 2 of the License, or
36 * (at your option) any later version.
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
43 * You should have received a copy of the GNU General Public License
44 * along with this program; if not, write to the Free Software
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46 * ------------------------------------------------------------------------
48 * See the file ChangeLog for a revision history. */
55 #include <sys/types.h>
64 #else /* not STDC_HEADERS */
67 # else /* not HAVE_STRING_H */
69 # endif /* not HAVE_STRING_H */
70 #endif /* not STDC_HEADERS */
73 # include <sys/fcntl.h>
74 #else /* not HAVE_SYS_FCNTL_H */
77 # endif /* HAVE_FCNTL_H */
78 #endif /* not HAVE_SYS_FCNTL_H */
80 #if TIME_WITH_SYS_TIME
81 # include <sys/time.h>
85 # include <sys/time.h>
91 #if defined(_amigados) && !defined(__GNUC__)
96 extern int gettimeofday(struct timeval *, struct timezone *);
104 #include "frontend.h"
111 #include "backendz.h"
112 #include "winboard.h"
114 /* A point in time */
116 long sec; /* Assuming this is >= 32 bits */
117 int ms; /* Assuming this is >= 16 bits */
120 int establish P((void));
121 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
122 char *buf, int count, int error));
123 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
124 char *buf, int count, int error));
125 void SendToICS P((char *s));
126 void SendToICSDelayed P((char *s, long msdelay));
127 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
129 void InitPosition P((int redraw));
130 void HandleMachineMove P((char *message, ChessProgramState *cps));
131 int AutoPlayOneMove P((void));
132 int LoadGameOneMove P((ChessMove readAhead));
133 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
134 int LoadPositionFromFile P((char *filename, int n, char *title));
135 int SavePositionToFile P((char *filename));
136 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
138 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
139 void ShowMove P((int fromX, int fromY, int toX, int toY));
140 void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
141 /*char*/int promoChar));
142 void BackwardInner P((int target));
143 void ForwardInner P((int target));
144 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
145 void EditPositionDone P((void));
146 void PrintOpponents P((FILE *fp));
147 void PrintPosition P((FILE *fp, int move));
148 void StartChessProgram P((ChessProgramState *cps));
149 void GuiCommand P((int command, int param));
150 void IcsAnalyzeOutPut P((ChessProgramState *cps, int endThink));
151 int SwitchGames P((void));
152 void SendToProgram P((char *message, ChessProgramState *cps));
153 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
154 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
155 char *buf, int count, int error));
156 void SendTimeControl P((ChessProgramState *cps,
157 int mps, long tc, int inc, int sd, int st));
158 char *TimeControlTagValue P((void));
159 void Attention P((ChessProgramState *cps));
160 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
161 void ResurrectChessProgram P((void));
162 void DisplayComment P((int moveNumber, char *text));
163 void DisplayMove P((int moveNumber));
164 void GetTimeMark P((TimeMark *));
165 void ParseGameHistory P((char *game));
166 void ParseBoard12 P((char *string));
167 void StartClocks P((void));
168 void SwitchClocks P((void));
169 void StopClocks P((void));
170 void ResetClocks P((void));
171 char *PGNDate P((void));
172 void SetGameInfo P((void));
173 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
174 int RegisterMove P((void));
175 void MakeRegisteredMove P((void));
176 void TruncateGame P((void));
177 int looking_at P((char *, int *, char *));
178 void CopyPlayerNameIntoFileName P((char **, char *));
179 char *SavePart P((char *));
180 int SaveGameOldStyle P((FILE *));
181 int SaveGamePGN P((FILE *));
183 long SubtractTimeMarks P((TimeMark *, TimeMark *));
184 int CheckFlags P((void));
185 long NextTickLength P((long));
186 void CheckTimeControl P((void));
187 void show_bytes P((FILE *, char *, int));
188 int string_to_rating P((char *str));
189 void ParseFeatures P((char* args, ChessProgramState *cps));
190 void InitBackEnd3 P((void));
191 void FeatureDone P((ChessProgramState* cps, int val));
192 void InitChessProgram P((ChessProgramState *cps));
193 extern int tinyLayout, smallLayout;
195 void ParseZippyP3 P((char* data, char* player));
197 /* States for ics_getting_history */
199 #define H_REQUESTED 1
200 #define H_GOT_REQ_HEADER 2
201 #define H_GOT_UNREQ_HEADER 3
202 #define H_GETTING_MOVES 4
203 #define H_GOT_UNWANTED_HEADER 5
205 /* whosays values for GameEnds */
212 /* Maximum number of games in a cmail message */
213 #define CMAIL_MAX_GAMES 20
215 /* Different types of move when calling RegisterMove */
217 #define CMAIL_RESIGN 1
219 #define CMAIL_ACCEPT 3
221 /* Different types of result to remember for each game */
222 #define CMAIL_NOT_RESULT 0
223 #define CMAIL_OLD_RESULT 1
224 #define CMAIL_NEW_RESULT 2
226 /* Telnet protocol constants */
236 /* Fake up flags for now, as we aren't keeping track of castling
241 int flags = F_ALL_CASTLE_OK;
242 if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
243 switch (gameInfo.variant) {
245 case VariantGiveaway:
246 flags |= F_IGNORE_CHECK;
247 flags &= ~F_ALL_CASTLE_OK;
250 flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
252 case VariantKriegspiel:
253 flags |= F_KRIEGSPIEL_CAPTURE;
255 case VariantNoCastle:
256 flags &= ~F_ALL_CASTLE_OK;
264 FILE *gameFileFP, *debugFP;
266 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
267 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
268 char thinkOutput1[MSG_SIZ*10];
270 /*ChessProgramState first, second; */
272 /* premove variables */
275 int premoveFromX = 0;
276 int premoveFromY = 0;
277 int premovePromoChar = 0;
279 Boolean alarmSounded;
280 /* end premove variables */
282 #define ICS_GENERIC 0
285 #define ICS_CHESSNET 3 /* not really supported */
286 int ics_type = ICS_GENERIC;
287 char *ics_prefix = "$";
289 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
290 int pauseExamForwardMostMove = 0;
291 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
292 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
293 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
294 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
295 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
296 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
297 int whiteFlag = FALSE, blackFlag = FALSE;
298 int userOfferedDraw = FALSE;
299 int ics_user_moved = 0, ics_getting_history = H_FALSE, ics_gamenum = -1;
300 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
301 int cmailMoveType[CMAIL_MAX_GAMES];
302 prefixHint = 0; /* PonderMove true/false */
303 long ics_clock_paused = 0;
304 ProcRef icsPR = NoProc, cmailPR = NoProc;
305 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
306 gameMode = BeginningOfGame;
307 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
308 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
309 char white_holding[64], black_holding[64];
310 TimeMark lastNodeCountTime;
311 long lastNodeCount = 0;
312 int have_sent_ICS_logon = 0;
314 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
315 long timeRemaining[2][MAX_MOVES];
317 TimeMark programStartTime;
318 char ics_handle[MSG_SIZ];
319 int have_set_title = 0;
320 smartQueue icsQueue[max_gamenum];
328 /* Search stats from chessprogram */
330 char movelist[MSG_SIZ]; /* Last PV we were sent */
331 char ponderMove[12]; /* Current ponder move */
332 int depth; /* Current search depth */
333 int nr_moves; /* Total nr of root moves */
334 int moves_left; /* Moves remaining to be searched */
335 char move_name[MOVE_LEN]; /* Current move being searched, if provided */
336 unsigned long nodes; /* # of nodes searched */
337 int time; /* Search time (centiseconds) */
338 int score; /* Score (centipawns) */
339 int got_only_move; /* If last msg was "(only move)" */
340 int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */
341 int ok_to_send; /* handshaking between send & recv */
342 int line_is_book; /* 1 if movelist is book moves */
343 int seen_stat; /* 1 if we've seen the stat01: line */
344 int GUI_time; /* Time from EngineRoom */
347 static ChessProgramStats programStats;
349 /* animateTraining preserves the state of appData.animate
350 * when Training mode is activated. This allows the
351 * response to be animated when appData.animate == TRUE and
352 * appData.animateDragging == TRUE.
354 Boolean animateTraining;
360 Board boards[MAX_MOVES];
361 Board initialPosition = {
362 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
363 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
364 { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
365 WhitePawn, WhitePawn, WhitePawn, WhitePawn },
366 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
367 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
368 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
369 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
370 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
371 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
372 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
373 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
374 { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
375 BlackPawn, BlackPawn, BlackPawn, BlackPawn },
376 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
377 BlackKing, BlackBishop, BlackKnight, BlackRook }
379 Board twoKingsPosition = {
380 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
381 WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
382 { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
383 WhitePawn, WhitePawn, WhitePawn, WhitePawn },
384 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
385 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
386 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
387 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
388 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
389 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
390 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
391 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
392 { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
393 BlackPawn, BlackPawn, BlackPawn, BlackPawn },
394 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
395 BlackKing, BlackKing, BlackKnight, BlackRook }
398 /* Convert str to a rating. Checks for special cases of "----",
399 "++++", etc. Also strips ()'s */
401 string_to_rating(str)
404 while(*str && !isdigit(*str)) ++str;
406 return 0; /* One of the special "no rating" cases */
414 /* Init programStats */
415 programStats.movelist[0] = NULLCHAR;
416 programStats.ponderMove[0] = 0;
417 programStats.depth = 0;
418 programStats.nr_moves = 0;
419 programStats.moves_left = 0;
420 programStats.nodes = 0;
421 programStats.time = 100;
422 programStats.score = 0;
423 programStats.got_only_move = 0;
424 programStats.got_fail = 0;
425 programStats.line_is_book = 0;
431 int matched, min, sec;
433 GetTimeMark(&programStartTime);
436 programStats.ok_to_send = 1;
437 programStats.seen_stat = 0;
441 * Initialize game list
447 * Internet chess server status
449 if (appData.icsActive) {
450 appData.matchMode = FALSE;
451 appData.matchGames = 0;
453 appData.noChessProgram = !appData.zippyPlay;
455 appData.zippyPlay = FALSE;
456 appData.zippyTalk = FALSE;
457 appData.noChessProgram = TRUE;
459 if (*appData.icsHelper != NULLCHAR) {
460 appData.useTelnet = TRUE;
461 appData.telnetProgram = appData.icsHelper;
464 appData.zippyTalk = appData.zippyPlay = FALSE;
468 * Parse timeControl resource
470 if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
471 appData.movesPerSession)) {
473 sprintf(buf, "bad timeControl option %s", appData.timeControl);
474 DisplayFatalError(buf, 0, 2);
478 * Parse searchTime resource
480 if (*appData.searchTime != NULLCHAR) {
481 matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
483 searchTime = min * 60;
484 } else if (matched == 2) {
485 searchTime = min * 60 + sec;
488 sprintf(buf, "bad searchTime option %s", appData.searchTime);
489 DisplayFatalError(buf, 0, 2);
493 first.which = "first";
494 second.which = "second";
495 first.maybeThinking = second.maybeThinking = FALSE;
496 first.pr = second.pr = NoProc;
497 first.isr = second.isr = NULL;
498 first.sendTime = second.sendTime = 2;
499 first.sendDrawOffers = 1;
500 if (appData.firstPlaysBlack) {
501 first.twoMachinesColor = "black\n";
502 second.twoMachinesColor = "white\n";
504 first.twoMachinesColor = "white\n";
505 second.twoMachinesColor = "black\n";
507 first.program = appData.firstChessProgram;
508 second.program = appData.secondChessProgram;
509 first.host = appData.firstHost;
510 second.host = appData.secondHost;
511 first.dir = appData.firstDirectory;
512 second.dir = appData.secondDirectory;
513 first.other = &second;
514 second.other = &first;
515 first.initString = appData.initString;
516 second.initString = appData.secondInitString;
517 first.computerString = appData.firstComputerString;
518 second.computerString = appData.secondComputerString;
519 first.useSigint = second.useSigint = TRUE;
520 first.useSigterm = second.useSigterm = TRUE;
521 first.reuse = appData.reuseFirst;
522 second.reuse = appData.reuseSecond;
523 first.useSetboard = second.useSetboard = FALSE;
524 first.useSAN = second.useSAN = FALSE;
525 first.usePing = second.usePing = FALSE;
526 first.lastPing = second.lastPing = 0;
527 first.lastPong = second.lastPong = 0;
528 first.usePlayother = second.usePlayother = FALSE;
529 first.useColors = second.useColors = TRUE;
530 first.useUsermove = second.useUsermove = FALSE;
531 first.sendICS = second.sendICS = FALSE;
532 first.sendName = second.sendName = appData.icsActive;
533 first.sdKludge = second.sdKludge = FALSE;
534 first.stKludge = second.stKludge = FALSE;
535 TidyProgramName(first.program, first.host, first.tidy);
536 TidyProgramName(second.program, second.host, second.tidy);
537 first.matchWins = second.matchWins = 0;
538 strcpy(first.variants, appData.variant);
539 strcpy(second.variants, appData.variant);
540 first.analysisSupport = second.analysisSupport = 2; /* detect */
541 first.analyzing = second.analyzing = FALSE;
542 first.initDone = second.initDone = FALSE;
544 if (appData.firstProtocolVersion > PROTOVER ||
545 appData.firstProtocolVersion < 1) {
547 sprintf(buf, "protocol version %d not supported",
548 appData.firstProtocolVersion);
549 DisplayFatalError(buf, 0, 2);
551 first.protocolVersion = appData.firstProtocolVersion;
554 if (appData.secondProtocolVersion > PROTOVER ||
555 appData.secondProtocolVersion < 1) {
557 sprintf(buf, "protocol version %d not supported",
558 appData.secondProtocolVersion);
559 DisplayFatalError(buf, 0, 2);
561 second.protocolVersion = appData.secondProtocolVersion;
564 if (appData.icsActive) {
565 appData.clockMode = TRUE; /* changes dynamically in ICS mode */
566 } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
567 appData.clockMode = FALSE;
568 first.sendTime = second.sendTime = 0;
572 /* Override some settings from environment variables, for backward
573 compatibility. Unfortunately it's not feasible to have the env
574 vars just set defaults, at least in xboard. Ugh.
576 if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
581 if (appData.noChessProgram) {
582 programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
583 + strlen(PATCHLEVEL));
584 sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
588 while (*q != ' ' && *q != NULLCHAR) q++;
590 while (p > first.program && *(p-1) != '/') p--;
591 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
592 + strlen(PATCHLEVEL) + (q - p));
593 sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
594 strncat(programVersion, p, q - p);
597 if (!appData.icsActive) {
599 /* Check for variants that are supported only in ICS mode,
600 or not at all. Some that are accepted here nevertheless
601 have bugs; see comments below.
603 VariantClass variant = StringToVariant(appData.variant);
605 case VariantBughouse: /* need four players and two boards */
606 case VariantKriegspiel: /* need to hide pieces and move details */
607 case VariantFischeRandom: /* castling doesn't work, shuffle not done */
608 sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
609 DisplayFatalError(buf, 0, 2);
613 case VariantLoadable:
623 sprintf(buf, "Unknown variant name %s", appData.variant);
624 DisplayFatalError(buf, 0, 2);
627 case VariantNormal: /* definitely works! */
628 case VariantWildCastle: /* pieces not automatically shuffled */
629 case VariantNoCastle: /* pieces not automatically shuffled */
630 case VariantCrazyhouse: /* holdings not shown,
631 offboard interposition not understood */
632 case VariantLosers: /* should work except for win condition,
633 and doesn't know captures are mandatory */
634 case VariantSuicide: /* should work except for win condition,
635 and doesn't know captures are mandatory */
636 case VariantGiveaway: /* should work except for win condition,
637 and doesn't know captures are mandatory */
638 case VariantTwoKings: /* should work */
639 case VariantAtomic: /* should work except for win condition */
640 case Variant3Check: /* should work except for win condition */
641 case VariantShatranj: /* might work if TestLegality is off */
648 ParseTimeControl(tc, ti, mps)
653 int matched, min, sec;
655 matched = sscanf(tc, "%d:%d", &min, &sec);
657 timeControl = min * 60 * 1000;
658 } else if (matched == 2) {
659 timeControl = (min * 60 + sec) * 1000;
665 timeIncrement = ti * 1000; /* convert to ms */
669 movesPerSession = mps;
677 if (appData.debugMode) {
678 fprintf(debugFP, "%s\n", programVersion);
681 if (appData.matchGames > 0) {
682 appData.matchMode = TRUE;
683 } else if (appData.matchMode) {
684 appData.matchGames = 1;
687 if (appData.noChessProgram || first.protocolVersion == 1) {
690 /* kludge: allow timeout for initial "feature" commands */
692 if (appData.icsActive) {
693 DisplayMessage("", "ICS-Mode: Waiting for chessprogram...");
695 DisplayMessage("", "Starting chessprogram");
697 ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
702 InitBackEnd3 P((void))
704 GameMode initialMode;
708 InitChessProgram(&first);
710 /* Make a console window if needed */
711 if (appData.icsActive) {
717 if (appData.icsActive) {
720 if (*appData.icsCommPort != NULLCHAR) {
721 sprintf(buf, "Could not open comm port %s",
722 appData.icsCommPort);
724 sprintf(buf, "Could not connect to host %s, port %s",
725 appData.icsHost, appData.icsPort);
727 DisplayFatalError(buf, err, 1);
732 AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
734 AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
735 } else if (appData.noChessProgram) {
741 if (*appData.cmailGameName != NULLCHAR) {
743 OpenLoopback(&cmailPR);
745 AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
749 DisplayMessage("", "");
750 if (StrCaseCmp(appData.initialMode, "") == 0) {
751 initialMode = BeginningOfGame;
752 } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
753 initialMode = TwoMachinesPlay;
754 } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
755 initialMode = AnalyzeFile;
756 } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
757 initialMode = AnalyzeMode;
758 } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
759 initialMode = MachinePlaysWhite;
760 } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
761 initialMode = MachinePlaysBlack;
762 } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
763 initialMode = EditGame;
764 } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
765 initialMode = EditPosition;
766 } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
767 initialMode = Training;
769 sprintf(buf, "Unknown initialMode %s", appData.initialMode);
770 DisplayFatalError(buf, 0, 2);
774 if (appData.matchMode) {
775 /* Set up machine vs. machine match */
776 if (appData.noChessProgram) {
777 DisplayFatalError("Can't have a match with no chess programs",
783 if (*appData.loadGameFile != NULLCHAR) {
784 if (!LoadGameFromFile(appData.loadGameFile,
785 appData.loadGameIndex,
786 appData.loadGameFile, FALSE)) {
787 DisplayFatalError("Bad game file", 0, 1);
790 } else if (*appData.loadPositionFile != NULLCHAR) {
791 if (!LoadPositionFromFile(appData.loadPositionFile,
792 appData.loadPositionIndex,
793 appData.loadPositionFile)) {
794 DisplayFatalError("Bad position file", 0, 1);
799 } else if (*appData.cmailGameName != NULLCHAR) {
800 /* Set up cmail mode */
801 ReloadCmailMsgEvent(TRUE);
803 /* Set up other modes */
804 if (initialMode == AnalyzeFile) {
805 if (*appData.loadGameFile == NULLCHAR) {
806 DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
810 if (*appData.loadGameFile != NULLCHAR) {
811 (void) LoadGameFromFile(appData.loadGameFile,
812 appData.loadGameIndex,
813 appData.loadGameFile, TRUE);
814 } else if (*appData.loadPositionFile != NULLCHAR) {
815 (void) LoadPositionFromFile(appData.loadPositionFile,
816 appData.loadPositionIndex,
817 appData.loadPositionFile);
819 if (initialMode == AnalyzeMode) {
820 if (appData.noChessProgram) {
821 DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
824 if (appData.icsActive) {
825 DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
829 } else if (initialMode == AnalyzeFile) {
830 ShowThinkingEvent(TRUE);
832 AnalysisPeriodicEvent(1);
833 } else if (initialMode == MachinePlaysWhite) {
834 if (appData.noChessProgram) {
835 DisplayFatalError("MachineWhite mode requires a chess engine",
839 if (appData.icsActive) {
840 DisplayFatalError("MachineWhite mode does not work with ICS mode",
845 } else if (initialMode == MachinePlaysBlack) {
846 if (appData.noChessProgram) {
847 DisplayFatalError("MachineBlack mode requires a chess engine",
851 if (appData.icsActive) {
852 DisplayFatalError("MachineBlack mode does not work with ICS mode",
857 } else if (initialMode == TwoMachinesPlay) {
858 if (appData.noChessProgram) {
859 DisplayFatalError("TwoMachines mode requires a chess engine",
863 if (appData.icsActive) {
864 DisplayFatalError("TwoMachines mode does not work with ICS mode",
869 } else if (initialMode == EditGame) {
871 } else if (initialMode == EditPosition) {
873 } else if (initialMode == Training) {
874 if (*appData.loadGameFile == NULLCHAR) {
875 DisplayFatalError("Training mode requires a game file", 0, 2);
881 /* If EngineRoom TRUE start */
882 if (appData.AnalysisWindow && appData.showThinking &&
883 !appData.noChessProgram) {
884 if (appData.icsActive && appData.zippyPlay) {
885 AnalysisPopUp(0,0,0,0,0,0,0,5);
887 if (!appData.icsActive) AnalysisPopUp(0,0,0,0,0,0,0,5);
888 StartAnalysisClock();
893 * Establish will establish a contact to a remote host.port.
894 * Sets icsPR to a ProcRef for a process (or pseudo-process)
895 * used to talk to the host.
896 * Returns 0 if okay, error code if not.
903 if (*appData.icsCommPort != NULLCHAR) {
904 /* Talk to the host through a serial comm port */
905 return OpenCommPort(appData.icsCommPort, &icsPR);
907 } else if (*appData.gateway != NULLCHAR) {
908 if (*appData.remoteShell == NULLCHAR) {
909 /* Use the rcmd protocol to run telnet program on a gateway host */
910 sprintf(buf, "%s %s %s",
911 appData.telnetProgram, appData.icsHost, appData.icsPort);
912 return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
915 /* Use the rsh program to run telnet program on a gateway host */
916 if (*appData.remoteUser == NULLCHAR) {
917 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
918 appData.gateway, appData.telnetProgram,
919 appData.icsHost, appData.icsPort);
921 sprintf(buf, "%s %s -l %s %s %s %s",
922 appData.remoteShell, appData.gateway,
923 appData.remoteUser, appData.telnetProgram,
924 appData.icsHost, appData.icsPort);
926 return StartChildProcess(buf, "", &icsPR);
929 } else if (appData.useTelnet) {
930 return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
933 /* TCP socket interface differs somewhat between
934 Unix and NT; handle details in the front end.
936 return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
941 show_bytes(fp, buf, count)
947 if (*buf < 040 || *(unsigned char *) buf > 0177) {
948 fprintf(fp, "\\%03o", *buf & 0xff);
957 /* Returns an errno value */
959 OutputMaybeTelnet(pr, message, count, outError)
965 char buf[8192], *p, *q, *buflim;
966 int left, newcount, outcount;
968 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
969 *appData.gateway != NULLCHAR) {
970 if (appData.debugMode) {
971 fprintf(debugFP, ">ICS: ");
972 show_bytes(debugFP, message, count);
973 fprintf(debugFP, "\n");
975 return OutputToProcess(pr, message, count, outError);
978 buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
985 if (appData.debugMode) {
986 fprintf(debugFP, ">ICS: ");
987 show_bytes(debugFP, buf, newcount);
988 fprintf(debugFP, "\n");
990 outcount = OutputToProcess(pr, buf, newcount, outError);
991 if (outcount < newcount) return -1; /* to be sure */
998 } else if (((unsigned char) *p) == TN_IAC) {
999 *q++ = (char) TN_IAC;
1006 if (appData.debugMode) {
1007 fprintf(debugFP, ">ICS: ");
1008 show_bytes(debugFP, buf, newcount);
1009 fprintf(debugFP, "\n");
1011 outcount = OutputToProcess(pr, buf, newcount, outError);
1012 if (outcount < newcount) return -1; /* to be sure */
1017 read_from_player(isr, closure, message, count, error)
1024 int outError, outCount;
1025 static int gotEof = 0;
1027 /* Pass data read from player on to ICS */
1030 outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
1031 if (outCount < count) {
1032 DisplayFatalError("Error writing to ICS", outError, 1);
1034 } else if (count < 0) {
1035 RemoveInputSource(isr);
1036 DisplayFatalError("Error reading from keyboard", error, 1);
1037 } else if (gotEof++ > 0) {
1038 RemoveInputSource(isr);
1039 DisplayFatalError("Got end of file from keyboard", 0, 0);
1047 int count, outCount, outError;
1049 if (icsPR == NULL) return;
1052 outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
1053 if (outCount < count) {
1054 DisplayFatalError("Error writing to ICS", outError, 1);
1058 /* This is used for sending logon scripts to the ICS. Sending
1059 without a delay causes problems when using timestamp on ICC
1060 (at least on my machine). */
1062 SendToICSDelayed(s,msdelay)
1066 int count, outCount, outError;
1068 if (icsPR == NULL) return;
1071 if (appData.debugMode) {
1072 fprintf(debugFP, ">ICS: ");
1073 show_bytes(debugFP, s, count);
1074 fprintf(debugFP, "\n");
1076 outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
1078 if (outCount < count) {
1079 DisplayFatalError("Error writing to ICS", outError, 1);
1084 /* Remove all highlighting escape sequences in s
1085 Also deletes any suffix starting with '('
1088 StripHighlightAndTitle(s)
1091 static char retbuf[MSG_SIZ];
1094 while (*s != NULLCHAR) {
1095 while (*s == '\033') {
1096 while (*s != NULLCHAR && !isalpha(*s)) s++;
1097 if (*s != NULLCHAR) s++;
1099 while (*s != NULLCHAR && *s != '\033') {
1100 if (*s == '(' || *s == '[') {
1111 /* Remove all highlighting escape sequences in s */
1116 static char retbuf[MSG_SIZ];
1119 while (*s != NULLCHAR) {
1120 while (*s == '\033') {
1121 while (*s != NULLCHAR && !isalpha(*s)) s++;
1122 if (*s != NULLCHAR) s++;
1124 while (*s != NULLCHAR && *s != '\033') {
1132 char *variantNames[] = VARIANT_NAMES;
1137 return variantNames[v];
1141 /* Identify a variant from the strings the chess servers use or the
1142 PGN Variant tag names we use. */
1149 VariantClass v = VariantNormal;
1150 int i, found = FALSE;
1155 for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
1156 if (StrCaseStr(e, variantNames[i])) {
1157 v = (VariantClass) i;
1164 if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
1165 || StrCaseStr(e, "wild/fr")) {
1166 v = VariantFischeRandom;
1167 } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
1168 (i = 1, p = StrCaseStr(e, "w"))) {
1170 while (*p && (isspace(*p) || *p == '(')) p++;
1177 case 0: /* FICS only, actually */
1179 /* Castling legal even if K starts on d-file */
1180 v = VariantWildCastle;
1185 /* Castling illegal even if K & R happen to start in
1186 normal positions. */
1187 v = VariantNoCastle;
1200 /* Castling legal iff K & R start in normal positions */
1206 /* Special wilds for position setup; unclear what to do here */
1207 v = VariantLoadable;
1210 /* Bizarre ICC game */
1211 v = VariantTwoKings;
1214 v = VariantKriegspiel;
1220 v = VariantFischeRandom;
1223 v = VariantCrazyhouse;
1226 v = VariantBughouse;
1232 /* Not quite the same as FICS suicide! */
1233 v = VariantGiveaway;
1239 v = VariantShatranj;
1242 /* Temporary names for future ICC types. The name *will* change in
1243 the next xboard/WinBoard release after ICC defines it. */
1270 /* Found "wild" or "w" in the string but no number;
1271 must assume it's normal chess. */
1275 sprintf(buf, "Unknown wild type %d", wnum);
1276 DisplayError(buf, 0);
1282 if (appData.debugMode) {
1283 fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
1284 e, wnum, VariantName(v));
1289 static int leftover_start = 0, leftover_len = 0;
1290 char star_match[STAR_MATCH_N][MSG_SIZ];
1292 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
1293 advance *index beyond it, and set leftover_start to the new value of
1294 *index; else return FALSE. If pattern contains the character '*', it
1295 matches any sequence of characters not containing '\r', '\n', or the
1296 character following the '*' (if any), and the matched sequence(s) are
1297 copied into star_match.
1300 looking_at(buf, index, pattern)
1305 char *bufp = &buf[*index], *patternp = pattern;
1307 char *matchp = star_match[0];
1310 if (*patternp == NULLCHAR) {
1311 *index = leftover_start = bufp - buf;
1315 if (*bufp == NULLCHAR) return FALSE;
1316 if (*patternp == '*') {
1317 if (*bufp == *(patternp + 1)) {
1319 matchp = star_match[++star_count];
1323 } else if (*bufp == '\n' || *bufp == '\r') {
1325 if (*patternp == NULLCHAR)
1330 *matchp++ = *bufp++;
1334 if (*patternp != *bufp) return FALSE;
1341 SendToPlayer(data, length)
1345 int error, outCount;
1346 outCount = OutputToProcess(NoProc, data, length, &error);
1347 if (outCount < length) {
1348 DisplayFatalError("Error writing to display", error, 1);
1353 PackHolding(packed, holding)
1365 switch (runlength) {
1376 sprintf(q, "%d", runlength);
1388 /* Telnet protocol requests from the front end */
1390 TelnetRequest(ddww, option)
1391 unsigned char ddww, option;
1393 unsigned char msg[3];
1394 int outCount, outError;
1396 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
1398 if (appData.debugMode) {
1399 char buf1[8], buf2[8], *ddwwStr, *optionStr;
1415 sprintf(buf1, "%d", ddww);
1424 sprintf(buf2, "%d", option);
1427 fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
1432 outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
1434 DisplayFatalError("Error writing to ICS", outError, 1);
1441 if (!appData.icsActive) return;
1442 TelnetRequest(TN_DO, TN_ECHO);
1448 if (!appData.icsActive) return;
1449 TelnetRequest(TN_DONT, TN_ECHO);
1452 static int loggedOn = FALSE;
1454 /*-- Game start info cache: --*/
1456 char gs_kind[MSG_SIZ];
1457 static char player1Name[128] = "";
1458 static char player2Name[128] = "";
1459 static int player1Rating = -1;
1460 static int player2Rating = -1;
1461 /*----------------------------*/
1464 /* ICC user send showinfo <game number> und we send latest pv */
1465 /* If username beginn with guest* send advertisement ;-) */
1467 showInfo(data, player)
1476 /* this feature is only for ICC admins or chessprogramer */
1477 /* You must set commandline /icc to use this */
1478 if (appData.userVersion == TRUE) return;
1479 if (appData.ICC_feature == FALSE || ics_type != ICS_ICC) return;
1480 while (*ptr == ' ') ptr++;
1481 if (*ptr == 0 || (sizeof(ptr) > MSG_SIZ)) return;
1482 sscanf(ptr, "%s", buf);
1484 /* showgames command */
1485 if (strncmp(buf, "showgames", 9) == 0) {
1488 for (counter = 1; counter <= max_gamenum; counter++) {
1489 if (icsQueue[counter].killPv > 0) {
1490 sprintf(temp, "tell %s I analyze game %d (%s/%s)\n", player, counter,
1491 icsQueue[counter].white, icsQueue[counter].black);
1498 i = atoi(buf); /* security first - user could try to buffer overflow data ! */
1499 if (i > max_gamenum || icsQueue[i].killPv == 0) {
1500 sprintf(temp, "tell %s I don't analysis this game\n", player);
1502 sprintf(temp, "tell %s \"tell %s showgames\" to see which games are currently analyzed.\n", player, ics_handle);
1504 } else if (icsQueue[i].killPv > 0) {
1505 sprintf(temp, "tell %s Hello %s from Winboard-DM\n", player, player);
1507 /* pos after "Depth " */
1508 if (icsQueue[i].lastpv[0]) {
1509 sprintf(temp, "tell %s Last analyze of game %d (%s/%s): (%s) Depth=%s\n",
1510 player, i, icsQueue[i].white, icsQueue[i].black, first.tidy, icsQueue[i].lastpv);
1512 sprintf(temp, "tell %s Sorry, no analysis avaible from game %d at the moment. Please try it later again.\n",
1516 /* for ICC guest send member and register info */
1517 if (strncmp(player, "guest", 5) == 0) {
1518 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);
1525 Call this to see if we have a game where we not send a message
1526 for the current move
1535 for (counter = 1; counter <= max_gamenum; counter++) {
1536 if (icsQueue[counter].killPv > 0 && icsQueue[counter].CurrentMove >=
1537 icsQueue[counter].MessageMove && icsQueue[counter].flag == 0) {
1538 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: We switch to game %d \n", counter);
1539 sprintf(buf, "refresh %d \n", counter);
1548 /* parse and action from zippy password3 */
1550 ParseZippyP3(data, player)
1560 if (appData.userVersion == TRUE) return;
1564 sprintf(temp, "tell %s Sorry, to many values. Max 6 values each line. Try again.\n", player);
1566 sprintf(temp, "tell %s Or try send me \"help\"\n", player);
1570 while (*ptr == ' ') ptr++;
1571 if (*ptr == 0) break;
1572 sscanf(ptr, "%s", command[counter].string);
1573 if (appData.debugMode) fprintf(debugFP, "zp3: %s\n", command[counter].string);
1574 ptr = ptr + strlen(command[counter].string);
1577 if(strncmp(command[forward].string, "help", 4) == 0) {
1578 /* icc don't accept text after new line :((( */
1579 /* So, we must write every line */
1580 sprintf(temp, "tell %s Hello %s from %s %s%s\n", player, player, PRODUCT, VERSION, PATCHLEVEL);
1582 sprintf(temp, "tell %s analyze <observe|follow|unobserve|unfollow> <Playername|Gamenumber>\n", player);
1584 sprintf(temp, "tell %s analyze <start|stop>\n", player);
1586 sprintf(temp, "tell %s analyze output <whisper|kibitz|tell|none> if tell <value>\n", player);
1588 sprintf(temp, "tell %s analyze killpv <value> <Gamenumber>\n", player);
1590 sprintf(temp, "tell %s if you are setup killpv you enable it automaticly\n", player);
1592 sprintf(temp, "tell %s analyze smartqueue <1|0>\n", player);
1594 sprintf(temp, "tell %s analyze show <Gamenumber>\n", player);
1596 sprintf(temp, "tell %s engine reset\n", player);
1598 sprintf(temp, "tell %s <whisper|kibitz>\n", player);
1603 if (strncmp(command[forward].string, "engine", 6) == 0) {
1604 if (strncmp(command[forward+1].string, "reset", 5) == 0) {
1605 sprintf(temp, "tell %s reset engine...\n", player);
1607 ResurrectChessProgram();
1612 if (strncmp(command[forward].string, "whisper", 7) == 0) {
1613 sprintf(temp, "tell %s GUI = whisper\n", player);
1615 appData.SendOutPutToICS = 1;
1617 } else if (trncmp(command[forward].string, "kibitz", 6) == 0) {
1618 sprintf(temp, "tell %s GUI = kibitz\n", player);
1620 appData.SendOutPutToICS = 2;
1624 if(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
1625 sprintf(temp, "tell %s Sorry, i'm playing a game!\n", player);
1629 if (strncmp(command[forward].string, "analyze", 10) == 0) {
1630 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1631 strncmp(command[forward+1].string, "follow", 6) == 0 ||
1632 strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1633 strncmp(command[forward+1].string, "unfollow", 8) == 0) {
1635 /* reset Queue if leave */
1636 if (strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1637 strncmp(command[forward+1].string, "unfollow", 8) == 0) ResetIcsQueue(ics_gamenum);
1640 if(gameMode != IcsPlayingWhite || gameMode != IcsPlayingBlack) {
1641 sprintf(temp, "%s %s\n", command[forward+1].string,
1642 command[forward+2].string);
1644 sprintf(temp, "tell %s action: %s %s\n", player, command[forward+1].string,
1645 command[forward+2].string);
1646 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1647 strncmp(command[forward+1].string, "follow", 6) == 0) {
1649 if (ics_gamenum > max_gamenum) {
1650 sprintf(temp, "tell %s gamenumber to high\n", player);
1656 sprintf(temp, "tell %s Sorry, i'm playing\n", player);
1662 if (strncmp(command[forward+1].string, "start", 5) == 0) {
1663 if (gameMode != IcsObserving) {
1664 sprintf(temp, "tell %s I must observe a game\n", player);
1665 } else if (gameMode == IcsObserving) {
1666 sprintf(temp, "tell %s Starting ICS analyze...\n", player);
1667 appData.icsAnalyze = TRUE;
1673 if (strncmp(command[forward+1].string, "stop", 4) == 0) {
1674 if (gameMode != IcsObserving) {
1675 sprintf(temp, "tell %s I must observe a game\n", player);
1676 } else if (gameMode == IcsObserving) {
1677 sprintf(temp, "tell %s Stopping ICS analyze...\n", player);
1683 if (strncmp(command[forward+1].string, "output", 6) == 0) {
1684 if(strncmp(command[forward+2].string, "whisper", 7) == 0) {
1685 appData.icsAnalyzeOutPut = 1;
1686 sprintf(temp, "tell %s analyze output is whisper\n", player);
1687 } else if (strncmp(command[forward+2].string, "kibitz", 6) == 0) {
1688 appData.icsAnalyzeOutPut = 2;
1689 sprintf(temp, "tell %s analyze output is kibitz\n", player);
1690 } else if (strncmp(command[forward+2].string, "tell", 4) == 0) {
1691 appData.icsAnalyzeOutPut = 3;
1692 if (command[forward+3].string[0] != NULLCHAR) {
1693 if (strncmp(command[forward+3].string, "none", 4) == 0) {
1694 appData.icsAnalyzeOutPut = 4;
1695 sprintf(temp, "tell %s analyze output is: none\n", player);
1699 strcpy(appData.icsTells, command[forward+3].string);
1700 sprintf(temp, "tell %s analyze output is tell: %s\n", player,
1703 sprintf(temp, "tell %s Error: I don't know where i should send my analysis\n", player);
1704 appData.icsAnalyzeOutPut = 4;
1709 if (strncmp(command[forward+1].string, "killpv", 6) == 0) {
1710 i = atoi(command[forward+3].string);
1711 j = atoi(command[forward+2].string);
1712 if (i <= max_gamenum && i != 0) {
1713 if (icsQueue[i].killPv == 0) {
1714 sprintf(temp, "tell %s Error: Mh, killpv is zero. Wrong gamenumber?\n", player);
1717 if ( j > 20 || j < 1) {
1718 sprintf(temp, "tell %s Error: killpv must be 1-20\n", player);
1720 sprintf(temp, "tell %s killpv for game %d is now %d\n", player, i, j);
1721 icsQueue[i].killPv = j;
1722 appData.icsEngineKillPV = TRUE;
1727 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1732 if (strncmp(command[forward+1].string, "smartqueue", 10) == 0) {
1733 j = atoi(command[forward+2].string);
1734 if (j == 0 || j == 1) {
1735 appData.smartQueue = j;
1736 sprintf(temp, "tell %s smartqueue is now: %d", player, appData.smartQueue);
1738 sprintf(temp, "tell %s Error: not possible smartquere value\n", player);
1743 if (strncmp(command[forward+1].string, "show", 4) == 0) {
1744 i = atoi(command[forward+2].string);
1745 if (i <= max_gamenum && i != 0) {
1746 if (icsQueue[i].killPv == 0) {
1747 sprintf(temp, "tell %s Error: emtpy game slot. Wrong gamenumber?\n", player);
1750 sprintf(temp, "tell %s summary information about game: %d\n", player, i);
1752 sprintf(temp, "tell %s enable killpv: %d, killpv value = %d\n", player,
1753 appData.icsEngineKillPV, icsQueue[i].killPv);
1755 sprintf(temp, "tell %s enable smartqueue (all games): %d\n", player, appData.smartQueue);
1757 sprintf(temp, "tell %s analyze output (all games) is %d\n", player,
1758 appData.icsAnalyzeOutPut);
1762 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1771 read_from_ics(isr, closure, data, count, error)
1778 #define BUF_SIZE 8192
1779 #define STARTED_NONE 0
1780 #define STARTED_MOVES 1
1781 #define STARTED_BOARD 2
1782 #define STARTED_OBSERVE 3
1783 #define STARTED_HOLDINGS 4
1784 #define STARTED_CHATTER 5
1785 #define STARTED_COMMENT 6
1786 #define STARTED_MOVES_NOHIDE 7
1788 static int started = STARTED_NONE;
1789 static char parse[20000];
1790 static int parse_pos = 0;
1791 static char buf[BUF_SIZE + 1];
1792 static int firstTime = TRUE, intfSet = FALSE;
1793 static ColorClass curColor = ColorNormal;
1794 static ColorClass prevColor = ColorNormal;
1795 static int savingComment = FALSE;
1801 /* extra zippy vars */
1806 char reply[MSG_SIZ];
1809 /* If last read ended with a partial line that we couldn't parse,
1810 prepend it to the new read and try again. */
1811 if (leftover_len > 0) {
1812 for (i=0; i<leftover_len; i++)
1813 buf[i] = buf[leftover_start + i];
1816 /* Copy in new characters, removing nulls and \r's */
1817 buf_len = leftover_len;
1818 for (i = 0; i < count; i++) {
1819 if (data[i] != NULLCHAR && data[i] != '\r')
1820 buf[buf_len++] = data[i];
1823 buf[buf_len] = NULLCHAR;
1824 next_out = leftover_len;
1828 while (i < buf_len) {
1829 /* Deal with part of the TELNET option negotiation
1830 protocol. We refuse to do anything beyond the
1831 defaults, except that we allow the WILL ECHO option,
1832 which ICS uses to turn off password echoing when we are
1833 directly connected to it. We reject this option
1834 if localLineEditing mode is on (always on in xboard)
1835 and we are talking to port 23, which might be a real
1836 telnet server that will try to keep WILL ECHO on permanently.
1838 if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
1839 static int remoteEchoOption = FALSE; /* telnet ECHO option */
1840 unsigned char option;
1842 switch ((unsigned char) buf[++i]) {
1844 if (appData.debugMode)
1845 fprintf(debugFP, "\n<WILL ");
1846 switch (option = (unsigned char) buf[++i]) {
1848 if (appData.debugMode)
1849 fprintf(debugFP, "ECHO ");
1850 /* Reply only if this is a change, according
1851 to the protocol rules. */
1852 if (remoteEchoOption) break;
1853 if (appData.localLineEditing &&
1854 atoi(appData.icsPort) == TN_PORT) {
1855 TelnetRequest(TN_DONT, TN_ECHO);
1858 TelnetRequest(TN_DO, TN_ECHO);
1859 remoteEchoOption = TRUE;
1863 if (appData.debugMode)
1864 fprintf(debugFP, "%d ", option);
1865 /* Whatever this is, we don't want it. */
1866 TelnetRequest(TN_DONT, option);
1871 if (appData.debugMode)
1872 fprintf(debugFP, "\n<WONT ");
1873 switch (option = (unsigned char) buf[++i]) {
1875 if (appData.debugMode)
1876 fprintf(debugFP, "ECHO ");
1877 /* Reply only if this is a change, according
1878 to the protocol rules. */
1879 if (!remoteEchoOption) break;
1881 TelnetRequest(TN_DONT, TN_ECHO);
1882 remoteEchoOption = FALSE;
1885 if (appData.debugMode)
1886 fprintf(debugFP, "%d ", (unsigned char) option);
1887 /* Whatever this is, it must already be turned
1888 off, because we never agree to turn on
1889 anything non-default, so according to the
1890 protocol rules, we don't reply. */
1895 if (appData.debugMode)
1896 fprintf(debugFP, "\n<DO ");
1897 switch (option = (unsigned char) buf[++i]) {
1899 /* Whatever this is, we refuse to do it. */
1900 if (appData.debugMode)
1901 fprintf(debugFP, "%d ", option);
1902 TelnetRequest(TN_WONT, option);
1907 if (appData.debugMode)
1908 fprintf(debugFP, "\n<DONT ");
1909 switch (option = (unsigned char) buf[++i]) {
1911 if (appData.debugMode)
1912 fprintf(debugFP, "%d ", option);
1913 /* Whatever this is, we are already not doing
1914 it, because we never agree to do anything
1915 non-default, so according to the protocol
1916 rules, we don't reply. */
1921 if (appData.debugMode)
1922 fprintf(debugFP, "\n<IAC ");
1923 /* Doubled IAC; pass it through */
1927 if (appData.debugMode)
1928 fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
1929 /* Drop all other telnet commands on the floor */
1932 if (oldi > next_out)
1933 SendToPlayer(&buf[next_out], oldi - next_out);
1939 /* OK, this at least will *usually* work */
1940 if (!loggedOn && looking_at(buf, &i, "ics%")) {
1944 if (loggedOn && !intfSet) {
1945 if (ics_type == ICS_ICC) {
1947 "/set-quietly interface %s\n/set-quietly style 12\n",
1950 } else if (ics_type == ICS_CHESSNET) {
1951 sprintf(str, "/style 12\n");
1953 strcpy(str, "alias $ @\n$set interface ");
1954 strcat(str, programVersion);
1955 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
1957 strcat(str, "$iset nohighlight 1\n");
1959 strcat(str, "$iset lock 1\n$style 12\n");
1965 if (started == STARTED_COMMENT) {
1966 /* Accumulate characters in comment */
1967 parse[parse_pos++] = buf[i];
1968 if (buf[i] == '\n') {
1969 parse[parse_pos] = NULLCHAR;
1970 AppendComment(forwardMostMove, StripHighlight(parse));
1971 started = STARTED_NONE;
1973 /* Don't match patterns against characters in chatter */
1978 if (started == STARTED_CHATTER) {
1979 if (buf[i] != '\n') {
1980 /* Don't match patterns against characters in chatter */
1984 started = STARTED_NONE;
1987 /* Kludge to deal with rcmd protocol */
1988 if (firstTime && looking_at(buf, &i, "\001*")) {
1989 DisplayFatalError(&buf[1], 0, 1);
1995 if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
1998 if (appData.debugMode)
1999 fprintf(debugFP, "ics_type %d\n", ics_type);
2002 if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
2003 ics_type = ICS_FICS;
2005 if (appData.debugMode)
2006 fprintf(debugFP, "ics_type %d\n", ics_type);
2009 if (!loggedOn && looking_at(buf, &i, "chess.net")) {
2010 ics_type = ICS_CHESSNET;
2012 if (appData.debugMode)
2013 fprintf(debugFP, "ics_type %d\n", ics_type);
2018 (looking_at(buf, &i, "\"*\" is *a registered name") ||
2019 looking_at(buf, &i, "Logging you in as \"*\"") ||
2020 looking_at(buf, &i, "will be \"*\""))) {
2021 strcpy(ics_handle, star_match[0]);
2025 if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
2027 sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
2028 DisplayIcsInteractionTitle(buf);
2029 have_set_title = TRUE;
2032 /* skip finger notes */
2033 if (started == STARTED_NONE &&
2034 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
2035 (buf[i] == '1' && buf[i+1] == '0')) &&
2036 buf[i+2] == ':' && buf[i+3] == ' ') {
2037 started = STARTED_CHATTER;
2042 /* skip formula vars */
2043 if (started == STARTED_NONE &&
2044 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
2045 started = STARTED_CHATTER;
2051 /* save position of char array pointer for zippy */
2053 /* Try found special things that never works with color */
2054 /* I really don't know why - code is ok. */
2057 if (appData.zippyTalk || appData.zippyPlay) {
2058 if (looking_at(buf, &save, "* tells you: *") ||
2059 looking_at(buf, &save, "* says: *")) {
2060 player = StripHighlightAndTitle(star_match[0]);
2061 if (appData.zippyPassword[0] != NULLCHAR &&
2062 strncmp(star_match[1], appData.zippyPassword,
2063 strlen(appData.zippyPassword)) == 0) {
2064 q = star_match[1] + strlen(appData.zippyPassword);
2065 while (*q == ' ') q++;
2066 fprintf(debugFP, "zippy 1 %s \n", q);
2069 } else if(appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
2070 strncmp(star_match[1], appData.zippyPassword2,
2071 strlen(appData.zippyPassword2)) == 0) {
2072 fprintf(debugFP, "zippy2vor: %s \n", q);
2073 q = star_match[1] + strlen(appData.zippyPassword2);
2074 while (*q == ' ') q++;
2075 SendToProgram(q, &first);
2076 SendToProgram("\n", &first);
2078 } else if (appData.zippyPassword3[0] != NULLCHAR && first.initDone &&
2079 strncmp(star_match[1], appData.zippyPassword3,
2080 strlen(appData.zippyPassword3)) == 0 &&
2081 appData.userVersion == FALSE) {
2082 q = star_match[1] + strlen(appData.zippyPassword3);
2083 ParseZippyP3(q, player);
2085 } else if (strncmp(star_match[1], "showinfo", 8) == 0 &&
2086 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2087 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2088 q = star_match[1] + strlen("showinfo");
2089 showInfo(q, player);
2090 } else if (strncmp(star_match[1], "showgames", 9) == 0 &&
2091 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2092 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2094 showInfo(q, player);
2096 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
2097 strncmp(star_match[1], appData.zippyWrongPassword,
2098 strlen(appData.zippyWrongPassword)) == 0) {
2099 q = star_match[1] + strlen(appData.zippyWrongPassword);
2100 while (*q == ' ') q++;
2101 sprintf(reply, "wrong %s\n", player);
2105 /* workaround for icc and freechess.org */
2106 if (looking_at(buf, &save, "Your opponent offers you a draw") ||
2107 looking_at(buf, &save, "offers you a draw") ||
2108 looking_at(buf, &save, "* offers you a draw")) {
2109 if (first.sendDrawOffers && first.initDone) {
2110 SendToProgram("draw\n", &first);
2111 /* Handling zippy Draw */
2112 } else if (appData.zippyDraw && first.initDone) {
2113 //ZippyDraw(1, programStats.score, programStats.depth);
2116 if (appData.zippyPlay && first.initDone && loggedOn == TRUE) ZippyMatch(buf, &save, player);
2119 /* Make color for all and for zippy */
2120 if (/* Don't color "message" or "messages" output */
2121 (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
2122 looking_at(buf, &i, "*. * at *:*: ") ||
2123 looking_at(buf, &i, "--* (*:*): ") ||
2124 /* Regular tells and says */
2125 (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
2126 looking_at(buf, &i, "* (your partner) tells you: ") ||
2127 looking_at(buf, &i, "* says: ") ||
2128 /* Message notifications (same color as tells) */
2129 looking_at(buf, &i, "* has left a message ") ||
2130 looking_at(buf, &i, "* just sent you a message:\n") ||
2131 /* Whispers and kibitzes */
2132 (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
2133 looking_at(buf, &i, "* kibitzes: ") ||
2135 (tkind = 3, looking_at(buf, &i, "*(*: "))) {
2137 if (tkind == 1 && strchr(star_match[0], ':')) {
2138 /* Avoid "tells you:" spoofs in channels */
2141 if (star_match[0][0] == NULLCHAR ||
2142 strchr(star_match[0], ' ') ||
2143 (tkind == 3 && strchr(star_match[1], ' '))) {
2144 /* Reject bogus matches */
2147 if (appData.colorize) {
2148 if (oldi > next_out) {
2149 SendToPlayer(&buf[next_out], oldi - next_out);
2154 Colorize(ColorTell, FALSE);
2155 curColor = ColorTell;
2158 Colorize(ColorKibitz, FALSE);
2159 curColor = ColorKibitz;
2162 p = strrchr(star_match[1], '(');
2169 Colorize(ColorChannel1, FALSE);
2170 curColor = ColorChannel1;
2172 Colorize(ColorChannel, FALSE);
2173 curColor = ColorChannel;
2177 curColor = ColorNormal;
2181 if (started == STARTED_NONE && appData.autoComment &&
2182 (gameMode == IcsObserving ||
2183 gameMode == IcsPlayingWhite ||
2184 gameMode == IcsPlayingBlack)) {
2185 parse_pos = i - oldi;
2186 memcpy(parse, &buf[oldi], parse_pos);
2187 parse[parse_pos] = NULLCHAR;
2188 started = STARTED_COMMENT;
2189 savingComment = TRUE;
2191 started = STARTED_CHATTER;
2192 savingComment = FALSE;
2199 if (looking_at(buf, &i, "* s-shouts: ") ||
2200 looking_at(buf, &i, "* c-shouts: ")) {
2201 if (appData.colorize) {
2202 if (oldi > next_out) {
2203 SendToPlayer(&buf[next_out], oldi - next_out);
2206 Colorize(ColorSShout, FALSE);
2207 curColor = ColorSShout;
2210 started = STARTED_CHATTER;
2214 if (looking_at(buf, &i, "--->")) {
2219 if (looking_at(buf, &i, "* shouts: ") ||
2220 looking_at(buf, &i, "--> ")) {
2221 if (appData.colorize) {
2222 if (oldi > next_out) {
2223 SendToPlayer(&buf[next_out], oldi - next_out);
2226 Colorize(ColorShout, FALSE);
2227 curColor = ColorShout;
2230 started = STARTED_CHATTER;
2234 if (looking_at( buf, &i, "Challenge:")) {
2235 if (appData.colorize) {
2236 if (oldi > next_out) {
2237 SendToPlayer(&buf[next_out], oldi - next_out);
2240 Colorize(ColorChallenge, FALSE);
2241 curColor = ColorChallenge;
2247 if (looking_at(buf, &i, "* offers you") ||
2248 looking_at(buf, &i, "* offers to be") ||
2249 looking_at(buf, &i, "* would like to") ||
2250 looking_at(buf, &i, "* requests to") ||
2251 looking_at(buf, &i, "Your opponent offers") ||
2252 looking_at(buf, &i, "Your opponent requests")) {
2254 if (appData.colorize) {
2255 if (oldi > next_out) {
2256 SendToPlayer(&buf[next_out], oldi - next_out);
2259 Colorize(ColorRequest, FALSE);
2260 curColor = ColorRequest;
2265 if (looking_at(buf, &i, "* (*) seeking")) {
2266 if (appData.colorize) {
2267 if (oldi > next_out) {
2268 SendToPlayer(&buf[next_out], oldi - next_out);
2271 Colorize(ColorSeek, FALSE);
2272 curColor = ColorSeek;
2278 if (looking_at(buf, &i, "\\ ")) {
2279 if (prevColor != ColorNormal) {
2280 if (oldi > next_out) {
2281 SendToPlayer(&buf[next_out], oldi - next_out);
2284 Colorize(prevColor, TRUE);
2285 curColor = prevColor;
2287 if (savingComment) {
2288 parse_pos = i - oldi;
2289 memcpy(parse, &buf[oldi], parse_pos);
2290 parse[parse_pos] = NULLCHAR;
2291 started = STARTED_COMMENT;
2293 started = STARTED_CHATTER;
2298 if (looking_at(buf, &i, "Black Strength :") ||
2299 looking_at(buf, &i, "<<< style 10 board >>>") ||
2300 looking_at(buf, &i, "<10>") ||
2301 looking_at(buf, &i, "#@#")) {
2302 /* Wrong board style */
2304 SendToICS(ics_prefix);
2305 SendToICS("set style 12\n");
2306 SendToICS(ics_prefix);
2307 SendToICS("refresh\n");
2311 if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
2313 have_sent_ICS_logon = 1;
2317 if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
2318 (looking_at(buf, &i, "\n<12> ") ||
2319 looking_at(buf, &i, "<12> "))) {
2321 if (oldi > next_out) {
2322 SendToPlayer(&buf[next_out], oldi - next_out);
2325 started = STARTED_BOARD;
2330 if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
2331 looking_at(buf, &i, "<b1> ")) {
2332 if (oldi > next_out) {
2333 SendToPlayer(&buf[next_out], oldi - next_out);
2336 started = STARTED_HOLDINGS;
2341 /* Send buf now to zippy */
2342 if (appData.zippyTalk || appData.zippyPlay) {
2344 if (ZippyControl(buf, &save) || ZippyConverse(buf, &save)) {
2351 /* ICS: init icsQueue */
2352 if (appData.zippyPlay && first.initDone && gameMode == IcsObserving) {
2353 if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
2354 if (appData.debugMode) fprintf(debugFP, "To high gamenumber or gamenumber -1 !\n");
2357 if (icsQueue[ics_gamenum].killPv == 0) {
2358 icsQueue[ics_gamenum].move = currentMove;
2359 icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
2360 icsQueue[ics_gamenum].counter = 0;
2361 strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
2362 strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
2366 if (looking_at(buf, &i, "* *vs. * *--- *")) {
2368 /* Header for a move list -- first line */
2369 switch (ics_getting_history) {
2373 case BeginningOfGame:
2374 /* User typed "moves" or "oldmoves" while we
2375 were idle. Pretend we asked for these
2376 moves and soak them up so user can step
2377 through them and/or save them.
2380 gameMode = IcsObserving;
2383 ics_getting_history = H_GOT_UNREQ_HEADER;
2385 case EditGame: /*?*/
2386 case EditPosition: /*?*/
2387 /* Should above feature work in these modes too? */
2388 /* For now it doesn't */
2389 ics_getting_history = H_GOT_UNWANTED_HEADER;
2392 ics_getting_history = H_GOT_UNWANTED_HEADER;
2397 /* Is this the right one? */
2398 if (gameInfo.white && gameInfo.black &&
2399 strcmp(gameInfo.white, star_match[0]) == 0 &&
2400 strcmp(gameInfo.black, star_match[2]) == 0) {
2402 ics_getting_history = H_GOT_REQ_HEADER;
2405 case H_GOT_REQ_HEADER:
2406 case H_GOT_UNREQ_HEADER:
2407 case H_GOT_UNWANTED_HEADER:
2408 case H_GETTING_MOVES:
2409 /* Should not happen */
2410 DisplayError("Error gathering move list: two headers", 0);
2411 ics_getting_history = H_FALSE;
2415 /* Save player ratings into gameInfo if needed */
2416 if ((ics_getting_history == H_GOT_REQ_HEADER ||
2417 ics_getting_history == H_GOT_UNREQ_HEADER) &&
2418 (gameInfo.whiteRating == -1 ||
2419 gameInfo.blackRating == -1)) {
2421 gameInfo.whiteRating = string_to_rating(star_match[1]);
2422 gameInfo.blackRating = string_to_rating(star_match[3]);
2423 if (appData.debugMode)
2424 fprintf(debugFP, "Ratings from header: W %d, B %d\n",
2425 gameInfo.whiteRating, gameInfo.blackRating);
2430 if (looking_at(buf, &i,
2431 "* * match, initial time: * minute*, increment: * second")) {
2432 /* Header for a move list -- second line */
2433 /* Initial board will follow if this is a wild game */
2435 if (gameInfo.event != NULL) free(gameInfo.event);
2436 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
2437 gameInfo.event = StrSave(str);
2438 gameInfo.variant = StringToVariant(gameInfo.event);
2442 if (looking_at(buf, &i, "Move ")) {
2443 /* Beginning of a move list */
2444 switch (ics_getting_history) {
2446 /* Normally should not happen */
2447 /* Maybe user hit reset while we were parsing */
2450 /* Happens if we are ignoring a move list that is not
2451 * the one we just requested. Common if the user
2452 * tries to observe two games without turning off
2455 case H_GETTING_MOVES:
2456 /* Should not happen */
2457 DisplayError("Error gathering move list: nested", 0);
2458 ics_getting_history = H_FALSE;
2460 case H_GOT_REQ_HEADER:
2461 ics_getting_history = H_GETTING_MOVES;
2462 started = STARTED_MOVES;
2464 if (oldi > next_out) {
2465 SendToPlayer(&buf[next_out], oldi - next_out);
2468 case H_GOT_UNREQ_HEADER:
2469 ics_getting_history = H_GETTING_MOVES;
2470 started = STARTED_MOVES_NOHIDE;
2473 case H_GOT_UNWANTED_HEADER:
2474 ics_getting_history = H_FALSE;
2480 if (looking_at(buf, &i, "% ") ||
2481 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
2482 && looking_at(buf, &i, "}*"))) {
2483 savingComment = FALSE;
2486 case STARTED_MOVES_NOHIDE:
2487 memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
2488 parse[parse_pos + i - oldi] = NULLCHAR;
2489 ParseGameHistory(parse);
2491 if (appData.zippyPlay && first.initDone) {
2492 if (appData.icsAnalyze && gameMode == IcsObserving) {
2495 /* icsAnalyze send the moves to engine if we start new */
2496 if (!appData.icsAnalyze) FeedMovesToProgram(&first, forwardMostMove);
2497 /* Bug 4.2.6: Engine want play, skip that */
2498 if (gameMode == IcsExamining) {
2500 SendToProgram("force\n", &first);
2502 if (gameMode == IcsPlayingWhite) {
2503 if (appData.icsAnalyze) {
2505 appData.icsAnalyze = FALSE;
2508 if (WhiteOnMove(forwardMostMove)) {
2509 if (first.sendTime) {
2510 if (first.useColors) {
2511 SendToProgram("black\n", &first);
2513 SendTimeRemaining(&first, TRUE);
2515 if (first.useColors) {
2516 SendToProgram("white\ngo\n", &first);
2518 SendToProgram("go\n", &first);
2520 first.maybeThinking = TRUE;
2522 if (first.usePlayother) {
2523 if (first.sendTime) {
2524 SendTimeRemaining(&first, TRUE);
2526 SendToProgram("playother\n", &first);
2532 } else if (gameMode == IcsPlayingBlack) {
2533 if (appData.icsAnalyze) {
2535 appData.icsAnalyze = FALSE;
2536 SendToICS("refresh\n");
2538 /* engineRoom stay forever */
2539 if (!WhiteOnMove(forwardMostMove)) {
2540 if (first.sendTime) {
2541 if (first.useColors) {
2542 SendToProgram("white\n", &first);
2544 SendTimeRemaining(&first, FALSE);
2546 if (first.useColors) {
2547 SendToProgram("black\ngo\n", &first);
2549 SendToProgram("go\n", &first);
2551 first.maybeThinking = TRUE;
2553 if (first.usePlayother) {
2554 if (first.sendTime) {
2555 SendTimeRemaining(&first, FALSE);
2557 SendToProgram("playother\n", &first);
2568 if (gameMode == IcsObserving && ics_gamenum == -1) {
2569 /* Moves came from oldmoves or moves command
2570 while we weren't doing anything else.
2572 currentMove = forwardMostMove;
2573 ClearHighlights();/*!!could figure this out*/
2574 flipView = appData.flipView;
2575 DrawPosition(FALSE, boards[currentMove]);
2576 DisplayBothClocks();
2577 sprintf(str, "%s vs. %s",
2578 gameInfo.white, gameInfo.black);
2582 /* Moves were history of an active game */
2583 if (gameInfo.resultDetails != NULL) {
2584 free(gameInfo.resultDetails);
2585 gameInfo.resultDetails = NULL;
2588 HistorySet(parseList, backwardMostMove,
2589 forwardMostMove, currentMove-1);
2590 DisplayMove(currentMove - 1);
2591 if (started == STARTED_MOVES) next_out = i;
2592 started = STARTED_NONE;
2593 ics_getting_history = H_FALSE;
2596 case STARTED_OBSERVE:
2597 started = STARTED_NONE;
2598 SendToICS(ics_prefix);
2599 SendToICS("refresh\n");
2608 if ((started == STARTED_MOVES || started == STARTED_BOARD ||
2609 started == STARTED_HOLDINGS ||
2610 started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
2611 /* Accumulate characters in move list or board */
2612 parse[parse_pos++] = buf[i];
2615 /* Start of game messages. Mostly we detect start of game
2616 when the first board image arrives. On some versions
2617 of the ICS, though, we need to do a "refresh" after starting
2618 to observe in order to get the current board right away. */
2619 if (looking_at(buf, &i, "Adding game * to observation list")) {
2620 started = STARTED_OBSERVE;
2624 /* Handle auto-observe */
2625 if (appData.autoObserve &&
2626 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
2627 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
2629 /* Choose the player that was highlighted, if any. */
2630 if (star_match[0][0] == '\033' ||
2631 star_match[1][0] != '\033') {
2632 player = star_match[0];
2634 player = star_match[2];
2636 sprintf(str, "%sobserve %s\n",
2637 ics_prefix, StripHighlightAndTitle(player));
2640 /* Save ratings from notify string */
2641 strcpy(player1Name, star_match[0]);
2642 player1Rating = string_to_rating(star_match[1]);
2643 strcpy(player2Name, star_match[2]);
2644 player2Rating = string_to_rating(star_match[3]);
2646 if (appData.debugMode)
2648 "Ratings from 'Game notification:' %s %d, %s %d\n",
2649 player1Name, player1Rating,
2650 player2Name, player2Rating);
2655 /* Deal with automatic examine mode after a game,
2656 and with IcsObserving -> IcsExamining transition */
2657 if (looking_at(buf, &i, "Entering examine mode for game *") ||
2658 looking_at(buf, &i, "has made you an examiner of game *")) {
2660 int gamenum = atoi(star_match[0]);
2661 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
2662 gamenum == ics_gamenum) {
2663 /* We were already playing or observing this game;
2664 no need to refetch history */
2665 gameMode = IcsExamining;
2667 pauseExamForwardMostMove = forwardMostMove;
2668 } else if (currentMove < forwardMostMove) {
2669 ForwardInner(forwardMostMove);
2672 /* I don't think this case really can happen */
2673 SendToICS(ics_prefix);
2674 SendToICS("refresh\n");
2679 /* Error messages */
2680 if (ics_user_moved) {
2681 if (looking_at(buf, &i, "Illegal move") ||
2682 looking_at(buf, &i, "Not a legal move") ||
2683 looking_at(buf, &i, "Your king is in check") ||
2684 looking_at(buf, &i, "It isn't your turn") ||
2685 looking_at(buf, &i, "It is not your move")) {
2688 if (forwardMostMove > backwardMostMove) {
2689 currentMove = --forwardMostMove;
2690 DisplayMove(currentMove - 1); /* before DMError */
2691 DisplayMoveError("Illegal move (rejected by ICS)");
2692 DrawPosition(FALSE, boards[currentMove]);
2694 DisplayBothClocks();
2700 if (looking_at(buf, &i, "still have time") ||
2701 looking_at(buf, &i, "not out of time") ||
2702 looking_at(buf, &i, "either player is out of time") ||
2703 looking_at(buf, &i, "has timeseal; checking")) {
2704 /* We must have called his flag a little too soon */
2705 whiteFlag = blackFlag = FALSE;
2709 if (looking_at(buf, &i, "added * seconds to") ||
2710 looking_at(buf, &i, "seconds were added to")) {
2711 /* Update the clocks */
2712 SendToICS(ics_prefix);
2713 SendToICS("refresh\n");
2717 if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
2718 ics_clock_paused = TRUE;
2723 if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
2724 ics_clock_paused = FALSE;
2729 /* Grab player ratings from the Creating: message.
2730 Note we have to check for the special case when
2731 the ICS inserts things like [white] or [black]. */
2732 if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
2733 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
2735 0 player 1 name (not necessarily white)
2737 2 empty, white, or black (IGNORED)
2738 3 player 2 name (not necessarily black)
2741 The names/ratings are sorted out when the game
2742 actually starts (below).
2744 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
2745 player1Rating = string_to_rating(star_match[1]);
2746 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
2747 player2Rating = string_to_rating(star_match[4]);
2749 if (appData.debugMode)
2751 "Ratings from 'Creating:' %s %d, %s %d\n",
2752 player1Name, player1Rating,
2753 player2Name, player2Rating);
2758 /* Improved generic start/end-of-game messages */
2759 if (looking_at(buf, &i, "{Game * (* vs. *) *}*")) {
2760 /* star_match[0] is the game number */
2761 /* [1] is the white player's name */
2762 /* [2] is the black player's name */
2763 /* For end-of-game: */
2764 /* [3] is the reason for the game end */
2765 /* [4] is a PGN end game-token, preceded by " " */
2766 /* For start-of-game: */
2767 /* [3] begins with "Creating" or "Continuing" */
2768 /* [4] is " *" or empty (don't care). */
2769 int gamenum = atoi(star_match[0]);
2770 char *why = star_match[3];
2771 char *endtoken = star_match[4];
2772 ChessMove endtype = (ChessMove) 0;
2774 /* Game start messages */
2775 if (strncmp(why, "Creating ", 9) == 0 ||
2776 strncmp(why, "Continuing ", 11) == 0) {
2777 gs_gamenum = gamenum;
2778 strcpy(gs_kind, strchr(why, ' ') + 1);
2780 if (appData.zippyPlay) {
2781 ZippyGameStart(star_match[1], star_match[2]);
2787 /* Game end messages */
2788 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
2789 ics_gamenum != gamenum) {
2792 while (endtoken[0] == ' ') endtoken++;
2793 switch (endtoken[0]) {
2796 endtype = GameUnfinished;
2799 endtype = BlackWins;
2802 if (endtoken[1] == '/')
2803 endtype = GameIsDrawn;
2805 endtype = WhiteWins;
2808 GameEnds(endtype, why, GE_ICS);
2810 if (appData.zippyPlay && first.initDone) {
2811 ZippyGameEnd(endtype, why);
2812 if (first.pr == NULL) {
2813 /* Start the next process early so that we'll
2814 be ready for the next challenge */
2815 StartChessProgram(&first);
2817 /* Send "new" early, in case this command takes
2818 a long time to finish, so that we'll be ready
2819 for the next challenge. */
2826 if (looking_at(buf, &i, "Removing game * from observation") ||
2827 looking_at(buf, &i, "no longer observing game *") ||
2828 looking_at(buf, &i, "Game * (*) has no examiners")) {
2829 if (gameMode == IcsObserving &&
2830 atoi(star_match[0]) == ics_gamenum)
2832 if (appData.icsAnalyze) IcsAnalyze(FALSE);
2833 ResetIcsQueue(ics_gamenum);
2837 ics_user_moved = FALSE;
2842 if (looking_at(buf, &i, "no longer examining game *")) {
2843 if (gameMode == IcsExamining &&
2844 atoi(star_match[0]) == ics_gamenum)
2846 if (appData.icsAnalyze) IcsAnalyze(FALSE);
2847 ResetIcsQueue(ics_gamenum);
2850 ics_user_moved = FALSE;
2855 /* Advance leftover_start past any newlines we find,
2856 so only partial lines can get reparsed */
2857 if (looking_at(buf, &i, "\n")) {
2858 prevColor = curColor;
2859 if (curColor != ColorNormal) {
2860 if (oldi > next_out) {
2861 SendToPlayer(&buf[next_out], oldi - next_out);
2864 Colorize(ColorNormal, FALSE);
2865 curColor = ColorNormal;
2867 if (started == STARTED_BOARD) {
2868 started = STARTED_NONE;
2869 parse[parse_pos] = NULLCHAR;
2870 ParseBoard12(parse);
2873 /* Send premove here */
2874 if (appData.premove) {
2876 if (currentMove == 0 &&
2877 gameMode == IcsPlayingWhite &&
2878 appData.premoveWhite) {
2879 sprintf(str, "%s%s\n", ics_prefix,
2880 appData.premoveWhiteText);
2881 if (appData.debugMode)
2882 fprintf(debugFP, "Sending premove:\n");
2884 } else if (currentMove == 1 &&
2885 gameMode == IcsPlayingBlack &&
2886 appData.premoveBlack) {
2887 sprintf(str, "%s%s\n", ics_prefix,
2888 appData.premoveBlackText);
2889 if (appData.debugMode)
2890 fprintf(debugFP, "Sending premove:\n");
2892 } else if (gotPremove) {
2894 ClearPremoveHighlights();
2895 if (appData.debugMode)
2896 fprintf(debugFP, "Sending premove:\n");
2897 UserMoveEvent(premoveFromX, premoveFromY,
2898 premoveToX, premoveToY,
2903 /* Usually suppress following prompt */
2904 if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
2905 if (looking_at(buf, &i, "*% ")) {
2906 savingComment = FALSE;
2910 } else if (started == STARTED_HOLDINGS) {
2912 char new_piece[MSG_SIZ];
2913 started = STARTED_NONE;
2914 parse[parse_pos] = NULLCHAR;
2915 if (appData.debugMode)
2916 fprintf(debugFP, "Parsing holdings: %s\n", parse);
2917 if (sscanf(parse, " game %d", &gamenum) == 1 &&
2918 gamenum == ics_gamenum) {
2919 if (gameInfo.variant == VariantNormal) {
2920 gameInfo.variant = VariantCrazyhouse; /*temp guess*/
2921 /* Get a move list just to see the header, which
2922 will tell us whether this is really bug or zh */
2923 if (ics_getting_history == H_FALSE) {
2924 ics_getting_history = H_REQUESTED;
2925 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2929 new_piece[0] = NULLCHAR;
2930 sscanf(parse, "game %d white [%s black [%s <- %s",
2931 &gamenum, white_holding, black_holding,
2933 white_holding[strlen(white_holding)-1] = NULLCHAR;
2934 black_holding[strlen(black_holding)-1] = NULLCHAR;
2936 if (appData.zippyPlay && first.initDone) {
2937 ZippyHoldings(white_holding, black_holding,
2941 if (tinyLayout || smallLayout) {
2942 char wh[16], bh[16];
2943 PackHolding(wh, white_holding);
2944 PackHolding(bh, black_holding);
2945 sprintf(str, "[%s-%s] %s-%s", wh, bh,
2946 gameInfo.white, gameInfo.black);
2948 sprintf(str, "%s [%s] vs. %s [%s]",
2949 gameInfo.white, white_holding,
2950 gameInfo.black, black_holding);
2952 DrawPosition(FALSE, NULL);
2955 /* Suppress following prompt */
2956 if (looking_at(buf, &i, "*% ")) {
2957 savingComment = FALSE;
2964 i++; /* skip unparsed character and loop back */
2967 if (started != STARTED_MOVES && started != STARTED_BOARD &&
2968 started != STARTED_HOLDINGS && i > next_out) {
2969 SendToPlayer(&buf[next_out], i - next_out);
2973 leftover_len = buf_len - leftover_start;
2974 /* if buffer ends with something we couldn't parse,
2975 reparse it after appending the next read */
2977 } else if (count == 0) {
2978 RemoveInputSource(isr);
2979 DisplayFatalError("Connection closed by ICS", 0, 0);
2981 DisplayFatalError("Error reading from ICS", error, 1);
2986 /* Board style 12 looks like this:
2988 <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
2990 * The "<12> " is stripped before it gets to this routine. The two
2991 * trailing 0's (flip state and clock ticking) are later addition, and
2992 * some chess servers may not have them, or may have only the first.
2993 * Additional trailing fields may be added in the future.
2996 #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"
2998 #define RELATION_OBSERVING_PLAYED 0
2999 #define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
3000 #define RELATION_PLAYING_MYMOVE 1
3001 #define RELATION_PLAYING_NOTMYMOVE -1
3002 #define RELATION_EXAMINING 2
3003 #define RELATION_ISOLATED_BOARD -3
3004 #define RELATION_STARTING_POSITION -4 /* FICS only */
3007 ParseBoard12(string)
3010 GameMode newGameMode;
3011 int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
3012 int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
3013 int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
3015 char to_play, board_chars[72];
3016 char move_str[500], str[500], elapsed_time[500];
3017 char black[32], white[32];
3019 int prevMove = currentMove;
3022 int fromX, fromY, toX, toY;
3025 fromX = fromY = toX = toY = -1;
3029 if (appData.debugMode)
3030 fprintf(debugFP, "Parsing board: %s\n", string);
3032 move_str[0] = NULLCHAR;
3033 elapsed_time[0] = NULLCHAR;
3034 n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
3035 &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
3036 &gamenum, white, black, &relation, &basetime, &increment,
3037 &white_stren, &black_stren, &white_time, &black_time,
3038 &moveNum, str, elapsed_time, move_str, &ics_flip,
3042 sprintf(str, "Failed to parse board string:\n\"%s\"", string);
3043 DisplayError(str, 0);
3047 /* Convert the move number to internal form */
3048 moveNum = (moveNum - 1) * 2;
3049 if (to_play == 'B') moveNum++;
3050 if (moveNum >= MAX_MOVES) {
3051 DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
3057 case RELATION_OBSERVING_PLAYED:
3058 case RELATION_OBSERVING_STATIC:
3059 if (gamenum == -1) {
3060 /* Old ICC buglet */
3061 relation = RELATION_OBSERVING_STATIC;
3063 newGameMode = IcsObserving;
3065 case RELATION_PLAYING_MYMOVE:
3066 case RELATION_PLAYING_NOTMYMOVE:
3068 ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
3069 IcsPlayingWhite : IcsPlayingBlack;
3071 case RELATION_EXAMINING:
3072 newGameMode = IcsExamining;
3074 case RELATION_ISOLATED_BOARD:
3076 /* Just display this board. If user was doing something else,
3077 we will forget about it until the next board comes. */
3078 newGameMode = IcsIdle;
3080 case RELATION_STARTING_POSITION:
3081 newGameMode = gameMode;
3082 /* if we switch to a new board start IcsAnalyze */
3083 if(appData.icsAnalyze) IcsAnalyze(TRUE);
3087 /* Modify behavior for initial board display on move listing
3090 switch (ics_getting_history) {
3094 case H_GOT_REQ_HEADER:
3095 case H_GOT_UNREQ_HEADER:
3096 /* This is the initial position of the current game */
3097 gamenum = ics_gamenum;
3098 moveNum = 0; /* old ICS bug workaround */
3099 if (to_play == 'B') {
3100 startedFromSetupPosition = TRUE;
3101 blackPlaysFirst = TRUE;
3103 if (forwardMostMove == 0) forwardMostMove = 1;
3104 if (backwardMostMove == 0) backwardMostMove = 1;
3105 if (currentMove == 0) currentMove = 1;
3107 newGameMode = gameMode;
3108 relation = RELATION_STARTING_POSITION; /* ICC needs this */
3110 case H_GOT_UNWANTED_HEADER:
3111 /* This is an initial board that we don't want */
3113 case H_GETTING_MOVES:
3114 /* Should not happen */
3115 DisplayError("Error gathering move list: extra board", 0);
3116 ics_getting_history = H_FALSE;
3120 /* Take action if this is the first board of a new game, or of a
3121 different game than is currently being displayed. */
3122 if (gamenum != ics_gamenum || newGameMode != gameMode ||
3123 relation == RELATION_ISOLATED_BOARD) {
3125 /* Forget the old game and get the history (if any) of the new one */
3126 if (gameMode != BeginningOfGame) {
3130 if (appData.autoRaiseBoard) BoardToTop();
3132 if (gamenum == -1) {
3133 newGameMode = IcsIdle;
3134 } else if (moveNum > 0 && newGameMode != IcsIdle &&
3135 appData.getMoveList) {
3136 /* Need to get game history */
3137 ics_getting_history = H_REQUESTED;
3138 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3143 /* Initially flip the board to have black on the bottom if playing
3144 black or if the ICS flip flag is set, but let the user change
3145 it with the Flip View button. */
3146 flipView = appData.autoFlipView ?
3147 (newGameMode == IcsPlayingBlack) || ics_flip :
3150 /* Done with values from previous mode; copy in new ones */
3151 gameMode = newGameMode;
3153 ics_gamenum = gamenum;
3154 if (gamenum == gs_gamenum) {
3155 int klen = strlen(gs_kind);
3156 if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
3157 sprintf(str, "ICS %s", gs_kind);
3158 gameInfo.event = StrSave(str);
3160 gameInfo.event = StrSave("ICS game");
3162 gameInfo.site = StrSave(appData.icsHost);
3163 gameInfo.date = PGNDate();
3164 gameInfo.round = StrSave("-");
3165 gameInfo.white = StrSave(white);
3166 gameInfo.black = StrSave(black);
3167 timeControl = basetime * 60 * 1000;
3168 timeIncrement = increment * 1000;
3169 movesPerSession = 0;
3170 gameInfo.timeControl = TimeControlTagValue();
3171 gameInfo.variant = StringToVariant(gameInfo.event);
3173 /* Do we have the ratings? */
3174 if (strcmp(player1Name, white) == 0 &&
3175 strcmp(player2Name, black) == 0) {
3176 if (appData.debugMode)
3177 fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3178 player1Rating, player2Rating);
3179 gameInfo.whiteRating = player1Rating;
3180 gameInfo.blackRating = player2Rating;
3181 } else if (strcmp(player2Name, white) == 0 &&
3182 strcmp(player1Name, black) == 0) {
3183 if (appData.debugMode)
3184 fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3185 player2Rating, player1Rating);
3186 gameInfo.whiteRating = player2Rating;
3187 gameInfo.blackRating = player1Rating;
3189 player1Name[0] = player2Name[0] = NULLCHAR;
3191 /* Silence shouts if requested */
3192 if (appData.quietPlay &&
3193 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
3194 SendToICS(ics_prefix);
3195 SendToICS("set shout 0\n");
3199 /* Deal with midgame name changes */
3201 if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
3202 if (gameInfo.white) free(gameInfo.white);
3203 gameInfo.white = StrSave(white);
3205 if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
3206 if (gameInfo.black) free(gameInfo.black);
3207 gameInfo.black = StrSave(black);
3211 /* Throw away game result if anything actually changes in examine mode */
3212 if (gameMode == IcsExamining && !newGame) {
3213 gameInfo.result = GameUnfinished;
3214 if (gameInfo.resultDetails != NULL) {
3215 free(gameInfo.resultDetails);
3216 gameInfo.resultDetails = NULL;
3220 /* In pausing && IcsExamining mode, we ignore boards coming
3221 in if they are in a different variation than we are. */
3222 if (pauseExamInvalid) return;
3223 if (pausing && gameMode == IcsExamining) {
3224 if (moveNum <= pauseExamForwardMostMove) {
3225 pauseExamInvalid = TRUE;
3226 forwardMostMove = pauseExamForwardMostMove;
3231 /* Parse the board */
3232 for (k = 0; k < 8; k++)
3233 for (j = 0; j < 8; j++)
3234 board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
3235 CopyBoard(boards[moveNum], board);
3237 startedFromSetupPosition =
3238 !CompareBoards(board, initialPosition);
3241 if (ics_getting_history == H_GOT_REQ_HEADER ||
3242 ics_getting_history == H_GOT_UNREQ_HEADER) {
3243 /* This was an initial position from a move list, not
3244 the current position */
3248 /* Update currentMove and known move number limits */
3249 newMove = newGame || moveNum > forwardMostMove;
3252 /* If we found takebacks during IcsAnalyze try send to engine */
3253 if (!newGame && appData.icsAnalyze && first.analyzing &&
3254 moveNum < forwardMostMove) {
3255 if (appData.debugMode) fprintf(debugFP, "take back move\n");
3256 takeback = forwardMostMove - moveNum;
3257 if (!appData.icsWBprotoAgr) {
3259 SendToProgram("exit\n", &first);
3260 SendToProgram("force\n", &first);
3261 for (i = 0; i < takeback; i++) {
3262 SendToProgram("undo\n", &first);
3263 /* Some engine need analyze and stat again */
3265 SendToProgram("analyze\n", &first);
3273 forwardMostMove = backwardMostMove = currentMove = moveNum;
3274 if (gameMode == IcsExamining && moveNum == 0) {
3275 /* Workaround for ICS limitation: we are not told the wild
3276 type when starting to examine a game. But if we ask for
3277 the move list, the move list header will tell us */
3278 ics_getting_history = H_REQUESTED;
3279 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3282 } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
3283 || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
3284 forwardMostMove = moveNum;
3285 if (!pausing || currentMove > forwardMostMove)
3286 currentMove = forwardMostMove;
3288 /* New part of history that is not contiguous with old part */
3289 if (pausing && gameMode == IcsExamining) {
3290 pauseExamInvalid = TRUE;
3291 forwardMostMove = pauseExamForwardMostMove;
3294 forwardMostMove = backwardMostMove = currentMove = moveNum;
3295 if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
3296 ics_getting_history = H_REQUESTED;
3297 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3304 /* Update the clocks */
3305 if (strchr(elapsed_time, '.')) {
3307 timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
3308 timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
3310 /* Time is in seconds */
3312 timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
3313 timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
3317 /* Time workaround for ICC - send only sec :( */
3318 if (ics_type == ICS_ICC) {
3319 if (timeRemaining[0][moveNum] >= 1500) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 1500;
3320 if (timeRemaining[1][moveNum] >= 1500) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 1500;
3322 if (timeRemaining[0][moveNum] == 1000) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 950;
3323 if (timeRemaining[0][moveNum] == 1000) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 950;
3328 if (appData.zippyPlay && newGame &&
3329 gameMode != IcsObserving && gameMode != IcsIdle &&
3330 gameMode != IcsExamining)
3331 ZippyFirstBoard(moveNum, basetime, increment);
3334 /* Put the move on the move list, first converting
3335 to canonical algebraic form. */
3337 if (moveNum <= backwardMostMove) {
3338 /* We don't know what the board looked like before
3340 strcpy(parseList[moveNum - 1], move_str);
3341 strcat(parseList[moveNum - 1], " ");
3342 strcat(parseList[moveNum - 1], elapsed_time);
3343 moveList[moveNum - 1][0] = NULLCHAR;
3344 } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
3345 &fromX, &fromY, &toX, &toY, &promoChar)) {
3346 (void) CoordsToAlgebraic(boards[moveNum - 1],
3347 PosFlags(moveNum - 1), EP_UNKNOWN,
3348 fromY, fromX, toY, toX, promoChar,
3349 parseList[moveNum-1]);
3350 switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
3356 strcat(parseList[moveNum - 1], "+");
3359 strcat(parseList[moveNum - 1], "#");
3362 strcat(parseList[moveNum - 1], " ");
3363 strcat(parseList[moveNum - 1], elapsed_time);
3364 /* currentMoveString is set as a side-effect of ParseOneMove */
3365 strcpy(moveList[moveNum - 1], currentMoveString);
3366 strcat(moveList[moveNum - 1], "\n");
3367 } else if (strcmp(move_str, "none") == 0) {
3368 /* Again, we don't know what the board looked like;
3369 this is really the start of the game. */
3370 parseList[moveNum - 1][0] = NULLCHAR;
3371 moveList[moveNum - 1][0] = NULLCHAR;
3372 backwardMostMove = moveNum;
3373 startedFromSetupPosition = TRUE;
3374 fromX = fromY = toX = toY = -1;
3376 /* Move from ICS was illegal!? Punt. */
3378 if (appData.testLegality && appData.debugMode) {
3379 sprintf(str, "Illegal move \"%s\" from ICS", move_str);
3380 DisplayError(str, 0);
3383 strcpy(parseList[moveNum - 1], move_str);
3384 strcat(parseList[moveNum - 1], " ");
3385 strcat(parseList[moveNum - 1], elapsed_time);
3386 moveList[moveNum - 1][0] = NULLCHAR;
3387 fromX = fromY = toX = toY = -1;
3391 /* Send move to chess program (BEFORE animating it). */
3392 if (appData.zippyPlay && !newGame && newMove &&
3393 (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
3395 if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
3396 (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
3397 if (moveList[moveNum - 1][0] == NULLCHAR) {
3398 sprintf(str, "Couldn't parse move \"%s\" from ICS",
3400 DisplayError(str, 0);
3402 if (first.sendTime) {
3403 SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
3405 SendMoveToProgram(moveNum - 1, &first);
3408 if (first.useColors) {
3409 SendToProgram(gameMode == IcsPlayingWhite ?
3411 "black\ngo\n", &first);
3413 SendToProgram("go\n", &first);
3415 first.maybeThinking = TRUE;
3418 } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
3419 if (moveList[moveNum - 1][0] == NULLCHAR) {
3420 sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
3421 DisplayError(str, 0);
3423 SendMoveToProgram(moveNum - 1, &first);
3430 if (moveNum > 0 && !gotPremove) {
3431 /* If move comes from a remote source, animate it. If it
3432 isn't remote, it will have already been animated. */
3433 if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
3434 AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
3436 if (!pausing && appData.highlightLastMove) {
3437 SetHighlights(fromX, fromY, toX, toY);
3441 /* Start the clocks */
3442 whiteFlag = blackFlag = FALSE;
3443 appData.clockMode = !(basetime == 0 && increment == 0);
3445 ics_clock_paused = TRUE;
3447 } else if (ticking == 1) {
3448 ics_clock_paused = FALSE;
3450 if (gameMode == IcsIdle ||
3451 relation == RELATION_OBSERVING_STATIC ||
3452 relation == RELATION_EXAMINING ||
3454 DisplayBothClocks();
3458 /* Display opponents and material strengths */
3459 if (gameInfo.variant != VariantBughouse &&
3460 gameInfo.variant != VariantCrazyhouse) {
3461 if (tinyLayout || smallLayout) {
3462 sprintf(str, "%s(%d) %s(%d) {%d %d}",
3463 gameInfo.white, white_stren, gameInfo.black, black_stren,
3464 basetime, increment);
3466 sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
3467 gameInfo.white, white_stren, gameInfo.black, black_stren,
3468 basetime, increment);
3474 /* Display the board */
3477 if (appData.premove)
3479 ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
3480 ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
3481 ClearPremoveHighlights();
3483 DrawPosition(FALSE, boards[currentMove]);
3484 DisplayMove(moveNum - 1);
3485 if (appData.ringBellAfterMoves && !ics_user_moved)
3489 HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
3496 if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
3497 ics_getting_history = H_REQUESTED;
3498 sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
3504 AnalysisPeriodicEvent(force)
3507 /* WB engine room */
3508 if (appData.AnalysisWindow && programStats.depth > 2) {
3509 /* GUI disable send stat ? */
3510 if (!appData.engineStatLine) SendToProgram(".\n", &first);
3511 /* don't support Stats on game ?*/
3512 if (supportStat == 0) {
3513 /* call Display every sec for time and nodes */
3514 DisplayAnalysis(1,0);
3516 /* GUI makes time ???? */
3517 /* at the moment: yes! */
3518 DisplayAnalysis(1,0);
3520 } else if (appData.icsAnalyze && programStats.depth > 2) {
3521 SendToProgram(".\n", &first);
3522 DisplayAnalysis(1,0);
3523 } else if (((programStats.ok_to_send == 0 || programStats.line_is_book)
3524 && !force) || !appData.periodicUpdates)
3527 /* Send . command to Crafty to collect stats */
3528 if (!appData.AnalysisWindow && (gameMode == AnalyzeMode ||
3529 gameMode == AnalyzeFile)) SendToProgram(".\n", &first);
3531 /* Don't send another until we get a response (this makes
3532 us stop sending to old Crafty's which don't understand
3533 the "." command (sending illegal cmds resets node count & time,
3534 which looks bad)) */
3535 programStats.ok_to_send = 0;
3539 SendMoveToProgram(moveNum, cps)
3541 ChessProgramState *cps;
3544 if (cps->useUsermove) {
3545 SendToProgram("usermove ", cps);
3549 if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
3550 int len = space - parseList[moveNum];
3551 memcpy(buf, parseList[moveNum], len);
3553 buf[len] = NULLCHAR;
3555 sprintf(buf, "%s\n", parseList[moveNum]);
3557 SendToProgram(buf, cps);
3559 SendToProgram(moveList[moveNum], cps);
3564 SendMoveToICS(moveType, fromX, fromY, toX, toY)
3566 int fromX, fromY, toX, toY;
3568 char user_move[MSG_SIZ];
3572 sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
3573 (int)moveType, fromX, fromY, toX, toY);
3574 DisplayError(user_move + strlen("say "), 0);
3576 case WhiteKingSideCastle:
3577 case BlackKingSideCastle:
3578 case WhiteQueenSideCastleWild:
3579 case BlackQueenSideCastleWild:
3580 sprintf(user_move, "o-o\n");
3582 case WhiteQueenSideCastle:
3583 case BlackQueenSideCastle:
3584 case WhiteKingSideCastleWild:
3585 case BlackKingSideCastleWild:
3586 sprintf(user_move, "o-o-o\n");
3588 case WhitePromotionQueen:
3589 case BlackPromotionQueen:
3590 case WhitePromotionRook:
3591 case BlackPromotionRook:
3592 case WhitePromotionBishop:
3593 case BlackPromotionBishop:
3594 case WhitePromotionKnight:
3595 case BlackPromotionKnight:
3596 case WhitePromotionKing:
3597 case BlackPromotionKing:
3598 sprintf(user_move, "%c%c%c%c=%c\n",
3599 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
3600 PieceToChar(PromoPiece(moveType)));
3604 sprintf(user_move, "%c@%c%c\n",
3605 ToUpper(PieceToChar((ChessSquare) fromX)),
3606 'a' + toX, '1' + toY);
3609 case WhiteCapturesEnPassant:
3610 case BlackCapturesEnPassant:
3611 case IllegalMove: /* could be a variant we don't quite understand */
3612 sprintf(user_move, "%c%c%c%c\n",
3613 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
3616 SendToICS(user_move);
3620 CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
3625 if (rf == DROP_RANK) {
3626 sprintf(move, "%c@%c%c\n",
3627 ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
3629 if (promoChar == 'x' || promoChar == NULLCHAR) {
3630 sprintf(move, "%c%c%c%c\n",
3631 'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
3633 sprintf(move, "%c%c%c%c%c\n",
3634 'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
3640 ProcessICSInitScript(f)
3645 while (fgets(buf, MSG_SIZ, f)) {
3646 SendToICSDelayed(buf,(long)appData.msLoginDelay);
3653 /* Parser for moves from gnuchess, ICS, or user typein box */
3655 ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
3658 ChessMove *moveType;
3659 int *fromX, *fromY, *toX, *toY;
3662 *moveType = yylexstr(moveNum, move);
3663 switch (*moveType) {
3664 case WhitePromotionQueen:
3665 case BlackPromotionQueen:
3666 case WhitePromotionRook:
3667 case BlackPromotionRook:
3668 case WhitePromotionBishop:
3669 case BlackPromotionBishop:
3670 case WhitePromotionKnight:
3671 case BlackPromotionKnight:
3672 case WhitePromotionKing:
3673 case BlackPromotionKing:
3675 case WhiteCapturesEnPassant:
3676 case BlackCapturesEnPassant:
3677 case WhiteKingSideCastle:
3678 case WhiteQueenSideCastle:
3679 case BlackKingSideCastle:
3680 case BlackQueenSideCastle:
3681 case WhiteKingSideCastleWild:
3682 case WhiteQueenSideCastleWild:
3683 case BlackKingSideCastleWild:
3684 case BlackQueenSideCastleWild:
3685 case IllegalMove: /* bug or odd chess variant */
3686 *fromX = currentMoveString[0] - 'a';
3687 *fromY = currentMoveString[1] - '1';
3688 *toX = currentMoveString[2] - 'a';
3689 *toY = currentMoveString[3] - '1';
3690 *promoChar = currentMoveString[4];
3691 if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
3692 *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
3693 *fromX = *fromY = *toX = *toY = 0;
3696 if (appData.testLegality) {
3697 return (*moveType != IllegalMove);
3699 return !(fromX == fromY && toX == toY);
3704 *fromX = *moveType == WhiteDrop ?
3705 (int) CharToPiece(ToUpper(currentMoveString[0])) :
3706 (int) CharToPiece(ToLower(currentMoveString[0]));
3708 *toX = currentMoveString[2] - 'a';
3709 *toY = currentMoveString[3] - '1';
3710 *promoChar = NULLCHAR;
3714 case ImpossibleMove:
3715 case (ChessMove) 0: /* end of file */
3725 *fromX = *fromY = *toX = *toY = 0;
3726 *promoChar = NULLCHAR;
3733 InitPosition(redraw)
3736 currentMove = forwardMostMove = backwardMostMove = 0;
3737 switch (gameInfo.variant) {
3739 CopyBoard(boards[0], initialPosition);
3741 case VariantTwoKings:
3742 CopyBoard(boards[0], twoKingsPosition);
3743 startedFromSetupPosition = TRUE;
3745 case VariantWildCastle:
3746 CopyBoard(boards[0], initialPosition);
3747 /* !!?shuffle with kings guaranteed to be on d or e file */
3749 case VariantNoCastle:
3750 CopyBoard(boards[0], initialPosition);
3751 /* !!?unconstrained back-rank shuffle */
3753 case VariantFischeRandom:
3754 CopyBoard(boards[0], initialPosition);
3755 /* !!shuffle according to FR rules */
3759 DrawPosition(FALSE, boards[currentMove]);
3763 SendBoard(cps, moveNum)
3764 ChessProgramState *cps;
3767 char message[MSG_SIZ];
3769 if (cps->useSetboard) {
3770 char* fen = PositionToFEN(moveNum);
3771 sprintf(message, "setboard %s\n", fen);
3772 SendToProgram(message, cps);
3778 /* Kludge to set black to move, avoiding the troublesome and now
3779 * deprecated "black" command.
3781 if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
3783 SendToProgram("edit\n", cps);
3784 SendToProgram("#\n", cps);
3785 for (i = BOARD_SIZE - 1; i >= 0; i--) {
3786 bp = &boards[moveNum][i][0];
3787 for (j = 0; j < BOARD_SIZE; j++, bp++) {
3788 if ((int) *bp < (int) BlackPawn) {
3789 sprintf(message, "%c%c%c\n", PieceToChar(*bp),
3791 SendToProgram(message, cps);
3796 SendToProgram("c\n", cps);
3797 for (i = BOARD_SIZE - 1; i >= 0; i--) {
3798 bp = &boards[moveNum][i][0];
3799 for (j = 0; j < BOARD_SIZE; j++, bp++) {
3800 if (((int) *bp != (int) EmptySquare)
3801 && ((int) *bp >= (int) BlackPawn)) {
3802 sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
3804 SendToProgram(message, cps);
3809 SendToProgram(".\n", cps);
3814 IsPromotion(fromX, fromY, toX, toY)
3815 int fromX, fromY, toX, toY;
3817 return gameMode != EditPosition &&
3818 fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
3819 ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
3820 (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
3825 PieceForSquare (x, y)
3829 if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
3832 return boards[currentMove][y][x];
3836 OKToStartUserMove(x, y)
3839 ChessSquare from_piece;
3842 if (matchMode) return FALSE;
3843 if (gameMode == EditPosition) return TRUE;
3845 if (x >= 0 && y >= 0)
3846 from_piece = boards[currentMove][y][x];
3848 from_piece = EmptySquare;
3850 if (from_piece == EmptySquare) return FALSE;
3852 white_piece = (int)from_piece >= (int)WhitePawn &&
3853 (int)from_piece <= (int)WhiteKing;
3856 case PlayFromGameFile:
3858 case TwoMachinesPlay:
3866 case MachinePlaysWhite:
3867 case IcsPlayingBlack:
3868 if (appData.zippyPlay) return FALSE;
3870 DisplayMoveError("You are playing Black");
3875 case MachinePlaysBlack:
3876 case IcsPlayingWhite:
3877 if (appData.zippyPlay) return FALSE;
3879 DisplayMoveError("You are playing White");
3885 if (!white_piece && WhiteOnMove(currentMove)) {
3886 DisplayMoveError("It is White's turn");
3889 if (white_piece && !WhiteOnMove(currentMove)) {
3890 DisplayMoveError("It is Black's turn");
3893 if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
3894 /* Editing correspondence game history */
3895 /* Could disallow this or prompt for confirmation */
3898 if (currentMove < forwardMostMove) {
3899 /* Discarding moves */
3900 /* Could prompt for confirmation here,
3901 but I don't think that's such a good idea */
3902 forwardMostMove = currentMove;
3906 case BeginningOfGame:
3907 if (appData.icsActive) return FALSE;
3908 if (!appData.noChessProgram) {
3910 DisplayMoveError("You are playing White");
3917 if (!white_piece && WhiteOnMove(currentMove)) {
3918 DisplayMoveError("It is White's turn");
3921 if (white_piece && !WhiteOnMove(currentMove)) {
3922 DisplayMoveError("It is Black's turn");
3931 if (currentMove != forwardMostMove && gameMode != AnalyzeMode
3932 && gameMode != AnalyzeFile && gameMode != Training) {
3933 DisplayMoveError("Displayed position is not current");
3939 FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
3940 int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
3941 int lastLoadGameUseList = FALSE;
3942 char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
3943 ChessMove lastLoadGameStart = (ChessMove) 0;
3947 UserMoveEvent(fromX, fromY, toX, toY, promoChar)
3948 int fromX, fromY, toX, toY;
3953 if (fromX < 0 || fromY < 0) return;
3954 if ((fromX == toX) && (fromY == toY)) {
3958 /* Check if the user is playing in turn. This is complicated because we
3959 let the user "pick up" a piece before it is his turn. So the piece he
3960 tried to pick up may have been captured by the time he puts it down!
3961 Therefore we use the color the user is supposed to be playing in this
3962 test, not the color of the piece that is currently on the starting
3963 square---except in EditGame mode, where the user is playing both
3964 sides; fortunately there the capture race can't happen. (It can
3965 now happen in IcsExamining mode, but that's just too bad. The user
3966 will get a somewhat confusing message in that case.)
3970 case PlayFromGameFile:
3972 case TwoMachinesPlay:
3976 /* We switched into a game mode where moves are not accepted,
3977 perhaps while the mouse button was down. */
3980 case MachinePlaysWhite:
3981 /* User is moving for Black */
3982 if (WhiteOnMove(currentMove)) {
3983 DisplayMoveError("It is White's turn");
3988 case MachinePlaysBlack:
3989 /* User is moving for White */
3990 if (!WhiteOnMove(currentMove)) {
3991 DisplayMoveError("It is Black's turn");
3998 case BeginningOfGame:
4001 if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
4002 (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
4003 /* User is moving for Black */
4004 if (WhiteOnMove(currentMove)) {
4005 DisplayMoveError("It is White's turn");
4009 /* User is moving for White */
4010 if (!WhiteOnMove(currentMove)) {
4011 DisplayMoveError("It is Black's turn");
4017 case IcsPlayingBlack:
4018 /* User is moving for Black */
4019 if (WhiteOnMove(currentMove)) {
4020 if (!appData.premove) {
4021 DisplayMoveError("It is White's turn");
4022 } else if (toX >= 0 && toY >= 0) {
4025 premoveFromX = fromX;
4026 premoveFromY = fromY;
4027 premovePromoChar = promoChar;
4029 if (appData.debugMode)
4030 fprintf(debugFP, "Got premove: fromX %d,"
4031 "fromY %d, toX %d, toY %d\n",
4032 fromX, fromY, toX, toY);
4038 case IcsPlayingWhite:
4039 /* User is moving for White */
4040 if (!WhiteOnMove(currentMove)) {
4041 if (!appData.premove) {
4042 DisplayMoveError("It is Black's turn");
4043 } else if (toX >= 0 && toY >= 0) {
4046 premoveFromX = fromX;
4047 premoveFromY = fromY;
4048 premovePromoChar = promoChar;
4050 if (appData.debugMode)
4051 fprintf(debugFP, "Got premove: fromX %d,"
4052 "fromY %d, toX %d, toY %d\n",
4053 fromX, fromY, toX, toY);
4063 if (toX == -2 || toY == -2) {
4064 boards[0][fromY][fromX] = EmptySquare;
4065 DrawPosition(FALSE, boards[currentMove]);
4066 } else if (toX >= 0 && toY >= 0) {
4067 boards[0][toY][toX] = boards[0][fromY][fromX];
4068 boards[0][fromY][fromX] = EmptySquare;
4069 DrawPosition(FALSE, boards[currentMove]);
4074 if (toX < 0 || toY < 0) return;
4075 userOfferedDraw = FALSE;
4077 if (appData.testLegality) {
4078 moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
4079 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
4080 if (moveType == IllegalMove || moveType == ImpossibleMove) {
4081 DisplayMoveError("Illegal move");
4085 moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
4088 if (gameMode == Training) {
4089 /* compare the move played on the board to the next move in the
4090 * game. If they match, display the move and the opponent's response.
4091 * If they don't match, display an error message.
4095 CopyBoard(testBoard, boards[currentMove]);
4096 ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
4098 if (CompareBoards(testBoard, boards[currentMove+1])) {
4099 ForwardInner(currentMove+1);
4101 /* Autoplay the opponent's response.
4102 * if appData.animate was TRUE when Training mode was entered,
4103 * the response will be animated.
4105 saveAnimate = appData.animate;
4106 appData.animate = animateTraining;
4107 ForwardInner(currentMove+1);
4108 appData.animate = saveAnimate;
4110 /* check for the end of the game */
4111 if (currentMove >= forwardMostMove) {
4112 gameMode = PlayFromGameFile;
4114 SetTrainingModeOff();
4115 DisplayInformation("End of game");
4118 DisplayError("Incorrect move", 0);
4123 FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
4126 /* Common tail of UserMoveEvent and DropMenuEvent */
4128 FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
4130 int fromX, fromY, toX, toY;
4131 /*char*/int promoChar;
4133 /* Ok, now we know that the move is good, so we can kill
4134 the previous line in Analysis Mode */
4135 if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
4136 forwardMostMove = currentMove;
4139 /* If we need the chess program but it's dead, restart it */
4140 ResurrectChessProgram();
4142 /* A user move restarts a paused game*/
4146 thinkOutput[0] = NULLCHAR;
4148 MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
4150 if (gameMode == BeginningOfGame) {
4151 if (appData.noChessProgram) {
4152 gameMode = EditGame;
4156 gameMode = MachinePlaysBlack;
4158 sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
4160 if (first.sendName) {
4161 sprintf(buf, "name %s\n", gameInfo.white);
4162 SendToProgram(buf, &first);
4168 /* Relay move to ICS or chess engine */
4169 if (appData.icsActive) {
4170 if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4171 gameMode == IcsExamining) {
4172 SendMoveToICS(moveType, fromX, fromY, toX, toY);
4176 if (first.sendTime && (gameMode == BeginningOfGame ||
4177 gameMode == MachinePlaysWhite ||
4178 gameMode == MachinePlaysBlack)) {
4179 SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
4182 SendMoveToProgram(forwardMostMove-1, &first);
4183 if (gameMode != EditGame && gameMode != PlayFromGameFile) {
4184 first.maybeThinking = TRUE;
4186 if (currentMove == cmailOldMove + 1) {
4187 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
4191 ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
4195 switch (MateTest(boards[currentMove], PosFlags(currentMove),
4201 if (WhiteOnMove(currentMove)) {
4202 GameEnds(BlackWins, "Black mates", GE_PLAYER);
4204 GameEnds(WhiteWins, "White mates", GE_PLAYER);
4208 GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
4213 case MachinePlaysBlack:
4214 case MachinePlaysWhite:
4215 /* disable certain menu options while machine is thinking */
4216 SetMachineThinkingEnables();
4225 HandleMachineMove(message, cps)
4227 ChessProgramState *cps;
4229 char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
4230 char realname[MSG_SIZ];
4231 int fromX, fromY, toX, toY;
4238 * Kludge to ignore BEL characters
4240 while (*message == '\007') message++;
4243 * Look for book output
4245 if (cps == &first && bookRequested) {
4246 if (message[0] == '\t' || message[0] == ' ') {
4247 /* Part of the book output is here; append it */
4248 strcat(bookOutput, message);
4249 strcat(bookOutput, " \n");
4251 } else if (bookOutput[0] != NULLCHAR) {
4252 /* All of book output has arrived; display it */
4253 char *p = bookOutput;
4254 while (*p != NULLCHAR) {
4255 if (*p == '\t') *p = ' ';
4258 DisplayInformation(bookOutput);
4259 bookRequested = FALSE;
4260 /* Fall through to parse the current output */
4265 * Look for machine move.
4267 if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
4268 strcmp(buf2, "...") == 0) ||
4269 (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
4270 strcmp(buf1, "move") == 0)) {
4271 /* Save last score befor move for zippy draw handling */
4272 if (appData.icsActive && appData.zippyDraw) {
4273 // ZippyDraw(0, programStats.score, programStats.depth);
4275 /* This method is only useful on engines that support ping */
4276 if (cps->lastPing != cps->lastPong) {
4277 if (gameMode == BeginningOfGame) {
4278 /* Extra move from before last new; ignore */
4279 if (appData.debugMode) {
4280 fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
4283 if (appData.debugMode) {
4284 fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
4285 cps->which, gameMode);
4287 SendToProgram("undo\n", cps);
4293 case BeginningOfGame:
4294 /* Extra move from before last reset; ignore */
4295 if (appData.debugMode) {
4296 fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
4303 /* Extra move after we tried to stop. The mode test is
4304 not a reliable way of detecting this problem, but it's
4305 the best we can do on engines that don't support ping.
4307 if (appData.debugMode) {
4308 fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
4309 cps->which, gameMode);
4311 SendToProgram("undo\n", cps);
4314 case MachinePlaysWhite:
4315 case IcsPlayingWhite:
4316 machineWhite = TRUE;
4319 case MachinePlaysBlack:
4320 case IcsPlayingBlack:
4321 machineWhite = FALSE;
4324 case TwoMachinesPlay:
4325 machineWhite = (cps->twoMachinesColor[0] == 'w');
4328 if (WhiteOnMove(forwardMostMove) != machineWhite) {
4329 if (appData.debugMode) {
4331 "Ignoring move out of turn by %s, gameMode %d"
4332 ", forwardMost %d\n",
4333 cps->which, gameMode, forwardMostMove);
4338 if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
4339 &fromX, &fromY, &toX, &toY, &promoChar)) {
4340 /* Machine move could not be parsed; ignore it. */
4341 sprintf(buf1, "Illegal move \"%s\" from %s machine",
4342 machineMove, cps->which);
4343 /*!!if (appData.debugMode)*/ DisplayError(buf1, 0);
4347 hintRequested = FALSE;
4348 lastHint[0] = NULLCHAR;
4349 bookRequested = FALSE;
4350 /* Program may be pondering now */
4351 cps->maybeThinking = TRUE;
4352 if (cps->sendTime == 2) cps->sendTime = 1;
4353 if (cps->offeredDraw) cps->offeredDraw--;
4356 if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
4358 SendMoveToICS(moveType, fromX, fromY, toX, toY);
4362 /* currentMoveString is set as a side-effect of ParseOneMove */
4363 strcpy(machineMove, currentMoveString);
4364 strcat(machineMove, "\n");
4365 strcpy(moveList[forwardMostMove], machineMove);
4367 MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
4369 if (gameMode == TwoMachinesPlay) {
4370 if (cps->other->sendTime) {
4371 SendTimeRemaining(cps->other,
4372 cps->other->twoMachinesColor[0] == 'w');
4374 SendMoveToProgram(forwardMostMove-1, cps->other);
4377 if (cps->other->useColors) {
4378 SendToProgram(cps->other->twoMachinesColor, cps->other);
4380 SendToProgram("go\n", cps->other);
4382 cps->other->maybeThinking = TRUE;
4385 ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
4386 if (!pausing && appData.ringBellAfterMoves) {
4391 * Reenable menu items that were disabled while
4392 * machine was thinking
4394 if (gameMode != TwoMachinesPlay)
4395 SetUserThinkingEnables();
4400 /* Set special modes for chess engines. Later something general
4401 * could be added here; for now there is just one kludge feature,
4402 * needed because Crafty 15.10 and earlier don't ignore SIGINT
4403 * when "xboard" is given as an interactive command.
4405 if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
4406 cps->useSigint = FALSE;
4407 cps->useSigterm = FALSE;
4411 * Look for communication commands
4413 if (!strncmp(message, "telluser ", 9)) {
4414 DisplayInformation(message + 9);
4417 if (!strncmp(message, "tellusererror ", 14)) {
4418 DisplayError(message + 14, 0);
4421 if (!strncmp(message, "tellopponent ", 13)) {
4422 if (appData.icsActive) {
4424 sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
4428 DisplayInformation(message + 13);
4432 if (!strncmp(message, "tellothers ", 11)) {
4433 if (appData.icsActive) {
4435 sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
4441 if (!strncmp(message, "tellall ", 8)) {
4442 if (appData.icsActive) {
4444 if (loggedOn && !appData.icsAnalyze) {
4445 sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
4449 DisplayInformation(message + 8);
4453 if (strncmp(message, "warning", 7) == 0) {
4454 /* Undocumented feature, use tellusererror in new code */
4455 DisplayError(message, 0);
4458 if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
4459 strcpy(realname, cps->tidy);
4460 strcat(realname, " query");
4461 AskQuestion(realname, buf2, buf1, cps->pr);
4464 /* Commands from the engine directly to ICS. We don't allow these to be
4465 * sent until we are logged on. Crafty kibitzes have been known to
4466 * interfere with the login process.
4469 if (!strncmp(message, "tellics ", 8)) {
4470 SendToICS(message + 8);
4474 if (!strncmp(message, "tellicsnoalias ", 15)) {
4475 SendToICS(ics_prefix);
4476 SendToICS(message + 15);
4480 /* The following are for backward compatibility only */
4481 if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
4482 !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
4483 SendToICS(ics_prefix);
4489 if (strncmp(message, "feature ", 8) == 0) {
4490 ParseFeatures(message+8, cps);
4492 if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
4496 * If the move is illegal, cancel it and redraw the board.
4497 * Also deal with other error cases. Matching is rather loose
4498 * here to accommodate engines written before the spec.
4500 if (strncmp(message + 1, "llegal move", 11) == 0 ||
4501 strncmp(message, "Error", 5) == 0) {
4502 if (StrStr(message, "name") ||
4503 StrStr(message, "rating") || StrStr(message, "?") ||
4504 StrStr(message, "result") || StrStr(message, "board") ||
4505 StrStr(message, "bk") || StrStr(message, "computer") ||
4506 StrStr(message, "variant") || StrStr(message, "hint") ||
4507 StrStr(message, "random") || StrStr(message, "depth") ||
4508 StrStr(message, "accepted")) {
4511 if (StrStr(message, "protover")) {
4512 /* Program is responding to input, so it's apparently done
4513 initializing, and this error message indicates it is
4514 protocol version 1. So we don't need to wait any longer
4515 for it to initialize and send feature commands. */
4516 FeatureDone(cps, 1);
4517 cps->protocolVersion = 1;
4520 cps->maybeThinking = FALSE;
4522 if (StrStr(message, "draw")) {
4523 /* Program doesn't have "draw" command */
4524 cps->sendDrawOffers = 0;
4527 if (cps->sendTime != 1 &&
4528 (StrStr(message, "time") || StrStr(message, "otim"))) {
4529 /* Program apparently doesn't have "time" or "otim" command */
4533 if (StrStr(message, "analyze")) {
4534 cps->analysisSupport = FALSE;
4535 cps->analyzing = FALSE;
4537 sprintf(buf2, "%s does not support analysis", cps->tidy);
4538 DisplayError(buf2, 0);
4541 if (StrStr(message, "st")) {
4542 cps->stKludge = TRUE;
4543 SendTimeControl(cps, movesPerSession, timeControl,
4544 timeIncrement, appData.searchDepth,
4548 if (StrStr(message, "sd")) {
4549 cps->sdKludge = TRUE;
4550 SendTimeControl(cps, movesPerSession, timeControl,
4551 timeIncrement, appData.searchDepth,
4555 if (!StrStr(message, "llegal")) return;
4556 if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
4557 gameMode == IcsIdle) return;
4558 if (forwardMostMove <= backwardMostMove) return;
4559 if (cps == &first && programStats.ok_to_send == 0) {
4560 /* Bogus message from Crafty responding to "." This filtering
4561 can miss some of the bad messages, but fortunately the bug
4562 is fixed in current Crafty versions, so it doesn't matter. */
4565 if (pausing) PauseEvent();
4566 if (gameMode == PlayFromGameFile) {
4567 /* Stop reading this game file */
4568 gameMode = EditGame;
4573 currentMove = --forwardMostMove;
4574 DisplayMove(currentMove-1); /* before DisplayMoveError */
4576 DisplayBothClocks();
4577 sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",
4578 parseList[currentMove], cps->which);
4579 DisplayMoveError(buf1);
4580 DrawPosition(FALSE, boards[currentMove]);
4583 if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
4584 /* Program has a broken "time" command that
4585 outputs a string not ending in newline.
4591 * If chess program startup fails, exit with an error message.
4592 * Attempts to recover here are futile.
4594 if ((StrStr(message, "unknown host") != NULL)
4595 || (StrStr(message, "No remote directory") != NULL)
4596 || (StrStr(message, "not found") != NULL)
4597 || (StrStr(message, "No such file") != NULL)
4598 || (StrStr(message, "can't alloc") != NULL)
4599 || (StrStr(message, "Permission denied") != NULL)) {
4601 cps->maybeThinking = FALSE;
4602 sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",
4603 cps->which, cps->program, cps->host, message);
4604 RemoveInputSource(cps->isr);
4605 DisplayFatalError(buf1, 0, 1);
4610 * Look for hint output
4612 if (sscanf(message, "Hint: %s", buf1) == 1) {
4614 case IcsPlayingWhite:
4615 case IcsPlayingBlack:
4616 case MachinePlaysWhite:
4617 case MachinePlaysBlack:
4618 strcpy(programStats.ponderMove, buf1);
4621 programStats.ponderMove[0] = NULLCHAR;
4624 if (cps == &first && hintRequested) {
4625 hintRequested = FALSE;
4626 if (ParseOneMove(buf1, forwardMostMove, &moveType,
4627 &fromX, &fromY, &toX, &toY, &promoChar)) {
4628 (void) CoordsToAlgebraic(boards[forwardMostMove],
4629 PosFlags(forwardMostMove), EP_UNKNOWN,
4630 fromY, fromX, toY, toX, promoChar, buf1);
4631 sprintf(buf2, "Hint: %s", buf1);
4632 DisplayInformation(buf2);
4634 /* Hint move could not be parsed!? */
4636 "Illegal hint move \"%s\"\nfrom %s chess program",
4638 DisplayError(buf2, 0);
4641 /* Copy ponder move */
4642 strcpy(lastHint, buf1);
4648 * Ignore other messages if game is not in progress
4650 if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
4651 gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
4654 * look for win, lose, draw, or draw offer
4656 if (strncmp(message, "1-0", 3) == 0) {
4657 char *p, *q, *r = "";
4658 p = strchr(message, '{');
4666 GameEnds(WhiteWins, r, GE_ENGINE);
4668 } else if (strncmp(message, "0-1", 3) == 0) {
4669 char *p, *q, *r = "";
4670 p = strchr(message, '{');
4678 /* Kludge for Arasan 4.1 bug */
4679 if (strcmp(r, "Black resigns") == 0) {
4680 GameEnds(WhiteWins, r, GE_ENGINE);
4683 GameEnds(BlackWins, r, GE_ENGINE);
4685 } else if (strncmp(message, "1/2", 3) == 0) {
4686 char *p, *q, *r = "";
4687 p = strchr(message, '{');
4695 GameEnds(GameIsDrawn, r, GE_ENGINE);
4698 } else if (strncmp(message, "White resign", 12) == 0) {
4699 GameEnds(BlackWins, "White resigns", GE_ENGINE);
4701 } else if (strncmp(message, "Black resign", 12) == 0) {
4702 GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4704 } else if (strncmp(message, "White", 5) == 0 &&
4705 message[5] != '(' &&
4706 StrStr(message, "Black") == NULL) {
4707 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4709 } else if (strncmp(message, "Black", 5) == 0 &&
4710 message[5] != '(') {
4711 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4713 } else if (strcmp(message, "resign") == 0 ||
4714 strcmp(message, "computer resigns") == 0) {
4716 case MachinePlaysBlack:
4717 case IcsPlayingBlack:
4718 GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4720 case MachinePlaysWhite:
4721 case IcsPlayingWhite:
4722 GameEnds(BlackWins, "White resigns", GE_ENGINE);
4724 case TwoMachinesPlay:
4725 if (cps->twoMachinesColor[0] == 'w')
4726 GameEnds(BlackWins, "White resigns", GE_ENGINE);
4728 GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4735 } else if (strncmp(message, "opponent mates", 14) == 0) {
4737 case MachinePlaysBlack:
4738 case IcsPlayingBlack:
4739 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4741 case MachinePlaysWhite:
4742 case IcsPlayingWhite:
4743 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4745 case TwoMachinesPlay:
4746 if (cps->twoMachinesColor[0] == 'w')
4747 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4749 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4756 } else if (strncmp(message, "computer mates", 14) == 0) {
4758 case MachinePlaysBlack:
4759 case IcsPlayingBlack:
4760 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4762 case MachinePlaysWhite:
4763 case IcsPlayingWhite:
4764 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4766 case TwoMachinesPlay:
4767 if (cps->twoMachinesColor[0] == 'w')
4768 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4770 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4777 } else if (strncmp(message, "checkmate", 9) == 0) {
4778 if (WhiteOnMove(forwardMostMove)) {
4779 GameEnds(BlackWins, "Black mates", GE_ENGINE);
4781 GameEnds(WhiteWins, "White mates", GE_ENGINE);
4784 } else if (strstr(message, "Draw") != NULL ||
4785 strstr(message, "game is a draw") != NULL) {
4786 GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
4788 } else if (strstr(message, "offer") != NULL &&
4789 strstr(message, "draw") != NULL) {
4791 if (appData.zippyPlay && first.initDone) {
4792 /* Relay offer to ICS */
4793 SendToICS(ics_prefix);
4794 SendToICS("draw\n");
4797 cps->offeredDraw = 2; /* valid until this engine moves twice */
4798 if (gameMode == TwoMachinesPlay) {
4799 if (cps->other->offeredDraw) {
4800 GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
4802 if (cps->other->sendDrawOffers) {
4803 SendToProgram("draw\n", cps->other);
4806 } else if (gameMode == MachinePlaysWhite ||
4807 gameMode == MachinePlaysBlack) {
4808 if (userOfferedDraw) {
4809 DisplayInformation("Machine accepts your draw offer");
4810 GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
4812 DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");
4819 * Look for thinking output
4821 /* Using icsAnalyze for future function */
4822 if (appData.showThinking || appData.icsAnalyze) {
4823 int plylev, mvleft, mvtot, curscore, time;
4824 char mvname[MOVE_LEN];
4825 unsigned long nodes;
4829 mvname[0] = NULLCHAR;
4831 /* reset thinkoutput */
4832 thinkOutput[0] = NULLCHAR;
4835 case MachinePlaysBlack:
4836 case IcsPlayingBlack:
4837 if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
4839 case MachinePlaysWhite:
4840 case IcsPlayingWhite:
4841 if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
4849 case TwoMachinesPlay:
4850 if ((cps->twoMachinesColor[0] == 'w') !=
4851 WhiteOnMove(forwardMostMove)) {
4861 /* reset ponder move display */
4863 if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
4864 &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
4866 if (plyext != ' ' && plyext != '\t') {
4869 programStats.depth = plylev;
4870 programStats.nodes = nodes;
4871 programStats.time = time;
4872 programStats.score = curscore;
4873 programStats.got_only_move = 0;
4875 /* Buffer overflow protection */
4876 if (buf1[0] != NULLCHAR) {
4877 if (strlen(buf1) >= MSG_SIZ) {
4878 if (appData.debugMode) fprintf(debugFP, "PV is to long. I use the first %d bytes. \n", MSG_SIZ);
4879 strncpy(programStats.movelist, buf1, MSG_SIZ);
4881 strcpy(programStats.movelist, buf1);
4884 sprintf(programStats.movelist, " no PV \n");
4885 if (appData.debugMode) fprintf(debugFP, "I found no PV \n");
4888 if (programStats.seen_stat) {
4889 programStats.ok_to_send = 1;
4892 if (strchr(programStats.movelist, '(') != NULL) {
4893 programStats.line_is_book = 1;
4894 programStats.nr_moves = 0;
4895 programStats.moves_left = 0;
4897 programStats.line_is_book = 0;
4900 sprintf(thinkOutput, "%d %c%+.2f %s%s%s",
4902 (gameMode == TwoMachinesPlay ?
4903 ToUpper(cps->twoMachinesColor[0]) : ' '),
4904 ((double) programStats.score) / 100.0,
4905 prefixHint ? lastHint : "",
4906 prefixHint ? " " : "", buf1);
4907 /* Using icsAnalyze for future function */
4908 if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
4909 appData.icsAnalyzeWindow || appData.AnalysisWindow ||
4910 gameMode == AnalyzeFile) {
4911 if (appData.icsAnalyzeWindow ||
4912 appData.AnalysisWindow) DisplayAnalysis(0,1);
4913 DisplayMove(currentMove - 1);
4915 if (appData.showThinking) DisplayMove(currentMove - 1);
4919 } else if ((p=StrStr(message, "(only move)")) != NULL) {
4920 /* crafty (9.25+) says "(only move) <move>"
4921 * if there is only 1 legal move
4923 sscanf(p, "(only move) %s", buf1);
4924 sprintf(thinkOutput, "%s (only move)", buf1);
4925 sprintf(programStats.movelist, "%s (only move)", buf1);
4926 programStats.depth = 2; /* don't use 0 or 1 it's book depth */
4927 programStats.nr_moves = 1;
4928 programStats.moves_left = 1;
4929 programStats.nodes = 1;
4930 programStats.time = 1;
4931 programStats.got_only_move = 1;
4933 /* Not really, but we also use this member to
4934 mean "line isn't going to change" (Crafty
4935 isn't searching, so stats won't change) */
4936 programStats.line_is_book = 1;
4937 if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
4938 appData.icsAnalyzeWindow || appData.AnalysisWindow ||
4939 gameMode == AnalyzeFile) {
4940 if (appData.icsAnalyzeWindow ||
4941 appData.AnalysisWindow) DisplayAnalysis(0,1);
4942 DisplayMove(currentMove - 1);
4944 if (appData.showThinking) DisplayMove(currentMove - 1);
4948 } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",
4949 &time, &nodes, &plylev, &mvleft,
4950 &mvtot, mvname) >= 5) {
4951 /* The stat01: line is from Crafty (9.29+) in response
4952 to the "." command */
4953 programStats.seen_stat = 1;
4954 /* for display engine room */
4956 cps->maybeThinking = TRUE;
4958 if (programStats.got_only_move || !appData.periodicUpdates) return;
4960 programStats.depth = plylev;
4961 programStats.time = time;
4962 programStats.nodes = nodes;
4963 programStats.moves_left = mvleft;
4964 programStats.nr_moves = mvtot;
4965 strcpy(programStats.move_name, mvname);
4966 programStats.ok_to_send = 1;
4967 if (appData.icsAnalyzeWindow ||
4968 appData.AnalysisWindow) DisplayAnalysis(0,0);
4971 } else if (strncmp(message,"++",2) == 0) {
4972 /* Crafty 9.29+ outputs this */
4973 programStats.got_fail = 2;
4976 } else if (strncmp(message,"--",2) == 0) {
4977 /* Crafty 9.29+ outputs this */
4978 programStats.got_fail = 1;
4981 } else if (thinkOutput[0] != NULLCHAR &&
4982 strncmp(message, " ", 4) == 0) {
4984 while (*p && *p == ' ') p++;
4985 strcat(thinkOutput, " ");
4986 strcat(thinkOutput, p);
4987 strcat(programStats.movelist, " ");
4988 strcat(programStats.movelist, p);
4990 if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
4991 appData.icsAnalyzeWindow || appData.AnalysisWindow ||
4992 gameMode == AnalyzeFile) {
4993 if (appData.icsAnalyzeWindow ||
4994 appData.AnalysisWindow) DisplayAnalysis(0,1);
4995 DisplayMove(currentMove - 1);
4997 if (appData.showThinking) DisplayMove(currentMove - 1);
5007 /* Parse a game score from the character string "game", and
5008 record it as the history of the current game. The game
5009 score is NOT assumed to start from the standard position.
5010 The display is not updated in any way.
5013 ParseGameHistory(game)
5017 int fromX, fromY, toX, toY, boardIndex;
5022 if (appData.debugMode)
5023 fprintf(debugFP, "Parsing game history: %s\n", game);
5025 if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
5026 gameInfo.site = StrSave(appData.icsHost);
5027 gameInfo.date = PGNDate();
5028 gameInfo.round = StrSave("-");
5030 /* Parse out names of players */
5031 while (*game == ' ') game++;
5033 while (*game != ' ') *p++ = *game++;
5035 gameInfo.white = StrSave(buf);
5036 while (*game == ' ') game++;
5038 while (*game != ' ' && *game != '\n') *p++ = *game++;
5040 gameInfo.black = StrSave(buf);
5043 boardIndex = blackPlaysFirst ? 1 : 0;
5046 yyboardindex = boardIndex;
5047 moveType = (ChessMove) yylex();
5049 case WhitePromotionQueen:
5050 case BlackPromotionQueen:
5051 case WhitePromotionRook:
5052 case BlackPromotionRook:
5053 case WhitePromotionBishop:
5054 case BlackPromotionBishop:
5055 case WhitePromotionKnight:
5056 case BlackPromotionKnight:
5057 case WhitePromotionKing:
5058 case BlackPromotionKing:
5060 case WhiteCapturesEnPassant:
5061 case BlackCapturesEnPassant:
5062 case WhiteKingSideCastle:
5063 case WhiteQueenSideCastle:
5064 case BlackKingSideCastle:
5065 case BlackQueenSideCastle:
5066 case WhiteKingSideCastleWild:
5067 case WhiteQueenSideCastleWild:
5068 case BlackKingSideCastleWild:
5069 case BlackQueenSideCastleWild:
5070 case IllegalMove: /* maybe suicide chess, etc. */
5071 fromX = currentMoveString[0] - 'a';
5072 fromY = currentMoveString[1] - '1';
5073 toX = currentMoveString[2] - 'a';
5074 toY = currentMoveString[3] - '1';
5075 promoChar = currentMoveString[4];
5079 fromX = moveType == WhiteDrop ?
5080 (int) CharToPiece(ToUpper(currentMoveString[0])) :
5081 (int) CharToPiece(ToLower(currentMoveString[0]));
5083 toX = currentMoveString[2] - 'a';
5084 toY = currentMoveString[3] - '1';
5085 promoChar = NULLCHAR;
5089 sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);
5090 DisplayError(buf, 0);
5092 case ImpossibleMove:
5094 sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);
5095 DisplayError(buf, 0);
5097 case (ChessMove) 0: /* end of file */
5098 if (boardIndex < backwardMostMove) {
5099 /* Oops, gap. How did that happen? */
5100 DisplayError("Gap in move list", 0);
5103 backwardMostMove = blackPlaysFirst ? 1 : 0;
5104 if (boardIndex > forwardMostMove) {
5105 forwardMostMove = boardIndex;
5109 if (boardIndex > 0) {
5110 strcat(parseList[boardIndex-1], " ");
5111 strcat(parseList[boardIndex-1], yy_text);
5123 case GameUnfinished:
5124 if (gameMode == IcsExamining) {
5125 if (boardIndex < backwardMostMove) {
5126 /* Oops, gap. How did that happen? */
5129 backwardMostMove = blackPlaysFirst ? 1 : 0;
5132 gameInfo.result = moveType;
5133 p = strchr(yy_text, '{');
5134 if (p == NULL) p = strchr(yy_text, '(');
5137 if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
5139 q = strchr(p, *p == '{' ? '}' : ')');
5140 if (q != NULL) *q = NULLCHAR;
5143 gameInfo.resultDetails = StrSave(p);
5146 if (boardIndex >= forwardMostMove &&
5147 !(gameMode == IcsObserving && ics_gamenum == -1)) {
5148 backwardMostMove = blackPlaysFirst ? 1 : 0;
5151 (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
5152 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
5153 parseList[boardIndex]);
5154 CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
5155 /* currentMoveString is set as a side-effect of yylex */
5156 strcpy(moveList[boardIndex], currentMoveString);
5157 strcat(moveList[boardIndex], "\n");
5159 ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
5160 switch (MateTest(boards[boardIndex],
5161 PosFlags(boardIndex), EP_UNKNOWN)) {
5167 strcat(parseList[boardIndex - 1], "+");
5170 strcat(parseList[boardIndex - 1], "#");
5177 /* Apply a move to the given board */
5179 ApplyMove(fromX, fromY, toX, toY, promoChar, board)
5180 int fromX, fromY, toX, toY;
5184 ChessSquare captured = board[toY][toX];
5185 if (fromY == DROP_RANK) {
5187 board[toY][toX] = (ChessSquare) fromX;
5188 } else if (fromX == toX && fromY == toY) {
5190 } else if (fromY == 0 && fromX == 4
5191 && board[fromY][fromX] == WhiteKing
5192 && toY == 0 && toX == 6) {
5193 board[fromY][fromX] = EmptySquare;
5194 board[toY][toX] = WhiteKing;
5195 board[fromY][7] = EmptySquare;
5196 board[toY][5] = WhiteRook;
5197 } else if (fromY == 0 && fromX == 4
5198 && board[fromY][fromX] == WhiteKing
5199 && toY == 0 && toX == 2) {
5200 board[fromY][fromX] = EmptySquare;
5201 board[toY][toX] = WhiteKing;
5202 board[fromY][0] = EmptySquare;
5203 board[toY][3] = WhiteRook;
5204 } else if (fromY == 0 && fromX == 3
5205 && board[fromY][fromX] == WhiteKing
5206 && toY == 0 && toX == 5) {
5207 board[fromY][fromX] = EmptySquare;
5208 board[toY][toX] = WhiteKing;
5209 board[fromY][7] = EmptySquare;
5210 board[toY][4] = WhiteRook;
5211 } else if (fromY == 0 && fromX == 3
5212 && board[fromY][fromX] == WhiteKing
5213 && toY == 0 && toX == 1) {
5214 board[fromY][fromX] = EmptySquare;
5215 board[toY][toX] = WhiteKing;
5216 board[fromY][0] = EmptySquare;
5217 board[toY][2] = WhiteRook;
5218 } else if (board[fromY][fromX] == WhitePawn
5220 /* white pawn promotion */
5221 board[7][toX] = CharToPiece(ToUpper(promoChar));
5222 if (board[7][toX] == EmptySquare) {
5223 board[7][toX] = WhiteQueen;
5225 board[fromY][fromX] = EmptySquare;
5226 } else if ((fromY == 4)
5228 && (board[fromY][fromX] == WhitePawn)
5229 && (board[toY][toX] == EmptySquare)) {
5230 board[fromY][fromX] = EmptySquare;
5231 board[toY][toX] = WhitePawn;
5232 captured = board[toY - 1][toX];
5233 board[toY - 1][toX] = EmptySquare;
5234 } else if (fromY == 7 && fromX == 4
5235 && board[fromY][fromX] == BlackKing
5236 && toY == 7 && toX == 6) {
5237 board[fromY][fromX] = EmptySquare;
5238 board[toY][toX] = BlackKing;
5239 board[fromY][7] = EmptySquare;
5240 board[toY][5] = BlackRook;
5241 } else if (fromY == 7 && fromX == 4
5242 && board[fromY][fromX] == BlackKing
5243 && toY == 7 && toX == 2) {
5244 board[fromY][fromX] = EmptySquare;
5245 board[toY][toX] = BlackKing;
5246 board[fromY][0] = EmptySquare;
5247 board[toY][3] = BlackRook;
5248 } else if (fromY == 7 && fromX == 3
5249 && board[fromY][fromX] == BlackKing
5250 && toY == 7 && toX == 5) {
5251 board[fromY][fromX] = EmptySquare;
5252 board[toY][toX] = BlackKing;
5253 board[fromY][7] = EmptySquare;
5254 board[toY][4] = BlackRook;
5255 } else if (fromY == 7 && fromX == 3
5256 && board[fromY][fromX] == BlackKing
5257 && toY == 7 && toX == 1) {
5258 board[fromY][fromX] = EmptySquare;
5259 board[toY][toX] = BlackKing;
5260 board[fromY][0] = EmptySquare;
5261 board[toY][2] = BlackRook;
5262 } else if (board[fromY][fromX] == BlackPawn
5264 /* black pawn promotion */
5265 board[0][toX] = CharToPiece(ToLower(promoChar));
5266 if (board[0][toX] == EmptySquare) {
5267 board[0][toX] = BlackQueen;
5269 board[fromY][fromX] = EmptySquare;
5270 } else if ((fromY == 3)
5272 && (board[fromY][fromX] == BlackPawn)
5273 && (board[toY][toX] == EmptySquare)) {
5274 board[fromY][fromX] = EmptySquare;
5275 board[toY][toX] = BlackPawn;
5276 captured = board[toY + 1][toX];
5277 board[toY + 1][toX] = EmptySquare;
5279 board[toY][toX] = board[fromY][fromX];
5280 board[fromY][fromX] = EmptySquare;
5282 if (gameInfo.variant == VariantCrazyhouse) {
5284 /* !!A lot more code needs to be written to support holdings */
5285 if (fromY == DROP_RANK) {
5286 /* Delete from holdings */
5287 if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
5289 if (captured != EmptySquare) {
5290 /* Add to holdings */
5291 if (captured < BlackPawn) {
5292 holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
5294 holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
5298 } else if (gameInfo.variant == VariantAtomic) {
5299 if (captured != EmptySquare) {
5301 for (y = toY-1; y <= toY+1; y++) {
5302 for (x = toX-1; x <= toX+1; x++) {
5303 if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
5304 board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
5305 board[y][x] = EmptySquare;
5309 board[toY][toX] = EmptySquare;
5314 /* Updates forwardMostMove */
5316 MakeMove(fromX, fromY, toX, toY, promoChar)
5317 int fromX, fromY, toX, toY;
5321 if (forwardMostMove >= MAX_MOVES) {
5322 DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
5327 timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
5328 timeRemaining[1][forwardMostMove] = blackTimeRemaining;
5329 if (commentList[forwardMostMove] != NULL) {
5330 free(commentList[forwardMostMove]);
5331 commentList[forwardMostMove] = NULL;
5333 CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
5334 ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);
5335 gameInfo.result = GameUnfinished;
5336 if (gameInfo.resultDetails != NULL) {
5337 free(gameInfo.resultDetails);
5338 gameInfo.resultDetails = NULL;
5340 CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
5341 moveList[forwardMostMove - 1]);
5342 (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
5343 PosFlags(forwardMostMove - 1), EP_UNKNOWN,
5344 fromY, fromX, toY, toX, promoChar,
5345 parseList[forwardMostMove - 1]);
5346 switch (MateTest(boards[forwardMostMove],
5347 PosFlags(forwardMostMove), EP_UNKNOWN)){
5353 strcat(parseList[forwardMostMove - 1], "+");
5356 strcat(parseList[forwardMostMove - 1], "#");
5361 /* Updates currentMove if not pausing */
5363 ShowMove(fromX, fromY, toX, toY)
5365 int instant = (gameMode == PlayFromGameFile) ?
5366 (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
5367 if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
5369 if (forwardMostMove == currentMove + 1) {
5370 AnimateMove(boards[forwardMostMove - 1],
5371 fromX, fromY, toX, toY);
5373 if (appData.highlightLastMove) {
5374 SetHighlights(fromX, fromY, toX, toY);
5377 currentMove = forwardMostMove;
5380 if (instant) return;
5381 DisplayMove(currentMove - 1);
5382 DrawPosition(FALSE, boards[currentMove]);
5383 DisplayBothClocks();
5384 HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
5389 InitChessProgram(cps)
5390 ChessProgramState *cps;
5393 if (appData.noChessProgram) return;
5394 hintRequested = FALSE;
5395 bookRequested = FALSE;
5396 SendToProgram(cps->initString, cps);
5397 if (gameInfo.variant != VariantNormal &&
5398 gameInfo.variant != VariantLoadable) {
5399 char *v = VariantName(gameInfo.variant);
5400 if (StrStr(cps->variants, v) == NULL) {
5401 sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);
5402 DisplayFatalError(buf, 0, 1);
5405 sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
5406 SendToProgram(buf, cps);
5409 sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
5410 SendToProgram(buf, cps);
5412 cps->maybeThinking = FALSE;
5413 cps->offeredDraw = 0;
5414 if (!appData.icsActive) {
5415 SendTimeControl(cps, movesPerSession, timeControl,
5416 timeIncrement, appData.searchDepth,
5419 if (appData.showThinking) {
5420 SendToProgram("post\n", cps);
5422 SendToProgram("hard\n", cps);
5423 if (!appData.ponderNextMove) {
5424 /* Warning: "easy" is a toggle in GNU Chess, so don't send
5425 it without being sure what state we are in first. "hard"
5426 is not a toggle, so that one is OK.
5428 SendToProgram("easy\n", cps);
5431 sprintf(buf, "ping %d\n", ++cps->lastPing);
5432 SendToProgram(buf, cps);
5434 cps->initDone = TRUE;
5439 StartChessProgram(cps)
5440 ChessProgramState *cps;
5445 if (appData.noChessProgram) return;
5446 cps->initDone = FALSE;
5448 if (strcmp(cps->host, "localhost") == 0) {
5449 err = StartChildProcess(cps->program, cps->dir, &cps->pr);
5450 } else if (*appData.remoteShell == NULLCHAR) {
5451 err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
5453 if (*appData.remoteUser == NULLCHAR) {
5454 sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
5457 sprintf(buf, "%s %s -l %s %s", appData.remoteShell,
5458 cps->host, appData.remoteUser, cps->program);
5460 err = StartChildProcess(buf, "", &cps->pr);
5464 sprintf(buf, "Startup failure on '%s'", cps->program);
5465 DisplayFatalError(buf, err, 1);
5471 cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
5472 if (cps->protocolVersion > 1) {
5473 sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
5474 SendToProgram(buf, cps);
5476 SendToProgram("xboard\n", cps);
5482 TwoMachinesEventIfReady P((void))
5484 if (first.lastPing != first.lastPong) {
5485 DisplayMessage("", "Waiting for first chess program");
5486 ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
5489 if (second.lastPing != second.lastPong) {
5490 DisplayMessage("", "Waiting for second chess program");
5491 ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
5499 NextMatchGame P((void))
5502 if (*appData.loadGameFile != NULLCHAR) {
5503 LoadGameFromFile(appData.loadGameFile,
5504 appData.loadGameIndex,
5505 appData.loadGameFile, FALSE);
5506 } else if (*appData.loadPositionFile != NULLCHAR) {
5507 LoadPositionFromFile(appData.loadPositionFile,
5508 appData.loadPositionIndex,
5509 appData.loadPositionFile);
5511 TwoMachinesEventIfReady();
5515 GameEnds(result, resultDetails, whosays)
5517 char *resultDetails;
5520 GameMode nextGameMode;
5523 if (appData.debugMode) {
5524 fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
5525 result, resultDetails ? resultDetails : "(null)", whosays);
5528 if (appData.icsAnalyze && gameMode == IcsObserving) ResetIcsQueue(ics_gamenum);
5530 if (appData.icsActive && whosays == GE_ENGINE) {
5531 /* If we are playing on ICS, the server decides when the
5532 game is over, but the engine can offer to draw, claim
5536 if (appData.zippyPlay && first.initDone) {
5537 if (result == GameIsDrawn) {
5538 /* In case draw still needs to be claimed */
5539 SendToICS(ics_prefix);
5540 SendToICS("draw\n");
5541 } else if (StrCaseStr(resultDetails, "resign")) {
5542 SendToICS(ics_prefix);
5543 SendToICS("resign\n");
5550 /* If we're loading the game from a file, stop */
5551 if (whosays == GE_FILE) {
5552 (void) StopLoadGameTimer();
5556 /* Cancel draw offers */
5557 first.offeredDraw = second.offeredDraw = 0;
5559 /* If this is an ICS game, only ICS can really say it's done;
5560 if not, anyone can. */
5561 isIcsGame = (gameMode == IcsPlayingWhite ||
5562 gameMode == IcsPlayingBlack ||
5563 gameMode == IcsObserving ||
5564 gameMode == IcsExamining);
5566 if (!isIcsGame || whosays == GE_ICS) {
5567 /* OK -- not an ICS game, or ICS said it was done */
5569 if (!isIcsGame && !appData.noChessProgram)
5570 SetUserThinkingEnables();
5572 if (resultDetails != NULL) {
5573 gameInfo.result = result;
5574 gameInfo.resultDetails = StrSave(resultDetails);
5576 /* Tell program how game ended in case it is learning */
5577 if (gameMode == MachinePlaysWhite ||
5578 gameMode == MachinePlaysBlack ||
5579 gameMode == TwoMachinesPlay ||
5580 gameMode == IcsPlayingWhite ||
5581 gameMode == IcsPlayingBlack ||
5582 gameMode == BeginningOfGame) {
5584 sprintf(buf, "result %s {%s}\n", PGNResult(result),
5586 if (first.pr != NoProc) {
5587 SendToProgram(buf, &first);
5589 if (second.pr != NoProc &&
5590 gameMode == TwoMachinesPlay) {
5591 SendToProgram(buf, &second);
5595 /* display last move only if game was not loaded from file */
5596 if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
5597 DisplayMove(currentMove - 1);
5599 if (forwardMostMove != 0) {
5600 if (gameMode != PlayFromGameFile && gameMode != EditGame) {
5601 if (*appData.saveGameFile != NULLCHAR) {
5602 SaveGameToFile(appData.saveGameFile, TRUE);
5603 } else if (appData.autoSaveGames) {
5606 if (*appData.savePositionFile != NULLCHAR) {
5607 SavePositionToFile(appData.savePositionFile);
5613 if (appData.icsActive) {
5614 if (appData.quietPlay &&
5615 (gameMode == IcsPlayingWhite ||
5616 gameMode == IcsPlayingBlack)) {
5617 SendToICS(ics_prefix);
5618 SendToICS("set shout 1\n");
5620 nextGameMode = IcsIdle;
5621 ics_user_moved = FALSE;
5622 /* clean up premove. It's ugly when the game has ended and the
5623 * premove highlights are still on the board.
5627 ClearPremoveHighlights();
5628 DrawPosition(FALSE, boards[currentMove]);
5630 if (whosays == GE_ICS) {
5633 if (gameMode == IcsPlayingWhite)
5635 else if(gameMode == IcsPlayingBlack)
5639 if (gameMode == IcsPlayingBlack)
5641 else if(gameMode == IcsPlayingWhite)
5648 PlayIcsUnfinishedSound();
5651 } else if (gameMode == EditGame ||
5652 gameMode == PlayFromGameFile ||
5653 gameMode == AnalyzeMode ||
5654 gameMode == AnalyzeFile) {
5655 nextGameMode = gameMode;
5657 nextGameMode = EndOfGame;
5662 nextGameMode = gameMode;
5665 if (appData.noChessProgram) {
5666 gameMode = nextGameMode;
5672 /* Put first chess program into idle state */
5673 if (first.pr != NoProc &&
5674 (gameMode == MachinePlaysWhite ||
5675 gameMode == MachinePlaysBlack ||
5676 gameMode == TwoMachinesPlay ||
5677 gameMode == IcsPlayingWhite ||
5678 gameMode == IcsPlayingBlack ||
5679 gameMode == BeginningOfGame)) {
5680 SendToProgram("force\n", &first);
5681 if (first.usePing) {
5683 sprintf(buf, "ping %d\n", ++first.lastPing);
5684 SendToProgram(buf, &first);
5687 } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
5688 /* Kill off first chess program */
5689 if (first.isr != NULL)
5690 RemoveInputSource(first.isr);
5693 if (first.pr != NoProc) {
5695 SendToProgram("quit\n", &first);
5696 DestroyChildProcess(first.pr, first.useSigterm);
5701 /* Put second chess program into idle state */
5702 if (second.pr != NoProc &&
5703 gameMode == TwoMachinesPlay) {
5704 SendToProgram("force\n", &second);
5705 if (second.usePing) {
5707 sprintf(buf, "ping %d\n", ++second.lastPing);
5708 SendToProgram(buf, &second);
5711 } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
5712 /* Kill off second chess program */
5713 if (second.isr != NULL)
5714 RemoveInputSource(second.isr);
5717 if (second.pr != NoProc) {
5718 SendToProgram("quit\n", &second);
5719 DestroyChildProcess(second.pr, second.useSigterm);
5724 if (matchMode && gameMode == TwoMachinesPlay) {
5727 if (first.twoMachinesColor[0] == 'w') {
5734 if (first.twoMachinesColor[0] == 'b') {
5743 if (matchGame < appData.matchGames) {
5745 tmp = first.twoMachinesColor;
5746 first.twoMachinesColor = second.twoMachinesColor;
5747 second.twoMachinesColor = tmp;
5748 gameMode = nextGameMode;
5750 ScheduleDelayedEvent(NextMatchGame, 10000);
5754 gameMode = nextGameMode;
5755 sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",
5756 first.tidy, second.tidy,
5757 first.matchWins, second.matchWins,
5758 appData.matchGames - (first.matchWins + second.matchWins));
5759 DisplayFatalError(buf, 0, 0);
5762 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
5763 !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
5765 gameMode = nextGameMode;
5769 /* Assumes program was just initialized (initString sent).
5770 Leaves program in force mode. */
5772 FeedMovesToProgram(cps, upto)
5773 ChessProgramState *cps;
5778 if (appData.debugMode)
5779 fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
5780 startedFromSetupPosition ? "position and " : "",
5781 backwardMostMove, upto, cps->which);
5783 if(!appData.icsAnalyze) SendToProgram("force\n", cps);
5784 if (startedFromSetupPosition) {
5785 SendBoard(cps, backwardMostMove);
5787 for (i = backwardMostMove; i < upto; i++) {
5789 SendMoveToProgram(i, cps);
5795 ResurrectChessProgram()
5797 /* The chess program may have exited.
5798 If so, restart it and feed it all the moves made so far. */
5800 if (appData.noChessProgram || first.pr != NoProc) return;
5802 StartChessProgram(&first);
5803 InitChessProgram(&first);
5804 FeedMovesToProgram(&first, currentMove);
5806 if (!first.sendTime) {
5807 /* can't tell gnuchess what its clock should read,
5808 so we bow to its notion. */
5810 timeRemaining[0][currentMove] = whiteTimeRemaining;
5811 timeRemaining[1][currentMove] = blackTimeRemaining;
5814 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
5815 first.analysisSupport) {
5816 SendToProgram("analyze\n", &first);
5817 first.analyzing = TRUE;
5830 if (appData.debugMode) {
5831 fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
5832 redraw, init, gameMode);
5835 /* reset send engine output to ics */
5836 appData.SendOutPutToICS = 1;
5838 pausing = pauseExamInvalid = FALSE;
5839 startedFromSetupPosition = blackPlaysFirst = FALSE;
5841 whiteFlag = blackFlag = FALSE;
5842 userOfferedDraw = FALSE;
5843 hintRequested = bookRequested = FALSE;
5844 first.maybeThinking = FALSE;
5845 second.maybeThinking = FALSE;
5846 thinkOutput[0] = NULLCHAR;
5847 lastHint[0] = NULLCHAR;
5848 ClearGameInfo(&gameInfo);
5849 gameInfo.variant = StringToVariant(appData.variant);
5850 ics_user_moved = ics_clock_paused = FALSE;
5851 ics_getting_history = H_FALSE;
5853 white_holding[0] = black_holding[0] = NULLCHAR;
5854 ClearProgramStats();
5858 flipView = appData.flipView;
5859 ClearPremoveHighlights();
5861 alarmSounded = FALSE;
5863 GameEnds((ChessMove) 0, NULL, GE_PLAYER);
5865 gameMode = BeginningOfGame;
5867 InitPosition(redraw);
5868 for (i = 0; i < MAX_MOVES; i++) {
5869 if (commentList[i] != NULL) {
5870 free(commentList[i]);
5871 commentList[i] = NULL;
5875 timeRemaining[0][0] = whiteTimeRemaining;
5876 timeRemaining[1][0] = blackTimeRemaining;
5877 if (first.pr == NULL) {
5878 StartChessProgram(&first);
5880 if (init) InitChessProgram(&first);
5882 DisplayMessage("", "");
5883 HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
5890 if (!AutoPlayOneMove())
5892 if (matchMode || appData.timeDelay == 0)
5894 if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
5896 StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
5905 int fromX, fromY, toX, toY;
5907 if (appData.debugMode) {
5908 fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
5911 if (gameMode != PlayFromGameFile)
5914 if (currentMove >= forwardMostMove) {
5915 gameMode = EditGame;
5920 toX = moveList[currentMove][2] - 'a';
5921 toY = moveList[currentMove][3] - '1';
5923 if (moveList[currentMove][1] == '@') {
5924 if (appData.highlightLastMove) {
5925 SetHighlights(-1, -1, toX, toY);
5928 fromX = moveList[currentMove][0] - 'a';
5929 fromY = moveList[currentMove][1] - '1';
5930 AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
5932 if (appData.highlightLastMove) {
5933 SetHighlights(fromX, fromY, toX, toY);
5936 DisplayMove(currentMove);
5937 SendMoveToProgram(currentMove++, &first);
5938 DisplayBothClocks();
5939 DrawPosition(FALSE, boards[currentMove]);
5940 if (commentList[currentMove] != NULL) {
5941 DisplayComment(currentMove - 1, commentList[currentMove]);
5948 LoadGameOneMove(readAhead)
5949 ChessMove readAhead;
5951 int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
5952 char promoChar = NULLCHAR;
5957 if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
5958 gameMode != AnalyzeMode && gameMode != Training) {
5963 yyboardindex = forwardMostMove;
5964 if (readAhead != (ChessMove)0) {
5965 moveType = readAhead;
5967 if (gameFileFP == NULL)
5969 moveType = (ChessMove) yylex();
5975 if (appData.debugMode)
5976 fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
5978 if (*p == '{' || *p == '[' || *p == '(') {
5979 p[strlen(p) - 1] = NULLCHAR;
5983 /* append the comment but don't display it */
5984 while (*p == '\n') p++;
5985 AppendComment(currentMove, p);
5988 case WhiteCapturesEnPassant:
5989 case BlackCapturesEnPassant:
5990 case WhitePromotionQueen:
5991 case BlackPromotionQueen:
5992 case WhitePromotionRook:
5993 case BlackPromotionRook:
5994 case WhitePromotionBishop:
5995 case BlackPromotionBishop:
5996 case WhitePromotionKnight:
5997 case BlackPromotionKnight:
5998 case WhitePromotionKing:
5999 case BlackPromotionKing:
6001 case WhiteKingSideCastle:
6002 case WhiteQueenSideCastle:
6003 case BlackKingSideCastle:
6004 case BlackQueenSideCastle:
6005 case WhiteKingSideCastleWild:
6006 case WhiteQueenSideCastleWild:
6007 case BlackKingSideCastleWild:
6008 case BlackQueenSideCastleWild:
6009 if (appData.debugMode)
6010 fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
6011 fromX = currentMoveString[0] - 'a';
6012 fromY = currentMoveString[1] - '1';
6013 toX = currentMoveString[2] - 'a';
6014 toY = currentMoveString[3] - '1';
6015 promoChar = currentMoveString[4];
6020 if (appData.debugMode)
6021 fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
6022 fromX = moveType == WhiteDrop ?
6023 (int) CharToPiece(ToUpper(currentMoveString[0])) :
6024 (int) CharToPiece(ToLower(currentMoveString[0]));
6026 toX = currentMoveString[2] - 'a';
6027 toY = currentMoveString[3] - '1';
6033 case GameUnfinished:
6034 if (appData.debugMode)
6035 fprintf(debugFP, "Parsed game end: %s\n", yy_text);
6036 p = strchr(yy_text, '{');
6037 if (p == NULL) p = strchr(yy_text, '(');
6040 if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
6042 q = strchr(p, *p == '{' ? '}' : ')');
6043 if (q != NULL) *q = NULLCHAR;
6046 GameEnds(moveType, p, GE_FILE);
6048 if (cmailMsgLoaded) {
6050 flipView = WhiteOnMove(currentMove);
6051 if (moveType == GameUnfinished) flipView = !flipView;
6052 if (appData.debugMode)
6053 fprintf(debugFP, "Setting flipView to %d\n", flipView) ;
6057 case (ChessMove) 0: /* end of file */
6058 if (appData.debugMode)
6059 fprintf(debugFP, "Parser hit end of file\n");
6060 switch (MateTest(boards[currentMove], PosFlags(currentMove),
6066 if (WhiteOnMove(currentMove)) {
6067 GameEnds(BlackWins, "Black mates", GE_FILE);
6069 GameEnds(WhiteWins, "White mates", GE_FILE);
6073 GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
6080 if (lastLoadGameStart == GNUChessGame) {
6081 /* GNUChessGames have numbers, but they aren't move numbers */
6082 if (appData.debugMode)
6083 fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
6084 yy_text, (int) moveType);
6085 return LoadGameOneMove((ChessMove)0); /* tail recursion */
6087 /* else fall thru */
6092 /* Reached start of next game in file */
6093 if (appData.debugMode)
6094 fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
6095 switch (MateTest(boards[currentMove], PosFlags(currentMove),
6101 if (WhiteOnMove(currentMove)) {
6102 GameEnds(BlackWins, "Black mates", GE_FILE);
6104 GameEnds(WhiteWins, "White mates", GE_FILE);
6108 GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
6114 case PositionDiagram: /* should not happen; ignore */
6115 case ElapsedTime: /* ignore */
6116 case NAG: /* ignore */
6117 if (appData.debugMode)
6118 fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
6119 yy_text, (int) moveType);
6120 return LoadGameOneMove((ChessMove)0); /* tail recursion */
6123 if (appData.testLegality) {
6124 if (appData.debugMode)
6125 fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
6126 sprintf(move, "Illegal move: %d.%s%s",
6127 (forwardMostMove / 2) + 1,
6128 WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6129 DisplayError(move, 0);
6132 if (appData.debugMode)
6133 fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
6134 yy_text, currentMoveString);
6135 fromX = currentMoveString[0] - 'a';
6136 fromY = currentMoveString[1] - '1';
6137 toX = currentMoveString[2] - 'a';
6138 toY = currentMoveString[3] - '1';
6139 promoChar = currentMoveString[4];
6144 if (appData.debugMode)
6145 fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
6146 sprintf(move, "Ambiguous move: %d.%s%s",
6147 (forwardMostMove / 2) + 1,
6148 WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6149 DisplayError(move, 0);
6154 case ImpossibleMove:
6155 if (appData.debugMode)
6156 fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);
6157 sprintf(move, "Illegal move: %d.%s%s",
6158 (forwardMostMove / 2) + 1,
6159 WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6160 DisplayError(move, 0);
6166 if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
6167 DrawPosition(FALSE, boards[currentMove]);
6168 DisplayBothClocks();
6169 if (!appData.matchMode && commentList[currentMove] != NULL)
6170 DisplayComment(currentMove - 1, commentList[currentMove]);
6172 (void) StopLoadGameTimer();
6174 cmailOldMove = forwardMostMove;
6177 /* currentMoveString is set as a side-effect of yylex */
6178 strcat(currentMoveString, "\n");
6179 strcpy(moveList[forwardMostMove], currentMoveString);
6181 thinkOutput[0] = NULLCHAR;
6182 MakeMove(fromX, fromY, toX, toY, promoChar);
6183 currentMove = forwardMostMove;
6188 /* Load the nth game from the given file */
6190 LoadGameFromFile(filename, n, title, useList)
6194 /*Boolean*/ int useList;
6199 if (strcmp(filename, "-") == 0) {
6203 f = fopen(filename, "rb");
6205 sprintf(buf, "Can't open \"%s\"", filename);
6206 DisplayError(buf, errno);
6210 if (fseek(f, 0, 0) == -1) {
6211 /* f is not seekable; probably a pipe */
6214 if (useList && n == 0) {
6215 int error = GameListBuild(f);
6217 DisplayError("Cannot build game list", error);
6218 } else if (!ListEmpty(&gameList) &&
6219 ((ListGame *) gameList.tailPred)->number > 1) {
6220 GameListPopUp(f, title);
6227 return LoadGame(f, n, title, FALSE);
6232 MakeRegisteredMove()
6234 int fromX, fromY, toX, toY;
6236 if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
6237 switch (cmailMoveType[lastLoadGameNumber - 1]) {
6240 if (appData.debugMode)
6241 fprintf(debugFP, "Restoring %s for game %d\n",
6242 cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
6244 thinkOutput[0] = NULLCHAR;
6245 strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
6246 fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';
6247 fromY = cmailMove[lastLoadGameNumber - 1][1] - '1';
6248 toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';
6249 toY = cmailMove[lastLoadGameNumber - 1][3] - '1';
6250 promoChar = cmailMove[lastLoadGameNumber - 1][4];
6251 MakeMove(fromX, fromY, toX, toY, promoChar);
6252 ShowMove(fromX, fromY, toX, toY);
6254 switch (MateTest(boards[currentMove], PosFlags(currentMove),
6261 if (WhiteOnMove(currentMove)) {
6262 GameEnds(BlackWins, "Black mates", GE_PLAYER);
6264 GameEnds(WhiteWins, "White mates", GE_PLAYER);
6269 GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
6276 if (WhiteOnMove(currentMove)) {
6277 GameEnds(BlackWins, "White resigns", GE_PLAYER);
6279 GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
6284 GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
6295 /* Wrapper around LoadGame for use when a Cmail message is loaded */
6297 CmailLoadGame(f, gameNumber, title, useList)
6305 if (gameNumber > nCmailGames) {
6306 DisplayError("No more games in this message", 0);
6309 if (f == lastLoadGameFP) {
6310 int offset = gameNumber - lastLoadGameNumber;
6312 cmailMsg[0] = NULLCHAR;
6313 if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
6314 cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
6315 nCmailMovesRegistered--;
6317 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
6318 if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {
6319 cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;
6322 if (! RegisterMove()) return FALSE;
6326 retVal = LoadGame(f, gameNumber, title, useList);
6328 /* Make move registered during previous look at this game, if any */
6329 MakeRegisteredMove();
6331 if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {
6332 commentList[currentMove]
6333 = StrSave(cmailCommentList[lastLoadGameNumber - 1]);
6334 DisplayComment(currentMove - 1, commentList[currentMove]);
6340 /* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
6345 int gameNumber = lastLoadGameNumber + offset;
6346 if (lastLoadGameFP == NULL) {
6347 DisplayError("No game has been loaded yet", 0);
6350 if (gameNumber <= 0) {
6351 DisplayError("Can't back up any further", 0);
6354 if (cmailMsgLoaded) {
6355 return CmailLoadGame(lastLoadGameFP, gameNumber,
6356 lastLoadGameTitle, lastLoadGameUseList);
6358 return LoadGame(lastLoadGameFP, gameNumber,
6359 lastLoadGameTitle, lastLoadGameUseList);
6365 /* Load the nth game from open file f */
6367 LoadGame(f, gameNumber, title, useList)
6375 int gn = gameNumber;
6376 ListGame *lg = NULL;
6379 GameMode oldGameMode;
6381 if (appData.debugMode)
6382 fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
6384 if (gameMode == Training )
6385 SetTrainingModeOff();
6387 oldGameMode = gameMode;
6388 if (gameMode != BeginningOfGame) {
6393 if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
6394 fclose(lastLoadGameFP);
6398 lg = (ListGame *) ListElem(&gameList, gameNumber-1);
6401 fseek(f, lg->offset, 0);
6402 GameListHighlight(gameNumber);
6406 DisplayError("Game number out of range", 0);
6411 if (fseek(f, 0, 0) == -1) {
6412 if (f == lastLoadGameFP ?
6413 gameNumber == lastLoadGameNumber + 1 :
6417 DisplayError("Can't seek on game file", 0);
6423 lastLoadGameNumber = gameNumber;
6424 strcpy(lastLoadGameTitle, title);
6425 lastLoadGameUseList = useList;
6430 if (lg && lg->gameInfo.white && lg->gameInfo.black) {
6431 sprintf(buf, "%s vs. %s", lg->gameInfo.white,
6432 lg->gameInfo.black);
6434 } else if (*title != NULLCHAR) {
6435 if (gameNumber > 1) {
6436 sprintf(buf, "%s %d", title, gameNumber);
6439 DisplayTitle(title);
6443 if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {
6444 gameMode = PlayFromGameFile;
6448 currentMove = forwardMostMove = backwardMostMove = 0;
6449 CopyBoard(boards[0], initialPosition);
6453 * Skip the first gn-1 games in the file.
6454 * Also skip over anything that precedes an identifiable
6455 * start of game marker, to avoid being confused by
6456 * garbage at the start of the file. Currently
6457 * recognized start of game markers are the move number "1",
6458 * the pattern "gnuchess .* game", the pattern
6459 * "^[#;%] [^ ]* game file", and a PGN tag block.
6460 * A game that starts with one of the latter two patterns
6461 * will also have a move number 1, possibly
6462 * following a position diagram.
6464 cm = lastLoadGameStart = (ChessMove) 0;
6467 yyboardindex = forwardMostMove;
6468 cm = (ChessMove) yylex();
6469 yyskipmoves = FALSE;
6472 if (cmailMsgLoaded) {
6473 nCmailGames = CMAIL_MAX_GAMES - gn;
6476 DisplayError("Game not found in file", 0);
6478 yyskipmoves = FALSE;
6484 lastLoadGameStart = cm;
6488 switch (lastLoadGameStart) {
6495 gn--; /* count this game */
6496 lastLoadGameStart = cm;
6505 switch (lastLoadGameStart) {
6510 gn--; /* count this game */
6511 lastLoadGameStart = cm;
6514 lastLoadGameStart = cm; /* game counted already */
6522 yyboardindex = forwardMostMove;
6523 cm = (ChessMove) yylex();
6524 } while (cm == PGNTag || cm == Comment);
6531 if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {
6532 if ( cmailResult[CMAIL_MAX_GAMES - gn - 1]
6533 != CMAIL_OLD_RESULT) {
6535 cmailResult[ CMAIL_MAX_GAMES
6536 - gn - 1] = CMAIL_OLD_RESULT;
6545 yyskipmoves = FALSE;
6547 if (appData.debugMode)
6548 fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
6550 if (cm == XBoardGame) {
6551 /* Skip any header junk before position diagram and/or move 1 */
6553 yyboardindex = forwardMostMove;
6554 cm = (ChessMove) yylex();
6556 if (cm == (ChessMove) 0 ||
6557 cm == GNUChessGame || cm == XBoardGame) {
6558 /* Empty game; pretend end-of-file and handle later */
6563 if (cm == MoveNumberOne || cm == PositionDiagram ||
6564 cm == PGNTag || cm == Comment)
6567 } else if (cm == GNUChessGame) {
6568 if (gameInfo.event != NULL) {
6569 free(gameInfo.event);
6571 gameInfo.event = StrSave(yy_text);
6574 startedFromSetupPosition = FALSE;
6575 while (cm == PGNTag) {
6576 if (appData.debugMode)
6577 fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
6578 err = ParsePGNTag(yy_text, &gameInfo);
6579 if (!err) numPGNTags++;
6581 if (gameInfo.fen != NULL) {
6582 Board initial_position;
6583 startedFromSetupPosition = TRUE;
6584 if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
6586 DisplayError("Bad FEN position in file", 0);
6589 CopyBoard(boards[0], initial_position);
6590 if (blackPlaysFirst) {
6591 currentMove = forwardMostMove = backwardMostMove = 1;
6592 CopyBoard(boards[1], initial_position);
6593 strcpy(moveList[0], "");
6594 strcpy(parseList[0], "");
6595 timeRemaining[0][1] = whiteTimeRemaining;
6596 timeRemaining[1][1] = blackTimeRemaining;
6597 if (commentList[0] != NULL) {
6598 commentList[1] = commentList[0];
6599 commentList[0] = NULL;
6602 currentMove = forwardMostMove = backwardMostMove = 0;
6604 yyboardindex = forwardMostMove;
6606 gameInfo.fen = NULL;
6609 yyboardindex = forwardMostMove;
6610 cm = (ChessMove) yylex();
6612 /* Handle comments interspersed among the tags */
6613 while (cm == Comment) {
6615 if (appData.debugMode)
6616 fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
6618 if (*p == '{' || *p == '[' || *p == '(') {
6619 p[strlen(p) - 1] = NULLCHAR;
6622 while (*p == '\n') p++;
6623 AppendComment(currentMove, p);
6624 yyboardindex = forwardMostMove;
6625 cm = (ChessMove) yylex();
6629 /* don't rely on existence of Event tag since if game was
6630 * pasted from clipboard the Event tag may not exist
6632 if (numPGNTags > 0){
6634 if (gameInfo.variant == VariantNormal) {
6635 gameInfo.variant = StringToVariant(gameInfo.event);
6638 tags = PGNTags(&gameInfo);
6639 TagsPopUp(tags, CmailMsg());
6643 /* Make something up, but don't display it now */
6648 if (cm == PositionDiagram) {
6651 Board initial_position;
6653 if (appData.debugMode)
6654 fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);
6656 if (!startedFromSetupPosition) {
6658 for (i = BOARD_SIZE - 1; i >= 0; i--)
6659 for (j = 0; j < BOARD_SIZE; p++)
6669 initial_position[i][j++] = CharToPiece(*p);
6672 while (*p == ' ' || *p == '\t' ||
6673 *p == '\n' || *p == '\r') p++;
6675 if (strncmp(p, "black", strlen("black"))==0)
6676 blackPlaysFirst = TRUE;
6678 blackPlaysFirst = FALSE;
6679 startedFromSetupPosition = TRUE;
6681 CopyBoard(boards[0], initial_position);
6682 if (blackPlaysFirst) {
6683 currentMove = forwardMostMove = backwardMostMove = 1;
6684 CopyBoard(boards[1], initial_position);
6685 strcpy(moveList[0], "");
6686 strcpy(parseList[0], "");
6687 timeRemaining[0][1] = whiteTimeRemaining;
6688 timeRemaining[1][1] = blackTimeRemaining;
6689 if (commentList[0] != NULL) {
6690 commentList[1] = commentList[0];
6691 commentList[0] = NULL;
6694 currentMove = forwardMostMove = backwardMostMove = 0;
6697 yyboardindex = forwardMostMove;
6698 cm = (ChessMove) yylex();
6701 if (first.pr == NoProc) {
6702 StartChessProgram(&first);
6704 InitChessProgram(&first);
6705 SendToProgram("force\n", &first);
6706 if (startedFromSetupPosition) {
6707 SendBoard(&first, forwardMostMove);
6708 DisplayBothClocks();
6711 while (cm == Comment) {
6713 if (appData.debugMode)
6714 fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
6716 if (*p == '{' || *p == '[' || *p == '(') {
6717 p[strlen(p) - 1] = NULLCHAR;
6720 while (*p == '\n') p++;
6721 AppendComment(currentMove, p);
6722 yyboardindex = forwardMostMove;
6723 cm = (ChessMove) yylex();
6726 if (cm == (ChessMove) 0 || cm == WhiteWins || cm == BlackWins ||
6727 cm == GameIsDrawn || cm == GameUnfinished) {
6728 DisplayMessage("", "No moves in game");
6729 if (cmailMsgLoaded) {
6730 if (appData.debugMode)
6731 fprintf(debugFP, "Setting flipView to %d.\n", FALSE);
6735 DrawPosition(FALSE, boards[currentMove]);
6736 DisplayBothClocks();
6737 gameMode = EditGame;
6744 if (commentList[currentMove] != NULL) {
6745 if (!matchMode && (pausing || appData.timeDelay != 0)) {
6746 DisplayComment(currentMove - 1, commentList[currentMove]);
6749 if (!matchMode && appData.timeDelay != 0)
6750 DrawPosition(FALSE, boards[currentMove]);
6752 if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
6753 programStats.ok_to_send = 1;
6756 /* if the first token after the PGN tags is a move
6757 * and not move number 1, retrieve it from the parser
6759 if (cm != MoveNumberOne)
6760 LoadGameOneMove(cm);
6762 /* load the remaining moves from the file */
6763 while (LoadGameOneMove((ChessMove)0)) {
6764 timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
6765 timeRemaining[1][forwardMostMove] = blackTimeRemaining;
6768 /* rewind to the start of the game */
6769 currentMove = backwardMostMove;
6771 HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
6773 if (oldGameMode == AnalyzeFile ||
6774 oldGameMode == AnalyzeMode) {
6778 if (matchMode || appData.timeDelay == 0) {
6780 gameMode = EditGame;
6782 } else if (appData.timeDelay > 0) {
6786 if (appData.debugMode)
6787 fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
6791 /* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
6793 ReloadPosition(offset)
6796 int positionNumber = lastLoadPositionNumber + offset;
6797 if (lastLoadPositionFP == NULL) {
6798 DisplayError("No position has been loaded yet", 0);
6801 if (positionNumber <= 0) {
6802 DisplayError("Can't back up any further", 0);
6805 return LoadPosition(lastLoadPositionFP, positionNumber,
6806 lastLoadPositionTitle);
6809 /* Load the nth position from the given file */
6811 LoadPositionFromFile(filename, n, title)
6819 if (strcmp(filename, "-") == 0) {
6820 return LoadPosition(stdin, n, "stdin");
6822 f = fopen(filename, "rb");
6824 sprintf(buf, "Can't open \"%s\"", filename);
6825 DisplayError(buf, errno);
6828 return LoadPosition(f, n, title);
6833 /* Load the nth position from the given open file, and close it */
6835 LoadPosition(f, positionNumber, title)
6840 char *p, line[MSG_SIZ];
6841 Board initial_position;
6842 int i, j, fenMode, pn;
6844 if (gameMode == Training )
6845 SetTrainingModeOff();
6847 if (gameMode != BeginningOfGame) {
6850 if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {
6851 fclose(lastLoadPositionFP);
6853 if (positionNumber == 0) positionNumber = 1;
6854 lastLoadPositionFP = f;
6855 lastLoadPositionNumber = positionNumber;
6856 strcpy(lastLoadPositionTitle, title);
6857 if (first.pr == NoProc) {
6858 StartChessProgram(&first);
6859 InitChessProgram(&first);
6861 pn = positionNumber;
6862 if (positionNumber < 0) {
6863 /* Negative position number means to seek to that byte offset */
6864 if (fseek(f, -positionNumber, 0) == -1) {
6865 DisplayError("Can't seek on position file", 0);
6870 if (fseek(f, 0, 0) == -1) {
6871 if (f == lastLoadPositionFP ?
6872 positionNumber == lastLoadPositionNumber + 1 :
6873 positionNumber == 1) {
6876 DisplayError("Can't seek on position file", 0);
6881 /* See if this file is FEN or old-style xboard */
6882 if (fgets(line, MSG_SIZ, f) == NULL) {
6883 DisplayError("Position not found in file", 0);
6891 case 'p': case 'n': case 'b': case 'r': case 'q': case 'k':
6892 case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':
6893 case '1': case '2': case '3': case '4': case '5': case '6':
6900 if (fenMode || line[0] == '#') pn--;
6902 /* skip postions before number pn */
6903 if (fgets(line, MSG_SIZ, f) == NULL) {
6904 DisplayError("Position not found in file", 0);
6907 if (fenMode || line[0] == '#') pn--;
6912 if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
6913 DisplayError("Bad FEN position in file", 0);
6917 (void) fgets(line, MSG_SIZ, f);
6918 (void) fgets(line, MSG_SIZ, f);
6920 for (i = BOARD_SIZE - 1; i >= 0; i--) {
6921 (void) fgets(line, MSG_SIZ, f);
6922 for (p = line, j = 0; j < BOARD_SIZE; p++) {
6925 initial_position[i][j++] = CharToPiece(*p);
6929 blackPlaysFirst = FALSE;
6931 (void) fgets(line, MSG_SIZ, f);
6932 if (strncmp(line, "black", strlen("black"))==0)
6933 blackPlaysFirst = TRUE;
6936 startedFromSetupPosition = TRUE;
6938 SendToProgram("force\n", &first);
6939 CopyBoard(boards[0], initial_position);
6940 if (blackPlaysFirst) {
6941 currentMove = forwardMostMove = backwardMostMove = 1;
6942 strcpy(moveList[0], "");
6943 strcpy(parseList[0], "");
6944 CopyBoard(boards[1], initial_position);
6945 DisplayMessage("", "Black to play");
6947 currentMove = forwardMostMove = backwardMostMove = 0;
6948 DisplayMessage("", "White to play");
6950 SendBoard(&first, forwardMostMove);
6952 if (positionNumber > 1) {
6953 sprintf(line, "%s %d", title, positionNumber);
6956 DisplayTitle(title);
6958 gameMode = EditGame;
6961 timeRemaining[0][1] = whiteTimeRemaining;
6962 timeRemaining[1][1] = blackTimeRemaining;
6963 DrawPosition(FALSE, boards[currentMove]);
6970 CopyPlayerNameIntoFileName(dest, src)
6973 while (*src != NULLCHAR && *src != ',') {
6978 *(*dest)++ = *src++;
6983 char *DefaultFileName(ext)
6986 static char def[MSG_SIZ];
6989 if (gameInfo.white != NULL && gameInfo.white[0] != '-') {
6991 CopyPlayerNameIntoFileName(&p, gameInfo.white);
6993 CopyPlayerNameIntoFileName(&p, gameInfo.black);
7002 /* Save the current game to the given file */
7004 SaveGameToFile(filename, append)
7011 if (strcmp(filename, "-") == 0) {
7012 return SaveGame(stdout, 0, NULL);
7014 f = fopen(filename, append ? "a" : "w");
7016 sprintf(buf, "Can't open \"%s\"", filename);
7017 DisplayError(buf, errno);
7020 return SaveGame(f, 0, NULL);
7029 static char buf[MSG_SIZ];
7032 p = strchr(str, ' ');
7033 if (p == NULL) return str;
7034 strncpy(buf, str, p - str);
7035 buf[p - str] = NULLCHAR;
7039 #define PGN_MAX_LINE 75
7041 /* Save game in PGN style and close the file */
7046 int i, offset, linelen, newblock;
7050 int movelen, numlen, blank;
7052 tm = time((time_t *) NULL);
7054 PrintPGNTags(f, &gameInfo);
7056 if (backwardMostMove > 0 || startedFromSetupPosition) {
7057 char *fen = PositionToFEN(backwardMostMove);
7058 fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
7059 fprintf(f, "\n{--------------\n");
7060 PrintPosition(f, backwardMostMove);
7061 fprintf(f, "--------------}\n");
7067 i = backwardMostMove;
7068 offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
7072 while (i < forwardMostMove) {
7073 /* Print comments preceding this move */
7074 if (commentList[i] != NULL) {
7075 if (linelen > 0) fprintf(f, "\n");
7076 fprintf(f, "{\n%s}\n", commentList[i]);
7081 /* Format move number */
7083 sprintf(numtext, "%d.", (i - offset)/2 + 1);
7086 sprintf(numtext, "%d...", (i - offset)/2 + 1);
7088 numtext[0] = NULLCHAR;
7091 numlen = strlen(numtext);
7094 /* Print move number */
7095 blank = linelen > 0 && numlen > 0;
7096 if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {
7105 fprintf(f, numtext);
7109 movetext = SavePart(parseList[i]);
7110 movelen = strlen(movetext);
7113 blank = linelen > 0 && movelen > 0;
7114 if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
7123 fprintf(f, movetext);
7129 /* Start a new line */
7130 if (linelen > 0) fprintf(f, "\n");
7132 /* Print comments after last move */
7133 if (commentList[i] != NULL) {
7134 fprintf(f, "{\n%s}\n", commentList[i]);
7138 if (gameInfo.resultDetails != NULL &&
7139 gameInfo.resultDetails[0] != NULLCHAR) {
7140 fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,
7141 PGNResult(gameInfo.result));
7143 fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
7150 /* Save game in old style and close the file */
7158 tm = time((time_t *) NULL);
7160 fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
7163 if (backwardMostMove > 0 || startedFromSetupPosition) {
7164 fprintf(f, "\n[--------------\n");
7165 PrintPosition(f, backwardMostMove);
7166 fprintf(f, "--------------]\n");
7171 i = backwardMostMove;
7172 offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
7174 while (i < forwardMostMove) {
7175 if (commentList[i] != NULL) {
7176 fprintf(f, "[%s]\n", commentList[i]);
7180 fprintf(f, "%d. ... %s\n", (i - offset)/2 + 1, parseList[i]);
7183 fprintf(f, "%d. %s ", (i - offset)/2 + 1, parseList[i]);
7185 if (commentList[i] != NULL) {
7189 if (i >= forwardMostMove) {
7193 fprintf(f, "%s\n", parseList[i]);
7198 if (commentList[i] != NULL) {
7199 fprintf(f, "[%s]\n", commentList[i]);
7202 /* This isn't really the old style, but it's close enough */
7203 if (gameInfo.resultDetails != NULL &&
7204 gameInfo.resultDetails[0] != NULLCHAR) {
7205 fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),
7206 gameInfo.resultDetails);
7208 fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
7215 /* Save the current game to open file f and close the file */
7217 SaveGame(f, dummy, dummy2)
7222 if (gameMode == EditPosition) EditPositionDone();
7223 if (appData.oldSaveStyle)
7224 return SaveGameOldStyle(f);
7226 return SaveGamePGN(f);
7229 /* Save the current position to the given file */
7231 SavePositionToFile(filename)
7237 if (strcmp(filename, "-") == 0) {
7238 return SavePosition(stdout, 0, NULL);
7240 f = fopen(filename, "a");
7242 sprintf(buf, "Can't open \"%s\"", filename);
7243 DisplayError(buf, errno);
7246 SavePosition(f, 0, NULL);
7252 /* Save the current position to the given open file and close the file */
7254 SavePosition(f, dummy, dummy2)
7262 if (appData.oldSaveStyle) {
7263 tm = time((time_t *) NULL);
7265 fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
7267 fprintf(f, "[--------------\n");
7268 PrintPosition(f, currentMove);
7269 fprintf(f, "--------------]\n");
7271 fen = PositionToFEN(currentMove);
7272 fprintf(f, "%s\n", fen);
7280 ReloadCmailMsgEvent(unregister)
7283 static char *inFilename = NULL;
7284 static char *outFilename;
7286 struct stat inbuf, outbuf;
7289 /* Any registered moves are unregistered if unregister is set, */
7290 /* i.e. invoked by the signal handler */
7292 for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
7293 cmailMoveRegistered[i] = FALSE;
7294 if (cmailCommentList[i] != NULL) {
7295 free(cmailCommentList[i]);
7296 cmailCommentList[i] = NULL;
7299 nCmailMovesRegistered = 0;
7302 for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
7303 cmailResult[i] = CMAIL_NOT_RESULT;
7307 if (inFilename == NULL) {
7308 /* Because the filenames are static they only get malloced once */
7309 /* and they never get freed */
7310 inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);
7311 sprintf(inFilename, "%s.game.in", appData.cmailGameName);
7313 outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
7314 sprintf(outFilename, "%s.out", appData.cmailGameName);
7317 status = stat(outFilename, &outbuf);
7319 cmailMailedMove = FALSE;
7321 status = stat(inFilename, &inbuf);
7322 cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
7325 /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
7326 counts the games, notes how each one terminated, etc.
7328 It would be nice to remove this kludge and instead gather all
7329 the information while building the game list. (And to keep it
7330 in the game list nodes instead of having a bunch of fixed-size
7331 parallel arrays.) Note this will require getting each game's
7332 termination from the PGN tags, as the game list builder does
7333 not process the game moves. --mann
7335 cmailMsgLoaded = TRUE;
7336 LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
7338 /* Load first game in the file or popup game menu */
7339 LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
7348 char string[MSG_SIZ];
7350 if ( cmailMailedMove
7351 || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {
7352 return TRUE; /* Allow free viewing */
7355 /* Unregister move to ensure that we don't leave RegisterMove */
7356 /* with the move registered when the conditions for registering no */
7358 if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
7359 cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
7360 nCmailMovesRegistered --;
7362 if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
7364 free(cmailCommentList[lastLoadGameNumber - 1]);
7365 cmailCommentList[lastLoadGameNumber - 1] = NULL;
7369 if (cmailOldMove == -1) {
7370 DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);
7374 if (currentMove > cmailOldMove + 1) {
7375 DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);
7379 if (currentMove < cmailOldMove) {
7380 DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);
7384 if (forwardMostMove > currentMove) {
7385 /* Silently truncate extra moves */
7389 if ( (currentMove == cmailOldMove + 1)
7390 || ( (currentMove == cmailOldMove)
7391 && ( (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)
7392 || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {
7393 if (gameInfo.result != GameUnfinished) {
7394 cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;
7397 if (commentList[currentMove] != NULL) {
7398 cmailCommentList[lastLoadGameNumber - 1]
7399 = StrSave(commentList[currentMove]);
7401 strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
7403 if (appData.debugMode)
7404 fprintf(debugFP, "Saving %s for game %d\n",
7405 cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
7408 "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
7410 f = fopen(string, "w");
7411 if (appData.oldSaveStyle) {
7412 SaveGameOldStyle(f); /* also closes the file */
7414 sprintf(string, "%s.pos.out", appData.cmailGameName);
7415 f = fopen(string, "w");
7416 SavePosition(f, 0, NULL); /* also closes the file */
7418 fprintf(f, "{--------------\n");
7419 PrintPosition(f, currentMove);
7420 fprintf(f, "--------------}\n\n");
7422 SaveGame(f, 0, NULL); /* also closes the file*/
7425 cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
7426 nCmailMovesRegistered ++;
7427 } else if (nCmailGames == 1) {
7428 DisplayError("You have not made a move yet", 0);
7438 static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
7439 FILE *commandOutput;
7440 char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];
7441 int nBytes = 0; /* Suppress warnings on uninitialized variables */
7447 if (! cmailMsgLoaded) {
7448 DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);
7452 if (nCmailGames == nCmailResults) {
7453 DisplayError("No unfinished games", 0);
7457 #if CMAIL_PROHIBIT_REMAIL
7458 if (cmailMailedMove) {
7459 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);
7460 DisplayError(msg, 0);
7465 if (! (cmailMailedMove || RegisterMove())) return;
7467 if ( cmailMailedMove
7468 || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
7469 sprintf(string, partCommandString,
7470 appData.debugMode ? " -v" : "", appData.cmailGameName);
7471 commandOutput = popen(string, "r");
7473 if (commandOutput == NULL) {
7474 DisplayError("Failed to invoke cmail", 0);
7476 for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {
7477 nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);
7480 (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);
7481 (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);
7482 nBytes = MSG_SIZ - 1;
7484 (void) memcpy(msg, buffer, nBytes);
7486 *(msg + nBytes) = '\0'; /* \0 for end-of-string*/
7488 if(StrStr(msg, "Mailed cmail message to ") != NULL) {
7489 cmailMailedMove = TRUE; /* Prevent >1 moves */
7492 for (i = 0; i < nCmailGames; i ++) {
7493 if (cmailResult[i] == CMAIL_NOT_RESULT) {
7498 && ( (arcDir = (char *) getenv("CMAIL_ARCDIR"))
7500 sprintf(buffer, "%s/%s.%s.archive",
7502 appData.cmailGameName,
7504 LoadGameFromFile(buffer, 1, buffer, FALSE);
7505 cmailMsgLoaded = FALSE;
7509 DisplayInformation(msg);
7510 pclose(commandOutput);
7513 if ((*cmailMsg) != '\0') {
7514 DisplayInformation(cmailMsg);
7524 int prependComma = 0;
7526 char string[MSG_SIZ]; /* Space for game-list */
7529 if (!cmailMsgLoaded) return "";
7531 if (cmailMailedMove) {
7532 sprintf(cmailMsg, "Waiting for reply from opponent\n");
7534 /* Create a list of games left */
7535 sprintf(string, "[");
7536 for (i = 0; i < nCmailGames; i ++) {
7537 if (! ( cmailMoveRegistered[i]
7538 || (cmailResult[i] == CMAIL_OLD_RESULT))) {
7540 sprintf(number, ",%d", i + 1);
7542 sprintf(number, "%d", i + 1);
7546 strcat(string, number);
7549 strcat(string, "]");
7551 if (nCmailMovesRegistered + nCmailResults == 0) {
7552 switch (nCmailGames) {
7555 "Still need to make move for game\n");
7560 "Still need to make moves for both games\n");
7565 "Still need to make moves for all %d games\n",
7570 switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
7573 "Still need to make a move for game %s\n",
7578 if (nCmailResults == nCmailGames) {
7579 sprintf(cmailMsg, "No unfinished games\n");
7581 sprintf(cmailMsg, "Ready to send mail\n");
7587 "Still need to make moves for games %s\n",
7599 if (gameMode == Training)
7600 SetTrainingModeOff();
7603 cmailMsgLoaded = FALSE;
7604 if (appData.icsActive) {
7605 SendToICS(ics_prefix);
7606 SendToICS("refresh\n");
7610 static int exiting = 0;
7618 /* Give up on clean exit */
7622 /* Keep trying for clean exit */
7626 if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);
7628 if (telnetISR != NULL) {
7629 RemoveInputSource(telnetISR);
7631 if (icsPR != NoProc) {
7632 DestroyChildProcess(icsPR, TRUE);
7634 /* Save game if resource set and not already saved by GameEnds() */
7635 if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {
7636 if (*appData.saveGameFile != NULLCHAR) {
7637 SaveGameToFile(appData.saveGameFile, TRUE);
7638 } else if (appData.autoSaveGames) {
7641 if (*appData.savePositionFile != NULLCHAR) {
7642 SavePositionToFile(appData.savePositionFile);
7645 GameEnds((ChessMove) 0, NULL, GE_PLAYER);
7647 /* Kill off chess programs */
7648 if (first.pr != NoProc) {
7650 SendToProgram("quit\n", &first);
7651 DestroyChildProcess(first.pr, first.useSigterm);
7653 if (second.pr != NoProc) {
7654 SendToProgram("quit\n", &second);
7655 DestroyChildProcess(second.pr, second.useSigterm);
7657 if (first.isr != NULL) {
7658 RemoveInputSource(first.isr);
7660 if (second.isr != NULL) {
7661 RemoveInputSource(second.isr);
7671 if (appData.debugMode)
7672 fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
7676 if (gameMode == MachinePlaysWhite ||
7677 gameMode == MachinePlaysBlack) {
7680 DisplayBothClocks();
7682 if (gameMode == PlayFromGameFile) {
7683 if (appData.timeDelay >= 0)
7685 } else if (gameMode == IcsExamining && pauseExamInvalid) {
7687 SendToICS(ics_prefix);
7688 SendToICS("refresh\n");
7689 } else if (currentMove < forwardMostMove) {
7690 ForwardInner(forwardMostMove);
7692 pauseExamInvalid = FALSE;
7698 pauseExamForwardMostMove = forwardMostMove;
7699 pauseExamInvalid = FALSE;
7702 case IcsPlayingWhite:
7703 case IcsPlayingBlack:
7707 case PlayFromGameFile:
7708 (void) StopLoadGameTimer();
7712 case BeginningOfGame:
7713 if (appData.icsActive) return;
7714 /* else fall through */
7715 case MachinePlaysWhite:
7716 case MachinePlaysBlack:
7717 case TwoMachinesPlay:
7718 if (forwardMostMove == 0)
7719 return; /* don't pause if no one has moved */
7720 if ((gameMode == MachinePlaysWhite &&
7721 !WhiteOnMove(forwardMostMove)) ||
7722 (gameMode == MachinePlaysBlack &&
7723 WhiteOnMove(forwardMostMove))) {
7736 char title[MSG_SIZ];
7738 if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
7739 strcpy(title, "Edit comment");
7741 sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,
7742 WhiteOnMove(currentMove - 1) ? " " : ".. ",
7743 parseList[currentMove - 1]);
7746 EditCommentPopUp(currentMove, title, commentList[currentMove]);
7753 char *tags = PGNTags(&gameInfo);
7754 EditTagsPopUp(tags);
7761 if (appData.noChessProgram || gameMode == AnalyzeMode)
7764 /* Engine Room enable */
7765 /* We need a AnalysisDisplay(TRUE) for timer */
7766 appData.AnalysisWindow = TRUE;
7768 if (gameMode != AnalyzeFile) {
7770 if (gameMode != EditGame) return;
7771 ResurrectChessProgram();
7772 SendToProgram("analyze\n", &first);
7773 first.analyzing = TRUE;
7774 /*first.maybeThinking = TRUE;*/
7775 first.maybeThinking = FALSE; /* avoid killing GNU Chess */
7777 gameMode = AnalyzeMode;
7781 StartAnalysisClock();
7782 GetTimeMark(&lastNodeCountTime);
7784 PeriodicUpdatesEvent(TRUE);
7788 void IcsAnalyze(newState)
7792 if (appData.noChessProgram) {
7793 DisplayFatalError("ICS-Analysis requires a chess engine", 0, 1);
7797 if (!newState && first.analyzing) {
7798 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: Shutting down engine\n");
7800 DisplayMessage("","Close ICS engine analyse...");
7804 if (gameMode != IcsObserving) {
7805 MessageBox(0,"You are not observing a game",
7806 "ICS Engine Analyse", MB_OK);
7807 appData.icsAnalyze = FALSE;
7808 /* Set state for GUI */
7809 appData.icsEngineNone = TRUE;
7810 appData.icsEngineWhisper = FALSE;
7811 appData.icsEngineKibitz = FALSE;
7812 appData.icsEngineTell = FALSE;
7813 appData.icsAnalyzeOutPut = 4;
7817 if (first.analysisSupport == FALSE) {
7818 MessageBox(0,"Sorry, this engine does not support analyze",
7819 "ICS Engine Analyse", MB_OK);
7824 if (appData.debugMode) fprintf(debugFP, "Starting ICS analyze\n");
7825 DisplayMessage("","Starting ICS engine analyse");
7826 if (!appData.icsWBprotoAgr) {
7828 /* Wo do that only if we start new or have a problem */
7829 SendToProgram("new\n", &first);
7830 SendToProgram("post\n", &first);
7831 SendToProgram("force\n", &first);
7832 FeedMovesToProgram(&first, currentMove);
7833 SendToProgram("analyze\n", &first);
7836 SendToProgram("exit\n", &first);
7837 SendToProgram("new\n", &first);
7838 SendToProgram("post\n", &first);
7839 SendToProgram("hart\n", &first);
7840 SendToProgram("force\n", &first);
7841 FeedMovesToProgram(&first, currentMove);
7842 SendToProgram("analyze\n", &first);
7843 SendToProgram(".\n", &first);
7845 PeriodicUpdatesEvent(TRUE); /* appData.periodicUpdates */
7846 first.analyzing = TRUE; /* Used for GUI and other ways */
7851 IcsAnalyzeWindowUp()
7853 /* Using allso for startup Engine Room
7854 * So function name looks like mismatch
7855 * Change it for a better program style -
7856 * but this is not crirical
7859 if (appData.AnalysisWindow) {
7860 ClearProgramStats();
7861 DisplayAnalysis(0,0);
7862 PeriodicUpdatesEvent(TRUE);
7864 /* StartAnalysisClock check gameMode */
7865 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
7866 appData.icsAnalyze || appData.AnalysisWindow) {
7867 StartAnalysisClock();
7869 GetTimeMark(&lastNodeCountTime);
7870 lastNodeCount = 0; /* importent */
7876 if (appData.noChessProgram || gameMode == AnalyzeFile)
7880 /* We need a DisplayAnalysis(TRUE); */
7881 appData.AnalysisWindow = TRUE;
7883 if (gameMode != AnalyzeMode) {
7885 if (gameMode != EditGame) return;
7886 ResurrectChessProgram();
7887 SendToProgram("analyze\n", &first);
7888 first.analyzing = TRUE;
7889 /*first.maybeThinking = TRUE;*/
7890 first.maybeThinking = FALSE; /* avoid killing GNU Chess */
7892 gameMode = AnalyzeFile;
7897 StartAnalysisClock();
7898 GetTimeMark(&lastNodeCountTime);
7907 if (appData.noChessProgram || (gameMode == MachinePlaysWhite))
7911 if (gameMode == PlayFromGameFile ||
7912 gameMode == TwoMachinesPlay ||
7913 gameMode == Training ||
7914 gameMode == AnalyzeMode ||
7915 gameMode == EndOfGame)
7918 if (gameMode == EditPosition)
7921 if (!WhiteOnMove(currentMove)) {
7922 DisplayError("It is not White's turn", 0);
7926 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
7930 if (gameMode == EditGame || gameMode == AnalyzeMode ||
7931 gameMode == AnalyzeFile)
7934 ResurrectChessProgram(); /* in case it isn't running */
7935 gameMode = MachinePlaysWhite;
7939 sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
7941 if (first.sendName) {
7942 sprintf(buf, "name %s\n", gameInfo.black);
7943 SendToProgram(buf, &first);
7945 if (first.sendTime) {
7946 if (first.useColors) {
7947 SendToProgram("black\n", &first); /*gnu kludge*/
7949 SendTimeRemaining(&first, TRUE);
7951 if (first.useColors) {
7952 SendToProgram("white\ngo\n", &first);
7954 SendToProgram("go\n", &first);
7956 SetMachineThinkingEnables();
7957 first.maybeThinking = TRUE;
7960 if (appData.autoFlipView && !flipView) {
7961 flipView = !flipView;
7962 DrawPosition(FALSE, NULL);
7964 if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
7972 if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
7976 if (gameMode == PlayFromGameFile ||
7977 gameMode == TwoMachinesPlay ||
7978 gameMode == Training ||
7979 gameMode == AnalyzeMode ||
7980 gameMode == EndOfGame)
7983 if (gameMode == EditPosition)
7986 if (WhiteOnMove(currentMove)) {
7987 DisplayError("It is not Black's turn", 0);
7991 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
7994 if (gameMode == EditGame || gameMode == AnalyzeMode ||
7995 gameMode == AnalyzeFile)
7998 ResurrectChessProgram(); /* in case it isn't running */
7999 gameMode = MachinePlaysBlack;
8003 sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
8005 if (first.sendName) {
8006 sprintf(buf, "name %s\n", gameInfo.white);
8007 SendToProgram(buf, &first);
8009 if (first.sendTime) {
8010 if (first.useColors) {
8011 SendToProgram("white\n", &first); /*gnu kludge*/
8013 SendTimeRemaining(&first, FALSE);
8015 if (first.useColors) {
8016 SendToProgram("black\ngo\n", &first);
8018 SendToProgram("go\n", &first);
8020 SetMachineThinkingEnables();
8021 first.maybeThinking = TRUE;
8024 if (appData.autoFlipView && flipView) {
8025 flipView = !flipView;
8026 DrawPosition(FALSE, NULL);
8028 if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
8033 DisplayTwoMachinesTitle()
8036 if (appData.matchGames > 0) {
8037 if (first.twoMachinesColor[0] == 'w') {
8038 sprintf(buf, "%s vs. %s (%d-%d-%d)",
8039 gameInfo.white, gameInfo.black,
8040 first.matchWins, second.matchWins,
8041 matchGame - 1 - (first.matchWins + second.matchWins));
8043 sprintf(buf, "%s vs. %s (%d-%d-%d)",
8044 gameInfo.white, gameInfo.black,
8045 second.matchWins, first.matchWins,
8046 matchGame - 1 - (first.matchWins + second.matchWins));
8049 sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
8055 TwoMachinesEvent P((void))
8059 ChessProgramState *onmove;
8061 if (appData.noChessProgram) return;
8064 case TwoMachinesPlay:
8066 case MachinePlaysWhite:
8067 case MachinePlaysBlack:
8068 if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
8069 DisplayError("Wait until your turn,\nor select Move Now", 0);
8073 case BeginningOfGame:
8074 case PlayFromGameFile:
8077 if (gameMode != EditGame) return;
8091 forwardMostMove = currentMove;
8092 ResurrectChessProgram(); /* in case first program isn't running */
8094 if (second.pr == NULL) {
8095 StartChessProgram(&second);
8096 if (second.protocolVersion == 1) {
8097 TwoMachinesEventIfReady();
8099 /* kludge: allow timeout for initial "feature" command */
8101 DisplayMessage("", "Starting second chess program");
8102 ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
8106 DisplayMessage("", "");
8107 InitChessProgram(&second);
8108 SendToProgram("force\n", &second);
8109 if (startedFromSetupPosition) {
8110 SendBoard(&second, backwardMostMove);
8112 for (i = backwardMostMove; i < forwardMostMove; i++) {
8113 SendMoveToProgram(i, &second);
8116 gameMode = TwoMachinesPlay;
8120 DisplayTwoMachinesTitle();
8122 if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {
8128 SendToProgram(first.computerString, &first);
8129 if (first.sendName) {
8130 sprintf(buf, "name %s\n", second.tidy);
8131 SendToProgram(buf, &first);
8133 SendToProgram(second.computerString, &second);
8134 if (second.sendName) {
8135 sprintf(buf, "name %s\n", first.tidy);
8136 SendToProgram(buf, &second);
8139 if (!first.sendTime || !second.sendTime) {
8141 timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
8142 timeRemaining[1][forwardMostMove] = blackTimeRemaining;
8144 if (onmove->sendTime) {
8145 if (onmove->useColors) {
8146 SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/
8148 SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));
8150 if (onmove->useColors) {
8151 SendToProgram(onmove->twoMachinesColor, onmove);
8153 SendToProgram("go\n", onmove);
8154 onmove->maybeThinking = TRUE;
8155 SetMachineThinkingEnables();
8163 if (gameMode == Training) {
8164 SetTrainingModeOff();
8165 gameMode = PlayFromGameFile;
8166 DisplayMessage("", "Training mode off");
8168 gameMode = Training;
8169 animateTraining = appData.animate;
8171 /* make sure we are not already at the end of the game */
8172 if (currentMove < forwardMostMove) {
8173 SetTrainingModeOn();
8174 DisplayMessage("", "Training mode on");
8176 gameMode = PlayFromGameFile;
8177 DisplayError("Already at end of game", 0);
8186 if (!appData.icsActive) return;
8188 case IcsPlayingWhite:
8189 case IcsPlayingBlack:
8192 case BeginningOfGame:
8226 SetTrainingModeOff();
8228 case MachinePlaysWhite:
8229 case MachinePlaysBlack:
8230 case BeginningOfGame:
8231 SendToProgram("force\n", &first);
8232 SetUserThinkingEnables();
8234 case PlayFromGameFile:
8235 (void) StopLoadGameTimer();
8236 if (gameFileFP != NULL) {
8246 SendToProgram("force\n", &first);
8248 case TwoMachinesPlay:
8249 GameEnds((ChessMove) 0, NULL, GE_PLAYER);
8250 ResurrectChessProgram();
8251 SetUserThinkingEnables();
8254 ResurrectChessProgram();
8256 case IcsPlayingBlack:
8257 case IcsPlayingWhite:
8258 DisplayError("Warning: You are still playing a game", 0);
8263 DisplayError("Warning: You are still examining a game", 0);
8274 if (gameMode != IcsObserving) StopClocks();
8276 first.offeredDraw = second.offeredDraw = 0;
8278 if (gameMode == PlayFromGameFile) {
8279 whiteTimeRemaining = timeRemaining[0][currentMove];
8280 blackTimeRemaining = timeRemaining[1][currentMove];
8284 if (gameMode == MachinePlaysWhite ||
8285 gameMode == MachinePlaysBlack ||
8286 gameMode == TwoMachinesPlay ||
8287 gameMode == EndOfGame) {
8288 i = forwardMostMove;
8289 while (i > currentMove) {
8290 SendToProgram("undo\n", &first);
8293 whiteTimeRemaining = timeRemaining[0][currentMove];
8294 blackTimeRemaining = timeRemaining[1][currentMove];
8295 DisplayBothClocks();
8296 if (whiteFlag || blackFlag) {
8297 whiteFlag = blackFlag = 0;
8302 gameMode = EditGame;
8311 if (gameMode == EditPosition) {
8317 if (gameMode != EditGame) return;
8319 gameMode = EditPosition;
8322 if (currentMove > 0)
8323 CopyBoard(boards[0], boards[currentMove]);
8325 blackPlaysFirst = !WhiteOnMove(currentMove);
8327 currentMove = forwardMostMove = backwardMostMove = 0;
8328 HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
8335 if (first.analysisSupport && first.analyzing) {
8336 SendToProgram("exit\n", &first);
8337 first.analyzing = FALSE;
8340 thinkOutput[0] = NULLCHAR;
8346 startedFromSetupPosition = TRUE;
8347 InitChessProgram(&first);
8348 SendToProgram("force\n", &first);
8349 if (blackPlaysFirst) {
8350 strcpy(moveList[0], "");
8351 strcpy(parseList[0], "");
8352 currentMove = forwardMostMove = backwardMostMove = 1;
8353 CopyBoard(boards[1], boards[0]);
8355 currentMove = forwardMostMove = backwardMostMove = 0;
8357 SendBoard(&first, forwardMostMove);
8359 timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
8360 timeRemaining[1][forwardMostMove] = blackTimeRemaining;
8361 gameMode = EditGame;
8363 HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
8366 /* Pause for `ms' milliseconds */
8367 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
8377 } while (SubtractTimeMarks(&m2, &m1) < ms);
8380 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
8382 SendMultiLineToICS(buf)
8385 char temp[MSG_SIZ+1], *p;
8392 strncpy(temp, buf, len);
8397 if (*p == '\n' || *p == '\r')
8404 SendToPlayer(temp, strlen(temp));
8408 SetWhiteToPlayEvent()
8410 if (gameMode == EditPosition) {
8411 blackPlaysFirst = FALSE;
8412 DisplayBothClocks(); /* works because currentMove is 0 */
8413 } else if (gameMode == IcsExamining) {
8414 SendToICS(ics_prefix);
8415 SendToICS("tomove white\n");
8420 SetBlackToPlayEvent()
8422 if (gameMode == EditPosition) {
8423 blackPlaysFirst = TRUE;
8424 currentMove = 1; /* kludge */
8425 DisplayBothClocks();
8427 } else if (gameMode == IcsExamining) {
8428 SendToICS(ics_prefix);
8429 SendToICS("tomove black\n");
8434 EditPositionMenuEvent(selection, x, y)
8435 ChessSquare selection;
8440 if (gameMode != EditPosition && gameMode != IcsExamining) return;
8442 switch (selection) {
8444 if (gameMode == IcsExamining && ics_type == ICS_FICS) {
8445 SendToICS(ics_prefix);
8446 SendToICS("bsetup clear\n");
8447 } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {
8448 SendToICS(ics_prefix);
8449 SendToICS("clearboard\n");
8451 for (x = 0; x < BOARD_SIZE; x++) {
8452 for (y = 0; y < BOARD_SIZE; y++) {
8453 if (gameMode == IcsExamining) {
8454 if (boards[currentMove][y][x] != EmptySquare) {
8455 sprintf(buf, "%sx@%c%c\n", ics_prefix,
8460 boards[0][y][x] = EmptySquare;
8465 if (gameMode == EditPosition) {
8466 DrawPosition(FALSE, boards[0]);
8471 SetWhiteToPlayEvent();
8475 SetBlackToPlayEvent();
8479 if (gameMode == IcsExamining) {
8480 sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, '1' + y);
8483 boards[0][y][x] = EmptySquare;
8484 DrawPosition(FALSE, boards[0]);
8489 if (gameMode == IcsExamining) {
8490 sprintf(buf, "%s%c@%c%c\n", ics_prefix,
8491 PieceToChar(selection), 'a' + x, '1' + y);
8494 boards[0][y][x] = selection;
8495 DrawPosition(FALSE, boards[0]);
8503 DropMenuEvent(selection, x, y)
8504 ChessSquare selection;
8510 case IcsPlayingWhite:
8511 case MachinePlaysBlack:
8512 if (!WhiteOnMove(currentMove)) {
8513 DisplayMoveError("It is Black's turn");
8516 moveType = WhiteDrop;
8518 case IcsPlayingBlack:
8519 case MachinePlaysWhite:
8520 if (WhiteOnMove(currentMove)) {
8521 DisplayMoveError("It is White's turn");
8524 moveType = BlackDrop;
8527 moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
8533 if (moveType == BlackDrop && selection < BlackPawn) {
8534 selection = (ChessSquare) ((int) selection
8535 + (int) BlackPawn - (int) WhitePawn);
8537 if (boards[currentMove][y][x] != EmptySquare) {
8538 DisplayMoveError("That square is occupied");
8542 FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);
8548 /* Accept a pending offer of any kind from opponent */
8550 if (appData.icsActive) {
8551 SendToICS(ics_prefix);
8552 SendToICS("accept\n");
8553 } else if (cmailMsgLoaded) {
8554 if (currentMove == cmailOldMove &&
8555 commentList[cmailOldMove] != NULL &&
8556 StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8557 "Black offers a draw" : "White offers a draw")) {
8559 GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
8560 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
8562 DisplayError("There is no pending offer on this move", 0);
8563 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
8566 /* Not used for offers from chess program */
8573 /* Decline a pending offer of any kind from opponent */
8575 if (appData.icsActive) {
8576 SendToICS(ics_prefix);
8577 SendToICS("decline\n");
8578 } else if (cmailMsgLoaded) {
8579 if (currentMove == cmailOldMove &&
8580 commentList[cmailOldMove] != NULL &&
8581 StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8582 "Black offers a draw" : "White offers a draw")) {
8584 AppendComment(cmailOldMove, "Draw declined");
8585 DisplayComment(cmailOldMove - 1, "Draw declined");
8588 DisplayError("There is no pending offer on this move", 0);
8591 /* Not used for offers from chess program */
8598 /* Issue ICS rematch command */
8599 if (appData.icsActive) {
8600 SendToICS(ics_prefix);
8601 SendToICS("rematch\n");
8608 /* Call your opponent's flag (claim a win on time) */
8609 if (appData.icsActive) {
8610 SendToICS(ics_prefix);
8611 SendToICS("flag\n");
8616 case MachinePlaysWhite:
8619 GameEnds(GameIsDrawn, "Both players ran out of time",
8622 GameEnds(BlackWins, "Black wins on time", GE_PLAYER);
8624 DisplayError("Your opponent is not out of time", 0);
8627 case MachinePlaysBlack:
8630 GameEnds(GameIsDrawn, "Both players ran out of time",
8633 GameEnds(WhiteWins, "White wins on time", GE_PLAYER);
8635 DisplayError("Your opponent is not out of time", 0);
8645 /* Offer draw or accept pending draw offer from opponent */
8647 if (appData.icsActive) {
8648 /* Note: tournament rules require draw offers to be
8649 made after you make your move but before you punch
8650 your clock. Currently ICS doesn't let you do that;
8651 instead, you immediately punch your clock after making
8652 a move, but you can offer a draw at any time. */
8654 SendToICS(ics_prefix);
8655 SendToICS("draw\n");
8656 } else if (cmailMsgLoaded) {
8657 if (currentMove == cmailOldMove &&
8658 commentList[cmailOldMove] != NULL &&
8659 StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8660 "Black offers a draw" : "White offers a draw")) {
8661 GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
8662 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
8663 } else if (currentMove == cmailOldMove + 1) {
8664 char *offer = WhiteOnMove(cmailOldMove) ?
8665 "White offers a draw" : "Black offers a draw";
8666 AppendComment(currentMove, offer);
8667 DisplayComment(currentMove - 1, offer);
8668 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;
8670 DisplayError("You must make your move before offering a draw", 0);
8671 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
8673 } else if (first.offeredDraw) {
8674 GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
8676 if (first.sendDrawOffers) {
8677 SendToProgram("draw\n", &first);
8678 userOfferedDraw = TRUE;
8686 /* Offer Adjourn or accept pending Adjourn offer from opponent */
8688 if (appData.icsActive) {
8689 SendToICS(ics_prefix);
8690 SendToICS("adjourn\n");
8692 /* Currently GNU Chess doesn't offer or accept Adjourns */
8700 /* Offer Abort or accept pending Abort offer from opponent */
8702 if (appData.icsActive) {
8703 SendToICS(ics_prefix);
8704 SendToICS("abort\n");
8706 GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);
8713 /* Resign. You can do this even if it's not your turn. */
8715 if (appData.icsActive) {
8716 SendToICS(ics_prefix);
8717 SendToICS("resign\n");
8720 case MachinePlaysWhite:
8721 GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
8723 case MachinePlaysBlack:
8724 GameEnds(BlackWins, "White resigns", GE_PLAYER);
8727 if (cmailMsgLoaded) {
8729 if (WhiteOnMove(cmailOldMove)) {
8730 GameEnds(BlackWins, "White resigns", GE_PLAYER);
8732 GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
8734 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;
8745 StopObservingEvent()
8747 /* Stop observing current games */
8748 SendToICS(ics_prefix);
8749 SendToICS("unobserve\n");
8753 StopExaminingEvent()
8755 /* Stop observing current game */
8756 SendToICS(ics_prefix);
8757 SendToICS("unexamine\n");
8761 ForwardInner(target)
8766 if (appData.debugMode)
8767 fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
8768 target, currentMove, forwardMostMove);
8770 if (gameMode == EditPosition)
8773 if (gameMode == PlayFromGameFile && !pausing)
8776 if (gameMode == IcsExamining && pausing)
8777 limit = pauseExamForwardMostMove;
8779 limit = forwardMostMove;
8781 if (target > limit) target = limit;
8783 if (target > 0 && moveList[target - 1][0]) {
8784 int fromX, fromY, toX, toY;
8785 toX = moveList[target - 1][2] - 'a';
8786 toY = moveList[target - 1][3] - '1';
8787 if (moveList[target - 1][1] == '@') {
8788 if (appData.highlightLastMove) {
8789 SetHighlights(-1, -1, toX, toY);
8792 fromX = moveList[target - 1][0] - 'a';
8793 fromY = moveList[target - 1][1] - '1';
8794 if (target == currentMove + 1) {
8795 AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
8797 if (appData.highlightLastMove) {
8798 SetHighlights(fromX, fromY, toX, toY);
8802 if (gameMode == EditGame || gameMode == AnalyzeMode ||
8803 gameMode == Training || gameMode == PlayFromGameFile ||
8804 gameMode == AnalyzeFile) {
8805 while (currentMove < target) {
8806 SendMoveToProgram(currentMove++, &first);
8809 currentMove = target;
8812 if (gameMode == EditGame || gameMode == EndOfGame) {
8813 whiteTimeRemaining = timeRemaining[0][currentMove];
8814 blackTimeRemaining = timeRemaining[1][currentMove];
8816 DisplayBothClocks();
8817 DisplayMove(currentMove - 1);
8818 DrawPosition(FALSE, boards[currentMove]);
8819 HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
8820 if (commentList[currentMove] && !matchMode && gameMode != Training) {
8821 DisplayComment(currentMove - 1, commentList[currentMove]);
8829 if (gameMode == IcsExamining && !pausing) {
8830 SendToICS(ics_prefix);
8831 SendToICS("forward\n");
8833 ForwardInner(currentMove + 1);
8840 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8841 /* to optimze, we temporarily turn off analysis mode while we feed
8842 * the remaining moves to the engine. Otherwise we get analysis output
8845 if (first.analysisSupport) {
8846 SendToProgram("exit\nforce\n", &first);
8847 first.analyzing = FALSE;
8851 if (gameMode == IcsExamining && !pausing) {
8852 SendToICS(ics_prefix);
8853 SendToICS("forward 999999\n");
8855 ForwardInner(forwardMostMove);
8858 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8859 /* we have fed all the moves, so reactivate analysis mode */
8860 SendToProgram("analyze\n", &first);
8861 first.analyzing = TRUE;
8862 /*first.maybeThinking = TRUE;*/
8863 first.maybeThinking = FALSE; /* avoid killing GNU Chess */
8868 BackwardInner(target)
8871 if (appData.debugMode)
8872 fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
8873 target, currentMove, forwardMostMove);
8875 if (gameMode == EditPosition) return;
8876 if (currentMove <= backwardMostMove) {
8878 DrawPosition(FALSE, boards[currentMove]);
8881 if (gameMode == PlayFromGameFile && !pausing)
8884 if (moveList[target][0]) {
8885 int fromX, fromY, toX, toY;
8886 toX = moveList[target][2] - 'a';
8887 toY = moveList[target][3] - '1';
8888 if (moveList[target][1] == '@') {
8889 if (appData.highlightLastMove) {
8890 SetHighlights(-1, -1, toX, toY);
8893 fromX = moveList[target][0] - 'a';
8894 fromY = moveList[target][1] - '1';
8895 if (target == currentMove - 1) {
8896 AnimateMove(boards[currentMove], toX, toY, fromX, fromY);
8898 if (appData.highlightLastMove) {
8899 SetHighlights(fromX, fromY, toX, toY);
8903 if (gameMode == EditGame || gameMode==AnalyzeMode ||
8904 gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
8905 while (currentMove > target) {
8906 SendToProgram("undo\n", &first);
8910 currentMove = target;
8913 if (gameMode == EditGame || gameMode == EndOfGame) {
8914 whiteTimeRemaining = timeRemaining[0][currentMove];
8915 blackTimeRemaining = timeRemaining[1][currentMove];
8917 DisplayBothClocks();
8918 DisplayMove(currentMove - 1);
8919 DrawPosition(FALSE, boards[currentMove]);
8920 HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
8921 if (commentList[currentMove] != NULL) {
8922 DisplayComment(currentMove - 1, commentList[currentMove]);
8929 if (gameMode == IcsExamining && !pausing) {
8930 SendToICS(ics_prefix);
8931 SendToICS("backward\n");
8933 BackwardInner(currentMove - 1);
8940 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8941 /* to optimze, we temporarily turn off analysis mode while we undo
8942 * all the moves. Otherwise we get analysis output after each undo.
8944 if (first.analysisSupport) {
8945 SendToProgram("exit\nforce\n", &first);
8946 first.analyzing = FALSE;
8950 if (gameMode == IcsExamining && !pausing) {
8951 SendToICS(ics_prefix);
8952 SendToICS("backward 999999\n");
8954 BackwardInner(backwardMostMove);
8957 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8958 /* we have fed all the moves, so reactivate analysis mode */
8959 SendToProgram("analyze\n", &first);
8960 first.analyzing = TRUE;
8961 /*first.maybeThinking = TRUE;*/
8962 first.maybeThinking = FALSE; /* avoid killing GNU Chess */
8969 if (gameMode == PlayFromGameFile && !pausing) PauseEvent();
8970 if (to >= forwardMostMove) to = forwardMostMove;
8971 if (to <= backwardMostMove) to = backwardMostMove;
8972 if (to < currentMove) {
8982 if (gameMode != IcsExamining) {
8983 DisplayError("You are not examining a game", 0);
8987 DisplayError("You can't revert while pausing", 0);
8990 SendToICS(ics_prefix);
8991 SendToICS("revert\n");
8998 case MachinePlaysWhite:
8999 case MachinePlaysBlack:
9000 if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
9001 DisplayError("Wait until your turn,\nor select Move Now", 0);
9004 if (forwardMostMove < 2) return;
9005 currentMove = forwardMostMove = forwardMostMove - 2;
9006 whiteTimeRemaining = timeRemaining[0][currentMove];
9007 blackTimeRemaining = timeRemaining[1][currentMove];
9008 DisplayBothClocks();
9009 DisplayMove(currentMove - 1);
9010 ClearHighlights();/*!! could figure this out*/
9011 DrawPosition(FALSE, boards[currentMove]);
9012 SendToProgram("remove\n", &first);
9013 /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
9016 case BeginningOfGame:
9020 case IcsPlayingWhite:
9021 case IcsPlayingBlack:
9022 if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {
9023 SendToICS(ics_prefix);
9024 SendToICS("takeback 2\n");
9026 SendToICS(ics_prefix);
9027 SendToICS("takeback 1\n");
9036 ChessProgramState *cps;
9039 case MachinePlaysWhite:
9040 if (!WhiteOnMove(forwardMostMove)) {
9041 DisplayError("It is your turn", 0);
9046 case MachinePlaysBlack:
9047 if (WhiteOnMove(forwardMostMove)) {
9048 DisplayError("It is your turn", 0);
9053 case TwoMachinesPlay:
9054 if (WhiteOnMove(forwardMostMove) ==
9055 (first.twoMachinesColor[0] == 'w')) {
9061 case BeginningOfGame:
9065 SendToProgram("?\n", cps);
9072 if (gameMode != EditGame) return;
9079 if (forwardMostMove > currentMove) {
9080 if (gameInfo.resultDetails != NULL) {
9081 free(gameInfo.resultDetails);
9082 gameInfo.resultDetails = NULL;
9083 gameInfo.result = GameUnfinished;
9085 forwardMostMove = currentMove;
9086 HistorySet(parseList, backwardMostMove, forwardMostMove,
9094 if (appData.noChessProgram) return;
9096 case MachinePlaysWhite:
9097 if (WhiteOnMove(forwardMostMove)) {
9098 DisplayError("Wait until your turn", 0);
9102 case BeginningOfGame:
9103 case MachinePlaysBlack:
9104 if (!WhiteOnMove(forwardMostMove)) {
9105 DisplayError("Wait until your turn", 0);
9110 DisplayError("No hint available", 0);
9113 SendToProgram("hint\n", &first);
9114 hintRequested = TRUE;
9120 if (appData.noChessProgram) return;
9122 case MachinePlaysWhite:
9123 if (WhiteOnMove(forwardMostMove)) {
9124 DisplayError("Wait until your turn", 0);
9128 case BeginningOfGame:
9129 case MachinePlaysBlack:
9130 if (!WhiteOnMove(forwardMostMove)) {
9131 DisplayError("Wait until your turn", 0);
9138 case TwoMachinesPlay:
9143 SendToProgram("bk\n", &first);
9144 bookOutput[0] = NULLCHAR;
9145 bookRequested = TRUE;
9151 char *tags = PGNTags(&gameInfo);
9152 TagsPopUp(tags, CmailMsg());
9156 /* end button procedures */
9159 PrintPosition(fp, move)
9165 for (i = BOARD_SIZE - 1; i >= 0; i--) {
9166 for (j = 0; j < BOARD_SIZE; j++) {
9167 char c = PieceToChar(boards[move][i][j]);
9168 fputc(c == 'x' ? '.' : c, fp);
9169 fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
9172 if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
9173 fprintf(fp, "white to play\n");
9175 fprintf(fp, "black to play\n");
9182 if (gameInfo.white != NULL) {
9183 fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);
9189 /* Find last component of program's own name, using some heuristics */
9191 TidyProgramName(prog, host, buf)
9192 char *prog, *host, buf[MSG_SIZ];
9195 int local = (strcmp(host, "localhost") == 0);
9196 while (!local && (p = strchr(prog, ';')) != NULL) {
9198 while (*p == ' ') p++;
9201 if (*prog == '"' || *prog == '\'') {
9202 q = strchr(prog + 1, *prog);
9204 q = strchr(prog, ' ');
9206 if (q == NULL) q = prog + strlen(prog);
9208 while (p >= prog && *p != '/' && *p != '\\') p--;
9210 if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
9211 memcpy(buf, p, q - p);
9212 buf[q - p] = NULLCHAR;
9220 TimeControlTagValue()
9223 if (!appData.clockMode) {
9225 } else if (movesPerSession > 0) {
9226 sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);
9227 } else if (timeIncrement == 0) {
9228 sprintf(buf, "%ld", timeControl/1000);
9230 sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);
9232 return StrSave(buf);
9238 /* This routine is used only for certain modes */
9239 VariantClass v = gameInfo.variant;
9240 ClearGameInfo(&gameInfo);
9241 gameInfo.variant = v;
9244 case MachinePlaysWhite:
9245 gameInfo.event = StrSave("Computer chess game");
9246 gameInfo.site = StrSave(HostName());
9247 gameInfo.date = PGNDate();
9248 gameInfo.round = StrSave("-");
9249 gameInfo.white = StrSave(first.tidy);
9250 gameInfo.black = StrSave(UserName());
9251 gameInfo.timeControl = TimeControlTagValue();
9254 case MachinePlaysBlack:
9255 gameInfo.event = StrSave("Computer chess game");
9256 gameInfo.site = StrSave(HostName());
9257 gameInfo.date = PGNDate();
9258 gameInfo.round = StrSave("-");
9259 gameInfo.white = StrSave(UserName());
9260 gameInfo.black = StrSave(first.tidy);
9261 gameInfo.timeControl = TimeControlTagValue();
9264 case TwoMachinesPlay:
9265 gameInfo.event = StrSave("Computer chess game");
9266 gameInfo.site = StrSave(HostName());
9267 gameInfo.date = PGNDate();
9268 if (matchGame > 0) {
9270 sprintf(buf, "%d", matchGame);
9271 gameInfo.round = StrSave(buf);
9273 gameInfo.round = StrSave("-");
9275 if (first.twoMachinesColor[0] == 'w') {
9276 gameInfo.white = StrSave(first.tidy);
9277 gameInfo.black = StrSave(second.tidy);
9279 gameInfo.white = StrSave(second.tidy);
9280 gameInfo.black = StrSave(first.tidy);
9282 gameInfo.timeControl = TimeControlTagValue();
9286 gameInfo.event = StrSave("Edited game");
9287 gameInfo.site = StrSave(HostName());
9288 gameInfo.date = PGNDate();
9289 gameInfo.round = StrSave("-");
9290 gameInfo.white = StrSave("-");
9291 gameInfo.black = StrSave("-");
9295 gameInfo.event = StrSave("Edited position");
9296 gameInfo.site = StrSave(HostName());
9297 gameInfo.date = PGNDate();
9298 gameInfo.round = StrSave("-");
9299 gameInfo.white = StrSave("-");
9300 gameInfo.black = StrSave("-");
9303 case IcsPlayingWhite:
9304 case IcsPlayingBlack:
9309 case PlayFromGameFile:
9310 gameInfo.event = StrSave("Game from non-PGN file");
9311 gameInfo.site = StrSave(HostName());
9312 gameInfo.date = PGNDate();
9313 gameInfo.round = StrSave("-");
9314 gameInfo.white = StrSave("?");
9315 gameInfo.black = StrSave("?");
9324 ReplaceComment(index, text)
9330 while (*text == '\n') text++;
9332 while (len > 0 && text[len - 1] == '\n') len--;
9334 if (commentList[index] != NULL)
9335 free(commentList[index]);
9338 commentList[index] = NULL;
9341 commentList[index] = (char *) malloc(len + 2);
9342 strncpy(commentList[index], text, len);
9343 commentList[index][len] = '\n';
9344 commentList[index][len + 1] = NULLCHAR;
9348 AppendComment(index, text)
9355 while (*text == '\n') text++;
9357 while (len > 0 && text[len - 1] == '\n') len--;
9359 if (len == 0) return;
9361 if (commentList[index] != NULL) {
9362 old = commentList[index];
9363 oldlen = strlen(old);
9364 commentList[index] = (char *) malloc(oldlen + len + 2);
9365 strcpy(commentList[index], old);
9367 strncpy(&commentList[index][oldlen], text, len);
9368 commentList[index][oldlen + len] = '\n';
9369 commentList[index][oldlen + len + 1] = NULLCHAR;
9371 commentList[index] = (char *) malloc(len + 2);
9372 strncpy(commentList[index], text, len);
9373 commentList[index][len] = '\n';
9374 commentList[index][len + 1] = NULLCHAR;
9379 SendToProgram(message, cps)
9381 ChessProgramState *cps;
9383 int count, outCount, error;
9386 if (cps->pr == NULL) return;
9389 if (appData.debugMode) {
9392 fprintf(debugFP, "%ld >%-6s: %s",
9393 SubtractTimeMarks(&now, &programStartTime),
9394 cps->which, message);
9397 count = strlen(message);
9398 outCount = OutputToProcess(cps->pr, message, count, &error);
9399 if (outCount < count && !exiting) {
9400 sprintf(buf, "Error writing to %s chess program", cps->which);
9401 DisplayFatalError(buf, error, 1);
9406 ReceiveFromProgram(isr, closure, message, count, error)
9415 ChessProgramState *cps = (ChessProgramState *)closure;
9417 if (isr != cps->isr) return; /* Killed intentionally */
9421 "Error: %s chess program (%s) exited unexpectedly",
9422 cps->which, cps->program);
9423 RemoveInputSource(cps->isr);
9424 DisplayFatalError(buf, 0, 1);
9427 "Error reading from %s chess program (%s)",
9428 cps->which, cps->program);
9429 RemoveInputSource(cps->isr);
9430 DisplayFatalError(buf, error, 1);
9432 GameEnds((ChessMove) 0, NULL, GE_PLAYER);
9436 if ((end_str = strchr(message, '\r')) != NULL)
9437 *end_str = NULLCHAR;
9438 if ((end_str = strchr(message, '\n')) != NULL)
9439 *end_str = NULLCHAR;
9441 if (appData.debugMode) {
9444 fprintf(debugFP, "%ld <%-6s: %s\n",
9445 SubtractTimeMarks(&now, &programStartTime),
9446 cps->which, message);
9449 if (appData.icsAnalyze) {
9450 /* wb2 - can reset a setting - we are in possible ics mode - drop */
9451 if (strstr(message, "whisper") != NULL ||
9452 strstr(message, "kibitz") != NULL ||
9453 strstr(message, "set 1") != NULL) {
9454 if (appData.debugMode) fprintf(debugFP, "ICS Analyze: Drop engine output\n");
9456 HandleMachineMove(message, cps);
9459 HandleMachineMove(message, cps);
9465 SendTimeControl(cps, mps, tc, inc, sd, st)
9466 ChessProgramState *cps;
9467 int mps, inc, sd, st;
9471 int seconds = (tc / 1000) % 60;
9474 /* Set exact time per move, normally using st command */
9475 if (cps->stKludge) {
9476 /* GNU Chess 4 has no st command; uses level in a nonstandard way */
9479 sprintf(buf, "level 1 %d\n", st/60);
9481 sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);
9484 sprintf(buf, "st %d\n", st);
9487 /* Set conventional or incremental time control, using level command */
9489 /* Note old gnuchess bug -- minutes:seconds used to not work.
9490 Fixed in later versions, but still avoid :seconds
9491 when seconds is 0. */
9492 sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);
9494 sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,
9498 SendToProgram(buf, cps);
9500 /* Orthoganally (except for GNU Chess 4), limit time to st seconds */
9501 /* Orthogonally, limit search to given depth */
9503 if (cps->sdKludge) {
9504 sprintf(buf, "depth\n%d\n", sd);
9506 sprintf(buf, "sd %d\n", sd);
9508 SendToProgram(buf, cps);
9513 SendTimeRemaining(cps, machineWhite)
9514 ChessProgramState *cps;
9515 int /*boolean*/ machineWhite;
9517 char message[MSG_SIZ];
9520 /* Note: this routine must be called when the clocks are stopped
9521 or when they have *just* been set or switched; otherwise
9522 it will be off by the time since the current tick started.
9525 time = whiteTimeRemaining / 10;
9526 otime = blackTimeRemaining / 10;
9528 time = blackTimeRemaining / 10;
9529 otime = whiteTimeRemaining / 10;
9531 if (time <= 0) time = 1;
9532 if (otime <= 0) otime = 1;
9534 sprintf(message, "time %ld\notim %ld\n", time, otime);
9535 SendToProgram(message, cps);
9539 BoolFeature(p, name, loc, cps)
9543 ChessProgramState *cps;
9546 int len = strlen(name);
9548 if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
9550 sscanf(*p, "%d", &val);
9552 while (**p && **p != ' ') (*p)++;
9553 sprintf(buf, "accepted %s\n", name);
9554 SendToProgram(buf, cps);
9561 IntFeature(p, name, loc, cps)
9565 ChessProgramState *cps;
9568 int len = strlen(name);
9569 if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
9571 sscanf(*p, "%d", loc);
9572 while (**p && **p != ' ') (*p)++;
9573 sprintf(buf, "accepted %s\n", name);
9574 SendToProgram(buf, cps);
9581 StringFeature(p, name, loc, cps)
9585 ChessProgramState *cps;
9588 int len = strlen(name);
9589 if (strncmp((*p), name, len) == 0
9590 && (*p)[len] == '=' && (*p)[len+1] == '\"') {
9592 sscanf(*p, "%[^\"]", loc);
9593 while (**p && **p != '\"') (*p)++;
9594 if (**p == '\"') (*p)++;
9595 sprintf(buf, "accepted %s\n", name);
9596 SendToProgram(buf, cps);
9603 FeatureDone(cps, val)
9604 ChessProgramState* cps;
9607 DelayedEventCallback cb = GetDelayedEvent();
9608 if ((cb == InitBackEnd3 && cps == &first) ||
9609 (cb == TwoMachinesEventIfReady && cps == &second)) {
9610 CancelDelayedEvent();
9611 ScheduleDelayedEvent(cb, val ? 1 : 3600000);
9613 cps->initDone = val;
9616 /* Parse feature command from engine */
9618 ParseFeatures(args, cps)
9620 ChessProgramState *cps;
9628 while (*p == ' ') p++;
9629 if (*p == NULLCHAR) return;
9631 if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
9632 if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;
9633 if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;
9634 if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;
9635 if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;
9636 if (BoolFeature(&p, "reuse", &val, cps)) {
9637 /* Engine can disable reuse, but can't enable it if user said no */
9638 if (!val) cps->reuse = FALSE;
9641 if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
9642 if (StringFeature(&p, "myname", &cps->tidy, cps)) {
9643 if (gameMode == TwoMachinesPlay) {
9644 DisplayTwoMachinesTitle();
9650 if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
9651 if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
9652 if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
9653 if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
9654 if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;
9655 if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;
9656 if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
9657 if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
9658 if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
9659 if (IntFeature(&p, "done", &val, cps)) {
9660 FeatureDone(cps, val);
9664 /* unknown feature: complain and skip */
9666 while (*q && *q != '=') q++;
9667 sprintf(buf, "rejected %.*s\n", q-p, p);
9668 SendToProgram(buf, cps);
9674 while (*p && *p != '\"') p++;
9675 if (*p == '\"') p++;
9677 while (*p && *p != ' ') p++;
9685 PeriodicUpdatesEvent(newState)
9688 if (newState == appData.periodicUpdates)
9691 appData.periodicUpdates=newState;
9693 /* Display type changes, so update it now */
9694 DisplayAnalysis(0,0);
9696 /* Get the ball rolling again... */
9698 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
9699 appData.icsAnalyze || appData.AnalysisWindow) {
9700 AnalysisPeriodicEvent(1);
9701 StartAnalysisClock();
9707 PonderNextMoveEvent(newState)
9710 if (newState == appData.ponderNextMove) return;
9711 if (gameMode == EditPosition) EditPositionDone();
9713 SendToProgram("hard\n", &first);
9714 if (gameMode == TwoMachinesPlay) {
9715 SendToProgram("hard\n", &second);
9718 SendToProgram("easy\n", &first);
9719 thinkOutput[0] = NULLCHAR;
9720 if (gameMode == TwoMachinesPlay) {
9721 SendToProgram("easy\n", &second);
9724 appData.ponderNextMove = newState;
9728 ShowThinkingEvent(newState)
9731 if (newState == appData.showThinking) return;
9732 if (gameMode == EditPosition) EditPositionDone();
9734 SendToProgram("post\n", &first);
9735 if (gameMode == TwoMachinesPlay) {
9736 SendToProgram("post\n", &second);
9739 if (appData.AnalysisWindow) {
9741 appData.AnalysisWindow = FALSE;
9743 SendToProgram("nopost\n", &first);
9744 thinkOutput[0] = NULLCHAR;
9745 if (gameMode == TwoMachinesPlay) {
9746 SendToProgram("nopost\n", &second);
9749 appData.showThinking = newState;
9753 AskQuestionEvent(title, question, replyPrefix, which)
9754 char *title; char *question; char *replyPrefix; char *which;
9756 ProcRef pr = (which[0] == '1') ? first.pr : second.pr;
9757 if (pr == NoProc) return;
9758 AskQuestion(title, question, replyPrefix, pr);
9762 DisplayMove(moveNumber)
9765 char message[MSG_SIZ];
9767 char cpThinkOutput[MSG_SIZ];
9768 static char last[1024];
9772 if (moveNumber == forwardMostMove - 1 ||
9773 gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
9775 strcpy(cpThinkOutput, thinkOutput);
9776 if (strchr(cpThinkOutput, '\n'))
9777 *strchr(cpThinkOutput, '\n') = NULLCHAR;
9779 *cpThinkOutput = NULLCHAR;
9782 if (moveNumber == forwardMostMove - 1 &&
9783 gameInfo.resultDetails != NULL) {
9784 if (gameInfo.resultDetails[0] == NULLCHAR) {
9785 sprintf(res, " %s", PGNResult(gameInfo.result));
9787 sprintf(res, " {%s} %s",
9788 gameInfo.resultDetails, PGNResult(gameInfo.result));
9795 if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
9796 /* Output to ICC if IcsAnalyze */
9797 if(appData.icsAnalyze) IcsAnalyzeOutPut(&first, TRUE);
9798 DisplayMessage(res, cpThinkOutput);
9800 sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,
9801 WhiteOnMove(moveNumber) ? " " : ".. ",
9802 parseList[moveNumber], res);
9803 if(appData.icsAnalyze) IcsAnalyzeOutPut(&first, FALSE);
9804 DisplayMessage(message, cpThinkOutput);
9807 /* Send Engine output to ICS */
9808 if (appData.icsActive && appData.ButtonSendOutPutToICS
9809 && appData.AnalysisWindow) {
9811 /* only send on move */
9813 case IcsPlayingWhite:
9814 if(WhiteOnMove(currentMove)) i = 1;
9816 case IcsPlayingBlack:
9817 if(!WhiteOnMove(currentMove)) i = 1;
9821 if (i == 0 || programStats.depth <= 1 || programStats.depth >= 30 ||
9822 programStats.GUI_time <= 3) return;
9824 if (strcmp(programStats.movelist, last) == 0) return;
9825 strcpy(last, programStats.movelist);
9827 switch (appData.SendOutPutToICS) {
9830 sprintf(res, "whisper time: %d sec %+.2f/%d %s \n", programStats.GUI_time,
9831 (((float)programStats.score)/100.0), programStats.depth, programStats.movelist);
9835 sprintf(res, "kibitz time: %d sec %+.2f/%d %s \n", programStats.GUI_time,
9836 (((float)programStats.score)/100.0), programStats.depth, programStats.movelist);
9848 IcsAnalyzeOutPut(cps, endThink)
9849 ChessProgramState *cps;
9852 char cpIcsThinkOutput[MSG_SIZ], icsOutput[32], str[8192];
9853 static char last[MSG_SIZ];
9855 int diff, s, h, m, wait, info;
9856 static int lastGame, lastMove, firstRun, say, lastdepth, sec;
9859 if (appData.icsAnalyzeOutPut == 4 || !appData.icsAnalyze ||
9860 gameMode != IcsObserving) return;
9862 if (appData.icsSmartQueue == 0) {
9868 currentTime = programStats.time;
9870 /* calculate some infos */
9871 s = currentTime / 100;
9872 sec = s; /* save complete sec */
9877 if (programStats.time == 0) programStats.time = 1;
9878 nps = ((((double)programStats.nodes) /
9879 (((double)programStats.time)/100.0)) /1000);
9881 if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
9882 DisplayFatalError("ICS-Analysis: Fatal array error - Please make a bugreport.", 0, 1);
9886 /* init ICS gamequeue - safty */
9887 /* normaly we do that with read_from_ics() */
9888 if (icsQueue[ics_gamenum].killPv == 0) {
9889 icsQueue[ics_gamenum].move = currentMove;
9890 icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
9891 icsQueue[ics_gamenum].counter = 0;
9892 icsQueue[ics_gamenum].lastpv[0] = NULLCHAR;
9895 /* for chess.fm on ICC we must copy player names each move */
9896 strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
9897 strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
9899 if (appData.smartQueue) {
9900 if (lastGame == 0) lastGame = ics_gamenum;
9901 if (lastMove == 0) lastMove = currentMove;
9902 if (ics_gamenum != lastGame || lastMove != currentMove) {
9904 icsQueue[ics_gamenum].counter = 0;
9908 /* checking other games - only for live broadcasting */
9909 /* init on a new move */
9910 if (appData.icsAnalyzeOutPut != 4) {
9911 if (icsQueue[ics_gamenum].CurrentMove != currentMove) {
9912 icsQueue[ics_gamenum].CurrentMove = currentMove;
9913 icsQueue[ics_gamenum].flag = 0; /* we have not send */
9914 icsQueue[ics_gamenum].counter = 0;
9918 strcpy(cpIcsThinkOutput, thinkOutput);
9919 if (strchr(cpIcsThinkOutput, '\n')) {
9920 *strchr(cpIcsThinkOutput, '\n') = NULLCHAR;
9922 if (cpIcsThinkOutput[0] == NULLCHAR) return;
9924 /* Special ICC fetures - works only with param /icc */
9925 if (ics_type == ICS_ICC && appData.ICC_feature == TRUE &&
9926 appData.userVersion == FALSE) {
9927 if (appData.icsAnalyzeOutPut == 3) {
9928 /* If we start a new broadcast analyse we inform user */
9929 if (icsQueue[ics_gamenum].lastpv[0] == NULLCHAR) {
9930 sprintf(str, "whisperto %d Hello from Winboard-DM \n", ics_gamenum);
9934 /* If long tourney games >= 120 0 */
9935 if (appData.icsSmartQueue == 0) {
9936 switch (currentMove) {
9937 case 20: case 40: case 60: case 80: case 100: case 135: case 160:
9938 if (icsQueue[ics_gamenum].event == 0) {
9939 sprintf(str, "whisperto %d Hello, i'm an analysis engine.\n", ics_gamenum);
9941 sprintf(str, "whisperto %d If you want my last analysis of this game, just type \"tell %s showinfo %d\"\n",
9942 ics_gamenum, ics_handle, ics_gamenum);
9944 if (strcmp(appData.icsTells, "211") == 0) {
9945 sprintf(str, "whisperto %d and/or type \"+ch 211\" for a ICC channel analysis\n", ics_gamenum);
9948 icsQueue[ics_gamenum].event = 1;
9950 /* channel 165 on ICC */
9951 if (currentMove == 20 && chessfm == 0) {
9954 } else if (currentMove == 40 && (chessfm == 1 || chessfm == 0)) {
9957 } else if (currentMove == 60 && (chessfm == 2 || chessfm == 0)) {
9960 } else if (currentMove == 80 && (chessfm == 3 || chessfm == 0)) {
9963 } else if (currentMove == 100 && (chessfm == 4 || chessfm == 0)) {
9971 sprintf(str, "tell 165 Hi, i'm analyzing live broadcast games. \"tell %s showgames \" to see which games are currently analyzed.\n", ics_handle);
9973 sprintf(str, "tell 165 \"finger %s\" and \"finger live\" for more details \n", ics_handle);
9979 icsQueue[ics_gamenum].event = 0;
9982 switch (currentMove) {
9983 case 20: case 60: case 100:
9984 if (icsQueue[ics_gamenum].event == 0) {
9985 sprintf(str, "whisperto %d Hello, i'm an analysis engine.\n", ics_gamenum);
9987 sprintf(str, "whisperto %d If you want my last analysis of this game, just type \"tell %s showinfo %d\"\n",
9988 ics_gamenum, ics_handle, ics_gamenum);
9990 if (strcmp(appData.icsTells, "211") == 0) {
9991 sprintf(str, "whisperto %d and/or type \"+ch 211\" for a ICC channel analysis\n", ics_gamenum);
9994 icsQueue[ics_gamenum].event = 1;
9998 icsQueue[ics_gamenum].event = 0;
10002 /* save last game pv for user request */
10003 sprintf(icsQueue[ics_gamenum].lastpv, "%s time: %02d:%02d min nps: %dK \n", cpIcsThinkOutput, m, s, (int)nps);
10007 switch(appData.icsAnalyzeOutPut) {
10009 switch (ics_type) {
10010 case ICS_ICC: strcpy(icsOutput, "whisperto");
10012 case ICS_FICS: strcpy(icsOutput, "xwhisper");
10017 DisplayFatalError("ICS-Analysis: Sorry, this Serverprotocoll is not supported",0,2);
10022 switch (ics_type) {
10023 case ICS_ICC: strcpy(icsOutput, "kibitzto");
10025 case ICS_FICS: strcpy(icsOutput, "xkibitz");
10030 DisplayFatalError("ICS-Analysis: Sorry, this Serverprotocoll is not supported",0,2);
10035 strcpy(icsOutput, "tell");
10038 /* could't happen */
10042 /* Drop double output - example Crafty */
10043 if (strcmp(cpIcsThinkOutput, last) == 0) {
10044 /* Check again if we can switch game - that will be faster as we wait for next ply */
10045 if (icsQueue[ics_gamenum].flag == 1) {
10046 if (SwitchGames() == 0) return;
10048 /* special crafty feature - let interation pass */
10049 if (strncmp(cps->tidy, "Crafty", 6) != 0) return;
10052 /* if skip high/fail low active we only send every complete interation from engine */
10053 if (lastdepth == 0 || lastdepth > programStats.depth) lastdepth = programStats.depth;
10056 /* Drop all about 25 plys - could be egtb output */
10057 if (programStats.depth > 25) {
10058 if (appData.icsAnalyzeOutPut == 3) {
10059 /* We don't stay longer on this game - just try to switching other games */
10060 icsQueue[ics_gamenum].flag = 1; /* done */
10061 icsQueue[ics_gamenum].MessageMove = currentMove;
10062 if (SwitchGames() == 0) return;
10067 /* Drop score above 10.0 - many engines send to many lines if mate */
10068 if ((programStats.score / 100) > 10 || (programStats.score / 100) < -10) {
10069 if (appData.icsAnalyzeOutPut == 3) {
10070 /* We don't stay longer on this game - just switching to other games */
10071 icsQueue[ics_gamenum].flag = 1; /* done */
10072 icsQueue[ics_gamenum].MessageMove = currentMove;
10073 if (SwitchGames() == 0) return;
10077 /* tell ICC fail high move if possible */
10078 if (ics_type == ICS_ICC && appData.ICC_feature == TRUE && appData.userVersion == FALSE &&
10079 appData.icsAnalyzeOutPut == 3 && programStats.depth >= icsQueue[ics_gamenum].killPv) {
10080 //if (strchr(cpIcsThinkOutput, '!!') != NULL && strncmp(cps->tidy, "Yace" , 4) != 0) {
10081 //sprintf(str, "whisperto %d Analysis: Something happened - Side on move maybe looks better as before. \n", ics_gamenum);
10083 //} else if (strchr(cpIcsThinkOutput, '(++)') != NULL && strncmp(cps->tidy, "Yace" , 4) == 0) {
10084 //sprintf(str, "whisperto %d Analysis: Something happened - Side on move maybe looks better as before. \n", ics_gamenum);
10089 /* Drop fail high/low moves from engines */
10090 if (appData.windowMove) {
10091 if (strchr(cpIcsThinkOutput, '?') != NULL || strchr(cpIcsThinkOutput, '!') != NULL ||
10092 strchr(cpIcsThinkOutput, 't') != NULL || strchr(cpIcsThinkOutput, 'u') != NULL) {
10093 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: Drop move with string \n");
10098 /* smart Queue fast down handling */
10099 if (sec > wait && programStats.depth <= (icsQueue[ics_gamenum].killPv - 1) &&
10100 appData.smartQueue) {
10101 if (icsQueue[ics_gamenum].killPv > 8) {
10102 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10103 if (lastMove == currentMove) icsQueue[ics_gamenum].killPv--;
10104 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv-- \n",
10105 icsQueue[ics_gamenum].killPv);
10108 /* smart Queue fast up handling */
10109 if (sec < 30 && programStats.depth >= icsQueue[ics_gamenum].killPv &&
10110 appData.smartQueue) {
10111 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10112 if (lastMove == currentMove ) {
10113 /* Crafty send 2x string so this is a importent reason */
10114 if (strncmp(cps->tidy, "Crafty", 6) == 0 &&
10115 strcmp(cpIcsThinkOutput, last) != 0) {
10116 icsQueue[ics_gamenum].killPv++;
10117 } else if (strncmp(cps->tidy, "Crafty", 6) != 0) {
10118 /* other programs */
10119 icsQueue[ics_gamenum].killPv++;
10122 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv++ \n",
10123 icsQueue[ics_gamenum].killPv);
10125 /* smart Queue handling */
10126 if (appData.smartQueue && programStats.depth >= icsQueue[ics_gamenum].killPv) {
10127 diff = ((int)currentTime / 100) - ((int)icsQueue[ics_gamenum].time / 100);
10128 if ((diff < 7 && diff != 0) && icsQueue[ics_gamenum].counter > 3 &&
10129 icsQueue[ics_gamenum].move <= currentMove) {
10130 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10131 appData.icsEngineKillPV = TRUE;
10132 icsQueue[ics_gamenum].killPv++;
10133 icsQueue[ics_gamenum].move = currentMove + 1;
10134 icsQueue[ics_gamenum].counter = 0;
10135 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv++ \n",
10136 icsQueue[ics_gamenum].killPv);
10138 } else if (diff > wait && icsQueue[ics_gamenum].counter < 2 &&
10139 icsQueue[ics_gamenum].move <= currentMove && firstRun == 1 &&
10140 lastMove != currentMove) {
10141 if(icsQueue[ics_gamenum].killPv > 8) {
10142 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10143 if (lastMove == currentMove) icsQueue[ics_gamenum].killPv--;
10145 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv-- \n",
10146 icsQueue[ics_gamenum].killPv);
10148 icsQueue[ics_gamenum].move = currentMove + 1;
10149 icsQueue[ics_gamenum].counter = 0;
10151 icsQueue[ics_gamenum].counter++;
10152 lastGame = ics_gamenum;
10153 lastMove = currentMove;
10156 icsQueue[ics_gamenum].time = currentTime;
10158 /* Check other games part 2 */
10159 if (appData.icsAnalyzeOutPut != 4) {
10160 /* if no other games avaible we coming back */
10161 if (icsQueue[ics_gamenum].flag == 1) {
10162 if (SwitchGames() == 0) return;
10167 if (programStats.line_is_book != 1 || programStats.depth != 0) {
10168 if (appData.icsEngineKillPV || appData.smartQueue) {
10169 if(programStats.depth < icsQueue[ics_gamenum].killPv ||
10170 programStats.depth < appData.icsKillPVs && programStats.depth != 0) return;
10174 /* send information */
10175 if (programStats.line_is_book == 1 && programStats.depth < 2 ||
10176 programStats.depth == 0) {
10177 if (appData.icsAnalyzeOutPut != 3) {
10178 /* We must whisperto <gamenumber> if we follow someone */
10179 /* on FCIS we need xwhisper */
10180 sprintf(str, "%s %d %s: Book depth=%s \n", icsOutput, ics_gamenum,
10181 cps->tidy, cpIcsThinkOutput);
10183 if (currentMove > -1) {
10184 sprintf(str, "%s %s %s: (%s/%s) Book %d.%s depth=%s (game \"observe %d\")\n", icsOutput,
10185 appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10186 currentMove / 2 + 1, WhiteOnMove(currentMove) ? " " : "... ",
10187 cpIcsThinkOutput, ics_gamenum);
10189 icsQueue[ics_gamenum].MessageMove = currentMove;
10191 icsQueue[ics_gamenum].flag = 1; /* we send */
10195 /* interation check */
10196 /* special crafty interation */
10197 if (appData.windowMove && strncmp(cps->tidy, "Crafty" , 6) == 0 &&
10198 strcmp(cpIcsThinkOutput, last) != 0) {
10199 strcpy(last,cpIcsThinkOutput);
10202 /* normal interation check */
10203 if (appData.windowMove && programStats.depth <= lastdepth) return;
10205 if (appData.icsAnalyzeOutPut != 3) {
10206 if (endThink == TRUE) {
10207 sprintf(str, "%s %d %s: first move (Depth=%s time: %02d:%02d min nps: %dK \n", icsOutput,
10208 ics_gamenum, cps->tidy, cpIcsThinkOutput, m, s, (int)nps);
10210 sprintf(str, "%s %d %s: Depth=%s time: %02d:%02d min nps: %dK \n", icsOutput, ics_gamenum,
10211 cps->tidy, cpIcsThinkOutput, m, s, (int)nps);
10213 icsQueue[ics_gamenum].flag = 1; /* we send */
10216 if (endThink == TRUE) {
10217 sprintf(str, "%s %s %s: (%s/%s) first move (Depth=%s time: %02d:%02d min nps: %dK (game \"observe %d\")\n", icsOutput,
10218 appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10219 cpIcsThinkOutput, m, s, (int)nps, ics_gamenum);
10221 sprintf(str, "%s %s %s: (%s/%s) Depth=%s time: %02d:%02d min nps: %dK (game \"observe %d\")\n", icsOutput,
10222 appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10223 cpIcsThinkOutput, m, s, (int)nps, ics_gamenum);
10225 icsQueue[ics_gamenum].flag = 1; /* we send */
10226 icsQueue[ics_gamenum].MessageMove = currentMove;
10230 strcpy(last,cpIcsThinkOutput);
10231 lastdepth = programStats.depth;
10233 /* At least we search for a open game once more */
10234 if (icsQueue[ics_gamenum].flag == 1) {
10235 if (SwitchGames() == 0 && appData.debugMode) {
10236 fprintf(debugFP, "Switch was at end of produce \n");
10242 /* Reset icsGameQueue if game ends or user abort */
10244 ResetIcsQueue(gamenumber)
10246 icsQueue[gamenumber].black[0] = NULLCHAR;
10247 icsQueue[gamenumber].white[0] = NULLCHAR;
10248 icsQueue[gamenumber].counter = 0;
10249 icsQueue[gamenumber].currentGame = 0;
10250 icsQueue[gamenumber].CurrentMove = 0;
10251 icsQueue[gamenumber].event = 0;
10252 icsQueue[gamenumber].killPv = 0;
10253 icsQueue[gamenumber].lastpv[0] = NULLCHAR;
10254 icsQueue[gamenumber].MessageMove = 0;
10255 icsQueue[gamenumber].move = 0;
10256 icsQueue[gamenumber].time = 0;
10259 /* GUI -> engine */
10261 GuiCommand(command, param)
10263 int param; /* reserved */
10267 switch (gameMode) {
10268 case MachinePlaysWhite:
10269 case MachinePlaysBlack:
10270 SendToProgram("?\n", &first);
10275 switch (gameMode) {
10278 if (first.analyzing == TRUE) {
10279 /* don't send stat */
10280 appData.engineStatLine = TRUE;
10281 SendToProgram("exit\n", &first);
10282 SendToProgram("force\n", &first);
10283 first.analyzing = FALSE;
10284 PeriodicUpdatesEvent(FALSE);
10285 DisplayAnalysis(6,0);
10287 /* safty with force */
10289 appData.engineStatLine = FALSE;
10290 SendToProgram("analyze\n", &first);
10291 first.analyzing = TRUE;
10292 PeriodicUpdatesEvent(TRUE);
10295 /* a lot of engine makes probs if we switch from
10296 * normal Mode to analyzeMode :( */
10297 case MachinePlaysWhite:
10298 case MachinePlaysBlack:
10300 if (first.maybeThinking == TRUE) {
10301 /* don't send stat */
10302 appData.engineStatLine = TRUE;
10304 first.maybeThinking = FALSE;
10306 /* get latest state */
10307 if (supportStat == 1) appData.engineStatLine = FALSE;
10308 if (WhiteOnMove(currentMove)) {
10309 MachineWhiteEvent();
10311 MachineBlackEvent();
10315 case BeginningOfGame:
10316 MachineWhiteEvent();
10328 while (*str && isspace(*str)) ++str;
10329 while (*str && !isspace(*str)) ++str;
10330 if (!*str) return 1;
10331 while (*str && isspace(*str)) ++str;
10332 if (!*str) return 1;
10337 DisplayAnalysis(newState, pv)
10338 int newState; /* int newState:
10341 * --- WB bootup (start) ---
10342 * 5 = WB startup if EngineRoom true: popup
10343 * --- GUI Action ---
10346 * 7 = Stop button */
10349 int pv; /* only pv comes 1 = TRUE */
10351 /* newState True = call from timer - see dirty hack */
10357 static char buf6[64]; /* static need if pv only */
10358 static char lastline[1024];
10359 static int lastdepth, last_icsgamenum;
10361 char currentline[1024];
10365 static char *xtra[] = { "", " (--)", " (++)" };
10366 static int oldGameMode; /* on the fly switch gameMode ? */
10367 static int StatLine_autodetect; /* Autodetect only first move out of book */
10368 /* 0 = should detect
10372 int state; /* Using for ponder, opponent time action and
10373 * using for color engine name
10374 * --- move statment (adv color)---
10375 * 0 = we have the move and stanard color
10376 * 1 = opponent have the move and opponent color
10377 * --- only color statement---
10380 * --- special things ----
10381 * 4 = Two engines - we don't support that
10383 static long timer; /* hack without a stat line */
10384 static int move; /* detect new move */
10386 strcpy(currentline, programStats.movelist);
10387 currentdepth = programStats.depth;
10389 if (appData.noChessProgram) return;
10390 /* if goes up to the end of this function */
10391 if (appData.icsAnalyzeWindow || appData.AnalysisWindow
10392 || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
10393 /* stop analysis */
10394 if (newState == 6) timer = 0;
10396 /* use for no stat support */
10397 /* If user switch on the fly gameMode we must know that */
10398 if (move != currentMove || oldGameMode != gameMode) {
10400 if (oldGameMode != gameMode) {
10401 /* clear pv screen */
10402 sprintf(buf, "changing mode...");
10403 sprintf(buf2, "wait");
10404 sprintf(buf3, "0kN/s");
10405 sprintf(buf4, "Nodes=0");
10406 sprintf(buf5, "0.00");
10407 sprintf(buf6, "wait...");
10408 sprintf(buf7, "------------ ");
10409 AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, 0);
10410 oldGameMode = gameMode;
10412 if (move > currentMove) {
10413 supportStat = 0; /* new Game ? */
10414 StatLine_autodetect = 0;
10416 move = currentMove;
10420 /* ICS Analyze more then one game - check if new game */
10421 if (appData.icsActive && ics_gamenum != last_icsgamenum) {
10423 last_icsgamenum = ics_gamenum;
10426 if (gameMode == EditGame) timer = 0; /* no clock if edit */
10428 /* hope a lot of programmer will add a stat line - easy to make */
10429 /* at the moment a dirty(!!) hack with time_id */
10430 /* calculate some infos */
10431 if (programStats.time == 0) programStats.time = 1;
10432 if (newState && gameMode != EditGame) timer++;
10440 programStats.GUI_time = timer;
10442 /* Who is on move or which mode ?*/
10443 /* Need for colorize EngineName */
10444 /* Check gameMode for Stat support */
10445 switch (gameMode) {
10446 case MachinePlaysWhite:
10447 case IcsPlayingWhite:
10448 if (WhiteOnMove(currentMove)) state = 0;
10449 if (!WhiteOnMove(currentMove)) state = 1;
10451 case MachinePlaysBlack:
10452 case IcsPlayingBlack:
10453 if (WhiteOnMove(currentMove)) state = 1;
10454 if (!WhiteOnMove(currentMove)) state = 0;
10458 appData.engineStatLine = FALSE;
10459 StatLine_autodetect = 0; /* switch back to play is a prob */
10465 /* not really a support for engine war - wait for war room */
10466 case TwoMachinesPlay:
10470 appData.engineStatLine = FALSE;
10471 state = 0; /* green */
10473 nps = ((((double)programStats.nodes) /
10474 (((double)programStats.time)/100.0)) /1000);
10476 if (programStats.nodes > 0 && pv == 1) {
10477 if (strcmp(lastline, currentline) != 0) {
10478 if (prefixHint == TRUE && programStats.ponderMove[0] != NULLCHAR) {
10479 if (programStats.score == 0 || programStats.score > 0) {
10480 /* format arial set */
10481 sprintf(buf, " d->%02d %+.2f (%s) %s", programStats.depth,
10482 (((float)programStats.score)/100.0),
10483 programStats.ponderMove, currentline);
10484 } else if (programStats.score < 0) {
10485 sprintf(buf, " d->%02d %+.2f (%s) %s", programStats.depth,
10486 (((float)programStats.score)/100.0),
10487 programStats.ponderMove, currentline);
10489 strcpy(lastline, currentline);
10491 /* format arial set */
10492 if (programStats.score == 0 || programStats.score > 0) {
10493 sprintf(buf, " d->%02d %+.2f %s", programStats.depth,
10494 (((float)programStats.score)/100.0), currentline);
10495 } else if (programStats.score < 0) {
10496 sprintf(buf, " d->%02d %+.2f %s", programStats.depth,
10497 (((float)programStats.score)/100.0), currentline);
10499 strcpy(lastline, currentline);
10501 lastdepth = currentdepth;
10503 if (lastdepth < currentdepth) {
10504 if (prefixHint == TRUE && programStats.ponderMove[0] != NULLCHAR) {
10505 if (programStats.score == 0 || programStats.score > 0) {
10506 /* format arial set */
10507 sprintf(buf, " d->%02d %+.2f (%s) %s", programStats.depth,
10508 (((float)programStats.score)/100.0),
10509 programStats.ponderMove, currentline);
10510 } else if (programStats.score < 0) {
10511 sprintf(buf, " d->%02d %+.2f (%s) %s", programStats.depth,
10512 (((float)programStats.score)/100.0),
10513 programStats.ponderMove, currentline);
10516 /* format arial set */
10517 if (programStats.score == 0 || programStats.score > 0) {
10518 sprintf(buf, " d->%02d %+.2f %s", programStats.depth,
10519 (((float)programStats.score)/100.0), currentline);
10520 } else if (programStats.score < 0) {
10521 sprintf(buf, " d->%02d %+.2f %s", programStats.depth,
10522 (((float)programStats.score)/100.0), currentline);
10525 lastdepth = currentdepth;
10534 /* remove Bookline - never works correct */
10535 /* dirty hack with wb2 proto :((*/
10536 if (programStats.depth == 0 || programStats.depth == 1) {
10537 sprintf(buf2, "Book");
10538 sprintf(buf3, "0kN/s");
10539 sprintf(buf4, "Nodes=0");
10540 sprintf(buf5, "0.00");
10541 sprintf(buf6, "wait...");
10543 sprintf(buf2, "Depth=%d", programStats.depth);
10544 sprintf(buf3, "%dkN/s", (int)nps);
10545 sprintf(buf4, "Nodes=%lu", programStats.nodes);
10546 sprintf(buf5, "%+.2f", (((float)programStats.score)/100.0));
10548 sprintf(buf7, "%02d:%02d:%02d ", h, m, s);
10550 if (pv == 1 && strcmp(lastline, currentline) != 0) {
10551 AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10553 } else if (pv == 1 && strcmp(lastline, currentline) == 0 &&
10554 lastdepth < currentdepth) {
10555 AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10559 /* remove Bookline - never works correct */
10560 if (programStats.depth == 0 || programStats.depth == 1) {
10561 sprintf(buf6, "wait...");
10563 if (supportStat == 0 && StatLine_autodetect == 0 &&
10565 /* we should disable sending "." now and tell GUI */
10566 /* Do that here only after first move out of book */
10567 appData.engineStatLine = TRUE; /* enable checkbox */
10568 StatLine_autodetect = 1; /* done */
10570 if (programStats.depth > 1) {
10571 if (supportStat == 0) {
10572 sprintf(buf6, "no support");
10574 diff = (programStats.nr_moves-programStats.moves_left);
10576 sprintf(buf6, "Error!");
10578 if (programStats.moves_left > 0) {
10579 if (programStats.move_name[0] != NULLCHAR) {
10580 sprintf(buf6, "%d/%d %s", diff,
10581 programStats.nr_moves, programStats.move_name);
10583 /* without move. e.g. crafty */
10584 sprintf(buf6, "%d/%d", diff,
10585 programStats.nr_moves, only_one_move(programStats.movelist)?
10586 xtra[programStats.got_fail] : "");
10593 AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10599 DisplayComment(moveNumber, text)
10603 char title[MSG_SIZ];
10605 if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
10606 strcpy(title, "Comment");
10608 sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
10609 WhiteOnMove(moveNumber) ? " " : ".. ",
10610 parseList[moveNumber]);
10613 CommentPopUp(title, text);
10616 /* This routine sends a ^C interrupt to gnuchess, to awaken it if it
10617 * might be busy thinking or pondering. It can be omitted if your
10618 * gnuchess is configured to stop thinking immediately on any user
10619 * input. However, that gnuchess feature depends on the FIONREAD
10620 * ioctl, which does not work properly on some flavors of Unix.
10624 ChessProgramState *cps;
10627 if (!cps->useSigint) return;
10628 if (appData.noChessProgram || (cps->pr == NoProc)) return;
10629 switch (gameMode) {
10630 case MachinePlaysWhite:
10631 case MachinePlaysBlack:
10632 case TwoMachinesPlay:
10633 case IcsPlayingWhite:
10634 case IcsPlayingBlack:
10637 /* Skip if we know it isn't thinking */
10638 if (!cps->maybeThinking) return;
10639 if (appData.debugMode)
10640 fprintf(debugFP, "Interrupting %s\n", cps->which);
10641 InterruptChildProcess(cps->pr);
10642 cps->maybeThinking = FALSE;
10647 #endif /*ATTENTION*/
10653 if (whiteTimeRemaining <= 0) {
10656 if (appData.icsActive) {
10657 if (appData.autoCallFlag &&
10658 gameMode == IcsPlayingBlack && !blackFlag) {
10659 SendToICS(ics_prefix);
10660 SendToICS("flag\n");
10664 DisplayTitle("Both flags fell");
10666 DisplayTitle("White's flag fell");
10667 if (appData.autoCallFlag) {
10668 GameEnds(BlackWins, "Black wins on time", GE_XBOARD);
10675 if (blackTimeRemaining <= 0) {
10678 if (appData.icsActive) {
10679 if (appData.autoCallFlag &&
10680 gameMode == IcsPlayingWhite && !whiteFlag) {
10681 SendToICS(ics_prefix);
10682 SendToICS("flag\n");
10686 DisplayTitle("Both flags fell");
10688 DisplayTitle("Black's flag fell");
10689 if (appData.autoCallFlag) {
10690 GameEnds(WhiteWins, "White wins on time", GE_XBOARD);
10703 if (!appData.clockMode || appData.icsActive ||
10704 gameMode == PlayFromGameFile || forwardMostMove == 0) return;
10706 if (timeIncrement >= 0) {
10707 if (WhiteOnMove(forwardMostMove)) {
10708 blackTimeRemaining += timeIncrement;
10710 whiteTimeRemaining += timeIncrement;
10714 * add time to clocks when time control is achieved
10716 if (movesPerSession) {
10717 switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
10719 /* White made time control */
10720 whiteTimeRemaining += timeControl;
10723 /* Black made time control */
10724 blackTimeRemaining += timeControl;
10733 DisplayBothClocks()
10735 int wom = gameMode == EditPosition ?
10736 !blackPlaysFirst : WhiteOnMove(currentMove);
10737 DisplayWhiteClock(whiteTimeRemaining, wom);
10738 DisplayBlackClock(blackTimeRemaining, !wom);
10742 /* Timekeeping seems to be a portability nightmare. I think everyone
10743 has ftime(), but I'm really not sure, so I'm including some ifdefs
10744 to use other calls if you don't. Clocks will be less accurate if
10745 you have neither ftime nor gettimeofday.
10748 /* Get the current time as a TimeMark */
10753 #if HAVE_GETTIMEOFDAY
10755 struct timeval timeVal;
10756 struct timezone timeZone;
10758 gettimeofday(&timeVal, &timeZone);
10759 tm->sec = (long) timeVal.tv_sec;
10760 tm->ms = (int) (timeVal.tv_usec / 1000L);
10762 #else /*!HAVE_GETTIMEOFDAY*/
10765 #include <sys/timeb.h>
10766 struct timeb timeB;
10769 tm->sec = (long) timeB.time;
10770 tm->ms = (int) timeB.millitm;
10772 #else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/
10773 tm->sec = (long) time(NULL);
10779 /* Return the difference in milliseconds between two
10780 time marks. We assume the difference will fit in a long!
10783 SubtractTimeMarks(tm2, tm1)
10784 TimeMark *tm2, *tm1;
10786 return 1000L*(tm2->sec - tm1->sec) +
10787 (long) (tm2->ms - tm1->ms);
10792 * Code to manage the game clocks.
10794 * In tournament play, black starts the clock and then white makes a move.
10795 * We give the human user a slight advantage if he is playing white---the
10796 * clocks don't run until he makes his first move, so it takes zero time.
10797 * Also, we don't account for network lag, so we could get out of sync
10798 * with GNU Chess's clock -- but then, referees are always right.
10801 static TimeMark tickStartTM;
10802 static long intendedTickLength;
10805 NextTickLength(timeRemaining)
10806 long timeRemaining;
10808 long nominalTickLength, nextTickLength;
10810 if (timeRemaining > 0L && timeRemaining <= 10000L)
10811 nominalTickLength = 100L;
10813 nominalTickLength = 1000L;
10814 nextTickLength = timeRemaining % nominalTickLength;
10815 if (nextTickLength <= 0) nextTickLength += nominalTickLength;
10817 return nextTickLength;
10820 /* Stop clocks and reset to a fresh time control */
10824 (void) StopClockTimer();
10825 if (appData.icsActive) {
10826 whiteTimeRemaining = blackTimeRemaining = 0;
10828 whiteTimeRemaining = blackTimeRemaining = timeControl;
10830 if (whiteFlag || blackFlag) {
10832 whiteFlag = blackFlag = FALSE;
10834 DisplayBothClocks();
10837 #define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
10839 /* Decrement running clock by amount of time that has passed */
10843 long timeRemaining;
10844 long lastTickLength, fudge;
10847 if (!appData.clockMode) return;
10848 if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;
10852 lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10854 /* Fudge if we woke up a little too soon */
10855 fudge = intendedTickLength - lastTickLength;
10856 if (fudge < 0 || fudge > FUDGE) fudge = 0;
10858 if (WhiteOnMove(forwardMostMove)) {
10859 timeRemaining = whiteTimeRemaining -= lastTickLength;
10860 DisplayWhiteClock(whiteTimeRemaining - fudge,
10861 WhiteOnMove(currentMove));
10863 timeRemaining = blackTimeRemaining -= lastTickLength;
10864 DisplayBlackClock(blackTimeRemaining - fudge,
10865 !WhiteOnMove(currentMove));
10868 if (CheckFlags()) return;
10871 intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
10872 StartClockTimer(intendedTickLength);
10874 /* if the time remaining has fallen below the alarm threshold, sound the
10875 * alarm. if the alarm has sounded and (due to a takeback or time control
10876 * with increment) the time remaining has increased to a level above the
10877 * threshold, reset the alarm so it can sound again.
10880 if (appData.icsActive && appData.icsAlarm) {
10882 /* make sure we are dealing with the user's clock */
10883 if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||
10884 ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))
10887 if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
10888 alarmSounded = FALSE;
10889 } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
10891 alarmSounded = TRUE;
10897 /* A player has just moved, so stop the previously running
10898 clock and (if in clock mode) start the other one.
10899 We redisplay both clocks in case we're in ICS mode, because
10900 ICS gives us an update to both clocks after every move.
10901 Note that this routine is called *after* forwardMostMove
10902 is updated, so the last fractional tick must be subtracted
10903 from the color that is *not* on move now.
10908 long lastTickLength;
10910 int flagged = FALSE;
10914 if (StopClockTimer() && appData.clockMode) {
10915 lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10916 if (WhiteOnMove(forwardMostMove)) {
10917 blackTimeRemaining -= lastTickLength;
10919 whiteTimeRemaining -= lastTickLength;
10921 flagged = CheckFlags();
10923 CheckTimeControl();
10925 if (flagged || !appData.clockMode) return;
10927 switch (gameMode) {
10928 case MachinePlaysBlack:
10929 case MachinePlaysWhite:
10930 case BeginningOfGame:
10931 if (pausing) return;
10935 case PlayFromGameFile:
10944 intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
10945 whiteTimeRemaining : blackTimeRemaining);
10946 StartClockTimer(intendedTickLength);
10950 /* Stop both clocks */
10954 long lastTickLength;
10957 if (!StopClockTimer()) return;
10958 if (!appData.clockMode) return;
10962 lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10963 if (WhiteOnMove(forwardMostMove)) {
10964 whiteTimeRemaining -= lastTickLength;
10965 DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
10967 blackTimeRemaining -= lastTickLength;
10968 DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
10973 /* Start clock of player on move. Time may have been reset, so
10974 if clock is already running, stop and restart it. */
10978 (void) StopClockTimer(); /* in case it was running already */
10979 DisplayBothClocks();
10980 if (CheckFlags()) return;
10982 if (!appData.clockMode) return;
10983 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;
10985 GetTimeMark(&tickStartTM);
10986 intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
10987 whiteTimeRemaining : blackTimeRemaining);
10988 StartClockTimer(intendedTickLength);
10995 long second, minute, hour, day;
10997 static char buf[32];
10999 if (ms > 0 && ms <= 9900) {
11000 /* convert milliseconds to tenths, rounding up */
11001 double tenths = floor( ((double)(ms + 99L)) / 100.00 );
11003 sprintf(buf, " %03.1f ", tenths/10.0);
11007 /* convert milliseconds to seconds, rounding up */
11008 /* use floating point to avoid strangeness of integer division
11009 with negative dividends on many machines */
11010 second = (long) floor(((double) (ms + 999L)) / 1000.0);
11017 day = second / (60 * 60 * 24);
11018 second = second % (60 * 60 * 24);
11019 hour = second / (60 * 60);
11020 second = second % (60 * 60);
11021 minute = second / 60;
11022 second = second % 60;
11025 sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",
11026 sign, day, hour, minute, second);
11028 sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
11030 sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);
11037 * This is necessary because some C libraries aren't ANSI C compliant yet.
11040 StrStr(string, match)
11041 char *string, *match;
11045 length = strlen(match);
11047 for (i = strlen(string) - length; i >= 0; i--, string++)
11048 if (!strncmp(match, string, length))
11055 StrCaseStr(string, match)
11056 char *string, *match;
11060 length = strlen(match);
11062 for (i = strlen(string) - length; i >= 0; i--, string++) {
11063 for (j = 0; j < length; j++) {
11064 if (ToLower(match[j]) != ToLower(string[j]))
11067 if (j == length) return string;
11081 c1 = ToLower(*s1++);
11082 c2 = ToLower(*s2++);
11083 if (c1 > c2) return 1;
11084 if (c1 < c2) return -1;
11085 if (c1 == NULLCHAR) return 0;
11094 return isupper(c) ? tolower(c) : c;
11102 return islower(c) ? toupper(c) : c;
11104 #endif /* !_amigados */
11112 if ((ret = (char *) malloc(strlen(s) + 1))) {
11119 StrSavePtr(s, savePtr)
11120 char *s, **savePtr;
11125 if ((*savePtr = (char *) malloc(strlen(s) + 1))) {
11126 strcpy(*savePtr, s);
11138 clock = time((time_t *)NULL);
11139 tm = localtime(&clock);
11140 sprintf(buf, "%04d.%02d.%02d",
11141 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
11142 return StrSave(buf);
11147 PositionToFEN(move)
11150 int i, j, fromX, fromY, toX, toY;
11156 whiteToPlay = (gameMode == EditPosition) ?
11157 !blackPlaysFirst : (move % 2 == 0);
11160 /* Piece placement data */
11161 for (i = BOARD_SIZE - 1; i >= 0; i--) {
11163 for (j = 0; j < BOARD_SIZE; j++) {
11164 if (boards[move][i][j] == EmptySquare) {
11167 if (emptycount > 0) {
11168 *p++ = '0' + emptycount;
11171 *p++ = PieceToChar(boards[move][i][j]);
11174 if (emptycount > 0) {
11175 *p++ = '0' + emptycount;
11183 *p++ = whiteToPlay ? 'w' : 'b';
11186 /* !!We don't keep track of castling availability, so fake it */
11188 if (boards[move][0][4] == WhiteKing) {
11189 if (boards[move][0][7] == WhiteRook) *p++ = 'K';
11190 if (boards[move][0][0] == WhiteRook) *p++ = 'Q';
11192 if (boards[move][7][4] == BlackKing) {
11193 if (boards[move][7][7] == BlackRook) *p++ = 'k';
11194 if (boards[move][7][0] == BlackRook) *p++ = 'q';
11196 if (q == p) *p++ = '-';
11199 /* En passant target square */
11200 if (move > backwardMostMove) {
11201 fromX = moveList[move - 1][0] - 'a';
11202 fromY = moveList[move - 1][1] - '1';
11203 toX = moveList[move - 1][2] - 'a';
11204 toY = moveList[move - 1][3] - '1';
11205 if (fromY == (whiteToPlay ? 6 : 1) &&
11206 toY == (whiteToPlay ? 4 : 3) &&
11207 boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
11209 /* 2-square pawn move just happened */
11211 *p++ = whiteToPlay ? '6' : '3';
11219 /* !!We don't keep track of halfmove clock for 50-move rule */
11223 /* Fullmove number */
11224 sprintf(p, "%d", (move / 2) + 1);
11226 return StrSave(buf);
11230 ParseFEN(board, blackPlaysFirst, fen)
11232 int *blackPlaysFirst;
11241 /* Piece placement data */
11242 for (i = BOARD_SIZE - 1; i >= 0; i--) {
11245 if (*p == '/' || *p == ' ') {
11246 if (*p == '/') p++;
11247 emptycount = BOARD_SIZE - j;
11248 while (emptycount--) board[i][j++] = EmptySquare;
11250 } else if (isdigit(*p)) {
11251 emptycount = *p++ - '0';
11252 if (j + emptycount > BOARD_SIZE) return FALSE;
11253 while (emptycount--) board[i][j++] = EmptySquare;
11254 } else if (isalpha(*p)) {
11255 if (j >= BOARD_SIZE) return FALSE;
11256 board[i][j++] = CharToPiece(*p++);
11262 while (*p == '/' || *p == ' ') p++;
11267 *blackPlaysFirst = FALSE;
11270 *blackPlaysFirst = TRUE;
11275 /* !!We ignore the rest of the FEN notation */
11280 EditPositionPasteFEN(char *fen)
11283 Board initial_position;
11285 if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {
11286 DisplayError("Bad FEN position in clipboard", 0);
11289 int savedBlackPlaysFirst = blackPlaysFirst;
11290 EditPositionEvent();
11291 blackPlaysFirst = savedBlackPlaysFirst;
11292 CopyBoard(boards[0], initial_position);
11293 EditPositionDone();
11294 DisplayBothClocks();
11295 DrawPosition(FALSE, boards[currentMove]);