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
75 # include <stdlib.h>
\r
76 # include <string.h>
\r
77 #else /* not STDC_HEADERS */
\r
79 # include <string.h>
\r
80 # else /* not HAVE_STRING_H */
\r
81 # include <strings.h>
\r
82 # endif /* not HAVE_STRING_H */
\r
83 #endif /* not STDC_HEADERS */
\r
85 #if HAVE_SYS_FCNTL_H
\r
86 # include <sys/fcntl.h>
\r
87 #else /* not HAVE_SYS_FCNTL_H */
\r
90 # endif /* HAVE_FCNTL_H */
\r
91 #endif /* not HAVE_SYS_FCNTL_H */
\r
93 #if TIME_WITH_SYS_TIME
\r
94 # include <sys/time.h>
\r
97 # if HAVE_SYS_TIME_H
\r
98 # include <sys/time.h>
\r
104 #if defined(_amigados) && !defined(__GNUC__)
\r
106 int tz_minuteswest;
\r
109 extern int gettimeofday(struct timeval *, struct timezone *);
\r
113 # include <unistd.h>
\r
116 #include "common.h"
\r
117 #include "frontend.h"
\r
118 #include "backend.h"
\r
119 #include "parser.h"
\r
122 # include "zippy.h"
\r
124 #include "backendz.h"
\r
125 #include "gettext.h"
\r
128 # define _(s) gettext (s)
\r
129 # define N_(s) gettext_noop (s)
\r
136 /* A point in time */
\r
138 long sec; /* Assuming this is >= 32 bits */
\r
139 int ms; /* Assuming this is >= 16 bits */
\r
142 int establish P((void));
\r
143 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
\r
144 char *buf, int count, int error));
\r
145 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
\r
146 char *buf, int count, int error));
\r
147 void SendToICS P((char *s));
\r
148 void SendToICSDelayed P((char *s, long msdelay));
\r
149 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
\r
150 int toX, int toY));
\r
151 void InitPosition P((int redraw));
\r
152 void HandleMachineMove P((char *message, ChessProgramState *cps));
\r
153 int AutoPlayOneMove P((void));
\r
154 int LoadGameOneMove P((ChessMove readAhead));
\r
155 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
\r
156 int LoadPositionFromFile P((char *filename, int n, char *title));
\r
157 int SavePositionToFile P((char *filename));
\r
158 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
\r
160 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
\r
161 void ShowMove P((int fromX, int fromY, int toX, int toY));
\r
162 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
163 /*char*/int promoChar));
\r
164 void BackwardInner P((int target));
\r
165 void ForwardInner P((int target));
\r
166 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
\r
167 void EditPositionDone P((void));
\r
168 void PrintOpponents P((FILE *fp));
\r
169 void PrintPosition P((FILE *fp, int move));
\r
170 void StartChessProgram P((ChessProgramState *cps));
\r
171 void SendToProgram P((char *message, ChessProgramState *cps));
\r
172 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
\r
173 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
\r
174 char *buf, int count, int error));
\r
175 void SendTimeControl P((ChessProgramState *cps,
\r
176 int mps, long tc, int inc, int sd, int st));
\r
177 char *TimeControlTagValue P((void));
\r
178 void Attention P((ChessProgramState *cps));
\r
179 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
\r
180 void ResurrectChessProgram P((void));
\r
181 void DisplayComment P((int moveNumber, char *text));
\r
182 void DisplayMove P((int moveNumber));
\r
183 void DisplayAnalysis P((void));
\r
185 void ParseGameHistory P((char *game));
\r
186 void ParseBoard12 P((char *string));
\r
187 void StartClocks P((void));
\r
188 void SwitchClocks P((void));
\r
189 void StopClocks P((void));
\r
190 void ResetClocks P((void));
\r
191 char *PGNDate P((void));
\r
192 void SetGameInfo P((void));
\r
193 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
194 int RegisterMove P((void));
\r
195 void MakeRegisteredMove P((void));
\r
196 void TruncateGame P((void));
\r
197 int looking_at P((char *, int *, char *));
\r
198 void CopyPlayerNameIntoFileName P((char **, char *));
\r
199 char *SavePart P((char *));
\r
200 int SaveGameOldStyle P((FILE *));
\r
201 int SaveGamePGN P((FILE *));
\r
202 void GetTimeMark P((TimeMark *));
\r
203 long SubtractTimeMarks P((TimeMark *, TimeMark *));
\r
204 int CheckFlags P((void));
\r
205 long NextTickLength P((long));
\r
206 void CheckTimeControl P((void));
\r
207 void show_bytes P((FILE *, char *, int));
\r
208 int string_to_rating P((char *str));
\r
209 void ParseFeatures P((char* args, ChessProgramState *cps));
\r
210 void InitBackEnd3 P((void));
\r
211 void FeatureDone P((ChessProgramState* cps, int val));
\r
212 void InitChessProgram P((ChessProgramState *cps, int setup));
\r
215 extern void ConsoleCreate();
\r
218 ChessProgramState *WhitePlayer();
\r
219 void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c
\r
220 int VerifyDisplayMode P(());
\r
222 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment
\r
223 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c
\r
224 char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
\r
225 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
\r
226 extern char installDir[MSG_SIZ];
\r
228 extern int tinyLayout, smallLayout;
\r
229 ChessProgramStats programStats;
\r
230 static int exiting = 0; /* [HGM] moved to top */
\r
231 static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;
\r
232 extern int startedFromPositionFile;
\r
233 int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
\r
234 char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */
\r
235 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */
\r
236 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
\r
237 int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
\r
238 int opponentKibitzes;
\r
240 /* States for ics_getting_history */
\r
242 #define H_REQUESTED 1
\r
243 #define H_GOT_REQ_HEADER 2
\r
244 #define H_GOT_UNREQ_HEADER 3
\r
245 #define H_GETTING_MOVES 4
\r
246 #define H_GOT_UNWANTED_HEADER 5
\r
248 /* whosays values for GameEnds */
\r
250 #define GE_ENGINE 1
\r
251 #define GE_PLAYER 2
\r
253 #define GE_XBOARD 4
\r
254 #define GE_ENGINE1 5
\r
255 #define GE_ENGINE2 6
\r
257 /* Maximum number of games in a cmail message */
\r
258 #define CMAIL_MAX_GAMES 20
\r
260 /* Different types of move when calling RegisterMove */
\r
261 #define CMAIL_MOVE 0
\r
262 #define CMAIL_RESIGN 1
\r
263 #define CMAIL_DRAW 2
\r
264 #define CMAIL_ACCEPT 3
\r
266 /* Different types of result to remember for each game */
\r
267 #define CMAIL_NOT_RESULT 0
\r
268 #define CMAIL_OLD_RESULT 1
\r
269 #define CMAIL_NEW_RESULT 2
\r
271 /* Telnet protocol constants */
\r
272 #define TN_WILL 0373
\r
273 #define TN_WONT 0374
\r
275 #define TN_DONT 0376
\r
276 #define TN_IAC 0377
\r
277 #define TN_ECHO 0001
\r
278 #define TN_SGA 0003
\r
282 static char * safeStrCpy( char * dst, const char * src, size_t count )
\r
284 assert( dst != NULL );
\r
285 assert( src != NULL );
\r
286 assert( count > 0 );
\r
288 strncpy( dst, src, count );
\r
289 dst[ count-1 ] = '\0';
\r
293 static char * safeStrCat( char * dst, const char * src, size_t count )
\r
297 assert( dst != NULL );
\r
298 assert( src != NULL );
\r
299 assert( count > 0 );
\r
301 dst_len = strlen(dst);
\r
303 assert( count > dst_len ); /* Buffer size must be greater than current length */
\r
305 safeStrCpy( dst + dst_len, src, count - dst_len );
\r
310 /* Some compiler can't cast u64 to double
\r
311 * This function do the job for us:
\r
313 * We use the highest bit for cast, this only
\r
314 * works if the highest bit is not
\r
315 * in use (This should not happen)
\r
317 * We used this for all compiler
\r
320 u64ToDouble(u64 value)
\r
323 u64 tmp = value & u64Const(0x7fffffffffffffff);
\r
324 r = (double)(s64)tmp;
\r
325 if (value & u64Const(0x8000000000000000))
\r
326 r += 9.2233720368547758080e18; /* 2^63 */
\r
330 /* Fake up flags for now, as we aren't keeping track of castling
\r
331 availability yet. [HGM] Change of logic: the flag now only
\r
332 indicates the type of castlings allowed by the rule of the game.
\r
333 The actual rights themselves are maintained in the array
\r
334 castlingRights, as part of the game history, and are not probed
\r
340 int flags = F_ALL_CASTLE_OK;
\r
341 if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
\r
342 switch (gameInfo.variant) {
\r
343 case VariantSuicide:
\r
344 flags &= ~F_ALL_CASTLE_OK;
\r
345 case VariantGiveaway: // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!
\r
346 flags |= F_IGNORE_CHECK;
\r
348 case VariantAtomic:
\r
349 flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
\r
351 case VariantKriegspiel:
\r
352 flags |= F_KRIEGSPIEL_CAPTURE;
\r
354 case VariantCapaRandom:
\r
355 case VariantFischeRandom:
\r
356 flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */
\r
357 case VariantNoCastle:
\r
358 case VariantShatranj:
\r
359 case VariantCourier:
\r
360 flags &= ~F_ALL_CASTLE_OK;
\r
368 FILE *gameFileFP, *debugFP;
\r
371 [AS] Note: sometimes, the sscanf() function is used to parse the input
\r
372 into a fixed-size buffer. Because of this, we must be prepared to
\r
373 receive strings as long as the size of the input buffer, which is currently
\r
374 set to 4K for Windows and 8K for the rest.
\r
375 So, we must either allocate sufficiently large buffers here, or
\r
376 reduce the size of the input buffer in the input reading part.
\r
379 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
\r
380 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
\r
381 char thinkOutput1[MSG_SIZ*10];
\r
383 ChessProgramState first, second;
\r
385 /* premove variables */
\r
386 int premoveToX = 0;
\r
387 int premoveToY = 0;
\r
388 int premoveFromX = 0;
\r
389 int premoveFromY = 0;
\r
390 int premovePromoChar = 0;
\r
391 int gotPremove = 0;
\r
392 Boolean alarmSounded;
\r
393 /* end premove variables */
\r
395 char *ics_prefix = "$";
\r
396 int ics_type = ICS_GENERIC;
\r
398 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
\r
399 int pauseExamForwardMostMove = 0;
\r
400 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
\r
401 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
\r
402 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
\r
403 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
\r
404 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
\r
405 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
\r
406 int whiteFlag = FALSE, blackFlag = FALSE;
\r
407 int userOfferedDraw = FALSE;
\r
408 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
\r
409 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
\r
410 int cmailMoveType[CMAIL_MAX_GAMES];
\r
411 long ics_clock_paused = 0;
\r
412 ProcRef icsPR = NoProc, cmailPR = NoProc;
\r
413 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
\r
414 GameMode gameMode = BeginningOfGame;
\r
415 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
\r
416 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
\r
417 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
\r
418 int hiddenThinkOutputState = 0; /* [AS] */
\r
419 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
\r
420 int adjudicateLossPlies = 6;
\r
421 char white_holding[64], black_holding[64];
\r
422 TimeMark lastNodeCountTime;
\r
423 long lastNodeCount=0;
\r
424 int have_sent_ICS_logon = 0;
\r
425 int movesPerSession;
\r
426 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
\r
427 long timeControl_2; /* [AS] Allow separate time controls */
\r
428 char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */
\r
429 long timeRemaining[2][MAX_MOVES];
\r
431 TimeMark programStartTime;
\r
432 char ics_handle[MSG_SIZ];
\r
433 int have_set_title = 0;
\r
435 /* animateTraining preserves the state of appData.animate
\r
436 * when Training mode is activated. This allows the
\r
437 * response to be animated when appData.animate == TRUE and
\r
438 * appData.animateDragging == TRUE.
\r
440 Boolean animateTraining;
\r
446 Board boards[MAX_MOVES];
\r
447 /* [HGM] Following 7 needed for accurate legality tests: */
\r
448 char epStatus[MAX_MOVES];
\r
449 char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
\r
450 char castlingRank[BOARD_SIZE]; // and corresponding ranks
\r
451 char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
\r
452 int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
\r
453 int initialRulePlies, FENrulePlies;
\r
455 FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
\r
457 int shuffleOpenings;
\r
459 ChessSquare FIDEArray[2][BOARD_SIZE] = {
\r
460 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
\r
461 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
\r
462 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
\r
463 BlackKing, BlackBishop, BlackKnight, BlackRook }
\r
466 ChessSquare twoKingsArray[2][BOARD_SIZE] = {
\r
467 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
\r
468 WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
\r
469 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
\r
470 BlackKing, BlackKing, BlackKnight, BlackRook }
\r
473 ChessSquare KnightmateArray[2][BOARD_SIZE] = {
\r
474 { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,
\r
475 WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },
\r
476 { BlackRook, BlackMan, BlackBishop, BlackQueen,
\r
477 BlackUnicorn, BlackBishop, BlackMan, BlackRook }
\r
480 ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */
\r
481 { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,
\r
482 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
\r
483 { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,
\r
484 BlackKing, BlackBishop, BlackKnight, BlackRook }
\r
487 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */
\r
488 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,
\r
489 WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
\r
490 { BlackRook, BlackKnight, BlackAlfil, BlackKing,
\r
491 BlackFerz, BlackAlfil, BlackKnight, BlackRook }
\r
495 #if (BOARD_SIZE>=10)
\r
496 ChessSquare ShogiArray[2][BOARD_SIZE] = {
\r
497 { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,
\r
498 WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },
\r
499 { BlackQueen, BlackKnight, BlackFerz, BlackWazir,
\r
500 BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }
\r
503 ChessSquare XiangqiArray[2][BOARD_SIZE] = {
\r
504 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,
\r
505 WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
\r
506 { BlackRook, BlackKnight, BlackAlfil, BlackFerz,
\r
507 BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }
\r
510 ChessSquare CapablancaArray[2][BOARD_SIZE] = {
\r
511 { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
\r
512 WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
\r
513 { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
\r
514 BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
\r
517 ChessSquare GreatArray[2][BOARD_SIZE] = {
\r
518 { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
\r
519 WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
\r
520 { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
\r
521 BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
\r
524 ChessSquare JanusArray[2][BOARD_SIZE] = {
\r
525 { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
\r
526 WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
\r
527 { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
\r
528 BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
\r
532 ChessSquare GothicArray[2][BOARD_SIZE] = {
\r
533 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
\r
534 WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
\r
535 { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
\r
536 BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }
\r
539 #define GothicArray CapablancaArray
\r
543 ChessSquare FalconArray[2][BOARD_SIZE] = {
\r
544 { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
\r
545 WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
\r
546 { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
\r
547 BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
\r
550 #define FalconArray CapablancaArray
\r
553 #else // !(BOARD_SIZE>=10)
\r
554 #define XiangqiPosition FIDEArray
\r
555 #define CapablancaArray FIDEArray
\r
556 #define GothicArray FIDEArray
\r
557 #define GreatArray FIDEArray
\r
558 #endif // !(BOARD_SIZE>=10)
\r
560 #if (BOARD_SIZE>=12)
\r
561 ChessSquare CourierArray[2][BOARD_SIZE] = {
\r
562 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,
\r
563 WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },
\r
564 { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
\r
565 BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
\r
567 #else // !(BOARD_SIZE>=12)
\r
568 #define CourierArray CapablancaArray
\r
569 #endif // !(BOARD_SIZE>=12)
\r
572 Board initialPosition;
\r
575 /* Convert str to a rating. Checks for special cases of "----",
\r
577 "++++", etc. Also strips ()'s */
\r
579 string_to_rating(str)
\r
582 while(*str && !isdigit(*str)) ++str;
\r
584 return 0; /* One of the special "no rating" cases */
\r
590 ClearProgramStats()
\r
592 /* Init programStats */
\r
593 programStats.movelist[0] = 0;
\r
594 programStats.depth = 0;
\r
595 programStats.nr_moves = 0;
\r
596 programStats.moves_left = 0;
\r
597 programStats.nodes = 0;
\r
598 programStats.time = -1; // [HGM] PGNtime: make invalid to recognize engine output
\r
599 programStats.score = 0;
\r
600 programStats.got_only_move = 0;
\r
601 programStats.got_fail = 0;
\r
602 programStats.line_is_book = 0;
\r
608 int matched, min, sec;
\r
610 ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
\r
612 GetTimeMark(&programStartTime);
\r
614 ClearProgramStats();
\r
615 programStats.ok_to_send = 1;
\r
616 programStats.seen_stat = 0;
\r
619 * Initialize game list
\r
621 ListNew(&gameList);
\r
625 * Internet chess server status
\r
627 if (appData.icsActive) {
\r
628 appData.matchMode = FALSE;
\r
629 appData.matchGames = 0;
\r
631 appData.noChessProgram = !appData.zippyPlay;
\r
633 appData.zippyPlay = FALSE;
\r
634 appData.zippyTalk = FALSE;
\r
635 appData.noChessProgram = TRUE;
\r
637 if (*appData.icsHelper != NULLCHAR) {
\r
638 appData.useTelnet = TRUE;
\r
639 appData.telnetProgram = appData.icsHelper;
\r
642 appData.zippyTalk = appData.zippyPlay = FALSE;
\r
645 /* [AS] Initialize pv info list [HGM] and game state */
\r
649 for( i=0; i<MAX_MOVES; i++ ) {
\r
650 pvInfoList[i].depth = -1;
\r
651 epStatus[i]=EP_NONE;
\r
652 for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;
\r
657 * Parse timeControl resource
\r
659 if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
\r
660 appData.movesPerSession)) {
\r
662 sprintf(buf, _("bad timeControl option %s"), appData.timeControl);
\r
663 DisplayFatalError(buf, 0, 2);
\r
667 * Parse searchTime resource
\r
669 if (*appData.searchTime != NULLCHAR) {
\r
670 matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
\r
671 if (matched == 1) {
\r
672 searchTime = min * 60;
\r
673 } else if (matched == 2) {
\r
674 searchTime = min * 60 + sec;
\r
677 sprintf(buf, _("bad searchTime option %s"), appData.searchTime);
\r
678 DisplayFatalError(buf, 0, 2);
\r
682 /* [AS] Adjudication threshold */
\r
683 adjudicateLossThreshold = appData.adjudicateLossThreshold;
\r
685 first.which = "first";
\r
686 second.which = "second";
\r
687 first.maybeThinking = second.maybeThinking = FALSE;
\r
688 first.pr = second.pr = NoProc;
\r
689 first.isr = second.isr = NULL;
\r
690 first.sendTime = second.sendTime = 2;
\r
691 first.sendDrawOffers = 1;
\r
692 if (appData.firstPlaysBlack) {
\r
693 first.twoMachinesColor = "black\n";
\r
694 second.twoMachinesColor = "white\n";
\r
696 first.twoMachinesColor = "white\n";
\r
697 second.twoMachinesColor = "black\n";
\r
699 first.program = appData.firstChessProgram;
\r
700 second.program = appData.secondChessProgram;
\r
701 first.host = appData.firstHost;
\r
702 second.host = appData.secondHost;
\r
703 first.dir = appData.firstDirectory;
\r
704 second.dir = appData.secondDirectory;
\r
705 first.other = &second;
\r
706 second.other = &first;
\r
707 first.initString = appData.initString;
\r
708 second.initString = appData.secondInitString;
\r
709 first.computerString = appData.firstComputerString;
\r
710 second.computerString = appData.secondComputerString;
\r
711 first.useSigint = second.useSigint = TRUE;
\r
712 first.useSigterm = second.useSigterm = TRUE;
\r
713 first.reuse = appData.reuseFirst;
\r
714 second.reuse = appData.reuseSecond;
\r
715 first.nps = appData.firstNPS; // [HGM] nps: copy nodes per second
\r
716 second.nps = appData.secondNPS;
\r
717 first.useSetboard = second.useSetboard = FALSE;
\r
718 first.useSAN = second.useSAN = FALSE;
\r
719 first.usePing = second.usePing = FALSE;
\r
720 first.lastPing = second.lastPing = 0;
\r
721 first.lastPong = second.lastPong = 0;
\r
722 first.usePlayother = second.usePlayother = FALSE;
\r
723 first.useColors = second.useColors = TRUE;
\r
724 first.useUsermove = second.useUsermove = FALSE;
\r
725 first.sendICS = second.sendICS = FALSE;
\r
726 first.sendName = second.sendName = appData.icsActive;
\r
727 first.sdKludge = second.sdKludge = FALSE;
\r
728 first.stKludge = second.stKludge = FALSE;
\r
729 TidyProgramName(first.program, first.host, first.tidy);
\r
730 TidyProgramName(second.program, second.host, second.tidy);
\r
731 first.matchWins = second.matchWins = 0;
\r
732 strcpy(first.variants, appData.variant);
\r
733 strcpy(second.variants, appData.variant);
\r
734 first.analysisSupport = second.analysisSupport = 2; /* detect */
\r
735 first.analyzing = second.analyzing = FALSE;
\r
736 first.initDone = second.initDone = FALSE;
\r
738 /* New features added by Tord: */
\r
739 first.useFEN960 = FALSE; second.useFEN960 = FALSE;
\r
740 first.useOOCastle = TRUE; second.useOOCastle = TRUE;
\r
741 /* End of new features added by Tord. */
\r
743 /* [HGM] time odds: set factor for each machine */
\r
744 first.timeOdds = appData.firstTimeOdds;
\r
745 second.timeOdds = appData.secondTimeOdds;
\r
747 if(appData.timeOddsMode) {
\r
748 norm = first.timeOdds;
\r
749 if(norm > second.timeOdds) norm = second.timeOdds;
\r
751 first.timeOdds /= norm;
\r
752 second.timeOdds /= norm;
\r
755 /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
\r
756 first.accumulateTC = appData.firstAccumulateTC;
\r
757 second.accumulateTC = appData.secondAccumulateTC;
\r
758 first.maxNrOfSessions = second.maxNrOfSessions = 1;
\r
761 first.debug = second.debug = FALSE;
\r
762 first.supportsNPS = second.supportsNPS = UNKNOWN;
\r
764 /* [HGM] options */
\r
765 first.optionSettings = appData.firstOptions;
\r
766 second.optionSettings = appData.secondOptions;
\r
768 first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
\r
769 second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
\r
770 first.isUCI = appData.firstIsUCI; /* [AS] */
\r
771 second.isUCI = appData.secondIsUCI; /* [AS] */
\r
772 first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
\r
773 second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
\r
775 if (appData.firstProtocolVersion > PROTOVER ||
\r
776 appData.firstProtocolVersion < 1) {
\r
778 sprintf(buf, _("protocol version %d not supported"),
\r
779 appData.firstProtocolVersion);
\r
780 DisplayFatalError(buf, 0, 2);
\r
782 first.protocolVersion = appData.firstProtocolVersion;
\r
785 if (appData.secondProtocolVersion > PROTOVER ||
\r
786 appData.secondProtocolVersion < 1) {
\r
788 sprintf(buf, _("protocol version %d not supported"),
\r
789 appData.secondProtocolVersion);
\r
790 DisplayFatalError(buf, 0, 2);
\r
792 second.protocolVersion = appData.secondProtocolVersion;
\r
795 if (appData.icsActive) {
\r
796 appData.clockMode = TRUE; /* changes dynamically in ICS mode */
\r
797 } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
\r
798 appData.clockMode = FALSE;
\r
799 first.sendTime = second.sendTime = 0;
\r
803 /* Override some settings from environment variables, for backward
\r
804 compatibility. Unfortunately it's not feasible to have the env
\r
805 vars just set defaults, at least in xboard. Ugh.
\r
807 if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
\r
812 if (appData.noChessProgram) {
\r
813 programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
\r
814 + strlen(PATCHLEVEL));
\r
815 sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
\r
820 while (*q != ' ' && *q != NULLCHAR) q++;
\r
822 while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */
\r
823 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
\r
824 + strlen(PATCHLEVEL) + (q - p));
\r
825 sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
\r
826 strncat(programVersion, p, q - p);
\r
828 /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
\r
829 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
\r
830 + strlen(PATCHLEVEL) + strlen(first.tidy));
\r
831 sprintf(programVersion, "%s %s.%s + %s", PRODUCT, VERSION, PATCHLEVEL, first.tidy);
\r
835 if (!appData.icsActive) {
\r
837 /* Check for variants that are supported only in ICS mode,
\r
838 or not at all. Some that are accepted here nevertheless
\r
839 have bugs; see comments below.
\r
841 VariantClass variant = StringToVariant(appData.variant);
\r
843 case VariantBughouse: /* need four players and two boards */
\r
844 case VariantKriegspiel: /* need to hide pieces and move details */
\r
845 /* case VariantFischeRandom: (Fabien: moved below) */
\r
846 sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);
\r
847 DisplayFatalError(buf, 0, 2);
\r
850 case VariantUnknown:
\r
851 case VariantLoadable:
\r
861 sprintf(buf, _("Unknown variant name %s"), appData.variant);
\r
862 DisplayFatalError(buf, 0, 2);
\r
865 case VariantXiangqi: /* [HGM] repetition rules not implemented */
\r
866 case VariantFairy: /* [HGM] TestLegality definitely off! */
\r
867 case VariantGothic: /* [HGM] should work */
\r
868 case VariantCapablanca: /* [HGM] should work */
\r
869 case VariantCourier: /* [HGM] initial forced moves not implemented */
\r
870 case VariantShogi: /* [HGM] drops not tested for legality */
\r
871 case VariantKnightmate: /* [HGM] should work */
\r
872 case VariantCylinder: /* [HGM] untested */
\r
873 case VariantFalcon: /* [HGM] untested */
\r
874 case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)
\r
875 offboard interposition not understood */
\r
876 case VariantNormal: /* definitely works! */
\r
877 case VariantWildCastle: /* pieces not automatically shuffled */
\r
878 case VariantNoCastle: /* pieces not automatically shuffled */
\r
879 case VariantFischeRandom: /* [HGM] works and shuffles pieces */
\r
880 case VariantLosers: /* should work except for win condition,
\r
881 and doesn't know captures are mandatory */
\r
882 case VariantSuicide: /* should work except for win condition,
\r
883 and doesn't know captures are mandatory */
\r
884 case VariantGiveaway: /* should work except for win condition,
\r
885 and doesn't know captures are mandatory */
\r
886 case VariantTwoKings: /* should work */
\r
887 case VariantAtomic: /* should work except for win condition */
\r
888 case Variant3Check: /* should work except for win condition */
\r
889 case VariantShatranj: /* should work except for all win conditions */
\r
890 case VariantBerolina: /* might work if TestLegality is off */
\r
891 case VariantCapaRandom: /* should work */
\r
892 case VariantJanus: /* should work */
\r
893 case VariantSuper: /* experimental */
\r
894 case VariantGreat: /* experimental, requires legality testing to be off */
\r
899 InitEngineUCI( installDir, &first ); // [HGM] moved here from winboard.c, to make available in xboard
\r
900 InitEngineUCI( installDir, &second );
\r
903 int NextIntegerFromString( char ** str, long * value )
\r
908 while( *s == ' ' || *s == '\t' ) {
\r
914 if( *s >= '0' && *s <= '9' ) {
\r
915 while( *s >= '0' && *s <= '9' ) {
\r
916 *value = *value * 10 + (*s - '0');
\r
928 int NextTimeControlFromString( char ** str, long * value )
\r
931 int result = NextIntegerFromString( str, &temp );
\r
933 if( result == 0 ) {
\r
934 *value = temp * 60; /* Minutes */
\r
935 if( **str == ':' ) {
\r
937 result = NextIntegerFromString( str, &temp );
\r
938 *value += temp; /* Seconds */
\r
945 int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)
\r
946 { /* [HGM] routine added to read '+moves/time' for secondary time control */
\r
947 int result = -1; long temp, temp2;
\r
949 if(**str != '+') return -1; // old params remain in force!
\r
951 if( NextTimeControlFromString( str, &temp ) ) return -1;
\r
954 /* time only: incremental or sudden-death time control */
\r
955 if(**str == '+') { /* increment follows; read it */
\r
957 if(result = NextIntegerFromString( str, &temp2)) return -1;
\r
958 *inc = temp2 * 1000;
\r
960 *moves = 0; *tc = temp * 1000;
\r
962 } else if(temp % 60 != 0) return -1; /* moves was given as min:sec */
\r
964 (*str)++; /* classical time control */
\r
965 result = NextTimeControlFromString( str, &temp2);
\r
968 *tc = temp2 * 1000;
\r
974 int GetTimeQuota(int movenr)
\r
975 { /* [HGM] get time to add from the multi-session time-control string */
\r
976 int moves=1; /* kludge to force reading of first session */
\r
977 long time, increment;
\r
978 char *s = fullTimeControlString;
\r
980 if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);
\r
982 if(moves) NextSessionFromString(&s, &moves, &time, &increment);
\r
983 if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
\r
984 if(movenr == -1) return time; /* last move before new session */
\r
985 if(!moves) return increment; /* current session is incremental */
\r
986 if(movenr >= 0) movenr -= moves; /* we already finished this session */
\r
987 } while(movenr >= -1); /* try again for next session */
\r
989 return 0; // no new time quota on this move
\r
993 ParseTimeControl(tc, ti, mps)
\r
999 int matched, min, sec;
\r
1001 matched = sscanf(tc, "%d:%d", &min, &sec);
\r
1002 if (matched == 1) {
\r
1003 timeControl = min * 60 * 1000;
\r
1004 } else if (matched == 2) {
\r
1005 timeControl = (min * 60 + sec) * 1000;
\r
1012 char buf[MSG_SIZ];
\r
1014 if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
\r
1017 sprintf(buf, "+%d/%s+%d", mps, tc, ti);
\r
1018 else sprintf(buf, "+%s+%d", tc, ti);
\r
1021 sprintf(buf, "+%d/%s", mps, tc);
\r
1022 else sprintf(buf, "+%s", tc);
\r
1024 fullTimeControlString = StrSave(buf);
\r
1026 if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
\r
1030 if( *tc == '/' ) {
\r
1031 /* Parse second time control */
\r
1034 if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
\r
1042 timeControl_2 = tc2 * 1000;
\r
1045 timeControl_2 = 0;
\r
1052 timeControl = tc1 * 1000;
\r
1056 timeIncrement = ti * 1000; /* convert to ms */
\r
1057 movesPerSession = 0;
\r
1059 timeIncrement = 0;
\r
1060 movesPerSession = mps;
\r
1068 if (appData.debugMode) {
\r
1069 fprintf(debugFP, "%s\n", programVersion);
\r
1072 if (appData.matchGames > 0) {
\r
1073 appData.matchMode = TRUE;
\r
1074 } else if (appData.matchMode) {
\r
1075 appData.matchGames = 1;
\r
1077 if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */
\r
1078 appData.matchGames = appData.sameColorGames;
\r
1079 if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */
\r
1080 if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;
\r
1081 if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;
\r
1083 Reset(TRUE, FALSE);
\r
1084 if (appData.noChessProgram || first.protocolVersion == 1) {
\r
1087 /* kludge: allow timeout for initial "feature" commands */
\r
1089 DisplayMessage("", _("Starting chess program"));
\r
1090 ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
\r
1095 InitBackEnd3 P((void))
\r
1097 GameMode initialMode;
\r
1098 char buf[MSG_SIZ];
\r
1101 InitChessProgram(&first, startedFromSetupPosition);
\r
1104 if (appData.icsActive) {
\r
1106 /* [DM] Make a console window if needed [HGM] merged ifs */
\r
1109 err = establish();
\r
1111 if (*appData.icsCommPort != NULLCHAR) {
\r
1112 sprintf(buf, _("Could not open comm port %s"),
\r
1113 appData.icsCommPort);
\r
1115 sprintf(buf, _("Could not connect to host %s, port %s"),
\r
1116 appData.icsHost, appData.icsPort);
\r
1118 DisplayFatalError(buf, err, 1);
\r
1123 AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
\r
1125 AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
\r
1126 } else if (appData.noChessProgram) {
\r
1132 if (*appData.cmailGameName != NULLCHAR) {
\r
1134 OpenLoopback(&cmailPR);
\r
1136 AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
\r
1140 DisplayMessage("", "");
\r
1141 if (StrCaseCmp(appData.initialMode, "") == 0) {
\r
1142 initialMode = BeginningOfGame;
\r
1143 } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
\r
1144 initialMode = TwoMachinesPlay;
\r
1145 } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
\r
1146 initialMode = AnalyzeFile;
\r
1147 } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
\r
1148 initialMode = AnalyzeMode;
\r
1149 } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
\r
1150 initialMode = MachinePlaysWhite;
\r
1151 } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
\r
1152 initialMode = MachinePlaysBlack;
\r
1153 } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
\r
1154 initialMode = EditGame;
\r
1155 } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
\r
1156 initialMode = EditPosition;
\r
1157 } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
\r
1158 initialMode = Training;
\r
1160 sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);
\r
1161 DisplayFatalError(buf, 0, 2);
\r
1165 if (appData.matchMode) {
\r
1166 /* Set up machine vs. machine match */
\r
1167 if (appData.noChessProgram) {
\r
1168 DisplayFatalError(_("Can't have a match with no chess programs"),
\r
1174 if (*appData.loadGameFile != NULLCHAR) {
\r
1175 int index = appData.loadGameIndex; // [HGM] autoinc
\r
1176 if(index<0) lastIndex = index = 1;
\r
1177 if (!LoadGameFromFile(appData.loadGameFile,
\r
1179 appData.loadGameFile, FALSE)) {
\r
1180 DisplayFatalError(_("Bad game file"), 0, 1);
\r
1183 } else if (*appData.loadPositionFile != NULLCHAR) {
\r
1184 int index = appData.loadPositionIndex; // [HGM] autoinc
\r
1185 if(index<0) lastIndex = index = 1;
\r
1186 if (!LoadPositionFromFile(appData.loadPositionFile,
\r
1188 appData.loadPositionFile)) {
\r
1189 DisplayFatalError(_("Bad position file"), 0, 1);
\r
1193 TwoMachinesEvent();
\r
1194 } else if (*appData.cmailGameName != NULLCHAR) {
\r
1195 /* Set up cmail mode */
\r
1196 ReloadCmailMsgEvent(TRUE);
\r
1198 /* Set up other modes */
\r
1199 if (initialMode == AnalyzeFile) {
\r
1200 if (*appData.loadGameFile == NULLCHAR) {
\r
1201 DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
\r
1205 if (*appData.loadGameFile != NULLCHAR) {
\r
1206 (void) LoadGameFromFile(appData.loadGameFile,
\r
1207 appData.loadGameIndex,
\r
1208 appData.loadGameFile, TRUE);
\r
1209 } else if (*appData.loadPositionFile != NULLCHAR) {
\r
1210 (void) LoadPositionFromFile(appData.loadPositionFile,
\r
1211 appData.loadPositionIndex,
\r
1212 appData.loadPositionFile);
\r
1213 /* [HGM] try to make self-starting even after FEN load */
\r
1214 /* to allow automatic setup of fairy variants with wtm */
\r
1215 if(initialMode == BeginningOfGame && !blackPlaysFirst) {
\r
1216 gameMode = BeginningOfGame;
\r
1217 setboardSpoiledMachineBlack = 1;
\r
1219 /* [HGM] loadPos: make that every new game uses the setup */
\r
1220 /* from file as long as we do not switch variant */
\r
1221 if(!blackPlaysFirst) { int i;
\r
1222 startedFromPositionFile = TRUE;
\r
1223 CopyBoard(filePosition, boards[0]);
\r
1224 for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];
\r
1227 if (initialMode == AnalyzeMode) {
\r
1228 if (appData.noChessProgram) {
\r
1229 DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
\r
1232 if (appData.icsActive) {
\r
1233 DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
\r
1236 AnalyzeModeEvent();
\r
1237 } else if (initialMode == AnalyzeFile) {
\r
1238 appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent
\r
1239 ShowThinkingEvent();
\r
1240 AnalyzeFileEvent();
\r
1241 AnalysisPeriodicEvent(1);
\r
1242 } else if (initialMode == MachinePlaysWhite) {
\r
1243 if (appData.noChessProgram) {
\r
1244 DisplayFatalError(_("MachineWhite mode requires a chess engine"),
\r
1248 if (appData.icsActive) {
\r
1249 DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
\r
1253 MachineWhiteEvent();
\r
1254 } else if (initialMode == MachinePlaysBlack) {
\r
1255 if (appData.noChessProgram) {
\r
1256 DisplayFatalError(_("MachineBlack mode requires a chess engine"),
\r
1260 if (appData.icsActive) {
\r
1261 DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
\r
1265 MachineBlackEvent();
\r
1266 } else if (initialMode == TwoMachinesPlay) {
\r
1267 if (appData.noChessProgram) {
\r
1268 DisplayFatalError(_("TwoMachines mode requires a chess engine"),
\r
1272 if (appData.icsActive) {
\r
1273 DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
\r
1277 TwoMachinesEvent();
\r
1278 } else if (initialMode == EditGame) {
\r
1280 } else if (initialMode == EditPosition) {
\r
1281 EditPositionEvent();
\r
1282 } else if (initialMode == Training) {
\r
1283 if (*appData.loadGameFile == NULLCHAR) {
\r
1284 DisplayFatalError(_("Training mode requires a game file"), 0, 2);
\r
1293 * Establish will establish a contact to a remote host.port.
\r
1294 * Sets icsPR to a ProcRef for a process (or pseudo-process)
\r
1295 * used to talk to the host.
\r
1296 * Returns 0 if okay, error code if not.
\r
1301 char buf[MSG_SIZ];
\r
1303 if (*appData.icsCommPort != NULLCHAR) {
\r
1304 /* Talk to the host through a serial comm port */
\r
1305 return OpenCommPort(appData.icsCommPort, &icsPR);
\r
1307 } else if (*appData.gateway != NULLCHAR) {
\r
1308 if (*appData.remoteShell == NULLCHAR) {
\r
1309 /* Use the rcmd protocol to run telnet program on a gateway host */
\r
1310 sprintf(buf, "%s %s %s",
\r
1311 appData.telnetProgram, appData.icsHost, appData.icsPort);
\r
1312 return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
\r
1315 /* Use the rsh program to run telnet program on a gateway host */
\r
1316 if (*appData.remoteUser == NULLCHAR) {
\r
1317 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
\r
1318 appData.gateway, appData.telnetProgram,
\r
1319 appData.icsHost, appData.icsPort);
\r
1321 sprintf(buf, "%s %s -l %s %s %s %s",
\r
1322 appData.remoteShell, appData.gateway,
\r
1323 appData.remoteUser, appData.telnetProgram,
\r
1324 appData.icsHost, appData.icsPort);
\r
1326 return StartChildProcess(buf, "", &icsPR);
\r
1329 } else if (appData.useTelnet) {
\r
1330 return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
\r
1333 /* TCP socket interface differs somewhat between
\r
1334 Unix and NT; handle details in the front end.
\r
1336 return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
\r
1341 show_bytes(fp, buf, count)
\r
1347 if (*buf < 040 || *(unsigned char *) buf > 0177) {
\r
1348 fprintf(fp, "\\%03o", *buf & 0xff);
\r
1357 /* Returns an errno value */
\r
1359 OutputMaybeTelnet(pr, message, count, outError)
\r
1365 char buf[8192], *p, *q, *buflim;
\r
1366 int left, newcount, outcount;
\r
1368 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
\r
1369 *appData.gateway != NULLCHAR) {
\r
1370 if (appData.debugMode) {
\r
1371 fprintf(debugFP, ">ICS: ");
\r
1372 show_bytes(debugFP, message, count);
\r
1373 fprintf(debugFP, "\n");
\r
1375 return OutputToProcess(pr, message, count, outError);
\r
1378 buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
\r
1384 if (q >= buflim) {
\r
1385 if (appData.debugMode) {
\r
1386 fprintf(debugFP, ">ICS: ");
\r
1387 show_bytes(debugFP, buf, newcount);
\r
1388 fprintf(debugFP, "\n");
\r
1390 outcount = OutputToProcess(pr, buf, newcount, outError);
\r
1391 if (outcount < newcount) return -1; /* to be sure */
\r
1398 } else if (((unsigned char) *p) == TN_IAC) {
\r
1399 *q++ = (char) TN_IAC;
\r
1406 if (appData.debugMode) {
\r
1407 fprintf(debugFP, ">ICS: ");
\r
1408 show_bytes(debugFP, buf, newcount);
\r
1409 fprintf(debugFP, "\n");
\r
1411 outcount = OutputToProcess(pr, buf, newcount, outError);
\r
1412 if (outcount < newcount) return -1; /* to be sure */
\r
1417 read_from_player(isr, closure, message, count, error)
\r
1418 InputSourceRef isr;
\r
1424 int outError, outCount;
\r
1425 static int gotEof = 0;
\r
1427 /* Pass data read from player on to ICS */
\r
1430 outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
\r
1431 if (outCount < count) {
\r
1432 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1434 } else if (count < 0) {
\r
1435 RemoveInputSource(isr);
\r
1436 DisplayFatalError(_("Error reading from keyboard"), error, 1);
\r
1437 } else if (gotEof++ > 0) {
\r
1438 RemoveInputSource(isr);
\r
1439 DisplayFatalError(_("Got end of file from keyboard"), 0, 0);
\r
1447 int count, outCount, outError;
\r
1449 if (icsPR == NULL) return;
\r
1451 count = strlen(s);
\r
1452 outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
\r
1453 if (outCount < count) {
\r
1454 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1458 /* This is used for sending logon scripts to the ICS. Sending
\r
1459 without a delay causes problems when using timestamp on ICC
\r
1460 (at least on my machine). */
\r
1462 SendToICSDelayed(s,msdelay)
\r
1466 int count, outCount, outError;
\r
1468 if (icsPR == NULL) return;
\r
1470 count = strlen(s);
\r
1471 if (appData.debugMode) {
\r
1472 fprintf(debugFP, ">ICS: ");
\r
1473 show_bytes(debugFP, s, count);
\r
1474 fprintf(debugFP, "\n");
\r
1476 outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
\r
1478 if (outCount < count) {
\r
1479 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1484 /* Remove all highlighting escape sequences in s
\r
1485 Also deletes any suffix starting with '('
\r
1488 StripHighlightAndTitle(s)
\r
1491 static char retbuf[MSG_SIZ];
\r
1494 while (*s != NULLCHAR) {
\r
1495 while (*s == '\033') {
\r
1496 while (*s != NULLCHAR && !isalpha(*s)) s++;
\r
1497 if (*s != NULLCHAR) s++;
\r
1499 while (*s != NULLCHAR && *s != '\033') {
\r
1500 if (*s == '(' || *s == '[') {
\r
1511 /* Remove all highlighting escape sequences in s */
\r
1516 static char retbuf[MSG_SIZ];
\r
1519 while (*s != NULLCHAR) {
\r
1520 while (*s == '\033') {
\r
1521 while (*s != NULLCHAR && !isalpha(*s)) s++;
\r
1522 if (*s != NULLCHAR) s++;
\r
1524 while (*s != NULLCHAR && *s != '\033') {
\r
1532 char *variantNames[] = VARIANT_NAMES;
\r
1537 return variantNames[v];
\r
1541 /* Identify a variant from the strings the chess servers use or the
\r
1542 PGN Variant tag names we use. */
\r
1544 StringToVariant(e)
\r
1549 VariantClass v = VariantNormal;
\r
1550 int i, found = FALSE;
\r
1551 char buf[MSG_SIZ];
\r
1555 /* [HGM] skip over optional board-size prefixes */
\r
1556 if( sscanf(e, "%dx%d_", &i, &i) == 2 ||
\r
1557 sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {
\r
1558 while( *e++ != '_');
\r
1561 for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
\r
1562 if (StrCaseStr(e, variantNames[i])) {
\r
1563 v = (VariantClass) i;
\r
1570 if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
\r
1571 || StrCaseStr(e, "wild/fr")
\r
1572 || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {
\r
1573 v = VariantFischeRandom;
\r
1574 } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
\r
1575 (i = 1, p = StrCaseStr(e, "w"))) {
\r
1577 while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
\r
1578 if (isdigit(*p)) {
\r
1584 case 0: /* FICS only, actually */
\r
1586 /* Castling legal even if K starts on d-file */
\r
1587 v = VariantWildCastle;
\r
1592 /* Castling illegal even if K & R happen to start in
\r
1593 normal positions. */
\r
1594 v = VariantNoCastle;
\r
1607 /* Castling legal iff K & R start in normal positions */
\r
1608 v = VariantNormal;
\r
1613 /* Special wilds for position setup; unclear what to do here */
\r
1614 v = VariantLoadable;
\r
1617 /* Bizarre ICC game */
\r
1618 v = VariantTwoKings;
\r
1621 v = VariantKriegspiel;
\r
1624 v = VariantLosers;
\r
1627 v = VariantFischeRandom;
\r
1630 v = VariantCrazyhouse;
\r
1633 v = VariantBughouse;
\r
1636 v = Variant3Check;
\r
1639 /* Not quite the same as FICS suicide! */
\r
1640 v = VariantGiveaway;
\r
1643 v = VariantAtomic;
\r
1646 v = VariantShatranj;
\r
1649 /* Temporary names for future ICC types. The name *will* change in
\r
1650 the next xboard/WinBoard release after ICC defines it. */
\r
1679 v = VariantXiangqi;
\r
1682 v = VariantCourier;
\r
1685 v = VariantGothic;
\r
1688 v = VariantCapablanca;
\r
1691 v = VariantKnightmate;
\r
1697 v = VariantCylinder;
\r
1700 v = VariantFalcon;
\r
1703 v = VariantCapaRandom;
\r
1706 v = VariantBerolina;
\r
1718 /* Found "wild" or "w" in the string but no number;
\r
1719 must assume it's normal chess. */
\r
1720 v = VariantNormal;
\r
1723 sprintf(buf, _("Unknown wild type %d"), wnum);
\r
1724 DisplayError(buf, 0);
\r
1725 v = VariantUnknown;
\r
1730 if (appData.debugMode) {
\r
1731 fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),
\r
1732 e, wnum, VariantName(v));
\r
1737 static int leftover_start = 0, leftover_len = 0;
\r
1738 char star_match[STAR_MATCH_N][MSG_SIZ];
\r
1740 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
\r
1741 advance *index beyond it, and set leftover_start to the new value of
\r
1742 *index; else return FALSE. If pattern contains the character '*', it
\r
1743 matches any sequence of characters not containing '\r', '\n', or the
\r
1744 character following the '*' (if any), and the matched sequence(s) are
\r
1745 copied into star_match.
\r
1748 looking_at(buf, index, pattern)
\r
1753 char *bufp = &buf[*index], *patternp = pattern;
\r
1754 int star_count = 0;
\r
1755 char *matchp = star_match[0];
\r
1758 if (*patternp == NULLCHAR) {
\r
1759 *index = leftover_start = bufp - buf;
\r
1760 *matchp = NULLCHAR;
\r
1763 if (*bufp == NULLCHAR) return FALSE;
\r
1764 if (*patternp == '*') {
\r
1765 if (*bufp == *(patternp + 1)) {
\r
1766 *matchp = NULLCHAR;
\r
1767 matchp = star_match[++star_count];
\r
1771 } else if (*bufp == '\n' || *bufp == '\r') {
\r
1773 if (*patternp == NULLCHAR)
\r
1778 *matchp++ = *bufp++;
\r
1782 if (*patternp != *bufp) return FALSE;
\r
1789 SendToPlayer(data, length)
\r
1793 int error, outCount;
\r
1794 outCount = OutputToProcess(NoProc, data, length, &error);
\r
1795 if (outCount < length) {
\r
1796 DisplayFatalError(_("Error writing to display"), error, 1);
\r
1801 PackHolding(packed, holding)
\r
1805 char *p = holding;
\r
1807 int runlength = 0;
\r
1813 switch (runlength) {
\r
1824 sprintf(q, "%d", runlength);
\r
1836 /* Telnet protocol requests from the front end */
\r
1838 TelnetRequest(ddww, option)
\r
1839 unsigned char ddww, option;
\r
1841 unsigned char msg[3];
\r
1842 int outCount, outError;
\r
1844 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
\r
1846 if (appData.debugMode) {
\r
1847 char buf1[8], buf2[8], *ddwwStr, *optionStr;
\r
1863 sprintf(buf1, "%d", ddww);
\r
1868 optionStr = "ECHO";
\r
1872 sprintf(buf2, "%d", option);
\r
1875 fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
\r
1880 outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
\r
1881 if (outCount < 3) {
\r
1882 DisplayFatalError(_("Error writing to ICS"), outError, 1);
\r
1889 if (!appData.icsActive) return;
\r
1890 TelnetRequest(TN_DO, TN_ECHO);
\r
1896 if (!appData.icsActive) return;
\r
1897 TelnetRequest(TN_DONT, TN_ECHO);
\r
1901 CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
\r
1903 /* put the holdings sent to us by the server on the board holdings area */
\r
1904 int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;
\r
1906 ChessSquare piece;
\r
1908 if(gameInfo.holdingsWidth < 2) return;
\r
1910 if( (int)lowestPiece >= BlackPawn ) {
\r
1911 holdingsColumn = 0;
\r
1913 holdingsStartRow = BOARD_HEIGHT-1;
\r
1916 holdingsColumn = BOARD_WIDTH-1;
\r
1917 countsColumn = BOARD_WIDTH-2;
\r
1918 holdingsStartRow = 0;
\r
1922 for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */
\r
1923 board[i][holdingsColumn] = EmptySquare;
\r
1924 board[i][countsColumn] = (ChessSquare) 0;
\r
1926 while( (p=*holdings++) != NULLCHAR ) {
\r
1927 piece = CharToPiece( ToUpper(p) );
\r
1928 if(piece == EmptySquare) continue;
\r
1929 /*j = (int) piece - (int) WhitePawn;*/
\r
1930 j = PieceToNumber(piece);
\r
1931 if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */
\r
1932 if(j < 0) continue; /* should not happen */
\r
1933 piece = (ChessSquare) ( (int)piece + (int)lowestPiece );
\r
1934 board[holdingsStartRow+j*direction][holdingsColumn] = piece;
\r
1935 board[holdingsStartRow+j*direction][countsColumn]++;
\r
1942 VariantSwitch(Board board, VariantClass newVariant)
\r
1944 int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
\r
1945 int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;
\r
1946 Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;
\r
1948 startedFromPositionFile = FALSE;
\r
1949 if(gameInfo.variant == newVariant) return;
\r
1951 /* [HGM] This routine is called each time an assignment is made to
\r
1952 * gameInfo.variant during a game, to make sure the board sizes
\r
1953 * are set to match the new variant. If that means adding or deleting
\r
1954 * holdings, we shift the playing board accordingly
\r
1955 * This kludge is needed because in ICS observe mode, we get boards
\r
1956 * of an ongoing game without knowing the variant, and learn about the
\r
1957 * latter only later. This can be because of the move list we requested,
\r
1958 * in which case the game history is refilled from the beginning anyway,
\r
1959 * but also when receiving holdings of a crazyhouse game. In the latter
\r
1960 * case we want to add those holdings to the already received position.
\r
1964 if (appData.debugMode) {
\r
1965 fprintf(debugFP, "Switch board from %s to %s\n",
\r
1966 VariantName(gameInfo.variant), VariantName(newVariant));
\r
1967 setbuf(debugFP, NULL);
\r
1969 shuffleOpenings = 0; /* [HGM] shuffle */
\r
1970 gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
\r
1971 switch(newVariant) {
\r
1972 case VariantShogi:
\r
1973 newWidth = 9; newHeight = 9;
\r
1974 gameInfo.holdingsSize = 7;
\r
1975 case VariantBughouse:
\r
1976 case VariantCrazyhouse:
\r
1977 newHoldingsWidth = 2; break;
\r
1979 newHoldingsWidth = gameInfo.holdingsSize = 0;
\r
1982 if(newWidth != gameInfo.boardWidth ||
\r
1983 newHeight != gameInfo.boardHeight ||
\r
1984 newHoldingsWidth != gameInfo.holdingsWidth ) {
\r
1986 /* shift position to new playing area, if needed */
\r
1987 if(newHoldingsWidth > gameInfo.holdingsWidth) {
\r
1988 for(i=0; i<BOARD_HEIGHT; i++)
\r
1989 for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
\r
1990 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
\r
1992 for(i=0; i<newHeight; i++) {
\r
1993 board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
\r
1994 board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
\r
1996 } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
\r
1997 for(i=0; i<BOARD_HEIGHT; i++)
\r
1998 for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
\r
1999 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
\r
2003 gameInfo.boardWidth = newWidth;
\r
2004 gameInfo.boardHeight = newHeight;
\r
2005 gameInfo.holdingsWidth = newHoldingsWidth;
\r
2006 gameInfo.variant = newVariant;
\r
2007 InitDrawingSizes(-2, 0);
\r
2009 /* [HGM] The following should definitely be solved in a better way */
\r
2011 CopyBoard(board, tempBoard); /* save position in case it is board[0] */
\r
2012 for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];
\r
2013 saveEP = epStatus[0];
\r
2015 InitPosition(FALSE); /* this sets up board[0], but also other stuff */
\r
2017 epStatus[0] = saveEP;
\r
2018 for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];
\r
2019 CopyBoard(tempBoard, board); /* restore position received from ICS */
\r
2021 } else { gameInfo.variant = newVariant; InitPosition(FALSE); }
\r
2023 forwardMostMove = oldForwardMostMove;
\r
2024 backwardMostMove = oldBackwardMostMove;
\r
2025 currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */
\r
2028 static int loggedOn = FALSE;
\r
2030 /*-- Game start info cache: --*/
\r
2032 char gs_kind[MSG_SIZ];
\r
2033 static char player1Name[128] = "";
\r
2034 static char player2Name[128] = "";
\r
2035 static int player1Rating = -1;
\r
2036 static int player2Rating = -1;
\r
2037 /*----------------------------*/
\r
2039 ColorClass curColor = ColorNormal;
\r
2040 int suppressKibitz = 0;
\r
2043 read_from_ics(isr, closure, data, count, error)
\r
2044 InputSourceRef isr;
\r
2050 #define BUF_SIZE 8192
\r
2051 #define STARTED_NONE 0
\r
2052 #define STARTED_MOVES 1
\r
2053 #define STARTED_BOARD 2
\r
2054 #define STARTED_OBSERVE 3
\r
2055 #define STARTED_HOLDINGS 4
\r
2056 #define STARTED_CHATTER 5
\r
2057 #define STARTED_COMMENT 6
\r
2058 #define STARTED_MOVES_NOHIDE 7
\r
2060 static int started = STARTED_NONE;
\r
2061 static char parse[20000];
\r
2062 static int parse_pos = 0;
\r
2063 static char buf[BUF_SIZE + 1];
\r
2064 static int firstTime = TRUE, intfSet = FALSE;
\r
2065 static ColorClass prevColor = ColorNormal;
\r
2066 static int savingComment = FALSE;
\r
2072 int backup; /* [DM] For zippy color lines */
\r
2075 if (appData.debugMode) {
\r
2077 fprintf(debugFP, "<ICS: ");
\r
2078 show_bytes(debugFP, data, count);
\r
2079 fprintf(debugFP, "\n");
\r
2083 if (appData.debugMode) { int f = forwardMostMove;
\r
2084 fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,
\r
2085 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
\r
2088 /* If last read ended with a partial line that we couldn't parse,
\r
2089 prepend it to the new read and try again. */
\r
2090 if (leftover_len > 0) {
\r
2091 for (i=0; i<leftover_len; i++)
\r
2092 buf[i] = buf[leftover_start + i];
\r
2095 /* Copy in new characters, removing nulls and \r's */
\r
2096 buf_len = leftover_len;
\r
2097 for (i = 0; i < count; i++) {
\r
2098 if (data[i] != NULLCHAR && data[i] != '\r')
\r
2099 buf[buf_len++] = data[i];
\r
2100 if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' &&
\r
2101 buf[buf_len-3]==' ' && buf[buf_len-2]==' ' && buf[buf_len-1]==' ')
\r
2102 buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous
\r
2105 buf[buf_len] = NULLCHAR;
\r
2106 next_out = leftover_len;
\r
2107 leftover_start = 0;
\r
2110 while (i < buf_len) {
\r
2111 /* Deal with part of the TELNET option negotiation
\r
2112 protocol. We refuse to do anything beyond the
\r
2113 defaults, except that we allow the WILL ECHO option,
\r
2114 which ICS uses to turn off password echoing when we are
\r
2115 directly connected to it. We reject this option
\r
2116 if localLineEditing mode is on (always on in xboard)
\r
2117 and we are talking to port 23, which might be a real
\r
2118 telnet server that will try to keep WILL ECHO on permanently.
\r
2120 if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
\r
2121 static int remoteEchoOption = FALSE; /* telnet ECHO option */
\r
2122 unsigned char option;
\r
2124 switch ((unsigned char) buf[++i]) {
\r
2126 if (appData.debugMode)
\r
2127 fprintf(debugFP, "\n<WILL ");
\r
2128 switch (option = (unsigned char) buf[++i]) {
\r
2130 if (appData.debugMode)
\r
2131 fprintf(debugFP, "ECHO ");
\r
2132 /* Reply only if this is a change, according
\r
2133 to the protocol rules. */
\r
2134 if (remoteEchoOption) break;
\r
2135 if (appData.localLineEditing &&
\r
2136 atoi(appData.icsPort) == TN_PORT) {
\r
2137 TelnetRequest(TN_DONT, TN_ECHO);
\r
2140 TelnetRequest(TN_DO, TN_ECHO);
\r
2141 remoteEchoOption = TRUE;
\r
2145 if (appData.debugMode)
\r
2146 fprintf(debugFP, "%d ", option);
\r
2147 /* Whatever this is, we don't want it. */
\r
2148 TelnetRequest(TN_DONT, option);
\r
2153 if (appData.debugMode)
\r
2154 fprintf(debugFP, "\n<WONT ");
\r
2155 switch (option = (unsigned char) buf[++i]) {
\r
2157 if (appData.debugMode)
\r
2158 fprintf(debugFP, "ECHO ");
\r
2159 /* Reply only if this is a change, according
\r
2160 to the protocol rules. */
\r
2161 if (!remoteEchoOption) break;
\r
2163 TelnetRequest(TN_DONT, TN_ECHO);
\r
2164 remoteEchoOption = FALSE;
\r
2167 if (appData.debugMode)
\r
2168 fprintf(debugFP, "%d ", (unsigned char) option);
\r
2169 /* Whatever this is, it must already be turned
\r
2170 off, because we never agree to turn on
\r
2171 anything non-default, so according to the
\r
2172 protocol rules, we don't reply. */
\r
2177 if (appData.debugMode)
\r
2178 fprintf(debugFP, "\n<DO ");
\r
2179 switch (option = (unsigned char) buf[++i]) {
\r
2181 /* Whatever this is, we refuse to do it. */
\r
2182 if (appData.debugMode)
\r
2183 fprintf(debugFP, "%d ", option);
\r
2184 TelnetRequest(TN_WONT, option);
\r
2189 if (appData.debugMode)
\r
2190 fprintf(debugFP, "\n<DONT ");
\r
2191 switch (option = (unsigned char) buf[++i]) {
\r
2193 if (appData.debugMode)
\r
2194 fprintf(debugFP, "%d ", option);
\r
2195 /* Whatever this is, we are already not doing
\r
2196 it, because we never agree to do anything
\r
2197 non-default, so according to the protocol
\r
2198 rules, we don't reply. */
\r
2203 if (appData.debugMode)
\r
2204 fprintf(debugFP, "\n<IAC ");
\r
2205 /* Doubled IAC; pass it through */
\r
2209 if (appData.debugMode)
\r
2210 fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
\r
2211 /* Drop all other telnet commands on the floor */
\r
2214 if (oldi > next_out)
\r
2215 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2216 if (++i > next_out)
\r
2221 /* OK, this at least will *usually* work */
\r
2222 if (!loggedOn && looking_at(buf, &i, "ics%")) {
\r
2226 if (loggedOn && !intfSet) {
\r
2227 if (ics_type == ICS_ICC) {
\r
2229 "/set-quietly interface %s\n/set-quietly style 12\n",
\r
2232 } else if (ics_type == ICS_CHESSNET) {
\r
2233 sprintf(str, "/style 12\n");
\r
2235 strcpy(str, "alias $ @\n$set interface ");
\r
2236 strcat(str, programVersion);
\r
2237 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
\r
2239 strcat(str, "$iset nohighlight 1\n");
\r
2241 strcat(str, "$iset lock 1\n$style 12\n");
\r
2247 if (started == STARTED_COMMENT) {
\r
2248 /* Accumulate characters in comment */
\r
2249 parse[parse_pos++] = buf[i];
\r
2250 if (buf[i] == '\n') {
\r
2251 parse[parse_pos] = NULLCHAR;
\r
2252 if(!suppressKibitz) // [HGM] kibitz
\r
2253 AppendComment(forwardMostMove, StripHighlight(parse));
\r
2254 else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
\r
2255 int nrDigit = 0, nrAlph = 0, i;
\r
2256 if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
\r
2257 { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
\r
2258 parse[parse_pos] = NULLCHAR;
\r
2259 // try to be smart: if it does not look like search info, it should go to
\r
2260 // ICS interaction window after all, not to engine-output window.
\r
2261 for(i=0; i<parse_pos; i++) { // count letters and digits
\r
2262 nrDigit += (parse[i] >= '0' && parse[i] <= '9');
\r
2263 nrAlph += (parse[i] >= 'a' && parse[i] <= 'z');
\r
2264 nrAlph += (parse[i] >= 'A' && parse[i] <= 'Z');
\r
2266 if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
\r
2267 OutputKibitz(suppressKibitz, parse);
\r
2269 char tmp[MSG_SIZ];
\r
2270 sprintf(tmp, _("your opponent kibitzes: %s"), parse);
\r
2271 SendToPlayer(tmp, strlen(tmp));
\r
2274 started = STARTED_NONE;
\r
2276 /* Don't match patterns against characters in chatter */
\r
2281 if (started == STARTED_CHATTER) {
\r
2282 if (buf[i] != '\n') {
\r
2283 /* Don't match patterns against characters in chatter */
\r
2287 started = STARTED_NONE;
\r
2290 /* Kludge to deal with rcmd protocol */
\r
2291 if (firstTime && looking_at(buf, &i, "\001*")) {
\r
2292 DisplayFatalError(&buf[1], 0, 1);
\r
2295 firstTime = FALSE;
\r
2298 if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
\r
2299 ics_type = ICS_ICC;
\r
2301 if (appData.debugMode)
\r
2302 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2305 if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
\r
2306 ics_type = ICS_FICS;
\r
2308 if (appData.debugMode)
\r
2309 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2312 if (!loggedOn && looking_at(buf, &i, "chess.net")) {
\r
2313 ics_type = ICS_CHESSNET;
\r
2315 if (appData.debugMode)
\r
2316 fprintf(debugFP, "ics_type %d\n", ics_type);
\r
2321 (looking_at(buf, &i, "\"*\" is *a registered name") ||
\r
2322 looking_at(buf, &i, "Logging you in as \"*\"") ||
\r
2323 looking_at(buf, &i, "will be \"*\""))) {
\r
2324 strcpy(ics_handle, star_match[0]);
\r
2328 if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
\r
2329 char buf[MSG_SIZ];
\r
2330 sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
\r
2331 DisplayIcsInteractionTitle(buf);
\r
2332 have_set_title = TRUE;
\r
2335 /* skip finger notes */
\r
2336 if (started == STARTED_NONE &&
\r
2337 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
\r
2338 (buf[i] == '1' && buf[i+1] == '0')) &&
\r
2339 buf[i+2] == ':' && buf[i+3] == ' ') {
\r
2340 started = STARTED_CHATTER;
\r
2345 /* skip formula vars */
\r
2346 if (started == STARTED_NONE &&
\r
2347 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
\r
2348 started = STARTED_CHATTER;
\r
2354 // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
\r
2355 if (appData.autoKibitz && started == STARTED_NONE &&
\r
2356 !appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
\r
2357 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
\r
2358 if(looking_at(buf, &i, "* kibitzes: ") &&
\r
2359 (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
\r
2360 StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent
\r
2361 suppressKibitz = TRUE;
\r
2362 if((StrStr(star_match[0], gameInfo.white) == star_match[0])
\r
2363 && (gameMode == IcsPlayingWhite) ||
\r
2364 (StrStr(star_match[0], gameInfo.black) == star_match[0])
\r
2365 && (gameMode == IcsPlayingBlack) ) // opponent kibitz
\r
2366 started = STARTED_CHATTER; // own kibitz we simply discard
\r
2368 started = STARTED_COMMENT; // make sure it will be collected in parse[]
\r
2369 parse_pos = 0; parse[0] = NULLCHAR;
\r
2370 savingComment = TRUE;
\r
2371 suppressKibitz = gameMode != IcsObserving ? 2 :
\r
2372 (StrStr(star_match[0], gameInfo.white) == NULL) + 1;
\r
2376 if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz
\r
2377 started = STARTED_CHATTER;
\r
2378 suppressKibitz = TRUE;
\r
2380 } // [HGM] kibitz: end of patch
\r
2382 if (appData.zippyTalk || appData.zippyPlay) {
\r
2383 /* [DM] Backup address for color zippy lines */
\r
2387 if (loggedOn == TRUE)
\r
2388 if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
\r
2389 (appData.zippyPlay && ZippyMatch(buf, &backup)));
\r
2391 if (ZippyControl(buf, &i) ||
\r
2392 ZippyConverse(buf, &i) ||
\r
2393 (appData.zippyPlay && ZippyMatch(buf, &i))) {
\r
2395 if (!appData.colorize) continue;
\r
2399 } // [DM] 'else { ' deleted
\r
2400 if (/* Don't color "message" or "messages" output */
\r
2401 (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
\r
2402 looking_at(buf, &i, "*. * at *:*: ") ||
\r
2403 looking_at(buf, &i, "--* (*:*): ") ||
\r
2404 /* Regular tells and says */
\r
2405 (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
\r
2406 looking_at(buf, &i, "* (your partner) tells you: ") ||
\r
2407 looking_at(buf, &i, "* says: ") ||
\r
2408 /* Message notifications (same color as tells) */
\r
2409 looking_at(buf, &i, "* has left a message ") ||
\r
2410 looking_at(buf, &i, "* just sent you a message:\n") ||
\r
2411 /* Whispers and kibitzes */
\r
2412 (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
\r
2413 looking_at(buf, &i, "* kibitzes: ") ||
\r
2414 /* Channel tells */
\r
2415 (tkind = 3, looking_at(buf, &i, "*(*: "))) {
\r
2417 if (tkind == 1 && strchr(star_match[0], ':')) {
\r
2418 /* Avoid "tells you:" spoofs in channels */
\r
2421 if (star_match[0][0] == NULLCHAR ||
\r
2422 strchr(star_match[0], ' ') ||
\r
2423 (tkind == 3 && strchr(star_match[1], ' '))) {
\r
2424 /* Reject bogus matches */
\r
2427 if (appData.colorize) {
\r
2428 if (oldi > next_out) {
\r
2429 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2434 Colorize(ColorTell, FALSE);
\r
2435 curColor = ColorTell;
\r
2438 Colorize(ColorKibitz, FALSE);
\r
2439 curColor = ColorKibitz;
\r
2442 p = strrchr(star_match[1], '(');
\r
2444 p = star_match[1];
\r
2448 if (atoi(p) == 1) {
\r
2449 Colorize(ColorChannel1, FALSE);
\r
2450 curColor = ColorChannel1;
\r
2452 Colorize(ColorChannel, FALSE);
\r
2453 curColor = ColorChannel;
\r
2457 curColor = ColorNormal;
\r
2461 if (started == STARTED_NONE && appData.autoComment &&
\r
2462 (gameMode == IcsObserving ||
\r
2463 gameMode == IcsPlayingWhite ||
\r
2464 gameMode == IcsPlayingBlack)) {
\r
2465 parse_pos = i - oldi;
\r
2466 memcpy(parse, &buf[oldi], parse_pos);
\r
2467 parse[parse_pos] = NULLCHAR;
\r
2468 started = STARTED_COMMENT;
\r
2469 savingComment = TRUE;
\r
2471 started = STARTED_CHATTER;
\r
2472 savingComment = FALSE;
\r
2479 if (looking_at(buf, &i, "* s-shouts: ") ||
\r
2480 looking_at(buf, &i, "* c-shouts: ")) {
\r
2481 if (appData.colorize) {
\r
2482 if (oldi > next_out) {
\r
2483 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2486 Colorize(ColorSShout, FALSE);
\r
2487 curColor = ColorSShout;
\r
2490 started = STARTED_CHATTER;
\r
2494 if (looking_at(buf, &i, "--->")) {
\r
2499 if (looking_at(buf, &i, "* shouts: ") ||
\r
2500 looking_at(buf, &i, "--> ")) {
\r
2501 if (appData.colorize) {
\r
2502 if (oldi > next_out) {
\r
2503 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2506 Colorize(ColorShout, FALSE);
\r
2507 curColor = ColorShout;
\r
2510 started = STARTED_CHATTER;
\r
2514 if (looking_at( buf, &i, "Challenge:")) {
\r
2515 if (appData.colorize) {
\r
2516 if (oldi > next_out) {
\r
2517 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2520 Colorize(ColorChallenge, FALSE);
\r
2521 curColor = ColorChallenge;
\r
2527 if (looking_at(buf, &i, "* offers you") ||
\r
2528 looking_at(buf, &i, "* offers to be") ||
\r
2529 looking_at(buf, &i, "* would like to") ||
\r
2530 looking_at(buf, &i, "* requests to") ||
\r
2531 looking_at(buf, &i, "Your opponent offers") ||
\r
2532 looking_at(buf, &i, "Your opponent requests")) {
\r
2534 if (appData.colorize) {
\r
2535 if (oldi > next_out) {
\r
2536 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2539 Colorize(ColorRequest, FALSE);
\r
2540 curColor = ColorRequest;
\r
2545 if (looking_at(buf, &i, "* (*) seeking")) {
\r
2546 if (appData.colorize) {
\r
2547 if (oldi > next_out) {
\r
2548 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2551 Colorize(ColorSeek, FALSE);
\r
2552 curColor = ColorSeek;
\r
2557 if (looking_at(buf, &i, "\\ ")) {
\r
2558 if (prevColor != ColorNormal) {
\r
2559 if (oldi > next_out) {
\r
2560 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2563 Colorize(prevColor, TRUE);
\r
2564 curColor = prevColor;
\r
2566 if (savingComment) {
\r
2567 parse_pos = i - oldi;
\r
2568 memcpy(parse, &buf[oldi], parse_pos);
\r
2569 parse[parse_pos] = NULLCHAR;
\r
2570 started = STARTED_COMMENT;
\r
2572 started = STARTED_CHATTER;
\r
2577 if (looking_at(buf, &i, "Black Strength :") ||
\r
2578 looking_at(buf, &i, "<<< style 10 board >>>") ||
\r
2579 looking_at(buf, &i, "<10>") ||
\r
2580 looking_at(buf, &i, "#@#")) {
\r
2581 /* Wrong board style */
\r
2583 SendToICS(ics_prefix);
\r
2584 SendToICS("set style 12\n");
\r
2585 SendToICS(ics_prefix);
\r
2586 SendToICS("refresh\n");
\r
2590 if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
\r
2592 have_sent_ICS_logon = 1;
\r
2596 if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
\r
2597 (looking_at(buf, &i, "\n<12> ") ||
\r
2598 looking_at(buf, &i, "<12> "))) {
\r
2600 if (oldi > next_out) {
\r
2601 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2604 started = STARTED_BOARD;
\r
2609 if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
\r
2610 looking_at(buf, &i, "<b1> ")) {
\r
2611 if (oldi > next_out) {
\r
2612 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2615 started = STARTED_HOLDINGS;
\r
2620 if (looking_at(buf, &i, "* *vs. * *--- *")) {
\r
2622 /* Header for a move list -- first line */
\r
2624 switch (ics_getting_history) {
\r
2626 switch (gameMode) {
\r
2628 case BeginningOfGame:
\r
2629 /* User typed "moves" or "oldmoves" while we
\r
2630 were idle. Pretend we asked for these
\r
2631 moves and soak them up so user can step
\r
2632 through them and/or save them.
\r
2634 Reset(FALSE, TRUE);
\r
2635 gameMode = IcsObserving;
\r
2638 ics_getting_history = H_GOT_UNREQ_HEADER;
\r
2640 case EditGame: /*?*/
\r
2641 case EditPosition: /*?*/
\r
2642 /* Should above feature work in these modes too? */
\r
2643 /* For now it doesn't */
\r
2644 ics_getting_history = H_GOT_UNWANTED_HEADER;
\r
2647 ics_getting_history = H_GOT_UNWANTED_HEADER;
\r
2652 /* Is this the right one? */
\r
2653 if (gameInfo.white && gameInfo.black &&
\r
2654 strcmp(gameInfo.white, star_match[0]) == 0 &&
\r
2655 strcmp(gameInfo.black, star_match[2]) == 0) {
\r
2657 ics_getting_history = H_GOT_REQ_HEADER;
\r
2660 case H_GOT_REQ_HEADER:
\r
2661 case H_GOT_UNREQ_HEADER:
\r
2662 case H_GOT_UNWANTED_HEADER:
\r
2663 case H_GETTING_MOVES:
\r
2664 /* Should not happen */
\r
2665 DisplayError(_("Error gathering move list: two headers"), 0);
\r
2666 ics_getting_history = H_FALSE;
\r
2670 /* Save player ratings into gameInfo if needed */
\r
2671 if ((ics_getting_history == H_GOT_REQ_HEADER ||
\r
2672 ics_getting_history == H_GOT_UNREQ_HEADER) &&
\r
2673 (gameInfo.whiteRating == -1 ||
\r
2674 gameInfo.blackRating == -1)) {
\r
2676 gameInfo.whiteRating = string_to_rating(star_match[1]);
\r
2677 gameInfo.blackRating = string_to_rating(star_match[3]);
\r
2678 if (appData.debugMode)
\r
2679 fprintf(debugFP, _("Ratings from header: W %d, B %d\n"),
\r
2680 gameInfo.whiteRating, gameInfo.blackRating);
\r
2685 if (looking_at(buf, &i,
\r
2686 "* * match, initial time: * minute*, increment: * second")) {
\r
2687 /* Header for a move list -- second line */
\r
2688 /* Initial board will follow if this is a wild game */
\r
2689 if (gameInfo.event != NULL) free(gameInfo.event);
\r
2690 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
\r
2691 gameInfo.event = StrSave(str);
\r
2692 /* [HGM] we switched variant. Translate boards if needed. */
\r
2693 VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));
\r
2697 if (looking_at(buf, &i, "Move ")) {
\r
2698 /* Beginning of a move list */
\r
2699 switch (ics_getting_history) {
\r
2701 /* Normally should not happen */
\r
2702 /* Maybe user hit reset while we were parsing */
\r
2705 /* Happens if we are ignoring a move list that is not
\r
2706 * the one we just requested. Common if the user
\r
2707 * tries to observe two games without turning off
\r
2710 case H_GETTING_MOVES:
\r
2711 /* Should not happen */
\r
2712 DisplayError(_("Error gathering move list: nested"), 0);
\r
2713 ics_getting_history = H_FALSE;
\r
2715 case H_GOT_REQ_HEADER:
\r
2716 ics_getting_history = H_GETTING_MOVES;
\r
2717 started = STARTED_MOVES;
\r
2719 if (oldi > next_out) {
\r
2720 SendToPlayer(&buf[next_out], oldi - next_out);
\r
2723 case H_GOT_UNREQ_HEADER:
\r
2724 ics_getting_history = H_GETTING_MOVES;
\r
2725 started = STARTED_MOVES_NOHIDE;
\r
2728 case H_GOT_UNWANTED_HEADER:
\r
2729 ics_getting_history = H_FALSE;
\r
2735 if (looking_at(buf, &i, "% ") ||
\r
2736 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
\r
2737 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
\r
2738 savingComment = FALSE;
\r
2739 switch (started) {
\r
2740 case STARTED_MOVES:
\r
2741 case STARTED_MOVES_NOHIDE:
\r
2742 memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
\r
2743 parse[parse_pos + i - oldi] = NULLCHAR;
\r
2744 ParseGameHistory(parse);
\r
2746 if (appData.zippyPlay && first.initDone) {
\r
2747 FeedMovesToProgram(&first, forwardMostMove);
\r
2748 if (gameMode == IcsPlayingWhite) {
\r
2749 if (WhiteOnMove(forwardMostMove)) {
\r
2750 if (first.sendTime) {
\r
2751 if (first.useColors) {
\r
2752 SendToProgram("black\n", &first);
\r
2754 SendTimeRemaining(&first, TRUE);
\r
2757 if (first.useColors) {
\r
2758 SendToProgram("white\ngo\n", &first);
\r
2760 SendToProgram("go\n", &first);
\r
2763 if (first.useColors) {
\r
2764 SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent
\r
2766 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos
\r
2768 first.maybeThinking = TRUE;
\r
2770 if (first.usePlayother) {
\r
2771 if (first.sendTime) {
\r
2772 SendTimeRemaining(&first, TRUE);
\r
2774 SendToProgram("playother\n", &first);
\r
2775 firstMove = FALSE;
\r
2780 } else if (gameMode == IcsPlayingBlack) {
\r
2781 if (!WhiteOnMove(forwardMostMove)) {
\r
2782 if (first.sendTime) {
\r
2783 if (first.useColors) {
\r
2784 SendToProgram("white\n", &first);
\r
2786 SendTimeRemaining(&first, FALSE);
\r
2789 if (first.useColors) {
\r
2790 SendToProgram("black\ngo\n", &first);
\r
2792 SendToProgram("go\n", &first);
\r
2795 if (first.useColors) {
\r
2796 SendToProgram("black\n", &first);
\r
2798 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);
\r
2800 first.maybeThinking = TRUE;
\r
2802 if (first.usePlayother) {
\r
2803 if (first.sendTime) {
\r
2804 SendTimeRemaining(&first, FALSE);
\r
2806 SendToProgram("playother\n", &first);
\r
2807 firstMove = FALSE;
\r
2815 if (gameMode == IcsObserving && ics_gamenum == -1) {
\r
2816 /* Moves came from oldmoves or moves command
\r
2817 while we weren't doing anything else.
\r
2819 currentMove = forwardMostMove;
\r
2820 ClearHighlights();/*!!could figure this out*/
\r
2821 flipView = appData.flipView;
\r
2822 DrawPosition(FALSE, boards[currentMove]);
\r
2823 DisplayBothClocks();
\r
2824 sprintf(str, "%s vs. %s",
\r
2825 gameInfo.white, gameInfo.black);
\r
2826 DisplayTitle(str);
\r
2827 gameMode = IcsIdle;
\r
2829 /* Moves were history of an active game */
\r
2830 if (gameInfo.resultDetails != NULL) {
\r
2831 free(gameInfo.resultDetails);
\r
2832 gameInfo.resultDetails = NULL;
\r
2835 HistorySet(parseList, backwardMostMove,
\r
2836 forwardMostMove, currentMove-1);
\r
2837 DisplayMove(currentMove - 1);
\r
2838 if (started == STARTED_MOVES) next_out = i;
\r
2839 started = STARTED_NONE;
\r
2840 ics_getting_history = H_FALSE;
\r
2843 case STARTED_OBSERVE:
\r
2844 started = STARTED_NONE;
\r
2845 SendToICS(ics_prefix);
\r
2846 SendToICS("refresh\n");
\r
2852 if(bookHit) { // [HGM] book: simulate book reply
\r
2853 static char bookMove[MSG_SIZ]; // a bit generous?
\r
2855 programStats.depth = programStats.nodes = programStats.time =
\r
2856 programStats.score = programStats.got_only_move = 0;
\r
2857 sprintf(programStats.movelist, "%s (xbook)", bookHit);
\r
2859 strcpy(bookMove, "move ");
\r
2860 strcat(bookMove, bookHit);
\r
2861 HandleMachineMove(bookMove, &first);
\r
2866 if ((started == STARTED_MOVES || started == STARTED_BOARD ||
\r
2867 started == STARTED_HOLDINGS ||
\r
2868 started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
\r
2869 /* Accumulate characters in move list or board */
\r
2870 parse[parse_pos++] = buf[i];
\r
2873 /* Start of game messages. Mostly we detect start of game
\r
2874 when the first board image arrives. On some versions
\r
2875 of the ICS, though, we need to do a "refresh" after starting
\r
2876 to observe in order to get the current board right away. */
\r
2877 if (looking_at(buf, &i, "Adding game * to observation list")) {
\r
2878 started = STARTED_OBSERVE;
\r
2882 /* Handle auto-observe */
\r
2883 if (appData.autoObserve &&
\r
2884 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
\r
2885 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
\r
2887 /* Choose the player that was highlighted, if any. */
\r
2888 if (star_match[0][0] == '\033' ||
\r
2889 star_match[1][0] != '\033') {
\r
2890 player = star_match[0];
\r
2892 player = star_match[2];
\r
2894 sprintf(str, "%sobserve %s\n",
\r
2895 ics_prefix, StripHighlightAndTitle(player));
\r
2898 /* Save ratings from notify string */
\r
2899 strcpy(player1Name, star_match[0]);
\r
2900 player1Rating = string_to_rating(star_match[1]);
\r
2901 strcpy(player2Name, star_match[2]);
\r
2902 player2Rating = string_to_rating(star_match[3]);
\r
2904 if (appData.debugMode)
\r
2906 "Ratings from 'Game notification:' %s %d, %s %d\n",
\r
2907 player1Name, player1Rating,
\r
2908 player2Name, player2Rating);
\r
2913 /* Deal with automatic examine mode after a game,
\r
2914 and with IcsObserving -> IcsExamining transition */
\r
2915 if (looking_at(buf, &i, "Entering examine mode for game *") ||
\r
2916 looking_at(buf, &i, "has made you an examiner of game *")) {
\r
2918 int gamenum = atoi(star_match[0]);
\r
2919 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
\r
2920 gamenum == ics_gamenum) {
\r
2921 /* We were already playing or observing this game;
\r
2922 no need to refetch history */
\r
2923 gameMode = IcsExamining;
\r
2925 pauseExamForwardMostMove = forwardMostMove;
\r
2926 } else if (currentMove < forwardMostMove) {
\r
2927 ForwardInner(forwardMostMove);
\r
2930 /* I don't think this case really can happen */
\r
2931 SendToICS(ics_prefix);
\r
2932 SendToICS("refresh\n");
\r
2937 /* Error messages */
\r
2938 if (ics_user_moved) {
\r
2939 if (looking_at(buf, &i, "Illegal move") ||
\r
2940 looking_at(buf, &i, "Not a legal move") ||
\r
2941 looking_at(buf, &i, "Your king is in check") ||
\r
2942 looking_at(buf, &i, "It isn't your turn") ||
\r
2943 looking_at(buf, &i, "It is not your move")) {
\r
2944 /* Illegal move */
\r
2945 ics_user_moved = 0;
\r
2946 if (forwardMostMove > backwardMostMove) {
\r
2947 currentMove = --forwardMostMove;
\r
2948 DisplayMove(currentMove - 1); /* before DMError */
\r
2949 DisplayMoveError(_("Illegal move (rejected by ICS)"));
\r
2950 DrawPosition(FALSE, boards[currentMove]);
\r
2952 DisplayBothClocks();
\r
2958 if (looking_at(buf, &i, "still have time") ||
\r
2959 looking_at(buf, &i, "not out of time") ||
\r
2960 looking_at(buf, &i, "either player is out of time") ||
\r
2961 looking_at(buf, &i, "has timeseal; checking")) {
\r
2962 /* We must have called his flag a little too soon */
\r
2963 whiteFlag = blackFlag = FALSE;
\r
2967 if (looking_at(buf, &i, "added * seconds to") ||
\r
2968 looking_at(buf, &i, "seconds were added to")) {
\r
2969 /* Update the clocks */
\r
2970 SendToICS(ics_prefix);
\r
2971 SendToICS("refresh\n");
\r
2975 if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
\r
2976 ics_clock_paused = TRUE;
\r
2981 if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
\r
2982 ics_clock_paused = FALSE;
\r
2987 /* Grab player ratings from the Creating: message.
\r
2988 Note we have to check for the special case when
\r
2989 the ICS inserts things like [white] or [black]. */
\r
2990 if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
\r
2991 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
\r
2993 0 player 1 name (not necessarily white)
\r
2995 2 empty, white, or black (IGNORED)
\r
2996 3 player 2 name (not necessarily black)
\r
2999 The names/ratings are sorted out when the game
\r
3000 actually starts (below).
\r
3002 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
\r
3003 player1Rating = string_to_rating(star_match[1]);
\r
3004 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
\r
3005 player2Rating = string_to_rating(star_match[4]);
\r
3007 if (appData.debugMode)
\r
3009 "Ratings from 'Creating:' %s %d, %s %d\n",
\r
3010 player1Name, player1Rating,
\r
3011 player2Name, player2Rating);
\r
3016 /* Improved generic start/end-of-game messages */
\r
3017 if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
\r
3018 (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
\r
3019 /* If tkind == 0: */
\r
3020 /* star_match[0] is the game number */
\r
3021 /* [1] is the white player's name */
\r
3022 /* [2] is the black player's name */
\r
3023 /* For end-of-game: */
\r
3024 /* [3] is the reason for the game end */
\r
3025 /* [4] is a PGN end game-token, preceded by " " */
\r
3026 /* For start-of-game: */
\r
3027 /* [3] begins with "Creating" or "Continuing" */
\r
3028 /* [4] is " *" or empty (don't care). */
\r
3029 int gamenum = atoi(star_match[0]);
\r
3030 char *whitename, *blackname, *why, *endtoken;
\r
3031 ChessMove endtype = (ChessMove) 0;
\r
3034 whitename = star_match[1];
\r
3035 blackname = star_match[2];
\r
3036 why = star_match[3];
\r
3037 endtoken = star_match[4];
\r
3039 whitename = star_match[1];
\r
3040 blackname = star_match[3];
\r
3041 why = star_match[5];
\r
3042 endtoken = star_match[6];
\r
3045 /* Game start messages */
\r
3046 if (strncmp(why, "Creating ", 9) == 0 ||
\r
3047 strncmp(why, "Continuing ", 11) == 0) {
\r
3048 gs_gamenum = gamenum;
\r
3049 strcpy(gs_kind, strchr(why, ' ') + 1);
\r
3051 if (appData.zippyPlay) {
\r
3052 ZippyGameStart(whitename, blackname);
\r
3058 /* Game end messages */
\r
3059 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
\r
3060 ics_gamenum != gamenum) {
\r
3063 while (endtoken[0] == ' ') endtoken++;
\r
3064 switch (endtoken[0]) {
\r
3067 endtype = GameUnfinished;
\r
3070 endtype = BlackWins;
\r
3073 if (endtoken[1] == '/')
\r
3074 endtype = GameIsDrawn;
\r
3076 endtype = WhiteWins;
\r
3079 GameEnds(endtype, why, GE_ICS);
\r
3081 if (appData.zippyPlay && first.initDone) {
\r
3082 ZippyGameEnd(endtype, why);
\r
3083 if (first.pr == NULL) {
\r
3084 /* Start the next process early so that we'll
\r
3085 be ready for the next challenge */
\r
3086 StartChessProgram(&first);
\r
3088 /* Send "new" early, in case this command takes
\r
3089 a long time to finish, so that we'll be ready
\r
3090 for the next challenge. */
\r
3091 gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'
\r
3092 Reset(TRUE, TRUE);
\r
3098 if (looking_at(buf, &i, "Removing game * from observation") ||
\r
3099 looking_at(buf, &i, "no longer observing game *") ||
\r
3100 looking_at(buf, &i, "Game * (*) has no examiners")) {
\r
3101 if (gameMode == IcsObserving &&
\r
3102 atoi(star_match[0]) == ics_gamenum)
\r
3104 /* icsEngineAnalyze */
\r
3105 if (appData.icsEngineAnalyze) {
\r
3106 ExitAnalyzeMode();
\r
3110 gameMode = IcsIdle;
\r
3112 ics_user_moved = FALSE;
\r
3117 if (looking_at(buf, &i, "no longer examining game *")) {
\r
3118 if (gameMode == IcsExamining &&
\r
3119 atoi(star_match[0]) == ics_gamenum)
\r
3121 gameMode = IcsIdle;
\r
3123 ics_user_moved = FALSE;
\r
3128 /* Advance leftover_start past any newlines we find,
\r
3129 so only partial lines can get reparsed */
\r
3130 if (looking_at(buf, &i, "\n")) {
\r
3131 prevColor = curColor;
\r
3132 if (curColor != ColorNormal) {
\r
3133 if (oldi > next_out) {
\r
3134 SendToPlayer(&buf[next_out], oldi - next_out);
\r
3137 Colorize(ColorNormal, FALSE);
\r
3138 curColor = ColorNormal;
\r
3140 if (started == STARTED_BOARD) {
\r
3141 started = STARTED_NONE;
\r
3142 parse[parse_pos] = NULLCHAR;
\r
3143 ParseBoard12(parse);
\r
3144 ics_user_moved = 0;
\r
3146 /* Send premove here */
\r
3147 if (appData.premove) {
\r
3148 char str[MSG_SIZ];
\r
3149 if (currentMove == 0 &&
\r
3150 gameMode == IcsPlayingWhite &&
\r
3151 appData.premoveWhite) {
\r
3152 sprintf(str, "%s%s\n", ics_prefix,
\r
3153 appData.premoveWhiteText);
\r
3154 if (appData.debugMode)
\r
3155 fprintf(debugFP, "Sending premove:\n");
\r
3157 } else if (currentMove == 1 &&
\r
3158 gameMode == IcsPlayingBlack &&
\r
3159 appData.premoveBlack) {
\r
3160 sprintf(str, "%s%s\n", ics_prefix,
\r
3161 appData.premoveBlackText);
\r
3162 if (appData.debugMode)
\r
3163 fprintf(debugFP, "Sending premove:\n");
\r
3165 } else if (gotPremove) {
\r
3167 ClearPremoveHighlights();
\r
3168 if (appData.debugMode)
\r
3169 fprintf(debugFP, "Sending premove:\n");
\r
3170 UserMoveEvent(premoveFromX, premoveFromY,
\r
3171 premoveToX, premoveToY,
\r
3172 premovePromoChar);
\r
3176 /* Usually suppress following prompt */
\r
3177 if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
\r
3178 if (looking_at(buf, &i, "*% ")) {
\r
3179 savingComment = FALSE;
\r
3183 } else if (started == STARTED_HOLDINGS) {
\r
3185 char new_piece[MSG_SIZ];
\r
3186 started = STARTED_NONE;
\r
3187 parse[parse_pos] = NULLCHAR;
\r
3188 if (appData.debugMode)
\r
3189 fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",
\r
3190 parse, currentMove);
\r
3191 if (sscanf(parse, " game %d", &gamenum) == 1 &&
\r
3192 gamenum == ics_gamenum) {
\r
3193 if (gameInfo.variant == VariantNormal) {
\r
3194 /* [HGM] We seem to switch variant during a game!
\r
3195 * Presumably no holdings were displayed, so we have
\r
3196 * to move the position two files to the right to
\r
3197 * create room for them!
\r
3199 VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */
\r
3200 /* Get a move list just to see the header, which
\r
3201 will tell us whether this is really bug or zh */
\r
3202 if (ics_getting_history == H_FALSE) {
\r
3203 ics_getting_history = H_REQUESTED;
\r
3204 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
\r
3208 new_piece[0] = NULLCHAR;
\r
3209 sscanf(parse, "game %d white [%s black [%s <- %s",
\r
3210 &gamenum, white_holding, black_holding,
\r
3212 white_holding[strlen(white_holding)-1] = NULLCHAR;
\r
3213 black_holding[strlen(black_holding)-1] = NULLCHAR;
\r
3214 /* [HGM] copy holdings to board holdings area */
\r
3215 CopyHoldings(boards[currentMove], white_holding, WhitePawn);
\r
3216 CopyHoldings(boards[currentMove], black_holding, BlackPawn);
\r
3218 if (appData.zippyPlay && first.initDone) {
\r
3219 ZippyHoldings(white_holding, black_holding,
\r
3223 if (tinyLayout || smallLayout) {
\r
3224 char wh[16], bh[16];
\r
3225 PackHolding(wh, white_holding);
\r
3226 PackHolding(bh, black_holding);
\r
3227 sprintf(str, "[%s-%s] %s-%s", wh, bh,
\r
3228 gameInfo.white, gameInfo.black);
\r
3230 sprintf(str, "%s [%s] vs. %s [%s]",
\r
3231 gameInfo.white, white_holding,
\r
3232 gameInfo.black, black_holding);
\r
3235 DrawPosition(FALSE, boards[currentMove]);
\r
3236 DisplayTitle(str);
\r
3238 /* Suppress following prompt */
\r
3239 if (looking_at(buf, &i, "*% ")) {
\r
3240 savingComment = FALSE;
\r
3247 i++; /* skip unparsed character and loop back */
\r
3250 if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window
\r
3251 started != STARTED_HOLDINGS && i > next_out) {
\r
3252 SendToPlayer(&buf[next_out], i - next_out);
\r
3255 suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above
\r
3257 leftover_len = buf_len - leftover_start;
\r
3258 /* if buffer ends with something we couldn't parse,
\r
3259 reparse it after appending the next read */
\r
3261 } else if (count == 0) {
\r
3262 RemoveInputSource(isr);
\r
3263 DisplayFatalError(_("Connection closed by ICS"), 0, 0);
\r
3265 DisplayFatalError(_("Error reading from ICS"), error, 1);
\r
3270 /* Board style 12 looks like this:
\r
3272 <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
3274 * The "<12> " is stripped before it gets to this routine. The two
\r
3275 * trailing 0's (flip state and clock ticking) are later addition, and
\r
3276 * some chess servers may not have them, or may have only the first.
\r
3277 * Additional trailing fields may be added in the future.
\r
3280 #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
3282 #define RELATION_OBSERVING_PLAYED 0
\r
3283 #define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
\r
3284 #define RELATION_PLAYING_MYMOVE 1
\r
3285 #define RELATION_PLAYING_NOTMYMOVE -1
\r
3286 #define RELATION_EXAMINING 2
\r
3287 #define RELATION_ISOLATED_BOARD -3
\r
3288 #define RELATION_STARTING_POSITION -4 /* FICS only */
\r
3291 ParseBoard12(string)
\r
3294 GameMode newGameMode;
\r
3295 int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
\r
3296 int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
\r
3297 int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
\r
3298 char to_play, board_chars[200];
\r
3299 char move_str[500], str[500], elapsed_time[500];
\r
3300 char black[32], white[32];
\r
3302 int prevMove = currentMove;
\r
3304 ChessMove moveType;
\r
3305 int fromX, fromY, toX, toY;
\r
3307 int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
\r
3308 char *bookHit = NULL; // [HGM] book
\r
3310 fromX = fromY = toX = toY = -1;
\r
3314 if (appData.debugMode)
\r
3315 fprintf(debugFP, _("Parsing board: %s\n"), string);
\r
3317 move_str[0] = NULLCHAR;
\r
3318 elapsed_time[0] = NULLCHAR;
\r
3319 { /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */
\r
3321 while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {
\r
3322 if(string[i] == ' ') { ranks++; files = 0; }
\r
3326 for(j = 0; j <i; j++) board_chars[j] = string[j];
\r
3327 board_chars[i] = '\0';
\r
3330 n = sscanf(string, PATTERN, &to_play, &double_push,
\r
3331 &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
\r
3332 &gamenum, white, black, &relation, &basetime, &increment,
\r
3333 &white_stren, &black_stren, &white_time, &black_time,
\r
3334 &moveNum, str, elapsed_time, move_str, &ics_flip,
\r
3338 sprintf(str, _("Failed to parse board string:\n\"%s\""), string);
\r
3339 DisplayError(str, 0);
\r
3343 /* Convert the move number to internal form */
\r
3344 moveNum = (moveNum - 1) * 2;
\r
3345 if (to_play == 'B') moveNum++;
\r
3346 if (moveNum >= MAX_MOVES) {
\r
3347 DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
\r
3352 switch (relation) {
\r
3353 case RELATION_OBSERVING_PLAYED:
\r
3354 case RELATION_OBSERVING_STATIC:
\r
3355 if (gamenum == -1) {
\r
3356 /* Old ICC buglet */
\r
3357 relation = RELATION_OBSERVING_STATIC;
\r
3359 newGameMode = IcsObserving;
\r
3361 case RELATION_PLAYING_MYMOVE:
\r
3362 case RELATION_PLAYING_NOTMYMOVE:
\r
3364 ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
\r
3365 IcsPlayingWhite : IcsPlayingBlack;
\r
3367 case RELATION_EXAMINING:
\r
3368 newGameMode = IcsExamining;
\r
3370 case RELATION_ISOLATED_BOARD:
\r
3372 /* Just display this board. If user was doing something else,
\r
3373 we will forget about it until the next board comes. */
\r
3374 newGameMode = IcsIdle;
\r
3376 case RELATION_STARTING_POSITION:
\r
3377 newGameMode = gameMode;
\r
3381 /* Modify behavior for initial board display on move listing
\r
3384 switch (ics_getting_history) {
\r
3388 case H_GOT_REQ_HEADER:
\r
3389 case H_GOT_UNREQ_HEADER:
\r
3390 /* This is the initial position of the current game */
\r
3391 gamenum = ics_gamenum;
\r
3392 moveNum = 0; /* old ICS bug workaround */
\r
3393 if (to_play == 'B') {
\r
3394 startedFromSetupPosition = TRUE;
\r
3395 blackPlaysFirst = TRUE;
\r
3397 if (forwardMostMove == 0) forwardMostMove = 1;
\r
3398 if (backwardMostMove == 0) backwardMostMove = 1;
\r
3399 if (currentMove == 0) currentMove = 1;
\r
3401 newGameMode = gameMode;
\r
3402 relation = RELATION_STARTING_POSITION; /* ICC needs this */
\r
3404 case H_GOT_UNWANTED_HEADER:
\r
3405 /* This is an initial board that we don't want */
\r
3407 case H_GETTING_MOVES:
\r
3408 /* Should not happen */
\r
3409 DisplayError(_("Error gathering move list: extra board"), 0);
\r
3410 ics_getting_history = H_FALSE;
\r
3414 /* Take action if this is the first board of a new game, or of a
\r
3415 different game than is currently being displayed. */
\r
3416 if (gamenum != ics_gamenum || newGameMode != gameMode ||
\r
3417 relation == RELATION_ISOLATED_BOARD) {
\r
3419 /* Forget the old game and get the history (if any) of the new one */
\r
3420 if (gameMode != BeginningOfGame) {
\r
3421 Reset(FALSE, TRUE);
\r
3424 if (appData.autoRaiseBoard) BoardToTop();
\r
3426 if (gamenum == -1) {
\r
3427 newGameMode = IcsIdle;
\r
3428 } else if (moveNum > 0 && newGameMode != IcsIdle &&
\r
3429 appData.getMoveList) {
\r
3430 /* Need to get game history */
\r
3431 ics_getting_history = H_REQUESTED;
\r
3432 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
\r
3436 /* Initially flip the board to have black on the bottom if playing
\r
3437 black or if the ICS flip flag is set, but let the user change
\r
3438 it with the Flip View button. */
\r
3439 flipView = appData.autoFlipView ?
\r
3440 (newGameMode == IcsPlayingBlack) || ics_flip :
\r
3443 /* Done with values from previous mode; copy in new ones */
\r
3444 gameMode = newGameMode;
\r
3446 ics_gamenum = gamenum;
\r
3447 if (gamenum == gs_gamenum) {
\r
3448 int klen = strlen(gs_kind);
\r
3449 if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
\r
3450 sprintf(str, "ICS %s", gs_kind);
\r
3451 gameInfo.event = StrSave(str);
\r
3453 gameInfo.event = StrSave("ICS game");
\r
3455 gameInfo.site = StrSave(appData.icsHost);
\r
3456 gameInfo.date = PGNDate();
\r
3457 gameInfo.round = StrSave("-");
\r
3458 gameInfo.white = StrSave(white);
\r
3459 gameInfo.black = StrSave(black);
\r
3460 timeControl = basetime * 60 * 1000;
\r
3461 timeControl_2 = 0;
\r
3462 timeIncrement = increment * 1000;
\r
3463 movesPerSession = 0;
\r
3464 gameInfo.timeControl = TimeControlTagValue();
\r
3465 VariantSwitch(board, StringToVariant(gameInfo.event) );
\r
3466 if (appData.debugMode) {
\r
3467 fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);
\r
3468 fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));
\r
3469 setbuf(debugFP, NULL);
\r
3472 gameInfo.outOfBook = NULL;
\r
3474 /* Do we have the ratings? */
\r
3475 if (strcmp(player1Name, white) == 0 &&
\r
3476 strcmp(player2Name, black) == 0) {
\r
3477 if (appData.debugMode)
\r