2 * backend.c -- Common back end for X and Windows NT versions of
\r
3 * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
6 * Massachusetts. Enhancements Copyright
\r
7 * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software
\r
10 * The following terms apply to Digital Equipment Corporation's copyright
\r
11 * interest in XBoard:
\r
12 * ------------------------------------------------------------------------
\r
13 * All Rights Reserved
\r
15 * Permission to use, copy, modify, and distribute this software and its
\r
16 * documentation for any purpose and without fee is hereby granted,
\r
17 * provided that the above copyright notice appear in all copies and that
\r
18 * both that copyright notice and this permission notice appear in
\r
19 * supporting documentation, and that the name of Digital not be
\r
20 * used in advertising or publicity pertaining to distribution of the
\r
21 * software without specific, written prior permission.
\r
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
30 * ------------------------------------------------------------------------
\r
32 * The following terms apply to the enhanced version of XBoard
\r
33 * distributed by the Free Software Foundation:
\r
34 * ------------------------------------------------------------------------
\r
36 * GNU XBoard is free software: you can redistribute it and/or modify
\r
37 * it under the terms of the GNU General Public License as published by
\r
38 * the Free Software Foundation, either version 3 of the License, or (at
\r
39 * your option) any later version.
\r
41 * GNU XBoard is distributed in the hope that it will be useful, but
\r
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
44 * General Public License for more details.
\r
46 * You should have received a copy of the GNU General Public License
\r
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
49 *------------------------------------------------------------------------
\r
50 ** See the file ChangeLog for a revision history. */
\r
52 /* [AS] Also useful here for debugging */
\r
54 #include <windows.h>
\r
56 #define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
\r
60 #define DoSleep( n ) if( (n) >= 0) sleep(n)
\r
70 #include <sys/types.h>
\r
71 #include <sys/stat.h>
\r
76 # include <stdlib.h>
\r
77 # include <string.h>
\r
78 #else /* not STDC_HEADERS */
\r
80 # include <string.h>
\r
81 # else /* not HAVE_STRING_H */
\r
82 # include <strings.h>
\r
83 # endif /* not HAVE_STRING_H */
\r
84 #endif /* not STDC_HEADERS */
\r
86 #if HAVE_SYS_FCNTL_H
\r
87 # include <sys/fcntl.h>
\r
88 #else /* not HAVE_SYS_FCNTL_H */
\r
91 # endif /* HAVE_FCNTL_H */
\r
92 #endif /* not HAVE_SYS_FCNTL_H */
\r
94 #if TIME_WITH_SYS_TIME
\r
95 # include <sys/time.h>
\r
98 # if HAVE_SYS_TIME_H
\r
99 # include <sys/time.h>
\r
105 #if defined(_amigados) && !defined(__GNUC__)
\r
107 int tz_minuteswest;
\r
110 extern int gettimeofday(struct timeval *, struct timezone *);
\r
114 # include <unistd.h>
\r
117 #include "common.h"
\r
118 #include "frontend.h"
\r
119 #include "backend.h"
\r
120 #include "parser.h"
\r
123 # include "zippy.h"
\r
125 #include "backendz.h"
\r
126 #include "gettext.h"
\r
129 # define _(s) gettext (s)
\r
130 # define N_(s) gettext_noop (s)
\r
137 /* A point in time */
\r
139 long sec; /* Assuming this is >= 32 bits */
\r
140 int ms; /* Assuming this is >= 16 bits */
\r
143 int establish P((void));
\r
144 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
\r
145 char *buf, int count, int error));
\r
146 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
\r
147 char *buf, int count, int error));
\r
148 void SendToICS P((char *s));
\r
149 void SendToICSDelayed P((char *s, long msdelay));
\r
150 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
\r
151 int toX, int toY));
\r
152 void InitPosition P((int redraw));
\r
153 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
154 int AutoPlayOneMove P((void));
\r
155 int LoadGameOneMove P((ChessMove readAhead));
\r
156 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
\r
157 int LoadPositionFromFile P((char *filename, int n, char *title));
\r
158 int SavePositionToFile P((char *filename));
\r
159 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
\r
161 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
\r
162 void ShowMove P((int fromX, int fromY, int toX, int toY));
\r
163 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
164 /*char*/int promoChar));
\r
165 void BackwardInner P((int target));
\r
166 void ForwardInner P((int target));
\r
167 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
\r
168 void EditPositionDone P((void));
\r
169 void PrintOpponents P((FILE *fp));
\r
170 void PrintPosition P((FILE *fp, int move));
\r
171 void StartChessProgram P((ChessProgramState *cps));
\r
172 void SendToProgram P((char *message, ChessProgramState *cps));
\r
173 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
\r
174 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
\r
175 char *buf, int count, int error));
\r
176 void SendTimeControl P((ChessProgramState *cps,
\r
177 int mps, long tc, int inc, int sd, int st));
\r
178 char *TimeControlTagValue P((void));
\r
179 void Attention P((ChessProgramState *cps));
\r
180 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
\r
181 void ResurrectChessProgram P((void));
\r
182 void DisplayComment P((int moveNumber, char *text));
\r
183 void DisplayMove P((int moveNumber));
\r
184 void DisplayAnalysis P((void));
\r
186 void ParseGameHistory P((char *game));
\r
187 void ParseBoard12 P((char *string));
\r
188 void StartClocks P((void));
\r
189 void SwitchClocks P((void));
\r
190 void StopClocks P((void));
\r
191 void ResetClocks P((void));
\r
192 char *PGNDate P((void));
\r
193 void SetGameInfo P((void));
\r
194 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
195 int RegisterMove P((void));
\r
196 void MakeRegisteredMove P((void));
\r
197 void TruncateGame P((void));
\r
198 int looking_at P((char *, int *, char *));
\r
199 void CopyPlayerNameIntoFileName P((char **, char *));
\r
200 char *SavePart P((char *));
\r
201 int SaveGameOldStyle P((FILE *));
\r
202 int SaveGamePGN P((FILE *));
\r
203 void GetTimeMark P((TimeMark *));
\r
204 long SubtractTimeMarks P((TimeMark *, TimeMark *));
\r
205 int CheckFlags P((void));
\r
206 long NextTickLength P((long));
\r
207 void CheckTimeControl P((void));
\r
208 void show_bytes P((FILE *, char *, int));
\r
209 int string_to_rating P((char *str));
\r
210 void ParseFeatures P((char* args, ChessProgramState *cps));
\r
211 void InitBackEnd3 P((void));
\r
212 void FeatureDone P((ChessProgramState* cps, int val));
\r
213 void InitChessProgram P((ChessProgramState *cps, int setup));
\r
214 void OutputKibitz(int window, char *text);
\r
215 int PerpetualChase(int first, int last);
\r
216 int EngineOutputIsUp();
\r
217 void InitDrawingSizes(int x, int y);
\r
220 extern void ConsoleCreate();
\r
223 ChessProgramState *WhitePlayer();
\r
224 void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c
\r
225 int VerifyDisplayMode P(());
\r
227 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment
\r
228 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c
\r
229 char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
\r
230 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
231 extern char installDir[MSG_SIZ];
\r
233 extern int tinyLayout, smallLayout;
\r
234 ChessProgramStats programStats;
\r
235 static int exiting = 0; /* [HGM] moved to top */
\r
236 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
\r
237 int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
\r
238 char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */
\r
239 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */
\r
240 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
\r
241 int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
\r
242 int opponentKibitzes;
\r
244 /* States for ics_getting_history */
\r
246 #define H_REQUESTED 1
\r
247 #define H_GOT_REQ_HEADER 2
\r
248 #define H_GOT_UNREQ_HEADER 3
\r
249 #define H_GETTING_MOVES 4
\r
250 #define H_GOT_UNWANTED_HEADER 5
\r
252 /* whosays values for GameEnds */
\r
254 #define GE_ENGINE 1
\r
255 #define GE_PLAYER 2
\r
257 #define GE_XBOARD 4
\r
258 #define GE_ENGINE1 5
\r
259 #define GE_ENGINE2 6
\r
261 /* Maximum number of games in a cmail message */
\r
262 #define CMAIL_MAX_GAMES 20
\r
264 /* Different types of move when calling RegisterMove */
\r
265 #define CMAIL_MOVE 0
\r
266 #define CMAIL_RESIGN 1
\r
267 #define CMAIL_DRAW 2
\r
268 #define CMAIL_ACCEPT 3
\r
270 /* Different types of result to remember for each game */
\r
271 #define CMAIL_NOT_RESULT 0
\r
272 #define CMAIL_OLD_RESULT 1
\r
273 #define CMAIL_NEW_RESULT 2
\r
275 /* Telnet protocol constants */
\r
276 #define TN_WILL 0373
\r
277 #define TN_WONT 0374
\r
279 #define TN_DONT 0376
\r
280 #define TN_IAC 0377
\r
281 #define TN_ECHO 0001
\r
282 #define TN_SGA 0003
\r
286 static char * safeStrCpy( char * dst, const char * src, size_t count )
\r
288 assert( dst != NULL );
\r
289 assert( src != NULL );
\r
290 assert( count > 0 );
\r
292 strncpy( dst, src, count );
\r
293 dst[ count-1 ] = '\0';
\r
298 //[HGM] for future use? Conditioned out for now to suppress warning.
\r
299 static char * safeStrCat( char * dst, const char * src, size_t count )
\r
303 assert( dst != NULL );
\r
304 assert( src != NULL );
\r
305 assert( count > 0 );
\r
307 dst_len = strlen(dst);
\r
309 assert( count > dst_len ); /* Buffer size must be greater than current length */
\r
311 safeStrCpy( dst + dst_len, src, count - dst_len );
\r
317 /* Some compiler can't cast u64 to double
\r
318 * This function do the job for us:
\r
320 * We use the highest bit for cast, this only
\r
321 * works if the highest bit is not
\r
322 * in use (This should not happen)
\r
324 * We used this for all compiler
\r
327 u64ToDouble(u64 value)
\r
330 u64 tmp = value & u64Const(0x7fffffffffffffff);
\r
331 r = (double)(s64)tmp;
\r
332 if (value & u64Const(0x8000000000000000))
\r
333 r += 9.2233720368547758080e18; /* 2^63 */
\r
337 /* Fake up flags for now, as we aren't keeping track of castling
\r
338 availability yet. [HGM] Change of logic: the flag now only
\r
339 indicates the type of castlings allowed by the rule of the game.
\r
340 The actual rights themselves are maintained in the array
\r
341 castlingRights, as part of the game history, and are not probed
\r
347 int flags = F_ALL_CASTLE_OK;
\r
348 if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
\r
349 switch (gameInfo.variant) {
\r
350 case VariantSuicide:
\r
351 flags &= ~F_ALL_CASTLE_OK;
\r
352 case VariantGiveaway: // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!
\r
353 flags |= F_IGNORE_CHECK;
\r
354 case VariantLosers:
\r
355 flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist
\r
357 case VariantAtomic:
\r
358 flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
\r
360 case VariantKriegspiel:
\r
361 flags |= F_KRIEGSPIEL_CAPTURE;
\r
363 case VariantCapaRandom:
\r
364 case VariantFischeRandom:
\r
365 flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */
\r
366 case VariantNoCastle:
\r
367 case VariantShatranj:
\r
368 case VariantCourier:
\r
369 flags &= ~F_ALL_CASTLE_OK;
\r
377 FILE *gameFileFP, *debugFP;
\r
380 [AS] Note: sometimes, the sscanf() function is used to parse the input
\r
381 into a fixed-size buffer. Because of this, we must be prepared to
\r
382 receive strings as long as the size of the input buffer, which is currently
\r
383 set to 4K for Windows and 8K for the rest.
\r
384 So, we must either allocate sufficiently large buffers here, or
\r
385 reduce the size of the input buffer in the input reading part.
\r
388 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
\r
389 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
\r
390 char thinkOutput1[MSG_SIZ*10];
\r
392 ChessProgramState first, second;
\r
394 /* premove variables */
\r
395 int premoveToX = 0;
\r
396 int premoveToY = 0;
\r
397 int premoveFromX = 0;
\r
398 int premoveFromY = 0;
\r
399 int premovePromoChar = 0;
\r
400 int gotPremove = 0;
\r
401 Boolean alarmSounded;
\r
402 /* end premove variables */
\r
404 char *ics_prefix = "$";
\r
405 int ics_type = ICS_GENERIC;
\r
407 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
\r
408 int pauseExamForwardMostMove = 0;
\r
409 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
\r
410 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
\r
411 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
\r
412 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
\r
413 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
\r
414 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
\r
415 int whiteFlag = FALSE, blackFlag = FALSE;
\r
416 int userOfferedDraw = FALSE;
\r
417 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
\r
418 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
\r
419 int cmailMoveType[CMAIL_MAX_GAMES];
\r
420 long ics_clock_paused = 0;
\r
421 ProcRef icsPR = NoProc, cmailPR = NoProc;
\r
422 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
\r
423 GameMode gameMode = BeginningOfGame;
\r
424 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
\r
425 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
\r
426 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
\r
427 int hiddenThinkOutputState = 0; /* [AS] */
\r
428 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
\r
429 int adjudicateLossPlies = 6;
\r
430 char white_holding[64], black_holding[64];
\r
431 TimeMark lastNodeCountTime;
\r
432 long lastNodeCount=0;
\r
433 int have_sent_ICS_logon = 0;
\r
434 int movesPerSession;
\r
435 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
\r
436 long timeControl_2; /* [AS] Allow separate time controls */
\r
437 char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */
\r
438 long timeRemaining[2][MAX_MOVES];
\r
440 TimeMark programStartTime;
\r
441 char ics_handle[MSG_SIZ];
\r
442 int have_set_title = 0;
\r
444 /* animateTraining preserves the state of appData.animate
\r
445 * when Training mode is activated. This allows the
\r
446 * response to be animated when appData.animate == TRUE and
\r
447 * appData.animateDragging == TRUE.
\r
449 Boolean animateTraining;
\r
455 Board boards[MAX_MOVES];
\r
456 /* [HGM] Following 7 needed for accurate legality tests: */
\r
457 char epStatus[MAX_MOVES];
\r
458 char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
\r
459 char castlingRank[BOARD_SIZE]; // and corresponding ranks
\r
460 char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
\r
461 int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
\r
462 int initialRulePlies, FENrulePlies;
\r
464 FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
\r
466 int shuffleOpenings;
\r
468 ChessSquare FIDEArray[2][BOARD_SIZE] = {
\r
469 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
\r
470 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
\r
471 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
\r
472 BlackKing, BlackBishop, BlackKnight, BlackRook }
\r
475 ChessSquare twoKingsArray[2][BOARD_SIZE] = {
\r
476 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
\r
477 WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
\r
478 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
\r
479 BlackKing, BlackKing, BlackKnight, BlackRook }
\r
482 ChessSquare KnightmateArray[2][BOARD_SIZE] = {
\r
483 { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,
\r
484 WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },
\r
485 { BlackRook, BlackMan, BlackBishop, BlackQueen,
\r
486 BlackUnicorn, BlackBishop, BlackMan, BlackRook }
\r
489 ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */
\r
490 { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,
\r
491 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
\r
492 { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,
\r
493 BlackKing, BlackBishop, BlackKnight, BlackRook }
\r
496 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */
\r
497 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,
\r
498 WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
\r
499 { BlackRook, BlackKnight, BlackAlfil, BlackKing,
\r
500 BlackFerz, BlackAlfil, BlackKnight, BlackRook }
\r
504 #if (BOARD_SIZE>=10)
\r
505 ChessSquare ShogiArray[2][BOARD_SIZE] = {
\r
506 { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,
\r
507 WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },
\r
508 { BlackQueen, BlackKnight, BlackFerz, BlackWazir,
\r
509 BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }
\r
512 ChessSquare XiangqiArray[2][BOARD_SIZE] = {
\r
513 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,
\r
514 WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
\r
515 { BlackRook, BlackKnight, BlackAlfil, BlackFerz,
\r
516 BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }
\r
519 ChessSquare CapablancaArray[2][BOARD_SIZE] = {
\r
520 { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
\r
521 WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
\r
522 { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
\r
523 BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
\r
526 ChessSquare GreatArray[2][BOARD_SIZE] = {
\r
527 { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
\r
528 WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
\r
529 { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
\r
530 BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
\r
533 ChessSquare JanusArray[2][BOARD_SIZE] = {
\r
534 { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
\r
535 WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
\r
536 { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
\r
537 BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
\r
541 ChessSquare GothicArray[2][BOARD_SIZE] = {
\r
542 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
\r
543 WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
\r
544 { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
\r
545 BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }
\r
548 #define GothicArray CapablancaArray
\r
552 ChessSquare FalconArray[2][BOARD_SIZE] = {
\r
553 { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
\r
554 WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
\r
555 { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
\r
556 BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
\r
559 #define FalconArray CapablancaArray
\r
562 #else // !(BOARD_SIZE>=10)
\r
563 #define XiangqiPosition FIDEArray
\r
564 #define CapablancaArray FIDEArray
\r
565 #define GothicArray FIDEArray
\r
566 #define GreatArray FIDEArray
\r
567 #endif // !(BOARD_SIZE>=10)
\r
569 #if (BOARD_SIZE>=12)
\r
570 ChessSquare CourierArray[2][BOARD_SIZE] = {
\r
571 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,
\r
572 WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },
\r
573 { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
\r
574 BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
\r
576 #else // !(BOARD_SIZE>=12)
\r
577 #define CourierArray CapablancaArray
\r
578 #endif // !(BOARD_SIZE>=12)
\r
581 Board initialPosition;
\r
584 /* Convert str to a rating. Checks for special cases of "----",
\r
586 "++++", etc. Also strips ()'s */
\r
588 string_to_rating(str)
\r
591 while(*str && !isdigit(*str)) ++str;
\r
593 return 0; /* One of the special "no rating" cases */
\r
599 ClearProgramStats()
\r
601 /* Init programStats */
\r
602 programStats.movelist[0] = 0;
\r
603 programStats.depth = 0;
\r
604 programStats.nr_moves = 0;
\r
605 programStats.moves_left = 0;
\r
606 programStats.nodes = 0;
\r
607 programStats.time = -1; // [HGM] PGNtime: make invalid to recognize engine output
\r
608 programStats.score = 0;
\r
609 programStats.got_only_move = 0;
\r
610 programStats.got_fail = 0;
\r
611 programStats.line_is_book = 0;
\r
617 int matched, min, sec;
\r
619 ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
\r
621 GetTimeMark(&programStartTime);
\r
622 srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level
\r
624 ClearProgramStats();
\r
625 programStats.ok_to_send = 1;
\r
626 programStats.seen_stat = 0;
\r
629 * Initialize game list
\r
631 ListNew(&gameList);
\r
635 * Internet chess server status
\r
637 if (appData.icsActive) {
\r
638 appData.matchMode = FALSE;
\r
639 appData.matchGames = 0;
\r
641 appData.noChessProgram = !appData.zippyPlay;
\r
643 appData.zippyPlay = FALSE;
\r
644 appData.zippyTalk = FALSE;
\r
645 appData.noChessProgram = TRUE;
\r
647 if (*appData.icsHelper != NULLCHAR) {
\r
648 appData.useTelnet = TRUE;
\r
649 appData.telnetProgram = appData.icsHelper;
\r
652 appData.zippyTalk = appData.zippyPlay = FALSE;
\r
655 /* [AS] Initialize pv info list [HGM] and game state */
\r
659 for( i=0; i<MAX_MOVES; i++ ) {
\r
660 pvInfoList[i].depth = -1;
\r
661 epStatus[i]=EP_NONE;
\r
662 for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;
\r
667 * Parse timeControl resource
\r
669 if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
\r
670 appData.movesPerSession)) {
\r
672 sprintf(buf, _("bad timeControl option %s"), appData.timeControl);
\r
673 DisplayFatalError(buf, 0, 2);
\r
677 * Parse searchTime resource
\r
679 if (*appData.searchTime != NULLCHAR) {
\r
680 matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
\r
681 if (matched == 1) {
\r
682 searchTime = min * 60;
\r
683 } else if (matched == 2) {
\r
684 searchTime = min * 60 + sec;
\r
687 sprintf(buf, _("bad searchTime option %s"), appData.searchTime);
\r
688 DisplayFatalError(buf, 0, 2);
\r
692 /* [AS] Adjudication threshold */
\r
693 adjudicateLossThreshold = appData.adjudicateLossThreshold;
\r
695 first.which = "first";
\r
696 second.which = "second";
\r
697 first.maybeThinking = second.maybeThinking = FALSE;
\r
698 first.pr = second.pr = NoProc;
\r
699 first.isr = second.isr = NULL;
\r
700 first.sendTime = second.sendTime = 2;
\r
701 first.sendDrawOffers = 1;
\r
702 if (appData.firstPlaysBlack) {
\r
703 first.twoMachinesColor = "black\n";
\r
704 second.twoMachinesColor = "white\n";
\r
706 first.twoMachinesColor = "white\n";
\r
707 second.twoMachinesColor = "black\n";
\r
709 first.program = appData.firstChessProgram;
\r
710 second.program = appData.secondChessProgram;
\r
711 first.host = appData.firstHost;
\r
712 second.host = appData.secondHost;
\r
713 first.dir = appData.firstDirectory;
\r
714 second.dir = appData.secondDirectory;
\r
715 first.other = &second;
\r
716 second.other = &first;
\r
717 first.initString = appData.initString;
\r
718 second.initString = appData.secondInitString;
\r
719 first.computerString = appData.firstComputerString;
\r
720 second.computerString = appData.secondComputerString;
\r
721 first.useSigint = second.useSigint = TRUE;
\r
722 first.useSigterm = second.useSigterm = TRUE;
\r
723 first.reuse = appData.reuseFirst;
\r
724 second.reuse = appData.reuseSecond;
\r
725 first.nps = appData.firstNPS; // [HGM] nps: copy nodes per second
\r
726 second.nps = appData.secondNPS;
\r
727 first.useSetboard = second.useSetboard = FALSE;
\r
728 first.useSAN = second.useSAN = FALSE;
\r
729 first.usePing = second.usePing = FALSE;
\r
730 first.lastPing = second.lastPing = 0;
\r
731 first.lastPong = second.lastPong = 0;
\r
732 first.usePlayother = second.usePlayother = FALSE;
\r
733 first.useColors = second.useColors = TRUE;
\r
734 first.useUsermove = second.useUsermove = FALSE;
\r
735 first.sendICS = second.sendICS = FALSE;
\r
736 first.sendName = second.sendName = appData.icsActive;
\r
737 first.sdKludge = second.sdKludge = FALSE;
\r
738 first.stKludge = second.stKludge = FALSE;
\r
739 TidyProgramName(first.program, first.host, first.tidy);
\r
740 TidyProgramName(second.program, second.host, second.tidy);
\r
741 first.matchWins = second.matchWins = 0;
\r
742 strcpy(first.variants, appData.variant);
\r
743 strcpy(second.variants, appData.variant);
\r
744 first.analysisSupport = second.analysisSupport = 2; /* detect */
\r
745 first.analyzing = second.analyzing = FALSE;
\r
746 first.initDone = second.initDone = FALSE;
\r
748 /* New features added by Tord: */
\r
749 first.useFEN960 = FALSE; second.useFEN960 = FALSE;
\r
750 first.useOOCastle = TRUE; second.useOOCastle = TRUE;
\r
751 /* End of new features added by Tord. */
\r
753 /* [HGM] time odds: set factor for each machine */
\r
754 first.timeOdds = appData.firstTimeOdds;
\r
755 second.timeOdds = appData.secondTimeOdds;
\r
757 if(appData.timeOddsMode) {
\r
758 norm = first.timeOdds;
\r
759 if(norm > second.timeOdds) norm = second.timeOdds;
\r
761 first.timeOdds /= norm;
\r
762 second.timeOdds /= norm;
\r
765 /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
\r
766 first.accumulateTC = appData.firstAccumulateTC;
\r
767 second.accumulateTC = appData.secondAccumulateTC;
\r
768 first.maxNrOfSessions = second.maxNrOfSessions = 1;
\r
771 first.debug = second.debug = FALSE;
\r
772 first.supportsNPS = second.supportsNPS = UNKNOWN;
\r
774 /* [HGM] options */
\r
775 first.optionSettings = appData.firstOptions;
\r
776 second.optionSettings = appData.secondOptions;
\r
778 first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
\r
779 second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
\r
780 first.isUCI = appData.firstIsUCI; /* [AS] */
\r
781 second.isUCI = appData.secondIsUCI; /* [AS] */
\r
782 first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
\r
783 second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
\r
785 if (appData.firstProtocolVersion > PROTOVER ||
\r
786 appData.firstProtocolVersion < 1) {
\r
788 sprintf(buf, _("protocol version %d not supported"),
\r
789 appData.firstProtocolVersion);
\r
790 DisplayFatalError(buf, 0, 2);
\r
792 first.protocolVersion = appData.firstProtocolVersion;
\r
795 if (appData.secondProtocolVersion > PROTOVER ||
\r
796 appData.secondProtocolVersion < 1) {
\r
798 sprintf(buf, _("protocol version %d not supported"),
\r
799 appData.secondProtocolVersion);
\r
800 DisplayFatalError(buf, 0, 2);
\r
802 second.protocolVersion = appData.secondProtocolVersion;
\r
805 if (appData.icsActive) {
\r
806 appData.clockMode = TRUE; /* changes dynamically in ICS mode */
\r
807 } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
\r
808 appData.clockMode = FALSE;
\r
809 first.sendTime = second.sendTime = 0;
\r
813 /* Override some settings from environment variables, for backward
\r
814 compatibility. Unfortunately it's not feasible to have the env
\r
815 vars just set defaults, at least in xboard. Ugh.
\r
817 if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
\r
822 if (appData.noChessProgram) {
\r
823 programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
\r
824 + strlen(PATCHLEVEL));
\r
825 sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
\r
830 while (*q != ' ' && *q != NULLCHAR) q++;
\r
832 while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */
\r
833 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
\r
834 + strlen(PATCHLEVEL) + (q - p));
\r
835 sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
\r
836 strncat(programVersion, p, q - p);
\r
838 /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
\r
839 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
\r
840 + strlen(PATCHLEVEL) + strlen(first.tidy));
\r
841 sprintf(programVersion, "%s %s.%s + %s", PRODUCT, VERSION, PATCHLEVEL, first.tidy);
\r
845 if (!appData.icsActive) {
\r
847 /* Check for variants that are supported only in ICS mode,
\r
848 or not at all. Some that are accepted here nevertheless
\r
849 have bugs; see comments below.
\r
851 VariantClass variant = StringToVariant(appData.variant);
\r
853 case VariantBughouse: /* need four players and two boards */
\r
854 case VariantKriegspiel: /* need to hide pieces and move details */
\r
855 /* case VariantFischeRandom: (Fabien: moved below) */
\r
856 sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);
\r
857 DisplayFatalError(buf, 0, 2);
\r
860 case VariantUnknown:
\r
861 case VariantLoadable:
\r
871 sprintf(buf, _("Unknown variant name %s"), appData.variant);
\r
872 DisplayFatalError(buf, 0, 2);
\r
875 case VariantXiangqi: /* [HGM] repetition rules not implemented */
\r
876 case VariantFairy: /* [HGM] TestLegality definitely off! */
\r
877 case VariantGothic: /* [HGM] should work */
\r
878 case VariantCapablanca: /* [HGM] should work */
\r
879 case VariantCourier: /* [HGM] initial forced moves not implemented */
\r
880 case VariantShogi: /* [HGM] drops not tested for legality */
\r
881 case VariantKnightmate: /* [HGM] should work */
\r
882 case VariantCylinder: /* [HGM] untested */
\r
883 case VariantFalcon: /* [HGM] untested */
\r
884 case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)
\r
885 offboard interposition not understood */
\r
886 case VariantNormal: /* definitely works! */
\r
887 case VariantWildCastle: /* pieces not automatically shuffled */
\r
888 case VariantNoCastle: /* pieces not automatically shuffled */
\r
889 case VariantFischeRandom: /* [HGM] works and shuffles pieces */
\r
890 case VariantLosers: /* should work except for win condition,
\r
891 and doesn't know captures are mandatory */
\r
892 case VariantSuicide: /* should work except for win condition,
\r
893 and doesn't know captures are mandatory */
\r
894 case VariantGiveaway: /* should work except for win condition,
\r
895 and doesn't know captures are mandatory */
\r
896 case VariantTwoKings: /* should work */
\r
897 case VariantAtomic: /* should work except for win condition */
\r
898 case Variant3Check: /* should work except for win condition */
\r
899 case VariantShatranj: /* should work except for all win conditions */
\r
900 case VariantBerolina: /* might work if TestLegality is off */
\r
901 case VariantCapaRandom: /* should work */
\r
902 case VariantJanus: /* should work */
\r
903 case VariantSuper: /* experimental */
\r
904 case VariantGreat: /* experimental, requires legality testing to be off */
\r
909 InitEngineUCI( installDir, &first ); // [HGM] moved here from winboard.c, to make available in xboard
\r
910 InitEngineUCI( installDir, &second );
\r
913 int NextIntegerFromString( char ** str, long * value )
\r
918 while( *s == ' ' || *s == '\t' ) {
\r
924 if( *s >= '0' && *s <= '9' ) {
\r
925 while( *s >= '0' && *s <= '9' ) {
\r
926 *value = *value * 10 + (*s - '0');
\r
938 int NextTimeControlFromString( char ** str, long * value )
\r
941 int result = NextIntegerFromString( str, &temp );
\r
943 if( result == 0 ) {
\r
944 *value = temp * 60; /* Minutes */
\r
945 if( **str == ':' ) {
\r
947 result = NextIntegerFromString( str, &temp );
\r
948 *value += temp; /* Seconds */
\r
955 int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)
\r
956 { /* [HGM] routine added to read '+moves/time' for secondary time control */
\r
957 int result = -1; long temp, temp2;
\r
959 if(**str != '+') return -1; // old params remain in force!
\r
961 if( NextTimeControlFromString( str, &temp ) ) return -1;
\r
964 /* time only: incremental or sudden-death time control */
\r
965 if(**str == '+') { /* increment follows; read it */
\r
967 if(result = NextIntegerFromString( str, &temp2)) return -1;
\r
968 *inc = temp2 * 1000;
\r
970 *moves = 0; *tc = temp * 1000;
\r
972 } else if(temp % 60 != 0) return -1; /* moves was given as min:sec */
\r
974 (*str)++; /* classical time control */
\r
975 result = NextTimeControlFromString( str, &temp2);
\r
978 *tc = temp2 * 1000;
\r
984 int GetTimeQuota(int movenr)
\r
985 { /* [HGM] get time to add from the multi-session time-control string */
\r
986 int moves=1; /* kludge to force reading of first session */
\r
987 long time, increment;
\r
988 char *s = fullTimeControlString;
\r
990 if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);
\r
992 if(moves) NextSessionFromString(&s, &moves, &time, &increment);
\r
993 if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
\r
994 if(movenr == -1) return time; /* last move before new session */
\r
995 if(!moves) return increment; /* current session is incremental */
\r
996 if(movenr >= 0) movenr -= moves; /* we already finished this session */
\r
997 } while(movenr >= -1); /* try again for next session */
\r
999 return 0; // no new time quota on this move
\r
1003 ParseTimeControl(tc, ti, mps)
\r
1009 int matched, min, sec;
\r
1011 matched = sscanf(tc, "%d:%d", &min, &sec);
\r
1012 if (matched == 1) {
\r
1013 timeControl = min * 60 * 1000;
\r
1014 } else if (matched == 2) {
\r
1015 timeControl = (min * 60 + sec) * 1000;
\r
1022 char buf[MSG_SIZ];
\r
1024 if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
\r
1027 sprintf(buf, "+%d/%s+%d", mps, tc, ti);
\r
1028 else sprintf(buf, "+%s+%d", tc, ti);
\r
1031 sprintf(buf, "+%d/%s", mps, tc);
\r
1032 else sprintf(buf, "+%s", tc);
\r
1034 fullTimeControlString = StrSave(buf);
\r
1036 if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
\r
1040 if( *tc == '/' ) {
\r
1041 /* Parse second time control */
\r
1044 if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
\r
1052 timeControl_2 = tc2 * 1000;
\r
1055 timeControl_2 = 0;
\r
1062 timeControl = tc1 * 1000;
\r
1066 timeIncrement = ti * 1000; /* convert to ms */
\r
1067 movesPerSession = 0;
\r
1069 timeIncrement = 0;
\r
1070 movesPerSession = mps;
\r
1078 if (appData.debugMode) {
\r
1079 fprintf(debugFP, "%s\n", programVersion);
\r
1082 if (appData.matchGames > 0) {
\r
1083 appData.matchMode = TRUE;
\r
1084 } else if (appData.matchMode) {
\r
1085 appData.matchGames = 1;
\r
1087 if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */
\r
1088 appData.matchGames = appData.sameColorGames;
\r
1089 if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */
\r
1090 if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;
\r
1091 if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;
\r
1093 Reset(TRUE, FALSE);
\r
1094 if (appData.noChessProgram || first.protocolVersion == 1) {
\r
1097 /* kludge: allow timeout for initial "feature" commands */
\r
1099 DisplayMessage("", _("Starting chess program"));
\r
1100 ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
\r
1105 InitBackEnd3 P((void))
\r
1107 GameMode initialMode;
\r
1108 char buf[MSG_SIZ];
\r
1111 InitChessProgram(&first, startedFromSetupPosition);
\r
1114 if (appData.icsActive) {
\r
1116 /* [DM] Make a console window if needed [HGM] merged ifs */
\r
1119 err = establish();
\r
1121 if (*appData.icsCommPort != NULLCHAR) {
\r
1122 sprintf(buf, _("Could not open comm port %s"),
\r
1123 appData.icsCommPort);
\r
1125 sprintf(buf, _("Could not connect to host %s, port %s"),
\r
1126 appData.icsHost, appData.icsPort);
\r
1128 DisplayFatalError(buf, err, 1);
\r
1133 AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
\r
1135 AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
\r
1136 } else if (appData.noChessProgram) {
\r
1142 if (*appData.cmailGameName != NULLCHAR) {
\r
1144 OpenLoopback(&cmailPR);
\r
1146 AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
\r
1150 DisplayMessage("", "");
\r
1151 if (StrCaseCmp(appData.initialMode, "") == 0) {
\r
1152 initialMode = BeginningOfGame;
\r
1153 } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
\r
1154 initialMode = TwoMachinesPlay;
\r
1155 } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
\r
1156 initialMode = AnalyzeFile;
\r
1157 } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
\r
1158 initialMode = AnalyzeMode;
\r
1159 } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
\r
1160 initialMode = MachinePlaysWhite;
\r
1161 } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
\r
1162 initialMode = MachinePlaysBlack;
\r
1163 } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
\r
1164 initialMode = EditGame;
\r
1165 } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
\r
1166 initialMode = EditPosition;
\r
1167 } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
\r
1168 initialMode = Training;
\r
1170 sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);
\r
1171 DisplayFatalError(buf, 0, 2);
\r
1175 if (appData.matchMode) {
\r
1176 /* Set up machine vs. machine match */
\r
1177 if (appData.noChessProgram) {
\r
1178 DisplayFatalError(_("Can't have a match with no chess programs"),
\r
1184 if (*appData.loadGameFile != NULLCHAR) {
\r
1185 int index = appData.loadGameIndex; // [HGM] autoinc
\r
1186 if(index<0) lastIndex = index = 1;
\r
1187 if (!LoadGameFromFile(appData.loadGameFile,
\r
1189 appData.loadGameFile, FALSE)) {
\r
1190 DisplayFatalError(_("Bad game file"), 0, 1);
\r
1193 } else if (*appData.loadPositionFile != NULLCHAR) {
\r
1194 int index = appData.loadPositionIndex; // [HGM] autoinc
\r
1195 if(index<0) lastIndex = index = 1;
\r
1196 if (!LoadPositionFromFile(appData.loadPositionFile,
\r
1198 appData.loadPositionFile)) {
\r
1199 DisplayFatalError(_("Bad position file"), 0, 1);
\r
1203 TwoMachinesEvent();
\r
1204 } else if (*appData.cmailGameName != NULLCHAR) {
\r
1205 /* Set up cmail mode */
\r
1206 ReloadCmailMsgEvent(TRUE);
\r
1208 /* Set up other modes */
\r
1209 if (initialMode == AnalyzeFile) {
\r
1210 if (*appData.loadGameFile == NULLCHAR) {
\r
1211 DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
\r
1215 if (*appData.loadGameFile != NULLCHAR) {
\r
1216 (void) LoadGameFromFile(appData.loadGameFile,
\r
1217 appData.loadGameIndex,
\r
1218 appData.loadGameFile, TRUE);
\r
1219 } else if (*appData.loadPositionFile != NULLCHAR) {
\r
1220 (void) LoadPositionFromFile(appData.loadPositionFile,
\r
1221 appData.loadPositionIndex,
\r
1222 appData.loadPositionFile);
\r
1223 /* [HGM] try to make self-starting even after FEN load */
\r
1224 /* to allow automatic setup of fairy variants with wtm */
\r
1225 if(initialMode == BeginningOfGame && !blackPlaysFirst) {
\r
1226 gameMode = BeginningOfGame;
\r
1227 setboardSpoiledMachineBlack = 1;
\r
1229 /* [HGM] loadPos: make that every new game uses the setup */
\r
1230 /* from file as long as we do not switch variant */
\r
1231 if(!blackPlaysFirst) { int i;
\r
1232 startedFromPositionFile = TRUE;
\r
1233 CopyBoard(filePosition, boards[0]);
\r
1234 for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];
\r
1237 if (initialMode == AnalyzeMode) {
\r
1238 if (appData.noChessProgram) {
\r
1239 DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
\r
1242 if (appData.icsActive) {
\r
1243 DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
\r
1246 AnalyzeModeEvent();
\r
1247 } else if (initialMode == AnalyzeFile) {
\r
1248 appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent
\r
1249 ShowThinkingEvent();
\r
1250 AnalyzeFileEvent();
\r
1251 AnalysisPeriodicEvent(1);
\r
1252 } else if (initialMode == MachinePlaysWhite) {
\r
1253 if (appData.noChessProgram) {
\r
1254 DisplayFatalError(_("MachineWhite mode requires a chess engine"),
\r
1258 if (appData.icsActive) {
\r
1259 DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
\r
1263 MachineWhiteEvent();
\r
1264 } else if (initialMode == MachinePlaysBlack) {
\r
1265 if (appData.noChessProgram) {
\r
1266 DisplayFatalError(_("MachineBlack mode requires a chess engine"),
\r
1270 if (appData.icsActive) {
\r
1271 DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
\r
1275 MachineBlackEvent();
\r
1276 } else if (initialMode == TwoMachinesPlay) {
\r
1277 if (appData.noChessProgram) {
\r
1278 DisplayFatalError(_("TwoMachines mode requires a chess engine"),
\r
1282 if (appData.icsActive) {
\r
1283 DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
\r
1287 TwoMachinesEvent();
\r
1288 } else if (initialMode == EditGame) {
\r
1290 } else if (initialMode == EditPosition) {
\r
1291 EditPositionEvent();
\r
1292 } else if (initialMode == Training) {
\r
1293 if (*appData.loadGameFile == NULLCHAR) {
\r
1294 DisplayFatalError(_("Training mode requires a game file"), 0, 2);
\r
1303 * Establish will establish a contact to a remote host.port.
\r
1304 * Sets icsPR to a ProcRef for a process (or pseudo-process)
\r
1305 * used to talk to the host.
\r
1306 * Returns 0 if okay, error code if not.
\r
1311 char buf[MSG_SIZ];
\r
1313 if (*appData.icsCommPort != NULLCHAR) {
\r
1314 /* Talk to the host through a serial comm port */
\r
1315 return OpenCommPort(appData.icsCommPort, &icsPR);
\r
1317 } else if (*appData.gateway != NULLCHAR) {
\r
1318 if (*appData.remoteShell == NULLCHAR) {
\r
1319 /* Use the rcmd protocol to run telnet program on a gateway host */
\r
1320 sprintf(buf, "%s %s %s",
\r
1321 appData.telnetProgram, appData.icsHost, appData.icsPort);
\r
1322 return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
\r
1325 /* Use the rsh program to run telnet program on a gateway host */
\r
1326 if (*appData.remoteUser == NULLCHAR) {
\r
1327 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
\r
1328 appData.gateway, appData.telnetProgram,
\r
1329 appData.icsHost, appData.icsPort);
\r
1331 sprintf(buf, "%s %s -l %s %s %s %s",
\r
1332 appData.remoteShell, appData.gateway,
\r
1333 appData.remoteUser, appData.telnetProgram,
\r
1334 appData.icsHost, appData.icsPort);
\r
1336 return StartChildProcess(buf, "", &icsPR);
\r
1339 } else if (appData.useTelnet) {
\r
1340 return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
\r
1343 /* TCP socket interface differs somewhat between
\r
1344 Unix and NT; handle details in the front end.
\r
1346 return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
\r
1351 show_bytes(fp, buf, count)
\r
1357 if (*buf < 040 || *(unsigned char *) buf > 0177) {
\r
1358 fprintf(fp, "\\%03o", *buf & 0xff);
\r
1367 /* Returns an errno value */
\r
1369 OutputMaybeTelnet(pr, message, count, outError)
\r
1375 char buf[8192], *p, *q, *buflim;
\r
1376 int left, newcount, outcount;
\r
1378 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
\r
1379 *appData.gateway != NULLCHAR) {
\r
1380 if (appData.debugMode) {
\r
1381 fprintf(debugFP, ">ICS: ");
\r
1382 show_bytes(debugFP, message, count);
\r
1383 fprintf(debugFP, "\n");
\r
1385 return OutputToProcess(pr, message, count, outError);
\r
1388 buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
\r
1394 if (q >= buflim) {
\r
1395 if (appData.debugMode) {
\r
1396 fprintf(debugFP, ">ICS: ");
\r
1397 show_bytes(debugFP, buf, newcount);
\r
1398 fprintf(debugFP, "\n");
\r
1400 outcount = OutputToProcess(pr, buf, newcount, outError);
\r
1401 if (outcount < newcount) return -1; /* to be sure */
\r
1408 } else if (((unsigned char) *p) == TN_IAC) {
\r
1409 *q++ = (char) TN_IAC;
\r
1416 if (appData.debugMode) {
\r
1417 fprintf(debugFP, ">ICS: ");
\r
1418 show_bytes(debugFP, buf, newcount);
\r
1419 fprintf(debugFP, "\n");
\r
1421 outcount = OutputToProcess(pr, buf, newcount, outError);
\r
1422 if (outcount < newcount) return -1; /* to be sure */
\r
1427 read_from_player(isr, closure, message, count, error)
\r
1428 InputSourceRef isr;
\r
1434 int outError, outCount;
\r
1435 static int gotEof = 0;
\r
1437 /* Pass data read from player on to ICS */
\r
1440 outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
\r
1441 if (outCount < count) {
\r
1442 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1444 } else if (count < 0) {
\r
1445 RemoveInputSource(isr);
\r
1446 DisplayFatalError(_("Error reading from keyboard"), error, 1);
\r
1447 } else if (gotEof++ > 0) {
\r
1448 RemoveInputSource(isr);
\r
1449 DisplayFatalError(_("Got end of file from keyboard"), 0, 0);
\r
1457 int count, outCount, outError;
\r
1459 if (icsPR == NULL) return;
\r
1461 count = strlen(s);
\r
1462 outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
\r
1463 if (outCount < count) {
\r
1464 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1468 /* This is used for sending logon scripts to the ICS. Sending
\r
1469 without a delay causes problems when using timestamp on ICC
\r
1470 (at least on my machine). */
\r
1472 SendToICSDelayed(s,msdelay)
\r
1476 int count, outCount, outError;
\r
1478 if (icsPR == NULL) return;
\r
1480 count = strlen(s);
\r
1481 if (appData.debugMode) {
\r
1482 fprintf(debugFP, ">ICS: ");
\r
1483 show_bytes(debugFP, s, count);
\r
1484 fprintf(debugFP, "\n");
\r
1486 outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
\r
1488 if (outCount < count) {
\r
1489 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1494 /* Remove all highlighting escape sequences in s
\r
1495 Also deletes any suffix starting with '('
\r
1498 StripHighlightAndTitle(s)
\r
1501 static char retbuf[MSG_SIZ];
\r
1504 while (*s != NULLCHAR) {
\r
1505 while (*s == '\033') {
\r
1506 while (*s != NULLCHAR && !isalpha(*s)) s++;
\r
1507 if (*s != NULLCHAR) s++;
\r
1509 while (*s != NULLCHAR && *s != '\033') {
\r
1510 if (*s == '(' || *s == '[') {
\r
1521 /* Remove all highlighting escape sequences in s */
\r
1526 static char retbuf[MSG_SIZ];
\r
1529 while (*s != NULLCHAR) {
\r
1530 while (*s == '\033') {
\r
1531 while (*s != NULLCHAR && !isalpha(*s)) s++;
\r
1532 if (*s != NULLCHAR) s++;
\r
1534 while (*s != NULLCHAR && *s != '\033') {
\r
1542 char *variantNames[] = VARIANT_NAMES;
\r
1547 return variantNames[v];
\r
1551 /* Identify a variant from the strings the chess servers use or the
\r
1552 PGN Variant tag names we use. */
\r
1554 StringToVariant(e)
\r
1559 VariantClass v = VariantNormal;
\r
1560 int i, found = FALSE;
\r
1561 char buf[MSG_SIZ];
\r
1565 /* [HGM] skip over optional board-size prefixes */
\r
1566 if( sscanf(e, "%dx%d_", &i, &i) == 2 ||
\r
1567 sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {
\r
1568 while( *e++ != '_');
\r
1571 for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
\r
1572 if (StrCaseStr(e, variantNames[i])) {
\r
1573 v = (VariantClass) i;
\r
1580 if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
\r
1581 || StrCaseStr(e, "wild/fr")
\r
1582 || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {
\r
1583 v = VariantFischeRandom;
\r
1584 } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
\r
1585 (i = 1, p = StrCaseStr(e, "w"))) {
\r
1587 while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
\r
1588 if (isdigit(*p)) {
\r
1594 case 0: /* FICS only, actually */
\r
1596 /* Castling legal even if K starts on d-file */
\r
1597 v = VariantWildCastle;
\r
1602 /* Castling illegal even if K & R happen to start in
\r
1603 normal positions. */
\r
1604 v = VariantNoCastle;
\r
1617 /* Castling legal iff K & R start in normal positions */
\r
1618 v = VariantNormal;
\r
1623 /* Special wilds for position setup; unclear what to do here */
\r
1624 v = VariantLoadable;
\r
1627 /* Bizarre ICC game */
\r
1628 v = VariantTwoKings;
\r
1631 v = VariantKriegspiel;
\r
1634 v = VariantLosers;
\r
1637 v = VariantFischeRandom;
\r
1640 v = VariantCrazyhouse;
\r
1643 v = VariantBughouse;
\r
1646 v = Variant3Check;
\r
1649 /* Not quite the same as FICS suicide! */
\r
1650 v = VariantGiveaway;
\r
1653 v = VariantAtomic;
\r
1656 v = VariantShatranj;
\r
1659 /* Temporary names for future ICC types. The name *will* change in
\r
1660 the next xboard/WinBoard release after ICC defines it. */
\r
1689 v = VariantXiangqi;
\r
1692 v = VariantCourier;
\r
1695 v = VariantGothic;
\r
1698 v = VariantCapablanca;
\r
1701 v = VariantKnightmate;
\r
1707 v = VariantCylinder;
\r
1710 v = VariantFalcon;
\r
1713 v = VariantCapaRandom;
\r
1716 v = VariantBerolina;
\r
1728 /* Found "wild" or "w" in the string but no number;
\r
1729 must assume it's normal chess. */
\r
1730 v = VariantNormal;
\r
1733 sprintf(buf, _("Unknown wild type %d"), wnum);
\r
1734 DisplayError(buf, 0);
\r
1735 v = VariantUnknown;
\r
1740 if (appData.debugMode) {
\r
1741 fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),
\r
1742 e, wnum, VariantName(v));
\r
1747 static int leftover_start = 0, leftover_len = 0;
\r
1748 char star_match[STAR_MATCH_N][MSG_SIZ];
\r
1750 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
\r
1751 advance *index beyond it, and set leftover_start to the new value of
\r
1752 *index; else return FALSE. If pattern contains the character '*', it
\r
1753 matches any sequence of characters not containing '\r', '\n', or the
\r
1754 character following the '*' (if any), and the matched sequence(s) are
\r
1755 copied into star_match.
\r
1758 looking_at(buf, index, pattern)
\r
1763 char *bufp = &buf[*index], *patternp = pattern;
\r
1764 int star_count = 0;
\r
1765 char *matchp = star_match[0];
\r
1768 if (*patternp == NULLCHAR) {
\r
1769 *index = leftover_start = bufp - buf;
\r
1770 *matchp = NULLCHAR;
\r
1773 if (*bufp == NULLCHAR) return FALSE;
\r
1774 if (*patternp == '*') {
\r
1775 if (*bufp == *(patternp + 1)) {
\r
1776 *matchp = NULLCHAR;
\r
1777 matchp = star_match[++star_count];
\r
1781 } else if (*bufp == '\n' || *bufp == '\r') {
\r
1783 if (*patternp == NULLCHAR)
\r
1788 *matchp++ = *bufp++;
\r
1792 if (*patternp != *bufp) return FALSE;
\r
1799 SendToPlayer(data, length)
\r
1803 int error, outCount;
\r
1804 outCount = OutputToProcess(NoProc, data, length, &error);
\r
1805 if (outCount < length) {
\r
1806 DisplayFatalError(_("Error writing to display"), error, 1);
\r
1811 PackHolding(packed, holding)
\r
1815 char *p = holding;
\r
1817 int runlength = 0;
\r
1823 switch (runlength) {
\r
1834 sprintf(q, "%d", runlength);
\r
1846 /* Telnet protocol requests from the front end */
\r
1848 TelnetRequest(ddww, option)
\r
1849 unsigned char ddww, option;
\r
1851 unsigned char msg[3];
\r
1852 int outCount, outError;
\r
1854 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
\r
1856 if (appData.debugMode) {
\r
1857 char buf1[8], buf2[8], *ddwwStr, *optionStr;
\r
1873 sprintf(buf1, "%d", ddww);
\r
1878 optionStr = "ECHO";
\r
1882 sprintf(buf2, "%d", option);
\r
1885 fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
\r
1890 outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
\r
1891 if (outCount < 3) {
\r
1892 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1899 if (!appData.icsActive) return;
\r
1900 TelnetRequest(TN_DO, TN_ECHO);
\r
1906 if (!appData.icsActive) return;
\r
1907 TelnetRequest(TN_DONT, TN_ECHO);
\r
1911 CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
\r
1913 /* put the holdings sent to us by the server on the board holdings area */
\r
1914 int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;
\r
1916 ChessSquare piece;
\r
1918 if(gameInfo.holdingsWidth < 2) return;
\r
1920 if( (int)lowestPiece >= BlackPawn ) {
\r
1921 holdingsColumn = 0;
\r
1923 holdingsStartRow = BOARD_HEIGHT-1;
\r
1926 holdingsColumn = BOARD_WIDTH-1;
\r
1927 countsColumn = BOARD_WIDTH-2;
\r
1928 holdingsStartRow = 0;
\r
1932 for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */
\r
1933 board[i][holdingsColumn] = EmptySquare;
\r
1934 board[i][countsColumn] = (ChessSquare) 0;
\r
1936 while( (p=*holdings++) != NULLCHAR ) {
\r
1937 piece = CharToPiece( ToUpper(p) );
\r
1938 if(piece == EmptySquare) continue;
\r
1939 /*j = (int) piece - (int) WhitePawn;*/
\r
1940 j = PieceToNumber(piece);
\r
1941 if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */
\r
1942 if(j < 0) continue; /* should not happen */
\r
1943 piece = (ChessSquare) ( (int)piece + (int)lowestPiece );
\r
1944 board[holdingsStartRow+j*direction][holdingsColumn] = piece;
\r
1945 board[holdingsStartRow+j*direction][countsColumn]++;
\r
1952 VariantSwitch(Board board, VariantClass newVariant)
\r
1954 int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
\r
1955 int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;
\r
1956 // Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;
\r
1958 startedFromPositionFile = FALSE;
\r
1959 if(gameInfo.variant == newVariant) return;
\r
1961 /* [HGM] This routine is called each time an assignment is made to
\r
1962 * gameInfo.variant during a game, to make sure the board sizes
\r
1963 * are set to match the new variant. If that means adding or deleting
\r
1964 * holdings, we shift the playing board accordingly
\r
1965 * This kludge is needed because in ICS observe mode, we get boards
\r
1966 * of an ongoing game without knowing the variant, and learn about the
\r
1967 * latter only later. This can be because of the move list we requested,
\r
1968 * in which case the game history is refilled from the beginning anyway,
\r
1969 * but also when receiving holdings of a crazyhouse game. In the latter
\r
1970 * case we want to add those holdings to the already received position.
\r
1974 if (appData.debugMode) {
\r
1975 fprintf(debugFP, "Switch board from %s to %s\n",
\r
1976 VariantName(gameInfo.variant), VariantName(newVariant));
\r
1977 setbuf(debugFP, NULL);
\r
1979 shuffleOpenings = 0; /* [HGM] shuffle */
\r
1980 gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
\r
1981 switch(newVariant) {
\r
1982 case VariantShogi:
\r
1983 newWidth = 9; newHeight = 9;
\r
1984 gameInfo.holdingsSize = 7;
\r
1985 case VariantBughouse:
\r
1986 case VariantCrazyhouse:
\r
1987 newHoldingsWidth = 2; break;
\r
1989 newHoldingsWidth = gameInfo.holdingsSize = 0;
\r
1992 if(newWidth != gameInfo.boardWidth ||
\r
1993 newHeight != gameInfo.boardHeight ||
\r
1994 newHoldingsWidth != gameInfo.holdingsWidth ) {
\r
1996 /* shift position to new playing area, if needed */
\r
1997 if(newHoldingsWidth > gameInfo.holdingsWidth) {
\r
1998 for(i=0; i<BOARD_HEIGHT; i++)
\r
1999 for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
\r
2000 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
\r
2002 for(i=0; i<newHeight; i++) {
\r
2003 board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
\r
2004 board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
\r
2006 } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
\r
2007 for(i=0; i<BOARD_HEIGHT; i++)
\r
2008 for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
\r
2009 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
\r
2013 gameInfo.boardWidth = newWidth;
\r
2014 gameInfo.boardHeight = newHeight;
\r
2015 gameInfo.holdingsWidth = newHoldingsWidth;
\r
2016 gameInfo.variant = newVariant;
\r
2017 InitDrawingSizes(-2, 0);
\r
2019 /* [HGM] The following should definitely be solved in a better way */
\r
2021 CopyBoard(board, tempBoard); /* save position in case it is board[0] */
\r
2022 for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];
\r
2023 saveEP = epStatus[0];
\r
2025 InitPosition(FALSE); /* this sets up board[0], but also other stuff */
\r
2027 epStatus[0] = saveEP;
\r
2028 for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];
\r
2029 CopyBoard(tempBoard, board); /* restore position received from ICS */
\r
2031 } else { gameInfo.variant = newVariant; InitPosition(FALSE); }
\r
2033 forwardMostMove = oldForwardMostMove;
\r
2034 backwardMostMove = oldBackwardMostMove;
\r
2035 currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */
\r
2038 static int loggedOn = FALSE;
\r
2040 /*-- Game start info cache: --*/
\r
2042 char gs_kind[MSG_SIZ];
\r
2043 static char player1Name[128] = "";
\r
2044 static char player2Name[128] = "";
\r
2045 static int player1Rating = -1;
\r
2046 static int player2Rating = -1;
\r
2047 /*----------------------------*/
\r
2049 ColorClass curColor = ColorNormal;
\r
2050 int suppressKibitz = 0;
\r
2053 read_from_ics(isr, closure, data, count, error)
\r
2054 InputSourceRef isr;
\r
2060 #define BUF_SIZE 8192
\r
2061 #define STARTED_NONE 0
\r
2062 #define STARTED_MOVES 1
\r
2063 #define STARTED_BOARD 2
\r
2064 #define STARTED_OBSERVE 3
\r
2065 #define STARTED_HOLDINGS 4
\r
2066 #define STARTED_CHATTER 5
\r
2067 #define STARTED_COMMENT 6
\r
2068 #define STARTED_MOVES_NOHIDE 7
\r
2070 static int started = STARTED_NONE;
\r
2071 static char parse[20000];
\r
2072 static int parse_pos = 0;
\r
2073 static char buf[BUF_SIZE + 1];
\r
2074 static int firstTime = TRUE, intfSet = FALSE;
\r
2075 static ColorClass prevColor = ColorNormal;
\r
2076 static int savingComment = FALSE;
\r
2082 int backup; /* [DM] For zippy color lines */
\r
2085 if (appData.debugMode) {
\r
2087 fprintf(debugFP, "<ICS: ");
\r
2088 show_bytes(debugFP, data, count);
\r
2089 fprintf(debugFP, "\n");
\r
2093 if (appData.debugMode) { int f = forwardMostMove;
\r
2094 fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,
\r
2095 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
\r
2098 /* If last read ended with a partial line that we couldn't parse,
\r
2099 prepend it to the new read and try again. */
\r
2100 if (leftover_len > 0) {
\r
2101 for (i=0; i<leftover_len; i++)
\r
2102 buf[i] = buf[leftover_start + i];
\r
2105 /* Copy in new characters, removing nulls and \r's */
\r
2106 buf_len = leftover_len;
\r
2107 for (i = 0; i < count; i++) {
\r
2108 if (data[i] != NULLCHAR && data[i] != '\r')
\r
2109 buf[buf_len++] = data[i];
\r
2110 if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' &&
\r
2111 buf[buf_len-3]==' ' && buf[buf_len-2]==' ' && buf[buf_len-1]==' ')
\r
2112 buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous
\r
2115 buf[buf_len] = NULLCHAR;
\r
2116 next_out = leftover_len;
\r
2117 leftover_start = 0;
\r
2120 while (i < buf_len) {
\r
2121 /* Deal with part of the TELNET option negotiation
\r
2122 protocol. We refuse to do anything beyond the
\r
2123 defaults, except that we allow the WILL ECHO option,
\r
2124 which ICS uses to turn off password echoing when we are
\r
2125 directly connected to it. We reject this option
\r
2126 if localLineEditing mode is on (always on in xboard)
\r
2127 and we are talking to port 23, which might be a real
\r
2128 telnet server that will try to keep WILL ECHO on permanently.
\r
2130 if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
\r
2131 static int remoteEchoOption = FALSE; /* telnet ECHO option */
\r
2132 unsigned char option;
\r
2134 switch ((unsigned char) buf[++i]) {
\r
2136 if (appData.debugMode)
\r
2137 fprintf(debugFP, "\n<WILL ");
\r
2138 switch (option = (unsigned char) buf[++i]) {
\r
2140 if (appData.debugMode)
\r
2141 fprintf(debugFP, "ECHO ");
\r
2142 /* Reply only if this is a change, according
\r
2143 to the protocol rules. */
\r
2144 if (remoteEchoOption) break;
\r
2145 if (appData.localLineEditing &&
\r
2146 atoi(appData.icsPort) == TN_PORT) {
\r
2147 TelnetRequest(TN_DONT, TN_ECHO);
\r
2150 TelnetRequest(TN_DO, TN_ECHO);
\r
2151 remoteEchoOption = TRUE;
\r
2155 if (appData.debugMode)
\r
2156 fprintf(debugFP, "%d ", option);
\r
2157 /* Whatever this is, we don't want it. */
\r
2158 TelnetRequest(TN_DONT, option);
\r
2163 if (appData.debugMode)
\r
2164 fprintf(debugFP, "\n<WONT ");
\r
2165 switch (option = (unsigned char) buf[++i]) {
\r
2167 if (appData.debugMode)
\r
2168 fprintf(debugFP, "ECHO ");
\r
2169 /* Reply only if this is a change, according
\r
2170 to the protocol rules. */
\r
2171 if (!remoteEchoOption) break;
\r
2173 TelnetRequest(TN_DONT, TN_ECHO);
\r
2174 remoteEchoOption = FALSE;
\r
2177 if (appData.debugMode)
\r
2178 fprintf(debugFP, "%d ", (unsigned char) option);
\r
2179 /* Whatever this is, it must already be turned
\r
2180 off, because we never agree to turn on
\r
2181 anything non-default, so according to the
\r
2182 protocol rules, we don't reply. */
\r
2187 if (appData.debugMode)
\r
2188 fprintf(debugFP, "\n<DO ");
\r
2189 switch (option = (unsigned char) buf[++i]) {
\r
2191 /* Whatever this is, we refuse to do it. */
\r
2192 if (appData.debugMode)
\r
2193 fprintf(debugFP, "%d ", option);
\r
2194 TelnetRequest(TN_WONT, option);
\r
2199 if (appData.debugMode)
\r
2200 fprintf(debugFP, "\n<DONT ");
\r
2201 switch (option = (unsigned char) buf[++i]) {
\r
2203 if (appData.debugMode)
\r
2204 fprintf(debugFP, "%d ", option);
\r
2205 /* Whatever this is, we are already not doing
\r
2206 it, because we never agree to do anything
\r
2207 non-default, so according to the protocol
\r
2208 rules, we don't reply. */
\r
2213 if (appData.debugMode)
\r
2214 fprintf(debugFP, "\n<IAC ");
\r
2215 /* Doubled IAC; pass it through */
\r
2219 if (appData.debugMode)
\r
2220 fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
\r
2221 /* Drop all other telnet commands on the floor */
\r
2224 if (oldi > next_out)
\r
2225 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2226 if (++i > next_out)
\r
2231 /* OK, this at least will *usually* work */
\r
2232 if (!loggedOn && looking_at(buf, &i, "ics%")) {
\r
2236 if (loggedOn && !intfSet) {
\r
2237 if (ics_type == ICS_ICC) {
\r
2239 "/set-quietly interface %s\n/set-quietly style 12\n",
\r
2242 } else if (ics_type == ICS_CHESSNET) {
\r
2243 sprintf(str, "/style 12\n");
\r
2245 strcpy(str, "alias $ @\n$set interface ");
\r
2246 strcat(str, programVersion);
\r
2247 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
\r
2249 strcat(str, "$iset nohighlight 1\n");
\r
2251 strcat(str, "$iset lock 1\n$style 12\n");
\r
2257 if (started == STARTED_COMMENT) {
\r
2258 /* Accumulate characters in comment */
\r
2259 parse[parse_pos++] = buf[i];
\r
2260 if (buf[i] == '\n') {
\r
2261 parse[parse_pos] = NULLCHAR;
\r
2262 if(!suppressKibitz) // [HGM] kibitz
\r
2263 AppendComment(forwardMostMove, StripHighlight(parse));
\r
2264 else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
\r
2265 int nrDigit = 0, nrAlph = 0, i;
\r
2266 if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
\r
2267 { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
\r
2268 parse[parse_pos] = NULLCHAR;
\r
2269 // try to be smart: if it does not look like search info, it should go to
\r
2270 // ICS interaction window after all, not to engine-output window.
\r
2271 for(i=0; i<parse_pos; i++) { // count letters and digits
\r
2272 nrDigit += (parse[i] >= '0' && parse[i] <= '9');
\r
2273 nrAlph += (parse[i] >= 'a' && parse[i] <= 'z');
\r
2274 nrAlph += (parse[i] >= 'A' && parse[i] <= 'Z');
\r
2276 if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
\r
2277 OutputKibitz(suppressKibitz, parse);
\r
2279 char tmp[MSG_SIZ];
\r
2280 sprintf(tmp, _("your opponent kibitzes: %s"), parse);
\r
2281 SendToPlayer(tmp, strlen(tmp));
\r
2284 started = STARTED_NONE;
\r
2286 /* Don't match patterns against characters in chatter */
\r
2291 if (started == STARTED_CHATTER) {
\r
2292 if (buf[i] != '\n') {
\r
2293 /* Don't match patterns against characters in chatter */
\r
2297 started = STARTED_NONE;
\r
2300 /* Kludge to deal with rcmd protocol */
\r
2301 if (firstTime && looking_at(buf, &i, "\001*")) {
\r
2302 DisplayFatalError(&buf[1], 0, 1);
\r
2305 firstTime = FALSE;
\r
2308 if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
\r
2309 ics_type = ICS_ICC;
\r
2311 if (appData.debugMode)
\r
2312 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2315 if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
\r
2316 ics_type = ICS_FICS;
\r
2318 if (appData.debugMode)
\r
2319 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2322 if (!loggedOn && looking_at(buf, &i, "chess.net")) {
\r
2323 ics_type = ICS_CHESSNET;
\r
2325 if (appData.debugMode)
\r
2326 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2331 (looking_at(buf, &i, "\"*\" is *a registered name") ||
\r
2332 looking_at(buf, &i, "Logging you in as \"*\"") ||
\r
2333 looking_at(buf, &i, "will be \"*\""))) {
\r
2334 strcpy(ics_handle, star_match[0]);
\r
2338 if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
\r
2339 char buf[MSG_SIZ];
\r
2340 sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
\r
2341 DisplayIcsInteractionTitle(buf);
\r
2342 have_set_title = TRUE;
\r
2345 /* skip finger notes */
\r
2346 if (started == STARTED_NONE &&
\r
2347 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
\r
2348 (buf[i] == '1' && buf[i+1] == '0')) &&
\r
2349 buf[i+2] == ':' && buf[i+3] == ' ') {
\r
2350 started = STARTED_CHATTER;
\r
2355 /* skip formula vars */
\r
2356 if (started == STARTED_NONE &&
\r
2357 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
\r
2358 started = STARTED_CHATTER;
\r
2364 // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
\r
2365 if (appData.autoKibitz && started == STARTED_NONE &&
\r
2366 !appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
\r
2367 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
\r
2368 if(looking_at(buf, &i, "* kibitzes: ") &&
\r
2369 (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
\r
2370 StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent
\r
2371 suppressKibitz = TRUE;
\r
2372 if((StrStr(star_match[0], gameInfo.white) == star_match[0]
\r
2373 && (gameMode == IcsPlayingWhite)) ||
\r
2374 (StrStr(star_match[0], gameInfo.black) == star_match[0]
\r
2375 && (gameMode == IcsPlayingBlack)) ) // opponent kibitz
\r
2376 started = STARTED_CHATTER; // own kibitz we simply discard
\r
2378 started = STARTED_COMMENT; // make sure it will be collected in parse[]
\r
2379 parse_pos = 0; parse[0] = NULLCHAR;
\r
2380 savingComment = TRUE;
\r
2381 suppressKibitz = gameMode != IcsObserving ? 2 :
\r
2382 (StrStr(star_match[0], gameInfo.white) == NULL) + 1;
\r
2386 if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz
\r
2387 started = STARTED_CHATTER;
\r
2388 suppressKibitz = TRUE;
\r
2390 } // [HGM] kibitz: end of patch
\r
2392 if (appData.zippyTalk || appData.zippyPlay) {
\r
2393 /* [DM] Backup address for color zippy lines */
\r
2397 if (loggedOn == TRUE)
\r
2398 if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
\r
2399 (appData.zippyPlay && ZippyMatch(buf, &backup)));
\r
2401 if (ZippyControl(buf, &i) ||
\r
2402 ZippyConverse(buf, &i) ||
\r
2403 (appData.zippyPlay && ZippyMatch(buf, &i))) {
\r
2405 if (!appData.colorize) continue;
\r
2409 } // [DM] 'else { ' deleted
\r
2410 if (/* Don't color "message" or "messages" output */
\r
2411 (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
\r
2412 looking_at(buf, &i, "*. * at *:*: ") ||
\r
2413 looking_at(buf, &i, "--* (*:*): ") ||
\r
2414 /* Regular tells and says */
\r
2415 (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
\r
2416 looking_at(buf, &i, "* (your partner) tells you: ") ||
\r
2417 looking_at(buf, &i, "* says: ") ||
\r
2418 /* Message notifications (same color as tells) */
\r
2419 looking_at(buf, &i, "* has left a message ") ||
\r
2420 looking_at(buf, &i, "* just sent you a message:\n") ||
\r
2421 /* Whispers and kibitzes */
\r
2422 (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
\r
2423 looking_at(buf, &i, "* kibitzes: ") ||
\r
2424 /* Channel tells */
\r
2425 (tkind = 3, looking_at(buf, &i, "*(*: "))) {
\r
2427 if (tkind == 1 && strchr(star_match[0], ':')) {
\r
2428 /* Avoid "tells you:" spoofs in channels */
\r
2431 if (star_match[0][0] == NULLCHAR ||
\r
2432 strchr(star_match[0], ' ') ||
\r
2433 (tkind == 3 && strchr(star_match[1], ' '))) {
\r
2434 /* Reject bogus matches */
\r
2437 if (appData.colorize) {
\r
2438 if (oldi > next_out) {
\r
2439 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2444 Colorize(ColorTell, FALSE);
\r
2445 curColor = ColorTell;
\r
2448 Colorize(ColorKibitz, FALSE);
\r
2449 curColor = ColorKibitz;
\r
2452 p = strrchr(star_match[1], '(');
\r
2454 p = star_match[1];
\r
2458 if (atoi(p) == 1) {
\r
2459 Colorize(ColorChannel1, FALSE);
\r
2460 curColor = ColorChannel1;
\r
2462 Colorize(ColorChannel, FALSE);
\r
2463 curColor = ColorChannel;
\r
2467 curColor = ColorNormal;
\r
2471 if (started == STARTED_NONE && appData.autoComment &&
\r
2472 (gameMode == IcsObserving ||
\r
2473 gameMode == IcsPlayingWhite ||
\r
2474 gameMode == IcsPlayingBlack)) {
\r
2475 parse_pos = i - oldi;
\r
2476 memcpy(parse, &buf[oldi], parse_pos);
\r
2477 parse[parse_pos] = NULLCHAR;
\r
2478 started = STARTED_COMMENT;
\r
2479 savingComment = TRUE;
\r
2481 started = STARTED_CHATTER;
\r
2482 savingComment = FALSE;
\r
2489 if (looking_at(buf, &i, "* s-shouts: ") ||
\r
2490 looking_at(buf, &i, "* c-shouts: ")) {
\r
2491 if (appData.colorize) {
\r
2492 if (oldi > next_out) {
\r
2493 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2496 Colorize(ColorSShout, FALSE);
\r
2497 curColor = ColorSShout;
\r
2500 started = STARTED_CHATTER;
\r
2504 if (looking_at(buf, &i, "--->")) {
\r
2509 if (looking_at(buf, &i, "* shouts: ") ||
\r
2510 looking_at(buf, &i, "--> ")) {
\r
2511 if (appData.colorize) {
\r
2512 if (oldi > next_out) {
\r
2513 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2516 Colorize(ColorShout, FALSE);
\r
2517 curColor = ColorShout;
\r
2520 started = STARTED_CHATTER;
\r
2524 if (looking_at( buf, &i, "Challenge:")) {
\r
2525 if (appData.colorize) {
\r
2526 if (oldi > next_out) {
\r
2527 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2530 Colorize(ColorChallenge, FALSE);
\r
2531 curColor = ColorChallenge;
\r
2537 if (looking_at(buf, &i, "* offers you") ||
\r
2538 looking_at(buf, &i, "* offers to be") ||
\r
2539 looking_at(buf, &i, "* would like to") ||
\r
2540 looking_at(buf, &i, "* requests to") ||
\r
2541 looking_at(buf, &i, "Your opponent offers") ||
\r
2542 looking_at(buf, &i, "Your opponent requests")) {
\r
2544 if (appData.colorize) {
\r
2545 if (oldi > next_out) {
\r
2546 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2549 Colorize(ColorRequest, FALSE);
\r
2550 curColor = ColorRequest;
\r
2555 if (looking_at(buf, &i, "* (*) seeking")) {
\r
2556 if (appData.colorize) {
\r
2557 if (oldi > next_out) {
\r
2558 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2561 Colorize(ColorSeek, FALSE);
\r
2562 curColor = ColorSeek;
\r
2567 if (looking_at(buf, &i, "\\ ")) {
\r
2568 if (prevColor != ColorNormal) {
\r
2569 if (oldi > next_out) {
\r
2570 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2573 Colorize(prevColor, TRUE);
\r
2574 curColor = prevColor;
\r
2576 if (savingComment) {
\r
2577 parse_pos = i - oldi;
\r
2578 memcpy(parse, &buf[oldi], parse_pos);
\r
2579 parse[parse_pos] = NULLCHAR;
\r
2580 started = STARTED_COMMENT;
\r
2582 started = STARTED_CHATTER;
\r
2587 if (looking_at(buf, &i, "Black Strength :") ||
\r
2588 looking_at(buf, &i, "<<< style 10 board >>>") ||
\r
2589 looking_at(buf, &i, "<10>") ||
\r
2590 looking_at(buf, &i, "#@#")) {
\r
2591 /* Wrong board style */
\r
2593 SendToICS(ics_prefix);
\r
2594 SendToICS("set style 12\n");
\r
2595 SendToICS(ics_prefix);
\r
2596 SendToICS("refresh\n");
\r
2600 if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
\r
2602 have_sent_ICS_logon = 1;
\r
2606 if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
\r
2607 (looking_at(buf, &i, "\n<12> ") ||
\r
2608 looking_at(buf, &i, "<12> "))) {
\r
2610 if (oldi > next_out) {
\r
2611 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2614 started = STARTED_BOARD;
\r
2619 if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
\r
2620 looking_at(buf, &i, "<b1> ")) {
\r
2621 if (oldi > next_out) {
\r
2622 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2625 started = STARTED_HOLDINGS;
\r
2630 if (looking_at(buf, &i, "* *vs. * *--- *")) {
\r
2632 /* Header for a move list -- first line */
\r
2634 switch (ics_getting_history) {
\r
2636 switch (gameMode) {
\r
2638 case BeginningOfGame:
\r
2639 /* User typed "moves" or "oldmoves" while we
\r
2640 were idle. Pretend we asked for these
\r
2641 moves and soak them up so user can step
\r
2642 through them and/or save them.
\r
2644 Reset(FALSE, TRUE);
\r
2645 gameMode = IcsObserving;
\r
2648 ics_getting_history = H_GOT_UNREQ_HEADER;
\r
2650 case EditGame: /*?*/
\r
2651 case EditPosition: /*?*/
\r
2652 /* Should above feature work in these modes too? */
\r
2653 /* For now it doesn't */
\r
2654 ics_getting_history = H_GOT_UNWANTED_HEADER;
\r
2657 ics_getting_history = H_GOT_UNWANTED_HEADER;
\r
2662 /* Is this the right one? */
\r
2663 if (gameInfo.white && gameInfo.black &&
\r
2664 strcmp(gameInfo.white, star_match[0]) == 0 &&
\r
2665 strcmp(gameInfo.black, star_match[2]) == 0) {
\r
2667 ics_getting_history = H_GOT_REQ_HEADER;
\r
2670 case H_GOT_REQ_HEADER:
\r
2671 case H_GOT_UNREQ_HEADER:
\r
2672 case H_GOT_UNWANTED_HEADER:
\r
2673 case H_GETTING_MOVES:
\r
2674 /* Should not happen */
\r
2675 DisplayError(_("Error gathering move list: two headers"), 0);
\r
2676 ics_getting_history = H_FALSE;
\r
2680 /* Save player ratings into gameInfo if needed */
\r
2681 if ((ics_getting_history == H_GOT_REQ_HEADER ||
\r
2682 ics_getting_history == H_GOT_UNREQ_HEADER) &&
\r
2683 (gameInfo.whiteRating == -1 ||
\r
2684 gameInfo.blackRating == -1)) {
\r
2686 gameInfo.whiteRating = string_to_rating(star_match[1]);
\r
2687 gameInfo.blackRating = string_to_rating(star_match[3]);
\r
2688 if (appData.debugMode)
\r
2689 fprintf(debugFP, _("Ratings from header: W %d, B %d\n"),
\r
2690 gameInfo.whiteRating, gameInfo.blackRating);
\r
2695 if (looking_at(buf, &i,
\r
2696 "* * match, initial time: * minute*, increment: * second")) {
\r
2697 /* Header for a move list -- second line */
\r
2698 /* Initial board will follow if this is a wild game */
\r
2699 if (gameInfo.event != NULL) free(gameInfo.event);
\r
2700 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
\r
2701 gameInfo.event = StrSave(str);
\r
2702 /* [HGM] we switched variant. Translate boards if needed. */
\r
2703 VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));
\r
2707 if (looking_at(buf, &i, "Move ")) {
\r
2708 /* Beginning of a move list */
\r
2709 switch (ics_getting_history) {
\r
2711 /* Normally should not happen */
\r
2712 /* Maybe user hit reset while we were parsing */
\r
2715 /* Happens if we are ignoring a move list that is not
\r
2716 * the one we just requested. Common if the user
\r
2717 * tries to observe two games without turning off
\r
2720 case H_GETTING_MOVES:
\r
2721 /* Should not happen */
\r
2722 DisplayError(_("Error gathering move list: nested"), 0);
\r
2723 ics_getting_history = H_FALSE;
\r
2725 case H_GOT_REQ_HEADER:
\r
2726 ics_getting_history = H_GETTING_MOVES;
\r
2727 started = STARTED_MOVES;
\r
2729 if (oldi > next_out) {
\r
2730 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2733 case H_GOT_UNREQ_HEADER:
\r
2734 ics_getting_history = H_GETTING_MOVES;
\r
2735 started = STARTED_MOVES_NOHIDE;
\r
2738 case H_GOT_UNWANTED_HEADER:
\r
2739 ics_getting_history = H_FALSE;
\r
2745 if (looking_at(buf, &i, "% ") ||
\r
2746 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
\r
2747 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
\r
2748 savingComment = FALSE;
\r
2749 switch (started) {
\r
2750 case STARTED_MOVES:
\r
2751 case STARTED_MOVES_NOHIDE:
\r
2752 memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
\r
2753 parse[parse_pos + i - oldi] = NULLCHAR;
\r
2754 ParseGameHistory(parse);
\r
2756 if (appData.zippyPlay && first.initDone) {
\r
2757 FeedMovesToProgram(&first, forwardMostMove);
\r
2758 if (gameMode == IcsPlayingWhite) {
\r
2759 if (WhiteOnMove(forwardMostMove)) {
\r
2760 if (first.sendTime) {
\r
2761 if (first.useColors) {
\r
2762 SendToProgram("black\n", &first);
\r
2764 SendTimeRemaining(&first, TRUE);
\r
2767 if (first.useColors) {
\r
2768 SendToProgram("white\ngo\n", &first);
\r
2770 SendToProgram("go\n", &first);
\r
2773 if (first.useColors) {
\r
2774 SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent
\r
2776 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos
\r
2778 first.maybeThinking = TRUE;
\r
2780 if (first.usePlayother) {
\r
2781 if (first.sendTime) {
\r
2782 SendTimeRemaining(&first, TRUE);
\r
2784 SendToProgram("playother\n", &first);
\r
2785 firstMove = FALSE;
\r
2790 } else if (gameMode == IcsPlayingBlack) {
\r
2791 if (!WhiteOnMove(forwardMostMove)) {
\r
2792 if (first.sendTime) {
\r
2793 if (first.useColors) {
\r
2794 SendToProgram("white\n", &first);
\r
2796 SendTimeRemaining(&first, FALSE);
\r
2799 if (first.useColors) {
\r
2800 SendToProgram("black\ngo\n", &first);
\r
2802 SendToProgram("go\n", &first);
\r
2805 if (first.useColors) {
\r
2806 SendToProgram("black\n", &first);
\r
2808 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);
\r
2810 first.maybeThinking = TRUE;
\r
2812 if (first.usePlayother) {
\r
2813 if (first.sendTime) {
\r
2814 SendTimeRemaining(&first, FALSE);
\r
2816 SendToProgram("playother\n", &first);
\r
2817 firstMove = FALSE;
\r
2825 if (gameMode == IcsObserving && ics_gamenum == -1) {
\r
2826 /* Moves came from oldmoves or moves command
\r
2827 while we weren't doing anything else.
\r
2829 currentMove = forwardMostMove;
\r
2830 ClearHighlights();/*!!could figure this out*/
\r
2831 flipView = appData.flipView;
\r
2832 DrawPosition(FALSE, boards[currentMove]);
\r
2833 DisplayBothClocks();
\r
2834 sprintf(str, "%s vs. %s",
\r
2835 gameInfo.white, gameInfo.black);
\r
2836 DisplayTitle(str);
\r
2837 gameMode = IcsIdle;
\r
2839 /* Moves were history of an active game */
\r
2840 if (gameInfo.resultDetails != NULL) {
\r
2841 free(gameInfo.resultDetails);
\r
2842 gameInfo.resultDetails = NULL;
\r
2845 HistorySet(parseList, backwardMostMove,
\r
2846 forwardMostMove, currentMove-1);
\r
2847 DisplayMove(currentMove - 1);
\r
2848 if (started == STARTED_MOVES) next_out = i;
\r
2849 started = STARTED_NONE;
\r
2850 ics_getting_history = H_FALSE;
\r
2853 case STARTED_OBSERVE:
\r
2854 started = STARTED_NONE;
\r
2855 SendToICS(ics_prefix);
\r
2856 SendToICS("refresh\n");
\r
2862 if(bookHit) { // [HGM] book: simulate book reply
\r
2863 static char bookMove[MSG_SIZ]; // a bit generous?
\r
2865 programStats.nodes = programStats.depth = programStats.time =
\r
2866 programStats.score = programStats.got_only_move = 0;
\r
2867 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
2869 strcpy(bookMove, "move ");
\r
2870 strcat(bookMove, bookHit);
\r
2871 HandleMachineMove(bookMove, &first);
\r
2876 if ((started == STARTED_MOVES || started == STARTED_BOARD ||
\r
2877 started == STARTED_HOLDINGS ||
\r
2878 started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
\r
2879 /* Accumulate characters in move list or board */
\r
2880 parse[parse_pos++] = buf[i];
\r
2883 /* Start of game messages. Mostly we detect start of game
\r
2884 when the first board image arrives. On some versions
\r
2885 of the ICS, though, we need to do a "refresh" after starting
\r
2886 to observe in order to get the current board right away. */
\r
2887 if (looking_at(buf, &i, "Adding game * to observation list")) {
\r
2888 started = STARTED_OBSERVE;
\r
2892 /* Handle auto-observe */
\r
2893 if (appData.autoObserve &&
\r
2894 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
\r
2895 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
\r
2897 /* Choose the player that was highlighted, if any. */
\r
2898 if (star_match[0][0] == '\033' ||
\r
2899 star_match[1][0] != '\033') {
\r
2900 player = star_match[0];
\r
2902 player = star_match[2];
\r
2904 sprintf(str, "%sobserve %s\n",
\r
2905 ics_prefix, StripHighlightAndTitle(player));
\r
2908 /* Save ratings from notify string */
\r
2909 strcpy(player1Name, star_match[0]);
\r
2910 player1Rating = string_to_rating(star_match[1]);
\r
2911 strcpy(player2Name, star_match[2]);
\r
2912 player2Rating = string_to_rating(star_match[3]);
\r
2914 if (appData.debugMode)
\r
2916 "Ratings from 'Game notification:' %s %d, %s %d\n",
\r
2917 player1Name, player1Rating,
\r
2918 player2Name, player2Rating);
\r
2923 /* Deal with automatic examine mode after a game,
\r
2924 and with IcsObserving -> IcsExamining transition */
\r
2925 if (looking_at(buf, &i, "Entering examine mode for game *") ||
\r
2926 looking_at(buf, &i, "has made you an examiner of game *")) {
\r
2928 int gamenum = atoi(star_match[0]);
\r
2929 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
\r
2930 gamenum == ics_gamenum) {
\r
2931 /* We were already playing or observing this game;
\r
2932 no need to refetch history */
\r
2933 gameMode = IcsExamining;
\r
2935 pauseExamForwardMostMove = forwardMostMove;
\r
2936 } else if (currentMove < forwardMostMove) {
\r
2937 ForwardInner(forwardMostMove);
\r
2940 /* I don't think this case really can happen */
\r
2941 SendToICS(ics_prefix);
\r
2942 SendToICS("refresh\n");
\r
2947 /* Error messages */
\r
2948 if (ics_user_moved) {
\r
2949 if (looking_at(buf, &i, "Illegal move") ||
\r
2950 looking_at(buf, &i, "Not a legal move") ||
\r
2951 looking_at(buf, &i, "Your king is in check") ||
\r
2952 looking_at(buf, &i, "It isn't your turn") ||
\r
2953 looking_at(buf, &i, "It is not your move")) {
\r
2954 /* Illegal move */
\r
2955 ics_user_moved = 0;
\r
2956 if (forwardMostMove > backwardMostMove) {
\r
2957 currentMove = --forwardMostMove;
\r
2958 DisplayMove(currentMove - 1); /* before DMError */
\r
2959 DisplayMoveError(_("Illegal move (rejected by ICS)"));
\r
2960 DrawPosition(FALSE, boards[currentMove]);
\r
2962 DisplayBothClocks();
\r
2968 if (looking_at(buf, &i, "still have time") ||
\r
2969 looking_at(buf, &i, "not out of time") ||
\r
2970 looking_at(buf, &i, "either player is out of time") ||
\r
2971 looking_at(buf, &i, "has timeseal; checking")) {
\r
2972 /* We must have called his flag a little too soon */
\r
2973 whiteFlag = blackFlag = FALSE;
\r
2977 if (looking_at(buf, &i, "added * seconds to") ||
\r
2978 looking_at(buf, &i, "seconds were added to")) {
\r
2979 /* Update the clocks */
\r
2980 SendToICS(ics_prefix);
\r
2981 SendToICS("refresh\n");
\r
2985 if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
\r
2986 ics_clock_paused = TRUE;
\r
2991 if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
\r
2992 ics_clock_paused = FALSE;
\r
2997 /* Grab player ratings from the Creating: message.
\r
2998 Note we have to check for the special case when
\r
2999 the ICS inserts things like [white] or [black]. */
\r
3000 if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
\r
3001 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
\r
3003 0 player 1 name (not necessarily white)
\r
3005 2 empty, white, or black (IGNORED)
\r
3006 3 player 2 name (not necessarily black)
\r
3009 The names/ratings are sorted out when the game
\r
3010 actually starts (below).
\r
3012 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
\r
3013 player1Rating = string_to_rating(star_match[1]);
\r
3014 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
\r
3015 player2Rating = string_to_rating(star_match[4]);
\r
3017 if (appData.debugMode)
\r
3019 "Ratings from 'Creating:' %s %d, %s %d\n",
\r
3020 player1Name, player1Rating,
\r
3021 player2Name, player2Rating);
\r
3026 /* Improved generic start/end-of-game messages */
\r
3027 if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
\r
3028 (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
\r
3029 /* If tkind == 0: */
\r
3030 /* star_match[0] is the game number */
\r
3031 /* [1] is the white player's name */
\r
3032 /* [2] is the black player's name */
\r
3033 /* For end-of-game: */
\r
3034 /* [3] is the reason for the game end */
\r
3035 /* [4] is a PGN end game-token, preceded by " " */
\r
3036 /* For start-of-game: */
\r
3037 /* [3] begins with "Creating" or "Continuing" */
\r
3038 /* [4] is " *" or empty (don't care). */
\r
3039 int gamenum = atoi(star_match[0]);
\r
3040 char *whitename, *blackname, *why, *endtoken;
\r
3041 ChessMove endtype = (ChessMove) 0;
\r
3044 whitename = star_match[1];
\r
3045 blackname = star_match[2];
\r
3046 why = star_match[3];
\r
3047 endtoken = star_match[4];
\r
3049 whitename = star_match[1];
\r
3050 blackname = star_match[3];
\r
3051 why = star_match[5];
\r
3052 endtoken = star_match[6];
\r
3055 /* Game start messages */
\r
3056 if (strncmp(why, "Creating ", 9) == 0 ||
\r
3057 strncmp(why, "Continuing ", 11) == 0) {
\r
3058 gs_gamenum = gamenum;
\r
3059 strcpy(gs_kind, strchr(why, ' ') + 1);
\r
3061 if (appData.zippyPlay) {
\r
3062 ZippyGameStart(whitename, blackname);
\r
3068 /* Game end messages */
\r
3069 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
\r
3070 ics_gamenum != gamenum) {
\r
3073 while (endtoken[0] == ' ') endtoken++;
\r
3074 switch (endtoken[0]) {
\r
3077 endtype = GameUnfinished;
\r
3080 endtype = BlackWins;
\r
3083 if (endtoken[1] == '/')
\r
3084 endtype = GameIsDrawn;
\r
3086 endtype = WhiteWins;
\r
3089 GameEnds(endtype, why, GE_ICS);
\r
3091 if (appData.zippyPlay && first.initDone) {
\r
3092 ZippyGameEnd(endtype, why);
\r
3093 if (first.pr == NULL) {
\r
3094 /* Start the next process early so that we'll
\r
3095 be ready for the next challenge */
\r
3096 StartChessProgram(&first);
\r
3098 /* Send "new" early, in case this command takes
\r
3099 a long time to finish, so that we'll be ready
\r
3100 for the next challenge. */
\r
3101 gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'
\r
3102 Reset(TRUE, TRUE);
\r
3108 if (looking_at(buf, &i, "Removing game * from observation") ||
\r
3109 looking_at(buf, &i, "no longer observing game *") ||
\r
3110 looking_at(buf, &i, "Game * (*) has no examiners")) {
\r
3111 if (gameMode == IcsObserving &&
\r
3112 atoi(star_match[0]) == ics_gamenum)
\r
3114 /* icsEngineAnalyze */
\r
3115 if (appData.icsEngineAnalyze) {
\r
3116 ExitAnalyzeMode();
\r
3120 gameMode = IcsIdle;
\r
3122 ics_user_moved = FALSE;
\r
3127 if (looking_at(buf, &i, "no longer examining game *")) {
\r
3128 if (gameMode == IcsExamining &&
\r
3129 atoi(star_match[0]) == ics_gamenum)
\r
3131 gameMode = IcsIdle;
\r
3133 ics_user_moved = FALSE;
\r
3138 /* Advance leftover_start past any newlines we find,
\r
3139 so only partial lines can get reparsed */
\r
3140 if (looking_at(buf, &i, "\n")) {
\r
3141 prevColor = curColor;
\r
3142 if (curColor != ColorNormal) {
\r
3143 if (oldi > next_out) {
\r
3144 SendToPlayer(&buf[next_out], oldi - next_out);
\r
3147 Colorize(ColorNormal, FALSE);
\r
3148 curColor = ColorNormal;
\r
3150 if (started == STARTED_BOARD) {
\r
3151 started = STARTED_NONE;
\r
3152 parse[parse_pos] = NULLCHAR;
\r
3153 ParseBoard12(parse);
\r
3154 ics_user_moved = 0;
\r
3156 /* Send premove here */
\r
3157 if (appData.premove) {
\r
3158 char str[MSG_SIZ];
\r
3159 if (currentMove == 0 &&
\r
3160 gameMode == IcsPlayingWhite &&
\r
3161 appData.premoveWhite) {
\r
3162 sprintf(str, "%s%s\n", ics_prefix,
\r
3163 appData.premoveWhiteText);
\r
3164 if (appData.debugMode)
\r
3165 fprintf(debugFP, "Sending premove:\n");
\r
3167 } else if (currentMove == 1 &&
\r
3168 gameMode == IcsPlayingBlack &&
\r
3169 appData.premoveBlack) {
\r
3170 sprintf(str, "%s%s\n", ics_prefix,
\r
3171 appData.premoveBlackText);
\r
3172 if (appData.debugMode)
\r
3173 fprintf(debugFP, "Sending premove:\n");
\r
3175 } else if (gotPremove) {
\r
3177 ClearPremoveHighlights();
\r
3178 if (appData.debugMode)
\r
3179 fprintf(debugFP, "Sending premove:\n");
\r
3180 UserMoveEvent(premoveFromX, premoveFromY,
\r
3181 premoveToX, premoveToY,
\r
3182 premovePromoChar);
\r
3186 /* Usually suppress following prompt */
\r
3187 if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
\r
3188 if (looking_at(buf, &i, "*% ")) {
\r
3189 savingComment = FALSE;
\r
3193 } else if (started == STARTED_HOLDINGS) {
\r
3195 char new_piece[MSG_SIZ];
\r
3196 started = STARTED_NONE;
\r
3197 parse[parse_pos] = NULLCHAR;
\r
3198 if (appData.debugMode)
\r
3199 fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",
\r
3200 parse, currentMove);
\r
3201 if (sscanf(parse, " game %d", &gamenum) == 1 &&
\r
3202 gamenum == ics_gamenum) {
\r
3203 if (gameInfo.variant == VariantNormal) {
\r
3204 /* [HGM] We seem to switch variant during a game!
\r
3205 * Presumably no holdings were displayed, so we have
\r
3206 * to move the position two files to the right to
\r
3207 * create room for them!
\r
3209 VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */
\r
3210 /* Get a move list just to see the header, which
\r
3211 will tell us whether this is really bug or zh */
\r
3212 if (ics_getting_history == H_FALSE) {
\r
3213 ics_getting_history = H_REQUESTED;
\r
3214 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
\r
3218 new_piece[0] = NULLCHAR;
\r
3219 sscanf(parse, "game %d white [%s black [%s <- %s",
\r
3220 &gamenum, white_holding, black_holding,
\r
3222 white_holding[strlen(white_holding)-1] = NULLCHAR;
\r
3223 black_holding[strlen(black_holding)-1] = NULLCHAR;
\r
3224 /* [HGM] copy holdings to board holdings area */
\r
3225 CopyHoldings(boards[currentMove], white_holding, WhitePawn);
\r
3226 CopyHoldings(boards[currentMove], black_holding, BlackPawn);
\r
3228 if (appData.zippyPlay && first.initDone) {
\r
3229 ZippyHoldings(white_holding, black_holding,
\r
3233 if (tinyLayout || smallLayout) {
\r
3234 char wh[16], bh[16];
\r
3235 PackHolding(wh, white_holding);
\r
3236 PackHolding(bh, black_holding);
\r
3237 sprintf(str, "[%s-%s] %s-%s", wh, bh,
\r
3238 gameInfo.white, gameInfo.black);
\r
3240 sprintf(str, "%s [%s] vs. %s [%s]",
\r
3241 gameInfo.white, white_holding,
\r
3242 gameInfo.black, black_holding);
\r
3245 DrawPosition(FALSE, boards[currentMove]);
\r
3246 DisplayTitle(str);
\r
3248 /* Suppress following prompt */
\r
3249 if (looking_at(buf, &i, "*% ")) {
\r
3250 savingComment = FALSE;
\r
3257 i++; /* skip unparsed character and loop back */
\r
3260 if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window
\r
3261 started != STARTED_HOLDINGS && i > next_out) {
\r
3262 SendToPlayer(&buf[next_out], i - next_out);
\r
3265 suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above
\r
3267 leftover_len = buf_len - leftover_start;
\r
3268 /* if buffer ends with something we couldn't parse,
\r
3269 reparse it after appending the next read */
\r
3271 } else if (count == 0) {
\r
3272 RemoveInputSource(isr);
\r
3273 DisplayFatalError(_("Connection closed by ICS"), 0, 0);
\r
3275 DisplayFatalError(_("Error reading from ICS"), error, 1);
\r
3280 /* Board style 12 looks like this:
\r
3282 <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
\r
3284 * The "<12> " is stripped before it gets to this routine. The two
\r
3285 * trailing 0's (flip state and clock ticking) are later addition, and
\r
3286 * some chess servers may not have them, or may have only the first.
\r
3287 * Additional trailing fields may be added in the future.
\r
3290 #define PATTERN "%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
\r
3292 #define RELATION_OBSERVING_PLAYED 0
\r
3293 #define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
\r
3294 #define RELATION_PLAYING_MYMOVE 1
\r
3295 #define RELATION_PLAYING_NOTMYMOVE -1
\r
3296 #define RELATION_EXAMINING 2
\r
3297 #define RELATION_ISOLATED_BOARD -3
\r
3298 #define RELATION_STARTING_POSITION -4 /* FICS only */
\r
3301 ParseBoard12(string)
\r
3304 GameMode newGameMode;
\r
3305 int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
\r
3306 int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
\r
3307 int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
\r
3308 char to_play, board_chars[200];
\r
3309 char move_str[500], str[500], elapsed_time[500];
\r
3310 char black[32], white[32];
\r
3312 int prevMove = currentMove;
\r
3314 ChessMove moveType;
\r
3315 int fromX, fromY, toX, toY;
\r
3317 int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
\r
3318 char *bookHit = NULL; // [HGM] book
\r
3320 fromX = fromY = toX = toY = -1;
\r
3324 if (appData.debugMode)
\r
3325 fprintf(debugFP, _("Parsing board: %s\n"), string);
\r
3327 move_str[0] = NULLCHAR;
\r
3328 elapsed_time[0] = NULLCHAR;
\r
3329 { /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */
\r
3331 while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {
\r
3332 if(string[i] == ' ') { ranks++; files = 0; }
\r
3336 for(j = 0; j <i; j++) board_chars[j] = string[j];
\r
3337 board_chars[i] = '\0';
\r
3340 n = sscanf(string, PATTERN, &to_play, &double_push,
\r
3341 &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
\r
3342 &gamenum, white, black, &relation, &basetime, &increment,
\r
3343 &white_stren, &black_stren, &white_time, &black_time,
\r
3344 &moveNum, str, elapsed_time, move_str, &ics_flip,
\r
3348 sprintf(str, _("Failed to parse board string:\n\"%s\""), string);
\r
3349 DisplayError(str, 0);
\r
3353 /* Convert the move number to internal form */
\r
3354 moveNum = (moveNum - 1) * 2;
\r
3355 if (to_play == 'B') moveNum++;
\r
3356 if (moveNum >= MAX_MOVES) {
\r
3357 DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
\r
3362 switch (relation) {
\r
3363 case RELATION_OBSERVING_PLAYED:
\r
3364 case RELATION_OBSERVING_STATIC:
\r
3365 if (gamenum == -1) {
\r
3366 /* Old ICC buglet */
\r
3367 relation = RELATION_OBSERVING_STATIC;
\r
3369 newGameMode = IcsObserving;
\r
3371 case RELATION_PLAYING_MYMOVE:
\r
3372 case RELATION_PLAYING_NOTMYMOVE:
\r
3374 ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
\r
3375 IcsPlayingWhite : IcsPlayingBlack;
\r
3377 case RELATION_EXAMINING:
\r
3378 newGameMode = IcsExamining;
\r
3380 case RELATION_ISOLATED_BOARD:
\r
3382 /* Just display this board. If user was doing something else,
\r
3383 we will forget about it until the next board comes. */
\r
3384 newGameMode = IcsIdle;
\r
3386 case RELATION_STARTING_POSITION:
\r
3387 newGameMode = gameMode;
\r
3391 /* Modify behavior for initial board display on move listing
\r
3394 switch (ics_getting_history) {
\r
3398 case H_GOT_REQ_HEADER:
\r
3399 case H_GOT_UNREQ_HEADER:
\r
3400 /* This is the initial position of the current game */
\r
3401 gamenum = ics_gamenum;
\r
3402 moveNum = 0; /* old ICS bug workaround */
\r
3403 if (to_play == 'B') {
\r
3404 startedFromSetupPosition = TRUE;
\r
3405 blackPlaysFirst = TRUE;
\r
3407 if (forwardMostMove == 0) forwardMostMove = 1;
\r
3408 if (backwardMostMove == 0) backwardMostMove = 1;
\r
3409 if (currentMove == 0) currentMove = 1;
\r
3411 newGameMode = gameMode;
\r
3412 relation = RELATION_STARTING_POSITION; /* ICC needs this */
\r
3414 case H_GOT_UNWANTED_HEADER:
\r
3415 /* This is an initial board that we don't want */
\r
3417 case H_GETTING_MOVES:
\r
3418 /* Should not happen */
\r
3419 DisplayError(_("Error gathering move list: extra board"), 0);
\r
3420 ics_getting_history = H_FALSE;
\r
3424 /* Take action if this is the first board of a new game, or of a
\r
3425 different game than is currently being displayed. */
\r
3426 if (gamenum != ics_gamenum || newGameMode != gameMode ||
\r
3427 relation == RELATION_ISOLATED_BOARD) {
\r
3429 /* Forget the old game and get the history (if any) of the new one */
\r
3430 if (gameMode != BeginningOfGame) {
\r
3431 Reset(FALSE, TRUE);
\r
3434 if (appData.autoRaiseBoard) BoardToTop();
\r
3436 if (gamenum == -1) {
\r
3437 newGameMode = IcsIdle;
\r
3438 } else if (moveNum > 0 && newGameMode != IcsIdle &&
\r
3439 appData.getMoveList) {
\r
3440 /* Need to get game history */
\r
3441 ics_getting_history = H_REQUESTED;
\r
3442 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
\r
3446 /* Initially flip the board to have black on the bottom if playing
\r
3447 black or if the ICS flip flag is set, but let the user change
\r
3448 it with the Flip View button. */
\r
3449 flipView = appData.autoFlipView ?
\r
3450 (newGameMode == IcsPlayingBlack) || ics_flip :
\r
3453 /* Done with values from previous mode; copy in new ones */
\r
3454 gameMode = newGameMode;
\r
3456 ics_gamenum = gamenum;
\r
3457 if (gamenum == gs_gamenum) {
\r
3458 int klen = strlen(gs_kind);
\r
3459 if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
\r
3460 sprintf(str, "ICS %s", gs_kind);
\r
3461 gameInfo.event = StrSave(str);
\r
3463 gameInfo.event = StrSave("ICS game");
\r
3465 gameInfo.site = StrSave(appData.icsHost);
\r
3466 gameInfo.date = PGNDate();
\r
3467 gameInfo.round = StrSave("-");
\r
3468 gameInfo.white = StrSave(white);
\r
3469 gameInfo.black = StrSave(black);
\r
3470 timeControl = basetime * 60 * 1000;
\r
3471 timeControl_2 = 0;
\r
3472 timeIncrement = increment * 1000;
\r
3473 movesPerSession = 0;
\r
3474 gameInfo.timeControl = TimeControlTagValue();
\r
3475 VariantSwitch(board, StringToVariant(gameInfo.event) );
\r
3476 if (appData.debugMode) {
\r
3477 fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);
\r