2 * backend.c -- Common back end for X and Windows NT versions of
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * Enhancements Copyright 2005 Alessandro Scotti
12 * The following terms apply to Digital Equipment Corporation's copyright
14 * ------------------------------------------------------------------------
17 * Permission to use, copy, modify, and distribute this software and its
18 * documentation for any purpose and without fee is hereby granted,
19 * provided that the above copyright notice appear in all copies and that
20 * both that copyright notice and this permission notice appear in
21 * supporting documentation, and that the name of Digital not be
22 * used in advertising or publicity pertaining to distribution of the
23 * software without specific, written prior permission.
25 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
27 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
30 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32 * ------------------------------------------------------------------------
34 * The following terms apply to the enhanced version of XBoard
35 * distributed by the Free Software Foundation:
36 * ------------------------------------------------------------------------
38 * GNU XBoard is free software: you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation, either version 3 of the License, or (at
41 * your option) any later version.
43 * GNU XBoard is distributed in the hope that it will be useful, but
44 * WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46 * General Public License for more details.
48 * You should have received a copy of the GNU General Public License
49 * along with this program. If not, see http://www.gnu.org/licenses/. *
51 *------------------------------------------------------------------------
52 ** See the file ChangeLog for a revision history. */
54 /* [AS] Also useful here for debugging */
58 #define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
60 int flock(int f, int code);
66 #define DoSleep( n ) if( (n) >= 0) sleep(n)
77 #include <sys/types.h>
86 #else /* not STDC_HEADERS */
89 # else /* not HAVE_STRING_H */
91 # endif /* not HAVE_STRING_H */
92 #endif /* not STDC_HEADERS */
95 # include <sys/fcntl.h>
96 #else /* not HAVE_SYS_FCNTL_H */
99 # endif /* HAVE_FCNTL_H */
100 #endif /* not HAVE_SYS_FCNTL_H */
102 #if TIME_WITH_SYS_TIME
103 # include <sys/time.h>
107 # include <sys/time.h>
113 #if defined(_amigados) && !defined(__GNUC__)
118 extern int gettimeofday(struct timeval *, struct timezone *);
126 #include "frontend.h"
133 #include "backendz.h"
137 # define _(s) gettext (s)
138 # define N_(s) gettext_noop (s)
139 # define T_(s) gettext(s)
152 /* A point in time */
154 long sec; /* Assuming this is >= 32 bits */
155 int ms; /* Assuming this is >= 16 bits */
158 int establish P((void));
159 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
160 char *buf, int count, int error));
161 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
162 char *buf, int count, int error));
163 void ics_printf P((char *format, ...));
164 void SendToICS P((char *s));
165 void SendToICSDelayed P((char *s, long msdelay));
166 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY, int toX, int toY, char promoChar));
167 void HandleMachineMove P((char *message, ChessProgramState *cps));
168 int AutoPlayOneMove P((void));
169 int LoadGameOneMove P((ChessMove readAhead));
170 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
171 int LoadPositionFromFile P((char *filename, int n, char *title));
172 int SavePositionToFile P((char *filename));
173 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
175 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
176 void ShowMove P((int fromX, int fromY, int toX, int toY));
177 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
178 /*char*/int promoChar));
179 void BackwardInner P((int target));
180 void ForwardInner P((int target));
181 int Adjudicate P((ChessProgramState *cps));
182 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
183 void EditPositionDone P((Boolean fakeRights));
184 void PrintOpponents P((FILE *fp));
185 void PrintPosition P((FILE *fp, int move));
186 void StartChessProgram P((ChessProgramState *cps));
187 void SendToProgram P((char *message, ChessProgramState *cps));
188 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
189 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
190 char *buf, int count, int error));
191 void SendTimeControl P((ChessProgramState *cps,
192 int mps, long tc, int inc, int sd, int st));
193 char *TimeControlTagValue P((void));
194 void Attention P((ChessProgramState *cps));
195 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
196 int ResurrectChessProgram P((void));
197 void DisplayComment P((int moveNumber, char *text));
198 void DisplayMove P((int moveNumber));
200 void ParseGameHistory P((char *game));
201 void ParseBoard12 P((char *string));
202 void KeepAlive P((void));
203 void StartClocks P((void));
204 void SwitchClocks P((int nr));
205 void StopClocks P((void));
206 void ResetClocks P((void));
207 char *PGNDate P((void));
208 void SetGameInfo P((void));
209 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
210 int RegisterMove P((void));
211 void MakeRegisteredMove P((void));
212 void TruncateGame P((void));
213 int looking_at P((char *, int *, char *));
214 void CopyPlayerNameIntoFileName P((char **, char *));
215 char *SavePart P((char *));
216 int SaveGameOldStyle P((FILE *));
217 int SaveGamePGN P((FILE *));
218 void GetTimeMark P((TimeMark *));
219 long SubtractTimeMarks P((TimeMark *, TimeMark *));
220 int CheckFlags P((void));
221 long NextTickLength P((long));
222 void CheckTimeControl P((void));
223 void show_bytes P((FILE *, char *, int));
224 int string_to_rating P((char *str));
225 void ParseFeatures P((char* args, ChessProgramState *cps));
226 void InitBackEnd3 P((void));
227 void FeatureDone P((ChessProgramState* cps, int val));
228 void InitChessProgram P((ChessProgramState *cps, int setup));
229 void OutputKibitz(int window, char *text);
230 int PerpetualChase(int first, int last);
231 int EngineOutputIsUp();
232 void InitDrawingSizes(int x, int y);
233 void NextMatchGame P((void));
234 int NextTourneyGame P((int nr, int *swap));
235 int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync));
236 FILE *WriteTourneyFile P((char *results));
239 extern void ConsoleCreate();
242 ChessProgramState *WhitePlayer();
243 void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c
244 int VerifyDisplayMode P(());
246 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment
247 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c
248 char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
249 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
250 void ics_update_width P((int new_width));
251 extern char installDir[MSG_SIZ];
252 VariantClass startVariant; /* [HGM] nicks: initial variant */
255 extern int tinyLayout, smallLayout;
256 ChessProgramStats programStats;
257 char lastPV[2][2*MSG_SIZ]; /* [HGM] pv: last PV in thinking output of each engine */
259 static int exiting = 0; /* [HGM] moved to top */
260 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
261 int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
262 Board partnerBoard; /* [HGM] bughouse: for peeking at partner game */
263 int partnerHighlight[2];
264 Boolean partnerBoardValid = 0;
265 char partnerStatus[MSG_SIZ];
267 Boolean originalFlip;
268 Boolean twoBoards = 0;
269 char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */
270 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */
271 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
272 int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
273 Boolean connectionAlive;/* [HGM] alive: ICS connection status from probing */
274 int opponentKibitzes;
275 int lastSavedGame; /* [HGM] save: ID of game */
276 char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
277 extern int chatCount;
279 char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
280 char lastMsg[MSG_SIZ];
281 ChessSquare pieceSweep = EmptySquare;
282 ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
283 int promoDefaultAltered;
285 /* States for ics_getting_history */
287 #define H_REQUESTED 1
288 #define H_GOT_REQ_HEADER 2
289 #define H_GOT_UNREQ_HEADER 3
290 #define H_GETTING_MOVES 4
291 #define H_GOT_UNWANTED_HEADER 5
293 /* whosays values for GameEnds */
302 /* Maximum number of games in a cmail message */
303 #define CMAIL_MAX_GAMES 20
305 /* Different types of move when calling RegisterMove */
307 #define CMAIL_RESIGN 1
309 #define CMAIL_ACCEPT 3
311 /* Different types of result to remember for each game */
312 #define CMAIL_NOT_RESULT 0
313 #define CMAIL_OLD_RESULT 1
314 #define CMAIL_NEW_RESULT 2
316 /* Telnet protocol constants */
327 safeStrCpy( char *dst, const char *src, size_t count )
330 assert( dst != NULL );
331 assert( src != NULL );
334 for(i=0; i<count; i++) if((dst[i] = src[i]) == NULLCHAR) break;
335 if( i == count && dst[count-1] != NULLCHAR)
337 dst[ count-1 ] = '\0'; // make sure incomplete copy still null-terminated
338 if(appData.debugMode)
339 fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst, (int)count);
345 /* Some compiler can't cast u64 to double
346 * This function do the job for us:
348 * We use the highest bit for cast, this only
349 * works if the highest bit is not
350 * in use (This should not happen)
352 * We used this for all compiler
355 u64ToDouble(u64 value)
358 u64 tmp = value & u64Const(0x7fffffffffffffff);
359 r = (double)(s64)tmp;
360 if (value & u64Const(0x8000000000000000))
361 r += 9.2233720368547758080e18; /* 2^63 */
365 /* Fake up flags for now, as we aren't keeping track of castling
366 availability yet. [HGM] Change of logic: the flag now only
367 indicates the type of castlings allowed by the rule of the game.
368 The actual rights themselves are maintained in the array
369 castlingRights, as part of the game history, and are not probed
375 int flags = F_ALL_CASTLE_OK;
376 if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
377 switch (gameInfo.variant) {
379 flags &= ~F_ALL_CASTLE_OK;
380 case VariantGiveaway: // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!
381 flags |= F_IGNORE_CHECK;
383 flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist
386 flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
388 case VariantKriegspiel:
389 flags |= F_KRIEGSPIEL_CAPTURE;
391 case VariantCapaRandom:
392 case VariantFischeRandom:
393 flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */
394 case VariantNoCastle:
395 case VariantShatranj:
398 flags &= ~F_ALL_CASTLE_OK;
406 FILE *gameFileFP, *debugFP;
409 [AS] Note: sometimes, the sscanf() function is used to parse the input
410 into a fixed-size buffer. Because of this, we must be prepared to
411 receive strings as long as the size of the input buffer, which is currently
412 set to 4K for Windows and 8K for the rest.
413 So, we must either allocate sufficiently large buffers here, or
414 reduce the size of the input buffer in the input reading part.
417 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
418 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
419 char thinkOutput1[MSG_SIZ*10];
421 ChessProgramState first, second;
423 /* premove variables */
426 int premoveFromX = 0;
427 int premoveFromY = 0;
428 int premovePromoChar = 0;
430 Boolean alarmSounded;
431 /* end premove variables */
433 char *ics_prefix = "$";
434 int ics_type = ICS_GENERIC;
436 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
437 int pauseExamForwardMostMove = 0;
438 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
439 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
440 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
441 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
442 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
443 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
444 int whiteFlag = FALSE, blackFlag = FALSE;
445 int userOfferedDraw = FALSE;
446 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
447 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
448 int cmailMoveType[CMAIL_MAX_GAMES];
449 long ics_clock_paused = 0;
450 ProcRef icsPR = NoProc, cmailPR = NoProc;
451 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
452 GameMode gameMode = BeginningOfGame;
453 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
454 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
455 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
456 int hiddenThinkOutputState = 0; /* [AS] */
457 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
458 int adjudicateLossPlies = 6;
459 char white_holding[64], black_holding[64];
460 TimeMark lastNodeCountTime;
461 long lastNodeCount=0;
462 int shiftKey; // [HGM] set by mouse handler
464 int have_sent_ICS_logon = 0;
466 int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */
467 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack;
468 long timeControl_2; /* [AS] Allow separate time controls */
469 char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC; /* [HGM] secondary TC: merge of MPS, TC and inc */
470 long timeRemaining[2][MAX_MOVES];
471 int matchGame = 0, nextGame = 0, roundNr = 0;
472 Boolean waitingForGame = FALSE;
473 TimeMark programStartTime, pauseStart;
474 char ics_handle[MSG_SIZ];
475 int have_set_title = 0;
477 /* animateTraining preserves the state of appData.animate
478 * when Training mode is activated. This allows the
479 * response to be animated when appData.animate == TRUE and
480 * appData.animateDragging == TRUE.
482 Boolean animateTraining;
488 Board boards[MAX_MOVES];
489 /* [HGM] Following 7 needed for accurate legality tests: */
490 signed char castlingRank[BOARD_FILES]; // and corresponding ranks
491 signed char initialRights[BOARD_FILES];
492 int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
493 int initialRulePlies, FENrulePlies;
494 FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
497 int mute; // mute all sounds
499 // [HGM] vari: next 12 to save and restore variations
500 #define MAX_VARIATIONS 10
501 int framePtr = MAX_MOVES-1; // points to free stack entry
503 int savedFirst[MAX_VARIATIONS];
504 int savedLast[MAX_VARIATIONS];
505 int savedFramePtr[MAX_VARIATIONS];
506 char *savedDetails[MAX_VARIATIONS];
507 ChessMove savedResult[MAX_VARIATIONS];
509 void PushTail P((int firstMove, int lastMove));
510 Boolean PopTail P((Boolean annotate));
511 void PushInner P((int firstMove, int lastMove));
512 void PopInner P((Boolean annotate));
513 void CleanupTail P((void));
515 ChessSquare FIDEArray[2][BOARD_FILES] = {
516 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
517 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
518 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
519 BlackKing, BlackBishop, BlackKnight, BlackRook }
522 ChessSquare twoKingsArray[2][BOARD_FILES] = {
523 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
524 WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
525 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
526 BlackKing, BlackKing, BlackKnight, BlackRook }
529 ChessSquare KnightmateArray[2][BOARD_FILES] = {
530 { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,
531 WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },
532 { BlackRook, BlackMan, BlackBishop, BlackQueen,
533 BlackUnicorn, BlackBishop, BlackMan, BlackRook }
536 ChessSquare SpartanArray[2][BOARD_FILES] = {
537 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
538 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
539 { BlackAlfil, BlackMarshall, BlackKing, BlackDragon,
540 BlackDragon, BlackKing, BlackAngel, BlackAlfil }
543 ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
544 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
545 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
546 { BlackCardinal, BlackAlfil, BlackMarshall, BlackAngel,
547 BlackKing, BlackMarshall, BlackAlfil, BlackCardinal }
550 ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
551 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,
552 WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
553 { BlackRook, BlackKnight, BlackAlfil, BlackKing,
554 BlackFerz, BlackAlfil, BlackKnight, BlackRook }
557 ChessSquare makrukArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
558 { WhiteRook, WhiteKnight, WhiteMan, WhiteKing,
559 WhiteFerz, WhiteMan, WhiteKnight, WhiteRook },
560 { BlackRook, BlackKnight, BlackMan, BlackFerz,
561 BlackKing, BlackMan, BlackKnight, BlackRook }
565 #if (BOARD_FILES>=10)
566 ChessSquare ShogiArray[2][BOARD_FILES] = {
567 { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,
568 WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },
569 { BlackQueen, BlackKnight, BlackFerz, BlackWazir,
570 BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }
573 ChessSquare XiangqiArray[2][BOARD_FILES] = {
574 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,
575 WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
576 { BlackRook, BlackKnight, BlackAlfil, BlackFerz,
577 BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }
580 ChessSquare CapablancaArray[2][BOARD_FILES] = {
581 { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen,
582 WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
583 { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen,
584 BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
587 ChessSquare GreatArray[2][BOARD_FILES] = {
588 { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing,
589 WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
590 { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing,
591 BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
594 ChessSquare JanusArray[2][BOARD_FILES] = {
595 { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing,
596 WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
597 { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing,
598 BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
602 ChessSquare GothicArray[2][BOARD_FILES] = {
603 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall,
604 WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
605 { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall,
606 BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }
609 #define GothicArray CapablancaArray
613 ChessSquare FalconArray[2][BOARD_FILES] = {
614 { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
615 WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
616 { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
617 BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
620 #define FalconArray CapablancaArray
623 #else // !(BOARD_FILES>=10)
624 #define XiangqiPosition FIDEArray
625 #define CapablancaArray FIDEArray
626 #define GothicArray FIDEArray
627 #define GreatArray FIDEArray
628 #endif // !(BOARD_FILES>=10)
630 #if (BOARD_FILES>=12)
631 ChessSquare CourierArray[2][BOARD_FILES] = {
632 { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,
633 WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },
634 { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
635 BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
637 #else // !(BOARD_FILES>=12)
638 #define CourierArray CapablancaArray
639 #endif // !(BOARD_FILES>=12)
642 Board initialPosition;
645 /* Convert str to a rating. Checks for special cases of "----",
647 "++++", etc. Also strips ()'s */
649 string_to_rating(str)
652 while(*str && !isdigit(*str)) ++str;
654 return 0; /* One of the special "no rating" cases */
662 /* Init programStats */
663 programStats.movelist[0] = 0;
664 programStats.depth = 0;
665 programStats.nr_moves = 0;
666 programStats.moves_left = 0;
667 programStats.nodes = 0;
668 programStats.time = -1; // [HGM] PGNtime: make invalid to recognize engine output
669 programStats.score = 0;
670 programStats.got_only_move = 0;
671 programStats.got_fail = 0;
672 programStats.line_is_book = 0;
677 { // [HGM] moved some code here from InitBackend1 that has to be done after both engines have contributed their settings
678 if (appData.firstPlaysBlack) {
679 first.twoMachinesColor = "black\n";
680 second.twoMachinesColor = "white\n";
682 first.twoMachinesColor = "white\n";
683 second.twoMachinesColor = "black\n";
686 first.other = &second;
687 second.other = &first;
690 if(appData.timeOddsMode) {
691 norm = appData.timeOdds[0];
692 if(norm > appData.timeOdds[1]) norm = appData.timeOdds[1];
694 first.timeOdds = appData.timeOdds[0]/norm;
695 second.timeOdds = appData.timeOdds[1]/norm;
698 if(programVersion) free(programVersion);
699 if (appData.noChessProgram) {
700 programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
701 sprintf(programVersion, "%s", PACKAGE_STRING);
703 /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
704 programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
705 sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
710 UnloadEngine(ChessProgramState *cps)
712 /* Kill off first chess program */
713 if (cps->isr != NULL)
714 RemoveInputSource(cps->isr);
717 if (cps->pr != NoProc) {
719 DoSleep( appData.delayBeforeQuit );
720 SendToProgram("quit\n", cps);
721 DoSleep( appData.delayAfterQuit );
722 DestroyChildProcess(cps->pr, cps->useSigterm);
725 if(appData.debugMode) fprintf(debugFP, "Unload %s\n", cps->which);
729 ClearOptions(ChessProgramState *cps)
732 cps->nrOptions = cps->comboCnt = 0;
733 for(i=0; i<MAX_OPTIONS; i++) {
734 cps->option[i].min = cps->option[i].max = cps->option[i].value = 0;
735 cps->option[i].textValue = 0;
739 char *engineNames[] = {
745 InitEngine(ChessProgramState *cps, int n)
746 { // [HGM] all engine initialiation put in a function that does one engine
750 cps->which = engineNames[n];
751 cps->maybeThinking = FALSE;
755 cps->sendDrawOffers = 1;
757 cps->program = appData.chessProgram[n];
758 cps->host = appData.host[n];
759 cps->dir = appData.directory[n];
760 cps->initString = appData.engInitString[n];
761 cps->computerString = appData.computerString[n];
762 cps->useSigint = TRUE;
763 cps->useSigterm = TRUE;
764 cps->reuse = appData.reuse[n];
765 cps->nps = appData.NPS[n]; // [HGM] nps: copy nodes per second
766 cps->useSetboard = FALSE;
768 cps->usePing = FALSE;
771 cps->usePlayother = FALSE;
772 cps->useColors = TRUE;
773 cps->useUsermove = FALSE;
774 cps->sendICS = FALSE;
775 cps->sendName = appData.icsActive;
776 cps->sdKludge = FALSE;
777 cps->stKludge = FALSE;
778 TidyProgramName(cps->program, cps->host, cps->tidy);
780 safeStrCpy(cps->variants, appData.variant, MSG_SIZ);
781 cps->analysisSupport = 2; /* detect */
782 cps->analyzing = FALSE;
783 cps->initDone = FALSE;
785 /* New features added by Tord: */
786 cps->useFEN960 = FALSE;
787 cps->useOOCastle = TRUE;
788 /* End of new features added by Tord. */
789 cps->fenOverride = appData.fenOverride[n];
791 /* [HGM] time odds: set factor for each machine */
792 cps->timeOdds = appData.timeOdds[n];
794 /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
795 cps->accumulateTC = appData.accumulateTC[n];
796 cps->maxNrOfSessions = 1;
801 cps->supportsNPS = UNKNOWN;
802 cps->memSize = FALSE;
803 cps->maxCores = FALSE;
804 cps->egtFormats[0] = NULLCHAR;
807 cps->optionSettings = appData.engOptions[n];
809 cps->scoreIsAbsolute = appData.scoreIsAbsolute[n]; /* [AS] */
810 cps->isUCI = appData.isUCI[n]; /* [AS] */
811 cps->hasOwnBookUCI = appData.hasOwnBookUCI[n]; /* [AS] */
813 if (appData.protocolVersion[n] > PROTOVER
814 || appData.protocolVersion[n] < 1)
819 len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
820 appData.protocolVersion[n]);
821 if( (len > MSG_SIZ) && appData.debugMode )
822 fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
824 DisplayFatalError(buf, 0, 2);
828 cps->protocolVersion = appData.protocolVersion[n];
831 InitEngineUCI( installDir, cps ); // [HGM] moved here from winboard.c, to make available in xboard
834 ChessProgramState *savCps;
840 if(WaitForEngine(savCps, LoadEngine)) return;
841 CommonEngineInit(); // recalculate time odds
842 if(gameInfo.variant != StringToVariant(appData.variant)) {
843 // we changed variant when loading the engine; this forces us to reset
844 Reset(TRUE, savCps != &first);
845 EditGameEvent(); // for consistency with other path, as Reset changes mode
847 InitChessProgram(savCps, FALSE);
848 SendToProgram("force\n", savCps);
849 DisplayMessage("", "");
850 if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove);
851 for (i = backwardMostMove; i < forwardMostMove; i++) SendMoveToProgram(i, savCps);
857 ReplaceEngine(ChessProgramState *cps, int n)
861 appData.noChessProgram = FALSE;
862 appData.clockMode = TRUE;
864 if(n) return; // only startup first engine immediately; second can wait
865 savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-(
869 extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
870 extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
872 static char resetOptions[] =
873 "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
874 "-firstOptions \"\" -firstNPS -1 -fn \"\"";
877 Load(ChessProgramState *cps, int i)
879 char *p, *q, buf[MSG_SIZ], command[MSG_SIZ];
880 if(engineLine[0]) { // an engine was selected from the combo box
881 snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
882 SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
883 ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
884 ParseArgsFromString(buf);
886 ReplaceEngine(cps, i);
890 while(q = strchr(p, SLASH)) p = q+1;
891 if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; }
892 if(engineDir[0] != NULLCHAR)
893 appData.directory[i] = engineDir;
894 else if(p != engineName) { // derive directory from engine path, when not given
896 appData.directory[i] = strdup(engineName);
898 } else appData.directory[i] = ".";
900 snprintf(command, MSG_SIZ, "%s %s", p, params);
903 appData.chessProgram[i] = strdup(p);
904 appData.isUCI[i] = isUCI;
905 appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
906 appData.hasOwnBookUCI[i] = hasBook;
907 if(!nickName[0]) useNick = FALSE;
908 if(useNick) ASSIGN(appData.pgnName[i], nickName);
911 q = firstChessProgramNames;
912 if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
913 snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "\"%s\" -fd \"%s\"%s%s%s%s%s%s%s%s\n", p, appData.directory[i],
914 useNick ? " -fn \"" : "",
915 useNick ? nickName : "",
917 v1 ? " -firstProtocolVersion 1" : "",
918 hasBook ? "" : " -fNoOwnBookUCI",
919 isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
920 storeVariant ? " -variant " : "",
921 storeVariant ? VariantName(gameInfo.variant) : "");
922 firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
923 snprintf(firstChessProgramNames, len, "%s%s", q, buf);
926 ReplaceEngine(cps, i);
932 int matched, min, sec;
934 * Parse timeControl resource
936 if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
937 appData.movesPerSession)) {
939 snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
940 DisplayFatalError(buf, 0, 2);
944 * Parse searchTime resource
946 if (*appData.searchTime != NULLCHAR) {
947 matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
949 searchTime = min * 60;
950 } else if (matched == 2) {
951 searchTime = min * 60 + sec;
954 snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
955 DisplayFatalError(buf, 0, 2);
964 ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
965 startVariant = StringToVariant(appData.variant); // [HGM] nicks: remember original variant
967 GetTimeMark(&programStartTime);
968 srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
969 pauseStart = programStartTime; pauseStart.sec -= 100; // [HGM] matchpause: fake a pause that has long since ended
972 programStats.ok_to_send = 1;
973 programStats.seen_stat = 0;
976 * Initialize game list
982 * Internet chess server status
984 if (appData.icsActive) {
985 appData.matchMode = FALSE;
986 appData.matchGames = 0;
988 appData.noChessProgram = !appData.zippyPlay;
990 appData.zippyPlay = FALSE;
991 appData.zippyTalk = FALSE;
992 appData.noChessProgram = TRUE;
994 if (*appData.icsHelper != NULLCHAR) {
995 appData.useTelnet = TRUE;
996 appData.telnetProgram = appData.icsHelper;
999 appData.zippyTalk = appData.zippyPlay = FALSE;
1002 /* [AS] Initialize pv info list [HGM] and game state */
1006 for( i=0; i<=framePtr; i++ ) {
1007 pvInfoList[i].depth = -1;
1008 boards[i][EP_STATUS] = EP_NONE;
1009 for( j=0; j<BOARD_FILES-2; j++ ) boards[i][CASTLING][j] = NoRights;
1015 /* [AS] Adjudication threshold */
1016 adjudicateLossThreshold = appData.adjudicateLossThreshold;
1018 InitEngine(&first, 0);
1019 InitEngine(&second, 1);
1022 if (appData.icsActive) {
1023 appData.clockMode = TRUE; /* changes dynamically in ICS mode */
1024 } else if (appData.noChessProgram) { // [HGM] st: searchTime mode now also is clockMode
1025 appData.clockMode = FALSE;
1026 first.sendTime = second.sendTime = 0;
1030 /* Override some settings from environment variables, for backward
1031 compatibility. Unfortunately it's not feasible to have the env
1032 vars just set defaults, at least in xboard. Ugh.
1034 if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
1039 if (!appData.icsActive) {
1043 /* Check for variants that are supported only in ICS mode,
1044 or not at all. Some that are accepted here nevertheless
1045 have bugs; see comments below.
1047 VariantClass variant = StringToVariant(appData.variant);
1049 case VariantBughouse: /* need four players and two boards */
1050 case VariantKriegspiel: /* need to hide pieces and move details */
1051 /* case VariantFischeRandom: (Fabien: moved below) */
1052 len = snprintf(buf,MSG_SIZ, _("Variant %s supported only in ICS mode"), appData.variant);
1053 if( (len > MSG_SIZ) && appData.debugMode )
1054 fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
1056 DisplayFatalError(buf, 0, 2);
1059 case VariantUnknown:
1060 case VariantLoadable:
1070 len = snprintf(buf, MSG_SIZ, _("Unknown variant name %s"), appData.variant);
1071 if( (len > MSG_SIZ) && appData.debugMode )
1072 fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
1074 DisplayFatalError(buf, 0, 2);
1077 case VariantXiangqi: /* [HGM] repetition rules not implemented */
1078 case VariantFairy: /* [HGM] TestLegality definitely off! */
1079 case VariantGothic: /* [HGM] should work */
1080 case VariantCapablanca: /* [HGM] should work */
1081 case VariantCourier: /* [HGM] initial forced moves not implemented */
1082 case VariantShogi: /* [HGM] could still mate with pawn drop */
1083 case VariantKnightmate: /* [HGM] should work */
1084 case VariantCylinder: /* [HGM] untested */
1085 case VariantFalcon: /* [HGM] untested */
1086 case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)
1087 offboard interposition not understood */
1088 case VariantNormal: /* definitely works! */
1089 case VariantWildCastle: /* pieces not automatically shuffled */
1090 case VariantNoCastle: /* pieces not automatically shuffled */
1091 case VariantFischeRandom: /* [HGM] works and shuffles pieces */
1092 case VariantLosers: /* should work except for win condition,
1093 and doesn't know captures are mandatory */
1094 case VariantSuicide: /* should work except for win condition,
1095 and doesn't know captures are mandatory */
1096 case VariantGiveaway: /* should work except for win condition,
1097 and doesn't know captures are mandatory */
1098 case VariantTwoKings: /* should work */
1099 case VariantAtomic: /* should work except for win condition */
1100 case Variant3Check: /* should work except for win condition */
1101 case VariantShatranj: /* should work except for all win conditions */
1102 case VariantMakruk: /* should work except for daw countdown */
1103 case VariantBerolina: /* might work if TestLegality is off */
1104 case VariantCapaRandom: /* should work */
1105 case VariantJanus: /* should work */
1106 case VariantSuper: /* experimental */
1107 case VariantGreat: /* experimental, requires legality testing to be off */
1108 case VariantSChess: /* S-Chess, should work */
1109 case VariantSpartan: /* should work */
1116 int NextIntegerFromString( char ** str, long * value )
1121 while( *s == ' ' || *s == '\t' ) {
1127 if( *s >= '0' && *s <= '9' ) {
1128 while( *s >= '0' && *s <= '9' ) {
1129 *value = *value * 10 + (*s - '0');
1141 int NextTimeControlFromString( char ** str, long * value )
1144 int result = NextIntegerFromString( str, &temp );
1147 *value = temp * 60; /* Minutes */
1148 if( **str == ':' ) {
1150 result = NextIntegerFromString( str, &temp );
1151 *value += temp; /* Seconds */
1158 int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *incType)
1159 { /* [HGM] routine added to read '+moves/time' for secondary time control. */
1160 int result = -1, type = 0; long temp, temp2;
1162 if(**str != ':') return -1; // old params remain in force!
1164 if(**str == '*') type = *(*str)++, temp = 0; // sandclock TC
1165 if( NextIntegerFromString( str, &temp ) ) return -1;
1166 if(type) { *moves = 0; *tc = temp * 500; *inc = temp * 1000; *incType = '*'; return 0; }
1169 /* time only: incremental or sudden-death time control */
1170 if(**str == '+') { /* increment follows; read it */
1172 if(**str == '!') type = *(*str)++; // Bronstein TC
1173 if(result = NextIntegerFromString( str, &temp2)) return -1;
1174 *inc = temp2 * 1000;
1175 if(**str == '.') { // read fraction of increment
1176 char *start = ++(*str);
1177 if(result = NextIntegerFromString( str, &temp2)) return -1;
1179 while(start++ < *str) temp2 /= 10;
1183 *moves = 0; *tc = temp * 1000; *incType = type;
1187 (*str)++; /* classical time control */
1188 result = NextIntegerFromString( str, &temp2); // NOTE: already converted to seconds by ParseTimeControl()
1199 int GetTimeQuota(int movenr, int lastUsed, char *tcString)
1200 { /* [HGM] get time to add from the multi-session time-control string */
1201 int incType, moves=1; /* kludge to force reading of first session */
1202 long time, increment;
1205 if(!*s) return 0; // empty TC string means we ran out of the last sudden-death version
1206 if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", tcString);
1208 if(moves) NextSessionFromString(&s, &moves, &time, &increment, &incType);
1209 nextSession = s; suddenDeath = moves == 0 && increment == 0;
1210 if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
1211 if(movenr == -1) return time; /* last move before new session */
1212 if(incType == '*') increment = 0; else // for sandclock, time is added while not thinking
1213 if(incType == '!' && lastUsed < increment) increment = lastUsed;
1214 if(!moves) return increment; /* current session is incremental */
1215 if(movenr >= 0) movenr -= moves; /* we already finished this session */
1216 } while(movenr >= -1); /* try again for next session */
1218 return 0; // no new time quota on this move
1222 ParseTimeControl(tc, ti, mps)
1229 char buf[MSG_SIZ], buf2[MSG_SIZ], *mytc = tc;
1232 if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
1233 if(!strchr(tc, '+') && !strchr(tc, '/') && sscanf(tc, "%d:%d", &min, &sec) >= 1)
1234 sprintf(mytc=buf2, "%d", 60*min+sec); // convert 'classical' min:sec tc string to seconds
1238 snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti);
1240 snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti);
1243 snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc);
1245 snprintf(buf, MSG_SIZ, ":%s", mytc);
1247 fullTimeControlString = StrSave(buf); // this should now be in PGN format
1249 if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
1254 /* Parse second time control */
1257 if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
1265 timeControl_2 = tc2 * 1000;
1275 timeControl = tc1 * 1000;
1278 timeIncrement = ti * 1000; /* convert to ms */
1279 movesPerSession = 0;
1282 movesPerSession = mps;
1290 if (appData.debugMode) {
1291 fprintf(debugFP, "%s\n", programVersion);
1294 set_cont_sequence(appData.wrapContSeq);
1295 if (appData.matchGames > 0) {
1296 appData.matchMode = TRUE;
1297 } else if (appData.matchMode) {
1298 appData.matchGames = 1;
1300 if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */
1301 appData.matchGames = appData.sameColorGames;
1302 if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */
1303 if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;
1304 if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;
1307 if (appData.noChessProgram || first.protocolVersion == 1) {
1310 /* kludge: allow timeout for initial "feature" commands */
1312 DisplayMessage("", _("Starting chess program"));
1313 ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
1318 CalculateIndex(int index, int gameNr)
1319 { // [HGM] autoinc: absolute way to determine load index from game number (taking auto-inc and rewind into account)
1321 if(index > 0) return index; // fixed nmber
1322 if(index == 0) return 1;
1323 res = (index == -1 ? gameNr : (gameNr-1)/2 + 1); // autoinc
1324 if(appData.rewindIndex > 0) res = (res-1) % appData.rewindIndex + 1; // rewind
1329 LoadGameOrPosition(int gameNr)
1330 { // [HGM] taken out of MatchEvent and NextMatchGame (to combine it)
1331 if (*appData.loadGameFile != NULLCHAR) {
1332 if (!LoadGameFromFile(appData.loadGameFile,
1333 CalculateIndex(appData.loadGameIndex, gameNr),
1334 appData.loadGameFile, FALSE)) {
1335 DisplayFatalError(_("Bad game file"), 0, 1);
1338 } else if (*appData.loadPositionFile != NULLCHAR) {
1339 if (!LoadPositionFromFile(appData.loadPositionFile,
1340 CalculateIndex(appData.loadPositionIndex, gameNr),
1341 appData.loadPositionFile)) {
1342 DisplayFatalError(_("Bad position file"), 0, 1);
1350 ReserveGame(int gameNr, char resChar)
1352 FILE *tf = fopen(appData.tourneyFile, "r+");
1353 char *p, *q, c, buf[MSG_SIZ];
1354 if(tf == NULL) { nextGame = appData.matchGames + 1; return; } // kludge to terminate match
1355 safeStrCpy(buf, lastMsg, MSG_SIZ);
1356 DisplayMessage(_("Pick new game"), "");
1357 flock(fileno(tf), LOCK_EX); // lock the tourney file while we are messing with it
1358 ParseArgsFromFile(tf);
1359 p = q = appData.results;
1360 if(appData.debugMode) {
1361 char *r = appData.participants;
1362 fprintf(debugFP, "results = '%s'\n", p);
1363 while(*r) fprintf(debugFP, *r >= ' ' ? "%c" : "\\%03o", *r), r++;
1364 fprintf(debugFP, "\n");
1366 while(*q && *q != ' ') q++; // get first un-played game (could be beyond end!)
1368 q = malloc(strlen(p) + 2); // could be arbitrary long, but allow to extend by one!
1369 safeStrCpy(q, p, strlen(p) + 2);
1370 if(gameNr >= 0) q[gameNr] = resChar; // replace '*' with result
1371 if(appData.debugMode) fprintf(debugFP, "pick next game from '%s': %d\n", q, nextGame);
1372 if(nextGame <= appData.matchGames && resChar != ' ') { // already reserve next game, if tourney not yet done
1373 if(q[nextGame] == NULLCHAR) q[nextGame+1] = NULLCHAR; // append one char
1376 fseek(tf, -(strlen(p)+4), SEEK_END);
1378 if(c != '"') // depending on DOS or Unix line endings we can be one off
1379 fseek(tf, -(strlen(p)+2), SEEK_END);
1380 else fseek(tf, -(strlen(p)+3), SEEK_END);
1381 fprintf(tf, "%s\"\n", q); fclose(tf); // update, and flush by closing
1382 DisplayMessage(buf, "");
1383 free(p); appData.results = q;
1384 if(nextGame <= appData.matchGames && resChar != ' ' &&
1385 (gameNr < 0 || nextGame / appData.defaultMatchGames != gameNr / appData.defaultMatchGames)) {
1386 UnloadEngine(&first); // next game belongs to other pairing;
1387 UnloadEngine(&second); // already unload the engines, so TwoMachinesEvent will load new ones.
1392 MatchEvent(int mode)
1393 { // [HGM] moved out of InitBackend3, to make it callable when match starts through menu
1395 if(matchMode) { // already in match mode: switch it off
1397 appData.matchGames = appData.tourneyFile[0] ? nextGame: matchGame; // kludge to let match terminate after next game.
1398 ModeHighlight(); // kludgey way to remove checkmark...
1401 // if(gameMode != BeginningOfGame) {
1402 // DisplayError(_("You can only start a match from the initial position."), 0);
1406 appData.matchGames = appData.defaultMatchGames;
1407 /* Set up machine vs. machine match */
1409 NextTourneyGame(0, &dummy); // sets appData.matchGames if this is tourney, to make sure ReserveGame knows it
1410 if(appData.tourneyFile[0]) {
1412 if(nextGame > appData.matchGames) {
1414 if(strchr(appData.results, '*') == NULL) {
1416 appData.tourneyCycles++;
1417 if(f = WriteTourneyFile(appData.results)) { // make a tourney file with increased number of cycles
1419 NextTourneyGame(-1, &dummy);
1421 if(nextGame <= appData.matchGames) {
1422 DisplayNote(_("You restarted an already completed tourney\nOne more cycle will now be added to it\nGames commence in 10 sec"));
1424 ScheduleDelayedEvent(NextMatchGame, 10000);
1429 snprintf(buf, MSG_SIZ, _("All games in tourney '%s' are already played or playing"), appData.tourneyFile);
1430 DisplayError(buf, 0);
1431 appData.tourneyFile[0] = 0;
1435 if (appData.noChessProgram) { // [HGM] in tourney engines are loaded automatically
1436 DisplayFatalError(_("Can't have a match with no chess programs"),
1441 matchGame = roundNr = 1;
1442 first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches
\r
1447 InitBackEnd3 P((void))
1449 GameMode initialMode;
1453 InitChessProgram(&first, startedFromSetupPosition);
1455 if(!appData.noChessProgram) { /* [HGM] tidy: redo program version to use name from myname feature */
1456 free(programVersion);
1457 programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
1458 sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
1461 if (appData.icsActive) {
1463 /* [DM] Make a console window if needed [HGM] merged ifs */
1469 if (*appData.icsCommPort != NULLCHAR)
1470 len = snprintf(buf, MSG_SIZ, _("Could not open comm port %s"),
1471 appData.icsCommPort);
1473 len = snprintf(buf, MSG_SIZ, _("Could not connect to host %s, port %s"),
1474 appData.icsHost, appData.icsPort);
1476 if( (len > MSG_SIZ) && appData.debugMode )
1477 fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
1479 DisplayFatalError(buf, err, 1);
1484 AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
1486 AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
1487 if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command
1488 ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
1489 } else if (appData.noChessProgram) {
1495 if (*appData.cmailGameName != NULLCHAR) {
1497 OpenLoopback(&cmailPR);
1499 AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
1503 DisplayMessage("", "");
1504 if (StrCaseCmp(appData.initialMode, "") == 0) {
1505 initialMode = BeginningOfGame;
1506 if(!appData.icsActive && appData.noChessProgram) { // [HGM] could be fall-back
1507 gameMode = MachinePlaysBlack; // "Machine Black" might have been implicitly highlighted
1508 ModeHighlight(); // make sure XBoard knows it is highlighted, so it will un-highlight it
1509 gameMode = BeginningOfGame; // in case BeginningOfGame now means "Edit Position"
1512 } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
1513 initialMode = TwoMachinesPlay;
1514 } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
1515 initialMode = AnalyzeFile;
1516 } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
1517 initialMode = AnalyzeMode;
1518 } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
1519 initialMode = MachinePlaysWhite;
1520 } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
1521 initialMode = MachinePlaysBlack;
1522 } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
1523 initialMode = EditGame;
1524 } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
1525 initialMode = EditPosition;
1526 } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
1527 initialMode = Training;
1529 len = snprintf(buf, MSG_SIZ, _("Unknown initialMode %s"), appData.initialMode);
1530 if( (len > MSG_SIZ) && appData.debugMode )
1531 fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
1533 DisplayFatalError(buf, 0, 2);
1537 if (appData.matchMode) {
1538 if(appData.tourneyFile[0]) { // start tourney from command line
1540 if(f = fopen(appData.tourneyFile, "r")) {
1541 ParseArgsFromFile(f); // make sure tourney parmeters re known
1543 } else appData.tourneyFile[0] = NULLCHAR; // for now ignore bad tourney file
1546 } else if (*appData.cmailGameName != NULLCHAR) {
1547 /* Set up cmail mode */
1548 ReloadCmailMsgEvent(TRUE);
1550 /* Set up other modes */
1551 if (initialMode == AnalyzeFile) {
1552 if (*appData.loadGameFile == NULLCHAR) {
1553 DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
1557 if (*appData.loadGameFile != NULLCHAR) {
1558 (void) LoadGameFromFile(appData.loadGameFile,
1559 appData.loadGameIndex,
1560 appData.loadGameFile, TRUE);
1561 } else if (*appData.loadPositionFile != NULLCHAR) {
1562 (void) LoadPositionFromFile(appData.loadPositionFile,
1563 appData.loadPositionIndex,
1564 appData.loadPositionFile);
1565 /* [HGM] try to make self-starting even after FEN load */
1566 /* to allow automatic setup of fairy variants with wtm */
1567 if(initialMode == BeginningOfGame && !blackPlaysFirst) {
1568 gameMode = BeginningOfGame;
1569 setboardSpoiledMachineBlack = 1;
1571 /* [HGM] loadPos: make that every new game uses the setup */
1572 /* from file as long as we do not switch variant */
1573 if(!blackPlaysFirst) {
1574 startedFromPositionFile = TRUE;
1575 CopyBoard(filePosition, boards[0]);
1578 if (initialMode == AnalyzeMode) {
1579 if (appData.noChessProgram) {
1580 DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
1583 if (appData.icsActive) {
1584 DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
1588 } else if (initialMode == AnalyzeFile) {
1589 appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent
1590 ShowThinkingEvent();
1592 AnalysisPeriodicEvent(1);
1593 } else if (initialMode == MachinePlaysWhite) {
1594 if (appData.noChessProgram) {
1595 DisplayFatalError(_("MachineWhite mode requires a chess engine"),
1599 if (appData.icsActive) {
1600 DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
1604 MachineWhiteEvent();
1605 } else if (initialMode == MachinePlaysBlack) {
1606 if (appData.noChessProgram) {
1607 DisplayFatalError(_("MachineBlack mode requires a chess engine"),
1611 if (appData.icsActive) {
1612 DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
1616 MachineBlackEvent();
1617 } else if (initialMode == TwoMachinesPlay) {
1618 if (appData.noChessProgram) {
1619 DisplayFatalError(_("TwoMachines mode requires a chess engine"),
1623 if (appData.icsActive) {
1624 DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
1629 } else if (initialMode == EditGame) {
1631 } else if (initialMode == EditPosition) {
1632 EditPositionEvent();
1633 } else if (initialMode == Training) {
1634 if (*appData.loadGameFile == NULLCHAR) {
1635 DisplayFatalError(_("Training mode requires a game file"), 0, 2);
1644 * Establish will establish a contact to a remote host.port.
1645 * Sets icsPR to a ProcRef for a process (or pseudo-process)
1646 * used to talk to the host.
1647 * Returns 0 if okay, error code if not.
1654 if (*appData.icsCommPort != NULLCHAR) {
1655 /* Talk to the host through a serial comm port */
1656 return OpenCommPort(appData.icsCommPort, &icsPR);
1658 } else if (*appData.gateway != NULLCHAR) {
1659 if (*appData.remoteShell == NULLCHAR) {
1660 /* Use the rcmd protocol to run telnet program on a gateway host */
1661 snprintf(buf, sizeof(buf), "%s %s %s",
1662 appData.telnetProgram, appData.icsHost, appData.icsPort);
1663 return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
1666 /* Use the rsh program to run telnet program on a gateway host */
1667 if (*appData.remoteUser == NULLCHAR) {
1668 snprintf(buf, sizeof(buf), "%s %s %s %s %s", appData.remoteShell,
1669 appData.gateway, appData.telnetProgram,
1670 appData.icsHost, appData.icsPort);
1672 snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s",
1673 appData.remoteShell, appData.gateway,
1674 appData.remoteUser, appData.telnetProgram,
1675 appData.icsHost, appData.icsPort);
1677 return StartChildProcess(buf, "", &icsPR);
1680 } else if (appData.useTelnet) {
1681 return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
1684 /* TCP socket interface differs somewhat between
1685 Unix and NT; handle details in the front end.
1687 return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
1691 void EscapeExpand(char *p, char *q)
1692 { // [HGM] initstring: routine to shape up string arguments
1693 while(*p++ = *q++) if(p[-1] == '\\')
1695 case 'n': p[-1] = '\n'; break;
1696 case 'r': p[-1] = '\r'; break;
1697 case 't': p[-1] = '\t'; break;
1698 case '\\': p[-1] = '\\'; break;
1699 case 0: *p = 0; return;
1700 default: p[-1] = q[-1]; break;
1705 show_bytes(fp, buf, count)
1711 if (*buf < 040 || *(unsigned char *) buf > 0177) {
1712 fprintf(fp, "\\%03o", *buf & 0xff);
1721 /* Returns an errno value */
1723 OutputMaybeTelnet(pr, message, count, outError)
1729 char buf[8192], *p, *q, *buflim;
1730 int left, newcount, outcount;
1732 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
1733 *appData.gateway != NULLCHAR) {
1734 if (appData.debugMode) {
1735 fprintf(debugFP, ">ICS: ");
1736 show_bytes(debugFP, message, count);
1737 fprintf(debugFP, "\n");
1739 return OutputToProcess(pr, message, count, outError);
1742 buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
1749 if (appData.debugMode) {
1750 fprintf(debugFP, ">ICS: ");
1751 show_bytes(debugFP, buf, newcount);
1752 fprintf(debugFP, "\n");
1754 outcount = OutputToProcess(pr, buf, newcount, outError);
1755 if (outcount < newcount) return -1; /* to be sure */
1762 } else if (((unsigned char) *p) == TN_IAC) {
1763 *q++ = (char) TN_IAC;
1770 if (appData.debugMode) {
1771 fprintf(debugFP, ">ICS: ");
1772 show_bytes(debugFP, buf, newcount);
1773 fprintf(debugFP, "\n");
1775 outcount = OutputToProcess(pr, buf, newcount, outError);
1776 if (outcount < newcount) return -1; /* to be sure */
1781 read_from_player(isr, closure, message, count, error)
1788 int outError, outCount;
1789 static int gotEof = 0;
1791 /* Pass data read from player on to ICS */
1794 outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
1795 if (outCount < count) {
1796 DisplayFatalError(_("Error writing to ICS"), outError, 1);
1798 } else if (count < 0) {
1799 RemoveInputSource(isr);
1800 DisplayFatalError(_("Error reading from keyboard"), error, 1);
1801 } else if (gotEof++ > 0) {
1802 RemoveInputSource(isr);
1803 DisplayFatalError(_("Got end of file from keyboard"), 0, 0);
1809 { // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out
1810 if(!connectionAlive) DisplayFatalError("No response from ICS", 0, 1);
1811 connectionAlive = FALSE; // only sticks if no response to 'date' command.
1812 SendToICS("date\n");
1813 if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
1816 /* added routine for printf style output to ics */
1817 void ics_printf(char *format, ...)
1819 char buffer[MSG_SIZ];
1822 va_start(args, format);
1823 vsnprintf(buffer, sizeof(buffer), format, args);
1824 buffer[sizeof(buffer)-1] = '\0';
1833 int count, outCount, outError;
1835 if (icsPR == NULL) return;
1838 outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
1839 if (outCount < count) {
1840 DisplayFatalError(_("Error writing to ICS"), outError, 1);
1844 /* This is used for sending logon scripts to the ICS. Sending
1845 without a delay causes problems when using timestamp on ICC
1846 (at least on my machine). */
1848 SendToICSDelayed(s,msdelay)
1852 int count, outCount, outError;
1854 if (icsPR == NULL) return;
1857 if (appData.debugMode) {
1858 fprintf(debugFP, ">ICS: ");
1859 show_bytes(debugFP, s, count);
1860 fprintf(debugFP, "\n");
1862 outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
1864 if (outCount < count) {
1865 DisplayFatalError(_("Error writing to ICS"), outError, 1);
1870 /* Remove all highlighting escape sequences in s
1871 Also deletes any suffix starting with '('
1874 StripHighlightAndTitle(s)
1877 static char retbuf[MSG_SIZ];
1880 while (*s != NULLCHAR) {
1881 while (*s == '\033') {
1882 while (*s != NULLCHAR && !isalpha(*s)) s++;
1883 if (*s != NULLCHAR) s++;
1885 while (*s != NULLCHAR && *s != '\033') {
1886 if (*s == '(' || *s == '[') {
1897 /* Remove all highlighting escape sequences in s */
1902 static char retbuf[MSG_SIZ];
1905 while (*s != NULLCHAR) {
1906 while (*s == '\033') {
1907 while (*s != NULLCHAR && !isalpha(*s)) s++;
1908 if (*s != NULLCHAR) s++;
1910 while (*s != NULLCHAR && *s != '\033') {
1918 char *variantNames[] = VARIANT_NAMES;
1923 return variantNames[v];
1927 /* Identify a variant from the strings the chess servers use or the
1928 PGN Variant tag names we use. */
1935 VariantClass v = VariantNormal;
1936 int i, found = FALSE;
1942 /* [HGM] skip over optional board-size prefixes */
1943 if( sscanf(e, "%dx%d_", &i, &i) == 2 ||
1944 sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {
1945 while( *e++ != '_');
1948 if(StrCaseStr(e, "misc/")) { // [HGM] on FICS, misc/shogi is not shogi
1952 for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
1953 if (StrCaseStr(e, variantNames[i])) {
1954 v = (VariantClass) i;
1961 if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
1962 || StrCaseStr(e, "wild/fr")
1963 || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {
1964 v = VariantFischeRandom;
1965 } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
1966 (i = 1, p = StrCaseStr(e, "w"))) {
1968 while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
1975 case 0: /* FICS only, actually */
1977 /* Castling legal even if K starts on d-file */
1978 v = VariantWildCastle;
1983 /* Castling illegal even if K & R happen to start in
1984 normal positions. */
1985 v = VariantNoCastle;
1998 /* Castling legal iff K & R start in normal positions */
2004 /* Special wilds for position setup; unclear what to do here */
2005 v = VariantLoadable;
2008 /* Bizarre ICC game */
2009 v = VariantTwoKings;
2012 v = VariantKriegspiel;
2018 v = VariantFischeRandom;
2021 v = VariantCrazyhouse;
2024 v = VariantBughouse;
2030 /* Not quite the same as FICS suicide! */
2031 v = VariantGiveaway;
2037 v = VariantShatranj;
2040 /* Temporary names for future ICC types. The name *will* change in
2041 the next xboard/WinBoard release after ICC defines it. */
2079 v = VariantCapablanca;
2082 v = VariantKnightmate;
2088 v = VariantCylinder;
2094 v = VariantCapaRandom;
2097 v = VariantBerolina;
2109 /* Found "wild" or "w" in the string but no number;
2110 must assume it's normal chess. */
2114 len = snprintf(buf, MSG_SIZ, _("Unknown wild type %d"), wnum);
2115 if( (len > MSG_SIZ) && appData.debugMode )
2116 fprintf(debugFP, "StringToVariant: buffer truncated.\n");
2118 DisplayError(buf, 0);
2124 if (appData.debugMode) {
2125 fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),
2126 e, wnum, VariantName(v));
2131 static int leftover_start = 0, leftover_len = 0;
2132 char star_match[STAR_MATCH_N][MSG_SIZ];
2134 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
2135 advance *index beyond it, and set leftover_start to the new value of
2136 *index; else return FALSE. If pattern contains the character '*', it
2137 matches any sequence of characters not containing '\r', '\n', or the
2138 character following the '*' (if any), and the matched sequence(s) are
2139 copied into star_match.
2142 looking_at(buf, index, pattern)
2147 char *bufp = &buf[*index], *patternp = pattern;
2149 char *matchp = star_match[0];
2152 if (*patternp == NULLCHAR) {
2153 *index = leftover_start = bufp - buf;
2157 if (*bufp == NULLCHAR) return FALSE;
2158 if (*patternp == '*') {
2159 if (*bufp == *(patternp + 1)) {
2161 matchp = star_match[++star_count];
2165 } else if (*bufp == '\n' || *bufp == '\r') {
2167 if (*patternp == NULLCHAR)
2172 *matchp++ = *bufp++;
2176 if (*patternp != *bufp) return FALSE;
2183 SendToPlayer(data, length)
2187 int error, outCount;
2188 outCount = OutputToProcess(NoProc, data, length, &error);
2189 if (outCount < length) {
2190 DisplayFatalError(_("Error writing to display"), error, 1);
2195 PackHolding(packed, holding)
2207 switch (runlength) {
2218 sprintf(q, "%d", runlength);
2230 /* Telnet protocol requests from the front end */
2232 TelnetRequest(ddww, option)
2233 unsigned char ddww, option;
2235 unsigned char msg[3];
2236 int outCount, outError;
2238 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
2240 if (appData.debugMode) {
2241 char buf1[8], buf2[8], *ddwwStr, *optionStr;
2257 snprintf(buf1,sizeof(buf1)/sizeof(buf1[0]), "%d", ddww);
2266 snprintf(buf2,sizeof(buf2)/sizeof(buf2[0]), "%d", option);
2269 fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
2274 outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
2276 DisplayFatalError(_("Error writing to ICS"), outError, 1);
2283 if (!appData.icsActive) return;
2284 TelnetRequest(TN_DO, TN_ECHO);
2290 if (!appData.icsActive) return;
2291 TelnetRequest(TN_DONT, TN_ECHO);
2295 CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
2297 /* put the holdings sent to us by the server on the board holdings area */
2298 int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;
2302 if(gameInfo.holdingsWidth < 2) return;
2303 if(gameInfo.variant != VariantBughouse && board[HOLDINGS_SET])
2304 return; // prevent overwriting by pre-board holdings
2306 if( (int)lowestPiece >= BlackPawn ) {
2309 holdingsStartRow = BOARD_HEIGHT-1;
2312 holdingsColumn = BOARD_WIDTH-1;
2313 countsColumn = BOARD_WIDTH-2;
2314 holdingsStartRow = 0;
2318 for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */
2319 board[i][holdingsColumn] = EmptySquare;
2320 board[i][countsColumn] = (ChessSquare) 0;
2322 while( (p=*holdings++) != NULLCHAR ) {
2323 piece = CharToPiece( ToUpper(p) );
2324 if(piece == EmptySquare) continue;
2325 /*j = (int) piece - (int) WhitePawn;*/
2326 j = PieceToNumber(piece);
2327 if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */
2328 if(j < 0) continue; /* should not happen */
2329 piece = (ChessSquare) ( (int)piece + (int)lowestPiece );
2330 board[holdingsStartRow+j*direction][holdingsColumn] = piece;
2331 board[holdingsStartRow+j*direction][countsColumn]++;
2337 VariantSwitch(Board board, VariantClass newVariant)
2339 int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
2340 static Board oldBoard;
2342 startedFromPositionFile = FALSE;
2343 if(gameInfo.variant == newVariant) return;
2345 /* [HGM] This routine is called each time an assignment is made to
2346 * gameInfo.variant during a game, to make sure the board sizes
2347 * are set to match the new variant. If that means adding or deleting
2348 * holdings, we shift the playing board accordingly
2349 * This kludge is needed because in ICS observe mode, we get boards
2350 * of an ongoing game without knowing the variant, and learn about the
2351 * latter only later. This can be because of the move list we requested,
2352 * in which case the game history is refilled from the beginning anyway,
2353 * but also when receiving holdings of a crazyhouse game. In the latter
2354 * case we want to add those holdings to the already received position.
2358 if (appData.debugMode) {
2359 fprintf(debugFP, "Switch board from %s to %s\n",
2360 VariantName(gameInfo.variant), VariantName(newVariant));
2361 setbuf(debugFP, NULL);
2363 shuffleOpenings = 0; /* [HGM] shuffle */
2364 gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
2368 newWidth = 9; newHeight = 9;
2369 gameInfo.holdingsSize = 7;
2370 case VariantBughouse:
2371 case VariantCrazyhouse:
2372 newHoldingsWidth = 2; break;
2376 newHoldingsWidth = 2;
2377 gameInfo.holdingsSize = 8;
2380 case VariantCapablanca:
2381 case VariantCapaRandom:
2384 newHoldingsWidth = gameInfo.holdingsSize = 0;
2387 if(newWidth != gameInfo.boardWidth ||
2388 newHeight != gameInfo.boardHeight ||
2389 newHoldingsWidth != gameInfo.holdingsWidth ) {
2391 /* shift position to new playing area, if needed */
2392 if(newHoldingsWidth > gameInfo.holdingsWidth) {
2393 for(i=0; i<BOARD_HEIGHT; i++)
2394 for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
2395 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
2397 for(i=0; i<newHeight; i++) {
2398 board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
2399 board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
2401 } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
2402 for(i=0; i<BOARD_HEIGHT; i++)
2403 for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
2404 board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
2407 gameInfo.boardWidth = newWidth;
2408 gameInfo.boardHeight = newHeight;
2409 gameInfo.holdingsWidth = newHoldingsWidth;
2410 gameInfo.variant = newVariant;
2411 InitDrawingSizes(-2, 0);
2412 } else gameInfo.variant = newVariant;
2413 CopyBoard(oldBoard, board); // remember correctly formatted board
2414 InitPosition(FALSE); /* this sets up board[0], but also other stuff */
2415 DrawPosition(TRUE, currentMove ? boards[currentMove] : oldBoard);
2418 static int loggedOn = FALSE;
2420 /*-- Game start info cache: --*/
2422 char gs_kind[MSG_SIZ];
2423 static char player1Name[128] = "";
2424 static char player2Name[128] = "";
2425 static char cont_seq[] = "\n\\ ";
2426 static int player1Rating = -1;
2427 static int player2Rating = -1;
2428 /*----------------------------*/
2430 ColorClass curColor = ColorNormal;
2431 int suppressKibitz = 0;
2434 Boolean soughtPending = FALSE;
2435 Boolean seekGraphUp;
2436 #define MAX_SEEK_ADS 200
2438 char *seekAdList[MAX_SEEK_ADS];
2439 int ratingList[MAX_SEEK_ADS], xList[MAX_SEEK_ADS], yList[MAX_SEEK_ADS], seekNrList[MAX_SEEK_ADS], zList[MAX_SEEK_ADS];
2440 float tcList[MAX_SEEK_ADS];
2441 char colorList[MAX_SEEK_ADS];
2442 int nrOfSeekAds = 0;
2443 int minRating = 1010, maxRating = 2800;
2444 int hMargin = 10, vMargin = 20, h, w;
2445 extern int squareSize, lineGap;
2450 int x, y, color = 0, r = ratingList[i]; float tc = tcList[i];
2451 xList[i] = yList[i] = -100; // outside graph, so cannot be clicked
2452 if(r < minRating+100 && r >=0 ) r = minRating+100;
2453 if(r > maxRating) r = maxRating;
2454 if(tc < 1.) tc = 1.;
2455 if(tc > 95.) tc = 95.;
2456 x = (w-hMargin-squareSize/8-7)* log(tc)/log(95.) + hMargin;
2457 y = ((double)r - minRating)/(maxRating - minRating)
2458 * (h-vMargin-squareSize/8-1) + vMargin;
2459 if(ratingList[i] < 0) y = vMargin + squareSize/4;
2460 if(strstr(seekAdList[i], " u ")) color = 1;
2461 if(!strstr(seekAdList[i], "lightning") && // for now all wilds same color
2462 !strstr(seekAdList[i], "bullet") &&
2463 !strstr(seekAdList[i], "blitz") &&
2464 !strstr(seekAdList[i], "standard") ) color = 2;
2465 if(strstr(seekAdList[i], "(C) ")) color |= SQUARE; // plot computer seeks as squares
2466 DrawSeekDot(xList[i]=x+3*(color&~SQUARE), yList[i]=h-1-y, colorList[i]=color);
2470 AddAd(char *handle, char *rating, int base, int inc, char rated, char *type, int nr, Boolean plot)
2472 char buf[MSG_SIZ], *ext = "";
2473 VariantClass v = StringToVariant(type);
2474 if(strstr(type, "wild")) {
2475 ext = type + 4; // append wild number
2476 if(v == VariantFischeRandom) type = "chess960"; else
2477 if(v == VariantLoadable) type = "setup"; else
2478 type = VariantName(v);
2480 snprintf(buf, MSG_SIZ, "%s (%s) %d %d %c %s%s", handle, rating, base, inc, rated, type, ext);
2481 if(nrOfSeekAds < MAX_SEEK_ADS-1) {
2482 if(seekAdList[nrOfSeekAds]) free(seekAdList[nrOfSeekAds]);
2483 ratingList[nrOfSeekAds] = -1; // for if seeker has no rating
2484 sscanf(rating, "%d", &ratingList[nrOfSeekAds]);
2485 tcList[nrOfSeekAds] = base + (2./3.)*inc;
2486 seekNrList[nrOfSeekAds] = nr;
2487 zList[nrOfSeekAds] = 0;
2488 seekAdList[nrOfSeekAds++] = StrSave(buf);
2489 if(plot) PlotSeekAd(nrOfSeekAds-1);
2496 int x = xList[i], y = yList[i], d=squareSize/4, k;
2497 DrawSeekBackground(x-squareSize/8, y-squareSize/8, x+squareSize/8+1, y+squareSize/8+1);
2498 if(x < hMargin+d) DrawSeekAxis(hMargin, y-squareSize/8, hMargin, y+squareSize/8+1);
2499 // now replot every dot that overlapped
2500 for(k=0; k<nrOfSeekAds; k++) if(k != i) {
2501 int xx = xList[k], yy = yList[k];
2502 if(xx <= x+d && xx > x-d && yy <= y+d && yy > y-d)
2503 DrawSeekDot(xx, yy, colorList[k]);
2508 RemoveSeekAd(int nr)
2511 for(i=0; i<nrOfSeekAds; i++) if(seekNrList[i] == nr) {
2513 if(seekAdList[i]) free(seekAdList[i]);
2514 seekAdList[i] = seekAdList[--nrOfSeekAds];
2515 seekNrList[i] = seekNrList[nrOfSeekAds];
2516 ratingList[i] = ratingList[nrOfSeekAds];
2517 colorList[i] = colorList[nrOfSeekAds];
2518 tcList[i] = tcList[nrOfSeekAds];
2519 xList[i] = xList[nrOfSeekAds];
2520 yList[i] = yList[nrOfSeekAds];
2521 zList[i] = zList[nrOfSeekAds];
2522 seekAdList[nrOfSeekAds] = NULL;
2528 MatchSoughtLine(char *line)
2530 char handle[MSG_SIZ], rating[MSG_SIZ], type[MSG_SIZ];
2531 int nr, base, inc, u=0; char dummy;
2533 if(sscanf(line, "%d %s %s %d %d rated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
2534 sscanf(line, "%d %s %s %s %d %d rated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7 ||
2536 (sscanf(line, "%d %s %s %d %d unrated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
2537 sscanf(line, "%d %s %s %s %d %d unrated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7) ) {
2538 // match: compact and save the line
2539 AddAd(handle, rating, base, inc, u ? 'u' : 'r', type, nr, FALSE);
2549 if(!seekGraphUp) return FALSE;
2550 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
2551 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
2553 DrawSeekBackground(0, 0, w, h);
2554 DrawSeekAxis(hMargin, h-1-vMargin, w-5, h-1-vMargin);
2555 DrawSeekAxis(hMargin, h-1-vMargin, hMargin, 5);
2556 for(i=0; i<4000; i+= 100) if(i>=minRating && i<maxRating) {
2557 int yy =((double)i - minRating)/(maxRating - minRating)*(h-vMargin-squareSize/8-1) + vMargin;
2559 DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
2562 snprintf(buf, MSG_SIZ, "%d", i);
2563 DrawSeekText(buf, hMargin+squareSize/8+7, yy);
2566 DrawSeekText("unrated", hMargin+squareSize/8+7, h-1-vMargin-squareSize/4);
2567 for(i=1; i<100; i+=(i<10?1:5)) {
2568 int xx = (w-hMargin-squareSize/8-7)* log((double)i)/log(95.) + hMargin;
2569 DrawSeekAxis(xx, h-1-vMargin, xx, h-6-vMargin-3*(i%10==0)); // TC ticks
2570 if(i<=5 || (i>40 ? i%20 : i%10) == 0) {
2572 snprintf(buf, MSG_SIZ, "%d", i);
2573 DrawSeekText(buf, xx-2-3*(i>9), h-1-vMargin/2);
2576 for(i=0; i<nrOfSeekAds; i++) PlotSeekAd(i);
2580 int SeekGraphClick(ClickType click, int x, int y, int moving)
2582 static int lastDown = 0, displayed = 0, lastSecond;
2583 if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
2584 if(click == Release || moving) return FALSE;
2586 soughtPending = TRUE;
2587 SendToICS(ics_prefix);
2588 SendToICS("sought\n"); // should this be "sought all"?
2589 } else { // issue challenge based on clicked ad
2590 int dist = 10000; int i, closest = 0, second = 0;
2591 for(i=0; i<nrOfSeekAds; i++) {
2592 int d = (x-xList[i])*(x-xList[i]) + (y-yList[i])*(y-yList[i]) + zList[i];
2593 if(d < dist) { dist = d; closest = i; }
2594 second += (d - zList[i] < 120); // count in-range ads
2595 if(click == Press && moving != 1 && zList[i]>0) zList[i] *= 0.8; // age priority
2599 second = (second > 1);
2600 if(displayed != closest || second != lastSecond) {
2601 DisplayMessage(second ? "!" : "", seekAdList[closest]);
2602 lastSecond = second; displayed = closest;
2604 if(click == Press) {
2605 if(moving == 2) zList[closest] = 100; // right-click; push to back on press
2608 } // on press 'hit', only show info
2609 if(moving == 2) return TRUE; // ignore right up-clicks on dot
2610 snprintf(buf, MSG_SIZ, "play %d\n", seekNrList[closest]);
2611 SendToICS(ics_prefix);
2613 return TRUE; // let incoming board of started game pop down the graph
2614 } else if(click == Release) { // release 'miss' is ignored
2615 zList[lastDown] = 100; // make future selection of the rejected ad more difficult
2616 if(moving == 2) { // right up-click
2617 nrOfSeekAds = 0; // refresh graph
2618 soughtPending = TRUE;
2619 SendToICS(ics_prefix);
2620 SendToICS("sought\n"); // should this be "sought all"?
2623 } else if(moving) { if(displayed >= 0) DisplayMessage("", ""); displayed = -1; return TRUE; }
2624 // press miss or release hit 'pop down' seek graph
2625 seekGraphUp = FALSE;
2626 DrawPosition(TRUE, NULL);
2632 read_from_ics(isr, closure, data, count, error)
2639 #define BUF_SIZE (16*1024) /* overflowed at 8K with "inchannel 1" on FICS? */
2640 #define STARTED_NONE 0
2641 #define STARTED_MOVES 1
2642 #define STARTED_BOARD 2
2643 #define STARTED_OBSERVE 3
2644 #define STARTED_HOLDINGS 4
2645 #define STARTED_CHATTER 5
2646 #define STARTED_COMMENT 6
2647 #define STARTED_MOVES_NOHIDE 7
2649 static int started = STARTED_NONE;
2650 static char parse[20000];
2651 static int parse_pos = 0;
2652 static char buf[BUF_SIZE + 1];
2653 static int firstTime = TRUE, intfSet = FALSE;
2654 static ColorClass prevColor = ColorNormal;
2655 static int savingComment = FALSE;
2656 static int cmatch = 0; // continuation sequence match
2663 int backup; /* [DM] For zippy color lines */
2665 char talker[MSG_SIZ]; // [HGM] chat
2668 connectionAlive = TRUE; // [HGM] alive: I think, therefore I am...
2670 if (appData.debugMode) {
2672 fprintf(debugFP, "<ICS: ");
2673 show_bytes(debugFP, data, count);
2674 fprintf(debugFP, "\n");
2678 if (appData.debugMode) { int f = forwardMostMove;
2679 fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,
2680 boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
2681 boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
2684 /* If last read ended with a partial line that we couldn't parse,
2685 prepend it to the new read and try again. */
2686 if (leftover_len > 0) {
2687 for (i=0; i<leftover_len; i++)
2688 buf[i] = buf[leftover_start + i];
2691 /* copy new characters into the buffer */
2692 bp = buf + leftover_len;
2693 buf_len=leftover_len;
2694 for (i=0; i<count; i++)
2697 if (data[i] == '\r')
2700 // join lines split by ICS?
2701 if (!appData.noJoin)
2704 Joining just consists of finding matches against the
2705 continuation sequence, and discarding that sequence
2706 if found instead of copying it. So, until a match
2707 fails, there's nothing to do since it might be the
2708 complete sequence, and thus, something we don't want
2711 if (data[i] == cont_seq[cmatch])
2714 if (cmatch == strlen(cont_seq))
2716 cmatch = 0; // complete match. just reset the counter
2719 it's possible for the ICS to not include the space
2720 at the end of the last word, making our [correct]
2721 join operation fuse two separate words. the server
2722 does this when the space occurs at the width setting.
2724 if (!buf_len || buf[buf_len-1] != ' ')
2735 match failed, so we have to copy what matched before
2736 falling through and copying this character. In reality,
2737 this will only ever be just the newline character, but
2738 it doesn't hurt to be precise.
2740 strncpy(bp, cont_seq, cmatch);
2752 buf[buf_len] = NULLCHAR;
2753 // next_out = leftover_len; // [HGM] should we set this to 0, and not print it in advance?
2758 while (i < buf_len) {
2759 /* Deal with part of the TELNET option negotiation
2760 protocol. We refuse to do anything beyond the
2761 defaults, except that we allow the WILL ECHO option,
2762 which ICS uses to turn off password echoing when we are
2763 directly connected to it. We reject this option
2764 if localLineEditing mode is on (always on in xboard)
2765 and we are talking to port 23, which might be a real
2766 telnet server that will try to keep WILL ECHO on permanently.
2768 if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
2769 static int remoteEchoOption = FALSE; /* telnet ECHO option */
2770 unsigned char option;
2772 switch ((unsigned char) buf[++i]) {
2774 if (appData.debugMode)
2775 fprintf(debugFP, "\n<WILL ");
2776 switch (option = (unsigned char) buf[++i]) {
2778 if (appData.debugMode)
2779 fprintf(debugFP, "ECHO ");
2780 /* Reply only if this is a change, according
2781 to the protocol rules. */
2782 if (remoteEchoOption) break;
2783 if (appData.localLineEditing &&
2784 atoi(appData.icsPort) == TN_PORT) {
2785 TelnetRequest(TN_DONT, TN_ECHO);
2788 TelnetRequest(TN_DO, TN_ECHO);
2789 remoteEchoOption = TRUE;
2793 if (appData.debugMode)
2794 fprintf(debugFP, "%d ", option);
2795 /* Whatever this is, we don't want it. */
2796 TelnetRequest(TN_DONT, option);
2801 if (appData.debugMode)
2802 fprintf(debugFP, "\n<WONT ");
2803 switch (option = (unsigned char) buf[++i]) {
2805 if (appData.debugMode)
2806 fprintf(debugFP, "ECHO ");
2807 /* Reply only if this is a change, according
2808 to the protocol rules. */
2809 if (!remoteEchoOption) break;
2811 TelnetRequest(TN_DONT, TN_ECHO);
2812 remoteEchoOption = FALSE;
2815 if (appData.debugMode)
2816 fprintf(debugFP, "%d ", (unsigned char) option);
2817 /* Whatever this is, it must already be turned
2818 off, because we never agree to turn on
2819 anything non-default, so according to the
2820 protocol rules, we don't reply. */
2825 if (appData.debugMode)
2826 fprintf(debugFP, "\n<DO ");
2827 switch (option = (unsigned char) buf[++i]) {
2829 /* Whatever this is, we refuse to do it. */
2830 if (appData.debugMode)
2831 fprintf(debugFP, "%d ", option);
2832 TelnetRequest(TN_WONT, option);
2837 if (appData.debugMode)
2838 fprintf(debugFP, "\n<DONT ");
2839 switch (option = (unsigned char) buf[++i]) {
2841 if (appData.debugMode)
2842 fprintf(debugFP, "%d ", option);
2843 /* Whatever this is, we are already not doing
2844 it, because we never agree to do anything
2845 non-default, so according to the protocol
2846 rules, we don't reply. */
2851 if (appData.debugMode)
2852 fprintf(debugFP, "\n<IAC ");
2853 /* Doubled IAC; pass it through */
2857 if (appData.debugMode)
2858 fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
2859 /* Drop all other telnet commands on the floor */
2862 if (oldi > next_out)
2863 SendToPlayer(&buf[next_out], oldi - next_out);
2869 /* OK, this at least will *usually* work */
2870 if (!loggedOn && looking_at(buf, &i, "ics%")) {
2874 if (loggedOn && !intfSet) {
2875 if (ics_type == ICS_ICC) {
2876 snprintf(str, MSG_SIZ,
2877 "/set-quietly interface %s\n/set-quietly style 12\n",
2879 if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
2880 strcat(str, "/set-2 51 1\n/set seek 1\n");
2881 } else if (ics_type == ICS_CHESSNET) {
2882 snprintf(str, MSG_SIZ, "/style 12\n");
2884 safeStrCpy(str, "alias $ @\n$set interface ", sizeof(str)/sizeof(str[0]));
2885 strcat(str, programVersion);
2886 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
2887 if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
2888 strcat(str, "$iset seekremove 1\n$set seek 1\n");
2890 strcat(str, "$iset nohighlight 1\n");
2892 strcat(str, "$iset lock 1\n$style 12\n");
2895 NotifyFrontendLogin();
2899 if (started == STARTED_COMMENT) {
2900 /* Accumulate characters in comment */
2901 parse[parse_pos++] = buf[i];
2902 if (buf[i] == '\n') {
2903 parse[parse_pos] = NULLCHAR;
2904 if(chattingPartner>=0) {
2906 snprintf(mess, MSG_SIZ, "%s%s", talker, parse);
2907 OutputChatMessage(chattingPartner, mess);
2908 chattingPartner = -1;
2909 next_out = i+1; // [HGM] suppress printing in ICS window
2911 if(!suppressKibitz) // [HGM] kibitz
2912 AppendComment(forwardMostMove, StripHighlight(parse), TRUE);
2913 else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
2914 int nrDigit = 0, nrAlph = 0, j;
2915 if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
2916 { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
2917 parse[parse_pos] = NULLCHAR;
2918 // try to be smart: if it does not look like search info, it should go to
2919 // ICS interaction window after all, not to engine-output window.
2920 for(j=0; j<parse_pos; j++) { // count letters and digits
2921 nrDigit += (parse[j] >= '0' && parse[j] <= '9');
2922 nrAlph += (parse[j] >= 'a' && parse[j] <= 'z');
2923 nrAlph += (parse[j] >= 'A' && parse[j] <= 'Z');
2925 if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
2926 int depth=0; float score;
2927 if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {
2928 // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph
2929 pvInfoList[forwardMostMove-1].depth = depth;
2930 pvInfoList[forwardMostMove-1].score = 100*score;
2932 OutputKibitz(suppressKibitz, parse);
2935 snprintf(tmp, MSG_SIZ, _("your opponent kibitzes: %s"), parse);
2936 SendToPlayer(tmp, strlen(tmp));
2938 next_out = i+1; // [HGM] suppress printing in ICS window
2940 started = STARTED_NONE;
2942 /* Don't match patterns against characters in comment */
2947 if (started == STARTED_CHATTER) {
2948 if (buf[i] != '\n') {
2949 /* Don't match patterns against characters in chatter */
2953 started = STARTED_NONE;
2954 if(suppressKibitz) next_out = i+1;
2957 /* Kludge to deal with rcmd protocol */
2958 if (firstTime && looking_at(buf, &i, "\001*")) {
2959 DisplayFatalError(&buf[1], 0, 1);
2965 if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
2968 if (appData.debugMode)
2969 fprintf(debugFP, "ics_type %d\n", ics_type);
2972 if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
2973 ics_type = ICS_FICS;
2975 if (appData.debugMode)
2976 fprintf(debugFP, "ics_type %d\n", ics_type);
2979 if (!loggedOn && looking_at(buf, &i, "chess.net")) {
2980 ics_type = ICS_CHESSNET;
2982 if (appData.debugMode)
2983 fprintf(debugFP, "ics_type %d\n", ics_type);
2988 (looking_at(buf, &i, "\"*\" is *a registered name") ||
2989 looking_at(buf, &i, "Logging you in as \"*\"") ||
2990 looking_at(buf, &i, "will be \"*\""))) {
2991 safeStrCpy(ics_handle, star_match[0], sizeof(ics_handle)/sizeof(ics_handle[0]));
2995 if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
2997 snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost);
2998 DisplayIcsInteractionTitle(buf);
2999 have_set_title = TRUE;
3002 /* skip finger notes */
3003 if (started == STARTED_NONE &&
3004 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
3005 (buf[i] == '1' && buf[i+1] == '0')) &&
3006 buf[i+2] == ':' && buf[i+3] == ' ') {
3007 started = STARTED_CHATTER;
3013 // [HGM] seekgraph: recognize sought lines and end-of-sought message
3014 if(appData.seekGraph) {
3015 if(soughtPending && MatchSoughtLine(buf+i)) {
3016 i = strstr(buf+i, "rated") - buf;
3017 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3018 next_out = leftover_start = i;
3019 started = STARTED_CHATTER;
3020 suppressKibitz = TRUE;
3023 if((gameMode == IcsIdle || gameMode == BeginningOfGame)
3024 && looking_at(buf, &i, "* ads displayed")) {
3025 soughtPending = FALSE;
3030 if(appData.autoRefresh) {
3031 if(looking_at(buf, &i, "* (*) seeking * * * * *\"play *\" to respond)\n")) {
3032 int s = (ics_type == ICS_ICC); // ICC format differs
3034 AddAd(star_match[0], star_match[1], atoi(star_match[2+s]), atoi(star_match[3+s]),
3035 star_match[4+s][0], star_match[5-3*s], atoi(star_match[7]), TRUE);
3036 looking_at(buf, &i, "*% "); // eat prompt
3037 if(oldi > 0 && buf[oldi-1] == '\n') oldi--; // suppress preceding LF, if any
3038 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3039 next_out = i; // suppress
3042 if(looking_at(buf, &i, "\nAds removed: *\n") || looking_at(buf, &i, "\031(51 * *\031)")) {
3043 char *p = star_match[0];
3045 if(seekGraphUp) RemoveSeekAd(atoi(p));
3046 while(*p && *p++ != ' '); // next
3048 looking_at(buf, &i, "*% "); // eat prompt
3049 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3056 /* skip formula vars */
3057 if (started == STARTED_NONE &&
3058 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
3059 started = STARTED_CHATTER;
3064 // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
3065 if (appData.autoKibitz && started == STARTED_NONE &&
3066 !appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
3067 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
3068 if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
3069 (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
3070 StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent
3071 suppressKibitz = TRUE;
3072 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3074 if((StrStr(star_match[0], gameInfo.white) == star_match[0]
3075 && (gameMode == IcsPlayingWhite)) ||
3076 (StrStr(star_match[0], gameInfo.black) == star_match[0]
3077 && (gameMode == IcsPlayingBlack)) ) // opponent kibitz
3078 started = STARTED_CHATTER; // own kibitz we simply discard
3080 started = STARTED_COMMENT; // make sure it will be collected in parse[]
3081 parse_pos = 0; parse[0] = NULLCHAR;
3082 savingComment = TRUE;
3083 suppressKibitz = gameMode != IcsObserving ? 2 :
3084 (StrStr(star_match[0], gameInfo.white) == NULL) + 1;
3088 if((looking_at(buf, &i, "\nkibitzed to *\n") || looking_at(buf, &i, "kibitzed to *\n") ||
3089 looking_at(buf, &i, "\n(kibitzed to *\n") || looking_at(buf, &i, "(kibitzed to *\n"))
3090 && atoi(star_match[0])) {
3091 // suppress the acknowledgements of our own autoKibitz
3093 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3094 if(p = strchr(star_match[0], ' ')) p[1] = NULLCHAR; // clip off "players)" on FICS
3095 SendToPlayer(star_match[0], strlen(star_match[0]));
3096 if(looking_at(buf, &i, "*% ")) // eat prompt
3097 suppressKibitz = FALSE;
3101 } // [HGM] kibitz: end of patch
3103 // [HGM] chat: intercept tells by users for which we have an open chat window
3105 if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
3106 looking_at(buf, &i, "* whispers:") ||
3107 looking_at(buf, &i, "* kibitzes:") ||
3108 looking_at(buf, &i, "* shouts:") ||
3109 looking_at(buf, &i, "* c-shouts:") ||
3110 looking_at(buf, &i, "--> * ") ||
3111 looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) ||
3112 looking_at(buf, &i, "*(*)(*):") && (sscanf(star_match[2], "%d", &channel),1) ||
3113 looking_at(buf, &i, "*(*)(*)(*):") && (sscanf(star_match[3], "%d", &channel),1) ||
3114 looking_at(buf, &i, "*(*)(*)(*)(*):") && sscanf(star_match[4], "%d", &channel) == 1 )) {
3116 sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
3117 chattingPartner = -1;
3119 if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
3120 for(p=0; p<MAX_CHAT; p++) {
3121 if(chatPartner[p][0] >= '0' && chatPartner[p][0] <= '9' && channel == atoi(chatPartner[p])) {
3122 talker[0] = '['; strcat(talker, "] ");
3123 Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE);
3124 chattingPartner = p; break;
3127 if(buf[i-3] == 'e') // kibitz; look if there is a KIBITZ chatbox
3128 for(p=0; p<MAX_CHAT; p++) {
3129 if(!strcmp("kibitzes", chatPartner[p])) {
3130 talker[0] = '['; strcat(talker, "] ");
3131 chattingPartner = p; break;
3134 if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
3135 for(p=0; p<MAX_CHAT; p++) {
3136 if(!strcmp("whispers", chatPartner[p])) {
3137 talker[0] = '['; strcat(talker, "] ");
3138 chattingPartner = p; break;
3141 if(buf[i-3] == 't' || buf[oldi+2] == '>') {// shout, c-shout or it; look if there is a 'shouts' chatbox
3142 if(buf[i-8] == '-' && buf[i-3] == 't')
3143 for(p=0; p<MAX_CHAT; p++) { // c-shout; check if dedicatesd c-shout box exists
3144 if(!strcmp("c-shouts", chatPartner[p])) {
3145 talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE);
3146 chattingPartner = p; break;
3149 if(chattingPartner < 0)
3150 for(p=0; p<MAX_CHAT; p++) {
3151 if(!strcmp("shouts", chatPartner[p])) {
3152 if(buf[oldi+2] == '>') { talker[0] = '<'; strcat(talker, "> "); Colorize(ColorShout, FALSE); }
3153 else if(buf[i-8] == '-') { talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE); }
3154 else { talker[0] = '['; strcat(talker, "] "); Colorize(ColorShout, FALSE); }
3155 chattingPartner = p; break;
3159 if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
3160 for(p=0; p<MAX_CHAT; p++) if(!StrCaseCmp(talker+1, chatPartner[p])) {
3161 talker[0] = 0; Colorize(ColorTell, FALSE);
3162 chattingPartner = p; break;
3164 if(chattingPartner<0) i = oldi; else {
3165 Colorize(curColor, TRUE); // undo the bogus colorations we just made to trigger the souds
3166 if(oldi > 0 && buf[oldi-1] == '\n') oldi--;
3167 if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
3168 started = STARTED_COMMENT;
3169 parse_pos = 0; parse[0] = NULLCHAR;
3170 savingComment = 3 + chattingPartner; // counts as TRUE
3171 suppressKibitz = TRUE;
3174 } // [HGM] chat: end of patch
3177 if (appData.zippyTalk || appData.zippyPlay) {
3178 /* [DM] Backup address for color zippy lines */
3180 if (loggedOn == TRUE)
3181 if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
3182 (appData.zippyPlay && ZippyMatch(buf, &backup)));
3184 } // [DM] 'else { ' deleted
3186 /* Regular tells and says */
3187 (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
3188 looking_at(buf, &i, "* (your partner) tells you: ") ||
3189 looking_at(buf, &i, "* says: ") ||
3190 /* Don't color "message" or "messages" output */
3191 (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
3192 looking_at(buf, &i, "*. * at *:*: ") ||
3193 looking_at(buf, &i, "--* (*:*): ") ||
3194 /* Message notifications (same color as tells) */
3195 looking_at(buf, &i, "* has left a message ") ||
3196 looking_at(buf, &i, "* just sent you a message:\n") ||
3197 /* Whispers and kibitzes */
3198 (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
3199 looking_at(buf, &i, "* kibitzes: ") ||
3201 (tkind = 3, looking_at(buf, &i, "*(*: "))) {
3203 if (tkind == 1 && strchr(star_match[0], ':')) {
3204 /* Avoid "tells you:" spoofs in channels */
3207 if (star_match[0][0] == NULLCHAR ||
3208 strchr(star_match[0], ' ') ||
3209 (tkind == 3 && strchr(star_match[1], ' '))) {
3210 /* Reject bogus matches */
3213 if (appData.colorize) {
3214 if (oldi > next_out) {
3215 SendToPlayer(&buf[next_out], oldi - next_out);
3220 Colorize(ColorTell, FALSE);
3221 curColor = ColorTell;
3224 Colorize(ColorKibitz, FALSE);
3225 curColor = ColorKibitz;
3228 p = strrchr(star_match[1], '(');
3235 Colorize(ColorChannel1, FALSE);
3236 curColor = ColorChannel1;
3238 Colorize(ColorChannel, FALSE);
3239 curColor = ColorChannel;
3243 curColor = ColorNormal;
3247 if (started == STARTED_NONE && appData.autoComment &&
3248 (gameMode == IcsObserving ||
3249 gameMode == IcsPlayingWhite ||
3250 gameMode == IcsPlayingBlack)) {
3251 parse_pos = i - oldi;
3252 memcpy(parse, &buf[oldi], parse_pos);
3253 parse[parse_pos] = NULLCHAR;
3254 started = STARTED_COMMENT;
3255 savingComment = TRUE;
3257 started = STARTED_CHATTER;
3258 savingComment = FALSE;
3265 if (looking_at(buf, &i, "* s-shouts: ") ||
3266 looking_at(buf, &i, "* c-shouts: ")) {
3267 if (appData.colorize) {
3268 if (oldi > next_out) {
3269 SendToPlayer(&buf[next_out], oldi - next_out);
3272 Colorize(ColorSShout, FALSE);
3273 curColor = ColorSShout;
3276 started = STARTED_CHATTER;
3280 if (looking_at(buf, &i, "--->")) {
3285 if (looking_at(buf, &i, "* shouts: ") ||
3286 looking_at(buf, &i, "--> ")) {
3287 if (appData.colorize) {
3288 if (oldi > next_out) {
3289 SendToPlayer(&buf[next_out], oldi - next_out);
3292 Colorize(ColorShout, FALSE);
3293 curColor = ColorShout;
3296 started = STARTED_CHATTER;
3300 if (looking_at( buf, &i, "Challenge:")) {
3301 if (appData.colorize) {
3302 if (oldi > next_out) {
3303 SendToPlayer(&buf[next_out], oldi - next_out);
3306 Colorize(ColorChallenge, FALSE);
3307 curColor = ColorChallenge;
3313 if (looking_at(buf, &i, "* offers you") ||
3314 looking_at(buf, &i, "* offers to be") ||
3315 looking_at(buf, &i, "* would like to") ||
3316 looking_at(buf, &i, "* requests to") ||
3317 looking_at(buf, &i, "Your opponent offers") ||
3318 looking_at(buf, &i, "Your opponent requests")) {
3320 if (appData.colorize) {
3321 if (oldi > next_out) {
3322 SendToPlayer(&buf[next_out], oldi - next_out);
3325 Colorize(ColorRequest, FALSE);
3326 curColor = ColorRequest;
3331 if (looking_at(buf, &i, "* (*) seeking")) {
3332 if (appData.colorize) {
3333 if (oldi > next_out) {
3334 SendToPlayer(&buf[next_out], oldi - next_out);
3337 Colorize(ColorSeek, FALSE);
3338 curColor = ColorSeek;
3343 if(i < backup) { i = backup; continue; } // [HGM] for if ZippyControl matches, but the colorie code doesn't
3345 if (looking_at(buf, &i, "\\ ")) {
3346 if (prevColor != ColorNormal) {
3347 if (oldi > next_out) {
3348 SendToPlayer(&buf[next_out], oldi - next_out);
3351 Colorize(prevColor, TRUE);
3352 curColor = prevColor;
3354 if (savingComment) {
3355 parse_pos = i - oldi;
3356 memcpy(parse, &buf[oldi], parse_pos);
3357 parse[parse_pos] = NULLCHAR;
3358 started = STARTED_COMMENT;
3359 if(savingComment >= 3) // [HGM] chat: continuation of line for chat box
3360 chattingPartner = savingComment - 3; // kludge to remember the box
3362 started = STARTED_CHATTER;
3367 if (looking_at(buf, &i, "Black Strength :") ||
3368 looking_at(buf, &i, "<<< style 10 board >>>") ||
3369 looking_at(buf, &i, "<10>") ||
3370 looking_at(buf, &i, "#@#")) {
3371 /* Wrong board style */
3373 SendToICS(ics_prefix);
3374 SendToICS("set style 12\n");
3375 SendToICS(ics_prefix);
3376 SendToICS("refresh\n");
3380 if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
3382 have_sent_ICS_logon = 1;
3386 if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
3387 (looking_at(buf, &i, "\n<12> ") ||
3388 looking_at(buf, &i, "<12> "))) {
3390 if (oldi > next_out) {
3391 SendToPlayer(&buf[next_out], oldi - next_out);
3394 started = STARTED_BOARD;
3399 if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
3400 looking_at(buf, &i, "<b1> ")) {
3401 if (oldi > next_out) {
3402 SendToPlayer(&buf[next_out], oldi - next_out);
3405 started = STARTED_HOLDINGS;
3410 if (looking_at(buf, &i, "* *vs. * *--- *")) {
3412 /* Header for a move list -- first line */
3414 switch (ics_getting_history) {