-/*
- * backend.c -- Common back end for X and Windows NT versions of
- * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $
- *
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
- *
- * The following terms apply to Digital Equipment Corporation's copyright
- * interest in XBoard:
- * ------------------------------------------------------------------------
- * All Rights Reserved
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and that
- * both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of Digital not be
- * used in advertising or publicity pertaining to distribution of the
- * software without specific, written prior permission.
- *
- * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
- * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
- * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
- * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- * ------------------------------------------------------------------------
- *
- * The following terms apply to the enhanced version of XBoard distributed
- * by the Free Software Foundation:
- * ------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * ------------------------------------------------------------------------
- *
- * See the file ChangeLog for a revision history. */
-
-/* [AS] Also useful here for debugging */
-#ifdef WIN32
-#include <windows.h>
-
-#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
-
-#else
-
-#define DoSleep( n )
-
-#endif
-
-#include "config.h"
-
-#include <assert.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <math.h>
-
-#if STDC_HEADERS
-# include <stdlib.h>
-# include <string.h>
-#else /* not STDC_HEADERS */
-# if HAVE_STRING_H
-# include <string.h>
-# else /* not HAVE_STRING_H */
-# include <strings.h>
-# endif /* not HAVE_STRING_H */
-#endif /* not STDC_HEADERS */
-
-#if HAVE_SYS_FCNTL_H
-# include <sys/fcntl.h>
-#else /* not HAVE_SYS_FCNTL_H */
-# if HAVE_FCNTL_H
-# include <fcntl.h>
-# endif /* HAVE_FCNTL_H */
-#endif /* not HAVE_SYS_FCNTL_H */
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
-#if defined(_amigados) && !defined(__GNUC__)
-struct timezone {
- int tz_minuteswest;
- int tz_dsttime;
-};
-extern int gettimeofday(struct timeval *, struct timezone *);
-#endif
-
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include "common.h"
-#include "frontend.h"
-#include "backend.h"
-#include "parser.h"
-#include "moves.h"
-#if ZIPPY
-# include "zippy.h"
-#endif
-#include "backendz.h"
-
-/* A point in time */
-typedef struct {
- long sec; /* Assuming this is >= 32 bits */
- int ms; /* Assuming this is >= 16 bits */
-} TimeMark;
-
-/* Search stats from chessprogram */
-typedef struct {
- char movelist[2*MSG_SIZ]; /* Last PV we were sent */
- int depth; /* Current search depth */
- int nr_moves; /* Total nr of root moves */
- int moves_left; /* Moves remaining to be searched */
- char move_name[MOVE_LEN]; /* Current move being searched, if provided */
- unsigned long nodes; /* # of nodes searched */
- int time; /* Search time (centiseconds) */
- int score; /* Score (centipawns) */
- int got_only_move; /* If last msg was "(only move)" */
- int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */
- int ok_to_send; /* handshaking between send & recv */
- int line_is_book; /* 1 if movelist is book moves */
- int seen_stat; /* 1 if we've seen the stat01: line */
-} ChessProgramStats;
-
-int establish P((void));
-void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void SendToICS P((char *s));
-void SendToICSDelayed P((char *s, long msdelay));
-void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
- int toX, int toY));
-void InitPosition P((int redraw));
-void HandleMachineMove P((char *message, ChessProgramState *cps));
-int AutoPlayOneMove P((void));
-int LoadGameOneMove P((ChessMove readAhead));
-int LoadGameFromFile P((char *filename, int n, char *title, int useList));
-int LoadPositionFromFile P((char *filename, int n, char *title));
-int SavePositionToFile P((char *filename));
-void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
- Board board));
-void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
-void ShowMove P((int fromX, int fromY, int toX, int toY));
-void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
- /*char*/int promoChar));
-void BackwardInner P((int target));
-void ForwardInner P((int target));
-void GameEnds P((ChessMove result, char *resultDetails, int whosays));
-void EditPositionDone P((void));
-void PrintOpponents P((FILE *fp));
-void PrintPosition P((FILE *fp, int move));
-void StartChessProgram P((ChessProgramState *cps));
-void SendToProgram P((char *message, ChessProgramState *cps));
-void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
-void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
- char *buf, int count, int error));
-void SendTimeControl P((ChessProgramState *cps,
- int mps, long tc, int inc, int sd, int st));
-char *TimeControlTagValue P((void));
-void Attention P((ChessProgramState *cps));
-void FeedMovesToProgram P((ChessProgramState *cps, int upto));
-void ResurrectChessProgram P((void));
-void DisplayComment P((int moveNumber, char *text));
-void DisplayMove P((int moveNumber));
-void DisplayAnalysis P((void));
-
-void ParseGameHistory P((char *game));
-void ParseBoard12 P((char *string));
-void StartClocks P((void));
-void SwitchClocks P((void));
-void StopClocks P((void));
-void ResetClocks P((void));
-char *PGNDate P((void));
-void SetGameInfo P((void));
-Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
-int RegisterMove P((void));
-void MakeRegisteredMove P((void));
-void TruncateGame P((void));
-int looking_at P((char *, int *, char *));
-void CopyPlayerNameIntoFileName P((char **, char *));
-char *SavePart P((char *));
-int SaveGameOldStyle P((FILE *));
-int SaveGamePGN P((FILE *));
-void GetTimeMark P((TimeMark *));
-long SubtractTimeMarks P((TimeMark *, TimeMark *));
-int CheckFlags P((void));
-long NextTickLength P((long));
-void CheckTimeControl P((void));
-void show_bytes P((FILE *, char *, int));
-int string_to_rating P((char *str));
-void ParseFeatures P((char* args, ChessProgramState *cps));
-void InitBackEnd3 P((void));
-void FeatureDone P((ChessProgramState* cps, int val));
-void InitChessProgram P((ChessProgramState *cps));
-
-void GetInfoFromComment( int, char * );
-
-extern int tinyLayout, smallLayout;
-static ChessProgramStats programStats;
-
-/* States for ics_getting_history */
-#define H_FALSE 0
-#define H_REQUESTED 1
-#define H_GOT_REQ_HEADER 2
-#define H_GOT_UNREQ_HEADER 3
-#define H_GETTING_MOVES 4
-#define H_GOT_UNWANTED_HEADER 5
-
-/* whosays values for GameEnds */
-#define GE_ICS 0
-#define GE_ENGINE 1
-#define GE_PLAYER 2
-#define GE_FILE 3
-#define GE_XBOARD 4
-
-/* Maximum number of games in a cmail message */
-#define CMAIL_MAX_GAMES 20
-
-/* Different types of move when calling RegisterMove */
-#define CMAIL_MOVE 0
-#define CMAIL_RESIGN 1
-#define CMAIL_DRAW 2
-#define CMAIL_ACCEPT 3
-
-/* Different types of result to remember for each game */
-#define CMAIL_NOT_RESULT 0
-#define CMAIL_OLD_RESULT 1
-#define CMAIL_NEW_RESULT 2
-
-/* Telnet protocol constants */
-#define TN_WILL 0373
-#define TN_WONT 0374
-#define TN_DO 0375
-#define TN_DONT 0376
-#define TN_IAC 0377
-#define TN_ECHO 0001
-#define TN_SGA 0003
-#define TN_PORT 23
-
-/* [AS] */
-static char * safeStrCpy( char * dst, const char * src, size_t count )
-{
- assert( dst != NULL );
- assert( src != NULL );
- assert( count > 0 );
-
- strncpy( dst, src, count );
- dst[ count-1 ] = '\0';
- return dst;
-}
-
-static char * safeStrCat( char * dst, const char * src, size_t count )
-{
- size_t dst_len;
-
- assert( dst != NULL );
- assert( src != NULL );
- assert( count > 0 );
-
- dst_len = strlen(dst);
-
- assert( count > dst_len ); /* Buffer size must be greater than current length */
-
- safeStrCpy( dst + dst_len, src, count - dst_len );
-
- return dst;
-}
-
-/* Fake up flags for now, as we aren't keeping track of castling
- availability yet */
-int
-PosFlags(index)
-{
- int flags = F_ALL_CASTLE_OK;
- if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
- switch (gameInfo.variant) {
- case VariantSuicide:
- case VariantGiveaway:
- flags |= F_IGNORE_CHECK;
- flags &= ~F_ALL_CASTLE_OK;
- break;
- case VariantAtomic:
- flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
- break;
- case VariantKriegspiel:
- flags |= F_KRIEGSPIEL_CAPTURE;
- break;
- case VariantNoCastle:
- flags &= ~F_ALL_CASTLE_OK;
- break;
- default:
- break;
- }
- return flags;
-}
-
-FILE *gameFileFP, *debugFP;
-
-/*
- [AS] Note: sometimes, the sscanf() function is used to parse the input
- into a fixed-size buffer. Because of this, we must be prepared to
- receive strings as long as the size of the input buffer, which is currently
- set to 4K for Windows and 8K for the rest.
- So, we must either allocate sufficiently large buffers here, or
- reduce the size of the input buffer in the input reading part.
-*/
-
-char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
-char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
-char thinkOutput1[MSG_SIZ*10];
-
-ChessProgramState first, second;
-
-/* premove variables */
-int premoveToX = 0;
-int premoveToY = 0;
-int premoveFromX = 0;
-int premoveFromY = 0;
-int premovePromoChar = 0;
-int gotPremove = 0;
-Boolean alarmSounded;
-/* end premove variables */
-
-#define ICS_GENERIC 0
-#define ICS_ICC 1
-#define ICS_FICS 2
-#define ICS_CHESSNET 3 /* not really supported */
-int ics_type = ICS_GENERIC;
-char *ics_prefix = "$";
-
-int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
-int pauseExamForwardMostMove = 0;
-int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
-int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
-int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
-int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
-int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
-int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
-int whiteFlag = FALSE, blackFlag = FALSE;
-int userOfferedDraw = FALSE;
-int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
-int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
-int cmailMoveType[CMAIL_MAX_GAMES];
-long ics_clock_paused = 0;
-ProcRef icsPR = NoProc, cmailPR = NoProc;
-InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
-GameMode gameMode = BeginningOfGame;
-char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
-char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
-ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
-int hiddenThinkOutputState = 0; /* [AS] */
-int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
-int adjudicateLossPlies = 6;
-char white_holding[64], black_holding[64];
-TimeMark lastNodeCountTime;
-long lastNodeCount=0;
-int have_sent_ICS_logon = 0;
-int movesPerSession;
-long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
-long timeControl_2; /* [AS] Allow separate time controls */
-long timeRemaining[2][MAX_MOVES];
-int matchGame = 0;
-TimeMark programStartTime;
-char ics_handle[MSG_SIZ];
-int have_set_title = 0;
-
-/* animateTraining preserves the state of appData.animate
- * when Training mode is activated. This allows the
- * response to be animated when appData.animate == TRUE and
- * appData.animateDragging == TRUE.
- */
-Boolean animateTraining;
-
-GameInfo gameInfo;
-
-AppData appData;
-
-Board boards[MAX_MOVES];
-Board initialPosition = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
- WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
- { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
- WhitePawn, WhitePawn, WhitePawn, WhitePawn },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
- BlackPawn, BlackPawn, BlackPawn, BlackPawn },
- { BlackRook, BlackKnight, BlackBishop, BlackQueen,
- BlackKing, BlackBishop, BlackKnight, BlackRook }
-};
-Board twoKingsPosition = {
- { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
- WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
- { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
- WhitePawn, WhitePawn, WhitePawn, WhitePawn },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
- EmptySquare, EmptySquare, EmptySquare, EmptySquare },
- { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
- BlackPawn, BlackPawn, BlackPawn, BlackPawn },
- { BlackRook, BlackKnight, BlackBishop, BlackQueen,
- BlackKing, BlackKing, BlackKnight, BlackRook }
-};
-
-
-/* Convert str to a rating. Checks for special cases of "----",
- "++++", etc. Also strips ()'s */
-int
-string_to_rating(str)
- char *str;
-{
- while(*str && !isdigit(*str)) ++str;
- if (!*str)
- return 0; /* One of the special "no rating" cases */
- else
- return atoi(str);
-}
-
-void
-ClearProgramStats()
-{
- /* Init programStats */
- programStats.movelist[0] = 0;
- programStats.depth = 0;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
- programStats.nodes = 0;
- programStats.time = 100;
- programStats.score = 0;
- programStats.got_only_move = 0;
- programStats.got_fail = 0;
- programStats.line_is_book = 0;
-}
-
-void
-InitBackEnd1()
-{
- int matched, min, sec;
-
- GetTimeMark(&programStartTime);
-
- ClearProgramStats();
- programStats.ok_to_send = 1;
- programStats.seen_stat = 0;
-
- /*
- * Initialize game list
- */
- ListNew(&gameList);
-
-
- /*
- * Internet chess server status
- */
- if (appData.icsActive) {
- appData.matchMode = FALSE;
- appData.matchGames = 0;
-#if ZIPPY
- appData.noChessProgram = !appData.zippyPlay;
-#else
- appData.zippyPlay = FALSE;
- appData.zippyTalk = FALSE;
- appData.noChessProgram = TRUE;
-#endif
- if (*appData.icsHelper != NULLCHAR) {
- appData.useTelnet = TRUE;
- appData.telnetProgram = appData.icsHelper;
- }
- } else {
- appData.zippyTalk = appData.zippyPlay = FALSE;
- }
-
- /* [AS] Initialize pv info list */
- {
- int i;
-
- for( i=0; i<MAX_MOVES; i++ ) {
- pvInfoList[i].depth = 0;
- }
- }
-
- /*
- * Parse timeControl resource
- */
- if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
- appData.movesPerSession)) {
- char buf[MSG_SIZ];
- sprintf(buf, "bad timeControl option %s", appData.timeControl);
- DisplayFatalError(buf, 0, 2);
- }
-
- /*
- * Parse searchTime resource
- */
- if (*appData.searchTime != NULLCHAR) {
- matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
- if (matched == 1) {
- searchTime = min * 60;
- } else if (matched == 2) {
- searchTime = min * 60 + sec;
- } else {
- char buf[MSG_SIZ];
- sprintf(buf, "bad searchTime option %s", appData.searchTime);
- DisplayFatalError(buf, 0, 2);
- }
- }
-
- /* [AS] Adjudication threshold */
- adjudicateLossThreshold = appData.adjudicateLossThreshold;
-
- first.which = "first";
- second.which = "second";
- first.maybeThinking = second.maybeThinking = FALSE;
- first.pr = second.pr = NoProc;
- first.isr = second.isr = NULL;
- first.sendTime = second.sendTime = 2;
- first.sendDrawOffers = 1;
- if (appData.firstPlaysBlack) {
- first.twoMachinesColor = "black\n";
- second.twoMachinesColor = "white\n";
- } else {
- first.twoMachinesColor = "white\n";
- second.twoMachinesColor = "black\n";
- }
- first.program = appData.firstChessProgram;
- second.program = appData.secondChessProgram;
- first.host = appData.firstHost;
- second.host = appData.secondHost;
- first.dir = appData.firstDirectory;
- second.dir = appData.secondDirectory;
- first.other = &second;
- second.other = &first;
- first.initString = appData.initString;
- second.initString = appData.secondInitString;
- first.computerString = appData.firstComputerString;
- second.computerString = appData.secondComputerString;
- first.useSigint = second.useSigint = TRUE;
- first.useSigterm = second.useSigterm = TRUE;
- first.reuse = appData.reuseFirst;
- second.reuse = appData.reuseSecond;
- first.useSetboard = second.useSetboard = FALSE;
- first.useSAN = second.useSAN = FALSE;
- first.usePing = second.usePing = FALSE;
- first.lastPing = second.lastPing = 0;
- first.lastPong = second.lastPong = 0;
- first.usePlayother = second.usePlayother = FALSE;
- first.useColors = second.useColors = TRUE;
- first.useUsermove = second.useUsermove = FALSE;
- first.sendICS = second.sendICS = FALSE;
- first.sendName = second.sendName = appData.icsActive;
- first.sdKludge = second.sdKludge = FALSE;
- first.stKludge = second.stKludge = FALSE;
- TidyProgramName(first.program, first.host, first.tidy);
- TidyProgramName(second.program, second.host, second.tidy);
- first.matchWins = second.matchWins = 0;
- strcpy(first.variants, appData.variant);
- strcpy(second.variants, appData.variant);
- first.analysisSupport = second.analysisSupport = 2; /* detect */
- first.analyzing = second.analyzing = FALSE;
- first.initDone = second.initDone = FALSE;
-
- /* New features added by Tord: */
- first.useFEN960 = FALSE; second.useFEN960 = FALSE;
- first.useOOCastle = TRUE; second.useOOCastle = TRUE;
- /* End of new features added by Tord. */
-
- first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
- second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
- first.isUCI = appData.firstIsUCI; /* [AS] */
- second.isUCI = appData.secondIsUCI; /* [AS] */
- first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
- second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
-
- if (appData.firstProtocolVersion > PROTOVER ||
- appData.firstProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, "protocol version %d not supported",
- appData.firstProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- first.protocolVersion = appData.firstProtocolVersion;
- }
-
- if (appData.secondProtocolVersion > PROTOVER ||
- appData.secondProtocolVersion < 1) {
- char buf[MSG_SIZ];
- sprintf(buf, "protocol version %d not supported",
- appData.secondProtocolVersion);
- DisplayFatalError(buf, 0, 2);
- } else {
- second.protocolVersion = appData.secondProtocolVersion;
- }
-
- if (appData.icsActive) {
- appData.clockMode = TRUE; /* changes dynamically in ICS mode */
- } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
- appData.clockMode = FALSE;
- first.sendTime = second.sendTime = 0;
- }
-
-#if ZIPPY
- /* Override some settings from environment variables, for backward
- compatibility. Unfortunately it's not feasible to have the env
- vars just set defaults, at least in xboard. Ugh.
- */
- if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
- ZippyInit();
- }
-#endif
-
- if (appData.noChessProgram) {
- programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
- + strlen(PATCHLEVEL));
- sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
- } else {
- char *p, *q;
- q = first.program;
- while (*q != ' ' && *q != NULLCHAR) q++;
- p = q;
- while (p > first.program && *(p-1) != '/') p--;
- programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
- + strlen(PATCHLEVEL) + (q - p));
- sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
- strncat(programVersion, p, q - p);
- }
-
- if (!appData.icsActive) {
- char buf[MSG_SIZ];
- /* Check for variants that are supported only in ICS mode,
- or not at all. Some that are accepted here nevertheless
- have bugs; see comments below.
- */
- VariantClass variant = StringToVariant(appData.variant);
- switch (variant) {
- case VariantBughouse: /* need four players and two boards */
- case VariantKriegspiel: /* need to hide pieces and move details */
- /* case VariantFischeRandom: (Fabien: moved below) */
- sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
- DisplayFatalError(buf, 0, 2);
- return;
-
- case VariantUnknown:
- case VariantLoadable:
- case Variant29:
- case Variant30:
- case Variant31:
- case Variant32:
- case Variant33:
- case Variant34:
- case Variant35:
- case Variant36:
- default:
- sprintf(buf, "Unknown variant name %s", appData.variant);
- DisplayFatalError(buf, 0, 2);
- return;
-
- case VariantNormal: /* definitely works! */
- case VariantWildCastle: /* pieces not automatically shuffled */
- case VariantNoCastle: /* pieces not automatically shuffled */
- case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */
- case VariantCrazyhouse: /* holdings not shown,
- offboard interposition not understood */
- case VariantLosers: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantSuicide: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantGiveaway: /* should work except for win condition,
- and doesn't know captures are mandatory */
- case VariantTwoKings: /* should work */
- case VariantAtomic: /* should work except for win condition */
- case Variant3Check: /* should work except for win condition */
- case VariantShatranj: /* might work if TestLegality is off */
- break;
- }
- }
-}
-
-int NextIntegerFromString( char ** str, long * value )
-{
- int result = -1;
- char * s = *str;
-
- while( *s == ' ' || *s == '\t' ) {
- s++;
- }
-
- *value = 0;
-
- if( *s >= '0' && *s <= '9' ) {
- while( *s >= '0' && *s <= '9' ) {
- *value = *value * 10 + (*s - '0');
- s++;
- }
-
- result = 0;
- }
-
- *str = s;
-
- return result;
-}
-
-int NextTimeControlFromString( char ** str, long * value )
-{
- long temp;
- int result = NextIntegerFromString( str, &temp );
-
- if( result == 0 ) {
- *value = temp * 60; /* Minutes */
- if( **str == ':' ) {
- (*str)++;
- result = NextIntegerFromString( str, &temp );
- *value += temp; /* Seconds */
- }
- }
-
- return result;
-}
-
-int GetTimeControlForWhite()
-{
- int result = timeControl;
-
- return result;
-}
-
-int GetTimeControlForBlack()
-{
- int result = timeControl;
-
- if( timeControl_2 > 0 ) {
- result = timeControl_2;
- }
-
- return result;
-}
-
-int
-ParseTimeControl(tc, ti, mps)
- char *tc;
- int ti;
- int mps;
-{
-#if 0
- int matched, min, sec;
-
- matched = sscanf(tc, "%d:%d", &min, &sec);
- if (matched == 1) {
- timeControl = min * 60 * 1000;
- } else if (matched == 2) {
- timeControl = (min * 60 + sec) * 1000;
- } else {
- return FALSE;
- }
-#else
- long tc1;
- long tc2;
-
- if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
- return FALSE;
- }
-
- if( *tc == '/' ) {
- /* Parse second time control */
- tc++;
-
- if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
- return FALSE;
- }
-
- if( tc2 == 0 ) {
- return FALSE;
- }
-
- timeControl_2 = tc2 * 1000;
- }
- else {
- timeControl_2 = 0;
- }
-
- if( tc1 == 0 ) {
- return FALSE;
- }
-
- timeControl = tc1 * 1000;
-#endif
-
- if (ti >= 0) {
- timeIncrement = ti * 1000; /* convert to ms */
- movesPerSession = 0;
- } else {
- timeIncrement = 0;
- movesPerSession = mps;
- }
- return TRUE;
-}
-
-void
-InitBackEnd2()
-{
- if (appData.debugMode) {
- fprintf(debugFP, "%s\n", programVersion);
- }
-
- if (appData.matchGames > 0) {
- appData.matchMode = TRUE;
- } else if (appData.matchMode) {
- appData.matchGames = 1;
- }
- Reset(TRUE, FALSE);
- if (appData.noChessProgram || first.protocolVersion == 1) {
- InitBackEnd3();
- } else {
- /* kludge: allow timeout for initial "feature" commands */
- FreezeUI();
- DisplayMessage("", "Starting chess program");
- ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
- }
-}
-
-void
-InitBackEnd3 P((void))
-{
- GameMode initialMode;
- char buf[MSG_SIZ];
- int err;
-
- InitChessProgram(&first);
-
- if (appData.icsActive) {
- err = establish();
- if (err != 0) {
- if (*appData.icsCommPort != NULLCHAR) {
- sprintf(buf, "Could not open comm port %s",
- appData.icsCommPort);
- } else {
- sprintf(buf, "Could not connect to host %s, port %s",
- appData.icsHost, appData.icsPort);
- }
- DisplayFatalError(buf, err, 1);
- return;
- }
- SetICSMode();
- telnetISR =
- AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
- fromUserISR =
- AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
- } else if (appData.noChessProgram) {
- SetNCPMode();
- } else {
- SetGNUMode();
- }
-
- if (*appData.cmailGameName != NULLCHAR) {
- SetCmailMode();
- OpenLoopback(&cmailPR);
- cmailISR =
- AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
- }
-
- ThawUI();
- DisplayMessage("", "");
- if (StrCaseCmp(appData.initialMode, "") == 0) {
- initialMode = BeginningOfGame;
- } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
- initialMode = TwoMachinesPlay;
- } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
- initialMode = AnalyzeFile;
- } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
- initialMode = AnalyzeMode;
- } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
- initialMode = MachinePlaysWhite;
- } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
- initialMode = MachinePlaysBlack;
- } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
- initialMode = EditGame;
- } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
- initialMode = EditPosition;
- } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
- initialMode = Training;
- } else {
- sprintf(buf, "Unknown initialMode %s", appData.initialMode);
- DisplayFatalError(buf, 0, 2);
- return;
- }
-
- if (appData.matchMode) {
- /* Set up machine vs. machine match */
- if (appData.noChessProgram) {
- DisplayFatalError("Can't have a match with no chess programs",
- 0, 2);
- return;
- }
- matchMode = TRUE;
- matchGame = 1;
- if (*appData.loadGameFile != NULLCHAR) {
- if (!LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, FALSE)) {
- DisplayFatalError("Bad game file", 0, 1);
- return;
- }
- } else if (*appData.loadPositionFile != NULLCHAR) {
- if (!LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile)) {
- DisplayFatalError("Bad position file", 0, 1);
- return;
- }
- }
- TwoMachinesEvent();
- } else if (*appData.cmailGameName != NULLCHAR) {
- /* Set up cmail mode */
- ReloadCmailMsgEvent(TRUE);
- } else {
- /* Set up other modes */
- if (initialMode == AnalyzeFile) {
- if (*appData.loadGameFile == NULLCHAR) {
- DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
- return;
- }
- }
- if (*appData.loadGameFile != NULLCHAR) {
- (void) LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, TRUE);
- } else if (*appData.loadPositionFile != NULLCHAR) {
- (void) LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile);
- }
- if (initialMode == AnalyzeMode) {
- if (appData.noChessProgram) {
- DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
- return;
- }
- AnalyzeModeEvent();
- } else if (initialMode == AnalyzeFile) {
- ShowThinkingEvent(TRUE);
- AnalyzeFileEvent();
- AnalysisPeriodicEvent(1);
- } else if (initialMode == MachinePlaysWhite) {
- if (appData.noChessProgram) {
- DisplayFatalError("MachineWhite mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("MachineWhite mode does not work with ICS mode",
- 0, 2);
- return;
- }
- MachineWhiteEvent();
- } else if (initialMode == MachinePlaysBlack) {
- if (appData.noChessProgram) {
- DisplayFatalError("MachineBlack mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("MachineBlack mode does not work with ICS mode",
- 0, 2);
- return;
- }
- MachineBlackEvent();
- } else if (initialMode == TwoMachinesPlay) {
- if (appData.noChessProgram) {
- DisplayFatalError("TwoMachines mode requires a chess engine",
- 0, 2);
- return;
- }
- if (appData.icsActive) {
- DisplayFatalError("TwoMachines mode does not work with ICS mode",
- 0, 2);
- return;
- }
- TwoMachinesEvent();
- } else if (initialMode == EditGame) {
- EditGameEvent();
- } else if (initialMode == EditPosition) {
- EditPositionEvent();
- } else if (initialMode == Training) {
- if (*appData.loadGameFile == NULLCHAR) {
- DisplayFatalError("Training mode requires a game file", 0, 2);
- return;
- }
- TrainingEvent();
- }
- }
-}
-
-/*
- * Establish will establish a contact to a remote host.port.
- * Sets icsPR to a ProcRef for a process (or pseudo-process)
- * used to talk to the host.
- * Returns 0 if okay, error code if not.
- */
-int
-establish()
-{
- char buf[MSG_SIZ];
-
- if (*appData.icsCommPort != NULLCHAR) {
- /* Talk to the host through a serial comm port */
- return OpenCommPort(appData.icsCommPort, &icsPR);
-
- } else if (*appData.gateway != NULLCHAR) {
- if (*appData.remoteShell == NULLCHAR) {
- /* Use the rcmd protocol to run telnet program on a gateway host */
- sprintf(buf, "%s %s %s",
- appData.telnetProgram, appData.icsHost, appData.icsPort);
- return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
-
- } else {
- /* Use the rsh program to run telnet program on a gateway host */
- if (*appData.remoteUser == NULLCHAR) {
- sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
- appData.gateway, appData.telnetProgram,
- appData.icsHost, appData.icsPort);
- } else {
- sprintf(buf, "%s %s -l %s %s %s %s",
- appData.remoteShell, appData.gateway,
- appData.remoteUser, appData.telnetProgram,
- appData.icsHost, appData.icsPort);
- }
- return StartChildProcess(buf, "", &icsPR);
-
- }
- } else if (appData.useTelnet) {
- return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
-
- } else {
- /* TCP socket interface differs somewhat between
- Unix and NT; handle details in the front end.
- */
- return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
- }
-}
-
-void
-show_bytes(fp, buf, count)
- FILE *fp;
- char *buf;
- int count;
-{
- while (count--) {
- if (*buf < 040 || *(unsigned char *) buf > 0177) {
- fprintf(fp, "\\%03o", *buf & 0xff);
- } else {
- putc(*buf, fp);
- }
- buf++;
- }
- fflush(fp);
-}
-
-/* Returns an errno value */
-int
-OutputMaybeTelnet(pr, message, count, outError)
- ProcRef pr;
- char *message;
- int count;
- int *outError;
-{
- char buf[8192], *p, *q, *buflim;
- int left, newcount, outcount;
-
- if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
- *appData.gateway != NULLCHAR) {
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, message, count);
- fprintf(debugFP, "\n");
- }
- return OutputToProcess(pr, message, count, outError);
- }
-
- buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
- p = message;
- q = buf;
- left = count;
- newcount = 0;
- while (left) {
- if (q >= buflim) {
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, buf, newcount);
- fprintf(debugFP, "\n");
- }
- outcount = OutputToProcess(pr, buf, newcount, outError);
- if (outcount < newcount) return -1; /* to be sure */
- q = buf;
- newcount = 0;
- }
- if (*p == '\n') {
- *q++ = '\r';
- newcount++;
- } else if (((unsigned char) *p) == TN_IAC) {
- *q++ = (char) TN_IAC;
- newcount ++;
- }
- *q++ = *p++;
- newcount++;
- left--;
- }
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, buf, newcount);
- fprintf(debugFP, "\n");
- }
- outcount = OutputToProcess(pr, buf, newcount, outError);
- if (outcount < newcount) return -1; /* to be sure */
- return count;
-}
-
-void
-read_from_player(isr, closure, message, count, error)
- InputSourceRef isr;
- VOIDSTAR closure;
- char *message;
- int count;
- int error;
-{
- int outError, outCount;
- static int gotEof = 0;
-
- /* Pass data read from player on to ICS */
- if (count > 0) {
- gotEof = 0;
- outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
- } else if (count < 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Error reading from keyboard", error, 1);
- } else if (gotEof++ > 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Got end of file from keyboard", 0, 0);
- }
-}
-
-void
-SendToICS(s)
- char *s;
-{
- int count, outCount, outError;
-
- if (icsPR == NULL) return;
-
- count = strlen(s);
- outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-/* This is used for sending logon scripts to the ICS. Sending
- without a delay causes problems when using timestamp on ICC
- (at least on my machine). */
-void
-SendToICSDelayed(s,msdelay)
- char *s;
- long msdelay;
-{
- int count, outCount, outError;
-
- if (icsPR == NULL) return;
-
- count = strlen(s);
- if (appData.debugMode) {
- fprintf(debugFP, ">ICS: ");
- show_bytes(debugFP, s, count);
- fprintf(debugFP, "\n");
- }
- outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
- msdelay);
- if (outCount < count) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-
-/* Remove all highlighting escape sequences in s
- Also deletes any suffix starting with '('
- */
-char *
-StripHighlightAndTitle(s)
- char *s;
-{
- static char retbuf[MSG_SIZ];
- char *p = retbuf;
-
- while (*s != NULLCHAR) {
- while (*s == '\033') {
- while (*s != NULLCHAR && !isalpha(*s)) s++;
- if (*s != NULLCHAR) s++;
- }
- while (*s != NULLCHAR && *s != '\033') {
- if (*s == '(' || *s == '[') {
- *p = NULLCHAR;
- return retbuf;
- }
- *p++ = *s++;
- }
- }
- *p = NULLCHAR;
- return retbuf;
-}
-
-/* Remove all highlighting escape sequences in s */
-char *
-StripHighlight(s)
- char *s;
-{
- static char retbuf[MSG_SIZ];
- char *p = retbuf;
-
- while (*s != NULLCHAR) {
- while (*s == '\033') {
- while (*s != NULLCHAR && !isalpha(*s)) s++;
- if (*s != NULLCHAR) s++;
- }
- while (*s != NULLCHAR && *s != '\033') {
- *p++ = *s++;
- }
- }
- *p = NULLCHAR;
- return retbuf;
-}
-
-char *variantNames[] = VARIANT_NAMES;
-char *
-VariantName(v)
- VariantClass v;
-{
- return variantNames[v];
-}
-
-
-/* Identify a variant from the strings the chess servers use or the
- PGN Variant tag names we use. */
-VariantClass
-StringToVariant(e)
- char *e;
-{
- char *p;
- int wnum = -1;
- VariantClass v = VariantNormal;
- int i, found = FALSE;
- char buf[MSG_SIZ];
-
- if (!e) return v;
-
- for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
- if (StrCaseStr(e, variantNames[i])) {
- v = (VariantClass) i;
- found = TRUE;
- break;
- }
- }
-
- if (!found) {
- if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
- || StrCaseStr(e, "wild/fr")) {
- v = VariantFischeRandom;
- } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
- (i = 1, p = StrCaseStr(e, "w"))) {
- p += i;
- while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
- if (isdigit(*p)) {
- wnum = atoi(p);
- } else {
- wnum = -1;
- }
- switch (wnum) {
- case 0: /* FICS only, actually */
- case 1:
- /* Castling legal even if K starts on d-file */
- v = VariantWildCastle;
- break;
- case 2:
- case 3:
- case 4:
- /* Castling illegal even if K & R happen to start in
- normal positions. */
- v = VariantNoCastle;
- break;
- case 5:
- case 7:
- case 8:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 18:
- case 19:
- /* Castling legal iff K & R start in normal positions */
- v = VariantNormal;
- break;
- case 6:
- case 20:
- case 21:
- /* Special wilds for position setup; unclear what to do here */
- v = VariantLoadable;
- break;
- case 9:
- /* Bizarre ICC game */
- v = VariantTwoKings;
- break;
- case 16:
- v = VariantKriegspiel;
- break;
- case 17:
- v = VariantLosers;
- break;
- case 22:
- v = VariantFischeRandom;
- break;
- case 23:
- v = VariantCrazyhouse;
- break;
- case 24:
- v = VariantBughouse;
- break;
- case 25:
- v = Variant3Check;
- break;
- case 26:
- /* Not quite the same as FICS suicide! */
- v = VariantGiveaway;
- break;
- case 27:
- v = VariantAtomic;
- break;
- case 28:
- v = VariantShatranj;
- break;
-
- /* Temporary names for future ICC types. The name *will* change in
- the next xboard/WinBoard release after ICC defines it. */
- case 29:
- v = Variant29;
- break;
- case 30:
- v = Variant30;
- break;
- case 31:
- v = Variant31;
- break;
- case 32:
- v = Variant32;
- break;
- case 33:
- v = Variant33;
- break;
- case 34:
- v = Variant34;
- break;
- case 35:
- v = Variant35;
- break;
- case 36:
- v = Variant36;
- break;
-
- case -1:
- /* Found "wild" or "w" in the string but no number;
- must assume it's normal chess. */
- v = VariantNormal;
- break;
- default:
- sprintf(buf, "Unknown wild type %d", wnum);
- DisplayError(buf, 0);
- v = VariantUnknown;
- break;
- }
- }
- }
- if (appData.debugMode) {
- fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
- e, wnum, VariantName(v));
- }
- return v;
-}
-
-static int leftover_start = 0, leftover_len = 0;
-char star_match[STAR_MATCH_N][MSG_SIZ];
-
-/* Test whether pattern is present at &buf[*index]; if so, return TRUE,
- advance *index beyond it, and set leftover_start to the new value of
- *index; else return FALSE. If pattern contains the character '*', it
- matches any sequence of characters not containing '\r', '\n', or the
- character following the '*' (if any), and the matched sequence(s) are
- copied into star_match.
- */
-int
-looking_at(buf, index, pattern)
- char *buf;
- int *index;
- char *pattern;
-{
- char *bufp = &buf[*index], *patternp = pattern;
- int star_count = 0;
- char *matchp = star_match[0];
-
- for (;;) {
- if (*patternp == NULLCHAR) {
- *index = leftover_start = bufp - buf;
- *matchp = NULLCHAR;
- return TRUE;
- }
- if (*bufp == NULLCHAR) return FALSE;
- if (*patternp == '*') {
- if (*bufp == *(patternp + 1)) {
- *matchp = NULLCHAR;
- matchp = star_match[++star_count];
- patternp += 2;
- bufp++;
- continue;
- } else if (*bufp == '\n' || *bufp == '\r') {
- patternp++;
- if (*patternp == NULLCHAR)
- continue;
- else
- return FALSE;
- } else {
- *matchp++ = *bufp++;
- continue;
- }
- }
- if (*patternp != *bufp) return FALSE;
- patternp++;
- bufp++;
- }
-}
-
-void
-SendToPlayer(data, length)
- char *data;
- int length;
-{
- int error, outCount;
- outCount = OutputToProcess(NoProc, data, length, &error);
- if (outCount < length) {
- DisplayFatalError("Error writing to display", error, 1);
- }
-}
-
-void
-PackHolding(packed, holding)
- char packed[];
- char *holding;
-{
- char *p = holding;
- char *q = packed;
- int runlength = 0;
- int curr = 9999;
- do {
- if (*p == curr) {
- runlength++;
- } else {
- switch (runlength) {
- case 0:
- break;
- case 1:
- *q++ = curr;
- break;
- case 2:
- *q++ = curr;
- *q++ = curr;
- break;
- default:
- sprintf(q, "%d", runlength);
- while (*q) q++;
- *q++ = curr;
- break;
- }
- runlength = 1;
- curr = *p;
- }
- } while (*p++);
- *q = NULLCHAR;
-}
-
-/* Telnet protocol requests from the front end */
-void
-TelnetRequest(ddww, option)
- unsigned char ddww, option;
-{
- unsigned char msg[3];
- int outCount, outError;
-
- if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
-
- if (appData.debugMode) {
- char buf1[8], buf2[8], *ddwwStr, *optionStr;
- switch (ddww) {
- case TN_DO:
- ddwwStr = "DO";
- break;
- case TN_DONT:
- ddwwStr = "DONT";
- break;
- case TN_WILL:
- ddwwStr = "WILL";
- break;
- case TN_WONT:
- ddwwStr = "WONT";
- break;
- default:
- ddwwStr = buf1;
- sprintf(buf1, "%d", ddww);
- break;
- }
- switch (option) {
- case TN_ECHO:
- optionStr = "ECHO";
- break;
- default:
- optionStr = buf2;
- sprintf(buf2, "%d", option);
- break;
- }
- fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
- }
- msg[0] = TN_IAC;
- msg[1] = ddww;
- msg[2] = option;
- outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
- if (outCount < 3) {
- DisplayFatalError("Error writing to ICS", outError, 1);
- }
-}
-
-void
-DoEcho()
-{
- if (!appData.icsActive) return;
- TelnetRequest(TN_DO, TN_ECHO);
-}
-
-void
-DontEcho()
-{
- if (!appData.icsActive) return;
- TelnetRequest(TN_DONT, TN_ECHO);
-}
-
-static int loggedOn = FALSE;
-
-/*-- Game start info cache: --*/
-int gs_gamenum;
-char gs_kind[MSG_SIZ];
-static char player1Name[128] = "";
-static char player2Name[128] = "";
-static int player1Rating = -1;
-static int player2Rating = -1;
-/*----------------------------*/
-
-ColorClass curColor = ColorNormal;
-
-void
-read_from_ics(isr, closure, data, count, error)
- InputSourceRef isr;
- VOIDSTAR closure;
- char *data;
- int count;
- int error;
-{
-#define BUF_SIZE 8192
-#define STARTED_NONE 0
-#define STARTED_MOVES 1
-#define STARTED_BOARD 2
-#define STARTED_OBSERVE 3
-#define STARTED_HOLDINGS 4
-#define STARTED_CHATTER 5
-#define STARTED_COMMENT 6
-#define STARTED_MOVES_NOHIDE 7
-
- static int started = STARTED_NONE;
- static char parse[20000];
- static int parse_pos = 0;
- static char buf[BUF_SIZE + 1];
- static int firstTime = TRUE, intfSet = FALSE;
- static ColorClass prevColor = ColorNormal;
- static int savingComment = FALSE;
- char str[500];
- int i, oldi;
- int buf_len;
- int next_out;
- int tkind;
- char *p;
-
-#ifdef WIN32
- if (appData.debugMode) {
- if (!error) {
- fprintf(debugFP, "<ICS: ");
- show_bytes(debugFP, data, count);
- fprintf(debugFP, "\n");
- }
- }
-#endif
-
- if (count > 0) {
- /* If last read ended with a partial line that we couldn't parse,
- prepend it to the new read and try again. */
- if (leftover_len > 0) {
- for (i=0; i<leftover_len; i++)
- buf[i] = buf[leftover_start + i];
- }
-
- /* Copy in new characters, removing nulls and \r's */
- buf_len = leftover_len;
- for (i = 0; i < count; i++) {
- if (data[i] != NULLCHAR && data[i] != '\r')
- buf[buf_len++] = data[i];
- }
-
- buf[buf_len] = NULLCHAR;
- next_out = leftover_len;
- leftover_start = 0;
-
- i = 0;
- while (i < buf_len) {
- /* Deal with part of the TELNET option negotiation
- protocol. We refuse to do anything beyond the
- defaults, except that we allow the WILL ECHO option,
- which ICS uses to turn off password echoing when we are
- directly connected to it. We reject this option
- if localLineEditing mode is on (always on in xboard)
- and we are talking to port 23, which might be a real
- telnet server that will try to keep WILL ECHO on permanently.
- */
- if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
- static int remoteEchoOption = FALSE; /* telnet ECHO option */
- unsigned char option;
- oldi = i;
- switch ((unsigned char) buf[++i]) {
- case TN_WILL:
- if (appData.debugMode)
- fprintf(debugFP, "\n<WILL ");
- switch (option = (unsigned char) buf[++i]) {
- case TN_ECHO:
- if (appData.debugMode)
- fprintf(debugFP, "ECHO ");
- /* Reply only if this is a change, according
- to the protocol rules. */
- if (remoteEchoOption) break;
- if (appData.localLineEditing &&
- atoi(appData.icsPort) == TN_PORT) {
- TelnetRequest(TN_DONT, TN_ECHO);
- } else {
- EchoOff();
- TelnetRequest(TN_DO, TN_ECHO);
- remoteEchoOption = TRUE;
- }
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- /* Whatever this is, we don't want it. */
- TelnetRequest(TN_DONT, option);
- break;
- }
- break;
- case TN_WONT:
- if (appData.debugMode)
- fprintf(debugFP, "\n<WONT ");
- switch (option = (unsigned char) buf[++i]) {
- case TN_ECHO:
- if (appData.debugMode)
- fprintf(debugFP, "ECHO ");
- /* Reply only if this is a change, according
- to the protocol rules. */
- if (!remoteEchoOption) break;
- EchoOn();
- TelnetRequest(TN_DONT, TN_ECHO);
- remoteEchoOption = FALSE;
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", (unsigned char) option);
- /* Whatever this is, it must already be turned
- off, because we never agree to turn on
- anything non-default, so according to the
- protocol rules, we don't reply. */
- break;
- }
- break;
- case TN_DO:
- if (appData.debugMode)
- fprintf(debugFP, "\n<DO ");
- switch (option = (unsigned char) buf[++i]) {
- default:
- /* Whatever this is, we refuse to do it. */
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- TelnetRequest(TN_WONT, option);
- break;
- }
- break;
- case TN_DONT:
- if (appData.debugMode)
- fprintf(debugFP, "\n<DONT ");
- switch (option = (unsigned char) buf[++i]) {
- default:
- if (appData.debugMode)
- fprintf(debugFP, "%d ", option);
- /* Whatever this is, we are already not doing
- it, because we never agree to do anything
- non-default, so according to the protocol
- rules, we don't reply. */
- break;
- }
- break;
- case TN_IAC:
- if (appData.debugMode)
- fprintf(debugFP, "\n<IAC ");
- /* Doubled IAC; pass it through */
- i--;
- break;
- default:
- if (appData.debugMode)
- fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
- /* Drop all other telnet commands on the floor */
- break;
- }
- if (oldi > next_out)
- SendToPlayer(&buf[next_out], oldi - next_out);
- if (++i > next_out)
- next_out = i;
- continue;
- }
-
- /* OK, this at least will *usually* work */
- if (!loggedOn && looking_at(buf, &i, "ics%")) {
- loggedOn = TRUE;
- }
-
- if (loggedOn && !intfSet) {
- if (ics_type == ICS_ICC) {
- sprintf(str,
- "/set-quietly interface %s\n/set-quietly style 12\n",
- programVersion);
-
- } else if (ics_type == ICS_CHESSNET) {
- sprintf(str, "/style 12\n");
- } else {
- strcpy(str, "alias $ @\n$set interface ");
- strcat(str, programVersion);
- strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
-#ifdef WIN32
- strcat(str, "$iset nohighlight 1\n");
-#endif
- strcat(str, "$iset lock 1\n$style 12\n");
- }
- SendToICS(str);
- intfSet = TRUE;
- }
-
- if (started == STARTED_COMMENT) {
- /* Accumulate characters in comment */
- parse[parse_pos++] = buf[i];
- if (buf[i] == '\n') {
- parse[parse_pos] = NULLCHAR;
- AppendComment(forwardMostMove, StripHighlight(parse));
- started = STARTED_NONE;
- } else {
- /* Don't match patterns against characters in chatter */
- i++;
- continue;
- }
- }
- if (started == STARTED_CHATTER) {
- if (buf[i] != '\n') {
- /* Don't match patterns against characters in chatter */
- i++;
- continue;
- }
- started = STARTED_NONE;
- }
-
- /* Kludge to deal with rcmd protocol */
- if (firstTime && looking_at(buf, &i, "\001*")) {
- DisplayFatalError(&buf[1], 0, 1);
- continue;
- } else {
- firstTime = FALSE;
- }
-
- if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
- ics_type = ICS_ICC;
- ics_prefix = "/";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
- if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
- ics_type = ICS_FICS;
- ics_prefix = "$";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
- if (!loggedOn && looking_at(buf, &i, "chess.net")) {
- ics_type = ICS_CHESSNET;
- ics_prefix = "/";
- if (appData.debugMode)
- fprintf(debugFP, "ics_type %d\n", ics_type);
- continue;
- }
-
- if (!loggedOn &&
- (looking_at(buf, &i, "\"*\" is *a registered name") ||
- looking_at(buf, &i, "Logging you in as \"*\"") ||
- looking_at(buf, &i, "will be \"*\""))) {
- strcpy(ics_handle, star_match[0]);
- continue;
- }
-
- if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
- char buf[MSG_SIZ];
- sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
- DisplayIcsInteractionTitle(buf);
- have_set_title = TRUE;
- }
-
- /* skip finger notes */
- if (started == STARTED_NONE &&
- ((buf[i] == ' ' && isdigit(buf[i+1])) ||
- (buf[i] == '1' && buf[i+1] == '0')) &&
- buf[i+2] == ':' && buf[i+3] == ' ') {
- started = STARTED_CHATTER;
- i += 3;
- continue;
- }
-
- /* skip formula vars */
- if (started == STARTED_NONE &&
- buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
- started = STARTED_CHATTER;
- i += 3;
- continue;
- }
-
- oldi = i;
- if (appData.zippyTalk || appData.zippyPlay) {
-#if ZIPPY
- if (ZippyControl(buf, &i) ||
- ZippyConverse(buf, &i) ||
- (appData.zippyPlay && ZippyMatch(buf, &i))) {
- loggedOn = TRUE;
- continue;
- }
-#endif
- } else {
- if (/* Don't color "message" or "messages" output */
- (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
- looking_at(buf, &i, "*. * at *:*: ") ||
- looking_at(buf, &i, "--* (*:*): ") ||
- /* Regular tells and says */
- (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
- looking_at(buf, &i, "* (your partner) tells you: ") ||
- looking_at(buf, &i, "* says: ") ||
- /* Message notifications (same color as tells) */
- looking_at(buf, &i, "* has left a message ") ||
- looking_at(buf, &i, "* just sent you a message:\n") ||
- /* Whispers and kibitzes */
- (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
- looking_at(buf, &i, "* kibitzes: ") ||
- /* Channel tells */
- (tkind = 3, looking_at(buf, &i, "*(*: "))) {
-
- if (tkind == 1 && strchr(star_match[0], ':')) {
- /* Avoid "tells you:" spoofs in channels */
- tkind = 3;
- }
- if (star_match[0][0] == NULLCHAR ||
- strchr(star_match[0], ' ') ||
- (tkind == 3 && strchr(star_match[1], ' '))) {
- /* Reject bogus matches */
- i = oldi;
- } else {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- switch (tkind) {
- case 1:
- Colorize(ColorTell, FALSE);
- curColor = ColorTell;
- break;
- case 2:
- Colorize(ColorKibitz, FALSE);
- curColor = ColorKibitz;
- break;
- case 3:
- p = strrchr(star_match[1], '(');
- if (p == NULL) {
- p = star_match[1];
- } else {
- p++;
- }
- if (atoi(p) == 1) {
- Colorize(ColorChannel1, FALSE);
- curColor = ColorChannel1;
- } else {
- Colorize(ColorChannel, FALSE);
- curColor = ColorChannel;
- }
- break;
- case 5:
- curColor = ColorNormal;
- break;
- }
- }
- if (started == STARTED_NONE && appData.autoComment &&
- (gameMode == IcsObserving ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack)) {
- parse_pos = i - oldi;
- memcpy(parse, &buf[oldi], parse_pos);
- parse[parse_pos] = NULLCHAR;
- started = STARTED_COMMENT;
- savingComment = TRUE;
- } else {
- started = STARTED_CHATTER;
- savingComment = FALSE;
- }
- loggedOn = TRUE;
- continue;
- }
- }
-
- if (looking_at(buf, &i, "* s-shouts: ") ||
- looking_at(buf, &i, "* c-shouts: ")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorSShout, FALSE);
- curColor = ColorSShout;
- }
- loggedOn = TRUE;
- started = STARTED_CHATTER;
- continue;
- }
-
- if (looking_at(buf, &i, "--->")) {
- loggedOn = TRUE;
- continue;
- }
-
- if (looking_at(buf, &i, "* shouts: ") ||
- looking_at(buf, &i, "--> ")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorShout, FALSE);
- curColor = ColorShout;
- }
- loggedOn = TRUE;
- started = STARTED_CHATTER;
- continue;
- }
-
- if (looking_at( buf, &i, "Challenge:")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorChallenge, FALSE);
- curColor = ColorChallenge;
- }
- loggedOn = TRUE;
- continue;
- }
-
- if (looking_at(buf, &i, "* offers you") ||
- looking_at(buf, &i, "* offers to be") ||
- looking_at(buf, &i, "* would like to") ||
- looking_at(buf, &i, "* requests to") ||
- looking_at(buf, &i, "Your opponent offers") ||
- looking_at(buf, &i, "Your opponent requests")) {
-
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorRequest, FALSE);
- curColor = ColorRequest;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "* (*) seeking")) {
- if (appData.colorize) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorSeek, FALSE);
- curColor = ColorSeek;
- }
- continue;
- }
- }
-
- if (looking_at(buf, &i, "\\ ")) {
- if (prevColor != ColorNormal) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(prevColor, TRUE);
- curColor = prevColor;
- }
- if (savingComment) {
- parse_pos = i - oldi;
- memcpy(parse, &buf[oldi], parse_pos);
- parse[parse_pos] = NULLCHAR;
- started = STARTED_COMMENT;
- } else {
- started = STARTED_CHATTER;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "Black Strength :") ||
- looking_at(buf, &i, "<<< style 10 board >>>") ||
- looking_at(buf, &i, "<10>") ||
- looking_at(buf, &i, "#@#")) {
- /* Wrong board style */
- loggedOn = TRUE;
- SendToICS(ics_prefix);
- SendToICS("set style 12\n");
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- continue;
- }
-
- if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
- ICSInitScript();
- have_sent_ICS_logon = 1;
- continue;
- }
-
- if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
- (looking_at(buf, &i, "\n<12> ") ||
- looking_at(buf, &i, "<12> "))) {
- loggedOn = TRUE;
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- next_out = i;
- started = STARTED_BOARD;
- parse_pos = 0;
- continue;
- }
-
- if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
- looking_at(buf, &i, "<b1> ")) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- next_out = i;
- started = STARTED_HOLDINGS;
- parse_pos = 0;
- continue;
- }
-
- if (looking_at(buf, &i, "* *vs. * *--- *")) {
- loggedOn = TRUE;
- /* Header for a move list -- first line */
-
- switch (ics_getting_history) {
- case H_FALSE:
- switch (gameMode) {
- case IcsIdle:
- case BeginningOfGame:
- /* User typed "moves" or "oldmoves" while we
- were idle. Pretend we asked for these
- moves and soak them up so user can step
- through them and/or save them.
- */
- Reset(FALSE, TRUE);
- gameMode = IcsObserving;
- ModeHighlight();
- ics_gamenum = -1;
- ics_getting_history = H_GOT_UNREQ_HEADER;
- break;
- case EditGame: /*?*/
- case EditPosition: /*?*/
- /* Should above feature work in these modes too? */
- /* For now it doesn't */
- ics_getting_history = H_GOT_UNWANTED_HEADER;
- break;
- default:
- ics_getting_history = H_GOT_UNWANTED_HEADER;
- break;
- }
- break;
- case H_REQUESTED:
- /* Is this the right one? */
- if (gameInfo.white && gameInfo.black &&
- strcmp(gameInfo.white, star_match[0]) == 0 &&
- strcmp(gameInfo.black, star_match[2]) == 0) {
- /* All is well */
- ics_getting_history = H_GOT_REQ_HEADER;
- }
- break;
- case H_GOT_REQ_HEADER:
- case H_GOT_UNREQ_HEADER:
- case H_GOT_UNWANTED_HEADER:
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: two headers", 0);
- ics_getting_history = H_FALSE;
- break;
- }
-
- /* Save player ratings into gameInfo if needed */
- if ((ics_getting_history == H_GOT_REQ_HEADER ||
- ics_getting_history == H_GOT_UNREQ_HEADER) &&
- (gameInfo.whiteRating == -1 ||
- gameInfo.blackRating == -1)) {
-
- gameInfo.whiteRating = string_to_rating(star_match[1]);
- gameInfo.blackRating = string_to_rating(star_match[3]);
- if (appData.debugMode)
- fprintf(debugFP, "Ratings from header: W %d, B %d\n",
- gameInfo.whiteRating, gameInfo.blackRating);
- }
- continue;
- }
-
- if (looking_at(buf, &i,
- "* * match, initial time: * minute*, increment: * second")) {
- /* Header for a move list -- second line */
- /* Initial board will follow if this is a wild game */
-
- if (gameInfo.event != NULL) free(gameInfo.event);
- sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
- gameInfo.event = StrSave(str);
- gameInfo.variant = StringToVariant(gameInfo.event);
- continue;
- }
-
- if (looking_at(buf, &i, "Move ")) {
- /* Beginning of a move list */
- switch (ics_getting_history) {
- case H_FALSE:
- /* Normally should not happen */
- /* Maybe user hit reset while we were parsing */
- break;
- case H_REQUESTED:
- /* Happens if we are ignoring a move list that is not
- * the one we just requested. Common if the user
- * tries to observe two games without turning off
- * getMoveList */
- break;
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: nested", 0);
- ics_getting_history = H_FALSE;
- break;
- case H_GOT_REQ_HEADER:
- ics_getting_history = H_GETTING_MOVES;
- started = STARTED_MOVES;
- parse_pos = 0;
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- }
- break;
- case H_GOT_UNREQ_HEADER:
- ics_getting_history = H_GETTING_MOVES;
- started = STARTED_MOVES_NOHIDE;
- parse_pos = 0;
- break;
- case H_GOT_UNWANTED_HEADER:
- ics_getting_history = H_FALSE;
- break;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "% ") ||
- ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
- && looking_at(buf, &i, "}*"))) {
- savingComment = FALSE;
- switch (started) {
- case STARTED_MOVES:
- case STARTED_MOVES_NOHIDE:
- memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
- parse[parse_pos + i - oldi] = NULLCHAR;
- ParseGameHistory(parse);
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- FeedMovesToProgram(&first, forwardMostMove);
- if (gameMode == IcsPlayingWhite) {
- if (WhiteOnMove(forwardMostMove)) {
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("black\n", &first);
- }
- SendTimeRemaining(&first, TRUE);
- }
- if (first.useColors) {
- SendToProgram("white\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- } else {
- if (first.usePlayother) {
- if (first.sendTime) {
- SendTimeRemaining(&first, TRUE);
- }
- SendToProgram("playother\n", &first);
- firstMove = FALSE;
- } else {
- firstMove = TRUE;
- }
- }
- } else if (gameMode == IcsPlayingBlack) {
- if (!WhiteOnMove(forwardMostMove)) {
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("white\n", &first);
- }
- SendTimeRemaining(&first, FALSE);
- }
- if (first.useColors) {
- SendToProgram("black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- } else {
- if (first.usePlayother) {
- if (first.sendTime) {
- SendTimeRemaining(&first, FALSE);
- }
- SendToProgram("playother\n", &first);
- firstMove = FALSE;
- } else {
- firstMove = TRUE;
- }
- }
- }
- }
-#endif
- if (gameMode == IcsObserving && ics_gamenum == -1) {
- /* Moves came from oldmoves or moves command
- while we weren't doing anything else.
- */
- currentMove = forwardMostMove;
- ClearHighlights();/*!!could figure this out*/
- flipView = appData.flipView;
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- sprintf(str, "%s vs. %s",
- gameInfo.white, gameInfo.black);
- DisplayTitle(str);
- gameMode = IcsIdle;
- } else {
- /* Moves were history of an active game */
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- }
- HistorySet(parseList, backwardMostMove,
- forwardMostMove, currentMove-1);
- DisplayMove(currentMove - 1);
- if (started == STARTED_MOVES) next_out = i;
- started = STARTED_NONE;
- ics_getting_history = H_FALSE;
- break;
-
- case STARTED_OBSERVE:
- started = STARTED_NONE;
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- break;
-
- default:
- break;
- }
- continue;
- }
-
- if ((started == STARTED_MOVES || started == STARTED_BOARD ||
- started == STARTED_HOLDINGS ||
- started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
- /* Accumulate characters in move list or board */
- parse[parse_pos++] = buf[i];
- }
-
- /* Start of game messages. Mostly we detect start of game
- when the first board image arrives. On some versions
- of the ICS, though, we need to do a "refresh" after starting
- to observe in order to get the current board right away. */
- if (looking_at(buf, &i, "Adding game * to observation list")) {
- started = STARTED_OBSERVE;
- continue;
- }
-
- /* Handle auto-observe */
- if (appData.autoObserve &&
- (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
- looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
- char *player;
- /* Choose the player that was highlighted, if any. */
- if (star_match[0][0] == '\033' ||
- star_match[1][0] != '\033') {
- player = star_match[0];
- } else {
- player = star_match[2];
- }
- sprintf(str, "%sobserve %s\n",
- ics_prefix, StripHighlightAndTitle(player));
- SendToICS(str);
-
- /* Save ratings from notify string */
- strcpy(player1Name, star_match[0]);
- player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, star_match[2]);
- player2Rating = string_to_rating(star_match[3]);
-
- if (appData.debugMode)
- fprintf(debugFP,
- "Ratings from 'Game notification:' %s %d, %s %d\n",
- player1Name, player1Rating,
- player2Name, player2Rating);
-
- continue;
- }
-
- /* Deal with automatic examine mode after a game,
- and with IcsObserving -> IcsExamining transition */
- if (looking_at(buf, &i, "Entering examine mode for game *") ||
- looking_at(buf, &i, "has made you an examiner of game *")) {
-
- int gamenum = atoi(star_match[0]);
- if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
- gamenum == ics_gamenum) {
- /* We were already playing or observing this game;
- no need to refetch history */
- gameMode = IcsExamining;
- if (pausing) {
- pauseExamForwardMostMove = forwardMostMove;
- } else if (currentMove < forwardMostMove) {
- ForwardInner(forwardMostMove);
- }
- } else {
- /* I don't think this case really can happen */
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- }
- continue;
- }
-
- /* Error messages */
- if (ics_user_moved) {
- if (looking_at(buf, &i, "Illegal move") ||
- looking_at(buf, &i, "Not a legal move") ||
- looking_at(buf, &i, "Your king is in check") ||
- looking_at(buf, &i, "It isn't your turn") ||
- looking_at(buf, &i, "It is not your move")) {
- /* Illegal move */
- ics_user_moved = 0;
- if (forwardMostMove > backwardMostMove) {
- currentMove = --forwardMostMove;
- DisplayMove(currentMove - 1); /* before DMError */
- DisplayMoveError("Illegal move (rejected by ICS)");
- DrawPosition(FALSE, boards[currentMove]);
- SwitchClocks();
- DisplayBothClocks();
- }
- continue;
- }
- }
-
- if (looking_at(buf, &i, "still have time") ||
- looking_at(buf, &i, "not out of time") ||
- looking_at(buf, &i, "either player is out of time") ||
- looking_at(buf, &i, "has timeseal; checking")) {
- /* We must have called his flag a little too soon */
- whiteFlag = blackFlag = FALSE;
- continue;
- }
-
- if (looking_at(buf, &i, "added * seconds to") ||
- looking_at(buf, &i, "seconds were added to")) {
- /* Update the clocks */
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- continue;
- }
-
- if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
- ics_clock_paused = TRUE;
- StopClocks();
- continue;
- }
-
- if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
- ics_clock_paused = FALSE;
- StartClocks();
- continue;
- }
-
- /* Grab player ratings from the Creating: message.
- Note we have to check for the special case when
- the ICS inserts things like [white] or [black]. */
- if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
- looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
- /* star_matches:
- 0 player 1 name (not necessarily white)
- 1 player 1 rating
- 2 empty, white, or black (IGNORED)
- 3 player 2 name (not necessarily black)
- 4 player 2 rating
-
- The names/ratings are sorted out when the game
- actually starts (below).
- */
- strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
- player1Rating = string_to_rating(star_match[1]);
- strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
- player2Rating = string_to_rating(star_match[4]);
-
- if (appData.debugMode)
- fprintf(debugFP,
- "Ratings from 'Creating:' %s %d, %s %d\n",
- player1Name, player1Rating,
- player2Name, player2Rating);
-
- continue;
- }
-
- /* Improved generic start/end-of-game messages */
- if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
- (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
- /* If tkind == 0: */
- /* star_match[0] is the game number */
- /* [1] is the white player's name */
- /* [2] is the black player's name */
- /* For end-of-game: */
- /* [3] is the reason for the game end */
- /* [4] is a PGN end game-token, preceded by " " */
- /* For start-of-game: */
- /* [3] begins with "Creating" or "Continuing" */
- /* [4] is " *" or empty (don't care). */
- int gamenum = atoi(star_match[0]);
- char *whitename, *blackname, *why, *endtoken;
- ChessMove endtype = (ChessMove) 0;
-
- if (tkind == 0) {
- whitename = star_match[1];
- blackname = star_match[2];
- why = star_match[3];
- endtoken = star_match[4];
- } else {
- whitename = star_match[1];
- blackname = star_match[3];
- why = star_match[5];
- endtoken = star_match[6];
- }
-
- /* Game start messages */
- if (strncmp(why, "Creating ", 9) == 0 ||
- strncmp(why, "Continuing ", 11) == 0) {
- gs_gamenum = gamenum;
- strcpy(gs_kind, strchr(why, ' ') + 1);
-#if ZIPPY
- if (appData.zippyPlay) {
- ZippyGameStart(whitename, blackname);
- }
-#endif /*ZIPPY*/
- continue;
- }
-
- /* Game end messages */
- if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
- ics_gamenum != gamenum) {
- continue;
- }
- while (endtoken[0] == ' ') endtoken++;
- switch (endtoken[0]) {
- case '*':
- default:
- endtype = GameUnfinished;
- break;
- case '0':
- endtype = BlackWins;
- break;
- case '1':
- if (endtoken[1] == '/')
- endtype = GameIsDrawn;
- else
- endtype = WhiteWins;
- break;
- }
- GameEnds(endtype, why, GE_ICS);
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- ZippyGameEnd(endtype, why);
- if (first.pr == NULL) {
- /* Start the next process early so that we'll
- be ready for the next challenge */
- StartChessProgram(&first);
- }
- /* Send "new" early, in case this command takes
- a long time to finish, so that we'll be ready
- for the next challenge. */
- Reset(TRUE, TRUE);
- }
-#endif /*ZIPPY*/
- continue;
- }
-
- if (looking_at(buf, &i, "Removing game * from observation") ||
- looking_at(buf, &i, "no longer observing game *") ||
- looking_at(buf, &i, "Game * (*) has no examiners")) {
- if (gameMode == IcsObserving &&
- atoi(star_match[0]) == ics_gamenum)
- {
- StopClocks();
- gameMode = IcsIdle;
- ics_gamenum = -1;
- ics_user_moved = FALSE;
- }
- continue;
- }
-
- if (looking_at(buf, &i, "no longer examining game *")) {
- if (gameMode == IcsExamining &&
- atoi(star_match[0]) == ics_gamenum)
- {
- gameMode = IcsIdle;
- ics_gamenum = -1;
- ics_user_moved = FALSE;
- }
- continue;
- }
-
- /* Advance leftover_start past any newlines we find,
- so only partial lines can get reparsed */
- if (looking_at(buf, &i, "\n")) {
- prevColor = curColor;
- if (curColor != ColorNormal) {
- if (oldi > next_out) {
- SendToPlayer(&buf[next_out], oldi - next_out);
- next_out = oldi;
- }
- Colorize(ColorNormal, FALSE);
- curColor = ColorNormal;
- }
- if (started == STARTED_BOARD) {
- started = STARTED_NONE;
- parse[parse_pos] = NULLCHAR;
- ParseBoard12(parse);
- ics_user_moved = 0;
-
- /* Send premove here */
- if (appData.premove) {
- char str[MSG_SIZ];
- if (currentMove == 0 &&
- gameMode == IcsPlayingWhite &&
- appData.premoveWhite) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveWhiteText);
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- SendToICS(str);
- } else if (currentMove == 1 &&
- gameMode == IcsPlayingBlack &&
- appData.premoveBlack) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveBlackText);
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- SendToICS(str);
- } else if (gotPremove) {
- gotPremove = 0;
- ClearPremoveHighlights();
- if (appData.debugMode)
- fprintf(debugFP, "Sending premove:\n");
- UserMoveEvent(premoveFromX, premoveFromY,
- premoveToX, premoveToY,
- premovePromoChar);
- }
- }
-
- /* Usually suppress following prompt */
- if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
- if (looking_at(buf, &i, "*% ")) {
- savingComment = FALSE;
- }
- }
- next_out = i;
- } else if (started == STARTED_HOLDINGS) {
- int gamenum;
- char new_piece[MSG_SIZ];
- started = STARTED_NONE;
- parse[parse_pos] = NULLCHAR;
- if (appData.debugMode)
- fprintf(debugFP, "Parsing holdings: %s\n", parse);
- if (sscanf(parse, " game %d", &gamenum) == 1 &&
- gamenum == ics_gamenum) {
- if (gameInfo.variant == VariantNormal) {
- gameInfo.variant = VariantCrazyhouse; /*temp guess*/
- /* Get a move list just to see the header, which
- will tell us whether this is really bug or zh */
- if (ics_getting_history == H_FALSE) {
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- }
- new_piece[0] = NULLCHAR;
- sscanf(parse, "game %d white [%s black [%s <- %s",
- &gamenum, white_holding, black_holding,
- new_piece);
- white_holding[strlen(white_holding)-1] = NULLCHAR;
- black_holding[strlen(black_holding)-1] = NULLCHAR;
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- ZippyHoldings(white_holding, black_holding,
- new_piece);
- }
-#endif /*ZIPPY*/
- if (tinyLayout || smallLayout) {
- char wh[16], bh[16];
- PackHolding(wh, white_holding);
- PackHolding(bh, black_holding);
- sprintf(str, "[%s-%s] %s-%s", wh, bh,
- gameInfo.white, gameInfo.black);
- } else {
- sprintf(str, "%s [%s] vs. %s [%s]",
- gameInfo.white, white_holding,
- gameInfo.black, black_holding);
- }
- DrawPosition(FALSE, NULL);
- DisplayTitle(str);
- }
- /* Suppress following prompt */
- if (looking_at(buf, &i, "*% ")) {
- savingComment = FALSE;
- }
- next_out = i;
- }
- continue;
- }
-
- i++; /* skip unparsed character and loop back */
- }
-
- if (started != STARTED_MOVES && started != STARTED_BOARD &&
- started != STARTED_HOLDINGS && i > next_out) {
- SendToPlayer(&buf[next_out], i - next_out);
- next_out = i;
- }
-
- leftover_len = buf_len - leftover_start;
- /* if buffer ends with something we couldn't parse,
- reparse it after appending the next read */
-
- } else if (count == 0) {
- RemoveInputSource(isr);
- DisplayFatalError("Connection closed by ICS", 0, 0);
- } else {
- DisplayFatalError("Error reading from ICS", error, 1);
- }
-}
-
-
-/* Board style 12 looks like this:
-
- <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0
-
- * The "<12> " is stripped before it gets to this routine. The two
- * trailing 0's (flip state and clock ticking) are later addition, and
- * some chess servers may not have them, or may have only the first.
- * Additional trailing fields may be added in the future.
- */
-
-#define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
-
-#define RELATION_OBSERVING_PLAYED 0
-#define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */
-#define RELATION_PLAYING_MYMOVE 1
-#define RELATION_PLAYING_NOTMYMOVE -1
-#define RELATION_EXAMINING 2
-#define RELATION_ISOLATED_BOARD -3
-#define RELATION_STARTING_POSITION -4 /* FICS only */
-
-void
-ParseBoard12(string)
- char *string;
-{
- GameMode newGameMode;
- int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
- int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
- int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
- char to_play, board_chars[72];
- char move_str[500], str[500], elapsed_time[500];
- char black[32], white[32];
- Board board;
- int prevMove = currentMove;
- int ticking = 2;
- ChessMove moveType;
- int fromX, fromY, toX, toY;
- char promoChar;
-
- fromX = fromY = toX = toY = -1;
-
- newGame = FALSE;
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsing board: %s\n", string);
-
- move_str[0] = NULLCHAR;
- elapsed_time[0] = NULLCHAR;
- n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
- &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
- &gamenum, white, black, &relation, &basetime, &increment,
- &white_stren, &black_stren, &white_time, &black_time,
- &moveNum, str, elapsed_time, move_str, &ics_flip,
- &ticking);
-
- if (n < 22) {
- sprintf(str, "Failed to parse board string:\n\"%s\"", string);
- DisplayError(str, 0);
- return;
- }
-
- /* Convert the move number to internal form */
- moveNum = (moveNum - 1) * 2;
- if (to_play == 'B') moveNum++;
- if (moveNum >= MAX_MOVES) {
- DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
- 0, 1);
- return;
- }
-
- switch (relation) {
- case RELATION_OBSERVING_PLAYED:
- case RELATION_OBSERVING_STATIC:
- if (gamenum == -1) {
- /* Old ICC buglet */
- relation = RELATION_OBSERVING_STATIC;
- }
- newGameMode = IcsObserving;
- break;
- case RELATION_PLAYING_MYMOVE:
- case RELATION_PLAYING_NOTMYMOVE:
- newGameMode =
- ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
- IcsPlayingWhite : IcsPlayingBlack;
- break;
- case RELATION_EXAMINING:
- newGameMode = IcsExamining;
- break;
- case RELATION_ISOLATED_BOARD:
- default:
- /* Just display this board. If user was doing something else,
- we will forget about it until the next board comes. */
- newGameMode = IcsIdle;
- break;
- case RELATION_STARTING_POSITION:
- newGameMode = gameMode;
- break;
- }
-
- /* Modify behavior for initial board display on move listing
- of wild games.
- */
- switch (ics_getting_history) {
- case H_FALSE:
- case H_REQUESTED:
- break;
- case H_GOT_REQ_HEADER:
- case H_GOT_UNREQ_HEADER:
- /* This is the initial position of the current game */
- gamenum = ics_gamenum;
- moveNum = 0; /* old ICS bug workaround */
- if (to_play == 'B') {
- startedFromSetupPosition = TRUE;
- blackPlaysFirst = TRUE;
- moveNum = 1;
- if (forwardMostMove == 0) forwardMostMove = 1;
- if (backwardMostMove == 0) backwardMostMove = 1;
- if (currentMove == 0) currentMove = 1;
- }
- newGameMode = gameMode;
- relation = RELATION_STARTING_POSITION; /* ICC needs this */
- break;
- case H_GOT_UNWANTED_HEADER:
- /* This is an initial board that we don't want */
- return;
- case H_GETTING_MOVES:
- /* Should not happen */
- DisplayError("Error gathering move list: extra board", 0);
- ics_getting_history = H_FALSE;
- return;
- }
-
- /* Take action if this is the first board of a new game, or of a
- different game than is currently being displayed. */
- if (gamenum != ics_gamenum || newGameMode != gameMode ||
- relation == RELATION_ISOLATED_BOARD) {
-
- /* Forget the old game and get the history (if any) of the new one */
- if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
- }
- newGame = TRUE;
- if (appData.autoRaiseBoard) BoardToTop();
- prevMove = -3;
- if (gamenum == -1) {
- newGameMode = IcsIdle;
- } else if (moveNum > 0 && newGameMode != IcsIdle &&
- appData.getMoveList) {
- /* Need to get game history */
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
-
- /* Initially flip the board to have black on the bottom if playing
- black or if the ICS flip flag is set, but let the user change
- it with the Flip View button. */
- flipView = appData.autoFlipView ?
- (newGameMode == IcsPlayingBlack) || ics_flip :
- appData.flipView;
-
- /* Done with values from previous mode; copy in new ones */
- gameMode = newGameMode;
- ModeHighlight();
- ics_gamenum = gamenum;
- if (gamenum == gs_gamenum) {
- int klen = strlen(gs_kind);
- if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
- sprintf(str, "ICS %s", gs_kind);
- gameInfo.event = StrSave(str);
- } else {
- gameInfo.event = StrSave("ICS game");
- }
- gameInfo.site = StrSave(appData.icsHost);
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave(white);
- gameInfo.black = StrSave(black);
- timeControl = basetime * 60 * 1000;
- timeControl_2 = 0;
- timeIncrement = increment * 1000;
- movesPerSession = 0;
- gameInfo.timeControl = TimeControlTagValue();
- gameInfo.variant = StringToVariant(gameInfo.event);
- gameInfo.outOfBook = NULL;
-
- /* Do we have the ratings? */
- if (strcmp(player1Name, white) == 0 &&
- strcmp(player2Name, black) == 0) {
- if (appData.debugMode)
- fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
- player1Rating, player2Rating);
- gameInfo.whiteRating = player1Rating;
- gameInfo.blackRating = player2Rating;
- } else if (strcmp(player2Name, white) == 0 &&
- strcmp(player1Name, black) == 0) {
- if (appData.debugMode)
- fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
- player2Rating, player1Rating);
- gameInfo.whiteRating = player2Rating;
- gameInfo.blackRating = player1Rating;
- }
- player1Name[0] = player2Name[0] = NULLCHAR;
-
- /* Silence shouts if requested */
- if (appData.quietPlay &&
- (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
- SendToICS(ics_prefix);
- SendToICS("set shout 0\n");
- }
- }
-
- /* Deal with midgame name changes */
- if (!newGame) {
- if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
- if (gameInfo.white) free(gameInfo.white);
- gameInfo.white = StrSave(white);
- }
- if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
- if (gameInfo.black) free(gameInfo.black);
- gameInfo.black = StrSave(black);
- }
- }
-
- /* Throw away game result if anything actually changes in examine mode */
- if (gameMode == IcsExamining && !newGame) {
- gameInfo.result = GameUnfinished;
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- }
-
- /* In pausing && IcsExamining mode, we ignore boards coming
- in if they are in a different variation than we are. */
- if (pauseExamInvalid) return;
- if (pausing && gameMode == IcsExamining) {
- if (moveNum <= pauseExamForwardMostMove) {
- pauseExamInvalid = TRUE;
- forwardMostMove = pauseExamForwardMostMove;
- return;
- }
- }
-
- /* Parse the board */
- for (k = 0; k < 8; k++)
- for (j = 0; j < 8; j++)
- board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
- CopyBoard(boards[moveNum], board);
- if (moveNum == 0) {
- startedFromSetupPosition =
- !CompareBoards(board, initialPosition);
- }
-
- if (ics_getting_history == H_GOT_REQ_HEADER ||
- ics_getting_history == H_GOT_UNREQ_HEADER) {
- /* This was an initial position from a move list, not
- the current position */
- return;
- }
-
- /* Update currentMove and known move number limits */
- newMove = newGame || moveNum > forwardMostMove;
- if (newGame) {
- forwardMostMove = backwardMostMove = currentMove = moveNum;
- if (gameMode == IcsExamining && moveNum == 0) {
- /* Workaround for ICS limitation: we are not told the wild
- type when starting to examine a game. But if we ask for
- the move list, the move list header will tell us */
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
- || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
- forwardMostMove = moveNum;
- if (!pausing || currentMove > forwardMostMove)
- currentMove = forwardMostMove;
- } else {
- /* New part of history that is not contiguous with old part */
- if (pausing && gameMode == IcsExamining) {
- pauseExamInvalid = TRUE;
- forwardMostMove = pauseExamForwardMostMove;
- return;
- }
- forwardMostMove = backwardMostMove = currentMove = moveNum;
- if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
- ics_getting_history = H_REQUESTED;
- sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
- SendToICS(str);
- }
- }
-
- /* Update the clocks */
- if (strchr(elapsed_time, '.')) {
- /* Time is in ms */
- timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
- timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
- } else {
- /* Time is in seconds */
- timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
- timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
- }
-
-
-#if ZIPPY
- if (appData.zippyPlay && newGame &&
- gameMode != IcsObserving && gameMode != IcsIdle &&
- gameMode != IcsExamining)
- ZippyFirstBoard(moveNum, basetime, increment);
-#endif
-
- /* Put the move on the move list, first converting
- to canonical algebraic form. */
- if (moveNum > 0) {
- if (moveNum <= backwardMostMove) {
- /* We don't know what the board looked like before
- this move. Punt. */
- strcpy(parseList[moveNum - 1], move_str);
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- moveList[moveNum - 1][0] = NULLCHAR;
- } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- (void) CoordsToAlgebraic(boards[moveNum - 1],
- PosFlags(moveNum - 1), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar,
- parseList[moveNum-1]);
- switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[moveNum - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[moveNum - 1], "#");
- break;
- }
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- /* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(moveList[moveNum - 1], currentMoveString);
- strcat(moveList[moveNum - 1], "\n");
- } else if (strcmp(move_str, "none") == 0) {
- /* Again, we don't know what the board looked like;
- this is really the start of the game. */
- parseList[moveNum - 1][0] = NULLCHAR;
- moveList[moveNum - 1][0] = NULLCHAR;
- backwardMostMove = moveNum;
- startedFromSetupPosition = TRUE;
- fromX = fromY = toX = toY = -1;
- } else {
- /* Move from ICS was illegal!? Punt. */
-#if 0
- if (appData.testLegality && appData.debugMode) {
- sprintf(str, "Illegal move \"%s\" from ICS", move_str);
- DisplayError(str, 0);
- }
-#endif
- strcpy(parseList[moveNum - 1], move_str);
- strcat(parseList[moveNum - 1], " ");
- strcat(parseList[moveNum - 1], elapsed_time);
- moveList[moveNum - 1][0] = NULLCHAR;
- fromX = fromY = toX = toY = -1;
- }
-
-#if ZIPPY
- /* Send move to chess program (BEFORE animating it). */
- if (appData.zippyPlay && !newGame && newMove &&
- (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
-
- if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
- (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
- if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, "Couldn't parse move \"%s\" from ICS",
- move_str);
- DisplayError(str, 0);
- } else {
- if (first.sendTime) {
- SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
- }
- SendMoveToProgram(moveNum - 1, &first);
- if (firstMove) {
- firstMove = FALSE;
- if (first.useColors) {
- SendToProgram(gameMode == IcsPlayingWhite ?
- "white\ngo\n" :
- "black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- first.maybeThinking = TRUE;
- }
- }
- } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
- if (moveList[moveNum - 1][0] == NULLCHAR) {
- sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
- DisplayError(str, 0);
- } else {
- SendMoveToProgram(moveNum - 1, &first);
- }
- }
- }
-#endif
- }
-
- if (moveNum > 0 && !gotPremove) {
- /* If move comes from a remote source, animate it. If it
- isn't remote, it will have already been animated. */
- if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
- AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
- }
- if (!pausing && appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
-
- /* Start the clocks */
- whiteFlag = blackFlag = FALSE;
- appData.clockMode = !(basetime == 0 && increment == 0);
- if (ticking == 0) {
- ics_clock_paused = TRUE;
- StopClocks();
- } else if (ticking == 1) {
- ics_clock_paused = FALSE;
- }
- if (gameMode == IcsIdle ||
- relation == RELATION_OBSERVING_STATIC ||
- relation == RELATION_EXAMINING ||
- ics_clock_paused)
- DisplayBothClocks();
- else
- StartClocks();
-
- /* Display opponents and material strengths */
- if (gameInfo.variant != VariantBughouse &&
- gameInfo.variant != VariantCrazyhouse) {
- if (tinyLayout || smallLayout) {
- sprintf(str, "%s(%d) %s(%d) {%d %d}",
- gameInfo.white, white_stren, gameInfo.black, black_stren,
- basetime, increment);
- } else {
- sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
- gameInfo.white, white_stren, gameInfo.black, black_stren,
- basetime, increment);
- }
- DisplayTitle(str);
- }
-
-
- /* Display the board */
- if (!pausing) {
-
- if (appData.premove)
- if (!gotPremove ||
- ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
- ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
- ClearPremoveHighlights();
-
- DrawPosition(FALSE, boards[currentMove]);
- DisplayMove(moveNum - 1);
- if (appData.ringBellAfterMoves && !ics_user_moved)
- RingBell();
- }
-
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-GetMoveListEvent()
-{
- char buf[MSG_SIZ];
- if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
- ics_getting_history = H_REQUESTED;
- sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
- SendToICS(buf);
- }
-}
-
-void
-AnalysisPeriodicEvent(force)
- int force;
-{
- if (((programStats.ok_to_send == 0 || programStats.line_is_book)
- && !force) || !appData.periodicUpdates)
- return;
-
- /* Send . command to Crafty to collect stats */
- SendToProgram(".\n", &first);
-
- /* Don't send another until we get a response (this makes
- us stop sending to old Crafty's which don't understand
- the "." command (sending illegal cmds resets node count & time,
- which looks bad)) */
- programStats.ok_to_send = 0;
-}
-
-void
-SendMoveToProgram(moveNum, cps)
- int moveNum;
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- if (cps->useUsermove) {
- SendToProgram("usermove ", cps);
- }
- if (cps->useSAN) {
- char *space;
- if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
- int len = space - parseList[moveNum];
- memcpy(buf, parseList[moveNum], len);
- buf[len++] = '\n';
- buf[len] = NULLCHAR;
- } else {
- sprintf(buf, "%s\n", parseList[moveNum]);
- }
- SendToProgram(buf, cps);
- } else {
- /* Added by Tord: Send castle moves in "O-O" in FRC games if required by
- * the engine. It would be nice to have a better way to identify castle
- * moves here. */
- if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {
- int fromX = moveList[moveNum][0] - 'a';
- int fromY = moveList[moveNum][1] - '1';
- int toX = moveList[moveNum][2] - 'a';
- int toY = moveList[moveNum][3] - '1';
- if((boards[currentMove][fromY][fromX] == WhiteKing
- && boards[currentMove][toY][toX] == WhiteRook)
- || (boards[currentMove][fromY][fromX] == BlackKing
- && boards[currentMove][toY][toX] == BlackRook)) {
- if(toX > fromX) SendToProgram("O-O\n", cps);
- else SendToProgram("O-O-O\n", cps);
- }
- else SendToProgram(moveList[moveNum], cps);
- }
- else SendToProgram(moveList[moveNum], cps);
- /* End of additions by Tord */
- }
-}
-
-void
-SendMoveToICS(moveType, fromX, fromY, toX, toY)
- ChessMove moveType;
- int fromX, fromY, toX, toY;
-{
- char user_move[MSG_SIZ];
-
- switch (moveType) {
- default:
- sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
- (int)moveType, fromX, fromY, toX, toY);
- DisplayError(user_move + strlen("say "), 0);
- break;
- case WhiteKingSideCastle:
- case BlackKingSideCastle:
- case WhiteQueenSideCastleWild:
- case BlackQueenSideCastleWild:
- /* PUSH Fabien */
- case WhiteHSideCastleFR:
- case BlackHSideCastleFR:
- /* POP Fabien */
- sprintf(user_move, "o-o\n");
- break;
- case WhiteQueenSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case BlackKingSideCastleWild:
- /* PUSH Fabien */
- case WhiteASideCastleFR:
- case BlackASideCastleFR:
- /* POP Fabien */
- sprintf(user_move, "o-o-o\n");
- break;
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- sprintf(user_move, "%c%c%c%c=%c\n",
- 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
- PieceToChar(PromoPiece(moveType)));
- break;
- case WhiteDrop:
- case BlackDrop:
- sprintf(user_move, "%c@%c%c\n",
- ToUpper(PieceToChar((ChessSquare) fromX)),
- 'a' + toX, '1' + toY);
- break;
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case IllegalMove: /* could be a variant we don't quite understand */
- sprintf(user_move, "%c%c%c%c\n",
- 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
- break;
- }
- SendToICS(user_move);
-}
-
-void
-CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
- int rf, ff, rt, ft;
- char promoChar;
- char move[7];
-{
- if (rf == DROP_RANK) {
- sprintf(move, "%c@%c%c\n",
- ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
- } else {
- if (promoChar == 'x' || promoChar == NULLCHAR) {
- sprintf(move, "%c%c%c%c\n",
- 'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
- } else {
- sprintf(move, "%c%c%c%c%c\n",
- 'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
- }
- }
-}
-
-void
-ProcessICSInitScript(f)
- FILE *f;
-{
- char buf[MSG_SIZ];
-
- while (fgets(buf, MSG_SIZ, f)) {
- SendToICSDelayed(buf,(long)appData.msLoginDelay);
- }
-
- fclose(f);
-}
-
-
-/* Parser for moves from gnuchess, ICS, or user typein box */
-Boolean
-ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
- char *move;
- int moveNum;
- ChessMove *moveType;
- int *fromX, *fromY, *toX, *toY;
- char *promoChar;
-{
- *moveType = yylexstr(moveNum, move);
- switch (*moveType) {
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case WhiteKingSideCastle:
- case WhiteQueenSideCastle:
- case BlackKingSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case WhiteQueenSideCastleWild:
- case BlackKingSideCastleWild:
- case BlackQueenSideCastleWild:
- /* Code added by Tord: */
- case WhiteHSideCastleFR:
- case WhiteASideCastleFR:
- case BlackHSideCastleFR:
- case BlackASideCastleFR:
- /* End of code added by Tord */
- case IllegalMove: /* bug or odd chess variant */
- *fromX = currentMoveString[0] - 'a';
- *fromY = currentMoveString[1] - '1';
- *toX = currentMoveString[2] - 'a';
- *toY = currentMoveString[3] - '1';
- *promoChar = currentMoveString[4];
- if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
- *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
- *fromX = *fromY = *toX = *toY = 0;
- return FALSE;
- }
- if (appData.testLegality) {
- return (*moveType != IllegalMove);
- } else {
- return !(fromX == fromY && toX == toY);
- }
-
- case WhiteDrop:
- case BlackDrop:
- *fromX = *moveType == WhiteDrop ?
- (int) CharToPiece(ToUpper(currentMoveString[0])) :
- (int) CharToPiece(ToLower(currentMoveString[0]));
- *fromY = DROP_RANK;
- *toX = currentMoveString[2] - 'a';
- *toY = currentMoveString[3] - '1';
- *promoChar = NULLCHAR;
- return TRUE;
-
- case AmbiguousMove:
- case ImpossibleMove:
- case (ChessMove) 0: /* end of file */
- case ElapsedTime:
- case Comment:
- case PGNTag:
- case NAG:
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- default:
- /* bug? */
- *fromX = *fromY = *toX = *toY = 0;
- *promoChar = NULLCHAR;
- return FALSE;
- }
-}
-
-/* [AS] FRC game initialization */
-static int FindEmptySquare( Board board, int n )
-{
- int i = 0;
-
- while( 1 ) {
- while( board[0][i] != EmptySquare ) i++;
- if( n == 0 )
- break;
- n--;
- i++;
- }
-
- return i;
-}
-
-static void ShuffleFRC( Board board )
-{
- int i;
-
- srand( time(0) );
-
- for( i=0; i<8; i++ ) {
- board[0][i] = EmptySquare;
- }
-
- board[0][(rand() % 4)*2 ] = WhiteBishop; /* On dark square */
- board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */
- board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;
- board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;
- board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;
- board[0][FindEmptySquare(board, 0)] = WhiteRook;
- board[0][FindEmptySquare(board, 0)] = WhiteKing;
- board[0][FindEmptySquare(board, 0)] = WhiteRook;
-
- for( i=0; i<8; i++ ) {
- board[7][i] = board[0][i] + BlackPawn - WhitePawn;
- }
-}
-
-static unsigned char FRC_KnightTable[10] = {
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33
-};
-
-static void SetupFRC( Board board, int pos_index )
-{
- int i;
- unsigned char knights;
-
- /* Bring the position index into a safe range (just in case...) */
- if( pos_index < 0 ) pos_index = 0;
-
- pos_index %= 960;
-
- /* Clear the board */
- for( i=0; i<8; i++ ) {
- board[0][i] = EmptySquare;
- }
-
- /* Place bishops and queen */
- board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */
- pos_index /= 4;
-
- board[0][ (pos_index % 4)*2 ] = WhiteBishop; /* On dark square */
- pos_index /= 4;
-
- board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;
- pos_index /= 6;
-
- /* Place knigths */
- knights = FRC_KnightTable[ pos_index ];
-
- board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;
- board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;
-
- /* Place rooks and king */
- board[0][ FindEmptySquare(board, 0) ] = WhiteRook;
- board[0][ FindEmptySquare(board, 0) ] = WhiteKing;
- board[0][ FindEmptySquare(board, 0) ] = WhiteRook;
-
- /* Mirror piece placement for black */
- for( i=0; i<8; i++ ) {
- board[7][i] = board[0][i] + BlackPawn - WhitePawn;
- }
-}
-
-void
-InitPosition(redraw)
- int redraw;
-{
- currentMove = forwardMostMove = backwardMostMove = 0;
-
- /* [AS] Initialize pv info list */
- {
- int i;
-
- for( i=0; i<MAX_MOVES; i++ ) {
- pvInfoList[i].depth = 0;
- }
- }
-
- switch (gameInfo.variant) {
- default:
- CopyBoard(boards[0], initialPosition);
- break;
- case VariantTwoKings:
- CopyBoard(boards[0], twoKingsPosition);
- startedFromSetupPosition = TRUE;
- break;
- case VariantWildCastle:
- CopyBoard(boards[0], initialPosition);
- /* !!?shuffle with kings guaranteed to be on d or e file */
- break;
- case VariantNoCastle:
- CopyBoard(boards[0], initialPosition);
- /* !!?unconstrained back-rank shuffle */
- break;
- case VariantFischeRandom:
- CopyBoard(boards[0], initialPosition);
- if( appData.defaultFrcPosition < 0 ) {
- ShuffleFRC( boards[0] );
- }
- else {
- SetupFRC( boards[0], appData.defaultFrcPosition );
- }
- break;
- }
-
- if (redraw)
- DrawPosition(TRUE, boards[currentMove]);
-}
-
-void
-SendBoard(cps, moveNum)
- ChessProgramState *cps;
- int moveNum;
-{
- char message[MSG_SIZ];
-
- if (cps->useSetboard) {
- char* fen = PositionToFEN(moveNum, cps->useFEN960);
- sprintf(message, "setboard %s\n", fen);
- SendToProgram(message, cps);
- free(fen);
-
- } else {
- ChessSquare *bp;
- int i, j;
- /* Kludge to set black to move, avoiding the troublesome and now
- * deprecated "black" command.
- */
- if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
-
- SendToProgram("edit\n", cps);
- SendToProgram("#\n", cps);
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- bp = &boards[moveNum][i][0];
- for (j = 0; j < BOARD_SIZE; j++, bp++) {
- if ((int) *bp < (int) BlackPawn) {
- sprintf(message, "%c%c%c\n", PieceToChar(*bp),
- 'a' + j, '1' + i);
- SendToProgram(message, cps);
- }
- }
- }
-
- SendToProgram("c\n", cps);
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- bp = &boards[moveNum][i][0];
- for (j = 0; j < BOARD_SIZE; j++, bp++) {
- if (((int) *bp != (int) EmptySquare)
- && ((int) *bp >= (int) BlackPawn)) {
- sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
- 'a' + j, '1' + i);
- SendToProgram(message, cps);
- }
- }
- }
-
- SendToProgram(".\n", cps);
- }
-}
-
-int
-IsPromotion(fromX, fromY, toX, toY)
- int fromX, fromY, toX, toY;
-{
- return gameMode != EditPosition &&
- fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
- ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
- (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
-}
-
-
-int
-PieceForSquare (x, y)
- int x;
- int y;
-{
- if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
- return -1;
- else
- return boards[currentMove][y][x];
-}
-
-int
-OKToStartUserMove(x, y)
- int x, y;
-{
- ChessSquare from_piece;
- int white_piece;
-
- if (matchMode) return FALSE;
- if (gameMode == EditPosition) return TRUE;
-
- if (x >= 0 && y >= 0)
- from_piece = boards[currentMove][y][x];
- else
- from_piece = EmptySquare;
-
- if (from_piece == EmptySquare) return FALSE;
-
- white_piece = (int)from_piece >= (int)WhitePawn &&
- (int)from_piece <= (int)WhiteKing;
-
- switch (gameMode) {
- case PlayFromGameFile:
- case AnalyzeFile:
- case TwoMachinesPlay:
- case EndOfGame:
- return FALSE;
-
- case IcsObserving:
- case IcsIdle:
- return FALSE;
-
- case MachinePlaysWhite:
- case IcsPlayingBlack:
- if (appData.zippyPlay) return FALSE;
- if (white_piece) {
- DisplayMoveError("You are playing Black");
- return FALSE;
- }
- break;
-
- case MachinePlaysBlack:
- case IcsPlayingWhite:
- if (appData.zippyPlay) return FALSE;
- if (!white_piece) {
- DisplayMoveError("You are playing White");
- return FALSE;
- }
- break;
-
- case EditGame:
- if (!white_piece && WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return FALSE;
- }
- if (white_piece && !WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return FALSE;
- }
- if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
- /* Editing correspondence game history */
- /* Could disallow this or prompt for confirmation */
- cmailOldMove = -1;
- }
- if (currentMove < forwardMostMove) {
- /* Discarding moves */
- /* Could prompt for confirmation here,
- but I don't think that's such a good idea */
- forwardMostMove = currentMove;
- }
- break;
-
- case BeginningOfGame:
- if (appData.icsActive) return FALSE;
- if (!appData.noChessProgram) {
- if (!white_piece) {
- DisplayMoveError("You are playing White");
- return FALSE;
- }
- }
- break;
-
- case Training:
- if (!white_piece && WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return FALSE;
- }
- if (white_piece && !WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return FALSE;
- }
- break;
-
- default:
- case IcsExamining:
- break;
- }
- if (currentMove != forwardMostMove && gameMode != AnalyzeMode
- && gameMode != AnalyzeFile && gameMode != Training) {
- DisplayMoveError("Displayed position is not current");
- return FALSE;
- }
- return TRUE;
-}
-
-FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
-int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
-int lastLoadGameUseList = FALSE;
-char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
-ChessMove lastLoadGameStart = (ChessMove) 0;
-
-
-void
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)
- int fromX, fromY, toX, toY;
- int promoChar;
-{
- ChessMove moveType;
-
- if (fromX < 0 || fromY < 0) return;
- if ((fromX == toX) && (fromY == toY)) {
- return;
- }
-
- /* Check if the user is playing in turn. This is complicated because we
- let the user "pick up" a piece before it is his turn. So the piece he
- tried to pick up may have been captured by the time he puts it down!
- Therefore we use the color the user is supposed to be playing in this
- test, not the color of the piece that is currently on the starting
- square---except in EditGame mode, where the user is playing both
- sides; fortunately there the capture race can't happen. (It can
- now happen in IcsExamining mode, but that's just too bad. The user
- will get a somewhat confusing message in that case.)
- */
-
- switch (gameMode) {
- case PlayFromGameFile:
- case AnalyzeFile:
- case TwoMachinesPlay:
- case EndOfGame:
- case IcsObserving:
- case IcsIdle:
- /* We switched into a game mode where moves are not accepted,
- perhaps while the mouse button was down. */
- return;
-
- case MachinePlaysWhite:
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return;
- }
- break;
-
- case MachinePlaysBlack:
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return;
- }
- break;
-
- case EditGame:
- case IcsExamining:
- case BeginningOfGame:
- case AnalyzeMode:
- case Training:
- if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
- (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return;
- }
- } else {
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return;
- }
- }
- break;
-
- case IcsPlayingBlack:
- /* User is moving for Black */
- if (WhiteOnMove(currentMove)) {
- if (!appData.premove) {
- DisplayMoveError("It is White's turn");
- } else if (toX >= 0 && toY >= 0) {
- premoveToX = toX;
- premoveToY = toY;
- premoveFromX = fromX;
- premoveFromY = fromY;
- premovePromoChar = promoChar;
- gotPremove = 1;
- if (appData.debugMode)
- fprintf(debugFP, "Got premove: fromX %d,"
- "fromY %d, toX %d, toY %d\n",
- fromX, fromY, toX, toY);
- }
- return;
- }
- break;
-
- case IcsPlayingWhite:
- /* User is moving for White */
- if (!WhiteOnMove(currentMove)) {
- if (!appData.premove) {
- DisplayMoveError("It is Black's turn");
- } else if (toX >= 0 && toY >= 0) {
- premoveToX = toX;
- premoveToY = toY;
- premoveFromX = fromX;
- premoveFromY = fromY;
- premovePromoChar = promoChar;
- gotPremove = 1;
- if (appData.debugMode)
- fprintf(debugFP, "Got premove: fromX %d,"
- "fromY %d, toX %d, toY %d\n",
- fromX, fromY, toX, toY);
- }
- return;
- }
- break;
-
- default:
- break;
-
- case EditPosition:
- if (toX == -2 || toY == -2) {
- boards[0][fromY][fromX] = EmptySquare;
- DrawPosition(FALSE, boards[currentMove]);
- } else if (toX >= 0 && toY >= 0) {
- boards[0][toY][toX] = boards[0][fromY][fromX];
- boards[0][fromY][fromX] = EmptySquare;
- DrawPosition(FALSE, boards[currentMove]);
- }
- return;
- }
-
- if (toX < 0 || toY < 0) return;
- userOfferedDraw = FALSE;
-
- if (appData.testLegality) {
- moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
- if (moveType == IllegalMove || moveType == ImpossibleMove) {
- DisplayMoveError("Illegal move");
- return;
- }
- } else {
- moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
- }
-
- if (gameMode == Training) {
- /* compare the move played on the board to the next move in the
- * game. If they match, display the move and the opponent's response.
- * If they don't match, display an error message.
- */
- int saveAnimate;
- Board testBoard;
- CopyBoard(testBoard, boards[currentMove]);
- ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
-
- if (CompareBoards(testBoard, boards[currentMove+1])) {
- ForwardInner(currentMove+1);
-
- /* Autoplay the opponent's response.
- * if appData.animate was TRUE when Training mode was entered,
- * the response will be animated.
- */
- saveAnimate = appData.animate;
- appData.animate = animateTraining;
- ForwardInner(currentMove+1);
- appData.animate = saveAnimate;
-
- /* check for the end of the game */
- if (currentMove >= forwardMostMove) {
- gameMode = PlayFromGameFile;
- ModeHighlight();
- SetTrainingModeOff();
- DisplayInformation("End of game");
- }
- } else {
- DisplayError("Incorrect move", 0);
- }
- return;
- }
-
- FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
-}
-
-/* Common tail of UserMoveEvent and DropMenuEvent */
-void
-FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
- ChessMove moveType;
- int fromX, fromY, toX, toY;
- /*char*/int promoChar;
-{
- /* Ok, now we know that the move is good, so we can kill
- the previous line in Analysis Mode */
- if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
- forwardMostMove = currentMove;
- }
-
- /* If we need the chess program but it's dead, restart it */
- ResurrectChessProgram();
-
- /* A user move restarts a paused game*/
- if (pausing)
- PauseEvent();
-
- thinkOutput[0] = NULLCHAR;
-
- MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
-
- if (gameMode == BeginningOfGame) {
- if (appData.noChessProgram) {
- gameMode = EditGame;
- SetGameInfo();
- } else {
- char buf[MSG_SIZ];
- gameMode = MachinePlaysBlack;
- SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
- DisplayTitle(buf);
- if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.white);
- SendToProgram(buf, &first);
- }
- }
- ModeHighlight();
- }
-
- /* Relay move to ICS or chess engine */
- if (appData.icsActive) {
- if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
- gameMode == IcsExamining) {
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
- ics_user_moved = 1;
- }
- } else {
- if (first.sendTime && (gameMode == BeginningOfGame ||
- gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack)) {
- SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
- }
- SendMoveToProgram(forwardMostMove-1, &first);
- if (gameMode != EditGame && gameMode != PlayFromGameFile) {
- first.maybeThinking = TRUE;
- }
- if (currentMove == cmailOldMove + 1) {
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
- }
- }
-
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
- switch (gameMode) {
- case EditGame:
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN)) {
- case MT_NONE:
- case MT_CHECK:
- break;
- case MT_CHECKMATE:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "Black mates", GE_PLAYER);
- } else {
- GameEnds(WhiteWins, "White mates", GE_PLAYER);
- }
- break;
- case MT_STALEMATE:
- GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
- break;
- }
- break;
-
- case MachinePlaysBlack:
- case MachinePlaysWhite:
- /* disable certain menu options while machine is thinking */
- SetMachineThinkingEnables();
- break;
-
- default:
- break;
- }
-}
-
-void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )
-{
- char * hint = lastHint;
- FrontEndProgramStats stats;
-
- stats.which = cps == &first ? 0 : 1;
- stats.depth = cpstats->depth;
- stats.nodes = cpstats->nodes;
- stats.score = cpstats->score;
- stats.time = cpstats->time;
- stats.pv = cpstats->movelist;
- stats.hint = lastHint;
- stats.an_move_index = 0;
- stats.an_move_count = 0;
-
- if( gameMode == AnalyzeMode || gameMode == AnalyzeFile ) {
- stats.hint = cpstats->move_name;
- stats.an_move_index = cpstats->nr_moves - cpstats->moves_left;
- stats.an_move_count = cpstats->nr_moves;
- }
-
- SetProgramStats( &stats );
-}
-
-void
-HandleMachineMove(message, cps)
- char *message;
- ChessProgramState *cps;
-{
- char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
- char realname[MSG_SIZ];
- int fromX, fromY, toX, toY;
- ChessMove moveType;
- char promoChar;
- char *p;
- int machineWhite;
-
- /*
- * Kludge to ignore BEL characters
- */
- while (*message == '\007') message++;
-
- /*
- * Look for book output
- */
- if (cps == &first && bookRequested) {
- if (message[0] == '\t' || message[0] == ' ') {
- /* Part of the book output is here; append it */
- strcat(bookOutput, message);
- strcat(bookOutput, " \n");
- return;
- } else if (bookOutput[0] != NULLCHAR) {
- /* All of book output has arrived; display it */
- char *p = bookOutput;
- while (*p != NULLCHAR) {
- if (*p == '\t') *p = ' ';
- p++;
- }
- DisplayInformation(bookOutput);
- bookRequested = FALSE;
- /* Fall through to parse the current output */
- }
- }
-
- /*
- * Look for machine move.
- */
- if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
- (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
- {
- /* This method is only useful on engines that support ping */
- if (cps->lastPing != cps->lastPong) {
- if (gameMode == BeginningOfGame) {
- /* Extra move from before last new; ignore */
- if (appData.debugMode) {
- fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
- }
- } else {
- if (appData.debugMode) {
- fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
- cps->which, gameMode);
- }
-
- SendToProgram("undo\n", cps);
- }
- return;
- }
-
- switch (gameMode) {
- case BeginningOfGame:
- /* Extra move from before last reset; ignore */
- if (appData.debugMode) {
- fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
- }
- return;
-
- case EndOfGame:
- case IcsIdle:
- default:
- /* Extra move after we tried to stop. The mode test is
- not a reliable way of detecting this problem, but it's
- the best we can do on engines that don't support ping.
- */
- if (appData.debugMode) {
- fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
- cps->which, gameMode);
- }
- SendToProgram("undo\n", cps);
- return;
-
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- machineWhite = TRUE;
- break;
-
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- machineWhite = FALSE;
- break;
-
- case TwoMachinesPlay:
- machineWhite = (cps->twoMachinesColor[0] == 'w');
- break;
- }
- if (WhiteOnMove(forwardMostMove) != machineWhite) {
- if (appData.debugMode) {
- fprintf(debugFP,
- "Ignoring move out of turn by %s, gameMode %d"
- ", forwardMost %d\n",
- cps->which, gameMode, forwardMostMove);
- }
- return;
- }
-
- if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- /* Machine move could not be parsed; ignore it. */
- sprintf(buf1, "Illegal move \"%s\" from %s machine",
- machineMove, cps->which);
- DisplayError(buf1, 0);
- if (gameMode == TwoMachinesPlay) {
- GameEnds(machineWhite ? BlackWins : WhiteWins,
- "Forfeit due to illegal move", GE_XBOARD);
- }
- return;
- }
-
- hintRequested = FALSE;
- lastHint[0] = NULLCHAR;
- bookRequested = FALSE;
- /* Program may be pondering now */
- cps->maybeThinking = TRUE;
- if (cps->sendTime == 2) cps->sendTime = 1;
- if (cps->offeredDraw) cps->offeredDraw--;
-
-#if ZIPPY
- if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
- first.initDone) {
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
- ics_user_moved = 1;
- }
-#endif
- /* currentMoveString is set as a side-effect of ParseOneMove */
- strcpy(machineMove, currentMoveString);
- strcat(machineMove, "\n");
- strcpy(moveList[forwardMostMove], machineMove);
-
- /* [AS] Save move info and clear stats for next move */
- pvInfoList[ forwardMostMove ].score = programStats.score;
- pvInfoList[ forwardMostMove ].depth = programStats.depth;
- pvInfoList[ forwardMostMove ].time = -1;
- ClearProgramStats();
- thinkOutput[0] = NULLCHAR;
- hiddenThinkOutputState = 0;
-
- MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
-
- /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
- if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {
- int count = 0;
-
- while( count < adjudicateLossPlies ) {
- int score = pvInfoList[ forwardMostMove - count - 1 ].score;
-
- if( count & 1 ) {
- score = -score; /* Flip score for winning side */
- }
-
- if( score > adjudicateLossThreshold ) {
- break;
- }
-
- count++;
- }
-
- if( count >= adjudicateLossPlies ) {
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication",
- GE_XBOARD );
-
- return;
- }
- }
-
- if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
- GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );
-
- return;
- }
-
- if (gameMode == TwoMachinesPlay) {
- if (cps->other->sendTime) {
- SendTimeRemaining(cps->other,
- cps->other->twoMachinesColor[0] == 'w');
- }
- SendMoveToProgram(forwardMostMove-1, cps->other);
- if (firstMove) {
- firstMove = FALSE;
- if (cps->other->useColors) {
- SendToProgram(cps->other->twoMachinesColor, cps->other);
- }
- SendToProgram("go\n", cps->other);
- }
- cps->other->maybeThinking = TRUE;
- }
-
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
- if (!pausing && appData.ringBellAfterMoves) {
- RingBell();
- }
-
- /*
- * Reenable menu items that were disabled while
- * machine was thinking
- */
- if (gameMode != TwoMachinesPlay)
- SetUserThinkingEnables();
-
- return;
- }
-
- /* Set special modes for chess engines. Later something general
- * could be added here; for now there is just one kludge feature,
- * needed because Crafty 15.10 and earlier don't ignore SIGINT
- * when "xboard" is given as an interactive command.
- */
- if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
- cps->useSigint = FALSE;
- cps->useSigterm = FALSE;
- }
-
- /*
- * Look for communication commands
- */
- if (!strncmp(message, "telluser ", 9)) {
- DisplayNote(message + 9);
- return;
- }
- if (!strncmp(message, "tellusererror ", 14)) {
- DisplayError(message + 14, 0);
- return;
- }
- if (!strncmp(message, "tellopponent ", 13)) {
- if (appData.icsActive) {
- if (loggedOn) {
- sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
- SendToICS(buf1);
- }
- } else {
- DisplayNote(message + 13);
- }
- return;
- }
- if (!strncmp(message, "tellothers ", 11)) {
- if (appData.icsActive) {
- if (loggedOn) {
- sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
- SendToICS(buf1);
- }
- }
- return;
- }
- if (!strncmp(message, "tellall ", 8)) {
- if (appData.icsActive) {
- if (loggedOn) {
- sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
- SendToICS(buf1);
- }
- } else {
- DisplayNote(message + 8);
- }
- return;
- }
- if (strncmp(message, "warning", 7) == 0) {
- /* Undocumented feature, use tellusererror in new code */
- DisplayError(message, 0);
- return;
- }
- if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
- strcpy(realname, cps->tidy);
- strcat(realname, " query");
- AskQuestion(realname, buf2, buf1, cps->pr);
- return;
- }
- /* Commands from the engine directly to ICS. We don't allow these to be
- * sent until we are logged on. Crafty kibitzes have been known to
- * interfere with the login process.
- */
- if (loggedOn) {
- if (!strncmp(message, "tellics ", 8)) {
- SendToICS(message + 8);
- SendToICS("\n");
- return;
- }
- if (!strncmp(message, "tellicsnoalias ", 15)) {
- SendToICS(ics_prefix);
- SendToICS(message + 15);
- SendToICS("\n");
- return;
- }
- /* The following are for backward compatibility only */
- if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
- !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
- SendToICS(ics_prefix);
- SendToICS(message);
- SendToICS("\n");
- return;
- }
- }
- if (strncmp(message, "feature ", 8) == 0) {
- ParseFeatures(message+8, cps);
- }
- if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
- return;
- }
- /*
- * If the move is illegal, cancel it and redraw the board.
- * Also deal with other error cases. Matching is rather loose
- * here to accommodate engines written before the spec.
- */
- if (strncmp(message + 1, "llegal move", 11) == 0 ||
- strncmp(message, "Error", 5) == 0) {
- if (StrStr(message, "name") ||
- StrStr(message, "rating") || StrStr(message, "?") ||
- StrStr(message, "result") || StrStr(message, "board") ||
- StrStr(message, "bk") || StrStr(message, "computer") ||
- StrStr(message, "variant") || StrStr(message, "hint") ||
- StrStr(message, "random") || StrStr(message, "depth") ||
- StrStr(message, "accepted")) {
- return;
- }
- if (StrStr(message, "protover")) {
- /* Program is responding to input, so it's apparently done
- initializing, and this error message indicates it is
- protocol version 1. So we don't need to wait any longer
- for it to initialize and send feature commands. */
- FeatureDone(cps, 1);
- cps->protocolVersion = 1;
- return;
- }
- cps->maybeThinking = FALSE;
-
- if (StrStr(message, "draw")) {
- /* Program doesn't have "draw" command */
- cps->sendDrawOffers = 0;
- return;
- }
- if (cps->sendTime != 1 &&
- (StrStr(message, "time") || StrStr(message, "otim"))) {
- /* Program apparently doesn't have "time" or "otim" command */
- cps->sendTime = 0;
- return;
- }
- if (StrStr(message, "analyze")) {
- cps->analysisSupport = FALSE;
- cps->analyzing = FALSE;
- Reset(FALSE, TRUE);
- sprintf(buf2, "%s does not support analysis", cps->tidy);
- DisplayError(buf2, 0);
- return;
- }
- if (StrStr(message, "(no matching move)st")) {
- /* Special kludge for GNU Chess 4 only */
- cps->stKludge = TRUE;
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- return;
- }
- if (StrStr(message, "(no matching move)sd")) {
- /* Special kludge for GNU Chess 4 only */
- cps->sdKludge = TRUE;
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- return;
- }
- if (!StrStr(message, "llegal")) return;
- if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
- gameMode == IcsIdle) return;
- if (forwardMostMove <= backwardMostMove) return;
-#if 0
- /* Following removed: it caused a bug where a real illegal move
- message in analyze mored would be ignored. */
- if (cps == &first && programStats.ok_to_send == 0) {
- /* Bogus message from Crafty responding to "." This filtering
- can miss some of the bad messages, but fortunately the bug
- is fixed in current Crafty versions, so it doesn't matter. */
- return;
- }
-#endif
- if (pausing) PauseEvent();
- if (gameMode == PlayFromGameFile) {
- /* Stop reading this game file */
- gameMode = EditGame;
- ModeHighlight();
- }
- currentMove = --forwardMostMove;
- DisplayMove(currentMove-1); /* before DisplayMoveError */
- SwitchClocks();
- DisplayBothClocks();
- sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",
- parseList[currentMove], cps->which);
- DisplayMoveError(buf1);
- DrawPosition(FALSE, boards[currentMove]);
- return;
- }
- if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
- /* Program has a broken "time" command that
- outputs a string not ending in newline.
- Don't use it. */
- cps->sendTime = 0;
- }
-
- /*
- * If chess program startup fails, exit with an error message.
- * Attempts to recover here are futile.
- */
- if ((StrStr(message, "unknown host") != NULL)
- || (StrStr(message, "No remote directory") != NULL)
- || (StrStr(message, "not found") != NULL)
- || (StrStr(message, "No such file") != NULL)
- || (StrStr(message, "can't alloc") != NULL)
- || (StrStr(message, "Permission denied") != NULL)) {
-
- cps->maybeThinking = FALSE;
- sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",
- cps->which, cps->program, cps->host, message);
- RemoveInputSource(cps->isr);
- DisplayFatalError(buf1, 0, 1);
- return;
- }
-
- /*
- * Look for hint output
- */
- if (sscanf(message, "Hint: %s", buf1) == 1) {
- if (cps == &first && hintRequested) {
- hintRequested = FALSE;
- if (ParseOneMove(buf1, forwardMostMove, &moveType,
- &fromX, &fromY, &toX, &toY, &promoChar)) {
- (void) CoordsToAlgebraic(boards[forwardMostMove],
- PosFlags(forwardMostMove), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar, buf1);
- sprintf(buf2, "Hint: %s", buf1);
- DisplayInformation(buf2);
- } else {
- /* Hint move could not be parsed!? */
- sprintf(buf2,
- "Illegal hint move \"%s\"\nfrom %s chess program",
- buf1, cps->which);
- DisplayError(buf2, 0);
- }
- } else {
- strcpy(lastHint, buf1);
- }
- return;
- }
-
- /*
- * Ignore other messages if game is not in progress
- */
- if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
- gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
-
- /*
- * look for win, lose, draw, or draw offer
- */
- if (strncmp(message, "1-0", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- GameEnds(WhiteWins, r, GE_ENGINE);
- return;
- } else if (strncmp(message, "0-1", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- /* Kludge for Arasan 4.1 bug */
- if (strcmp(r, "Black resigns") == 0) {
- GameEnds(WhiteWins, r, GE_ENGINE);
- return;
- }
- GameEnds(BlackWins, r, GE_ENGINE);
- return;
- } else if (strncmp(message, "1/2", 3) == 0) {
- char *p, *q, *r = "";
- p = strchr(message, '{');
- if (p) {
- q = strchr(p, '}');
- if (q) {
- *q = NULLCHAR;
- r = p + 1;
- }
- }
- GameEnds(GameIsDrawn, r, GE_ENGINE);
- return;
-
- } else if (strncmp(message, "White resign", 12) == 0) {
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- return;
- } else if (strncmp(message, "Black resign", 12) == 0) {
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- return;
- } else if (strncmp(message, "White", 5) == 0 &&
- message[5] != '(' &&
- StrStr(message, "Black") == NULL) {
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- return;
- } else if (strncmp(message, "Black", 5) == 0 &&
- message[5] != '(') {
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- return;
- } else if (strcmp(message, "resign") == 0 ||
- strcmp(message, "computer resigns") == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(BlackWins, "White resigns", GE_ENGINE);
- else
- GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "opponent mates", 14) == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- else
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "computer mates", 14) == 0) {
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- break;
- case TwoMachinesPlay:
- if (cps->twoMachinesColor[0] == 'w')
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- else
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- break;
- default:
- /* can't happen */
- break;
- }
- return;
- } else if (strncmp(message, "checkmate", 9) == 0) {
- if (WhiteOnMove(forwardMostMove)) {
- GameEnds(BlackWins, "Black mates", GE_ENGINE);
- } else {
- GameEnds(WhiteWins, "White mates", GE_ENGINE);
- }
- return;
- } else if (strstr(message, "Draw") != NULL ||
- strstr(message, "game is a draw") != NULL) {
- GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
- return;
- } else if (strstr(message, "offer") != NULL &&
- strstr(message, "draw") != NULL) {
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- /* Relay offer to ICS */
- SendToICS(ics_prefix);
- SendToICS("draw\n");
- }
-#endif
- cps->offeredDraw = 2; /* valid until this engine moves twice */
- if (gameMode == TwoMachinesPlay) {
- if (cps->other->offeredDraw) {
- GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
- } else {
- if (cps->other->sendDrawOffers) {
- SendToProgram("draw\n", cps->other);
- }
- }
- } else if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack) {
- if (userOfferedDraw) {
- DisplayInformation("Machine accepts your draw offer");
- GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
- } else {
- DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");
- }
- }
- }
-
-
- /*
- * Look for thinking output
- */
- if ( appData.showThinking) {
- int plylev, mvleft, mvtot, curscore, time;
- char mvname[MOVE_LEN];
- unsigned long nodes;
- char plyext;
- int ignore = FALSE;
- int prefixHint = FALSE;
- mvname[0] = NULLCHAR;
-
- switch (gameMode) {
- case MachinePlaysBlack:
- case IcsPlayingBlack:
- if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
- break;
- case MachinePlaysWhite:
- case IcsPlayingWhite:
- if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
- break;
- case AnalyzeMode:
- case AnalyzeFile:
- break;
- case TwoMachinesPlay:
- if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {
- ignore = TRUE;
- }
- break;
- default:
- ignore = TRUE;
- break;
- }
-
- if (!ignore) {
- buf1[0] = NULLCHAR;
- if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
- &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
-
- if (plyext != ' ' && plyext != '\t') {
- time *= 100;
- }
-
- /* [AS] Negate score if machine is playing black and reporting absolute scores */
- if( cps->scoreIsAbsolute &&
- ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )
- {
- curscore = -curscore;
- }
-
-
- programStats.depth = plylev;
- programStats.nodes = nodes;
- programStats.time = time;
- programStats.score = curscore;
- programStats.got_only_move = 0;
-
- /* Buffer overflow protection */
- if (buf1[0] != NULLCHAR) {
- if (strlen(buf1) >= sizeof(programStats.movelist)
- && appData.debugMode) {
- fprintf(debugFP,
- "PV is too long; using the first %d bytes.\n",
- sizeof(programStats.movelist) - 1);
- }
-
- safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
- } else {
- sprintf(programStats.movelist, " no PV\n");
- }
-
- if (programStats.seen_stat) {
- programStats.ok_to_send = 1;
- }
-
- if (strchr(programStats.movelist, '(') != NULL) {
- programStats.line_is_book = 1;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
- } else {
- programStats.line_is_book = 0;
- }
-
- SendProgramStatsToFrontend( cps, &programStats );
-
- /*
- [AS] Protect the thinkOutput buffer from overflow... this
- is only useful if buf1 hasn't overflowed first!
- */
- sprintf(thinkOutput, "[%d]%c%+.2f %s%s",
- plylev,
- (gameMode == TwoMachinesPlay ?
- ToUpper(cps->twoMachinesColor[0]) : ' '),
- ((double) curscore) / 100.0,
- prefixHint ? lastHint : "",
- prefixHint ? " " : "" );
-
- if( buf1[0] != NULLCHAR ) {
- unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;
-
- if( strlen(buf1) > max_len ) {
- if( appData.debugMode) {
- fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");
- }
- buf1[max_len+1] = '\0';
- }
-
- strcat( thinkOutput, buf1 );
- }
-
- if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- DisplayMove(currentMove - 1);
- DisplayAnalysis();
- }
- return;
-
- } else if ((p=StrStr(message, "(only move)")) != NULL) {
- /* crafty (9.25+) says "(only move) <move>"
- * if there is only 1 legal move
- */
- sscanf(p, "(only move) %s", buf1);
- sprintf(thinkOutput, "%s (only move)", buf1);
- sprintf(programStats.movelist, "%s (only move)", buf1);
- programStats.depth = 1;
- programStats.nr_moves = 1;
- programStats.moves_left = 1;
- programStats.nodes = 1;
- programStats.time = 1;
- programStats.got_only_move = 1;
-
- /* Not really, but we also use this member to
- mean "line isn't going to change" (Crafty
- isn't searching, so stats won't change) */
- programStats.line_is_book = 1;
-
- SendProgramStatsToFrontend( cps, &programStats );
-
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
- DisplayMove(currentMove - 1);
- DisplayAnalysis();
- }
- return;
- } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",
- &time, &nodes, &plylev, &mvleft,
- &mvtot, mvname) >= 5) {
- /* The stat01: line is from Crafty (9.29+) in response
- to the "." command */
- programStats.seen_stat = 1;
- cps->maybeThinking = TRUE;
-
- if (programStats.got_only_move || !appData.periodicUpdates)
- return;
-
- programStats.depth = plylev;
- programStats.time = time;
- programStats.nodes = nodes;
- programStats.moves_left = mvleft;
- programStats.nr_moves = mvtot;
- strcpy(programStats.move_name, mvname);
- programStats.ok_to_send = 1;
- programStats.movelist[0] = '\0';
-
- SendProgramStatsToFrontend( cps, &programStats );
-
- DisplayAnalysis();
- return;
-
- } else if (strncmp(message,"++",2) == 0) {
- /* Crafty 9.29+ outputs this */
- programStats.got_fail = 2;
- return;
-
- } else if (strncmp(message,"--",2) == 0) {
- /* Crafty 9.29+ outputs this */
- programStats.got_fail = 1;
- return;
-
- } else if (thinkOutput[0] != NULLCHAR &&
- strncmp(message, " ", 4) == 0) {
- unsigned message_len;
-
- p = message;
- while (*p && *p == ' ') p++;
-
- message_len = strlen( p );
-
- /* [AS] Avoid buffer overflow */
- if( sizeof(thinkOutput) - strlen(thinkOutput) - 1 > message_len ) {
- strcat(thinkOutput, " ");
- strcat(thinkOutput, p);
- }
-
- if( sizeof(programStats.movelist) - strlen(programStats.movelist) - 1 > message_len ) {
- strcat(programStats.movelist, " ");
- strcat(programStats.movelist, p);
- }
-
- if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
- DisplayMove(currentMove - 1);
- DisplayAnalysis();
- }
- return;
- }
- }
- else {
- buf1[0] = NULLCHAR;
-
- if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
- &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5)
- {
- ChessProgramStats cpstats;
-
- if (plyext != ' ' && plyext != '\t') {
- time *= 100;
- }
-
- /* [AS] Negate score if machine is playing black and reporting absolute scores */
- if( cps->scoreIsAbsolute && ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) ) {
- curscore = -curscore;
- }
-
- cpstats.depth = plylev;
- cpstats.nodes = nodes;
- cpstats.time = time;
- cpstats.score = curscore;
- cpstats.got_only_move = 0;
- cpstats.movelist[0] = '\0';
-
- if (buf1[0] != NULLCHAR) {
- safeStrCpy( cpstats.movelist, buf1, sizeof(cpstats.movelist) );
- }
-
- cpstats.ok_to_send = 0;
- cpstats.line_is_book = 0;
- cpstats.nr_moves = 0;
- cpstats.moves_left = 0;
-
- SendProgramStatsToFrontend( cps, &cpstats );
- }
- }
- }
-}
-
-
-/* Parse a game score from the character string "game", and
- record it as the history of the current game. The game
- score is NOT assumed to start from the standard position.
- The display is not updated in any way.
- */
-void
-ParseGameHistory(game)
- char *game;
-{
- ChessMove moveType;
- int fromX, fromY, toX, toY, boardIndex;
- char promoChar;
- char *p, *q;
- char buf[MSG_SIZ];
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsing game history: %s\n", game);
-
- if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
- gameInfo.site = StrSave(appData.icsHost);
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
-
- /* Parse out names of players */
- while (*game == ' ') game++;
- p = buf;
- while (*game != ' ') *p++ = *game++;
- *p = NULLCHAR;
- gameInfo.white = StrSave(buf);
- while (*game == ' ') game++;
- p = buf;
- while (*game != ' ' && *game != '\n') *p++ = *game++;
- *p = NULLCHAR;
- gameInfo.black = StrSave(buf);
-
- /* Parse moves */
- boardIndex = blackPlaysFirst ? 1 : 0;
- yynewstr(game);
- for (;;) {
- yyboardindex = boardIndex;
- moveType = (ChessMove) yylex();
- switch (moveType) {
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case NormalMove:
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case WhiteKingSideCastle:
- case WhiteQueenSideCastle:
- case BlackKingSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case WhiteQueenSideCastleWild:
- case BlackKingSideCastleWild:
- case BlackQueenSideCastleWild:
- /* PUSH Fabien */
- case WhiteHSideCastleFR:
- case WhiteASideCastleFR:
- case BlackHSideCastleFR:
- case BlackASideCastleFR:
- /* POP Fabien */
- case IllegalMove: /* maybe suicide chess, etc. */
- fromX = currentMoveString[0] - 'a';
- fromY = currentMoveString[1] - '1';
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = currentMoveString[4];
- break;
- case WhiteDrop:
- case BlackDrop:
- fromX = moveType == WhiteDrop ?
- (int) CharToPiece(ToUpper(currentMoveString[0])) :
- (int) CharToPiece(ToLower(currentMoveString[0]));
- fromY = DROP_RANK;
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = NULLCHAR;
- break;
- case AmbiguousMove:
- /* bug? */
- sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);
- DisplayError(buf, 0);
- return;
- case ImpossibleMove:
- /* bug? */
- sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);
- DisplayError(buf, 0);
- return;
- case (ChessMove) 0: /* end of file */
- if (boardIndex < backwardMostMove) {
- /* Oops, gap. How did that happen? */
- DisplayError("Gap in move list", 0);
- return;
- }
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- if (boardIndex > forwardMostMove) {
- forwardMostMove = boardIndex;
- }
- return;
- case ElapsedTime:
- if (boardIndex > (blackPlaysFirst ? 1 : 0)) {
- strcat(parseList[boardIndex-1], " ");
- strcat(parseList[boardIndex-1], yy_text);
- }
- continue;
- case Comment:
- case PGNTag:
- case NAG:
- default:
- /* ignore */
- continue;
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- case GameUnfinished:
- if (gameMode == IcsExamining) {
- if (boardIndex < backwardMostMove) {
- /* Oops, gap. How did that happen? */
- return;
- }
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- return;
- }
- gameInfo.result = moveType;
- p = strchr(yy_text, '{');
- if (p == NULL) p = strchr(yy_text, '(');
- if (p == NULL) {
- p = yy_text;
- if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
- } else {
- q = strchr(p, *p == '{' ? '}' : ')');
- if (q != NULL) *q = NULLCHAR;
- p++;
- }
- gameInfo.resultDetails = StrSave(p);
- continue;
- }
- if (boardIndex >= forwardMostMove &&
- !(gameMode == IcsObserving && ics_gamenum == -1)) {
- backwardMostMove = blackPlaysFirst ? 1 : 0;
- return;
- }
- (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
- EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
- parseList[boardIndex]);
- CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
- /* currentMoveString is set as a side-effect of yylex */
- strcpy(moveList[boardIndex], currentMoveString);
- strcat(moveList[boardIndex], "\n");
- boardIndex++;
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
- switch (MateTest(boards[boardIndex],
- PosFlags(boardIndex), EP_UNKNOWN)) {
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[boardIndex - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[boardIndex - 1], "#");
- break;
- }
- }
-}
-
-
-/* Apply a move to the given board */
-void
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)
- int fromX, fromY, toX, toY;
- int promoChar;
- Board board;
-{
- ChessSquare captured = board[toY][toX];
- if (fromY == DROP_RANK) {
- /* must be first */
- board[toY][toX] = (ChessSquare) fromX;
- } else if (fromX == toX && fromY == toY) {
- return;
- }
-
- /* Code added by Tord: */
- /* FRC castling assumed when king captures friendly rook. */
- else if (board[fromY][fromX] == WhiteKing &&
- board[toY][toX] == WhiteRook) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = EmptySquare;
- if(toX > fromX) {
- board[0][6] = WhiteKing; board[0][5] = WhiteRook;
- } else {
- board[0][2] = WhiteKing; board[0][3] = WhiteRook;
- }
- } else if (board[fromY][fromX] == BlackKing &&
- board[toY][toX] == BlackRook) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = EmptySquare;
- if(toX > fromX) {
- board[7][6] = BlackKing; board[7][5] = BlackRook;
- } else {
- board[7][2] = BlackKing; board[7][3] = BlackRook;
- }
- /* End of code added by Tord */
-
- } else if (fromY == 0 && fromX == 4
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 6) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][7] = EmptySquare;
- board[toY][5] = WhiteRook;
- } else if (fromY == 0 && fromX == 4
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 2) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][0] = EmptySquare;
- board[toY][3] = WhiteRook;
- } else if (fromY == 0 && fromX == 3
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 5) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][7] = EmptySquare;
- board[toY][4] = WhiteRook;
- } else if (fromY == 0 && fromX == 3
- && board[fromY][fromX] == WhiteKing
- && toY == 0 && toX == 1) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhiteKing;
- board[fromY][0] = EmptySquare;
- board[toY][2] = WhiteRook;
- } else if (board[fromY][fromX] == WhitePawn
- && toY == 7) {
- /* white pawn promotion */
- board[7][toX] = CharToPiece(ToUpper(promoChar));
- if (board[7][toX] == EmptySquare) {
- board[7][toX] = WhiteQueen;
- }
- board[fromY][fromX] = EmptySquare;
- } else if ((fromY == 4)
- && (toX != fromX)
- && (board[fromY][fromX] == WhitePawn)
- && (board[toY][toX] == EmptySquare)) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhitePawn;
- captured = board[toY - 1][toX];
- board[toY - 1][toX] = EmptySquare;
- } else if (fromY == 7 && fromX == 4
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 6) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][7] = EmptySquare;
- board[toY][5] = BlackRook;
- } else if (fromY == 7 && fromX == 4
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 2) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][0] = EmptySquare;
- board[toY][3] = BlackRook;
- } else if (fromY == 7 && fromX == 3
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 5) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][7] = EmptySquare;
- board[toY][4] = BlackRook;
- } else if (fromY == 7 && fromX == 3
- && board[fromY][fromX] == BlackKing
- && toY == 7 && toX == 1) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackKing;
- board[fromY][0] = EmptySquare;
- board[toY][2] = BlackRook;
- } else if (board[fromY][fromX] == BlackPawn
- && toY == 0) {
- /* black pawn promotion */
- board[0][toX] = CharToPiece(ToLower(promoChar));
- if (board[0][toX] == EmptySquare) {
- board[0][toX] = BlackQueen;
- }
- board[fromY][fromX] = EmptySquare;
- } else if ((fromY == 3)
- && (toX != fromX)
- && (board[fromY][fromX] == BlackPawn)
- && (board[toY][toX] == EmptySquare)) {
- board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackPawn;
- captured = board[toY + 1][toX];
- board[toY + 1][toX] = EmptySquare;
- } else {
- board[toY][toX] = board[fromY][fromX];
- board[fromY][fromX] = EmptySquare;
- }
- if (gameInfo.variant == VariantCrazyhouse) {
-#if 0
- /* !!A lot more code needs to be written to support holdings */
- if (fromY == DROP_RANK) {
- /* Delete from holdings */
- if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
- }
- if (captured != EmptySquare) {
- /* Add to holdings */
- if (captured < BlackPawn) {
- holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
- } else {
- holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
- }
- }
-#endif
- } else if (gameInfo.variant == VariantAtomic) {
- if (captured != EmptySquare) {
- int y, x;
- for (y = toY-1; y <= toY+1; y++) {
- for (x = toX-1; x <= toX+1; x++) {
- if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
- board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
- board[y][x] = EmptySquare;
- }
- }
- }
- board[toY][toX] = EmptySquare;
- }
- }
-}
-
-/* Updates forwardMostMove */
-void
-MakeMove(fromX, fromY, toX, toY, promoChar)
- int fromX, fromY, toX, toY;
- int promoChar;
-{
- forwardMostMove++;
- if (forwardMostMove >= MAX_MOVES) {
- DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
- 0, 1);
- return;
- }
- SwitchClocks();
- timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove] = blackTimeRemaining;
- if (commentList[forwardMostMove] != NULL) {
- free(commentList[forwardMostMove]);
- commentList[forwardMostMove] = NULL;
- }
- CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);
- gameInfo.result = GameUnfinished;
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- }
- CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
- moveList[forwardMostMove - 1]);
- (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
- PosFlags(forwardMostMove - 1), EP_UNKNOWN,
- fromY, fromX, toY, toX, promoChar,
- parseList[forwardMostMove - 1]);
- switch (MateTest(boards[forwardMostMove],
- PosFlags(forwardMostMove), EP_UNKNOWN)){
- case MT_NONE:
- case MT_STALEMATE:
- default:
- break;
- case MT_CHECK:
- strcat(parseList[forwardMostMove - 1], "+");
- break;
- case MT_CHECKMATE:
- strcat(parseList[forwardMostMove - 1], "#");
- break;
- }
-}
-
-/* Updates currentMove if not pausing */
-void
-ShowMove(fromX, fromY, toX, toY)
-{
- int instant = (gameMode == PlayFromGameFile) ?
- (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
- if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
- if (!instant) {
- if (forwardMostMove == currentMove + 1) {
- AnimateMove(boards[forwardMostMove - 1],
- fromX, fromY, toX, toY);
- }
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
- currentMove = forwardMostMove;
- }
-
- if (instant) return;
-
- DisplayMove(currentMove - 1);
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-}
-
-
-void
-InitChessProgram(cps)
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- if (appData.noChessProgram) return;
- hintRequested = FALSE;
- bookRequested = FALSE;
- SendToProgram(cps->initString, cps);
- if (gameInfo.variant != VariantNormal &&
- gameInfo.variant != VariantLoadable) {
- char *v = VariantName(gameInfo.variant);
- if (StrStr(cps->variants, v) == NULL) {
- sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);
- DisplayFatalError(buf, 0, 1);
- return;
- }
- sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
- SendToProgram(buf, cps);
- }
- if (cps->sendICS) {
- sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
- SendToProgram(buf, cps);
- }
- cps->maybeThinking = FALSE;
- cps->offeredDraw = 0;
- if (!appData.icsActive) {
- SendTimeControl(cps, movesPerSession, timeControl,
- timeIncrement, appData.searchDepth,
- searchTime);
- }
- if (appData.showThinking) {
- SendToProgram("post\n", cps);
- }
- SendToProgram("hard\n", cps);
- if (!appData.ponderNextMove) {
- /* Warning: "easy" is a toggle in GNU Chess, so don't send
- it without being sure what state we are in first. "hard"
- is not a toggle, so that one is OK.
- */
- SendToProgram("easy\n", cps);
- }
- if (cps->usePing) {
- sprintf(buf, "ping %d\n", ++cps->lastPing);
- SendToProgram(buf, cps);
- }
- cps->initDone = TRUE;
-}
-
-
-void
-StartChessProgram(cps)
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- int err;
-
- if (appData.noChessProgram) return;
- cps->initDone = FALSE;
-
- if (strcmp(cps->host, "localhost") == 0) {
- err = StartChildProcess(cps->program, cps->dir, &cps->pr);
- } else if (*appData.remoteShell == NULLCHAR) {
- err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
- } else {
- if (*appData.remoteUser == NULLCHAR) {
- sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
- cps->program);
- } else {
- sprintf(buf, "%s %s -l %s %s", appData.remoteShell,
- cps->host, appData.remoteUser, cps->program);
- }
- err = StartChildProcess(buf, "", &cps->pr);
- }
-
- if (err != 0) {
- sprintf(buf, "Startup failure on '%s'", cps->program);
- DisplayFatalError(buf, err, 1);
- cps->pr = NoProc;
- cps->isr = NULL;
- return;
- }
-
- cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
- if (cps->protocolVersion > 1) {
- sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
- SendToProgram(buf, cps);
- } else {
- SendToProgram("xboard\n", cps);
- }
-}
-
-
-void
-TwoMachinesEventIfReady P((void))
-{
- if (first.lastPing != first.lastPong) {
- DisplayMessage("", "Waiting for first chess program");
- ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
- return;
- }
- if (second.lastPing != second.lastPong) {
- DisplayMessage("", "Waiting for second chess program");
- ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
- return;
- }
- ThawUI();
- TwoMachinesEvent();
-}
-
-void
-NextMatchGame P((void))
-{
- Reset(FALSE, TRUE);
- if (*appData.loadGameFile != NULLCHAR) {
- LoadGameFromFile(appData.loadGameFile,
- appData.loadGameIndex,
- appData.loadGameFile, FALSE);
- } else if (*appData.loadPositionFile != NULLCHAR) {
- LoadPositionFromFile(appData.loadPositionFile,
- appData.loadPositionIndex,
- appData.loadPositionFile);
- }
- TwoMachinesEventIfReady();
-}
-
-void UserAdjudicationEvent( int result )
-{
- ChessMove gameResult = GameIsDrawn;
-
- if( result > 0 ) {
- gameResult = WhiteWins;
- }
- else if( result < 0 ) {
- gameResult = BlackWins;
- }
-
- if( gameMode == TwoMachinesPlay ) {
- GameEnds( gameResult, "User adjudication", GE_XBOARD );
- }
-}
-
-
-void
-GameEnds(result, resultDetails, whosays)
- ChessMove result;
- char *resultDetails;
- int whosays;
-{
- GameMode nextGameMode;
- int isIcsGame;
-
- if (appData.debugMode) {
- fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
- result, resultDetails ? resultDetails : "(null)", whosays);
- }
-
- if (appData.icsActive && whosays == GE_ENGINE) {
- /* If we are playing on ICS, the server decides when the
- game is over, but the engine can offer to draw, claim
- a draw, or resign.
- */
-#if ZIPPY
- if (appData.zippyPlay && first.initDone) {
- if (result == GameIsDrawn) {
- /* In case draw still needs to be claimed */
- SendToICS(ics_prefix);
- SendToICS("draw\n");
- } else if (StrCaseStr(resultDetails, "resign")) {
- SendToICS(ics_prefix);
- SendToICS("resign\n");
- }
- }
-#endif
- return;
- }
-
- /* If we're loading the game from a file, stop */
- if (whosays == GE_FILE) {
- (void) StopLoadGameTimer();
- gameFileFP = NULL;
- }
-
- /* Cancel draw offers */
- first.offeredDraw = second.offeredDraw = 0;
-
- /* If this is an ICS game, only ICS can really say it's done;
- if not, anyone can. */
- isIcsGame = (gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == IcsObserving ||
- gameMode == IcsExamining);
-
- if (!isIcsGame || whosays == GE_ICS) {
- /* OK -- not an ICS game, or ICS said it was done */
- StopClocks();
- if (!isIcsGame && !appData.noChessProgram)
- SetUserThinkingEnables();
-
- if (resultDetails != NULL) {
- gameInfo.result = result;
- gameInfo.resultDetails = StrSave(resultDetails);
-
- /* Tell program how game ended in case it is learning */
- if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == BeginningOfGame) {
- char buf[MSG_SIZ];
- sprintf(buf, "result %s {%s}\n", PGNResult(result),
- resultDetails);
- if (first.pr != NoProc) {
- SendToProgram(buf, &first);
- }
- if (second.pr != NoProc &&
- gameMode == TwoMachinesPlay) {
- SendToProgram(buf, &second);
- }
- }
-
- /* display last move only if game was not loaded from file */
- if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
- DisplayMove(currentMove - 1);
-
- if (forwardMostMove != 0) {
- if (gameMode != PlayFromGameFile && gameMode != EditGame) {
- if (*appData.saveGameFile != NULLCHAR) {
- SaveGameToFile(appData.saveGameFile, TRUE);
- } else if (appData.autoSaveGames) {
- AutoSaveGame();
- }
- if (*appData.savePositionFile != NULLCHAR) {
- SavePositionToFile(appData.savePositionFile);
- }
- }
- }
- }
-
- if (appData.icsActive) {
- if (appData.quietPlay &&
- (gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack)) {
- SendToICS(ics_prefix);
- SendToICS("set shout 1\n");
- }
- nextGameMode = IcsIdle;
- ics_user_moved = FALSE;
- /* clean up premove. It's ugly when the game has ended and the
- * premove highlights are still on the board.
- */
- if (gotPremove) {
- gotPremove = FALSE;
- ClearPremoveHighlights();
- DrawPosition(FALSE, boards[currentMove]);
- }
- if (whosays == GE_ICS) {
- switch (result) {
- case WhiteWins:
- if (gameMode == IcsPlayingWhite)
- PlayIcsWinSound();
- else if(gameMode == IcsPlayingBlack)
- PlayIcsLossSound();
- break;
- case BlackWins:
- if (gameMode == IcsPlayingBlack)
- PlayIcsWinSound();
- else if(gameMode == IcsPlayingWhite)
- PlayIcsLossSound();
- break;
- case GameIsDrawn:
- PlayIcsDrawSound();
- break;
- default:
- PlayIcsUnfinishedSound();
- }
- }
- } else if (gameMode == EditGame ||
- gameMode == PlayFromGameFile ||
- gameMode == AnalyzeMode ||
- gameMode == AnalyzeFile) {
- nextGameMode = gameMode;
- } else {
- nextGameMode = EndOfGame;
- }
- pausing = FALSE;
- ModeHighlight();
- } else {
- nextGameMode = gameMode;
- }
-
- if (appData.noChessProgram) {
- gameMode = nextGameMode;
- ModeHighlight();
- return;
- }
-
- if (first.reuse) {
- /* Put first chess program into idle state */
- if (first.pr != NoProc &&
- (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay ||
- gameMode == IcsPlayingWhite ||
- gameMode == IcsPlayingBlack ||
- gameMode == BeginningOfGame)) {
- SendToProgram("force\n", &first);
- if (first.usePing) {
- char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++first.lastPing);
- SendToProgram(buf, &first);
- }
- }
- } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
- /* Kill off first chess program */
- if (first.isr != NULL)
- RemoveInputSource(first.isr);
- first.isr = NULL;
-
- if (first.pr != NoProc) {
- ExitAnalyzeMode();
- DoSleep( appData.delayBeforeQuit );
- SendToProgram("quit\n", &first);
- DoSleep( appData.delayAfterQuit );
- DestroyChildProcess(first.pr, first.useSigterm);
- }
- first.pr = NoProc;
- }
- if (second.reuse) {
- /* Put second chess program into idle state */
- if (second.pr != NoProc &&
- gameMode == TwoMachinesPlay) {
- SendToProgram("force\n", &second);
- if (second.usePing) {
- char buf[MSG_SIZ];
- sprintf(buf, "ping %d\n", ++second.lastPing);
- SendToProgram(buf, &second);
- }
- }
- } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
- /* Kill off second chess program */
- if (second.isr != NULL)
- RemoveInputSource(second.isr);
- second.isr = NULL;
-
- if (second.pr != NoProc) {
- DoSleep( appData.delayBeforeQuit );
- SendToProgram("quit\n", &second);
- DoSleep( appData.delayAfterQuit );
- DestroyChildProcess(second.pr, second.useSigterm);
- }
- second.pr = NoProc;
- }
-
- if (matchMode && gameMode == TwoMachinesPlay) {
- switch (result) {
- case WhiteWins:
- if (first.twoMachinesColor[0] == 'w') {
- first.matchWins++;
- } else {
- second.matchWins++;
- }
- break;
- case BlackWins:
- if (first.twoMachinesColor[0] == 'b') {
- first.matchWins++;
- } else {
- second.matchWins++;
- }
- break;
- default:
- break;
- }
- if (matchGame < appData.matchGames) {
- char *tmp;
- tmp = first.twoMachinesColor;
- first.twoMachinesColor = second.twoMachinesColor;
- second.twoMachinesColor = tmp;
- gameMode = nextGameMode;
- matchGame++;
- ScheduleDelayedEvent(NextMatchGame, 10000);
- return;
- } else {
- char buf[MSG_SIZ];
- gameMode = nextGameMode;
- sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",
- first.tidy, second.tidy,
- first.matchWins, second.matchWins,
- appData.matchGames - (first.matchWins + second.matchWins));
- DisplayFatalError(buf, 0, 0);
- }
- }
- if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
- !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
- ExitAnalyzeMode();
- gameMode = nextGameMode;
- ModeHighlight();
-}
-
-/* Assumes program was just initialized (initString sent).
- Leaves program in force mode. */
-void
-FeedMovesToProgram(cps, upto)
- ChessProgramState *cps;
- int upto;
-{
- int i;
-
- if (appData.debugMode)
- fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
- startedFromSetupPosition ? "position and " : "",
- backwardMostMove, upto, cps->which);
- SendToProgram("force\n", cps);
- if (startedFromSetupPosition) {
- SendBoard(cps, backwardMostMove);
- }
- for (i = backwardMostMove; i < upto; i++) {
- SendMoveToProgram(i, cps);
- }
-}
-
-
-void
-ResurrectChessProgram()
-{
- /* The chess program may have exited.
- If so, restart it and feed it all the moves made so far. */
-
- if (appData.noChessProgram || first.pr != NoProc) return;
-
- StartChessProgram(&first);
- InitChessProgram(&first);
- FeedMovesToProgram(&first, currentMove);
-
- if (!first.sendTime) {
- /* can't tell gnuchess what its clock should read,
- so we bow to its notion. */
- ResetClocks();
- timeRemaining[0][currentMove] = whiteTimeRemaining;
- timeRemaining[1][currentMove] = blackTimeRemaining;
- }
-
- if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
- first.analysisSupport) {
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- }
-}
-
-/*
- * Button procedures
- */
-void
-Reset(redraw, init)
- int redraw, init;
-{
- int i;
-
- if (appData.debugMode) {
- fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
- redraw, init, gameMode);
- }
-
- pausing = pauseExamInvalid = FALSE;
- startedFromSetupPosition = blackPlaysFirst = FALSE;
- firstMove = TRUE;
- whiteFlag = blackFlag = FALSE;
- userOfferedDraw = FALSE;
- hintRequested = bookRequested = FALSE;
- first.maybeThinking = FALSE;
- second.maybeThinking = FALSE;
- thinkOutput[0] = NULLCHAR;
- lastHint[0] = NULLCHAR;
- ClearGameInfo(&gameInfo);
- gameInfo.variant = StringToVariant(appData.variant);
- ics_user_moved = ics_clock_paused = FALSE;
- ics_getting_history = H_FALSE;
- ics_gamenum = -1;
- white_holding[0] = black_holding[0] = NULLCHAR;
- ClearProgramStats();
-
- ResetFrontEnd();
- ClearHighlights();
- flipView = appData.flipView;
- ClearPremoveHighlights();
- gotPremove = FALSE;
- alarmSounded = FALSE;
-
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
- ExitAnalyzeMode();
- gameMode = BeginningOfGame;
- ModeHighlight();
- InitPosition(redraw);
- for (i = 0; i < MAX_MOVES; i++) {
- if (commentList[i] != NULL) {
- free(commentList[i]);
- commentList[i] = NULL;
- }
- }
- ResetClocks();
- timeRemaining[0][0] = whiteTimeRemaining;
- timeRemaining[1][0] = blackTimeRemaining;
- if (first.pr == NULL) {
- StartChessProgram(&first);
- }
- if (init) InitChessProgram(&first);
- DisplayTitle("");
- DisplayMessage("", "");
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-AutoPlayGameLoop()
-{
- for (;;) {
- if (!AutoPlayOneMove())
- return;
- if (matchMode || appData.timeDelay == 0)
- continue;
- if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
- return;
- StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
- break;
- }
-}
-
-
-int
-AutoPlayOneMove()
-{
- int fromX, fromY, toX, toY;
-
- if (appData.debugMode) {
- fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
- }
-
- if (gameMode != PlayFromGameFile)
- return FALSE;
-
- if (currentMove >= forwardMostMove) {
- gameMode = EditGame;
- ModeHighlight();
-
- /* [AS] Clear current move marker at the end of a game */
- /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */
-
- return FALSE;
- }
-
- toX = moveList[currentMove][2] - 'a';
- toY = moveList[currentMove][3] - '1';
-
- if (moveList[currentMove][1] == '@') {
- if (appData.highlightLastMove) {
- SetHighlights(-1, -1, toX, toY);
- }
- } else {
- fromX = moveList[currentMove][0] - 'a';
- fromY = moveList[currentMove][1] - '1';
-
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
-
- AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
-
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
- DisplayMove(currentMove);
- SendMoveToProgram(currentMove++, &first);
- DisplayBothClocks();
- DrawPosition(FALSE, boards[currentMove]);
- if (commentList[currentMove] != NULL) {
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
- return TRUE;
-}
-
-
-int
-LoadGameOneMove(readAhead)
- ChessMove readAhead;
-{
- int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
- char promoChar = NULLCHAR;
- ChessMove moveType;
- char move[MSG_SIZ];
- char *p, *q;
-
- if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
- gameMode != AnalyzeMode && gameMode != Training) {
- gameFileFP = NULL;
- return FALSE;
- }
-
- yyboardindex = forwardMostMove;
- if (readAhead != (ChessMove)0) {
- moveType = readAhead;
- } else {
- if (gameFileFP == NULL)
- return FALSE;
- moveType = (ChessMove) yylex();
- }
-
- done = FALSE;
- switch (moveType) {
- case Comment:
- if (appData.debugMode)
- fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
- p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
-
- /* append the comment but don't display it */
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
- return TRUE;
-
- case WhiteCapturesEnPassant:
- case BlackCapturesEnPassant:
- case WhitePromotionQueen:
- case BlackPromotionQueen:
- case WhitePromotionRook:
- case BlackPromotionRook:
- case WhitePromotionBishop:
- case BlackPromotionBishop:
- case WhitePromotionKnight:
- case BlackPromotionKnight:
- case WhitePromotionKing:
- case BlackPromotionKing:
- case NormalMove:
- case WhiteKingSideCastle:
- case WhiteQueenSideCastle:
- case BlackKingSideCastle:
- case BlackQueenSideCastle:
- case WhiteKingSideCastleWild:
- case WhiteQueenSideCastleWild:
- case BlackKingSideCastleWild:
- case BlackQueenSideCastleWild:
- /* PUSH Fabien */
- case WhiteHSideCastleFR:
- case WhiteASideCastleFR:
- case BlackHSideCastleFR:
- case BlackASideCastleFR:
- /* POP Fabien */
- if (appData.debugMode)
- fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
- fromX = currentMoveString[0] - 'a';
- fromY = currentMoveString[1] - '1';
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = currentMoveString[4];
- break;
-
- case WhiteDrop:
- case BlackDrop:
- if (appData.debugMode)
- fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
- fromX = moveType == WhiteDrop ?
- (int) CharToPiece(ToUpper(currentMoveString[0])) :
- (int) CharToPiece(ToLower(currentMoveString[0]));
- fromY = DROP_RANK;
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- break;
-
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- case GameUnfinished:
- if (appData.debugMode)
- fprintf(debugFP, "Parsed game end: %s\n", yy_text);
- p = strchr(yy_text, '{');
- if (p == NULL) p = strchr(yy_text, '(');
- if (p == NULL) {
- p = yy_text;
- if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
- } else {
- q = strchr(p, *p == '{' ? '}' : ')');
- if (q != NULL) *q = NULLCHAR;
- p++;
- }
- GameEnds(moveType, p, GE_FILE);
- done = TRUE;
- if (cmailMsgLoaded) {
- ClearHighlights();
- flipView = WhiteOnMove(currentMove);
- if (moveType == GameUnfinished) flipView = !flipView;
- if (appData.debugMode)
- fprintf(debugFP, "Setting flipView to %d\n", flipView) ;
- }
- break;
-
- case (ChessMove) 0: /* end of file */
- if (appData.debugMode)
- fprintf(debugFP, "Parser hit end of file\n");
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN)) {
- case MT_NONE:
- case MT_CHECK:
- break;
- case MT_CHECKMATE:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "Black mates", GE_FILE);
- } else {
- GameEnds(WhiteWins, "White mates", GE_FILE);
- }
- break;
- case MT_STALEMATE:
- GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
- break;
- }
- done = TRUE;
- break;
-
- case MoveNumberOne:
- if (lastLoadGameStart == GNUChessGame) {
- /* GNUChessGames have numbers, but they aren't move numbers */
- if (appData.debugMode)
- fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
- yy_text, (int) moveType);
- return LoadGameOneMove((ChessMove)0); /* tail recursion */
- }
- /* else fall thru */
-
- case XBoardGame:
- case GNUChessGame:
- case PGNTag:
- /* Reached start of next game in file */
- if (appData.debugMode)
- fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN)) {
- case MT_NONE:
- case MT_CHECK:
- break;
- case MT_CHECKMATE:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "Black mates", GE_FILE);
- } else {
- GameEnds(WhiteWins, "White mates", GE_FILE);
- }
- break;
- case MT_STALEMATE:
- GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
- break;
- }
- done = TRUE;
- break;
-
- case PositionDiagram: /* should not happen; ignore */
- case ElapsedTime: /* ignore */
- case NAG: /* ignore */
- if (appData.debugMode)
- fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
- yy_text, (int) moveType);
- return LoadGameOneMove((ChessMove)0); /* tail recursion */
-
- case IllegalMove:
- if (appData.testLegality) {
- if (appData.debugMode)
- fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
- sprintf(move, "Illegal move: %d.%s%s",
- (forwardMostMove / 2) + 1,
- WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
- DisplayError(move, 0);
- done = TRUE;
- } else {
- if (appData.debugMode)
- fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
- yy_text, currentMoveString);
- fromX = currentMoveString[0] - 'a';
- fromY = currentMoveString[1] - '1';
- toX = currentMoveString[2] - 'a';
- toY = currentMoveString[3] - '1';
- promoChar = currentMoveString[4];
- }
- break;
-
- case AmbiguousMove:
- if (appData.debugMode)
- fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
- sprintf(move, "Ambiguous move: %d.%s%s",
- (forwardMostMove / 2) + 1,
- WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
- DisplayError(move, 0);
- done = TRUE;
- break;
-
- default:
- case ImpossibleMove:
- if (appData.debugMode)
- fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);
- sprintf(move, "Illegal move: %d.%s%s",
- (forwardMostMove / 2) + 1,
- WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
- DisplayError(move, 0);
- done = TRUE;
- break;
- }
-
- if (done) {
- if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- if (!appData.matchMode && commentList[currentMove] != NULL)
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
- (void) StopLoadGameTimer();
- gameFileFP = NULL;
- cmailOldMove = forwardMostMove;
- return FALSE;
- } else {
- /* currentMoveString is set as a side-effect of yylex */
- strcat(currentMoveString, "\n");
- strcpy(moveList[forwardMostMove], currentMoveString);
-
- thinkOutput[0] = NULLCHAR;
- MakeMove(fromX, fromY, toX, toY, promoChar);
- currentMove = forwardMostMove;
- return TRUE;
- }
-}
-
-/* Load the nth game from the given file */
-int
-LoadGameFromFile(filename, n, title, useList)
- char *filename;
- int n;
- char *title;
- /*Boolean*/ int useList;
-{
- FILE *f;
- char buf[MSG_SIZ];
-
- if (strcmp(filename, "-") == 0) {
- f = stdin;
- title = "stdin";
- } else {
- f = fopen(filename, "rb");
- if (f == NULL) {
- sprintf(buf, "Can't open \"%s\"", filename);
- DisplayError(buf, errno);
- return FALSE;
- }
- }
- if (fseek(f, 0, 0) == -1) {
- /* f is not seekable; probably a pipe */
- useList = FALSE;
- }
- if (useList && n == 0) {
- int error = GameListBuild(f);
- if (error) {
- DisplayError("Cannot build game list", error);
- } else if (!ListEmpty(&gameList) &&
- ((ListGame *) gameList.tailPred)->number > 1) {
- GameListPopUp(f, title);
- return TRUE;
- }
- GameListDestroy();
- n = 1;
- }
- if (n == 0) n = 1;
- return LoadGame(f, n, title, FALSE);
-}
-
-
-void
-MakeRegisteredMove()
-{
- int fromX, fromY, toX, toY;
- char promoChar;
- if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
- switch (cmailMoveType[lastLoadGameNumber - 1]) {
- case CMAIL_MOVE:
- case CMAIL_DRAW:
- if (appData.debugMode)
- fprintf(debugFP, "Restoring %s for game %d\n",
- cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
-
- thinkOutput[0] = NULLCHAR;
- strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
- fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';
- fromY = cmailMove[lastLoadGameNumber - 1][1] - '1';
- toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';
- toY = cmailMove[lastLoadGameNumber - 1][3] - '1';
- promoChar = cmailMove[lastLoadGameNumber - 1][4];
- MakeMove(fromX, fromY, toX, toY, promoChar);
- ShowMove(fromX, fromY, toX, toY);
-
- switch (MateTest(boards[currentMove], PosFlags(currentMove),
- EP_UNKNOWN)) {
- case MT_NONE:
- case MT_CHECK:
- break;
-
- case MT_CHECKMATE:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "Black mates", GE_PLAYER);
- } else {
- GameEnds(WhiteWins, "White mates", GE_PLAYER);
- }
- break;
-
- case MT_STALEMATE:
- GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
- break;
- }
-
- break;
-
- case CMAIL_RESIGN:
- if (WhiteOnMove(currentMove)) {
- GameEnds(BlackWins, "White resigns", GE_PLAYER);
- } else {
- GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
- }
- break;
-
- case CMAIL_ACCEPT:
- GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
- break;
-
- default:
- break;
- }
- }
-
- return;
-}
-
-/* Wrapper around LoadGame for use when a Cmail message is loaded */
-int
-CmailLoadGame(f, gameNumber, title, useList)
- FILE *f;
- int gameNumber;
- char *title;
- int useList;
-{
- int retVal;
-
- if (gameNumber > nCmailGames) {
- DisplayError("No more games in this message", 0);
- return FALSE;
- }
- if (f == lastLoadGameFP) {
- int offset = gameNumber - lastLoadGameNumber;
- if (offset == 0) {
- cmailMsg[0] = NULLCHAR;
- if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
- cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
- nCmailMovesRegistered--;
- }
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
- if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {
- cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;
- }
- } else {
- if (! RegisterMove()) return FALSE;
- }
- }
-
- retVal = LoadGame(f, gameNumber, title, useList);
-
- /* Make move registered during previous look at this game, if any */
- MakeRegisteredMove();
-
- if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {
- commentList[currentMove]
- = StrSave(cmailCommentList[lastLoadGameNumber - 1]);
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
-
- return retVal;
-}
-
-/* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
-int
-ReloadGame(offset)
- int offset;
-{
- int gameNumber = lastLoadGameNumber + offset;
- if (lastLoadGameFP == NULL) {
- DisplayError("No game has been loaded yet", 0);
- return FALSE;
- }
- if (gameNumber <= 0) {
- DisplayError("Can't back up any further", 0);
- return FALSE;
- }
- if (cmailMsgLoaded) {
- return CmailLoadGame(lastLoadGameFP, gameNumber,
- lastLoadGameTitle, lastLoadGameUseList);
- } else {
- return LoadGame(lastLoadGameFP, gameNumber,
- lastLoadGameTitle, lastLoadGameUseList);
- }
-}
-
-
-
-/* Load the nth game from open file f */
-int
-LoadGame(f, gameNumber, title, useList)
- FILE *f;
- int gameNumber;
- char *title;
- int useList;
-{
- ChessMove cm;
- char buf[MSG_SIZ];
- int gn = gameNumber;
- ListGame *lg = NULL;
- int numPGNTags = 0;
- int err;
- GameMode oldGameMode;
-
- if (appData.debugMode)
- fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
-
- if (gameMode == Training )
- SetTrainingModeOff();
-
- oldGameMode = gameMode;
- if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
- }
-
- gameFileFP = f;
- if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
- fclose(lastLoadGameFP);
- }
-
- if (useList) {
- lg = (ListGame *) ListElem(&gameList, gameNumber-1);
-
- if (lg) {
- fseek(f, lg->offset, 0);
- GameListHighlight(gameNumber);
- gn = 1;
- }
- else {
- DisplayError("Game number out of range", 0);
- return FALSE;
- }
- } else {
- GameListDestroy();
- if (fseek(f, 0, 0) == -1) {
- if (f == lastLoadGameFP ?
- gameNumber == lastLoadGameNumber + 1 :
- gameNumber == 1) {
- gn = 1;
- } else {
- DisplayError("Can't seek on game file", 0);
- return FALSE;
- }
- }
- }
- lastLoadGameFP = f;
- lastLoadGameNumber = gameNumber;
- strcpy(lastLoadGameTitle, title);
- lastLoadGameUseList = useList;
-
- yynewfile(f);
-
-
- if (lg && lg->gameInfo.white && lg->gameInfo.black) {
- sprintf(buf, "%s vs. %s", lg->gameInfo.white,
- lg->gameInfo.black);
- DisplayTitle(buf);
- } else if (*title != NULLCHAR) {
- if (gameNumber > 1) {
- sprintf(buf, "%s %d", title, gameNumber);
- DisplayTitle(buf);
- } else {
- DisplayTitle(title);
- }
- }
-
- if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {
- gameMode = PlayFromGameFile;
- ModeHighlight();
- }
-
- currentMove = forwardMostMove = backwardMostMove = 0;
- CopyBoard(boards[0], initialPosition);
- StopClocks();
-
- /*
- * Skip the first gn-1 games in the file.
- * Also skip over anything that precedes an identifiable
- * start of game marker, to avoid being confused by
- * garbage at the start of the file. Currently
- * recognized start of game markers are the move number "1",
- * the pattern "gnuchess .* game", the pattern
- * "^[#;%] [^ ]* game file", and a PGN tag block.
- * A game that starts with one of the latter two patterns
- * will also have a move number 1, possibly
- * following a position diagram.
- * 5-4-02: Let's try being more lenient and allowing a game to
- * start with an unnumbered move. Does that break anything?
- */
- cm = lastLoadGameStart = (ChessMove) 0;
- while (gn > 0) {
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
- switch (cm) {
- case (ChessMove) 0:
- if (cmailMsgLoaded) {
- nCmailGames = CMAIL_MAX_GAMES - gn;
- } else {
- Reset(TRUE, TRUE);
- DisplayError("Game not found in file", 0);
- }
- return FALSE;
-
- case GNUChessGame:
- case XBoardGame:
- gn--;
- lastLoadGameStart = cm;
- break;
-
- case MoveNumberOne:
- switch (lastLoadGameStart) {
- case GNUChessGame:
- case XBoardGame:
- case PGNTag:
- break;
- case MoveNumberOne:
- case (ChessMove) 0:
- gn--; /* count this game */
- lastLoadGameStart = cm;
- break;
- default:
- /* impossible */
- break;
- }
- break;
-
- case PGNTag:
- switch (lastLoadGameStart) {
- case GNUChessGame:
- case PGNTag:
- case MoveNumberOne:
- case (ChessMove) 0:
- gn--; /* count this game */
- lastLoadGameStart = cm;
- break;
- case XBoardGame:
- lastLoadGameStart = cm; /* game counted already */
- break;
- default:
- /* impossible */
- break;
- }
- if (gn > 0) {
- do {
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
- } while (cm == PGNTag || cm == Comment);
- }
- break;
-
- case WhiteWins:
- case BlackWins:
- case GameIsDrawn:
- if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {
- if ( cmailResult[CMAIL_MAX_GAMES - gn - 1]
- != CMAIL_OLD_RESULT) {
- nCmailResults ++ ;
- cmailResult[ CMAIL_MAX_GAMES
- - gn - 1] = CMAIL_OLD_RESULT;
- }
- }
- break;
-
- case NormalMove:
- /* Only a NormalMove can be at the start of a game
- * without a position diagram. */
- if (lastLoadGameStart == (ChessMove) 0) {
- gn--;
- lastLoadGameStart = MoveNumberOne;
- }
- break;
-
- default:
- break;
- }
- }
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
-
- if (cm == XBoardGame) {
- /* Skip any header junk before position diagram and/or move 1 */
- for (;;) {
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
-
- if (cm == (ChessMove) 0 ||
- cm == GNUChessGame || cm == XBoardGame) {
- /* Empty game; pretend end-of-file and handle later */
- cm = (ChessMove) 0;
- break;
- }
-
- if (cm == MoveNumberOne || cm == PositionDiagram ||
- cm == PGNTag || cm == Comment)
- break;
- }
- } else if (cm == GNUChessGame) {
- if (gameInfo.event != NULL) {
- free(gameInfo.event);
- }
- gameInfo.event = StrSave(yy_text);
- }
-
- startedFromSetupPosition = FALSE;
- while (cm == PGNTag) {
- if (appData.debugMode)
- fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
- err = ParsePGNTag(yy_text, &gameInfo);
- if (!err) numPGNTags++;
-
- if (gameInfo.fen != NULL) {
- Board initial_position;
- startedFromSetupPosition = TRUE;
- if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
- Reset(TRUE, TRUE);
- DisplayError("Bad FEN position in file", 0);
- return FALSE;
- }
- CopyBoard(boards[0], initial_position);
- if (blackPlaysFirst) {
- currentMove = forwardMostMove = backwardMostMove = 1;
- CopyBoard(boards[1], initial_position);
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
- timeRemaining[0][1] = whiteTimeRemaining;
- timeRemaining[1][1] = blackTimeRemaining;
- if (commentList[0] != NULL) {
- commentList[1] = commentList[0];
- commentList[0] = NULL;
- }
- } else {
- currentMove = forwardMostMove = backwardMostMove = 0;
- }
- yyboardindex = forwardMostMove;
- free(gameInfo.fen);
- gameInfo.fen = NULL;
- }
-
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
-
- /* Handle comments interspersed among the tags */
- while (cm == Comment) {
- char *p;
- if (appData.debugMode)
- fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
- p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
- }
- }
-
- /* don't rely on existence of Event tag since if game was
- * pasted from clipboard the Event tag may not exist
- */
- if (numPGNTags > 0){
- char *tags;
- if (gameInfo.variant == VariantNormal) {
- gameInfo.variant = StringToVariant(gameInfo.event);
- }
- if (!matchMode) {
- if( appData.autoDisplayTags ) {
- tags = PGNTags(&gameInfo);
- TagsPopUp(tags, CmailMsg());
- free(tags);
- }
- }
- } else {
- /* Make something up, but don't display it now */
- SetGameInfo();
- TagsPopDown();
- }
-
- if (cm == PositionDiagram) {
- int i, j;
- char *p;
- Board initial_position;
-
- if (appData.debugMode)
- fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);
-
- if (!startedFromSetupPosition) {
- p = yy_text;
- for (i = BOARD_SIZE - 1; i >= 0; i--)
- for (j = 0; j < BOARD_SIZE; p++)
- switch (*p) {
- case '[':
- case '-':
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- break;
- default:
- initial_position[i][j++] = CharToPiece(*p);
- break;
- }
- while (*p == ' ' || *p == '\t' ||
- *p == '\n' || *p == '\r') p++;
-
- if (strncmp(p, "black", strlen("black"))==0)
- blackPlaysFirst = TRUE;
- else
- blackPlaysFirst = FALSE;
- startedFromSetupPosition = TRUE;
-
- CopyBoard(boards[0], initial_position);
- if (blackPlaysFirst) {
- currentMove = forwardMostMove = backwardMostMove = 1;
- CopyBoard(boards[1], initial_position);
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
- timeRemaining[0][1] = whiteTimeRemaining;
- timeRemaining[1][1] = blackTimeRemaining;
- if (commentList[0] != NULL) {
- commentList[1] = commentList[0];
- commentList[0] = NULL;
- }
- } else {
- currentMove = forwardMostMove = backwardMostMove = 0;
- }
- }
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
- }
-
- if (first.pr == NoProc) {
- StartChessProgram(&first);
- }
- InitChessProgram(&first);
- SendToProgram("force\n", &first);
- if (startedFromSetupPosition) {
- SendBoard(&first, forwardMostMove);
- DisplayBothClocks();
- }
-
- while (cm == Comment) {
- char *p;
- if (appData.debugMode)
- fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
- p = yy_text;
- if (*p == '{' || *p == '[' || *p == '(') {
- p[strlen(p) - 1] = NULLCHAR;
- p++;
- }
- while (*p == '\n') p++;
- AppendComment(currentMove, p);
- yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
- }
-
- if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||
- cm == WhiteWins || cm == BlackWins ||
- cm == GameIsDrawn || cm == GameUnfinished) {
- DisplayMessage("", "No moves in game");
- if (cmailMsgLoaded) {
- if (appData.debugMode)
- fprintf(debugFP, "Setting flipView to %d.\n", FALSE);
- ClearHighlights();
- flipView = FALSE;
- }
- DrawPosition(FALSE, boards[currentMove]);
- DisplayBothClocks();
- gameMode = EditGame;
- ModeHighlight();
- gameFileFP = NULL;
- cmailOldMove = 0;
- return TRUE;
- }
-
- if (commentList[currentMove] != NULL) {
- if (!matchMode && (pausing || appData.timeDelay != 0)) {
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
- }
- if (!matchMode && appData.timeDelay != 0)
- DrawPosition(FALSE, boards[currentMove]);
-
- if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
- programStats.ok_to_send = 1;
- }
-
- /* if the first token after the PGN tags is a move
- * and not move number 1, retrieve it from the parser
- */
- if (cm != MoveNumberOne)
- LoadGameOneMove(cm);
-
- /* load the remaining moves from the file */
- while (LoadGameOneMove((ChessMove)0)) {
- timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove] = blackTimeRemaining;
- }
-
- /* rewind to the start of the game */
- currentMove = backwardMostMove;
-
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-
- if (oldGameMode == AnalyzeFile ||
- oldGameMode == AnalyzeMode) {
- AnalyzeFileEvent();
- }
-
- if (matchMode || appData.timeDelay == 0) {
- ToEndEvent();
- gameMode = EditGame;
- ModeHighlight();
- } else if (appData.timeDelay > 0) {
- AutoPlayGameLoop();
- }
-
- if (appData.debugMode)
- fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
- return TRUE;
-}
-
-/* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
-int
-ReloadPosition(offset)
- int offset;
-{
- int positionNumber = lastLoadPositionNumber + offset;
- if (lastLoadPositionFP == NULL) {
- DisplayError("No position has been loaded yet", 0);
- return FALSE;
- }
- if (positionNumber <= 0) {
- DisplayError("Can't back up any further", 0);
- return FALSE;
- }
- return LoadPosition(lastLoadPositionFP, positionNumber,
- lastLoadPositionTitle);
-}
-
-/* Load the nth position from the given file */
-int
-LoadPositionFromFile(filename, n, title)
- char *filename;
- int n;
- char *title;
-{
- FILE *f;
- char buf[MSG_SIZ];
-
- if (strcmp(filename, "-") == 0) {
- return LoadPosition(stdin, n, "stdin");
- } else {
- f = fopen(filename, "rb");
- if (f == NULL) {
- sprintf(buf, "Can't open \"%s\"", filename);
- DisplayError(buf, errno);
- return FALSE;
- } else {
- return LoadPosition(f, n, title);
- }
- }
-}
-
-/* Load the nth position from the given open file, and close it */
-int
-LoadPosition(f, positionNumber, title)
- FILE *f;
- int positionNumber;
- char *title;
-{
- char *p, line[MSG_SIZ];
- Board initial_position;
- int i, j, fenMode, pn;
-
- if (gameMode == Training )
- SetTrainingModeOff();
-
- if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
- }
- if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {
- fclose(lastLoadPositionFP);
- }
- if (positionNumber == 0) positionNumber = 1;
- lastLoadPositionFP = f;
- lastLoadPositionNumber = positionNumber;
- strcpy(lastLoadPositionTitle, title);
- if (first.pr == NoProc) {
- StartChessProgram(&first);
- InitChessProgram(&first);
- }
- pn = positionNumber;
- if (positionNumber < 0) {
- /* Negative position number means to seek to that byte offset */
- if (fseek(f, -positionNumber, 0) == -1) {
- DisplayError("Can't seek on position file", 0);
- return FALSE;
- };
- pn = 1;
- } else {
- if (fseek(f, 0, 0) == -1) {
- if (f == lastLoadPositionFP ?
- positionNumber == lastLoadPositionNumber + 1 :
- positionNumber == 1) {
- pn = 1;
- } else {
- DisplayError("Can't seek on position file", 0);
- return FALSE;
- }
- }
- }
- /* See if this file is FEN or old-style xboard */
- if (fgets(line, MSG_SIZ, f) == NULL) {
- DisplayError("Position not found in file", 0);
- return FALSE;
- }
- switch (line[0]) {
- case '#': case 'x':
- default:
- fenMode = FALSE;
- break;
- case 'p': case 'n': case 'b': case 'r': case 'q': case 'k':
- case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':
- case '1': case '2': case '3': case '4': case '5': case '6':
- case '7': case '8':
- fenMode = TRUE;
- break;
- }
-
- if (pn >= 2) {
- if (fenMode || line[0] == '#') pn--;
- while (pn > 0) {
- /* skip postions before number pn */
- if (fgets(line, MSG_SIZ, f) == NULL) {
- Reset(TRUE, TRUE);
- DisplayError("Position not found in file", 0);
- return FALSE;
- }
- if (fenMode || line[0] == '#') pn--;
- }
- }
-
- if (fenMode) {
- if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
- DisplayError("Bad FEN position in file", 0);
- return FALSE;
- }
- } else {
- (void) fgets(line, MSG_SIZ, f);
- (void) fgets(line, MSG_SIZ, f);
-
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- (void) fgets(line, MSG_SIZ, f);
- for (p = line, j = 0; j < BOARD_SIZE; p++) {
- if (*p == ' ')
- continue;
- initial_position[i][j++] = CharToPiece(*p);
- }
- }
-
- blackPlaysFirst = FALSE;
- if (!feof(f)) {
- (void) fgets(line, MSG_SIZ, f);
- if (strncmp(line, "black", strlen("black"))==0)
- blackPlaysFirst = TRUE;
- }
- }
- startedFromSetupPosition = TRUE;
-
- SendToProgram("force\n", &first);
- CopyBoard(boards[0], initial_position);
- if (blackPlaysFirst) {
- currentMove = forwardMostMove = backwardMostMove = 1;
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
- CopyBoard(boards[1], initial_position);
- DisplayMessage("", "Black to play");
- } else {
- currentMove = forwardMostMove = backwardMostMove = 0;
- DisplayMessage("", "White to play");
- }
- SendBoard(&first, forwardMostMove);
-
- if (positionNumber > 1) {
- sprintf(line, "%s %d", title, positionNumber);
- DisplayTitle(line);
- } else {
- DisplayTitle(title);
- }
- gameMode = EditGame;
- ModeHighlight();
- ResetClocks();
- timeRemaining[0][1] = whiteTimeRemaining;
- timeRemaining[1][1] = blackTimeRemaining;
- DrawPosition(FALSE, boards[currentMove]);
-
- return TRUE;
-}
-
-
-void
-CopyPlayerNameIntoFileName(dest, src)
- char **dest, *src;
-{
- while (*src != NULLCHAR && *src != ',') {
- if (*src == ' ') {
- *(*dest)++ = '_';
- src++;
- } else {
- *(*dest)++ = *src++;
- }
- }
-}
-
-char *DefaultFileName(ext)
- char *ext;
-{
- static char def[MSG_SIZ];
- char *p;
-
- if (gameInfo.white != NULL && gameInfo.white[0] != '-') {
- p = def;
- CopyPlayerNameIntoFileName(&p, gameInfo.white);
- *p++ = '-';
- CopyPlayerNameIntoFileName(&p, gameInfo.black);
- *p++ = '.';
- strcpy(p, ext);
- } else {
- def[0] = NULLCHAR;
- }
- return def;
-}
-
-/* Save the current game to the given file */
-int
-SaveGameToFile(filename, append)
- char *filename;
- int append;
-{
- FILE *f;
- char buf[MSG_SIZ];
-
- if (strcmp(filename, "-") == 0) {
- return SaveGame(stdout, 0, NULL);
- } else {
- f = fopen(filename, append ? "a" : "w");
- if (f == NULL) {
- sprintf(buf, "Can't open \"%s\"", filename);
- DisplayError(buf, errno);
- return FALSE;
- } else {
- return SaveGame(f, 0, NULL);
- }
- }
-}
-
-char *
-SavePart(str)
- char *str;
-{
- static char buf[MSG_SIZ];
- char *p;
-
- p = strchr(str, ' ');
- if (p == NULL) return str;
- strncpy(buf, str, p - str);
- buf[p - str] = NULLCHAR;
- return buf;
-}
-
-#define PGN_MAX_LINE 75
-
-#define PGN_SIDE_WHITE 0
-#define PGN_SIDE_BLACK 1
-
-/* [AS] */
-static int FindFirstMoveOutOfBook( int side )
-{
- int result = -1;
-
- if( backwardMostMove == 0 && ! startedFromSetupPosition) {
- int index = backwardMostMove;
- int has_book_hit = 0;
-
- if( (index % 2) != side ) {
- index++;
- }
-
- while( index < forwardMostMove ) {
- /* Check to see if engine is in book */
- int depth = pvInfoList[index].depth;
- int score = pvInfoList[index].score;
- int in_book = 0;
-
- if( depth <= 2 ) {
- in_book = 1;
- }
- else if( score == 0 && depth == 63 ) {
- in_book = 1; /* Zappa */
- }
- else if( score == 2 && depth == 99 ) {
- in_book = 1; /* Abrok */
- }
-
- has_book_hit += in_book;
-
- if( ! in_book ) {
- result = index;
-
- break;
- }
-
- index += 2;
- }
- }
-
- return result;
-}
-
-/* [AS] */
-void GetOutOfBookInfo( char * buf )
-{
- int oob[2];
- int i;
- int offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-
- oob[0] = FindFirstMoveOutOfBook( PGN_SIDE_WHITE );
- oob[1] = FindFirstMoveOutOfBook( PGN_SIDE_BLACK );
-
- *buf = '\0';
-
- if( oob[0] >= 0 || oob[1] >= 0 ) {
- for( i=0; i<2; i++ ) {
- int idx = oob[i];
-
- if( idx >= 0 ) {
- if( i > 0 && oob[0] >= 0 ) {
- strcat( buf, " " );
- }
-
- sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );
- sprintf( buf+strlen(buf), "%s%.2f",
- pvInfoList[idx].score >= 0 ? "+" : "",
- pvInfoList[idx].score / 100.0 );
- }
- }
- }
-}
-
-/* Save game in PGN style and close the file */
-int
-SaveGamePGN(f)
- FILE *f;
-{
- int i, offset, linelen, newblock;
- time_t tm;
- char *movetext;
- char numtext[32];
- int movelen, numlen, blank;
- char move_buffer[100]; /* [AS] Buffer for move+PV info */
-
- offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-
- tm = time((time_t *) NULL);
-
- PrintPGNTags(f, &gameInfo);
-
- if (backwardMostMove > 0 || startedFromSetupPosition) {
- char *fen = PositionToFEN(backwardMostMove, 1);
- fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
- fprintf(f, "\n{--------------\n");
- PrintPosition(f, backwardMostMove);
- fprintf(f, "--------------}\n");
- free(fen);
- }
- else {
- /* [AS] Out of book annotation */
- if( appData.saveOutOfBookInfo ) {
- char buf[64];
-
- GetOutOfBookInfo( buf );
-
- if( buf[0] != '\0' ) {
- fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf );
- }
- }
-
- fprintf(f, "\n");
- }
-
- i = backwardMostMove;
- linelen = 0;
- newblock = TRUE;
-
- while (i < forwardMostMove) {
- /* Print comments preceding this move */
- if (commentList[i] != NULL) {
- if (linelen > 0) fprintf(f, "\n");
- fprintf(f, "{\n%s}\n", commentList[i]);
- linelen = 0;
- newblock = TRUE;
- }
-
- /* Format move number */
- if ((i % 2) == 0) {
- sprintf(numtext, "%d.", (i - offset)/2 + 1);
- } else {
- if (newblock) {
- sprintf(numtext, "%d...", (i - offset)/2 + 1);
- } else {
- numtext[0] = NULLCHAR;
- }
- }
- numlen = strlen(numtext);
- newblock = FALSE;
-
- /* Print move number */
- blank = linelen > 0 && numlen > 0;
- if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {
- fprintf(f, "\n");
- linelen = 0;
- blank = 0;
- }
- if (blank) {
- fprintf(f, " ");
- linelen++;
- }
- fprintf(f, numtext);
- linelen += numlen;
-
- /* Get move */
- movetext = SavePart(parseList[i]);
-
- /* [AS] Add PV info if present */
- if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {
- sprintf( move_buffer, "%s {%s%.2f/%d}",
- movetext,
- pvInfoList[i].score >= 0 ? "+" : "",
- pvInfoList[i].score / 100.0,
- pvInfoList[i].depth );
- movetext = move_buffer;
- }
-
- movelen = strlen(movetext);
-
- /* Print move */
- blank = linelen > 0 && movelen > 0;
- if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
- fprintf(f, "\n");
- linelen = 0;
- blank = 0;
- }
- if (blank) {
- fprintf(f, " ");
- linelen++;
- }
- fprintf(f, movetext);
- linelen += movelen;
-
- i++;
- }
-
- /* Start a new line */
- if (linelen > 0) fprintf(f, "\n");
-
- /* Print comments after last move */
- if (commentList[i] != NULL) {
- fprintf(f, "{\n%s}\n", commentList[i]);
- }
-
- /* Print result */
- if (gameInfo.resultDetails != NULL &&
- gameInfo.resultDetails[0] != NULLCHAR) {
- fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,
- PGNResult(gameInfo.result));
- } else {
- fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
- }
-
- fclose(f);
- return TRUE;
-}
-
-/* Save game in old style and close the file */
-int
-SaveGameOldStyle(f)
- FILE *f;
-{
- int i, offset;
- time_t tm;
-
- tm = time((time_t *) NULL);
-
- fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
- PrintOpponents(f);
-
- if (backwardMostMove > 0 || startedFromSetupPosition) {
- fprintf(f, "\n[--------------\n");
- PrintPosition(f, backwardMostMove);
- fprintf(f, "--------------]\n");
- } else {
- fprintf(f, "\n");
- }
-
- i = backwardMostMove;
- offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-
- while (i < forwardMostMove) {
- if (commentList[i] != NULL) {
- fprintf(f, "[%s]\n", commentList[i]);
- }
-
- if ((i % 2) == 1) {
- fprintf(f, "%d. ... %s\n", (i - offset)/2 + 1, parseList[i]);
- i++;
- } else {
- fprintf(f, "%d. %s ", (i - offset)/2 + 1, parseList[i]);
- i++;
- if (commentList[i] != NULL) {
- fprintf(f, "\n");
- continue;
- }
- if (i >= forwardMostMove) {
- fprintf(f, "\n");
- break;
- }
- fprintf(f, "%s\n", parseList[i]);
- i++;
- }
- }
-
- if (commentList[i] != NULL) {
- fprintf(f, "[%s]\n", commentList[i]);
- }
-
- /* This isn't really the old style, but it's close enough */
- if (gameInfo.resultDetails != NULL &&
- gameInfo.resultDetails[0] != NULLCHAR) {
- fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),
- gameInfo.resultDetails);
- } else {
- fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
- }
-
- fclose(f);
- return TRUE;
-}
-
-/* Save the current game to open file f and close the file */
-int
-SaveGame(f, dummy, dummy2)
- FILE *f;
- int dummy;
- char *dummy2;
-{
- if (gameMode == EditPosition) EditPositionDone();
- if (appData.oldSaveStyle)
- return SaveGameOldStyle(f);
- else
- return SaveGamePGN(f);
-}
-
-/* Save the current position to the given file */
-int
-SavePositionToFile(filename)
- char *filename;
-{
- FILE *f;
- char buf[MSG_SIZ];
-
- if (strcmp(filename, "-") == 0) {
- return SavePosition(stdout, 0, NULL);
- } else {
- f = fopen(filename, "a");
- if (f == NULL) {
- sprintf(buf, "Can't open \"%s\"", filename);
- DisplayError(buf, errno);
- return FALSE;
- } else {
- SavePosition(f, 0, NULL);
- return TRUE;
- }
- }
-}
-
-/* Save the current position to the given open file and close the file */
-int
-SavePosition(f, dummy, dummy2)
- FILE *f;
- int dummy;
- char *dummy2;
-{
- time_t tm;
- char *fen;
-
- if (appData.oldSaveStyle) {
- tm = time((time_t *) NULL);
-
- fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
- PrintOpponents(f);
- fprintf(f, "[--------------\n");
- PrintPosition(f, currentMove);
- fprintf(f, "--------------]\n");
- } else {
- fen = PositionToFEN(currentMove, 1);
- fprintf(f, "%s\n", fen);
- free(fen);
- }
- fclose(f);
- return TRUE;
-}
-
-void
-ReloadCmailMsgEvent(unregister)
- int unregister;
-{
-#if !WIN32
- static char *inFilename = NULL;
- static char *outFilename;
- int i;
- struct stat inbuf, outbuf;
- int status;
-
- /* Any registered moves are unregistered if unregister is set, */
- /* i.e. invoked by the signal handler */
- if (unregister) {
- for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
- cmailMoveRegistered[i] = FALSE;
- if (cmailCommentList[i] != NULL) {
- free(cmailCommentList[i]);
- cmailCommentList[i] = NULL;
- }
- }
- nCmailMovesRegistered = 0;
- }
-
- for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
- cmailResult[i] = CMAIL_NOT_RESULT;
- }
- nCmailResults = 0;
-
- if (inFilename == NULL) {
- /* Because the filenames are static they only get malloced once */
- /* and they never get freed */
- inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);
- sprintf(inFilename, "%s.game.in", appData.cmailGameName);
-
- outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
- sprintf(outFilename, "%s.out", appData.cmailGameName);
- }
-
- status = stat(outFilename, &outbuf);
- if (status < 0) {
- cmailMailedMove = FALSE;
- } else {
- status = stat(inFilename, &inbuf);
- cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
- }
-
- /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
- counts the games, notes how each one terminated, etc.
-
- It would be nice to remove this kludge and instead gather all
- the information while building the game list. (And to keep it
- in the game list nodes instead of having a bunch of fixed-size
- parallel arrays.) Note this will require getting each game's
- termination from the PGN tags, as the game list builder does
- not process the game moves. --mann
- */
- cmailMsgLoaded = TRUE;
- LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
-
- /* Load first game in the file or popup game menu */
- LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
-
-#endif /* !WIN32 */
- return;
-}
-
-int
-RegisterMove()
-{
- FILE *f;
- char string[MSG_SIZ];
-
- if ( cmailMailedMove
- || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {
- return TRUE; /* Allow free viewing */
- }
-
- /* Unregister move to ensure that we don't leave RegisterMove */
- /* with the move registered when the conditions for registering no */
- /* longer hold */
- if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
- cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
- nCmailMovesRegistered --;
-
- if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
- {
- free(cmailCommentList[lastLoadGameNumber - 1]);
- cmailCommentList[lastLoadGameNumber - 1] = NULL;
- }
- }
-
- if (cmailOldMove == -1) {
- DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);
- return FALSE;
- }
-
- if (currentMove > cmailOldMove + 1) {
- DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);
- return FALSE;
- }
-
- if (currentMove < cmailOldMove) {
- DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);
- return FALSE;
- }
-
- if (forwardMostMove > currentMove) {
- /* Silently truncate extra moves */
- TruncateGame();
- }
-
- if ( (currentMove == cmailOldMove + 1)
- || ( (currentMove == cmailOldMove)
- && ( (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)
- || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {
- if (gameInfo.result != GameUnfinished) {
- cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;
- }
-
- if (commentList[currentMove] != NULL) {
- cmailCommentList[lastLoadGameNumber - 1]
- = StrSave(commentList[currentMove]);
- }
- strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
-
- if (appData.debugMode)
- fprintf(debugFP, "Saving %s for game %d\n",
- cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
-
- sprintf(string,
- "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
-
- f = fopen(string, "w");
- if (appData.oldSaveStyle) {
- SaveGameOldStyle(f); /* also closes the file */
-
- sprintf(string, "%s.pos.out", appData.cmailGameName);
- f = fopen(string, "w");
- SavePosition(f, 0, NULL); /* also closes the file */
- } else {
- fprintf(f, "{--------------\n");
- PrintPosition(f, currentMove);
- fprintf(f, "--------------}\n\n");
-
- SaveGame(f, 0, NULL); /* also closes the file*/
- }
-
- cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
- nCmailMovesRegistered ++;
- } else if (nCmailGames == 1) {
- DisplayError("You have not made a move yet", 0);
- return FALSE;
- }
-
- return TRUE;
-}
-
-void
-MailMoveEvent()
-{
-#if !WIN32
- static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
- FILE *commandOutput;
- char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];
- int nBytes = 0; /* Suppress warnings on uninitialized variables */
- int nBuffers;
- int i;
- int archived;
- char *arcDir;
-
- if (! cmailMsgLoaded) {
- DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);
- return;
- }
-
- if (nCmailGames == nCmailResults) {
- DisplayError("No unfinished games", 0);
- return;
- }
-
-#if CMAIL_PROHIBIT_REMAIL
- if (cmailMailedMove) {
- sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName);
- DisplayError(msg, 0);
- return;
- }
-#endif
-
- if (! (cmailMailedMove || RegisterMove())) return;
-
- if ( cmailMailedMove
- || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
- sprintf(string, partCommandString,
- appData.debugMode ? " -v" : "", appData.cmailGameName);
- commandOutput = popen(string, "rb");
-
- if (commandOutput == NULL) {
- DisplayError("Failed to invoke cmail", 0);
- } else {
- for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {
- nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);
- }
- if (nBuffers > 1) {
- (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);
- (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);
- nBytes = MSG_SIZ - 1;
- } else {
- (void) memcpy(msg, buffer, nBytes);
- }
- *(msg + nBytes) = '\0'; /* \0 for end-of-string*/
-
- if(StrStr(msg, "Mailed cmail message to ") != NULL) {
- cmailMailedMove = TRUE; /* Prevent >1 moves */
-
- archived = TRUE;
- for (i = 0; i < nCmailGames; i ++) {
- if (cmailResult[i] == CMAIL_NOT_RESULT) {
- archived = FALSE;
- }
- }
- if ( archived
- && ( (arcDir = (char *) getenv("CMAIL_ARCDIR"))
- != NULL)) {
- sprintf(buffer, "%s/%s.%s.archive",
- arcDir,
- appData.cmailGameName,
- gameInfo.date);
- LoadGameFromFile(buffer, 1, buffer, FALSE);
- cmailMsgLoaded = FALSE;
- }
- }
-
- DisplayInformation(msg);
- pclose(commandOutput);
- }
- } else {
- if ((*cmailMsg) != '\0') {
- DisplayInformation(cmailMsg);
- }
- }
-
- return;
-#endif /* !WIN32 */
-}
-
-char *
-CmailMsg()
-{
-#if WIN32
- return NULL;
-#else
- int prependComma = 0;
- char number[5];
- char string[MSG_SIZ]; /* Space for game-list */
- int i;
-
- if (!cmailMsgLoaded) return "";
-
- if (cmailMailedMove) {
- sprintf(cmailMsg, "Waiting for reply from opponent\n");
- } else {
- /* Create a list of games left */
- sprintf(string, "[");
- for (i = 0; i < nCmailGames; i ++) {
- if (! ( cmailMoveRegistered[i]
- || (cmailResult[i] == CMAIL_OLD_RESULT))) {
- if (prependComma) {
- sprintf(number, ",%d", i + 1);
- } else {
- sprintf(number, "%d", i + 1);
- prependComma = 1;
- }
-
- strcat(string, number);
- }
- }
- strcat(string, "]");
-
- if (nCmailMovesRegistered + nCmailResults == 0) {
- switch (nCmailGames) {
- case 1:
- sprintf(cmailMsg,
- "Still need to make move for game\n");
- break;
-
- case 2:
- sprintf(cmailMsg,
- "Still need to make moves for both games\n");
- break;
-
- default:
- sprintf(cmailMsg,
- "Still need to make moves for all %d games\n",
- nCmailGames);
- break;
- }
- } else {
- switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
- case 1:
- sprintf(cmailMsg,
- "Still need to make a move for game %s\n",
- string);
- break;
-
- case 0:
- if (nCmailResults == nCmailGames) {
- sprintf(cmailMsg, "No unfinished games\n");
- } else {
- sprintf(cmailMsg, "Ready to send mail\n");
- }
- break;
-
- default:
- sprintf(cmailMsg,
- "Still need to make moves for games %s\n",
- string);
- }
- }
- }
- return cmailMsg;
-#endif /* WIN32 */
-}
-
-void
-ResetGameEvent()
-{
- if (gameMode == Training)
- SetTrainingModeOff();
-
- Reset(TRUE, TRUE);
- cmailMsgLoaded = FALSE;
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- }
-}
-
-static int exiting = 0;
-
-void
-ExitEvent(status)
- int status;
-{
- exiting++;
- if (exiting > 2) {
- /* Give up on clean exit */
- exit(status);
- }
- if (exiting > 1) {
- /* Keep trying for clean exit */
- return;
- }
-
- if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);
-
- if (telnetISR != NULL) {
- RemoveInputSource(telnetISR);
- }
- if (icsPR != NoProc) {
- DestroyChildProcess(icsPR, TRUE);
- }
- /* Save game if resource set and not already saved by GameEnds() */
- if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {
- if (*appData.saveGameFile != NULLCHAR) {
- SaveGameToFile(appData.saveGameFile, TRUE);
- } else if (appData.autoSaveGames) {
- AutoSaveGame();
- }
- if (*appData.savePositionFile != NULLCHAR) {
- SavePositionToFile(appData.savePositionFile);
- }
- }
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-
- /* Kill off chess programs */
- if (first.pr != NoProc) {
- ExitAnalyzeMode();
-
- DoSleep( appData.delayBeforeQuit );
- SendToProgram("quit\n", &first);
- DoSleep( appData.delayAfterQuit );
- DestroyChildProcess(first.pr, 10 /* [AS] first.useSigterm */ );
- }
- if (second.pr != NoProc) {
- DoSleep( appData.delayBeforeQuit );
- SendToProgram("quit\n", &second);
- DoSleep( appData.delayAfterQuit );
- DestroyChildProcess(second.pr, 10 /* [AS] second.useSigterm */ );
- }
- if (first.isr != NULL) {
- RemoveInputSource(first.isr);
- }
- if (second.isr != NULL) {
- RemoveInputSource(second.isr);
- }
-
- ShutDownFrontEnd();
- exit(status);
-}
-
-void
-PauseEvent()
-{
- if (appData.debugMode)
- fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
- if (pausing) {
- pausing = FALSE;
- ModeHighlight();
- if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack) {
- StartClocks();
- } else {
- DisplayBothClocks();
- }
- if (gameMode == PlayFromGameFile) {
- if (appData.timeDelay >= 0)
- AutoPlayGameLoop();
- } else if (gameMode == IcsExamining && pauseExamInvalid) {
- Reset(FALSE, TRUE);
- SendToICS(ics_prefix);
- SendToICS("refresh\n");
- } else if (currentMove < forwardMostMove) {
- ForwardInner(forwardMostMove);
- }
- pauseExamInvalid = FALSE;
- } else {
- switch (gameMode) {
- default:
- return;
- case IcsExamining:
- pauseExamForwardMostMove = forwardMostMove;
- pauseExamInvalid = FALSE;
- /* fall through */
- case IcsObserving:
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- pausing = TRUE;
- ModeHighlight();
- return;
- case PlayFromGameFile:
- (void) StopLoadGameTimer();
- pausing = TRUE;
- ModeHighlight();
- break;
- case BeginningOfGame:
- if (appData.icsActive) return;
- /* else fall through */
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- case TwoMachinesPlay:
- if (forwardMostMove == 0)
- return; /* don't pause if no one has moved */
- if ((gameMode == MachinePlaysWhite &&
- !WhiteOnMove(forwardMostMove)) ||
- (gameMode == MachinePlaysBlack &&
- WhiteOnMove(forwardMostMove))) {
- StopClocks();
- }
- pausing = TRUE;
- ModeHighlight();
- break;
- }
- }
-}
-
-void
-EditCommentEvent()
-{
- char title[MSG_SIZ];
-
- if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
- strcpy(title, "Edit comment");
- } else {
- sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,
- WhiteOnMove(currentMove - 1) ? " " : ".. ",
- parseList[currentMove - 1]);
- }
-
- EditCommentPopUp(currentMove, title, commentList[currentMove]);
-}
-
-
-void
-EditTagsEvent()
-{
- char *tags = PGNTags(&gameInfo);
- EditTagsPopUp(tags);
- free(tags);
-}
-
-void
-AnalyzeModeEvent()
-{
- if (appData.noChessProgram || gameMode == AnalyzeMode)
- return;
-
- if (gameMode != AnalyzeFile) {
- EditGameEvent();
- if (gameMode != EditGame) return;
- ResurrectChessProgram();
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- /*first.maybeThinking = TRUE;*/
- first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- AnalysisPopUp("Analysis",
- "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");
- }
- gameMode = AnalyzeMode;
- pausing = FALSE;
- ModeHighlight();
- SetGameInfo();
-
- StartAnalysisClock();
- GetTimeMark(&lastNodeCountTime);
- lastNodeCount = 0;
-}
-
-void
-AnalyzeFileEvent()
-{
- if (appData.noChessProgram || gameMode == AnalyzeFile)
- return;
-
- if (gameMode != AnalyzeMode) {
- EditGameEvent();
- if (gameMode != EditGame) return;
- ResurrectChessProgram();
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- /*first.maybeThinking = TRUE;*/
- first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- AnalysisPopUp("Analysis",
- "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");
- }
- gameMode = AnalyzeFile;
- pausing = FALSE;
- ModeHighlight();
- SetGameInfo();
-
- StartAnalysisClock();
- GetTimeMark(&lastNodeCountTime);
- lastNodeCount = 0;
-}
-
-void
-MachineWhiteEvent()
-{
- char buf[MSG_SIZ];
-
- if (appData.noChessProgram || (gameMode == MachinePlaysWhite))
- return;
-
-
- if (gameMode == PlayFromGameFile ||
- gameMode == TwoMachinesPlay ||
- gameMode == Training ||
- gameMode == AnalyzeMode ||
- gameMode == EndOfGame)
- EditGameEvent();
-
- if (gameMode == EditPosition)
- EditPositionDone();
-
- if (!WhiteOnMove(currentMove)) {
- DisplayError("It is not White's turn", 0);
- return;
- }
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
- ExitAnalyzeMode();
-
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
- gameMode == AnalyzeFile)
- TruncateGame();
-
- ResurrectChessProgram(); /* in case it isn't running */
- gameMode = MachinePlaysWhite;
- pausing = FALSE;
- ModeHighlight();
- SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
- DisplayTitle(buf);
- if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.black);
- SendToProgram(buf, &first);
- }
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("black\n", &first); /*gnu kludge*/
- }
- SendTimeRemaining(&first, TRUE);
- }
- if (first.useColors) {
- SendToProgram("white\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- SetMachineThinkingEnables();
- first.maybeThinking = TRUE;
- StartClocks();
-
- if (appData.autoFlipView && !flipView) {
- flipView = !flipView;
- DrawPosition(FALSE, NULL);
- }
-}
-
-void
-MachineBlackEvent()
-{
- char buf[MSG_SIZ];
-
- if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
- return;
-
-
- if (gameMode == PlayFromGameFile ||
- gameMode == TwoMachinesPlay ||
- gameMode == Training ||
- gameMode == AnalyzeMode ||
- gameMode == EndOfGame)
- EditGameEvent();
-
- if (gameMode == EditPosition)
- EditPositionDone();
-
- if (WhiteOnMove(currentMove)) {
- DisplayError("It is not Black's turn", 0);
- return;
- }
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
- ExitAnalyzeMode();
-
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
- gameMode == AnalyzeFile)
- TruncateGame();
-
- ResurrectChessProgram(); /* in case it isn't running */
- gameMode = MachinePlaysBlack;
- pausing = FALSE;
- ModeHighlight();
- SetGameInfo();
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
- DisplayTitle(buf);
- if (first.sendName) {
- sprintf(buf, "name %s\n", gameInfo.white);
- SendToProgram(buf, &first);
- }
- if (first.sendTime) {
- if (first.useColors) {
- SendToProgram("white\n", &first); /*gnu kludge*/
- }
- SendTimeRemaining(&first, FALSE);
- }
- if (first.useColors) {
- SendToProgram("black\ngo\n", &first);
- } else {
- SendToProgram("go\n", &first);
- }
- SetMachineThinkingEnables();
- first.maybeThinking = TRUE;
- StartClocks();
-
- if (appData.autoFlipView && flipView) {
- flipView = !flipView;
- DrawPosition(FALSE, NULL);
- }
-}
-
-
-void
-DisplayTwoMachinesTitle()
-{
- char buf[MSG_SIZ];
- if (appData.matchGames > 0) {
- if (first.twoMachinesColor[0] == 'w') {
- sprintf(buf, "%s vs. %s (%d-%d-%d)",
- gameInfo.white, gameInfo.black,
- first.matchWins, second.matchWins,
- matchGame - 1 - (first.matchWins + second.matchWins));
- } else {
- sprintf(buf, "%s vs. %s (%d-%d-%d)",
- gameInfo.white, gameInfo.black,
- second.matchWins, first.matchWins,
- matchGame - 1 - (first.matchWins + second.matchWins));
- }
- } else {
- sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
- }
- DisplayTitle(buf);
-}
-
-void
-TwoMachinesEvent P((void))
-{
- int i;
- char buf[MSG_SIZ];
- ChessProgramState *onmove;
-
- if (appData.noChessProgram) return;
-
- switch (gameMode) {
- case TwoMachinesPlay:
- return;
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
- DisplayError("Wait until your turn,\nor select Move Now", 0);
- return;
- }
- /* fall through */
- case BeginningOfGame:
- case PlayFromGameFile:
- case EndOfGame:
- EditGameEvent();
- if (gameMode != EditGame) return;
- break;
- case EditPosition:
- EditPositionDone();
- break;
- case AnalyzeMode:
- case AnalyzeFile:
- ExitAnalyzeMode();
- break;
- case EditGame:
- default:
- break;
- }
-
- forwardMostMove = currentMove;
- ResurrectChessProgram(); /* in case first program isn't running */
-
- if (second.pr == NULL) {
- StartChessProgram(&second);
- if (second.protocolVersion == 1) {
- TwoMachinesEventIfReady();
- } else {
- /* kludge: allow timeout for initial "feature" command */
- FreezeUI();
- DisplayMessage("", "Starting second chess program");
- ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
- }
- return;
- }
- DisplayMessage("", "");
- InitChessProgram(&second);
- SendToProgram("force\n", &second);
- if (startedFromSetupPosition) {
- SendBoard(&second, backwardMostMove);
- }
- for (i = backwardMostMove; i < forwardMostMove; i++) {
- SendMoveToProgram(i, &second);
- }
-
- gameMode = TwoMachinesPlay;
- pausing = FALSE;
- ModeHighlight();
- SetGameInfo();
- DisplayTwoMachinesTitle();
- firstMove = TRUE;
- if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {
- onmove = &first;
- } else {
- onmove = &second;
- }
-
- SendToProgram(first.computerString, &first);
- if (first.sendName) {
- sprintf(buf, "name %s\n", second.tidy);
- SendToProgram(buf, &first);
- }
- SendToProgram(second.computerString, &second);
- if (second.sendName) {
- sprintf(buf, "name %s\n", first.tidy);
- SendToProgram(buf, &second);
- }
-
- if (!first.sendTime || !second.sendTime) {
- ResetClocks();
- timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove] = blackTimeRemaining;
- }
- if (onmove->sendTime) {
- if (onmove->useColors) {
- SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/
- }
- SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));
- }
- if (onmove->useColors) {
- SendToProgram(onmove->twoMachinesColor, onmove);
- }
- SendToProgram("go\n", onmove);
- onmove->maybeThinking = TRUE;
- SetMachineThinkingEnables();
-
- StartClocks();
-}
-
-void
-TrainingEvent()
-{
- if (gameMode == Training) {
- SetTrainingModeOff();
- gameMode = PlayFromGameFile;
- DisplayMessage("", "Training mode off");
- } else {
- gameMode = Training;
- animateTraining = appData.animate;
-
- /* make sure we are not already at the end of the game */
- if (currentMove < forwardMostMove) {
- SetTrainingModeOn();
- DisplayMessage("", "Training mode on");
- } else {
- gameMode = PlayFromGameFile;
- DisplayError("Already at end of game", 0);
- }
- }
- ModeHighlight();
-}
-
-void
-IcsClientEvent()
-{
- if (!appData.icsActive) return;
- switch (gameMode) {
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- case IcsObserving:
- case IcsIdle:
- case BeginningOfGame:
- case IcsExamining:
- return;
-
- case EditGame:
- break;
-
- case EditPosition:
- EditPositionDone();
- break;
-
- case AnalyzeMode:
- case AnalyzeFile:
- ExitAnalyzeMode();
- break;
-
- default:
- EditGameEvent();
- break;
- }
-
- gameMode = IcsIdle;
- ModeHighlight();
- return;
-}
-
-
-void
-EditGameEvent()
-{
- int i;
-
- switch (gameMode) {
- case Training:
- SetTrainingModeOff();
- break;
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- case BeginningOfGame:
- SendToProgram("force\n", &first);
- SetUserThinkingEnables();
- break;
- case PlayFromGameFile:
- (void) StopLoadGameTimer();
- if (gameFileFP != NULL) {
- gameFileFP = NULL;
- }
- break;
- case EditPosition:
- EditPositionDone();
- break;
- case AnalyzeMode:
- case AnalyzeFile:
- ExitAnalyzeMode();
- SendToProgram("force\n", &first);
- break;
- case TwoMachinesPlay:
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
- ResurrectChessProgram();
- SetUserThinkingEnables();
- break;
- case EndOfGame:
- ResurrectChessProgram();
- break;
- case IcsPlayingBlack:
- case IcsPlayingWhite:
- DisplayError("Warning: You are still playing a game", 0);
- break;
- case IcsObserving:
- DisplayError("Warning: You are still observing a game", 0);
- break;
- case IcsExamining:
- DisplayError("Warning: You are still examining a game", 0);
- break;
- case IcsIdle:
- break;
- case EditGame:
- default:
- return;
- }
-
- pausing = FALSE;
- StopClocks();
- first.offeredDraw = second.offeredDraw = 0;
-
- if (gameMode == PlayFromGameFile) {
- whiteTimeRemaining = timeRemaining[0][currentMove];
- blackTimeRemaining = timeRemaining[1][currentMove];
- DisplayTitle("");
- }
-
- if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack ||
- gameMode == TwoMachinesPlay ||
- gameMode == EndOfGame) {
- i = forwardMostMove;
- while (i > currentMove) {
- SendToProgram("undo\n", &first);
- i--;
- }
- whiteTimeRemaining = timeRemaining[0][currentMove];
- blackTimeRemaining = timeRemaining[1][currentMove];
- DisplayBothClocks();
- if (whiteFlag || blackFlag) {
- whiteFlag = blackFlag = 0;
- }
- DisplayTitle("");
- }
-
- gameMode = EditGame;
- ModeHighlight();
- SetGameInfo();
-}
-
-
-void
-EditPositionEvent()
-{
- if (gameMode == EditPosition) {
- EditGameEvent();
- return;
- }
-
- EditGameEvent();
- if (gameMode != EditGame) return;
-
- gameMode = EditPosition;
- ModeHighlight();
- SetGameInfo();
- if (currentMove > 0)
- CopyBoard(boards[0], boards[currentMove]);
-
- blackPlaysFirst = !WhiteOnMove(currentMove);
- ResetClocks();
- currentMove = forwardMostMove = backwardMostMove = 0;
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
- DisplayMove(-1);
-}
-
-void
-ExitAnalyzeMode()
-{
- if (first.analysisSupport && first.analyzing) {
- SendToProgram("exit\n", &first);
- first.analyzing = FALSE;
- }
- AnalysisPopDown();
- thinkOutput[0] = NULLCHAR;
-}
-
-void
-EditPositionDone()
-{
- startedFromSetupPosition = TRUE;
- InitChessProgram(&first);
- SendToProgram("force\n", &first);
- if (blackPlaysFirst) {
- strcpy(moveList[0], "");
- strcpy(parseList[0], "");
- currentMove = forwardMostMove = backwardMostMove = 1;
- CopyBoard(boards[1], boards[0]);
- } else {
- currentMove = forwardMostMove = backwardMostMove = 0;
- }
- SendBoard(&first, forwardMostMove);
- DisplayTitle("");
- timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove] = blackTimeRemaining;
- gameMode = EditGame;
- ModeHighlight();
- HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
- ClearHighlights(); /* [AS] */
-}
-
-/* Pause for `ms' milliseconds */
-/* !! Ugh, this is a kludge. Fix it sometime. --tpm */
-void
-TimeDelay(ms)
- long ms;
-{
- TimeMark m1, m2;
-
- GetTimeMark(&m1);
- do {
- GetTimeMark(&m2);
- } while (SubtractTimeMarks(&m2, &m1) < ms);
-}
-
-/* !! Ugh, this is a kludge. Fix it sometime. --tpm */
-void
-SendMultiLineToICS(buf)
- char *buf;
-{
- char temp[MSG_SIZ+1], *p;
- int len;
-
- len = strlen(buf);
- if (len > MSG_SIZ)
- len = MSG_SIZ;
-
- strncpy(temp, buf, len);
- temp[len] = 0;
-
- p = temp;
- while (*p) {
- if (*p == '\n' || *p == '\r')
- *p = ' ';
- ++p;
- }
-
- strcat(temp, "\n");
- SendToICS(temp);
- SendToPlayer(temp, strlen(temp));
-}
-
-void
-SetWhiteToPlayEvent()
-{
- if (gameMode == EditPosition) {
- blackPlaysFirst = FALSE;
- DisplayBothClocks(); /* works because currentMove is 0 */
- } else if (gameMode == IcsExamining) {
- SendToICS(ics_prefix);
- SendToICS("tomove white\n");
- }
-}
-
-void
-SetBlackToPlayEvent()
-{
- if (gameMode == EditPosition) {
- blackPlaysFirst = TRUE;
- currentMove = 1; /* kludge */
- DisplayBothClocks();
- currentMove = 0;
- } else if (gameMode == IcsExamining) {
- SendToICS(ics_prefix);
- SendToICS("tomove black\n");
- }
-}
-
-void
-EditPositionMenuEvent(selection, x, y)
- ChessSquare selection;
- int x, y;
-{
- char buf[MSG_SIZ];
-
- if (gameMode != EditPosition && gameMode != IcsExamining) return;
-
- switch (selection) {
- case ClearBoard:
- if (gameMode == IcsExamining && ics_type == ICS_FICS) {
- SendToICS(ics_prefix);
- SendToICS("bsetup clear\n");
- } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {
- SendToICS(ics_prefix);
- SendToICS("clearboard\n");
- } else {
- for (x = 0; x < BOARD_SIZE; x++) {
- for (y = 0; y < BOARD_SIZE; y++) {
- if (gameMode == IcsExamining) {
- if (boards[currentMove][y][x] != EmptySquare) {
- sprintf(buf, "%sx@%c%c\n", ics_prefix,
- 'a' + x, '1' + y);
- SendToICS(buf);
- }
- } else {
- boards[0][y][x] = EmptySquare;
- }
- }
- }
- }
- if (gameMode == EditPosition) {
- DrawPosition(FALSE, boards[0]);
- }
- break;
-
- case WhitePlay:
- SetWhiteToPlayEvent();
- break;
-
- case BlackPlay:
- SetBlackToPlayEvent();
- break;
-
- case EmptySquare:
- if (gameMode == IcsExamining) {
- sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, '1' + y);
- SendToICS(buf);
- } else {
- boards[0][y][x] = EmptySquare;
- DrawPosition(FALSE, boards[0]);
- }
- break;
-
- default:
- if (gameMode == IcsExamining) {
- sprintf(buf, "%s%c@%c%c\n", ics_prefix,
- PieceToChar(selection), 'a' + x, '1' + y);
- SendToICS(buf);
- } else {
- boards[0][y][x] = selection;
- DrawPosition(FALSE, boards[0]);
- }
- break;
- }
-}
-
-
-void
-DropMenuEvent(selection, x, y)
- ChessSquare selection;
- int x, y;
-{
- ChessMove moveType;
-
- switch (gameMode) {
- case IcsPlayingWhite:
- case MachinePlaysBlack:
- if (!WhiteOnMove(currentMove)) {
- DisplayMoveError("It is Black's turn");
- return;
- }
- moveType = WhiteDrop;
- break;
- case IcsPlayingBlack:
- case MachinePlaysWhite:
- if (WhiteOnMove(currentMove)) {
- DisplayMoveError("It is White's turn");
- return;
- }
- moveType = BlackDrop;
- break;
- case EditGame:
- moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
- break;
- default:
- return;
- }
-
- if (moveType == BlackDrop && selection < BlackPawn) {
- selection = (ChessSquare) ((int) selection
- + (int) BlackPawn - (int) WhitePawn);
- }
- if (boards[currentMove][y][x] != EmptySquare) {
- DisplayMoveError("That square is occupied");
- return;
- }
-
- FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);
-}
-
-void
-AcceptEvent()
-{
- /* Accept a pending offer of any kind from opponent */
-
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("accept\n");
- } else if (cmailMsgLoaded) {
- if (currentMove == cmailOldMove &&
- commentList[cmailOldMove] != NULL &&
- StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
- "Black offers a draw" : "White offers a draw")) {
- TruncateGame();
- GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
- } else {
- DisplayError("There is no pending offer on this move", 0);
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
- }
- } else {
- /* Not used for offers from chess program */
- }
-}
-
-void
-DeclineEvent()
-{
- /* Decline a pending offer of any kind from opponent */
-
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("decline\n");
- } else if (cmailMsgLoaded) {
- if (currentMove == cmailOldMove &&
- commentList[cmailOldMove] != NULL &&
- StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
- "Black offers a draw" : "White offers a draw")) {
-#ifdef NOTDEF
- AppendComment(cmailOldMove, "Draw declined");
- DisplayComment(cmailOldMove - 1, "Draw declined");
-#endif /*NOTDEF*/
- } else {
- DisplayError("There is no pending offer on this move", 0);
- }
- } else {
- /* Not used for offers from chess program */
- }
-}
-
-void
-RematchEvent()
-{
- /* Issue ICS rematch command */
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("rematch\n");
- }
-}
-
-void
-CallFlagEvent()
-{
- /* Call your opponent's flag (claim a win on time) */
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("flag\n");
- } else {
- switch (gameMode) {
- default:
- return;
- case MachinePlaysWhite:
- if (whiteFlag) {
- if (blackFlag)
- GameEnds(GameIsDrawn, "Both players ran out of time",
- GE_PLAYER);
- else
- GameEnds(BlackWins, "Black wins on time", GE_PLAYER);
- } else {
- DisplayError("Your opponent is not out of time", 0);
- }
- break;
- case MachinePlaysBlack:
- if (blackFlag) {
- if (whiteFlag)
- GameEnds(GameIsDrawn, "Both players ran out of time",
- GE_PLAYER);
- else
- GameEnds(WhiteWins, "White wins on time", GE_PLAYER);
- } else {
- DisplayError("Your opponent is not out of time", 0);
- }
- break;
- }
- }
-}
-
-void
-DrawEvent()
-{
- /* Offer draw or accept pending draw offer from opponent */
-
- if (appData.icsActive) {
- /* Note: tournament rules require draw offers to be
- made after you make your move but before you punch
- your clock. Currently ICS doesn't let you do that;
- instead, you immediately punch your clock after making
- a move, but you can offer a draw at any time. */
-
- SendToICS(ics_prefix);
- SendToICS("draw\n");
- } else if (cmailMsgLoaded) {
- if (currentMove == cmailOldMove &&
- commentList[cmailOldMove] != NULL &&
- StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
- "Black offers a draw" : "White offers a draw")) {
- GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
- } else if (currentMove == cmailOldMove + 1) {
- char *offer = WhiteOnMove(cmailOldMove) ?
- "White offers a draw" : "Black offers a draw";
- AppendComment(currentMove, offer);
- DisplayComment(currentMove - 1, offer);
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;
- } else {
- DisplayError("You must make your move before offering a draw", 0);
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
- }
- } else if (first.offeredDraw) {
- GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
- } else {
- if (first.sendDrawOffers) {
- SendToProgram("draw\n", &first);
- userOfferedDraw = TRUE;
- }
- }
-}
-
-void
-AdjournEvent()
-{
- /* Offer Adjourn or accept pending Adjourn offer from opponent */
-
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("adjourn\n");
- } else {
- /* Currently GNU Chess doesn't offer or accept Adjourns */
- }
-}
-
-
-void
-AbortEvent()
-{
- /* Offer Abort or accept pending Abort offer from opponent */
-
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("abort\n");
- } else {
- GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);
- }
-}
-
-void
-ResignEvent()
-{
- /* Resign. You can do this even if it's not your turn. */
-
- if (appData.icsActive) {
- SendToICS(ics_prefix);
- SendToICS("resign\n");
- } else {
- switch (gameMode) {
- case MachinePlaysWhite:
- GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
- break;
- case MachinePlaysBlack:
- GameEnds(BlackWins, "White resigns", GE_PLAYER);
- break;
- case EditGame:
- if (cmailMsgLoaded) {
- TruncateGame();
- if (WhiteOnMove(cmailOldMove)) {
- GameEnds(BlackWins, "White resigns", GE_PLAYER);
- } else {
- GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
- }
- cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;
- }
- break;
- default:
- break;
- }
- }
-}
-
-
-void
-StopObservingEvent()
-{
- /* Stop observing current games */
- SendToICS(ics_prefix);
- SendToICS("unobserve\n");
-}
-
-void
-StopExaminingEvent()
-{
- /* Stop observing current game */
- SendToICS(ics_prefix);
- SendToICS("unexamine\n");
-}
-
-void
-ForwardInner(target)
- int target;
-{
- int limit;
-
- if (appData.debugMode)
- fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
- target, currentMove, forwardMostMove);
-
- if (gameMode == EditPosition)
- return;
-
- if (gameMode == PlayFromGameFile && !pausing)
- PauseEvent();
-
- if (gameMode == IcsExamining && pausing)
- limit = pauseExamForwardMostMove;
- else
- limit = forwardMostMove;
-
- if (target > limit) target = limit;
-
- if (target > 0 && moveList[target - 1][0]) {
- int fromX, fromY, toX, toY;
- toX = moveList[target - 1][2] - 'a';
- toY = moveList[target - 1][3] - '1';
- if (moveList[target - 1][1] == '@') {
- if (appData.highlightLastMove) {
- SetHighlights(-1, -1, toX, toY);
- }
- } else {
- fromX = moveList[target - 1][0] - 'a';
- fromY = moveList[target - 1][1] - '1';
- if (target == currentMove + 1) {
- AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
- }
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
- }
- if (gameMode == EditGame || gameMode == AnalyzeMode ||
- gameMode == Training || gameMode == PlayFromGameFile ||
- gameMode == AnalyzeFile) {
- while (currentMove < target) {
- SendMoveToProgram(currentMove++, &first);
- }
- } else {
- currentMove = target;
- }
-
- if (gameMode == EditGame || gameMode == EndOfGame) {
- whiteTimeRemaining = timeRemaining[0][currentMove];
- blackTimeRemaining = timeRemaining[1][currentMove];
- }
- DisplayBothClocks();
- DisplayMove(currentMove - 1);
- DrawPosition(FALSE, boards[currentMove]);
- HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
- if (commentList[currentMove] && !matchMode && gameMode != Training) {
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
-}
-
-
-void
-ForwardEvent()
-{
- if (gameMode == IcsExamining && !pausing) {
- SendToICS(ics_prefix);
- SendToICS("forward\n");
- } else {
- ForwardInner(currentMove + 1);
- }
-}
-
-void
-ToEndEvent()
-{
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- /* to optimze, we temporarily turn off analysis mode while we feed
- * the remaining moves to the engine. Otherwise we get analysis output
- * after each move.
- */
- if (first.analysisSupport) {
- SendToProgram("exit\nforce\n", &first);
- first.analyzing = FALSE;
- }
- }
-
- if (gameMode == IcsExamining && !pausing) {
- SendToICS(ics_prefix);
- SendToICS("forward 999999\n");
- } else {
- ForwardInner(forwardMostMove);
- }
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- /* we have fed all the moves, so reactivate analysis mode */
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- /*first.maybeThinking = TRUE;*/
- first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- }
-}
-
-void
-BackwardInner(target)
- int target;
-{
- int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */
-
- if (appData.debugMode)
- fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
- target, currentMove, forwardMostMove);
-
- if (gameMode == EditPosition) return;
- if (currentMove <= backwardMostMove) {
- ClearHighlights();
- DrawPosition(full_redraw, boards[currentMove]);
- return;
- }
- if (gameMode == PlayFromGameFile && !pausing)
- PauseEvent();
-
- if (moveList[target][0]) {
- int fromX, fromY, toX, toY;
- toX = moveList[target][2] - 'a';
- toY = moveList[target][3] - '1';
- if (moveList[target][1] == '@') {
- if (appData.highlightLastMove) {
- SetHighlights(-1, -1, toX, toY);
- }
- } else {
- fromX = moveList[target][0] - 'a';
- fromY = moveList[target][1] - '1';
- if (target == currentMove - 1) {
- AnimateMove(boards[currentMove], toX, toY, fromX, fromY);
- }
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
- }
- }
- if (gameMode == EditGame || gameMode==AnalyzeMode ||
- gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
- while (currentMove > target) {
- SendToProgram("undo\n", &first);
- currentMove--;
- }
- } else {
- currentMove = target;
- }
-
- if (gameMode == EditGame || gameMode == EndOfGame) {
- whiteTimeRemaining = timeRemaining[0][currentMove];
- blackTimeRemaining = timeRemaining[1][currentMove];
- }
- DisplayBothClocks();
- DisplayMove(currentMove - 1);
- DrawPosition(full_redraw, boards[currentMove]);
- HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
- if (commentList[currentMove] != NULL) {
- DisplayComment(currentMove - 1, commentList[currentMove]);
- }
-}
-
-void
-BackwardEvent()
-{
- if (gameMode == IcsExamining && !pausing) {
- SendToICS(ics_prefix);
- SendToICS("backward\n");
- } else {
- BackwardInner(currentMove - 1);
- }
-}
-
-void
-ToStartEvent()
-{
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- /* to optimze, we temporarily turn off analysis mode while we undo
- * all the moves. Otherwise we get analysis output after each undo.
- */
- if (first.analysisSupport) {
- SendToProgram("exit\nforce\n", &first);
- first.analyzing = FALSE;
- }
- }
-
- if (gameMode == IcsExamining && !pausing) {
- SendToICS(ics_prefix);
- SendToICS("backward 999999\n");
- } else {
- BackwardInner(backwardMostMove);
- }
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- /* we have fed all the moves, so reactivate analysis mode */
- SendToProgram("analyze\n", &first);
- first.analyzing = TRUE;
- /*first.maybeThinking = TRUE;*/
- first.maybeThinking = FALSE; /* avoid killing GNU Chess */
- }
-}
-
-void
-ToNrEvent(int to)
-{
- if (gameMode == PlayFromGameFile && !pausing) PauseEvent();
- if (to >= forwardMostMove) to = forwardMostMove;
- if (to <= backwardMostMove) to = backwardMostMove;
- if (to < currentMove) {
- BackwardInner(to);
- } else {
- ForwardInner(to);
- }
-}
-
-void
-RevertEvent()
-{
- if (gameMode != IcsExamining) {
- DisplayError("You are not examining a game", 0);
- return;
- }
- if (pausing) {
- DisplayError("You can't revert while pausing", 0);
- return;
- }
- SendToICS(ics_prefix);
- SendToICS("revert\n");
-}
-
-void
-RetractMoveEvent()
-{
- switch (gameMode) {
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
- DisplayError("Wait until your turn,\nor select Move Now", 0);
- return;
- }
- if (forwardMostMove < 2) return;
- currentMove = forwardMostMove = forwardMostMove - 2;
- whiteTimeRemaining = timeRemaining[0][currentMove];
- blackTimeRemaining = timeRemaining[1][currentMove];
- DisplayBothClocks();
- DisplayMove(currentMove - 1);
- ClearHighlights();/*!! could figure this out*/
- DrawPosition(TRUE, boards[currentMove]); /* [AS] Changed to full redraw! */
- SendToProgram("remove\n", &first);
- /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
- break;
-
- case BeginningOfGame:
- default:
- break;
-
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {
- SendToICS(ics_prefix);
- SendToICS("takeback 2\n");
- } else {
- SendToICS(ics_prefix);
- SendToICS("takeback 1\n");
- }
- break;
- }
-}
-
-void
-MoveNowEvent()
-{
- ChessProgramState *cps;
-
- switch (gameMode) {
- case MachinePlaysWhite:
- if (!WhiteOnMove(forwardMostMove)) {
- DisplayError("It is your turn", 0);
- return;
- }
- cps = &first;
- break;
- case MachinePlaysBlack:
- if (WhiteOnMove(forwardMostMove)) {
- DisplayError("It is your turn", 0);
- return;
- }
- cps = &first;
- break;
- case TwoMachinesPlay:
- if (WhiteOnMove(forwardMostMove) ==
- (first.twoMachinesColor[0] == 'w')) {
- cps = &first;
- } else {
- cps = &second;
- }
- break;
- case BeginningOfGame:
- default:
- return;
- }
- SendToProgram("?\n", cps);
-}
-
-void
-TruncateGameEvent()
-{
- EditGameEvent();
- if (gameMode != EditGame) return;
- TruncateGame();
-}
-
-void
-TruncateGame()
-{
- if (forwardMostMove > currentMove) {
- if (gameInfo.resultDetails != NULL) {
- free(gameInfo.resultDetails);
- gameInfo.resultDetails = NULL;
- gameInfo.result = GameUnfinished;
- }
- forwardMostMove = currentMove;
- HistorySet(parseList, backwardMostMove, forwardMostMove,
- currentMove-1);
- }
-}
-
-void
-HintEvent()
-{
- if (appData.noChessProgram) return;
- switch (gameMode) {
- case MachinePlaysWhite:
- if (WhiteOnMove(forwardMostMove)) {
- DisplayError("Wait until your turn", 0);
- return;
- }
- break;
- case BeginningOfGame:
- case MachinePlaysBlack:
- if (!WhiteOnMove(forwardMostMove)) {
- DisplayError("Wait until your turn", 0);
- return;
- }
- break;
- default:
- DisplayError("No hint available", 0);
- return;
- }
- SendToProgram("hint\n", &first);
- hintRequested = TRUE;
-}
-
-void
-BookEvent()
-{
- if (appData.noChessProgram) return;
- switch (gameMode) {
- case MachinePlaysWhite:
- if (WhiteOnMove(forwardMostMove)) {
- DisplayError("Wait until your turn", 0);
- return;
- }
- break;
- case BeginningOfGame:
- case MachinePlaysBlack:
- if (!WhiteOnMove(forwardMostMove)) {
- DisplayError("Wait until your turn", 0);
- return;
- }
- break;
- case EditPosition:
- EditPositionDone();
- break;
- case TwoMachinesPlay:
- return;
- default:
- break;
- }
- SendToProgram("bk\n", &first);
- bookOutput[0] = NULLCHAR;
- bookRequested = TRUE;
-}
-
-void
-AboutGameEvent()
-{
- char *tags = PGNTags(&gameInfo);
- TagsPopUp(tags, CmailMsg());
- free(tags);
-}
-
-/* end button procedures */
-
-void
-PrintPosition(fp, move)
- FILE *fp;
- int move;
-{
- int i, j;
-
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- for (j = 0; j < BOARD_SIZE; j++) {
- char c = PieceToChar(boards[move][i][j]);
- fputc(c == 'x' ? '.' : c, fp);
- fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
- }
- }
- if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
- fprintf(fp, "white to play\n");
- else
- fprintf(fp, "black to play\n");
-}
-
-void
-PrintOpponents(fp)
- FILE *fp;
-{
- if (gameInfo.white != NULL) {
- fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);
- } else {
- fprintf(fp, "\n");
- }
-}
-
-/* Find last component of program's own name, using some heuristics */
-void
-TidyProgramName(prog, host, buf)
- char *prog, *host, buf[MSG_SIZ];
-{
- char *p, *q;
- int local = (strcmp(host, "localhost") == 0);
- while (!local && (p = strchr(prog, ';')) != NULL) {
- p++;
- while (*p == ' ') p++;
- prog = p;
- }
- if (*prog == '"' || *prog == '\'') {
- q = strchr(prog + 1, *prog);
- } else {
- q = strchr(prog, ' ');
- }
- if (q == NULL) q = prog + strlen(prog);
- p = q;
- while (p >= prog && *p != '/' && *p != '\\') p--;
- p++;
- if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
- memcpy(buf, p, q - p);
- buf[q - p] = NULLCHAR;
- if (!local) {
- strcat(buf, "@");
- strcat(buf, host);
- }
-}
-
-char *
-TimeControlTagValue()
-{
- char buf[MSG_SIZ];
- if (!appData.clockMode) {
- strcpy(buf, "-");
- } else if (movesPerSession > 0) {
- sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);
- } else if (timeIncrement == 0) {
- sprintf(buf, "%ld", timeControl/1000);
- } else {
- sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);
- }
- return StrSave(buf);
-}
-
-void
-SetGameInfo()
-{
- /* This routine is used only for certain modes */
- VariantClass v = gameInfo.variant;
- ClearGameInfo(&gameInfo);
- gameInfo.variant = v;
-
- switch (gameMode) {
- case MachinePlaysWhite:
- gameInfo.event = StrSave( appData.pgnEventHeader );
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave(first.tidy);
- gameInfo.black = StrSave(UserName());
- gameInfo.timeControl = TimeControlTagValue();
- break;
-
- case MachinePlaysBlack:
- gameInfo.event = StrSave( appData.pgnEventHeader );
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave(UserName());
- gameInfo.black = StrSave(first.tidy);
- gameInfo.timeControl = TimeControlTagValue();
- break;
-
- case TwoMachinesPlay:
- gameInfo.event = StrSave( appData.pgnEventHeader );
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- if (matchGame > 0) {
- char buf[MSG_SIZ];
- sprintf(buf, "%d", matchGame);
- gameInfo.round = StrSave(buf);
- } else {
- gameInfo.round = StrSave("-");
- }
- if (first.twoMachinesColor[0] == 'w') {
- gameInfo.white = StrSave(first.tidy);
- gameInfo.black = StrSave(second.tidy);
- } else {
- gameInfo.white = StrSave(second.tidy);
- gameInfo.black = StrSave(first.tidy);
- }
- gameInfo.timeControl = TimeControlTagValue();
- break;
-
- case EditGame:
- gameInfo.event = StrSave("Edited game");
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave("-");
- gameInfo.black = StrSave("-");
- break;
-
- case EditPosition:
- gameInfo.event = StrSave("Edited position");
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave("-");
- gameInfo.black = StrSave("-");
- break;
-
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- case IcsObserving:
- case IcsExamining:
- break;
-
- case PlayFromGameFile:
- gameInfo.event = StrSave("Game from non-PGN file");
- gameInfo.site = StrSave(HostName());
- gameInfo.date = PGNDate();
- gameInfo.round = StrSave("-");
- gameInfo.white = StrSave("?");
- gameInfo.black = StrSave("?");
- break;
-
- default:
- break;
- }
-}
-
-void
-ReplaceComment(index, text)
- int index;
- char *text;
-{
- int len;
-
- while (*text == '\n') text++;
- len = strlen(text);
- while (len > 0 && text[len - 1] == '\n') len--;
-
- if (commentList[index] != NULL)
- free(commentList[index]);
-
- if (len == 0) {
- commentList[index] = NULL;
- return;
- }
- commentList[index] = (char *) malloc(len + 2);
- strncpy(commentList[index], text, len);
- commentList[index][len] = '\n';
- commentList[index][len + 1] = NULLCHAR;
-}
-
-void
-CrushCRs(text)
- char *text;
-{
- char *p = text;
- char *q = text;
- char ch;
-
- do {
- ch = *p++;
- if (ch == '\r') continue;
- *q++ = ch;
- } while (ch != '\0');
-}
-
-void
-AppendComment(index, text)
- int index;
- char *text;
-{
- int oldlen, len;
- char *old;
-
- GetInfoFromComment( index, text );
-
- CrushCRs(text);
- while (*text == '\n') text++;
- len = strlen(text);
- while (len > 0 && text[len - 1] == '\n') len--;
-
- if (len == 0) return;
-
- if (commentList[index] != NULL) {
- old = commentList[index];
- oldlen = strlen(old);
- commentList[index] = (char *) malloc(oldlen + len + 2);
- strcpy(commentList[index], old);
- free(old);
- strncpy(&commentList[index][oldlen], text, len);
- commentList[index][oldlen + len] = '\n';
- commentList[index][oldlen + len + 1] = NULLCHAR;
- } else {
- commentList[index] = (char *) malloc(len + 2);
- strncpy(commentList[index], text, len);
- commentList[index][len] = '\n';
- commentList[index][len + 1] = NULLCHAR;
- }
-}
-
-static char * FindStr( char * text, char * sub_text )
-{
- char * result = strstr( text, sub_text );
-
- if( result != NULL ) {
- result += strlen( sub_text );
- }
-
- return result;
-}
-
-/* [AS] Try to extract PV info from PGN comment */
-void GetInfoFromComment( int index, char * text )
-{
- if( text != NULL && index > 0 ) {
- int score = 0;
- int depth = 0;
- int time = -1;
- char * s_eval = FindStr( text, "[%eval " );
- char * s_emt = FindStr( text, "[%emt " );
-
- if( s_eval != NULL || s_emt != NULL ) {
- /* New style */
- char delim;
-
- if( s_eval != NULL ) {
- if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {
- return;
- }
-
- if( delim != ']' ) {
- return;
- }
- }
-
- if( s_emt != NULL ) {
- }
- }
- else {
- /* We expect something like: [+|-]nnn.nn/dd */
- char * sep = strchr( text, '/' );
- int score_lo = 0;
-
- if( sep == NULL || sep < (text+4) ) {
- return;
- }
-
- if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
- return;
- }
-
- if( score_lo < 0 || score_lo >= 100 ) {
- return;
- }
-
- score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;
- }
-
- if( depth <= 0 ) {
- return;
- }
-
- if( time < 0 ) {
- time = -1;
- }
-
- pvInfoList[index-1].depth = depth;
- pvInfoList[index-1].score = score;
- pvInfoList[index-1].time = time;
- }
-}
-
-void
-SendToProgram(message, cps)
- char *message;
- ChessProgramState *cps;
-{
- int count, outCount, error;
- char buf[MSG_SIZ];
-
- if (cps->pr == NULL) return;
- Attention(cps);
-
- if (appData.debugMode) {
- TimeMark now;
- GetTimeMark(&now);
- fprintf(debugFP, "%ld >%-6s: %s",
- SubtractTimeMarks(&now, &programStartTime),
- cps->which, message);
- }
-
- count = strlen(message);
- outCount = OutputToProcess(cps->pr, message, count, &error);
- if (outCount < count && !exiting) {
- sprintf(buf, "Error writing to %s chess program", cps->which);
- DisplayFatalError(buf, error, 1);
- }
-}
-
-void
-ReceiveFromProgram(isr, closure, message, count, error)
- InputSourceRef isr;
- VOIDSTAR closure;
- char *message;
- int count;
- int error;
-{
- char *end_str;
- char buf[MSG_SIZ];
- ChessProgramState *cps = (ChessProgramState *)closure;
-
- if (isr != cps->isr) return; /* Killed intentionally */
- if (count <= 0) {
- if (count == 0) {
- sprintf(buf,
- "Error: %s chess program (%s) exited unexpectedly",
- cps->which, cps->program);
- RemoveInputSource(cps->isr);
- DisplayFatalError(buf, 0, 1);
- } else {
- sprintf(buf,
- "Error reading from %s chess program (%s)",
- cps->which, cps->program);
- RemoveInputSource(cps->isr);
-
- /* [AS] Program is misbehaving badly... kill it */
- if( count == -2 ) {
- DestroyChildProcess( cps->pr, 9 );
- cps->pr = NoProc;
- }
-
- DisplayFatalError(buf, error, 1);
- }
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);
- return;
- }
-
- if ((end_str = strchr(message, '\r')) != NULL)
- *end_str = NULLCHAR;
- if ((end_str = strchr(message, '\n')) != NULL)
- *end_str = NULLCHAR;
-
- if (appData.debugMode) {
- TimeMark now;
- GetTimeMark(&now);
- fprintf(debugFP, "%ld <%-6s: %s\n",
- SubtractTimeMarks(&now, &programStartTime),
- cps->which, message);
- }
- HandleMachineMove(message, cps);
-}
-
-
-void
-SendTimeControl(cps, mps, tc, inc, sd, st)
- ChessProgramState *cps;
- int mps, inc, sd, st;
- long tc;
-{
- char buf[MSG_SIZ];
- int seconds = (tc / 1000) % 60;
-
- if( timeControl_2 > 0 ) {
- if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {
- tc = timeControl_2;
- }
- }
-
- if (st > 0) {
- /* Set exact time per move, normally using st command */
- if (cps->stKludge) {
- /* GNU Chess 4 has no st command; uses level in a nonstandard way */
- seconds = st % 60;
- if (seconds == 0) {
- sprintf(buf, "level 1 %d\n", st/60);
- } else {
- sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);
- }
- } else {
- sprintf(buf, "st %d\n", st);
- }
- } else {
- /* Set conventional or incremental time control, using level command */
- if (seconds == 0) {
- /* Note old gnuchess bug -- minutes:seconds used to not work.
- Fixed in later versions, but still avoid :seconds
- when seconds is 0. */
- sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);
- } else {
- sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,
- seconds, inc/1000);
- }
- }
- SendToProgram(buf, cps);
-
- /* Orthoganally (except for GNU Chess 4), limit time to st seconds */
- /* Orthogonally, limit search to given depth */
- if (sd > 0) {
- if (cps->sdKludge) {
- sprintf(buf, "depth\n%d\n", sd);
- } else {
- sprintf(buf, "sd %d\n", sd);
- }
- SendToProgram(buf, cps);
- }
-}
-
-void
-SendTimeRemaining(cps, machineWhite)
- ChessProgramState *cps;
- int /*boolean*/ machineWhite;
-{
- char message[MSG_SIZ];
- long time, otime;
-
- /* Note: this routine must be called when the clocks are stopped
- or when they have *just* been set or switched; otherwise
- it will be off by the time since the current tick started.
- */
- if (machineWhite) {
- time = whiteTimeRemaining / 10;
- otime = blackTimeRemaining / 10;
- } else {
- time = blackTimeRemaining / 10;
- otime = whiteTimeRemaining / 10;
- }
- if (time <= 0) time = 1;
- if (otime <= 0) otime = 1;
-
- sprintf(message, "time %ld\n", time);
- SendToProgram(message, cps);
-
- sprintf(message, "otim %ld\n", otime);
- SendToProgram(message, cps);
-}
-
-int
-BoolFeature(p, name, loc, cps)
- char **p;
- char *name;
- int *loc;
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- int len = strlen(name);
- int val;
- if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
- (*p) += len + 1;
- sscanf(*p, "%d", &val);
- *loc = (val != 0);
- while (**p && **p != ' ') (*p)++;
- sprintf(buf, "accepted %s\n", name);
- SendToProgram(buf, cps);
- return TRUE;
- }
- return FALSE;
-}
-
-int
-IntFeature(p, name, loc, cps)
- char **p;
- char *name;
- int *loc;
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- int len = strlen(name);
- if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
- (*p) += len + 1;
- sscanf(*p, "%d", loc);
- while (**p && **p != ' ') (*p)++;
- sprintf(buf, "accepted %s\n", name);
- SendToProgram(buf, cps);
- return TRUE;
- }
- return FALSE;
-}
-
-int
-StringFeature(p, name, loc, cps)
- char **p;
- char *name;
- char loc[];
- ChessProgramState *cps;
-{
- char buf[MSG_SIZ];
- int len = strlen(name);
- if (strncmp((*p), name, len) == 0
- && (*p)[len] == '=' && (*p)[len+1] == '\"') {
- (*p) += len + 2;
- sscanf(*p, "%[^\"]", loc);
- while (**p && **p != '\"') (*p)++;
- if (**p == '\"') (*p)++;
- sprintf(buf, "accepted %s\n", name);
- SendToProgram(buf, cps);
- return TRUE;
- }
- return FALSE;
-}
-
-void
-FeatureDone(cps, val)
- ChessProgramState* cps;
- int val;
-{
- DelayedEventCallback cb = GetDelayedEvent();
- if ((cb == InitBackEnd3 && cps == &first) ||
- (cb == TwoMachinesEventIfReady && cps == &second)) {
- CancelDelayedEvent();
- ScheduleDelayedEvent(cb, val ? 1 : 3600000);
- }
- cps->initDone = val;
-}
-
-/* Parse feature command from engine */
-void
-ParseFeatures(args, cps)
- char* args;
- ChessProgramState *cps;
-{
- char *p = args;
- char *q;
- int val;
- char buf[MSG_SIZ];
-
- for (;;) {
- while (*p == ' ') p++;
- if (*p == NULLCHAR) return;
-
- if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
- if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;
- if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;
- if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;
- if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;
- if (BoolFeature(&p, "reuse", &val, cps)) {
- /* Engine can disable reuse, but can't enable it if user said no */
- if (!val) cps->reuse = FALSE;
- continue;
- }
- if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
- if (StringFeature(&p, "myname", &cps->tidy, cps)) {
- if (gameMode == TwoMachinesPlay) {
- DisplayTwoMachinesTitle();
- } else {
- DisplayTitle("");
- }
- continue;
- }
- if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
- if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
- if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
- if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
- if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;
- if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;
- if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
- if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
- if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
- if (IntFeature(&p, "done", &val, cps)) {
- FeatureDone(cps, val);
- continue;
- }
- /* Added by Tord: */
- if (BoolFeature(&p, "fen960", &cps->useFEN960, cps)) continue;
- if (BoolFeature(&p, "oocastle", &cps->useOOCastle, cps)) continue;
- /* End of additions by Tord */
-
- /* unknown feature: complain and skip */
- q = p;
- while (*q && *q != '=') q++;
- sprintf(buf, "rejected %.*s\n", q-p, p);
- SendToProgram(buf, cps);
- p = q;
- if (*p == '=') {
- p++;
- if (*p == '\"') {
- p++;
- while (*p && *p != '\"') p++;
- if (*p == '\"') p++;
- } else {
- while (*p && *p != ' ') p++;
- }
- }
- }
-
-}
-
-void
-PeriodicUpdatesEvent(newState)
- int newState;
-{
- if (newState == appData.periodicUpdates)
- return;
-
- appData.periodicUpdates=newState;
-
- /* Display type changes, so update it now */
- DisplayAnalysis();
-
- /* Get the ball rolling again... */
- if (newState) {
- AnalysisPeriodicEvent(1);
- StartAnalysisClock();
- }
-}
-
-void
-PonderNextMoveEvent(newState)
- int newState;
-{
- if (newState == appData.ponderNextMove) return;
- if (gameMode == EditPosition) EditPositionDone();
- if (newState) {
- SendToProgram("hard\n", &first);
- if (gameMode == TwoMachinesPlay) {
- SendToProgram("hard\n", &second);
- }
- } else {
- SendToProgram("easy\n", &first);
- thinkOutput[0] = NULLCHAR;
- if (gameMode == TwoMachinesPlay) {
- SendToProgram("easy\n", &second);
- }
- }
- appData.ponderNextMove = newState;
-}
-
-void
-ShowThinkingEvent(newState)
- int newState;
-{
- if (newState == appData.showThinking) return;
- if (gameMode == EditPosition) EditPositionDone();
- if (newState) {
- SendToProgram("post\n", &first);
- if (gameMode == TwoMachinesPlay) {
- SendToProgram("post\n", &second);
- }
- } else {
- SendToProgram("nopost\n", &first);
- thinkOutput[0] = NULLCHAR;
- if (gameMode == TwoMachinesPlay) {
- SendToProgram("nopost\n", &second);
- }
- }
- appData.showThinking = newState;
-}
-
-void
-AskQuestionEvent(title, question, replyPrefix, which)
- char *title; char *question; char *replyPrefix; char *which;
-{
- ProcRef pr = (which[0] == '1') ? first.pr : second.pr;
- if (pr == NoProc) return;
- AskQuestion(title, question, replyPrefix, pr);
-}
-
-void
-DisplayMove(moveNumber)
- int moveNumber;
-{
- char message[MSG_SIZ];
- char res[MSG_SIZ];
- char cpThinkOutput[MSG_SIZ];
-
- if (moveNumber == forwardMostMove - 1 ||
- gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-
- safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));
-
- if (strchr(cpThinkOutput, '\n')) {
- *strchr(cpThinkOutput, '\n') = NULLCHAR;
- }
- } else {
- *cpThinkOutput = NULLCHAR;
- }
-
- /* [AS] Hide thinking from human user */
- if( appData.hideThinkingFromHuman && gameMode != TwoMachinesPlay ) {
- *cpThinkOutput = NULLCHAR;
- if( thinkOutput[0] != NULLCHAR ) {
- int i;
-
- for( i=0; i<=hiddenThinkOutputState; i++ ) {
- cpThinkOutput[i] = '.';
- }
- cpThinkOutput[i] = NULLCHAR;
- hiddenThinkOutputState = (hiddenThinkOutputState + 1) % 3;
- }
- }
-
- if (moveNumber == forwardMostMove - 1 &&
- gameInfo.resultDetails != NULL) {
- if (gameInfo.resultDetails[0] == NULLCHAR) {
- sprintf(res, " %s", PGNResult(gameInfo.result));
- } else {
- sprintf(res, " {%s} %s",
- gameInfo.resultDetails, PGNResult(gameInfo.result));
- }
- } else {
- res[0] = NULLCHAR;
- }
-
- if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
- DisplayMessage(res, cpThinkOutput);
- } else {
- sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,
- WhiteOnMove(moveNumber) ? " " : ".. ",
- parseList[moveNumber], res);
- DisplayMessage(message, cpThinkOutput);
- }
-}
-
-void
-DisplayAnalysisText(text)
- char *text;
-{
- char buf[MSG_SIZ];
-
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- sprintf(buf, "Analysis (%s)", first.tidy);
- AnalysisPopUp(buf, text);
- }
-}
-
-static int
-only_one_move(str)
- char *str;
-{
- while (*str && isspace(*str)) ++str;
- while (*str && !isspace(*str)) ++str;
- if (!*str) return 1;
- while (*str && isspace(*str)) ++str;
- if (!*str) return 1;
- return 0;
-}
-
-void
-DisplayAnalysis()
-{
- char buf[MSG_SIZ];
- char lst[MSG_SIZ / 2];
- double nps;
- static char *xtra[] = { "", " (--)", " (++)" };
- int h, m, s, cs;
-
- if (programStats.time == 0) {
- programStats.time = 1;
- }
-
- if (programStats.got_only_move) {
- safeStrCpy(buf, programStats.movelist, sizeof(buf));
- } else {
- safeStrCpy( lst, programStats.movelist, sizeof(lst));
-
- nps = (((double)programStats.nodes) /
- (((double)programStats.time)/100.0));
-
- cs = programStats.time % 100;
- s = programStats.time / 100;
- h = (s / (60*60));
- s = s - h*60*60;
- m = (s/60);
- s = s - m*60;
-
- if (programStats.moves_left > 0 && appData.periodicUpdates) {
- if (programStats.move_name[0] != NULLCHAR) {
- sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, programStats.move_name,
- ((float)programStats.score)/100.0, lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- programStats.nodes, (int)nps, h, m, s, cs);
- } else {
- sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- programStats.nodes, (int)nps, h, m, s, cs);
- }
- } else {
- sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- programStats.nodes, (int)nps, h, m, s, cs);
- }
- }
- DisplayAnalysisText(buf);
-}
-
-void
-DisplayComment(moveNumber, text)
- int moveNumber;
- char *text;
-{
- char title[MSG_SIZ];
-
- if( appData.autoDisplayComment ) {
- if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
- strcpy(title, "Comment");
- } else {
- sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
- WhiteOnMove(moveNumber) ? " " : ".. ",
- parseList[moveNumber]);
- }
-
- CommentPopUp(title, text);
- }
-}
-
-/* This routine sends a ^C interrupt to gnuchess, to awaken it if it
- * might be busy thinking or pondering. It can be omitted if your
- * gnuchess is configured to stop thinking immediately on any user
- * input. However, that gnuchess feature depends on the FIONREAD
- * ioctl, which does not work properly on some flavors of Unix.
- */
-void
-Attention(cps)
- ChessProgramState *cps;
-{
-#if ATTENTION
- if (!cps->useSigint) return;
- if (appData.noChessProgram || (cps->pr == NoProc)) return;
- switch (gameMode) {
- case MachinePlaysWhite:
- case MachinePlaysBlack:
- case TwoMachinesPlay:
- case IcsPlayingWhite:
- case IcsPlayingBlack:
- case AnalyzeMode:
- case AnalyzeFile:
- /* Skip if we know it isn't thinking */
- if (!cps->maybeThinking) return;
- if (appData.debugMode)
- fprintf(debugFP, "Interrupting %s\n", cps->which);
- InterruptChildProcess(cps->pr);
- cps->maybeThinking = FALSE;
- break;
- default:
- break;
- }
-#endif /*ATTENTION*/
-}
-
-int
-CheckFlags()
-{
- if (whiteTimeRemaining <= 0) {
- if (!whiteFlag) {
- whiteFlag = TRUE;
- if (appData.icsActive) {
- if (appData.autoCallFlag &&
- gameMode == IcsPlayingBlack && !blackFlag) {
- SendToICS(ics_prefix);
- SendToICS("flag\n");
- }
- } else {
- if (blackFlag) {
- DisplayTitle("Both flags fell");
- } else {
- DisplayTitle("White's flag fell");
- if (appData.autoCallFlag) {
- GameEnds(BlackWins, "Black wins on time", GE_XBOARD);
- return TRUE;
- }
- }
- }
- }
- }
- if (blackTimeRemaining <= 0) {
- if (!blackFlag) {
- blackFlag = TRUE;
- if (appData.icsActive) {
- if (appData.autoCallFlag &&
- gameMode == IcsPlayingWhite && !whiteFlag) {
- SendToICS(ics_prefix);
- SendToICS("flag\n");
- }
- } else {
- if (whiteFlag) {
- DisplayTitle("Both flags fell");
- } else {
- DisplayTitle("Black's flag fell");
- if (appData.autoCallFlag) {
- GameEnds(WhiteWins, "White wins on time", GE_XBOARD);
- return TRUE;
- }
- }
- }
- }
- }
- return FALSE;
-}
-
-void
-CheckTimeControl()
-{
- if (!appData.clockMode || appData.icsActive ||
- gameMode == PlayFromGameFile || forwardMostMove == 0) return;
-
- if (timeIncrement >= 0) {
- if (WhiteOnMove(forwardMostMove)) {
- blackTimeRemaining += timeIncrement;
- } else {
- whiteTimeRemaining += timeIncrement;
- }
- }
- /*
- * add time to clocks when time control is achieved
- */
- if (movesPerSession) {
- switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
- case 0:
- /* White made time control */
- whiteTimeRemaining += GetTimeControlForWhite();
- break;
- case 1:
- /* Black made time control */
- blackTimeRemaining += GetTimeControlForBlack();
- break;
- default:
- break;
- }
- }
-}
-
-void
-DisplayBothClocks()
-{
- int wom = gameMode == EditPosition ?
- !blackPlaysFirst : WhiteOnMove(currentMove);
- DisplayWhiteClock(whiteTimeRemaining, wom);
- DisplayBlackClock(blackTimeRemaining, !wom);
-}
-
-
-/* Timekeeping seems to be a portability nightmare. I think everyone
- has ftime(), but I'm really not sure, so I'm including some ifdefs
- to use other calls if you don't. Clocks will be less accurate if
- you have neither ftime nor gettimeofday.
-*/
-
-/* Get the current time as a TimeMark */
-void
-GetTimeMark(tm)
- TimeMark *tm;
-{
-#if HAVE_GETTIMEOFDAY
-
- struct timeval timeVal;
- struct timezone timeZone;
-
- gettimeofday(&timeVal, &timeZone);
- tm->sec = (long) timeVal.tv_sec;
- tm->ms = (int) (timeVal.tv_usec / 1000L);
-
-#else /*!HAVE_GETTIMEOFDAY*/
-#if HAVE_FTIME
-
-#include <sys/timeb.h>
- struct timeb timeB;
-
- ftime(&timeB);
- tm->sec = (long) timeB.time;
- tm->ms = (int) timeB.millitm;
-
-#else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/
- tm->sec = (long) time(NULL);
- tm->ms = 0;
-#endif
-#endif
-}
-
-/* Return the difference in milliseconds between two
- time marks. We assume the difference will fit in a long!
-*/
-long
-SubtractTimeMarks(tm2, tm1)
- TimeMark *tm2, *tm1;
-{
- return 1000L*(tm2->sec - tm1->sec) +
- (long) (tm2->ms - tm1->ms);
-}
-
-
-/*
- * Code to manage the game clocks.
- *
- * In tournament play, black starts the clock and then white makes a move.
- * We give the human user a slight advantage if he is playing white---the
- * clocks don't run until he makes his first move, so it takes zero time.
- * Also, we don't account for network lag, so we could get out of sync
- * with GNU Chess's clock -- but then, referees are always right.
- */
-
-static TimeMark tickStartTM;
-static long intendedTickLength;
-
-long
-NextTickLength(timeRemaining)
- long timeRemaining;
-{
- long nominalTickLength, nextTickLength;
-
- if (timeRemaining > 0L && timeRemaining <= 10000L)
- nominalTickLength = 100L;
- else
- nominalTickLength = 1000L;
- nextTickLength = timeRemaining % nominalTickLength;
- if (nextTickLength <= 0) nextTickLength += nominalTickLength;
-
- return nextTickLength;
-}
-
-/* Stop clocks and reset to a fresh time control */
-void
-ResetClocks()
-{
- (void) StopClockTimer();
- if (appData.icsActive) {
- whiteTimeRemaining = blackTimeRemaining = 0;
- } else {
- whiteTimeRemaining = GetTimeControlForWhite();
- blackTimeRemaining = GetTimeControlForBlack();
- }
- if (whiteFlag || blackFlag) {
- DisplayTitle("");
- whiteFlag = blackFlag = FALSE;
- }
- DisplayBothClocks();
-}
-
-#define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
-
-/* Decrement running clock by amount of time that has passed */
-void
-DecrementClocks()
-{
- long timeRemaining;
- long lastTickLength, fudge;
- TimeMark now;
-
- if (!appData.clockMode) return;
- if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;
-
- GetTimeMark(&now);
-
- lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
-
- /* Fudge if we woke up a little too soon */
- fudge = intendedTickLength - lastTickLength;
- if (fudge < 0 || fudge > FUDGE) fudge = 0;
-
- if (WhiteOnMove(forwardMostMove)) {
- timeRemaining = whiteTimeRemaining -= lastTickLength;
- DisplayWhiteClock(whiteTimeRemaining - fudge,
- WhiteOnMove(currentMove));
- } else {
- timeRemaining = blackTimeRemaining -= lastTickLength;
- DisplayBlackClock(blackTimeRemaining - fudge,
- !WhiteOnMove(currentMove));
- }
-
- if (CheckFlags()) return;
-
- tickStartTM = now;
- intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
- StartClockTimer(intendedTickLength);
-
- /* if the time remaining has fallen below the alarm threshold, sound the
- * alarm. if the alarm has sounded and (due to a takeback or time control
- * with increment) the time remaining has increased to a level above the
- * threshold, reset the alarm so it can sound again.
- */
-
- if (appData.icsActive && appData.icsAlarm) {
-
- /* make sure we are dealing with the user's clock */
- if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||
- ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))
- )) return;
-
- if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
- alarmSounded = FALSE;
- } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
- PlayAlarmSound();
- alarmSounded = TRUE;
- }
- }
-}
-
-
-/* A player has just moved, so stop the previously running
- clock and (if in clock mode) start the other one.
- We redisplay both clocks in case we're in ICS mode, because
- ICS gives us an update to both clocks after every move.
- Note that this routine is called *after* forwardMostMove
- is updated, so the last fractional tick must be subtracted
- from the color that is *not* on move now.
-*/
-void
-SwitchClocks()
-{
- long lastTickLength;
- TimeMark now;
- int flagged = FALSE;
-
- GetTimeMark(&now);
-
- if (StopClockTimer() && appData.clockMode) {
- lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
- if (WhiteOnMove(forwardMostMove)) {
- blackTimeRemaining -= lastTickLength;
- } else {
- whiteTimeRemaining -= lastTickLength;
- }
- flagged = CheckFlags();
- }
- CheckTimeControl();
-
- if (flagged || !appData.clockMode) return;
-
- switch (gameMode) {
- case MachinePlaysBlack:
- case MachinePlaysWhite:
- case BeginningOfGame:
- if (pausing) return;
- break;
-
- case EditGame:
- case PlayFromGameFile:
- case IcsExamining:
- return;
-
- default:
- break;
- }
-
- tickStartTM = now;
- intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
- whiteTimeRemaining : blackTimeRemaining);
- StartClockTimer(intendedTickLength);
-}
-
-
-/* Stop both clocks */
-void
-StopClocks()
-{
- long lastTickLength;
- TimeMark now;
-
- if (!StopClockTimer()) return;
- if (!appData.clockMode) return;
-
- GetTimeMark(&now);
-
- lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
- if (WhiteOnMove(forwardMostMove)) {
- whiteTimeRemaining -= lastTickLength;
- DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
- } else {
- blackTimeRemaining -= lastTickLength;
- DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
- }
- CheckFlags();
-}
-
-/* Start clock of player on move. Time may have been reset, so
- if clock is already running, stop and restart it. */
-void
-StartClocks()
-{
- (void) StopClockTimer(); /* in case it was running already */
- DisplayBothClocks();
- if (CheckFlags()) return;
-
- if (!appData.clockMode) return;
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;
-
- GetTimeMark(&tickStartTM);
- intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
- whiteTimeRemaining : blackTimeRemaining);
- StartClockTimer(intendedTickLength);
-}
-
-char *
-TimeString(ms)
- long ms;
-{
- long second, minute, hour, day;
- char *sign = "";
- static char buf[32];
-
- if (ms > 0 && ms <= 9900) {
- /* convert milliseconds to tenths, rounding up */
- double tenths = floor( ((double)(ms + 99L)) / 100.00 );
-
- sprintf(buf, " %03.1f ", tenths/10.0);
- return buf;
- }
-
- /* convert milliseconds to seconds, rounding up */
- /* use floating point to avoid strangeness of integer division
- with negative dividends on many machines */
- second = (long) floor(((double) (ms + 999L)) / 1000.0);
-
- if (second < 0) {
- sign = "-";
- second = -second;
- }
-
- day = second / (60 * 60 * 24);
- second = second % (60 * 60 * 24);
- hour = second / (60 * 60);
- second = second % (60 * 60);
- minute = second / 60;
- second = second % 60;
-
- if (day > 0)
- sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",
- sign, day, hour, minute, second);
- else if (hour > 0)
- sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
- else
- sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);
-
- return buf;
-}
-
-
-/*
- * This is necessary because some C libraries aren't ANSI C compliant yet.
- */
-char *
-StrStr(string, match)
- char *string, *match;
-{
- int i, length;
-
- length = strlen(match);
-
- for (i = strlen(string) - length; i >= 0; i--, string++)
- if (!strncmp(match, string, length))
- return string;
-
- return NULL;
-}
-
-char *
-StrCaseStr(string, match)
- char *string, *match;
-{
- int i, j, length;
-
- length = strlen(match);
-
- for (i = strlen(string) - length; i >= 0; i--, string++) {
- for (j = 0; j < length; j++) {
- if (ToLower(match[j]) != ToLower(string[j]))
- break;
- }
- if (j == length) return string;
- }
-
- return NULL;
-}
-
-#ifndef _amigados
-int
-StrCaseCmp(s1, s2)
- char *s1, *s2;
-{
- char c1, c2;
-
- for (;;) {
- c1 = ToLower(*s1++);
- c2 = ToLower(*s2++);
- if (c1 > c2) return 1;
- if (c1 < c2) return -1;
- if (c1 == NULLCHAR) return 0;
- }
-}
-
-
-int
-ToLower(c)
- int c;
-{
- return isupper(c) ? tolower(c) : c;
-}
-
-
-int
-ToUpper(c)
- int c;
-{
- return islower(c) ? toupper(c) : c;
-}
-#endif /* !_amigados */
-
-char *
-StrSave(s)
- char *s;
-{
- char *ret;
-
- if ((ret = (char *) malloc(strlen(s) + 1))) {
- strcpy(ret, s);
- }
- return ret;
-}
-
-char *
-StrSavePtr(s, savePtr)
- char *s, **savePtr;
-{
- if (*savePtr) {
- free(*savePtr);
- }
- if ((*savePtr = (char *) malloc(strlen(s) + 1))) {
- strcpy(*savePtr, s);
- }
- return(*savePtr);
-}
-
-char *
-PGNDate()
-{
- time_t clock;
- struct tm *tm;
- char buf[MSG_SIZ];
-
- clock = time((time_t *)NULL);
- tm = localtime(&clock);
- sprintf(buf, "%04d.%02d.%02d",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
- return StrSave(buf);
-}
-
-
-char *
-PositionToFEN(move, useFEN960)
- int move;
- int useFEN960;
-{
- int i, j, fromX, fromY, toX, toY;
- int whiteToPlay;
- char buf[128];
- char *p, *q;
- int emptycount;
-
- whiteToPlay = (gameMode == EditPosition) ?
- !blackPlaysFirst : (move % 2 == 0);
- p = buf;
-
- /* Piece placement data */
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- emptycount = 0;
- for (j = 0; j < BOARD_SIZE; j++) {
- if (boards[move][i][j] == EmptySquare) {
- emptycount++;
- } else {
- if (emptycount > 0) {
- *p++ = '0' + emptycount;
- emptycount = 0;
- }
- *p++ = PieceToChar(boards[move][i][j]);
- }
- }
- if (emptycount > 0) {
- *p++ = '0' + emptycount;
- emptycount = 0;
- }
- *p++ = '/';
- }
- *(p - 1) = ' ';
-
- /* Active color */
- *p++ = whiteToPlay ? 'w' : 'b';
- *p++ = ' ';
-
- /* HACK: we don't keep track of castling availability, so fake it! */
-
- /* PUSH Fabien & Tord */
-
- /* Declare all potential FRC castling rights (conservative) */
- /* outermost rook on each side of the king */
-
- if( gameInfo.variant == VariantFischeRandom ) {
- int fk, fr;
-
- q = p;
-
- /* White castling rights */
-
- for (fk = 1; fk < 7; fk++) {
-
- if (boards[move][0][fk] == WhiteKing) {
-
- for (fr = 7; fr > fk; fr--) { /* H side */
- if (boards[move][0][fr] == WhiteRook) {
- *p++ = useFEN960 ? 'A' + fr : 'K';
- break;
- }
- }
-
- for (fr = 0; fr < fk; fr++) { /* A side */
- if (boards[move][0][fr] == WhiteRook) {
- *p++ = useFEN960 ? 'A' + fr : 'Q';
- break;
- }
- }
- }
- }
-
- /* Black castling rights */
-
- for (fk = 1; fk < 7; fk++) {
-
- if (boards[move][7][fk] == BlackKing) {
-
- for (fr = 7; fr > fk; fr--) { /* H side */
- if (boards[move][7][fr] == BlackRook) {
- *p++ = useFEN960 ? 'a' + fr : 'k';
- break;
- }
- }
-
- for (fr = 0; fr < fk; fr++) { /* A side */
- if (boards[move][7][fr] == BlackRook) {
- *p++ = useFEN960 ? 'a' + fr : 'q';
- break;
- }
- }
- }
- }
-
- if (q == p) *p++ = '-'; /* No castling rights */
- *p++ = ' ';
- }
- else {
- q = p;
- if (boards[move][0][4] == WhiteKing) {
- if (boards[move][0][7] == WhiteRook) *p++ = 'K';
- if (boards[move][0][0] == WhiteRook) *p++ = 'Q';
- }
- if (boards[move][7][4] == BlackKing) {
- if (boards[move][7][7] == BlackRook) *p++ = 'k';
- if (boards[move][7][0] == BlackRook) *p++ = 'q';
- }
- if (q == p) *p++ = '-';
- *p++ = ' ';
- }
-
- /* POP Fabien & Tord */
-
- /* En passant target square */
- if (move > backwardMostMove) {
- fromX = moveList[move - 1][0] - 'a';
- fromY = moveList[move - 1][1] - '1';
- toX = moveList[move - 1][2] - 'a';
- toY = moveList[move - 1][3] - '1';
- if (fromY == (whiteToPlay ? 6 : 1) &&
- toY == (whiteToPlay ? 4 : 3) &&
- boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
- fromX == toX) {
- /* 2-square pawn move just happened */
- *p++ = toX + 'a';
- *p++ = whiteToPlay ? '6' : '3';
- } else {
- *p++ = '-';
- }
- } else {
- *p++ = '-';
- }
-
- /* We don't keep track of halfmove clock for 50-move rule */
- strcpy(p, " 0 ");
- p += 3;
-
- /* Fullmove number */
- sprintf(p, "%d", (move / 2) + 1);
-
- return StrSave(buf);
-}
-
-Boolean
-ParseFEN(board, blackPlaysFirst, fen)
- Board board;
- int *blackPlaysFirst;
- char *fen;
-{
- int i, j;
- char *p;
- int emptycount;
-
- p = fen;
-
- /* Piece placement data */
- for (i = BOARD_SIZE - 1; i >= 0; i--) {
- j = 0;
- for (;;) {
- if (*p == '/' || *p == ' ') {
- if (*p == '/') p++;
- emptycount = BOARD_SIZE - j;
- while (emptycount--) board[i][j++] = EmptySquare;
- break;
- } else if (isdigit(*p)) {
- emptycount = *p++ - '0';
- if (j + emptycount > BOARD_SIZE) return FALSE;
- while (emptycount--) board[i][j++] = EmptySquare;
- } else if (isalpha(*p)) {
- if (j >= BOARD_SIZE) return FALSE;
- board[i][j++] = CharToPiece(*p++);
- } else {
- return FALSE;
- }
- }
- }
- while (*p == '/' || *p == ' ') p++;
-
- /* Active color */
- switch (*p) {
- case 'w':
- *blackPlaysFirst = FALSE;
- break;
- case 'b':
- *blackPlaysFirst = TRUE;
- break;
- default:
- return FALSE;
- }
-
- /* !!We ignore the rest of the FEN notation */
- return TRUE;
-}
-
-void
-EditPositionPasteFEN(char *fen)
-{
- if (fen != NULL) {
- Board initial_position;
-
- if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {
- DisplayError("Bad FEN position in clipboard", 0);
- return ;
- } else {
- int savedBlackPlaysFirst = blackPlaysFirst;
- EditPositionEvent();
- blackPlaysFirst = savedBlackPlaysFirst;
- CopyBoard(boards[0], initial_position);
- EditPositionDone();
- DisplayBothClocks();
- DrawPosition(FALSE, boards[currentMove]);
- }
- }
-}
+/*\r
+ * backend.c -- Common back end for X and Windows NT versions of\r
+ * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $\r
+ *\r
+ * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
+ * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
+ *\r
+ * The following terms apply to Digital Equipment Corporation's copyright\r
+ * interest in XBoard:\r
+ * ------------------------------------------------------------------------\r
+ * All Rights Reserved\r
+ *\r
+ * Permission to use, copy, modify, and distribute this software and its\r
+ * documentation for any purpose and without fee is hereby granted,\r
+ * provided that the above copyright notice appear in all copies and that\r
+ * both that copyright notice and this permission notice appear in\r
+ * supporting documentation, and that the name of Digital not be\r
+ * used in advertising or publicity pertaining to distribution of the\r
+ * software without specific, written prior permission.\r
+ *\r
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
+ * SOFTWARE.\r
+ * ------------------------------------------------------------------------\r
+ *\r
+ * The following terms apply to the enhanced version of XBoard distributed\r
+ * by the Free Software Foundation:\r
+ * ------------------------------------------------------------------------\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+ * ------------------------------------------------------------------------\r
+ *\r
+ * See the file ChangeLog for a revision history. */\r
+\r
+/* [AS] Also useful here for debugging */\r
+#ifdef WIN32\r
+#include <windows.h>\r
+\r
+#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );\r
+\r
+#else\r
+\r
+#define DoSleep( n )\r
+\r
+#endif\r
+\r
+#include "config.h"\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+#include <errno.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <math.h>\r
+\r
+#if STDC_HEADERS\r
+# include <stdlib.h>\r
+# include <string.h>\r
+#else /* not STDC_HEADERS */\r
+# if HAVE_STRING_H\r
+# include <string.h>\r
+# else /* not HAVE_STRING_H */\r
+# include <strings.h>\r
+# endif /* not HAVE_STRING_H */\r
+#endif /* not STDC_HEADERS */\r
+\r
+#if HAVE_SYS_FCNTL_H\r
+# include <sys/fcntl.h>\r
+#else /* not HAVE_SYS_FCNTL_H */\r
+# if HAVE_FCNTL_H\r
+# include <fcntl.h>\r
+# endif /* HAVE_FCNTL_H */\r
+#endif /* not HAVE_SYS_FCNTL_H */\r
+\r
+#if TIME_WITH_SYS_TIME\r
+# include <sys/time.h>\r
+# include <time.h>\r
+#else\r
+# if HAVE_SYS_TIME_H\r
+# include <sys/time.h>\r
+# else\r
+# include <time.h>\r
+# endif\r
+#endif\r
+\r
+#if defined(_amigados) && !defined(__GNUC__)\r
+struct timezone {\r
+ int tz_minuteswest;\r
+ int tz_dsttime;\r
+};\r
+extern int gettimeofday(struct timeval *, struct timezone *);\r
+#endif\r
+\r
+#if HAVE_UNISTD_H\r
+# include <unistd.h>\r
+#endif\r
+\r
+#include "common.h"\r
+#include "frontend.h"\r
+#include "backend.h"\r
+#include "parser.h"\r
+#include "moves.h"\r
+#if ZIPPY\r
+# include "zippy.h"\r
+#endif\r
+#include "backendz.h"\r
+\r
+/* A point in time */\r
+typedef struct {\r
+ long sec; /* Assuming this is >= 32 bits */\r
+ int ms; /* Assuming this is >= 16 bits */\r
+} TimeMark;\r
+\r
+/* Search stats from chessprogram */\r
+typedef struct {\r
+ char movelist[2*MSG_SIZ]; /* Last PV we were sent */\r
+ int depth; /* Current search depth */\r
+ int nr_moves; /* Total nr of root moves */\r
+ int moves_left; /* Moves remaining to be searched */\r
+ char move_name[MOVE_LEN]; /* Current move being searched, if provided */\r
+ unsigned long nodes; /* # of nodes searched */\r
+ int time; /* Search time (centiseconds) */\r
+ int score; /* Score (centipawns) */\r
+ int got_only_move; /* If last msg was "(only move)" */\r
+ int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */\r
+ int ok_to_send; /* handshaking between send & recv */\r
+ int line_is_book; /* 1 if movelist is book moves */\r
+ int seen_stat; /* 1 if we've seen the stat01: line */\r
+} ChessProgramStats;\r
+\r
+int establish P((void));\r
+void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
+ char *buf, int count, int error));\r
+void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,\r
+ char *buf, int count, int error));\r
+void SendToICS P((char *s));\r
+void SendToICSDelayed P((char *s, long msdelay));\r
+void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,\r
+ int toX, int toY));\r
+void InitPosition P((int redraw));\r
+void HandleMachineMove P((char *message, ChessProgramState *cps));\r
+int AutoPlayOneMove P((void));\r
+int LoadGameOneMove P((ChessMove readAhead));\r
+int LoadGameFromFile P((char *filename, int n, char *title, int useList));\r
+int LoadPositionFromFile P((char *filename, int n, char *title));\r
+int SavePositionToFile P((char *filename));\r
+void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
+ Board board));\r
+void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
+void ShowMove P((int fromX, int fromY, int toX, int toY));\r
+void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
+ /*char*/int promoChar));\r
+void BackwardInner P((int target));\r
+void ForwardInner P((int target));\r
+void GameEnds P((ChessMove result, char *resultDetails, int whosays));\r
+void EditPositionDone P((void));\r
+void PrintOpponents P((FILE *fp));\r
+void PrintPosition P((FILE *fp, int move));\r
+void StartChessProgram P((ChessProgramState *cps));\r
+void SendToProgram P((char *message, ChessProgramState *cps));\r
+void SendMoveToProgram P((int moveNum, ChessProgramState *cps));\r
+void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,\r
+ char *buf, int count, int error));\r
+void SendTimeControl P((ChessProgramState *cps,\r
+ int mps, long tc, int inc, int sd, int st));\r
+char *TimeControlTagValue P((void));\r
+void Attention P((ChessProgramState *cps));\r
+void FeedMovesToProgram P((ChessProgramState *cps, int upto));\r
+void ResurrectChessProgram P((void));\r
+void DisplayComment P((int moveNumber, char *text));\r
+void DisplayMove P((int moveNumber));\r
+void DisplayAnalysis P((void));\r
+\r
+void ParseGameHistory P((char *game));\r
+void ParseBoard12 P((char *string));\r
+void StartClocks P((void));\r
+void SwitchClocks P((void));\r
+void StopClocks P((void));\r
+void ResetClocks P((void));\r
+char *PGNDate P((void));\r
+void SetGameInfo P((void));\r
+Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
+int RegisterMove P((void));\r
+void MakeRegisteredMove P((void));\r
+void TruncateGame P((void));\r
+int looking_at P((char *, int *, char *));\r
+void CopyPlayerNameIntoFileName P((char **, char *));\r
+char *SavePart P((char *));\r
+int SaveGameOldStyle P((FILE *));\r
+int SaveGamePGN P((FILE *));\r
+void GetTimeMark P((TimeMark *));\r
+long SubtractTimeMarks P((TimeMark *, TimeMark *));\r
+int CheckFlags P((void));\r
+long NextTickLength P((long));\r
+void CheckTimeControl P((void));\r
+void show_bytes P((FILE *, char *, int));\r
+int string_to_rating P((char *str));\r
+void ParseFeatures P((char* args, ChessProgramState *cps));\r
+void InitBackEnd3 P((void));\r
+void FeatureDone P((ChessProgramState* cps, int val));\r
+void InitChessProgram P((ChessProgramState *cps));\r
+\r
+void GetInfoFromComment( int, char * );\r
+\r
+extern int tinyLayout, smallLayout;\r
+static ChessProgramStats programStats;\r
+\r
+/* States for ics_getting_history */\r
+#define H_FALSE 0\r
+#define H_REQUESTED 1\r
+#define H_GOT_REQ_HEADER 2\r
+#define H_GOT_UNREQ_HEADER 3\r
+#define H_GETTING_MOVES 4\r
+#define H_GOT_UNWANTED_HEADER 5\r
+\r
+/* whosays values for GameEnds */\r
+#define GE_ICS 0\r
+#define GE_ENGINE 1\r
+#define GE_PLAYER 2\r
+#define GE_FILE 3\r
+#define GE_XBOARD 4\r
+#define GE_ENGINE1 5\r
+#define GE_ENGINE2 6\r
+\r
+/* Maximum number of games in a cmail message */\r
+#define CMAIL_MAX_GAMES 20\r
+\r
+/* Different types of move when calling RegisterMove */\r
+#define CMAIL_MOVE 0\r
+#define CMAIL_RESIGN 1\r
+#define CMAIL_DRAW 2\r
+#define CMAIL_ACCEPT 3\r
+\r
+/* Different types of result to remember for each game */\r
+#define CMAIL_NOT_RESULT 0\r
+#define CMAIL_OLD_RESULT 1\r
+#define CMAIL_NEW_RESULT 2\r
+\r
+/* Telnet protocol constants */\r
+#define TN_WILL 0373\r
+#define TN_WONT 0374\r
+#define TN_DO 0375\r
+#define TN_DONT 0376\r
+#define TN_IAC 0377\r
+#define TN_ECHO 0001\r
+#define TN_SGA 0003\r
+#define TN_PORT 23\r
+\r
+/* [AS] */\r
+static char * safeStrCpy( char * dst, const char * src, size_t count )\r
+{\r
+ assert( dst != NULL );\r
+ assert( src != NULL );\r
+ assert( count > 0 );\r
+\r
+ strncpy( dst, src, count );\r
+ dst[ count-1 ] = '\0';\r
+ return dst;\r
+}\r
+\r
+static char * safeStrCat( char * dst, const char * src, size_t count )\r
+{\r
+ size_t dst_len;\r
+\r
+ assert( dst != NULL );\r
+ assert( src != NULL );\r
+ assert( count > 0 );\r
+\r
+ dst_len = strlen(dst);\r
+\r
+ assert( count > dst_len ); /* Buffer size must be greater than current length */\r
+\r
+ safeStrCpy( dst + dst_len, src, count - dst_len );\r
+\r
+ return dst;\r
+}\r
+\r
+/* Fake up flags for now, as we aren't keeping track of castling\r
+ availability yet */\r
+int\r
+PosFlags(index)\r
+{\r
+ int flags = F_ALL_CASTLE_OK;\r
+ if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
+ switch (gameInfo.variant) {\r
+ case VariantSuicide:\r
+ case VariantGiveaway:\r
+ flags |= F_IGNORE_CHECK;\r
+ flags &= ~F_ALL_CASTLE_OK;\r
+ break;\r
+ case VariantAtomic:\r
+ flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
+ break;\r
+ case VariantKriegspiel:\r
+ flags |= F_KRIEGSPIEL_CAPTURE;\r
+ break;\r
+ case VariantNoCastle:\r
+ flags &= ~F_ALL_CASTLE_OK;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ return flags;\r
+}\r
+\r
+FILE *gameFileFP, *debugFP;\r
+\r
+/* \r
+ [AS] Note: sometimes, the sscanf() function is used to parse the input\r
+ into a fixed-size buffer. Because of this, we must be prepared to\r
+ receive strings as long as the size of the input buffer, which is currently\r
+ set to 4K for Windows and 8K for the rest.\r
+ So, we must either allocate sufficiently large buffers here, or\r
+ reduce the size of the input buffer in the input reading part.\r
+*/\r
+\r
+char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];\r
+char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];\r
+char thinkOutput1[MSG_SIZ*10];\r
+\r
+ChessProgramState first, second;\r
+\r
+/* premove variables */\r
+int premoveToX = 0;\r
+int premoveToY = 0;\r
+int premoveFromX = 0;\r
+int premoveFromY = 0;\r
+int premovePromoChar = 0;\r
+int gotPremove = 0;\r
+Boolean alarmSounded;\r
+/* end premove variables */\r
+\r
+#define ICS_GENERIC 0\r
+#define ICS_ICC 1\r
+#define ICS_FICS 2\r
+#define ICS_CHESSNET 3 /* not really supported */\r
+int ics_type = ICS_GENERIC;\r
+char *ics_prefix = "$";\r
+\r
+int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;\r
+int pauseExamForwardMostMove = 0;\r
+int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;\r
+int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];\r
+int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;\r
+int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;\r
+int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;\r
+int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;\r
+int whiteFlag = FALSE, blackFlag = FALSE;\r
+int userOfferedDraw = FALSE;\r
+int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;\r
+int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;\r
+int cmailMoveType[CMAIL_MAX_GAMES];\r
+long ics_clock_paused = 0;\r
+ProcRef icsPR = NoProc, cmailPR = NoProc;\r
+InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;\r
+GameMode gameMode = BeginningOfGame;\r
+char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];\r
+char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];\r
+ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */\r
+int hiddenThinkOutputState = 0; /* [AS] */\r
+int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */\r
+int adjudicateLossPlies = 6;\r
+char white_holding[64], black_holding[64];\r
+TimeMark lastNodeCountTime;\r
+long lastNodeCount=0;\r
+int have_sent_ICS_logon = 0;\r
+int movesPerSession;\r
+long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
+long timeControl_2; /* [AS] Allow separate time controls */\r
+long timeRemaining[2][MAX_MOVES];\r
+int matchGame = 0;\r
+TimeMark programStartTime;\r
+char ics_handle[MSG_SIZ];\r
+int have_set_title = 0;\r
+\r
+/* animateTraining preserves the state of appData.animate\r
+ * when Training mode is activated. This allows the\r
+ * response to be animated when appData.animate == TRUE and\r
+ * appData.animateDragging == TRUE.\r
+ */\r
+Boolean animateTraining;\r
+\r
+GameInfo gameInfo;\r
+\r
+AppData appData;\r
+\r
+Board boards[MAX_MOVES];\r
+/* [HGM] Following 7 needed for accurate legality tests: */\r
+char epStatus[MAX_MOVES];\r
+char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1\r
+char castlingRank[BOARD_SIZE]; // and corresponding ranks\r
+char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE];\r
+int nrCastlingRights; // For TwoKings, or to implement castling-unknown status\r
+int initialRulePlies, FENrulePlies;\r
+char FENepStatus;\r
+\r
+ChessSquare FIDEArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
+ BlackKing, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare twoKingsArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
+ WhiteKing, WhiteKing, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
+ BlackKing, BlackKing, BlackKnight, BlackRook }\r
+};\r
+\r
+#ifdef FAIRY\r
+ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */\r
+ { WhiteFairyRook, WhiteFairyKnight, WhiteFairyBishop, WhiteQueen,\r
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
+ { BlackFairyRook, BlackFairyKnight, BlackFairyBishop, BlackQueen,\r
+ BlackKing, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
+ { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn,\r
+ WhiteKing, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn,\r
+ BlackKing, BlackFairyBishop, BlackKnight, BlackRook }\r
+};\r
+\r
+#if (BOARD_SIZE>=9)\r
+ChessSquare XiangqiArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn,\r
+ WhiteKing, WhiteFairyPawn, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn,\r
+ BlackKing, BlackFairyPawn, BlackFairyBishop, BlackKnight, BlackRook }\r
+};\r
+#else\r
+#define XiangqiPosition FIDEPosition\r
+#endif\r
+\r
+#if (BOARD_SIZE>=10)\r
+ChessSquare CapablancaArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteCardinal, WhiteBishop, WhiteQueen, \r
+ WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackCardinal, BlackBishop, BlackQueen, \r
+ BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
+};\r
+\r
+#ifdef GOTHIC\r
+ChessSquare GothicArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, \r
+ WhiteKing, WhiteCardinal, WhiteBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, \r
+ BlackKing, BlackCardinal, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !GOTHIC\r
+#define GothicArray CapablancaArray\r
+#endif // !GOTHIC\r
+\r
+#else // !(BOARD_SIZE>=10)\r
+#define CapablancaArray FIDEArray\r
+#define GothicArray FIDEArray\r
+#endif // !(BOARD_SIZE>=10)\r
+\r
+#if (BOARD_SIZE>=12)\r
+ChessSquare CourierArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteBishop, WhiteFairyKing, WhiteKing,\r
+ WhiteFairyPawn, WhiteFairyRook, WhiteBishop, WhiteFairyBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackFairyBishop, BlackBishop, BlackFairyKing, BlackKing,\r
+ BlackFairyPawn, BlackFairyRook, BlackBishop, BlackFairyBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !(BOARD_SIZE>=12)\r
+#define CourierArray CapablancaArray\r
+#endif // !(BOARD_SIZE>=12)\r
+#endif // FAIRY\r
+\r
+\r
+Board initialPosition;\r
+\r
+\r
+/* Convert str to a rating. Checks for special cases of "----",\r
+\r
+ "++++", etc. Also strips ()'s */\r
+int\r
+string_to_rating(str)\r
+ char *str;\r
+{\r
+ while(*str && !isdigit(*str)) ++str;\r
+ if (!*str)\r
+ return 0; /* One of the special "no rating" cases */\r
+ else\r
+ return atoi(str);\r
+}\r
+\r
+void\r
+ClearProgramStats()\r
+{\r
+ /* Init programStats */\r
+ programStats.movelist[0] = 0;\r
+ programStats.depth = 0;\r
+ programStats.nr_moves = 0;\r
+ programStats.moves_left = 0;\r
+ programStats.nodes = 0;\r
+ programStats.time = 100;\r
+ programStats.score = 0;\r
+ programStats.got_only_move = 0;\r
+ programStats.got_fail = 0;\r
+ programStats.line_is_book = 0;\r
+}\r
+\r
+void\r
+InitBackEnd1()\r
+{\r
+ int matched, min, sec;\r
+\r
+ GetTimeMark(&programStartTime);\r
+\r
+ ClearProgramStats();\r
+ programStats.ok_to_send = 1;\r
+ programStats.seen_stat = 0;\r
+\r
+ /*\r
+ * Initialize game list\r
+ */\r
+ ListNew(&gameList);\r
+\r
+\r
+ /*\r
+ * Internet chess server status\r
+ */\r
+ if (appData.icsActive) {\r
+ appData.matchMode = FALSE;\r
+ appData.matchGames = 0;\r
+#if ZIPPY \r
+ appData.noChessProgram = !appData.zippyPlay;\r
+#else\r
+ appData.zippyPlay = FALSE;\r
+ appData.zippyTalk = FALSE;\r
+ appData.noChessProgram = TRUE;\r
+#endif\r
+ if (*appData.icsHelper != NULLCHAR) {\r
+ appData.useTelnet = TRUE;\r
+ appData.telnetProgram = appData.icsHelper;\r
+ }\r
+ } else {\r
+ appData.zippyTalk = appData.zippyPlay = FALSE;\r
+ }\r
+\r
+ /* [AS] Initialize pv info list [HGM] and game state */\r
+ {\r
+ int i, j;\r
+\r
+ for( i=0; i<MAX_MOVES; i++ ) {\r
+ pvInfoList[i].depth = -1;\r
+ epStatus[i]=EP_NONE;\r
+ for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Parse timeControl resource\r
+ */\r
+ if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
+ appData.movesPerSession)) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "bad timeControl option %s", appData.timeControl);\r
+ DisplayFatalError(buf, 0, 2);\r
+ }\r
+\r
+ /*\r
+ * Parse searchTime resource\r
+ */\r
+ if (*appData.searchTime != NULLCHAR) {\r
+ matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);\r
+ if (matched == 1) {\r
+ searchTime = min * 60;\r
+ } else if (matched == 2) {\r
+ searchTime = min * 60 + sec;\r
+ } else {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "bad searchTime option %s", appData.searchTime);\r
+ DisplayFatalError(buf, 0, 2);\r
+ }\r
+ }\r
+\r
+ /* [AS] Adjudication threshold */\r
+ adjudicateLossThreshold = appData.adjudicateLossThreshold;\r
+ \r
+ first.which = "first";\r
+ second.which = "second";\r
+ first.maybeThinking = second.maybeThinking = FALSE;\r
+ first.pr = second.pr = NoProc;\r
+ first.isr = second.isr = NULL;\r
+ first.sendTime = second.sendTime = 2;\r
+ first.sendDrawOffers = 1;\r
+ if (appData.firstPlaysBlack) {\r
+ first.twoMachinesColor = "black\n";\r
+ second.twoMachinesColor = "white\n";\r
+ } else {\r
+ first.twoMachinesColor = "white\n";\r
+ second.twoMachinesColor = "black\n";\r
+ }\r
+ first.program = appData.firstChessProgram;\r
+ second.program = appData.secondChessProgram;\r
+ first.host = appData.firstHost;\r
+ second.host = appData.secondHost;\r
+ first.dir = appData.firstDirectory;\r
+ second.dir = appData.secondDirectory;\r
+ first.other = &second;\r
+ second.other = &first;\r
+ first.initString = appData.initString;\r
+ second.initString = appData.secondInitString;\r
+ first.computerString = appData.firstComputerString;\r
+ second.computerString = appData.secondComputerString;\r
+ first.useSigint = second.useSigint = TRUE;\r
+ first.useSigterm = second.useSigterm = TRUE;\r
+ first.reuse = appData.reuseFirst;\r
+ second.reuse = appData.reuseSecond;\r
+ first.useSetboard = second.useSetboard = FALSE;\r
+ first.useSAN = second.useSAN = FALSE;\r
+ first.usePing = second.usePing = FALSE;\r
+ first.lastPing = second.lastPing = 0;\r
+ first.lastPong = second.lastPong = 0;\r
+ first.usePlayother = second.usePlayother = FALSE;\r
+ first.useColors = second.useColors = TRUE;\r
+ first.useUsermove = second.useUsermove = FALSE;\r
+ first.sendICS = second.sendICS = FALSE;\r
+ first.sendName = second.sendName = appData.icsActive;\r
+ first.sdKludge = second.sdKludge = FALSE;\r
+ first.stKludge = second.stKludge = FALSE;\r
+ TidyProgramName(first.program, first.host, first.tidy);\r
+ TidyProgramName(second.program, second.host, second.tidy);\r
+ first.matchWins = second.matchWins = 0;\r
+ strcpy(first.variants, appData.variant);\r
+ strcpy(second.variants, appData.variant);\r
+ first.analysisSupport = second.analysisSupport = 2; /* detect */\r
+ first.analyzing = second.analyzing = FALSE;\r
+ first.initDone = second.initDone = FALSE;\r
+\r
+ /* New features added by Tord: */\r
+ first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
+ first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
+ /* End of new features added by Tord. */\r
+\r
+ first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
+ second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
+ first.isUCI = appData.firstIsUCI; /* [AS] */\r
+ second.isUCI = appData.secondIsUCI; /* [AS] */\r
+ first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */\r
+ second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */\r
+\r
+ if (appData.firstProtocolVersion > PROTOVER ||\r
+ appData.firstProtocolVersion < 1) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "protocol version %d not supported",\r
+ appData.firstProtocolVersion);\r
+ DisplayFatalError(buf, 0, 2);\r
+ } else {\r
+ first.protocolVersion = appData.firstProtocolVersion;\r
+ }\r
+\r
+ if (appData.secondProtocolVersion > PROTOVER ||\r
+ appData.secondProtocolVersion < 1) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "protocol version %d not supported",\r
+ appData.secondProtocolVersion);\r
+ DisplayFatalError(buf, 0, 2);\r
+ } else {\r
+ second.protocolVersion = appData.secondProtocolVersion;\r
+ }\r
+\r
+ if (appData.icsActive) {\r
+ appData.clockMode = TRUE; /* changes dynamically in ICS mode */\r
+ } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {\r
+ appData.clockMode = FALSE;\r
+ first.sendTime = second.sendTime = 0;\r
+ }\r
+ \r
+#if ZIPPY\r
+ /* Override some settings from environment variables, for backward\r
+ compatibility. Unfortunately it's not feasible to have the env\r
+ vars just set defaults, at least in xboard. Ugh.\r
+ */\r
+ if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {\r
+ ZippyInit();\r
+ }\r
+#endif\r
+ \r
+ if (appData.noChessProgram) {\r
+ programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)\r
+ + strlen(PATCHLEVEL));\r
+ sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);\r
+ } else {\r
+ char *p, *q;\r
+ q = first.program;\r
+ while (*q != ' ' && *q != NULLCHAR) q++;\r
+ p = q;\r
+ while (p > first.program && *(p-1) != '/') p--;\r
+ programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
+ + strlen(PATCHLEVEL) + (q - p));\r
+ sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
+ strncat(programVersion, p, q - p);\r
+ }\r
+\r
+ if (!appData.icsActive) {\r
+ char buf[MSG_SIZ];\r
+ /* Check for variants that are supported only in ICS mode,\r
+ or not at all. Some that are accepted here nevertheless\r
+ have bugs; see comments below.\r
+ */\r
+ VariantClass variant = StringToVariant(appData.variant);\r
+ switch (variant) {\r
+ case VariantBughouse: /* need four players and two boards */\r
+ case VariantKriegspiel: /* need to hide pieces and move details */\r
+ /* case VariantFischeRandom: (Fabien: moved below) */\r
+ sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);\r
+ DisplayFatalError(buf, 0, 2);\r
+ return;\r
+\r
+ case VariantUnknown:\r
+ case VariantLoadable:\r
+ case Variant29:\r
+ case Variant30:\r
+#ifndef FAIRY\r
+ case Variant31:\r
+ case Variant32:\r
+ case Variant33:\r
+ case Variant34:\r
+ case Variant35:\r
+ case Variant36:\r
+#endif\r
+ default:\r
+ sprintf(buf, "Unknown variant name %s", appData.variant);\r
+ DisplayFatalError(buf, 0, 2);\r
+ return;\r
+\r
+ case VariantNormal: /* definitely works! */\r
+ case VariantWildCastle: /* pieces not automatically shuffled */\r
+ case VariantNoCastle: /* pieces not automatically shuffled */\r
+ case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */\r
+ case VariantCrazyhouse: /* holdings not shown,\r
+ offboard interposition not understood */\r
+ case VariantLosers: /* should work except for win condition,\r
+ and doesn't know captures are mandatory */\r
+ case VariantSuicide: /* should work except for win condition,\r
+ and doesn't know captures are mandatory */\r
+ case VariantGiveaway: /* should work except for win condition,\r
+ and doesn't know captures are mandatory */\r
+ case VariantTwoKings: /* should work */\r
+ case VariantAtomic: /* should work except for win condition */\r
+ case Variant3Check: /* should work except for win condition */\r
+ case VariantShatranj: /* might work if TestLegality is off */\r
+#ifdef FAIRY\r
+ case VariantShogi:\r
+ case VariantXiangqi:\r
+ case VariantFairy: /* [HGM] TestLegality definitely off! */\r
+ case VariantGothic:\r
+ case VariantCapablanca:\r
+ case VariantCourier:\r
+#endif\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+int NextIntegerFromString( char ** str, long * value )\r
+{\r
+ int result = -1;\r
+ char * s = *str;\r
+\r
+ while( *s == ' ' || *s == '\t' ) {\r
+ s++;\r
+ }\r
+\r
+ *value = 0;\r
+\r
+ if( *s >= '0' && *s <= '9' ) {\r
+ while( *s >= '0' && *s <= '9' ) {\r
+ *value = *value * 10 + (*s - '0');\r
+ s++;\r
+ }\r
+\r
+ result = 0;\r
+ }\r
+\r
+ *str = s;\r
+\r
+ return result;\r
+}\r
+\r
+int NextTimeControlFromString( char ** str, long * value )\r
+{\r
+ long temp;\r
+ int result = NextIntegerFromString( str, &temp );\r
+\r
+ if( result == 0 ) {\r
+ *value = temp * 60; /* Minutes */\r
+ if( **str == ':' ) {\r
+ (*str)++;\r
+ result = NextIntegerFromString( str, &temp );\r
+ *value += temp; /* Seconds */\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+int GetTimeControlForWhite()\r
+{\r
+ int result = timeControl;\r
+\r
+ return result;\r
+}\r
+\r
+int GetTimeControlForBlack()\r
+{\r
+ int result = timeControl;\r
+\r
+ if( timeControl_2 > 0 ) {\r
+ result = timeControl_2;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+int\r
+ParseTimeControl(tc, ti, mps)\r
+ char *tc;\r
+ int ti;\r
+ int mps;\r
+{\r
+#if 0\r
+ int matched, min, sec;\r
+\r
+ matched = sscanf(tc, "%d:%d", &min, &sec);\r
+ if (matched == 1) {\r
+ timeControl = min * 60 * 1000;\r
+ } else if (matched == 2) {\r
+ timeControl = (min * 60 + sec) * 1000;\r
+ } else {\r
+ return FALSE;\r
+ }\r
+#else\r
+ long tc1;\r
+ long tc2;\r
+\r
+ if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {\r
+ return FALSE;\r
+ }\r
+\r
+ if( *tc == '/' ) {\r
+ /* Parse second time control */\r
+ tc++;\r
+\r
+ if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {\r
+ return FALSE;\r
+ }\r
+\r
+ if( tc2 == 0 ) {\r
+ return FALSE;\r
+ }\r
+\r
+ timeControl_2 = tc2 * 1000;\r
+ }\r
+ else {\r
+ timeControl_2 = 0;\r
+ }\r
+\r
+ if( tc1 == 0 ) {\r
+ return FALSE;\r
+ }\r
+\r
+ timeControl = tc1 * 1000;\r
+#endif\r
+\r
+ if (ti >= 0) {\r
+ timeIncrement = ti * 1000; /* convert to ms */\r
+ movesPerSession = 0;\r
+ } else {\r
+ timeIncrement = 0;\r
+ movesPerSession = mps;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+void\r
+InitBackEnd2()\r
+{\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "%s\n", programVersion);\r
+ }\r
+\r
+ if (appData.matchGames > 0) {\r
+ appData.matchMode = TRUE;\r
+ } else if (appData.matchMode) {\r
+ appData.matchGames = 1;\r
+ }\r
+ Reset(TRUE, FALSE);\r
+ if (appData.noChessProgram || first.protocolVersion == 1) {\r
+ InitBackEnd3();\r
+ } else {\r
+ /* kludge: allow timeout for initial "feature" commands */\r
+ FreezeUI();\r
+ DisplayMessage("", "Starting chess program");\r
+ ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);\r
+ }\r
+}\r
+\r
+void\r
+InitBackEnd3 P((void))\r
+{\r
+ GameMode initialMode;\r
+ char buf[MSG_SIZ];\r
+ int err;\r
+\r
+ InitChessProgram(&first);\r
+\r
+ if (appData.icsActive) {\r
+ err = establish();\r
+ if (err != 0) {\r
+ if (*appData.icsCommPort != NULLCHAR) {\r
+ sprintf(buf, "Could not open comm port %s", \r
+ appData.icsCommPort);\r
+ } else {\r
+ sprintf(buf, "Could not connect to host %s, port %s", \r
+ appData.icsHost, appData.icsPort);\r
+ }\r
+ DisplayFatalError(buf, err, 1);\r
+ return;\r
+ }\r
+ SetICSMode();\r
+ telnetISR =\r
+ AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);\r
+ fromUserISR =\r
+ AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);\r
+ } else if (appData.noChessProgram) {\r
+ SetNCPMode();\r
+ } else {\r
+ SetGNUMode();\r
+ }\r
+\r
+ if (*appData.cmailGameName != NULLCHAR) {\r
+ SetCmailMode();\r
+ OpenLoopback(&cmailPR);\r
+ cmailISR =\r
+ AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);\r
+ }\r
+ \r
+ ThawUI();\r
+ DisplayMessage("", "");\r
+ if (StrCaseCmp(appData.initialMode, "") == 0) {\r
+ initialMode = BeginningOfGame;\r
+ } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {\r
+ initialMode = TwoMachinesPlay;\r
+ } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {\r
+ initialMode = AnalyzeFile; \r
+ } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {\r
+ initialMode = AnalyzeMode;\r
+ } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {\r
+ initialMode = MachinePlaysWhite;\r
+ } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {\r
+ initialMode = MachinePlaysBlack;\r
+ } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {\r
+ initialMode = EditGame;\r
+ } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {\r
+ initialMode = EditPosition;\r
+ } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {\r
+ initialMode = Training;\r
+ } else {\r
+ sprintf(buf, "Unknown initialMode %s", appData.initialMode);\r
+ DisplayFatalError(buf, 0, 2);\r
+ return;\r
+ }\r
+\r
+ if (appData.matchMode) {\r
+ /* Set up machine vs. machine match */\r
+ if (appData.noChessProgram) {\r
+ DisplayFatalError("Can't have a match with no chess programs",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ matchMode = TRUE;\r
+ matchGame = 1;\r
+ if (*appData.loadGameFile != NULLCHAR) {\r
+ if (!LoadGameFromFile(appData.loadGameFile,\r
+ appData.loadGameIndex,\r
+ appData.loadGameFile, FALSE)) {\r
+ DisplayFatalError("Bad game file", 0, 1);\r
+ return;\r
+ }\r
+ } else if (*appData.loadPositionFile != NULLCHAR) {\r
+ if (!LoadPositionFromFile(appData.loadPositionFile,\r
+ appData.loadPositionIndex,\r
+ appData.loadPositionFile)) {\r
+ DisplayFatalError("Bad position file", 0, 1);\r
+ return;\r
+ }\r
+ }\r
+ TwoMachinesEvent();\r
+ } else if (*appData.cmailGameName != NULLCHAR) {\r
+ /* Set up cmail mode */\r
+ ReloadCmailMsgEvent(TRUE);\r
+ } else {\r
+ /* Set up other modes */\r
+ if (initialMode == AnalyzeFile) {\r
+ if (*appData.loadGameFile == NULLCHAR) {\r
+ DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);\r
+ return;\r
+ }\r
+ }\r
+ if (*appData.loadGameFile != NULLCHAR) {\r
+ (void) LoadGameFromFile(appData.loadGameFile,\r
+ appData.loadGameIndex,\r
+ appData.loadGameFile, TRUE);\r
+ } else if (*appData.loadPositionFile != NULLCHAR) {\r
+ (void) LoadPositionFromFile(appData.loadPositionFile,\r
+ appData.loadPositionIndex,\r
+ appData.loadPositionFile);\r
+ }\r
+ if (initialMode == AnalyzeMode) {\r
+ if (appData.noChessProgram) {\r
+ DisplayFatalError("Analysis mode requires a chess engine", 0, 2);\r
+ return;\r
+ }\r
+ if (appData.icsActive) {\r
+ DisplayFatalError("Analysis mode does not work with ICS mode",0,2);\r
+ return;\r
+ }\r
+ AnalyzeModeEvent();\r
+ } else if (initialMode == AnalyzeFile) {\r
+ ShowThinkingEvent(TRUE);\r
+ AnalyzeFileEvent();\r
+ AnalysisPeriodicEvent(1);\r
+ } else if (initialMode == MachinePlaysWhite) {\r
+ if (appData.noChessProgram) {\r
+ DisplayFatalError("MachineWhite mode requires a chess engine",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ if (appData.icsActive) {\r
+ DisplayFatalError("MachineWhite mode does not work with ICS mode",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ MachineWhiteEvent();\r
+ } else if (initialMode == MachinePlaysBlack) {\r
+ if (appData.noChessProgram) {\r
+ DisplayFatalError("MachineBlack mode requires a chess engine",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ if (appData.icsActive) {\r
+ DisplayFatalError("MachineBlack mode does not work with ICS mode",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ MachineBlackEvent();\r
+ } else if (initialMode == TwoMachinesPlay) {\r
+ if (appData.noChessProgram) {\r
+ DisplayFatalError("TwoMachines mode requires a chess engine",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ if (appData.icsActive) {\r
+ DisplayFatalError("TwoMachines mode does not work with ICS mode",\r
+ 0, 2);\r
+ return;\r
+ }\r
+ TwoMachinesEvent();\r
+ } else if (initialMode == EditGame) {\r
+ EditGameEvent();\r
+ } else if (initialMode == EditPosition) {\r
+ EditPositionEvent();\r
+ } else if (initialMode == Training) {\r
+ if (*appData.loadGameFile == NULLCHAR) {\r
+ DisplayFatalError("Training mode requires a game file", 0, 2);\r
+ return;\r
+ }\r
+ TrainingEvent();\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+ * Establish will establish a contact to a remote host.port.\r
+ * Sets icsPR to a ProcRef for a process (or pseudo-process)\r
+ * used to talk to the host.\r
+ * Returns 0 if okay, error code if not.\r
+ */\r
+int\r
+establish()\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ if (*appData.icsCommPort != NULLCHAR) {\r
+ /* Talk to the host through a serial comm port */\r
+ return OpenCommPort(appData.icsCommPort, &icsPR);\r
+\r
+ } else if (*appData.gateway != NULLCHAR) {\r
+ if (*appData.remoteShell == NULLCHAR) {\r
+ /* Use the rcmd protocol to run telnet program on a gateway host */\r
+ sprintf(buf, "%s %s %s",\r
+ appData.telnetProgram, appData.icsHost, appData.icsPort);\r
+ return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
+\r
+ } else {\r
+ /* Use the rsh program to run telnet program on a gateway host */\r
+ if (*appData.remoteUser == NULLCHAR) {\r
+ sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
+ appData.gateway, appData.telnetProgram,\r
+ appData.icsHost, appData.icsPort);\r
+ } else {\r
+ sprintf(buf, "%s %s -l %s %s %s %s",\r
+ appData.remoteShell, appData.gateway, \r
+ appData.remoteUser, appData.telnetProgram,\r
+ appData.icsHost, appData.icsPort);\r
+ }\r
+ return StartChildProcess(buf, "", &icsPR);\r
+\r
+ }\r
+ } else if (appData.useTelnet) {\r
+ return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);\r
+\r
+ } else {\r
+ /* TCP socket interface differs somewhat between\r
+ Unix and NT; handle details in the front end.\r
+ */\r
+ return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);\r
+ }\r
+}\r
+\r
+void\r
+show_bytes(fp, buf, count)\r
+ FILE *fp;\r
+ char *buf;\r
+ int count;\r
+{\r
+ while (count--) {\r
+ if (*buf < 040 || *(unsigned char *) buf > 0177) {\r
+ fprintf(fp, "\\%03o", *buf & 0xff);\r
+ } else {\r
+ putc(*buf, fp);\r
+ }\r
+ buf++;\r
+ }\r
+ fflush(fp);\r
+}\r
+\r
+/* Returns an errno value */\r
+int\r
+OutputMaybeTelnet(pr, message, count, outError)\r
+ ProcRef pr;\r
+ char *message;\r
+ int count;\r
+ int *outError;\r
+{\r
+ char buf[8192], *p, *q, *buflim;\r
+ int left, newcount, outcount;\r
+\r
+ if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||\r
+ *appData.gateway != NULLCHAR) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, ">ICS: ");\r
+ show_bytes(debugFP, message, count);\r
+ fprintf(debugFP, "\n");\r
+ }\r
+ return OutputToProcess(pr, message, count, outError);\r
+ }\r
+\r
+ buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */\r
+ p = message;\r
+ q = buf;\r
+ left = count;\r
+ newcount = 0;\r
+ while (left) {\r
+ if (q >= buflim) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, ">ICS: ");\r
+ show_bytes(debugFP, buf, newcount);\r
+ fprintf(debugFP, "\n");\r
+ }\r
+ outcount = OutputToProcess(pr, buf, newcount, outError);\r
+ if (outcount < newcount) return -1; /* to be sure */\r
+ q = buf;\r
+ newcount = 0;\r
+ }\r
+ if (*p == '\n') {\r
+ *q++ = '\r';\r
+ newcount++;\r
+ } else if (((unsigned char) *p) == TN_IAC) {\r
+ *q++ = (char) TN_IAC;\r
+ newcount ++;\r
+ }\r
+ *q++ = *p++;\r
+ newcount++;\r
+ left--;\r
+ }\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, ">ICS: ");\r
+ show_bytes(debugFP, buf, newcount);\r
+ fprintf(debugFP, "\n");\r
+ }\r
+ outcount = OutputToProcess(pr, buf, newcount, outError);\r
+ if (outcount < newcount) return -1; /* to be sure */\r
+ return count;\r
+}\r
+\r
+void\r
+read_from_player(isr, closure, message, count, error)\r
+ InputSourceRef isr;\r
+ VOIDSTAR closure;\r
+ char *message;\r
+ int count;\r
+ int error;\r
+{\r
+ int outError, outCount;\r
+ static int gotEof = 0;\r
+\r
+ /* Pass data read from player on to ICS */\r
+ if (count > 0) {\r
+ gotEof = 0;\r
+ outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
+ if (outCount < count) {\r
+ DisplayFatalError("Error writing to ICS", outError, 1);\r
+ }\r
+ } else if (count < 0) {\r
+ RemoveInputSource(isr);\r
+ DisplayFatalError("Error reading from keyboard", error, 1);\r
+ } else if (gotEof++ > 0) {\r
+ RemoveInputSource(isr);\r
+ DisplayFatalError("Got end of file from keyboard", 0, 0);\r
+ }\r
+}\r
+\r
+void\r
+SendToICS(s)\r
+ char *s;\r
+{\r
+ int count, outCount, outError;\r
+\r
+ if (icsPR == NULL) return;\r
+\r
+ count = strlen(s);\r
+ outCount = OutputMaybeTelnet(icsPR, s, count, &outError);\r
+ if (outCount < count) {\r
+ DisplayFatalError("Error writing to ICS", outError, 1);\r
+ }\r
+}\r
+\r
+/* This is used for sending logon scripts to the ICS. Sending\r
+ without a delay causes problems when using timestamp on ICC\r
+ (at least on my machine). */\r
+void\r
+SendToICSDelayed(s,msdelay)\r
+ char *s;\r
+ long msdelay;\r
+{\r
+ int count, outCount, outError;\r
+\r
+ if (icsPR == NULL) return;\r
+\r
+ count = strlen(s);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, ">ICS: ");\r
+ show_bytes(debugFP, s, count);\r
+ fprintf(debugFP, "\n");\r
+ }\r
+ outCount = OutputToProcessDelayed(icsPR, s, count, &outError,\r
+ msdelay);\r
+ if (outCount < count) {\r
+ DisplayFatalError("Error writing to ICS", outError, 1);\r
+ }\r
+}\r
+\r
+\r
+/* Remove all highlighting escape sequences in s\r
+ Also deletes any suffix starting with '(' \r
+ */\r
+char *\r
+StripHighlightAndTitle(s)\r
+ char *s;\r
+{\r
+ static char retbuf[MSG_SIZ];\r
+ char *p = retbuf;\r
+\r
+ while (*s != NULLCHAR) {\r
+ while (*s == '\033') {\r
+ while (*s != NULLCHAR && !isalpha(*s)) s++;\r
+ if (*s != NULLCHAR) s++;\r
+ }\r
+ while (*s != NULLCHAR && *s != '\033') {\r
+ if (*s == '(' || *s == '[') {\r
+ *p = NULLCHAR;\r
+ return retbuf;\r
+ }\r
+ *p++ = *s++;\r
+ }\r
+ }\r
+ *p = NULLCHAR;\r
+ return retbuf;\r
+}\r
+\r
+/* Remove all highlighting escape sequences in s */\r
+char *\r
+StripHighlight(s)\r
+ char *s;\r
+{\r
+ static char retbuf[MSG_SIZ];\r
+ char *p = retbuf;\r
+\r
+ while (*s != NULLCHAR) {\r
+ while (*s == '\033') {\r
+ while (*s != NULLCHAR && !isalpha(*s)) s++;\r
+ if (*s != NULLCHAR) s++;\r
+ }\r
+ while (*s != NULLCHAR && *s != '\033') {\r
+ *p++ = *s++;\r
+ }\r
+ }\r
+ *p = NULLCHAR;\r
+ return retbuf;\r
+}\r
+\r
+char *variantNames[] = VARIANT_NAMES;\r
+char *\r
+VariantName(v)\r
+ VariantClass v;\r
+{\r
+ return variantNames[v];\r
+}\r
+\r
+\r
+/* Identify a variant from the strings the chess servers use or the\r
+ PGN Variant tag names we use. */\r
+VariantClass\r
+StringToVariant(e)\r
+ char *e;\r
+{\r
+ char *p;\r
+ int wnum = -1;\r
+ VariantClass v = VariantNormal;\r
+ int i, found = FALSE;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (!e) return v;\r
+ \r
+ for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {\r
+ if (StrCaseStr(e, variantNames[i])) {\r
+ v = (VariantClass) i;\r
+ found = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (!found) {\r
+ if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
+ || StrCaseStr(e, "wild/fr")) {\r
+ v = VariantFischeRandom;\r
+ } else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
+ (i = 1, p = StrCaseStr(e, "w"))) {\r
+ p += i;\r
+ while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;\r
+ if (isdigit(*p)) {\r
+ wnum = atoi(p);\r
+ } else {\r
+ wnum = -1;\r
+ }\r
+ switch (wnum) {\r
+ case 0: /* FICS only, actually */\r
+ case 1:\r
+ /* Castling legal even if K starts on d-file */\r
+ v = VariantWildCastle;\r
+ break;\r
+ case 2:\r
+ case 3:\r
+ case 4:\r
+ /* Castling illegal even if K & R happen to start in\r
+ normal positions. */\r
+ v = VariantNoCastle;\r
+ break;\r
+ case 5:\r
+ case 7:\r
+ case 8:\r
+ case 10:\r
+ case 11:\r
+ case 12:\r
+ case 13:\r
+ case 14:\r
+ case 15:\r
+ case 18:\r
+ case 19:\r
+ /* Castling legal iff K & R start in normal positions */\r
+ v = VariantNormal;\r
+ break;\r
+ case 6:\r
+ case 20:\r
+ case 21:\r
+ /* Special wilds for position setup; unclear what to do here */\r
+ v = VariantLoadable;\r
+ break;\r
+ case 9:\r
+ /* Bizarre ICC game */\r
+ v = VariantTwoKings;\r
+ break;\r
+ case 16:\r
+ v = VariantKriegspiel;\r
+ break;\r
+ case 17:\r
+ v = VariantLosers;\r
+ break;\r
+ case 22:\r
+ v = VariantFischeRandom;\r
+ break;\r
+ case 23:\r
+ v = VariantCrazyhouse;\r
+ break;\r
+ case 24:\r
+ v = VariantBughouse;\r
+ break;\r
+ case 25:\r
+ v = Variant3Check;\r
+ break;\r
+ case 26:\r
+ /* Not quite the same as FICS suicide! */\r
+ v = VariantGiveaway;\r
+ break;\r
+ case 27:\r
+ v = VariantAtomic;\r
+ break;\r
+ case 28:\r
+ v = VariantShatranj;\r
+ break;\r
+\r
+ /* Temporary names for future ICC types. The name *will* change in \r
+ the next xboard/WinBoard release after ICC defines it. */\r
+ case 29:\r
+ v = Variant29;\r
+ break;\r
+ case 30:\r
+ v = Variant30;\r
+ break;\r
+ case 31:\r
+#ifdef FAIRY\r
+ v = VariantShogi;\r
+#else\r
+ v = Variant31;\r
+#endif\r
+ break;\r
+ case 32:\r
+#ifdef FAIRY\r
+ v = VariantXiangqi;\r
+#else\r
+ v = Variant32;\r
+#endif\r
+ break;\r
+ case 33:\r
+#ifdef FAIRY\r
+ v = VariantCourier;\r
+#else\r
+ v = Variant33;\r
+#endif\r
+ break;\r
+ case 34:\r
+#ifdef FAIRY\r
+ v = VariantGothic;\r
+#else\r
+ v = Variant34;\r
+#endif\r
+ break;\r
+ case 35:\r
+#ifdef FAIRY\r
+ v = VariantCapablanca;\r
+#else\r
+ v = Variant35;\r
+#endif\r
+ break;\r
+ case 36:\r
+#ifdef FAIRY\r
+ v = VariantFairy;\r
+#else\r
+ v = Variant36;\r
+#endif\r
+ break;\r
+\r
+ case -1:\r
+ /* Found "wild" or "w" in the string but no number;\r
+ must assume it's normal chess. */\r
+ v = VariantNormal;\r
+ break;\r
+ default:\r
+ sprintf(buf, "Unknown wild type %d", wnum);\r
+ DisplayError(buf, 0);\r
+ v = VariantUnknown;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",\r
+ e, wnum, VariantName(v));\r
+ }\r
+ return v;\r
+}\r
+\r
+static int leftover_start = 0, leftover_len = 0;\r
+char star_match[STAR_MATCH_N][MSG_SIZ];\r
+\r
+/* Test whether pattern is present at &buf[*index]; if so, return TRUE,\r
+ advance *index beyond it, and set leftover_start to the new value of\r
+ *index; else return FALSE. If pattern contains the character '*', it\r
+ matches any sequence of characters not containing '\r', '\n', or the\r
+ character following the '*' (if any), and the matched sequence(s) are\r
+ copied into star_match.\r
+ */\r
+int\r
+looking_at(buf, index, pattern)\r
+ char *buf;\r
+ int *index;\r
+ char *pattern;\r
+{\r
+ char *bufp = &buf[*index], *patternp = pattern;\r
+ int star_count = 0;\r
+ char *matchp = star_match[0];\r
+ \r
+ for (;;) {\r
+ if (*patternp == NULLCHAR) {\r
+ *index = leftover_start = bufp - buf;\r
+ *matchp = NULLCHAR;\r
+ return TRUE;\r
+ }\r
+ if (*bufp == NULLCHAR) return FALSE;\r
+ if (*patternp == '*') {\r
+ if (*bufp == *(patternp + 1)) {\r
+ *matchp = NULLCHAR;\r
+ matchp = star_match[++star_count];\r
+ patternp += 2;\r
+ bufp++;\r
+ continue;\r
+ } else if (*bufp == '\n' || *bufp == '\r') {\r
+ patternp++;\r
+ if (*patternp == NULLCHAR)\r
+ continue;\r
+ else\r
+ return FALSE;\r
+ } else {\r
+ *matchp++ = *bufp++;\r
+ continue;\r
+ }\r
+ }\r
+ if (*patternp != *bufp) return FALSE;\r
+ patternp++;\r
+ bufp++;\r
+ }\r
+}\r
+\r
+void\r
+SendToPlayer(data, length)\r
+ char *data;\r
+ int length;\r
+{\r
+ int error, outCount;\r
+ outCount = OutputToProcess(NoProc, data, length, &error);\r
+ if (outCount < length) {\r
+ DisplayFatalError("Error writing to display", error, 1);\r
+ }\r
+}\r
+\r
+void\r
+PackHolding(packed, holding)\r
+ char packed[];\r
+ char *holding;\r
+{\r
+ char *p = holding;\r
+ char *q = packed;\r
+ int runlength = 0;\r
+ int curr = 9999;\r
+ do {\r
+ if (*p == curr) {\r
+ runlength++;\r
+ } else {\r
+ switch (runlength) {\r
+ case 0:\r
+ break;\r
+ case 1:\r
+ *q++ = curr;\r
+ break;\r
+ case 2:\r
+ *q++ = curr;\r
+ *q++ = curr;\r
+ break;\r
+ default:\r
+ sprintf(q, "%d", runlength);\r
+ while (*q) q++;\r
+ *q++ = curr;\r
+ break;\r
+ }\r
+ runlength = 1;\r
+ curr = *p;\r
+ }\r
+ } while (*p++);\r
+ *q = NULLCHAR;\r
+}\r
+\r
+/* Telnet protocol requests from the front end */\r
+void\r
+TelnetRequest(ddww, option)\r
+ unsigned char ddww, option;\r
+{\r
+ unsigned char msg[3];\r
+ int outCount, outError;\r
+\r
+ if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;\r
+\r
+ if (appData.debugMode) {\r
+ char buf1[8], buf2[8], *ddwwStr, *optionStr;\r
+ switch (ddww) {\r
+ case TN_DO:\r
+ ddwwStr = "DO";\r
+ break;\r
+ case TN_DONT:\r
+ ddwwStr = "DONT";\r
+ break;\r
+ case TN_WILL:\r
+ ddwwStr = "WILL";\r
+ break;\r
+ case TN_WONT:\r
+ ddwwStr = "WONT";\r
+ break;\r
+ default:\r
+ ddwwStr = buf1;\r
+ sprintf(buf1, "%d", ddww);\r
+ break;\r
+ }\r
+ switch (option) {\r
+ case TN_ECHO:\r
+ optionStr = "ECHO";\r
+ break;\r
+ default:\r
+ optionStr = buf2;\r
+ sprintf(buf2, "%d", option);\r
+ break;\r
+ }\r
+ fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);\r
+ }\r
+ msg[0] = TN_IAC;\r
+ msg[1] = ddww;\r
+ msg[2] = option;\r
+ outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);\r
+ if (outCount < 3) {\r
+ DisplayFatalError("Error writing to ICS", outError, 1);\r
+ }\r
+}\r
+\r
+void\r
+DoEcho()\r
+{\r
+ if (!appData.icsActive) return;\r
+ TelnetRequest(TN_DO, TN_ECHO);\r
+}\r
+\r
+void\r
+DontEcho()\r
+{\r
+ if (!appData.icsActive) return;\r
+ TelnetRequest(TN_DONT, TN_ECHO);\r
+}\r
+\r
+static int loggedOn = FALSE;\r
+\r
+/*-- Game start info cache: --*/\r
+int gs_gamenum;\r
+char gs_kind[MSG_SIZ];\r
+static char player1Name[128] = "";\r
+static char player2Name[128] = "";\r
+static int player1Rating = -1;\r
+static int player2Rating = -1;\r
+/*----------------------------*/\r
+\r
+ColorClass curColor = ColorNormal;\r
+\r
+void\r
+read_from_ics(isr, closure, data, count, error)\r
+ InputSourceRef isr;\r
+ VOIDSTAR closure;\r
+ char *data;\r
+ int count;\r
+ int error;\r
+{\r
+#define BUF_SIZE 8192\r
+#define STARTED_NONE 0\r
+#define STARTED_MOVES 1\r
+#define STARTED_BOARD 2\r
+#define STARTED_OBSERVE 3\r
+#define STARTED_HOLDINGS 4\r
+#define STARTED_CHATTER 5\r
+#define STARTED_COMMENT 6\r
+#define STARTED_MOVES_NOHIDE 7\r
+ \r
+ static int started = STARTED_NONE;\r
+ static char parse[20000];\r
+ static int parse_pos = 0;\r
+ static char buf[BUF_SIZE + 1];\r
+ static int firstTime = TRUE, intfSet = FALSE;\r
+ static ColorClass prevColor = ColorNormal;\r
+ static int savingComment = FALSE;\r
+ char str[500];\r
+ int i, oldi;\r
+ int buf_len;\r
+ int next_out;\r
+ int tkind;\r
+ char *p;\r
+\r
+#ifdef WIN32\r
+ if (appData.debugMode) {\r
+ if (!error) {\r
+ fprintf(debugFP, "<ICS: ");\r
+ show_bytes(debugFP, data, count);\r
+ fprintf(debugFP, "\n");\r
+ }\r
+ }\r
+#endif\r
+\r
+ if (count > 0) {\r
+ /* If last read ended with a partial line that we couldn't parse,\r
+ prepend it to the new read and try again. */\r
+ if (leftover_len > 0) {\r
+ for (i=0; i<leftover_len; i++)\r
+ buf[i] = buf[leftover_start + i];\r
+ }\r
+\r
+ /* Copy in new characters, removing nulls and \r's */\r
+ buf_len = leftover_len;\r
+ for (i = 0; i < count; i++) {\r
+ if (data[i] != NULLCHAR && data[i] != '\r')\r
+ buf[buf_len++] = data[i];\r
+ }\r
+\r
+ buf[buf_len] = NULLCHAR;\r
+ next_out = leftover_len;\r
+ leftover_start = 0;\r
+ \r
+ i = 0;\r
+ while (i < buf_len) {\r
+ /* Deal with part of the TELNET option negotiation\r
+ protocol. We refuse to do anything beyond the\r
+ defaults, except that we allow the WILL ECHO option,\r
+ which ICS uses to turn off password echoing when we are\r
+ directly connected to it. We reject this option\r
+ if localLineEditing mode is on (always on in xboard)\r
+ and we are talking to port 23, which might be a real\r
+ telnet server that will try to keep WILL ECHO on permanently.\r
+ */\r
+ if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {\r
+ static int remoteEchoOption = FALSE; /* telnet ECHO option */\r
+ unsigned char option;\r
+ oldi = i;\r
+ switch ((unsigned char) buf[++i]) {\r
+ case TN_WILL:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<WILL ");\r
+ switch (option = (unsigned char) buf[++i]) {\r
+ case TN_ECHO:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ECHO ");\r
+ /* Reply only if this is a change, according\r
+ to the protocol rules. */\r
+ if (remoteEchoOption) break;\r
+ if (appData.localLineEditing &&\r
+ atoi(appData.icsPort) == TN_PORT) {\r
+ TelnetRequest(TN_DONT, TN_ECHO);\r
+ } else {\r
+ EchoOff();\r
+ TelnetRequest(TN_DO, TN_ECHO);\r
+ remoteEchoOption = TRUE;\r
+ }\r
+ break;\r
+ default:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "%d ", option);\r
+ /* Whatever this is, we don't want it. */\r
+ TelnetRequest(TN_DONT, option);\r
+ break;\r
+ }\r
+ break;\r
+ case TN_WONT:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<WONT ");\r
+ switch (option = (unsigned char) buf[++i]) {\r
+ case TN_ECHO:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ECHO ");\r
+ /* Reply only if this is a change, according\r
+ to the protocol rules. */\r
+ if (!remoteEchoOption) break;\r
+ EchoOn();\r
+ TelnetRequest(TN_DONT, TN_ECHO);\r
+ remoteEchoOption = FALSE;\r
+ break;\r
+ default:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "%d ", (unsigned char) option);\r
+ /* Whatever this is, it must already be turned\r
+ off, because we never agree to turn on\r
+ anything non-default, so according to the\r
+ protocol rules, we don't reply. */\r
+ break;\r
+ }\r
+ break;\r
+ case TN_DO:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<DO ");\r
+ switch (option = (unsigned char) buf[++i]) {\r
+ default:\r
+ /* Whatever this is, we refuse to do it. */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "%d ", option);\r
+ TelnetRequest(TN_WONT, option);\r
+ break;\r
+ }\r
+ break;\r
+ case TN_DONT:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<DONT ");\r
+ switch (option = (unsigned char) buf[++i]) {\r
+ default:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "%d ", option);\r
+ /* Whatever this is, we are already not doing\r
+ it, because we never agree to do anything\r
+ non-default, so according to the protocol\r
+ rules, we don't reply. */\r
+ break;\r
+ }\r
+ break;\r
+ case TN_IAC:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<IAC ");\r
+ /* Doubled IAC; pass it through */\r
+ i--;\r
+ break;\r
+ default:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);\r
+ /* Drop all other telnet commands on the floor */\r
+ break;\r
+ }\r
+ if (oldi > next_out)\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ if (++i > next_out)\r
+ next_out = i;\r
+ continue;\r
+ }\r
+ \r
+ /* OK, this at least will *usually* work */\r
+ if (!loggedOn && looking_at(buf, &i, "ics%")) {\r
+ loggedOn = TRUE;\r
+ }\r
+ \r
+ if (loggedOn && !intfSet) {\r
+ if (ics_type == ICS_ICC) {\r
+ sprintf(str,\r
+ "/set-quietly interface %s\n/set-quietly style 12\n",\r
+ programVersion);\r
+\r
+ } else if (ics_type == ICS_CHESSNET) {\r
+ sprintf(str, "/style 12\n");\r
+ } else {\r
+ strcpy(str, "alias $ @\n$set interface ");\r
+ strcat(str, programVersion);\r
+ strcat(str, "\n$iset startpos 1\n$iset ms 1\n");\r
+#ifdef WIN32\r
+ strcat(str, "$iset nohighlight 1\n");\r
+#endif\r
+ strcat(str, "$iset lock 1\n$style 12\n");\r
+ }\r
+ SendToICS(str);\r
+ intfSet = TRUE;\r
+ }\r
+\r
+ if (started == STARTED_COMMENT) {\r
+ /* Accumulate characters in comment */\r
+ parse[parse_pos++] = buf[i];\r
+ if (buf[i] == '\n') {\r
+ parse[parse_pos] = NULLCHAR;\r
+ AppendComment(forwardMostMove, StripHighlight(parse));\r
+ started = STARTED_NONE;\r
+ } else {\r
+ /* Don't match patterns against characters in chatter */\r
+ i++;\r
+ continue;\r
+ }\r
+ }\r
+ if (started == STARTED_CHATTER) {\r
+ if (buf[i] != '\n') {\r
+ /* Don't match patterns against characters in chatter */\r
+ i++;\r
+ continue;\r
+ }\r
+ started = STARTED_NONE;\r
+ }\r
+\r
+ /* Kludge to deal with rcmd protocol */\r
+ if (firstTime && looking_at(buf, &i, "\001*")) {\r
+ DisplayFatalError(&buf[1], 0, 1);\r
+ continue;\r
+ } else {\r
+ firstTime = FALSE;\r
+ }\r
+\r
+ if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {\r
+ ics_type = ICS_ICC;\r
+ ics_prefix = "/";\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ics_type %d\n", ics_type);\r
+ continue;\r
+ }\r
+ if (!loggedOn && looking_at(buf, &i, "freechess.org")) {\r
+ ics_type = ICS_FICS;\r
+ ics_prefix = "$";\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ics_type %d\n", ics_type);\r
+ continue;\r
+ }\r
+ if (!loggedOn && looking_at(buf, &i, "chess.net")) {\r
+ ics_type = ICS_CHESSNET;\r
+ ics_prefix = "/";\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ics_type %d\n", ics_type);\r
+ continue;\r
+ }\r
+\r
+ if (!loggedOn &&\r
+ (looking_at(buf, &i, "\"*\" is *a registered name") ||\r
+ looking_at(buf, &i, "Logging you in as \"*\"") ||\r
+ looking_at(buf, &i, "will be \"*\""))) {\r
+ strcpy(ics_handle, star_match[0]);\r
+ continue;\r
+ }\r
+\r
+ if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
+ DisplayIcsInteractionTitle(buf);\r
+ have_set_title = TRUE;\r
+ }\r
+\r
+ /* skip finger notes */\r
+ if (started == STARTED_NONE &&\r
+ ((buf[i] == ' ' && isdigit(buf[i+1])) ||\r
+ (buf[i] == '1' && buf[i+1] == '0')) &&\r
+ buf[i+2] == ':' && buf[i+3] == ' ') {\r
+ started = STARTED_CHATTER;\r
+ i += 3;\r
+ continue;\r
+ }\r
+\r
+ /* skip formula vars */\r
+ if (started == STARTED_NONE &&\r
+ buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {\r
+ started = STARTED_CHATTER;\r
+ i += 3;\r
+ continue;\r
+ }\r
+\r
+ oldi = i;\r
+ if (appData.zippyTalk || appData.zippyPlay) {\r
+#if ZIPPY\r
+ if (ZippyControl(buf, &i) ||\r
+ ZippyConverse(buf, &i) ||\r
+ (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
+ loggedOn = TRUE;\r
+ continue;\r
+ }\r
+#endif\r
+ } else {\r
+ if (/* Don't color "message" or "messages" output */\r
+ (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||\r
+ looking_at(buf, &i, "*. * at *:*: ") ||\r
+ looking_at(buf, &i, "--* (*:*): ") ||\r
+ /* Regular tells and says */\r
+ (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||\r
+ looking_at(buf, &i, "* (your partner) tells you: ") ||\r
+ looking_at(buf, &i, "* says: ") ||\r
+ /* Message notifications (same color as tells) */\r
+ looking_at(buf, &i, "* has left a message ") ||\r
+ looking_at(buf, &i, "* just sent you a message:\n") ||\r
+ /* Whispers and kibitzes */\r
+ (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||\r
+ looking_at(buf, &i, "* kibitzes: ") ||\r
+ /* Channel tells */\r
+ (tkind = 3, looking_at(buf, &i, "*(*: "))) {\r
+\r
+ if (tkind == 1 && strchr(star_match[0], ':')) {\r
+ /* Avoid "tells you:" spoofs in channels */\r
+ tkind = 3;\r
+ }\r
+ if (star_match[0][0] == NULLCHAR ||\r
+ strchr(star_match[0], ' ') ||\r
+ (tkind == 3 && strchr(star_match[1], ' '))) {\r
+ /* Reject bogus matches */\r
+ i = oldi;\r
+ } else {\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ switch (tkind) {\r
+ case 1:\r
+ Colorize(ColorTell, FALSE);\r
+ curColor = ColorTell;\r
+ break;\r
+ case 2:\r
+ Colorize(ColorKibitz, FALSE);\r
+ curColor = ColorKibitz;\r
+ break;\r
+ case 3:\r
+ p = strrchr(star_match[1], '(');\r
+ if (p == NULL) {\r
+ p = star_match[1];\r
+ } else {\r
+ p++;\r
+ }\r
+ if (atoi(p) == 1) {\r
+ Colorize(ColorChannel1, FALSE);\r
+ curColor = ColorChannel1;\r
+ } else {\r
+ Colorize(ColorChannel, FALSE);\r
+ curColor = ColorChannel;\r
+ }\r
+ break;\r
+ case 5:\r
+ curColor = ColorNormal;\r
+ break;\r
+ }\r
+ }\r
+ if (started == STARTED_NONE && appData.autoComment &&\r
+ (gameMode == IcsObserving ||\r
+ gameMode == IcsPlayingWhite ||\r
+ gameMode == IcsPlayingBlack)) {\r
+ parse_pos = i - oldi;\r
+ memcpy(parse, &buf[oldi], parse_pos);\r
+ parse[parse_pos] = NULLCHAR;\r
+ started = STARTED_COMMENT;\r
+ savingComment = TRUE;\r
+ } else {\r
+ started = STARTED_CHATTER;\r
+ savingComment = FALSE;\r
+ }\r
+ loggedOn = TRUE;\r
+ continue;\r
+ }\r
+ }\r
+\r
+ if (looking_at(buf, &i, "* s-shouts: ") ||\r
+ looking_at(buf, &i, "* c-shouts: ")) {\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorSShout, FALSE);\r
+ curColor = ColorSShout;\r
+ }\r
+ loggedOn = TRUE;\r
+ started = STARTED_CHATTER;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "--->")) {\r
+ loggedOn = TRUE;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "* shouts: ") ||\r
+ looking_at(buf, &i, "--> ")) {\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorShout, FALSE);\r
+ curColor = ColorShout;\r
+ }\r
+ loggedOn = TRUE;\r
+ started = STARTED_CHATTER;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at( buf, &i, "Challenge:")) {\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorChallenge, FALSE);\r
+ curColor = ColorChallenge;\r
+ }\r
+ loggedOn = TRUE;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "* offers you") ||\r
+ looking_at(buf, &i, "* offers to be") ||\r
+ looking_at(buf, &i, "* would like to") ||\r
+ looking_at(buf, &i, "* requests to") ||\r
+ looking_at(buf, &i, "Your opponent offers") ||\r
+ looking_at(buf, &i, "Your opponent requests")) {\r
+\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorRequest, FALSE);\r
+ curColor = ColorRequest;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "* (*) seeking")) {\r
+ if (appData.colorize) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorSeek, FALSE);\r
+ curColor = ColorSeek;\r
+ }\r
+ continue;\r
+ }\r
+ }\r
+\r
+ if (looking_at(buf, &i, "\\ ")) {\r
+ if (prevColor != ColorNormal) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(prevColor, TRUE);\r
+ curColor = prevColor;\r
+ }\r
+ if (savingComment) {\r
+ parse_pos = i - oldi;\r
+ memcpy(parse, &buf[oldi], parse_pos);\r
+ parse[parse_pos] = NULLCHAR;\r
+ started = STARTED_COMMENT;\r
+ } else {\r
+ started = STARTED_CHATTER;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "Black Strength :") ||\r
+ looking_at(buf, &i, "<<< style 10 board >>>") ||\r
+ looking_at(buf, &i, "<10>") ||\r
+ looking_at(buf, &i, "#@#")) {\r
+ /* Wrong board style */\r
+ loggedOn = TRUE;\r
+ SendToICS(ics_prefix);\r
+ SendToICS("set style 12\n");\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ continue;\r
+ }\r
+ \r
+ if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {\r
+ ICSInitScript();\r
+ have_sent_ICS_logon = 1;\r
+ continue;\r
+ }\r
+ \r
+ if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && \r
+ (looking_at(buf, &i, "\n<12> ") ||\r
+ looking_at(buf, &i, "<12> "))) {\r
+ loggedOn = TRUE;\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ }\r
+ next_out = i;\r
+ started = STARTED_BOARD;\r
+ parse_pos = 0;\r
+ continue;\r
+ }\r
+\r
+ if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||\r
+ looking_at(buf, &i, "<b1> ")) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ }\r
+ next_out = i;\r
+ started = STARTED_HOLDINGS;\r
+ parse_pos = 0;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "* *vs. * *--- *")) {\r
+ loggedOn = TRUE;\r
+ /* Header for a move list -- first line */\r
+\r
+ switch (ics_getting_history) {\r
+ case H_FALSE:\r
+ switch (gameMode) {\r
+ case IcsIdle:\r
+ case BeginningOfGame:\r
+ /* User typed "moves" or "oldmoves" while we\r
+ were idle. Pretend we asked for these\r
+ moves and soak them up so user can step\r
+ through them and/or save them.\r
+ */\r
+ Reset(FALSE, TRUE);\r
+ gameMode = IcsObserving;\r
+ ModeHighlight();\r
+ ics_gamenum = -1;\r
+ ics_getting_history = H_GOT_UNREQ_HEADER;\r
+ break;\r
+ case EditGame: /*?*/\r
+ case EditPosition: /*?*/\r
+ /* Should above feature work in these modes too? */\r
+ /* For now it doesn't */\r
+ ics_getting_history = H_GOT_UNWANTED_HEADER;\r
+ break;\r
+ default:\r
+ ics_getting_history = H_GOT_UNWANTED_HEADER;\r
+ break;\r
+ }\r
+ break;\r
+ case H_REQUESTED:\r
+ /* Is this the right one? */\r
+ if (gameInfo.white && gameInfo.black &&\r
+ strcmp(gameInfo.white, star_match[0]) == 0 &&\r
+ strcmp(gameInfo.black, star_match[2]) == 0) {\r
+ /* All is well */\r
+ ics_getting_history = H_GOT_REQ_HEADER;\r
+ }\r
+ break;\r
+ case H_GOT_REQ_HEADER:\r
+ case H_GOT_UNREQ_HEADER:\r
+ case H_GOT_UNWANTED_HEADER:\r
+ case H_GETTING_MOVES:\r
+ /* Should not happen */\r
+ DisplayError("Error gathering move list: two headers", 0);\r
+ ics_getting_history = H_FALSE;\r
+ break;\r
+ }\r
+\r
+ /* Save player ratings into gameInfo if needed */\r
+ if ((ics_getting_history == H_GOT_REQ_HEADER ||\r
+ ics_getting_history == H_GOT_UNREQ_HEADER) &&\r
+ (gameInfo.whiteRating == -1 ||\r
+ gameInfo.blackRating == -1)) {\r
+\r
+ gameInfo.whiteRating = string_to_rating(star_match[1]);\r
+ gameInfo.blackRating = string_to_rating(star_match[3]);\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Ratings from header: W %d, B %d\n", \r
+ gameInfo.whiteRating, gameInfo.blackRating);\r
+ }\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i,\r
+ "* * match, initial time: * minute*, increment: * second")) {\r
+ /* Header for a move list -- second line */\r
+ /* Initial board will follow if this is a wild game */\r
+\r
+ if (gameInfo.event != NULL) free(gameInfo.event);\r
+ sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);\r
+ gameInfo.event = StrSave(str);\r
+ gameInfo.variant = StringToVariant(gameInfo.event);\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "Move ")) {\r
+ /* Beginning of a move list */\r
+ switch (ics_getting_history) {\r
+ case H_FALSE:\r
+ /* Normally should not happen */\r
+ /* Maybe user hit reset while we were parsing */\r
+ break;\r
+ case H_REQUESTED:\r
+ /* Happens if we are ignoring a move list that is not\r
+ * the one we just requested. Common if the user\r
+ * tries to observe two games without turning off\r
+ * getMoveList */\r
+ break;\r
+ case H_GETTING_MOVES:\r
+ /* Should not happen */\r
+ DisplayError("Error gathering move list: nested", 0);\r
+ ics_getting_history = H_FALSE;\r
+ break;\r
+ case H_GOT_REQ_HEADER:\r
+ ics_getting_history = H_GETTING_MOVES;\r
+ started = STARTED_MOVES;\r
+ parse_pos = 0;\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ }\r
+ break;\r
+ case H_GOT_UNREQ_HEADER:\r
+ ics_getting_history = H_GETTING_MOVES;\r
+ started = STARTED_MOVES_NOHIDE;\r
+ parse_pos = 0;\r
+ break;\r
+ case H_GOT_UNWANTED_HEADER:\r
+ ics_getting_history = H_FALSE;\r
+ break;\r
+ }\r
+ continue;\r
+ } \r
+ \r
+ if (looking_at(buf, &i, "% ") ||\r
+ ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
+ && looking_at(buf, &i, "}*"))) {\r
+ savingComment = FALSE;\r
+ switch (started) {\r
+ case STARTED_MOVES:\r
+ case STARTED_MOVES_NOHIDE:\r
+ memcpy(&parse[parse_pos], &buf[oldi], i - oldi);\r
+ parse[parse_pos + i - oldi] = NULLCHAR;\r
+ ParseGameHistory(parse);\r
+#if ZIPPY\r
+ if (appData.zippyPlay && first.initDone) {\r
+ FeedMovesToProgram(&first, forwardMostMove);\r
+ if (gameMode == IcsPlayingWhite) {\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ if (first.sendTime) {\r
+ if (first.useColors) {\r
+ SendToProgram("black\n", &first); \r
+ }\r
+ SendTimeRemaining(&first, TRUE);\r
+ }\r
+ if (first.useColors) {\r
+ SendToProgram("white\ngo\n", &first);\r
+ } else {\r
+ SendToProgram("go\n", &first);\r
+ }\r
+ first.maybeThinking = TRUE;\r
+ } else {\r
+ if (first.usePlayother) {\r
+ if (first.sendTime) {\r
+ SendTimeRemaining(&first, TRUE);\r
+ }\r
+ SendToProgram("playother\n", &first);\r
+ firstMove = FALSE;\r
+ } else {\r
+ firstMove = TRUE;\r
+ }\r
+ }\r
+ } else if (gameMode == IcsPlayingBlack) {\r
+ if (!WhiteOnMove(forwardMostMove)) {\r
+ if (first.sendTime) {\r
+ if (first.useColors) {\r
+ SendToProgram("white\n", &first);\r
+ }\r
+ SendTimeRemaining(&first, FALSE);\r
+ }\r
+ if (first.useColors) {\r
+ SendToProgram("black\ngo\n", &first);\r
+ } else {\r
+ SendToProgram("go\n", &first);\r
+ }\r
+ first.maybeThinking = TRUE;\r
+ } else {\r
+ if (first.usePlayother) {\r
+ if (first.sendTime) {\r
+ SendTimeRemaining(&first, FALSE);\r
+ }\r
+ SendToProgram("playother\n", &first);\r
+ firstMove = FALSE;\r
+ } else {\r
+ firstMove = TRUE;\r
+ }\r
+ }\r
+ } \r
+ }\r
+#endif\r
+ if (gameMode == IcsObserving && ics_gamenum == -1) {\r
+ /* Moves came from oldmoves or moves command\r
+ while we weren't doing anything else.\r
+ */\r
+ currentMove = forwardMostMove;\r
+ ClearHighlights();/*!!could figure this out*/\r
+ flipView = appData.flipView;\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ DisplayBothClocks();\r
+ sprintf(str, "%s vs. %s",\r
+ gameInfo.white, gameInfo.black);\r
+ DisplayTitle(str);\r
+ gameMode = IcsIdle;\r
+ } else {\r
+ /* Moves were history of an active game */\r
+ if (gameInfo.resultDetails != NULL) {\r
+ free(gameInfo.resultDetails);\r
+ gameInfo.resultDetails = NULL;\r
+ }\r
+ }\r
+ HistorySet(parseList, backwardMostMove,\r
+ forwardMostMove, currentMove-1);\r
+ DisplayMove(currentMove - 1);\r
+ if (started == STARTED_MOVES) next_out = i;\r
+ started = STARTED_NONE;\r
+ ics_getting_history = H_FALSE;\r
+ break;\r
+\r
+ case STARTED_OBSERVE:\r
+ started = STARTED_NONE;\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ continue;\r
+ }\r
+ \r
+ if ((started == STARTED_MOVES || started == STARTED_BOARD ||\r
+ started == STARTED_HOLDINGS ||\r
+ started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {\r
+ /* Accumulate characters in move list or board */\r
+ parse[parse_pos++] = buf[i];\r
+ }\r
+ \r
+ /* Start of game messages. Mostly we detect start of game\r
+ when the first board image arrives. On some versions\r
+ of the ICS, though, we need to do a "refresh" after starting\r
+ to observe in order to get the current board right away. */\r
+ if (looking_at(buf, &i, "Adding game * to observation list")) {\r
+ started = STARTED_OBSERVE;\r
+ continue;\r
+ }\r
+\r
+ /* Handle auto-observe */\r
+ if (appData.autoObserve &&\r
+ (gameMode == IcsIdle || gameMode == BeginningOfGame) &&\r
+ looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {\r
+ char *player;\r
+ /* Choose the player that was highlighted, if any. */\r
+ if (star_match[0][0] == '\033' ||\r
+ star_match[1][0] != '\033') {\r
+ player = star_match[0];\r
+ } else {\r
+ player = star_match[2];\r
+ }\r
+ sprintf(str, "%sobserve %s\n",\r
+ ics_prefix, StripHighlightAndTitle(player));\r
+ SendToICS(str);\r
+\r
+ /* Save ratings from notify string */\r
+ strcpy(player1Name, star_match[0]);\r
+ player1Rating = string_to_rating(star_match[1]);\r
+ strcpy(player2Name, star_match[2]);\r
+ player2Rating = string_to_rating(star_match[3]);\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, \r
+ "Ratings from 'Game notification:' %s %d, %s %d\n",\r
+ player1Name, player1Rating,\r
+ player2Name, player2Rating);\r
+\r
+ continue;\r
+ }\r
+\r
+ /* Deal with automatic examine mode after a game,\r
+ and with IcsObserving -> IcsExamining transition */\r
+ if (looking_at(buf, &i, "Entering examine mode for game *") ||\r
+ looking_at(buf, &i, "has made you an examiner of game *")) {\r
+\r
+ int gamenum = atoi(star_match[0]);\r
+ if ((gameMode == IcsIdle || gameMode == IcsObserving) &&\r
+ gamenum == ics_gamenum) {\r
+ /* We were already playing or observing this game;\r
+ no need to refetch history */\r
+ gameMode = IcsExamining;\r
+ if (pausing) {\r
+ pauseExamForwardMostMove = forwardMostMove;\r
+ } else if (currentMove < forwardMostMove) {\r
+ ForwardInner(forwardMostMove);\r
+ }\r
+ } else {\r
+ /* I don't think this case really can happen */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ }\r
+ continue;\r
+ } \r
+ \r
+ /* Error messages */\r
+ if (ics_user_moved) {\r
+ if (looking_at(buf, &i, "Illegal move") ||\r
+ looking_at(buf, &i, "Not a legal move") ||\r
+ looking_at(buf, &i, "Your king is in check") ||\r
+ looking_at(buf, &i, "It isn't your turn") ||\r
+ looking_at(buf, &i, "It is not your move")) {\r
+ /* Illegal move */\r
+ ics_user_moved = 0;\r
+ if (forwardMostMove > backwardMostMove) {\r
+ currentMove = --forwardMostMove;\r
+ DisplayMove(currentMove - 1); /* before DMError */\r
+ DisplayMoveError("Illegal move (rejected by ICS)");\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ SwitchClocks();\r
+ DisplayBothClocks();\r
+ }\r
+ continue;\r
+ }\r
+ }\r
+\r
+ if (looking_at(buf, &i, "still have time") ||\r
+ looking_at(buf, &i, "not out of time") ||\r
+ looking_at(buf, &i, "either player is out of time") ||\r
+ looking_at(buf, &i, "has timeseal; checking")) {\r
+ /* We must have called his flag a little too soon */\r
+ whiteFlag = blackFlag = FALSE;\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "added * seconds to") ||\r
+ looking_at(buf, &i, "seconds were added to")) {\r
+ /* Update the clocks */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ continue;\r
+ }\r
+\r
+ if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {\r
+ ics_clock_paused = TRUE;\r
+ StopClocks();\r
+ continue;\r
+ }\r
+\r
+ if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {\r
+ ics_clock_paused = FALSE;\r
+ StartClocks();\r
+ continue;\r
+ }\r
+\r
+ /* Grab player ratings from the Creating: message.\r
+ Note we have to check for the special case when\r
+ the ICS inserts things like [white] or [black]. */\r
+ if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||\r
+ looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {\r
+ /* star_matches:\r
+ 0 player 1 name (not necessarily white)\r
+ 1 player 1 rating\r
+ 2 empty, white, or black (IGNORED)\r
+ 3 player 2 name (not necessarily black)\r
+ 4 player 2 rating\r
+ \r
+ The names/ratings are sorted out when the game\r
+ actually starts (below).\r
+ */\r
+ strcpy(player1Name, StripHighlightAndTitle(star_match[0]));\r
+ player1Rating = string_to_rating(star_match[1]);\r
+ strcpy(player2Name, StripHighlightAndTitle(star_match[3]));\r
+ player2Rating = string_to_rating(star_match[4]);\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, \r
+ "Ratings from 'Creating:' %s %d, %s %d\n",\r
+ player1Name, player1Rating,\r
+ player2Name, player2Rating);\r
+\r
+ continue;\r
+ }\r
+ \r
+ /* Improved generic start/end-of-game messages */\r
+ if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||\r
+ (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){\r
+ /* If tkind == 0: */\r
+ /* star_match[0] is the game number */\r
+ /* [1] is the white player's name */\r
+ /* [2] is the black player's name */\r
+ /* For end-of-game: */\r
+ /* [3] is the reason for the game end */\r
+ /* [4] is a PGN end game-token, preceded by " " */\r
+ /* For start-of-game: */\r
+ /* [3] begins with "Creating" or "Continuing" */\r
+ /* [4] is " *" or empty (don't care). */\r
+ int gamenum = atoi(star_match[0]);\r
+ char *whitename, *blackname, *why, *endtoken;\r
+ ChessMove endtype = (ChessMove) 0;\r
+\r
+ if (tkind == 0) {\r
+ whitename = star_match[1];\r
+ blackname = star_match[2];\r
+ why = star_match[3];\r
+ endtoken = star_match[4];\r
+ } else {\r
+ whitename = star_match[1];\r
+ blackname = star_match[3];\r
+ why = star_match[5];\r
+ endtoken = star_match[6];\r
+ }\r
+\r
+ /* Game start messages */\r
+ if (strncmp(why, "Creating ", 9) == 0 ||\r
+ strncmp(why, "Continuing ", 11) == 0) {\r
+ gs_gamenum = gamenum;\r
+ strcpy(gs_kind, strchr(why, ' ') + 1);\r
+#if ZIPPY\r
+ if (appData.zippyPlay) {\r
+ ZippyGameStart(whitename, blackname);\r
+ }\r
+#endif /*ZIPPY*/\r
+ continue;\r
+ }\r
+\r
+ /* Game end messages */\r
+ if (gameMode == IcsIdle || gameMode == BeginningOfGame ||\r
+ ics_gamenum != gamenum) {\r
+ continue;\r
+ }\r
+ while (endtoken[0] == ' ') endtoken++;\r
+ switch (endtoken[0]) {\r
+ case '*':\r
+ default:\r
+ endtype = GameUnfinished;\r
+ break;\r
+ case '0':\r
+ endtype = BlackWins;\r
+ break;\r
+ case '1':\r
+ if (endtoken[1] == '/')\r
+ endtype = GameIsDrawn;\r
+ else\r
+ endtype = WhiteWins;\r
+ break;\r
+ }\r
+ GameEnds(endtype, why, GE_ICS);\r
+#if ZIPPY\r
+ if (appData.zippyPlay && first.initDone) {\r
+ ZippyGameEnd(endtype, why);\r
+ if (first.pr == NULL) {\r
+ /* Start the next process early so that we'll\r
+ be ready for the next challenge */\r
+ StartChessProgram(&first);\r
+ }\r
+ /* Send "new" early, in case this command takes\r
+ a long time to finish, so that we'll be ready\r
+ for the next challenge. */\r
+ Reset(TRUE, TRUE);\r
+ }\r
+#endif /*ZIPPY*/\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "Removing game * from observation") ||\r
+ looking_at(buf, &i, "no longer observing game *") ||\r
+ looking_at(buf, &i, "Game * (*) has no examiners")) {\r
+ if (gameMode == IcsObserving &&\r
+ atoi(star_match[0]) == ics_gamenum)\r
+ {\r
+ StopClocks();\r
+ gameMode = IcsIdle;\r
+ ics_gamenum = -1;\r
+ ics_user_moved = FALSE;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ if (looking_at(buf, &i, "no longer examining game *")) {\r
+ if (gameMode == IcsExamining &&\r
+ atoi(star_match[0]) == ics_gamenum)\r
+ {\r
+ gameMode = IcsIdle;\r
+ ics_gamenum = -1;\r
+ ics_user_moved = FALSE;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ /* Advance leftover_start past any newlines we find,\r
+ so only partial lines can get reparsed */\r
+ if (looking_at(buf, &i, "\n")) {\r
+ prevColor = curColor;\r
+ if (curColor != ColorNormal) {\r
+ if (oldi > next_out) {\r
+ SendToPlayer(&buf[next_out], oldi - next_out);\r
+ next_out = oldi;\r
+ }\r
+ Colorize(ColorNormal, FALSE);\r
+ curColor = ColorNormal;\r
+ }\r
+ if (started == STARTED_BOARD) {\r
+ started = STARTED_NONE;\r
+ parse[parse_pos] = NULLCHAR;\r
+ ParseBoard12(parse);\r
+ ics_user_moved = 0;\r
+\r
+ /* Send premove here */\r
+ if (appData.premove) {\r
+ char str[MSG_SIZ];\r
+ if (currentMove == 0 &&\r
+ gameMode == IcsPlayingWhite &&\r
+ appData.premoveWhite) {\r
+ sprintf(str, "%s%s\n", ics_prefix,\r
+ appData.premoveWhiteText);\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Sending premove:\n");\r
+ SendToICS(str);\r
+ } else if (currentMove == 1 &&\r
+ gameMode == IcsPlayingBlack &&\r
+ appData.premoveBlack) {\r
+ sprintf(str, "%s%s\n", ics_prefix,\r
+ appData.premoveBlackText);\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Sending premove:\n");\r
+ SendToICS(str);\r
+ } else if (gotPremove) {\r
+ gotPremove = 0;\r
+ ClearPremoveHighlights();\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Sending premove:\n");\r
+ UserMoveEvent(premoveFromX, premoveFromY, \r
+ premoveToX, premoveToY, \r
+ premovePromoChar);\r
+ }\r
+ }\r
+\r
+ /* Usually suppress following prompt */\r
+ if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {\r
+ if (looking_at(buf, &i, "*% ")) {\r
+ savingComment = FALSE;\r
+ }\r
+ }\r
+ next_out = i;\r
+ } else if (started == STARTED_HOLDINGS) {\r
+ int gamenum;\r
+ char new_piece[MSG_SIZ];\r
+ started = STARTED_NONE;\r
+ parse[parse_pos] = NULLCHAR;\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsing holdings: %s\n", parse);\r
+ if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
+ gamenum == ics_gamenum) {\r
+ if (gameInfo.variant == VariantNormal) {\r
+ gameInfo.variant = VariantCrazyhouse; /*temp guess*/\r
+ /* Get a move list just to see the header, which\r
+ will tell us whether this is really bug or zh */\r
+ if (ics_getting_history == H_FALSE) {\r
+ ics_getting_history = H_REQUESTED;\r
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+ SendToICS(str);\r
+ }\r
+ }\r
+ new_piece[0] = NULLCHAR;\r
+ sscanf(parse, "game %d white [%s black [%s <- %s",\r
+ &gamenum, white_holding, black_holding,\r
+ new_piece);\r
+ white_holding[strlen(white_holding)-1] = NULLCHAR;\r
+ black_holding[strlen(black_holding)-1] = NULLCHAR;\r
+#if ZIPPY\r
+ if (appData.zippyPlay && first.initDone) {\r
+ ZippyHoldings(white_holding, black_holding,\r
+ new_piece);\r
+ }\r
+#endif /*ZIPPY*/\r
+ if (tinyLayout || smallLayout) {\r
+ char wh[16], bh[16];\r
+ PackHolding(wh, white_holding);\r
+ PackHolding(bh, black_holding);\r
+ sprintf(str, "[%s-%s] %s-%s", wh, bh,\r
+ gameInfo.white, gameInfo.black);\r
+ } else {\r
+ sprintf(str, "%s [%s] vs. %s [%s]",\r
+ gameInfo.white, white_holding,\r
+ gameInfo.black, black_holding);\r
+ }\r
+ DrawPosition(FALSE, NULL);\r
+ DisplayTitle(str);\r
+ }\r
+ /* Suppress following prompt */\r
+ if (looking_at(buf, &i, "*% ")) {\r
+ savingComment = FALSE;\r
+ }\r
+ next_out = i;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ i++; /* skip unparsed character and loop back */\r
+ }\r
+ \r
+ if (started != STARTED_MOVES && started != STARTED_BOARD &&\r
+ started != STARTED_HOLDINGS && i > next_out) {\r
+ SendToPlayer(&buf[next_out], i - next_out);\r
+ next_out = i;\r
+ }\r
+ \r
+ leftover_len = buf_len - leftover_start;\r
+ /* if buffer ends with something we couldn't parse,\r
+ reparse it after appending the next read */\r
+ \r
+ } else if (count == 0) {\r
+ RemoveInputSource(isr);\r
+ DisplayFatalError("Connection closed by ICS", 0, 0);\r
+ } else {\r
+ DisplayFatalError("Error reading from ICS", error, 1);\r
+ }\r
+}\r
+\r
+\r
+/* Board style 12 looks like this:\r
+ \r
+ <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0\r
+ \r
+ * The "<12> " is stripped before it gets to this routine. The two\r
+ * trailing 0's (flip state and clock ticking) are later addition, and\r
+ * some chess servers may not have them, or may have only the first.\r
+ * Additional trailing fields may be added in the future. \r
+ */\r
+\r
+#define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"\r
+\r
+#define RELATION_OBSERVING_PLAYED 0\r
+#define RELATION_OBSERVING_STATIC -2 /* examined, oldmoves, or smoves */\r
+#define RELATION_PLAYING_MYMOVE 1\r
+#define RELATION_PLAYING_NOTMYMOVE -1\r
+#define RELATION_EXAMINING 2\r
+#define RELATION_ISOLATED_BOARD -3\r
+#define RELATION_STARTING_POSITION -4 /* FICS only */\r
+\r
+void\r
+ParseBoard12(string)\r
+ char *string;\r
+{ \r
+ GameMode newGameMode;\r
+ int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;\r
+ int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;\r
+ int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;\r
+ char to_play, board_chars[72];\r
+ char move_str[500], str[500], elapsed_time[500];\r
+ char black[32], white[32];\r
+ Board board;\r
+ int prevMove = currentMove;\r
+ int ticking = 2;\r
+ ChessMove moveType;\r
+ int fromX, fromY, toX, toY;\r
+ char promoChar;\r
+\r
+ fromX = fromY = toX = toY = -1;\r
+ \r
+ newGame = FALSE;\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsing board: %s\n", string);\r
+\r
+ move_str[0] = NULLCHAR;\r
+ elapsed_time[0] = NULLCHAR;\r
+ n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,\r
+ &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,\r
+ &gamenum, white, black, &relation, &basetime, &increment,\r
+ &white_stren, &black_stren, &white_time, &black_time,\r
+ &moveNum, str, elapsed_time, move_str, &ics_flip,\r
+ &ticking);\r
+\r
+ if (n < 22) {\r
+ sprintf(str, "Failed to parse board string:\n\"%s\"", string);\r
+ DisplayError(str, 0);\r
+ return;\r
+ }\r
+\r
+ /* Convert the move number to internal form */\r
+ moveNum = (moveNum - 1) * 2;\r
+ if (to_play == 'B') moveNum++;\r
+ if (moveNum >= MAX_MOVES) {\r
+ DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+ 0, 1);\r
+ return;\r
+ }\r
+ \r
+ switch (relation) {\r
+ case RELATION_OBSERVING_PLAYED:\r
+ case RELATION_OBSERVING_STATIC:\r
+ if (gamenum == -1) {\r
+ /* Old ICC buglet */\r
+ relation = RELATION_OBSERVING_STATIC;\r
+ }\r
+ newGameMode = IcsObserving;\r
+ break;\r
+ case RELATION_PLAYING_MYMOVE:\r
+ case RELATION_PLAYING_NOTMYMOVE:\r
+ newGameMode =\r
+ ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?\r
+ IcsPlayingWhite : IcsPlayingBlack;\r
+ break;\r
+ case RELATION_EXAMINING:\r
+ newGameMode = IcsExamining;\r
+ break;\r
+ case RELATION_ISOLATED_BOARD:\r
+ default:\r
+ /* Just display this board. If user was doing something else,\r
+ we will forget about it until the next board comes. */ \r
+ newGameMode = IcsIdle;\r
+ break;\r
+ case RELATION_STARTING_POSITION:\r
+ newGameMode = gameMode;\r
+ break;\r
+ }\r
+ \r
+ /* Modify behavior for initial board display on move listing\r
+ of wild games.\r
+ */\r
+ switch (ics_getting_history) {\r
+ case H_FALSE:\r
+ case H_REQUESTED:\r
+ break;\r
+ case H_GOT_REQ_HEADER:\r
+ case H_GOT_UNREQ_HEADER:\r
+ /* This is the initial position of the current game */\r
+ gamenum = ics_gamenum;\r
+ moveNum = 0; /* old ICS bug workaround */\r
+ if (to_play == 'B') {\r
+ startedFromSetupPosition = TRUE;\r
+ blackPlaysFirst = TRUE;\r
+ moveNum = 1;\r
+ if (forwardMostMove == 0) forwardMostMove = 1;\r
+ if (backwardMostMove == 0) backwardMostMove = 1;\r
+ if (currentMove == 0) currentMove = 1;\r
+ }\r
+ newGameMode = gameMode;\r
+ relation = RELATION_STARTING_POSITION; /* ICC needs this */\r
+ break;\r
+ case H_GOT_UNWANTED_HEADER:\r
+ /* This is an initial board that we don't want */\r
+ return;\r
+ case H_GETTING_MOVES:\r
+ /* Should not happen */\r
+ DisplayError("Error gathering move list: extra board", 0);\r
+ ics_getting_history = H_FALSE;\r
+ return;\r
+ }\r
+ \r
+ /* Take action if this is the first board of a new game, or of a\r
+ different game than is currently being displayed. */\r
+ if (gamenum != ics_gamenum || newGameMode != gameMode ||\r
+ relation == RELATION_ISOLATED_BOARD) {\r
+ \r
+ /* Forget the old game and get the history (if any) of the new one */\r
+ if (gameMode != BeginningOfGame) {\r
+ Reset(FALSE, TRUE);\r
+ }\r
+ newGame = TRUE;\r
+ if (appData.autoRaiseBoard) BoardToTop();\r
+ prevMove = -3;\r
+ if (gamenum == -1) {\r
+ newGameMode = IcsIdle;\r
+ } else if (moveNum > 0 && newGameMode != IcsIdle &&\r
+ appData.getMoveList) {\r
+ /* Need to get game history */\r
+ ics_getting_history = H_REQUESTED;\r
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+ SendToICS(str);\r
+ }\r
+ \r
+ /* Initially flip the board to have black on the bottom if playing\r
+ black or if the ICS flip flag is set, but let the user change\r
+ it with the Flip View button. */\r
+ flipView = appData.autoFlipView ? \r
+ (newGameMode == IcsPlayingBlack) || ics_flip :\r
+ appData.flipView;\r
+ \r
+ /* Done with values from previous mode; copy in new ones */\r
+ gameMode = newGameMode;\r
+ ModeHighlight();\r
+ ics_gamenum = gamenum;\r
+ if (gamenum == gs_gamenum) {\r
+ int klen = strlen(gs_kind);\r
+ if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;\r
+ sprintf(str, "ICS %s", gs_kind);\r
+ gameInfo.event = StrSave(str);\r
+ } else {\r
+ gameInfo.event = StrSave("ICS game");\r
+ }\r
+ gameInfo.site = StrSave(appData.icsHost);\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave(white);\r
+ gameInfo.black = StrSave(black);\r
+ timeControl = basetime * 60 * 1000;\r
+ timeControl_2 = 0;\r
+ timeIncrement = increment * 1000;\r
+ movesPerSession = 0;\r
+ gameInfo.timeControl = TimeControlTagValue();\r
+ gameInfo.variant = StringToVariant(gameInfo.event);\r
+ gameInfo.outOfBook = NULL;\r
+ \r
+ /* Do we have the ratings? */\r
+ if (strcmp(player1Name, white) == 0 &&\r
+ strcmp(player2Name, black) == 0) {\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
+ player1Rating, player2Rating);\r
+ gameInfo.whiteRating = player1Rating;\r
+ gameInfo.blackRating = player2Rating;\r
+ } else if (strcmp(player2Name, white) == 0 &&\r
+ strcmp(player1Name, black) == 0) {\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
+ player2Rating, player1Rating);\r
+ gameInfo.whiteRating = player2Rating;\r
+ gameInfo.blackRating = player1Rating;\r
+ }\r
+ player1Name[0] = player2Name[0] = NULLCHAR;\r
+\r
+ /* Silence shouts if requested */\r
+ if (appData.quietPlay &&\r
+ (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("set shout 0\n");\r
+ }\r
+ }\r
+ \r
+ /* Deal with midgame name changes */\r
+ if (!newGame) {\r
+ if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {\r
+ if (gameInfo.white) free(gameInfo.white);\r
+ gameInfo.white = StrSave(white);\r
+ }\r
+ if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {\r
+ if (gameInfo.black) free(gameInfo.black);\r
+ gameInfo.black = StrSave(black);\r
+ }\r
+ }\r
+ \r
+ /* Throw away game result if anything actually changes in examine mode */\r
+ if (gameMode == IcsExamining && !newGame) {\r
+ gameInfo.result = GameUnfinished;\r
+ if (gameInfo.resultDetails != NULL) {\r
+ free(gameInfo.resultDetails);\r
+ gameInfo.resultDetails = NULL;\r
+ }\r
+ }\r
+ \r
+ /* In pausing && IcsExamining mode, we ignore boards coming\r
+ in if they are in a different variation than we are. */\r
+ if (pauseExamInvalid) return;\r
+ if (pausing && gameMode == IcsExamining) {\r
+ if (moveNum <= pauseExamForwardMostMove) {\r
+ pauseExamInvalid = TRUE;\r
+ forwardMostMove = pauseExamForwardMostMove;\r
+ return;\r
+ }\r
+ }\r
+ \r
+ /* Parse the board */\r
+ for (k = 0; k < 8; k++)\r
+ for (j = 0; j < 8; j++)\r
+ board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);\r
+ CopyBoard(boards[moveNum], board);\r
+ if (moveNum == 0) {\r
+ startedFromSetupPosition =\r
+ !CompareBoards(board, initialPosition);\r
+ }\r
+ \r
+ if (ics_getting_history == H_GOT_REQ_HEADER ||\r
+ ics_getting_history == H_GOT_UNREQ_HEADER) {\r
+ /* This was an initial position from a move list, not\r
+ the current position */\r
+ return;\r
+ }\r
+ \r
+ /* Update currentMove and known move number limits */\r
+ newMove = newGame || moveNum > forwardMostMove;\r
+ if (newGame) {\r
+ forwardMostMove = backwardMostMove = currentMove = moveNum;\r
+ if (gameMode == IcsExamining && moveNum == 0) {\r
+ /* Workaround for ICS limitation: we are not told the wild\r
+ type when starting to examine a game. But if we ask for\r
+ the move list, the move list header will tell us */\r
+ ics_getting_history = H_REQUESTED;\r
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+ SendToICS(str);\r
+ }\r
+ } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove\r
+ || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {\r
+ forwardMostMove = moveNum;\r
+ if (!pausing || currentMove > forwardMostMove)\r
+ currentMove = forwardMostMove;\r
+ } else {\r
+ /* New part of history that is not contiguous with old part */ \r
+ if (pausing && gameMode == IcsExamining) {\r
+ pauseExamInvalid = TRUE;\r
+ forwardMostMove = pauseExamForwardMostMove;\r
+ return;\r
+ }\r
+ forwardMostMove = backwardMostMove = currentMove = moveNum;\r
+ if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {\r
+ ics_getting_history = H_REQUESTED;\r
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+ SendToICS(str);\r
+ }\r
+ }\r
+ \r
+ /* Update the clocks */\r
+ if (strchr(elapsed_time, '.')) {\r
+ /* Time is in ms */\r
+ timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;\r
+ timeRemaining[1][moveNum] = blackTimeRemaining = black_time;\r
+ } else {\r
+ /* Time is in seconds */\r
+ timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;\r
+ timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;\r
+ }\r
+ \r
+\r
+#if ZIPPY\r
+ if (appData.zippyPlay && newGame &&\r
+ gameMode != IcsObserving && gameMode != IcsIdle &&\r
+ gameMode != IcsExamining)\r
+ ZippyFirstBoard(moveNum, basetime, increment);\r
+#endif\r
+ \r
+ /* Put the move on the move list, first converting\r
+ to canonical algebraic form. */\r
+ if (moveNum > 0) {\r
+ if (moveNum <= backwardMostMove) {\r
+ /* We don't know what the board looked like before\r
+ this move. Punt. */\r
+ strcpy(parseList[moveNum - 1], move_str);\r
+ strcat(parseList[moveNum - 1], " ");\r
+ strcat(parseList[moveNum - 1], elapsed_time);\r
+ moveList[moveNum - 1][0] = NULLCHAR;\r
+ } else if (ParseOneMove(move_str, moveNum - 1, &moveType,\r
+ &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+ (void) CoordsToAlgebraic(boards[moveNum - 1],\r
+ PosFlags(moveNum - 1), EP_UNKNOWN,\r
+ fromY, fromX, toY, toX, promoChar,\r
+ parseList[moveNum-1]);\r
+ switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN,\r
+ castlingRights[moveNum]) ) {\r
+ case MT_NONE:\r
+ case MT_STALEMATE:\r
+ default:\r
+ break;\r
+ case MT_CHECK:\r
+ strcat(parseList[moveNum - 1], "+");\r
+ break;\r
+ case MT_CHECKMATE:\r
+ strcat(parseList[moveNum - 1], "#");\r
+ break;\r
+ }\r
+ strcat(parseList[moveNum - 1], " ");\r
+ strcat(parseList[moveNum - 1], elapsed_time);\r
+ /* currentMoveString is set as a side-effect of ParseOneMove */\r
+ strcpy(moveList[moveNum - 1], currentMoveString);\r
+ strcat(moveList[moveNum - 1], "\n");\r
+ } else if (strcmp(move_str, "none") == 0) {\r
+ /* Again, we don't know what the board looked like;\r
+ this is really the start of the game. */\r
+ parseList[moveNum - 1][0] = NULLCHAR;\r
+ moveList[moveNum - 1][0] = NULLCHAR;\r
+ backwardMostMove = moveNum;\r
+ startedFromSetupPosition = TRUE;\r
+ fromX = fromY = toX = toY = -1;\r
+ } else {\r
+ /* Move from ICS was illegal!? Punt. */\r
+#if 0\r
+ if (appData.testLegality && appData.debugMode) {\r
+ sprintf(str, "Illegal move \"%s\" from ICS", move_str);\r
+ DisplayError(str, 0);\r
+ }\r
+#endif\r
+ strcpy(parseList[moveNum - 1], move_str);\r
+ strcat(parseList[moveNum - 1], " ");\r
+ strcat(parseList[moveNum - 1], elapsed_time);\r
+ moveList[moveNum - 1][0] = NULLCHAR;\r
+ fromX = fromY = toX = toY = -1;\r
+ }\r
+\r
+#if ZIPPY\r
+ /* Send move to chess program (BEFORE animating it). */\r
+ if (appData.zippyPlay && !newGame && newMove && \r
+ (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {\r
+\r
+ if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||\r
+ (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {\r
+ if (moveList[moveNum - 1][0] == NULLCHAR) {\r
+ sprintf(str, "Couldn't parse move \"%s\" from ICS",\r
+ move_str);\r
+ DisplayError(str, 0);\r
+ } else {\r
+ if (first.sendTime) {\r
+ SendTimeRemaining(&first, gameMode == IcsPlayingWhite);\r
+ }\r
+ SendMoveToProgram(moveNum - 1, &first);\r
+ if (firstMove) {\r
+ firstMove = FALSE;\r
+ if (first.useColors) {\r
+ SendToProgram(gameMode == IcsPlayingWhite ?\r
+ "white\ngo\n" :\r
+ "black\ngo\n", &first);\r
+ } else {\r
+ SendToProgram("go\n", &first);\r
+ }\r
+ first.maybeThinking = TRUE;\r
+ }\r
+ }\r
+ } else if (gameMode == IcsObserving || gameMode == IcsExamining) {\r
+ if (moveList[moveNum - 1][0] == NULLCHAR) {\r
+ sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);\r
+ DisplayError(str, 0);\r
+ } else {\r
+ SendMoveToProgram(moveNum - 1, &first);\r
+ }\r
+ }\r
+ }\r
+#endif\r
+ }\r
+\r
+ if (moveNum > 0 && !gotPremove) {\r
+ /* If move comes from a remote source, animate it. If it\r
+ isn't remote, it will have already been animated. */\r
+ if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {\r
+ AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);\r
+ }\r
+ if (!pausing && appData.highlightLastMove) {\r
+ SetHighlights(fromX, fromY, toX, toY);\r
+ }\r
+ }\r
+ \r
+ /* Start the clocks */\r
+ whiteFlag = blackFlag = FALSE;\r
+ appData.clockMode = !(basetime == 0 && increment == 0);\r
+ if (ticking == 0) {\r
+ ics_clock_paused = TRUE;\r
+ StopClocks();\r
+ } else if (ticking == 1) {\r
+ ics_clock_paused = FALSE;\r
+ }\r
+ if (gameMode == IcsIdle ||\r
+ relation == RELATION_OBSERVING_STATIC ||\r
+ relation == RELATION_EXAMINING ||\r
+ ics_clock_paused)\r
+ DisplayBothClocks();\r
+ else\r
+ StartClocks();\r
+ \r
+ /* Display opponents and material strengths */\r
+ if (gameInfo.variant != VariantBughouse &&\r
+ gameInfo.variant != VariantCrazyhouse) {\r
+ if (tinyLayout || smallLayout) {\r
+ sprintf(str, "%s(%d) %s(%d) {%d %d}", \r
+ gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+ basetime, increment);\r
+ } else {\r
+ sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", \r
+ gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+ basetime, increment);\r
+ }\r
+ DisplayTitle(str);\r
+ }\r
+\r
+ \r
+ /* Display the board */\r
+ if (!pausing) {\r
+ \r
+ if (appData.premove)\r
+ if (!gotPremove || \r
+ ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||\r
+ ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))\r
+ ClearPremoveHighlights();\r
+\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ DisplayMove(moveNum - 1);\r
+ if (appData.ringBellAfterMoves && !ics_user_moved)\r
+ RingBell();\r
+ }\r
+\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+}\r
+\r
+void\r
+GetMoveListEvent()\r
+{\r
+ char buf[MSG_SIZ];\r
+ if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {\r
+ ics_getting_history = H_REQUESTED;\r
+ sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);\r
+ SendToICS(buf);\r
+ }\r
+}\r
+\r
+void\r
+AnalysisPeriodicEvent(force)\r
+ int force;\r
+{\r
+ if (((programStats.ok_to_send == 0 || programStats.line_is_book)\r
+ && !force) || !appData.periodicUpdates)\r
+ return;\r
+\r
+ /* Send . command to Crafty to collect stats */\r
+ SendToProgram(".\n", &first);\r
+\r
+ /* Don't send another until we get a response (this makes\r
+ us stop sending to old Crafty's which don't understand\r
+ the "." command (sending illegal cmds resets node count & time,\r
+ which looks bad)) */\r
+ programStats.ok_to_send = 0;\r
+}\r
+\r
+void\r
+SendMoveToProgram(moveNum, cps)\r
+ int moveNum;\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ if (cps->useUsermove) {\r
+ SendToProgram("usermove ", cps);\r
+ }\r
+ if (cps->useSAN) {\r
+ char *space;\r
+ if ((space = strchr(parseList[moveNum], ' ')) != NULL) {\r
+ int len = space - parseList[moveNum];\r
+ memcpy(buf, parseList[moveNum], len);\r
+ buf[len++] = '\n';\r
+ buf[len] = NULLCHAR;\r
+ } else {\r
+ sprintf(buf, "%s\n", parseList[moveNum]);\r
+ }\r
+ /* [HGM] decrement all digits to code ranks starting from 0 */\r
+ if(BOARD_HEIGHT>8) {\r
+ char *p = buf;\r
+ while(*p) { if(*p < 'A') (*p)--; p++; }\r
+ }\r
+ SendToProgram(buf, cps);\r
+ } else {\r
+ /* Added by Tord: Send castle moves in "O-O" in FRC games if required by\r
+ * the engine. It would be nice to have a better way to identify castle \r
+ * moves here. */\r
+ if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {\r
+ int fromX = moveList[moveNum][0] - 'a'; \r
+ int fromY = moveList[moveNum][1] - ONE;\r
+ int toX = moveList[moveNum][2] - 'a'; \r
+ int toY = moveList[moveNum][3] - ONE;\r
+ if((boards[currentMove][fromY][fromX] == WhiteKing \r
+ && boards[currentMove][toY][toX] == WhiteRook)\r
+ || (boards[currentMove][fromY][fromX] == BlackKing \r
+ && boards[currentMove][toY][toX] == BlackRook)) {\r
+ if(toX > fromX) SendToProgram("O-O\n", cps);\r
+ else SendToProgram("O-O-O\n", cps);\r
+ }\r
+ else SendToProgram(moveList[moveNum], cps);\r
+ }\r
+ else SendToProgram(moveList[moveNum], cps);\r
+ /* End of additions by Tord */\r
+ }\r
+}\r
+\r
+void\r
+SendMoveToICS(moveType, fromX, fromY, toX, toY)\r
+ ChessMove moveType;\r
+ int fromX, fromY, toX, toY;\r
+{\r
+ char user_move[MSG_SIZ];\r
+\r
+ switch (moveType) {\r
+ default:\r
+ sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",\r
+ (int)moveType, fromX, fromY, toX, toY);\r
+ DisplayError(user_move + strlen("say "), 0);\r
+ break;\r
+ case WhiteKingSideCastle:\r
+ case BlackKingSideCastle:\r
+ case WhiteQueenSideCastleWild:\r
+ case BlackQueenSideCastleWild:\r
+ /* PUSH Fabien */\r
+ case WhiteHSideCastleFR:\r
+ case BlackHSideCastleFR:\r
+ /* POP Fabien */\r
+ sprintf(user_move, "o-o\n");\r
+ break;\r
+ case WhiteQueenSideCastle:\r
+ case BlackQueenSideCastle:\r
+ case WhiteKingSideCastleWild:\r
+ case BlackKingSideCastleWild:\r
+ /* PUSH Fabien */\r
+ case WhiteASideCastleFR:\r
+ case BlackASideCastleFR:\r
+ /* POP Fabien */\r
+ sprintf(user_move, "o-o-o\n");\r
+ break;\r
+ case WhitePromotionQueen:\r
+ case BlackPromotionQueen:\r
+ case WhitePromotionRook:\r
+ case BlackPromotionRook:\r
+ case WhitePromotionBishop:\r
+ case BlackPromotionBishop:\r
+ case WhitePromotionKnight:\r
+ case BlackPromotionKnight:\r
+ case WhitePromotionKing:\r
+ case BlackPromotionKing:\r
+#ifdef FAIRY\r
+ case WhitePromotionChancellor:\r
+ case BlackPromotionChancellor:\r
+ case WhitePromotionArchbishop:\r
+ case BlackPromotionArchbishop:\r
+#endif\r
+ sprintf(user_move, "%c%c%c%c=%c\n",\r
+ 'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY,\r
+ PieceToChar(PromoPiece(moveType)));\r
+ break;\r
+ case WhiteDrop:\r
+ case BlackDrop:\r
+ sprintf(user_move, "%c@%c%c\n",\r
+ ToUpper(PieceToChar((ChessSquare) fromX)),\r
+ 'a' + toX, ONE + toY);\r
+ break;\r
+ case NormalMove:\r
+ case WhiteCapturesEnPassant:\r
+ case BlackCapturesEnPassant:\r
+ case IllegalMove: /* could be a variant we don't quite understand */\r
+ sprintf(user_move, "%c%c%c%c\n",\r
+ 'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY);\r
+ break;\r
+ }\r
+ SendToICS(user_move);\r
+}\r
+\r
+void\r
+CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)\r
+ int rf, ff, rt, ft;\r
+ char promoChar;\r
+ char move[7];\r
+{\r
+ if (rf == DROP_RANK) {\r
+ sprintf(move, "%c@%c%c\n",\r
+ ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, ONE + rt);\r
+ } else {\r
+ if (promoChar == 'x' || promoChar == NULLCHAR) {\r
+ sprintf(move, "%c%c%c%c\n",\r
+ 'a' + ff, ONE + rf, 'a' + ft, ONE + rt);\r
+ } else {\r
+ sprintf(move, "%c%c%c%c%c\n",\r
+ 'a' + ff, ONE + rf, 'a' + ft, ONE + rt, promoChar);\r
+ }\r
+ }\r
+}\r
+\r
+void\r
+ProcessICSInitScript(f)\r
+ FILE *f;\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ while (fgets(buf, MSG_SIZ, f)) {\r
+ SendToICSDelayed(buf,(long)appData.msLoginDelay);\r
+ }\r
+\r
+ fclose(f);\r
+}\r
+\r
+\r
+/* Parser for moves from gnuchess, ICS, or user typein box */\r
+Boolean\r
+ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)\r
+ char *move;\r
+ int moveNum;\r
+ ChessMove *moveType;\r
+ int *fromX, *fromY, *toX, *toY;\r
+ char *promoChar;\r
+{ \r
+ *moveType = yylexstr(moveNum, move);\r
+\r
+ switch (*moveType) {\r
+#ifdef FAIRY\r
+ case WhitePromotionChancellor:\r
+ case BlackPromotionChancellor:\r
+ case WhitePromotionArchbishop:\r
+ case BlackPromotionArchbishop:\r
+#endif\r
+ case WhitePromotionQueen:\r
+ case BlackPromotionQueen:\r
+ case WhitePromotionRook:\r
+ case BlackPromotionRook:\r
+ case WhitePromotionBishop:\r
+ case BlackPromotionBishop:\r
+ case WhitePromotionKnight:\r
+ case BlackPromotionKnight:\r
+ case WhitePromotionKing:\r
+ case BlackPromotionKing:\r
+ case NormalMove:\r
+ case WhiteCapturesEnPassant:\r
+ case BlackCapturesEnPassant:\r
+ case WhiteKingSideCastle:\r
+ case WhiteQueenSideCastle:\r
+ case BlackKingSideCastle:\r
+ case BlackQueenSideCastle:\r
+ case WhiteKingSideCastleWild:\r
+ case WhiteQueenSideCastleWild:\r
+ case BlackKingSideCastleWild:\r
+ case BlackQueenSideCastleWild:\r
+ /* Code added by Tord: */\r
+ case WhiteHSideCastleFR:\r
+ case WhiteASideCastleFR:\r
+ case BlackHSideCastleFR:\r
+ case BlackASideCastleFR:\r
+ /* End of code added by Tord */\r
+ case IllegalMove: /* bug or odd chess variant */\r
+ *fromX = currentMoveString[0] - 'a';\r
+ *fromY = currentMoveString[1] - ONE;\r
+ *toX = currentMoveString[2] - 'a';\r
+ *toY = currentMoveString[3] - ONE;\r
+ *promoChar = currentMoveString[4];\r
+ if (*fromX < 0 || *fromX >= BOARD_WIDTH || *fromY < 0 || *fromY >= BOARD_HEIGHT ||\r
+ *toX < 0 || *toX >= BOARD_WIDTH || *toY < 0 || *toY >= BOARD_HEIGHT) {\r
+ *fromX = *fromY = *toX = *toY = 0;\r
+ return FALSE;\r
+ }\r
+ if (appData.testLegality) {\r
+ return (*moveType != IllegalMove);\r
+ } else {\r
+ return !(fromX == fromY && toX == toY);\r
+ }\r
+\r
+ case WhiteDrop:\r
+ case BlackDrop:\r
+ *fromX = *moveType == WhiteDrop ?\r
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+ (int) CharToPiece(ToLower(currentMoveString[0]));\r
+ *fromY = DROP_RANK;\r
+ *toX = currentMoveString[2] - 'a';\r
+ *toY = currentMoveString[3] - ONE;\r
+ *promoChar = NULLCHAR;\r
+ return TRUE;\r
+\r
+ case AmbiguousMove:\r
+ case ImpossibleMove:\r
+ case (ChessMove) 0: /* end of file */\r
+ case ElapsedTime:\r
+ case Comment:\r
+ case PGNTag:\r
+ case NAG:\r
+ case WhiteWins:\r
+ case BlackWins:\r
+ case GameIsDrawn:\r
+ default:\r
+ /* bug? */\r
+ *fromX = *fromY = *toX = *toY = 0;\r
+ *promoChar = NULLCHAR;\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+/* [AS] FRC game initialization */\r
+static int FindEmptySquare( Board board, int n )\r
+{\r
+ int i = 0;\r
+\r
+ while( 1 ) {\r
+ while( board[0][i] != EmptySquare ) i++;\r
+ if( n == 0 )\r
+ break;\r
+ n--;\r
+ i++;\r
+ }\r
+\r
+ return i;\r
+}\r
+\r
+static void ShuffleFRC( Board board )\r
+{\r
+ int i;\r
+\r
+ srand( time(0) );\r
+ \r
+ for( i=0; i<8; i++ ) {\r
+ board[0][i] = EmptySquare;\r
+ }\r
+\r
+ board[0][(rand() % 4)*2 ] = WhiteBishop; /* On dark square */\r
+ board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */\r
+ board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;\r
+ board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;\r
+ board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;\r
+ board[0][FindEmptySquare(board, 0)] = WhiteRook;\r
+ board[0][FindEmptySquare(board, 0)] = WhiteKing;\r
+ board[0][FindEmptySquare(board, 0)] = WhiteRook;\r
+\r
+ for( i=0; i<BOARD_WIDTH; i++ ) {\r
+ board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
+ }\r
+}\r
+\r
+static unsigned char FRC_KnightTable[10] = {\r
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33\r
+};\r
+\r
+static void SetupFRC( Board board, int pos_index )\r
+{\r
+ int i;\r
+ unsigned char knights;\r
+\r
+ /* Bring the position index into a safe range (just in case...) */\r
+ if( pos_index < 0 ) pos_index = 0;\r
+\r
+ pos_index %= 960;\r
+\r
+ /* Clear the board */\r
+ for( i=0; i<8; i++ ) {\r
+ board[0][i] = EmptySquare;\r
+ }\r
+\r
+ /* Place bishops and queen */\r
+ board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */\r
+ pos_index /= 4;\r
+ \r
+ board[0][ (pos_index % 4)*2 ] = WhiteBishop; /* On dark square */\r
+ pos_index /= 4;\r
+\r
+ board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;\r
+ pos_index /= 6;\r
+\r
+ /* Place knigths */\r
+ knights = FRC_KnightTable[ pos_index ];\r
+\r
+ board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;\r
+ board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
+\r
+ /* Place rooks and king */\r
+ board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\r
+ board[0][ FindEmptySquare(board, 0) ] = WhiteKing;\r
+ board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\r
+\r
+ /* Mirror piece placement for black */\r
+ for( i=0; i<BOARD_WIDTH; i++ ) {\r
+ board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
+ }\r
+}\r
+\r
+void\r
+InitPosition(redraw)\r
+ int redraw;\r
+{\r
+ ChessSquare (* pieces)[BOARD_SIZE];\r
+ int i, j;\r
+\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+\r
+ /* [AS] Initialize pv info list [HGM] and game status */\r
+ {\r
+ for( i=0; i<MAX_MOVES; i++ ) {\r
+ pvInfoList[i].depth = 0;\r
+ epStatus[i]=EP_NONE;\r
+ for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
+ }\r
+\r
+ /* [HGM] Build normal castling rights */\r
+ for( j=0; j<BOARD_SIZE; j++ ) initialRights[j] = -1;\r
+ nrCastlingRights = 6;\r
+ castlingRights[0][0] = initialRights[0] = BOARD_WIDTH-1;\r
+ castlingRights[0][1] = initialRights[1] = 0;\r
+ castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;\r
+ castlingRights[0][3] = initialRights[3] = BOARD_WIDTH-1;\r
+ castlingRights[0][4] = initialRights[4] = 0;\r
+ castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
+\r
+ castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
+ castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
+\r
+ initialRulePlies = 0; /* 50-move counter start */\r
+ }\r
+\r
+ \r
+ /* [HGM] logic here is completely changed. In stead of full positions */\r
+ /* the initialized data only consist of the two backranks. The switch */\r
+ /* selects which one we will use, which is than copied to the Board */\r
+ /* initialPosition, which for the rest is initialized by Pawns and */\r
+ /* empty squares. This initial position is then copied to boards[0], */\r
+ /* possibly after shuffling, so that it remains available. */\r
+\r
+ switch (gameInfo.variant) {\r
+ default:\r
+ pieces = BOARD_WIDTH <= 8 ? FIDEArray :\r
+ BOARD_WIDTH <= 10 ? CapablancaArray : CourierArray;\r
+ break;\r
+ case VariantShatranj:\r
+ pieces = ShatranjArray;\r
+ CharToPiece('E'); /* associate PGN/FEN letter with internal piece type */\r
+ CharToPiece('e');\r
+ nrCastlingRights = 0;\r
+ for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+ break;\r
+ case VariantTwoKings:\r
+ pieces = twoKingsArray;\r
+ nrCastlingRights = 8; /* add rights for second King */\r
+ castlingRights[0][6] = initialRights[2] = 5;\r
+ castlingRights[0][7] = initialRights[5] = 5;\r
+ castlingRank[6] = 0;\r
+ castlingRank[6] = BOARD_HEIGHT-1;\r
+ startedFromSetupPosition = TRUE;\r
+ break;\r
+#ifdef FAIRY\r
+ case VariantCapablanca:\r
+ pieces = CapablancaArray;\r
+ break;\r
+ case VariantGothic:\r
+ pieces = GothicArray;\r
+ break;\r
+ case VariantXiangqi:\r
+ pieces = XiangqiArray;\r
+ break;\r
+ case VariantCourier:\r
+ pieces = CourierArray;\r
+ nrCastlingRights = 0;\r
+ for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+ break;\r
+ case VariantFairy:\r
+ pieces = fairyArray;\r
+ startedFromSetupPosition = TRUE;\r
+ break;\r
+#endif\r
+ case VariantWildCastle:\r
+ pieces = FIDEArray;\r
+ /* !!?shuffle with kings guaranteed to be on d or e file */\r
+ break;\r
+ case VariantNoCastle:\r
+ pieces = FIDEArray;\r
+ nrCastlingRights = 0;\r
+ for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+ /* !!?unconstrained back-rank shuffle */\r
+ break;\r
+ }\r
+\r
+ for( j=0; j<BOARD_WIDTH; j++ ) {\r
+ for( i=0; i<BOARD_HEIGHT; i++ )\r
+ initialPosition[i][j] = EmptySquare;\r
+ initialPosition[0][j] = pieces[0][j];\r
+ if(gameInfo.variant == VariantXiangqi) {\r
+ if(j&1) {\r
+ initialPosition[3][j] = \r
+ initialPosition[BOARD_HEIGHT-4][j] = EmptySquare;\r
+ if(j==1 || j>=BOARD_WIDTH-2) {\r
+ initialPosition[2][j] = WhiteFairyMarshall;\r
+ initialPosition[BOARD_HEIGHT-3][j] = BlackFairyMarshall;\r
+ }\r
+ } else {\r
+ initialPosition[3][j] = WhitePawn;\r
+ initialPosition[BOARD_HEIGHT-4][j] = BlackPawn;\r
+ }\r
+ } else {\r
+ initialPosition[1][j] = WhitePawn;\r
+ initialPosition[BOARD_HEIGHT-2][j] = BlackPawn;\r
+ }\r
+ initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j];\r
+ }\r
+\r
+ if(gameInfo.variant == VariantFischeRandom) {\r
+ if( appData.defaultFrcPosition < 0 ) {\r
+ ShuffleFRC( initialPosition );\r
+ }\r
+ else {\r
+ SetupFRC( initialPosition, appData.defaultFrcPosition );\r
+ }\r
+ }\r
+\r
+ CopyBoard(boards[0], initialPosition);\r
+\r
+ if (redraw)\r
+ DrawPosition(TRUE, boards[currentMove]);\r
+}\r
+\r
+void\r
+SendBoard(cps, moveNum)\r
+ ChessProgramState *cps;\r
+ int moveNum;\r
+{\r
+ char message[MSG_SIZ];\r
+ \r
+ if (cps->useSetboard) {\r
+ char* fen = PositionToFEN(moveNum, cps->useFEN960);\r
+ sprintf(message, "setboard %s\n", fen);\r
+ SendToProgram(message, cps);\r
+ free(fen);\r
+\r
+ } else {\r
+ ChessSquare *bp;\r
+ int i, j;\r
+ /* Kludge to set black to move, avoiding the troublesome and now\r
+ * deprecated "black" command.\r
+ */\r
+ if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);\r
+\r
+ SendToProgram("edit\n", cps);\r
+ SendToProgram("#\n", cps);\r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ bp = &boards[moveNum][i][0];\r
+ for (j = 0; j < BOARD_WIDTH; j++, bp++) {\r
+ if ((int) *bp < (int) BlackPawn) {\r
+ sprintf(message, "%c%c%c\n", PieceToChar(*bp), \r
+ 'a' + j, ONE + i);\r
+ SendToProgram(message, cps);\r
+ }\r
+ }\r
+ }\r
+ \r
+ SendToProgram("c\n", cps);\r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ bp = &boards[moveNum][i][0];\r
+ for (j = 0; j < BOARD_WIDTH; j++, bp++) {\r
+ if (((int) *bp != (int) EmptySquare)\r
+ && ((int) *bp >= (int) BlackPawn)) {\r
+ sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),\r
+ 'a' + j, ONE + i);\r
+ SendToProgram(message, cps);\r
+ }\r
+ }\r
+ }\r
+ \r
+ SendToProgram(".\n", cps);\r
+ }\r
+}\r
+\r
+int\r
+IsPromotion(fromX, fromY, toX, toY)\r
+ int fromX, fromY, toX, toY;\r
+{\r
+ return gameMode != EditPosition && gameInfo.variant != VariantXiangqi &&\r
+ fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&\r
+ ((boards[currentMove][fromY][fromX] == WhitePawn && toY == BOARD_HEIGHT-1) ||\r
+ (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));\r
+}\r
+\r
+\r
+int\r
+PieceForSquare (x, y)\r
+ int x;\r
+ int y;\r
+{\r
+ if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)\r
+ return -1;\r
+ else\r
+ return boards[currentMove][y][x];\r
+}\r
+\r
+int\r
+OKToStartUserMove(x, y)\r
+ int x, y;\r
+{\r
+ ChessSquare from_piece;\r
+ int white_piece;\r
+\r
+ if (matchMode) return FALSE;\r
+ if (gameMode == EditPosition) return TRUE;\r
+\r
+ if (x >= 0 && y >= 0)\r
+ from_piece = boards[currentMove][y][x];\r
+ else\r
+ from_piece = EmptySquare;\r
+\r
+ if (from_piece == EmptySquare) return FALSE;\r
+\r
+ white_piece = (int)from_piece >= (int)WhitePawn &&\r
+ (int)from_piece < (int)BlackPawn; /* [HGM] can be > King! */\r
+\r
+ switch (gameMode) {\r
+ case PlayFromGameFile:\r
+ case AnalyzeFile:\r
+ case TwoMachinesPlay:\r
+ case EndOfGame:\r
+ return FALSE;\r
+\r
+ case IcsObserving:\r
+ case IcsIdle:\r
+ return FALSE;\r
+\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingBlack:\r
+ if (appData.zippyPlay) return FALSE;\r
+ if (white_piece) {\r
+ DisplayMoveError("You are playing Black");\r
+ return FALSE;\r
+ }\r
+ break;\r
+\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingWhite:\r
+ if (appData.zippyPlay) return FALSE;\r
+ if (!white_piece) {\r
+ DisplayMoveError("You are playing White");\r
+ return FALSE;\r
+ }\r
+ break;\r
+\r
+ case EditGame:\r
+ if (!white_piece && WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is White's turn");\r
+ return FALSE;\r
+ } \r
+ if (white_piece && !WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is Black's turn");\r
+ return FALSE;\r
+ } \r
+ if (cmailMsgLoaded && (currentMove < cmailOldMove)) {\r
+ /* Editing correspondence game history */\r
+ /* Could disallow this or prompt for confirmation */\r
+ cmailOldMove = -1;\r
+ }\r
+ if (currentMove < forwardMostMove) {\r
+ /* Discarding moves */\r
+ /* Could prompt for confirmation here,\r
+ but I don't think that's such a good idea */\r
+ forwardMostMove = currentMove;\r
+ }\r
+ break;\r
+\r
+ case BeginningOfGame:\r
+ if (appData.icsActive) return FALSE;\r
+ if (!appData.noChessProgram) {\r
+ if (!white_piece) {\r
+ DisplayMoveError("You are playing White");\r
+ return FALSE;\r
+ }\r
+ }\r
+ break;\r
+ \r
+ case Training:\r
+ if (!white_piece && WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is White's turn");\r
+ return FALSE;\r
+ } \r
+ if (white_piece && !WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is Black's turn");\r
+ return FALSE;\r
+ } \r
+ break;\r
+\r
+ default:\r
+ case IcsExamining:\r
+ break;\r
+ }\r
+ if (currentMove != forwardMostMove && gameMode != AnalyzeMode\r
+ && gameMode != AnalyzeFile && gameMode != Training) {\r
+ DisplayMoveError("Displayed position is not current");\r
+ return FALSE;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;\r
+int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;\r
+int lastLoadGameUseList = FALSE;\r
+char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];\r
+ChessMove lastLoadGameStart = (ChessMove) 0;\r
+\r
+\r
+void\r
+UserMoveEvent(fromX, fromY, toX, toY, promoChar)\r
+ int fromX, fromY, toX, toY;\r
+ int promoChar;\r
+{\r
+ ChessMove moveType;\r
+\r
+ if (fromX < 0 || fromY < 0) return;\r
+ if ((fromX == toX) && (fromY == toY)) {\r
+ return;\r
+ }\r
+ \r
+ /* Check if the user is playing in turn. This is complicated because we\r
+ let the user "pick up" a piece before it is his turn. So the piece he\r
+ tried to pick up may have been captured by the time he puts it down!\r
+ Therefore we use the color the user is supposed to be playing in this\r
+ test, not the color of the piece that is currently on the starting\r
+ square---except in EditGame mode, where the user is playing both\r
+ sides; fortunately there the capture race can't happen. (It can\r
+ now happen in IcsExamining mode, but that's just too bad. The user\r
+ will get a somewhat confusing message in that case.)\r
+ */\r
+\r
+ switch (gameMode) {\r
+ case PlayFromGameFile:\r
+ case AnalyzeFile:\r
+ case TwoMachinesPlay:\r
+ case EndOfGame:\r
+ case IcsObserving:\r
+ case IcsIdle:\r
+ /* We switched into a game mode where moves are not accepted,\r
+ perhaps while the mouse button was down. */\r
+ return;\r
+\r
+ case MachinePlaysWhite:\r
+ /* User is moving for Black */\r
+ if (WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is White's turn");\r
+ return;\r
+ }\r
+ break;\r
+\r
+ case MachinePlaysBlack:\r
+ /* User is moving for White */\r
+ if (!WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is Black's turn");\r
+ return;\r
+ }\r
+ break;\r
+\r
+ case EditGame:\r
+ case IcsExamining:\r
+ case BeginningOfGame:\r
+ case AnalyzeMode:\r
+ case Training:\r
+ if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&\r
+ (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {\r
+ /* User is moving for Black */\r
+ if (WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is White's turn");\r
+ return;\r
+ }\r
+ } else {\r
+ /* User is moving for White */\r
+ if (!WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is Black's turn");\r
+ return;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case IcsPlayingBlack:\r
+ /* User is moving for Black */\r
+ if (WhiteOnMove(currentMove)) {\r
+ if (!appData.premove) {\r
+ DisplayMoveError("It is White's turn");\r
+ } else if (toX >= 0 && toY >= 0) {\r
+ premoveToX = toX;\r
+ premoveToY = toY;\r
+ premoveFromX = fromX;\r
+ premoveFromY = fromY;\r
+ premovePromoChar = promoChar;\r
+ gotPremove = 1;\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Got premove: fromX %d,"\r
+ "fromY %d, toX %d, toY %d\n",\r
+ fromX, fromY, toX, toY);\r
+ }\r
+ return;\r
+ }\r
+ break;\r
+\r
+ case IcsPlayingWhite:\r
+ /* User is moving for White */\r
+ if (!WhiteOnMove(currentMove)) {\r
+ if (!appData.premove) {\r
+ DisplayMoveError("It is Black's turn");\r
+ } else if (toX >= 0 && toY >= 0) {\r
+ premoveToX = toX;\r
+ premoveToY = toY;\r
+ premoveFromX = fromX;\r
+ premoveFromY = fromY;\r
+ premovePromoChar = promoChar;\r
+ gotPremove = 1;\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Got premove: fromX %d,"\r
+ "fromY %d, toX %d, toY %d\n",\r
+ fromX, fromY, toX, toY);\r
+ }\r
+ return;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+\r
+ case EditPosition:\r
+ if (toX == -2 || toY == -2) {\r
+ boards[0][fromY][fromX] = EmptySquare;\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ } else if (toX >= 0 && toY >= 0) {\r
+ boards[0][toY][toX] = boards[0][fromY][fromX];\r
+ boards[0][fromY][fromX] = EmptySquare;\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ }\r
+ return;\r
+ }\r
+\r
+ if (toX < 0 || toY < 0) return;\r
+ userOfferedDraw = FALSE;\r
+ \r
+ if (appData.testLegality) {\r
+ moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
+ EP_UNKNOWN, castlingRights[currentMove],\r
+ fromY, fromX, toY, toX, promoChar);\r
+ if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
+ DisplayMoveError("Illegal move");\r
+ return;\r
+ }\r
+ } else {\r
+ moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
+ }\r
+\r
+ if (gameMode == Training) {\r
+ /* compare the move played on the board to the next move in the\r
+ * game. If they match, display the move and the opponent's response. \r
+ * If they don't match, display an error message.\r
+ */\r
+ int saveAnimate;\r
+ Board testBoard;\r
+ CopyBoard(testBoard, boards[currentMove]);\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+\r
+ if (CompareBoards(testBoard, boards[currentMove+1])) {\r
+ ForwardInner(currentMove+1);\r
+\r
+ /* Autoplay the opponent's response.\r
+ * if appData.animate was TRUE when Training mode was entered,\r
+ * the response will be animated.\r
+ */\r
+ saveAnimate = appData.animate;\r
+ appData.animate = animateTraining;\r
+ ForwardInner(currentMove+1);\r
+ appData.animate = saveAnimate;\r
+\r
+ /* check for the end of the game */\r
+ if (currentMove >= forwardMostMove) {\r
+ gameMode = PlayFromGameFile;\r
+ ModeHighlight();\r
+ SetTrainingModeOff();\r
+ DisplayInformation("End of game");\r
+ }\r
+ } else {\r
+ DisplayError("Incorrect move", 0);\r
+ }\r
+ return;\r
+ }\r
+\r
+ FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
+}\r
+\r
+/* Common tail of UserMoveEvent and DropMenuEvent */\r
+void\r
+FinishMove(moveType, fromX, fromY, toX, toY, promoChar)\r
+ ChessMove moveType;\r
+ int fromX, fromY, toX, toY;\r
+ /*char*/int promoChar;\r
+{\r
+ /* Ok, now we know that the move is good, so we can kill\r
+ the previous line in Analysis Mode */\r
+ if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {\r
+ forwardMostMove = currentMove;\r
+ }\r
+\r
+ /* If we need the chess program but it's dead, restart it */\r
+ ResurrectChessProgram();\r
+\r
+ /* A user move restarts a paused game*/\r
+ if (pausing)\r
+ PauseEvent();\r
+\r
+ thinkOutput[0] = NULLCHAR;\r
+\r
+ MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
+\r
+ if (gameMode == BeginningOfGame) {\r
+ if (appData.noChessProgram) {\r
+ gameMode = EditGame;\r
+ SetGameInfo();\r
+ } else {\r
+ char buf[MSG_SIZ];\r
+ gameMode = MachinePlaysBlack;\r
+ SetGameInfo();\r
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+ DisplayTitle(buf);\r
+ if (first.sendName) {\r
+ sprintf(buf, "name %s\n", gameInfo.white);\r
+ SendToProgram(buf, &first);\r
+ }\r
+ }\r
+ ModeHighlight();\r
+ }\r
+\r
+ /* Relay move to ICS or chess engine */\r
+ if (appData.icsActive) {\r
+ if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
+ gameMode == IcsExamining) {\r
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
+ ics_user_moved = 1;\r
+ }\r
+ } else {\r
+ if (first.sendTime && (gameMode == BeginningOfGame ||\r
+ gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack)) {\r
+ SendTimeRemaining(&first, gameMode != MachinePlaysBlack);\r
+ }\r
+ SendMoveToProgram(forwardMostMove-1, &first);\r
+ if (gameMode != EditGame && gameMode != PlayFromGameFile) {\r
+ first.maybeThinking = TRUE;\r
+ }\r
+ if (currentMove == cmailOldMove + 1) {\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+ }\r
+ }\r
+\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+ switch (gameMode) {\r
+ case EditGame:\r
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+ EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+ case MT_NONE:\r
+ case MT_CHECK:\r
+ break;\r
+ case MT_CHECKMATE:\r
+ if (WhiteOnMove(currentMove)) {\r
+ GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
+ } else {\r
+ GameEnds(WhiteWins, "White mates", GE_PLAYER);\r
+ }\r
+ break;\r
+ case MT_STALEMATE:\r
+ GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);\r
+ break;\r
+ }\r
+ break;\r
+ \r
+ case MachinePlaysBlack:\r
+ case MachinePlaysWhite:\r
+ /* disable certain menu options while machine is thinking */\r
+ SetMachineThinkingEnables();\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )\r
+{\r
+ char * hint = lastHint;\r
+ FrontEndProgramStats stats;\r
+\r
+ stats.which = cps == &first ? 0 : 1;\r
+ stats.depth = cpstats->depth;\r
+ stats.nodes = cpstats->nodes;\r
+ stats.score = cpstats->score;\r
+ stats.time = cpstats->time;\r
+ stats.pv = cpstats->movelist;\r
+ stats.hint = lastHint;\r
+ stats.an_move_index = 0;\r
+ stats.an_move_count = 0;\r
+\r
+ if( gameMode == AnalyzeMode || gameMode == AnalyzeFile ) {\r
+ stats.hint = cpstats->move_name;\r
+ stats.an_move_index = cpstats->nr_moves - cpstats->moves_left;\r
+ stats.an_move_count = cpstats->nr_moves;\r
+ }\r
+\r
+ SetProgramStats( &stats );\r
+}\r
+\r
+void\r
+HandleMachineMove(message, cps)\r
+ char *message;\r
+ ChessProgramState *cps;\r
+{\r
+ char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];\r
+ char realname[MSG_SIZ];\r
+ int fromX, fromY, toX, toY;\r
+ ChessMove moveType;\r
+ char promoChar;\r
+ char *p;\r
+ int machineWhite;\r
+\r
+ /*\r
+ * Kludge to ignore BEL characters\r
+ */\r
+ while (*message == '\007') message++;\r
+\r
+ /*\r
+ * Look for book output\r
+ */\r
+ if (cps == &first && bookRequested) {\r
+ if (message[0] == '\t' || message[0] == ' ') {\r
+ /* Part of the book output is here; append it */\r
+ strcat(bookOutput, message);\r
+ strcat(bookOutput, " \n");\r
+ return;\r
+ } else if (bookOutput[0] != NULLCHAR) {\r
+ /* All of book output has arrived; display it */\r
+ char *p = bookOutput;\r
+ while (*p != NULLCHAR) {\r
+ if (*p == '\t') *p = ' ';\r
+ p++;\r
+ }\r
+ DisplayInformation(bookOutput);\r
+ bookRequested = FALSE;\r
+ /* Fall through to parse the current output */\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Look for machine move.\r
+ */\r
+ if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||\r
+ (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0)) \r
+ {\r
+ /* This method is only useful on engines that support ping */\r
+ if (cps->lastPing != cps->lastPong) {\r
+ if (gameMode == BeginningOfGame) {\r
+ /* Extra move from before last new; ignore */\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);\r
+ }\r
+ } else {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",\r
+ cps->which, gameMode);\r
+ }\r
+\r
+ SendToProgram("undo\n", cps);\r
+ }\r
+ return;\r
+ }\r
+\r
+ switch (gameMode) {\r
+ case BeginningOfGame:\r
+ /* Extra move from before last reset; ignore */\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);\r
+ }\r
+ return;\r
+\r
+ case EndOfGame:\r
+ case IcsIdle:\r
+ default:\r
+ /* Extra move after we tried to stop. The mode test is\r
+ not a reliable way of detecting this problem, but it's\r
+ the best we can do on engines that don't support ping.\r
+ */\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",\r
+ cps->which, gameMode);\r
+ }\r
+ SendToProgram("undo\n", cps);\r
+ return;\r
+\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingWhite:\r
+ machineWhite = TRUE;\r
+ break;\r
+\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingBlack:\r
+ machineWhite = FALSE;\r
+ break;\r
+\r
+ case TwoMachinesPlay:\r
+ machineWhite = (cps->twoMachinesColor[0] == 'w');\r
+ break;\r
+ }\r
+ if (WhiteOnMove(forwardMostMove) != machineWhite) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP,\r
+ "Ignoring move out of turn by %s, gameMode %d"\r
+ ", forwardMost %d\n",\r
+ cps->which, gameMode, forwardMostMove);\r
+ }\r
+ return;\r
+ }\r
+\r
+ if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
+ &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+ /* Machine move could not be parsed; ignore it. */\r
+ sprintf(buf1, "Illegal move \"%s\" from %s machine",\r
+ machineMove, cps->which);\r
+ DisplayError(buf1, 0);\r
+ if (gameMode == TwoMachinesPlay) {\r
+ GameEnds(machineWhite ? BlackWins : WhiteWins,\r
+ "Forfeit due to illegal move", GE_XBOARD);\r
+ }\r
+ return;\r
+ }\r
+\r
+ /* [HGM] Apparently legal, but so far only tested with EP_UNKOWN */\r
+ /* So we have to redo legality test with true e.p. status here, */\r
+ /* to make sure an illegal e.p. capture does not slip through, */\r
+ /* to cause a forfeit on a justified illegal-move complaint */\r
+ /* of the opponent. */\r
+ if(gameMode==TwoMachinesPlay && appData.testLegality &&\r
+ LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+ epStatus[forwardMostMove], castlingRights[forwardMostMove],\r
+ fromY, fromX, toY, toX, promoChar) == IllegalMove)\r
+ { static char buf[MSG_SIZ];\r
+ if (appData.debugMode) {\r
+ int i;\r
+ for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",\r
+ castlingRights[forwardMostMove][i], castlingRank[i]);\r
+ fprintf(debugFP, "castling rights\n");\r
+ }\r
+ sprintf(buf, "Xboard: Forfeit due to illegal move %s (%c%c%c%c)%c",\r
+ machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0);\r
+ GameEnds(machineWhite ? BlackWins : WhiteWins, buf, GE_XBOARD);\r
+ }\r
+ hintRequested = FALSE;\r
+ lastHint[0] = NULLCHAR;\r
+ bookRequested = FALSE;\r
+ /* Program may be pondering now */\r
+ cps->maybeThinking = TRUE;\r
+ if (cps->sendTime == 2) cps->sendTime = 1;\r
+ if (cps->offeredDraw) cps->offeredDraw--;\r
+\r
+#if ZIPPY\r
+ if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&\r
+ first.initDone) {\r
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
+ ics_user_moved = 1;\r
+ }\r
+#endif\r
+ /* currentMoveString is set as a side-effect of ParseOneMove */\r
+ strcpy(machineMove, currentMoveString);\r
+ strcat(machineMove, "\n");\r
+ strcpy(moveList[forwardMostMove], machineMove);\r
+\r
+ /* [AS] Save move info and clear stats for next move */\r
+ pvInfoList[ forwardMostMove ].score = programStats.score;\r
+ pvInfoList[ forwardMostMove ].depth = programStats.depth;\r
+ pvInfoList[ forwardMostMove ].time = -1;\r
+ ClearProgramStats();\r
+ thinkOutput[0] = NULLCHAR;\r
+ hiddenThinkOutputState = 0;\r
+\r
+ MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/\r
+\r
+ /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */\r
+ if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {\r
+ int count = 0;\r
+\r
+ while( count < adjudicateLossPlies ) {\r
+ int score = pvInfoList[ forwardMostMove - count - 1 ].score;\r
+\r
+ if( count & 1 ) {\r
+ score = -score; /* Flip score for winning side */\r
+ }\r
+\r
+ if( score > adjudicateLossThreshold ) {\r
+ break;\r
+ }\r
+\r
+ count++;\r
+ }\r
+\r
+ if( count >= adjudicateLossPlies ) {\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+ "Xboard adjudication", \r
+ GE_XBOARD );\r
+\r
+ return;\r
+ }\r
+ }\r
+\r
+#ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines\r
+\r
+ if( gameMode == TwoMachinesPlay ) {\r
+ int count = 0, epFile = epStatus[forwardMostMove];\r
+\r
+ if(appData.testLegality) // don't wait for engine to announce game end if we can judge ourselves\r
+ switch (MateTest(boards[forwardMostMove],\r
+ PosFlags(forwardMostMove), epFile,\r
+ castlingRights[forwardMostMove]) ) {\r
+ case MT_NONE:\r
+ case MT_CHECK:\r
+ default:\r
+ break;\r
+ case MT_STALEMATE:\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
+ GE_XBOARD );\r
+ break;\r
+ case MT_CHECKMATE:\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+ "Xboard adjudication: Checkmate", \r
+ GE_XBOARD );\r
+ break;\r
+ }\r
+\r
+ if( appData.testLegality )\r
+ { /* [HGM] Some more adjudications for obstinate engines */\r
+ int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
+ NrWQ=0, NrBQ=0,\r
+ NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
+ static int moveCount;\r
+\r
+ /* First absolutely insufficient mating material. Count what is on board. */\r
+ for(i=0; i<BOARD_HEIGHT; i++) for(j=0; j<BOARD_WIDTH; j++)\r
+ { ChessSquare p = boards[forwardMostMove][i][j];\r
+ int m=i;\r
+\r
+ switch((int) p)\r
+ { /* count B,N,R and other of each side */\r
+ case WhiteKnight:\r
+ NrWN++; break;\r
+ case WhiteBishop:\r
+ NrWB++; break;\r
+ case BlackKnight:\r
+ NrWN++; break;\r
+ case BlackBishop:\r
+ NrBB++; break;\r
+ case WhiteRook:\r
+ NrWR++; break;\r
+ case BlackRook:\r
+ NrBR++; break;\r
+ case WhiteQueen:\r
+ NrWR++; break;\r
+ case BlackQueen:\r
+ NrBR++; break;\r
+ case EmptySquare: \r
+ break;\r
+ case BlackPawn:\r
+ m = 7-i;\r
+ case WhitePawn:\r
+ PawnAdvance += m; NrPawns++;\r
+ }\r
+ NrPieces += (p != EmptySquare);\r
+ }\r
+\r
+ if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2 )\r
+ { /* KBK, KNK or KK */\r
+\r
+ /* always flag draws, for judging claims */\r
+ epStatus[forwardMostMove] = EP_INSUF_DRAW;\r
+\r
+ if(adjudicateLossThreshold != 0) {\r
+ /* but only adjudicate them if adjudication enabled */\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
+ return;\r
+ }\r
+ }\r
+\r
+ /* Then some trivial draws (only adjudicate, cannot be claimed) */\r
+ if(NrPieces == 4 && \r
+ ( NrWR == 1 && NrBR == 1 /* KRKR */\r
+ || NrWQ==1 && NrBQ==1 /* KQKQ */\r
+ || NrWN==2 || NrBN==2 /* KNNK */\r
+ || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */\r
+ ) ) {\r
+ if(--moveCount < 0 && adjudicateLossThreshold != 0)\r
+ { /* if the first 3 moves do not show a tactical win, declare draw */\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );\r
+ return;\r
+ }\r
+ } else moveCount = 6;\r
+\r
+ if (appData.debugMode) { int i;\r
+ fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",\r
+ forwardMostMove, backwardMostMove, epStatus[backwardMostMove],\r
+ appData.drawRepeats);\r
+ for( i=forwardMostMove; i>=backwardMostMove; i-- )\r
+ fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);\r
+\r
+ }\r
+ /* Check for rep-draws */\r
+ count = 0;\r
+ for(k = forwardMostMove-2;\r
+ k>=backwardMostMove && k>=forwardMostMove-100 &&\r
+ epStatus[k] <= EP_NONE && epStatus[k+1] <= EP_NONE;\r
+ k-=2)\r
+ { int rights=0;\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, " loop\n");\r
+ }\r
+ if(CompareBoards(boards[k], boards[forwardMostMove])) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "match\n");\r
+ }\r
+ /* compare castling rights */\r
+ if( castlingRights[forwardMostMove][2] != castlingRights[k][2] &&\r
+ (castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) )\r
+ rights++; /* King lost rights, while rook still had them */\r
+ if( castlingRights[forwardMostMove][2] >= 0 ) { /* king has rights */\r
+ if( castlingRights[forwardMostMove][0] != castlingRights[k][0] ||\r
+ castlingRights[forwardMostMove][1] != castlingRights[k][1] )\r
+ rights++; /* but at least one rook lost them */\r
+ }\r
+ if( castlingRights[forwardMostMove][5] != castlingRights[k][5] &&\r
+ (castlingRights[k][3] >= 0 || castlingRights[k][4] >= 0) )\r
+ rights++; \r
+ if( castlingRights[forwardMostMove][5] >= 0 ) {\r
+ if( castlingRights[forwardMostMove][3] != castlingRights[k][3] ||\r
+ castlingRights[forwardMostMove][4] != castlingRights[k][4] )\r
+ rights++;\r
+ }\r
+ if (appData.debugMode) {\r
+ for(i=0; i<nrCastlingRights; i++)\r
+ fprintf(debugFP, " (%d,%d)", castlingRights[forwardMostMove][i], castlingRights[k][i]);\r
+ }\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, " %d %d\n", rights, k);\r
+ }\r
+ if( rights == 0 && ++count > appData.drawRepeats-2\r
+ && adjudicateLossThreshold != 0) {\r
+ /* adjudicate after user-specified nr of repeats */\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
+ return;\r
+ }\r
+ if( rights == 0 && count > 1 ) /* occurred 2 or more times before */\r
+ epStatus[forwardMostMove] = EP_REP_DRAW;\r
+ }\r
+ }\r
+\r
+ /* Now we test for 50-move draws. Determine ply count */\r
+ count = forwardMostMove;\r
+ /* look for last irreversble move */\r
+ while( epStatus[count] <= EP_NONE && count > backwardMostMove )\r
+ count--;\r
+ /* if we hit starting position, add initial plies */\r
+ if( count == backwardMostMove )\r
+ count -= initialRulePlies;\r
+ count = forwardMostMove - count; \r
+ if( count >= 100)\r
+ epStatus[forwardMostMove] = EP_RULE_DRAW;\r
+ /* this is used to judge if draw claims are legal */\r
+ if(adjudicateLossThreshold != 0 && count >= 2*appData.ruleMoves) {\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
+ return;\r
+ }\r
+ }\r
+\r
+\r
+ }\r
+#endif\r
+ if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+\r
+ return;\r
+ }\r
+\r
+ if (gameMode == TwoMachinesPlay) {\r
+ if (cps->other->sendTime) {\r
+ SendTimeRemaining(cps->other,\r
+ cps->other->twoMachinesColor[0] == 'w');\r
+ }\r
+ SendMoveToProgram(forwardMostMove-1, cps->other);\r
+ if (firstMove) {\r
+ firstMove = FALSE;\r
+ if (cps->other->useColors) {\r
+ SendToProgram(cps->other->twoMachinesColor, cps->other);\r
+ }\r
+ SendToProgram("go\n", cps->other);\r
+ }\r
+ cps->other->maybeThinking = TRUE;\r
+ }\r
+\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ \r
+ if (!pausing && appData.ringBellAfterMoves) {\r
+ RingBell();\r
+ }\r
+\r
+ /* \r
+ * Reenable menu items that were disabled while\r
+ * machine was thinking\r
+ */\r
+ if (gameMode != TwoMachinesPlay)\r
+ SetUserThinkingEnables();\r
+\r
+ return;\r
+ }\r
+\r
+ /* Set special modes for chess engines. Later something general\r
+ * could be added here; for now there is just one kludge feature,\r
+ * needed because Crafty 15.10 and earlier don't ignore SIGINT\r
+ * when "xboard" is given as an interactive command.\r
+ */\r
+ if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {\r
+ cps->useSigint = FALSE;\r
+ cps->useSigterm = FALSE;\r
+ }\r
+\r
+ /*\r
+ * Look for communication commands\r
+ */\r
+ if (!strncmp(message, "telluser ", 9)) {\r
+ DisplayNote(message + 9);\r
+ return;\r
+ }\r
+ if (!strncmp(message, "tellusererror ", 14)) {\r
+ DisplayError(message + 14, 0);\r
+ return;\r
+ }\r
+ if (!strncmp(message, "tellopponent ", 13)) {\r
+ if (appData.icsActive) {\r
+ if (loggedOn) {\r
+ sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);\r
+ SendToICS(buf1);\r
+ }\r
+ } else {\r
+ DisplayNote(message + 13);\r
+ }\r
+ return;\r
+ }\r
+ if (!strncmp(message, "tellothers ", 11)) {\r
+ if (appData.icsActive) {\r
+ if (loggedOn) {\r
+ sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);\r
+ SendToICS(buf1);\r
+ }\r
+ }\r
+ return;\r
+ }\r
+ if (!strncmp(message, "tellall ", 8)) {\r
+ if (appData.icsActive) {\r
+ if (loggedOn) {\r
+ sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);\r
+ SendToICS(buf1);\r
+ }\r
+ } else {\r
+ DisplayNote(message + 8);\r
+ }\r
+ return;\r
+ }\r
+ if (strncmp(message, "warning", 7) == 0) {\r
+ /* Undocumented feature, use tellusererror in new code */\r
+ DisplayError(message, 0);\r
+ return;\r
+ }\r
+ if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {\r
+ strcpy(realname, cps->tidy);\r
+ strcat(realname, " query");\r
+ AskQuestion(realname, buf2, buf1, cps->pr);\r
+ return;\r
+ }\r
+ /* Commands from the engine directly to ICS. We don't allow these to be \r
+ * sent until we are logged on. Crafty kibitzes have been known to \r
+ * interfere with the login process.\r
+ */\r
+ if (loggedOn) {\r
+ if (!strncmp(message, "tellics ", 8)) {\r
+ SendToICS(message + 8);\r
+ SendToICS("\n");\r
+ return;\r
+ }\r
+ if (!strncmp(message, "tellicsnoalias ", 15)) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS(message + 15);\r
+ SendToICS("\n");\r
+ return;\r
+ }\r
+ /* The following are for backward compatibility only */\r
+ if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||\r
+ !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS(message);\r
+ SendToICS("\n");\r
+ return;\r
+ }\r
+ }\r
+ if (strncmp(message, "feature ", 8) == 0) {\r
+ ParseFeatures(message+8, cps);\r
+ }\r
+ if (sscanf(message, "pong %d", &cps->lastPong) == 1) {\r
+ return;\r
+ }\r
+ /*\r
+ * If the move is illegal, cancel it and redraw the board.\r
+ * Also deal with other error cases. Matching is rather loose\r
+ * here to accommodate engines written before the spec.\r
+ */\r
+ if (strncmp(message + 1, "llegal move", 11) == 0 ||\r
+ strncmp(message, "Error", 5) == 0) {\r
+ if (StrStr(message, "name") || \r
+ StrStr(message, "rating") || StrStr(message, "?") ||\r
+ StrStr(message, "result") || StrStr(message, "board") ||\r
+ StrStr(message, "bk") || StrStr(message, "computer") ||\r
+ StrStr(message, "variant") || StrStr(message, "hint") ||\r
+ StrStr(message, "random") || StrStr(message, "depth") ||\r
+ StrStr(message, "accepted")) {\r
+ return;\r
+ }\r
+ if (StrStr(message, "protover")) {\r
+ /* Program is responding to input, so it's apparently done\r
+ initializing, and this error message indicates it is\r
+ protocol version 1. So we don't need to wait any longer\r
+ for it to initialize and send feature commands. */\r
+ FeatureDone(cps, 1);\r
+ cps->protocolVersion = 1;\r
+ return;\r
+ }\r
+ cps->maybeThinking = FALSE;\r
+\r
+ if (StrStr(message, "draw")) {\r
+ /* Program doesn't have "draw" command */\r
+ cps->sendDrawOffers = 0;\r
+ return;\r
+ }\r
+ if (cps->sendTime != 1 &&\r
+ (StrStr(message, "time") || StrStr(message, "otim"))) {\r
+ /* Program apparently doesn't have "time" or "otim" command */\r
+ cps->sendTime = 0;\r
+ return;\r
+ }\r
+ if (StrStr(message, "analyze")) {\r
+ cps->analysisSupport = FALSE;\r
+ cps->analyzing = FALSE;\r
+ Reset(FALSE, TRUE);\r
+ sprintf(buf2, "%s does not support analysis", cps->tidy);\r
+ DisplayError(buf2, 0);\r
+ return;\r
+ }\r
+ if (StrStr(message, "(no matching move)st")) {\r
+ /* Special kludge for GNU Chess 4 only */\r
+ cps->stKludge = TRUE;\r
+ SendTimeControl(cps, movesPerSession, timeControl,\r
+ timeIncrement, appData.searchDepth,\r
+ searchTime);\r
+ return;\r
+ }\r
+ if (StrStr(message, "(no matching move)sd")) {\r
+ /* Special kludge for GNU Chess 4 only */\r
+ cps->sdKludge = TRUE;\r
+ SendTimeControl(cps, movesPerSession, timeControl,\r
+ timeIncrement, appData.searchDepth,\r
+ searchTime);\r
+ return;\r
+ }\r
+ if (!StrStr(message, "llegal")) {\r
+ return;\r
+ }\r
+ if (gameMode == BeginningOfGame || gameMode == EndOfGame ||\r
+ gameMode == IcsIdle) return;\r
+ if (forwardMostMove <= backwardMostMove) return;\r
+#if 0\r
+ /* Following removed: it caused a bug where a real illegal move\r
+ message in analyze mored would be ignored. */\r
+ if (cps == &first && programStats.ok_to_send == 0) {\r
+ /* Bogus message from Crafty responding to "." This filtering\r
+ can miss some of the bad messages, but fortunately the bug \r
+ is fixed in current Crafty versions, so it doesn't matter. */\r
+ return;\r
+ }\r
+#endif\r
+ if (pausing) PauseEvent();\r
+ if (gameMode == PlayFromGameFile) {\r
+ /* Stop reading this game file */\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ }\r
+ currentMove = --forwardMostMove;\r
+ DisplayMove(currentMove-1); /* before DisplayMoveError */\r
+ SwitchClocks();\r
+ DisplayBothClocks();\r
+ sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",\r
+ parseList[currentMove], cps->which);\r
+ DisplayMoveError(buf1);\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+\r
+ /* [HGM] illegal-move claim should forfeit game when Xboard */\r
+ /* only passes fully legal moves */\r
+ if( appData.testLegality && gameMode == TwoMachinesPlay ) {\r
+ static char buf[MSG_SIZ];\r
+ sprintf(buf, "False illegal-move claim on %s (%c%c%c%c)%c",\r
+ machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0);\r
+ GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,\r
+ buf, GE_XBOARD );\r
+ }\r
+ return;\r
+ }\r
+ if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {\r
+ /* Program has a broken "time" command that\r
+ outputs a string not ending in newline.\r
+ Don't use it. */\r
+ cps->sendTime = 0;\r
+ }\r
+ \r
+ /*\r
+ * If chess program startup fails, exit with an error message.\r
+ * Attempts to recover here are futile.\r
+ */\r
+ if ((StrStr(message, "unknown host") != NULL)\r
+ || (StrStr(message, "No remote directory") != NULL)\r
+ || (StrStr(message, "not found") != NULL)\r
+ || (StrStr(message, "No such file") != NULL)\r
+ || (StrStr(message, "can't alloc") != NULL)\r
+ || (StrStr(message, "Permission denied") != NULL)) {\r
+\r
+ cps->maybeThinking = FALSE;\r
+ sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",\r
+ cps->which, cps->program, cps->host, message);\r
+ RemoveInputSource(cps->isr);\r
+ DisplayFatalError(buf1, 0, 1);\r
+ return;\r
+ }\r
+ \r
+ /* \r
+ * Look for hint output\r
+ */\r
+ if (sscanf(message, "Hint: %s", buf1) == 1) {\r
+ if (cps == &first && hintRequested) {\r
+ hintRequested = FALSE;\r
+ if (ParseOneMove(buf1, forwardMostMove, &moveType,\r
+ &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+ (void) CoordsToAlgebraic(boards[forwardMostMove],\r
+ PosFlags(forwardMostMove), EP_UNKNOWN,\r
+ fromY, fromX, toY, toX, promoChar, buf1);\r
+ sprintf(buf2, "Hint: %s", buf1);\r
+ DisplayInformation(buf2);\r
+ } else {\r
+ /* Hint move could not be parsed!? */\r
+ sprintf(buf2,\r
+ "Illegal hint move \"%s\"\nfrom %s chess program",\r
+ buf1, cps->which);\r
+ DisplayError(buf2, 0);\r
+ }\r
+ } else {\r
+ strcpy(lastHint, buf1);\r
+ }\r
+ return;\r
+ }\r
+\r
+ /*\r
+ * Ignore other messages if game is not in progress\r
+ */\r
+ if (gameMode == BeginningOfGame || gameMode == EndOfGame ||\r
+ gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;\r
+\r
+ /*\r
+ * look for win, lose, draw, or draw offer\r
+ */\r
+ if (strncmp(message, "1-0", 3) == 0) {\r
+ char *p, *q, *r = "";\r
+ p = strchr(message, '{');\r
+ if (p) {\r
+ q = strchr(p, '}');\r
+ if (q) {\r
+ *q = NULLCHAR;\r
+ r = p + 1;\r
+ }\r
+ }\r
+ GameEnds(WhiteWins, r, GE_ENGINE1 + (cps != &first)); /* [HGM] pass claimer indication for claim test */\r
+ return;\r
+ } else if (strncmp(message, "0-1", 3) == 0) {\r
+ char *p, *q, *r = "";\r
+ p = strchr(message, '{');\r
+ if (p) {\r
+ q = strchr(p, '}');\r
+ if (q) {\r
+ *q = NULLCHAR;\r
+ r = p + 1;\r
+ }\r
+ }\r
+ /* Kludge for Arasan 4.1 bug */\r
+ if (strcmp(r, "Black resigns") == 0) {\r
+ GameEnds(WhiteWins, r, GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ }\r
+ GameEnds(BlackWins, r, GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strncmp(message, "1/2", 3) == 0) {\r
+ char *p, *q, *r = "";\r
+ p = strchr(message, '{');\r
+ if (p) {\r
+ q = strchr(p, '}');\r
+ if (q) {\r
+ *q = NULLCHAR;\r
+ r = p + 1;\r
+ }\r
+ }\r
+ \r
+ GameEnds(GameIsDrawn, r, GE_ENGINE1 + (cps != &first));\r
+ return;\r
+\r
+ } else if (strncmp(message, "White resign", 12) == 0) {\r
+ GameEnds(BlackWins, "White resigns", GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strncmp(message, "Black resign", 12) == 0) {\r
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strncmp(message, "White", 5) == 0 &&\r
+ message[5] != '(' &&\r
+ StrStr(message, "Black") == NULL) {\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strncmp(message, "Black", 5) == 0 &&\r
+ message[5] != '(') {\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strcmp(message, "resign") == 0 ||\r
+ strcmp(message, "computer resigns") == 0) {\r
+ switch (gameMode) {\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingBlack:\r
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE);\r
+ break;\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingWhite:\r
+ GameEnds(BlackWins, "White resigns", GE_ENGINE);\r
+ break;\r
+ case TwoMachinesPlay:\r
+ if (cps->twoMachinesColor[0] == 'w')\r
+ GameEnds(BlackWins, "White resigns", GE_ENGINE1 + (cps != &first));\r
+ else\r
+ GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first));\r
+ break;\r
+ default:\r
+ /* can't happen */\r
+ break;\r
+ }\r
+ return;\r
+ } else if (strncmp(message, "opponent mates", 14) == 0) {\r
+ switch (gameMode) {\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingBlack:\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);\r
+ break;\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingWhite:\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE);\r
+ break;\r
+ case TwoMachinesPlay:\r
+ if (cps->twoMachinesColor[0] == 'w')\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+ else\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+ break;\r
+ default:\r
+ /* can't happen */\r
+ break;\r
+ }\r
+ return;\r
+ } else if (strncmp(message, "computer mates", 14) == 0) {\r
+ switch (gameMode) {\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingBlack:\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE1);\r
+ break;\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingWhite:\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE);\r
+ break;\r
+ case TwoMachinesPlay:\r
+ if (cps->twoMachinesColor[0] == 'w')\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+ else\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+ break;\r
+ default:\r
+ /* can't happen */\r
+ break;\r
+ }\r
+ return;\r
+ } else if (strncmp(message, "checkmate", 9) == 0) {\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+ } else {\r
+ GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+ }\r
+ return;\r
+ } else if (strstr(message, "Draw") != NULL ||\r
+ strstr(message, "game is a draw") != NULL) {\r
+ GameEnds(GameIsDrawn, "Draw", GE_ENGINE1 + (cps != &first));\r
+ return;\r
+ } else if (strstr(message, "offer") != NULL &&\r
+ strstr(message, "draw") != NULL) {\r
+#if ZIPPY\r
+ if (appData.zippyPlay && first.initDone) {\r
+ /* Relay offer to ICS */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("draw\n");\r
+ }\r
+#endif\r
+ cps->offeredDraw = 2; /* valid until this engine moves twice */\r
+ if (gameMode == TwoMachinesPlay) {\r
+ if (cps->other->offeredDraw) {\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+ } else {\r
+ if (cps->other->sendDrawOffers) {\r
+ SendToProgram("draw\n", cps->other);\r
+ }\r
+ }\r
+ } else if (gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack) {\r
+ if (userOfferedDraw) {\r
+ DisplayInformation("Machine accepts your draw offer");\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+ } else {\r
+ DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ /*\r
+ * Look for thinking output\r
+ */\r
+ if ( appData.showThinking) {\r
+ int plylev, mvleft, mvtot, curscore, time;\r
+ char mvname[MOVE_LEN];\r
+ unsigned long nodes;\r
+ char plyext;\r
+ int ignore = FALSE;\r
+ int prefixHint = FALSE;\r
+ mvname[0] = NULLCHAR;\r
+\r
+ switch (gameMode) {\r
+ case MachinePlaysBlack:\r
+ case IcsPlayingBlack:\r
+ if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;\r
+ break;\r
+ case MachinePlaysWhite:\r
+ case IcsPlayingWhite:\r
+ if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;\r
+ break;\r
+ case AnalyzeMode:\r
+ case AnalyzeFile:\r
+ break;\r
+ case TwoMachinesPlay:\r
+ if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {\r
+ ignore = TRUE;\r
+ }\r
+ break;\r
+ default:\r
+ ignore = TRUE;\r
+ break;\r
+ }\r
+\r
+ if (!ignore) {\r
+ buf1[0] = NULLCHAR;\r
+ if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+ &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {\r
+\r
+ if (plyext != ' ' && plyext != '\t') {\r
+ time *= 100;\r
+ }\r
+\r
+ /* [AS] Negate score if machine is playing black and reporting absolute scores */\r
+ if( cps->scoreIsAbsolute && \r
+ ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )\r
+ {\r
+ curscore = -curscore;\r
+ }\r
+\r
+\r
+ programStats.depth = plylev;\r
+ programStats.nodes = nodes;\r
+ programStats.time = time;\r
+ programStats.score = curscore;\r
+ programStats.got_only_move = 0;\r
+\r
+ /* Buffer overflow protection */\r
+ if (buf1[0] != NULLCHAR) {\r
+ if (strlen(buf1) >= sizeof(programStats.movelist)\r
+ && appData.debugMode) {\r
+ fprintf(debugFP,\r
+ "PV is too long; using the first %d bytes.\n",\r
+ sizeof(programStats.movelist) - 1);\r
+ }\r
+\r
+ safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );\r
+ } else {\r
+ sprintf(programStats.movelist, " no PV\n");\r
+ }\r
+\r
+ if (programStats.seen_stat) {\r
+ programStats.ok_to_send = 1;\r
+ }\r
+\r
+ if (strchr(programStats.movelist, '(') != NULL) {\r
+ programStats.line_is_book = 1;\r
+ programStats.nr_moves = 0;\r
+ programStats.moves_left = 0;\r
+ } else {\r
+ programStats.line_is_book = 0;\r
+ }\r
+\r
+ SendProgramStatsToFrontend( cps, &programStats );\r
+\r
+ /* \r
+ [AS] Protect the thinkOutput buffer from overflow... this\r
+ is only useful if buf1 hasn't overflowed first!\r
+ */\r
+ sprintf(thinkOutput, "[%d]%c%+.2f %s%s",\r
+ plylev, \r
+ (gameMode == TwoMachinesPlay ?\r
+ ToUpper(cps->twoMachinesColor[0]) : ' '),\r
+ ((double) curscore) / 100.0,\r
+ prefixHint ? lastHint : "",\r
+ prefixHint ? " " : "" );\r
+\r
+ if( buf1[0] != NULLCHAR ) {\r
+ unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;\r
+\r
+ if( strlen(buf1) > max_len ) {\r
+ if( appData.debugMode) {\r
+ fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");\r
+ }\r
+ buf1[max_len+1] = '\0';\r
+ }\r
+\r
+ strcat( thinkOutput, buf1 );\r
+ }\r
+\r
+ if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ DisplayMove(currentMove - 1);\r
+ DisplayAnalysis();\r
+ }\r
+ return;\r
+\r
+ } else if ((p=StrStr(message, "(only move)")) != NULL) {\r
+ /* crafty (9.25+) says "(only move) <move>"\r
+ * if there is only 1 legal move\r
+ */\r
+ sscanf(p, "(only move) %s", buf1);\r
+ sprintf(thinkOutput, "%s (only move)", buf1);\r
+ sprintf(programStats.movelist, "%s (only move)", buf1);\r
+ programStats.depth = 1;\r
+ programStats.nr_moves = 1;\r
+ programStats.moves_left = 1;\r
+ programStats.nodes = 1;\r
+ programStats.time = 1;\r
+ programStats.got_only_move = 1;\r
+\r
+ /* Not really, but we also use this member to\r
+ mean "line isn't going to change" (Crafty\r
+ isn't searching, so stats won't change) */\r
+ programStats.line_is_book = 1;\r
+\r
+ SendProgramStatsToFrontend( cps, &programStats );\r
+ \r
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+ DisplayMove(currentMove - 1);\r
+ DisplayAnalysis();\r
+ }\r
+ return;\r
+ } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",\r
+ &time, &nodes, &plylev, &mvleft,\r
+ &mvtot, mvname) >= 5) {\r
+ /* The stat01: line is from Crafty (9.29+) in response\r
+ to the "." command */\r
+ programStats.seen_stat = 1;\r
+ cps->maybeThinking = TRUE;\r
+\r
+ if (programStats.got_only_move || !appData.periodicUpdates)\r
+ return;\r
+\r
+ programStats.depth = plylev;\r
+ programStats.time = time;\r
+ programStats.nodes = nodes;\r
+ programStats.moves_left = mvleft;\r
+ programStats.nr_moves = mvtot;\r
+ strcpy(programStats.move_name, mvname);\r
+ programStats.ok_to_send = 1;\r
+ programStats.movelist[0] = '\0';\r
+\r
+ SendProgramStatsToFrontend( cps, &programStats );\r
+\r
+ DisplayAnalysis();\r
+ return;\r
+\r
+ } else if (strncmp(message,"++",2) == 0) {\r
+ /* Crafty 9.29+ outputs this */\r
+ programStats.got_fail = 2;\r
+ return;\r
+\r
+ } else if (strncmp(message,"--",2) == 0) {\r
+ /* Crafty 9.29+ outputs this */\r
+ programStats.got_fail = 1;\r
+ return;\r
+\r
+ } else if (thinkOutput[0] != NULLCHAR &&\r
+ strncmp(message, " ", 4) == 0) {\r
+ unsigned message_len;\r
+\r
+ p = message;\r
+ while (*p && *p == ' ') p++;\r
+\r
+ message_len = strlen( p );\r
+\r
+ /* [AS] Avoid buffer overflow */\r
+ if( sizeof(thinkOutput) - strlen(thinkOutput) - 1 > message_len ) {\r
+ strcat(thinkOutput, " ");\r
+ strcat(thinkOutput, p);\r
+ }\r
+\r
+ if( sizeof(programStats.movelist) - strlen(programStats.movelist) - 1 > message_len ) {\r
+ strcat(programStats.movelist, " ");\r
+ strcat(programStats.movelist, p);\r
+ }\r
+\r
+ if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+ DisplayMove(currentMove - 1);\r
+ DisplayAnalysis();\r
+ }\r
+ return;\r
+ }\r
+ }\r
+ else {\r
+ buf1[0] = NULLCHAR;\r
+\r
+ if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+ &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) \r
+ {\r
+ ChessProgramStats cpstats;\r
+\r
+ if (plyext != ' ' && plyext != '\t') {\r
+ time *= 100;\r
+ }\r
+\r
+ /* [AS] Negate score if machine is playing black and reporting absolute scores */\r
+ if( cps->scoreIsAbsolute && ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) ) {\r
+ curscore = -curscore;\r
+ }\r
+\r
+ cpstats.depth = plylev;\r
+ cpstats.nodes = nodes;\r
+ cpstats.time = time;\r
+ cpstats.score = curscore;\r
+ cpstats.got_only_move = 0;\r
+ cpstats.movelist[0] = '\0';\r
+\r
+ if (buf1[0] != NULLCHAR) {\r
+ safeStrCpy( cpstats.movelist, buf1, sizeof(cpstats.movelist) );\r
+ }\r
+\r
+ cpstats.ok_to_send = 0;\r
+ cpstats.line_is_book = 0;\r
+ cpstats.nr_moves = 0;\r
+ cpstats.moves_left = 0;\r
+\r
+ SendProgramStatsToFrontend( cps, &cpstats );\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/* Parse a game score from the character string "game", and\r
+ record it as the history of the current game. The game\r
+ score is NOT assumed to start from the standard position. \r
+ The display is not updated in any way.\r
+ */\r
+void\r
+ParseGameHistory(game)\r
+ char *game;\r
+{\r
+ ChessMove moveType;\r
+ int fromX, fromY, toX, toY, boardIndex;\r
+ char promoChar;\r
+ char *p, *q;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsing game history: %s\n", game);\r
+\r
+ if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");\r
+ gameInfo.site = StrSave(appData.icsHost);\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+\r
+ /* Parse out names of players */\r
+ while (*game == ' ') game++;\r
+ p = buf;\r
+ while (*game != ' ') *p++ = *game++;\r
+ *p = NULLCHAR;\r
+ gameInfo.white = StrSave(buf);\r
+ while (*game == ' ') game++;\r
+ p = buf;\r
+ while (*game != ' ' && *game != '\n') *p++ = *game++;\r
+ *p = NULLCHAR;\r
+ gameInfo.black = StrSave(buf);\r
+\r
+ /* Parse moves */\r
+ boardIndex = blackPlaysFirst ? 1 : 0;\r
+ yynewstr(game);\r
+ for (;;) {\r
+ yyboardindex = boardIndex;\r
+ moveType = (ChessMove) yylex();\r
+ switch (moveType) {\r
+#ifdef FAIRY\r
+ case WhitePromotionChancellor:\r
+ case BlackPromotionChancellor:\r
+ case WhitePromotionArchbishop:\r
+ case BlackPromotionArchbishop:\r
+#endif\r
+ case WhitePromotionQueen:\r
+ case BlackPromotionQueen:\r
+ case WhitePromotionRook:\r
+ case BlackPromotionRook:\r
+ case WhitePromotionBishop:\r
+ case BlackPromotionBishop:\r
+ case WhitePromotionKnight:\r
+ case BlackPromotionKnight:\r
+ case WhitePromotionKing:\r
+ case BlackPromotionKing:\r
+ case NormalMove:\r
+ case WhiteCapturesEnPassant:\r
+ case BlackCapturesEnPassant:\r
+ case WhiteKingSideCastle:\r
+ case WhiteQueenSideCastle:\r
+ case BlackKingSideCastle:\r
+ case BlackQueenSideCastle:\r
+ case WhiteKingSideCastleWild:\r
+ case WhiteQueenSideCastleWild:\r
+ case BlackKingSideCastleWild:\r
+ case BlackQueenSideCastleWild:\r
+ /* PUSH Fabien */\r
+ case WhiteHSideCastleFR:\r
+ case WhiteASideCastleFR:\r
+ case BlackHSideCastleFR:\r
+ case BlackASideCastleFR:\r
+ /* POP Fabien */\r
+ case IllegalMove: /* maybe suicide chess, etc. */\r
+ fromX = currentMoveString[0] - 'a';\r
+ fromY = currentMoveString[1] - ONE;\r
+ toX = currentMoveString[2] - 'a';\r
+ toY = currentMoveString[3] - ONE;\r
+ promoChar = currentMoveString[4];\r
+ break;\r
+ case WhiteDrop:\r
+ case BlackDrop:\r
+ fromX = moveType == WhiteDrop ?\r
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+ (int) CharToPiece(ToLower(currentMoveString[0]));\r
+ fromY = DROP_RANK;\r
+ toX = currentMoveString[2] - 'a';\r
+ toY = currentMoveString[3] - ONE;\r
+ promoChar = NULLCHAR;\r
+ break;\r
+ case AmbiguousMove:\r
+ /* bug? */\r
+ sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);\r
+ DisplayError(buf, 0);\r
+ return;\r
+ case ImpossibleMove:\r
+ /* bug? */\r
+ sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);\r
+ DisplayError(buf, 0);\r
+ return;\r
+ case (ChessMove) 0: /* end of file */\r
+ if (boardIndex < backwardMostMove) {\r
+ /* Oops, gap. How did that happen? */\r
+ DisplayError("Gap in move list", 0);\r
+ return;\r
+ }\r
+ backwardMostMove = blackPlaysFirst ? 1 : 0;\r
+ if (boardIndex > forwardMostMove) {\r
+ forwardMostMove = boardIndex;\r
+ }\r
+ return;\r
+ case ElapsedTime:\r
+ if (boardIndex > (blackPlaysFirst ? 1 : 0)) {\r
+ strcat(parseList[boardIndex-1], " ");\r
+ strcat(parseList[boardIndex-1], yy_text);\r
+ }\r
+ continue;\r
+ case Comment:\r
+ case PGNTag:\r
+ case NAG:\r
+ default:\r
+ /* ignore */\r
+ continue;\r
+ case WhiteWins:\r
+ case BlackWins:\r
+ case GameIsDrawn:\r
+ case GameUnfinished:\r
+ if (gameMode == IcsExamining) {\r
+ if (boardIndex < backwardMostMove) {\r
+ /* Oops, gap. How did that happen? */\r
+ return;\r
+ }\r
+ backwardMostMove = blackPlaysFirst ? 1 : 0;\r
+ return;\r
+ }\r
+ gameInfo.result = moveType;\r
+ p = strchr(yy_text, '{');\r
+ if (p == NULL) p = strchr(yy_text, '(');\r
+ if (p == NULL) {\r
+ p = yy_text;\r
+ if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";\r
+ } else {\r
+ q = strchr(p, *p == '{' ? '}' : ')');\r
+ if (q != NULL) *q = NULLCHAR;\r
+ p++;\r
+ }\r
+ gameInfo.resultDetails = StrSave(p);\r
+ continue;\r
+ }\r
+ if (boardIndex >= forwardMostMove &&\r
+ !(gameMode == IcsObserving && ics_gamenum == -1)) {\r
+ backwardMostMove = blackPlaysFirst ? 1 : 0;\r
+ return;\r
+ }\r
+ (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),\r
+ EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
+ parseList[boardIndex]);\r
+ CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+ /* currentMoveString is set as a side-effect of yylex */\r
+ strcpy(moveList[boardIndex], currentMoveString);\r
+ strcat(moveList[boardIndex], "\n");\r
+ boardIndex++;\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+ switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
+ EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
+ case MT_NONE:\r
+ case MT_STALEMATE:\r
+ default:\r
+ break;\r
+ case MT_CHECK:\r
+ strcat(parseList[boardIndex - 1], "+");\r
+ break;\r
+ case MT_CHECKMATE:\r
+ strcat(parseList[boardIndex - 1], "#");\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/* Apply a move to the given board */\r
+void\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+ int fromX, fromY, toX, toY;\r
+ int promoChar;\r
+ Board board;\r
+{\r
+ /* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
+ if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\r
+ && promoChar != 0) promoChar = 'f';\r
+ \r
+ ChessSquare captured = board[toY][toX];\r
+ if (fromY == DROP_RANK) {\r
+ /* must be first */\r
+ board[toY][toX] = (ChessSquare) fromX;\r
+ } else if (fromX == toX && fromY == toY) {\r
+ return;\r
+ }\r
+\r
+ /* Code added by Tord: */\r
+ /* FRC castling assumed when king captures friendly rook. */\r
+ else if (board[fromY][fromX] == WhiteKing &&\r
+ board[toY][toX] == WhiteRook) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = EmptySquare;\r
+ if(toX > fromX) {\r
+ board[0][BOARD_WIDTH-2] = WhiteKing; board[0][BOARD_WIDTH-3] = WhiteRook;\r
+ } else {\r
+ board[0][2] = WhiteKing; board[0][3] = WhiteRook;\r
+ }\r
+ } else if (board[fromY][fromX] == BlackKing &&\r
+ board[toY][toX] == BlackRook) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = EmptySquare;\r
+ if(toX > fromX) {\r
+ board[BOARD_HEIGHT-1][BOARD_WIDTH-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_WIDTH-3] = BlackRook;\r
+ } else {\r
+ board[BOARD_HEIGHT-1][2] = BlackKing; board[BOARD_HEIGHT-1][3] = BlackRook;\r
+ }\r
+ /* End of code added by Tord */\r
+\r
+ } else if (initialPosition[fromY][fromX] == WhiteKing\r
+ && board[fromY][fromX] == WhiteKing\r
+ && toY == fromY && toX > fromX+1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = WhiteKing;\r
+ board[fromY][BOARD_WIDTH-1] = EmptySquare;\r
+ board[toY][toX-1] = WhiteRook;\r
+ } else if (initialPosition[fromY][fromX] == WhiteKing\r
+ && board[fromY][fromX] == WhiteKing\r
+ && toY == fromY && toX < fromX-1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = WhiteKing;\r
+ board[fromY][0] = EmptySquare;\r
+ board[toY][toX+1] = WhiteRook;\r
+ } else if (fromY == 0 && fromX == 3\r
+ && board[fromY][fromX] == WhiteKing\r
+ && toY == 0 && toX == 5) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = WhiteKing;\r
+ board[fromY][7] = EmptySquare;\r
+ board[toY][4] = WhiteRook;\r
+ } else if (fromY == 0 && fromX == 3\r
+ && board[fromY][fromX] == WhiteKing\r
+ && toY == 0 && toX == 1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = WhiteKing;\r
+ board[fromY][0] = EmptySquare;\r
+ board[toY][2] = WhiteRook;\r
+ } else if (board[fromY][fromX] == WhitePawn\r
+ && toY == BOARD_HEIGHT-1\r
+#ifdef FAIRY\r
+ && gameInfo.variant != VariantXiangqi\r
+#endif\r
+ ) {\r
+ /* white pawn promotion */\r
+ board[toY][toX] = CharToPiece(ToUpper(promoChar));\r
+ if (board[toY][toX] == EmptySquare) {\r
+ board[toY][toX] = WhiteQueen;\r
+ }\r
+ board[fromY][fromX] = EmptySquare;\r
+ } else if ((fromY == BOARD_HEIGHT-4)\r
+ && (toX != fromX)\r
+ && (board[fromY][fromX] == WhitePawn)\r
+ && (board[toY][toX] == EmptySquare)) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = WhitePawn;\r
+ captured = board[toY - 1][toX];\r
+ board[toY - 1][toX] = EmptySquare;\r
+ } else if (initialPosition[fromY][fromX] == BlackKing\r
+ && board[fromY][fromX] == BlackKing\r
+ && toY == fromY && toX > fromX+1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = BlackKing;\r
+ board[fromY][BOARD_WIDTH-1] = EmptySquare;\r
+ board[toY][toX-1] = BlackRook;\r
+ } else if (initialPosition[fromY][fromX] == BlackKing\r
+ && board[fromY][fromX] == BlackKing\r
+ && toY == fromY && toX < fromX-1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = BlackKing;\r
+ board[fromY][0] = EmptySquare;\r
+ board[toY][toX+1] = BlackRook;\r
+ } else if (fromY == 7 && fromX == 3\r
+ && board[fromY][fromX] == BlackKing\r
+ && toY == 7 && toX == 5) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = BlackKing;\r
+ board[fromY][7] = EmptySquare;\r
+ board[toY][4] = BlackRook;\r
+ } else if (fromY == 7 && fromX == 3\r
+ && board[fromY][fromX] == BlackKing\r
+ && toY == 7 && toX == 1) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = BlackKing;\r
+ board[fromY][0] = EmptySquare;\r
+ board[toY][2] = BlackRook;\r
+ } else if (board[fromY][fromX] == BlackPawn\r
+ && toY == 0\r
+#ifdef FAIRY\r
+ && gameInfo.variant != VariantXiangqi\r
+#endif\r
+ ) {\r
+ /* black pawn promotion */\r
+ board[0][toX] = CharToPiece(ToLower(promoChar));\r
+ if (board[0][toX] == EmptySquare) {\r
+ board[0][toX] = BlackQueen;\r
+ }\r
+ board[fromY][fromX] = EmptySquare;\r
+ } else if ((fromY == 3)\r
+ && (toX != fromX)\r
+ && (board[fromY][fromX] == BlackPawn)\r
+ && (board[toY][toX] == EmptySquare)) {\r
+ board[fromY][fromX] = EmptySquare;\r
+ board[toY][toX] = BlackPawn;\r
+ captured = board[toY + 1][toX];\r
+ board[toY + 1][toX] = EmptySquare;\r
+ } else {\r
+ board[toY][toX] = board[fromY][fromX];\r
+ board[fromY][fromX] = EmptySquare;\r
+ }\r
+ if (gameInfo.variant == VariantCrazyhouse) {\r
+#if 0\r
+ /* !!A lot more code needs to be written to support holdings */\r
+ if (fromY == DROP_RANK) {\r
+ /* Delete from holdings */\r
+ if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;\r
+ }\r
+ if (captured != EmptySquare) {\r
+ /* Add to holdings */\r
+ if (captured < BlackPawn) {\r
+ holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;\r
+ } else {\r
+ holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;\r
+ }\r
+ }\r
+#endif\r
+ } else if (gameInfo.variant == VariantAtomic) {\r
+ if (captured != EmptySquare) {\r
+ int y, x;\r
+ for (y = toY-1; y <= toY+1; y++) {\r
+ for (x = toX-1; x <= toX+1; x++) {\r
+ if (y >= 0 && y < BOARD_WIDTH && x >= 0 && x < BOARD_WIDTH &&\r
+ board[y][x] != WhitePawn && board[y][x] != BlackPawn) {\r
+ board[y][x] = EmptySquare;\r
+ }\r
+ }\r
+ }\r
+ board[toY][toX] = EmptySquare;\r
+ }\r
+ }\r
+}\r
+\r
+/* Updates forwardMostMove */\r
+void\r
+MakeMove(fromX, fromY, toX, toY, promoChar)\r
+ int fromX, fromY, toX, toY;\r
+ int promoChar;\r
+{\r
+ forwardMostMove++;\r
+\r
+ if (forwardMostMove >= MAX_MOVES) {\r
+ DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+ 0, 1);\r
+ return;\r
+ }\r
+ SwitchClocks();\r
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+ if (commentList[forwardMostMove] != NULL) {\r
+ free(commentList[forwardMostMove]);\r
+ commentList[forwardMostMove] = NULL;\r
+ }\r
+ CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);\r
+ /* [HGM] compute & store e.p. status and castling rights for new position */\r
+ { int i, j;\r
+\r
+ epStatus[forwardMostMove] = EP_NONE;\r
+\r
+ if( boards[forwardMostMove][toY][toX] != EmptySquare ) \r
+ epStatus[forwardMostMove] = EP_CAPTURE; \r
+\r
+ if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) {\r
+ epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
+ if( toY-fromY==2 &&\r
+ (toX>1 && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
+ toX<BOARD_WIDTH-1 && boards[forwardMostMove][toY][toX+1] == BlackPawn ) )\r
+ epStatus[forwardMostMove] = toX;\r
+ } else \r
+ if( boards[forwardMostMove][fromY][fromX] == BlackPawn ) {\r
+ epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
+ if( toY-fromY== -2 &&\r
+ (toX>1 && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
+ toX<BOARD_WIDTH-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
+ epStatus[forwardMostMove] = toX;\r
+ }\r
+\r
+ for(i=0; i<nrCastlingRights; i++) {\r
+ castlingRights[forwardMostMove][i] = castlingRights[forwardMostMove-1][i];\r
+ if(castlingRights[forwardMostMove][i] == fromX && castlingRank[i] == fromY ||\r
+ castlingRights[forwardMostMove][i] == toX && castlingRank[i] == toY \r
+ ) castlingRights[forwardMostMove][i] = -1; // revoke for moved or captured piece\r
+\r
+ }\r
+\r
+ }\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);\r
+ gameInfo.result = GameUnfinished;\r
+ if (gameInfo.resultDetails != NULL) {\r
+ free(gameInfo.resultDetails);\r
+ gameInfo.resultDetails = NULL;\r
+ }\r
+ CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,\r
+ moveList[forwardMostMove - 1]);\r
+ (void) CoordsToAlgebraic(boards[forwardMostMove - 1],\r
+ PosFlags(forwardMostMove - 1), EP_UNKNOWN,\r
+ fromY, fromX, toY, toX, promoChar,\r
+ parseList[forwardMostMove - 1]);\r
+ switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+ epStatus[forwardMostMove], /* [HGM] use true e.p. */\r
+ castlingRights[forwardMostMove]) ) {\r
+ case MT_NONE:\r
+ case MT_STALEMATE:\r
+ default:\r
+ break;\r
+ case MT_CHECK:\r
+ strcat(parseList[forwardMostMove - 1], "+");\r
+ break;\r
+ case MT_CHECKMATE:\r
+ strcat(parseList[forwardMostMove - 1], "#");\r
+ break;\r
+ }\r
+}\r
+\r
+/* Updates currentMove if not pausing */\r
+void\r
+ShowMove(fromX, fromY, toX, toY)\r
+{\r
+ int instant = (gameMode == PlayFromGameFile) ?\r
+ (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;\r
+ if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
+ if (!instant) {\r
+ if (forwardMostMove == currentMove + 1) {\r
+ AnimateMove(boards[forwardMostMove - 1],\r
+ fromX, fromY, toX, toY);\r
+ }\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(fromX, fromY, toX, toY);\r
+ }\r
+ }\r
+ currentMove = forwardMostMove;\r
+ }\r
+\r
+ if (instant) return;\r
+\r
+ DisplayMove(currentMove - 1);\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ DisplayBothClocks();\r
+ HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+}\r
+\r
+\r
+void\r
+InitChessProgram(cps)\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ if (appData.noChessProgram) return;\r
+ hintRequested = FALSE;\r
+ bookRequested = FALSE;\r
+ SendToProgram(cps->initString, cps);\r
+ if (gameInfo.variant != VariantNormal &&\r
+ gameInfo.variant != VariantLoadable) {\r
+ char *v = VariantName(gameInfo.variant);\r
+ if (StrStr(cps->variants, v) == NULL) {\r
+ sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);\r
+ DisplayFatalError(buf, 0, 1);\r
+ return;\r
+ }\r
+ sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));\r
+ SendToProgram(buf, cps);\r
+ }\r
+ if (cps->sendICS) {\r
+ sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
+ SendToProgram(buf, cps);\r
+ }\r
+ cps->maybeThinking = FALSE;\r
+ cps->offeredDraw = 0;\r
+ if (!appData.icsActive) {\r
+ SendTimeControl(cps, movesPerSession, timeControl,\r
+ timeIncrement, appData.searchDepth,\r
+ searchTime);\r
+ }\r
+ if (appData.showThinking) {\r
+ SendToProgram("post\n", cps);\r
+ }\r
+ SendToProgram("hard\n", cps);\r
+ if (!appData.ponderNextMove) {\r
+ /* Warning: "easy" is a toggle in GNU Chess, so don't send\r
+ it without being sure what state we are in first. "hard"\r
+ is not a toggle, so that one is OK.\r
+ */\r
+ SendToProgram("easy\n", cps);\r
+ }\r
+ if (cps->usePing) {\r
+ sprintf(buf, "ping %d\n", ++cps->lastPing);\r
+ SendToProgram(buf, cps);\r
+ }\r
+ cps->initDone = TRUE;\r
+} \r
+\r
+\r
+void\r
+StartChessProgram(cps)\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ int err;\r
+\r
+ if (appData.noChessProgram) return;\r
+ cps->initDone = FALSE;\r
+\r
+ if (strcmp(cps->host, "localhost") == 0) {\r
+ err = StartChildProcess(cps->program, cps->dir, &cps->pr);\r
+ } else if (*appData.remoteShell == NULLCHAR) {\r
+ err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);\r
+ } else {\r
+ if (*appData.remoteUser == NULLCHAR) {\r
+ sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,\r
+ cps->program);\r
+ } else {\r
+ sprintf(buf, "%s %s -l %s %s", appData.remoteShell,\r
+ cps->host, appData.remoteUser, cps->program);\r
+ }\r
+ err = StartChildProcess(buf, "", &cps->pr);\r
+ }\r
+ \r
+ if (err != 0) {\r
+ sprintf(buf, "Startup failure on '%s'", cps->program);\r
+ DisplayFatalError(buf, err, 1);\r
+ cps->pr = NoProc;\r
+ cps->isr = NULL;\r
+ return;\r
+ }\r
+ \r
+ cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);\r
+ if (cps->protocolVersion > 1) {\r
+ sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);\r
+ SendToProgram(buf, cps);\r
+ } else {\r
+ SendToProgram("xboard\n", cps);\r
+ }\r
+}\r
+\r
+\r
+void\r
+TwoMachinesEventIfReady P((void))\r
+{\r
+ if (first.lastPing != first.lastPong) {\r
+ DisplayMessage("", "Waiting for first chess program");\r
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+ return;\r
+ }\r
+ if (second.lastPing != second.lastPong) {\r
+ DisplayMessage("", "Waiting for second chess program");\r
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+ return;\r
+ }\r
+ ThawUI();\r
+ TwoMachinesEvent();\r
+}\r
+\r
+void\r
+NextMatchGame P((void))\r
+{\r
+ Reset(FALSE, TRUE);\r
+ if (*appData.loadGameFile != NULLCHAR) {\r
+ LoadGameFromFile(appData.loadGameFile,\r
+ appData.loadGameIndex,\r
+ appData.loadGameFile, FALSE);\r
+ } else if (*appData.loadPositionFile != NULLCHAR) {\r
+ LoadPositionFromFile(appData.loadPositionFile,\r
+ appData.loadPositionIndex,\r
+ appData.loadPositionFile);\r
+ }\r
+ TwoMachinesEventIfReady();\r
+}\r
+\r
+void UserAdjudicationEvent( int result )\r
+{\r
+ ChessMove gameResult = GameIsDrawn;\r
+\r
+ if( result > 0 ) {\r
+ gameResult = WhiteWins;\r
+ }\r
+ else if( result < 0 ) {\r
+ gameResult = BlackWins;\r
+ }\r
+\r
+ if( gameMode == TwoMachinesPlay ) {\r
+ GameEnds( gameResult, "User adjudication", GE_XBOARD );\r
+ }\r
+}\r
+\r
+\r
+void\r
+GameEnds(result, resultDetails, whosays)\r
+ ChessMove result;\r
+ char *resultDetails;\r
+ int whosays;\r
+{\r
+ GameMode nextGameMode;\r
+ int isIcsGame;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "GameEnds(%d, %s, %d)\n",\r
+ result, resultDetails ? resultDetails : "(null)", whosays);\r
+ }\r
+\r
+ if (appData.icsActive && whosays == (GE_ENGINE || whosays >= GE_ENGINE1)) {\r
+ /* If we are playing on ICS, the server decides when the\r
+ game is over, but the engine can offer to draw, claim \r
+ a draw, or resign. \r
+ */\r
+#if ZIPPY\r
+ if (appData.zippyPlay && first.initDone) {\r
+ if (result == GameIsDrawn) {\r
+ /* In case draw still needs to be claimed */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("draw\n");\r
+ } else if (StrCaseStr(resultDetails, "resign")) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("resign\n");\r
+ }\r
+ }\r
+#endif\r
+ return;\r
+ }\r
+\r
+ /* If we're loading the game from a file, stop */\r
+ if (whosays == GE_FILE) {\r
+ (void) StopLoadGameTimer();\r
+ gameFileFP = NULL;\r
+ }\r
+\r
+ /* Cancel draw offers */\r
+ first.offeredDraw = second.offeredDraw = 0;\r
+\r
+ /* If this is an ICS game, only ICS can really say it's done;\r
+ if not, anyone can. */\r
+ isIcsGame = (gameMode == IcsPlayingWhite || \r
+ gameMode == IcsPlayingBlack || \r
+ gameMode == IcsObserving || \r
+ gameMode == IcsExamining);\r
+\r
+ if (!isIcsGame || whosays == GE_ICS) {\r
+ /* OK -- not an ICS game, or ICS said it was done */\r
+ StopClocks();\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "GameEnds(%d, %s, %d) clock stopped\n",\r
+ result, resultDetails ? resultDetails : "(null)", whosays);\r
+ }\r
+ if (!isIcsGame && !appData.noChessProgram) \r
+ SetUserThinkingEnables();\r
+ \r
+ /* [HGM] if a machine claims the game end we verify this claim */\r
+ if( appData.testLegality && gameMode == TwoMachinesPlay &&\r
+ appData.testClaims && whosays >= GE_ENGINE1 ) {\r
+ char claimer;\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "GameEnds(%d, %s, %d) test claims\n",\r
+ result, resultDetails ? resultDetails : "(null)", whosays);\r
+ }\r
+ claimer = whosays == GE_ENGINE1 ? /* color of claimer */\r
+ first.twoMachinesColor[0] :\r
+ second.twoMachinesColor[0] ;\r
+ if( result == WhiteWins && claimer == 'w' ||\r
+ result == BlackWins && claimer == 'b' ) {\r
+ /* Xboard immediately adjudicates all mates, so win claims must be false */\r
+ sprintf(buf, "False win claim: '%s'", resultDetails);\r
+ result = claimer == 'w' ? BlackWins : WhiteWins;\r
+ resultDetails = buf;\r
+ } else\r
+ if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS ) {\r
+ /* Draw that was not flagged by Xboard is false */\r
+ sprintf(buf, "False draw claim: '%s'", resultDetails);\r
+ result = claimer == 'w' ? BlackWins : WhiteWins;\r
+ resultDetails = buf;\r
+ }\r
+ /* (Claiming a loss is accepted no questions asked!) */\r
+ }\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "GameEnds(%d, %s, %d) after test\n",\r
+ result, resultDetails ? resultDetails : "(null)", whosays);\r
+ }\r
+ if (resultDetails != NULL) {\r
+ gameInfo.result = result;\r
+ gameInfo.resultDetails = StrSave(resultDetails);\r
+\r
+ /* Tell program how game ended in case it is learning */\r
+ if (gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack ||\r
+ gameMode == TwoMachinesPlay ||\r
+ gameMode == IcsPlayingWhite ||\r
+ gameMode == IcsPlayingBlack ||\r
+ gameMode == BeginningOfGame) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "result %s {%s}\n", PGNResult(result),\r
+ resultDetails);\r
+ if (first.pr != NoProc) {\r
+ SendToProgram(buf, &first);\r
+ }\r
+ if (second.pr != NoProc &&\r
+ gameMode == TwoMachinesPlay) {\r
+ SendToProgram(buf, &second);\r
+ }\r
+ }\r
+\r
+ /* display last move only if game was not loaded from file */\r
+ if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
+ DisplayMove(currentMove - 1);\r
+ \r
+ if (forwardMostMove != 0) {\r
+ if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
+ if (*appData.saveGameFile != NULLCHAR) {\r
+ SaveGameToFile(appData.saveGameFile, TRUE);\r
+ } else if (appData.autoSaveGames) {\r
+ AutoSaveGame();\r
+ }\r
+ if (*appData.savePositionFile != NULLCHAR) {\r
+ SavePositionToFile(appData.savePositionFile);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (appData.icsActive) {\r
+ if (appData.quietPlay &&\r
+ (gameMode == IcsPlayingWhite ||\r
+ gameMode == IcsPlayingBlack)) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("set shout 1\n");\r
+ }\r
+ nextGameMode = IcsIdle;\r
+ ics_user_moved = FALSE;\r
+ /* clean up premove. It's ugly when the game has ended and the\r
+ * premove highlights are still on the board.\r
+ */\r
+ if (gotPremove) {\r
+ gotPremove = FALSE;\r
+ ClearPremoveHighlights();\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ }\r
+ if (whosays == GE_ICS) {\r
+ switch (result) {\r
+ case WhiteWins:\r
+ if (gameMode == IcsPlayingWhite)\r
+ PlayIcsWinSound();\r
+ else if(gameMode == IcsPlayingBlack)\r
+ PlayIcsLossSound();\r
+ break;\r
+ case BlackWins:\r
+ if (gameMode == IcsPlayingBlack)\r
+ PlayIcsWinSound();\r
+ else if(gameMode == IcsPlayingWhite)\r
+ PlayIcsLossSound();\r
+ break;\r
+ case GameIsDrawn:\r
+ PlayIcsDrawSound();\r
+ break;\r
+ default:\r
+ PlayIcsUnfinishedSound();\r
+ }\r
+ }\r
+ } else if (gameMode == EditGame ||\r
+ gameMode == PlayFromGameFile || \r
+ gameMode == AnalyzeMode || \r
+ gameMode == AnalyzeFile) {\r
+ nextGameMode = gameMode;\r
+ } else {\r
+ nextGameMode = EndOfGame;\r
+ }\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ } else {\r
+ nextGameMode = gameMode;\r
+ }\r
+\r
+ if (appData.noChessProgram) {\r
+ gameMode = nextGameMode;\r
+ ModeHighlight();\r
+ return;\r
+ }\r
+\r
+ if (first.reuse) {\r
+ /* Put first chess program into idle state */\r
+ if (first.pr != NoProc &&\r
+ (gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack ||\r
+ gameMode == TwoMachinesPlay ||\r
+ gameMode == IcsPlayingWhite ||\r
+ gameMode == IcsPlayingBlack ||\r
+ gameMode == BeginningOfGame)) {\r
+ SendToProgram("force\n", &first);\r
+ if (first.usePing) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "ping %d\n", ++first.lastPing);\r
+ SendToProgram(buf, &first);\r
+ }\r
+ }\r
+ } else if (result != GameUnfinished || nextGameMode == IcsIdle) {\r
+ /* Kill off first chess program */\r
+ if (first.isr != NULL)\r
+ RemoveInputSource(first.isr);\r
+ first.isr = NULL;\r
+ \r
+ if (first.pr != NoProc) {\r
+ ExitAnalyzeMode();\r
+ DoSleep( appData.delayBeforeQuit );\r
+ SendToProgram("quit\n", &first);\r
+ DoSleep( appData.delayAfterQuit );\r
+ DestroyChildProcess(first.pr, first.useSigterm);\r
+ }\r
+ first.pr = NoProc;\r
+ }\r
+ if (second.reuse) {\r
+ /* Put second chess program into idle state */\r
+ if (second.pr != NoProc &&\r
+ gameMode == TwoMachinesPlay) {\r
+ SendToProgram("force\n", &second);\r
+ if (second.usePing) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "ping %d\n", ++second.lastPing);\r
+ SendToProgram(buf, &second);\r
+ }\r
+ }\r
+ } else if (result != GameUnfinished || nextGameMode == IcsIdle) {\r
+ /* Kill off second chess program */\r
+ if (second.isr != NULL)\r
+ RemoveInputSource(second.isr);\r
+ second.isr = NULL;\r
+ \r
+ if (second.pr != NoProc) {\r
+ DoSleep( appData.delayBeforeQuit );\r
+ SendToProgram("quit\n", &second);\r
+ DoSleep( appData.delayAfterQuit );\r
+ DestroyChildProcess(second.pr, second.useSigterm);\r
+ }\r
+ second.pr = NoProc;\r
+ }\r
+\r
+ if (matchMode && gameMode == TwoMachinesPlay) {\r
+ switch (result) {\r
+ case WhiteWins:\r
+ if (first.twoMachinesColor[0] == 'w') {\r
+ first.matchWins++;\r
+ } else {\r
+ second.matchWins++;\r
+ }\r
+ break;\r
+ case BlackWins:\r
+ if (first.twoMachinesColor[0] == 'b') {\r
+ first.matchWins++;\r
+ } else {\r
+ second.matchWins++;\r
+ }\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ if (matchGame < appData.matchGames) {\r
+ char *tmp;\r
+ tmp = first.twoMachinesColor;\r
+ first.twoMachinesColor = second.twoMachinesColor;\r
+ second.twoMachinesColor = tmp;\r
+ gameMode = nextGameMode;\r
+ matchGame++;\r
+ if(appData.matchPause>10000 || appData.matchPause<10)\r
+ appData.matchPause = 10000; /* [HGM] make pause adjustable */\r
+ ScheduleDelayedEvent(NextMatchGame, appData.matchPause);\r
+ return;\r
+ } else {\r
+ char buf[MSG_SIZ];\r
+ gameMode = nextGameMode;\r
+ sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",\r
+ first.tidy, second.tidy,\r
+ first.matchWins, second.matchWins,\r
+ appData.matchGames - (first.matchWins + second.matchWins));\r
+ DisplayFatalError(buf, 0, 0);\r
+ }\r
+ }\r
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&\r
+ !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))\r
+ ExitAnalyzeMode();\r
+ gameMode = nextGameMode;\r
+ ModeHighlight();\r
+}\r
+\r
+/* Assumes program was just initialized (initString sent).\r
+ Leaves program in force mode. */\r
+void\r
+FeedMovesToProgram(cps, upto) \r
+ ChessProgramState *cps;\r
+ int upto;\r
+{\r
+ int i;\r
+ \r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",\r
+ startedFromSetupPosition ? "position and " : "",\r
+ backwardMostMove, upto, cps->which);\r
+ SendToProgram("force\n", cps);\r
+ if (startedFromSetupPosition) {\r
+ SendBoard(cps, backwardMostMove);\r
+ }\r
+ for (i = backwardMostMove; i < upto; i++) {\r
+ SendMoveToProgram(i, cps);\r
+ }\r
+}\r
+\r
+\r
+void\r
+ResurrectChessProgram()\r
+{\r
+ /* The chess program may have exited.\r
+ If so, restart it and feed it all the moves made so far. */\r
+\r
+ if (appData.noChessProgram || first.pr != NoProc) return;\r
+ \r
+ StartChessProgram(&first);\r
+ InitChessProgram(&first);\r
+ FeedMovesToProgram(&first, currentMove);\r
+\r
+ if (!first.sendTime) {\r
+ /* can't tell gnuchess what its clock should read,\r
+ so we bow to its notion. */\r
+ ResetClocks();\r
+ timeRemaining[0][currentMove] = whiteTimeRemaining;\r
+ timeRemaining[1][currentMove] = blackTimeRemaining;\r
+ }\r
+\r
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&\r
+ first.analysisSupport) {\r
+ SendToProgram("analyze\n", &first);\r
+ first.analyzing = TRUE;\r
+ }\r
+}\r
+\r
+/*\r
+ * Button procedures\r
+ */\r
+void\r
+Reset(redraw, init)\r
+ int redraw, init;\r
+{\r
+ int i;\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",\r
+ redraw, init, gameMode);\r
+ }\r
+\r
+ pausing = pauseExamInvalid = FALSE;\r
+ startedFromSetupPosition = blackPlaysFirst = FALSE;\r
+ firstMove = TRUE;\r
+ whiteFlag = blackFlag = FALSE;\r
+ userOfferedDraw = FALSE;\r
+ hintRequested = bookRequested = FALSE;\r
+ first.maybeThinking = FALSE;\r
+ second.maybeThinking = FALSE;\r
+ thinkOutput[0] = NULLCHAR;\r
+ lastHint[0] = NULLCHAR;\r
+ ClearGameInfo(&gameInfo);\r
+ gameInfo.variant = StringToVariant(appData.variant);\r
+ ics_user_moved = ics_clock_paused = FALSE;\r
+ ics_getting_history = H_FALSE;\r
+ ics_gamenum = -1;\r
+ white_holding[0] = black_holding[0] = NULLCHAR;\r
+ ClearProgramStats();\r
+ \r
+ ResetFrontEnd();\r
+ ClearHighlights();\r
+ flipView = appData.flipView;\r
+ ClearPremoveHighlights();\r
+ gotPremove = FALSE;\r
+ alarmSounded = FALSE;\r
+\r
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+ ExitAnalyzeMode();\r
+ gameMode = BeginningOfGame;\r
+ ModeHighlight();\r
+ InitPosition(redraw);\r
+ for (i = 0; i < MAX_MOVES; i++) {\r
+ if (commentList[i] != NULL) {\r
+ free(commentList[i]);\r
+ commentList[i] = NULL;\r
+ }\r
+ }\r
+ ResetClocks();\r
+ timeRemaining[0][0] = whiteTimeRemaining;\r
+ timeRemaining[1][0] = blackTimeRemaining;\r
+ if (first.pr == NULL) {\r
+ StartChessProgram(&first);\r
+ }\r
+ if (init) InitChessProgram(&first);\r
+ DisplayTitle("");\r
+ DisplayMessage("", "");\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+}\r
+\r
+void\r
+AutoPlayGameLoop()\r
+{\r
+ for (;;) {\r
+ if (!AutoPlayOneMove())\r
+ return;\r
+ if (matchMode || appData.timeDelay == 0)\r
+ continue;\r
+ if (appData.timeDelay < 0 || gameMode == AnalyzeFile)\r
+ return;\r
+ StartLoadGameTimer((long)(1000.0 * appData.timeDelay));\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+int\r
+AutoPlayOneMove()\r
+{\r
+ int fromX, fromY, toX, toY;\r
+\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);\r
+ }\r
+\r
+ if (gameMode != PlayFromGameFile)\r
+ return FALSE;\r
+\r
+ if (currentMove >= forwardMostMove) {\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+\r
+ /* [AS] Clear current move marker at the end of a game */\r
+ /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */\r
+\r
+ return FALSE;\r
+ }\r
+ \r
+ toX = moveList[currentMove][2] - 'a';\r
+ toY = moveList[currentMove][3] - ONE;\r
+\r
+ if (moveList[currentMove][1] == '@') {\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(-1, -1, toX, toY);\r
+ }\r
+ } else {\r
+ fromX = moveList[currentMove][0] - 'a';\r
+ fromY = moveList[currentMove][1] - ONE;\r
+\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */\r
+\r
+ AnimateMove(boards[currentMove], fromX, fromY, toX, toY);\r
+\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(fromX, fromY, toX, toY);\r
+ }\r
+ }\r
+ DisplayMove(currentMove);\r
+ SendMoveToProgram(currentMove++, &first);\r
+ DisplayBothClocks();\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ if (commentList[currentMove] != NULL) {\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+\r
+int\r
+LoadGameOneMove(readAhead)\r
+ ChessMove readAhead;\r
+{\r
+ int fromX = 0, fromY = 0, toX = 0, toY = 0, done;\r
+ char promoChar = NULLCHAR;\r
+ ChessMove moveType;\r
+ char move[MSG_SIZ];\r
+ char *p, *q;\r
+ \r
+ if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile && \r
+ gameMode != AnalyzeMode && gameMode != Training) {\r
+ gameFileFP = NULL;\r
+ return FALSE;\r
+ }\r
+ \r
+ yyboardindex = forwardMostMove;\r
+ if (readAhead != (ChessMove)0) {\r
+ moveType = readAhead;\r
+ } else {\r
+ if (gameFileFP == NULL)\r
+ return FALSE;\r
+ moveType = (ChessMove) yylex();\r
+ }\r
+ \r
+ done = FALSE;\r
+ switch (moveType) {\r
+ case Comment:\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+ p = yy_text;\r
+ if (*p == '{' || *p == '[' || *p == '(') {\r
+ p[strlen(p) - 1] = NULLCHAR;\r
+ p++;\r
+ }\r
+\r
+ /* append the comment but don't display it */\r
+ while (*p == '\n') p++;\r
+ AppendComment(currentMove, p);\r
+ return TRUE;\r
+\r
+ case WhiteCapturesEnPassant:\r
+ case BlackCapturesEnPassant:\r
+#ifdef FAIRY\r
+ case WhitePromotionChancellor:\r
+ case BlackPromotionChancellor:\r
+ case WhitePromotionArchbishop:\r
+ case BlackPromotionArchbishop:\r
+#endif\r
+ case WhitePromotionQueen:\r
+ case BlackPromotionQueen:\r
+ case WhitePromotionRook:\r
+ case BlackPromotionRook:\r
+ case WhitePromotionBishop:\r
+ case BlackPromotionBishop:\r
+ case WhitePromotionKnight:\r
+ case BlackPromotionKnight:\r
+ case WhitePromotionKing:\r
+ case BlackPromotionKing:\r
+ case NormalMove:\r
+ case WhiteKingSideCastle:\r
+ case WhiteQueenSideCastle:\r
+ case BlackKingSideCastle:\r
+ case BlackQueenSideCastle:\r
+ case WhiteKingSideCastleWild:\r
+ case WhiteQueenSideCastleWild:\r
+ case BlackKingSideCastleWild:\r
+ case BlackQueenSideCastleWild:\r
+ /* PUSH Fabien */\r
+ case WhiteHSideCastleFR:\r
+ case WhiteASideCastleFR:\r
+ case BlackHSideCastleFR:\r
+ case BlackASideCastleFR:\r
+ /* POP Fabien */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);\r
+ fromX = currentMoveString[0] - 'a';\r
+ fromY = currentMoveString[1] - ONE;\r
+ toX = currentMoveString[2] - 'a';\r
+ toY = currentMoveString[3] - ONE;\r
+ promoChar = currentMoveString[4];\r
+ break;\r
+\r
+ case WhiteDrop:\r
+ case BlackDrop:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);\r
+ fromX = moveType == WhiteDrop ?\r
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+ (int) CharToPiece(ToLower(currentMoveString[0]));\r
+ fromY = DROP_RANK;\r
+ toX = currentMoveString[2] - 'a';\r
+ toY = currentMoveString[3] - ONE;\r
+ break;\r
+\r
+ case WhiteWins:\r
+ case BlackWins:\r
+ case GameIsDrawn:\r
+ case GameUnfinished:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed game end: %s\n", yy_text);\r
+ p = strchr(yy_text, '{');\r
+ if (p == NULL) p = strchr(yy_text, '(');\r
+ if (p == NULL) {\r
+ p = yy_text;\r
+ if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";\r
+ } else {\r
+ q = strchr(p, *p == '{' ? '}' : ')');\r
+ if (q != NULL) *q = NULLCHAR;\r
+ p++;\r
+ }\r
+ GameEnds(moveType, p, GE_FILE);\r
+ done = TRUE;\r
+ if (cmailMsgLoaded) {\r
+ ClearHighlights();\r
+ flipView = WhiteOnMove(currentMove);\r
+ if (moveType == GameUnfinished) flipView = !flipView;\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Setting flipView to %d\n", flipView) ;\r
+ }\r
+ break;\r
+\r
+ case (ChessMove) 0: /* end of file */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parser hit end of file\n");\r
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+ EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+ case MT_NONE:\r
+ case MT_CHECK:\r
+ break;\r
+ case MT_CHECKMATE:\r
+ if (WhiteOnMove(currentMove)) {\r
+ GameEnds(BlackWins, "Black mates", GE_FILE);\r
+ } else {\r
+ GameEnds(WhiteWins, "White mates", GE_FILE);\r
+ }\r
+ break;\r
+ case MT_STALEMATE:\r
+ GameEnds(GameIsDrawn, "Stalemate", GE_FILE);\r
+ break;\r
+ }\r
+ done = TRUE;\r
+ break;\r
+\r
+ case MoveNumberOne:\r
+ if (lastLoadGameStart == GNUChessGame) {\r
+ /* GNUChessGames have numbers, but they aren't move numbers */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",\r
+ yy_text, (int) moveType);\r
+ return LoadGameOneMove((ChessMove)0); /* tail recursion */\r
+ }\r
+ /* else fall thru */\r
+\r
+ case XBoardGame:\r
+ case GNUChessGame:\r
+ case PGNTag:\r
+ /* Reached start of next game in file */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);\r
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+ EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+ case MT_NONE:\r
+ case MT_CHECK:\r
+ break;\r
+ case MT_CHECKMATE:\r
+ if (WhiteOnMove(currentMove)) {\r
+ GameEnds(BlackWins, "Black mates", GE_FILE);\r
+ } else {\r
+ GameEnds(WhiteWins, "White mates", GE_FILE);\r
+ }\r
+ break;\r
+ case MT_STALEMATE:\r
+ GameEnds(GameIsDrawn, "Stalemate", GE_FILE);\r
+ break;\r
+ }\r
+ done = TRUE;\r
+ break;\r
+\r
+ case PositionDiagram: /* should not happen; ignore */\r
+ case ElapsedTime: /* ignore */\r
+ case NAG: /* ignore */\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",\r
+ yy_text, (int) moveType);\r
+ return LoadGameOneMove((ChessMove)0); /* tail recursion */\r
+\r
+ case IllegalMove:\r
+ if (appData.testLegality) {\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);\r
+ sprintf(move, "Illegal move: %d.%s%s",\r
+ (forwardMostMove / 2) + 1,\r
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+ DisplayError(move, 0);\r
+ done = TRUE;\r
+ } else {\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed %s into IllegalMove %s\n",\r
+ yy_text, currentMoveString);\r
+ fromX = currentMoveString[0] - 'a';\r
+ fromY = currentMoveString[1] - ONE;\r
+ toX = currentMoveString[2] - 'a';\r
+ toY = currentMoveString[3] - ONE;\r
+ promoChar = currentMoveString[4];\r
+ }\r
+ break;\r
+\r
+ case AmbiguousMove:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);\r
+ sprintf(move, "Ambiguous move: %d.%s%s",\r
+ (forwardMostMove / 2) + 1,\r
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+ DisplayError(move, 0);\r
+ done = TRUE;\r
+ break;\r
+\r
+ default:\r
+ case ImpossibleMove:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);\r
+ sprintf(move, "Illegal move: %d.%s%s",\r
+ (forwardMostMove / 2) + 1,\r
+ WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+ DisplayError(move, 0);\r
+ done = TRUE;\r
+ break;\r
+ }\r
+\r
+ if (done) {\r
+ if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ DisplayBothClocks();\r
+ if (!appData.matchMode && commentList[currentMove] != NULL)\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+ (void) StopLoadGameTimer();\r
+ gameFileFP = NULL;\r
+ cmailOldMove = forwardMostMove;\r
+ return FALSE;\r
+ } else {\r
+ /* currentMoveString is set as a side-effect of yylex */\r
+ strcat(currentMoveString, "\n");\r
+ strcpy(moveList[forwardMostMove], currentMoveString);\r
+ \r
+ thinkOutput[0] = NULLCHAR;\r
+ MakeMove(fromX, fromY, toX, toY, promoChar);\r
+ currentMove = forwardMostMove;\r
+ return TRUE;\r
+ }\r
+}\r
+\r
+/* Load the nth game from the given file */\r
+int\r
+LoadGameFromFile(filename, n, title, useList)\r
+ char *filename;\r
+ int n;\r
+ char *title;\r
+ /*Boolean*/ int useList;\r
+{\r
+ FILE *f;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (strcmp(filename, "-") == 0) {\r
+ f = stdin;\r
+ title = "stdin";\r
+ } else {\r
+ f = fopen(filename, "rb");\r
+ if (f == NULL) {\r
+ sprintf(buf, "Can't open \"%s\"", filename);\r
+ DisplayError(buf, errno);\r
+ return FALSE;\r
+ }\r
+ }\r
+ if (fseek(f, 0, 0) == -1) {\r
+ /* f is not seekable; probably a pipe */\r
+ useList = FALSE;\r
+ }\r
+ if (useList && n == 0) {\r
+ int error = GameListBuild(f);\r
+ if (error) {\r
+ DisplayError("Cannot build game list", error);\r
+ } else if (!ListEmpty(&gameList) &&\r
+ ((ListGame *) gameList.tailPred)->number > 1) {\r
+ GameListPopUp(f, title);\r
+ return TRUE;\r
+ }\r
+ GameListDestroy();\r
+ n = 1;\r
+ }\r
+ if (n == 0) n = 1;\r
+ return LoadGame(f, n, title, FALSE);\r
+}\r
+\r
+\r
+void\r
+MakeRegisteredMove()\r
+{\r
+ int fromX, fromY, toX, toY;\r
+ char promoChar;\r
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+ switch (cmailMoveType[lastLoadGameNumber - 1]) {\r
+ case CMAIL_MOVE:\r
+ case CMAIL_DRAW:\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Restoring %s for game %d\n",\r
+ cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);\r
+ \r
+ thinkOutput[0] = NULLCHAR;\r
+ strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);\r
+ fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';\r
+ fromY = cmailMove[lastLoadGameNumber - 1][1] - ONE;\r
+ toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';\r
+ toY = cmailMove[lastLoadGameNumber - 1][3] - ONE;\r
+ promoChar = cmailMove[lastLoadGameNumber - 1][4];\r
+ MakeMove(fromX, fromY, toX, toY, promoChar);\r
+ ShowMove(fromX, fromY, toX, toY);\r
+ \r
+ switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+ EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+ case MT_NONE:\r
+ case MT_CHECK:\r
+ break;\r
+ \r
+ case MT_CHECKMATE:\r
+ if (WhiteOnMove(currentMove)) {\r
+ GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
+ } else {\r
+ GameEnds(WhiteWins, "White mates", GE_PLAYER);\r
+ }\r
+ break;\r
+ \r
+ case MT_STALEMATE:\r
+ GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);\r
+ break;\r
+ }\r
+\r
+ break;\r
+ \r
+ case CMAIL_RESIGN:\r
+ if (WhiteOnMove(currentMove)) {\r
+ GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+ } else {\r
+ GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+ }\r
+ break;\r
+ \r
+ case CMAIL_ACCEPT:\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+ break;\r
+ \r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ return;\r
+}\r
+\r
+/* Wrapper around LoadGame for use when a Cmail message is loaded */\r
+int\r
+CmailLoadGame(f, gameNumber, title, useList)\r
+ FILE *f;\r
+ int gameNumber;\r
+ char *title;\r
+ int useList;\r
+{\r
+ int retVal;\r
+\r
+ if (gameNumber > nCmailGames) {\r
+ DisplayError("No more games in this message", 0);\r
+ return FALSE;\r
+ }\r
+ if (f == lastLoadGameFP) {\r
+ int offset = gameNumber - lastLoadGameNumber;\r
+ if (offset == 0) {\r
+ cmailMsg[0] = NULLCHAR;\r
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+ cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;\r
+ nCmailMovesRegistered--;\r
+ }\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+ if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {\r
+ cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;\r
+ }\r
+ } else {\r
+ if (! RegisterMove()) return FALSE;\r
+ }\r
+ }\r
+\r
+ retVal = LoadGame(f, gameNumber, title, useList);\r
+\r
+ /* Make move registered during previous look at this game, if any */\r
+ MakeRegisteredMove();\r
+\r
+ if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {\r
+ commentList[currentMove]\r
+ = StrSave(cmailCommentList[lastLoadGameNumber - 1]);\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+\r
+ return retVal;\r
+}\r
+\r
+/* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */\r
+int\r
+ReloadGame(offset)\r
+ int offset;\r
+{\r
+ int gameNumber = lastLoadGameNumber + offset;\r
+ if (lastLoadGameFP == NULL) {\r
+ DisplayError("No game has been loaded yet", 0);\r
+ return FALSE;\r
+ }\r
+ if (gameNumber <= 0) {\r
+ DisplayError("Can't back up any further", 0);\r
+ return FALSE;\r
+ }\r
+ if (cmailMsgLoaded) {\r
+ return CmailLoadGame(lastLoadGameFP, gameNumber,\r
+ lastLoadGameTitle, lastLoadGameUseList);\r
+ } else {\r
+ return LoadGame(lastLoadGameFP, gameNumber,\r
+ lastLoadGameTitle, lastLoadGameUseList);\r
+ }\r
+}\r
+\r
+\r
+\r
+/* Load the nth game from open file f */\r
+int\r
+LoadGame(f, gameNumber, title, useList)\r
+ FILE *f;\r
+ int gameNumber;\r
+ char *title;\r
+ int useList;\r
+{\r
+ ChessMove cm;\r
+ char buf[MSG_SIZ];\r
+ int gn = gameNumber;\r
+ ListGame *lg = NULL;\r
+ int numPGNTags = 0;\r
+ int err;\r
+ GameMode oldGameMode;\r
+\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);\r
+\r
+ if (gameMode == Training )\r
+ SetTrainingModeOff();\r
+\r
+ oldGameMode = gameMode;\r
+ if (gameMode != BeginningOfGame) {\r
+ Reset(FALSE, TRUE);\r
+ }\r
+\r
+ gameFileFP = f;\r
+ if (lastLoadGameFP != NULL && lastLoadGameFP != f) {\r
+ fclose(lastLoadGameFP);\r
+ }\r
+\r
+ if (useList) {\r
+ lg = (ListGame *) ListElem(&gameList, gameNumber-1);\r
+ \r
+ if (lg) {\r
+ fseek(f, lg->offset, 0);\r
+ GameListHighlight(gameNumber);\r
+ gn = 1;\r
+ }\r
+ else {\r
+ DisplayError("Game number out of range", 0);\r
+ return FALSE;\r
+ }\r
+ } else {\r
+ GameListDestroy();\r
+ if (fseek(f, 0, 0) == -1) {\r
+ if (f == lastLoadGameFP ?\r
+ gameNumber == lastLoadGameNumber + 1 :\r
+ gameNumber == 1) {\r
+ gn = 1;\r
+ } else {\r
+ DisplayError("Can't seek on game file", 0);\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+ lastLoadGameFP = f;\r
+ lastLoadGameNumber = gameNumber;\r
+ strcpy(lastLoadGameTitle, title);\r
+ lastLoadGameUseList = useList;\r
+\r
+ yynewfile(f);\r
+\r
+\r
+ if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
+ sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
+ lg->gameInfo.black);\r
+ DisplayTitle(buf);\r
+ } else if (*title != NULLCHAR) {\r
+ if (gameNumber > 1) {\r
+ sprintf(buf, "%s %d", title, gameNumber);\r
+ DisplayTitle(buf);\r
+ } else {\r
+ DisplayTitle(title);\r
+ }\r
+ }\r
+\r
+ if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {\r
+ gameMode = PlayFromGameFile;\r
+ ModeHighlight();\r
+ }\r
+\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ CopyBoard(boards[0], initialPosition);\r
+ StopClocks();\r
+\r
+ /*\r
+ * Skip the first gn-1 games in the file.\r
+ * Also skip over anything that precedes an identifiable \r
+ * start of game marker, to avoid being confused by \r
+ * garbage at the start of the file. Currently \r
+ * recognized start of game markers are the move number "1",\r
+ * the pattern "gnuchess .* game", the pattern\r
+ * "^[#;%] [^ ]* game file", and a PGN tag block. \r
+ * A game that starts with one of the latter two patterns\r
+ * will also have a move number 1, possibly\r
+ * following a position diagram.\r
+ * 5-4-02: Let's try being more lenient and allowing a game to\r
+ * start with an unnumbered move. Does that break anything?\r
+ */\r
+ cm = lastLoadGameStart = (ChessMove) 0;\r
+ while (gn > 0) {\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+ switch (cm) {\r
+ case (ChessMove) 0:\r
+ if (cmailMsgLoaded) {\r
+ nCmailGames = CMAIL_MAX_GAMES - gn;\r
+ } else {\r
+ Reset(TRUE, TRUE);\r
+ DisplayError("Game not found in file", 0);\r
+ }\r
+ return FALSE;\r
+\r
+ case GNUChessGame:\r
+ case XBoardGame:\r
+ gn--;\r
+ lastLoadGameStart = cm;\r
+ break;\r
+ \r
+ case MoveNumberOne:\r
+ switch (lastLoadGameStart) {\r
+ case GNUChessGame:\r
+ case XBoardGame:\r
+ case PGNTag:\r
+ break;\r
+ case MoveNumberOne:\r
+ case (ChessMove) 0:\r
+ gn--; /* count this game */\r
+ lastLoadGameStart = cm;\r
+ break;\r
+ default:\r
+ /* impossible */\r
+ break;\r
+ }\r
+ break;\r
+\r
+ case PGNTag:\r
+ switch (lastLoadGameStart) {\r
+ case GNUChessGame:\r
+ case PGNTag:\r
+ case MoveNumberOne:\r
+ case (ChessMove) 0:\r
+ gn--; /* count this game */\r
+ lastLoadGameStart = cm;\r
+ break;\r
+ case XBoardGame:\r
+ lastLoadGameStart = cm; /* game counted already */\r
+ break;\r
+ default:\r
+ /* impossible */\r
+ break;\r
+ }\r
+ if (gn > 0) {\r
+ do {\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+ } while (cm == PGNTag || cm == Comment);\r
+ }\r
+ break;\r
+\r
+ case WhiteWins:\r
+ case BlackWins:\r
+ case GameIsDrawn:\r
+ if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {\r
+ if ( cmailResult[CMAIL_MAX_GAMES - gn - 1]\r
+ != CMAIL_OLD_RESULT) {\r
+ nCmailResults ++ ;\r
+ cmailResult[ CMAIL_MAX_GAMES\r
+ - gn - 1] = CMAIL_OLD_RESULT;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case NormalMove:\r
+ /* Only a NormalMove can be at the start of a game\r
+ * without a position diagram. */\r
+ if (lastLoadGameStart == (ChessMove) 0) {\r
+ gn--;\r
+ lastLoadGameStart = MoveNumberOne;\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ \r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);\r
+\r
+ if (cm == XBoardGame) {\r
+ /* Skip any header junk before position diagram and/or move 1 */\r
+ for (;;) {\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+\r
+ if (cm == (ChessMove) 0 ||\r
+ cm == GNUChessGame || cm == XBoardGame) {\r
+ /* Empty game; pretend end-of-file and handle later */\r
+ cm = (ChessMove) 0;\r
+ break;\r
+ }\r
+\r
+ if (cm == MoveNumberOne || cm == PositionDiagram ||\r
+ cm == PGNTag || cm == Comment)\r
+ break;\r
+ }\r
+ } else if (cm == GNUChessGame) {\r
+ if (gameInfo.event != NULL) {\r
+ free(gameInfo.event);\r
+ }\r
+ gameInfo.event = StrSave(yy_text);\r
+ } \r
+\r
+ startedFromSetupPosition = FALSE;\r
+ while (cm == PGNTag) {\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);\r
+ err = ParsePGNTag(yy_text, &gameInfo);\r
+ if (!err) numPGNTags++;\r
+\r
+ if (gameInfo.fen != NULL) {\r
+ Board initial_position;\r
+ startedFromSetupPosition = TRUE;\r
+ if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {\r
+ Reset(TRUE, TRUE);\r
+ DisplayError("Bad FEN position in file", 0);\r
+ return FALSE;\r
+ }\r
+ CopyBoard(boards[0], initial_position);\r
+ /* [HGM] copy FEN attributes as well */\r
+ { int i;\r
+ initialRulePlies = FENrulePlies;\r
+ epStatus[0] = FENepStatus;\r
+ for( i=0; i< nrCastlingRights; i++ )\r
+ castlingRights[0][i] = FENcastlingRights[i];\r
+ }\r
+ if (blackPlaysFirst) {\r
+ currentMove = forwardMostMove = backwardMostMove = 1;\r
+ CopyBoard(boards[1], initial_position);\r
+ strcpy(moveList[0], "");\r
+ strcpy(parseList[0], "");\r
+ timeRemaining[0][1] = whiteTimeRemaining;\r
+ timeRemaining[1][1] = blackTimeRemaining;\r
+ if (commentList[0] != NULL) {\r
+ commentList[1] = commentList[0];\r
+ commentList[0] = NULL;\r
+ }\r
+ } else {\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ }\r
+ yyboardindex = forwardMostMove;\r
+ free(gameInfo.fen);\r
+ gameInfo.fen = NULL;\r
+ }\r
+\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+\r
+ /* Handle comments interspersed among the tags */\r
+ while (cm == Comment) {\r
+ char *p;\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+ p = yy_text;\r
+ if (*p == '{' || *p == '[' || *p == '(') {\r
+ p[strlen(p) - 1] = NULLCHAR;\r
+ p++;\r
+ }\r
+ while (*p == '\n') p++;\r
+ AppendComment(currentMove, p);\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+ }\r
+ }\r
+\r
+ /* don't rely on existence of Event tag since if game was\r
+ * pasted from clipboard the Event tag may not exist\r
+ */\r
+ if (numPGNTags > 0){\r
+ char *tags;\r
+ if (gameInfo.variant == VariantNormal) {\r
+ gameInfo.variant = StringToVariant(gameInfo.event);\r
+ }\r
+ if (!matchMode) {\r
+ if( appData.autoDisplayTags ) {\r
+ tags = PGNTags(&gameInfo);\r
+ TagsPopUp(tags, CmailMsg());\r
+ free(tags);\r
+ }\r
+ }\r
+ } else {\r
+ /* Make something up, but don't display it now */\r
+ SetGameInfo();\r
+ TagsPopDown();\r
+ }\r
+\r
+ if (cm == PositionDiagram) {\r
+ int i, j;\r
+ char *p;\r
+ Board initial_position;\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);\r
+\r
+ if (!startedFromSetupPosition) {\r
+ p = yy_text;\r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--)\r
+ for (j = 0; j < BOARD_WIDTH; p++)\r
+ switch (*p) {\r
+ case '[':\r
+ case '-':\r
+ case ' ':\r
+ case '\t':\r
+ case '\n':\r
+ case '\r':\r
+ break;\r
+ default:\r
+ initial_position[i][j++] = CharToPiece(*p);\r
+ break;\r
+ }\r
+ while (*p == ' ' || *p == '\t' ||\r
+ *p == '\n' || *p == '\r') p++;\r
+ \r
+ if (strncmp(p, "black", strlen("black"))==0)\r
+ blackPlaysFirst = TRUE;\r
+ else\r
+ blackPlaysFirst = FALSE;\r
+ startedFromSetupPosition = TRUE;\r
+ \r
+ CopyBoard(boards[0], initial_position);\r
+ if (blackPlaysFirst) {\r
+ currentMove = forwardMostMove = backwardMostMove = 1;\r
+ CopyBoard(boards[1], initial_position);\r
+ strcpy(moveList[0], "");\r
+ strcpy(parseList[0], "");\r
+ timeRemaining[0][1] = whiteTimeRemaining;\r
+ timeRemaining[1][1] = blackTimeRemaining;\r
+ if (commentList[0] != NULL) {\r
+ commentList[1] = commentList[0];\r
+ commentList[0] = NULL;\r
+ }\r
+ } else {\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ }\r
+ }\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+ }\r
+\r
+ if (first.pr == NoProc) {\r
+ StartChessProgram(&first);\r
+ }\r
+ InitChessProgram(&first);\r
+ SendToProgram("force\n", &first);\r
+ if (startedFromSetupPosition) {\r
+ SendBoard(&first, forwardMostMove);\r
+ DisplayBothClocks();\r
+ } \r
+\r
+ while (cm == Comment) {\r
+ char *p;\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+ p = yy_text;\r
+ if (*p == '{' || *p == '[' || *p == '(') {\r
+ p[strlen(p) - 1] = NULLCHAR;\r
+ p++;\r
+ }\r
+ while (*p == '\n') p++;\r
+ AppendComment(currentMove, p);\r
+ yyboardindex = forwardMostMove;\r
+ cm = (ChessMove) yylex();\r
+ }\r
+\r
+ if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||\r
+ cm == WhiteWins || cm == BlackWins ||\r
+ cm == GameIsDrawn || cm == GameUnfinished) {\r
+ DisplayMessage("", "No moves in game");\r
+ if (cmailMsgLoaded) {\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Setting flipView to %d.\n", FALSE);\r
+ ClearHighlights();\r
+ flipView = FALSE;\r
+ }\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ DisplayBothClocks();\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ gameFileFP = NULL;\r
+ cmailOldMove = 0;\r
+ return TRUE;\r
+ }\r
+\r
+ if (commentList[currentMove] != NULL) {\r
+ if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+ }\r
+ if (!matchMode && appData.timeDelay != 0) \r
+ DrawPosition(FALSE, boards[currentMove]);\r
+\r
+ if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {\r
+ programStats.ok_to_send = 1;\r
+ }\r
+\r
+ /* if the first token after the PGN tags is a move\r
+ * and not move number 1, retrieve it from the parser \r
+ */\r
+ if (cm != MoveNumberOne)\r
+ LoadGameOneMove(cm);\r
+\r
+ /* load the remaining moves from the file */\r
+ while (LoadGameOneMove((ChessMove)0)) {\r
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+ }\r
+\r
+ /* rewind to the start of the game */\r
+ currentMove = backwardMostMove;\r
+\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+\r
+ if (oldGameMode == AnalyzeFile ||\r
+ oldGameMode == AnalyzeMode) {\r
+ AnalyzeFileEvent();\r
+ }\r
+\r
+ if (matchMode || appData.timeDelay == 0) {\r
+ ToEndEvent();\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ } else if (appData.timeDelay > 0) {\r
+ AutoPlayGameLoop();\r
+ }\r
+\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);\r
+ return TRUE;\r
+}\r
+\r
+/* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */\r
+int\r
+ReloadPosition(offset)\r
+ int offset;\r
+{\r
+ int positionNumber = lastLoadPositionNumber + offset;\r
+ if (lastLoadPositionFP == NULL) {\r
+ DisplayError("No position has been loaded yet", 0);\r
+ return FALSE;\r
+ }\r
+ if (positionNumber <= 0) {\r
+ DisplayError("Can't back up any further", 0);\r
+ return FALSE;\r
+ }\r
+ return LoadPosition(lastLoadPositionFP, positionNumber,\r
+ lastLoadPositionTitle);\r
+}\r
+\r
+/* Load the nth position from the given file */\r
+int\r
+LoadPositionFromFile(filename, n, title)\r
+ char *filename;\r
+ int n;\r
+ char *title;\r
+{\r
+ FILE *f;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (strcmp(filename, "-") == 0) {\r
+ return LoadPosition(stdin, n, "stdin");\r
+ } else {\r
+ f = fopen(filename, "rb");\r
+ if (f == NULL) {\r
+ sprintf(buf, "Can't open \"%s\"", filename);\r
+ DisplayError(buf, errno);\r
+ return FALSE;\r
+ } else {\r
+ return LoadPosition(f, n, title);\r
+ }\r
+ }\r
+}\r
+\r
+/* Load the nth position from the given open file, and close it */\r
+int\r
+LoadPosition(f, positionNumber, title)\r
+ FILE *f;\r
+ int positionNumber;\r
+ char *title;\r
+{\r
+ char *p, line[MSG_SIZ];\r
+ Board initial_position;\r
+ int i, j, fenMode, pn;\r
+ \r
+ if (gameMode == Training )\r
+ SetTrainingModeOff();\r
+\r
+ if (gameMode != BeginningOfGame) {\r
+ Reset(FALSE, TRUE);\r
+ }\r
+ if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {\r
+ fclose(lastLoadPositionFP);\r
+ }\r
+ if (positionNumber == 0) positionNumber = 1;\r
+ lastLoadPositionFP = f;\r
+ lastLoadPositionNumber = positionNumber;\r
+ strcpy(lastLoadPositionTitle, title);\r
+ if (first.pr == NoProc) {\r
+ StartChessProgram(&first);\r
+ InitChessProgram(&first);\r
+ } \r
+ pn = positionNumber;\r
+ if (positionNumber < 0) {\r
+ /* Negative position number means to seek to that byte offset */\r
+ if (fseek(f, -positionNumber, 0) == -1) {\r
+ DisplayError("Can't seek on position file", 0);\r
+ return FALSE;\r
+ };\r
+ pn = 1;\r
+ } else {\r
+ if (fseek(f, 0, 0) == -1) {\r
+ if (f == lastLoadPositionFP ?\r
+ positionNumber == lastLoadPositionNumber + 1 :\r
+ positionNumber == 1) {\r
+ pn = 1;\r
+ } else {\r
+ DisplayError("Can't seek on position file", 0);\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+ /* See if this file is FEN or old-style xboard */\r
+ if (fgets(line, MSG_SIZ, f) == NULL) {\r
+ DisplayError("Position not found in file", 0);\r
+ return FALSE;\r
+ }\r
+ switch (line[0]) {\r
+ case '#': case 'x':\r
+ default:\r
+ fenMode = FALSE;\r
+ break;\r
+ case 'p': case 'n': case 'b': case 'r': case 'q': case 'k':\r
+ case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':\r
+ case '1': case '2': case '3': case '4': case '5': case '6':\r
+ case '7': case '8': case '9':\r
+#ifdef FAIRY\r
+ case 'H': case 'A': case 'M': case 'h': case 'a': case 'm':\r
+ case 'E': case 'F': case 'G': case 'e': case 'f': case 'g':\r
+ case 'C': case 'W': case 'c': case 'w': \r
+#endif\r
+ fenMode = TRUE;\r
+ break;\r
+ }\r
+\r
+ if (pn >= 2) {\r
+ if (fenMode || line[0] == '#') pn--;\r
+ while (pn > 0) {\r
+ /* skip postions before number pn */\r
+ if (fgets(line, MSG_SIZ, f) == NULL) {\r
+ Reset(TRUE, TRUE);\r
+ DisplayError("Position not found in file", 0);\r
+ return FALSE;\r
+ }\r
+ if (fenMode || line[0] == '#') pn--;\r
+ }\r
+ }\r
+\r
+ if (fenMode) {\r
+ if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {\r
+ DisplayError("Bad FEN position in file", 0);\r
+ return FALSE;\r
+ }\r
+ } else {\r
+ (void) fgets(line, MSG_SIZ, f);\r
+ (void) fgets(line, MSG_SIZ, f);\r
+ \r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ (void) fgets(line, MSG_SIZ, f);\r
+ for (p = line, j = 0; j < BOARD_WIDTH; p++) {\r
+ if (*p == ' ')\r
+ continue;\r
+ initial_position[i][j++] = CharToPiece(*p);\r
+ }\r
+ }\r
+ \r
+ blackPlaysFirst = FALSE;\r
+ if (!feof(f)) {\r
+ (void) fgets(line, MSG_SIZ, f);\r
+ if (strncmp(line, "black", strlen("black"))==0)\r
+ blackPlaysFirst = TRUE;\r
+ }\r
+ }\r
+ startedFromSetupPosition = TRUE;\r
+ \r
+ SendToProgram("force\n", &first);\r
+ CopyBoard(boards[0], initial_position);\r
+ /* [HGM] copy FEN attributes as well */\r
+ { int i;\r
+ initialRulePlies = FENrulePlies;\r
+ epStatus[0] = FENepStatus;\r
+ for( i=0; i< nrCastlingRights; i++ )\r
+ castlingRights[0][i] = FENcastlingRights[i];\r
+ }\r
+ if (blackPlaysFirst) {\r
+ currentMove = forwardMostMove = backwardMostMove = 1;\r
+ strcpy(moveList[0], "");\r
+ strcpy(parseList[0], "");\r
+ CopyBoard(boards[1], initial_position);\r
+ DisplayMessage("", "Black to play");\r
+ } else {\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ DisplayMessage("", "White to play");\r
+ }\r
+ SendBoard(&first, forwardMostMove);\r
+\r
+ if (positionNumber > 1) {\r
+ sprintf(line, "%s %d", title, positionNumber);\r
+ DisplayTitle(line);\r
+ } else {\r
+ DisplayTitle(title);\r
+ }\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ ResetClocks();\r
+ timeRemaining[0][1] = whiteTimeRemaining;\r
+ timeRemaining[1][1] = blackTimeRemaining;\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ \r
+ return TRUE;\r
+}\r
+\r
+\r
+void\r
+CopyPlayerNameIntoFileName(dest, src)\r
+ char **dest, *src;\r
+{\r
+ while (*src != NULLCHAR && *src != ',') {\r
+ if (*src == ' ') {\r
+ *(*dest)++ = '_';\r
+ src++;\r
+ } else {\r
+ *(*dest)++ = *src++;\r
+ }\r
+ }\r
+}\r
+\r
+char *DefaultFileName(ext)\r
+ char *ext;\r
+{\r
+ static char def[MSG_SIZ];\r
+ char *p;\r
+\r
+ if (gameInfo.white != NULL && gameInfo.white[0] != '-') {\r
+ p = def;\r
+ CopyPlayerNameIntoFileName(&p, gameInfo.white);\r
+ *p++ = '-';\r
+ CopyPlayerNameIntoFileName(&p, gameInfo.black);\r
+ *p++ = '.';\r
+ strcpy(p, ext);\r
+ } else {\r
+ def[0] = NULLCHAR;\r
+ }\r
+ return def;\r
+}\r
+\r
+/* Save the current game to the given file */\r
+int\r
+SaveGameToFile(filename, append)\r
+ char *filename;\r
+ int append;\r
+{\r
+ FILE *f;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (strcmp(filename, "-") == 0) {\r
+ return SaveGame(stdout, 0, NULL);\r
+ } else {\r
+ f = fopen(filename, append ? "a" : "w");\r
+ if (f == NULL) {\r
+ sprintf(buf, "Can't open \"%s\"", filename);\r
+ DisplayError(buf, errno);\r
+ return FALSE;\r
+ } else {\r
+ return SaveGame(f, 0, NULL);\r
+ }\r
+ }\r
+}\r
+\r
+char *\r
+SavePart(str)\r
+ char *str;\r
+{\r
+ static char buf[MSG_SIZ];\r
+ char *p;\r
+ \r
+ p = strchr(str, ' ');\r
+ if (p == NULL) return str;\r
+ strncpy(buf, str, p - str);\r
+ buf[p - str] = NULLCHAR;\r
+ return buf;\r
+}\r
+\r
+#define PGN_MAX_LINE 75\r
+\r
+#define PGN_SIDE_WHITE 0\r
+#define PGN_SIDE_BLACK 1\r
+\r
+/* [AS] */\r
+static int FindFirstMoveOutOfBook( int side )\r
+{\r
+ int result = -1;\r
+\r
+ if( backwardMostMove == 0 && ! startedFromSetupPosition) {\r
+ int index = backwardMostMove;\r
+ int has_book_hit = 0;\r
+\r
+ if( (index % 2) != side ) {\r
+ index++;\r
+ }\r
+\r
+ while( index < forwardMostMove ) {\r
+ /* Check to see if engine is in book */\r
+ int depth = pvInfoList[index].depth;\r
+ int score = pvInfoList[index].score;\r
+ int in_book = 0;\r
+\r
+ if( depth <= 2 ) {\r
+ in_book = 1;\r
+ }\r
+ else if( score == 0 && depth == 63 ) {\r
+ in_book = 1; /* Zappa */\r
+ }\r
+ else if( score == 2 && depth == 99 ) {\r
+ in_book = 1; /* Abrok */\r
+ }\r
+\r
+ has_book_hit += in_book;\r
+\r
+ if( ! in_book ) {\r
+ result = index;\r
+\r
+ break;\r
+ }\r
+\r
+ index += 2;\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/* [AS] */\r
+void GetOutOfBookInfo( char * buf )\r
+{\r
+ int oob[2];\r
+ int i;\r
+ int offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+\r
+ oob[0] = FindFirstMoveOutOfBook( PGN_SIDE_WHITE );\r
+ oob[1] = FindFirstMoveOutOfBook( PGN_SIDE_BLACK );\r
+\r
+ *buf = '\0';\r
+\r
+ if( oob[0] >= 0 || oob[1] >= 0 ) {\r
+ for( i=0; i<2; i++ ) {\r
+ int idx = oob[i];\r
+\r
+ if( idx >= 0 ) {\r
+ if( i > 0 && oob[0] >= 0 ) {\r
+ strcat( buf, " " );\r
+ }\r
+\r
+ sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );\r
+ sprintf( buf+strlen(buf), "%s%.2f", \r
+ pvInfoList[idx].score >= 0 ? "+" : "",\r
+ pvInfoList[idx].score / 100.0 );\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/* Save game in PGN style and close the file */\r
+int\r
+SaveGamePGN(f)\r
+ FILE *f;\r
+{\r
+ int i, offset, linelen, newblock;\r
+ time_t tm;\r
+ char *movetext;\r
+ char numtext[32];\r
+ int movelen, numlen, blank;\r
+ char move_buffer[100]; /* [AS] Buffer for move+PV info */\r
+\r
+ offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+ \r
+ tm = time((time_t *) NULL);\r
+ \r
+ PrintPGNTags(f, &gameInfo);\r
+ \r
+ if (backwardMostMove > 0 || startedFromSetupPosition) {\r
+ char *fen = PositionToFEN(backwardMostMove, 1);\r
+ fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);\r
+ fprintf(f, "\n{--------------\n");\r
+ PrintPosition(f, backwardMostMove);\r
+ fprintf(f, "--------------}\n");\r
+ free(fen);\r
+ }\r
+ else {\r
+ /* [AS] Out of book annotation */\r
+ if( appData.saveOutOfBookInfo ) {\r
+ char buf[64];\r
+\r
+ GetOutOfBookInfo( buf );\r
+\r
+ if( buf[0] != '\0' ) {\r
+ fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf ); \r
+ }\r
+ }\r
+\r
+ fprintf(f, "\n");\r
+ }\r
+\r
+ i = backwardMostMove;\r
+ linelen = 0;\r
+ newblock = TRUE;\r
+\r
+ while (i < forwardMostMove) {\r
+ /* Print comments preceding this move */\r
+ if (commentList[i] != NULL) {\r
+ if (linelen > 0) fprintf(f, "\n");\r
+ fprintf(f, "{\n%s}\n", commentList[i]);\r
+ linelen = 0;\r
+ newblock = TRUE;\r
+ }\r
+\r
+ /* Format move number */\r
+ if ((i % 2) == 0) {\r
+ sprintf(numtext, "%d.", (i - offset)/2 + 1);\r
+ } else {\r
+ if (newblock) {\r
+ sprintf(numtext, "%d...", (i - offset)/2 + 1);\r
+ } else {\r
+ numtext[0] = NULLCHAR;\r
+ }\r
+ }\r
+ numlen = strlen(numtext);\r
+ newblock = FALSE;\r
+\r
+ /* Print move number */\r
+ blank = linelen > 0 && numlen > 0;\r
+ if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {\r
+ fprintf(f, "\n");\r
+ linelen = 0;\r
+ blank = 0;\r
+ }\r
+ if (blank) {\r
+ fprintf(f, " ");\r
+ linelen++;\r
+ }\r
+ fprintf(f, numtext);\r
+ linelen += numlen;\r
+\r
+ /* Get move */\r
+ movetext = SavePart(parseList[i]);\r
+\r
+ /* [AS] Add PV info if present */\r
+ if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {\r
+ /* [HGM] add time */\r
+ char buf[MSG_SIZ]; int seconds = 0;\r
+\r
+ if(i >= backwardMostMove) {\r
+ /* take the time that changed */\r
+ seconds = timeRemaining[0][i] - timeRemaining[0][i+1];\r
+ if(seconds <= 0)\r
+ seconds = timeRemaining[1][i] - timeRemaining[1][i+1];\r
+ }\r
+ seconds /= 1000;\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
+ timeRemaining[0][i+1], timeRemaining[0][i],\r
+ timeRemaining[1][i+1], timeRemaining[1][i], seconds\r
+ );\r
+ }\r
+\r
+ if( seconds < 0 ) buf[0] = 0; else\r
+ if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0);\r
+ else sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0);\r
+\r
+ sprintf( move_buffer, "%s {%s%.2f/%d%s}", \r
+ movetext, \r
+ pvInfoList[i].score >= 0 ? "+" : "",\r
+ pvInfoList[i].score / 100.0,\r
+ pvInfoList[i].depth,\r
+ buf );\r
+ movetext = move_buffer;\r
+ }\r
+\r
+ movelen = strlen(movetext);\r
+\r
+ /* Print move */\r
+ blank = linelen > 0 && movelen > 0;\r
+ if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
+ fprintf(f, "\n");\r
+ linelen = 0;\r
+ blank = 0;\r
+ }\r
+ if (blank) {\r
+ fprintf(f, " ");\r
+ linelen++;\r
+ }\r
+ fprintf(f, movetext);\r
+ linelen += movelen;\r
+\r
+ i++;\r
+ }\r
+ \r
+ /* Start a new line */\r
+ if (linelen > 0) fprintf(f, "\n");\r
+\r
+ /* Print comments after last move */\r
+ if (commentList[i] != NULL) {\r
+ fprintf(f, "{\n%s}\n", commentList[i]);\r
+ }\r
+\r
+ /* Print result */\r
+ if (gameInfo.resultDetails != NULL &&\r
+ gameInfo.resultDetails[0] != NULLCHAR) {\r
+ fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,\r
+ PGNResult(gameInfo.result));\r
+ } else {\r
+ fprintf(f, "%s\n\n", PGNResult(gameInfo.result));\r
+ }\r
+\r
+ fclose(f);\r
+ return TRUE;\r
+}\r
+\r
+/* Save game in old style and close the file */\r
+int\r
+SaveGameOldStyle(f)\r
+ FILE *f;\r
+{\r
+ int i, offset;\r
+ time_t tm;\r
+ \r
+ tm = time((time_t *) NULL);\r
+ \r
+ fprintf(f, "# %s game file -- %s", programName, ctime(&tm));\r
+ PrintOpponents(f);\r
+ \r
+ if (backwardMostMove > 0 || startedFromSetupPosition) {\r
+ fprintf(f, "\n[--------------\n");\r
+ PrintPosition(f, backwardMostMove);\r
+ fprintf(f, "--------------]\n");\r
+ } else {\r
+ fprintf(f, "\n");\r
+ }\r
+\r
+ i = backwardMostMove;\r
+ offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+\r
+ while (i < forwardMostMove) {\r
+ if (commentList[i] != NULL) {\r
+ fprintf(f, "[%s]\n", commentList[i]);\r
+ }\r
+\r
+ if ((i % 2) == 1) {\r
+ fprintf(f, "%d. ... %s\n", (i - offset)/2 + 1, parseList[i]);\r
+ i++;\r
+ } else {\r
+ fprintf(f, "%d. %s ", (i - offset)/2 + 1, parseList[i]);\r
+ i++;\r
+ if (commentList[i] != NULL) {\r
+ fprintf(f, "\n");\r
+ continue;\r
+ }\r
+ if (i >= forwardMostMove) {\r
+ fprintf(f, "\n");\r
+ break;\r
+ }\r
+ fprintf(f, "%s\n", parseList[i]);\r
+ i++;\r
+ }\r
+ }\r
+ \r
+ if (commentList[i] != NULL) {\r
+ fprintf(f, "[%s]\n", commentList[i]);\r
+ }\r
+\r
+ /* This isn't really the old style, but it's close enough */\r
+ if (gameInfo.resultDetails != NULL &&\r
+ gameInfo.resultDetails[0] != NULLCHAR) {\r
+ fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),\r
+ gameInfo.resultDetails);\r
+ } else {\r
+ fprintf(f, "%s\n\n", PGNResult(gameInfo.result));\r
+ }\r
+\r
+ fclose(f);\r
+ return TRUE;\r
+}\r
+\r
+/* Save the current game to open file f and close the file */\r
+int\r
+SaveGame(f, dummy, dummy2)\r
+ FILE *f;\r
+ int dummy;\r
+ char *dummy2;\r
+{\r
+ if (gameMode == EditPosition) EditPositionDone();\r
+ if (appData.oldSaveStyle)\r
+ return SaveGameOldStyle(f);\r
+ else\r
+ return SaveGamePGN(f);\r
+}\r
+\r
+/* Save the current position to the given file */\r
+int\r
+SavePositionToFile(filename)\r
+ char *filename;\r
+{\r
+ FILE *f;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (strcmp(filename, "-") == 0) {\r
+ return SavePosition(stdout, 0, NULL);\r
+ } else {\r
+ f = fopen(filename, "a");\r
+ if (f == NULL) {\r
+ sprintf(buf, "Can't open \"%s\"", filename);\r
+ DisplayError(buf, errno);\r
+ return FALSE;\r
+ } else {\r
+ SavePosition(f, 0, NULL);\r
+ return TRUE;\r
+ }\r
+ }\r
+}\r
+\r
+/* Save the current position to the given open file and close the file */\r
+int\r
+SavePosition(f, dummy, dummy2)\r
+ FILE *f;\r
+ int dummy;\r
+ char *dummy2;\r
+{\r
+ time_t tm;\r
+ char *fen;\r
+ \r
+ if (appData.oldSaveStyle) {\r
+ tm = time((time_t *) NULL);\r
+ \r
+ fprintf(f, "# %s position file -- %s", programName, ctime(&tm));\r
+ PrintOpponents(f);\r
+ fprintf(f, "[--------------\n");\r
+ PrintPosition(f, currentMove);\r
+ fprintf(f, "--------------]\n");\r
+ } else {\r
+ fen = PositionToFEN(currentMove, 1);\r
+ fprintf(f, "%s\n", fen);\r
+ free(fen);\r
+ }\r
+ fclose(f);\r
+ return TRUE;\r
+}\r
+\r
+void\r
+ReloadCmailMsgEvent(unregister)\r
+ int unregister;\r
+{\r
+#if !WIN32\r
+ static char *inFilename = NULL;\r
+ static char *outFilename;\r
+ int i;\r
+ struct stat inbuf, outbuf;\r
+ int status;\r
+ \r
+ /* Any registered moves are unregistered if unregister is set, */\r
+ /* i.e. invoked by the signal handler */\r
+ if (unregister) {\r
+ for (i = 0; i < CMAIL_MAX_GAMES; i ++) {\r
+ cmailMoveRegistered[i] = FALSE;\r
+ if (cmailCommentList[i] != NULL) {\r
+ free(cmailCommentList[i]);\r
+ cmailCommentList[i] = NULL;\r
+ }\r
+ }\r
+ nCmailMovesRegistered = 0;\r
+ }\r
+\r
+ for (i = 0; i < CMAIL_MAX_GAMES; i ++) {\r
+ cmailResult[i] = CMAIL_NOT_RESULT;\r
+ }\r
+ nCmailResults = 0;\r
+\r
+ if (inFilename == NULL) {\r
+ /* Because the filenames are static they only get malloced once */\r
+ /* and they never get freed */\r
+ inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);\r
+ sprintf(inFilename, "%s.game.in", appData.cmailGameName);\r
+\r
+ outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);\r
+ sprintf(outFilename, "%s.out", appData.cmailGameName);\r
+ }\r
+ \r
+ status = stat(outFilename, &outbuf);\r
+ if (status < 0) {\r
+ cmailMailedMove = FALSE;\r
+ } else {\r
+ status = stat(inFilename, &inbuf);\r
+ cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);\r
+ }\r
+ \r
+ /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE\r
+ counts the games, notes how each one terminated, etc.\r
+ \r
+ It would be nice to remove this kludge and instead gather all\r
+ the information while building the game list. (And to keep it\r
+ in the game list nodes instead of having a bunch of fixed-size\r
+ parallel arrays.) Note this will require getting each game's\r
+ termination from the PGN tags, as the game list builder does\r
+ not process the game moves. --mann\r
+ */\r
+ cmailMsgLoaded = TRUE;\r
+ LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);\r
+ \r
+ /* Load first game in the file or popup game menu */\r
+ LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);\r
+\r
+#endif /* !WIN32 */\r
+ return;\r
+}\r
+\r
+int\r
+RegisterMove()\r
+{\r
+ FILE *f;\r
+ char string[MSG_SIZ];\r
+\r
+ if ( cmailMailedMove\r
+ || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {\r
+ return TRUE; /* Allow free viewing */\r
+ }\r
+\r
+ /* Unregister move to ensure that we don't leave RegisterMove */\r
+ /* with the move registered when the conditions for registering no */\r
+ /* longer hold */\r
+ if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+ cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;\r
+ nCmailMovesRegistered --;\r
+\r
+ if (cmailCommentList[lastLoadGameNumber - 1] != NULL) \r
+ {\r
+ free(cmailCommentList[lastLoadGameNumber - 1]);\r
+ cmailCommentList[lastLoadGameNumber - 1] = NULL;\r
+ }\r
+ }\r
+\r
+ if (cmailOldMove == -1) {\r
+ DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);\r
+ return FALSE;\r
+ }\r
+\r
+ if (currentMove > cmailOldMove + 1) {\r
+ DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);\r
+ return FALSE;\r
+ }\r
+\r
+ if (currentMove < cmailOldMove) {\r
+ DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);\r
+ return FALSE;\r
+ }\r
+\r
+ if (forwardMostMove > currentMove) {\r
+ /* Silently truncate extra moves */\r
+ TruncateGame();\r
+ }\r
+\r
+ if ( (currentMove == cmailOldMove + 1)\r
+ || ( (currentMove == cmailOldMove)\r
+ && ( (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)\r
+ || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {\r
+ if (gameInfo.result != GameUnfinished) {\r
+ cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;\r
+ }\r
+\r
+ if (commentList[currentMove] != NULL) {\r
+ cmailCommentList[lastLoadGameNumber - 1]\r
+ = StrSave(commentList[currentMove]);\r
+ }\r
+ strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Saving %s for game %d\n",\r
+ cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);\r
+\r
+ sprintf(string,\r
+ "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);\r
+ \r
+ f = fopen(string, "w");\r
+ if (appData.oldSaveStyle) {\r
+ SaveGameOldStyle(f); /* also closes the file */\r
+ \r
+ sprintf(string, "%s.pos.out", appData.cmailGameName);\r
+ f = fopen(string, "w");\r
+ SavePosition(f, 0, NULL); /* also closes the file */\r
+ } else {\r
+ fprintf(f, "{--------------\n");\r
+ PrintPosition(f, currentMove);\r
+ fprintf(f, "--------------}\n\n");\r
+ \r
+ SaveGame(f, 0, NULL); /* also closes the file*/\r
+ }\r
+ \r
+ cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;\r
+ nCmailMovesRegistered ++;\r
+ } else if (nCmailGames == 1) {\r
+ DisplayError("You have not made a move yet", 0);\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+void\r
+MailMoveEvent()\r
+{\r
+#if !WIN32\r
+ static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";\r
+ FILE *commandOutput;\r
+ char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];\r
+ int nBytes = 0; /* Suppress warnings on uninitialized variables */\r
+ int nBuffers;\r
+ int i;\r
+ int archived;\r
+ char *arcDir;\r
+\r
+ if (! cmailMsgLoaded) {\r
+ DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);\r
+ return;\r
+ }\r
+\r
+ if (nCmailGames == nCmailResults) {\r
+ DisplayError("No unfinished games", 0);\r
+ return;\r
+ }\r
+\r
+#if CMAIL_PROHIBIT_REMAIL\r
+ if (cmailMailedMove) {\r
+ sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName);\r
+ DisplayError(msg, 0);\r
+ return;\r
+ }\r
+#endif\r
+\r
+ if (! (cmailMailedMove || RegisterMove())) return;\r
+ \r
+ if ( cmailMailedMove\r
+ || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {\r
+ sprintf(string, partCommandString,\r
+ appData.debugMode ? " -v" : "", appData.cmailGameName);\r
+ commandOutput = popen(string, "rb");\r
+\r
+ if (commandOutput == NULL) {\r
+ DisplayError("Failed to invoke cmail", 0);\r
+ } else {\r
+ for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {\r
+ nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);\r
+ }\r
+ if (nBuffers > 1) {\r
+ (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);\r
+ (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);\r
+ nBytes = MSG_SIZ - 1;\r
+ } else {\r
+ (void) memcpy(msg, buffer, nBytes);\r
+ }\r
+ *(msg + nBytes) = '\0'; /* \0 for end-of-string*/\r
+\r
+ if(StrStr(msg, "Mailed cmail message to ") != NULL) {\r
+ cmailMailedMove = TRUE; /* Prevent >1 moves */\r
+\r
+ archived = TRUE;\r
+ for (i = 0; i < nCmailGames; i ++) {\r
+ if (cmailResult[i] == CMAIL_NOT_RESULT) {\r
+ archived = FALSE;\r
+ }\r
+ }\r
+ if ( archived\r
+ && ( (arcDir = (char *) getenv("CMAIL_ARCDIR"))\r
+ != NULL)) {\r
+ sprintf(buffer, "%s/%s.%s.archive",\r
+ arcDir,\r
+ appData.cmailGameName,\r
+ gameInfo.date);\r
+ LoadGameFromFile(buffer, 1, buffer, FALSE);\r
+ cmailMsgLoaded = FALSE;\r
+ }\r
+ }\r
+\r
+ DisplayInformation(msg);\r
+ pclose(commandOutput);\r
+ }\r
+ } else {\r
+ if ((*cmailMsg) != '\0') {\r
+ DisplayInformation(cmailMsg);\r
+ }\r
+ }\r
+\r
+ return;\r
+#endif /* !WIN32 */\r
+}\r
+\r
+char *\r
+CmailMsg()\r
+{\r
+#if WIN32\r
+ return NULL;\r
+#else\r
+ int prependComma = 0;\r
+ char number[5];\r
+ char string[MSG_SIZ]; /* Space for game-list */\r
+ int i;\r
+ \r
+ if (!cmailMsgLoaded) return "";\r
+\r
+ if (cmailMailedMove) {\r
+ sprintf(cmailMsg, "Waiting for reply from opponent\n");\r
+ } else {\r
+ /* Create a list of games left */\r
+ sprintf(string, "[");\r
+ for (i = 0; i < nCmailGames; i ++) {\r
+ if (! ( cmailMoveRegistered[i]\r
+ || (cmailResult[i] == CMAIL_OLD_RESULT))) {\r
+ if (prependComma) {\r
+ sprintf(number, ",%d", i + 1);\r
+ } else {\r
+ sprintf(number, "%d", i + 1);\r
+ prependComma = 1;\r
+ }\r
+ \r
+ strcat(string, number);\r
+ }\r
+ }\r
+ strcat(string, "]");\r
+\r
+ if (nCmailMovesRegistered + nCmailResults == 0) {\r
+ switch (nCmailGames) {\r
+ case 1:\r
+ sprintf(cmailMsg,\r
+ "Still need to make move for game\n");\r
+ break;\r
+ \r
+ case 2:\r
+ sprintf(cmailMsg,\r
+ "Still need to make moves for both games\n");\r
+ break;\r
+ \r
+ default:\r
+ sprintf(cmailMsg,\r
+ "Still need to make moves for all %d games\n",\r
+ nCmailGames);\r
+ break;\r
+ }\r
+ } else {\r
+ switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {\r
+ case 1:\r
+ sprintf(cmailMsg,\r
+ "Still need to make a move for game %s\n",\r
+ string);\r
+ break;\r
+ \r
+ case 0:\r
+ if (nCmailResults == nCmailGames) {\r
+ sprintf(cmailMsg, "No unfinished games\n");\r
+ } else {\r
+ sprintf(cmailMsg, "Ready to send mail\n");\r
+ }\r
+ break;\r
+ \r
+ default:\r
+ sprintf(cmailMsg,\r
+ "Still need to make moves for games %s\n",\r
+ string);\r
+ }\r
+ }\r
+ }\r
+ return cmailMsg;\r
+#endif /* WIN32 */\r
+}\r
+\r
+void\r
+ResetGameEvent()\r
+{\r
+ if (gameMode == Training)\r
+ SetTrainingModeOff();\r
+\r
+ Reset(TRUE, TRUE);\r
+ cmailMsgLoaded = FALSE;\r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ }\r
+}\r
+\r
+static int exiting = 0;\r
+\r
+void\r
+ExitEvent(status)\r
+ int status;\r
+{\r
+ exiting++;\r
+ if (exiting > 2) {\r
+ /* Give up on clean exit */\r
+ exit(status);\r
+ }\r
+ if (exiting > 1) {\r
+ /* Keep trying for clean exit */\r
+ return;\r
+ }\r
+\r
+ if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);\r
+\r
+ if (telnetISR != NULL) {\r
+ RemoveInputSource(telnetISR);\r
+ }\r
+ if (icsPR != NoProc) {\r
+ DestroyChildProcess(icsPR, TRUE);\r
+ }\r
+ /* Save game if resource set and not already saved by GameEnds() */\r
+ if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {\r
+ if (*appData.saveGameFile != NULLCHAR) {\r
+ SaveGameToFile(appData.saveGameFile, TRUE);\r
+ } else if (appData.autoSaveGames) {\r
+ AutoSaveGame();\r
+ }\r
+ if (*appData.savePositionFile != NULLCHAR) {\r
+ SavePositionToFile(appData.savePositionFile);\r
+ }\r
+ }\r
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+\r
+ /* Kill off chess programs */\r
+ if (first.pr != NoProc) {\r
+ ExitAnalyzeMode();\r
+ \r
+ DoSleep( appData.delayBeforeQuit );\r
+ SendToProgram("quit\n", &first);\r
+ DoSleep( appData.delayAfterQuit );\r
+ DestroyChildProcess(first.pr, 10 /* [AS] first.useSigterm */ );\r
+ }\r
+ if (second.pr != NoProc) {\r
+ DoSleep( appData.delayBeforeQuit );\r
+ SendToProgram("quit\n", &second);\r
+ DoSleep( appData.delayAfterQuit );\r
+ DestroyChildProcess(second.pr, 10 /* [AS] second.useSigterm */ );\r
+ }\r
+ if (first.isr != NULL) {\r
+ RemoveInputSource(first.isr);\r
+ }\r
+ if (second.isr != NULL) {\r
+ RemoveInputSource(second.isr);\r
+ }\r
+\r
+ ShutDownFrontEnd();\r
+ exit(status);\r
+}\r
+\r
+void\r
+PauseEvent()\r
+{\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);\r
+ if (pausing) {\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ if (gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack) {\r
+ StartClocks();\r
+ } else {\r
+ DisplayBothClocks();\r
+ }\r
+ if (gameMode == PlayFromGameFile) {\r
+ if (appData.timeDelay >= 0) \r
+ AutoPlayGameLoop();\r
+ } else if (gameMode == IcsExamining && pauseExamInvalid) {\r
+ Reset(FALSE, TRUE);\r
+ SendToICS(ics_prefix);\r
+ SendToICS("refresh\n");\r
+ } else if (currentMove < forwardMostMove) {\r
+ ForwardInner(forwardMostMove);\r
+ }\r
+ pauseExamInvalid = FALSE;\r
+ } else {\r
+ switch (gameMode) {\r
+ default:\r
+ return;\r
+ case IcsExamining:\r
+ pauseExamForwardMostMove = forwardMostMove;\r
+ pauseExamInvalid = FALSE;\r
+ /* fall through */\r
+ case IcsObserving:\r
+ case IcsPlayingWhite:\r
+ case IcsPlayingBlack:\r
+ pausing = TRUE;\r
+ ModeHighlight();\r
+ return;\r
+ case PlayFromGameFile:\r
+ (void) StopLoadGameTimer();\r
+ pausing = TRUE;\r
+ ModeHighlight();\r
+ break;\r
+ case BeginningOfGame:\r
+ if (appData.icsActive) return;\r
+ /* else fall through */\r
+ case MachinePlaysWhite:\r
+ case MachinePlaysBlack:\r
+ case TwoMachinesPlay:\r
+ if (forwardMostMove == 0)\r
+ return; /* don't pause if no one has moved */\r
+ if ((gameMode == MachinePlaysWhite &&\r
+ !WhiteOnMove(forwardMostMove)) ||\r
+ (gameMode == MachinePlaysBlack &&\r
+ WhiteOnMove(forwardMostMove))) {\r
+ StopClocks();\r
+ }\r
+ pausing = TRUE;\r
+ ModeHighlight();\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+void\r
+EditCommentEvent()\r
+{\r
+ char title[MSG_SIZ];\r
+\r
+ if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {\r
+ strcpy(title, "Edit comment");\r
+ } else {\r
+ sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,\r
+ WhiteOnMove(currentMove - 1) ? " " : ".. ",\r
+ parseList[currentMove - 1]);\r
+ }\r
+\r
+ EditCommentPopUp(currentMove, title, commentList[currentMove]);\r
+}\r
+\r
+\r
+void\r
+EditTagsEvent()\r
+{\r
+ char *tags = PGNTags(&gameInfo);\r
+ EditTagsPopUp(tags);\r
+ free(tags);\r
+}\r
+\r
+void\r
+AnalyzeModeEvent()\r
+{\r
+ if (appData.noChessProgram || gameMode == AnalyzeMode)\r
+ return;\r
+\r
+ if (gameMode != AnalyzeFile) {\r
+ EditGameEvent();\r
+ if (gameMode != EditGame) return;\r
+ ResurrectChessProgram();\r
+ SendToProgram("analyze\n", &first);\r
+ first.analyzing = TRUE;\r
+ /*first.maybeThinking = TRUE;*/\r
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+ AnalysisPopUp("Analysis",\r
+ "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+ }\r
+ gameMode = AnalyzeMode;\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+\r
+ StartAnalysisClock();\r
+ GetTimeMark(&lastNodeCountTime);\r
+ lastNodeCount = 0;\r
+}\r
+\r
+void\r
+AnalyzeFileEvent()\r
+{\r
+ if (appData.noChessProgram || gameMode == AnalyzeFile)\r
+ return;\r
+\r
+ if (gameMode != AnalyzeMode) {\r
+ EditGameEvent();\r
+ if (gameMode != EditGame) return;\r
+ ResurrectChessProgram();\r
+ SendToProgram("analyze\n", &first);\r
+ first.analyzing = TRUE;\r
+ /*first.maybeThinking = TRUE;*/\r
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+ AnalysisPopUp("Analysis",\r
+ "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+ }\r
+ gameMode = AnalyzeFile;\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+\r
+ StartAnalysisClock();\r
+ GetTimeMark(&lastNodeCountTime);\r
+ lastNodeCount = 0;\r
+}\r
+\r
+void\r
+MachineWhiteEvent()\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ if (appData.noChessProgram || (gameMode == MachinePlaysWhite))\r
+ return;\r
+\r
+\r
+ if (gameMode == PlayFromGameFile || \r
+ gameMode == TwoMachinesPlay || \r
+ gameMode == Training || \r
+ gameMode == AnalyzeMode || \r
+ gameMode == EndOfGame)\r
+ EditGameEvent();\r
+\r
+ if (gameMode == EditPosition) \r
+ EditPositionDone();\r
+\r
+ if (!WhiteOnMove(currentMove)) {\r
+ DisplayError("It is not White's turn", 0);\r
+ return;\r
+ }\r
+ \r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)\r
+ ExitAnalyzeMode();\r
+\r
+ if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+ gameMode == AnalyzeFile)\r
+ TruncateGame();\r
+\r
+ ResurrectChessProgram(); /* in case it isn't running */\r
+ gameMode = MachinePlaysWhite;\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+ DisplayTitle(buf);\r
+ if (first.sendName) {\r
+ sprintf(buf, "name %s\n", gameInfo.black);\r
+ SendToProgram(buf, &first);\r
+ }\r
+ if (first.sendTime) {\r
+ if (first.useColors) {\r
+ SendToProgram("black\n", &first); /*gnu kludge*/\r
+ }\r
+ SendTimeRemaining(&first, TRUE);\r
+ }\r
+ if (first.useColors) {\r
+ SendToProgram("white\ngo\n", &first);\r
+ } else {\r
+ SendToProgram("go\n", &first);\r
+ }\r
+ SetMachineThinkingEnables();\r
+ first.maybeThinking = TRUE;\r
+ StartClocks();\r
+\r
+ if (appData.autoFlipView && !flipView) {\r
+ flipView = !flipView;\r
+ DrawPosition(FALSE, NULL);\r
+ }\r
+}\r
+\r
+void\r
+MachineBlackEvent()\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ if (appData.noChessProgram || (gameMode == MachinePlaysBlack))\r
+ return;\r
+\r
+\r
+ if (gameMode == PlayFromGameFile || \r
+ gameMode == TwoMachinesPlay || \r
+ gameMode == Training || \r
+ gameMode == AnalyzeMode || \r
+ gameMode == EndOfGame)\r
+ EditGameEvent();\r
+\r
+ if (gameMode == EditPosition) \r
+ EditPositionDone();\r
+\r
+ if (WhiteOnMove(currentMove)) {\r
+ DisplayError("It is not Black's turn", 0);\r
+ return;\r
+ }\r
+ \r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)\r
+ ExitAnalyzeMode();\r
+\r
+ if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+ gameMode == AnalyzeFile)\r
+ TruncateGame();\r
+\r
+ ResurrectChessProgram(); /* in case it isn't running */\r
+ gameMode = MachinePlaysBlack;\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+ DisplayTitle(buf);\r
+ if (first.sendName) {\r
+ sprintf(buf, "name %s\n", gameInfo.white);\r
+ SendToProgram(buf, &first);\r
+ }\r
+ if (first.sendTime) {\r
+ if (first.useColors) {\r
+ SendToProgram("white\n", &first); /*gnu kludge*/\r
+ }\r
+ SendTimeRemaining(&first, FALSE);\r
+ }\r
+ if (first.useColors) {\r
+ SendToProgram("black\ngo\n", &first);\r
+ } else {\r
+ SendToProgram("go\n", &first);\r
+ }\r
+ SetMachineThinkingEnables();\r
+ first.maybeThinking = TRUE;\r
+ StartClocks();\r
+\r
+ if (appData.autoFlipView && flipView) {\r
+ flipView = !flipView;\r
+ DrawPosition(FALSE, NULL);\r
+ }\r
+}\r
+\r
+\r
+void\r
+DisplayTwoMachinesTitle()\r
+{\r
+ char buf[MSG_SIZ];\r
+ if (appData.matchGames > 0) {\r
+ if (first.twoMachinesColor[0] == 'w') {\r
+ sprintf(buf, "%s vs. %s (%d-%d-%d)",\r
+ gameInfo.white, gameInfo.black,\r
+ first.matchWins, second.matchWins,\r
+ matchGame - 1 - (first.matchWins + second.matchWins));\r
+ } else {\r
+ sprintf(buf, "%s vs. %s (%d-%d-%d)",\r
+ gameInfo.white, gameInfo.black,\r
+ second.matchWins, first.matchWins,\r
+ matchGame - 1 - (first.matchWins + second.matchWins));\r
+ }\r
+ } else {\r
+ sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+ }\r
+ DisplayTitle(buf);\r
+}\r
+\r
+void\r
+TwoMachinesEvent P((void))\r
+{\r
+ int i;\r
+ char buf[MSG_SIZ];\r
+ ChessProgramState *onmove;\r
+ \r
+ if (appData.noChessProgram) return;\r
+\r
+ switch (gameMode) {\r
+ case TwoMachinesPlay:\r
+ return;\r
+ case MachinePlaysWhite:\r
+ case MachinePlaysBlack:\r
+ if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
+ DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+ return;\r
+ }\r
+ /* fall through */\r
+ case BeginningOfGame:\r
+ case PlayFromGameFile:\r
+ case EndOfGame:\r
+ EditGameEvent();\r
+ if (gameMode != EditGame) return;\r
+ break;\r
+ case EditPosition:\r
+ EditPositionDone();\r
+ break;\r
+ case AnalyzeMode:\r
+ case AnalyzeFile:\r
+ ExitAnalyzeMode();\r
+ break;\r
+ case EditGame:\r
+ default:\r
+ break;\r
+ }\r
+\r
+ forwardMostMove = currentMove;\r
+ ResurrectChessProgram(); /* in case first program isn't running */\r
+\r
+ if (second.pr == NULL) {\r
+ StartChessProgram(&second);\r
+ if (second.protocolVersion == 1) {\r
+ TwoMachinesEventIfReady();\r
+ } else {\r
+ /* kludge: allow timeout for initial "feature" command */\r
+ FreezeUI();\r
+ DisplayMessage("", "Starting second chess program");\r
+ ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);\r
+ }\r
+ return;\r
+ }\r
+ DisplayMessage("", "");\r
+ InitChessProgram(&second);\r
+ SendToProgram("force\n", &second);\r
+ if (startedFromSetupPosition) {\r
+ SendBoard(&second, backwardMostMove);\r
+ }\r
+ for (i = backwardMostMove; i < forwardMostMove; i++) {\r
+ SendMoveToProgram(i, &second);\r
+ }\r
+\r
+ gameMode = TwoMachinesPlay;\r
+ pausing = FALSE;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+ DisplayTwoMachinesTitle();\r
+ firstMove = TRUE;\r
+ if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {\r
+ onmove = &first;\r
+ } else {\r
+ onmove = &second;\r
+ }\r
+\r
+ SendToProgram(first.computerString, &first);\r
+ if (first.sendName) {\r
+ sprintf(buf, "name %s\n", second.tidy);\r
+ SendToProgram(buf, &first);\r
+ }\r
+ SendToProgram(second.computerString, &second);\r
+ if (second.sendName) {\r
+ sprintf(buf, "name %s\n", first.tidy);\r
+ SendToProgram(buf, &second);\r
+ }\r
+\r
+ if (!first.sendTime || !second.sendTime) {\r
+ ResetClocks();\r
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+ }\r
+ if (onmove->sendTime) {\r
+ if (onmove->useColors) {\r
+ SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/\r
+ }\r
+ SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));\r
+ }\r
+ if (onmove->useColors) {\r
+ SendToProgram(onmove->twoMachinesColor, onmove);\r
+ }\r
+ SendToProgram("go\n", onmove);\r
+ onmove->maybeThinking = TRUE;\r
+ SetMachineThinkingEnables();\r
+\r
+ StartClocks();\r
+}\r
+\r
+void\r
+TrainingEvent()\r
+{\r
+ if (gameMode == Training) {\r
+ SetTrainingModeOff();\r
+ gameMode = PlayFromGameFile;\r
+ DisplayMessage("", "Training mode off");\r
+ } else {\r
+ gameMode = Training;\r
+ animateTraining = appData.animate;\r
+\r
+ /* make sure we are not already at the end of the game */\r
+ if (currentMove < forwardMostMove) {\r
+ SetTrainingModeOn();\r
+ DisplayMessage("", "Training mode on");\r
+ } else {\r
+ gameMode = PlayFromGameFile;\r
+ DisplayError("Already at end of game", 0);\r
+ }\r
+ }\r
+ ModeHighlight();\r
+}\r
+\r
+void\r
+IcsClientEvent()\r
+{\r
+ if (!appData.icsActive) return;\r
+ switch (gameMode) {\r
+ case IcsPlayingWhite:\r
+ case IcsPlayingBlack:\r
+ case IcsObserving:\r
+ case IcsIdle:\r
+ case BeginningOfGame:\r
+ case IcsExamining:\r
+ return;\r
+\r
+ case EditGame:\r
+ break;\r
+\r
+ case EditPosition:\r
+ EditPositionDone();\r
+ break;\r
+\r
+ case AnalyzeMode:\r
+ case AnalyzeFile:\r
+ ExitAnalyzeMode();\r
+ break;\r
+ \r
+ default:\r
+ EditGameEvent();\r
+ break;\r
+ }\r
+\r
+ gameMode = IcsIdle;\r
+ ModeHighlight();\r
+ return;\r
+}\r
+\r
+\r
+void\r
+EditGameEvent()\r
+{\r
+ int i;\r
+\r
+ switch (gameMode) {\r
+ case Training:\r
+ SetTrainingModeOff();\r
+ break;\r
+ case MachinePlaysWhite:\r
+ case MachinePlaysBlack:\r
+ case BeginningOfGame:\r
+ SendToProgram("force\n", &first);\r
+ SetUserThinkingEnables();\r
+ break;\r
+ case PlayFromGameFile:\r
+ (void) StopLoadGameTimer();\r
+ if (gameFileFP != NULL) {\r
+ gameFileFP = NULL;\r
+ }\r
+ break;\r
+ case EditPosition:\r
+ EditPositionDone();\r
+ break;\r
+ case AnalyzeMode:\r
+ case AnalyzeFile:\r
+ ExitAnalyzeMode();\r
+ SendToProgram("force\n", &first);\r
+ break;\r
+ case TwoMachinesPlay:\r
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+ ResurrectChessProgram();\r
+ SetUserThinkingEnables();\r
+ break;\r
+ case EndOfGame:\r
+ ResurrectChessProgram();\r
+ break;\r
+ case IcsPlayingBlack:\r
+ case IcsPlayingWhite:\r
+ DisplayError("Warning: You are still playing a game", 0);\r
+ break;\r
+ case IcsObserving:\r
+ DisplayError("Warning: You are still observing a game", 0);\r
+ break;\r
+ case IcsExamining:\r
+ DisplayError("Warning: You are still examining a game", 0);\r
+ break;\r
+ case IcsIdle:\r
+ break;\r
+ case EditGame:\r
+ default:\r
+ return;\r
+ }\r
+ \r
+ pausing = FALSE;\r
+ StopClocks();\r
+ first.offeredDraw = second.offeredDraw = 0;\r
+\r
+ if (gameMode == PlayFromGameFile) {\r
+ whiteTimeRemaining = timeRemaining[0][currentMove];\r
+ blackTimeRemaining = timeRemaining[1][currentMove];\r
+ DisplayTitle("");\r
+ }\r
+\r
+ if (gameMode == MachinePlaysWhite ||\r
+ gameMode == MachinePlaysBlack ||\r
+ gameMode == TwoMachinesPlay ||\r
+ gameMode == EndOfGame) {\r
+ i = forwardMostMove;\r
+ while (i > currentMove) {\r
+ SendToProgram("undo\n", &first);\r
+ i--;\r
+ }\r
+ whiteTimeRemaining = timeRemaining[0][currentMove];\r
+ blackTimeRemaining = timeRemaining[1][currentMove];\r
+ DisplayBothClocks();\r
+ if (whiteFlag || blackFlag) {\r
+ whiteFlag = blackFlag = 0;\r
+ }\r
+ DisplayTitle("");\r
+ } \r
+ \r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+}\r
+\r
+\r
+void\r
+EditPositionEvent()\r
+{\r
+ if (gameMode == EditPosition) {\r
+ EditGameEvent();\r
+ return;\r
+ }\r
+ \r
+ EditGameEvent();\r
+ if (gameMode != EditGame) return;\r
+ \r
+ gameMode = EditPosition;\r
+ ModeHighlight();\r
+ SetGameInfo();\r
+ if (currentMove > 0)\r
+ CopyBoard(boards[0], boards[currentMove]);\r
+ \r
+ blackPlaysFirst = !WhiteOnMove(currentMove);\r
+ ResetClocks();\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+ DisplayMove(-1);\r
+}\r
+\r
+void\r
+ExitAnalyzeMode()\r
+{\r
+ if (first.analysisSupport && first.analyzing) {\r
+ SendToProgram("exit\n", &first);\r
+ first.analyzing = FALSE;\r
+ }\r
+ AnalysisPopDown();\r
+ thinkOutput[0] = NULLCHAR;\r
+}\r
+\r
+void\r
+EditPositionDone()\r
+{\r
+ startedFromSetupPosition = TRUE;\r
+ InitChessProgram(&first);\r
+ SendToProgram("force\n", &first);\r
+ if (blackPlaysFirst) {\r
+ strcpy(moveList[0], "");\r
+ strcpy(parseList[0], "");\r
+ currentMove = forwardMostMove = backwardMostMove = 1;\r
+ CopyBoard(boards[1], boards[0]);\r
+ } else {\r
+ currentMove = forwardMostMove = backwardMostMove = 0;\r
+ }\r
+ SendBoard(&first, forwardMostMove);\r
+ DisplayTitle("");\r
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+ gameMode = EditGame;\r
+ ModeHighlight();\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+ ClearHighlights(); /* [AS] */\r
+}\r
+\r
+/* Pause for `ms' milliseconds */\r
+/* !! Ugh, this is a kludge. Fix it sometime. --tpm */\r
+void\r
+TimeDelay(ms)\r
+ long ms;\r
+{\r
+ TimeMark m1, m2;\r
+\r
+ GetTimeMark(&m1);\r
+ do {\r
+ GetTimeMark(&m2);\r
+ } while (SubtractTimeMarks(&m2, &m1) < ms);\r
+}\r
+\r
+/* !! Ugh, this is a kludge. Fix it sometime. --tpm */\r
+void\r
+SendMultiLineToICS(buf)\r
+ char *buf;\r
+{\r
+ char temp[MSG_SIZ+1], *p;\r
+ int len;\r
+\r
+ len = strlen(buf);\r
+ if (len > MSG_SIZ)\r
+ len = MSG_SIZ;\r
+ \r
+ strncpy(temp, buf, len);\r
+ temp[len] = 0;\r
+\r
+ p = temp;\r
+ while (*p) {\r
+ if (*p == '\n' || *p == '\r')\r
+ *p = ' ';\r
+ ++p;\r
+ }\r
+\r
+ strcat(temp, "\n");\r
+ SendToICS(temp);\r
+ SendToPlayer(temp, strlen(temp));\r
+}\r
+\r
+void\r
+SetWhiteToPlayEvent()\r
+{\r
+ if (gameMode == EditPosition) {\r
+ blackPlaysFirst = FALSE;\r
+ DisplayBothClocks(); /* works because currentMove is 0 */\r
+ } else if (gameMode == IcsExamining) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("tomove white\n");\r
+ }\r
+}\r
+\r
+void\r
+SetBlackToPlayEvent()\r
+{\r
+ if (gameMode == EditPosition) {\r
+ blackPlaysFirst = TRUE;\r
+ currentMove = 1; /* kludge */\r
+ DisplayBothClocks();\r
+ currentMove = 0;\r
+ } else if (gameMode == IcsExamining) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("tomove black\n");\r
+ }\r
+}\r
+\r
+void\r
+EditPositionMenuEvent(selection, x, y)\r
+ ChessSquare selection;\r
+ int x, y;\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ if (gameMode != EditPosition && gameMode != IcsExamining) return;\r
+\r
+ switch (selection) {\r
+ case ClearBoard:\r
+ if (gameMode == IcsExamining && ics_type == ICS_FICS) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("bsetup clear\n");\r
+ } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("clearboard\n");\r
+ } else {\r
+ for (x = 0; x < BOARD_WIDTH; x++) {\r
+ for (y = 0; y < BOARD_HEIGHT; y++) {\r
+ if (gameMode == IcsExamining) {\r
+ if (boards[currentMove][y][x] != EmptySquare) {\r
+ sprintf(buf, "%sx@%c%c\n", ics_prefix,\r
+ 'a' + x, ONE + y);\r
+ SendToICS(buf);\r
+ }\r
+ } else {\r
+ boards[0][y][x] = EmptySquare;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (gameMode == EditPosition) {\r
+ DrawPosition(FALSE, boards[0]);\r
+ }\r
+ break;\r
+\r
+ case WhitePlay:\r
+ SetWhiteToPlayEvent();\r
+ break;\r
+\r
+ case BlackPlay:\r
+ SetBlackToPlayEvent();\r
+ break;\r
+\r
+ case EmptySquare:\r
+ if (gameMode == IcsExamining) {\r
+ sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, ONE + y);\r
+ SendToICS(buf);\r
+ } else {\r
+ boards[0][y][x] = EmptySquare;\r
+ DrawPosition(FALSE, boards[0]);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ if (gameMode == IcsExamining) {\r
+ sprintf(buf, "%s%c@%c%c\n", ics_prefix,\r
+ PieceToChar(selection), 'a' + x, ONE + y);\r
+ SendToICS(buf);\r
+ } else {\r
+ boards[0][y][x] = selection;\r
+ DrawPosition(FALSE, boards[0]);\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+void\r
+DropMenuEvent(selection, x, y)\r
+ ChessSquare selection;\r
+ int x, y;\r
+{\r
+ ChessMove moveType;\r
+\r
+ switch (gameMode) {\r
+ case IcsPlayingWhite:\r
+ case MachinePlaysBlack:\r
+ if (!WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is Black's turn");\r
+ return;\r
+ }\r
+ moveType = WhiteDrop;\r
+ break;\r
+ case IcsPlayingBlack:\r
+ case MachinePlaysWhite:\r
+ if (WhiteOnMove(currentMove)) {\r
+ DisplayMoveError("It is White's turn");\r
+ return;\r
+ }\r
+ moveType = BlackDrop;\r
+ break;\r
+ case EditGame:\r
+ moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
+ break;\r
+ default:\r
+ return;\r
+ }\r
+\r
+ if (moveType == BlackDrop && selection < BlackPawn) {\r
+ selection = (ChessSquare) ((int) selection\r
+ + (int) BlackPawn - (int) WhitePawn);\r
+ }\r
+ if (boards[currentMove][y][x] != EmptySquare) {\r
+ DisplayMoveError("That square is occupied");\r
+ return;\r
+ }\r
+\r
+ FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);\r
+}\r
+\r
+void\r
+AcceptEvent()\r
+{\r
+ /* Accept a pending offer of any kind from opponent */\r
+ \r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("accept\n");\r
+ } else if (cmailMsgLoaded) {\r
+ if (currentMove == cmailOldMove &&\r
+ commentList[cmailOldMove] != NULL &&\r
+ StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+ "Black offers a draw" : "White offers a draw")) {\r
+ TruncateGame();\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
+ } else {\r
+ DisplayError("There is no pending offer on this move", 0);\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+ }\r
+ } else {\r
+ /* Not used for offers from chess program */\r
+ }\r
+}\r
+\r
+void\r
+DeclineEvent()\r
+{\r
+ /* Decline a pending offer of any kind from opponent */\r
+ \r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("decline\n");\r
+ } else if (cmailMsgLoaded) {\r
+ if (currentMove == cmailOldMove &&\r
+ commentList[cmailOldMove] != NULL &&\r
+ StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+ "Black offers a draw" : "White offers a draw")) {\r
+#ifdef NOTDEF\r
+ AppendComment(cmailOldMove, "Draw declined");\r
+ DisplayComment(cmailOldMove - 1, "Draw declined");\r
+#endif /*NOTDEF*/\r
+ } else {\r
+ DisplayError("There is no pending offer on this move", 0);\r
+ }\r
+ } else {\r
+ /* Not used for offers from chess program */\r
+ }\r
+}\r
+\r
+void\r
+RematchEvent()\r
+{\r
+ /* Issue ICS rematch command */\r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("rematch\n");\r
+ }\r
+}\r
+\r
+void\r
+CallFlagEvent()\r
+{\r
+ /* Call your opponent's flag (claim a win on time) */\r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("flag\n");\r
+ } else {\r
+ switch (gameMode) {\r
+ default:\r
+ return;\r
+ case MachinePlaysWhite:\r
+ if (whiteFlag) {\r
+ if (blackFlag)\r
+ GameEnds(GameIsDrawn, "Both players ran out of time",\r
+ GE_PLAYER);\r
+ else\r
+ GameEnds(BlackWins, "Black wins on time", GE_PLAYER);\r
+ } else {\r
+ DisplayError("Your opponent is not out of time", 0);\r
+ }\r
+ break;\r
+ case MachinePlaysBlack:\r
+ if (blackFlag) {\r
+ if (whiteFlag)\r
+ GameEnds(GameIsDrawn, "Both players ran out of time",\r
+ GE_PLAYER);\r
+ else\r
+ GameEnds(WhiteWins, "White wins on time", GE_PLAYER);\r
+ } else {\r
+ DisplayError("Your opponent is not out of time", 0);\r
+ }\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+void\r
+DrawEvent()\r
+{\r
+ /* Offer draw or accept pending draw offer from opponent */\r
+ \r
+ if (appData.icsActive) {\r
+ /* Note: tournament rules require draw offers to be\r
+ made after you make your move but before you punch\r
+ your clock. Currently ICS doesn't let you do that;\r
+ instead, you immediately punch your clock after making\r
+ a move, but you can offer a draw at any time. */\r
+ \r
+ SendToICS(ics_prefix);\r
+ SendToICS("draw\n");\r
+ } else if (cmailMsgLoaded) {\r
+ if (currentMove == cmailOldMove &&\r
+ commentList[cmailOldMove] != NULL &&\r
+ StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+ "Black offers a draw" : "White offers a draw")) {\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
+ } else if (currentMove == cmailOldMove + 1) {\r
+ char *offer = WhiteOnMove(cmailOldMove) ?\r
+ "White offers a draw" : "Black offers a draw";\r
+ AppendComment(currentMove, offer);\r
+ DisplayComment(currentMove - 1, offer);\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;\r
+ } else {\r
+ DisplayError("You must make your move before offering a draw", 0);\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+ }\r
+ } else if (first.offeredDraw) {\r
+ GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+ } else {\r
+ if (first.sendDrawOffers) {\r
+ SendToProgram("draw\n", &first);\r
+ userOfferedDraw = TRUE;\r
+ }\r
+ }\r
+}\r
+\r
+void\r
+AdjournEvent()\r
+{\r
+ /* Offer Adjourn or accept pending Adjourn offer from opponent */\r
+ \r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("adjourn\n");\r
+ } else {\r
+ /* Currently GNU Chess doesn't offer or accept Adjourns */\r
+ }\r
+}\r
+\r
+\r
+void\r
+AbortEvent()\r
+{\r
+ /* Offer Abort or accept pending Abort offer from opponent */\r
+ \r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("abort\n");\r
+ } else {\r
+ GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);\r
+ }\r
+}\r
+\r
+void\r
+ResignEvent()\r
+{\r
+ /* Resign. You can do this even if it's not your turn. */\r
+ \r
+ if (appData.icsActive) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("resign\n");\r
+ } else {\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+ break;\r
+ case MachinePlaysBlack:\r
+ GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+ break;\r
+ case EditGame:\r
+ if (cmailMsgLoaded) {\r
+ TruncateGame();\r
+ if (WhiteOnMove(cmailOldMove)) {\r
+ GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+ } else {\r
+ GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+ }\r
+ cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;\r
+ }\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void\r
+StopObservingEvent()\r
+{\r
+ /* Stop observing current games */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("unobserve\n");\r
+}\r
+\r
+void\r
+StopExaminingEvent()\r
+{\r
+ /* Stop observing current game */\r
+ SendToICS(ics_prefix);\r
+ SendToICS("unexamine\n");\r
+}\r
+\r
+void\r
+ForwardInner(target)\r
+ int target;\r
+{\r
+ int limit;\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",\r
+ target, currentMove, forwardMostMove);\r
+\r
+ if (gameMode == EditPosition)\r
+ return;\r
+\r
+ if (gameMode == PlayFromGameFile && !pausing)\r
+ PauseEvent();\r
+ \r
+ if (gameMode == IcsExamining && pausing)\r
+ limit = pauseExamForwardMostMove;\r
+ else\r
+ limit = forwardMostMove;\r
+ \r
+ if (target > limit) target = limit;\r
+\r
+ if (target > 0 && moveList[target - 1][0]) {\r
+ int fromX, fromY, toX, toY;\r
+ toX = moveList[target - 1][2] - 'a';\r
+ toY = moveList[target - 1][3] - ONE;\r
+ if (moveList[target - 1][1] == '@') {\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(-1, -1, toX, toY);\r
+ }\r
+ } else {\r
+ fromX = moveList[target - 1][0] - 'a';\r
+ fromY = moveList[target - 1][1] - ONE;\r
+ if (target == currentMove + 1) {\r
+ AnimateMove(boards[currentMove], fromX, fromY, toX, toY);\r
+ }\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(fromX, fromY, toX, toY);\r
+ }\r
+ }\r
+ }\r
+ if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+ gameMode == Training || gameMode == PlayFromGameFile || \r
+ gameMode == AnalyzeFile) {\r
+ while (currentMove < target) {\r
+ SendMoveToProgram(currentMove++, &first);\r
+ }\r
+ } else {\r
+ currentMove = target;\r
+ }\r
+ \r
+ if (gameMode == EditGame || gameMode == EndOfGame) {\r
+ whiteTimeRemaining = timeRemaining[0][currentMove];\r
+ blackTimeRemaining = timeRemaining[1][currentMove];\r
+ }\r
+ DisplayBothClocks();\r
+ DisplayMove(currentMove - 1);\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+ if (commentList[currentMove] && !matchMode && gameMode != Training) {\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+}\r
+\r
+\r
+void\r
+ForwardEvent()\r
+{\r
+ if (gameMode == IcsExamining && !pausing) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("forward\n");\r
+ } else {\r
+ ForwardInner(currentMove + 1);\r
+ }\r
+}\r
+\r
+void\r
+ToEndEvent()\r
+{\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ /* to optimze, we temporarily turn off analysis mode while we feed\r
+ * the remaining moves to the engine. Otherwise we get analysis output\r
+ * after each move.\r
+ */ \r
+ if (first.analysisSupport) {\r
+ SendToProgram("exit\nforce\n", &first);\r
+ first.analyzing = FALSE;\r
+ }\r
+ }\r
+ \r
+ if (gameMode == IcsExamining && !pausing) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("forward 999999\n");\r
+ } else {\r
+ ForwardInner(forwardMostMove);\r
+ }\r
+\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ /* we have fed all the moves, so reactivate analysis mode */\r
+ SendToProgram("analyze\n", &first);\r
+ first.analyzing = TRUE;\r
+ /*first.maybeThinking = TRUE;*/\r
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+ }\r
+}\r
+\r
+void\r
+BackwardInner(target)\r
+ int target;\r
+{\r
+ int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */\r
+\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",\r
+ target, currentMove, forwardMostMove);\r
+\r
+ if (gameMode == EditPosition) return;\r
+ if (currentMove <= backwardMostMove) {\r
+ ClearHighlights();\r
+ DrawPosition(full_redraw, boards[currentMove]);\r
+ return;\r
+ }\r
+ if (gameMode == PlayFromGameFile && !pausing)\r
+ PauseEvent();\r
+ \r
+ if (moveList[target][0]) {\r
+ int fromX, fromY, toX, toY;\r
+ toX = moveList[target][2] - 'a';\r
+ toY = moveList[target][3] - ONE;\r
+ if (moveList[target][1] == '@') {\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(-1, -1, toX, toY);\r
+ }\r
+ } else {\r
+ fromX = moveList[target][0] - 'a';\r
+ fromY = moveList[target][1] - ONE;\r
+ if (target == currentMove - 1) {\r
+ AnimateMove(boards[currentMove], toX, toY, fromX, fromY);\r
+ }\r
+ if (appData.highlightLastMove) {\r
+ SetHighlights(fromX, fromY, toX, toY);\r
+ }\r
+ }\r
+ }\r
+ if (gameMode == EditGame || gameMode==AnalyzeMode ||\r
+ gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
+ while (currentMove > target) {\r
+ SendToProgram("undo\n", &first);\r
+ currentMove--;\r
+ }\r
+ } else {\r
+ currentMove = target;\r
+ }\r
+ \r
+ if (gameMode == EditGame || gameMode == EndOfGame) {\r
+ whiteTimeRemaining = timeRemaining[0][currentMove];\r
+ blackTimeRemaining = timeRemaining[1][currentMove];\r
+ }\r
+ DisplayBothClocks();\r
+ DisplayMove(currentMove - 1);\r
+ DrawPosition(full_redraw, boards[currentMove]);\r
+ HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+ if (commentList[currentMove] != NULL) {\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
+ }\r
+}\r
+\r
+void\r
+BackwardEvent()\r
+{\r
+ if (gameMode == IcsExamining && !pausing) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("backward\n");\r
+ } else {\r
+ BackwardInner(currentMove - 1);\r
+ }\r
+}\r
+\r
+void\r
+ToStartEvent()\r
+{\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ /* to optimze, we temporarily turn off analysis mode while we undo\r
+ * all the moves. Otherwise we get analysis output after each undo.\r
+ */ \r
+ if (first.analysisSupport) {\r
+ SendToProgram("exit\nforce\n", &first);\r
+ first.analyzing = FALSE;\r
+ }\r
+ }\r
+\r
+ if (gameMode == IcsExamining && !pausing) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("backward 999999\n");\r
+ } else {\r
+ BackwardInner(backwardMostMove);\r
+ }\r
+\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ /* we have fed all the moves, so reactivate analysis mode */\r
+ SendToProgram("analyze\n", &first);\r
+ first.analyzing = TRUE;\r
+ /*first.maybeThinking = TRUE;*/\r
+ first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+ }\r
+}\r
+\r
+void\r
+ToNrEvent(int to)\r
+{\r
+ if (gameMode == PlayFromGameFile && !pausing) PauseEvent();\r
+ if (to >= forwardMostMove) to = forwardMostMove;\r
+ if (to <= backwardMostMove) to = backwardMostMove;\r
+ if (to < currentMove) {\r
+ BackwardInner(to);\r
+ } else {\r
+ ForwardInner(to);\r
+ }\r
+}\r
+\r
+void\r
+RevertEvent()\r
+{\r
+ if (gameMode != IcsExamining) {\r
+ DisplayError("You are not examining a game", 0);\r
+ return;\r
+ }\r
+ if (pausing) {\r
+ DisplayError("You can't revert while pausing", 0);\r
+ return;\r
+ }\r
+ SendToICS(ics_prefix);\r
+ SendToICS("revert\n");\r
+}\r
+\r
+void\r
+RetractMoveEvent()\r
+{\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ case MachinePlaysBlack:\r
+ if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
+ DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+ return;\r
+ }\r
+ if (forwardMostMove < 2) return;\r
+ currentMove = forwardMostMove = forwardMostMove - 2;\r
+ whiteTimeRemaining = timeRemaining[0][currentMove];\r
+ blackTimeRemaining = timeRemaining[1][currentMove];\r
+ DisplayBothClocks();\r
+ DisplayMove(currentMove - 1);\r
+ ClearHighlights();/*!! could figure this out*/\r
+ DrawPosition(TRUE, boards[currentMove]); /* [AS] Changed to full redraw! */\r
+ SendToProgram("remove\n", &first);\r
+ /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */\r
+ break;\r
+\r
+ case BeginningOfGame:\r
+ default:\r
+ break;\r
+\r
+ case IcsPlayingWhite:\r
+ case IcsPlayingBlack:\r
+ if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("takeback 2\n");\r
+ } else {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("takeback 1\n");\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+void\r
+MoveNowEvent()\r
+{\r
+ ChessProgramState *cps;\r
+\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ if (!WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("It is your turn", 0);\r
+ return;\r
+ }\r
+ cps = &first;\r
+ break;\r
+ case MachinePlaysBlack:\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("It is your turn", 0);\r
+ return;\r
+ }\r
+ cps = &first;\r
+ break;\r
+ case TwoMachinesPlay:\r
+ if (WhiteOnMove(forwardMostMove) ==\r
+ (first.twoMachinesColor[0] == 'w')) {\r
+ cps = &first;\r
+ } else {\r
+ cps = &second;\r
+ }\r
+ break;\r
+ case BeginningOfGame:\r
+ default:\r
+ return;\r
+ }\r
+ SendToProgram("?\n", cps);\r
+}\r
+\r
+void\r
+TruncateGameEvent()\r
+{\r
+ EditGameEvent();\r
+ if (gameMode != EditGame) return;\r
+ TruncateGame();\r
+}\r
+\r
+void\r
+TruncateGame()\r
+{\r
+ if (forwardMostMove > currentMove) {\r
+ if (gameInfo.resultDetails != NULL) {\r
+ free(gameInfo.resultDetails);\r
+ gameInfo.resultDetails = NULL;\r
+ gameInfo.result = GameUnfinished;\r
+ }\r
+ forwardMostMove = currentMove;\r
+ HistorySet(parseList, backwardMostMove, forwardMostMove,\r
+ currentMove-1);\r
+ }\r
+}\r
+\r
+void\r
+HintEvent()\r
+{\r
+ if (appData.noChessProgram) return;\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("Wait until your turn", 0);\r
+ return;\r
+ }\r
+ break;\r
+ case BeginningOfGame:\r
+ case MachinePlaysBlack:\r
+ if (!WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("Wait until your turn", 0);\r
+ return;\r
+ }\r
+ break;\r
+ default:\r
+ DisplayError("No hint available", 0);\r
+ return;\r
+ }\r
+ SendToProgram("hint\n", &first);\r
+ hintRequested = TRUE;\r
+}\r
+\r
+void\r
+BookEvent()\r
+{\r
+ if (appData.noChessProgram) return;\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("Wait until your turn", 0);\r
+ return;\r
+ }\r
+ break;\r
+ case BeginningOfGame:\r
+ case MachinePlaysBlack:\r
+ if (!WhiteOnMove(forwardMostMove)) {\r
+ DisplayError("Wait until your turn", 0);\r
+ return;\r
+ }\r
+ break;\r
+ case EditPosition:\r
+ EditPositionDone();\r
+ break;\r
+ case TwoMachinesPlay:\r
+ return;\r
+ default:\r
+ break;\r
+ }\r
+ SendToProgram("bk\n", &first);\r
+ bookOutput[0] = NULLCHAR;\r
+ bookRequested = TRUE;\r
+}\r
+\r
+void\r
+AboutGameEvent()\r
+{\r
+ char *tags = PGNTags(&gameInfo);\r
+ TagsPopUp(tags, CmailMsg());\r
+ free(tags);\r
+}\r
+\r
+/* end button procedures */\r
+\r
+void\r
+PrintPosition(fp, move)\r
+ FILE *fp;\r
+ int move;\r
+{\r
+ int i, j;\r
+ \r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ for (j = 0; j < BOARD_WIDTH; j++) {\r
+ char c = PieceToChar(boards[move][i][j]);\r
+ fputc(c == 'x' ? '.' : c, fp);\r
+ fputc(j == BOARD_WIDTH - 1 ? '\n' : ' ', fp);\r
+ }\r
+ }\r
+ if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))\r
+ fprintf(fp, "white to play\n");\r
+ else\r
+ fprintf(fp, "black to play\n");\r
+}\r
+\r
+void\r
+PrintOpponents(fp)\r
+ FILE *fp;\r
+{\r
+ if (gameInfo.white != NULL) {\r
+ fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);\r
+ } else {\r
+ fprintf(fp, "\n");\r
+ }\r
+}\r
+\r
+/* Find last component of program's own name, using some heuristics */\r
+void\r
+TidyProgramName(prog, host, buf)\r
+ char *prog, *host, buf[MSG_SIZ];\r
+{\r
+ char *p, *q;\r
+ int local = (strcmp(host, "localhost") == 0);\r
+ while (!local && (p = strchr(prog, ';')) != NULL) {\r
+ p++;\r
+ while (*p == ' ') p++;\r
+ prog = p;\r
+ }\r
+ if (*prog == '"' || *prog == '\'') {\r
+ q = strchr(prog + 1, *prog);\r
+ } else {\r
+ q = strchr(prog, ' ');\r
+ }\r
+ if (q == NULL) q = prog + strlen(prog);\r
+ p = q;\r
+ while (p >= prog && *p != '/' && *p != '\\') p--;\r
+ p++;\r
+ if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;\r
+ memcpy(buf, p, q - p);\r
+ buf[q - p] = NULLCHAR;\r
+ if (!local) {\r
+ strcat(buf, "@");\r
+ strcat(buf, host);\r
+ }\r
+}\r
+\r
+char *\r
+TimeControlTagValue()\r
+{\r
+ char buf[MSG_SIZ];\r
+ if (!appData.clockMode) {\r
+ strcpy(buf, "-");\r
+ } else if (movesPerSession > 0) {\r
+ sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);\r
+ } else if (timeIncrement == 0) {\r
+ sprintf(buf, "%ld", timeControl/1000);\r
+ } else {\r
+ sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);\r
+ }\r
+ return StrSave(buf);\r
+}\r
+\r
+void\r
+SetGameInfo()\r
+{\r
+ /* This routine is used only for certain modes */\r
+ VariantClass v = gameInfo.variant;\r
+ ClearGameInfo(&gameInfo);\r
+ gameInfo.variant = v;\r
+\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ gameInfo.event = StrSave( appData.pgnEventHeader );\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave(first.tidy);\r
+ gameInfo.black = StrSave(UserName());\r
+ gameInfo.timeControl = TimeControlTagValue();\r
+ break;\r
+\r
+ case MachinePlaysBlack:\r
+ gameInfo.event = StrSave( appData.pgnEventHeader );\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave(UserName());\r
+ gameInfo.black = StrSave(first.tidy);\r
+ gameInfo.timeControl = TimeControlTagValue();\r
+ break;\r
+\r
+ case TwoMachinesPlay:\r
+ gameInfo.event = StrSave( appData.pgnEventHeader );\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ if (matchGame > 0) {\r
+ char buf[MSG_SIZ];\r
+ sprintf(buf, "%d", matchGame);\r
+ gameInfo.round = StrSave(buf);\r
+ } else {\r
+ gameInfo.round = StrSave("-");\r
+ }\r
+ if (first.twoMachinesColor[0] == 'w') {\r
+ gameInfo.white = StrSave(first.tidy);\r
+ gameInfo.black = StrSave(second.tidy);\r
+ } else {\r
+ gameInfo.white = StrSave(second.tidy);\r
+ gameInfo.black = StrSave(first.tidy);\r
+ }\r
+ gameInfo.timeControl = TimeControlTagValue();\r
+ break;\r
+\r
+ case EditGame:\r
+ gameInfo.event = StrSave("Edited game");\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave("-");\r
+ gameInfo.black = StrSave("-");\r
+ break;\r
+\r
+ case EditPosition:\r
+ gameInfo.event = StrSave("Edited position");\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave("-");\r
+ gameInfo.black = StrSave("-");\r
+ break;\r
+\r
+ case IcsPlayingWhite:\r
+ case IcsPlayingBlack:\r
+ case IcsObserving:\r
+ case IcsExamining:\r
+ break;\r
+\r
+ case PlayFromGameFile:\r
+ gameInfo.event = StrSave("Game from non-PGN file");\r
+ gameInfo.site = StrSave(HostName());\r
+ gameInfo.date = PGNDate();\r
+ gameInfo.round = StrSave("-");\r
+ gameInfo.white = StrSave("?");\r
+ gameInfo.black = StrSave("?");\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+void\r
+ReplaceComment(index, text)\r
+ int index;\r
+ char *text;\r
+{\r
+ int len;\r
+\r
+ while (*text == '\n') text++;\r
+ len = strlen(text);\r
+ while (len > 0 && text[len - 1] == '\n') len--;\r
+\r
+ if (commentList[index] != NULL)\r
+ free(commentList[index]);\r
+\r
+ if (len == 0) {\r
+ commentList[index] = NULL;\r
+ return;\r
+ }\r
+ commentList[index] = (char *) malloc(len + 2);\r
+ strncpy(commentList[index], text, len);\r
+ commentList[index][len] = '\n';\r
+ commentList[index][len + 1] = NULLCHAR;\r
+}\r
+\r
+void\r
+CrushCRs(text)\r
+ char *text;\r
+{\r
+ char *p = text;\r
+ char *q = text;\r
+ char ch;\r
+\r
+ do {\r
+ ch = *p++;\r
+ if (ch == '\r') continue;\r
+ *q++ = ch;\r
+ } while (ch != '\0');\r
+}\r
+\r
+void\r
+AppendComment(index, text)\r
+ int index;\r
+ char *text;\r
+{\r
+ int oldlen, len;\r
+ char *old;\r
+\r
+ GetInfoFromComment( index, text );\r
+\r
+ CrushCRs(text);\r
+ while (*text == '\n') text++;\r
+ len = strlen(text);\r
+ while (len > 0 && text[len - 1] == '\n') len--;\r
+\r
+ if (len == 0) return;\r
+\r
+ if (commentList[index] != NULL) {\r
+ old = commentList[index];\r
+ oldlen = strlen(old);\r
+ commentList[index] = (char *) malloc(oldlen + len + 2);\r
+ strcpy(commentList[index], old);\r
+ free(old);\r
+ strncpy(&commentList[index][oldlen], text, len);\r
+ commentList[index][oldlen + len] = '\n';\r
+ commentList[index][oldlen + len + 1] = NULLCHAR;\r
+ } else {\r
+ commentList[index] = (char *) malloc(len + 2);\r
+ strncpy(commentList[index], text, len);\r
+ commentList[index][len] = '\n';\r
+ commentList[index][len + 1] = NULLCHAR;\r
+ }\r
+}\r
+\r
+static char * FindStr( char * text, char * sub_text )\r
+{\r
+ char * result = strstr( text, sub_text );\r
+\r
+ if( result != NULL ) {\r
+ result += strlen( sub_text );\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/* [AS] Try to extract PV info from PGN comment */\r
+void GetInfoFromComment( int index, char * text )\r
+{\r
+ if( text != NULL && index > 0 ) {\r
+ int score = 0;\r
+ int depth = 0;\r
+ int time = -1;\r
+ char * s_eval = FindStr( text, "[%eval " );\r
+ char * s_emt = FindStr( text, "[%emt " );\r
+\r
+ if( s_eval != NULL || s_emt != NULL ) {\r
+ /* New style */\r
+ char delim;\r
+\r
+ if( s_eval != NULL ) {\r
+ if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {\r
+ return;\r
+ }\r
+\r
+ if( delim != ']' ) {\r
+ return;\r
+ }\r
+ }\r
+\r
+ if( s_emt != NULL ) {\r
+ }\r
+ }\r
+ else {\r
+ /* We expect something like: [+|-]nnn.nn/dd */\r
+ char * sep = strchr( text, '/' );\r
+ int score_lo = 0;\r
+\r
+ if( sep == NULL || sep < (text+4) ) {\r
+ return;\r
+ }\r
+\r
+ if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {\r
+ return;\r
+ }\r
+\r
+ if( score_lo < 0 || score_lo >= 100 ) {\r
+ return;\r
+ }\r
+\r
+ score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;\r
+ }\r
+\r
+ if( depth <= 0 ) {\r
+ return;\r
+ }\r
+\r
+ if( time < 0 ) {\r
+ time = -1;\r
+ }\r
+\r
+ pvInfoList[index-1].depth = depth;\r
+ pvInfoList[index-1].score = score;\r
+ pvInfoList[index-1].time = time;\r
+ }\r
+}\r
+\r
+void\r
+SendToProgram(message, cps)\r
+ char *message;\r
+ ChessProgramState *cps;\r
+{\r
+ int count, outCount, error;\r
+ char buf[MSG_SIZ];\r
+\r
+ if (cps->pr == NULL) return;\r
+ Attention(cps);\r
+ \r
+ if (appData.debugMode) {\r
+ TimeMark now;\r
+ GetTimeMark(&now);\r
+ fprintf(debugFP, "%ld >%-6s: %s", \r
+ SubtractTimeMarks(&now, &programStartTime),\r
+ cps->which, message);\r
+ }\r
+ \r
+ count = strlen(message);\r
+ outCount = OutputToProcess(cps->pr, message, count, &error);\r
+ if (outCount < count && !exiting) {\r
+ sprintf(buf, "Error writing to %s chess program", cps->which);\r
+ DisplayFatalError(buf, error, 1);\r
+ }\r
+}\r
+\r
+void\r
+ReceiveFromProgram(isr, closure, message, count, error)\r
+ InputSourceRef isr;\r
+ VOIDSTAR closure;\r
+ char *message;\r
+ int count;\r
+ int error;\r
+{\r
+ char *end_str;\r
+ char buf[MSG_SIZ];\r
+ ChessProgramState *cps = (ChessProgramState *)closure;\r
+\r
+ if (isr != cps->isr) return; /* Killed intentionally */\r
+ if (count <= 0) {\r
+ if (count == 0) {\r
+ sprintf(buf,\r
+ "Error: %s chess program (%s) exited unexpectedly",\r
+ cps->which, cps->program);\r
+ RemoveInputSource(cps->isr);\r
+ DisplayFatalError(buf, 0, 1);\r
+ } else {\r
+ sprintf(buf,\r
+ "Error reading from %s chess program (%s)",\r
+ cps->which, cps->program);\r
+ RemoveInputSource(cps->isr);\r
+\r
+ /* [AS] Program is misbehaving badly... kill it */\r
+ if( count == -2 ) {\r
+ DestroyChildProcess( cps->pr, 9 );\r
+ cps->pr = NoProc;\r
+ }\r
+\r
+ DisplayFatalError(buf, error, 1);\r
+ }\r
+ GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+ return;\r
+ }\r
+ \r
+ if ((end_str = strchr(message, '\r')) != NULL)\r
+ *end_str = NULLCHAR;\r
+ if ((end_str = strchr(message, '\n')) != NULL)\r
+ *end_str = NULLCHAR;\r
+ \r
+ if (appData.debugMode) {\r
+ TimeMark now;\r
+ GetTimeMark(&now);\r
+ fprintf(debugFP, "%ld <%-6s: %s\n", \r
+ SubtractTimeMarks(&now, &programStartTime),\r
+ cps->which, message);\r
+ }\r
+ HandleMachineMove(message, cps);\r
+}\r
+\r
+\r
+void\r
+SendTimeControl(cps, mps, tc, inc, sd, st)\r
+ ChessProgramState *cps;\r
+ int mps, inc, sd, st;\r
+ long tc;\r
+{\r
+ char buf[MSG_SIZ];\r
+ int seconds = (tc / 1000) % 60;\r
+\r
+ if( timeControl_2 > 0 ) {\r
+ if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {\r
+ tc = timeControl_2;\r
+ }\r
+ }\r
+\r
+ if (st > 0) {\r
+ /* Set exact time per move, normally using st command */\r
+ if (cps->stKludge) {\r
+ /* GNU Chess 4 has no st command; uses level in a nonstandard way */\r
+ seconds = st % 60;\r
+ if (seconds == 0) {\r
+ sprintf(buf, "level 1 %d\n", st/60);\r
+ } else {\r
+ sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);\r
+ }\r
+ } else {\r
+ sprintf(buf, "st %d\n", st);\r
+ }\r
+ } else {\r
+ /* Set conventional or incremental time control, using level command */\r
+ if (seconds == 0) {\r
+ /* Note old gnuchess bug -- minutes:seconds used to not work.\r
+ Fixed in later versions, but still avoid :seconds\r
+ when seconds is 0. */\r
+ sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);\r
+ } else {\r
+ sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,\r
+ seconds, inc/1000);\r
+ }\r
+ }\r
+ SendToProgram(buf, cps);\r
+\r
+ /* Orthoganally (except for GNU Chess 4), limit time to st seconds */\r
+ /* Orthogonally, limit search to given depth */\r
+ if (sd > 0) {\r
+ if (cps->sdKludge) {\r
+ sprintf(buf, "depth\n%d\n", sd);\r
+ } else {\r
+ sprintf(buf, "sd %d\n", sd);\r
+ }\r
+ SendToProgram(buf, cps);\r
+ }\r
+}\r
+\r
+void\r
+SendTimeRemaining(cps, machineWhite)\r
+ ChessProgramState *cps;\r
+ int /*boolean*/ machineWhite;\r
+{\r
+ char message[MSG_SIZ];\r
+ long time, otime;\r
+\r
+ /* Note: this routine must be called when the clocks are stopped\r
+ or when they have *just* been set or switched; otherwise\r
+ it will be off by the time since the current tick started.\r
+ */\r
+ if (machineWhite) {\r
+ time = whiteTimeRemaining / 10;\r
+ otime = blackTimeRemaining / 10;\r
+ } else {\r
+ time = blackTimeRemaining / 10;\r
+ otime = whiteTimeRemaining / 10;\r
+ }\r
+ if (time <= 0) time = 1;\r
+ if (otime <= 0) otime = 1;\r
+ \r
+ sprintf(message, "time %ld\n", time);\r
+ SendToProgram(message, cps);\r
+\r
+ sprintf(message, "otim %ld\n", otime);\r
+ SendToProgram(message, cps);\r
+}\r
+\r
+int\r
+BoolFeature(p, name, loc, cps)\r
+ char **p;\r
+ char *name;\r
+ int *loc;\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ int len = strlen(name);\r
+ int val;\r
+ if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {\r
+ (*p) += len + 1;\r
+ sscanf(*p, "%d", &val);\r
+ *loc = (val != 0);\r
+ while (**p && **p != ' ') (*p)++;\r
+ sprintf(buf, "accepted %s\n", name);\r
+ SendToProgram(buf, cps);\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+int\r
+IntFeature(p, name, loc, cps)\r
+ char **p;\r
+ char *name;\r
+ int *loc;\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ int len = strlen(name);\r
+ if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {\r
+ (*p) += len + 1;\r
+ sscanf(*p, "%d", loc);\r
+ while (**p && **p != ' ') (*p)++;\r
+ sprintf(buf, "accepted %s\n", name);\r
+ SendToProgram(buf, cps);\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+int\r
+StringFeature(p, name, loc, cps)\r
+ char **p;\r
+ char *name;\r
+ char loc[];\r
+ ChessProgramState *cps;\r
+{\r
+ char buf[MSG_SIZ];\r
+ int len = strlen(name);\r
+ if (strncmp((*p), name, len) == 0\r
+ && (*p)[len] == '=' && (*p)[len+1] == '\"') {\r
+ (*p) += len + 2;\r
+ sscanf(*p, "%[^\"]", loc);\r
+ while (**p && **p != '\"') (*p)++;\r
+ if (**p == '\"') (*p)++;\r
+ sprintf(buf, "accepted %s\n", name);\r
+ SendToProgram(buf, cps);\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+void\r
+FeatureDone(cps, val)\r
+ ChessProgramState* cps;\r
+ int val;\r
+{\r
+ DelayedEventCallback cb = GetDelayedEvent();\r
+ if ((cb == InitBackEnd3 && cps == &first) ||\r
+ (cb == TwoMachinesEventIfReady && cps == &second)) {\r
+ CancelDelayedEvent();\r
+ ScheduleDelayedEvent(cb, val ? 1 : 3600000);\r
+ }\r
+ cps->initDone = val;\r
+}\r
+\r
+/* Parse feature command from engine */\r
+void\r
+ParseFeatures(args, cps)\r
+ char* args;\r
+ ChessProgramState *cps; \r
+{\r
+ char *p = args;\r
+ char *q;\r
+ int val;\r
+ char buf[MSG_SIZ];\r
+\r
+ for (;;) {\r
+ while (*p == ' ') p++;\r
+ if (*p == NULLCHAR) return;\r
+\r
+ if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;\r
+ if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue; \r
+ if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue; \r
+ if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue; \r
+ if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue; \r
+ if (BoolFeature(&p, "reuse", &val, cps)) {\r
+ /* Engine can disable reuse, but can't enable it if user said no */\r
+ if (!val) cps->reuse = FALSE;\r
+ continue;\r
+ }\r
+ if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;\r
+ if (StringFeature(&p, "myname", &cps->tidy, cps)) {\r
+ if (gameMode == TwoMachinesPlay) {\r
+ DisplayTwoMachinesTitle();\r
+ } else {\r
+ DisplayTitle("");\r
+ }\r
+ continue;\r
+ }\r
+ if (StringFeature(&p, "variants", &cps->variants, cps)) continue;\r
+ if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;\r
+ if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;\r
+ if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;\r
+ if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;\r
+ if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;\r
+ if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;\r
+ if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;\r
+ if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */\r
+ if (IntFeature(&p, "done", &val, cps)) {\r
+ FeatureDone(cps, val);\r
+ continue;\r
+ }\r
+ /* Added by Tord: */\r
+ if (BoolFeature(&p, "fen960", &cps->useFEN960, cps)) continue;\r
+ if (BoolFeature(&p, "oocastle", &cps->useOOCastle, cps)) continue;\r
+ /* End of additions by Tord */\r
+\r
+ /* unknown feature: complain and skip */\r
+ q = p;\r
+ while (*q && *q != '=') q++;\r
+ sprintf(buf, "rejected %.*s\n", q-p, p);\r
+ SendToProgram(buf, cps);\r
+ p = q;\r
+ if (*p == '=') {\r
+ p++;\r
+ if (*p == '\"') {\r
+ p++;\r
+ while (*p && *p != '\"') p++;\r
+ if (*p == '\"') p++;\r
+ } else {\r
+ while (*p && *p != ' ') p++;\r
+ }\r
+ }\r
+ }\r
+\r
+}\r
+\r
+void\r
+PeriodicUpdatesEvent(newState)\r
+ int newState;\r
+{\r
+ if (newState == appData.periodicUpdates)\r
+ return;\r
+\r
+ appData.periodicUpdates=newState;\r
+\r
+ /* Display type changes, so update it now */\r
+ DisplayAnalysis();\r
+\r
+ /* Get the ball rolling again... */\r
+ if (newState) {\r
+ AnalysisPeriodicEvent(1);\r
+ StartAnalysisClock();\r
+ }\r
+}\r
+\r
+void\r
+PonderNextMoveEvent(newState)\r
+ int newState;\r
+{\r
+ if (newState == appData.ponderNextMove) return;\r
+ if (gameMode == EditPosition) EditPositionDone();\r
+ if (newState) {\r
+ SendToProgram("hard\n", &first);\r
+ if (gameMode == TwoMachinesPlay) {\r
+ SendToProgram("hard\n", &second);\r
+ }\r
+ } else {\r
+ SendToProgram("easy\n", &first);\r
+ thinkOutput[0] = NULLCHAR;\r
+ if (gameMode == TwoMachinesPlay) {\r
+ SendToProgram("easy\n", &second);\r
+ }\r
+ }\r
+ appData.ponderNextMove = newState;\r
+}\r
+\r
+void\r
+ShowThinkingEvent(newState)\r
+ int newState;\r
+{\r
+ if (newState == appData.showThinking) return;\r
+ if (gameMode == EditPosition) EditPositionDone();\r
+ if (newState) {\r
+ SendToProgram("post\n", &first);\r
+ if (gameMode == TwoMachinesPlay) {\r
+ SendToProgram("post\n", &second);\r
+ }\r
+ } else {\r
+ SendToProgram("nopost\n", &first);\r
+ thinkOutput[0] = NULLCHAR;\r
+ if (gameMode == TwoMachinesPlay) {\r
+ SendToProgram("nopost\n", &second);\r
+ }\r
+ }\r
+ appData.showThinking = newState;\r
+}\r
+\r
+void\r
+AskQuestionEvent(title, question, replyPrefix, which)\r
+ char *title; char *question; char *replyPrefix; char *which;\r
+{\r
+ ProcRef pr = (which[0] == '1') ? first.pr : second.pr;\r
+ if (pr == NoProc) return;\r
+ AskQuestion(title, question, replyPrefix, pr);\r
+}\r
+\r
+void\r
+DisplayMove(moveNumber)\r
+ int moveNumber;\r
+{\r
+ char message[MSG_SIZ];\r
+ char res[MSG_SIZ];\r
+ char cpThinkOutput[MSG_SIZ];\r
+\r
+ if (moveNumber == forwardMostMove - 1 || \r
+ gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+\r
+ safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));\r
+\r
+ if (strchr(cpThinkOutput, '\n')) {\r
+ *strchr(cpThinkOutput, '\n') = NULLCHAR;\r
+ }\r
+ } else {\r
+ *cpThinkOutput = NULLCHAR;\r
+ }\r
+\r
+ /* [AS] Hide thinking from human user */\r
+ if( appData.hideThinkingFromHuman && gameMode != TwoMachinesPlay ) {\r
+ *cpThinkOutput = NULLCHAR;\r
+ if( thinkOutput[0] != NULLCHAR ) {\r
+ int i;\r
+\r
+ for( i=0; i<=hiddenThinkOutputState; i++ ) {\r
+ cpThinkOutput[i] = '.';\r
+ }\r
+ cpThinkOutput[i] = NULLCHAR;\r
+ hiddenThinkOutputState = (hiddenThinkOutputState + 1) % 3;\r
+ }\r
+ }\r
+\r
+ if (moveNumber == forwardMostMove - 1 &&\r
+ gameInfo.resultDetails != NULL) {\r
+ if (gameInfo.resultDetails[0] == NULLCHAR) {\r
+ sprintf(res, " %s", PGNResult(gameInfo.result));\r
+ } else {\r
+ sprintf(res, " {%s} %s",\r
+ gameInfo.resultDetails, PGNResult(gameInfo.result));\r
+ }\r
+ } else {\r
+ res[0] = NULLCHAR;\r
+ }\r
+ \r
+ if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
+ DisplayMessage(res, cpThinkOutput);\r
+ } else {\r
+ sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,\r
+ WhiteOnMove(moveNumber) ? " " : ".. ",\r
+ parseList[moveNumber], res);\r
+ DisplayMessage(message, cpThinkOutput);\r
+ }\r
+}\r
+\r
+void\r
+DisplayAnalysisText(text)\r
+ char *text;\r
+{\r
+ char buf[MSG_SIZ];\r
+\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+ sprintf(buf, "Analysis (%s)", first.tidy);\r
+ AnalysisPopUp(buf, text);\r
+ }\r
+}\r
+\r
+static int\r
+only_one_move(str)\r
+ char *str;\r
+{\r
+ while (*str && isspace(*str)) ++str;\r
+ while (*str && !isspace(*str)) ++str;\r
+ if (!*str) return 1;\r
+ while (*str && isspace(*str)) ++str;\r
+ if (!*str) return 1;\r
+ return 0;\r
+}\r
+\r
+void\r
+DisplayAnalysis()\r
+{\r
+ char buf[MSG_SIZ];\r
+ char lst[MSG_SIZ / 2];\r
+ double nps;\r
+ static char *xtra[] = { "", " (--)", " (++)" };\r
+ int h, m, s, cs;\r
+ \r
+ if (programStats.time == 0) {\r
+ programStats.time = 1;\r
+ }\r
+ \r
+ if (programStats.got_only_move) {\r
+ safeStrCpy(buf, programStats.movelist, sizeof(buf));\r
+ } else {\r
+ safeStrCpy( lst, programStats.movelist, sizeof(lst));\r
+\r
+ nps = (((double)programStats.nodes) /\r
+ (((double)programStats.time)/100.0));\r
+\r
+ cs = programStats.time % 100;\r
+ s = programStats.time / 100;\r
+ h = (s / (60*60));\r
+ s = s - h*60*60;\r
+ m = (s/60);\r
+ s = s - m*60;\r
+\r
+ if (programStats.moves_left > 0 && appData.periodicUpdates) {\r
+ if (programStats.move_name[0] != NULLCHAR) {\r
+ sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+ programStats.depth,\r
+ programStats.nr_moves-programStats.moves_left,\r
+ programStats.nr_moves, programStats.move_name,\r
+ ((float)programStats.score)/100.0, lst,\r
+ only_one_move(lst)?\r
+ xtra[programStats.got_fail] : "",\r
+ programStats.nodes, (int)nps, h, m, s, cs);\r
+ } else {\r
+ sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+ programStats.depth,\r
+ programStats.nr_moves-programStats.moves_left,\r
+ programStats.nr_moves, ((float)programStats.score)/100.0,\r
+ lst,\r
+ only_one_move(lst)?\r
+ xtra[programStats.got_fail] : "",\r
+ programStats.nodes, (int)nps, h, m, s, cs);\r
+ }\r
+ } else {\r
+ sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+ programStats.depth,\r
+ ((float)programStats.score)/100.0,\r
+ lst,\r
+ only_one_move(lst)?\r
+ xtra[programStats.got_fail] : "",\r
+ programStats.nodes, (int)nps, h, m, s, cs);\r
+ }\r
+ }\r
+ DisplayAnalysisText(buf);\r
+}\r
+\r
+void\r
+DisplayComment(moveNumber, text)\r
+ int moveNumber;\r
+ char *text;\r
+{\r
+ char title[MSG_SIZ];\r
+\r
+ if( appData.autoDisplayComment ) {\r
+ if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
+ strcpy(title, "Comment");\r
+ } else {\r
+ sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,\r
+ WhiteOnMove(moveNumber) ? " " : ".. ",\r
+ parseList[moveNumber]);\r
+ }\r
+\r
+ CommentPopUp(title, text);\r
+ }\r
+}\r
+\r
+/* This routine sends a ^C interrupt to gnuchess, to awaken it if it\r
+ * might be busy thinking or pondering. It can be omitted if your\r
+ * gnuchess is configured to stop thinking immediately on any user\r
+ * input. However, that gnuchess feature depends on the FIONREAD\r
+ * ioctl, which does not work properly on some flavors of Unix.\r
+ */\r
+void\r
+Attention(cps)\r
+ ChessProgramState *cps;\r
+{\r
+#if ATTENTION\r
+ if (!cps->useSigint) return;\r
+ if (appData.noChessProgram || (cps->pr == NoProc)) return;\r
+ switch (gameMode) {\r
+ case MachinePlaysWhite:\r
+ case MachinePlaysBlack:\r
+ case TwoMachinesPlay:\r
+ case IcsPlayingWhite:\r
+ case IcsPlayingBlack:\r
+ case AnalyzeMode:\r
+ case AnalyzeFile:\r
+ /* Skip if we know it isn't thinking */\r
+ if (!cps->maybeThinking) return;\r
+ if (appData.debugMode)\r
+ fprintf(debugFP, "Interrupting %s\n", cps->which);\r
+ InterruptChildProcess(cps->pr);\r
+ cps->maybeThinking = FALSE;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+#endif /*ATTENTION*/\r
+}\r
+\r
+int\r
+CheckFlags()\r
+{\r
+ if (whiteTimeRemaining <= 0) {\r
+ if (!whiteFlag) {\r
+ whiteFlag = TRUE;\r
+ if (appData.icsActive) {\r
+ if (appData.autoCallFlag &&\r
+ gameMode == IcsPlayingBlack && !blackFlag) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("flag\n");\r
+ }\r
+ } else {\r
+ if (blackFlag) {\r
+ if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+ } else {\r
+ if(gameMode != TwoMachinesPlay) DisplayTitle("White's flag fell");\r
+ if (appData.autoCallFlag) {\r
+ GameEnds(BlackWins, "Black wins on time", GE_XBOARD);\r
+ return TRUE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (blackTimeRemaining <= 0) {\r
+ if (!blackFlag) {\r
+ blackFlag = TRUE;\r
+ if (appData.icsActive) {\r
+ if (appData.autoCallFlag &&\r
+ gameMode == IcsPlayingWhite && !whiteFlag) {\r
+ SendToICS(ics_prefix);\r
+ SendToICS("flag\n");\r
+ }\r
+ } else {\r
+ if (whiteFlag) {\r
+ if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+ } else {\r
+ if(gameMode != TwoMachinesPlay) DisplayTitle("Black's flag fell");\r
+ if (appData.autoCallFlag) {\r
+ GameEnds(WhiteWins, "White wins on time", GE_XBOARD);\r
+ return TRUE;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+void\r
+CheckTimeControl()\r
+{\r
+ if (!appData.clockMode || appData.icsActive ||\r
+ gameMode == PlayFromGameFile || forwardMostMove == 0) return;\r
+\r
+ if (timeIncrement >= 0) {\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ blackTimeRemaining += timeIncrement;\r
+ } else {\r
+ whiteTimeRemaining += timeIncrement;\r
+ }\r
+ }\r
+ /*\r
+ * add time to clocks when time control is achieved\r
+ */\r
+ if (movesPerSession) {\r
+ switch ((forwardMostMove + 1) % (movesPerSession * 2)) {\r
+ case 0:\r
+ /* White made time control */\r
+ whiteTimeRemaining += GetTimeControlForWhite();\r
+ break;\r
+ case 1:\r
+ /* Black made time control */\r
+ blackTimeRemaining += GetTimeControlForBlack();\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+void\r
+DisplayBothClocks()\r
+{\r
+ int wom = gameMode == EditPosition ?\r
+ !blackPlaysFirst : WhiteOnMove(currentMove);\r
+ DisplayWhiteClock(whiteTimeRemaining, wom);\r
+ DisplayBlackClock(blackTimeRemaining, !wom);\r
+}\r
+\r
+\r
+/* Timekeeping seems to be a portability nightmare. I think everyone\r
+ has ftime(), but I'm really not sure, so I'm including some ifdefs\r
+ to use other calls if you don't. Clocks will be less accurate if\r
+ you have neither ftime nor gettimeofday.\r
+*/\r
+\r
+/* Get the current time as a TimeMark */\r
+void\r
+GetTimeMark(tm)\r
+ TimeMark *tm;\r
+{\r
+#if HAVE_GETTIMEOFDAY\r
+\r
+ struct timeval timeVal;\r
+ struct timezone timeZone;\r
+\r
+ gettimeofday(&timeVal, &timeZone);\r
+ tm->sec = (long) timeVal.tv_sec; \r
+ tm->ms = (int) (timeVal.tv_usec / 1000L);\r
+\r
+#else /*!HAVE_GETTIMEOFDAY*/\r
+#if HAVE_FTIME\r
+\r
+#include <sys/timeb.h>\r
+ struct timeb timeB;\r
+\r
+ ftime(&timeB);\r
+ tm->sec = (long) timeB.time;\r
+ tm->ms = (int) timeB.millitm;\r
+\r
+#else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/\r
+ tm->sec = (long) time(NULL);\r
+ tm->ms = 0;\r
+#endif\r
+#endif\r
+}\r
+\r
+/* Return the difference in milliseconds between two\r
+ time marks. We assume the difference will fit in a long!\r
+*/\r
+long\r
+SubtractTimeMarks(tm2, tm1)\r
+ TimeMark *tm2, *tm1;\r
+{\r
+ return 1000L*(tm2->sec - tm1->sec) +\r
+ (long) (tm2->ms - tm1->ms);\r
+}\r
+\r
+\r
+/*\r
+ * Code to manage the game clocks.\r
+ *\r
+ * In tournament play, black starts the clock and then white makes a move.\r
+ * We give the human user a slight advantage if he is playing white---the\r
+ * clocks don't run until he makes his first move, so it takes zero time.\r
+ * Also, we don't account for network lag, so we could get out of sync\r
+ * with GNU Chess's clock -- but then, referees are always right. \r
+ */\r
+\r
+static TimeMark tickStartTM;\r
+static long intendedTickLength;\r
+\r
+long\r
+NextTickLength(timeRemaining)\r
+ long timeRemaining;\r
+{\r
+ long nominalTickLength, nextTickLength;\r
+\r
+ if (timeRemaining > 0L && timeRemaining <= 10000L)\r
+ nominalTickLength = 100L;\r
+ else\r
+ nominalTickLength = 1000L;\r
+ nextTickLength = timeRemaining % nominalTickLength;\r
+ if (nextTickLength <= 0) nextTickLength += nominalTickLength;\r
+\r
+ return nextTickLength;\r
+}\r
+\r
+/* Stop clocks and reset to a fresh time control */\r
+void\r
+ResetClocks() \r
+{\r
+ (void) StopClockTimer();\r
+ if (appData.icsActive) {\r
+ whiteTimeRemaining = blackTimeRemaining = 0;\r
+ } else {\r
+ whiteTimeRemaining = GetTimeControlForWhite();\r
+ blackTimeRemaining = GetTimeControlForBlack();\r
+ }\r
+ if (whiteFlag || blackFlag) {\r
+ DisplayTitle("");\r
+ whiteFlag = blackFlag = FALSE;\r
+ }\r
+ DisplayBothClocks();\r
+}\r
+\r
+#define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */\r
+\r
+/* Decrement running clock by amount of time that has passed */\r
+void\r
+DecrementClocks()\r
+{\r
+ long timeRemaining;\r
+ long lastTickLength, fudge;\r
+ TimeMark now;\r
+\r
+ if (!appData.clockMode) return;\r
+ if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;\r
+ \r
+ GetTimeMark(&now);\r
+\r
+ lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+\r
+ /* Fudge if we woke up a little too soon */\r
+ fudge = intendedTickLength - lastTickLength;\r
+ if (fudge < 0 || fudge > FUDGE) fudge = 0;\r
+\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ timeRemaining = whiteTimeRemaining -= lastTickLength;\r
+ DisplayWhiteClock(whiteTimeRemaining - fudge,\r
+ WhiteOnMove(currentMove));\r
+ } else {\r
+ timeRemaining = blackTimeRemaining -= lastTickLength;\r
+ DisplayBlackClock(blackTimeRemaining - fudge,\r
+ !WhiteOnMove(currentMove));\r
+ }\r
+\r
+ if (CheckFlags()) return;\r
+ \r
+ tickStartTM = now;\r
+ intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;\r
+ StartClockTimer(intendedTickLength);\r
+\r
+ /* if the time remaining has fallen below the alarm threshold, sound the\r
+ * alarm. if the alarm has sounded and (due to a takeback or time control\r
+ * with increment) the time remaining has increased to a level above the\r
+ * threshold, reset the alarm so it can sound again. \r
+ */\r
+ \r
+ if (appData.icsActive && appData.icsAlarm) {\r
+\r
+ /* make sure we are dealing with the user's clock */\r
+ if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||\r
+ ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))\r
+ )) return;\r
+\r
+ if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {\r
+ alarmSounded = FALSE;\r
+ } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) { \r
+ PlayAlarmSound();\r
+ alarmSounded = TRUE;\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/* A player has just moved, so stop the previously running\r
+ clock and (if in clock mode) start the other one.\r
+ We redisplay both clocks in case we're in ICS mode, because\r
+ ICS gives us an update to both clocks after every move.\r
+ Note that this routine is called *after* forwardMostMove\r
+ is updated, so the last fractional tick must be subtracted\r
+ from the color that is *not* on move now.\r
+*/\r
+void\r
+SwitchClocks()\r
+{\r
+ long lastTickLength;\r
+ TimeMark now;\r
+ int flagged = FALSE;\r
+\r
+ GetTimeMark(&now);\r
+\r
+ if (StopClockTimer() && appData.clockMode) {\r
+ lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ blackTimeRemaining -= lastTickLength;\r
+ } else {\r
+ whiteTimeRemaining -= lastTickLength;\r
+ }\r
+ /* [HGM] save time for PGN file if engine did not give it */\r
+ if(pvInfoList[forwardMostMove-1].time == -1)\r
+ pvInfoList[forwardMostMove-1].time = lastTickLength/100;\r
+ flagged = CheckFlags();\r
+ }\r
+ CheckTimeControl();\r
+\r
+ if (flagged || !appData.clockMode) return;\r
+\r
+ switch (gameMode) {\r
+ case MachinePlaysBlack:\r
+ case MachinePlaysWhite:\r
+ case BeginningOfGame:\r
+ if (pausing) return;\r
+ break;\r
+\r
+ case EditGame:\r
+ case PlayFromGameFile:\r
+ case IcsExamining:\r
+ return;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ tickStartTM = now;\r
+ intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?\r
+ whiteTimeRemaining : blackTimeRemaining);\r
+ StartClockTimer(intendedTickLength);\r
+}\r
+ \r
+\r
+/* Stop both clocks */\r
+void\r
+StopClocks()\r
+{ \r
+ long lastTickLength;\r
+ TimeMark now;\r
+\r
+ if (!StopClockTimer()) return;\r
+ if (!appData.clockMode) return;\r
+\r
+ GetTimeMark(&now);\r
+\r
+ lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+ if (WhiteOnMove(forwardMostMove)) {\r
+ whiteTimeRemaining -= lastTickLength;\r
+ DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));\r
+ } else {\r
+ blackTimeRemaining -= lastTickLength;\r
+ DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));\r
+ }\r
+ CheckFlags();\r
+}\r
+ \r
+/* Start clock of player on move. Time may have been reset, so\r
+ if clock is already running, stop and restart it. */\r
+void\r
+StartClocks()\r
+{\r
+ (void) StopClockTimer(); /* in case it was running already */\r
+ DisplayBothClocks();\r
+ if (CheckFlags()) return;\r
+\r
+ if (!appData.clockMode) return;\r
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;\r
+\r
+ GetTimeMark(&tickStartTM);\r
+ intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?\r
+ whiteTimeRemaining : blackTimeRemaining);\r
+ StartClockTimer(intendedTickLength);\r
+}\r
+\r
+char *\r
+TimeString(ms)\r
+ long ms;\r
+{\r
+ long second, minute, hour, day;\r
+ char *sign = "";\r
+ static char buf[32];\r
+ \r
+ if (ms > 0 && ms <= 9900) {\r
+ /* convert milliseconds to tenths, rounding up */\r
+ double tenths = floor( ((double)(ms + 99L)) / 100.00 );\r
+\r
+ sprintf(buf, " %03.1f ", tenths/10.0);\r
+ return buf;\r
+ }\r
+\r
+ /* convert milliseconds to seconds, rounding up */\r
+ /* use floating point to avoid strangeness of integer division\r
+ with negative dividends on many machines */\r
+ second = (long) floor(((double) (ms + 999L)) / 1000.0);\r
+\r
+ if (second < 0) {\r
+ sign = "-";\r
+ second = -second;\r
+ }\r
+ \r
+ day = second / (60 * 60 * 24);\r
+ second = second % (60 * 60 * 24);\r
+ hour = second / (60 * 60);\r
+ second = second % (60 * 60);\r
+ minute = second / 60;\r
+ second = second % 60;\r
+ \r
+ if (day > 0)\r
+ sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",\r
+ sign, day, hour, minute, second);\r
+ else if (hour > 0)\r
+ sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);\r
+ else\r
+ sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);\r
+ \r
+ return buf;\r
+}\r
+\r
+\r
+/*\r
+ * This is necessary because some C libraries aren't ANSI C compliant yet.\r
+ */\r
+char *\r
+StrStr(string, match)\r
+ char *string, *match;\r
+{\r
+ int i, length;\r
+ \r
+ length = strlen(match);\r
+ \r
+ for (i = strlen(string) - length; i >= 0; i--, string++)\r
+ if (!strncmp(match, string, length))\r
+ return string;\r
+ \r
+ return NULL;\r
+}\r
+\r
+char *\r
+StrCaseStr(string, match)\r
+ char *string, *match;\r
+{\r
+ int i, j, length;\r
+ \r
+ length = strlen(match);\r
+ \r
+ for (i = strlen(string) - length; i >= 0; i--, string++) {\r
+ for (j = 0; j < length; j++) {\r
+ if (ToLower(match[j]) != ToLower(string[j]))\r
+ break;\r
+ }\r
+ if (j == length) return string;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+#ifndef _amigados\r
+int\r
+StrCaseCmp(s1, s2)\r
+ char *s1, *s2;\r
+{\r
+ char c1, c2;\r
+ \r
+ for (;;) {\r
+ c1 = ToLower(*s1++);\r
+ c2 = ToLower(*s2++);\r
+ if (c1 > c2) return 1;\r
+ if (c1 < c2) return -1;\r
+ if (c1 == NULLCHAR) return 0;\r
+ }\r
+}\r
+\r
+\r
+int\r
+ToLower(c)\r
+ int c;\r
+{\r
+ return isupper(c) ? tolower(c) : c;\r
+}\r
+\r
+\r
+int\r
+ToUpper(c)\r
+ int c;\r
+{\r
+ return islower(c) ? toupper(c) : c;\r
+}\r
+#endif /* !_amigados */\r
+\r
+char *\r
+StrSave(s)\r
+ char *s;\r
+{\r
+ char *ret;\r
+\r
+ if ((ret = (char *) malloc(strlen(s) + 1))) {\r
+ strcpy(ret, s);\r
+ }\r
+ return ret;\r
+}\r
+\r
+char *\r
+StrSavePtr(s, savePtr)\r
+ char *s, **savePtr;\r
+{\r
+ if (*savePtr) {\r
+ free(*savePtr);\r
+ }\r
+ if ((*savePtr = (char *) malloc(strlen(s) + 1))) {\r
+ strcpy(*savePtr, s);\r
+ }\r
+ return(*savePtr);\r
+}\r
+\r
+char *\r
+PGNDate()\r
+{\r
+ time_t clock;\r
+ struct tm *tm;\r
+ char buf[MSG_SIZ];\r
+\r
+ clock = time((time_t *)NULL);\r
+ tm = localtime(&clock);\r
+ sprintf(buf, "%04d.%02d.%02d",\r
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);\r
+ return StrSave(buf);\r
+}\r
+\r
+\r
+char *\r
+PositionToFEN(move, useFEN960)\r
+ int move;\r
+ int useFEN960;\r
+{\r
+ int i, j, fromX, fromY, toX, toY;\r
+ int whiteToPlay;\r
+ char buf[128];\r
+ char *p, *q;\r
+ int emptycount;\r
+\r
+ whiteToPlay = (gameMode == EditPosition) ?\r
+ !blackPlaysFirst : (move % 2 == 0);\r
+ p = buf;\r
+\r
+ /* Piece placement data */\r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ emptycount = 0;\r
+ for (j = 0; j < BOARD_WIDTH; j++) {\r
+ if (boards[move][i][j] == EmptySquare) {\r
+ emptycount++;\r
+ } else {\r
+ if (emptycount > 0) {\r
+ if(emptycount<10) /* [HGM] can be >= 10 */\r
+ *p++ = '0' + emptycount;\r
+ else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
+ emptycount = 0;\r
+ }\r
+ *p++ = PieceToChar(boards[move][i][j]);\r
+ }\r
+ }\r
+ if (emptycount > 0) {\r
+ if(emptycount<10) /* [HGM] can be >= 10 */\r
+ *p++ = '0' + emptycount;\r
+ else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
+ emptycount = 0;\r
+ }\r
+ *p++ = '/';\r
+ }\r
+ *(p - 1) = ' ';\r
+\r
+ /* Active color */\r
+ *p++ = whiteToPlay ? 'w' : 'b';\r
+ *p++ = ' ';\r
+\r
+ /* HACK: we don't keep track of castling availability, so fake it! */\r
+ /* Tord! please fix with the aid of castlingRights[move][...] */\r
+\r
+ /* PUSH Fabien & Tord */\r
+\r
+ /* Declare all potential FRC castling rights (conservative) */\r
+ /* outermost rook on each side of the king */\r
+\r
+ if( gameInfo.variant == VariantFischeRandom ) {\r
+ int fk, fr;\r
+\r
+ q = p;\r
+\r
+ /* White castling rights */\r
+\r
+ for (fk = 1; fk < BOARD_WIDTH-1; fk++) {\r
+\r
+ if (boards[move][0][fk] == WhiteKing) {\r
+\r
+ for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */\r
+ if (boards[move][0][fr] == WhiteRook) {\r
+ *p++ = useFEN960 ? 'A' + fr : 'K';\r
+ break;\r
+ }\r
+ }\r
+\r
+ for (fr = 0; fr < fk; fr++) { /* A side */\r
+ if (boards[move][0][fr] == WhiteRook) {\r
+ *p++ = useFEN960 ? 'A' + fr : 'Q';\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /* Black castling rights */\r
+\r
+ for (fk = 1; fk < BOARD_WIDTH-1; fk++) {\r
+\r
+ if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) {\r
+\r
+ for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */\r
+ if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
+ *p++ = useFEN960 ? 'a' + fr : 'k';\r
+ break;\r
+ }\r
+ }\r
+\r
+ for (fr = 0; fr < fk; fr++) { /* A side */\r
+ if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
+ *p++ = useFEN960 ? 'a' + fr : 'q';\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (q == p) *p++ = '-'; /* No castling rights */\r
+ *p++ = ' ';\r
+ }\r
+ else {\r
+ q = p;\r
+\r
+#ifdef OLDCASTLINGCODE\r
+ if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) {\r
+ if (boards[move][0][BOARD_WIDTH-1] == WhiteRook) *p++ = 'K';\r
+ if (boards[move][0][0] == WhiteRook) *p++ = 'Q';\r
+ }\r
+ if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) {\r
+ if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k';\r
+ if (boards[move][BOARD_HEIGHT-1][0] == BlackRook) *p++ = 'q';\r
+ } \r
+#else\r
+ /* [HGM] write true castling rights */\r
+ if( nrCastlingRights == 6 ) {\r
+ if(castlingRights[move][0] == BOARD_WIDTH-1 &&\r
+ castlingRights[move][2] >= 0 ) *p++ = 'K';\r
+ if(castlingRights[move][1] == 0 &&\r
+ castlingRights[move][2] >= 0 ) *p++ = 'Q';\r
+ if(castlingRights[move][3] == BOARD_WIDTH-1 &&\r
+ castlingRights[move][5] >= 0 ) *p++ = 'k';\r
+ if(castlingRights[move][4] == 0 &&\r
+ castlingRights[move][5] >= 0 ) *p++ = 'q';\r
+ }\r
+#endif\r
+ if (q == p) *p++ = '-';\r
+ *p++ = ' ';\r
+ }\r
+\r
+ /* POP Fabien & Tord */\r
+\r
+ /* En passant target square */\r
+ if (move > backwardMostMove) {\r
+ fromX = moveList[move - 1][0] - 'a';\r
+ fromY = moveList[move - 1][1] - ONE;\r
+ toX = moveList[move - 1][2] - 'a';\r
+ toY = moveList[move - 1][3] - ONE;\r
+ if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) &&\r
+ toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) &&\r
+ boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&\r
+ fromX == toX) {\r
+ /* 2-square pawn move just happened */\r
+ *p++ = toX + 'a';\r
+ *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';\r
+ } else {\r
+ *p++ = '-';\r
+ }\r
+ } else {\r
+ *p++ = '-';\r
+ }\r
+\r
+ /* [HGM] find reversible plies */\r
+ { int i = 0, j=move;\r
+\r
+ if (appData.debugMode) { int k;\r
+ fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
+ for(k=backwardMostMove; k<=forwardMostMove; k++)\r
+ fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
+\r
+ }\r
+\r
+ while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++;\r
+ if( j == backwardMostMove ) i += initialRulePlies;\r
+ sprintf(p, " %d", i);\r
+ p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
+ }\r
+ /* Fullmove number */\r
+ sprintf(p, " %d", (move / 2) + 1);\r
+ \r
+ return StrSave(buf);\r
+}\r
+\r
+Boolean\r
+ParseFEN(board, blackPlaysFirst, fen)\r
+ Board board;\r
+ int *blackPlaysFirst;\r
+ char *fen;\r
+{\r
+ int i, j;\r
+ char *p;\r
+ int emptycount;\r
+\r
+ p = fen;\r
+\r
+ /* Piece placement data */\r
+ for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+ j = 0;\r
+ for (;;) {\r
+ if (*p == '/' || *p == ' ') {\r
+ if (*p == '/') p++;\r
+ emptycount = BOARD_WIDTH - j;\r
+ while (emptycount--) board[i][j++] = EmptySquare;\r
+ break;\r
+#if(BOARD_SIZE >= 10)\r
+ } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */\r
+ p++; emptycount=10;\r
+ if (j + emptycount > BOARD_WIDTH) return FALSE;\r
+ while (emptycount--) board[i][j++] = EmptySquare;\r
+#endif\r
+ } else if (isdigit(*p)) {\r
+ emptycount = *p++ - '0';\r
+ while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */\r
+ if (j + emptycount > BOARD_WIDTH) return FALSE;\r
+ while (emptycount--) board[i][j++] = EmptySquare;\r
+ } else if (isalpha(*p)) {\r
+ if (j >= BOARD_WIDTH) return FALSE;\r
+ board[i][j++] = CharToPiece(*p++);\r
+ } else {\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+ while (*p == '/' || *p == ' ') p++;\r
+\r
+ /* Active color */\r
+ switch (*p++) {\r
+ case 'w':\r
+ *blackPlaysFirst = FALSE;\r
+ break;\r
+ case 'b': \r
+ *blackPlaysFirst = TRUE;\r
+ break;\r
+ default:\r
+ return FALSE;\r
+ }\r
+\r
+ /* [HGM] We NO LONGER ignore the rest of the FEN notation */\r
+ /* return the extra info in global variiables */\r
+ {\r
+ /* set defaults in case FEN is incomplete */\r
+ FENepStatus = EP_UNKNOWN;\r
+ for(i=0; i<nrCastlingRights; i++ ) {\r
+ FENcastlingRights[i] = initialRights[i];\r
+ } /* assume possible unless obviously impossible */\r
+ if(board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;\r
+ if(board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;\r
+ if(board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;\r
+ if(board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;\r
+ if(board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;\r
+ if(board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;\r
+ FENrulePlies = 0;\r
+\r
+ while(*p==' ') p++;\r
+\r
+ if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
+ /* castling indicator present, so default is impossible */\r
+ for(i=0; i<nrCastlingRights; i++ ) {\r
+ FENcastlingRights[i] = -1;\r
+ }\r
+ }\r
+ while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
+ switch(*p++) {\r
+ case'K':\r
+ FENcastlingRights[0] = BOARD_WIDTH-1;\r
+ FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+ break;\r
+ case'Q':\r
+ FENcastlingRights[1] = 0;\r
+ FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+ break;\r
+ case'k':\r
+ FENcastlingRights[3] = BOARD_WIDTH-1;\r
+ FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+ break;\r
+ case'q':\r
+ FENcastlingRights[4] = 0;\r
+ FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+ break;\r
+ /* Tord! FRC! */\r
+ }\r
+ }\r
+\r
+ while(*p==' ') p++;\r
+\r
+\r
+ if(*p=='-') {\r
+ p++; FENepStatus = EP_NONE;\r
+ } else {\r
+ char c = *p++ - 'a';\r
+\r
+ if(c < 0 || c >= BOARD_WIDTH) return TRUE;\r
+ if(*p >= '0' && *p <='9') *p++;\r
+ FENepStatus = c;\r
+ }\r
+\r
+ if(sscanf(p, "%d", &i) == 1) {\r
+ FENrulePlies = i; /* 50-move ply counter */\r
+ /* (The move number is still ignored) */\r
+ }\r
+ }\r
+ return TRUE;\r
+}\r
+ \r
+void\r
+EditPositionPasteFEN(char *fen)\r
+{\r
+ if (fen != NULL) {\r
+ Board initial_position;\r
+\r
+ if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {\r
+ DisplayError("Bad FEN position in clipboard", 0);\r
+ return ;\r
+ } else {\r
+ int savedBlackPlaysFirst = blackPlaysFirst;\r
+ EditPositionEvent();\r
+ blackPlaysFirst = savedBlackPlaysFirst;\r
+ CopyBoard(boards[0], initial_position);\r
+ /* [HGM] copy FEN attributes as well */\r
+ { int i;\r
+ initialRulePlies = FENrulePlies;\r
+ epStatus[0] = FENepStatus;\r
+ for( i=0; i<nrCastlingRights; i++ )\r
+ castlingRights[0][i] = FENcastlingRights[i];\r
+ }\r
+ EditPositionDone();\r
+ DisplayBothClocks();\r
+ DrawPosition(FALSE, boards[currentMove]);\r
+ }\r
+ }\r
+}\r
-/* \r
+/*\r
* WinBoard.c -- Windows NT front end to XBoard\r
- * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $
+ * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
*\r
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
* Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
\r
#include <stdio.h>\r
#include <stdlib.h>\r
-#include <time.h>
+#include <time.h>\r
#include <malloc.h>\r
#include <sys/stat.h>\r
#include <fcntl.h>\r
#include "wsockerr.h"\r
#include "defaults.h"\r
\r
-#include "wsnap.h"
-
-void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
-
-int myrandom(void);
-void mysrandom(unsigned int seed);
-
+#include "wsnap.h"\r
+\r
+void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
+\r
+ int myrandom(void);\r
+ void mysrandom(unsigned int seed);\r
+\r
+extern int whiteFlag, blackFlag;\r
+\r
typedef struct {\r
ChessSquare piece; \r
POINT pos; /* window coordinates of current pos */\r
char *firstChessProgramNames;\r
char *secondChessProgramNames;\r
\r
-#define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */
+#define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
\r
#define PALETTESIZE 256\r
\r
\r
HWND hCommPort = NULL; /* currently open comm port */\r
static HWND hwndPause; /* pause button */\r
-static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];\r
+static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
static HBRUSH lightSquareBrush, darkSquareBrush,\r
whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
static int lastSizing = 0;\r
static int prevStderrPort;\r
\r
-/* [AS] Support for background textures */
-#define BACK_TEXTURE_MODE_DISABLED 0
-#define BACK_TEXTURE_MODE_PLAIN 1
-#define BACK_TEXTURE_MODE_FULL_RANDOM 2
-
-static HBITMAP liteBackTexture = NULL;
-static HBITMAP darkBackTexture = NULL;
-static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
-static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
-static int backTextureSquareSize = 0;
-static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];
-
+/* [AS] Support for background textures */\r
+#define BACK_TEXTURE_MODE_DISABLED 0\r
+#define BACK_TEXTURE_MODE_PLAIN 1\r
+#define BACK_TEXTURE_MODE_FULL_RANDOM 2\r
+\r
+static HBITMAP liteBackTexture = NULL;\r
+static HBITMAP darkBackTexture = NULL;\r
+static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
+static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
+static int backTextureSquareSize = 0;\r
+static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
+\r
#if __GNUC__ && !defined(_winmajor)\r
#define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
#else\r
#define MF(x) {x, {0, }, {0, }, 0}\r
MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
{\r
- { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
- { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
+ { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
+ { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
};\r
\r
MyFont *font[NUM_SIZES][NUM_FONTS];\r
void ParseIcsTextMenu(char *icsTextMenuString);\r
VOID PopUpMoveDialog(char firstchar);\r
VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
-
-/* [AS] */
-int NewGameFRC();
-int GameListOptions();
-
-HWND moveHistoryDialog = NULL;
-BOOLEAN moveHistoryDialogUp = FALSE;
-
-WindowPlacement wpMoveHistory;
-
-HWND evalGraphDialog = NULL;
-BOOLEAN evalGraphDialogUp = FALSE;
-
-WindowPlacement wpEvalGraph;
-
-HWND engineOutputDialog = NULL;
-BOOLEAN engineOutputDialogUp = FALSE;
-
-WindowPlacement wpEngineOutput;
-
-VOID MoveHistoryPopUp();
-VOID MoveHistoryPopDown();
-VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );
-BOOL MoveHistoryIsUp();
-
-VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
-VOID EvalGraphPopUp();
-VOID EvalGraphPopDown();
-BOOL EvalGraphIsUp();
-
-VOID EngineOutputPopUp();
-VOID EngineOutputPopDown();
-BOOL EngineOutputIsUp();
-VOID EngineOutputUpdate( FrontEndProgramStats * stats );
\r
+/* [AS] */\r
+int NewGameFRC();\r
+int GameListOptions();\r
+\r
+HWND moveHistoryDialog = NULL;\r
+BOOLEAN moveHistoryDialogUp = FALSE;\r
+\r
+WindowPlacement wpMoveHistory;\r
+\r
+HWND evalGraphDialog = NULL;\r
+BOOLEAN evalGraphDialogUp = FALSE;\r
+\r
+WindowPlacement wpEvalGraph;\r
+\r
+HWND engineOutputDialog = NULL;\r
+BOOLEAN engineOutputDialogUp = FALSE;\r
+\r
+WindowPlacement wpEngineOutput;\r
+\r
+VOID MoveHistoryPopUp();\r
+VOID MoveHistoryPopDown();\r
+VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
+BOOL MoveHistoryIsUp();\r
+\r
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
+VOID EvalGraphPopUp();\r
+VOID EvalGraphPopDown();\r
+BOOL EvalGraphIsUp();\r
+\r
+VOID EngineOutputPopUp();\r
+VOID EngineOutputPopDown();\r
+BOOL EngineOutputIsUp();\r
+VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
+\r
+VOID GothicPopUp(char *title);\r
/*\r
* Setting "frozen" should disable all user input other than deleting\r
* the window. We do this while engines are initializing themselves.\r
LPSTR lpCmdLine, int nCmdShow)\r
{\r
MSG msg;\r
- HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
+ HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
\r
debugFP = stderr;\r
\r
\r
hAccelMain = LoadAccelerators (hInstance, szAppName);\r
hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
- hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
+ hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
\r
/* Acquire and dispatch messages until a WM_QUIT message is received. */\r
\r
0)) /* highest message to examine */\r
{\r
if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
- !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
- !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
- !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
+ !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
+ !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
+ !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
!(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
!(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
!(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
!(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
- !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
+ !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
!(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
TranslateMessage(&msg); /* Translates virtual key codes */\r
DispatchMessage(&msg); /* Dispatches message to window */\r
void\r
EnsureOnScreen(int *x, int *y)\r
{\r
+ int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
/* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
if (*x > screenWidth - 32) *x = 0;\r
if (*y > screenHeight - 32) *y = 0;\r
+ if (*x < 10) *x = 10;\r
+ if (*y < gap) *y = gap;\r
}\r
\r
BOOL\r
}\r
InitAppData(lpCmdLine); /* Get run-time parameters */\r
if (appData.debugMode) {\r
- debugFP = fopen(appData.nameOfDebugFile, "w");
+ debugFP = fopen(appData.nameOfDebugFile, "w");\r
setbuf(debugFP, NULL);\r
}\r
\r
InitBackEnd1();\r
\r
- InitEngineUCI( installDir, &first );
- InitEngineUCI( installDir, &second );
-
+ InitEngineUCI( installDir, &first );\r
+ InitEngineUCI( installDir, &second );\r
+\r
/* Create a main window for this application instance. */\r
hwnd = CreateWindow(szAppName, szTitle,\r
(WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
size that fits on this screen as the default. */\r
InitDrawingSizes((BoardSize)ibs, 0);\r
if (boardSize == (BoardSize)-1 &&\r
- winHeight <= screenHeight && winWidth <= screenWidth) {\r
+ winHeight <= screenHeight\r
+ - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
+ && winWidth <= screenWidth) {\r
boardSize = (BoardSize)ibs;\r
}\r
}\r
InitMenuChecks();\r
buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
\r
- /* [AS] Load textures if specified */
- ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
-
- if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
- liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
- liteBackTextureMode = appData.liteBackTextureMode;
-
- if (liteBackTexture == NULL && appData.debugMode) {
- fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
- }
- }
-
- if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
- darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
- darkBackTextureMode = appData.darkBackTextureMode;
-
- if (darkBackTexture == NULL && appData.debugMode) {
- fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
- }
- }
-
- mysrandom( (unsigned) time(NULL) );
-
+ /* [AS] Load textures if specified */\r
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
+ \r
+ if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
+ liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+ liteBackTextureMode = appData.liteBackTextureMode;\r
+\r
+ if (liteBackTexture == NULL && appData.debugMode) {\r
+ fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
+ }\r
+ }\r
+ \r
+ if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
+ darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+ darkBackTextureMode = appData.darkBackTextureMode;\r
+\r
+ if (darkBackTexture == NULL && appData.debugMode) {\r
+ fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
+ }\r
+ }\r
+\r
+ mysrandom( (unsigned) time(NULL) );\r
+\r
/* Make a console window if needed */\r
if (appData.icsActive) {\r
ConsoleCreate();\r
}\r
\r
- /* [AS] Restore layout */
- if( wpMoveHistory.visible ) {
- MoveHistoryPopUp();
- }
-
- if( wpEvalGraph.visible ) {
- EvalGraphPopUp();
- }
-
- if( wpEngineOutput.visible ) {
- EngineOutputPopUp();
- }
-
+ /* [AS] Restore layout */\r
+ if( wpMoveHistory.visible ) {\r
+ MoveHistoryPopUp();\r
+ }\r
+\r
+ if( wpEvalGraph.visible ) {\r
+ EvalGraphPopUp();\r
+ }\r
+\r
+ if( wpEngineOutput.visible ) {\r
+ EngineOutputPopUp();\r
+ }\r
+\r
InitBackEnd2();\r
\r
/* Make the window visible; update its client area; and return "success" */\r
SetWindowPlacement(hwndMain, &wp);\r
\r
SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
- 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
-
- /* [AS] Disable the FRC stuff if not playing the proper variant */
- if( gameInfo.variant != VariantFischeRandom ) {
- EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );
- }
-
+ 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
+\r
+ /* [AS] Disable the FRC stuff if not playing the proper variant */\r
+ if( gameInfo.variant != VariantFischeRandom ) {\r
+ EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
+ }\r
+#ifdef FAIRY\r
+#ifdef GOTHIC\r
+ /* [HGM] Gothic licensing requirement */\r
+ if(gameInfo.variant == VariantGothic)\r
+ GothicPopUp(GOTHIC);\r
+#endif // GOTHIC\r
+#endif // FAIRY\r
if (hwndConsole) {\r
#if AOT_CONSOLE\r
SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
{ "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
{ "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
{ "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
- { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */
+ { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
{ "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
TRUE }, /* must come after all fonts */\r
{ "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
{ "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
- { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },
- { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },
+ { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
+ { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
{ "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
{ "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
- /* [AS] New features */
- { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },
- { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },
- { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },
- { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },
- { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },
- { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },
- { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },
- { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },
- { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },
- { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },
- { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },
- { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },
- { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },
- { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },
- { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },
- { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },
- { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },
- { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },
- { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },
- { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
- { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
- { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },
- { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },
- { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },
- { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },
- { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },
- { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },
- { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },
- { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },
- { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },
- { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },
- { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },
- { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },
- { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },
- { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },
- { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },
- { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },
- { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },
- { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },
- { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },
- { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },
- { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },
- { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },
- { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },
- { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },
- { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE },
- { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },
- { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },
-
- /* [AS] Layout stuff */
- { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },
- { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },
- { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },
- { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },
- { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },
-
- { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },
- { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },
- { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },
- { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },
- { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },
-
- { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },
- { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },
- { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },
- { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },
- { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },
-
+ /* [AS] New features */\r
+ { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
+ { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
+ { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
+ { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
+ { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
+ { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
+ { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
+ { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
+ { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
+ { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
+ { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
+ { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
+ { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
+ { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
+ { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
+ { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
+ { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
+ { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
+ { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
+ { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
+ { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
+ { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
+ { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
+ { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
+ { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
+ { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
+ { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
+ { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
+ { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
+ { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
+ { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
+ { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
+ { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
+ { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
+ { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
+ { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
+ { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
+ { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
+ { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
+ { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
+ { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
+ { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
+ { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
+ { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
+ { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
+ { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
+ { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
+ { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
+\r
+ /* [AS] Layout stuff */\r
+ { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
+ { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
+ { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
+ { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
+ { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
+\r
+ { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
+ { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
+ { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
+ { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
+ { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
+\r
+ { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
+ { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
+ { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
+ { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
+ { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
+\r
+ /* [HGM] User-selectable board size */\r
+ { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
+ { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
+ { "matchPause", ArgInt, (LPVOID) &appData.matchPause, FALSE },\r
+ { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
+ { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
+ { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
+\r
#ifdef ZIPPY\r
{ "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
{ "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
appData.firstProtocolVersion = PROTOVER;\r
appData.secondProtocolVersion = PROTOVER;\r
appData.showButtonBar = TRUE;\r
-
- /* [AS] New properties (see comments in header file) */
- appData.firstScoreIsAbsolute = FALSE;
- appData.secondScoreIsAbsolute = FALSE;
- appData.saveExtendedInfoInPGN = FALSE;
- appData.hideThinkingFromHuman = FALSE;
- appData.liteBackTextureFile = "";
- appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
- appData.darkBackTextureFile = "";
- appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
- appData.renderPiecesWithFont = "";
- appData.fontToPieceTable = "";
- appData.fontBackColorWhite = 0;
- appData.fontForeColorWhite = 0;
- appData.fontBackColorBlack = 0;
- appData.fontForeColorBlack = 0;
- appData.fontPieceSize = 80;
- appData.overrideLineGap = 1;
- appData.adjudicateLossThreshold = 0;
- appData.delayBeforeQuit = 0;
- appData.delayAfterQuit = 0;
- appData.nameOfDebugFile = "winboard.debug";
- appData.pgnEventHeader = "Computer Chess Game";
- appData.defaultFrcPosition = -1;
- appData.gameListTags = GLT_DEFAULT_TAGS;
- appData.saveOutOfBookInfo = TRUE;
- appData.showEvalInMoveHistory = TRUE;
- appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );
- appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );
- appData.highlightMoveWithArrow = FALSE;
- appData.highlightArrowColor = ParseColorName( "#FFFF80" );
- appData.useStickyWindows = TRUE;
- appData.adjudicateDrawMoves = 0;
- appData.autoDisplayComment = TRUE;
- appData.autoDisplayTags = TRUE;
- appData.firstIsUCI = FALSE;
- appData.secondIsUCI = FALSE;
- appData.firstHasOwnBookUCI = TRUE;
- appData.secondHasOwnBookUCI = TRUE;
- appData.polyglotDir = "";
- appData.usePolyglotBook = FALSE;
- appData.polyglotBook = "";
- appData.defaultHashSize = 64;
- appData.defaultCacheSizeEGTB = 4;
- appData.defaultPathEGTB = "c:\\egtb";
-
- InitWindowPlacement( &wpMoveHistory );
- InitWindowPlacement( &wpEvalGraph );
- InitWindowPlacement( &wpEngineOutput );
-
+\r
+ /* [AS] New properties (see comments in header file) */\r
+ appData.firstScoreIsAbsolute = FALSE;\r
+ appData.secondScoreIsAbsolute = FALSE;\r
+ appData.saveExtendedInfoInPGN = FALSE;\r
+ appData.hideThinkingFromHuman = FALSE;\r
+ appData.liteBackTextureFile = "";\r
+ appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
+ appData.darkBackTextureFile = "";\r
+ appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
+ appData.renderPiecesWithFont = "";\r
+ appData.fontToPieceTable = "";\r
+ appData.fontBackColorWhite = 0;\r
+ appData.fontForeColorWhite = 0;\r
+ appData.fontBackColorBlack = 0;\r
+ appData.fontForeColorBlack = 0;\r
+ appData.fontPieceSize = 80;\r
+ appData.overrideLineGap = 1;\r
+ appData.adjudicateLossThreshold = 0;\r
+ appData.delayBeforeQuit = 0;\r
+ appData.delayAfterQuit = 0;\r
+ appData.nameOfDebugFile = "winboard.debug";\r
+ appData.pgnEventHeader = "Computer Chess Game";\r
+ appData.defaultFrcPosition = -1;\r
+ appData.gameListTags = GLT_DEFAULT_TAGS;\r
+ appData.saveOutOfBookInfo = TRUE;\r
+ appData.showEvalInMoveHistory = TRUE;\r
+ appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
+ appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
+ appData.highlightMoveWithArrow = FALSE;\r
+ appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
+ appData.useStickyWindows = TRUE;\r
+ appData.adjudicateDrawMoves = 0;\r
+ appData.autoDisplayComment = TRUE;\r
+ appData.autoDisplayTags = TRUE;\r
+ appData.firstIsUCI = FALSE;\r
+ appData.secondIsUCI = FALSE;\r
+ appData.firstHasOwnBookUCI = TRUE;\r
+ appData.secondHasOwnBookUCI = TRUE;\r
+ appData.polyglotDir = "";\r
+ appData.usePolyglotBook = FALSE;\r
+ appData.polyglotBook = "";\r
+ appData.defaultHashSize = 64;\r
+ appData.defaultCacheSizeEGTB = 4;\r
+ appData.defaultPathEGTB = "c:\\egtb";\r
+\r
+ InitWindowPlacement( &wpMoveHistory );\r
+ InitWindowPlacement( &wpEvalGraph );\r
+ InitWindowPlacement( &wpEngineOutput );\r
+\r
+ /* [HGM] User-selectable board size */\r
+ appData.NrFiles = 8;\r
+ appData.NrRanks = 8;\r
+ appData.matchPause = 10000;\r
+ appData.testClaims = FALSE;\r
+ appData.ruleMoves = 51;\r
+ appData.drawRepeats = 6;\r
+\r
#ifdef ZIPPY\r
appData.zippyTalk = ZIPPY_TALK;\r
appData.zippyPlay = ZIPPY_PLAY;\r
/* Parse command line */\r
ParseArgs(StringGet, &lpCmdLine);\r
\r
+ /* [HGM] make sure board size is acceptable */\r
+ if(appData.NrFiles > BOARD_SIZE ||\r
+ appData.NrRanks > BOARD_SIZE )\r
+ DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
+\r
/* Propagate options that affect others */\r
if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
if (appData.icsActive || appData.noChessProgram) {\r
gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
}\r
\r
- /* [AS] Move history */
- wpMoveHistory.visible = MoveHistoryIsUp();
-
- if( moveHistoryDialog ) {
- GetWindowPlacement(moveHistoryDialog, &wp);
- wpMoveHistory.x = wp.rcNormalPosition.left;
- wpMoveHistory.y = wp.rcNormalPosition.top;
- wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
- wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
- }
-
- /* [AS] Eval graph */
- wpEvalGraph.visible = EvalGraphIsUp();
-
- if( evalGraphDialog ) {
- GetWindowPlacement(evalGraphDialog, &wp);
- wpEvalGraph.x = wp.rcNormalPosition.left;
- wpEvalGraph.y = wp.rcNormalPosition.top;
- wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
- wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
- }
-
- /* [AS] Engine output */
- wpEngineOutput.visible = EngineOutputIsUp();
-
- if( engineOutputDialog ) {
- GetWindowPlacement(engineOutputDialog, &wp);
- wpEngineOutput.x = wp.rcNormalPosition.left;
- wpEngineOutput.y = wp.rcNormalPosition.top;
- wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
- wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
- }
-
+ /* [AS] Move history */\r
+ wpMoveHistory.visible = MoveHistoryIsUp();\r
+ \r
+ if( moveHistoryDialog ) {\r
+ GetWindowPlacement(moveHistoryDialog, &wp);\r
+ wpMoveHistory.x = wp.rcNormalPosition.left;\r
+ wpMoveHistory.y = wp.rcNormalPosition.top;\r
+ wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
+ wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
+ }\r
+\r
+ /* [AS] Eval graph */\r
+ wpEvalGraph.visible = EvalGraphIsUp();\r
+\r
+ if( evalGraphDialog ) {\r
+ GetWindowPlacement(evalGraphDialog, &wp);\r
+ wpEvalGraph.x = wp.rcNormalPosition.left;\r
+ wpEvalGraph.y = wp.rcNormalPosition.top;\r
+ wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
+ wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
+ }\r
+\r
+ /* [AS] Engine output */\r
+ wpEngineOutput.visible = EngineOutputIsUp();\r
+\r
+ if( engineOutputDialog ) {\r
+ GetWindowPlacement(engineOutputDialog, &wp);\r
+ wpEngineOutput.x = wp.rcNormalPosition.left;\r
+ wpEngineOutput.y = wp.rcNormalPosition.top;\r
+ wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
+ wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
+ }\r
+\r
for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
if (!ad->save) continue;\r
switch (ad->argType) {\r
*\r
\*---------------------------------------------------------------------------*/\r
\r
-/* [AS] Draw square using background texture */
-static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
-{
- XFORM x;
-
- if( mode == 0 ) {
- return; /* Should never happen! */
- }
-
- SetGraphicsMode( dst, GM_ADVANCED );
-
- switch( mode ) {
- case 1:
- /* Identity */
- break;
- case 2:
- /* X reflection */
- x.eM11 = -1.0;
- x.eM12 = 0;
- x.eM21 = 0;
- x.eM22 = 1.0;
- x.eDx = (FLOAT) dw + dx - 1;
- x.eDy = 0;
- dx = 0;
- SetWorldTransform( dst, &x );
- break;
- case 3:
- /* Y reflection */
- x.eM11 = 1.0;
- x.eM12 = 0;
- x.eM21 = 0;
- x.eM22 = -1.0;
- x.eDx = 0;
- x.eDy = (FLOAT) dh + dy - 1;
- dy = 0;
- SetWorldTransform( dst, &x );
- break;
- case 4:
- /* X/Y flip */
- x.eM11 = 0;
- x.eM12 = 1.0;
- x.eM21 = 1.0;
- x.eM22 = 0;
- x.eDx = (FLOAT) dx;
- x.eDy = (FLOAT) dy;
- dx = 0;
- dy = 0;
- SetWorldTransform( dst, &x );
- break;
- }
-
- BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
-
- x.eM11 = 1.0;
- x.eM12 = 0;
- x.eM21 = 0;
- x.eM22 = 1.0;
- x.eDx = 0;
- x.eDy = 0;
- SetWorldTransform( dst, &x );
-
- ModifyWorldTransform( dst, 0, MWT_IDENTITY );
-}
-
-/* [AS] */
-enum {
- PM_WP = 0,
- PM_WN = 1,
- PM_WB = 2,
- PM_WR = 3,
- PM_WQ = 4,
- PM_WK = 5,
- PM_BP = 6,
- PM_BN = 7,
- PM_BB = 8,
- PM_BR = 9,
- PM_BQ = 10,
- PM_BK = 11
-};
-
-static HFONT hPieceFont = NULL;
-static HBITMAP hPieceMask[12];
-static HBITMAP hPieceFace[12];
-static int fontBitmapSquareSize = 0;
-static char pieceToFontChar[12] = { 'p', 'n', 'b', 'r', 'q', 'k', 'o', 'm', 'v', 't', 'w', 'l' };
-
-static BOOL SetPieceToFontCharTable( const char * map )
-{
- BOOL result = FALSE;
-
- if( map != NULL && strlen(map) == 12 ) {
- int i;
-
- for( i=0; i<12; i++ ) {
- pieceToFontChar[i] = map[i];
- }
-
- result = TRUE;
- }
-
- return result;
-}
-
-static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
-{
- HBRUSH hbrush;
- BYTE r1 = GetRValue( color );
- BYTE g1 = GetGValue( color );
- BYTE b1 = GetBValue( color );
- BYTE r2 = r1 / 2;
- BYTE g2 = g1 / 2;
- BYTE b2 = b1 / 2;
- RECT rc;
-
- /* Create a uniform background first */
- hbrush = CreateSolidBrush( color );
- SetRect( &rc, 0, 0, squareSize, squareSize );
- FillRect( hdc, &rc, hbrush );
- DeleteObject( hbrush );
-
- if( mode == 1 ) {
- /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
- int steps = squareSize / 2;
- int i;
-
- for( i=0; i<steps; i++ ) {
- BYTE r = r1 - (r1-r2) * i / steps;
- BYTE g = g1 - (g1-g2) * i / steps;
- BYTE b = b1 - (b1-b2) * i / steps;
-
- hbrush = CreateSolidBrush( RGB(r,g,b) );
- SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
- FillRect( hdc, &rc, hbrush );
- DeleteObject(hbrush);
- }
- }
- else if( mode == 2 ) {
- /* Diagonal gradient, good more or less for every piece */
- POINT triangle[3];
- HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
- HBRUSH hbrush_old;
- int steps = squareSize;
- int i;
-
- triangle[0].x = squareSize - steps;
- triangle[0].y = squareSize;
- triangle[1].x = squareSize;
- triangle[1].y = squareSize;
- triangle[2].x = squareSize;
- triangle[2].y = squareSize - steps;
-
- for( i=0; i<steps; i++ ) {
- BYTE r = r1 - (r1-r2) * i / steps;
- BYTE g = g1 - (g1-g2) * i / steps;
- BYTE b = b1 - (b1-b2) * i / steps;
-
- hbrush = CreateSolidBrush( RGB(r,g,b) );
- hbrush_old = SelectObject( hdc, hbrush );
- Polygon( hdc, triangle, 3 );
- SelectObject( hdc, hbrush_old );
- DeleteObject(hbrush);
- triangle[0].x++;
- triangle[2].y++;
- }
-
- SelectObject( hdc, hpen );
- }
-}
-
-/*
- [AS] The method I use to create the bitmaps it a bit tricky, but it
- seems to work ok. The main problem here is to find the "inside" of a chess
- piece: follow the steps as explained below.
-*/
-static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
-{
- HBITMAP hbm;
- HBITMAP hbm_old;
- COLORREF chroma = RGB(0xFF,0x00,0xFF);
- RECT rc;
- SIZE sz;
- POINT pt;
- int backColor = whitePieceColor;
- int foreColor = blackPieceColor;
- int shapeIndex = index < 6 ? index+6 : index;
-
- if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
- backColor = appData.fontBackColorWhite;
- foreColor = appData.fontForeColorWhite;
- }
- else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
- backColor = appData.fontBackColorBlack;
- foreColor = appData.fontForeColorBlack;
- }
-
- /* Mask */
- hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
-
- hbm_old = SelectObject( hdc, hbm );
-
- rc.left = 0;
- rc.top = 0;
- rc.right = squareSize;
- rc.bottom = squareSize;
-
- /* Step 1: background is now black */
- FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
-
- GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
-
- pt.x = (squareSize - sz.cx) / 2;
- pt.y = (squareSize - sz.cy) / 2;
-
- SetBkMode( hdc, TRANSPARENT );
- SetTextColor( hdc, chroma );
- /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
- TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
-
- SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
- /* Step 3: the area outside the piece is filled with white */
- FloodFill( hdc, 0, 0, chroma );
- SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
- /*
- Step 4: this is the tricky part, the area inside the piece is filled with black,
- but if the start point is not inside the piece we're lost!
- There should be a better way to do this... if we could create a region or path
- from the fill operation we would be fine for example.
- */
- FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
-
- SetTextColor( hdc, 0 );
- /*
- Step 5: some fonts have "disconnected" areas that are skipped by the fill:
- draw the piece again in black for safety.
- */
- TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
-
- SelectObject( hdc, hbm_old );
-
- if( hPieceMask[index] != NULL ) {
- DeleteObject( hPieceMask[index] );
- }
-
- hPieceMask[index] = hbm;
-
- /* Face */
- hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
-
- SelectObject( hdc, hbm );
-
- {
- HDC dc1 = CreateCompatibleDC( hdc_window );
- HDC dc2 = CreateCompatibleDC( hdc_window );
- HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
-
- SelectObject( dc1, hPieceMask[index] );
- SelectObject( dc2, bm2 );
- FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
- BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
-
- /*
- Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
- the piece background and deletes (makes transparent) the rest.
- Thanks to that mask, we are free to paint the background with the greates
- freedom, as we'll be able to mask off the unwanted parts when finished.
- We use this, to make gradients and give the pieces a "roundish" look.
- */
- SetPieceBackground( hdc, backColor, 2 );
- BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
-
- DeleteDC( dc2 );
- DeleteDC( dc1 );
- DeleteObject( bm2 );
- }
-
- SetTextColor( hdc, foreColor );
- TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
-
- SelectObject( hdc, hbm_old );
-
- if( hPieceFace[index] != NULL ) {
- DeleteObject( hPieceFace[index] );
- }
-
- hPieceFace[index] = hbm;
-}
-
-static int TranslatePieceToFontPiece( int piece )
-{
- switch( piece ) {
- case BlackPawn:
- return PM_BP;
- case BlackKnight:
- return PM_BN;
- case BlackBishop:
- return PM_BB;
- case BlackRook:
- return PM_BR;
- case BlackQueen:
- return PM_BQ;
- case BlackKing:
- return PM_BK;
- case WhitePawn:
- return PM_WP;
- case WhiteKnight:
- return PM_WN;
- case WhiteBishop:
- return PM_WB;
- case WhiteRook:
- return PM_WR;
- case WhiteQueen:
- return PM_WQ;
- case WhiteKing:
- return PM_WK;
- }
-
- return 0;
-}
-
-void CreatePiecesFromFont()
-{
- LOGFONT lf;
- HDC hdc_window = NULL;
- HDC hdc = NULL;
- HFONT hfont_old;
- int fontHeight;
- int i;
-
- if( fontBitmapSquareSize < 0 ) {
- /* Something went seriously wrong in the past: do not try to recreate fonts! */
- return;
- }
-
- if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
- fontBitmapSquareSize = -1;
- return;
- }
-
- if( fontBitmapSquareSize != squareSize ) {
- hdc_window = GetDC( hwndMain );
- hdc = CreateCompatibleDC( hdc_window );
-
- if( hPieceFont != NULL ) {
- DeleteObject( hPieceFont );
- }
- else {
- for( i=0; i<12; i++ ) {
- hPieceMask[i] = NULL;
- hPieceFace[i] = NULL;
- }
- }
-
- fontHeight = 75;
-
- if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
- fontHeight = appData.fontPieceSize;
- }
-
- fontHeight = (fontHeight * squareSize) / 100;
-
- lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
- lf.lfWidth = 0;
- lf.lfEscapement = 0;
- lf.lfOrientation = 0;
- lf.lfWeight = FW_NORMAL;
- lf.lfItalic = 0;
- lf.lfUnderline = 0;
- lf.lfStrikeOut = 0;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
- lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lf.lfQuality = PROOF_QUALITY;
- lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
- strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
- lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
-
- hPieceFont = CreateFontIndirect( &lf );
-
- if( hPieceFont == NULL ) {
- fontBitmapSquareSize = -2;
- }
- else {
- /* Setup font-to-piece character table */
- if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {
- /* No (or wrong) global settings, try to detect the font */
- if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
- /* Alpha */
- SetPieceToFontCharTable("phbrqkojntwl");
- }
- else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
- /* DiagramTT* family */
- SetPieceToFontCharTable("PNLRQKpnlrqk");
- }
- else {
- /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
- SetPieceToFontCharTable("pnbrqkomvtwl");
- }
- }
-
- /* Create bitmaps */
- hfont_old = SelectObject( hdc, hPieceFont );
-
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );
- CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );
-
- SelectObject( hdc, hfont_old );
-
- fontBitmapSquareSize = squareSize;
- }
- }
-
- if( hdc != NULL ) {
- DeleteDC( hdc );
- }
-
- if( hdc_window != NULL ) {
- ReleaseDC( hwndMain, hdc_window );
- }
-}
-
+/* [AS] Draw square using background texture */\r
+static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
+{\r
+ XFORM x;\r
+\r
+ if( mode == 0 ) {\r
+ return; /* Should never happen! */\r
+ }\r
+\r
+ SetGraphicsMode( dst, GM_ADVANCED );\r
+\r
+ switch( mode ) {\r
+ case 1:\r
+ /* Identity */\r
+ break;\r
+ case 2:\r
+ /* X reflection */\r
+ x.eM11 = -1.0;\r
+ x.eM12 = 0;\r
+ x.eM21 = 0;\r
+ x.eM22 = 1.0;\r
+ x.eDx = (FLOAT) dw + dx - 1;\r
+ x.eDy = 0;\r
+ dx = 0;\r
+ SetWorldTransform( dst, &x );\r
+ break;\r
+ case 3:\r
+ /* Y reflection */\r
+ x.eM11 = 1.0;\r
+ x.eM12 = 0;\r
+ x.eM21 = 0;\r
+ x.eM22 = -1.0;\r
+ x.eDx = 0;\r
+ x.eDy = (FLOAT) dh + dy - 1;\r
+ dy = 0;\r
+ SetWorldTransform( dst, &x );\r
+ break;\r
+ case 4:\r
+ /* X/Y flip */\r
+ x.eM11 = 0;\r
+ x.eM12 = 1.0;\r
+ x.eM21 = 1.0;\r
+ x.eM22 = 0;\r
+ x.eDx = (FLOAT) dx;\r
+ x.eDy = (FLOAT) dy;\r
+ dx = 0;\r
+ dy = 0;\r
+ SetWorldTransform( dst, &x );\r
+ break;\r
+ }\r
+\r
+ BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
+\r
+ x.eM11 = 1.0;\r
+ x.eM12 = 0;\r
+ x.eM21 = 0;\r
+ x.eM22 = 1.0;\r
+ x.eDx = 0;\r
+ x.eDy = 0;\r
+ SetWorldTransform( dst, &x );\r
+\r
+ ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
+}\r
+\r
+/* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
+enum {\r
+ PM_WP = (int) WhitePawn, \r
+ PM_WN = (int) WhiteKnight, \r
+ PM_WB = (int) WhiteBishop, \r
+ PM_WR = (int) WhiteRook, \r
+#ifdef FAIRY\r
+ PM_WA = (int) WhiteCardinal, \r
+ PM_WC = (int) WhiteMarshall, \r
+ PM_WFP = (int) WhiteFairyPawn, \r
+ PM_WFN = (int) WhiteFairyKnight, \r
+ PM_WFB = (int) WhiteFairyBishop, \r
+ PM_WFR = (int) WhiteFairyRook, \r
+ PM_WFA = (int) WhiteFairyCardinal, \r
+ PM_WFC = (int) WhiteFairyMarshall, \r
+ PM_WFQ = (int) WhiteFairyQueen, \r
+ PM_WFK = (int) WhiteFairyKing, \r
+#endif\r
+ PM_WQ = (int) WhiteQueen, \r
+ PM_WK = (int) WhiteKing,\r
+ PM_BP = (int) BlackPawn, \r
+ PM_BN = (int) BlackKnight, \r
+ PM_BB = (int) BlackBishop, \r
+ PM_BR = (int) BlackRook, \r
+#ifdef FAIRY\r
+ PM_BA = (int) BlackCardinal, \r
+ PM_BC = (int) BlackMarshall, \r
+ PM_BFP = (int) BlackFairyPawn, \r
+ PM_BFN = (int) BlackFairyKnight, \r
+ PM_BFB = (int) BlackFairyBishop, \r
+ PM_BFR = (int) BlackFairyRook, \r
+ PM_BFA = (int) BlackFairyCardinal, \r
+ PM_BFC = (int) BlackFairyMarshall, \r
+ PM_BFQ = (int) BlackFairyQueen, \r
+ PM_BFK = (int) BlackFairyKing,\r
+#endif\r
+ PM_BQ = (int) BlackQueen, \r
+ PM_BK = (int) BlackKing\r
+};\r
+\r
+static HFONT hPieceFont = NULL;\r
+static HBITMAP hPieceMask[(int) EmptySquare];\r
+static HBITMAP hPieceFace[(int) EmptySquare];\r
+static int fontBitmapSquareSize = 0;\r
+static char pieceToFontChar[(int) EmptySquare] =\r
+ { 'p', 'n', 'b', 'r',\r
+#ifdef FAIRY\r
+ 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
+#endif\r
+ 'q', 'k', 'o', 'm', 'v', 't', \r
+#ifdef FAIRY\r
+ 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
+#endif\r
+ 'w', 'l' };\r
+\r
+static BOOL SetPieceToFontCharTable( const char * map )\r
+{\r
+ BOOL result = FALSE; int NrPieces;\r
+\r
+ if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
+ && NrPieces >= 12 && !(NrPieces&1)) {\r
+ int i; /* [HGM] Accept even length from 12 to 32 */\r
+\r
+ for( i=0; i<(int) EmptySquare; i++ ) pieceToFontChar[i] = 0;\r
+ for( i=0; i<NrPieces/2-2; i++ ) {\r
+ pieceToFontChar[i] = map[i];\r
+ pieceToFontChar[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
+ }\r
+ pieceToFontChar[(int) WhiteQueen] = map[NrPieces/2-2];\r
+ pieceToFontChar[(int) WhiteKing] = map[NrPieces/2-1];\r
+ pieceToFontChar[(int) BlackQueen] = map[NrPieces-2];\r
+ pieceToFontChar[(int) BlackKing] = map[NrPieces-1];\r
+\r
+ result = TRUE;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
+{\r
+ HBRUSH hbrush;\r
+ BYTE r1 = GetRValue( color );\r
+ BYTE g1 = GetGValue( color );\r
+ BYTE b1 = GetBValue( color );\r
+ BYTE r2 = r1 / 2;\r
+ BYTE g2 = g1 / 2;\r
+ BYTE b2 = b1 / 2;\r
+ RECT rc;\r
+\r
+ /* Create a uniform background first */\r
+ hbrush = CreateSolidBrush( color );\r
+ SetRect( &rc, 0, 0, squareSize, squareSize );\r
+ FillRect( hdc, &rc, hbrush );\r
+ DeleteObject( hbrush );\r
+ \r
+ if( mode == 1 ) {\r
+ /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
+ int steps = squareSize / 2;\r
+ int i;\r
+\r
+ for( i=0; i<steps; i++ ) {\r
+ BYTE r = r1 - (r1-r2) * i / steps;\r
+ BYTE g = g1 - (g1-g2) * i / steps;\r
+ BYTE b = b1 - (b1-b2) * i / steps;\r
+\r
+ hbrush = CreateSolidBrush( RGB(r,g,b) );\r
+ SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
+ FillRect( hdc, &rc, hbrush );\r
+ DeleteObject(hbrush);\r
+ }\r
+ }\r
+ else if( mode == 2 ) {\r
+ /* Diagonal gradient, good more or less for every piece */\r
+ POINT triangle[3];\r
+ HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
+ HBRUSH hbrush_old;\r
+ int steps = squareSize;\r
+ int i;\r
+\r
+ triangle[0].x = squareSize - steps;\r
+ triangle[0].y = squareSize;\r
+ triangle[1].x = squareSize;\r
+ triangle[1].y = squareSize;\r
+ triangle[2].x = squareSize;\r
+ triangle[2].y = squareSize - steps;\r
+\r
+ for( i=0; i<steps; i++ ) {\r
+ BYTE r = r1 - (r1-r2) * i / steps;\r
+ BYTE g = g1 - (g1-g2) * i / steps;\r
+ BYTE b = b1 - (b1-b2) * i / steps;\r
+\r
+ hbrush = CreateSolidBrush( RGB(r,g,b) );\r
+ hbrush_old = SelectObject( hdc, hbrush );\r
+ Polygon( hdc, triangle, 3 );\r
+ SelectObject( hdc, hbrush_old );\r
+ DeleteObject(hbrush);\r
+ triangle[0].x++;\r
+ triangle[2].y++;\r
+ }\r
+\r
+ SelectObject( hdc, hpen );\r
+ }\r
+}\r
+\r
+/*\r
+ [AS] The method I use to create the bitmaps it a bit tricky, but it\r
+ seems to work ok. The main problem here is to find the "inside" of a chess\r
+ piece: follow the steps as explained below.\r
+*/\r
+static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
+{\r
+ HBITMAP hbm;\r
+ HBITMAP hbm_old;\r
+ COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
+ RECT rc;\r
+ SIZE sz;\r
+ POINT pt;\r
+ int backColor = whitePieceColor; \r
+ int foreColor = blackPieceColor;\r
+ int shapeIndex = index < 6 ? index+6 : index;\r
+ \r
+ if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
+ backColor = appData.fontBackColorWhite;\r
+ foreColor = appData.fontForeColorWhite;\r
+ }\r
+ else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
+ backColor = appData.fontBackColorBlack;\r
+ foreColor = appData.fontForeColorBlack;\r
+ }\r
+\r
+ /* Mask */\r
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
+\r
+ hbm_old = SelectObject( hdc, hbm );\r
+\r
+ rc.left = 0;\r
+ rc.top = 0;\r
+ rc.right = squareSize;\r
+ rc.bottom = squareSize;\r
+\r
+ /* Step 1: background is now black */\r
+ FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
+\r
+ GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
+\r
+ pt.x = (squareSize - sz.cx) / 2;\r
+ pt.y = (squareSize - sz.cy) / 2;\r
+\r
+ SetBkMode( hdc, TRANSPARENT );\r
+ SetTextColor( hdc, chroma );\r
+ /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
+\r
+ SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
+ /* Step 3: the area outside the piece is filled with white */\r
+ FloodFill( hdc, 0, 0, chroma );\r
+ SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
+ /* \r
+ Step 4: this is the tricky part, the area inside the piece is filled with black,\r
+ but if the start point is not inside the piece we're lost!\r
+ There should be a better way to do this... if we could create a region or path\r
+ from the fill operation we would be fine for example.\r
+ */\r
+ FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
+\r
+ SetTextColor( hdc, 0 );\r
+ /* \r
+ Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
+ draw the piece again in black for safety.\r
+ */\r
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
+\r
+ SelectObject( hdc, hbm_old );\r
+\r
+ if( hPieceMask[index] != NULL ) {\r
+ DeleteObject( hPieceMask[index] );\r
+ }\r
+\r
+ hPieceMask[index] = hbm;\r
+\r
+ /* Face */\r
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
+\r
+ SelectObject( hdc, hbm );\r
+\r
+ {\r
+ HDC dc1 = CreateCompatibleDC( hdc_window );\r
+ HDC dc2 = CreateCompatibleDC( hdc_window );\r
+ HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
+\r
+ SelectObject( dc1, hPieceMask[index] );\r
+ SelectObject( dc2, bm2 );\r
+ FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
+ BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
+ \r
+ /* \r
+ Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
+ the piece background and deletes (makes transparent) the rest.\r
+ Thanks to that mask, we are free to paint the background with the greates\r
+ freedom, as we'll be able to mask off the unwanted parts when finished.\r
+ We use this, to make gradients and give the pieces a "roundish" look.\r
+ */\r
+ SetPieceBackground( hdc, backColor, 2 );\r
+ BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
+\r
+ DeleteDC( dc2 );\r
+ DeleteDC( dc1 );\r
+ DeleteObject( bm2 );\r
+ }\r
+\r
+ SetTextColor( hdc, foreColor );\r
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
+\r
+ SelectObject( hdc, hbm_old );\r
+\r
+ if( hPieceFace[index] != NULL ) {\r
+ DeleteObject( hPieceFace[index] );\r
+ }\r
+\r
+ hPieceFace[index] = hbm;\r
+}\r
+\r
+static int TranslatePieceToFontPiece( int piece )\r
+{\r
+ switch( piece ) {\r
+ case BlackPawn:\r
+ return PM_BP;\r
+ case BlackKnight:\r
+ return PM_BN;\r
+ case BlackBishop:\r
+ return PM_BB;\r
+ case BlackRook:\r
+ return PM_BR;\r
+ case BlackQueen:\r
+ return PM_BQ;\r
+ case BlackKing:\r
+ return PM_BK;\r
+ case WhitePawn:\r
+ return PM_WP;\r
+ case WhiteKnight:\r
+ return PM_WN;\r
+ case WhiteBishop:\r
+ return PM_WB;\r
+ case WhiteRook:\r
+ return PM_WR;\r
+ case WhiteQueen:\r
+ return PM_WQ;\r
+ case WhiteKing:\r
+ return PM_WK;\r
+#ifdef FAIRY\r
+ case BlackCardinal:\r
+ return PM_BA;\r
+ case BlackMarshall:\r
+ return PM_BC;\r
+ case BlackFairyPawn:\r
+ return PM_BFP;\r
+ case BlackFairyKnight:\r
+ return PM_BFN;\r
+ case BlackFairyBishop:\r
+ return PM_BFB;\r
+ case BlackFairyRook:\r
+ return PM_BFR;\r
+ case BlackFairyCardinal:\r
+ return PM_BFA;\r
+ case BlackFairyMarshall:\r
+ return PM_BFC;\r
+ case BlackFairyQueen:\r
+ return PM_BFQ;\r
+ case BlackFairyKing:\r
+ return PM_BFK;\r
+ case WhiteCardinal:\r
+ return PM_WA;\r
+ case WhiteMarshall:\r
+ return PM_WC;\r
+ case WhiteFairyPawn:\r
+ return PM_WFP;\r
+ case WhiteFairyKnight:\r
+ return PM_WFN;\r
+ case WhiteFairyBishop:\r
+ return PM_WFB;\r
+ case WhiteFairyRook:\r
+ return PM_WFR;\r
+ case WhiteFairyCardinal:\r
+ return PM_WFA;\r
+ case WhiteFairyMarshall:\r
+ return PM_WFC;\r
+ case WhiteFairyQueen:\r
+ return PM_WFQ;\r
+ case WhiteFairyKing:\r
+ return PM_WFK;\r
+#endif\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+void CreatePiecesFromFont()\r
+{\r
+ LOGFONT lf;\r
+ HDC hdc_window = NULL;\r
+ HDC hdc = NULL;\r
+ HFONT hfont_old;\r
+ int fontHeight;\r
+ int i;\r
+\r
+ if( fontBitmapSquareSize < 0 ) {\r
+ /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
+ return;\r
+ }\r
+\r
+ if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
+ fontBitmapSquareSize = -1;\r
+ return;\r
+ }\r
+\r
+ if( fontBitmapSquareSize != squareSize ) {\r
+ hdc_window = GetDC( hwndMain );\r
+ hdc = CreateCompatibleDC( hdc_window );\r
+\r
+ if( hPieceFont != NULL ) {\r
+ DeleteObject( hPieceFont );\r
+ }\r
+ else {\r
+ for( i=0; i<12; i++ ) {\r
+ hPieceMask[i] = NULL;\r
+ hPieceFace[i] = NULL;\r
+ }\r
+ }\r
+\r
+ fontHeight = 75;\r
+\r
+ if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
+ fontHeight = appData.fontPieceSize;\r
+ }\r
+\r
+ fontHeight = (fontHeight * squareSize) / 100;\r
+\r
+ lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
+ lf.lfWidth = 0;\r
+ lf.lfEscapement = 0;\r
+ lf.lfOrientation = 0;\r
+ lf.lfWeight = FW_NORMAL;\r
+ lf.lfItalic = 0;\r
+ lf.lfUnderline = 0;\r
+ lf.lfStrikeOut = 0;\r
+ lf.lfCharSet = DEFAULT_CHARSET;\r
+ lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
+ lf.lfQuality = PROOF_QUALITY;\r
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
+ strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
+ lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
+\r
+ hPieceFont = CreateFontIndirect( &lf );\r
+\r
+ if( hPieceFont == NULL ) {\r
+ fontBitmapSquareSize = -2;\r
+ }\r
+ else {\r
+ /* Setup font-to-piece character table */\r
+ if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {\r
+ /* No (or wrong) global settings, try to detect the font */\r
+ if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
+ /* Alpha */\r
+ SetPieceToFontCharTable("phbrqkojntwl");\r
+ }\r
+ else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
+ /* DiagramTT* family */\r
+ SetPieceToFontCharTable("PNLRQKpnlrqk");\r
+ }\r
+#ifdef FAIRY\r
+ else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
+ /* Fairy symbols */\r
+ SetPieceToFontCharTable("PNBRACFHEWUOGMQKpnbracfewuogmqk");\r
+ }\r
+#endif\r
+ else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
+ /* Good Companion (Some characters get warped as literal :-( */\r
+ char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
+ s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
+ SetPieceToFontCharTable(s);\r
+ }\r
+ else {\r
+ /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
+ SetPieceToFontCharTable("pnbrqkomvtwl");\r
+ }\r
+ }\r
+\r
+ /* Create bitmaps */\r
+ hfont_old = SelectObject( hdc, hPieceFont );\r
+\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
+#ifdef FAIRY\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFP );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFN );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFB );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFR );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFA );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFC );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFQ );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WFK );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFP );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFN );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFB );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFR );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFA );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFC );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFQ );\r
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BFK );\r
+#endif\r
+\r
+ SelectObject( hdc, hfont_old );\r
+\r
+ fontBitmapSquareSize = squareSize;\r
+ }\r
+ }\r
+\r
+ if( hdc != NULL ) {\r
+ DeleteDC( hdc );\r
+ }\r
+\r
+ if( hdc_window != NULL ) {\r
+ ReleaseDC( hwndMain, hdc_window );\r
+ }\r
+}\r
+\r
HBITMAP\r
DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
{\r
whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
-
- /* [AS] Force rendering of the font-based pieces */
- if( fontBitmapSquareSize > 0 ) {
- fontBitmapSquareSize = 0;
- }
+\r
+ /* [AS] Force rendering of the font-based pieces */\r
+ if( fontBitmapSquareSize > 0 ) {\r
+ fontBitmapSquareSize = 0;\r
+ }\r
}\r
\r
\r
int\r
-BoardWidth(int boardSize)\r
-{\r
- int lineGap = sizeInfo[boardSize].lineGap;
-
- if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
- lineGap = appData.overrideLineGap;
- }
-
- return (BOARD_SIZE + 1) * lineGap +
- BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
+BoardWidth(int boardSize, int n)\r
+{ /* [HGM] argument n added to allow different width and height */\r
+ int lineGap = sizeInfo[boardSize].lineGap;\r
+\r
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
+ lineGap = appData.overrideLineGap;\r
+ }\r
+\r
+ return (n + 1) * lineGap +\r
+ n * sizeInfo[boardSize].squareSize;\r
}\r
\r
/* Respond to board resize by dragging edge */\r
VOID\r
InitDrawingSizes(BoardSize boardSize, int flags)\r
{\r
- int i, boardWidth;\r
+ int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
ChessSquare piece;\r
static int oldBoardSize = -1, oldTinyLayout = 0;\r
HDC hdc;\r
squareSize = sizeInfo[boardSize].squareSize;\r
lineGap = sizeInfo[boardSize].lineGap;\r
\r
- if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
- lineGap = appData.overrideLineGap;
- }
-
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
+ lineGap = appData.overrideLineGap;\r
+ }\r
+\r
if (tinyLayout != oldTinyLayout) {\r
long style = GetWindowLong(hwndMain, GWL_STYLE);\r
if (tinyLayout) {\r
DrawMenuBar(hwndMain);\r
}\r
\r
- boardWidth = BoardWidth(boardSize);\r
+ boardWidth = BoardWidth(boardSize, BOARD_WIDTH);\r
+ boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
\r
/* Get text area sizes */\r
hdc = GetDC(hwndMain);\r
boardRect.left = whiteRect.left;\r
boardRect.right = boardRect.left + boardWidth;\r
boardRect.top = messageRect.bottom + INNER_MARGIN;\r
- boardRect.bottom = boardRect.top + boardWidth;\r
+ boardRect.bottom = boardRect.top + boardHeight;\r
\r
sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
lineGap, &logbrush, 0, NULL);\r
\r
- for (i = 0; i < BOARD_SIZE + 1; i++) {\r
+ /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
+ for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
- gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;\r
gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
- BOARD_SIZE * (squareSize + lineGap);\r
- gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =\r
- gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +\r
+ BOARD_WIDTH * (squareSize + lineGap);\r
+ lineGap / 2 + (i * (squareSize + lineGap));\r
+ gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
+ }\r
+ for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
+ gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
+ gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
+ gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
lineGap / 2 + (i * (squareSize + lineGap));\r
- gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =\r
- boardRect.top + BOARD_SIZE * (squareSize + lineGap);\r
+ gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
+ boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
}\r
}\r
/* Load piece bitmaps for this board size */\r
for (i=0; i<=2; i++) {\r
for (piece = WhitePawn;\r
- (int) piece <= (int) WhiteKing;\r
+ (int) piece < (int) BlackPawn;\r
piece = (ChessSquare) ((int) piece + 1)) {\r
if (pieceBitmap[i][piece] != NULL)\r
DeleteObject(pieceBitmap[i][piece]);\r
pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
+#ifdef FAIRY\r
+ if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */\r
+ pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
+ pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
+ pieceBitmap[0][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
+ pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
+ pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
+ pieceBitmap[1][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
+ pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
+ pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
+ pieceBitmap[2][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
+ }\r
+#endif\r
\r
}\r
\r
SquareToPos(int row, int column, int * x, int * y)\r
{\r
if (flipView) {\r
- *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
+ *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
*y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
} else {\r
*x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
- *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
+ *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
}\r
}\r
\r
VOID\r
DrawCoordsOnDC(HDC hdc)\r
{\r
- static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};\r
- static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};\r
+ static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
+ static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
char str[2] = { NULLCHAR, NULLCHAR };\r
int oldMode, oldAlign, x, y, start, i;\r
HFONT oldFont;\r
if (!appData.showCoords)\r
return;\r
\r
- start = flipView ? 0 : 8;\r
+ start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
\r
oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
x = boardRect.left + lineGap;\r
\r
SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
- for (i = 0; i < 8; i++) {\r
+ for (i = 0; i < BOARD_HEIGHT; i++) {\r
str[0] = files[start + i];\r
ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
y += squareSize + lineGap;\r
}\r
\r
+ start = flipView ? 12-BOARD_WIDTH : 12;\r
+\r
SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
- for (i = 0; i < 8; i++) {\r
+ for (i = 0; i < BOARD_WIDTH; i++) {\r
str[0] = ranks[start + i];\r
ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
x += squareSize + lineGap;\r
\r
if (lineGap != 0) {\r
oldPen = SelectObject(hdc, gridPen);\r
- PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);\r
+ PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
SelectObject(hdc, oldPen);\r
}\r
}\r
if (lineGap == 0) return;\r
if (flipView) {\r
x1 = boardRect.left +\r
- lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);\r
+ lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
y1 = boardRect.top +\r
lineGap/2 + y * (squareSize + lineGap);\r
} else {\r
x1 = boardRect.left +\r
lineGap/2 + x * (squareSize + lineGap);\r
y1 = boardRect.top +\r
- lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);\r
+ lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
}\r
hPen = pen ? premovePen : highlightPen;\r
oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
\r
if (appData.blindfold) return;\r
\r
- /* [AS] Use font-based pieces if needed */
- if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
- /* Create piece bitmaps, or do nothing if piece set is up to date */
- CreatePiecesFromFont();
-
- if( fontBitmapSquareSize == squareSize ) {
- int index = TranslatePieceToFontPiece( piece );
-
- SelectObject( tmphdc, hPieceMask[ index ] );
-
- BitBlt( hdc,
- x, y,
- squareSize, squareSize,
- tmphdc,
- 0, 0,
- SRCAND );
-
- SelectObject( tmphdc, hPieceFace[ index ] );
-
- BitBlt( hdc,
- x, y,
- squareSize, squareSize,
- tmphdc,
- 0, 0,
- SRCPAINT );
-
- return;
- }
- }
-
+ /* [AS] Use font-based pieces if needed */\r
+ if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
+ /* Create piece bitmaps, or do nothing if piece set is up to date */\r
+ CreatePiecesFromFont();\r
+\r
+ if( fontBitmapSquareSize == squareSize ) {\r
+ int index = TranslatePieceToFontPiece( piece );\r
+\r
+ SelectObject( tmphdc, hPieceMask[ index ] );\r
+\r
+ BitBlt( hdc,\r
+ x, y,\r
+ squareSize, squareSize,\r
+ tmphdc,\r
+ 0, 0,\r
+ SRCAND );\r
+\r
+ SelectObject( tmphdc, hPieceFace[ index ] );\r
+\r
+ BitBlt( hdc,\r
+ x, y,\r
+ squareSize, squareSize,\r
+ tmphdc,\r
+ 0, 0,\r
+ SRCPAINT );\r
+\r
+ return;\r
+ }\r
+ }\r
+\r
if (appData.monoMode) {\r
SelectObject(tmphdc, PieceBitmap(piece, \r
color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
}\r
}\r
\r
-/* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
-int GetBackTextureMode( int algo )
-{
- int result = BACK_TEXTURE_MODE_DISABLED;
-
- switch( algo )
- {
- case BACK_TEXTURE_MODE_PLAIN:
- result = 1; /* Always use identity map */
- break;
- case BACK_TEXTURE_MODE_FULL_RANDOM:
- result = 1 + (myrandom() % 3); /* Pick a transformation at random */
- break;
- }
-
- return result;
-}
-
-/*
- [AS] Compute and save texture drawing info, otherwise we may not be able
- to handle redraws cleanly (as random numbers would always be different).
-*/
-VOID RebuildTextureSquareInfo()
-{
- BITMAP bi;
- int lite_w = 0;
- int lite_h = 0;
- int dark_w = 0;
- int dark_h = 0;
- int row;
- int col;
-
- ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
-
- if( liteBackTexture != NULL ) {
- if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
- lite_w = bi.bmWidth;
- lite_h = bi.bmHeight;
- }
- }
-
- if( darkBackTexture != NULL ) {
- if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
- dark_w = bi.bmWidth;
- dark_h = bi.bmHeight;
- }
- }
-
- for( row=0; row<BOARD_SIZE; row++ ) {
- for( col=0; col<BOARD_SIZE; col++ ) {
- if( (col + row) & 1 ) {
- /* Lite square */
- if( lite_w >= squareSize && lite_h >= squareSize ) {
- backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_SIZE;
- backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_SIZE;
- backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
- }
- }
- else {
- /* Dark square */
- if( dark_w >= squareSize && dark_h >= squareSize ) {
- backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_SIZE;
- backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_SIZE;
- backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
- }
- }
- }
- }
-}
-
-/* [AS] Arrow highlighting support */
-
-static int A_WIDTH = 5; /* Width of arrow body */
-
-#define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
-#define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
-
-static double Sqr( double x )
-{
- return x*x;
-}
-
-static int Round( double x )
-{
- return (int) (x + 0.5);
-}
-
-/* Draw an arrow between two points using current settings */
-VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
-{
- POINT arrow[7];
- double dx, dy, j, k, x, y;
-
- if( d_x == s_x ) {
- int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
-
- arrow[0].x = s_x + A_WIDTH;
- arrow[0].y = s_y;
-
- arrow[1].x = s_x + A_WIDTH;
- arrow[1].y = d_y - h;
-
- arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
- arrow[2].y = d_y - h;
-
- arrow[3].x = d_x;
- arrow[3].y = d_y;
-
- arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
- arrow[4].y = d_y - h;
-
- arrow[5].x = s_x - A_WIDTH;
- arrow[5].y = d_y - h;
-
- arrow[6].x = s_x - A_WIDTH;
- arrow[6].y = s_y;
- }
- else if( d_y == s_y ) {
- int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
-
- arrow[0].x = s_x;
- arrow[0].y = s_y + A_WIDTH;
-
- arrow[1].x = d_x - w;
- arrow[1].y = s_y + A_WIDTH;
-
- arrow[2].x = d_x - w;
- arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
-
- arrow[3].x = d_x;
- arrow[3].y = d_y;
-
- arrow[4].x = d_x - w;
- arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
-
- arrow[5].x = d_x - w;
- arrow[5].y = s_y - A_WIDTH;
-
- arrow[6].x = s_x;
- arrow[6].y = s_y - A_WIDTH;
- }
- else {
- /* [AS] Needed a lot of paper for this! :-) */
- dy = (double) (d_y - s_y) / (double) (d_x - s_x);
- dx = (double) (s_x - d_x) / (double) (s_y - d_y);
-
- j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
-
- k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
-
- x = s_x;
- y = s_y;
-
- arrow[0].x = Round(x - j);
- arrow[0].y = Round(y + j*dx);
-
- arrow[1].x = Round(x + j);
- arrow[1].y = Round(y - j*dx);
-
- if( d_x > s_x ) {
- x = (double) d_x - k;
- y = (double) d_y - k*dy;
- }
- else {
- x = (double) d_x + k;
- y = (double) d_y + k*dy;
- }
-
- arrow[2].x = Round(x + j);
- arrow[2].y = Round(y - j*dx);
-
- arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
- arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
-
- arrow[4].x = d_x;
- arrow[4].y = d_y;
-
- arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
- arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
-
- arrow[6].x = Round(x - j);
- arrow[6].y = Round(y + j*dx);
- }
-
- Polygon( hdc, arrow, 7 );
-}
-
-/* [AS] Draw an arrow between two squares */
-VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
-{
- int s_x, s_y, d_x, d_y;
- HPEN hpen;
- HPEN holdpen;
- HBRUSH hbrush;
- HBRUSH holdbrush;
- LOGBRUSH stLB;
-
- if( s_col == d_col && s_row == d_row ) {
- return;
- }
-
- /* Get source and destination points */
- SquareToPos( s_row, s_col, &s_x, &s_y);
- SquareToPos( d_row, d_col, &d_x, &d_y);
-
- if( d_y > s_y ) {
- d_y += squareSize / 4;
- }
- else if( d_y < s_y ) {
- d_y += 3 * squareSize / 4;
- }
- else {
- d_y += squareSize / 2;
- }
-
- if( d_x > s_x ) {
- d_x += squareSize / 4;
- }
- else if( d_x < s_x ) {
- d_x += 3 * squareSize / 4;
- }
- else {
- d_x += squareSize / 2;
- }
-
- s_x += squareSize / 2;
- s_y += squareSize / 2;
-
- /* Adjust width */
- A_WIDTH = squareSize / 14;
-
- /* Draw */
- stLB.lbStyle = BS_SOLID;
- stLB.lbColor = appData.highlightArrowColor;
- stLB.lbHatch = 0;
-
- hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
- holdpen = SelectObject( hdc, hpen );
- hbrush = CreateBrushIndirect( &stLB );
- holdbrush = SelectObject( hdc, hbrush );
-
- DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
-
- SelectObject( hdc, holdpen );
- SelectObject( hdc, holdbrush );
- DeleteObject( hpen );
- DeleteObject( hbrush );
-}
-
-BOOL HasHighlightInfo()
-{
- BOOL result = FALSE;
-
- if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
- highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
- {
- result = TRUE;
- }
-
- return result;
-}
-
-BOOL IsDrawArrowEnabled()
-{
- BOOL result = FALSE;
-
- if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
- result = TRUE;
- }
-
- return result;
-}
-
-VOID DrawArrowHighlight( HDC hdc )
-{
- if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
- DrawArrowBetweenSquares( hdc,
- highlightInfo.sq[0].x, highlightInfo.sq[0].y,
- highlightInfo.sq[1].x, highlightInfo.sq[1].y );
- }
-}
-
-HRGN GetArrowHighlightClipRegion( HDC hdc )
-{
- HRGN result = NULL;
-
- if( HasHighlightInfo() ) {
- int x1, y1, x2, y2;
- int sx, sy, dx, dy;
-
- SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
- SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
-
- sx = MIN( x1, x2 );
- sy = MIN( y1, y2 );
- dx = MAX( x1, x2 ) + squareSize;
- dy = MAX( y1, y2 ) + squareSize;
-
- result = CreateRectRgn( sx, sy, dx, dy );
- }
-
- return result;
-}
-
-/*
- Warning: this function modifies the behavior of several other functions.
-
- Basically, Winboard is optimized to avoid drawing the whole board if not strictly
- needed. Unfortunately, the decision whether or not to perform a full or partial
- repaint is scattered all over the place, which is not good for features such as
- "arrow highlighting" that require a full repaint of the board.
-
- So, I've tried to patch the code where I thought it made sense (e.g. after or during
- user interaction, when speed is not so important) but especially to avoid errors
- in the displayed graphics.
-
- In such patched places, I always try refer to this function so there is a single
- place to maintain knowledge.
-
- To restore the original behavior, just return FALSE unconditionally.
-*/
-BOOL IsFullRepaintPreferrable()
-{
- BOOL result = FALSE;
-
- if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
- /* Arrow may appear on the board */
- result = TRUE;
- }
-
- return result;
-}
-
-/*
- This function is called by DrawPosition to know whether a full repaint must
- be forced or not.
-
- Only DrawPosition may directly call this function, which makes use of
- some state information. Other function should call DrawPosition specifying
- the repaint flag, and can use IsFullRepaintPreferrable if needed.
-*/
-BOOL DrawPositionNeedsFullRepaint()
-{
- BOOL result = FALSE;
-
- /*
- Probably a slightly better policy would be to trigger a full repaint
- when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
- but animation is fast enough that it's difficult to notice.
- */
- if( animInfo.piece == EmptySquare ) {
- if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {
- result = TRUE;
- }
- }
-
- return result;
-}
-
+/* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
+int GetBackTextureMode( int algo )\r
+{\r
+ int result = BACK_TEXTURE_MODE_DISABLED;\r
+\r
+ switch( algo ) \r
+ {\r
+ case BACK_TEXTURE_MODE_PLAIN:\r
+ result = 1; /* Always use identity map */\r
+ break;\r
+ case BACK_TEXTURE_MODE_FULL_RANDOM:\r
+ result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
+ break;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/* \r
+ [AS] Compute and save texture drawing info, otherwise we may not be able\r
+ to handle redraws cleanly (as random numbers would always be different).\r
+*/\r
+VOID RebuildTextureSquareInfo()\r
+{\r
+ BITMAP bi;\r
+ int lite_w = 0;\r
+ int lite_h = 0;\r
+ int dark_w = 0;\r
+ int dark_h = 0;\r
+ int row;\r
+ int col;\r
+\r
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
+\r
+ if( liteBackTexture != NULL ) {\r
+ if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
+ lite_w = bi.bmWidth;\r
+ lite_h = bi.bmHeight;\r
+ }\r
+ }\r
+\r
+ if( darkBackTexture != NULL ) {\r
+ if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
+ dark_w = bi.bmWidth;\r
+ dark_h = bi.bmHeight;\r
+ }\r
+ }\r
+\r
+ for( row=0; row<BOARD_HEIGHT; row++ ) {\r
+ for( col=0; col<BOARD_WIDTH; col++ ) {\r
+ if( (col + row) & 1 ) {\r
+ /* Lite square */\r
+ if( lite_w >= squareSize && lite_h >= squareSize ) {\r
+ backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
+ backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
+ }\r
+ }\r
+ else {\r
+ /* Dark square */\r
+ if( dark_w >= squareSize && dark_h >= squareSize ) {\r
+ backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
+ backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/* [AS] Arrow highlighting support */\r
+\r
+static int A_WIDTH = 5; /* Width of arrow body */\r
+\r
+#define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */\r
+#define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */\r
+\r
+static double Sqr( double x )\r
+{\r
+ return x*x;\r
+}\r
+\r
+static int Round( double x )\r
+{\r
+ return (int) (x + 0.5);\r
+}\r
+\r
+/* Draw an arrow between two points using current settings */\r
+VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
+{\r
+ POINT arrow[7];\r
+ double dx, dy, j, k, x, y;\r
+\r
+ if( d_x == s_x ) {\r
+ int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
+\r
+ arrow[0].x = s_x + A_WIDTH;\r
+ arrow[0].y = s_y;\r
+\r
+ arrow[1].x = s_x + A_WIDTH;\r
+ arrow[1].y = d_y - h;\r
+\r
+ arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
+ arrow[2].y = d_y - h;\r
+\r
+ arrow[3].x = d_x;\r
+ arrow[3].y = d_y;\r
+\r
+ arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
+ arrow[4].y = d_y - h;\r
+\r
+ arrow[5].x = s_x - A_WIDTH;\r
+ arrow[5].y = d_y - h;\r
+\r
+ arrow[6].x = s_x - A_WIDTH;\r
+ arrow[6].y = s_y;\r
+ }\r
+ else if( d_y == s_y ) {\r
+ int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
+\r
+ arrow[0].x = s_x;\r
+ arrow[0].y = s_y + A_WIDTH;\r
+\r
+ arrow[1].x = d_x - w;\r
+ arrow[1].y = s_y + A_WIDTH;\r
+\r
+ arrow[2].x = d_x - w;\r
+ arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
+\r
+ arrow[3].x = d_x;\r
+ arrow[3].y = d_y;\r
+\r
+ arrow[4].x = d_x - w;\r
+ arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
+\r
+ arrow[5].x = d_x - w;\r
+ arrow[5].y = s_y - A_WIDTH;\r
+\r
+ arrow[6].x = s_x;\r
+ arrow[6].y = s_y - A_WIDTH;\r
+ }\r
+ else {\r
+ /* [AS] Needed a lot of paper for this! :-) */\r
+ dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
+ dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
+ \r
+ j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
+\r
+ k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
+\r
+ x = s_x;\r
+ y = s_y;\r
+\r
+ arrow[0].x = Round(x - j);\r
+ arrow[0].y = Round(y + j*dx);\r
+\r
+ arrow[1].x = Round(x + j);\r
+ arrow[1].y = Round(y - j*dx);\r
+\r
+ if( d_x > s_x ) {\r
+ x = (double) d_x - k;\r
+ y = (double) d_y - k*dy;\r
+ }\r
+ else {\r
+ x = (double) d_x + k;\r
+ y = (double) d_y + k*dy;\r
+ }\r
+\r
+ arrow[2].x = Round(x + j);\r
+ arrow[2].y = Round(y - j*dx);\r
+\r
+ arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
+ arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
+\r
+ arrow[4].x = d_x;\r
+ arrow[4].y = d_y;\r
+\r
+ arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
+ arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
+\r
+ arrow[6].x = Round(x - j);\r
+ arrow[6].y = Round(y + j*dx);\r
+ }\r
+\r
+ Polygon( hdc, arrow, 7 );\r
+}\r
+\r
+/* [AS] Draw an arrow between two squares */\r
+VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
+{\r
+ int s_x, s_y, d_x, d_y;\r
+ HPEN hpen;\r
+ HPEN holdpen;\r
+ HBRUSH hbrush;\r
+ HBRUSH holdbrush;\r
+ LOGBRUSH stLB;\r
+\r
+ if( s_col == d_col && s_row == d_row ) {\r
+ return;\r
+ }\r
+\r
+ /* Get source and destination points */\r
+ SquareToPos( s_row, s_col, &s_x, &s_y);\r
+ SquareToPos( d_row, d_col, &d_x, &d_y);\r
+\r
+ if( d_y > s_y ) {\r
+ d_y += squareSize / 4;\r
+ }\r
+ else if( d_y < s_y ) {\r
+ d_y += 3 * squareSize / 4;\r
+ }\r
+ else {\r
+ d_y += squareSize / 2;\r
+ }\r
+\r
+ if( d_x > s_x ) {\r
+ d_x += squareSize / 4;\r
+ }\r
+ else if( d_x < s_x ) {\r
+ d_x += 3 * squareSize / 4;\r
+ }\r
+ else {\r
+ d_x += squareSize / 2;\r
+ }\r
+\r
+ s_x += squareSize / 2;\r
+ s_y += squareSize / 2;\r
+\r
+ /* Adjust width */\r
+ A_WIDTH = squareSize / 14;\r
+\r
+ /* Draw */\r
+ stLB.lbStyle = BS_SOLID;\r
+ stLB.lbColor = appData.highlightArrowColor;\r
+ stLB.lbHatch = 0;\r
+\r
+ hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
+ holdpen = SelectObject( hdc, hpen );\r
+ hbrush = CreateBrushIndirect( &stLB );\r
+ holdbrush = SelectObject( hdc, hbrush );\r
+\r
+ DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
+\r
+ SelectObject( hdc, holdpen );\r
+ SelectObject( hdc, holdbrush );\r
+ DeleteObject( hpen );\r
+ DeleteObject( hbrush );\r
+}\r
+\r
+BOOL HasHighlightInfo()\r
+{\r
+ BOOL result = FALSE;\r
+\r
+ if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
+ highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
+ {\r
+ result = TRUE;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+BOOL IsDrawArrowEnabled()\r
+{\r
+ BOOL result = FALSE;\r
+\r
+ if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
+ result = TRUE;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+VOID DrawArrowHighlight( HDC hdc )\r
+{\r
+ if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
+ DrawArrowBetweenSquares( hdc,\r
+ highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
+ highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
+ }\r
+}\r
+\r
+HRGN GetArrowHighlightClipRegion( HDC hdc )\r
+{\r
+ HRGN result = NULL;\r
+\r
+ if( HasHighlightInfo() ) {\r
+ int x1, y1, x2, y2;\r
+ int sx, sy, dx, dy;\r
+\r
+ SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
+ SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
+\r
+ sx = MIN( x1, x2 );\r
+ sy = MIN( y1, y2 );\r
+ dx = MAX( x1, x2 ) + squareSize;\r
+ dy = MAX( y1, y2 ) + squareSize;\r
+\r
+ result = CreateRectRgn( sx, sy, dx, dy );\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/*\r
+ Warning: this function modifies the behavior of several other functions. \r
+ \r
+ Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
+ needed. Unfortunately, the decision whether or not to perform a full or partial\r
+ repaint is scattered all over the place, which is not good for features such as\r
+ "arrow highlighting" that require a full repaint of the board.\r
+\r
+ So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
+ user interaction, when speed is not so important) but especially to avoid errors\r
+ in the displayed graphics.\r
+\r
+ In such patched places, I always try refer to this function so there is a single\r
+ place to maintain knowledge.\r
+ \r
+ To restore the original behavior, just return FALSE unconditionally.\r
+*/\r
+BOOL IsFullRepaintPreferrable()\r
+{\r
+ BOOL result = FALSE;\r
+\r
+ if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
+ /* Arrow may appear on the board */\r
+ result = TRUE;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/* \r
+ This function is called by DrawPosition to know whether a full repaint must\r
+ be forced or not.\r
+\r
+ Only DrawPosition may directly call this function, which makes use of \r
+ some state information. Other function should call DrawPosition specifying \r
+ the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
+*/\r
+BOOL DrawPositionNeedsFullRepaint()\r
+{\r
+ BOOL result = FALSE;\r
+\r
+ /* \r
+ Probably a slightly better policy would be to trigger a full repaint\r
+ when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
+ but animation is fast enough that it's difficult to notice.\r
+ */\r
+ if( animInfo.piece == EmptySquare ) {\r
+ if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
+ result = TRUE;\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
VOID\r
DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
{\r
int row, column, x, y, square_color, piece_color;\r
ChessSquare piece;\r
HBRUSH oldBrush;\r
- HDC texture_hdc = NULL;
-
- /* [AS] Initialize background textures if needed */
- if( liteBackTexture != NULL || darkBackTexture != NULL ) {
- if( backTextureSquareSize != squareSize ) {
- backTextureSquareSize = squareSize;
- RebuildTextureSquareInfo();
- }
-
- texture_hdc = CreateCompatibleDC( hdc );
- }
-\r
- for (row = 0; row < BOARD_SIZE; row++) {\r
- for (column = 0; column < BOARD_SIZE; column++) {\r
+ HDC texture_hdc = NULL;\r
+\r
+ /* [AS] Initialize background textures if needed */\r
+ if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
+ if( backTextureSquareSize != squareSize ) {\r
+ backTextureSquareSize = squareSize;\r
+ RebuildTextureSquareInfo();\r
+ }\r
+\r
+ texture_hdc = CreateCompatibleDC( hdc );\r
+ }\r
+\r
+ for (row = 0; row < BOARD_HEIGHT; row++) {\r
+ for (column = 0; column < BOARD_WIDTH; column++) {\r
\r
SquareToPos(row, column, &x, &y);\r
\r
piece = board[row][column];\r
\r
square_color = ((column + row) % 2) == 1;\r
+ if(!strcmp(appData.variant, "xiangqi") ) {\r
+ square_color = 1;\r
+ if( (row < 3 || row > BOARD_HEIGHT-4) &&\r
+ column < (BOARD_WIDTH + 4)/2 &&\r
+ column > (BOARD_WIDTH - 5)/2 ) square_color = 0;\r
+ }\r
piece_color = (int) piece < (int) BlackPawn;\r
\r
if (appData.monoMode) {\r
} else {\r
DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
}\r
- }
- else if( backTextureSquareInfo[row][column].mode > 0 ) {
- /* [AS] Draw the square using a texture bitmap */
- HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
-
- DrawTile( x, y,
- squareSize, squareSize,
- hdc,
- texture_hdc,
- backTextureSquareInfo[row][column].mode,
- backTextureSquareInfo[row][column].x,
- backTextureSquareInfo[row][column].y );
-
- SelectObject( texture_hdc, hbm );
-
- if (piece != EmptySquare) {
- DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
- }
- }
- else {
+ } \r
+ else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
+ /* [AS] Draw the square using a texture bitmap */\r
+ HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
+\r
+ DrawTile( x, y, \r
+ squareSize, squareSize, \r
+ hdc, \r
+ texture_hdc,\r
+ backTextureSquareInfo[row][column].mode,\r
+ backTextureSquareInfo[row][column].x,\r
+ backTextureSquareInfo[row][column].y );\r
+\r
+ SelectObject( texture_hdc, hbm );\r
+\r
+ if (piece != EmptySquare) {\r
+ DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
+ }\r
+ }\r
+ else {\r
oldBrush = SelectObject(hdc, square_color ?\r
lightSquareBrush : darkSquareBrush);\r
BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
}\r
}\r
}\r
-
- if( texture_hdc != NULL ) {
- DeleteDC( texture_hdc );
- }
+\r
+ if( texture_hdc != NULL ) {\r
+ DeleteDC( texture_hdc );\r
+ }\r
}\r
\r
#define MAX_CLIPS 200 /* more than enough */\r
*/\r
Boolean fullrepaint = repaint;\r
\r
- if( DrawPositionNeedsFullRepaint() ) {
- fullrepaint = TRUE;
- }
-
-#if 0
- if( fullrepaint ) {
- static int repaint_count = 0;
- char buf[128];
-
- repaint_count++;
- sprintf( buf, "FULL repaint: %d\n", repaint_count );
- OutputDebugString( buf );
- }
-#endif
-
+ if( DrawPositionNeedsFullRepaint() ) {\r
+ fullrepaint = TRUE;\r
+ }\r
+\r
+#if 0\r
+ if( fullrepaint ) {\r
+ static int repaint_count = 0;\r
+ char buf[128];\r
+\r
+ repaint_count++;\r
+ sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
+ OutputDebugString( buf );\r
+ }\r
+#endif\r
+\r
if (board == NULL) {\r
if (!lastReqValid) {\r
return;\r
dragInfo.pos.x, dragInfo.pos.y,\r
dragInfo.lastpos.x, dragInfo.lastpos.y);\r
fprintf(debugFP, "prev: ");\r
- for (row = 0; row < 8; row++) {\r
- for (column = 0; column < 8; column++) {\r
+ for (row = 0; row < BOARD_HEIGHT; row++) {\r
+ for (column = 0; column < BOARD_WIDTH; column++) {\r
fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
}\r
}\r
fprintf(debugFP, "\n");\r
fprintf(debugFP, "board: ");\r
- for (row = 0; row < 8; row++) {\r
- for (column = 0; column < 8; column++) {\r
+ for (row = 0; row < BOARD_HEIGHT; row++) {\r
+ for (column = 0; column < BOARD_WIDTH; column++) {\r
fprintf(debugFP, "%d ", board[row][column]);\r
}\r
}\r
* flipping has changed.\r
*/\r
if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
- for (row = 0; row < 8; row++) {\r
- for (column = 0; column < 8; column++) {\r
+ for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
+ for (column = 0; column < BOARD_WIDTH; column++) {\r
if (lastDrawn[row][column] != board[row][column]) {\r
SquareToPos(row, column, &x, &y);\r
clips[num_clips++] =\r
DrawGridOnDC(hdcmem);\r
DrawHighlightsOnDC(hdcmem);\r
DrawBoardOnDC(hdcmem, board, tmphdc);\r
-
- if( appData.highlightMoveWithArrow ) {
- DrawArrowHighlight(hdcmem);
- }
-
+\r
+ if( appData.highlightMoveWithArrow ) {\r
+ DrawArrowHighlight(hdcmem);\r
+ }\r
+\r
DrawCoordsOnDC(hdcmem);\r
\r
/* Put the dragged piece back into place and draw it */\r
POINT pt;\r
static int recursive = 0;\r
HMENU hmenu;\r
- BOOLEAN needsRedraw = FALSE;
+ BOOLEAN needsRedraw = FALSE;\r
BOOLEAN saveAnimate;\r
- BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
+ BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
static BOOLEAN sameAgain = FALSE;\r
\r
if (recursive) {\r
x = EventToSquare(pt.x - boardRect.left);\r
y = EventToSquare(pt.y - boardRect.top);\r
if (!flipView && y >= 0) {\r
- y = BOARD_SIZE - 1 - y;\r
+ y = BOARD_HEIGHT - 1 - y;\r
}\r
if (flipView && x >= 0) {\r
- x = BOARD_SIZE - 1 - x;\r
+ x = BOARD_WIDTH - 1 - x;\r
}\r
\r
switch (message) {\r
}\r
if (!appData.highlightLastMove) {\r
ClearHighlights();\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
}\r
fromX = fromY = -1;\r
dragInfo.start.x = dragInfo.start.y = -1;\r
} else if (fromX == x && fromY == y) {\r
/* Downclick on same square again */\r
ClearHighlights();\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
sameAgain = TRUE; \r
} else if (fromX != -1) {\r
/* Downclick on different square */\r
ChessSquare pdown, pup;\r
pdown = boards[currentMove][fromY][fromX];\r
pup = boards[currentMove][y][x];\r
- if (gameMode == EditPosition ||\r
- !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
- WhitePawn <= pup && pup <= WhiteKing) ||\r
- (BlackPawn <= pdown && pdown <= BlackKing &&\r
- BlackPawn <= pup && pup <= BlackKing))) {\r
+ if (gameMode == EditPosition || /* [HGM] max piece > King! */\r
+ !((WhitePawn <= pdown && pdown < BlackPawn &&\r
+ WhitePawn <= pup && pup < BlackPawn) ||\r
+ (BlackPawn <= pdown && pdown < EmptySquare &&\r
+ BlackPawn <= pup && pup < EmptySquare))) {\r
/* EditPosition, empty square, or different color piece;\r
click-click move is possible */\r
toX = x;\r
UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
if (!appData.highlightLastMove) {\r
ClearHighlights();\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
}\r
} else {\r
SetHighlights(fromX, fromY, toX, toY);\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
PromotionPopup(hwnd);\r
}\r
} else { /* not a promotion */\r
UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
if (appData.animate && !appData.highlightLastMove) {\r
ClearHighlights();\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
}\r
}\r
if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
break;\r
}\r
ClearHighlights();\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
}\r
/* First downclick, or restart on a square with same color piece */\r
if (!frozen && OKToStartUserMove(x, y)) {\r
fromX = fromY = -1;\r
dragInfo.start.x = dragInfo.start.y = -1;\r
dragInfo.from = dragInfo.start;\r
- DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
+ DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
}\r
break;\r
\r
/* First square clicked: start click-click move */\r
SetHighlights(fromX, fromY, -1, -1);\r
}\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
} else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
/* Errant click; ignore */\r
break;\r
if (appData.alwaysPromoteToQueen) {\r
UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
} else {\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
PromotionPopup(hwnd);\r
}\r
} else {\r
}\r
if (appData.animate || appData.animateDragging ||\r
appData.highlightDragging || gotPremove) {\r
- DrawPosition(forceFullRepaint || FALSE, NULL);
+ DrawPosition(forceFullRepaint || FALSE, NULL);\r
}\r
}\r
dragInfo.start.x = dragInfo.start.y = -1; \r
case WM_MOUSEMOVE:\r
if ((appData.animateDragging || appData.highlightDragging)\r
&& (wParam & MK_LBUTTON)\r
- && dragInfo.from.x >= 0)
- {
- BOOL full_repaint = FALSE;
-
+ && dragInfo.from.x >= 0) \r
+ {\r
+ BOOL full_repaint = FALSE;\r
+\r
if (appData.animateDragging) {\r
dragInfo.pos = pt;\r
}\r
if (appData.highlightDragging) {\r
SetHighlights(fromX, fromY, x, y);\r
- if( IsDrawArrowEnabled() && (x < 0 || x > 7 || y < 0 || y > y) ) {
- full_repaint = TRUE;
+ if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y > BOARD_WIDTH) ) {\r
+ full_repaint = TRUE;\r
+ }\r
}\r
- }
-
- DrawPosition( full_repaint, NULL);
-
+ \r
+ DrawPosition( full_repaint, NULL);\r
+ \r
dragInfo.lastpos = dragInfo.pos;\r
}\r
break;\r
(!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
gameInfo.variant == VariantGiveaway) ?\r
SW_SHOW : SW_HIDE);\r
+#ifdef FAIRY\r
+ /* [HGM] Only allow C & A promotions in Capablanca Chess */\r
+ ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
+ (gameInfo.variant == VariantCapablanca || \r
+ gameInfo.variant == VariantGothic) ?\r
+ SW_SHOW : SW_HIDE);\r
+ ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
+ (gameInfo.variant == VariantCapablanca || \r
+ gameInfo.variant == VariantGothic) ?\r
+ SW_SHOW : SW_HIDE);\r
+#endif\r
return TRUE;\r
\r
case WM_COMMAND: /* message: received a command */\r
case PB_Bishop:\r
promoChar = 'b';\r
break;\r
+#ifdef FAIRY\r
+ case PB_Chancellor:\r
+ promoChar = 'c';\r
+ break;\r
+ case PB_Archbishop:\r
+ promoChar = 'a';\r
+ break;\r
+#endif\r
case PB_Knight:\r
promoChar = 'n';\r
break;\r
FILE *f;\r
UINT number;\r
char fileTitle[MSG_SIZ];\r
- static SnapData sd;
+ static SnapData sd;\r
\r
switch (message) {\r
\r
AnalysisPopDown();\r
break;\r
\r
- case IDM_NewGameFRC:
- if( NewGameFRC() == 0 ) {
- ResetGameEvent();
- AnalysisPopDown();
- }
- break;
-
+ case IDM_NewGameFRC:\r
+ if( NewGameFRC() == 0 ) {\r
+ ResetGameEvent();\r
+ AnalysisPopDown();\r
+ }\r
+ break;\r
+\r
case IDM_LoadGame:\r
LoadGameDialog(hwnd, "Load Game from File");\r
break;\r
PasteGameFromClipboard();\r
break;\r
\r
- case IDM_CopyGameListToClipboard:
- CopyGameListToClipboard();
- break;
-
- /* [AS] Autodetect FEN or PGN data */
- case IDM_PasteAny:
- PasteGameOrFENFromClipboard();
- break;
-
- /* [AS] Move history */
- case IDM_ShowMoveHistory:
- if( MoveHistoryIsUp() ) {
- MoveHistoryPopDown();
- }
- else {
- MoveHistoryPopUp();
- }
- break;
-
- /* [AS] Eval graph */
- case IDM_ShowEvalGraph:
- if( EvalGraphIsUp() ) {
- EvalGraphPopDown();
- }
- else {
- EvalGraphPopUp();
- }
- break;
-
- /* [AS] Engine output */
- case IDM_ShowEngineOutput:
- if( EngineOutputIsUp() ) {
- EngineOutputPopDown();
- }
- else {
- EngineOutputPopUp();
- }
- break;
-
- /* [AS] User adjudication */
- case IDM_UserAdjudication_White:
- UserAdjudicationEvent( +1 );
- break;
-
- case IDM_UserAdjudication_Black:
- UserAdjudicationEvent( -1 );
- break;
-
- case IDM_UserAdjudication_Draw:
- UserAdjudicationEvent( 0 );
- break;
-
- /* [AS] Game list options dialog */
- case IDM_GameListOptions:
- GameListOptions();
- break;
-
+ case IDM_CopyGameListToClipboard:\r
+ CopyGameListToClipboard();\r
+ break;\r
+\r
+ /* [AS] Autodetect FEN or PGN data */\r
+ case IDM_PasteAny:\r
+ PasteGameOrFENFromClipboard();\r
+ break;\r
+\r
+ /* [AS] Move history */\r
+ case IDM_ShowMoveHistory:\r
+ if( MoveHistoryIsUp() ) {\r
+ MoveHistoryPopDown();\r
+ }\r
+ else {\r
+ MoveHistoryPopUp();\r
+ }\r
+ break;\r
+\r
+ /* [AS] Eval graph */\r
+ case IDM_ShowEvalGraph:\r
+ if( EvalGraphIsUp() ) {\r
+ EvalGraphPopDown();\r
+ }\r
+ else {\r
+ EvalGraphPopUp();\r
+ }\r
+ break;\r
+\r
+ /* [AS] Engine output */\r
+ case IDM_ShowEngineOutput:\r
+ if( EngineOutputIsUp() ) {\r
+ EngineOutputPopDown();\r
+ }\r
+ else {\r
+ EngineOutputPopUp();\r
+ }\r
+ break;\r
+\r
+ /* [AS] User adjudication */\r
+ case IDM_UserAdjudication_White:\r
+ UserAdjudicationEvent( +1 );\r
+ break;\r
+\r
+ case IDM_UserAdjudication_Black:\r
+ UserAdjudicationEvent( -1 );\r
+ break;\r
+\r
+ case IDM_UserAdjudication_Draw:\r
+ UserAdjudicationEvent( 0 );\r
+ break;\r
+\r
+ /* [AS] Game list options dialog */\r
+ case IDM_GameListOptions:\r
+ GameListOptions();\r
+ break;\r
+\r
case IDM_CopyPosition:\r
CopyFENToClipboard();\r
break;\r
\r
case IDM_GeneralOptions:\r
GeneralOptionsPopup(hwnd);\r
- DrawPosition(TRUE, NULL);
+ DrawPosition(TRUE, NULL);\r
break;\r
\r
case IDM_BoardOptions:\r
BoardOptionsPopup(hwnd);\r
break;\r
\r
- case IDM_EnginePlayOptions:
- EnginePlayOptionsPopup(hwnd);
- break;
-
- case IDM_OptionsUCI:
- UciOptionsPopup(hwnd);
- break;
-
+ case IDM_EnginePlayOptions:\r
+ EnginePlayOptionsPopup(hwnd);\r
+ break;\r
+\r
+ case IDM_OptionsUCI:\r
+ UciOptionsPopup(hwnd);\r
+ break;\r
+\r
case IDM_IcsOptions:\r
IcsOptionsPopup(hwnd);\r
break;\r
char dir[MSG_SIZ];\r
GetCurrentDirectory(MSG_SIZ, dir);\r
SetCurrentDirectory(installDir);\r
- debugFP = fopen(appData.nameOfDebugFile, "w");
+ debugFP = fopen(appData.nameOfDebugFile, "w");\r
SetCurrentDirectory(dir);\r
setbuf(debugFP, NULL);\r
} else {\r
InputEvent(hwnd, message, wParam, lParam);\r
break;\r
\r
- /* [AS] Also move "attached" child windows */
- case WM_WINDOWPOSCHANGING:
- if( hwnd == hwndMain && appData.useStickyWindows ) {
- LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
-
- if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
- /* Window is moving */
- RECT rcMain;
-
- GetWindowRect( hwnd, &rcMain );
-
- ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
- ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
- ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
- }
- }
- break;
-
- /* [AS] Snapping */
+ /* [AS] Also move "attached" child windows */\r
+ case WM_WINDOWPOSCHANGING:\r
+ if( hwnd == hwndMain && appData.useStickyWindows ) {\r
+ LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
+\r
+ if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
+ /* Window is moving */\r
+ RECT rcMain;\r
+\r
+ GetWindowRect( hwnd, &rcMain );\r
+ \r
+ ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
+ ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
+ ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
+ }\r
+ }\r
+ break;\r
+\r
+ /* [AS] Snapping */\r
case WM_ENTERSIZEMOVE:\r
if (hwnd == hwndMain) {\r
doingSizing = TRUE;\r
lastSizing = 0;\r
}\r
- return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
+ return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
break;\r
\r
case WM_SIZING:\r
}\r
break;\r
\r
- case WM_MOVING:
- return OnMoving( &sd, hwnd, wParam, lParam );
-
+ case WM_MOVING:\r
+ return OnMoving( &sd, hwnd, wParam, lParam );\r
+\r
case WM_EXITSIZEMOVE:\r
if (hwnd == hwndMain) {\r
RECT client;\r
ResizeBoard(client.right, client.bottom, lastSizing);\r
lastSizing = 0;\r
}\r
- return OnExitSizeMove( &sd, hwnd, wParam, lParam );
+ return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
break;\r
\r
case WM_DESTROY: /* message: window being destroyed */\r
EndDeferWindowPos(cl.hdwp);\r
}\r
\r
-BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
+BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
{\r
RECT rChild, rParent;\r
int wChild, hChild, wParent, hParent;\r
}\r
\r
/* Calculate new Y position, then adjust for screen */\r
- if( mode == 0 ) {
- yNew = rParent.top + ((hParent - hChild) /2);\r
- }
- else {
- yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
- }
-
+ if( mode == 0 ) {\r
+ yNew = rParent.top + ((hParent - hChild) /2);\r
+ }\r
+ else {\r
+ yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
+ }\r
+\r
if (yNew < 0) {\r
yNew = 0;\r
} else if ((yNew+hChild) > hScreen) {\r
xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
}\r
\r
-/* Center one window over another */
-BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
-{
- return CenterWindowEx( hwndChild, hwndParent, 0 );
-}
-
+/* Center one window over another */\r
+BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
+{\r
+ return CenterWindowEx( hwndChild, hwndParent, 0 );\r
+}\r
+\r
/*---------------------------------------------------------------------------*\\r
*\r
* Startup Dialog functions\r
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
}\r
-
- if (appData.icsActive) {
+\r
+ if (appData.icsActive) {\r
CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
- }
- else if (appData.noChessProgram) {
+ }\r
+ else if (appData.noChessProgram) {\r
CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
}\r
- else {
- CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
- }
-
+ else {\r
+ CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
+ }\r
+\r
SetStartupDialogEnables(hDlg);\r
return TRUE;\r
\r
case WM_INITDIALOG:\r
move[0] = (char) lParam;\r
move[1] = NULLCHAR;\r
- CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
+ CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
hInput = GetDlgItem(hDlg, OPT_Move);\r
SetWindowText(hInput, move);\r
SetFocus(hInput);\r
switch (message) {\r
case WM_INITDIALOG:\r
GetWindowRect(hDlg, &rChild);\r
-
- /*
+\r
+ /*\r
SetWindowPos(hDlg, NULL, rChild.left,\r
rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
- */
-
- /*
- [AS] It seems that the above code wants to move the dialog up in the "caption
- area" of the main window, but it uses the dialog height as an hard-coded constant,
- and it doesn't work when you resize the dialog.
- For now, just give it a default position.
- */
- SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
-
+ */\r
+\r
+ /* \r
+ [AS] It seems that the above code wants to move the dialog up in the "caption\r
+ area" of the main window, but it uses the dialog height as an hard-coded constant,\r
+ and it doesn't work when you resize the dialog.\r
+ For now, just give it a default position.\r
+ */\r
+ SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
+\r
errorDialog = hDlg;\r
SetWindowText(hDlg, errorTitle);\r
hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
return FALSE;\r
}\r
\r
+#ifdef GOTHIC\r
+LRESULT CALLBACK\r
+GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+ HANDLE hwndText;\r
+ RECT rChild;\r
+ int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
+\r
+ switch (message) {\r
+ case WM_INITDIALOG:\r
+ GetWindowRect(hDlg, &rChild);\r
+\r
+ SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
+ SWP_NOZORDER);\r
+\r
+ /* \r
+ [AS] It seems that the above code wants to move the dialog up in the "caption\r
+ area" of the main window, but it uses the dialog height as an hard-coded constant,\r
+ and it doesn't work when you resize the dialog.\r
+ For now, just give it a default position.\r
+ */\r
+\r
+ SetWindowText(hDlg, errorTitle);\r
+ hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
+ SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
+ return FALSE;\r
+\r
+ case WM_COMMAND:\r
+ switch (LOWORD(wParam)) {\r
+ case IDOK:\r
+ case IDCANCEL:\r
+ if (errorDialog == hDlg) errorDialog = NULL;\r
+ DestroyWindow(hDlg);\r
+ return TRUE;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ break;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+VOID\r
+GothicPopUp(char *title)\r
+{\r
+ FARPROC lpProc;\r
+ char *p, *q;\r
+ BOOLEAN modal = hwndMain == NULL;\r
+\r
+ strncpy(errorTitle, title, sizeof(errorTitle));\r
+ errorTitle[sizeof(errorTitle) - 1] = '\0';\r
+ \r
+ lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
+ CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
+ hwndMain, (DLGPROC)lpProc);\r
+ FreeProcInstance(lpProc);\r
+}\r
+#endif\r
+\r
/*---------------------------------------------------------------------------*\\r
*\r
* Ics Interaction console functions\r
LRESULT CALLBACK\r
ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
{\r
- static SnapData sd;
+ static SnapData sd;\r
static HWND hText, hInput, hFocus;\r
InputSource *is = consoleInputSource;\r
RECT rect;\r
mmi->ptMinTrackSize.x = 100;\r
mmi->ptMinTrackSize.y = 100;\r
break;\r
-
- /* [AS] Snapping */
- case WM_ENTERSIZEMOVE:
- return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
-
- case WM_SIZING:
- return OnSizing( &sd, hDlg, wParam, lParam );
-
- case WM_MOVING:
- return OnMoving( &sd, hDlg, wParam, lParam );
-
- case WM_EXITSIZEMOVE:
- return OnExitSizeMove( &sd, hDlg, wParam, lParam );
- }\r
-
+\r
+ /* [AS] Snapping */\r
+ case WM_ENTERSIZEMOVE:\r
+ return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_SIZING:\r
+ return OnSizing( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_MOVING:\r
+ return OnMoving( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_EXITSIZEMOVE:\r
+ return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
+ }\r
+\r
return DefWindowProc(hDlg, message, wParam, lParam);\r
}\r
\r
\r
void\r
DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
- RECT *rect, char *color)\r
+ RECT *rect, char *color, char *flagFell)\r
{\r
char buf[100];\r
char *str;\r
\r
if (appData.clockMode) {\r
if (tinyLayout)\r
- sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));\r
+ sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
else\r
- sprintf(buf, "%s: %s", color, TimeString(timeRemaining));\r
+ sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
str = buf;\r
} else {\r
str = color;\r
{\r
int ok, err;\r
\r
- /* [AS] */
- if( count <= 0 ) {
- if (appData.debugMode) {
- fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
- }
-
- return ERROR_INVALID_USER_BUFFER;
- }
-
+ /* [AS] */\r
+ if( count <= 0 ) {\r
+ if (appData.debugMode) {\r
+ fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
+ }\r
+\r
+ return ERROR_INVALID_USER_BUFFER;\r
+ }\r
+\r
ResetEvent(ovl->hEvent);\r
ovl->Offset = ovl->OffsetHigh = 0;\r
ok = ReadFile(hFile, buf, count, outCount, ovl);\r
return err;\r
}\r
\r
-/* [AS] If input is line by line and a line exceed the buffer size, force an error */
-void CheckForInputBufferFull( InputSource * is )
-{
- if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
- /* Look for end of line */
- char * p = is->buf;
-
- while( p < is->next && *p != '\n' ) {
- p++;
- }
-
- if( p >= is->next ) {
- if (appData.debugMode) {
- fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );
- }
-
- is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
- is->count = (DWORD) -1;
- is->next = is->buf;
- }
- }
-}
+/* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
+void CheckForInputBufferFull( InputSource * is )\r
+{\r
+ if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
+ /* Look for end of line */\r
+ char * p = is->buf;\r
+ \r
+ while( p < is->next && *p != '\n' ) {\r
+ p++;\r
+ }\r
+\r
+ if( p >= is->next ) {\r
+ if (appData.debugMode) {\r
+ fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
+ }\r
+\r
+ is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
+ is->count = (DWORD) -1;\r
+ is->next = is->buf;\r
+ }\r
+ }\r
+}\r
\r
DWORD\r
InputThread(LPVOID arg)\r
is->count = 0;\r
} else {\r
is->count = (DWORD) -1;\r
- /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
- break;
+ /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
+ break; \r
}\r
}\r
-
- CheckForInputBufferFull( is );
-
+\r
+ CheckForInputBufferFull( is );\r
+\r
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
-
- if( is->count == ((DWORD) -1) ) break; /* [AS] */
-
+\r
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
+\r
if (is->count <= 0) break; /* Quit on EOF or error */\r
}\r
-
+\r
CloseHandle(ovl.hEvent);\r
CloseHandle(is->hFile);\r
-
- if (appData.debugMode) {
- fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );
- }
-
+\r
+ if (appData.debugMode) {\r
+ fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
+ }\r
+\r
return 0;\r
}\r
\r
is->count = (DWORD) -1;\r
}\r
}\r
-
- CheckForInputBufferFull( is );
-
+\r
+ CheckForInputBufferFull( is );\r
+\r
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
-
- if( is->count == ((DWORD) -1) ) break; /* [AS] */
-
+\r
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
+\r
if (is->count < 0) break; /* Quit on error */\r
}\r
CloseHandle(is->hFile);\r
}\r
}\r
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
-
- if( is->count == ((DWORD) -1) ) break; /* [AS] */
-
+\r
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
+\r
if (is->count <= 0) break; /* Quit on EOF or error */\r
}\r
return 0;\r
p = q;\r
}\r
}\r
-
+ \r
/* Move any partial line to the start of the buffer */\r
q = is->buf;\r
while (p < is->next) {\r
*q++ = *p++;\r
}\r
is->next = q;\r
-
+\r
if (is->error != NO_ERROR || is->count == 0) {\r
/* Notify backend of the error. Note: If there was a partial\r
line at the end, it is not flushed through. */\r
FreeProcInstance(lpProc);\r
}\r
\r
-/* [AS] Pick FRC position */
-LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
-{
- static int * lpIndexFRC;
- BOOL index_is_ok;
- char buf[16];
-
- switch( message )
- {
- case WM_INITDIALOG:
- lpIndexFRC = (int *) lParam;
-
- CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
-
- SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
- SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
- SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
- SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
-
- break;
-
- case WM_COMMAND:
- switch( LOWORD(wParam) ) {
- case IDOK:
- *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
- EndDialog( hDlg, 0 );
- return TRUE;
- case IDCANCEL:
- EndDialog( hDlg, 1 );
- return TRUE;
- case IDC_NFG_Edit:
- if( HIWORD(wParam) == EN_CHANGE ) {
- GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
-
- EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
- }
- return TRUE;
- case IDC_NFG_Random:
- sprintf( buf, "%d", myrandom() % 960 );
- SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
- return TRUE;
- }
-
- break;
- }
-
- return FALSE;
-}
-
-int NewGameFRC()
-{
- int result;
- int index = appData.defaultFrcPosition;
- FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
-
- result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
-
- if( result == 0 ) {
- appData.defaultFrcPosition = index;
- }
-
- return result;
-}
-
-/* [AS] Game list options */
-typedef struct {
- char id;
- char * name;
-} GLT_Item;
-
-static GLT_Item GLT_ItemInfo[] = {
- { GLT_EVENT, "Event" },
- { GLT_SITE, "Site" },
- { GLT_DATE, "Date" },
- { GLT_ROUND, "Round" },
- { GLT_PLAYERS, "Players" },
- { GLT_RESULT, "Result" },
- { GLT_WHITE_ELO, "White Rating" },
- { GLT_BLACK_ELO, "Black Rating" },
- { GLT_TIME_CONTROL,"Time Control" },
- { GLT_VARIANT, "Variant" },
- { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
- { 0, 0 }
-};
-
-const char * GLT_FindItem( char id )
-{
- const char * result = 0;
-
- GLT_Item * list = GLT_ItemInfo;
-
- while( list->id != 0 ) {
- if( list->id == id ) {
- result = list->name;
- break;
- }
-
- list++;
- }
-
- return result;
-}
-
-void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
-{
- const char * name = GLT_FindItem( id );
-
- if( name != 0 ) {
- if( index >= 0 ) {
- SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
- }
- else {
- SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
- }
- }
-}
-
-void GLT_TagsToList( HWND hDlg, char * tags )
-{
- char * pc = tags;
-
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
-
- while( *pc ) {
- GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
- pc++;
- }
-
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
-
- pc = GLT_ALL_TAGS;
-
- while( *pc ) {
- if( strchr( tags, *pc ) == 0 ) {
- GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
- }
- pc++;
- }
-
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
-}
-
-char GLT_ListItemToTag( HWND hDlg, int index )
-{
- char result = '\0';
- char name[128];
-
- GLT_Item * list = GLT_ItemInfo;
-
- if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
- while( list->id != 0 ) {
- if( strcmp( list->name, name ) == 0 ) {
- result = list->id;
- break;
- }
-
- list++;
- }
- }
-
- return result;
-}
-
-void GLT_MoveSelection( HWND hDlg, int delta )
-{
- int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
- int idx2 = idx1 + delta;
- int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
-
- if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
- char buf[128];
-
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
- SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
- }
-}
-
-LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
-{
- static char glt[64];
- static char * lpUserGLT;
-
- switch( message )
- {
- case WM_INITDIALOG:
- lpUserGLT = (char *) lParam;
-
- strcpy( glt, lpUserGLT );
-
- CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
-
- /* Initialize list */
- GLT_TagsToList( hDlg, glt );
-
- SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
-
- break;
-
- case WM_COMMAND:
- switch( LOWORD(wParam) ) {
- case IDOK:
- {
- char * pc = lpUserGLT;
- int idx = 0;
- int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
- char id;
-
- do {
- id = GLT_ListItemToTag( hDlg, idx );
-
- *pc++ = id;
- idx++;
- } while( id != '\0' );
- }
- EndDialog( hDlg, 0 );
- return TRUE;
- case IDCANCEL:
- EndDialog( hDlg, 1 );
- return TRUE;
-
- case IDC_GLT_Default:
- strcpy( glt, GLT_DEFAULT_TAGS );
- GLT_TagsToList( hDlg, glt );
- return TRUE;
-
- case IDC_GLT_Restore:
- strcpy( glt, lpUserGLT );
- GLT_TagsToList( hDlg, glt );
- return TRUE;
-
- case IDC_GLT_Up:
- GLT_MoveSelection( hDlg, -1 );
- return TRUE;
-
- case IDC_GLT_Down:
- GLT_MoveSelection( hDlg, +1 );
- return TRUE;
- }
-
- break;
- }
-
- return FALSE;
-}
-
-int GameListOptions()
-{
- char glt[64];
- int result;
- FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
-
- strcpy( glt, appData.gameListTags );
-
- result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
-
- if( result == 0 ) {
- /* [AS] Memory leak here! */
- appData.gameListTags = strdup( glt );
- }
-
- return result;
-}
-
+/* [AS] Pick FRC position */\r
+LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+ static int * lpIndexFRC;\r
+ BOOL index_is_ok;\r
+ char buf[16];\r
+\r
+ switch( message )\r
+ {\r
+ case WM_INITDIALOG:\r
+ lpIndexFRC = (int *) lParam;\r
+\r
+ CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+\r
+ SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
+ SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
+ SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
+ SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
+\r
+ break;\r
+\r
+ case WM_COMMAND:\r
+ switch( LOWORD(wParam) ) {\r
+ case IDOK:\r
+ *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
+ EndDialog( hDlg, 0 );\r
+ return TRUE;\r
+ case IDCANCEL:\r
+ EndDialog( hDlg, 1 ); \r
+ return TRUE;\r
+ case IDC_NFG_Edit:\r
+ if( HIWORD(wParam) == EN_CHANGE ) {\r
+ GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
+\r
+ EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
+ }\r
+ return TRUE;\r
+ case IDC_NFG_Random:\r
+ sprintf( buf, "%d", myrandom() % 960 );\r
+ SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
+ return TRUE;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+int NewGameFRC()\r
+{\r
+ int result;\r
+ int index = appData.defaultFrcPosition;\r
+ FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
+\r
+ result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
+\r
+ if( result == 0 ) {\r
+ appData.defaultFrcPosition = index;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/* [AS] Game list options */\r
+typedef struct {\r
+ char id;\r
+ char * name;\r
+} GLT_Item;\r
+\r
+static GLT_Item GLT_ItemInfo[] = {\r
+ { GLT_EVENT, "Event" },\r
+ { GLT_SITE, "Site" },\r
+ { GLT_DATE, "Date" },\r
+ { GLT_ROUND, "Round" },\r
+ { GLT_PLAYERS, "Players" },\r
+ { GLT_RESULT, "Result" },\r
+ { GLT_WHITE_ELO, "White Rating" },\r
+ { GLT_BLACK_ELO, "Black Rating" },\r
+ { GLT_TIME_CONTROL,"Time Control" },\r
+ { GLT_VARIANT, "Variant" },\r
+ { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
+ { 0, 0 }\r
+};\r
+\r
+const char * GLT_FindItem( char id )\r
+{\r
+ const char * result = 0;\r
+\r
+ GLT_Item * list = GLT_ItemInfo;\r
+\r
+ while( list->id != 0 ) {\r
+ if( list->id == id ) {\r
+ result = list->name;\r
+ break;\r
+ }\r
+\r
+ list++;\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
+{\r
+ const char * name = GLT_FindItem( id );\r
+\r
+ if( name != 0 ) {\r
+ if( index >= 0 ) {\r
+ SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
+ }\r
+ else {\r
+ SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
+ }\r
+ }\r
+}\r
+\r
+void GLT_TagsToList( HWND hDlg, char * tags )\r
+{\r
+ char * pc = tags;\r
+\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
+\r
+ while( *pc ) {\r
+ GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
+ pc++;\r
+ }\r
+\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
+\r
+ pc = GLT_ALL_TAGS;\r
+\r
+ while( *pc ) {\r
+ if( strchr( tags, *pc ) == 0 ) {\r
+ GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
+ }\r
+ pc++;\r
+ }\r
+\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
+}\r
+\r
+char GLT_ListItemToTag( HWND hDlg, int index )\r
+{\r
+ char result = '\0';\r
+ char name[128];\r
+\r
+ GLT_Item * list = GLT_ItemInfo;\r
+\r
+ if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
+ while( list->id != 0 ) {\r
+ if( strcmp( list->name, name ) == 0 ) {\r
+ result = list->id;\r
+ break;\r
+ }\r
+\r
+ list++;\r
+ }\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+void GLT_MoveSelection( HWND hDlg, int delta )\r
+{\r
+ int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
+ int idx2 = idx1 + delta;\r
+ int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
+\r
+ if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
+ char buf[128];\r
+\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
+ SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
+ }\r
+}\r
+\r
+LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+ static char glt[64];\r
+ static char * lpUserGLT;\r
+\r
+ switch( message )\r
+ {\r
+ case WM_INITDIALOG:\r
+ lpUserGLT = (char *) lParam;\r
+ \r
+ strcpy( glt, lpUserGLT );\r
+\r
+ CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+\r
+ /* Initialize list */\r
+ GLT_TagsToList( hDlg, glt );\r
+\r
+ SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
+\r
+ break;\r
+\r
+ case WM_COMMAND:\r
+ switch( LOWORD(wParam) ) {\r
+ case IDOK:\r
+ {\r
+ char * pc = lpUserGLT;\r
+ int idx = 0;\r
+ int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
+ char id;\r
+\r
+ do {\r
+ id = GLT_ListItemToTag( hDlg, idx );\r
+\r
+ *pc++ = id;\r
+ idx++;\r
+ } while( id != '\0' );\r
+ }\r
+ EndDialog( hDlg, 0 );\r
+ return TRUE;\r
+ case IDCANCEL:\r
+ EndDialog( hDlg, 1 );\r
+ return TRUE;\r
+\r
+ case IDC_GLT_Default:\r
+ strcpy( glt, GLT_DEFAULT_TAGS );\r
+ GLT_TagsToList( hDlg, glt );\r
+ return TRUE;\r
+\r
+ case IDC_GLT_Restore:\r
+ strcpy( glt, lpUserGLT );\r
+ GLT_TagsToList( hDlg, glt );\r
+ return TRUE;\r
+\r
+ case IDC_GLT_Up:\r
+ GLT_MoveSelection( hDlg, -1 );\r
+ return TRUE;\r
+\r
+ case IDC_GLT_Down:\r
+ GLT_MoveSelection( hDlg, +1 );\r
+ return TRUE;\r
+ }\r
+\r
+ break;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+int GameListOptions()\r
+{\r
+ char glt[64];\r
+ int result;\r
+ FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
+\r
+ strcpy( glt, appData.gameListTags );\r
+\r
+ result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
+\r
+ if( result == 0 ) {\r
+ /* [AS] Memory leak here! */\r
+ appData.gameListTags = strdup( glt ); \r
+ }\r
+\r
+ return result;\r
+}\r
+\r
\r
VOID\r
DisplayIcsInteractionTitle(char *str)\r
{\r
HDC hdc;\r
hdc = GetDC(hwndMain);\r
+ char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
+\r
if (!IsIconic(hwndMain)) {\r
- DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");\r
+ DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White", flag);\r
}\r
if (highlight && iconCurrent == iconBlack) {\r
iconCurrent = iconWhite;\r
DisplayBlackClock(long timeRemaining, int highlight)\r
{\r
HDC hdc;\r
+ char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
+\r
hdc = GetDC(hwndMain);\r
if (!IsIconic(hwndMain)) {\r
- DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");\r
+ DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black", flag);\r
}\r
if (highlight && iconCurrent == iconWhite) {\r
iconCurrent = iconBlack;\r
we could arrange for this even though neither WinBoard\r
nor the chess program uses a console for stdio? */\r
/*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
-
- /* [AS] Special termination modes for misbehaving programs... */
- if( signal == 9 ) {
- if ( appData.debugMode) {
- fprintf( debugFP, "Terminating process %u\n", cp->pid );
- }
-
- TerminateProcess( cp->hProcess, 0 );
- }
- else if( signal == 10 ) {
- DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
-
- if( dw != WAIT_OBJECT_0 ) {
- if ( appData.debugMode) {
- fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );
- }
-
- TerminateProcess( cp->hProcess, 0 );
- }
- }
-
+\r
+ /* [AS] Special termination modes for misbehaving programs... */\r
+ if( signal == 9 ) {\r
+ if ( appData.debugMode) {\r
+ fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
+ }\r
+\r
+ TerminateProcess( cp->hProcess, 0 );\r
+ }\r
+ else if( signal == 10 ) {\r
+ DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
+\r
+ if( dw != WAIT_OBJECT_0 ) {\r
+ if ( appData.debugMode) {\r
+ fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
+ }\r
+\r
+ TerminateProcess( cp->hProcess, 0 );\r
+ }\r
+ }\r
+\r
CloseHandle(cp->hProcess);\r
break;\r
\r
AddInputSource(ProcRef pr, int lineByLine,\r
InputCallback func, VOIDSTAR closure)\r
{\r
- InputSource *is, *is2 = NULL;
+ InputSource *is, *is2 = NULL;\r
ChildProc *cp = (ChildProc *) pr;\r
\r
is = (InputSource *) calloc(1, sizeof(InputSource));\r
consoleInputSource = is;\r
} else {\r
is->kind = cp->kind;\r
- /*
- [AS] Try to avoid a race condition if the thread is given control too early:
- we create all threads suspended so that the is->hThread variable can be
- safely assigned, then let the threads start with ResumeThread.
- */
+ /* \r
+ [AS] Try to avoid a race condition if the thread is given control too early:\r
+ we create all threads suspended so that the is->hThread variable can be\r
+ safely assigned, then let the threads start with ResumeThread.\r
+ */\r
switch (cp->kind) {\r
case CPReal:\r
is->hFile = cp->hFrom;\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
- (LPVOID) is, CREATE_SUSPENDED, &is->id);
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
break;\r
\r
case CPComm:\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
- (LPVOID) is, CREATE_SUSPENDED, &is->id);
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
break;\r
\r
case CPSock:\r
is->sock = cp->sock;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, CREATE_SUSPENDED, &is->id);
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
break;\r
\r
case CPRcmd:\r
is2->second = is2;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, CREATE_SUSPENDED, &is->id);
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
is2->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
+ (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
break;\r
}\r
-
- if( is->hThread != NULL ) {
- ResumeThread( is->hThread );
+\r
+ if( is->hThread != NULL ) {\r
+ ResumeThread( is->hThread );\r
+ }\r
+\r
+ if( is2 != NULL && is2->hThread != NULL ) {\r
+ ResumeThread( is2->hThread );\r
+ }\r
}\r
-
- if( is2 != NULL && is2->hThread != NULL ) {
- ResumeThread( is2->hThread );
- }
- }
-
+\r
return (InputSourceRef) is;\r
}\r
\r
FARPROC lpProc;\r
char *p, *q;\r
\r
- /* [AS] */
- EngineOutputPopUp();
- return;
-
+ /* [AS] */\r
+ EngineOutputPopUp();\r
+ return;\r
+\r
if (str == NULL) str = "";\r
p = (char *) malloc(2 * strlen(str) + 2);\r
q = p;\r
int column; int row; POINT * pt;\r
{\r
if (flipView) {\r
- pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
+ pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
pt->y = lineGap + row * (squareSize + lineGap);\r
} else {\r
pt->x = lineGap + column * (squareSize + lineGap);\r
- pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
+ pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
}\r
}\r
\r
}\r
\r
void\r
-HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
-{\r
-#if 0
- char buf[256];
-\r
- sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",
- first, last, current, current >= 0 ? movelist[current] : "n/a" );
-\r
- OutputDebugString( buf );
-#endif
-
- MoveHistorySet( movelist, first, last, current, pvInfoList );
-
- EvalGraphSet( first, last, current, pvInfoList );
-}
-
-void SetProgramStats( FrontEndProgramStats * stats )
-{
-#if 0
- char buf[1024];
-
- sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",
- stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );
-
- OutputDebugString( buf );
-#endif
-
- EngineOutputUpdate( stats );
-}
+HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
+{\r
+#if 0\r
+ char buf[256];\r
+\r
+ sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
+ first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
+\r
+ OutputDebugString( buf );\r
+#endif\r
+\r
+ MoveHistorySet( movelist, first, last, current, pvInfoList );\r
+\r
+ EvalGraphSet( first, last, current, pvInfoList );\r
+}\r
+\r
+void SetProgramStats( FrontEndProgramStats * stats )\r
+{\r
+#if 0\r
+ char buf[1024];\r
+\r
+ sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
+ stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
+\r
+ OutputDebugString( buf );\r
+#endif\r
+\r
+ EngineOutputUpdate( stats );\r
+}\r