From 056614196635a4730170261fe0e638191f14c620 Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Sun, 19 Apr 2009 09:34:31 -0700 Subject: [PATCH] changes from H.G. Muller; version 4.3.2 --- backend.c |22228 ++++++++++++++++++++++++++------------------------ bitmaps/a49o.bmp | Bin 0 -> 454 bytes bitmaps/a49s.bmp | Bin 0 -> 454 bytes bitmaps/a49w.bmp | Bin 0 -> 454 bytes bitmaps/a72o.bmp | Bin 0 -> 926 bytes bitmaps/a72s.bmp | Bin 0 -> 926 bytes bitmaps/a72w.bmp | Bin 0 -> 926 bytes bitmaps/c49o.bmp | Bin 0 -> 454 bytes bitmaps/c49s.bmp | Bin 0 -> 454 bytes bitmaps/c49w.bmp | Bin 0 -> 454 bytes bitmaps/c72o.bmp | Bin 0 -> 926 bytes bitmaps/c72s.bmp | Bin 0 -> 926 bytes bitmaps/c72w.bmp | Bin 0 -> 926 bytes bitmaps/e49o.bmp | Bin 0 -> 454 bytes bitmaps/e49s.bmp | Bin 0 -> 454 bytes bitmaps/e49w.bmp | Bin 0 -> 454 bytes bitmaps/e72o.bmp | Bin 0 -> 926 bytes bitmaps/e72s.bmp | Bin 0 -> 926 bytes bitmaps/e72w.bmp | Bin 0 -> 926 bytes bitmaps/f49o.bmp | Bin 0 -> 454 bytes bitmaps/f49s.bmp | Bin 0 -> 454 bytes bitmaps/f49w.bmp | Bin 0 -> 454 bytes bitmaps/f72o.bmp | Bin 0 -> 926 bytes bitmaps/f72s.bmp | Bin 0 -> 926 bytes bitmaps/f72w.bmp | Bin 0 -> 926 bytes bitmaps/h49o.bmp | Bin 0 -> 454 bytes bitmaps/h49s.bmp | Bin 0 -> 454 bytes bitmaps/h49w.bmp | Bin 0 -> 454 bytes bitmaps/h72o.bmp | Bin 0 -> 926 bytes bitmaps/h72s.bmp | Bin 0 -> 926 bytes bitmaps/h72w.bmp | Bin 0 -> 926 bytes bitmaps/m49o.bmp | Bin 0 -> 454 bytes bitmaps/m49s.bmp | Bin 0 -> 454 bytes bitmaps/m49w.bmp | Bin 0 -> 454 bytes bitmaps/m72o.bmp | Bin 0 -> 926 bytes bitmaps/m72s.bmp | Bin 0 -> 926 bytes bitmaps/m72w.bmp | Bin 0 -> 926 bytes bitmaps/o49o.bmp | Bin 0 -> 454 bytes bitmaps/o49s.bmp | Bin 0 -> 454 bytes bitmaps/o49w.bmp | Bin 0 -> 454 bytes bitmaps/o72o.bmp | Bin 0 -> 926 bytes bitmaps/o72s.bmp | Bin 0 -> 926 bytes bitmaps/o72w.bmp | Bin 0 -> 926 bytes bitmaps/u49o.bmp | Bin 0 -> 454 bytes bitmaps/u49s.bmp | Bin 0 -> 454 bytes bitmaps/u49w.bmp | Bin 0 -> 454 bytes bitmaps/u72o.bmp | Bin 0 -> 926 bytes bitmaps/u72s.bmp | Bin 0 -> 926 bytes bitmaps/u72w.bmp | Bin 0 -> 926 bytes bitmaps/w49o.bmp | Bin 0 -> 454 bytes bitmaps/w49s.bmp | Bin 0 -> 454 bytes bitmaps/w49w.bmp | Bin 0 -> 454 bytes bitmaps/w72o.bmp | Bin 0 -> 926 bytes bitmaps/w72s.bmp | Bin 0 -> 926 bytes bitmaps/w72w.bmp | Bin 0 -> 926 bytes common.h | 1104 ++-- config.h | 276 +- moves.c | 2265 +++--- moves.h | 303 +- parser.h | 119 +- parser.l | 1919 +++--- readme_HGM.txt | 125 + winboard/resource.h | 162 +- winboard/winboard.c | 3860 +++++---- winboard/winboard.rc | 948 ++- 65 files changed, 17527 insertions(+), 15782 deletions(-) create mode 100644 bitmaps/a49o.bmp create mode 100644 bitmaps/a49s.bmp create mode 100644 bitmaps/a49w.bmp create mode 100644 bitmaps/a72o.bmp create mode 100644 bitmaps/a72s.bmp create mode 100644 bitmaps/a72w.bmp create mode 100644 bitmaps/c49o.bmp create mode 100644 bitmaps/c49s.bmp create mode 100644 bitmaps/c49w.bmp create mode 100644 bitmaps/c72o.bmp create mode 100644 bitmaps/c72s.bmp create mode 100644 bitmaps/c72w.bmp create mode 100644 bitmaps/e49o.bmp create mode 100644 bitmaps/e49s.bmp create mode 100644 bitmaps/e49w.bmp create mode 100644 bitmaps/e72o.bmp create mode 100644 bitmaps/e72s.bmp create mode 100644 bitmaps/e72w.bmp create mode 100644 bitmaps/f49o.bmp create mode 100644 bitmaps/f49s.bmp create mode 100644 bitmaps/f49w.bmp create mode 100644 bitmaps/f72o.bmp create mode 100644 bitmaps/f72s.bmp create mode 100644 bitmaps/f72w.bmp create mode 100644 bitmaps/h49o.bmp create mode 100644 bitmaps/h49s.bmp create mode 100644 bitmaps/h49w.bmp create mode 100644 bitmaps/h72o.bmp create mode 100644 bitmaps/h72s.bmp create mode 100644 bitmaps/h72w.bmp create mode 100644 bitmaps/m49o.bmp create mode 100644 bitmaps/m49s.bmp create mode 100644 bitmaps/m49w.bmp create mode 100644 bitmaps/m72o.bmp create mode 100644 bitmaps/m72s.bmp create mode 100644 bitmaps/m72w.bmp create mode 100644 bitmaps/o49o.bmp create mode 100644 bitmaps/o49s.bmp create mode 100644 bitmaps/o49w.bmp create mode 100644 bitmaps/o72o.bmp create mode 100644 bitmaps/o72s.bmp create mode 100644 bitmaps/o72w.bmp create mode 100644 bitmaps/u49o.bmp create mode 100644 bitmaps/u49s.bmp create mode 100644 bitmaps/u49w.bmp create mode 100644 bitmaps/u72o.bmp create mode 100644 bitmaps/u72s.bmp create mode 100644 bitmaps/u72w.bmp create mode 100644 bitmaps/w49o.bmp create mode 100644 bitmaps/w49s.bmp create mode 100644 bitmaps/w49w.bmp create mode 100644 bitmaps/w72o.bmp create mode 100644 bitmaps/w72s.bmp create mode 100644 bitmaps/w72w.bmp create mode 100644 readme_HGM.txt diff --git a/backend.c b/backend.c index af1a7c5..29ebca6 100644 --- a/backend.c +++ b/backend.c @@ -1,10787 +1,11441 @@ -/* - * 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 - -#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) ); - -#else - -#define DoSleep( n ) - -#endif - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#if STDC_HEADERS -# include -# include -#else /* not STDC_HEADERS */ -# if HAVE_STRING_H -# include -# else /* not HAVE_STRING_H */ -# include -# endif /* not HAVE_STRING_H */ -#endif /* not STDC_HEADERS */ - -#if HAVE_SYS_FCNTL_H -# include -#else /* not HAVE_SYS_FCNTL_H */ -# if HAVE_FCNTL_H -# include -# endif /* HAVE_FCNTL_H */ -#endif /* not HAVE_SYS_FCNTL_H */ - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# 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 -#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 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%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, " 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= 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 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 ")) || - looking_at(buf, &i, " ")) { - 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; iuseSetboard) { - 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) " - * 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 - 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]); - } - } -} +/* + * 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 + +#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) ); + +#else + +#define DoSleep( n ) + +#endif + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_SYS_FCNTL_H +# include +#else /* not HAVE_SYS_FCNTL_H */ +# if HAVE_FCNTL_H +# include +# endif /* HAVE_FCNTL_H */ +#endif /* not HAVE_SYS_FCNTL_H */ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# 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 +#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 +#define GE_ENGINE1 5 +#define GE_ENGINE2 6 + +/* 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]; +/* [HGM] Following 7 needed for accurate legality tests: */ +char epStatus[MAX_MOVES]; +char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1 +char castlingRank[BOARD_SIZE]; // and corresponding ranks +char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE]; +int nrCastlingRights; // For TwoKings, or to implement castling-unknown status +int initialRulePlies, FENrulePlies; +char FENepStatus; + +ChessSquare FIDEArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, + WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackBishop, BlackQueen, + BlackKing, BlackBishop, BlackKnight, BlackRook } +}; + +ChessSquare twoKingsArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, + WhiteKing, WhiteKing, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackBishop, BlackQueen, + BlackKing, BlackKing, BlackKnight, BlackRook } +}; + +#ifdef FAIRY +ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */ + { WhiteFairyRook, WhiteFairyKnight, WhiteFairyBishop, WhiteQueen, + WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, + { BlackFairyRook, BlackFairyKnight, BlackFairyBishop, BlackQueen, + BlackKing, BlackBishop, BlackKnight, BlackRook } +}; + +ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */ + { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn, + WhiteKing, WhiteFairyBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn, + BlackKing, BlackFairyBishop, BlackKnight, BlackRook } +}; + +#if (BOARD_SIZE>=9) +ChessSquare XiangqiArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteFairyPawn, + WhiteKing, WhiteFairyPawn, WhiteFairyBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackFairyBishop, BlackFairyPawn, + BlackKing, BlackFairyPawn, BlackFairyBishop, BlackKnight, BlackRook } +}; +#else +#define XiangqiPosition FIDEPosition +#endif + +#if (BOARD_SIZE>=10) +ChessSquare CapablancaArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteCardinal, WhiteBishop, WhiteQueen, + WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackCardinal, BlackBishop, BlackQueen, + BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook } +}; + +#ifdef GOTHIC +ChessSquare GothicArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, + WhiteKing, WhiteCardinal, WhiteBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, + BlackKing, BlackCardinal, BlackBishop, BlackKnight, BlackRook } +}; +#else // !GOTHIC +#define GothicArray CapablancaArray +#endif // !GOTHIC + +#else // !(BOARD_SIZE>=10) +#define CapablancaArray FIDEArray +#define GothicArray FIDEArray +#endif // !(BOARD_SIZE>=10) + +#if (BOARD_SIZE>=12) +ChessSquare CourierArray[2][BOARD_SIZE] = { + { WhiteRook, WhiteKnight, WhiteFairyBishop, WhiteBishop, WhiteFairyKing, WhiteKing, + WhiteFairyPawn, WhiteFairyRook, WhiteBishop, WhiteFairyBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackFairyBishop, BlackBishop, BlackFairyKing, BlackKing, + BlackFairyPawn, BlackFairyRook, BlackBishop, BlackFairyBishop, BlackKnight, BlackRook } +}; +#else // !(BOARD_SIZE>=12) +#define CourierArray CapablancaArray +#endif // !(BOARD_SIZE>=12) +#endif // FAIRY + + +Board initialPosition; + + +/* 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 [HGM] and game state */ + { + int i, j; + + for( i=0; i 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: +#ifndef FAIRY + case Variant31: + case Variant32: + case Variant33: + case Variant34: + case Variant35: + case Variant36: +#endif + 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 */ +#ifdef FAIRY + case VariantShogi: + case VariantXiangqi: + case VariantFairy: /* [HGM] TestLegality definitely off! */ + case VariantGothic: + case VariantCapablanca: + case VariantCourier: +#endif + 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%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, " 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= 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 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 ")) || + looking_at(buf, &i, " ")) { + 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, + castlingRights[moveNum]) ) { + 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]); + } + /* [HGM] decrement all digits to code ranks starting from 0 */ + if(BOARD_HEIGHT>8) { + char *p = buf; + while(*p) { if(*p < 'A') (*p)--; p++; } + } + 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] - ONE; + int toX = moveList[moveNum][2] - 'a'; + int toY = moveList[moveNum][3] - ONE; + 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: +#ifdef FAIRY + case WhitePromotionChancellor: + case BlackPromotionChancellor: + case WhitePromotionArchbishop: + case BlackPromotionArchbishop: +#endif + sprintf(user_move, "%c%c%c%c=%c\n", + 'a' + fromX, ONE + fromY, 'a' + toX, ONE + toY, + PieceToChar(PromoPiece(moveType))); + break; + case WhiteDrop: + case BlackDrop: + sprintf(user_move, "%c@%c%c\n", + ToUpper(PieceToChar((ChessSquare) fromX)), + 'a' + toX, ONE + 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, ONE + fromY, 'a' + toX, ONE + 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, ONE + rt); + } else { + if (promoChar == 'x' || promoChar == NULLCHAR) { + sprintf(move, "%c%c%c%c\n", + 'a' + ff, ONE + rf, 'a' + ft, ONE + rt); + } else { + sprintf(move, "%c%c%c%c%c\n", + 'a' + ff, ONE + rf, 'a' + ft, ONE + 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) { +#ifdef FAIRY + case WhitePromotionChancellor: + case BlackPromotionChancellor: + case WhitePromotionArchbishop: + case BlackPromotionArchbishop: +#endif + 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] - ONE; + *toX = currentMoveString[2] - 'a'; + *toY = currentMoveString[3] - ONE; + *promoChar = currentMoveString[4]; + if (*fromX < 0 || *fromX >= BOARD_WIDTH || *fromY < 0 || *fromY >= BOARD_HEIGHT || + *toX < 0 || *toX >= BOARD_WIDTH || *toY < 0 || *toY >= BOARD_HEIGHT) { + *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] - ONE; + *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>1; + castlingRights[0][3] = initialRights[3] = BOARD_WIDTH-1; + castlingRights[0][4] = initialRights[4] = 0; + castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1; + + castlingRank[0] = castlingRank[1] = castlingRank[2] = 0; + castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1; + + initialRulePlies = 0; /* 50-move counter start */ + } + + + /* [HGM] logic here is completely changed. In stead of full positions */ + /* the initialized data only consist of the two backranks. The switch */ + /* selects which one we will use, which is than copied to the Board */ + /* initialPosition, which for the rest is initialized by Pawns and */ + /* empty squares. This initial position is then copied to boards[0], */ + /* possibly after shuffling, so that it remains available. */ + + switch (gameInfo.variant) { + default: + pieces = BOARD_WIDTH <= 8 ? FIDEArray : + BOARD_WIDTH <= 10 ? CapablancaArray : CourierArray; + break; + case VariantShatranj: + pieces = ShatranjArray; + CharToPiece('E'); /* associate PGN/FEN letter with internal piece type */ + CharToPiece('e'); + nrCastlingRights = 0; + for(i=0; i=BOARD_WIDTH-2) { + initialPosition[2][j] = WhiteFairyMarshall; + initialPosition[BOARD_HEIGHT-3][j] = BlackFairyMarshall; + } + } else { + initialPosition[3][j] = WhitePawn; + initialPosition[BOARD_HEIGHT-4][j] = BlackPawn; + } + } else { + initialPosition[1][j] = WhitePawn; + initialPosition[BOARD_HEIGHT-2][j] = BlackPawn; + } + initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j]; + } + + if(gameInfo.variant == VariantFischeRandom) { + if( appData.defaultFrcPosition < 0 ) { + ShuffleFRC( initialPosition ); + } + else { + SetupFRC( initialPosition, appData.defaultFrcPosition ); + } + } + + CopyBoard(boards[0], initialPosition); + + 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_HEIGHT - 1; i >= 0; i--) { + bp = &boards[moveNum][i][0]; + for (j = 0; j < BOARD_WIDTH; j++, bp++) { + if ((int) *bp < (int) BlackPawn) { + sprintf(message, "%c%c%c\n", PieceToChar(*bp), + 'a' + j, ONE + i); + SendToProgram(message, cps); + } + } + } + + SendToProgram("c\n", cps); + for (i = BOARD_HEIGHT - 1; i >= 0; i--) { + bp = &boards[moveNum][i][0]; + for (j = 0; j < BOARD_WIDTH; j++, bp++) { + if (((int) *bp != (int) EmptySquare) + && ((int) *bp >= (int) BlackPawn)) { + sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)), + 'a' + j, ONE + i); + SendToProgram(message, cps); + } + } + } + + SendToProgram(".\n", cps); + } +} + +int +IsPromotion(fromX, fromY, toX, toY) + int fromX, fromY, toX, toY; +{ + return gameMode != EditPosition && gameInfo.variant != VariantXiangqi && + fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 && + ((boards[currentMove][fromY][fromX] == WhitePawn && toY == BOARD_HEIGHT-1) || + (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0)); +} + + +int +PieceForSquare (x, y) + int x; + int y; +{ + if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) + 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)BlackPawn; /* [HGM] can be > King! */ + + 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) EmptySquare) { + /* 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, castlingRights[currentMove], + 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, castlingRights[currentMove]) ) { + 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; + } + + /* [HGM] Apparently legal, but so far only tested with EP_UNKOWN */ + /* So we have to redo legality test with true e.p. status here, */ + /* to make sure an illegal e.p. capture does not slip through, */ + /* to cause a forfeit on a justified illegal-move complaint */ + /* of the opponent. */ + if(gameMode==TwoMachinesPlay && appData.testLegality && + LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove), + epStatus[forwardMostMove], castlingRights[forwardMostMove], + fromY, fromX, toY, toX, promoChar) == IllegalMove) + { static char buf[MSG_SIZ]; + if (appData.debugMode) { + int i; + for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ", + castlingRights[forwardMostMove][i], castlingRank[i]); + fprintf(debugFP, "castling rights\n"); + } + sprintf(buf, "Xboard: Forfeit due to illegal move %s (%c%c%c%c)%c", + machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0); + GameEnds(machineWhite ? BlackWins : WhiteWins, buf, GE_XBOARD); + } + 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; + } + } + +#ifdef ADJUDICATE // [HGM] some adjudications useful with buggy engines + + if( gameMode == TwoMachinesPlay ) { + int count = 0, epFile = epStatus[forwardMostMove]; + + if(appData.testLegality) // don't wait for engine to announce game end if we can judge ourselves + switch (MateTest(boards[forwardMostMove], + PosFlags(forwardMostMove), epFile, + castlingRights[forwardMostMove]) ) { + case MT_NONE: + case MT_CHECK: + default: + break; + case MT_STALEMATE: + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", + GE_XBOARD ); + break; + case MT_CHECKMATE: + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, + "Xboard adjudication: Checkmate", + GE_XBOARD ); + break; + } + + if( appData.testLegality ) + { /* [HGM] Some more adjudications for obstinate engines */ + int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0, + NrWQ=0, NrBQ=0, + NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k; + static int moveCount; + + /* First absolutely insufficient mating material. Count what is on board. */ + for(i=0; i=backwardMostMove; i-- ) + fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]); + + } + /* Check for rep-draws */ + count = 0; + for(k = forwardMostMove-2; + k>=backwardMostMove && k>=forwardMostMove-100 && + epStatus[k] <= EP_NONE && epStatus[k+1] <= EP_NONE; + k-=2) + { int rights=0; + if (appData.debugMode) { + fprintf(debugFP, " loop\n"); + } + if(CompareBoards(boards[k], boards[forwardMostMove])) { + if (appData.debugMode) { + fprintf(debugFP, "match\n"); + } + /* compare castling rights */ + if( castlingRights[forwardMostMove][2] != castlingRights[k][2] && + (castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) ) + rights++; /* King lost rights, while rook still had them */ + if( castlingRights[forwardMostMove][2] >= 0 ) { /* king has rights */ + if( castlingRights[forwardMostMove][0] != castlingRights[k][0] || + castlingRights[forwardMostMove][1] != castlingRights[k][1] ) + rights++; /* but at least one rook lost them */ + } + if( castlingRights[forwardMostMove][5] != castlingRights[k][5] && + (castlingRights[k][3] >= 0 || castlingRights[k][4] >= 0) ) + rights++; + if( castlingRights[forwardMostMove][5] >= 0 ) { + if( castlingRights[forwardMostMove][3] != castlingRights[k][3] || + castlingRights[forwardMostMove][4] != castlingRights[k][4] ) + rights++; + } + if (appData.debugMode) { + for(i=0; i appData.drawRepeats-2 + && adjudicateLossThreshold != 0) { + /* adjudicate after user-specified nr of repeats */ + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD ); + return; + } + if( rights == 0 && count > 1 ) /* occurred 2 or more times before */ + epStatus[forwardMostMove] = EP_REP_DRAW; + } + } + + /* Now we test for 50-move draws. Determine ply count */ + count = forwardMostMove; + /* look for last irreversble move */ + while( epStatus[count] <= EP_NONE && count > backwardMostMove ) + count--; + /* if we hit starting position, add initial plies */ + if( count == backwardMostMove ) + count -= initialRulePlies; + count = forwardMostMove - count; + if( count >= 100) + epStatus[forwardMostMove] = EP_RULE_DRAW; + /* this is used to judge if draw claims are legal */ + if(adjudicateLossThreshold != 0 && count >= 2*appData.ruleMoves) { + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD ); + return; + } + } + + + } +#endif + 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]); + + /* [HGM] illegal-move claim should forfeit game when Xboard */ + /* only passes fully legal moves */ + if( appData.testLegality && gameMode == TwoMachinesPlay ) { + static char buf[MSG_SIZ]; + sprintf(buf, "False illegal-move claim on %s (%c%c%c%c)%c", + machineMove, fromX+'a', fromY+ONE, toX+'a', toY+ONE, 0); + GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins, + buf, GE_XBOARD ); + } + 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_ENGINE1 + (cps != &first)); /* [HGM] pass claimer indication for claim test */ + 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_ENGINE1 + (cps != &first)); + return; + } + GameEnds(BlackWins, r, GE_ENGINE1 + (cps != &first)); + 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_ENGINE1 + (cps != &first)); + return; + + } else if (strncmp(message, "White resign", 12) == 0) { + GameEnds(BlackWins, "White resigns", GE_ENGINE1 + (cps != &first)); + return; + } else if (strncmp(message, "Black resign", 12) == 0) { + GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first)); + return; + } else if (strncmp(message, "White", 5) == 0 && + message[5] != '(' && + StrStr(message, "Black") == NULL) { + GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first)); + return; + } else if (strncmp(message, "Black", 5) == 0 && + message[5] != '(') { + GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first)); + 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_ENGINE1 + (cps != &first)); + else + GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first)); + 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_ENGINE1 + (cps != &first)); + else + GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first)); + 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_ENGINE1); + break; + case MachinePlaysWhite: + case IcsPlayingWhite: + GameEnds(WhiteWins, "White mates", GE_ENGINE); + break; + case TwoMachinesPlay: + if (cps->twoMachinesColor[0] == 'w') + GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first)); + else + GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first)); + break; + default: + /* can't happen */ + break; + } + return; + } else if (strncmp(message, "checkmate", 9) == 0) { + if (WhiteOnMove(forwardMostMove)) { + GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first)); + } else { + GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first)); + } + return; + } else if (strstr(message, "Draw") != NULL || + strstr(message, "game is a draw") != NULL) { + GameEnds(GameIsDrawn, "Draw", GE_ENGINE1 + (cps != &first)); + 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) " + * 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) { +#ifdef FAIRY + case WhitePromotionChancellor: + case BlackPromotionChancellor: + case WhitePromotionArchbishop: + case BlackPromotionArchbishop: +#endif + 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] - ONE; + toX = currentMoveString[2] - 'a'; + toY = currentMoveString[3] - ONE; + 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] - ONE; + 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, castlingRights[boardIndex]) ) { + 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; +{ + /* [HGM] In Shatranj and Courier all promotions are to Ferz */ + if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier) + && promoChar != 0) promoChar = 'f'; + + 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][BOARD_WIDTH-2] = WhiteKing; board[0][BOARD_WIDTH-3] = 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[BOARD_HEIGHT-1][BOARD_WIDTH-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_WIDTH-3] = BlackRook; + } else { + board[BOARD_HEIGHT-1][2] = BlackKing; board[BOARD_HEIGHT-1][3] = BlackRook; + } + /* End of code added by Tord */ + + } else if (initialPosition[fromY][fromX] == WhiteKing + && board[fromY][fromX] == WhiteKing + && toY == fromY && toX > fromX+1) { + board[fromY][fromX] = EmptySquare; + board[toY][toX] = WhiteKing; + board[fromY][BOARD_WIDTH-1] = EmptySquare; + board[toY][toX-1] = WhiteRook; + } else if (initialPosition[fromY][fromX] == WhiteKing + && board[fromY][fromX] == WhiteKing + && toY == fromY && toX < fromX-1) { + board[fromY][fromX] = EmptySquare; + board[toY][toX] = WhiteKing; + board[fromY][0] = EmptySquare; + board[toY][toX+1] = 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 == BOARD_HEIGHT-1 +#ifdef FAIRY + && gameInfo.variant != VariantXiangqi +#endif + ) { + /* white pawn promotion */ + board[toY][toX] = CharToPiece(ToUpper(promoChar)); + if (board[toY][toX] == EmptySquare) { + board[toY][toX] = WhiteQueen; + } + board[fromY][fromX] = EmptySquare; + } else if ((fromY == BOARD_HEIGHT-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 (initialPosition[fromY][fromX] == BlackKing + && board[fromY][fromX] == BlackKing + && toY == fromY && toX > fromX+1) { + board[fromY][fromX] = EmptySquare; + board[toY][toX] = BlackKing; + board[fromY][BOARD_WIDTH-1] = EmptySquare; + board[toY][toX-1] = BlackRook; + } else if (initialPosition[fromY][fromX] == BlackKing + && board[fromY][fromX] == BlackKing + && toY == fromY && toX < fromX-1) { + board[fromY][fromX] = EmptySquare; + board[toY][toX] = BlackKing; + board[fromY][0] = EmptySquare; + board[toY][toX+1] = 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 +#ifdef FAIRY + && gameInfo.variant != VariantXiangqi +#endif + ) { + /* 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 < BOARD_WIDTH && x >= 0 && x < BOARD_WIDTH && + 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]); + /* [HGM] compute & store e.p. status and castling rights for new position */ + { int i, j; + + epStatus[forwardMostMove] = EP_NONE; + + if( boards[forwardMostMove][toY][toX] != EmptySquare ) + epStatus[forwardMostMove] = EP_CAPTURE; + + if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) { + epStatus[forwardMostMove] = EP_PAWN_MOVE; + if( toY-fromY==2 && + (toX>1 && boards[forwardMostMove][toY][toX-1] == BlackPawn || + toX1 && boards[forwardMostMove][toY][toX-1] == WhitePawn || + toXinitString, 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; + char buf[MSG_SIZ]; + + if (appData.debugMode) { + fprintf(debugFP, "GameEnds(%d, %s, %d)\n", + result, resultDetails ? resultDetails : "(null)", whosays); + } + + if (appData.icsActive && whosays == (GE_ENGINE || whosays >= GE_ENGINE1)) { + /* 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 (appData.debugMode) { + fprintf(debugFP, "GameEnds(%d, %s, %d) clock stopped\n", + result, resultDetails ? resultDetails : "(null)", whosays); + } + if (!isIcsGame && !appData.noChessProgram) + SetUserThinkingEnables(); + + /* [HGM] if a machine claims the game end we verify this claim */ + if( appData.testLegality && gameMode == TwoMachinesPlay && + appData.testClaims && whosays >= GE_ENGINE1 ) { + char claimer; + + if (appData.debugMode) { + fprintf(debugFP, "GameEnds(%d, %s, %d) test claims\n", + result, resultDetails ? resultDetails : "(null)", whosays); + } + claimer = whosays == GE_ENGINE1 ? /* color of claimer */ + first.twoMachinesColor[0] : + second.twoMachinesColor[0] ; + if( result == WhiteWins && claimer == 'w' || + result == BlackWins && claimer == 'b' ) { + /* Xboard immediately adjudicates all mates, so win claims must be false */ + sprintf(buf, "False win claim: '%s'", resultDetails); + result = claimer == 'w' ? BlackWins : WhiteWins; + resultDetails = buf; + } else + if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS ) { + /* Draw that was not flagged by Xboard is false */ + sprintf(buf, "False draw claim: '%s'", resultDetails); + result = claimer == 'w' ? BlackWins : WhiteWins; + resultDetails = buf; + } + /* (Claiming a loss is accepted no questions asked!) */ + } + + if (appData.debugMode) { + fprintf(debugFP, "GameEnds(%d, %s, %d) after test\n", + result, resultDetails ? resultDetails : "(null)", whosays); + } + 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++; + if(appData.matchPause>10000 || appData.matchPause<10) + appData.matchPause = 10000; /* [HGM] make pause adjustable */ + ScheduleDelayedEvent(NextMatchGame, appData.matchPause); + 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] - ONE; + + if (moveList[currentMove][1] == '@') { + if (appData.highlightLastMove) { + SetHighlights(-1, -1, toX, toY); + } + } else { + fromX = moveList[currentMove][0] - 'a'; + fromY = moveList[currentMove][1] - ONE; + + 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: +#ifdef FAIRY + case WhitePromotionChancellor: + case BlackPromotionChancellor: + case WhitePromotionArchbishop: + case BlackPromotionArchbishop: +#endif + 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] - ONE; + toX = currentMoveString[2] - 'a'; + toY = currentMoveString[3] - ONE; + 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] - ONE; + 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, castlingRights[currentMove]) ) { + 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, castlingRights[currentMove]) ) { + 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] - ONE; + toX = currentMoveString[2] - 'a'; + toY = currentMoveString[3] - ONE; + 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] - ONE; + toX = cmailMove[lastLoadGameNumber - 1][2] - 'a'; + toY = cmailMove[lastLoadGameNumber - 1][3] - ONE; + promoChar = cmailMove[lastLoadGameNumber - 1][4]; + MakeMove(fromX, fromY, toX, toY, promoChar); + ShowMove(fromX, fromY, toX, toY); + + switch (MateTest(boards[currentMove], PosFlags(currentMove), + EP_UNKNOWN, castlingRights[currentMove]) ) { + 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); + /* [HGM] copy FEN attributes as well */ + { int i; + initialRulePlies = FENrulePlies; + epStatus[0] = FENepStatus; + for( i=0; i< nrCastlingRights; i++ ) + castlingRights[0][i] = FENcastlingRights[i]; + } + 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_HEIGHT - 1; i >= 0; i--) + for (j = 0; j < BOARD_WIDTH; 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': case '9': +#ifdef FAIRY + case 'H': case 'A': case 'M': case 'h': case 'a': case 'm': + case 'E': case 'F': case 'G': case 'e': case 'f': case 'g': + case 'C': case 'W': case 'c': case 'w': +#endif + 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_HEIGHT - 1; i >= 0; i--) { + (void) fgets(line, MSG_SIZ, f); + for (p = line, j = 0; j < BOARD_WIDTH; 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); + /* [HGM] copy FEN attributes as well */ + { int i; + initialRulePlies = FENrulePlies; + epStatus[0] = FENepStatus; + for( i=0; i< nrCastlingRights; i++ ) + castlingRights[0][i] = FENcastlingRights[i]; + } + 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 ) { + /* [HGM] add time */ + char buf[MSG_SIZ]; int seconds = 0; + + if(i >= backwardMostMove) { + /* take the time that changed */ + seconds = timeRemaining[0][i] - timeRemaining[0][i+1]; + if(seconds <= 0) + seconds = timeRemaining[1][i] - timeRemaining[1][i+1]; + } + seconds /= 1000; + if (appData.debugMode) { + fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n", + timeRemaining[0][i+1], timeRemaining[0][i], + timeRemaining[1][i+1], timeRemaining[1][i], seconds + ); + } + + if( seconds < 0 ) buf[0] = 0; else + if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0); + else sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0); + + sprintf( move_buffer, "%s {%s%.2f/%d%s}", + movetext, + pvInfoList[i].score >= 0 ? "+" : "", + pvInfoList[i].score / 100.0, + pvInfoList[i].depth, + buf ); + 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_WIDTH; x++) { + for (y = 0; y < BOARD_HEIGHT; y++) { + if (gameMode == IcsExamining) { + if (boards[currentMove][y][x] != EmptySquare) { + sprintf(buf, "%sx@%c%c\n", ics_prefix, + 'a' + x, ONE + 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, ONE + 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, ONE + 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] - ONE; + 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] - ONE; + 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] - ONE; + if (moveList[target][1] == '@') { + if (appData.highlightLastMove) { + SetHighlights(-1, -1, toX, toY); + } + } else { + fromX = moveList[target][0] - 'a'; + fromY = moveList[target][1] - ONE; + 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_HEIGHT - 1; i >= 0; i--) { + for (j = 0; j < BOARD_WIDTH; j++) { + char c = PieceToChar(boards[move][i][j]); + fputc(c == 'x' ? '.' : c, fp); + fputc(j == BOARD_WIDTH - 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) { + if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell"); + } else { + if(gameMode != TwoMachinesPlay) 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) { + if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell"); + } else { + if(gameMode != TwoMachinesPlay) 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 + 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; + } + /* [HGM] save time for PGN file if engine did not give it */ + if(pvInfoList[forwardMostMove-1].time == -1) + pvInfoList[forwardMostMove-1].time = lastTickLength/100; + 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_HEIGHT - 1; i >= 0; i--) { + emptycount = 0; + for (j = 0; j < BOARD_WIDTH; j++) { + if (boards[move][i][j] == EmptySquare) { + emptycount++; + } else { + if (emptycount > 0) { + if(emptycount<10) /* [HGM] can be >= 10 */ + *p++ = '0' + emptycount; + else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; } + emptycount = 0; + } + *p++ = PieceToChar(boards[move][i][j]); + } + } + if (emptycount > 0) { + if(emptycount<10) /* [HGM] can be >= 10 */ + *p++ = '0' + emptycount; + else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; } + emptycount = 0; + } + *p++ = '/'; + } + *(p - 1) = ' '; + + /* Active color */ + *p++ = whiteToPlay ? 'w' : 'b'; + *p++ = ' '; + + /* HACK: we don't keep track of castling availability, so fake it! */ + /* Tord! please fix with the aid of castlingRights[move][...] */ + + /* 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 < BOARD_WIDTH-1; fk++) { + + if (boards[move][0][fk] == WhiteKing) { + + for (fr = BOARD_WIDTH-1; 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 < BOARD_WIDTH-1; fk++) { + + if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) { + + for (fr = BOARD_WIDTH-1; fr > fk; fr--) { /* H side */ + if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) { + *p++ = useFEN960 ? 'a' + fr : 'k'; + break; + } + } + + for (fr = 0; fr < fk; fr++) { /* A side */ + if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) { + *p++ = useFEN960 ? 'a' + fr : 'q'; + break; + } + } + } + } + + if (q == p) *p++ = '-'; /* No castling rights */ + *p++ = ' '; + } + else { + q = p; + +#ifdef OLDCASTLINGCODE + if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) { + if (boards[move][0][BOARD_WIDTH-1] == WhiteRook) *p++ = 'K'; + if (boards[move][0][0] == WhiteRook) *p++ = 'Q'; + } + if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) { + if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k'; + if (boards[move][BOARD_HEIGHT-1][0] == BlackRook) *p++ = 'q'; + } +#else + /* [HGM] write true castling rights */ + if( nrCastlingRights == 6 ) { + if(castlingRights[move][0] == BOARD_WIDTH-1 && + castlingRights[move][2] >= 0 ) *p++ = 'K'; + if(castlingRights[move][1] == 0 && + castlingRights[move][2] >= 0 ) *p++ = 'Q'; + if(castlingRights[move][3] == BOARD_WIDTH-1 && + castlingRights[move][5] >= 0 ) *p++ = 'k'; + if(castlingRights[move][4] == 0 && + castlingRights[move][5] >= 0 ) *p++ = 'q'; + } +#endif + 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] - ONE; + toX = moveList[move - 1][2] - 'a'; + toY = moveList[move - 1][3] - ONE; + if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) && + toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) && + boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) && + fromX == toX) { + /* 2-square pawn move just happened */ + *p++ = toX + 'a'; + *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3'; + } else { + *p++ = '-'; + } + } else { + *p++ = '-'; + } + + /* [HGM] find reversible plies */ + { int i = 0, j=move; + + if (appData.debugMode) { int k; + fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove); + for(k=backwardMostMove; k<=forwardMostMove; k++) + fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]); + + } + + while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++; + if( j == backwardMostMove ) i += initialRulePlies; + sprintf(p, " %d", i); + p += i>=100 ? 4 : i >= 10 ? 3 : 2; + } + /* 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_HEIGHT - 1; i >= 0; i--) { + j = 0; + for (;;) { + if (*p == '/' || *p == ' ') { + if (*p == '/') p++; + emptycount = BOARD_WIDTH - j; + while (emptycount--) board[i][j++] = EmptySquare; + break; +#if(BOARD_SIZE >= 10) + } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */ + p++; emptycount=10; + if (j + emptycount > BOARD_WIDTH) return FALSE; + while (emptycount--) board[i][j++] = EmptySquare; +#endif + } else if (isdigit(*p)) { + emptycount = *p++ - '0'; + while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */ + if (j + emptycount > BOARD_WIDTH) return FALSE; + while (emptycount--) board[i][j++] = EmptySquare; + } else if (isalpha(*p)) { + if (j >= BOARD_WIDTH) 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; + } + + /* [HGM] We NO LONGER ignore the rest of the FEN notation */ + /* return the extra info in global variiables */ + { + /* set defaults in case FEN is incomplete */ + FENepStatus = EP_UNKNOWN; + for(i=0; i>1; + break; + case'Q': + FENcastlingRights[1] = 0; + FENcastlingRights[2] = BOARD_WIDTH>>1; + break; + case'k': + FENcastlingRights[3] = BOARD_WIDTH-1; + FENcastlingRights[5] = BOARD_WIDTH>>1; + break; + case'q': + FENcastlingRights[4] = 0; + FENcastlingRights[5] = BOARD_WIDTH>>1; + break; + /* Tord! FRC! */ + } + } + + while(*p==' ') p++; + + + if(*p=='-') { + p++; FENepStatus = EP_NONE; + } else { + char c = *p++ - 'a'; + + if(c < 0 || c >= BOARD_WIDTH) return TRUE; + if(*p >= '0' && *p <='9') *p++; + FENepStatus = c; + } + + if(sscanf(p, "%d", &i) == 1) { + FENrulePlies = i; /* 50-move ply counter */ + /* (The move number is still ignored) */ + } + } + 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); + /* [HGM] copy FEN attributes as well */ + { int i; + initialRulePlies = FENrulePlies; + epStatus[0] = FENepStatus; + for( i=0; i5QX<2g^nX=wJsntdUtROx=lyt2ssLcp>zbL@@Eu3^GBYy2^E!{Y`)o- z_cA+~?kmYP<~?MO-d#y~SP{Ek>1=#o0EEr*U%Z&S^wLQGE^`vMGa4^<#DF#Z(qD*L zj(W~h&)im-(WQaSPvB%sz$R0~WuHQSauAC?>tud%pMP4Qwp;@qvWB+C0F7j_Z=Dy~ z6(=t(E?v~NKWFaGxY`_zfjI|FN*;j+$Qy7r(h!Wam3;ygo9njldMHe;5G;HDk1s1B BA|3z$ literal 0 HcmV?d00001 diff --git a/bitmaps/a49s.bmp b/bitmaps/a49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7b3836f32dcec3788964e6ee783f15e1400fa63e GIT binary patch literal 454 zcmbV|u?@m75Ji9FA{`mQEiDTWiJl$UB-g=MAR*?+2rg4ZG5`DG2tkLXyC?hpoxgoL zzZ#TvWZp9lYc%k}jd<=nif~OiXE36gO;tnegWC-d5Pgi;FVwDiTS&yR$Renk|!MO=Y|p8eV}X)Z^b-v;9xDmP%z zUgr*s%AH&=)w@6F{l13+dd+1ZiVM(*iFz2=&O-@or+})L1JX}3{WbS`d&dj}&bPgO Wujyn>^|EwEfA>CGt{J3&zaP-u50|8*f!_ zOhY{<<%A*uHIjLnGD2td{9zuidQ*R;mt2&YMR|fUKQ7sFVPXJUuc0sioYZ4~njDQa tn4z5?$ShVpED>6)F|(EE{Jpn$b>!3`J>p@RqFEw07(XYM@h>-XSNce3JGm9(oWR!J`WpV*e+pR0xo<0!Y7n zQ`8Tw*Z0p+Y`q{qA>JYe8BD+8{rAbkgh;9?2w%_|>K)f?Z)%2qoQ<~`yo-#wiX`xc z_&}V4FC7C-N4+i9*I@S4Vm~G&tL~DJovY*)Ctgy%khWQNM!6-~~Jc134NdSh&y- z88*Sm7%jm*!^L=@PRGzCJ5O3LE%44-aB{Yqd*&3lm-Rhs=zQw(cNY6$xd$A8<=(iN r9|=jB;-S1exBuJEgd5u2d{^X^<-r9fHSX@ii)A!Ujv<~`eoEToj2Wao?T`R6XV z++MBNI>Vn}*RVz!i{G*T-n&^KoT>`U7h)Clhu2~|Ye9TD7*9QT7HM@5>A(~00XqaA z69X*{y)Ek3VAj;pUUo`Wouz|!UZs55hxPa*oWpp5?}Ir7HY}c*ox6x{ajuh@la|Qc z;+WR@W=*b>`{vn3WbLB<0B(by;3gQztD*CYE5T}-ZWo%&%0w(ab1xkkjTyYC&*sU!={;GU2gvJr^PMoEep6S5A1DtV(W!F!AGZG= LsH@&E>im^ImDqTc literal 0 HcmV?d00001 diff --git a/bitmaps/c49o.bmp b/bitmaps/c49o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..fd470a928d333ce2d34d6a4cc1083e268e4c0043 GIT binary patch literal 454 zcmZvYyA1*{42E+HumUYD3lNE(85n^PawS`(LEIQAZh&+|B2J7Sv5-z@$@ygew&Ub_ zdrwa6Lg$J6JbZF0JPEIZx)hEB5gj?%yuv$KUiJAFDd<@>1A4H?%sZZ*N(wD}ZH0DOV2g17O ie&A)lv6t-$Ay?KB^*w*uJB{{F+kdgV(R3u^xym0#k18|( literal 0 HcmV?d00001 diff --git a/bitmaps/c49s.bmp b/bitmaps/c49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..29b6d36045bc44e268eef5370a122b5ca47b01ac GIT binary patch literal 454 zcmZvYF%H5o3`KvMp-V-N(3P=VfL83BsTtWg0$0g^xCZ40WoxC-V8^i#h)B-Uw_Dr! zhvUs4oju)Kid_x^DVzw`jYRq|Uol2d&~bmbc9IXxH(z(rzxBD6ymBg8hhZw$&SCCG z8yaE{6if#gVu1CadURE5BGI>%urDm@F>`XJWKFD@3vkZ7FrR=jNz+9V+Iu0#`#K%f@D8^+t}d8^W&(W zyZEcvZ8t}jSOo8jSufljGn1H{e8MeRyv1{l`E{~7um;s_u3<}=)>M3pLY@7?rdslx ziJUlo9rUUTQD-Fzs)dtM8i8?-gFP?Iha4b3%bq4>-(AQKL_zo5PKT; Kf;fo$|K7jHRoh15QSH`aF-JV)aD2&f)-Mx$sK%!^d@U?8}JRHtJn~5sVf*%xE5|C8207u zpEQL7S+vWix3h0&XI|gGS0;{z>l5c&o|UPhzTy7QLz+>@3jo8J2DO5?!~3wA^=e`N zta$qmR`F0in*EFRar{a5%BNy)XMM!N6Y8w*F`&gdo~>}h!LI@{;U1lQMBbV;j%J$q zQkWi)q%+|iQar2WjZHq-Iccm<{^CaZyLgwk1I|*oM=v@5`ifiOw!<`K{h+uzV7z7h z+(HWXF;2rk5Jms8QQRP>rLYuAN=nlZL`B0La)jIFfsA_r{7keC9H_{K1Cp&UOfhJ8CC z^yaf-Y=_pkLF_vu{}BZatd3bSp+voKg^C?gU2uonnVUzfd1KQ(cLx+P>)jr4=7C&f zU3u1h40EINEEcY@p8v~gO2^ewr>|M>va{Mo-f>u1?{58(Mtg#Li&^m!z=3;A>=nmg zY(2z_vGvw+wEuhy(R}4I->4C?1R2Sx&n@}#rjuhP=J(abal^g&aOoU>IK@Ng25Jjh9!A_3A)>CNW%8hsMCN$w5o&^&R;t^cBAdJ7i1B{AMn@sz7w7-<@ z4yQucHt5%=y;YDvf5iLCDi?)ysw$}5;6~I1ZqrNiVdjC0vj3OW<#>M1X{LR9@w}b< zT|C$3TfIrGCBHdQo%GT%e2JqTirvCT*d`;Nn_Jad65M zBUE!~bGg6o-V5lU58nOwzg&*w&M%$<#dSvO5%qD~fszOJ#IhsJ8`}kdqNa*}thD58 zB2EfX8`f1dYY#?jbNIlHr()JUOT1PMoIvX@N%*tFi%JNOKpWB)22ri>zo~*YrO&6dR{GJE!62jy}T+4K#ll rwe$Zq{9c4DpA*5}`g3Ui*n17kyh-abtoHdMT*y7o!{7G}U$OcIN-H8q literal 0 HcmV?d00001 diff --git a/bitmaps/e49s.bmp b/bitmaps/e49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..46ffee236ec4310687bfb71b6335606131ae23fa GIT binary patch literal 454 zcmZ|Lu}Z^G6b9h`lF~So8ala|(Mc%o4cOTS=+MCz5Zskqaj^TCVTrI$yuus+;$Fk*&w(V<@tspJ&mmlo#ehuP_Ixq|8m` z)bjCz61K6zG&ppW-Sf#fGX?7GmmiU1k}uN1diD$K$1s%AKg7=xr{o4O3%r4#g c57Rkm|J*9|1E9A2@suN9E-7tg>br4LKc}I=Jpcdz literal 0 HcmV?d00001 diff --git a/bitmaps/e72o.bmp b/bitmaps/e72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e6c8d83698e6b42ac724dc467aee2374009e75f8 GIT binary patch literal 926 zcmbW$Jxjwt7zgkZhi)zILIwxjT`GbBU0nPQ-CU)e1o0FHamuQo?H3WOFEqF$tD_Py zYeNQ42>17S?#U%8P99wN-IM>la3R}!<3;7#7CqOf@78@mEx7uC-amDvyF&c{pr}iu znkU#+g>K9?s@Hg~&l_jR8pkLaNBHzMom1m&4^-t{1u=r|F~- z%`F4=<0NHA^N_c74%o)oxI9g!i~;8nvH{NgbnNP!v7vVat6KxP%M^$xC6-T@xw?G1 z?=HX9|1ez}U65q{<$5nSx3#IyR#DxAa!$7GwZ=4-ePcSpYfrpSHi5HUAs2AQ68Wt5 f1+CMmJ$3genODWXinvm$1k$ literal 0 HcmV?d00001 diff --git a/bitmaps/e72s.bmp b/bitmaps/e72s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6d756a5acbbe413fb225cda8331380cc28d2777e GIT binary patch literal 926 zcmb``F-ycS6bJAZ3U-mZ!Q~DL8Ff5xsUQ}dT>K9A5z09UVmrCIIvjoy!N7?)-Rzi2 z*T5|u%02%tNmEzBK$2fyUy?^kFR!07^6diS3Hnv+ij zPnCZbcQj3QlqXxtk`1Zkc62xFi+NwiGvHnq)W`z3Rqx4`R>>|alWn#hJ>QNlzDG}P z+F?&~d}fkQ*S5k@a+bN>*5X(3-B&5r;``OPX5M=@{95~u&SPyo_RLMFQ>@%*s-jEO wpu;`%GpR@J9G|(sq}ZGBXW%OZKVZtEH*ghg{cAtSMc@|*#zU3N(qeq@4_W1Aw*UYD literal 0 HcmV?d00001 diff --git a/bitmaps/e72w.bmp b/bitmaps/e72w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..233988319bc1a6bf17dd08157adc0476592d650c GIT binary patch literal 926 zcmd7RF;2uV5CzchjkM`D7g#Bjl$2CNqN3pr9D$Mo5ub#s;DUqr2q|4eF@I)ad!=o* zB$Az%$v7F?$@A-X2R)wH-?P2!&B1B>jrV^~)-Ht^mn^So%&)bDpoH5G)#ypPI5r^u9b(fsj`DPcI5ik5+9qt~RAI>%zzAJp3)IZ|S zqk0s79Wds+sb`2+vzN)IVorE52U)P@xj4$;C^>Onz!lo9E79VZL(Rcxo|un54D6Rt z+k*-jl7k$ z5nE8tscSxU2iB9L>X!*?pyS=WO5{GT%BFtFH}y$-iJ6EECiH$3DKwN{03jkX|X^mYchyg()=ab?` zqO8^@gKjN&pK#1W7-(Y89ETcRVc3WW9K75QfkN^nVAxiAt%DbHhg!@Wp5oM@DY@>< zc?)bf?|HIgW8d2uFtD6Y9jdO*wx9Fj+;~sB$-3Kq%*&7 y*ygbbXgHTq*gDy;bF#3^UG{#SQoQrg1m|6FY2T^1sCrRxQF`m6&%gitoPPl|LeJs= literal 0 HcmV?d00001 diff --git a/bitmaps/f72o.bmp b/bitmaps/f72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..257c225607eb7748a67d0b719e79e46070451f06 GIT binary patch literal 926 zcmdszF;2rU7=;~m$VLVhHm@9_A`%NM+@Tw{z(EX*y#c)nRS;#%6(Ut?*Hm4MWaaVy zFRrnK-~d?iuP-mp&riO-dzl%oE18$FZ{sdZL46_r-_E8@Ar1gW_OwJhA^zr9GZQC0 zgUl+jsSzYXb#KKmSaR=#LrHE0ftI`^AAax?^)>az$;VibKlIZ6oZRuQEqTul`-!35 zHsm97I`TH-o%BVxq5f{1WZsjzzJf2N%q1FQ?2-BkgDH{s{G`5N=H&0mC3Ua)Lq!hk zS(A0>6LYlEQ%aHb@N;DE02_I<2z6wX7{o@uykDF6phu(b^=RZUuM>}Rmlzj<(Syf~ jn~XF6_)sVR=)O5%mGs{@_vbmt@{!-WmdH~##s9!xfb~By literal 0 HcmV?d00001 diff --git a/bitmaps/f72s.bmp b/bitmaps/f72s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..271e64f2d29efe3de949c26912e4862af7b731ff GIT binary patch literal 926 zcmdUsF;2uV5JerONVj_gw_JgSNK`c3fg^B(O-IQE5GMi2D=i9lyeeMIlE7S|D8(&@MsNZ4#ZD)&u7ZC|q_Cc#Ce{%{wI@vR$?wx^Z zp=BXQawZStMDEEGxjW-0>X+0XwW<3gzZY=)A}7%d=94jBX7kTsloE_JnubG>tmyoq z8~p?IqfGELPH_&yTC*KQa^*elbDlH721bp)&0X#x?`&=vk+Y2K5$5!_O2~;vL?`B; z4$k^;(Rl@r&erKw?_PCe-gwcY-c$6b^BN8t+Yg5`6nWL+{L*mKzn=JvTYov36@S!! TJ1dR9OgLII92-`w`hVtcfN$V) literal 0 HcmV?d00001 diff --git a/bitmaps/f72w.bmp b/bitmaps/f72w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3347e0fba50ed980f217d70bfa5b0cd58525517c GIT binary patch literal 926 zcmds!y$!-Z423UBkq$;k%M?VSqG1O%AuPfyhr}Q^Ldq0T%=0@Z@`)c&6Uk4XUhH?y z<#4*yfzKYgLmjQEP|-i*{bQ4hhKYy-RDR$sDBn4T8lCJJpj%_0MyQPBmYm29Ig)E~ zPp*DuKkGR$C+BgwVQV~(n5s99wa0R=Aik33Rh@wci0MX_I(JD$o1FTSB9m$SA ze?0CGmc~!(8O@CL`sQ^a^<42jV_dYDh$ZgH%ZWuTY!?7w@c6%G7M=3_$n4purw6GE9e!#ihdwIt^V8eP0 zLHkGS13E7W$B=_s0+wFb#o^8iySO~7hP~rl$Ay~Z&AxL-7aRw=0@9i;fy0bGaZP>i kxu%J^%JrQ(BG>ZYmp<^*oIUg5Fsh7#8p6{ZxxFWOUV=D&?2RKRSp!_ ze|FYNJed91*p4$YvTjJ{z?0_J9MeRu h7Hp%Ii2oR>9~a-vcXD%T@94c*mgfJ_=)GR!%Oute#!n6 zwV(T>zNyX1*kC~yBsv&6P+)|;Q3erDBCUfYittZNkTQo4qy*Uo;hEgav#2Wv>}9?1 xUTY5qtV1H3$;j`trat0~N>4`EkT_reSC&SZ#2e4{A5${Uv+?mXBJ12AegF+*vi$%6 literal 0 HcmV?d00001 diff --git a/bitmaps/h72o.bmp b/bitmaps/h72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3ad890494c54498d68501e4317be656a06b71d5d GIT binary patch literal 926 zcma))u};H45JVHDfQl5UDA2X^X%UvFX!wV8sSzJ=L@8(xDX3`3uh;^`6o`BRL@Cn7 za&wkWUS@VLfeK;itf%qp?%bU(uAX}xea`Vd#kx$Z+v%!4!uQuBTPmcNh;*>*fwn{W zHwW$H)c>0sZ8cZj_|ZJ7Pn9FN?f-DFKG8!jthUX-rLmCZN&Vu;6XO~Az_GEtGNUK@3M@nNVHU_dzSwmfta(B;iy^u;+96oPZDHMZL7Q@BN~ZID2= z&bR*S1qJa8JIToO5&1At zc5ORlR=?YaTQw)0eh<`p?mDDqDaR$zteN4C3+`jq0@q{ zq?n65250byu7{Fvh+7(cbmA=|ktl;X%z;Q(gL#?eU@ehvnhSQbjcp$ehI}h$z7)Z$ zf3SKumS}07o1gi-YL+;qF-$Ddv0m?ctFMl6)h*0*A5LGr`PH@i1)|nnL>YSHuHqtJ zgAY*B%xtf{u{Wspk1xIXV3K(29_xdj#v)npv}{QcT-Gh;zXa9tu7h&hU(5XtN`lXr e4jyf}&er+qmS;`A&QDews}}1#fddnI?Dj7f@=2`nM*BBUIw{G2neQqO zC9-_FvJH69LdZN4m(srG0ZKB4gE!& z{YB8Qtl<+6EBia_Az*YUg@H%@o+^T4vVjcqtL(eNm{Q6LgmxO&`SPDNl;l3&7t#cRCEq*S z-#*`FdyXX7n&*q(*NP zapWNbl1AkiGrd!}JJ#namCdPCYMWcCv`i!aroRHORK-6XW=7m52&BHJ<6e&OKc;*i o)D3C#E&l*6M(+^*$-A={o*l+E|INDW@OV literal 0 HcmV?d00001 diff --git a/bitmaps/m49w.bmp b/bitmaps/m49w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..eaa07951fc491a222b542037848997f223c97329 GIT binary patch literal 454 zcmaKoF%E+;3`HGvU@J#p?b-_zse8{9j~pvQ)pKx!jtmI*{}>AiQ6kwdvA-QB&(|kO zT@Uyh?7mtOPYl5O>Q;rTGc$o<>3=RFJ@bkpxS6$`%1B;R3Yk=tgx|9;jQmJK|0Nrh ziEOui(Km=|ak0uNGv=c1jEcN%^JDAhg0UFKKn)r1#6I(EOYi?OcgLJskM9}!sJHAo Jd~@x4%pb$>w50$5 literal 0 HcmV?d00001 diff --git a/bitmaps/m72o.bmp b/bitmaps/m72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1e1ad2a3a76cde9f92964265ca769e7d27080cab GIT binary patch literal 926 zcmc(dF-`+95JhJdiFBk*fwYtyfod&L(c})fiObM&0R&fB1X4<_uqZ{^L@6-B^6=-| zn`9dlXs|SYpML(#*ivq0k0aq}pjW8dQk7Kn=lK2gJQs~3sw${_;Pt3~;)E`bi8cpP zKH9;IGBzu5Llh4V6K?Bj@CI#mCg4>@U1n~jQu2G5xv>1TILw?{K5%9dn-k{hc{++xPwaJ&81Z1!gz6|nZG#miTTf8a%A4I z_Y_VZ4G??dfF1mpeXYSknb8G!Tetb>!3`Hq;@KzteYfljaox1c6y@_*>B4=@d-lGOOWc5}LftdflC_o(oq+Ke2 z+2PMv8T;2&YnVaby6uh{>1D5gX>cQ;sB$W`<|aYogQj_d;$!B=t56ct=E zY!{i0h+FW0xCZxyCr43XO()*YzF6mH>UTD?l-cJp@`P!h;B&c+&T>mAKAIzsz?H;{ zxD&5p&DqZUgEvmv57KX#!8~~Q^t=b#h?xtQOODe2mM@xTQtdrl9(R`h7IU+H7d-^t zMTrHz3+A9hFn6cQDIONU51wV3)K7_oY_9~x7gc0N62gc_lCb3{%)99_5Zyr)xMKbqPRnhX|1GD>)T~{@ z>B0^3msk(;f8mFS{8b_y!iBLeS{Z8+UIiksLntSf7HIJq>l4WF6+DJ|%3osfef-=j QO7pC{- zPU@W0wg0kC_Z^bVgE{!2-d&+;lCAXSWLE-v`UE;h8{o_yRzObKerWav)Uriz(F-5$ W?Zv6yD^b3PhBDg!k9B40|LO-+AT4PC literal 0 HcmV?d00001 diff --git a/bitmaps/o49s.bmp b/bitmaps/o49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..61e191fb511edeb8236586532673c4c338c50a07 GIT binary patch literal 454 zcmb`@p$@_@6a~<`AZ7@&C?>(;2_<-bX`AF1_!u$-LGcY_4hacnV(_@PtysXW*?T8< zZP%sS-6eq^YxV_e87o1U*pu@llwTN|nSsSo{rfVz>;2X2Aa7&z@BLUl^xqXWY2VsT${(Je(Uv;e014OfLC&UW?R^4i)CEWWK3c9)ZvD;B OjFGu3gKxp?8Ne^1EzO4j literal 0 HcmV?d00001 diff --git a/bitmaps/o49w.bmp b/bitmaps/o49w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a4092eedcbc3e64000c31e00993e7a9fa1400944 GIT binary patch literal 454 zcmZ?rJ;ulY26jMF1BeZwn2`Y@(E+56@If$GA(Z(4|33o|13?2O{rUg@=OFO||Nl24 z(+%}NO$`ixKxW9(iDrk|gHH2<+}*&y02Ifd{{h_xlK%mu?Sb?I1_nl;T99&iAOVsF p>G=TE4W;cNG&49negIX0_&JeZ;a4FE@E#uoqp literal 0 HcmV?d00001 diff --git a/bitmaps/o72o.bmp b/bitmaps/o72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f0d0d520c37e5299b7b7652234c4aa59b3f2828b GIT binary patch literal 926 zcmd6kJx&BM422U>Ovm1Uw47osQPFS*j<7e#a1r(@6!ctSr7dW8v`t1Djd^}H5NU-( z2a%ll=EXm8eE0BtmAJOpZ(;XgIc4gPIDfr%qY#^_CYUd1pQyj)7u98hEEYRfZA0*l z=yVL0qUXtZt$Tf+FFDmQ4J|WI&s)SEr2)Robg2@`BWI`T-AQb%_D;uNnSkCxmsWDzwA&qctvMlY&C7!-Gp?>A$u>K&p*>Y E00JyA#Q*>R literal 0 HcmV?d00001 diff --git a/bitmaps/o72s.bmp b/bitmaps/o72s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f9aa0fff60866c71a1c65fb9572a6f2478fd03d6 GIT binary patch literal 926 zcmd6lO$viR423fYbysho+n%BWU3Jqt^d@?e_9`xVkRHL}MnOzpeiT6jcWofyBlBJi z#LMlGkj5GHguHqN3fjNp{dKg|h8Jcgqy>5t@@}q~#}re{1(zKj2mH$l3JtSyuvLKPGyn6IdkDGn+b{@EKcRYoX+ZW Y&Qv1$(VW_sUrIyf#SQkh?)k_31^Nn(^b literal 0 HcmV?d00001 diff --git a/bitmaps/o72w.bmp b/bitmaps/o72w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3e557bebcd377ee52750baec794c3389ff993dfd GIT binary patch literal 926 zcmdUtF%E+;3`G+$u$3cZ?I{|mQDjXT;4ThRFH@-(M#S7CebaK-fWTLpjW&$kL&UdWT--);VM;&BjSeG+slJ z=?mLDk$wJ`L)QOb^M_P%cYtes-KS=1WabS=xYv74-wJtBZ#Eu)`!2xAc2t1+5;TuU xSgG{4re*oA_!fZR%vZk#7S5dNdo|3b*$)n4JtPjIdsvI5Gdols75fp22Q{Me z@XkaekD)VM(~6v8VO5_fvtL`}T;&?ylFxxCJ^=@D#?5Syk^kDo_%dy48SZ+YifwJIvCp8;Y_CV&6{ literal 0 HcmV?d00001 diff --git a/bitmaps/u49s.bmp b/bitmaps/u49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ca21af0bc9d0605d0f6a0f8af7045b200062a0d4 GIT binary patch literal 454 zcmZwDF>b;@5Czb`azO{m2~0`L1pp!1+#yGxq(Jf_MaZ|v6*xkQD4~n!D4nFFSqi`s zv6nxy6QxL2o_U(_XuR_D{?$Rxh3CERm#g~5EOYPXlc}(O1Nfb_N&o-= literal 0 HcmV?d00001 diff --git a/bitmaps/u49w.bmp b/bitmaps/u49w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d7909631a87f5f8807862fb3ee1373f4a312d752 GIT binary patch literal 454 zcmZwDu?@m75Czb0QKW;GE-fW201`brFajk7Qe3hHD=uPmrS8QzM+m%p(G-j48Q{gW|1>CApeD^njC` x_P)#{FFA(Qb5Yt*=`Z)y4*3~v#>mCETIxN95Y)P^)$8r9p18}oi@7ru)}PEKuM+?O literal 0 HcmV?d00001 diff --git a/bitmaps/u72o.bmp b/bitmaps/u72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d4ea8562569d9a7707ab716e2a116541093aecde GIT binary patch literal 926 zcma*kF-yZh6bJB>E)K;EUEH#|imMn96c@jP;3p7w59!cJ^996ym4*oIAk9axr9#%m zEEhQP{J;0E?JbH2m;QLYd-u=f;^wU%$vDUJ81vfCUeuHNE&hK$)F}o3AfgCU2Wnf= zzip*cBI`r9+^W-hXdBesZ#MhAUAi}gPJ((Ccm_K?cT?@XT(eWo=`QP?4+b1a>aKU> zjJK^{9#iky&dt`1%PH~o#;pklTkhE;HI=XtTS5qR>$po0LflSh+z@SeJ#J^k zCKzGtgEwxc8*YtYBcdg*=@6#C9yZ?yda4l&eR8QI7+i=E3@@xkFq}7%I)cGtF`Dd* zjc_dW(Df(rCzX1O1h{5(&Q8@U*cvro^G0pY*d%pqIqwT?@2kDVmZ6JEBjL{VY_m1%3ay*R a&F~@^SYJ@aS1sm!#(ToQJg`Rbw|@Y5_%2rf literal 0 HcmV?d00001 diff --git a/bitmaps/u72s.bmp b/bitmaps/u72s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ff6f72b1b2a2ab17c32f5cdb644000dff2fcc6e9 GIT binary patch literal 926 zcmb8tF-pWh6b9fIQY3}!ZkfthT52x_1;NJNa={Di2~6+;djWB;GK+$pdV~~$ZO|qQ zVaNY}GqcIEC^|67mp^YNk1*#~53%St#q|*L($7dD&8K+(G;&h|f3emIrW?ot?VsK1 zPPx|q*WF<4PhJmd*p-O;)|RV5ZhAL$?^<0Tu-!$&T%GP|({v}9HbJ*Cx(zjrq-F2O z_Kt0Z2i7-rWDV{t#8-PaQr01FBoA(FztofD?nCF_eCj;};N#XK+x^%{Y+gJ}MP8<5szZ`j2NMh%e zffMJiJ9zB;5m)@Iu6;Q(DYjSNo3{+xsE^HchH`p+W6}vq=!A-<45JktzA)E*u_{xzA2yo&;50tPK;B>GZKr3E>L>n$4#~h*ze`aiKv9r#rx42$oKIZAjsei`(k4u{>@`afR(*|5Y{xdIH$!q?HOmJ$-XR>C+}j+xlWn0KE3M>Hsm<&GB)-L0LNtHKis=a^)_HDOW-OCpp_Y5liV3a zMOnv=R&-5o?7Pz^P&U+j8zd}2^Z*GLq6R(_>w|qC66hY-Xi)EIulM9iB6+i}xgN3r l(#{$M_m!p5iw8398_o~P`96{tyd(mDRi61JYo=8E&oAqMCba+n literal 0 HcmV?d00001 diff --git a/bitmaps/w49s.bmp b/bitmaps/w49s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0dcd4f107d409faa3cee80d8b73e66d7231dc516 GIT binary patch literal 454 zcmb`@y$!-J5C!0Oii8_PS~@6c>8J>zX9iYagSez)1Xgi@SO=FbP|SOFlnOd5oxj}W z;-4H(PeU1d&Kt&d3`2c!C67go(l|~zXD~SY7Xy0L6VS>4G}7~9sWny}gK~MGXS$|0 zx&&NHXTZ);8(fgEgd$H%b03NWaH;jlu@4Da2lk%Se#*6 literal 0 HcmV?d00001 diff --git a/bitmaps/w49w.bmp b/bitmaps/w49w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d8158afe66d34cdfd9b21b3e50e34042ade30898 GIT binary patch literal 454 zcmb`DF%rTc5Ji`b1!(xl{kJ%^|vy+%PEXw1O?c3wn^x)9)4&A4tyKm^> t6Fj;XJ^G?W@6S1-7jp3p7q~%+dS4Q`(BFr_pqvA#x4-}Z literal 0 HcmV?d00001 diff --git a/bitmaps/w72o.bmp b/bitmaps/w72o.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c9c8969f2e5c6ec0190c8f43ad1bb791bc820ee7 GIT binary patch literal 926 zcmdVYKT5BdA5%_o$YamEY+-^Ao+J4|HhtN1(sFx}ygje>$hPm{a$9l zjpRC;lk=g;p1A~kH4hjB-I)j9)#z=uv!KV$Bes0R>ZSCBr&h}Nza9OOuN|5tU%SUj zTU(pJaW-mt(R{$h8>#3~B>H?w+A(HAyC1-DrMr z*!w_d-tH?cXS(&q{Gqmc6up>Sj8l-*T21npLCokk{|EG*6UNfL;DUUQGIP|4i}7JG J^ppQ*e*;uhIIsW! literal 0 HcmV?d00001 diff --git a/bitmaps/w72s.bmp b/bitmaps/w72s.bmp new file mode 100644 index 0000000000000000000000000000000000000000..31d4be9370b19cb629f57eeac66431d44aa42add GIT binary patch literal 926 zcmds#Jx;_h5QWD|VXcHnOP6+!K!qhL8t%X`sBpN*p2Yt$>6?1lInV4kJCY4k5wZg8_Q7d$1`04{@n0c=o|z5oCK literal 0 HcmV?d00001 diff --git a/bitmaps/w72w.bmp b/bitmaps/w72w.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b7d068200167097a8785cf3d2a77b06b24cd26d9 GIT binary patch literal 926 zcmds#F%H5o3`HGv=thsg$|A*p9gpLdd^W*OnBUHq|L?l~JKfg}u zcy~BAVrz@MLF_$}B-4-BzpNN0cv4kCa6zl6pLvo5Cq2l$^91gkfyIQZx56+LE*6n> z;5Kk$oXwLKSb|O-&Hus!By~M -#endif -#endif - - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -#if !HAVE_RANDOM -# if HAVE_RAND48 -# define srandom srand48 -# define random lrand48 -# else /* not HAVE_RAND48 */ -# define srandom srand -# define random rand -# endif /* not HAVE_RAND48 */ -#endif /* !HAVE_RANDOM */ - -/* End compatibility grunge */ - -#define PROTOVER 2 /* engine protocol version */ - -#define BOARD_SIZE 8 -#define DROP_RANK -3 -#define MAX_MOVES 1000 -#define MSG_SIZ 512 -#define DIALOG_SIZE 256 -#define STAR_MATCH_N 16 -#define MOVE_LEN 32 -#define TIME_CONTROL "5" /* in minutes */ -#define TIME_DELAY_QUOTE "1.0" /* seconds between moves */ -#define TIME_DELAY ((float) 1.0) -#define MOVES_PER_SESSION 40 /* moves per TIME_CONTROL */ -#define TIME_INCREMENT -1 /* if >= 0, MOVES_PER_SESSION unused */ -#define WhiteOnMove(move) (((move) % 2) == 0) -#define ICS_HOST "chessclub.com" -#define ICS_PORT "5000" -#define ICS_COMM_PORT "" -#define FIRST_HOST "localhost" -#define SECOND_HOST "localhost" -#define TELNET_PROGRAM "telnet" -#define MATCH_MODE "False" -#define INIT_STRING "new\nrandom\n" -#define WHITE_STRING "white\ngo\n" -#define BLACK_STRING "black\ngo\n" -#define COMPUTER_STRING "computer\n" -#define REUSE_CHESS_PROGRAMS 1 -#define WHITE_PIECE_COLOR "#FFFFCC" -#define BLACK_PIECE_COLOR "#202020" -#define LIGHT_SQUARE_COLOR "#C8C365" -#define DARK_SQUARE_COLOR "#77A26D" -#define JAIL_SQUARE_COLOR "#808080" -#define HIGHLIGHT_SQUARE_COLOR "#FFFF00" -#define PREMOVE_HIGHLIGHT_COLOR "#FF0000" -#define BELLCHAR '\007' -#define NULLCHAR '\000' -#define FEATURE_TIMEOUT 10000 /*ms*/ - -/* Zippy defaults */ -#define ZIPPY_TALK FALSE -#define ZIPPY_PLAY FALSE -#define ZIPPY_LINES "yow.lines" -#define ZIPPY_PINHEAD "" -#define ZIPPY_PASSWORD "" -#define ZIPPY_PASSWORD2 "" -#define ZIPPY_WRONG_PASSWORD "" -#define ZIPPY_ACCEPT_ONLY "" -#define ZIPPY_USE_I TRUE -#define ZIPPY_BUGHOUSE 0 -#define ZIPPY_NOPLAY_CRAFTY FALSE -#define ZIPPY_GAME_END "gameend\n" -#define ZIPPY_GAME_START "" -#define ZIPPY_ADJOURN FALSE -#define ZIPPY_ABORT FALSE -#define ZIPPY_VARIANTS "normal" -#define ZIPPY_MAX_GAMES 0 -#define ZIPPY_REPLAY_TIMEOUT 120 - -typedef enum { - BeginningOfGame, MachinePlaysWhite, MachinePlaysBlack, - AnalyzeMode, AnalyzeFile, TwoMachinesPlay, - EditGame, PlayFromGameFile, EndOfGame, EditPosition, Training, - IcsIdle, IcsPlayingWhite, IcsPlayingBlack, IcsObserving, - IcsExamining - } GameMode; - -typedef enum { - WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, WhiteQueen, WhiteKing, - BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen, BlackKing, - EmptySquare, - ClearBoard, WhitePlay, BlackPlay /*for use on EditPosition menus*/ - } ChessSquare; - -typedef ChessSquare Board[BOARD_SIZE][BOARD_SIZE]; - -typedef enum { - WhiteKingSideCastle = 1, WhiteQueenSideCastle, - WhiteKingSideCastleWild, WhiteQueenSideCastleWild, - WhiteHSideCastleFR, WhiteASideCastleFR, - BlackKingSideCastle, BlackQueenSideCastle, - BlackKingSideCastleWild, BlackQueenSideCastleWild, - BlackHSideCastleFR, BlackASideCastleFR, - WhitePromotionKnight, WhitePromotionBishop, - WhitePromotionRook, WhitePromotionQueen, WhitePromotionKing, - BlackPromotionKnight, BlackPromotionBishop, - BlackPromotionRook, BlackPromotionQueen, BlackPromotionKing, - WhiteCapturesEnPassant, BlackCapturesEnPassant, - WhiteDrop, BlackDrop, - NormalMove, AmbiguousMove, IllegalMove, ImpossibleMove, - WhiteWins, BlackWins, GameIsDrawn, GameUnfinished, - GNUChessGame, XBoardGame, MoveNumberOne, - Comment, PositionDiagram, ElapsedTime, PGNTag, NAG - } ChessMove; - -typedef enum { - ColorShout, ColorSShout, ColorChannel1, ColorChannel, ColorKibitz, - ColorTell, ColorChallenge, ColorRequest, ColorSeek, ColorNormal, - ColorNone, NColorClasses -} ColorClass; - -typedef enum { - SoundMove, SoundBell, SoundAlarm, SoundIcsWin, SoundIcsLoss, - SoundIcsDraw, SoundIcsUnfinished, NSoundClasses -} SoundClass; - -/* Names for chess variants, not necessarily supported */ -typedef enum { - VariantNormal, /* Normal chess */ - VariantLoadable, /* "loadgame" command allowed (not really a variant)*/ - VariantWildCastle, /* Shuffle chess where king can castle from d file */ - VariantNoCastle, /* Shuffle chess with no castling at all */ - VariantFischeRandom, /* FischeRandom */ - VariantBughouse, /* Bughouse, ICC/FICS rules */ - VariantCrazyhouse, /* Crazyhouse, ICC/FICS rules */ - VariantLosers, /* Try to lose all pieces or get mated (ICC wild 17)*/ - VariantSuicide, /* Try to lose all pieces incl. king (FICS) */ - VariantGiveaway, /* Try to have no legal moves left (ICC wild 26) */ - VariantTwoKings, /* Weird ICC wild 9 */ - VariantKriegspiel, /* Kriegspiel; pawns can capture invisible pieces */ - VariantAtomic, /* Capturing piece explodes (ICC wild 27) */ - Variant3Check, /* Win by giving check 3 times (ICC wild 25) */ - VariantShatranj, /* Unsupported (ICC wild 28) */ - Variant29, /* Temporary name for possible future ICC wild 29 */ - Variant30, /* Temporary name for possible future ICC wild 30 */ - Variant31, /* Temporary name for possible future ICC wild 31 */ - Variant32, /* Temporary name for possible future ICC wild 32 */ - Variant33, /* Temporary name for possible future ICC wild 33 */ - Variant34, /* Temporary name for possible future ICC wild 34 */ - Variant35, /* Temporary name for possible future ICC wild 35 */ - Variant36, /* Temporary name for possible future ICC wild 36 */ - VariantUnknown /* Catchall for other unknown variants */ -} VariantClass; - -#define VARIANT_NAMES { \ - "normal", \ - "normal", \ - "wildcastle", \ - "nocastle", \ - "fischerandom", \ - "bughouse", \ - "crazyhouse", \ - "losers", \ - "suicide", \ - "giveaway", \ - "twokings", \ - "kriegspiel", \ - "atomic", \ - "3check", \ - "shatranj", \ - "wild29", \ - "wild30", \ - "wild31", \ - "wild32", \ - "wild33", \ - "wild34", \ - "wild35", \ - "wild36", \ - "unknown" \ -} - -typedef struct { -#if !defined(_amigados) - char *whitePieceColor; - char *blackPieceColor; - char *lightSquareColor; - char *darkSquareColor; - char *jailSquareColor; - char *highlightSquareColor; - char *premoveHighlightColor; -#else - int whitePieceColor; - int blackPieceColor; - int lightSquareColor; - int darkSquareColor; - int jailSquareColor; - int highlightSquareColor; - int premoveHighlightColor; -#endif - int movesPerSession; - int timeIncrement; - char *initString; - char *secondInitString; - char *firstComputerString; - char *secondComputerString; - char *firstChessProgram; - char *secondChessProgram; - char *firstDirectory; - char *secondDirectory; - Boolean firstPlaysBlack; - Boolean noChessProgram; - char *firstHost; - char *secondHost; - char *bitmapDirectory; - char *remoteShell; - char *remoteUser; - float timeDelay; - char *timeControl; - Boolean icsActive; - char *icsHost; - char *icsPort; - char *icsCommPort; /* if set, use serial port instead of tcp host/port */ - char *icsLogon; /* Hack to permit variable logon scripts. */ - char *icsHelper; - Boolean icsInputBox; - Boolean useTelnet; - char *telnetProgram; - char *gateway; - char *loadGameFile; - int loadGameIndex; /* game # within file */ - char *saveGameFile; - Boolean autoSaveGames; - char *loadPositionFile; - int loadPositionIndex; /* position # within file */ - char *savePositionFile; - Boolean matchMode; - int matchGames; - Boolean monoMode; - Boolean debugMode; - Boolean clockMode; - char *boardSize; - Boolean Iconic; - char *searchTime; - int searchDepth; - Boolean showCoords; - char *clockFont; - char *messageFont; /* WinBoard only */ - char *coordFont; - char *font; /* xboard only: all other fonts */ - char *tagsFont; /* WinBoard only */ - char *commentFont; /* WinBoard only */ - char *icsFont; /* WinBoard only */ - Boolean ringBellAfterMoves; - Boolean autoCallFlag; - Boolean flipView; - Boolean autoFlipView; - char *cmailGameName; /* xboard only */ - Boolean alwaysPromoteToQueen; - Boolean oldSaveStyle; - Boolean quietPlay; - Boolean showThinking; - Boolean ponderNextMove; - Boolean periodicUpdates; - Boolean autoObserve; - Boolean autoComment; - Boolean getMoveList; - Boolean testLegality; - int borderXoffset; /* xboard only */ - int borderYoffset; /* xboard only */ - Boolean titleInWindow; /* xboard only */ - Boolean localLineEditing; /* WinBoard only */ - Boolean zippyTalk; - Boolean zippyPlay; - int flashCount; /* Number of times to flash (xboard only) */ - int flashRate; /* Flashes per second (xboard only) */ - char *pixmapDirectory; /* Path to XPM/XIM files to use (xboard only) */ - int msLoginDelay; /* Delay per character (in msec) while sending - ICS logon script (xboard only) */ - Boolean colorize; /* If True, use the following colors to color text */ - /* Strings for colors, as "fg, bg, bold" (strings used in xboard only) */ - char *colorShout; - char *colorSShout; - char *colorChannel1; - char *colorChannel; - char *colorKibitz; - char *colorTell; - char *colorChallenge; - char *colorRequest; - char *colorSeek; - char *colorNormal; - char *soundProgram; /* sound-playing program */ - char *soundShout; - char *soundSShout; - char *soundChannel1; - char *soundChannel; - char *soundKibitz; - char *soundTell; - char *soundChallenge; - char *soundRequest; - char *soundSeek; - char *soundMove; - char *soundIcsWin; - char *soundIcsLoss; - char *soundIcsDraw; - char *soundIcsUnfinished; - char *soundIcsAlarm; - Boolean reuseFirst; - Boolean reuseSecond; - Boolean animateDragging; /* If True, animate mouse dragging of pieces */ - Boolean animate; /* If True, animate non-mouse moves */ - int animSpeed; /* Delay in milliseconds between animation frames */ - Boolean popupMoveErrors; - Boolean popupExitMessage; - int showJail; - Boolean highlightLastMove; - Boolean highlightDragging; - Boolean blindfold; /* if true, no pieces are drawn */ - Boolean premove; /* true if premove feature enabled */ - Boolean premoveWhite; /* true if premoving White first move */ - char *premoveWhiteText; /* text of White premove 1 */ - Boolean premoveBlack; /* true if premoving Black first move */ - char *premoveBlackText; /* text of Black premove 1 */ - Boolean icsAlarm; /* true if sounding alarm at a certain time */ - int icsAlarmTime; /* time to sound alarm, in milliseconds */ - Boolean autoRaiseBoard; - int fontSizeTolerance; /* xboard only */ - char *initialMode; - char *variant; - int firstProtocolVersion; - int secondProtocolVersion; - Boolean showButtonBar; - - /* [AS] New properties (down to the "ZIPPY" part) */ - Boolean firstScoreIsAbsolute; /* If true, engine score is always from white side */ - Boolean secondScoreIsAbsolute; /* If true, engine score is always from white side */ - Boolean saveExtendedInfoInPGN; /* If true, saved PGN games contain extended info */ - Boolean hideThinkingFromHuman; /* If true, program thinking is generated but not displayed in human/computer matches */ - char * liteBackTextureFile; /* Name of texture bitmap for lite squares */ - char * darkBackTextureFile; /* Name of texture bitmap for dark squares */ - int liteBackTextureMode; - int darkBackTextureMode; - char * renderPiecesWithFont; /* Name of font for rendering chess pieces */ - char * fontToPieceTable; /* Map to translate font character to chess pieces */ - int fontBackColorWhite; - int fontForeColorWhite; - int fontBackColorBlack; - int fontForeColorBlack; - int fontPieceSize; /* Size of font relative to square (percentage) */ - int overrideLineGap; /* If >= 0 overrides the lineGap value of the board size properties */ - int adjudicateLossThreshold; /* Adjudicate a two-machine game if both engines agree the score is below this for 6 plies */ - int delayBeforeQuit; - int delayAfterQuit; - char * nameOfDebugFile; - char * pgnEventHeader; - int defaultFrcPosition; - char * gameListTags; - Boolean saveOutOfBookInfo; - Boolean showEvalInMoveHistory; - int evalHistColorWhite; - int evalHistColorBlack; - Boolean highlightMoveWithArrow; - int highlightArrowColor; - Boolean useStickyWindows; - int adjudicateDrawMoves; - Boolean autoDisplayComment; - Boolean autoDisplayTags; - Boolean firstIsUCI; - Boolean secondIsUCI; - Boolean firstHasOwnBookUCI; - Boolean secondHasOwnBookUCI; - char * polyglotDir; - Boolean usePolyglotBook; - char * polyglotBook; - int defaultHashSize; - int defaultCacheSizeEGTB; - char * defaultPathEGTB; - -#if ZIPPY - char *zippyLines; - char *zippyPinhead; - char *zippyPassword; - char *zippyPassword2; - char *zippyWrongPassword; - char *zippyAcceptOnly; - int zippyUseI; - int zippyBughouse; - int zippyNoplayCrafty; - char *zippyGameEnd; - char *zippyGameStart; - int zippyAdjourn; - int zippyAbort; - char *zippyVariants; - int zippyMaxGames; - int zippyReplayTimeout; /*seconds*/ -#endif -} AppData, *AppDataPtr; - -/* [AS] PGN tags (for showing in the game list) */ -#define GLT_EVENT 'e' -#define GLT_SITE 's' -#define GLT_DATE 'd' -#define GLT_ROUND 'o' -#define GLT_PLAYERS 'p' /* I.e. white "-" black */ -#define GLT_RESULT 'r' -#define GLT_WHITE_ELO 'w' -#define GLT_BLACK_ELO 'b' -#define GLT_TIME_CONTROL 't' -#define GLT_VARIANT 'v' -#define GLT_OUT_OF_BOOK 'a' - -#define GLT_DEFAULT_TAGS "eprd" /* Event, players, result, date */ - -#define GLT_ALL_TAGS "esdoprwbtva" - -#define PGN_OUT_OF_BOOK "Annotator" - -extern AppData appData; - -typedef struct { - /* PGN 7-tag info */ - char *event; - char *site; - char *date; - char *round; - char *white; - char *black; - ChessMove result; - /* Additional info */ - char *fen; /* NULL or FEN for starting position; input only */ - char *resultDetails; - char *timeControl; - char *extraTags; /* NULL or "[Tag \"Value\"]\n", etc. */ - int whiteRating; /* -1 if unknown */ - int blackRating; /* -1 if unknown */ - VariantClass variant; - char *outOfBook; /* [AS] Move and score when engine went out of book */ -} GameInfo; - - -#endif +/* + * common.h -- Common definitions for X and Windows NT versions of XBoard + * $Id: common.h,v 2.1 2003/10/27 19:21:00 mann Exp $ + * + * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. + * Enhancements Copyright 1992-95 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. + * ------------------------------------------------------------------------ + */ + +#ifndef _COMMON +#define _COMMON + + +/* Begin compatibility grunge */ + +#if defined(__STDC__) || defined(WIN32) || defined(_amigados) +#define P(args) args +typedef void *VOIDSTAR; +#else +#define P(args) () +typedef char *VOIDSTAR; +#endif + +#ifdef WIN32 +typedef char Boolean; +typedef char *String; +#define popen _popen +#define pclose _pclose + +#else +#ifdef _amigados /* It is important, that these types have */ +typedef int Boolean; /* a length of 4 bytes each, as we are */ +typedef char *String; /* using ReadArgs() for argument parsing. */ +#ifdef _DCC +FILE *popen(const char *, const char *); +int pclose(FILE *); +#endif + +#else +#include +#endif +#endif + + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#if !HAVE_RANDOM +# if HAVE_RAND48 +# define srandom srand48 +# define random lrand48 +# else /* not HAVE_RAND48 */ +# define srandom srand +# define random rand +# endif /* not HAVE_RAND48 */ +#endif /* !HAVE_RANDOM */ + +/* End compatibility grunge */ + +#define PROTOVER 2 /* engine protocol version */ + +#define BOARD_SIZE 12 /* [HGM] for in declarations */ +#define BOARD_WIDTH (appData.NrFiles) /* [HGM] made user adjustable */ +#define BOARD_HEIGHT (appData.NrRanks) +#define ONE ('1'-(BOARD_HEIGHT>9)) /* [HGM] foremost board rank */ +#define DROP_RANK -3 +#define MAX_MOVES 1000 +#define MSG_SIZ 512 +#define DIALOG_SIZE 256 +#define STAR_MATCH_N 16 +#define MOVE_LEN 32 +#define TIME_CONTROL "5" /* in minutes */ +#define TIME_DELAY_QUOTE "1.0" /* seconds between moves */ +#define TIME_DELAY ((float) 1.0) +#define MOVES_PER_SESSION 40 /* moves per TIME_CONTROL */ +#define TIME_INCREMENT -1 /* if >= 0, MOVES_PER_SESSION unused */ +#define WhiteOnMove(move) (((move) % 2) == 0) +#define ICS_HOST "chessclub.com" +#define ICS_PORT "5000" +#define ICS_COMM_PORT "" +#define FIRST_HOST "localhost" +#define SECOND_HOST "localhost" +#define TELNET_PROGRAM "telnet" +#define MATCH_MODE "False" +#define INIT_STRING "new\nrandom\n" +#define WHITE_STRING "white\ngo\n" +#define BLACK_STRING "black\ngo\n" +#define COMPUTER_STRING "computer\n" +#define REUSE_CHESS_PROGRAMS 1 +#define WHITE_PIECE_COLOR "#FFFFCC" +#define BLACK_PIECE_COLOR "#202020" +#define LIGHT_SQUARE_COLOR "#C8C365" +#define DARK_SQUARE_COLOR "#77A26D" +#define JAIL_SQUARE_COLOR "#808080" +#define HIGHLIGHT_SQUARE_COLOR "#FFFF00" +#define PREMOVE_HIGHLIGHT_COLOR "#FF0000" +#define BELLCHAR '\007' +#define NULLCHAR '\000' +#define FEATURE_TIMEOUT 10000 /*ms*/ + +/* Zippy defaults */ +#define ZIPPY_TALK FALSE +#define ZIPPY_PLAY FALSE +#define ZIPPY_LINES "yow.lines" +#define ZIPPY_PINHEAD "" +#define ZIPPY_PASSWORD "" +#define ZIPPY_PASSWORD2 "" +#define ZIPPY_WRONG_PASSWORD "" +#define ZIPPY_ACCEPT_ONLY "" +#define ZIPPY_USE_I TRUE +#define ZIPPY_BUGHOUSE 0 +#define ZIPPY_NOPLAY_CRAFTY FALSE +#define ZIPPY_GAME_END "gameend\n" +#define ZIPPY_GAME_START "" +#define ZIPPY_ADJOURN FALSE +#define ZIPPY_ABORT FALSE +#define ZIPPY_VARIANTS "normal" +#define ZIPPY_MAX_GAMES 0 +#define ZIPPY_REPLAY_TIMEOUT 120 + +typedef enum { + BeginningOfGame, MachinePlaysWhite, MachinePlaysBlack, + AnalyzeMode, AnalyzeFile, TwoMachinesPlay, + EditGame, PlayFromGameFile, EndOfGame, EditPosition, Training, + IcsIdle, IcsPlayingWhite, IcsPlayingBlack, IcsObserving, + IcsExamining + } GameMode; + +typedef enum { + WhitePawn, WhiteKnight, WhiteBishop, WhiteRook, +#ifdef FAIRY + WhiteCardinal, WhiteMarshall, WhiteFairyPawn, WhiteFairyKnight, + WhiteFairyBishop, WhiteFairyRook, WhiteFairyCardinal, WhiteFairyMarshall, + WhiteFairyQueen, WhiteFairyKing, +#endif + WhiteQueen, WhiteKing, + BlackPawn, BlackKnight, BlackBishop, BlackRook, +#ifdef FAIRY + BlackCardinal, BlackMarshall, BlackFairyPawn, BlackFairyKnight, + BlackFairyBishop, BlackFairyRook, BlackFairyCardinal, BlackFairyMarshall, + BlackFairyQueen, BlackFairyKing, +#endif + BlackQueen, BlackKing, + EmptySquare, + ClearBoard, WhitePlay, BlackPlay /*for use on EditPosition menus*/ + } ChessSquare; + +typedef ChessSquare Board[BOARD_SIZE][BOARD_SIZE]; + +typedef enum { + WhiteKingSideCastle = 1, WhiteQueenSideCastle, + WhiteKingSideCastleWild, WhiteQueenSideCastleWild, + WhiteHSideCastleFR, WhiteASideCastleFR, + BlackKingSideCastle, BlackQueenSideCastle, + BlackKingSideCastleWild, BlackQueenSideCastleWild, + BlackHSideCastleFR, BlackASideCastleFR, + WhitePromotionKnight, WhitePromotionBishop, + WhitePromotionRook, WhitePromotionQueen, WhitePromotionKing, +#ifdef FAIRY + WhitePromotionChancellor, WhitePromotionArchbishop, +#endif + BlackPromotionKnight, BlackPromotionBishop, + BlackPromotionRook, BlackPromotionQueen, BlackPromotionKing, +#ifdef FAIRY + BlackPromotionChancellor, BlackPromotionArchbishop, +#endif + WhiteCapturesEnPassant, BlackCapturesEnPassant, + WhiteDrop, BlackDrop, + NormalMove, AmbiguousMove, IllegalMove, ImpossibleMove, + WhiteWins, BlackWins, GameIsDrawn, GameUnfinished, + GNUChessGame, XBoardGame, MoveNumberOne, + Comment, PositionDiagram, ElapsedTime, PGNTag, NAG + } ChessMove; + +typedef enum { + ColorShout, ColorSShout, ColorChannel1, ColorChannel, ColorKibitz, + ColorTell, ColorChallenge, ColorRequest, ColorSeek, ColorNormal, + ColorNone, NColorClasses +} ColorClass; + +typedef enum { + SoundMove, SoundBell, SoundAlarm, SoundIcsWin, SoundIcsLoss, + SoundIcsDraw, SoundIcsUnfinished, NSoundClasses +} SoundClass; + +/* Names for chess variants, not necessarily supported */ +typedef enum { + VariantNormal, /* Normal chess */ + VariantLoadable, /* "loadgame" command allowed (not really a variant)*/ + VariantWildCastle, /* Shuffle chess where king can castle from d file */ + VariantNoCastle, /* Shuffle chess with no castling at all */ + VariantFischeRandom, /* FischeRandom */ + VariantBughouse, /* Bughouse, ICC/FICS rules */ + VariantCrazyhouse, /* Crazyhouse, ICC/FICS rules */ + VariantLosers, /* Try to lose all pieces or get mated (ICC wild 17)*/ + VariantSuicide, /* Try to lose all pieces incl. king (FICS) */ + VariantGiveaway, /* Try to have no legal moves left (ICC wild 26) */ + VariantTwoKings, /* Weird ICC wild 9 */ + VariantKriegspiel, /* Kriegspiel; pawns can capture invisible pieces */ + VariantAtomic, /* Capturing piece explodes (ICC wild 27) */ + Variant3Check, /* Win by giving check 3 times (ICC wild 25) */ + VariantShatranj, /* Unsupported (ICC wild 28) */ + Variant29, /* Temporary name for possible future ICC wild 29 */ + Variant30, /* Temporary name for possible future ICC wild 30 */ +#ifdef FAIRY + VariantShogi, /* [HGM] To be supported in next version */ + VariantXiangqi, + VariantCourier, + VariantGothic, + VariantCapablanca, + VariantFairy, /* [HGM] allow more piece types */ +#else + Variant31, /* Temporary name for possible future ICC wild 31 */ + Variant32, /* Temporary name for possible future ICC wild 32 */ + Variant33, + Variant34, /* Temporary name for possible future ICC wild 34 */ + Variant35, /* Temporary name for possible future ICC wild 35 */ + Variant36, /* Temporary name for possible future ICC wild 36 */ +#endif + VariantUnknown /* Catchall for other unknown variants */ +} VariantClass; + +#ifdef FAIRY +#define VARIANT_NAMES { \ + "normal", \ + "normal", \ + "wildcastle", \ + "nocastle", \ + "fischerandom", \ + "bughouse", \ + "crazyhouse", \ + "losers", \ + "suicide", \ + "giveaway", \ + "twokings", \ + "kriegspiel", \ + "atomic", \ + "3check", \ + "shatranj", \ + "wild29", \ + "wild30", \ + "shogi", \ + "xiangqi", \ + "courier", \ + "gothic", \ + "capablanca", \ + "fairy", \ + "unknown" \ +} +#else +#define VARIANT_NAMES { \ + "normal", \ + "normal", \ + "wildcastle", \ + "nocastle", \ + "fischerandom", \ + "bughouse", \ + "crazyhouse", \ + "losers", \ + "suicide", \ + "giveaway", \ + "twokings", \ + "kriegspiel", \ + "atomic", \ + "3check", \ + "shatranj", \ + "wild29", \ + "wild30", \ + "wild31", \ + "wild32", \ + "wild33", \ + "wild34", \ + "wild35", \ + "wild36", \ + "unknown" \ +} +#endif + +typedef struct { +#if !defined(_amigados) + char *whitePieceColor; + char *blackPieceColor; + char *lightSquareColor; + char *darkSquareColor; + char *jailSquareColor; + char *highlightSquareColor; + char *premoveHighlightColor; +#else + int whitePieceColor; + int blackPieceColor; + int lightSquareColor; + int darkSquareColor; + int jailSquareColor; + int highlightSquareColor; + int premoveHighlightColor; +#endif + int movesPerSession; + int timeIncrement; + char *initString; + char *secondInitString; + char *firstComputerString; + char *secondComputerString; + char *firstChessProgram; + char *secondChessProgram; + char *firstDirectory; + char *secondDirectory; + Boolean firstPlaysBlack; + Boolean noChessProgram; + char *firstHost; + char *secondHost; + char *bitmapDirectory; + char *remoteShell; + char *remoteUser; + float timeDelay; + char *timeControl; + Boolean icsActive; + char *icsHost; + char *icsPort; + char *icsCommPort; /* if set, use serial port instead of tcp host/port */ + char *icsLogon; /* Hack to permit variable logon scripts. */ + char *icsHelper; + Boolean icsInputBox; + Boolean useTelnet; + char *telnetProgram; + char *gateway; + char *loadGameFile; + int loadGameIndex; /* game # within file */ + char *saveGameFile; + Boolean autoSaveGames; + char *loadPositionFile; + int loadPositionIndex; /* position # within file */ + char *savePositionFile; + Boolean matchMode; + int matchGames; + Boolean monoMode; + Boolean debugMode; + Boolean clockMode; + char *boardSize; + Boolean Iconic; + char *searchTime; + int searchDepth; + Boolean showCoords; + char *clockFont; + char *messageFont; /* WinBoard only */ + char *coordFont; + char *font; /* xboard only: all other fonts */ + char *tagsFont; /* WinBoard only */ + char *commentFont; /* WinBoard only */ + char *icsFont; /* WinBoard only */ + Boolean ringBellAfterMoves; + Boolean autoCallFlag; + Boolean flipView; + Boolean autoFlipView; + char *cmailGameName; /* xboard only */ + Boolean alwaysPromoteToQueen; + Boolean oldSaveStyle; + Boolean quietPlay; + Boolean showThinking; + Boolean ponderNextMove; + Boolean periodicUpdates; + Boolean autoObserve; + Boolean autoComment; + Boolean getMoveList; + Boolean testLegality; + int borderXoffset; /* xboard only */ + int borderYoffset; /* xboard only */ + Boolean titleInWindow; /* xboard only */ + Boolean localLineEditing; /* WinBoard only */ + Boolean zippyTalk; + Boolean zippyPlay; + int flashCount; /* Number of times to flash (xboard only) */ + int flashRate; /* Flashes per second (xboard only) */ + char *pixmapDirectory; /* Path to XPM/XIM files to use (xboard only) */ + int msLoginDelay; /* Delay per character (in msec) while sending + ICS logon script (xboard only) */ + Boolean colorize; /* If True, use the following colors to color text */ + /* Strings for colors, as "fg, bg, bold" (strings used in xboard only) */ + char *colorShout; + char *colorSShout; + char *colorChannel1; + char *colorChannel; + char *colorKibitz; + char *colorTell; + char *colorChallenge; + char *colorRequest; + char *colorSeek; + char *colorNormal; + char *soundProgram; /* sound-playing program */ + char *soundShout; + char *soundSShout; + char *soundChannel1; + char *soundChannel; + char *soundKibitz; + char *soundTell; + char *soundChallenge; + char *soundRequest; + char *soundSeek; + char *soundMove; + char *soundIcsWin; + char *soundIcsLoss; + char *soundIcsDraw; + char *soundIcsUnfinished; + char *soundIcsAlarm; + Boolean reuseFirst; + Boolean reuseSecond; + Boolean animateDragging; /* If True, animate mouse dragging of pieces */ + Boolean animate; /* If True, animate non-mouse moves */ + int animSpeed; /* Delay in milliseconds between animation frames */ + Boolean popupMoveErrors; + Boolean popupExitMessage; + int showJail; + Boolean highlightLastMove; + Boolean highlightDragging; + Boolean blindfold; /* if true, no pieces are drawn */ + Boolean premove; /* true if premove feature enabled */ + Boolean premoveWhite; /* true if premoving White first move */ + char *premoveWhiteText; /* text of White premove 1 */ + Boolean premoveBlack; /* true if premoving Black first move */ + char *premoveBlackText; /* text of Black premove 1 */ + Boolean icsAlarm; /* true if sounding alarm at a certain time */ + int icsAlarmTime; /* time to sound alarm, in milliseconds */ + Boolean autoRaiseBoard; + int fontSizeTolerance; /* xboard only */ + char *initialMode; + char *variant; + int firstProtocolVersion; + int secondProtocolVersion; + Boolean showButtonBar; + + /* [AS] New properties (down to the "ZIPPY" part) */ + Boolean firstScoreIsAbsolute; /* If true, engine score is always from white side */ + Boolean secondScoreIsAbsolute; /* If true, engine score is always from white side */ + Boolean saveExtendedInfoInPGN; /* If true, saved PGN games contain extended info */ + Boolean hideThinkingFromHuman; /* If true, program thinking is generated but not displayed in human/computer matches */ + char * liteBackTextureFile; /* Name of texture bitmap for lite squares */ + char * darkBackTextureFile; /* Name of texture bitmap for dark squares */ + int liteBackTextureMode; + int darkBackTextureMode; + char * renderPiecesWithFont; /* Name of font for rendering chess pieces */ + char * fontToPieceTable; /* Map to translate font character to chess pieces */ + int fontBackColorWhite; + int fontForeColorWhite; + int fontBackColorBlack; + int fontForeColorBlack; + int fontPieceSize; /* Size of font relative to square (percentage) */ + int overrideLineGap; /* If >= 0 overrides the lineGap value of the board size properties */ + int adjudicateLossThreshold; /* Adjudicate a two-machine game if both engines agree the score is below this for 6 plies */ + int delayBeforeQuit; + int delayAfterQuit; + char * nameOfDebugFile; + char * pgnEventHeader; + int defaultFrcPosition; + char * gameListTags; + Boolean saveOutOfBookInfo; + Boolean showEvalInMoveHistory; + int evalHistColorWhite; + int evalHistColorBlack; + Boolean highlightMoveWithArrow; + int highlightArrowColor; + Boolean useStickyWindows; + int adjudicateDrawMoves; + Boolean autoDisplayComment; + Boolean autoDisplayTags; + Boolean firstIsUCI; + Boolean secondIsUCI; + Boolean firstHasOwnBookUCI; + Boolean secondHasOwnBookUCI; + char * polyglotDir; + Boolean usePolyglotBook; + char * polyglotBook; + int defaultHashSize; + int defaultCacheSizeEGTB; + char * defaultPathEGTB; + + /* [HGM] Board size */ + int NrFiles; + int NrRanks; + int matchPause; + Boolean testClaims; + int ruleMoves; + int drawRepeats; + +#if ZIPPY + char *zippyLines; + char *zippyPinhead; + char *zippyPassword; + char *zippyPassword2; + char *zippyWrongPassword; + char *zippyAcceptOnly; + int zippyUseI; + int zippyBughouse; + int zippyNoplayCrafty; + char *zippyGameEnd; + char *zippyGameStart; + int zippyAdjourn; + int zippyAbort; + char *zippyVariants; + int zippyMaxGames; + int zippyReplayTimeout; /*seconds*/ +#endif +} AppData, *AppDataPtr; + +/* [AS] PGN tags (for showing in the game list) */ +#define GLT_EVENT 'e' +#define GLT_SITE 's' +#define GLT_DATE 'd' +#define GLT_ROUND 'o' +#define GLT_PLAYERS 'p' /* I.e. white "-" black */ +#define GLT_RESULT 'r' +#define GLT_WHITE_ELO 'w' +#define GLT_BLACK_ELO 'b' +#define GLT_TIME_CONTROL 't' +#define GLT_VARIANT 'v' +#define GLT_OUT_OF_BOOK 'a' + +#define GLT_DEFAULT_TAGS "eprd" /* Event, players, result, date */ + +#define GLT_ALL_TAGS "esdoprwbtva" + +#define PGN_OUT_OF_BOOK "Annotator" + +extern AppData appData; + +typedef struct { + /* PGN 7-tag info */ + char *event; + char *site; + char *date; + char *round; + char *white; + char *black; + ChessMove result; + /* Additional info */ + char *fen; /* NULL or FEN for starting position; input only */ + char *resultDetails; + char *timeControl; + char *extraTags; /* NULL or "[Tag \"Value\"]\n", etc. */ + int whiteRating; /* -1 if unknown */ + int blackRating; /* -1 if unknown */ + VariantClass variant; + char *outOfBook; /* [AS] Move and score when engine went out of book */ +} GameInfo; + + +#endif diff --git a/config.h b/config.h index ff433ae..b29bde6 100644 --- a/config.h +++ b/config.h @@ -1,133 +1,143 @@ -/* config.h.in. Generated automatically from configure.in by autoheader. */ - -/* Define if you have that is POSIX.1 compatible. */ -/*#undef HAVE_SYS_WAIT_H*/ - -/* Define if you need to in order for stat and other things to work. */ -/*#undef _POSIX_SOURCE*/ - -/* Define as the return type of signal handlers (int or void). */ -/*#undef RETSIGTYPE*/ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both and . */ -/*#undef TIME_WITH_SYS_TIME*/ - -/* Define if lex declares yytext as a char * by default, not a char[]. */ -/*#undef YYTEXT_POINTER*/ - -/*#define FIRST_PTY_LETTER 'p'*/ - -#define HAVE_FCNTL_H 1 - -#define HAVE_GETHOSTNAME 0 - -#define HAVE_GETTIMEOFDAY 0 - -/* Use our own random() defined in winboard.c. */ -#define HAVE_RANDOM 1 -#define random myrandom -#define srandom mysrandom - -#define HAVE_SYS_SOCKET_H 0 - -/*#undef IBMRTAIX*/ - -#define LAST_PTY_LETTER 'q' - -#define PATCHLEVEL "7" - -#define PRODUCT "WinBoard" - -#define PTY_ITERATION - -#define PTY_NAME_SPRINTF - -#define PTY_TTY_NAME_SPRINTF - -#define REMOTE_SHELL "" - -/*#undef RTU*/ - -/*#undef UNIPLUS*/ - -#define USE_PTYS 0 - -#define VERSION "4.2" - -/*#undef X_WCHAR*/ - -#ifndef __BORLANDC__ -#define WIN32 1 -#else -#define WIN32 -#endif - -#define ZIPPY 1 - -/* Define if you have the _getpty function. */ -/*#undef HAVE__GETPTY*/ - -/* Define if you have the ftime function. */ -#define HAVE_FTIME 1 - -/* Define if you have the grantpt function. */ -/*#undef HAVE_GRANTPT*/ - -/* Define if you have the rand48 function. */ -/*#undef HAVE_RAND48*/ - -/* Define if you have the sysinfo function. */ -/*#undef HAVE_SYSINFO*/ - -/* Define if you have the header file. */ -/*#undef HAVE_LAN_SOCKET_H*/ - -/* Define if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define if you have the header file. */ -/*#undef HAVE_STROPTS_H*/ - -/* Define if you have the header file. */ -#define HAVE_SYS_FCNTL_H 0 - -/* Define if you have the header file. */ -/*#undef HAVE_SYS_SYSTEMINFO_H*/ - -/* Define if you have the header file. */ -/*#undef HAVE_SYS_TIME_H*/ - -/* Define if you have the header file. */ -/*#undef HAVE_UNISTD_H*/ - -/* Define if you have the i library (-li). */ -/*#undef HAVE_LIBI*/ - -/* Define if you have the seq library (-lseq). */ -/*#undef HAVE_LIBSEQ*/ - -/* - Options - -DEMULATE_RSH -DREMOTE_SHELL=\"\" is necessary on Windows 95, because it - does not have its own rsh command. It works better this way on NT too, - because the NT rsh does not propagate signals to the remote process. - -DATTENTION is included even though I haven't been able to send signals to - child processes on Windows, because at least I can send them over rsh to - Unix programs. On Windows I send a newline instead, which wakes up the - chess program if it's polling. On my GNU Chess port the newline actually - works even for Move Now. -*/ -#define EMULATE_RSH 1 -#define ATTENTION 1 - -#ifdef __BORLANDC__ -#define _strdup(x) strdup(x) -#define STRICT -#define _winmajor 3 /* windows 95 */ -#define SCF_DEFAULT 0x0000 -#define SCF_ALL 0x0004 -#endif - +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* [HGM] Defining ADJUDICATE switches on rep-draw and insuf-material draws */ +#define ADJUDICATE + +/* [HGM] Defining FAIRY allows different K- & Q-side piece symbols */ +#define FAIRY + +#ifdef FAIRY +#define GOTHIC "Gothic Chess (see www.GothicChess.com) is licensed under U.S. Patent #6,481,716 by Ed Trice" +#endif + +/* Define if you have that is POSIX.1 compatible. */ +/*#undef HAVE_SYS_WAIT_H*/ + +/* Define if you need to in order for stat and other things to work. */ +/*#undef _POSIX_SOURCE*/ + +/* Define as the return type of signal handlers (int or void). */ +/*#undef RETSIGTYPE*/ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +/*#undef TIME_WITH_SYS_TIME*/ + +/* Define if lex declares yytext as a char * by default, not a char[]. */ +/*#undef YYTEXT_POINTER*/ + +/*#define FIRST_PTY_LETTER 'p'*/ + +#define HAVE_FCNTL_H 1 + +#define HAVE_GETHOSTNAME 0 + +#define HAVE_GETTIMEOFDAY 0 + +/* Use our own random() defined in winboard.c. */ +#define HAVE_RANDOM 1 +#define random myrandom +#define srandom mysrandom + +#define HAVE_SYS_SOCKET_H 0 + +/*#undef IBMRTAIX*/ + +#define LAST_PTY_LETTER 'q' + +#define PATCHLEVEL "2" + +#define PRODUCT "WinBoard" + +#define PTY_ITERATION + +#define PTY_NAME_SPRINTF + +#define PTY_TTY_NAME_SPRINTF + +#define REMOTE_SHELL "" + +/*#undef RTU*/ + +/*#undef UNIPLUS*/ + +#define USE_PTYS 0 + +#define VERSION "4.3" + +/*#undef X_WCHAR*/ + +#ifndef __BORLANDC__ +#define WIN32 1 +#else +#define WIN32 +#endif + +#define ZIPPY 1 + +/* Define if you have the _getpty function. */ +/*#undef HAVE__GETPTY*/ + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the grantpt function. */ +/*#undef HAVE_GRANTPT*/ + +/* Define if you have the rand48 function. */ +/*#undef HAVE_RAND48*/ + +/* Define if you have the sysinfo function. */ +/*#undef HAVE_SYSINFO*/ + +/* Define if you have the header file. */ +/*#undef HAVE_LAN_SOCKET_H*/ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/*#undef HAVE_STROPTS_H*/ + +/* Define if you have the header file. */ +#define HAVE_SYS_FCNTL_H 0 + +/* Define if you have the header file. */ +/*#undef HAVE_SYS_SYSTEMINFO_H*/ + +/* Define if you have the header file. */ +/*#undef HAVE_SYS_TIME_H*/ + +/* Define if you have the header file. */ +/*#undef HAVE_UNISTD_H*/ + +/* Define if you have the i library (-li). */ +/*#undef HAVE_LIBI*/ + +/* Define if you have the seq library (-lseq). */ +/*#undef HAVE_LIBSEQ*/ + +/* + Options + -DEMULATE_RSH -DREMOTE_SHELL=\"\" is necessary on Windows 95, because it + does not have its own rsh command. It works better this way on NT too, + because the NT rsh does not propagate signals to the remote process. + -DATTENTION is included even though I haven't been able to send signals to + child processes on Windows, because at least I can send them over rsh to + Unix programs. On Windows I send a newline instead, which wakes up the + chess program if it's polling. On my GNU Chess port the newline actually + works even for Move Now. +*/ +#define EMULATE_RSH 1 +#define ATTENTION 1 + +#ifdef __BORLANDC__ +#define _strdup(x) strdup(x) +#define STRICT +#define _winmajor 3 /* windows 95 */ +#define SCF_DEFAULT 0x0000 +#define SCF_ALL 0x0004 +#endif + diff --git a/moves.c b/moves.c index accbfed..57e350e 100644 --- a/moves.c +++ b/moves.c @@ -1,1024 +1,1241 @@ -/* - * moves.c - Move generation and checking - * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $ - * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-95 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. - * ------------------------------------------------------------------------ - */ - -#include "config.h" - -#include -#if HAVE_STRING_H -# include -#else /* not HAVE_STRING_H */ -# include -#endif /* not HAVE_STRING_H */ -#include "common.h" -#include "backend.h" -#include "moves.h" -#include "parser.h" - -int WhitePiece P((ChessSquare)); -int BlackPiece P((ChessSquare)); -int SameColor P((ChessSquare, ChessSquare)); - - -int WhitePiece(piece) - ChessSquare piece; -{ - return (int) piece >= (int) WhitePawn && (int) piece <= (int) WhiteKing; -} - -int BlackPiece(piece) - ChessSquare piece; -{ - return (int) piece >= (int) BlackPawn && (int) piece <= (int) BlackKing; -} - -int SameColor(piece1, piece2) - ChessSquare piece1, piece2; -{ - return ((int) piece1 >= (int) WhitePawn && - (int) piece1 <= (int) WhiteKing && - (int) piece2 >= (int) WhitePawn && - (int) piece2 <= (int) WhiteKing) - || ((int) piece1 >= (int) BlackPawn && - (int) piece1 <= (int) BlackKing && - (int) piece2 >= (int) BlackPawn && - (int) piece2 <= (int) BlackKing); -} - -ChessSquare PromoPiece(moveType) - ChessMove moveType; -{ - switch (moveType) { - default: - return EmptySquare; - case WhitePromotionQueen: - return WhiteQueen; - case BlackPromotionQueen: - return BlackQueen; - case WhitePromotionRook: - return WhiteRook; - case BlackPromotionRook: - return BlackRook; - case WhitePromotionBishop: - return WhiteBishop; - case BlackPromotionBishop: - return BlackBishop; - case WhitePromotionKnight: - return WhiteKnight; - case BlackPromotionKnight: - return BlackKnight; - case WhitePromotionKing: - return WhiteKing; - case BlackPromotionKing: - return BlackKing; - } -} - -ChessMove PromoCharToMoveType(whiteOnMove, promoChar) - int whiteOnMove; - int promoChar; -{ - if (whiteOnMove) { - switch (promoChar) { - case 'n': - case 'N': - return WhitePromotionKnight; - case 'b': - case 'B': - return WhitePromotionBishop; - case 'r': - case 'R': - return WhitePromotionRook; - case 'q': - case 'Q': - return WhitePromotionQueen; - case 'k': - case 'K': - return WhitePromotionKing; - case NULLCHAR: - default: - return NormalMove; - } - } else { - switch (promoChar) { - case 'n': - case 'N': - return BlackPromotionKnight; - case 'b': - case 'B': - return BlackPromotionBishop; - case 'r': - case 'R': - return BlackPromotionRook; - case 'q': - case 'Q': - return BlackPromotionQueen; - case 'k': - case 'K': - return BlackPromotionKing; - case NULLCHAR: - default: - return NormalMove; - } - } -} - -char pieceToChar[] = { - 'P', 'N', 'B', 'R', 'Q', 'K', - 'p', 'n', 'b', 'r', 'q', 'k', 'x' - }; - -char PieceToChar(p) - ChessSquare p; -{ - return pieceToChar[(int) p]; -} - -ChessSquare CharToPiece(c) - int c; -{ - switch (c) { - default: - case 'x': return EmptySquare; - case 'P': return WhitePawn; - case 'R': return WhiteRook; - case 'N': return WhiteKnight; - case 'B': return WhiteBishop; - case 'Q': return WhiteQueen; - case 'K': return WhiteKing; - case 'p': return BlackPawn; - case 'r': return BlackRook; - case 'n': return BlackKnight; - case 'b': return BlackBishop; - case 'q': return BlackQueen; - case 'k': return BlackKing; - } -} - -void CopyBoard(to, from) - Board to, from; -{ - int i, j; - - for (i = 0; i < BOARD_SIZE; i++) - for (j = 0; j < BOARD_SIZE; j++) - to[i][j] = from[i][j]; -} - -int CompareBoards(board1, board2) - Board board1, board2; -{ - int i, j; - - for (i = 0; i < BOARD_SIZE; i++) - for (j = 0; j < BOARD_SIZE; j++) { - if (board1[i][j] != board2[i][j]) - return FALSE; - } - return TRUE; -} - - -/* Call callback once for each pseudo-legal move in the given - position, except castling moves. A move is pseudo-legal if it is - legal, or if it would be legal except that it leaves the king in - check. In the arguments, epfile is EP_NONE if the previous move - was not a double pawn push, or the file 0..7 if it was, or - EP_UNKNOWN if we don't know and want to allow all e.p. captures. - Promotion moves generated are to Queen only. -*/ -void GenPseudoLegal(board, flags, epfile, callback, closure) - Board board; - int flags; - int epfile; - MoveCallback callback; - VOIDSTAR closure; -{ - int rf, ff; - int i, j, d, s, fs, rs, rt, ft; - - for (rf = 0; rf <= 7; rf++) - for (ff = 0; ff <= 7; ff++) { - if (flags & F_WHITE_ON_MOVE) { - if (!WhitePiece(board[rf][ff])) continue; - } else { - if (!BlackPiece(board[rf][ff])) continue; - } - switch (board[rf][ff]) { - case EmptySquare: - default: - /* can't happen */ - break; - - case WhitePawn: - if (rf < 7 && board[rf + 1][ff] == EmptySquare) { - callback(board, flags, - rf == 6 ? WhitePromotionQueen : NormalMove, - rf, ff, rf + 1, ff, closure); - } - if (rf == 1 && board[2][ff] == EmptySquare && - board[3][ff] == EmptySquare) { - callback(board, flags, NormalMove, - rf, ff, 3, ff, closure); - } - for (s = -1; s <= 1; s += 2) { - if (rf < 7 && ff + s >= 0 && ff + s <= 7 && - ((flags & F_KRIEGSPIEL_CAPTURE) || - BlackPiece(board[rf + 1][ff + s]))) { - callback(board, flags, - rf == 6 ? WhitePromotionQueen : NormalMove, - rf, ff, rf + 1, ff + s, closure); - } - if (rf == 4) { - if (ff + s >= 0 && ff + s <= 7 && - (epfile == ff + s || epfile == EP_UNKNOWN) && - board[4][ff + s] == BlackPawn && - board[5][ff + s] == EmptySquare) { - callback(board, flags, WhiteCapturesEnPassant, - rf, ff, 5, ff + s, closure); - } - } - } - break; - - case BlackPawn: - if (rf > 0 && board[rf - 1][ff] == EmptySquare) { - callback(board, flags, - rf == 1 ? BlackPromotionQueen : NormalMove, - rf, ff, rf - 1, ff, closure); - } - if (rf == 6 && board[5][ff] == EmptySquare && - board[4][ff] == EmptySquare) { - callback(board, flags, NormalMove, - rf, ff, 4, ff, closure); - } - for (s = -1; s <= 1; s += 2) { - if (rf > 0 && ff + s >= 0 && ff + s <= 7 && - ((flags & F_KRIEGSPIEL_CAPTURE) || - WhitePiece(board[rf - 1][ff + s]))) { - callback(board, flags, - rf == 1 ? BlackPromotionQueen : NormalMove, - rf, ff, rf - 1, ff + s, closure); - } - if (rf == 3) { - if (ff + s >= 0 && ff + s <= 7 && - (epfile == ff + s || epfile == EP_UNKNOWN) && - board[3][ff + s] == WhitePawn && - board[2][ff + s] == EmptySquare) { - callback(board, flags, BlackCapturesEnPassant, - rf, ff, 2, ff + s, closure); - } - } - } - break; - - case WhiteKnight: - case BlackKnight: - for (i = -1; i <= 1; i += 2) - for (j = -1; j <= 1; j += 2) - for (s = 1; s <= 2; s++) { - rt = rf + i*s; - ft = ff + j*(3-s); - if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue; - if (SameColor(board[rf][ff], board[rt][ft])) continue; - callback(board, flags, NormalMove, - rf, ff, rt, ft, closure); - } - break; - - case WhiteBishop: - case BlackBishop: - for (rs = -1; rs <= 1; rs += 2) - for (fs = -1; fs <= 1; fs += 2) - for (i = 1;; i++) { - rt = rf + (i * rs); - ft = ff + (i * fs); - if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break; - if (SameColor(board[rf][ff], board[rt][ft])) break; - callback(board, flags, NormalMove, - rf, ff, rt, ft, closure); - if (board[rt][ft] != EmptySquare) break; - } - break; - - case WhiteRook: - case BlackRook: - for (d = 0; d <= 1; d++) - for (s = -1; s <= 1; s += 2) - for (i = 1;; i++) { - rt = rf + (i * s) * d; - ft = ff + (i * s) * (1 - d); - if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break; - if (SameColor(board[rf][ff], board[rt][ft])) break; - callback(board, flags, NormalMove, - rf, ff, rt, ft, closure); - if (board[rt][ft] != EmptySquare) break; - } - break; - - case WhiteQueen: - case BlackQueen: - for (rs = -1; rs <= 1; rs++) - for (fs = -1; fs <= 1; fs++) { - if (rs == 0 && fs == 0) continue; - for (i = 1;; i++) { - rt = rf + (i * rs); - ft = ff + (i * fs); - if (rt < 0 || rt > 7 || ft < 0 || ft > 7) break; - if (SameColor(board[rf][ff], board[rt][ft])) break; - callback(board, flags, NormalMove, - rf, ff, rt, ft, closure); - if (board[rt][ft] != EmptySquare) break; - } - } - break; - - case WhiteKing: - case BlackKing: - for (i = -1; i <= 1; i++) - for (j = -1; j <= 1; j++) { - if (i == 0 && j == 0) continue; - rt = rf + i; - ft = ff + j; - if (rt < 0 || rt > 7 || ft < 0 || ft > 7) continue; - if (SameColor(board[rf][ff], board[rt][ft])) continue; - callback(board, flags, NormalMove, - rf, ff, rt, ft, closure); - } - break; - } - } -} - - -typedef struct { - MoveCallback cb; - VOIDSTAR cl; -} GenLegalClosure; - -extern void GenLegalCallback P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - -void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register GenLegalClosure *cl = (GenLegalClosure *) closure; - - if (!(flags & F_IGNORE_CHECK) && - CheckTest(board, flags, rf, ff, rt, ft, - kind == WhiteCapturesEnPassant || - kind == BlackCapturesEnPassant)) return; - if (flags & F_ATOMIC_CAPTURE) { - if (board[rt][ft] != EmptySquare || - kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) { - int r, f; - ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing; - if (board[rf][ff] == king) return; - for (r = rt-1; r <= rt+1; r++) { - for (f = ft-1; f <= ft+1; f++) { - if (r >= 0 && r <= 7 && f >= 0 && f <= 7 && - board[r][f] == king) return; - } - } - } - } - cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl); -} - - -typedef struct { - int rf, ff, rt, ft; - ChessMove kind; -} LegalityTestClosure; - - -/* Like GenPseudoLegal, but (1) include castling moves, (2) unless - F_IGNORE_CHECK is set in the flags, omit moves that would leave the - king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit - moves that would destroy your own king. The CASTLE_OK flags are - true if castling is not yet ruled out by a move of the king or - rook. Return TRUE if the player on move is currently in check and - F_IGNORE_CHECK is not set. */ -int GenLegal(board, flags, epfile, callback, closure) - Board board; - int flags; - int epfile; - MoveCallback callback; - VOIDSTAR closure; -{ - GenLegalClosure cl; - int ff, ft; - int ignoreCheck = (flags & F_IGNORE_CHECK) != 0; - - cl.cb = callback; - cl.cl = closure; - GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl); - - if (!ignoreCheck && - CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE; - - /* Generate castling moves */ - for (ff = 4; ff >= 3; ff-- /*ics wild 1*/) { - if ((flags & F_WHITE_ON_MOVE) && - (flags & F_WHITE_KCASTLE_OK) && - board[0][ff] == WhiteKing && - board[0][ff + 1] == EmptySquare && - board[0][ff + 2] == EmptySquare && - board[0][6] == EmptySquare && - board[0][7] == WhiteRook && - (ignoreCheck || - (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) && - !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) { - - callback(board, flags, - ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild, - 0, ff, 0, ff + 2, closure); - } - if ((flags & F_WHITE_ON_MOVE) && - (flags & F_WHITE_QCASTLE_OK) && - board[0][ff] == WhiteKing && - board[0][ff - 1] == EmptySquare && - board[0][ff - 2] == EmptySquare && - board[0][1] == EmptySquare && - board[0][0] == WhiteRook && - (ignoreCheck || - (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) && - !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) { - - callback(board, flags, - ff==4 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild, - 0, ff, 0, ff - 2, closure); - } - if (!(flags & F_WHITE_ON_MOVE) && - (flags & F_BLACK_KCASTLE_OK) && - board[7][ff] == BlackKing && - board[7][ff + 1] == EmptySquare && - board[7][ff + 2] == EmptySquare && - board[7][6] == EmptySquare && - board[7][7] == BlackRook && - (ignoreCheck || - (!CheckTest(board, flags, 7, ff, 7, ff + 1, FALSE) && - !CheckTest(board, flags, 7, ff, 7, ff + 2, FALSE)))) { - - callback(board, flags, - ff==4 ? BlackKingSideCastle : BlackKingSideCastleWild, - 7, ff, 7, ff + 2, closure); - } - if (!(flags & F_WHITE_ON_MOVE) && - (flags & F_BLACK_QCASTLE_OK) && - board[7][ff] == BlackKing && - board[7][ff - 1] == EmptySquare && - board[7][ff - 2] == EmptySquare && - board[7][1] == EmptySquare && - board[7][0] == BlackRook && - (ignoreCheck || - (!CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE) && - !CheckTest(board, flags, 7, ff, 7, ff - 1, FALSE)))) { - - callback(board, flags, - ff==4 ? BlackQueenSideCastle : BlackQueenSideCastleWild, - 7, ff, 7, ff - 2, closure); - } - } - - /* PUSH Fabien */ - - /* generate all potential FRC castling moves (KxR), ignoring flags */ - - if ((flags & F_WHITE_ON_MOVE) != 0) { - - for (ff = 1; ff < 7; ff++) { - if (board[0][ff] == WhiteKing) { - for (ft = 0; ft < 8; ft++) { - if (board[0][ft] == WhiteRook) { - callback(board, flags, - (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR, - 0, ff, 0, ft, closure); - } - } - } - } - - } else { - - for (ff = 1; ff < 7; ff++) { - if (board[7][ff] == BlackKing) { - for (ft = 0; ft < 8; ft++) { - if (board[7][ft] == BlackRook) { - callback(board, flags, - (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR, - 7, ff, 7, ft, closure); - } - } - } - } - } - - /* POP Fabien */ - - return FALSE; -} - - -typedef struct { - int rking, fking; - int check; -} CheckTestClosure; - - -extern void CheckTestCallback P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - - -void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register CheckTestClosure *cl = (CheckTestClosure *) closure; - - if (rt == cl->rking && ft == cl->fking) cl->check++; -} - - -/* If the player on move were to move from (rf, ff) to (rt, ft), would - he leave himself in check? Or if rf == -1, is the player on move - in check now? enPassant must be TRUE if the indicated move is an - e.p. capture. The possibility of castling out of a check along the - back rank is not accounted for (i.e., we still return nonzero), as - this is illegal anyway. Return value is the number of times the - king is in check. */ -int CheckTest(board, flags, rf, ff, rt, ft, enPassant) - Board board; - int flags; - int rf, ff, rt, ft, enPassant; -{ - CheckTestClosure cl; - ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing; - ChessSquare captured = EmptySquare; - /* Suppress warnings on uninitialized variables */ - - if (rf >= 0) { - if (enPassant) { - captured = board[rf][ft]; - board[rf][ft] = EmptySquare; - } else { - captured = board[rt][ft]; - } - board[rt][ft] = board[rf][ff]; - board[rf][ff] = EmptySquare; - } - - /* For compatibility with ICS wild 9, we scan the board in the - order a1, a2, a3, ... b1, b2, ..., h8 to find the first king, - and we test only whether that one is in check. */ - cl.check = 0; - for (cl.fking = 0; cl.fking <= 7; cl.fking++) - for (cl.rking = 0; cl.rking <= 7; cl.rking++) { - if (board[cl.rking][cl.fking] == king) { - GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1, - CheckTestCallback, (VOIDSTAR) &cl); - goto undo_move; /* 2-level break */ - } - } - - undo_move: - - if (rf >= 0) { - board[rf][ff] = board[rt][ft]; - if (enPassant) { - board[rf][ft] = captured; - board[rt][ft] = EmptySquare; - } else { - board[rt][ft] = captured; - } - } - - return cl.check; -} - - -extern void LegalityTestCallback P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - -void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register LegalityTestClosure *cl = (LegalityTestClosure *) closure; - - if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) - cl->kind = kind; -} - -ChessMove LegalityTest(board, flags, epfile, rf, ff, rt, ft, promoChar) - Board board; - int flags, epfile; - int rf, ff, rt, ft, promoChar; -{ - LegalityTestClosure cl; - - cl.rf = rf; - cl.ff = ff; - cl.rt = rt; - cl.ft = ft; - cl.kind = IllegalMove; - GenLegal(board, flags, epfile, LegalityTestCallback, (VOIDSTAR) &cl); - if (promoChar != NULLCHAR && promoChar != 'x') { - if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) { - cl.kind = - PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar); - } else { - cl.kind = IllegalMove; - } - } - return cl.kind; -} - -typedef struct { - int count; -} MateTestClosure; - -extern void MateTestCallback P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - -void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register MateTestClosure *cl = (MateTestClosure *) closure; - - cl->count++; -} - -/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */ -int MateTest(board, flags, epfile) - Board board; - int flags, epfile; -{ - MateTestClosure cl; - int inCheck; - - cl.count = 0; - inCheck = GenLegal(board, flags, epfile, MateTestCallback, (VOIDSTAR) &cl); - if (cl.count > 0) { - return inCheck ? MT_CHECK : MT_NONE; - } else { - return inCheck ? MT_CHECKMATE : MT_STALEMATE; - } -} - - -extern void DisambiguateCallback P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - -void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register DisambiguateClosure *cl = (DisambiguateClosure *) closure; - - if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) && - (cl->rfIn == -1 || cl->rfIn == rf) && - (cl->ffIn == -1 || cl->ffIn == ff) && - (cl->rtIn == -1 || cl->rtIn == rt) && - (cl->ftIn == -1 || cl->ftIn == ft)) { - - cl->count++; - cl->piece = board[rf][ff]; - cl->rf = rf; - cl->ff = ff; - cl->rt = rt; - cl->ft = ft; - cl->kind = kind; - } -} - -void Disambiguate(board, flags, epfile, closure) - Board board; - int flags, epfile; - DisambiguateClosure *closure; -{ - int illegal = 0; - closure->count = 0; - closure->rf = closure->ff = closure->rt = closure->ft = 0; - closure->kind = ImpossibleMove; - GenLegal(board, flags, epfile, DisambiguateCallback, (VOIDSTAR) closure); - if (closure->count == 0) { - /* See if it's an illegal move due to check */ - illegal = 1; - GenLegal(board, flags|F_IGNORE_CHECK, epfile, DisambiguateCallback, - (VOIDSTAR) closure); - if (closure->count == 0) { - /* No, it's not even that */ - return; - } - } - if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') { - if (closure->kind == WhitePromotionQueen - || closure->kind == BlackPromotionQueen) { - closure->kind = - PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, - closure->promoCharIn); - } else { - closure->kind = IllegalMove; - } - } - closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind))); - if (closure->promoChar == 'x') closure->promoChar = NULLCHAR; - if (closure->count > 1) { - closure->kind = AmbiguousMove; - } - if (illegal) { - /* Note: If more than one illegal move matches, but no legal - moves, we return IllegalMove, not AmbiguousMove. Caller - can look at closure->count to detect this. - */ - closure->kind = IllegalMove; - } -} - - -typedef struct { - /* Input */ - ChessSquare piece; - int rf, ff, rt, ft; - /* Output */ - ChessMove kind; - int rank; - int file; - int either; -} CoordsToAlgebraicClosure; - -extern void CoordsToAlgebraicCallback P((Board board, int flags, - ChessMove kind, int rf, int ff, - int rt, int ft, VOIDSTAR closure)); - -void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure) - Board board; - int flags; - ChessMove kind; - int rf, ff, rt, ft; - VOIDSTAR closure; -{ - register CoordsToAlgebraicClosure *cl = - (CoordsToAlgebraicClosure *) closure; - - if (rt == cl->rt && ft == cl->ft && - board[rf][ff] == cl->piece) { - if (rf == cl->rf) { - if (ff == cl->ff) { - cl->kind = kind; /* this is the move we want */ - } else { - cl->file++; /* need file to rule out this move */ - } - } else { - if (ff == cl->ff) { - cl->rank++; /* need rank to rule out this move */ - } else { - cl->either++; /* rank or file will rule out this move */ - } - } - } -} - -/* Convert coordinates to normal algebraic notation. - promoChar must be NULLCHAR or 'x' if not a promotion. -*/ -ChessMove CoordsToAlgebraic(board, flags, epfile, - rf, ff, rt, ft, promoChar, out) - Board board; - int flags, epfile; - int rf, ff, rt, ft; - int promoChar; - char out[MOVE_LEN]; -{ - ChessSquare piece; - ChessMove kind; - char *outp = out; - CoordsToAlgebraicClosure cl; - - if (rf == DROP_RANK) { - /* Bughouse piece drop */ - *outp++ = ToUpper(PieceToChar((ChessSquare) ff)); - *outp++ = '@'; - *outp++ = ft + 'a'; - *outp++ = rt + '1'; - *outp = NULLCHAR; - return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop; - } - - if (promoChar == 'x') promoChar = NULLCHAR; - piece = board[rf][ff]; - switch (piece) { - case WhitePawn: - case BlackPawn: - kind = LegalityTest(board, flags, epfile, rf, ff, rt, ft, promoChar); - if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { - /* Keep short notation if move is illegal only because it - leaves the player in check, but still return IllegalMove */ - kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, - rf, ff, rt, ft, promoChar); - if (kind == IllegalMove) break; - kind = IllegalMove; - } - /* Pawn move */ - *outp++ = ff + 'a'; - if (ff == ft) { - /* Non-capture; use style "e5" */ - *outp++ = rt + '1'; - } else { - /* Capture; use style "exd5" */ - *outp++ = 'x'; - *outp++ = ft + 'a'; - *outp++ = rt + '1'; - } - /* Use promotion suffix style "=Q" */ - if (promoChar != NULLCHAR && promoChar != 'x') { - *outp++ = '='; - *outp++ = ToUpper(promoChar); - } - *outp = NULLCHAR; - return kind; - - - case WhiteKing: - case BlackKing: - /* Fabien moved code: FRC castling first (if KxR), wild castling second */ - /* Code added by Tord: FRC castling. */ - if((piece == WhiteKing && board[rt][ft] == WhiteRook) || - (piece == BlackKing && board[rt][ft] == BlackRook)) { - if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O"); - return LegalityTest(board, flags, epfile, - rf, ff, rt, ft, promoChar); - } - /* End of code added by Tord */ - /* Test for castling or ICS wild castling */ - /* Use style "O-O" (oh-oh) for PGN compatibility */ - else if (rf == rt && - rf == ((piece == WhiteKing) ? 0 : 7) && - ((ff == 4 && (ft == 2 || ft == 6)) || - (ff == 3 && (ft == 1 || ft == 5)))) { - switch (ft) { - case 1: - case 6: - strcpy(out, "O-O"); - break; - case 2: - case 5: - strcpy(out, "O-O-O"); - break; - } - /* This notation is always unambiguous, unless there are - kings on both the d and e files, with "wild castling" - possible for the king on the d file and normal castling - possible for the other. ICS rules for wild 9 - effectively make castling illegal for either king in - this situation. So I am not going to worry about it; - I'll just generate an ambiguous O-O in this case. - */ - return LegalityTest(board, flags, epfile, - rf, ff, rt, ft, promoChar); - } - - /* else fall through */ - - default: - /* Piece move */ - cl.rf = rf; - cl.ff = ff; - cl.rt = rt; - cl.ft = ft; - cl.piece = piece; - cl.kind = IllegalMove; - cl.rank = cl.file = cl.either = 0; - GenLegal(board, flags, epfile, - CoordsToAlgebraicCallback, (VOIDSTAR) &cl); - - if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { - /* Generate pretty moves for moving into check, but - still return IllegalMove. - */ - GenLegal(board, flags|F_IGNORE_CHECK, epfile, - CoordsToAlgebraicCallback, (VOIDSTAR) &cl); - if (cl.kind == IllegalMove) break; - cl.kind = IllegalMove; - } - - /* Style is "Nf3" or "Nxf7" if this is unambiguous, - else "Ngf3" or "Ngxf7", - else "N1f3" or "N5xf7", - else "Ng1f3" or "Ng5xf7". - */ - *outp++ = ToUpper(PieceToChar(piece)); - - if (cl.file || (cl.either && !cl.rank)) { - *outp++ = ff + 'a'; - } - if (cl.rank) { - *outp++ = rf + '1'; - } - - if(board[rt][ft] != EmptySquare) - *outp++ = 'x'; - - *outp++ = ft + 'a'; - *outp++ = rt + '1'; - *outp = NULLCHAR; - return cl.kind; - - case EmptySquare: - /* Moving a nonexistent piece */ - break; - } - - /* Not a legal move, even ignoring check. - If there was a piece on the from square, - use style "Ng1g3" or "Ng1xe8"; - if there was a pawn or nothing (!), - use style "g1g3" or "g1xe8". Use "x" - if a piece was on the to square, even - a piece of the same color. - */ - outp = out; - if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) { - *outp++ = ToUpper(PieceToChar(piece)); - } - *outp++ = ff + 'a'; - *outp++ = rf + '1'; - if (board[rt][ft] != EmptySquare) *outp++ = 'x'; - *outp++ = ft + 'a'; - *outp++ = rt + '1'; - /* Use promotion suffix style "=Q" */ - if (promoChar != NULLCHAR && promoChar != 'x') { - *outp++ = '='; - *outp++ = ToUpper(promoChar); - } - *outp = NULLCHAR; - - return IllegalMove; -} +/* + * moves.c - Move generation and checking + * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $ + * + * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. + * Enhancements Copyright 1992-95 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. + * ------------------------------------------------------------------------ + */ + +#include "config.h" + +#include +#if HAVE_STRING_H +# include +#else /* not HAVE_STRING_H */ +# include +#endif /* not HAVE_STRING_H */ +#include "common.h" +#include "backend.h" +#include "moves.h" +#include "parser.h" + +int WhitePiece P((ChessSquare)); +int BlackPiece P((ChessSquare)); +int SameColor P((ChessSquare, ChessSquare)); + +extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */ + + +int WhitePiece(piece) + ChessSquare piece; +{ + return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn; +} + +int BlackPiece(piece) + ChessSquare piece; +{ + return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare; +} + +int SameColor(piece1, piece2) + ChessSquare piece1, piece2; +{ + return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */ + (int) piece1 < (int) BlackPawn && + (int) piece2 >= (int) WhitePawn && + (int) piece2 < (int) BlackPawn) + || ((int) piece1 >= (int) BlackPawn && + (int) piece1 < (int) EmptySquare && + (int) piece2 >= (int) BlackPawn && + (int) piece2 < (int) EmptySquare); +} + +ChessSquare PromoPiece(moveType) + ChessMove moveType; +{ + switch (moveType) { + default: + return EmptySquare; + case WhitePromotionQueen: + return WhiteQueen; + case BlackPromotionQueen: + return BlackQueen; + case WhitePromotionRook: + return WhiteRook; + case BlackPromotionRook: + return BlackRook; + case WhitePromotionBishop: + return WhiteBishop; + case BlackPromotionBishop: + return BlackBishop; + case WhitePromotionKnight: + return WhiteKnight; + case BlackPromotionKnight: + return BlackKnight; + case WhitePromotionKing: + return WhiteKing; + case BlackPromotionKing: + return BlackKing; +#ifdef FAIRY + case WhitePromotionChancellor: + return WhiteFairyRook; + case BlackPromotionChancellor: + return BlackFairyRook; + case WhitePromotionArchbishop: + return WhiteFairyBishop; + case BlackPromotionArchbishop: + return BlackFairyBishop; +#endif + } +} + +ChessMove PromoCharToMoveType(whiteOnMove, promoChar) + int whiteOnMove; + int promoChar; +{ + if (whiteOnMove) { + switch (promoChar) { + case 'n': + case 'N': + return WhitePromotionKnight; + case 'b': + case 'B': + return WhitePromotionBishop; + case 'r': + case 'R': + return WhitePromotionRook; +#ifdef FAIRY + case 'a': + case 'A': + return WhitePromotionArchbishop; + case 'c': + case 'C': + return WhitePromotionChancellor; +#endif + case 'q': + case 'Q': + return WhitePromotionQueen; + case 'k': + case 'K': + return WhitePromotionKing; + case NULLCHAR: + default: + return NormalMove; + } + } else { + switch (promoChar) { + case 'n': + case 'N': + return BlackPromotionKnight; + case 'b': + case 'B': + return BlackPromotionBishop; + case 'r': + case 'R': + return BlackPromotionRook; +#ifdef FAIRY + case 'a': + case 'A': + return BlackPromotionArchbishop; + case 'c': + case 'C': + return BlackPromotionChancellor; +#endif + case 'q': + case 'Q': + return BlackPromotionQueen; + case 'k': + case 'K': + return BlackPromotionKing; + case NULLCHAR: + default: + return NormalMove; + } + } +} + +char pieceToChar[] = { + 'P', 'N', 'B', 'R', +#ifdef FAIRY + 'A', 'C', 'F', 'H', 'E', 'W', 'D', 'O', 'G', 'M', +#endif + 'Q', 'K', 'p', 'n', 'b', 'r', +#ifdef FAIRY + 'a', 'c', 'f', 'h', 'e', 'w', 'd', 'o', 'g', 'm', +#endif + 'q', 'k', 'x' + }; + +char PieceToChar(p) + ChessSquare p; +{ + return pieceToChar[(int) p]; +} + +ChessSquare CharToPiece(c) + int c; +{ switch (c) { + default: + case 'x': return EmptySquare; + case 'P': return WhitePawn; + case 'R': return WhiteRook; + case 'N': return WhiteKnight; + case 'B': return WhiteBishop; + case 'Q': return WhiteQueen; + case 'K': return WhiteKing; + case 'p': return BlackPawn; + case 'r': return BlackRook; + case 'n': return BlackKnight; + case 'b': return BlackBishop; + case 'q': return BlackQueen; + case 'k': return BlackKing; +#ifdef FAIRY + case 'A': return WhiteCardinal; + case 'C': return WhiteMarshall; + case 'F': return WhiteFairyPawn; + case 'H': return WhiteFairyKnight; + case 'E': return WhiteFairyBishop; + case 'W': return WhiteFairyRook; + case 'D': return WhiteFairyCardinal; + case 'O': return WhiteFairyMarshall; + case 'G': return WhiteFairyQueen; + case 'M': return WhiteFairyKing; + + case 'a': return BlackCardinal; + case 'c': return BlackMarshall; + case 'f': return BlackFairyPawn; + case 'h': return BlackFairyKnight; + case 'e': return BlackFairyBishop; + case 'w': return BlackFairyRook; + case 'd': return BlackFairyCardinal; + case 'o': return BlackFairyMarshall; + case 'g': return BlackFairyQueen; + case 'm': return BlackFairyKing; + +#endif + } +} + +void CopyBoard(to, from) + Board to, from; +{ + int i, j; + + for (i = 0; i < BOARD_HEIGHT; i++) + for (j = 0; j < BOARD_WIDTH; j++) + to[i][j] = from[i][j]; +} + +int CompareBoards(board1, board2) + Board board1, board2; +{ + int i, j; + + for (i = 0; i < BOARD_HEIGHT; i++) + for (j = 0; j < BOARD_WIDTH; j++) { + if (board1[i][j] != board2[i][j]) + return FALSE; + } + return TRUE; +} + + +/* Call callback once for each pseudo-legal move in the given + position, except castling moves. A move is pseudo-legal if it is + legal, or if it would be legal except that it leaves the king in + check. In the arguments, epfile is EP_NONE if the previous move + was not a double pawn push, or the file 0..7 if it was, or + EP_UNKNOWN if we don't know and want to allow all e.p. captures. + Promotion moves generated are to Queen only. +*/ +void GenPseudoLegal(board, flags, epfile, callback, closure) + Board board; + int flags; + int epfile; + MoveCallback callback; + VOIDSTAR closure; +{ + int rf, ff; + int i, j, d, s, fs, rs, rt, ft, m; + + for (rf = 0; rf < BOARD_HEIGHT; rf++) + for (ff = 0; ff < BOARD_WIDTH; ff++) { + if (flags & F_WHITE_ON_MOVE) { + if (!WhitePiece(board[rf][ff])) continue; + } else { + if (!BlackPiece(board[rf][ff])) continue; + } + m = 0; + switch (board[rf][ff]) { + case EmptySquare: + default: + /* can't happen ([HGM] except for faries...) */ + break; + + case WhitePawn: +#ifdef FAIRY + if(gameInfo.variant == VariantXiangqi) { + /* [HGM] capture and move straight ahead in Xiangqi */ + if (rf < BOARD_HEIGHT-1 && + !SameColor(board[rf][ff], board[rf + 1][ff]) ) { + callback(board, flags, NormalMove, + rf, ff, rf + 1, ff, closure); + } + /* and move sideways when across the river */ + for (s = -1; s <= 1; s += 2) { + if (rf >= BOARD_HEIGHT>>1 && + ff + s >= 0 && ff + s < BOARD_WIDTH && + !WhitePiece(board[rf][ff+s]) ) { + callback(board, flags, NormalMove, + rf, ff, rf, ff+s, closure); + } + } + break; + } +#endif + if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) { + callback(board, flags, + rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove, + rf, ff, rf + 1, ff, closure); + } + if (rf == 1 && board[2][ff] == EmptySquare && + gameInfo.variant != VariantShatranj && /* [HGM] */ + board[3][ff] == EmptySquare ) { + callback(board, flags, NormalMove, + rf, ff, 3, ff, closure); + } + for (s = -1; s <= 1; s += 2) { + if (rf < BOARD_HEIGHT-1 && ff + s >= 0 && ff + s < BOARD_WIDTH && + ((flags & F_KRIEGSPIEL_CAPTURE) || + BlackPiece(board[rf + 1][ff + s]))) { + callback(board, flags, + rf == BOARD_HEIGHT-2 ? WhitePromotionQueen : NormalMove, + rf, ff, rf + 1, ff + s, closure); + } + if (rf == BOARD_HEIGHT-4) { + if (ff + s >= 0 && ff + s < BOARD_WIDTH && + (epfile == ff + s || epfile == EP_UNKNOWN) && + board[BOARD_HEIGHT-4][ff + s] == BlackPawn && + board[BOARD_HEIGHT-3][ff + s] == EmptySquare) { + callback(board, flags, WhiteCapturesEnPassant, + rf, ff, 5, ff + s, closure); + } + } + } + break; + + case BlackPawn: +#ifdef FAIRY + if(gameInfo.variant == VariantXiangqi) { + /* [HGM] capture straight ahead in Xiangqi */ + if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) { + callback(board, flags, NormalMove, + rf, ff, rf - 1, ff, closure); + } + /* and move sideways when across the river */ + for (s = -1; s <= 1; s += 2) { + if (rf < BOARD_HEIGHT>>1 && + ff + s >= 0 && ff + s < BOARD_WIDTH && + !BlackPiece(board[rf][ff+s]) ) { + callback(board, flags, NormalMove, + rf, ff, rf, ff+s, closure); + } + } + break; + } +#endif + if (rf > 0 && board[rf - 1][ff] == EmptySquare) { + callback(board, flags, + rf == 1 ? BlackPromotionQueen : NormalMove, + rf, ff, rf - 1, ff, closure); + } + if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare && + gameInfo.variant != VariantShatranj && /* [HGM] */ + board[BOARD_HEIGHT-4][ff] == EmptySquare) { + callback(board, flags, NormalMove, + rf, ff, BOARD_HEIGHT-4, ff, closure); + } + for (s = -1; s <= 1; s += 2) { + if (rf > 0 && ff + s >= 0 && ff + s < BOARD_WIDTH && + ((flags & F_KRIEGSPIEL_CAPTURE) || + WhitePiece(board[rf - 1][ff + s]))) { + callback(board, flags, + rf == 1 ? BlackPromotionQueen : NormalMove, + rf, ff, rf - 1, ff + s, closure); + } + if (rf == 3) { + if (ff + s >= 0 && ff + s < BOARD_WIDTH && + (epfile == ff + s || epfile == EP_UNKNOWN) && + board[3][ff + s] == WhitePawn && + board[2][ff + s] == EmptySquare) { + callback(board, flags, BlackCapturesEnPassant, + rf, ff, 2, ff + s, closure); + } + } + } + break; + + case WhiteKnight: + case BlackKnight: + mounted: + for (i = -1; i <= 1; i += 2) + for (j = -1; j <= 1; j += 2) + for (s = 1; s <= 2; s++) { + rt = rf + i*s; + ft = ff + j*(3-s); + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) continue; + if (SameColor(board[rf][ff], board[rt][ft])) continue; + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + } + break; +#ifdef FAIRY + case WhiteFairyMarshall: + case BlackFairyMarshall: + for (d = 0; d <= 1; d++) + for (s = -1; s <= 1; s += 2) { + m = 0; + for (i = 1;; i++) { + rt = rf + (i * s) * d; + ft = ff + (i * s) * (1 - d); + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break; + if (m == 0 && board[rt][ft] == EmptySquare) + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + if (m == 1 && board[rt][ft] != EmptySquare && + !SameColor(board[rf][ff], board[rt][ft]) ) + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + if (board[rt][ft] != EmptySquare && m++) break; + } + } + break; + + case WhiteFairyRook: + case BlackFairyRook: + for (d = 0; d <= 1; d++) + for (s = -1; s <= 1; s += 2) + rt = rf + s * d; + ft = ff + s * (1 - d); + if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) + && !SameColor(board[rf][ff], board[rt][ft])) + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + break; + + case WhiteFairyBishop: + case BlackFairyBishop: + /* [HGM] support Shatranj pieces */ + for (rs = -1; rs <= 1; rs += 2) + for (fs = -1; fs <= 1; fs += 2) { + rt = rf + 2 * rs; + ft = ff + 2 * fs; + if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) + && !SameColor(board[rf][ff], board[rt][ft])) + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + } + break; + + case WhiteCardinal: + case BlackCardinal: + m++; +#endif + case WhiteBishop: + case BlackBishop: + for (rs = -1; rs <= 1; rs += 2) + for (fs = -1; fs <= 1; fs += 2) + for (i = 1;; i++) { + rt = rf + (i * rs); + ft = ff + (i * fs); + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break; + if (SameColor(board[rf][ff], board[rt][ft])) break; + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + if (board[rt][ft] != EmptySquare) break; + } + if(m) goto mounted; + break; + +#ifdef FAIRY + case WhiteMarshall: + case BlackMarshall: + m++; +#endif + case WhiteRook: + case BlackRook: + for (d = 0; d <= 1; d++) + for (s = -1; s <= 1; s += 2) + for (i = 1;; i++) { + rt = rf + (i * s) * d; + ft = ff + (i * s) * (1 - d); + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break; + if (SameColor(board[rf][ff], board[rt][ft])) break; + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + if (board[rt][ft] != EmptySquare) break; + } + if(m) goto mounted; + break; + + case WhiteQueen: + case BlackQueen: + for (rs = -1; rs <= 1; rs++) + for (fs = -1; fs <= 1; fs++) { + if (rs == 0 && fs == 0) continue; + for (i = 1;; i++) { + rt = rf + (i * rs); + ft = ff + (i * fs); + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break; + if (SameColor(board[rf][ff], board[rt][ft])) break; + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + if (board[rt][ft] != EmptySquare) break; + } + } + break; + +#ifdef FAIRY + case WhiteFairyPawn: + case BlackFairyPawn: + /* [HGM] support Shatranj pieces */ + for (rs = -1; rs <= 1; rs += 2) + for (fs = -1; fs <= 1; fs += 2) { + rt = rf + rs; + ft = ff + fs; + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) break; + if (!SameColor(board[rf][ff], board[rt][ft])) + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + } + break; + + case WhiteFairyKing: + case BlackFairyKing: +#endif + case WhiteKing: + case BlackKing: + for (i = -1; i <= 1; i++) + for (j = -1; j <= 1; j++) { + if (i == 0 && j == 0) continue; + rt = rf + i; + ft = ff + j; + if (rt < 0 || rt >= BOARD_HEIGHT || ft < 0 || ft >= BOARD_WIDTH) continue; + if (SameColor(board[rf][ff], board[rt][ft])) continue; + callback(board, flags, NormalMove, + rf, ff, rt, ft, closure); + } + break; + } + } +} + + +typedef struct { + MoveCallback cb; + VOIDSTAR cl; +} GenLegalClosure; + +extern void GenLegalCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register GenLegalClosure *cl = (GenLegalClosure *) closure; + + if (!(flags & F_IGNORE_CHECK) && + CheckTest(board, flags, rf, ff, rt, ft, + kind == WhiteCapturesEnPassant || + kind == BlackCapturesEnPassant)) return; + if (flags & F_ATOMIC_CAPTURE) { + if (board[rt][ft] != EmptySquare || + kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) { + int r, f; + ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing; + if (board[rf][ff] == king) return; + for (r = rt-1; r <= rt+1; r++) { + for (f = ft-1; f <= ft+1; f++) { + if (r >= 0 && r < BOARD_HEIGHT && f >= 0 && f < BOARD_WIDTH && + board[r][f] == king) return; + } + } + } + } + cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl); +} + + +typedef struct { + int rf, ff, rt, ft; + ChessMove kind; +} LegalityTestClosure; + + +/* Like GenPseudoLegal, but (1) include castling moves, (2) unless + F_IGNORE_CHECK is set in the flags, omit moves that would leave the + king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit + moves that would destroy your own king. The CASTLE_OK flags are + true if castling is not yet ruled out by a move of the king or + rook. Return TRUE if the player on move is currently in check and + F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */ +int GenLegal(board, flags, epfile, castlingRights, callback, closure) + Board board; + int flags; + int epfile; + char castlingRights[]; + MoveCallback callback; + VOIDSTAR closure; +{ + GenLegalClosure cl; + int ff, ft; + int ignoreCheck = (flags & F_IGNORE_CHECK) != 0; + + cl.cb = callback; + cl.cl = closure; + GenPseudoLegal(board, flags, epfile, GenLegalCallback, (VOIDSTAR) &cl); + + if (!ignoreCheck && + CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE; + + /* Generate castling moves */ + for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) { + if ((flags & F_WHITE_ON_MOVE) && + (flags & F_WHITE_KCASTLE_OK) && + board[0][ff] == WhiteKing && + board[0][ff + 1] == EmptySquare && + board[0][ff + 2] == EmptySquare && + board[0][BOARD_WIDTH-3] == EmptySquare && + board[0][BOARD_WIDTH-2] == EmptySquare && + board[0][BOARD_WIDTH-1] == WhiteRook && + castlingRights[0] >= 0 && /* [HGM] check rights */ + ( castlingRights[2] == ff || castlingRights[6] == ff ) && + (ignoreCheck || + (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) && + !CheckTest(board, flags, 0, ff, 0, BOARD_WIDTH-3, FALSE) && + !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) { + + callback(board, flags, + ff==4 ? WhiteKingSideCastle : WhiteKingSideCastleWild, + 0, ff, 0, ff + ((BOARD_WIDTH+2)>>2), closure); + } + if ((flags & F_WHITE_ON_MOVE) && + (flags & F_WHITE_QCASTLE_OK) && + board[0][ff] == WhiteKing && + board[0][ff - 1] == EmptySquare && + board[0][ff - 2] == EmptySquare && + board[0][2] == EmptySquare && + board[0][1] == EmptySquare && + board[0][0] == WhiteRook && + castlingRights[1] >= 0 && /* [HGM] check rights */ + ( castlingRights[2] == ff || castlingRights[6] == ff ) && + (ignoreCheck || + (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) && + !CheckTest(board, flags, 0, ff, 0, 3, FALSE) && + !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) { + + callback(board, flags, + ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild, + 0, ff, 0, ff - ((BOARD_WIDTH+2)>>2), closure); + } + if (!(flags & F_WHITE_ON_MOVE) && + (flags & F_BLACK_KCASTLE_OK) && + board[BOARD_HEIGHT-1][ff] == BlackKing && + board[BOARD_HEIGHT-1][ff + 1] == EmptySquare && + board[BOARD_HEIGHT-1][ff + 2] == EmptySquare && + board[BOARD_HEIGHT-1][BOARD_WIDTH-3] == EmptySquare && + board[BOARD_HEIGHT-1][BOARD_WIDTH-2] == EmptySquare && + board[BOARD_HEIGHT-1][BOARD_WIDTH-1] == BlackRook && + castlingRights[3] >= 0 && /* [HGM] check rights */ + ( castlingRights[5] == ff || castlingRights[7] == ff ) && + (ignoreCheck || + (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) && + !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_WIDTH-3, FALSE) && + !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) { + + callback(board, flags, + ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild, + BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((BOARD_WIDTH+2)>>2), closure); + } + if (!(flags & F_WHITE_ON_MOVE) && + (flags & F_BLACK_QCASTLE_OK) && + board[BOARD_HEIGHT-1][ff] == BlackKing && + board[BOARD_HEIGHT-1][ff - 1] == EmptySquare && + board[BOARD_HEIGHT-1][ff - 2] == EmptySquare && + board[BOARD_HEIGHT-1][2] == EmptySquare && + board[BOARD_HEIGHT-1][1] == EmptySquare && + board[BOARD_HEIGHT-1][0] == BlackRook && + castlingRights[4] >= 0 && /* [HGM] check rights */ + ( castlingRights[5] == ff || castlingRights[7] == ff ) && + (ignoreCheck || + (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) && + !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, 3, FALSE) && + !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE)))) { + + callback(board, flags, + ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild, + BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((BOARD_WIDTH+2)>>2), closure); + } + } + + /* PUSH Fabien */ + + /* generate all potential FRC castling moves (KxR), ignoring flags */ + /* [HGM] Tord! Help requested! */ + + if ((flags & F_WHITE_ON_MOVE) != 0) { + + for (ff = 1; ff < BOARD_WIDTH-1; ff++) { + if (board[0][ff] == WhiteKing) { + for (ft = 0; ft < BOARD_WIDTH; ft++) { + if (board[0][ft] == WhiteRook) { + callback(board, flags, + (ft > ff) ? WhiteHSideCastleFR : WhiteASideCastleFR, + 0, ff, 0, ft, closure); + } + } + } + } + + } else { + + for (ff = 1; ff < BOARD_WIDTH-1; ff++) { + if (board[BOARD_HEIGHT-1][ff] == BlackKing) { + for (ft = 0; ft < BOARD_WIDTH; ft++) { + if (board[BOARD_HEIGHT-1][ft] == BlackRook) { + callback(board, flags, + (ft > ff) ? BlackHSideCastleFR : BlackASideCastleFR, + BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure); + } + } + } + } + } + + /* POP Fabien */ + + return FALSE; +} + + +typedef struct { + int rking, fking; + int check; +} CheckTestClosure; + + +extern void CheckTestCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + + +void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register CheckTestClosure *cl = (CheckTestClosure *) closure; + + if (rt == cl->rking && ft == cl->fking) cl->check++; +} + + +/* If the player on move were to move from (rf, ff) to (rt, ft), would + he leave himself in check? Or if rf == -1, is the player on move + in check now? enPassant must be TRUE if the indicated move is an + e.p. capture. The possibility of castling out of a check along the + back rank is not accounted for (i.e., we still return nonzero), as + this is illegal anyway. Return value is the number of times the + king is in check. */ +int CheckTest(board, flags, rf, ff, rt, ft, enPassant) + Board board; + int flags; + int rf, ff, rt, ft, enPassant; +{ + CheckTestClosure cl; + ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing; + ChessSquare captured = EmptySquare; + /* Suppress warnings on uninitialized variables */ + + if (rf >= 0) { + if (enPassant) { + captured = board[rf][ft]; + board[rf][ft] = EmptySquare; + } else { + captured = board[rt][ft]; + } + board[rt][ft] = board[rf][ff]; + board[rf][ff] = EmptySquare; + } + + /* For compatibility with ICS wild 9, we scan the board in the + order a1, a2, a3, ... b1, b2, ..., h8 to find the first king, + and we test only whether that one is in check. */ + cl.check = 0; + for (cl.fking = 0; cl.fking < BOARD_WIDTH; cl.fking++) + for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) { + if (board[cl.rking][cl.fking] == king) { + GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, -1, + CheckTestCallback, (VOIDSTAR) &cl); + goto undo_move; /* 2-level break */ + } + } + + undo_move: + + if (rf >= 0) { + board[rf][ff] = board[rt][ft]; + if (enPassant) { + board[rf][ft] = captured; + board[rt][ft] = EmptySquare; + } else { + board[rt][ft] = captured; + } + } + + return cl.check; +} + + +extern void LegalityTestCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register LegalityTestClosure *cl = (LegalityTestClosure *) closure; + + if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) + cl->kind = kind; +} + +ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, promoChar) + Board board; + int flags, epfile; + int rf, ff, rt, ft, promoChar; + char castlingRights[]; +{ + LegalityTestClosure cl; + + cl.rf = rf; + cl.ff = ff; + cl.rt = rt; + cl.ft = ft; + cl.kind = IllegalMove; + GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl); + if (promoChar != NULLCHAR && promoChar != 'x') { + if (cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen) { + cl.kind = + PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, promoChar); + } else { + cl.kind = IllegalMove; + } + } + return cl.kind; +} + +typedef struct { + int count; +} MateTestClosure; + +extern void MateTestCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register MateTestClosure *cl = (MateTestClosure *) closure; + + cl->count++; +} + +/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */ +int MateTest(board, flags, epfile, castlingRights) + Board board; + int flags, epfile; + char castlingRights[]; +{ + MateTestClosure cl; + int inCheck; + + cl.count = 0; + inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl); + if (cl.count > 0) { + return inCheck ? MT_CHECK : MT_NONE; + } else { + return inCheck ? MT_CHECKMATE : MT_STALEMATE; + } +} + + +extern void DisambiguateCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register DisambiguateClosure *cl = (DisambiguateClosure *) closure; + + if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]) && + (cl->rfIn == -1 || cl->rfIn == rf) && + (cl->ffIn == -1 || cl->ffIn == ff) && + (cl->rtIn == -1 || cl->rtIn == rt) && + (cl->ftIn == -1 || cl->ftIn == ft)) { + + cl->count++; + cl->piece = board[rf][ff]; + cl->rf = rf; + cl->ff = ff; + cl->rt = rt; + cl->ft = ft; + cl->kind = kind; + } +} + +void Disambiguate(board, flags, epfile, closure) + Board board; + int flags, epfile; + DisambiguateClosure *closure; +{ + int illegal = 0; + closure->count = 0; + closure->rf = closure->ff = closure->rt = closure->ft = 0; + closure->kind = ImpossibleMove; + GenLegal(board, flags, epfile, initialRights, DisambiguateCallback, (VOIDSTAR) closure); + if (closure->count == 0) { + /* See if it's an illegal move due to check */ + illegal = 1; + GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, DisambiguateCallback, + (VOIDSTAR) closure); + if (closure->count == 0) { + /* No, it's not even that */ + return; + } + } + if (closure->promoCharIn != NULLCHAR && closure->promoCharIn != 'x') { + if (closure->kind == WhitePromotionQueen + || closure->kind == BlackPromotionQueen) { + closure->kind = + PromoCharToMoveType((flags & F_WHITE_ON_MOVE) != 0, + closure->promoCharIn); + } else { + closure->kind = IllegalMove; + } + } + closure->promoChar = ToLower(PieceToChar(PromoPiece(closure->kind))); + if (closure->promoChar == 'x') closure->promoChar = NULLCHAR; + if (closure->count > 1) { + closure->kind = AmbiguousMove; + } + if (illegal) { + /* Note: If more than one illegal move matches, but no legal + moves, we return IllegalMove, not AmbiguousMove. Caller + can look at closure->count to detect this. + */ + closure->kind = IllegalMove; + } +} + + +typedef struct { + /* Input */ + ChessSquare piece; + int rf, ff, rt, ft; + /* Output */ + ChessMove kind; + int rank; + int file; + int either; +} CoordsToAlgebraicClosure; + +extern void CoordsToAlgebraicCallback P((Board board, int flags, + ChessMove kind, int rf, int ff, + int rt, int ft, VOIDSTAR closure)); + +void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ + register CoordsToAlgebraicClosure *cl = + (CoordsToAlgebraicClosure *) closure; + + if (rt == cl->rt && ft == cl->ft && + board[rf][ff] == cl->piece) { + if (rf == cl->rf) { + if (ff == cl->ff) { + cl->kind = kind; /* this is the move we want */ + } else { + cl->file++; /* need file to rule out this move */ + } + } else { + if (ff == cl->ff) { + cl->rank++; /* need rank to rule out this move */ + } else { + cl->either++; /* rank or file will rule out this move */ + } + } + } +} + +/* Convert coordinates to normal algebraic notation. + promoChar must be NULLCHAR or 'x' if not a promotion. +*/ +ChessMove CoordsToAlgebraic(board, flags, epfile, + rf, ff, rt, ft, promoChar, out) + Board board; + int flags, epfile; + int rf, ff, rt, ft; + int promoChar; + char out[MOVE_LEN]; +{ + ChessSquare piece; + ChessMove kind; + char *outp = out; + CoordsToAlgebraicClosure cl; + + if (rf == DROP_RANK) { + /* Bughouse piece drop */ + *outp++ = ToUpper(PieceToChar((ChessSquare) ff)); + *outp++ = '@'; + *outp++ = ft + 'a'; + if(rt+ONE <= '9') + *outp++ = rt + ONE; + else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + *outp = NULLCHAR; + return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop; + } + + if (promoChar == 'x') promoChar = NULLCHAR; + piece = board[rf][ff]; + switch (piece) { + case WhitePawn: + case BlackPawn: + kind = LegalityTest(board, flags, epfile, initialRights, rf, ff, rt, ft, promoChar); + if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { + /* Keep short notation if move is illegal only because it + leaves the player in check, but still return IllegalMove */ + kind = LegalityTest(board, flags|F_IGNORE_CHECK, epfile, initialRights, + rf, ff, rt, ft, promoChar); + if (kind == IllegalMove) break; + kind = IllegalMove; + } + /* Pawn move */ + *outp++ = ff + 'a'; + if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */ + /* Non-capture; use style "e5" */ + if(rt+ONE <= '9') + *outp++ = rt + ONE; + else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + } else { + /* Capture; use style "exd5" */ + if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare ) + *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */ + *outp++ = ft + 'a'; + if(rt+ONE <= '9') + *outp++ = rt + ONE; + else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + } + /* Use promotion suffix style "=Q" */ + if (promoChar != NULLCHAR && promoChar != 'x') { + *outp++ = '='; + *outp++ = ToUpper(promoChar); + } + *outp = NULLCHAR; + return kind; + + + case WhiteKing: + case BlackKing: + /* Fabien moved code: FRC castling first (if KxR), wild castling second */ + /* Code added by Tord: FRC castling. */ + if((piece == WhiteKing && board[rt][ft] == WhiteRook) || + (piece == BlackKing && board[rt][ft] == BlackRook)) { + if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O"); + return LegalityTest(board, flags, epfile, initialRights, + rf, ff, rt, ft, promoChar); + } + /* End of code added by Tord */ + /* Test for castling or ICS wild castling */ + /* Use style "O-O" (oh-oh) for PGN compatibility */ + else if (rf == rt && + rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) && + ((ff == BOARD_WIDTH>>1 && (ft == 2 || ft == BOARD_WIDTH-2)) || + (ff == (BOARD_WIDTH-1)>>1 && (ft == 1 || ft == BOARD_WIDTH-3)))) { + if(ft==1 || ft==BOARD_WIDTH-2) + strcpy(out, "O-O"); + else + strcpy(out, "O-O-O"); + + /* This notation is always unambiguous, unless there are + kings on both the d and e files, with "wild castling" + possible for the king on the d file and normal castling + possible for the other. ICS rules for wild 9 + effectively make castling illegal for either king in + this situation. So I am not going to worry about it; + I'll just generate an ambiguous O-O in this case. + */ + return LegalityTest(board, flags, epfile, initialRights, + rf, ff, rt, ft, promoChar); + } + + /* else fall through */ + default: + /* Piece move */ + cl.rf = rf; + cl.ff = ff; + cl.rt = rt; + cl.ft = ft; + cl.piece = piece; + cl.kind = IllegalMove; + cl.rank = cl.file = cl.either = 0; + GenLegal(board, flags, epfile, initialRights, + CoordsToAlgebraicCallback, (VOIDSTAR) &cl); + + if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { + /* Generate pretty moves for moving into check, but + still return IllegalMove. + */ + GenLegal(board, flags|F_IGNORE_CHECK, epfile, initialRights, + CoordsToAlgebraicCallback, (VOIDSTAR) &cl); + if (cl.kind == IllegalMove) break; + cl.kind = IllegalMove; + } + + /* Style is "Nf3" or "Nxf7" if this is unambiguous, + else "Ngf3" or "Ngxf7", + else "N1f3" or "N5xf7", + else "Ng1f3" or "Ng5xf7". + */ + *outp++ = ToUpper(PieceToChar(piece)); + + if (cl.file || (cl.either && !cl.rank)) { + *outp++ = ff + 'a'; + } + if (cl.rank) { + if(rf+ONE <= '9') + *outp++ = rf + ONE; + else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; } + } + + if(board[rt][ft] != EmptySquare) + *outp++ = 'x'; + + *outp++ = ft + 'a'; + if(rt+ONE <= '9') + *outp++ = rt + ONE; + else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + *outp = NULLCHAR; + return cl.kind; + +#ifdef FAIRY + /* [HGM] Always long notation for fairies, don't know how they move */ + case WhiteFairyRook: + case BlackFairyRook: + case WhiteFairyKnight: + case BlackFairyKnight: + case WhiteFairyQueen: + case BlackFairyQueen: +#endif + case EmptySquare: + /* Moving a nonexistent piece */ + break; + } + + /* Not a legal move, even ignoring check. + If there was a piece on the from square, + use style "Ng1g3" or "Ng1xe8"; + if there was a pawn or nothing (!), + use style "g1g3" or "g1xe8". Use "x" + if a piece was on the to square, even + a piece of the same color. + */ + outp = out; + if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) { + *outp++ = ToUpper(PieceToChar(piece)); + } + *outp++ = ff + 'a'; + if(rf+ONE <= '9') + *outp++ = rf + ONE; + else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; } + if (board[rt][ft] != EmptySquare) *outp++ = 'x'; + *outp++ = ft + 'a'; + if(rt+ONE <= '9') + *outp++ = rt + ONE; + else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + /* Use promotion suffix style "=Q" */ + if (promoChar != NULLCHAR && promoChar != 'x') { + *outp++ = '='; + *outp++ = ToUpper(promoChar); + } + *outp = NULLCHAR; + + return IllegalMove; +} + + diff --git a/moves.h b/moves.h index c0c6b75..764aef7 100644 --- a/moves.h +++ b/moves.h @@ -1,147 +1,156 @@ -/* - * moves.h - Move generation and checking - * $Id: moves.h,v 2.1 2003/10/27 19:21:00 mann Exp $ - * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-95 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. - * ------------------------------------------------------------------------ - */ - -extern ChessSquare PromoPiece P((ChessMove moveType)); -extern ChessMove PromoCharToMoveType P((int whiteOnMove, int promoChar)); -extern char PieceToChar P((ChessSquare p)); -extern ChessSquare CharToPiece P((int c)); - -extern void CopyBoard P((Board to, Board from)); -extern int CompareBoards P((Board board1, Board board2)); - -typedef void (*MoveCallback) P((Board board, int flags, ChessMove kind, - int rf, int ff, int rt, int ft, - VOIDSTAR closure)); - -/* Values for flags arguments */ -#define F_WHITE_ON_MOVE 1 -#define F_WHITE_KCASTLE_OK 2 -#define F_WHITE_QCASTLE_OK 4 -#define F_BLACK_KCASTLE_OK 8 -#define F_BLACK_QCASTLE_OK 16 -#define F_ALL_CASTLE_OK (F_WHITE_KCASTLE_OK | F_WHITE_QCASTLE_OK | \ - F_BLACK_KCASTLE_OK | F_BLACK_QCASTLE_OK) -#define F_IGNORE_CHECK 32 -#define F_KRIEGSPIEL_CAPTURE 64 /* pawns can try to capture invisible pieces */ -#define F_ATOMIC_CAPTURE 128 /* capturing piece explodes, destroying itself - and all non-pawns on adjacent squares; - destroying your own king is illegal */ - -/* Special epfile values */ -#define EP_NONE -1 -#define EP_UNKNOWN -2 - -/* Call callback once for each pseudo-legal move in the given - position, except castling moves. A move is pseudo-legal if it is - legal, or if it would be legal except that it leaves the king in - check. In the arguments, epfile is EP_NONE if the previous move - was not a double pawn push, or the file 0..7 if it was, or - EP_UNKNOWN if we don't know and want to allow all e.p. captures. - Promotion moves generated are to Queen only. -*/ -extern void GenPseudoLegal P((Board board, int flags, int epfile, - MoveCallback callback, VOIDSTAR closure)); - -/* Like GenPseudoLegal, but include castling moves and (unless - F_IGNORE_CHECK is set in the flags) omit moves that would leave the - king in check. The CASTLE_OK flags are true if castling is not yet - ruled out by a move of the king or rook. Return TRUE if the player - on move is currently in check and F_IGNORE_CHECK is not set. -*/ -extern int GenLegal P((Board board, int flags, int epfile, - MoveCallback callback, VOIDSTAR closure)); - -/* If the player on move were to move from (rf, ff) to (rt, ft), would - he leave himself in check? Or if rf == -1, is the player on move - in check now? enPassant must be TRUE if the indicated move is an - e.p. capture. The possibility of castling out of a check along the - back rank is not accounted for (i.e., we still return nonzero), as - this is illegal anyway. Return value is the number of times the - king is in check. */ -extern int CheckTest P((Board board, int flags, - int rf, int ff, int rt, int ft, int enPassant)); - -/* Is a move from (rf, ff) to (rt, ft) legal for the player whom the - flags say is on move? Other arguments as in GenPseudoLegal. - Returns the type of move made, taking promoChar into account. */ -extern ChessMove LegalityTest P((Board board, int flags, int epfile, - int rf, int ff, int rt, int ft, - int promoChar)); - -#define MT_NONE 0 -#define MT_CHECK 1 -#define MT_CHECKMATE 2 -#define MT_STALEMATE 3 - -/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */ -extern int MateTest P((Board board, int flags, int epfile)); - -typedef struct { - /* Input data */ - ChessSquare pieceIn; /* EmptySquare if unknown */ - int rfIn, ffIn, rtIn, ftIn; /* -1 if unknown */ - int promoCharIn; /* NULLCHAR if unknown */ - /* Output data for matched move */ - ChessMove kind; - ChessSquare piece; - int rf, ff, rt, ft; - int promoChar; /* 'q' if a promotion and promoCharIn was NULLCHAR */ - int count; /* Number of possibilities found */ -} DisambiguateClosure; - -/* Disambiguate a partially-known move */ -void Disambiguate P((Board board, int flags, int epfile, - DisambiguateClosure *closure)); - - -/* Convert coordinates to normal algebraic notation. - promoChar must be NULLCHAR or '.' if not a promotion. -*/ -ChessMove CoordsToAlgebraic P((Board board, int flags, int epfile, - int rf, int ff, int rt, int ft, - int promoChar, char out[MOVE_LEN])); +/* + * moves.h - Move generation and checking + * $Id: moves.h,v 2.1 2003/10/27 19:21:00 mann Exp $ + * + * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. + * Enhancements Copyright 1992-95 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. + * ------------------------------------------------------------------------ + */ + +extern ChessSquare PromoPiece P((ChessMove moveType)); +extern ChessMove PromoCharToMoveType P((int whiteOnMove, int promoChar)); +extern char PieceToChar P((ChessSquare p)); +extern ChessSquare CharToPiece P((int c)); + +extern void CopyBoard P((Board to, Board from)); +extern int CompareBoards P((Board board1, Board board2)); + +typedef void (*MoveCallback) P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +/* Values for flags arguments */ +#define F_WHITE_ON_MOVE 1 +#define F_WHITE_KCASTLE_OK 2 +#define F_WHITE_QCASTLE_OK 4 +#define F_BLACK_KCASTLE_OK 8 +#define F_BLACK_QCASTLE_OK 16 +#define F_ALL_CASTLE_OK (F_WHITE_KCASTLE_OK | F_WHITE_QCASTLE_OK | \ + F_BLACK_KCASTLE_OK | F_BLACK_QCASTLE_OK) +#define F_IGNORE_CHECK 32 +#define F_KRIEGSPIEL_CAPTURE 64 /* pawns can try to capture invisible pieces */ +#define F_ATOMIC_CAPTURE 128 /* capturing piece explodes, destroying itself + and all non-pawns on adjacent squares; + destroying your own king is illegal */ + +/* Special epfile values. [HGM] positive values are non-reversible moves! */ +#define EP_NONE (-4) /* [HGM] Tricky! order matters: */ +#define EP_UNKNOWN (-1) /* >= EP_UNKNOWN spils rep-draw */ +#define EP_CAPTURE (-2) /* <= EP_NONE is reversible move */ +#define EP_PAWN_MOVE (-3) +#define EP_REP_DRAW (-15) +#define EP_RULE_DRAW (-14) +#define EP_INSUF_DRAW (-13) +#define EP_DRAWS (-10) + +/* Call callback once for each pseudo-legal move in the given + position, except castling moves. A move is pseudo-legal if it is + legal, or if it would be legal except that it leaves the king in + check. In the arguments, epfile is EP_NONE if the previous move + was not a double pawn push, or the file 0..7 if it was, or + EP_UNKNOWN if we don't know and want to allow all e.p. captures. + Promotion moves generated are to Queen only. +*/ +extern void GenPseudoLegal P((Board board, int flags, int epfile, + MoveCallback callback, VOIDSTAR closure)); + +/* Like GenPseudoLegal, but include castling moves and (unless + F_IGNORE_CHECK is set in the flags) omit moves that would leave the + king in check. The CASTLE_OK flags are true if castling is not yet + ruled out by a move of the king or rook. Return TRUE if the player + on move is currently in check and F_IGNORE_CHECK is not set. +*/ +extern int GenLegal P((Board board, int flags, int epfile, + char castlingRights[], /* [HGM] */ + MoveCallback callback, VOIDSTAR closure)); + +/* If the player on move were to move from (rf, ff) to (rt, ft), would + he leave himself in check? Or if rf == -1, is the player on move + in check now? enPassant must be TRUE if the indicated move is an + e.p. capture. The possibility of castling out of a check along the + back rank is not accounted for (i.e., we still return nonzero), as + this is illegal anyway. Return value is the number of times the + king is in check. */ +extern int CheckTest P((Board board, int flags, + int rf, int ff, int rt, int ft, int enPassant)); + +/* Is a move from (rf, ff) to (rt, ft) legal for the player whom the + flags say is on move? Other arguments as in GenPseudoLegal. + Returns the type of move made, taking promoChar into account. */ +extern ChessMove LegalityTest P((Board board, int flags, int epfile, + char castlingRights[], /* [HGM] */ + int rf, int ff, int rt, int ft, + int promoChar)); + +#define MT_NONE 0 +#define MT_CHECK 1 +#define MT_CHECKMATE 2 +#define MT_STALEMATE 3 + +/* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */ +extern int MateTest P((Board board, int flags, int epfile, + char castlingRights[])); /* [HGM] */ + +typedef struct { + /* Input data */ + ChessSquare pieceIn; /* EmptySquare if unknown */ + int rfIn, ffIn, rtIn, ftIn; /* -1 if unknown */ + int promoCharIn; /* NULLCHAR if unknown */ + /* Output data for matched move */ + ChessMove kind; + ChessSquare piece; + int rf, ff, rt, ft; + int promoChar; /* 'q' if a promotion and promoCharIn was NULLCHAR */ + int count; /* Number of possibilities found */ +} DisambiguateClosure; + +/* Disambiguate a partially-known move */ +void Disambiguate P((Board board, int flags, int epfile, + DisambiguateClosure *closure)); + + +/* Convert coordinates to normal algebraic notation. + promoChar must be NULLCHAR or '.' if not a promotion. +*/ +ChessMove CoordsToAlgebraic P((Board board, int flags, int epfile, + int rf, int ff, int rt, int ft, + int promoChar, char out[MOVE_LEN])); diff --git a/parser.h b/parser.h index 434a971..7718604 100644 --- a/parser.h +++ b/parser.h @@ -1,59 +1,60 @@ -/* - * parser.h -- Interface to XBoard move parser - * $Id: parser.h,v 2.1 2003/10/27 19:21:00 mann Exp $ - * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-95 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. - * ------------------------------------------------------------------------ - */ - -extern void yynewfile P((FILE *f)); -extern void yynewstr P((char *s)); -extern int yylex P((void)); -extern ChessMove yylexstr P((int boardIndex, char *s)); -extern char currentMoveString[]; -extern int yyboardindex; -extern int yyskipmoves; /* If TRUE, all moves are reported as AmbiguousMove - instead of being disambiguated. */ -extern char *yy_text; /* Needed because yytext can be either a char[] - or a (non-constant) char* */ -extern int yyoffset P((void)); +/* + * parser.h -- Interface to XBoard move parser + * $Id: parser.h,v 2.1 2003/10/27 19:21:00 mann Exp $ + * + * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. + * Enhancements Copyright 1992-95 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. + * ------------------------------------------------------------------------ + */ + +extern void yynewfile P((FILE *f)); +extern void yynewstr P((char *s)); +extern int yylex P((void)); +extern ChessMove yylexstr P((int boardIndex, char *s)); +extern char currentMoveString[]; +extern int yyboardindex; +extern int yyskipmoves; /* If TRUE, all moves are reported as AmbiguousMove + instead of being disambiguated. */ +extern char *yy_text; /* Needed because yytext can be either a char[] + or a (non-constant) char* */ +extern int yyoffset P((void)); +extern char initialRights[BOARD_SIZE]; diff --git a/parser.l b/parser.l index 39264f5..e35de38 100644 --- a/parser.l +++ b/parser.l @@ -1,922 +1,997 @@ -%a 10000 -%o 10000 -%e 2000 -%k 2500 -%p 7000 -%n 1000 -%{ -/* - * parser.l -- lex parser of algebraic chess moves for XBoard - * $Id$ - * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-95 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. - * ------------------------------------------------------------------------ - */ - -/* This parser handles all forms of promotion. - * The parser resolves ambiguous moves by searching and check-testing. - * It also parses comments of the form [anything] or (anything). - */ - -#include "config.h" - -#define NO_CONSTRAINT -1 -#undef YYLMAX -#define YYLMAX 4096 -#define UNPUT_BUF_SIZE YYLMAX - -#ifdef FLEX_SCANNER -/* yytext is probably a char*, but could be a char[]. yy_text is set - in YY_DECL below, because if yytext is a char*, its value is not - constant. */ -char *yy_text; -#else /*!FLEX_SCANNER*/ -/* yytext is definitely a char[], so yy_text can be set here, statically. */ -char *yy_text = (char *) yytext; -#endif - -#ifdef FLEX_SCANNER -/* This is flex */ -#undef YY_INPUT -#define YY_INPUT(buf, result, max_size) my_yy_input(buf, &result, max_size) -#undef YY_DECL -#define YY_DECL \ - int _yylex YY_PROTO((void)); \ - int yylex YY_PROTO((void)) \ - { \ - int result = _yylex(); \ - yy_text = (char *) yytext; \ - return(result); \ - } \ - int _yylex YY_PROTO((void)) -#else -/* This is lex */ -#undef input -#undef output -#undef unput -#endif - -/* The includes must be here, below the #undef input */ - -#include - -#if STDC_HEADERS -# include -# include -#else /* not STDC_HEADERS */ -# if HAVE_STRING_H -# include -# else /* not HAVE_STRING_H */ -# include -# endif /* not HAVE_STRING_H */ -#endif /* not STDC_HEADERS */ - -#if HAVE_UNISTD_H -# include -#endif - -#if defined(_amigados) -# include -# if HAVE_FCNTL_H -# include /* isatty() prototype */ -# endif /* HAVE_FCNTL_H */ -#endif /* defined(_amigados) */ - -#include "common.h" -#include "backend.h" -#include "frontend.h" -#include "parser.h" -#include "moves.h" - -extern int PosFlags P((int)); - -extern Board boards[MAX_MOVES]; -int yyboardindex; -int yyskipmoves = FALSE; -char currentMoveString[YYLMAX]; -#ifndef FLEX_SCANNER -char unputBuffer[UNPUT_BUF_SIZE]; -int unputCount = 0; -#endif - -#ifdef FLEX_SCANNER -void my_yy_input P((char *buf, int *result, int max_size)); -#else /*!FLEX_SCANNER*/ -static int input P((void)); -static void output P((int ch)); -static void unput P((int ch)); -int yylook P((void)); -int yyback P((int *, int)); -#endif -#undef yywrap -int yywrap P((void)); -extern void CopyBoard P((Board to, Board from)); - -%} -%% - -[RrBbNnQqKkPp][/]?[a-h][1-8][xX:-]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)? { - /* - * Fully-qualified algebraic move, possibly with promotion - */ - int skip1 = 0, skip2 = 0; - ChessSquare piece; - ChessMove result; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove the / */ - if (yytext[1] == '/') skip1 = 1; - - /* remove the [xX:-] */ - if ((yytext[3+skip1] == 'x') || (yytext[3+skip1] == 'X') || - (yytext[3+skip1] == '-') || (yytext[3+skip1] == ':')) skip2 = 1; - - currentMoveString[0] = yytext[1+skip1]; - currentMoveString[1] = yytext[2+skip1]; - currentMoveString[2] = yytext[3+skip1+skip2]; - currentMoveString[3] = yytext[4+skip1+skip2]; - currentMoveString[4] = NULLCHAR; - - if (yyleng-skip1-skip2 > 5) { - if (yytext[yyleng-1] == ')') { - currentMoveString[4] = ToLower(yytext[yyleng-2]); - } else { - currentMoveString[4] = ToLower(yytext[yyleng-1]); - } - currentMoveString[5] = NULLCHAR; - } - - piece = boards[yyboardindex] - [currentMoveString[1] - '1'][currentMoveString[0] - 'a']; - if (ToLower(yytext[0]) != ToLower(PieceToChar(piece))) - return (int) IllegalMove; - - result = LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - currentMoveString[1] - '1', - currentMoveString[0] - 'a', - currentMoveString[3] - '1', - currentMoveString[2] - 'a', - currentMoveString[4]); - - if (currentMoveString[4] == NULLCHAR && - (result == WhitePromotionQueen || result == BlackPromotionQueen)) { - currentMoveString[4] = 'q'; - currentMoveString[5] = NULLCHAR; - } - - return (int) result; -} - -[a-h][1-8][xX:-]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)? { - /* - * Simple algebraic move, possibly with promotion - */ - int skip = 0; - ChessMove result; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove the [xX:-] */ - if ((yytext[2] == 'x') || (yytext[2] == 'X') || - (yytext[2] == '-') || (yytext[2] == ':')) skip = 1; - - currentMoveString[0] = yytext[0]; - currentMoveString[1] = yytext[1]; - currentMoveString[2] = yytext[2+skip]; - currentMoveString[3] = yytext[3+skip]; - currentMoveString[4] = NULLCHAR; - - if (yyleng-skip > 4) { - if (yytext[yyleng-1] == ')') { - currentMoveString[4] = ToLower(yytext[yyleng-2]); - } else { - currentMoveString[4] = ToLower(yytext[yyleng-1]); - } - currentMoveString[5] = NULLCHAR; - } - - result = LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - currentMoveString[1] - '1', - currentMoveString[0] - 'a', - currentMoveString[3] - '1', - currentMoveString[2] - 'a', - currentMoveString[4]); - - if (currentMoveString[4] == NULLCHAR && - (result == WhitePromotionQueen || result == BlackPromotionQueen)) { - currentMoveString[4] = 'q'; - currentMoveString[5] = NULLCHAR; - } - - return (int) result; -} - -[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)? { - /* - * Pawn move, possibly with promotion - */ - DisambiguateClosure cl; - int skip = 0; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove the =() */ - if (yytext[2] == '=') skip++; - if (yytext[2+skip] == '(') skip++; - - cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn; - cl.rfIn = -1; - cl.ffIn = yytext[0] - 'a'; - cl.rtIn = yytext[1] - '1'; - cl.ftIn = yytext[0] - 'a'; - cl.promoCharIn = yytext[2+skip]; - Disambiguate(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, &cl); - - currentMoveString[0] = cl.ff + 'a'; - currentMoveString[1] = cl.rf + '1'; - currentMoveString[2] = cl.ft + 'a'; - currentMoveString[3] = cl.rt + '1'; - currentMoveString[4] = cl.promoChar; - currentMoveString[5] = NULLCHAR; - - return (int) cl.kind; -} - - -(ab|bc|cd|de|ef|fg|gh|hg|gf|fe|ed|dc|cb|ba|([a-h][xX:-][a-h]))(=?\(?[RrBbNnQqKk]\)?)?(ep|"e.p.")? { - /* - * Pawn capture, possibly with promotion, possibly ambiguous - */ - DisambiguateClosure cl; - int skip1 = 0, skip2 = 0; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove trailing ep or e.p. (nonstandard PGN) */ - if (yytext[yyleng-1] == 'p') { - yyleng -= 2; - yytext[yyleng] = NULLCHAR; - } else if (yytext[yyleng-1] == '.') { - yyleng -= 4; - yytext[yyleng] = NULLCHAR; - } - - /* remove the [xX:-] and =() */ - if ((yytext[1] == 'x') || (yytext[1] == 'X') - || (yytext[1] == ':') || (yytext[1] == '-')) skip1 = 1; - if (yytext[2+skip1] == '=') skip2++; - if (yytext[2+skip1+skip2] == '(') skip2++; - - cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn; - cl.rfIn = -1; - cl.ffIn = yytext[0] - 'a'; - cl.rtIn = -1; - cl.ftIn = yytext[1+skip1] - 'a'; - cl.promoCharIn = yytext[2+skip1+skip2]; - Disambiguate(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, &cl); - - currentMoveString[0] = cl.ff + 'a'; - currentMoveString[1] = cl.rf + '1'; - currentMoveString[2] = cl.ft + 'a'; - currentMoveString[3] = cl.rt + '1'; - currentMoveString[4] = cl.promoChar; - currentMoveString[5] = NULLCHAR; - - return (int) cl.kind; -} - -[a-h][xX:]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)?(ep|"e.p.")? { - /* - * unambiguously abbreviated Pawn capture, possibly with promotion - */ - int skip = 0; - ChessMove result; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove trailing ep or e.p. (nonstandard PGN) */ - if (yytext[yyleng-1] == 'p') { - yyleng -= 2; - yytext[yyleng] = NULLCHAR; - } else if (yytext[yyleng-1] == '.') { - yyleng -= 4; - yytext[yyleng] = NULLCHAR; - } - - /* remove the [xX:-] */ - if ((yytext[1] == 'x') || (yytext[1] == 'X') - || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1; - - currentMoveString[0] = yytext[0]; - currentMoveString[2] = yytext[1+skip]; - currentMoveString[3] = yytext[2+skip]; - if (WhiteOnMove(yyboardindex)) { - if (yytext[2+skip] == '1') return (int) ImpossibleMove; - currentMoveString[1] = yytext[2+skip] - 1; - } else { - if (yytext[2+skip] == '8') return (int) ImpossibleMove; - currentMoveString[1] = yytext[2+skip] + 1; - } - if (yyleng-skip > 3) { - if (yytext[yyleng-1] == ')') - currentMoveString[4] = ToLower(yytext[yyleng-2]); - else - currentMoveString[4] = ToLower(yytext[yyleng-1]); - currentMoveString[5] = NULLCHAR; - } else { - currentMoveString[4] = NULLCHAR; - } - - result = LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - currentMoveString[1] - '1', - currentMoveString[0] - 'a', - currentMoveString[3] - '1', - currentMoveString[2] - 'a', - currentMoveString[4]); - - if (currentMoveString[4] == NULLCHAR && - (result == WhitePromotionQueen || result == BlackPromotionQueen)) { - currentMoveString[4] = 'q'; - currentMoveString[5] = NULLCHAR; - } - - if (result != IllegalMove) return (int) result; - - /* Special case: improperly written en passant capture */ - if (WhiteOnMove(yyboardindex)) { - if (currentMoveString[3] == '5') { - currentMoveString[1] = '5'; - currentMoveString[3] = '6'; - } else { - return (int) IllegalMove; - } - } else { - if (currentMoveString[3] == '4') { - currentMoveString[1] = '4'; - currentMoveString[3] = '3'; - } else { - return (int) IllegalMove; - } - } - - result = LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - currentMoveString[1] - '1', - currentMoveString[0] - 'a', - currentMoveString[3] - '1', - currentMoveString[2] - 'a', - currentMoveString[4]); - - if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant) - return (int) result; - else - return (int) IllegalMove; -} - -[RrBbNnQqKk][xX:-]?[a-h][1-8] { - /* - * piece move, possibly ambiguous - */ - DisambiguateClosure cl; - int skip = 0; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove the [xX:-] */ - if ((yytext[1] == 'x') || (yytext[1] == 'X') - || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1; - - if (WhiteOnMove(yyboardindex)) { - cl.pieceIn = CharToPiece(ToUpper(yytext[0])); - } else { - cl.pieceIn = CharToPiece(ToLower(yytext[0])); - } - cl.rfIn = -1; - cl.ffIn = -1; - cl.rtIn = yytext[2+skip] - '1'; - cl.ftIn = yytext[1+skip] - 'a'; - cl.promoCharIn = NULLCHAR; - Disambiguate(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, &cl); - - currentMoveString[0] = cl.ff + 'a'; - currentMoveString[1] = cl.rf + '1'; - currentMoveString[2] = cl.ft + 'a'; - currentMoveString[3] = cl.rt + '1'; - currentMoveString[4] = cl.promoChar; - currentMoveString[5] = NULLCHAR; - - return (int) cl.kind; -} - -[RrBbNnQqKk][a-h1-8][xX:-]?[a-h][1-8] { - /* - * piece move with rank or file disambiguator - */ - DisambiguateClosure cl; - int skip = 0; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - /* remove the [xX:-] */ - if ((yytext[2] == 'x') || (yytext[2] == 'X') - || (yytext[2] == ':') || (yytext[2] == '-')) skip = 1; - - if (WhiteOnMove(yyboardindex)) { - cl.pieceIn = CharToPiece(ToUpper(yytext[0])); - } else { - cl.pieceIn = CharToPiece(ToLower(yytext[0])); - } - if (isalpha(yytext[1])) { - cl.rfIn = -1; - cl.ffIn = yytext[1] - 'a'; - } else { - cl.rfIn = yytext[1] - '1'; - cl.ffIn = -1; - } - cl.rtIn = yytext[3+skip] - '1'; - cl.ftIn = yytext[2+skip] - 'a'; - cl.promoCharIn = NULLCHAR; - Disambiguate(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, &cl); - - currentMoveString[0] = cl.ff + 'a'; - currentMoveString[1] = cl.rf + '1'; - currentMoveString[2] = cl.ft + 'a'; - currentMoveString[3] = cl.rt + '1'; - currentMoveString[4] = cl.promoChar; - currentMoveString[5] = NULLCHAR; - - return (int) cl.kind; -} - -000|0-0-0|ooo|OOO|o-o-o|O-O-O { - int rf, ff, rt, ft; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - if (WhiteOnMove(yyboardindex)) { - if (boards[yyboardindex][0][3] == WhiteKing) { - /* ICS wild castling */ - strcpy(currentMoveString, "d1f1"); - rf = 0; - ff = 3; - rt = 0; - ft = 5; - } else { - strcpy(currentMoveString, "e1c1"); - rf = 0; - ff = 4; - rt = 0; - ft = 2; - } - } else{ - if (boards[yyboardindex][7][3] == BlackKing) { - /* ICS wild castling */ - strcpy(currentMoveString, "d8f8"); - rf = 7; - ff = 3; - rt = 7; - ft = 5; - } else { - strcpy(currentMoveString, "e8c8"); - rf = 7; - ff = 4; - rt = 7; - ft = 2; - } - } - return (int) LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - rf, ff, rt, ft, NULLCHAR); -} - -00|0-0|oo|OO|o-o|O-O { - int rf, ff, rt, ft; - - if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ - - if (WhiteOnMove(yyboardindex)) { - if (boards[yyboardindex][0][3] == WhiteKing) { - /* ICS wild castling */ - strcpy(currentMoveString, "d1b1"); - rf = 0; - ff = 3; - rt = 0; - ft = 1; - } else { - strcpy(currentMoveString, "e1g1"); - rf = 0; - ff = 4; - rt = 0; - ft = 6; - } - } else { - if (boards[yyboardindex][7][3] == BlackKing) { - /* ICS wild castling */ - strcpy(currentMoveString, "d8b8"); - rf = 7; - ff = 3; - rt = 7; - ft = 1; - } else { - strcpy(currentMoveString, "e8g8"); - rf = 7; - ff = 4; - rt = 7; - ft = 6; - } - } - return (int) LegalityTest(boards[yyboardindex], - PosFlags(yyboardindex), EP_UNKNOWN, - rf, ff, rt, ft, NULLCHAR); -} - -[PpNnBbRrQq]@[a-h][1-8] { - /* Bughouse piece drop. No legality checking for now. */ - currentMoveString[1] = '@'; - currentMoveString[2] = yytext[2]; - currentMoveString[3] = yytext[3]; - currentMoveString[4] = NULLCHAR; - if (WhiteOnMove(yyboardindex)) { - currentMoveString[0] = ToUpper(yytext[0]); - return (int) WhiteDrop; - } else { - currentMoveString[0] = ToLower(yytext[0]); - return (int) BlackDrop; - } -} - -[Rr]esign(s|ed)? { - if (WhiteOnMove(yyboardindex)) - return (int) BlackWins; - else - return (int) WhiteWins; -} - -(([Ww](hite)?)|([Bb](lack)?))" "(([Rr]esign)|([Ff]orfeit))(s|ed)? { - return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins); -} - -(([Ww](hite)?)|([Bb](lack)?))" "[Dd]isconnect(s|ed) { - return (int) GameUnfinished; -} - -[Ss]talemate { - return (int) GameIsDrawn; -} - -"+-+" { - return (int) GameIsDrawn; -} - -([Cc]heck)?[Mm]ate { - if (WhiteOnMove(yyboardindex)) - return (int) BlackWins; - else - return (int) WhiteWins; -} - -"++" { - if (WhiteOnMove(yyboardindex)) - return (int) BlackWins; - else - return (int) WhiteWins; -} - -[Dd]raw(n)?(" "by)?(" "[Rr]epetition)|(" "[Aa]gree(d|ment)) { - return (int) GameIsDrawn; -} - -[Dd]raw(n)?(" (".*")")? { - return (int) GameIsDrawn; -} - -(([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ww][io]n(s)?.*) { - return (int) (ToUpper(yytext[0]) == 'W' ? WhiteWins : BlackWins); -} - -(([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ll]os[tes]+.*) { - return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins); -} - -("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { - return (int) WhiteWins; -} - -("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { - return (int) BlackWins; -} - -("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? { - return (int) GameIsDrawn; -} - -("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? { - return (int) GameUnfinished; -} - -[1-9][0-9]*/"."?[ \t\n]*[a-hNnPpRrBQqKkOo] { - /* move numbers */ - if ((yyleng == 1) && (yytext[0] == '1')) - return (int) MoveNumberOne; -} - -\([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} { - /* elapsed time indication, e.g. (0:12) or {10:21.071} */ - return (int) ElapsedTime; -} - -"[--"[^\]]*"--]" { - /* position diagram enclosed in [-- --] */ - return (int) PositionDiagram; -} - -^"{--------------"\n[^\}]*\n"--------------}"$ { - /* position diagram enclosed in {-- --} */ - return (int) PositionDiagram; -} - -\[[ \t\n]*[A-Za-z0-9][A-Za-z0-9_+#=-]*[ \t\n]*\"[^"]*\"[ \t\n]*\] { - return (int) PGNTag; -} - -[Gg](nu|NU)" "?[Cc](hess|HESS).*[Gg](ame|AME) { - return (int) GNUChessGame; -} - -^[#;%]" "[^ ]*(" game file"|" position file").*$ { - return (int) XBoardGame; -} - -\$[0-9]+ { /* numeric annotation glyph */ - return (int) NAG; -} - -\{[^\}]*\} { /* anything in {} */ - return (int) Comment; -} - -;.*$ { /* ; to end of line */ - return (int) Comment; -} - -\[[^\]]*\] { /* anything in [] */ - return (int) Comment; -} - -\([^()]*(\([^()]*\)[^()]*)+[^()]*\) { /* nested () */ - return (int) Comment; -} - -\([^)][^)]+\) { /* >=2 chars in () */ - return (int) Comment; -} - -^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)* { - /* Skip mail headers */ -} - -[a-zA-Z0-9'-]+ { - /* Skip random words */ -} - -.|\n { - /* Skip everything else */ -} - -%% - - -static char *StringToLex; - -#ifndef FLEX_SCANNER -static FILE *lexFP; - -static int input() -{ - int ret; - - if (StringToLex != NULL) { - ret = *StringToLex; - if (ret == NULLCHAR) - ret = EOF; - else - StringToLex++; - } else if (unputCount > 0) { - ret = unputBuffer[--unputCount]; - } else { - ret = fgetc(lexFP); - } - - if (ret == EOF) - return 0; - else - return ret; -} - -/* - * Return offset of next pattern within current file - */ -int yyoffset() -{ - int offset = ftell(lexFP) - unputCount; - - if (offset < 0) { - offset = 0; - } - return(offset); -} - -static void output(ch) - int ch; -{ - fprintf(stderr, "PARSER BUG: unmatched character '%c' (0%o)\n", - ch, ch); -} - -static void unput(ch) - int ch; -{ - if (ch == 0) return; - if (StringToLex != NULL) { - StringToLex--; - } else { - if (unputCount >= UNPUT_BUF_SIZE) - fprintf(stderr, "PARSER BUG: unput buffer overflow '%c' (0%o)\n", - ch, ch); - unputBuffer[unputCount++] = ch; - } -} - -/* Get ready to lex from a new file. Kludge below sticks - an artificial newline at the front of the file, which the - above grammar ignores, but which makes ^ at start of pattern - match at the real start of the file. -*/ -void yynewfile(f) - FILE *f; -{ - lexFP = f; - StringToLex = NULL; - unputCount = 0; - unput('\n'); /* kludge */ -} - -/* Get ready to lex from a string. ^ at start of pattern WON'T - match at the start of the string! -*/ -void yynewstr(s) - char *s; -{ - lexFP = NULL; - StringToLex = s; - unputCount = 0; -} -#endif /*!FLEX_SCANNER*/ - -#ifdef FLEX_SCANNER -void my_yy_input(buf, result, max_size) - char *buf; - int *result; - int max_size; -{ - int count; - - if (StringToLex != NULL) { - count = 0; - while (*StringToLex != NULLCHAR) { - *buf++ = *StringToLex++; - count++; - } - *result = count; - return; - } else { - count = fread(buf, 1, max_size, yyin); - if (count == 0) { - *result = YY_NULL; - } else { - *result = count; - } - return; - } -} - -static YY_BUFFER_STATE my_file_buffer = NULL; - -/* - Return offset of next pattern in the current file. -*/ -int yyoffset() -{ - int pos = yy_c_buf_p - yy_current_buffer->yy_ch_buf; - - return(ftell(yy_current_buffer->yy_input_file) - - yy_n_chars + pos); -} - - -void yynewstr(s) - char *s; -{ - if (my_file_buffer != NULL) - yy_delete_buffer(my_file_buffer); - StringToLex = s; - my_file_buffer = yy_create_buffer(stdin, YY_BUF_SIZE); - yy_switch_to_buffer(my_file_buffer); -} - -void yynewfile(f) - FILE *f; -{ - if (my_file_buffer != NULL) - yy_delete_buffer(my_file_buffer); - StringToLex = NULL; - my_file_buffer = yy_create_buffer(f, YY_BUF_SIZE); - yy_switch_to_buffer(my_file_buffer); -} -#endif /*FLEX_SCANNER*/ - -int yywrap() -{ - return TRUE; -} - -/* Parse a move from the given string s */ -/* ^ at start of pattern WON'T work here unless using flex */ -ChessMove yylexstr(boardIndex, s) - int boardIndex; - char *s; -{ - ChessMove ret; - char *oldStringToLex; -#ifdef FLEX_SCANNER - YY_BUFFER_STATE buffer, oldBuffer; -#endif - - yyboardindex = boardIndex; - oldStringToLex = StringToLex; - StringToLex = s; -#ifdef FLEX_SCANNER - buffer = yy_create_buffer(stdin, YY_BUF_SIZE); - oldBuffer = YY_CURRENT_BUFFER; - yy_switch_to_buffer(buffer); -#endif /*FLEX_SCANNER*/ - - ret = (ChessMove) yylex(); - -#ifdef FLEX_SCANNER - if (oldBuffer != NULL) - yy_switch_to_buffer(oldBuffer); - yy_delete_buffer(buffer); -#endif /*FLEX_SCANNER*/ - StringToLex = oldStringToLex; - - return ret; -} +%a 10000 +%o 10000 +%e 2000 +%k 2500 +%p 7000 +%n 1000 +%{ +/* + * parser.l -- lex parser of algebraic chess moves for XBoard + * $Id: parser.l,v 2.1 2003/10/27 19:21:00 mann Exp $ + * + * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. + * Enhancements Copyright 1992-95 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. + * ------------------------------------------------------------------------ + */ + +/* This parser handles all forms of promotion. + * The parser resolves ambiguous moves by searching and check-testing. + * It also parses comments of the form [anything] or (anything). + */ + +#include "config.h" + +#define NO_CONSTRAINT -1 +#undef YYLMAX +#define YYLMAX 4096 +#define UNPUT_BUF_SIZE YYLMAX + +#ifdef FLEX_SCANNER +/* yytext is probably a char*, but could be a char[]. yy_text is set + in YY_DECL below, because if yytext is a char*, its value is not + constant. */ +char *yy_text; +#else /*!FLEX_SCANNER*/ +/* yytext is definitely a char[], so yy_text can be set here, statically. */ +char *yy_text = (char *) yytext; +#endif + +#ifdef FLEX_SCANNER +/* This is flex */ +#undef YY_INPUT +#define YY_INPUT(buf, result, max_size) my_yy_input(buf, &result, max_size) +#undef YY_DECL +#define YY_DECL \ + int _yylex YY_PROTO((void)); \ + int yylex YY_PROTO((void)) \ + { \ + int result = _yylex(); \ + yy_text = (char *) yytext; \ + return(result); \ + } \ + int _yylex YY_PROTO((void)) +#else +/* This is lex */ +#undef input +#undef output +#undef unput +#endif + +/* The includes must be here, below the #undef input */ + +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#if defined(_amigados) +# include +# if HAVE_FCNTL_H +# include /* isatty() prototype */ +# endif /* HAVE_FCNTL_H */ +#endif /* defined(_amigados) */ + +#include "common.h" +#include "backend.h" +#include "frontend.h" +#include "parser.h" +#include "moves.h" + +extern int PosFlags P((int)); + +extern Board boards[MAX_MOVES]; +int yyboardindex; +int yyskipmoves = FALSE; +char currentMoveString[YYLMAX]; +#ifndef FLEX_SCANNER +char unputBuffer[UNPUT_BUF_SIZE]; +int unputCount = 0; +#endif + +#ifdef FLEX_SCANNER +void my_yy_input P((char *buf, int *result, int max_size)); +#else /*!FLEX_SCANNER*/ +static int input P((void)); +static void output P((int ch)); +static void unput P((int ch)); +int yylook P((void)); +int yyback P((int *, int)); +#endif +#undef yywrap +int yywrap P((void)); +extern void CopyBoard P((Board to, Board from)); + +%} +%% + +[RrBbNnQqKkPpACDEFGHMWO][/]?[a-l][0-9][xX:-]?[a-l][0-9](=?\(?[RrBbNnQqKkAaCc]\)?)? { + /* + * Fully-qualified algebraic move, possibly with promotion + * [HGM] Bigger-than-8x8 boards must rely on long algebraic formats + * where I allowed piece types A & C (also as promotions) + * files a-l and ranks 0-9 + */ + int skip1 = 0, skip2 = 0; + ChessSquare piece; + ChessMove result; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove the / */ + if (yytext[1] == '/') skip1 = 1; + + /* remove the [xX:-] */ + if ((yytext[3+skip1] == 'x') || (yytext[3+skip1] == 'X') || + (yytext[3+skip1] == '-') || (yytext[3+skip1] == ':')) skip2 = 1; + + currentMoveString[0] = yytext[1+skip1]; + currentMoveString[1] = yytext[2+skip1]; + currentMoveString[2] = yytext[3+skip1+skip2]; + currentMoveString[3] = yytext[4+skip1+skip2]; + currentMoveString[4] = NULLCHAR; + + if (yyleng-skip1-skip2 > 5) { + if (yytext[yyleng-1] == ')') { + currentMoveString[4] = ToLower(yytext[yyleng-2]); + } else { + currentMoveString[4] = ToLower(yytext[yyleng-1]); + } + currentMoveString[5] = NULLCHAR; + } + + /* [HGM] do not allow values beyond board size */ + if(currentMoveString[1] - ONE >= BOARD_HEIGHT || + currentMoveString[0] - 'a' >= BOARD_WIDTH || + currentMoveString[3] - ONE >= BOARD_HEIGHT || + currentMoveString[2] - 'a' >= BOARD_WIDTH ) + return 0; + + piece = boards[yyboardindex] + [currentMoveString[1] - ONE][currentMoveString[0] - 'a']; + if (ToLower(yytext[0]) != ToLower(PieceToChar(piece))) + return (int) IllegalMove; + + result = LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + currentMoveString[1] - ONE, + currentMoveString[0] - 'a', + currentMoveString[3] - ONE, + currentMoveString[2] - 'a', + currentMoveString[4]); + + if (currentMoveString[4] == NULLCHAR && + (result == WhitePromotionQueen || result == BlackPromotionQueen)) { + currentMoveString[4] = 'q'; + currentMoveString[5] = NULLCHAR; + } + + return (int) result; +} + +[a-l][0-9][xX:-]?[a-l][0-9](=?\(?[RrBbNnQqKkAaCc]\)?)? { + /* + * Simple algebraic move, possibly with promotion + * [HGM] Bigger-than-8x8 boards must rely on this format + * where I allowed piece types A & C (also as promotions) + * files a-l and ranks 0-9 + */ + int skip = 0; + ChessMove result; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove the [xX:-] */ + if ((yytext[2] == 'x') || (yytext[2] == 'X') || + (yytext[2] == '-') || (yytext[2] == ':')) skip = 1; + + currentMoveString[0] = yytext[0]; + currentMoveString[1] = yytext[1]; + currentMoveString[2] = yytext[2+skip]; + currentMoveString[3] = yytext[3+skip]; + currentMoveString[4] = NULLCHAR; + + if (yyleng-skip > 4) { + if (yytext[yyleng-1] == ')') { + currentMoveString[4] = ToLower(yytext[yyleng-2]); + } else { + currentMoveString[4] = ToLower(yytext[yyleng-1]); + } + currentMoveString[5] = NULLCHAR; + } + + /* [HGM] do not allow values beyond board size */ + if(currentMoveString[1] - ONE >= BOARD_HEIGHT || + currentMoveString[0] - 'a' >= BOARD_WIDTH || + currentMoveString[3] - ONE >= BOARD_HEIGHT || + currentMoveString[2] - 'a' >= BOARD_WIDTH ) + return 0; + + result = LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + currentMoveString[1] - ONE, + currentMoveString[0] - 'a', + currentMoveString[3] - ONE, + currentMoveString[2] - 'a', + currentMoveString[4]); + + if (currentMoveString[4] == NULLCHAR && + (result == WhitePromotionQueen || result == BlackPromotionQueen)) { + currentMoveString[4] = 'q'; + currentMoveString[5] = NULLCHAR; + } + + return (int) result; +} + +[a-l][0-9](=?\(?[RrBbNnQqKkAaCc]\)?)? { + /* + * Pawn move, possibly with promotion + */ + DisambiguateClosure cl; + int skip = 0; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove the =() */ + if (yytext[2] == '=') skip++; + if (yytext[2+skip] == '(') skip++; + + cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn; + cl.rfIn = -1; + cl.ffIn = yytext[0] - 'a'; + cl.rtIn = yytext[1] - ONE; + cl.ftIn = yytext[0] - 'a'; + cl.promoCharIn = yytext[2+skip]; + + /* [HGM] do not allow values beyond board size */ + if(cl.rtIn >= BOARD_HEIGHT || + cl.ffIn >= BOARD_WIDTH || + cl.ftIn >= BOARD_WIDTH ) + return 0; + + + Disambiguate(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, &cl); + + currentMoveString[0] = cl.ff + 'a'; + currentMoveString[1] = cl.rf + ONE; + currentMoveString[2] = cl.ft + 'a'; + currentMoveString[3] = cl.rt + ONE; + currentMoveString[4] = cl.promoChar; + currentMoveString[5] = NULLCHAR; + + return (int) cl.kind; +} + + +(ab|bc|cd|de|ef|fg|gh|hi|ij|jk|kl|lk|kj|ji|ih|hg|gf|fe|ed|dc|cb|ba|aa|bb|cc|dd|ee|ff|gg|hh|ii|jj|kk|ll|([a-l][xX:-][a-l]))(=?\(?[RrBbNnQqKkAaCc]\)?)?(ep|"e.p.")? { + /* + * Pawn capture, possibly with promotion, possibly ambiguous + */ + DisambiguateClosure cl; + int skip1 = 0, skip2 = 0; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove trailing ep or e.p. (nonstandard PGN) */ + if (yytext[yyleng-1] == 'p') { + yyleng -= 2; + yytext[yyleng] = NULLCHAR; + } else if (yytext[yyleng-1] == '.') { + yyleng -= 4; + yytext[yyleng] = NULLCHAR; + } + + /* remove the [xX:-] and =() */ + if ((yytext[1] == 'x') || (yytext[1] == 'X') + || (yytext[1] == ':') || (yytext[1] == '-')) skip1 = 1; + if (yytext[2+skip1] == '=') skip2++; + if (yytext[2+skip1+skip2] == '(') skip2++; + + cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn; + cl.rfIn = -1; + cl.ffIn = yytext[0] - 'a'; + cl.rtIn = -1; + cl.ftIn = yytext[1+skip1] - 'a'; + cl.promoCharIn = yytext[2+skip1+skip2]; + + /* [HGM] do not allow values beyond board size */ + if(cl.ffIn >= BOARD_WIDTH || + cl.ftIn >= BOARD_WIDTH ) + return 0; + + Disambiguate(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, &cl); + + currentMoveString[0] = cl.ff + 'a'; + currentMoveString[1] = cl.rf + ONE; + currentMoveString[2] = cl.ft + 'a'; + currentMoveString[3] = cl.rt + ONE; + currentMoveString[4] = cl.promoChar; + currentMoveString[5] = NULLCHAR; + + return (int) cl.kind; +} + +[a-l][xX:]?[a-l][0-9](=?\(?[RrBbNnQqKkAaCc]\)?)?(ep|"e.p.")? { + /* + * unambiguously abbreviated Pawn capture, possibly with promotion + */ + int skip = 0; + ChessMove result; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove trailing ep or e.p. (nonstandard PGN) */ + if (yytext[yyleng-1] == 'p') { + yyleng -= 2; + yytext[yyleng] = NULLCHAR; + } else if (yytext[yyleng-1] == '.') { + yyleng -= 4; + yytext[yyleng] = NULLCHAR; + } + + /* remove the [xX:-] */ + if ((yytext[1] == 'x') || (yytext[1] == 'X') + || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1; + + currentMoveString[0] = yytext[0]; + currentMoveString[2] = yytext[1+skip]; + currentMoveString[3] = yytext[2+skip]; + + /* [HGM] do not allow values beyond board size */ + if(currentMoveString[0] - 'a' >= BOARD_WIDTH || + currentMoveString[3] - ONE >= BOARD_HEIGHT || + currentMoveString[2] - 'a' >= BOARD_WIDTH ) + return 0; + + if (gameInfo.variant == VariantXiangqi && /* [HGM] In Xiangqi rank stays same */ + currentMoveString[0] != currentMoveString[2] ) { + if (yytext[2+skip] == ONE) return (int) ImpossibleMove; + currentMoveString[1] = yytext[2+skip]; + } else + if (WhiteOnMove(yyboardindex)) { + if (yytext[2+skip] == ONE) return (int) ImpossibleMove; + currentMoveString[1] = yytext[2+skip] - 1; + } else { + currentMoveString[1] = currentMoveString[3] + 1; + if (currentMoveString[3] == ONE+BOARD_HEIGHT-1) return (int) ImpossibleMove; + } + if (yyleng-skip > 3) { + if (yytext[yyleng-1] == ')') + currentMoveString[4] = ToLower(yytext[yyleng-2]); + else + currentMoveString[4] = ToLower(yytext[yyleng-1]); + currentMoveString[5] = NULLCHAR; + } else { + currentMoveString[4] = NULLCHAR; + } + + result = LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + currentMoveString[1] - ONE, + currentMoveString[0] - 'a', + currentMoveString[3] - ONE, + currentMoveString[2] - 'a', + currentMoveString[4]); + + if (currentMoveString[4] == NULLCHAR && + (result == WhitePromotionQueen || result == BlackPromotionQueen)) { + currentMoveString[4] = 'q'; + currentMoveString[5] = NULLCHAR; + } + + if (result != IllegalMove) return (int) result; + + /* Special case: improperly written en passant capture */ + if (WhiteOnMove(yyboardindex)) { + if (currentMoveString[3] == '5') { + currentMoveString[1] = '5'; + currentMoveString[3] = '6'; + } else { + return (int) IllegalMove; + } + } else { + if (currentMoveString[3] == '4') { + currentMoveString[1] = '4'; + currentMoveString[3] = '3'; + } else { + return (int) IllegalMove; + } + } + + result = LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + currentMoveString[1] - ONE, + currentMoveString[0] - 'a', + currentMoveString[3] - ONE, + currentMoveString[2] - 'a', + currentMoveString[4]); + + if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant) + return (int) result; + else + return (int) IllegalMove; +} + +[RrBbNnQqKkACDEFGHMWO][xX:-]?[a-l][0-9] { + /* + * piece move, possibly ambiguous + */ + DisambiguateClosure cl; + int skip = 0; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove the [xX:-] */ + if ((yytext[1] == 'x') || (yytext[1] == 'X') + || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1; + + if (WhiteOnMove(yyboardindex)) { + cl.pieceIn = CharToPiece(ToUpper(yytext[0])); + } else { + cl.pieceIn = CharToPiece(ToLower(yytext[0])); + } + cl.rfIn = -1; + cl.ffIn = -1; + cl.rtIn = yytext[2+skip] - ONE; + cl.ftIn = yytext[1+skip] - 'a'; + cl.promoCharIn = NULLCHAR; + + /* [HGM] but do not allow values beyond board size */ + if(cl.rtIn >= BOARD_HEIGHT || + cl.ftIn >= BOARD_WIDTH ) + return 0; + + Disambiguate(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, &cl); + + currentMoveString[0] = cl.ff + 'a'; + currentMoveString[1] = cl.rf + ONE; + currentMoveString[2] = cl.ft + 'a'; + currentMoveString[3] = cl.rt + ONE; + currentMoveString[4] = cl.promoChar; + currentMoveString[5] = NULLCHAR; + + return (int) cl.kind; +} + +[RrBbNnQqKkACDEFGHMWO][a-l0-9][xX:-]?[a-l][0-9] { + /* + * piece move with rank or file disambiguator + */ + DisambiguateClosure cl; + int skip = 0; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* remove the [xX:-] */ + if ((yytext[2] == 'x') || (yytext[2] == 'X') + || (yytext[2] == ':') || (yytext[2] == '-')) skip = 1; + + if (WhiteOnMove(yyboardindex)) { + cl.pieceIn = CharToPiece(ToUpper(yytext[0])); + } else { + cl.pieceIn = CharToPiece(ToLower(yytext[0])); + } + if (isalpha(yytext[1])) { + cl.rfIn = -1; + cl.ffIn = yytext[1] - 'a'; + } else { + cl.rfIn = yytext[1] - ONE; + cl.ffIn = -1; + } + cl.rtIn = yytext[3+skip] - ONE; + cl.ftIn = yytext[2+skip] - 'a'; + cl.promoCharIn = NULLCHAR; + + /* [HGM] do not allow values beyond board size */ + if(cl.rtIn >= BOARD_HEIGHT || + cl.rfIn >= BOARD_HEIGHT || + cl.ffIn >= BOARD_WIDTH || + cl.ftIn >= BOARD_WIDTH ) + return 0; + + Disambiguate(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, &cl); + + currentMoveString[0] = cl.ff + 'a'; + currentMoveString[1] = cl.rf + ONE; + currentMoveString[2] = cl.ft + 'a'; + currentMoveString[3] = cl.rt + ONE; + currentMoveString[4] = cl.promoChar; + currentMoveString[5] = NULLCHAR; + + return (int) cl.kind; +} + +000|0-0-0|ooo|OOO|o-o-o|O-O-O { + int rf, ff, rt, ft; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + /* [HGM] all squares referenced to board edges in stead of absolute */ + if (WhiteOnMove(yyboardindex)) { + if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) { + /* ICS wild castling */ + rf = 0; + ff = (BOARD_WIDTH-1)>>1; + rt = 0; + ft = BOARD_WIDTH-3; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } else { + rf = 0; + ff = BOARD_WIDTH>>1; + rt = 0; + ft = 2; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } + } else{ + if (boards[yyboardindex][BOARD_HEIGHT-1][3] == BlackKing) { + /* ICS wild castling */ + rf = BOARD_HEIGHT-1; + ff = (BOARD_WIDTH-1)>>1; + rt = BOARD_HEIGHT-1; + ft = BOARD_WIDTH-3; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } else { + rf = BOARD_HEIGHT-1; + ff = BOARD_WIDTH>>1; + rt = BOARD_HEIGHT-1; + ft = 2; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } + } + return (int) LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + rf, ff, rt, ft, NULLCHAR); +} + +00|0-0|oo|OO|o-o|O-O { + int rf, ff, rt, ft; + + if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ + + if (WhiteOnMove(yyboardindex)) { + if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) { + /* ICS wild castling */ + rf = 0; + ff = (BOARD_WIDTH-1)>>1; + rt = 0; + ft = 1; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } else { + rf = 0; + ff = BOARD_WIDTH>>1; + rt = 0; + ft = BOARD_WIDTH-2; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } + } else { + if (boards[yyboardindex][BOARD_HEIGHT-1][(BOARD_WIDTH-1)>>1] == BlackKing) { + /* ICS wild castling */ + rf = BOARD_HEIGHT-1; + ff = (BOARD_WIDTH-1)>>1; + rt = BOARD_HEIGHT-1; + ft = 1; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } else { + rf = BOARD_HEIGHT-1; + ff = BOARD_WIDTH>>1; + rt = BOARD_HEIGHT-1; + ft = BOARD_WIDTH-2; + sprintf(currentMoveString, "%c%c%c%c",ff+'a',rf+ONE,ft+'a',rt+ONE); + } + } + return (int) LegalityTest(boards[yyboardindex], + PosFlags(yyboardindex), EP_UNKNOWN, + initialRights, /* [HGM] assume all castlings allowed */ + rf, ff, rt, ft, NULLCHAR); +} + +[PpNnBbRrQqAaCc]@[a-l][0-9] { + /* Bughouse piece drop. No legality checking for now. */ + currentMoveString[1] = '@'; + currentMoveString[2] = yytext[2]; + currentMoveString[3] = yytext[3]; + currentMoveString[4] = NULLCHAR; + + /* [HGM] do not allow values beyond board size */ + if(currentMoveString[1] - ONE >= BOARD_HEIGHT || + currentMoveString[0] - 'a' >= BOARD_WIDTH || + currentMoveString[3] - ONE >= BOARD_HEIGHT || + currentMoveString[2] - 'a' >= BOARD_WIDTH ) + return 0; + + if (WhiteOnMove(yyboardindex)) { + currentMoveString[0] = ToUpper(yytext[0]); + return (int) WhiteDrop; + } else { + currentMoveString[0] = ToLower(yytext[0]); + return (int) BlackDrop; + } +} + +[Rr]esign(s|ed)? { + if (WhiteOnMove(yyboardindex)) + return (int) BlackWins; + else + return (int) WhiteWins; +} + +(([Ww](hite)?)|([Bb](lack)?))" "(([Rr]esign)|([Ff]orfeit))(s|ed)? { + return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins); +} + +(([Ww](hite)?)|([Bb](lack)?))" "[Dd]isconnect(s|ed) { + return (int) GameUnfinished; +} + +[Ss]talemate { + return (int) GameIsDrawn; +} + +"+-+" { + return (int) GameIsDrawn; +} + +([Cc]heck)?[Mm]ate { + if (WhiteOnMove(yyboardindex)) + return (int) BlackWins; + else + return (int) WhiteWins; +} + +"++" { + if (WhiteOnMove(yyboardindex)) + return (int) BlackWins; + else + return (int) WhiteWins; +} + +[Dd]raw(n)?(" "by)?(" "[Rr]epetition)|(" "[Aa]gree(d|ment)) { + return (int) GameIsDrawn; +} + +[Dd]raw(n)?(" (".*")")? { + return (int) GameIsDrawn; +} + +(([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ww][io]n(s)?.*) { + return (int) (ToUpper(yytext[0]) == 'W' ? WhiteWins : BlackWins); +} + +(([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ll]os[tes]+.*) { + return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins); +} + +("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { + return (int) WhiteWins; +} + +("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { + return (int) BlackWins; +} + +("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? { + return (int) GameIsDrawn; +} + +("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? { + return (int) GameUnfinished; +} + +[1-9][0-9]*/"."?[ \t\n]*[a-lNnPpRrBQqKACFEWDGHOo] { + /* move numbers */ + if ((yyleng == 1) && (yytext[0] == '1')) + return (int) MoveNumberOne; +} + +\([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} { + /* elapsed time indication, e.g. (0:12) or {10:21.071} */ + return (int) ElapsedTime; +} + +"[--"[^\]]*"--]" { + /* position diagram enclosed in [-- --] */ + return (int) PositionDiagram; +} + +^"{--------------"\n[^\}]*\n"--------------}"$ { + /* position diagram enclosed in {-- --} */ + return (int) PositionDiagram; +} + +\[[ \t\n]*[A-Za-z0-9][A-Za-z0-9_+#=-]*[ \t\n]*\"[^"]*\"[ \t\n]*\] { + return (int) PGNTag; +} + +[Gg](nu|NU)" "?[Cc](hess|HESS).*[Gg](ame|AME) { + return (int) GNUChessGame; +} + +^[#;%]" "[^ ]*(" game file"|" position file").*$ { + return (int) XBoardGame; +} + +\$[0-9]+ { /* numeric annotation glyph */ + return (int) NAG; +} + +\{[^\}]*\} { /* anything in {} */ + return (int) Comment; +} + +;.*$ { /* ; to end of line */ + return (int) Comment; +} + +\[[^\]]*\] { /* anything in [] */ + return (int) Comment; +} + +\([^()]*(\([^()]*\)[^()]*)+[^()]*\) { /* nested () */ + return (int) Comment; +} + +\([^)][^)]+\) { /* >=2 chars in () */ + return (int) Comment; +} + +^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)* { + /* Skip mail headers */ +} + +[a-zA-Z0-9'-]+ { + /* Skip random words */ +} + +.|\n { + /* Skip everything else */ +} + +%% + + +static char *StringToLex; + +#ifndef FLEX_SCANNER +static FILE *lexFP; + +static int input() +{ + int ret; + + if (StringToLex != NULL) { + ret = *StringToLex; + if (ret == NULLCHAR) + ret = EOF; + else + StringToLex++; + } else if (unputCount > 0) { + ret = unputBuffer[--unputCount]; + } else { + ret = fgetc(lexFP); + } + + if (ret == EOF) + return 0; + else + return ret; +} + +/* + * Return offset of next pattern within current file + */ +int yyoffset() +{ + int offset = ftell(lexFP) - unputCount; + + if (offset < 0) { + offset = 0; + } + return(offset); +} + +static void output(ch) + int ch; +{ + fprintf(stderr, "PARSER BUG: unmatched character '%c' (0%o)\n", + ch, ch); +} + +static void unput(ch) + int ch; +{ + if (ch == 0) return; + if (StringToLex != NULL) { + StringToLex--; + } else { + if (unputCount >= UNPUT_BUF_SIZE) + fprintf(stderr, "PARSER BUG: unput buffer overflow '%c' (0%o)\n", + ch, ch); + unputBuffer[unputCount++] = ch; + } +} + +/* Get ready to lex from a new file. Kludge below sticks + an artificial newline at the front of the file, which the + above grammar ignores, but which makes ^ at start of pattern + match at the real start of the file. +*/ +void yynewfile(f) + FILE *f; +{ + lexFP = f; + StringToLex = NULL; + unputCount = 0; + unput('\n'); /* kludge */ +} + +/* Get ready to lex from a string. ^ at start of pattern WON'T + match at the start of the string! +*/ +void yynewstr(s) + char *s; +{ + lexFP = NULL; + StringToLex = s; + unputCount = 0; +} +#endif /*!FLEX_SCANNER*/ + +#ifdef FLEX_SCANNER +void my_yy_input(buf, result, max_size) + char *buf; + int *result; + int max_size; +{ + int count; + + if (StringToLex != NULL) { + count = 0; + while (*StringToLex != NULLCHAR) { + *buf++ = *StringToLex++; + count++; + } + *result = count; + return; + } else { + count = fread(buf, 1, max_size, yyin); + if (count == 0) { + *result = YY_NULL; + } else { + *result = count; + } + return; + } +} + +static YY_BUFFER_STATE my_file_buffer = NULL; + +/* + Return offset of next pattern in the current file. +*/ +int yyoffset() +{ + int pos = yy_c_buf_p - yy_current_buffer->yy_ch_buf; + + return(ftell(yy_current_buffer->yy_input_file) - + yy_n_chars + pos); +} + + +void yynewstr(s) + char *s; +{ + if (my_file_buffer != NULL) + yy_delete_buffer(my_file_buffer); + StringToLex = s; + my_file_buffer = yy_create_buffer(stdin, YY_BUF_SIZE); + yy_switch_to_buffer(my_file_buffer); +} + +void yynewfile(f) + FILE *f; +{ + if (my_file_buffer != NULL) + yy_delete_buffer(my_file_buffer); + StringToLex = NULL; + my_file_buffer = yy_create_buffer(f, YY_BUF_SIZE); + yy_switch_to_buffer(my_file_buffer); +} +#endif /*FLEX_SCANNER*/ + +int yywrap() +{ + return TRUE; +} + +/* Parse a move from the given string s */ +/* ^ at start of pattern WON'T work here unless using flex */ +ChessMove yylexstr(boardIndex, s) + int boardIndex; + char *s; +{ + ChessMove ret; + char *oldStringToLex; +#ifdef FLEX_SCANNER + YY_BUFFER_STATE buffer, oldBuffer; +#endif + + yyboardindex = boardIndex; + oldStringToLex = StringToLex; + StringToLex = s; +#ifdef FLEX_SCANNER + buffer = yy_create_buffer(stdin, YY_BUF_SIZE); + oldBuffer = YY_CURRENT_BUFFER; + yy_switch_to_buffer(buffer); +#endif /*FLEX_SCANNER*/ + + ret = (ChessMove) yylex(); + +#ifdef FLEX_SCANNER + if (oldBuffer != NULL) + yy_switch_to_buffer(oldBuffer); + yy_delete_buffer(buffer); +#endif /*FLEX_SCANNER*/ + StringToLex = oldStringToLex; + + return ret; +} diff --git a/readme_HGM.txt b/readme_HGM.txt new file mode 100644 index 0000000..63d27d4 --- /dev/null +++ b/readme_HGM.txt @@ -0,0 +1,125 @@ +These are the release notes of Winboard_F 4.3.2, +which is released under the GPL. +This version was derived from Allessandro Scotti's Winboard_x 4.2.7 source files. +It only includes the files that were changed: + +config.h +common.h +parser.h +moves.h +resource.h + +winboard.c +backend.c +moves.c + +parser.l +winboard.rc + +and some bitmaps for piece symbols + + + +I made modifications in the following areas: + +1) Adjudication and claim verification +2) Fairy pieces and board sizes other than 8x8 +3) Miscellaneous + +Miscellaneous + +/matchPause=10000 +is an option to set the length of the pause between two games of a match. The value is in msec, default value is 10000 (I will present all newly implemented options with their default value as example). Be aware that some engines might not be stopped yet if you make the pause too small, but might still be puking output, which then will interfere with the next game. But the fixed value of 10 sec of the old Winboard seemed like overdoing it. + +Time info in PGN +When you ask for the PV-info to be stored in the PGN (a Winboard_x option), it now also stores the time spent on the move with it. + +Flag fell +In engine-engine games the messge "white/black/both" flag(s) fell" no longer appears in the window caption, but as an exclamation point behind the clock time. (To prevent the annoying overwriting of the normal header line). + +Adjudications and Claim verification. + +These functions are only present in engine-engine games, and only if legality-testing is switched on. (The latter will be typically switched off in games with bizarre rules, which the GUI doesn't know, and in that case the GUI can never have an opinion on the outcome of a game.) + +Illegal-move forfeit +As soon as one of the engines plays an illegal move, it forfeits the game. This feature was already present, but it should be 100% reliable now, as it also takes e.p. and castling rights into account, rather than erring on the safe side. + +Illegal-move claim +From the above, it follows that any illegal-move claims by an engine must be false, and will result in forfeiting the game. (In Winboard_x this message is ignored, causing the game or match to hang.) + +Checkmate adjudication +As soon as one of the engines does a move that results in checkmate, the GUI declares the game won, without waiting for the engine to claim it. + +Insufficient mating material +As soon as the material on the board has shrunk to KK, KNK or KBK, the game is declared draw. + +/adjudicateLossThreshold=0 +This option was already present in Winboard_x, (to declare a game lost for which both engines agree for 3 moves that the score is below the given threshold), but a non-zero value is now also used to enable the following adjudications. If you only want the latter, just make the threshold impossibly low (-40000 will usually do the trick). + +Trivial draws +If we are 3 moves into a KQKQ, KRKR, KBKB KBKN or KNKN end-game, the game is adjudicated as draw. + +/repeatsToDraw=6 +When the specified number of repeats occurs, the game is adjudicated draw. Should keep track of e.p. and castling rights. This does not require legality-testing to be switched on. The engines retain the legal right to claim after a 3-fold repetition, though. If you set this parameter to 3 or less, they will never get the chance. Better not set it to 1 or less. + +/ruleMoves=51 +After the given number of full moves without capture or Pawn move, the game is adjudicated draw. Even without legality testing. The engines retain the legal right to claim after 50 moves. + +/testClaims=FALSE +When enabled, this option verifies all result claims made by the engines, and overrules the claim if it is false (forfeiting the game for the claimer). An engine can still safely claim a win for its opponent on a nonsense reason, though; this is taken to be the equivalent of 'resign'. Draw claims (made before a draw adjudication) are checked against the 50-move, 3-fold-repetition or insufficient-material rules. Win claims are always considered false, as the GUI adjudicates checkmates (and stalemates) before any engine can claim them. + +Fairy-Chess support + +/boardWidth=8 +Sets the number of files on the board. The additional files are named i, j, k, l... in PGN, and should be indicated this way in communicating moves to and from the engine. Currently works upto 12. No guarantees on how the rest of the display (clocks, etc.) looks if you make this number < 8. + +/boardHeight=8 +Sets the number of ranks. Extra ranks are numbered 9, 10, 11... in PGN. This is so far largely untested, and unlikely to work for double-digit ranks. Displaying boards with upto 12 ranks seems to work, though, but double-digit ranks might cause all kind of unforseen problems in PGN file and move parser, or in communication with the engine. To avoid such problems as much as possible, in boards with more than 9 ranks the counting of ranks will start at zero rather than one! + +/fontPieceToCharTable="......." +This paramater, controlling the mapping of font symbols to piece types, was already present in Winboard_x. The default is dependent on the font selected with the /renderPiecesWithFont option. It can now accept upto 32 pieces, but the length should always be even. The first half designates the white pieces, the second half the black, both in the order PNBRACHFEWDOGMQK. (The letters mentioned here are the letters by which the pieces will be indicated in PGN and FEN notation, what you have to give as argument depends on the font you use. A black king might be 'l', for instance). +If you give fewer then 32 pieces, this will go at the expense of what is just before Queen. So the last two symbols you give for each color are always Queen and King, the others are assigned in the order Pawn, Knight, ... Pieces that do not get a symbol assigned will remain invisible. + +fairy-FEN support +The letters ACDEFGHMOW are accepted in FENs in addition to the regular PNBRQK,(and of course the lower case versions for black), and are passed to the engine in a setboard or edit menu. +Double-digit skips are acceptable in FENs. 'x' is interpreted as a skip of 10. +Castling rights should no longer be ignored. (Doesn't work for FRC yet, though.) +The 50-move-plies field should also be meaningful now. + +Legality testing for Fairy pieces +There is a build in notion of what some of the new pieces can do, according to +A = Archbishop (aka Cardinal) = N+B +C = Chancellor (aka Marshall) = N+R +F = Ferz (aka General), moves 1 step diagonal +W = Wazir (aka Grand Vizer), moves 1 step orthogonal +E = Elephant (aka Alfil), jumps 2 steps diagonal +D = Dabbabah (aka War Machine), jumps 2 steps orthogonal +M = Man (aka Commoner), moves as King, but is not a royal piece +O = Cannon, moves along Rook lines, but cannot capture unless it jumps over + exactly one piece (friend or foe), and captures the first piece + it encounters thereafter. It can only jump if it captures something. + The piece jumped over (the 'platform') is not affected. +The other pieces have not yet any moves implemented: +H = Nightrider +G = Grasshopper +In games that use the mentioned pieces as described (Shatranj, Courier, Capablanca Chess) you can leave legality testing on. If you use them to represent pieces that move differently, you should switch legality testing off. + +Pawn moves +Pawn motion is made dependent on the variant played: in Shatranj and Courier the double move is forbidden. In those games promotions are always to Ferz. In Capablanca Chess the ArchBishop and Chancellor also appear as choices in the promotion popup box. + + +/variant="normal" +Several new variants names are added (replacing "variant31" upto "variant36"). They affect the initial position. (Board size has to be set separately.) They are: +courier (a Medieval predecessor of modern Chess, played on a 12x8 board) +capablanca (on a 10x8 board, featuring Archbishop and Chancellor) +gothic (as Capablanca, but with a more exciting initial setup) +xiangqi (Chinese Chess) +shogi (Japanese Chess, no support yet) +fairy (This variant plays on 8x8 with HEW in stead of NBR on the Queen side, + so that all back-rank pieces are (potentially) different) +Make sure the selected board size matches the variant; this is not automatic + +Xiangqi and Shogi support (or lack thereof) +Xiangqi is only partially supported. The board display is western-style (the pieces play on squares, rather than grid intersections). The legality testing uses the Shatranj Knight and Elephant, that cannot be blocked, and thus allows some moves that should be illegal in Xiangqi. The Palace region is indicated on the board, but there is no testing if the King or Mandarins (implemented as ordinary Ferzes) leave the Palace. SAN might be non-standard, as it uses O to indicate Cannon. Most of this will be fixed in a future version. +Shogi is not yet supported at all, first the shaky implementation of Crazyhouse will have to be beefed up. + diff --git a/winboard/resource.h b/winboard/resource.h index ae996c7..0dd963e 100644 --- a/winboard/resource.h +++ b/winboard/resource.h @@ -86,27 +86,29 @@ #define DLG_IcsOptions 454 #define DLG_BoardOptions 455 #define DLG_Fonts 456 -#define DLG_NewGameFRC 457 -#define DLG_GameListOptions 458 -#define DLG_MoveHistory 459 -#define DLG_EvalGraph 461 -#define DLG_EngineOutput 463 -#define DLG_EnginePlayOptions 464 -#define IDI_WHITE_14 466 -#define IDI_BLACK_14 467 -#define DLG_OptionsUCI 467 -#define IDI_PONDER_14 468 -#define IDI_TRANS_14 469 -#define IDI_CLOCK_14 470 -#define IDI_UNKNOWN_14 471 -#define IDI_BALOON_14 472 -#define IDI_ANALYZE_14 473 -#define IDI_ANALYZE2_14 474 +#define DLG_NewGameFRC 457 +#define DLG_GameListOptions 458 +#define DLG_MoveHistory 459 +#define DLG_EvalGraph 461 +#define DLG_EngineOutput 463 +#define DLG_EnginePlayOptions 464 +#define IDI_WHITE_14 466 +#define IDI_BLACK_14 467 +#define DLG_OptionsUCI 467 +#define IDI_PONDER_14 468 +#define IDI_TRANS_14 469 +#define IDI_CLOCK_14 470 +#define IDI_UNKNOWN_14 471 +#define IDI_BALOON_14 472 +#define IDI_ANALYZE_14 473 +#define IDI_ANALYZE2_14 474 #define DLG_Promotion 500 #define PB_Queen 502 #define PB_Rook 503 #define PB_Bishop 504 #define PB_Knight 505 +#define PB_Chancellor 506 /* [HGM] Capablanca pieces */ +#define PB_Archbishop 507 #define EP_White 600 #define EP_WhitePawn 601 #define EP_WhiteKnight 602 @@ -167,10 +169,10 @@ #define DLG_ConsoleRich 1022 #define IDC_EDIT1 1023 #define OPT_DarkSquareColor 1023 -#define IDC_GameListFilter 1023 -#define IDC_NFG_Edit 1023 -#define IDC_EpDrawMoveCount 1023 -#define IDC_PolyglotDir 1023 +#define IDC_GameListFilter 1023 +#define IDC_NFG_Edit 1023 +#define IDC_EpDrawMoveCount 1023 +#define IDC_PolyglotDir 1023 #define OPT_ConsoleText 1024 #define OPT_LightSquareColor 1024 #define OPT_CommandInput 1025 @@ -187,8 +189,8 @@ #define OPT_SampleDarkSquare 1030 #define OPT_PremoveWhiteText 1031 #define IDC_EDIT2 1032 -#define IDC_EpAdjudicationThreshold 1032 -#define IDC_HashSize 1032 +#define IDC_EpAdjudicationThreshold 1032 +#define IDC_HashSize 1032 #define OPT_ConsoleInput 1033 #define OPT_AdditionalOptions 1034 #define OPT_PremoveBlackText 1035 @@ -236,22 +238,22 @@ #define IDM_GeneralOptions 1299 #define IDM_BoardOptions 1300 #define IDM_Fonts 1301 -#define IDM_UserAdjudication_White 1302 -#define IDM_UserAdjudication_Black 1303 -#define IDM_UserAdjudication_Draw 1304 -#define IDM_NewGameFRC 1305 -#define IDM_GameListOptions 1306 +#define IDM_UserAdjudication_White 1302 +#define IDM_UserAdjudication_Black 1303 +#define IDM_UserAdjudication_Draw 1304 +#define IDM_NewGameFRC 1305 +#define IDM_GameListOptions 1306 #define PB_King 1307 -#define IDM_ShowMoveHistory 1307 -#define IDM_PasteAny 1309 -#define IDM_ShowEvalGraph 1310 -#define IDM_CopyGameListToClipboard 1311 +#define IDM_ShowMoveHistory 1307 +#define IDM_PasteAny 1309 +#define IDM_ShowEvalGraph 1310 +#define IDM_CopyGameListToClipboard 1311 #define OPT_Bold 1312 -#define IDM_ShowEngineOutput 1312 +#define IDM_ShowEngineOutput 1312 #define OPT_Italic 1313 -#define IDM_EnginePlayOptions 1313 +#define IDM_EnginePlayOptions 1313 #define OPT_Underline 1314 -#define IDM_OptionsUCI 1314 +#define IDM_OptionsUCI 1314 #define OPT_Strikeout 1315 #define OPT_ChooseColor 1316 #define OPT_Sample 1318 @@ -372,51 +374,51 @@ #define OPT_ChooseCommentsFont 1417 #define OPT_ChooseConsoleFont 1418 #define OPT_DefaultFonts 1419 -#define OPT_ChooseMoveHistoryFont 1420 +#define OPT_ChooseMoveHistoryFont 1420 #define OPT_AutoRaiseBoard 1421 -#define OPT_SampleMoveHistoryFont 1421 +#define OPT_SampleMoveHistoryFont 1421 #define OPT_ShowButtonBar 1422 -#define OPT_MessageFont6 1422 -#define OPT_SaveExtPGN 1423 -#define OPT_HideThinkFromHuman 1424 -#define IDC_GameListDoFilter 1425 -#define IDC_NFG_Random 1426 -#define IDC_NFG_Label 1427 -#define IDC_GameListTags 1428 -#define IDC_GLT 1429 -#define IDC_GLT_Up 1430 -#define IDC_GLT_Down 1431 -#define IDC_GLT_Restore 1432 -#define IDC_GLT_Default 1433 -#define IDC_GLT_RestoreTo 1434 -#define IDC_MoveHistory 1435 -#define OPT_OutOfBookInfo 1436 -#define OPT_ExtraInfoInMoveHistory 1437 -#define OPT_HighlightMoveArrow 1438 -#define IDC_BUTTON1 1440 -#define IDC_BrowseForPolyglotDir 1440 -#define IDC_BrowseForEGTB 1441 -#define IDC_BrowseForBook 1442 -#define IDC_EngineMemo1 1443 -#define IDC_EngineMemo2 1444 -#define IDC_EngineLabel1 1445 -#define IDC_EngineLabel2 1446 -#define IDC_EpShowThinking 1447 -#define IDC_EpHideThinkingHuman 1448 -#define IDC_EpPonder 1449 -#define IDC_EpPeriodicUpdates 1450 -#define IDC_Engine1_NPS 1451 -#define IDC_Engine2_NPS 1452 -#define IDC_Color1 1459 -#define IDC_Color2 1460 -#define IDC_StateIcon1 1461 -#define IDC_StateData1 1462 -#define IDC_StateData2 1463 -#define IDC_StateIcon2 1464 -#define IDC_PathToEGTB 1466 -#define IDC_SizeOfEGTB 1467 -#define IDC_UseBook 1469 -#define IDC_BookFile 1470 +#define OPT_MessageFont6 1422 +#define OPT_SaveExtPGN 1423 +#define OPT_HideThinkFromHuman 1424 +#define IDC_GameListDoFilter 1425 +#define IDC_NFG_Random 1426 +#define IDC_NFG_Label 1427 +#define IDC_GameListTags 1428 +#define IDC_GLT 1429 +#define IDC_GLT_Up 1430 +#define IDC_GLT_Down 1431 +#define IDC_GLT_Restore 1432 +#define IDC_GLT_Default 1433 +#define IDC_GLT_RestoreTo 1434 +#define IDC_MoveHistory 1435 +#define OPT_OutOfBookInfo 1436 +#define OPT_ExtraInfoInMoveHistory 1437 +#define OPT_HighlightMoveArrow 1438 +#define IDC_BUTTON1 1440 +#define IDC_BrowseForPolyglotDir 1440 +#define IDC_BrowseForEGTB 1441 +#define IDC_BrowseForBook 1442 +#define IDC_EngineMemo1 1443 +#define IDC_EngineMemo2 1444 +#define IDC_EngineLabel1 1445 +#define IDC_EngineLabel2 1446 +#define IDC_EpShowThinking 1447 +#define IDC_EpHideThinkingHuman 1448 +#define IDC_EpPonder 1449 +#define IDC_EpPeriodicUpdates 1450 +#define IDC_Engine1_NPS 1451 +#define IDC_Engine2_NPS 1452 +#define IDC_Color1 1459 +#define IDC_Color2 1460 +#define IDC_StateIcon1 1461 +#define IDC_StateData1 1462 +#define IDC_StateData2 1463 +#define IDC_StateIcon2 1464 +#define IDC_PathToEGTB 1466 +#define IDC_SizeOfEGTB 1467 +#define IDC_UseBook 1469 +#define IDC_BookFile 1470 #define IDC_STATIC -1 // Next default values for new objects @@ -424,9 +426,9 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 468 -#define _APS_NEXT_COMMAND_VALUE 1315 -#define _APS_NEXT_CONTROL_VALUE 1471 +#define _APS_NEXT_RESOURCE_VALUE 468 +#define _APS_NEXT_COMMAND_VALUE 1315 +#define _APS_NEXT_CONTROL_VALUE 1471 #define _APS_NEXT_SYMED_VALUE 1404 #endif #endif diff --git a/winboard/winboard.c b/winboard/winboard.c index f9c566b..d2facc2 100644 --- a/winboard/winboard.c +++ b/winboard/winboard.c @@ -1,6 +1,6 @@ -/* +/* * WinBoard.c -- Windows NT front end to XBoard - * $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 $ * * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. * Enhancements Copyright 1992-2001 Free Software Foundation, Inc. @@ -57,7 +57,7 @@ #include #include -#include +#include #include #include #include @@ -84,13 +84,15 @@ #include "wsockerr.h" #include "defaults.h" -#include "wsnap.h" - -void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); - -int myrandom(void); -void mysrandom(unsigned int seed); - +#include "wsnap.h" + +void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); + + int myrandom(void); + void mysrandom(unsigned int seed); + +extern int whiteFlag, blackFlag; + typedef struct { ChessSquare piece; POINT pos; /* window coordinates of current pos */ @@ -148,7 +150,7 @@ char *icsNames; char *firstChessProgramNames; char *secondChessProgramNames; -#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! */ #define PALETTESIZE 256 @@ -164,7 +166,7 @@ ColorClass currentColorClass; HWND hCommPort = NULL; /* currently open comm port */ static HWND hwndPause; /* pause button */ -static HBITMAP pieceBitmap[3][(int) WhiteKing + 1]; +static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */ static HBRUSH lightSquareBrush, darkSquareBrush, whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush; static POINT gridEndpoints[(BOARD_SIZE + 1) * 4]; @@ -179,18 +181,18 @@ static int doingSizing = FALSE; static int lastSizing = 0; static int prevStderrPort; -/* [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 */ +#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]; + #if __GNUC__ && !defined(_winmajor) #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */ #else @@ -239,24 +241,24 @@ SizeInfo sizeInfo[] = #define MF(x) {x, {0, }, {0, }, 0} MyFont fontRec[NUM_SIZES][NUM_FONTS] = { - { 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) }, + { 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) }, }; MyFont *font[NUM_SIZES][NUM_FONTS]; @@ -383,41 +385,42 @@ VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def); void ParseIcsTextMenu(char *icsTextMenuString); VOID PopUpMoveDialog(char firstchar); VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca); - -/* [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 ); +/* [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 ); + +VOID GothicPopUp(char *title); /* * Setting "frozen" should disable all user input other than deleting * the window. We do this while engines are initializing themselves. @@ -464,7 +467,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; - HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS; + HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS; debugFP = stderr; @@ -480,7 +483,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, hAccelMain = LoadAccelerators (hInstance, szAppName); hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT"); - hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */ + hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */ /* Acquire and dispatch messages until a WM_QUIT message is received. */ @@ -490,14 +493,14 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 0)) /* highest message to examine */ { if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) && - !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) && - !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) && - !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) && + !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) && + !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) && + !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) && !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) && !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) && !(errorDialog && IsDialogMessage(errorDialog, &msg)) && !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && - !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) && + !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) && !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) { TranslateMessage(&msg); /* Translates virtual key codes */ DispatchMessage(&msg); /* Dispatches message to window */ @@ -558,9 +561,12 @@ int screenHeight, screenWidth; void EnsureOnScreen(int *x, int *y) { + int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); /* Be sure window at (x,y) is not off screen (or even mostly off screen) */ if (*x > screenWidth - 32) *x = 0; if (*y > screenHeight - 32) *y = 0; + if (*x < 10) *x = 10; + if (*y < gap) *y = gap; } BOOL @@ -580,15 +586,15 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) } InitAppData(lpCmdLine); /* Get run-time parameters */ if (appData.debugMode) { - debugFP = fopen(appData.nameOfDebugFile, "w"); + debugFP = fopen(appData.nameOfDebugFile, "w"); setbuf(debugFP, NULL); } InitBackEnd1(); - InitEngineUCI( installDir, &first ); - InitEngineUCI( installDir, &second ); - + InitEngineUCI( installDir, &first ); + InitEngineUCI( installDir, &second ); + /* Create a main window for this application instance. */ hwnd = CreateWindow(szAppName, szTitle, (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX), @@ -612,7 +618,9 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) size that fits on this screen as the default. */ InitDrawingSizes((BoardSize)ibs, 0); if (boardSize == (BoardSize)-1 && - winHeight <= screenHeight && winWidth <= screenWidth) { + winHeight <= screenHeight + - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10 + && winWidth <= screenWidth) { boardSize = (BoardSize)ibs; } } @@ -620,47 +628,47 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) InitMenuChecks(); buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS); - /* [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 */ + 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) ); + /* Make a console window if needed */ if (appData.icsActive) { ConsoleCreate(); } - /* [AS] Restore layout */ - if( wpMoveHistory.visible ) { - MoveHistoryPopUp(); - } - - if( wpEvalGraph.visible ) { - EvalGraphPopUp(); - } - - if( wpEngineOutput.visible ) { - EngineOutputPopUp(); - } - + /* [AS] Restore layout */ + if( wpMoveHistory.visible ) { + MoveHistoryPopUp(); + } + + if( wpEvalGraph.visible ) { + EvalGraphPopUp(); + } + + if( wpEngineOutput.visible ) { + EngineOutputPopUp(); + } + InitBackEnd2(); /* Make the window visible; update its client area; and return "success" */ @@ -676,13 +684,19 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) SetWindowPlacement(hwndMain, &wp); SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); - - /* [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); + + /* [AS] Disable the FRC stuff if not playing the proper variant */ + if( gameInfo.variant != VariantFischeRandom ) { + EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED ); + } +#ifdef FAIRY +#ifdef GOTHIC + /* [HGM] Gothic licensing requirement */ + if(gameInfo.variant == VariantGothic) + GothicPopUp(GOTHIC); +#endif // GOTHIC +#endif // FAIRY if (hwndConsole) { #if AOT_CONSOLE SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, @@ -876,7 +890,7 @@ ArgDescriptor argDescriptors[] = { { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE }, { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE }, { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE }, - { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */ + { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */ { "boardSize", ArgBoardSize, (LPVOID) &boardSize, TRUE }, /* must come after all fonts */ { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE }, @@ -1074,81 +1088,89 @@ ArgDescriptor argDescriptors[] = { { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE }, { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE }, { "variant", ArgString, (LPVOID) &appData.variant, FALSE }, - { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE }, - { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE }, + { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE }, + { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE }, { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE }, { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE }, { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE }, { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE }, - /* [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 */ + { "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 }, + + /* [HGM] User-selectable board size */ + { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE }, + { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE }, + { "matchPause", ArgInt, (LPVOID) &appData.matchPause, FALSE }, + { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE }, + { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE }, + { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE }, + #ifdef ZIPPY { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE }, { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE }, @@ -1803,56 +1825,64 @@ InitAppData(LPSTR lpCmdLine) appData.firstProtocolVersion = PROTOVER; appData.secondProtocolVersion = PROTOVER; appData.showButtonBar = TRUE; - - /* [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 ); - + + /* [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 ); + + /* [HGM] User-selectable board size */ + appData.NrFiles = 8; + appData.NrRanks = 8; + appData.matchPause = 10000; + appData.testClaims = FALSE; + appData.ruleMoves = 51; + appData.drawRepeats = 6; + #ifdef ZIPPY appData.zippyTalk = ZIPPY_TALK; appData.zippyPlay = ZIPPY_PLAY; @@ -1891,6 +1921,11 @@ InitAppData(LPSTR lpCmdLine) /* Parse command line */ ParseArgs(StringGet, &lpCmdLine); + /* [HGM] make sure board size is acceptable */ + if(appData.NrFiles > BOARD_SIZE || + appData.NrRanks > BOARD_SIZE ) + DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2); + /* Propagate options that affect others */ if (appData.matchMode || appData.matchGames) chessProgram = TRUE; if (appData.icsActive || appData.noChessProgram) { @@ -2041,39 +2076,39 @@ SaveSettings(char* name) gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; } - /* [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 */ + 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; + } + for (ad = argDescriptors; ad->argName != NULL; ad++) { if (!ad->save) continue; switch (ad->argType) { @@ -2180,436 +2215,552 @@ SaveSettings(char* name) * \*---------------------------------------------------------------------------*/ -/* [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= 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 */ +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] [HGM] Make room for more piece types, so all pieces can be different */ +enum { + PM_WP = (int) WhitePawn, + PM_WN = (int) WhiteKnight, + PM_WB = (int) WhiteBishop, + PM_WR = (int) WhiteRook, +#ifdef FAIRY + PM_WA = (int) WhiteCardinal, + PM_WC = (int) WhiteMarshall, + PM_WFP = (int) WhiteFairyPawn, + PM_WFN = (int) WhiteFairyKnight, + PM_WFB = (int) WhiteFairyBishop, + PM_WFR = (int) WhiteFairyRook, + PM_WFA = (int) WhiteFairyCardinal, + PM_WFC = (int) WhiteFairyMarshall, + PM_WFQ = (int) WhiteFairyQueen, + PM_WFK = (int) WhiteFairyKing, +#endif + PM_WQ = (int) WhiteQueen, + PM_WK = (int) WhiteKing, + PM_BP = (int) BlackPawn, + PM_BN = (int) BlackKnight, + PM_BB = (int) BlackBishop, + PM_BR = (int) BlackRook, +#ifdef FAIRY + PM_BA = (int) BlackCardinal, + PM_BC = (int) BlackMarshall, + PM_BFP = (int) BlackFairyPawn, + PM_BFN = (int) BlackFairyKnight, + PM_BFB = (int) BlackFairyBishop, + PM_BFR = (int) BlackFairyRook, + PM_BFA = (int) BlackFairyCardinal, + PM_BFC = (int) BlackFairyMarshall, + PM_BFQ = (int) BlackFairyQueen, + PM_BFK = (int) BlackFairyKing, +#endif + PM_BQ = (int) BlackQueen, + PM_BK = (int) BlackKing +}; + +static HFONT hPieceFont = NULL; +static HBITMAP hPieceMask[(int) EmptySquare]; +static HBITMAP hPieceFace[(int) EmptySquare]; +static int fontBitmapSquareSize = 0; +static char pieceToFontChar[(int) EmptySquare] = + { 'p', 'n', 'b', 'r', +#ifdef FAIRY + 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k', +#endif + 'q', 'k', 'o', 'm', 'v', 't', +#ifdef FAIRY + 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l', +#endif + 'w', 'l' }; + +static BOOL SetPieceToFontCharTable( const char * map ) +{ + BOOL result = FALSE; int NrPieces; + + if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare + && NrPieces >= 12 && !(NrPieces&1)) { + int i; /* [HGM] Accept even length from 12 to 32 */ + + for( i=0; i<(int) EmptySquare; i++ ) pieceToFontChar[i] = 0; + for( i=0; i= 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; +#ifdef FAIRY + case BlackCardinal: + return PM_BA; + case BlackMarshall: + return PM_BC; + case BlackFairyPawn: + return PM_BFP; + case BlackFairyKnight: + return PM_BFN; + case BlackFairyBishop: + return PM_BFB; + case BlackFairyRook: + return PM_BFR; + case BlackFairyCardinal: + return PM_BFA; + case BlackFairyMarshall: + return PM_BFC; + case BlackFairyQueen: + return PM_BFQ; + case BlackFairyKing: + return PM_BFK; + case WhiteCardinal: + return PM_WA; + case WhiteMarshall: + return PM_WC; + case WhiteFairyPawn: + return PM_WFP; + case WhiteFairyKnight: + return PM_WFN; + case WhiteFairyBishop: + return PM_WFB; + case WhiteFairyRook: + return PM_WFR; + case WhiteFairyCardinal: + return PM_WFA; + case WhiteFairyMarshall: + return PM_WFC; + case WhiteFairyQueen: + return PM_WFQ; + case WhiteFairyKing: + return PM_WFK; +#endif + } + + 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"); + } +#ifdef FAIRY + else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) { + /* Fairy symbols */ + SetPieceToFontCharTable("PNBRACFHEWUOGMQKpnbracfewuogmqk"); + } +#endif + else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) { + /* Good Companion (Some characters get warped as literal :-( */ + char s[] = "1cmWG0ñueOS¯®oYI23wgQU"; + s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3; + SetPieceToFontCharTable(s); + } + 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 ); +#ifdef FAIRY + CreatePieceMaskFromFont( hdc_window, hdc, PM_WA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFP ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFN ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFB ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFR ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFQ ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WFK ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFP ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFN ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFB ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFR ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFQ ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BFK ); +#endif + + SelectObject( hdc, hfont_old ); + + fontBitmapSquareSize = squareSize; + } + } + + if( hdc != NULL ) { + DeleteDC( hdc ); + } + + if( hdc_window != NULL ) { + ReleaseDC( hwndMain, hdc_window ); + } +} + HBITMAP DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix) { @@ -2681,25 +2832,25 @@ InitDrawingColors() whitePieceBrush = CreateSolidBrush(whitePieceColor); blackPieceBrush = CreateSolidBrush(blackPieceColor); iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND)); - - /* [AS] Force rendering of the font-based pieces */ - if( fontBitmapSquareSize > 0 ) { - fontBitmapSquareSize = 0; - } + + /* [AS] Force rendering of the font-based pieces */ + if( fontBitmapSquareSize > 0 ) { + fontBitmapSquareSize = 0; + } } int -BoardWidth(int boardSize) -{ - 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; +BoardWidth(int boardSize, int n) +{ /* [HGM] argument n added to allow different width and height */ + int lineGap = sizeInfo[boardSize].lineGap; + + if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { + lineGap = appData.overrideLineGap; + } + + return (n + 1) * lineGap + + n * sizeInfo[boardSize].squareSize; } /* Respond to board resize by dragging edge */ @@ -2726,7 +2877,7 @@ ResizeBoard(int newSizeX, int newSizeY, int flags) VOID InitDrawingSizes(BoardSize boardSize, int flags) { - int i, boardWidth; + int i, boardWidth, boardHeight; /* [HGM] height treated separately */ ChessSquare piece; static int oldBoardSize = -1, oldTinyLayout = 0; HDC hdc; @@ -2744,10 +2895,10 @@ InitDrawingSizes(BoardSize boardSize, int flags) squareSize = sizeInfo[boardSize].squareSize; lineGap = sizeInfo[boardSize].lineGap; - if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { - lineGap = appData.overrideLineGap; - } - + if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { + lineGap = appData.overrideLineGap; + } + if (tinyLayout != oldTinyLayout) { long style = GetWindowLong(hwndMain, GWL_STYLE); if (tinyLayout) { @@ -2767,7 +2918,8 @@ InitDrawingSizes(BoardSize boardSize, int flags) DrawMenuBar(hwndMain); } - boardWidth = BoardWidth(boardSize); + boardWidth = BoardWidth(boardSize, BOARD_WIDTH); + boardHeight = BoardWidth(boardSize, BOARD_HEIGHT); /* Get text area sizes */ hdc = GetDC(hwndMain); @@ -2808,7 +2960,7 @@ InitDrawingSizes(BoardSize boardSize, int flags) boardRect.left = whiteRect.left; boardRect.right = boardRect.left + boardWidth; boardRect.top = messageRect.bottom + INNER_MARGIN; - boardRect.bottom = boardRect.top + boardWidth; + boardRect.bottom = boardRect.top + boardHeight; sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN; sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN; @@ -2896,18 +3048,23 @@ InitDrawingSizes(BoardSize boardSize, int flags) ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER, lineGap, &logbrush, 0, NULL); - for (i = 0; i < BOARD_SIZE + 1; i++) { + /* [HGM] Loop had to be split in part for vert. and hor. lines */ + for (i = 0; i < BOARD_HEIGHT + 1; i++) { gridEndpoints[i*2].x = boardRect.left + lineGap / 2; - gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2; gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y = boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)); gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 + - BOARD_SIZE * (squareSize + lineGap); - gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x = - gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left + + BOARD_WIDTH * (squareSize + lineGap); + lineGap / 2 + (i * (squareSize + lineGap)); + gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2; + } + for (i = 0; i < BOARD_WIDTH + 1; i++) { + gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2; + gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x = + gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left + lineGap / 2 + (i * (squareSize + lineGap)); - gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y = - boardRect.top + BOARD_SIZE * (squareSize + lineGap); + gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y = + boardRect.top + BOARD_HEIGHT * (squareSize + lineGap); gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2; } } @@ -2919,7 +3076,7 @@ InitDrawingSizes(BoardSize boardSize, int flags) /* Load piece bitmaps for this board size */ for (i=0; i<=2; i++) { for (piece = WhitePawn; - (int) piece <= (int) WhiteKing; + (int) piece < (int) BlackPawn; piece = (ChessSquare) ((int) piece + 1)) { if (pieceBitmap[i][piece] != NULL) DeleteObject(pieceBitmap[i][piece]); @@ -2944,6 +3101,40 @@ InitDrawingSizes(BoardSize boardSize, int flags) pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w"); pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w"); pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w"); +#ifdef FAIRY + if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */ + pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s"); + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s"); + pieceBitmap[0][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "s"); + pieceBitmap[0][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "s"); + pieceBitmap[0][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "s"); + pieceBitmap[0][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "s"); + pieceBitmap[0][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "s"); + pieceBitmap[0][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "s"); + pieceBitmap[0][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "s"); + pieceBitmap[0][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "s"); + pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o"); + pieceBitmap[1][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "o"); + pieceBitmap[1][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "o"); + pieceBitmap[1][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "o"); + pieceBitmap[1][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "o"); + pieceBitmap[1][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "o"); + pieceBitmap[1][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "o"); + pieceBitmap[1][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "o"); + pieceBitmap[1][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "o"); + pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w"); + pieceBitmap[2][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "w"); + pieceBitmap[2][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "w"); + pieceBitmap[2][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "w"); + pieceBitmap[2][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "w"); + pieceBitmap[2][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "w"); + pieceBitmap[2][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "w"); + pieceBitmap[2][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "w"); + pieceBitmap[2][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "w"); + } +#endif } @@ -2969,19 +3160,19 @@ VOID SquareToPos(int row, int column, int * x, int * y) { if (flipView) { - *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap); + *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap); *y = boardRect.top + lineGap + row * (squareSize + lineGap); } else { *x = boardRect.left + lineGap + column * (squareSize + lineGap); - *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap); + *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap); } } VOID DrawCoordsOnDC(HDC hdc) { - static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'}; - static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'}; + 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'}; + 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'}; char str[2] = { NULLCHAR, NULLCHAR }; int oldMode, oldAlign, x, y, start, i; HFONT oldFont; @@ -2990,7 +3181,7 @@ DrawCoordsOnDC(HDC hdc) if (!appData.showCoords) return; - start = flipView ? 0 : 8; + start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT; oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH)); oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT)); @@ -3001,14 +3192,16 @@ DrawCoordsOnDC(HDC hdc) x = boardRect.left + lineGap; SetTextAlign(hdc, TA_LEFT|TA_TOP); - for (i = 0; i < 8; i++) { + for (i = 0; i < BOARD_HEIGHT; i++) { str[0] = files[start + i]; ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL); y += squareSize + lineGap; } + start = flipView ? 12-BOARD_WIDTH : 12; + SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM); - for (i = 0; i < 8; i++) { + for (i = 0; i < BOARD_WIDTH; i++) { str[0] = ranks[start + i]; ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL); x += squareSize + lineGap; @@ -3027,7 +3220,7 @@ DrawGridOnDC(HDC hdc) if (lineGap != 0) { oldPen = SelectObject(hdc, gridPen); - PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2); + PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2); SelectObject(hdc, oldPen); } } @@ -3043,14 +3236,14 @@ DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen) if (lineGap == 0) return; if (flipView) { x1 = boardRect.left + - lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap); + lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap); y1 = boardRect.top + lineGap/2 + y * (squareSize + lineGap); } else { x1 = boardRect.left + lineGap/2 + x * (squareSize + lineGap); y1 = boardRect.top + - lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap); + lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap); } hPen = pen ? premovePen : highlightPen; oldPen = SelectObject(hdc, on ? hPen : gridPen); @@ -3094,36 +3287,36 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, if (appData.blindfold) return; - /* [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 */ + 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; + } + } + if (appData.monoMode) { SelectObject(tmphdc, PieceBitmap(piece, color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE)); @@ -3171,391 +3364,397 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, } } -/* [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= 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) */ +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= squareSize && lite_h >= squareSize ) { + backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH; + backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT; + 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_WIDTH; + backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT; + 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; +} + VOID DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) { int row, column, x, y, square_color, piece_color; ChessSquare piece; HBRUSH oldBrush; - 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 ); - } - - for (row = 0; row < BOARD_SIZE; row++) { - for (column = 0; column < BOARD_SIZE; column++) { + 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 ); + } + + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { SquareToPos(row, column, &x, &y); piece = board[row][column]; square_color = ((column + row) % 2) == 1; + if(!strcmp(appData.variant, "xiangqi") ) { + square_color = 1; + if( (row < 3 || row > BOARD_HEIGHT-4) && + column < (BOARD_WIDTH + 4)/2 && + column > (BOARD_WIDTH - 5)/2 ) square_color = 0; + } piece_color = (int) piece < (int) BlackPawn; if (appData.monoMode) { @@ -3565,26 +3764,26 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) } else { DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc); } - } - 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 { + } + 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 { oldBrush = SelectObject(hdc, square_color ? lightSquareBrush : darkSquareBrush); BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY); @@ -3594,10 +3793,10 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) } } } - - if( texture_hdc != NULL ) { - DeleteDC( texture_hdc ); - } + + if( texture_hdc != NULL ) { + DeleteDC( texture_hdc ); + } } #define MAX_CLIPS 200 /* more than enough */ @@ -3628,21 +3827,21 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) */ Boolean fullrepaint = repaint; - 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() ) { + 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 (board == NULL) { if (!lastReqValid) { return; @@ -3685,15 +3884,15 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) dragInfo.pos.x, dragInfo.pos.y, dragInfo.lastpos.x, dragInfo.lastpos.y); fprintf(debugFP, "prev: "); - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { fprintf(debugFP, "%d ", lastDrawn[row][column]); } } fprintf(debugFP, "\n"); fprintf(debugFP, "board: "); - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { fprintf(debugFP, "%d ", board[row][column]); } } @@ -3710,8 +3909,8 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) * flipping has changed. */ if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) { - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */ + for (column = 0; column < BOARD_WIDTH; column++) { if (lastDrawn[row][column] != board[row][column]) { SquareToPos(row, column, &x, &y); clips[num_clips++] = @@ -3845,11 +4044,11 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) DrawGridOnDC(hdcmem); DrawHighlightsOnDC(hdcmem); DrawBoardOnDC(hdcmem, board, tmphdc); - - if( appData.highlightMoveWithArrow ) { - DrawArrowHighlight(hdcmem); - } - + + if( appData.highlightMoveWithArrow ) { + DrawArrowHighlight(hdcmem); + } + DrawCoordsOnDC(hdcmem); /* Put the dragged piece back into place and draw it */ @@ -4027,9 +4226,9 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) POINT pt; static int recursive = 0; HMENU hmenu; - BOOLEAN needsRedraw = FALSE; + BOOLEAN needsRedraw = FALSE; BOOLEAN saveAnimate; - BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */ + BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */ static BOOLEAN sameAgain = FALSE; if (recursive) { @@ -4049,10 +4248,10 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) x = EventToSquare(pt.x - boardRect.left); y = EventToSquare(pt.y - boardRect.top); if (!flipView && y >= 0) { - y = BOARD_SIZE - 1 - y; + y = BOARD_HEIGHT - 1 - y; } if (flipView && x >= 0) { - x = BOARD_SIZE - 1 - x; + x = BOARD_WIDTH - 1 - x; } switch (message) { @@ -4078,7 +4277,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } if (!appData.highlightLastMove) { ClearHighlights(); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } fromX = fromY = -1; dragInfo.start.x = dragInfo.start.y = -1; @@ -4089,18 +4288,18 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } else if (fromX == x && fromY == y) { /* Downclick on same square again */ ClearHighlights(); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); sameAgain = TRUE; } else if (fromX != -1) { /* Downclick on different square */ ChessSquare pdown, pup; pdown = boards[currentMove][fromY][fromX]; pup = boards[currentMove][y][x]; - if (gameMode == EditPosition || - !((WhitePawn <= pdown && pdown <= WhiteKing && - WhitePawn <= pup && pup <= WhiteKing) || - (BlackPawn <= pdown && pdown <= BlackKing && - BlackPawn <= pup && pup <= BlackKing))) { + if (gameMode == EditPosition || /* [HGM] max piece > King! */ + !((WhitePawn <= pdown && pdown < BlackPawn && + WhitePawn <= pup && pup < BlackPawn) || + (BlackPawn <= pdown && pdown < EmptySquare && + BlackPawn <= pup && pup < EmptySquare))) { /* EditPosition, empty square, or different color piece; click-click move is possible */ toX = x; @@ -4110,11 +4309,11 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) UserMoveEvent(fromX, fromY, toX, toY, 'q'); if (!appData.highlightLastMove) { ClearHighlights(); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } } else { SetHighlights(fromX, fromY, toX, toY); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); PromotionPopup(hwnd); } } else { /* not a promotion */ @@ -4126,7 +4325,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR); if (appData.animate && !appData.highlightLastMove) { ClearHighlights(); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } } if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); @@ -4134,7 +4333,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; } ClearHighlights(); - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } /* First downclick, or restart on a square with same color piece */ if (!frozen && OKToStartUserMove(x, y)) { @@ -4149,7 +4348,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = fromY = -1; dragInfo.start.x = dragInfo.start.y = -1; dragInfo.from = dragInfo.start; - DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */ + DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */ } break; @@ -4168,7 +4367,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) /* First square clicked: start click-click move */ SetHighlights(fromX, fromY, -1, -1); } - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) { /* Errant click; ignore */ break; @@ -4183,7 +4382,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (appData.alwaysPromoteToQueen) { UserMoveEvent(fromX, fromY, toX, toY, 'q'); } else { - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); PromotionPopup(hwnd); } } else { @@ -4197,7 +4396,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } if (appData.animate || appData.animateDragging || appData.highlightDragging || gotPremove) { - DrawPosition(forceFullRepaint || FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } } dragInfo.start.x = dragInfo.start.y = -1; @@ -4207,22 +4406,22 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_MOUSEMOVE: if ((appData.animateDragging || appData.highlightDragging) && (wParam & MK_LBUTTON) - && dragInfo.from.x >= 0) - { - BOOL full_repaint = FALSE; - + && dragInfo.from.x >= 0) + { + BOOL full_repaint = FALSE; + if (appData.animateDragging) { dragInfo.pos = pt; } if (appData.highlightDragging) { SetHighlights(fromX, fromY, x, y); - 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) ) { + full_repaint = TRUE; + } } - } - - DrawPosition( full_repaint, NULL); - + + DrawPosition( full_repaint, NULL); + dragInfo.lastpos = dragInfo.pos; } break; @@ -4368,6 +4567,17 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) (!appData.testLegality || gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) ? SW_SHOW : SW_HIDE); +#ifdef FAIRY + /* [HGM] Only allow C & A promotions in Capablanca Chess */ + ShowWindow(GetDlgItem(hDlg, PB_Archbishop), + (gameInfo.variant == VariantCapablanca || + gameInfo.variant == VariantGothic) ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, PB_Chancellor), + (gameInfo.variant == VariantCapablanca || + gameInfo.variant == VariantGothic) ? + SW_SHOW : SW_HIDE); +#endif return TRUE; case WM_COMMAND: /* message: received a command */ @@ -4389,6 +4599,14 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case PB_Bishop: promoChar = 'b'; break; +#ifdef FAIRY + case PB_Chancellor: + promoChar = 'c'; + break; + case PB_Archbishop: + promoChar = 'a'; + break; +#endif case PB_Knight: promoChar = 'n'; break; @@ -4510,7 +4728,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) FILE *f; UINT number; char fileTitle[MSG_SIZ]; - static SnapData sd; + static SnapData sd; switch (message) { @@ -4606,13 +4824,13 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) AnalysisPopDown(); break; - case IDM_NewGameFRC: - if( NewGameFRC() == 0 ) { - ResetGameEvent(); - AnalysisPopDown(); - } - break; - + case IDM_NewGameFRC: + if( NewGameFRC() == 0 ) { + ResetGameEvent(); + AnalysisPopDown(); + } + break; + case IDM_LoadGame: LoadGameDialog(hwnd, "Load Game from File"); break; @@ -4685,63 +4903,63 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) PasteGameFromClipboard(); break; - 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: + 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_CopyPosition: CopyFENToClipboard(); break; @@ -4953,21 +5171,21 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_GeneralOptions: GeneralOptionsPopup(hwnd); - DrawPosition(TRUE, NULL); + DrawPosition(TRUE, NULL); break; case IDM_BoardOptions: BoardOptionsPopup(hwnd); break; - case IDM_EnginePlayOptions: - EnginePlayOptionsPopup(hwnd); - break; - - case IDM_OptionsUCI: - UciOptionsPopup(hwnd); - break; - + case IDM_EnginePlayOptions: + EnginePlayOptionsPopup(hwnd); + break; + + case IDM_OptionsUCI: + UciOptionsPopup(hwnd); + break; + case IDM_IcsOptions: IcsOptionsPopup(hwnd); break; @@ -5025,7 +5243,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) char dir[MSG_SIZ]; GetCurrentDirectory(MSG_SIZ, dir); SetCurrentDirectory(installDir); - debugFP = fopen(appData.nameOfDebugFile, "w"); + debugFP = fopen(appData.nameOfDebugFile, "w"); SetCurrentDirectory(dir); setbuf(debugFP, NULL); } else { @@ -5218,31 +5436,31 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) InputEvent(hwnd, message, wParam, lParam); break; - /* [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 */ + 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 */ case WM_ENTERSIZEMOVE: if (hwnd == hwndMain) { doingSizing = TRUE; lastSizing = 0; } - return OnEnterSizeMove( &sd, hwnd, wParam, lParam ); + return OnEnterSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_SIZING: @@ -5251,9 +5469,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; - case WM_MOVING: - return OnMoving( &sd, hwnd, wParam, lParam ); - + case WM_MOVING: + return OnMoving( &sd, hwnd, wParam, lParam ); + case WM_EXITSIZEMOVE: if (hwnd == hwndMain) { RECT client; @@ -5263,7 +5481,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ResizeBoard(client.right, client.bottom, lastSizing); lastSizing = 0; } - return OnExitSizeMove( &sd, hwnd, wParam, lParam ); + return OnExitSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_DESTROY: /* message: window being destroyed */ @@ -5656,7 +5874,7 @@ ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, EndDeferWindowPos(cl.hdwp); } -BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode) +BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode) { RECT rChild, rParent; int wChild, hChild, wParent, hParent; @@ -5688,13 +5906,13 @@ BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode) } /* Calculate new Y position, then adjust for screen */ - if( mode == 0 ) { - yNew = rParent.top + ((hParent - hChild) /2); - } - else { - yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3; - } - + if( mode == 0 ) { + yNew = rParent.top + ((hParent - hChild) /2); + } + else { + yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3; + } + if (yNew < 0) { yNew = 0; } else if ((yNew+hChild) > hScreen) { @@ -5706,12 +5924,12 @@ BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode) xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } -/* Center one window over another */ -BOOL CenterWindow (HWND hwndChild, HWND hwndParent) -{ - return CenterWindowEx( hwndChild, hwndParent, 0 ); -} - +/* Center one window over another */ +BOOL CenterWindow (HWND hwndChild, HWND hwndParent) +{ + return CenterWindowEx( hwndChild, hwndParent, 0 ); +} + /*---------------------------------------------------------------------------*\ * * Startup Dialog functions @@ -5847,17 +6065,17 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0); SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf); } - - if (appData.icsActive) { + + if (appData.icsActive) { CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED); - } - else if (appData.noChessProgram) { + } + else if (appData.noChessProgram) { CheckDlgButton(hDlg, OPT_View, BST_CHECKED); } - else { - CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED); - } - + else { + CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED); + } + SetStartupDialogEnables(hDlg); return TRUE; @@ -6132,7 +6350,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: move[0] = (char) lParam; move[1] = NULLCHAR; - CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 ); + CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 ); hInput = GetDlgItem(hDlg, OPT_Move); SetWindowText(hInput, move); SetFocus(hInput); @@ -6251,21 +6469,21 @@ ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) switch (message) { case WM_INITDIALOG: GetWindowRect(hDlg, &rChild); - - /* + + /* SetWindowPos(hDlg, NULL, rChild.left, rChild.top + boardRect.top - (rChild.bottom - rChild.top), 0, 0, SWP_NOZORDER|SWP_NOSIZE); - */ - - /* - [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); - + */ + + /* + [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); + errorDialog = hDlg; SetWindowText(hDlg, errorTitle); hwndText = GetDlgItem(hDlg, OPT_ErrorText); @@ -6288,6 +6506,66 @@ ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) return FALSE; } +#ifdef GOTHIC +LRESULT CALLBACK +GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HANDLE hwndText; + RECT rChild; + int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME); + + switch (message) { + case WM_INITDIALOG: + GetWindowRect(hDlg, &rChild); + + SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height, + SWP_NOZORDER); + + /* + [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. + */ + + SetWindowText(hDlg, errorTitle); + hwndText = GetDlgItem(hDlg, OPT_ErrorText); + SetDlgItemText(hDlg, OPT_ErrorText, errorMessage); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + if (errorDialog == hDlg) errorDialog = NULL; + DestroyWindow(hDlg); + return TRUE; + + default: + break; + } + break; + } + return FALSE; +} + +VOID +GothicPopUp(char *title) +{ + FARPROC lpProc; + char *p, *q; + BOOLEAN modal = hwndMain == NULL; + + strncpy(errorTitle, title, sizeof(errorTitle)); + errorTitle[sizeof(errorTitle) - 1] = '\0'; + + lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst); + CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error), + hwndMain, (DLGPROC)lpProc); + FreeProcInstance(lpProc); +} +#endif + /*---------------------------------------------------------------------------*\ * * Ics Interaction console functions @@ -6791,7 +7069,7 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { - static SnapData sd; + static SnapData sd; static HWND hText, hInput, hFocus; InputSource *is = consoleInputSource; RECT rect; @@ -6878,21 +7156,21 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) mmi->ptMinTrackSize.x = 100; mmi->ptMinTrackSize.y = 100; break; - - /* [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 ); - } - + + /* [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 ); + } + return DefWindowProc(hDlg, message, wParam, lParam); } @@ -7001,7 +7279,7 @@ ConsoleOutput(char* data, int length, int forceVisible) void DisplayAClock(HDC hdc, int timeRemaining, int highlight, - RECT *rect, char *color) + RECT *rect, char *color, char *flagFell) { char buf[100]; char *str; @@ -7010,9 +7288,9 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight, if (appData.clockMode) { if (tinyLayout) - sprintf(buf, "%c %s", color[0], TimeString(timeRemaining)); + sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell); else - sprintf(buf, "%s: %s", color, TimeString(timeRemaining)); + sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell); str = buf; } else { str = color; @@ -7043,15 +7321,15 @@ DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount, { int ok, err; - /* [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] */ + 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; + } + ResetEvent(ovl->hEvent); ovl->Offset = ovl->OffsetHigh = 0; ok = ReadFile(hFile, buf, count, outCount, ovl); @@ -7094,28 +7372,28 @@ DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount, return err; } -/* [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 */ +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; + } + } +} DWORD InputThread(LPVOID arg) @@ -7138,27 +7416,27 @@ InputThread(LPVOID arg) is->count = 0; } else { is->count = (DWORD) -1; - /* [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! */ + break; } } - - CheckForInputBufferFull( is ); - + + CheckForInputBufferFull( is ); + SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); - - if( is->count == ((DWORD) -1) ) break; /* [AS] */ - + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count <= 0) break; /* Quit on EOF or error */ } - + CloseHandle(ovl.hEvent); CloseHandle(is->hFile); - - if (appData.debugMode) { - fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count ); - } - + + if (appData.debugMode) { + fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count ); + } + return 0; } @@ -7208,13 +7486,13 @@ NonOvlInputThread(LPVOID arg) is->count = (DWORD) -1; } } - - CheckForInputBufferFull( is ); - + + CheckForInputBufferFull( is ); + SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); - - if( is->count == ((DWORD) -1) ) break; /* [AS] */ - + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count < 0) break; /* Quit on error */ } CloseHandle(is->hFile); @@ -7241,9 +7519,9 @@ SocketInputThread(LPVOID arg) } } SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); - - if( is->count == ((DWORD) -1) ) break; /* [AS] */ - + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count <= 0) break; /* Quit on EOF or error */ } return 0; @@ -7265,14 +7543,14 @@ InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) p = q; } } - + /* Move any partial line to the start of the buffer */ q = is->buf; while (p < is->next) { *q++ = *p++; } is->next = q; - + if (is->error != NO_ERROR || is->count == 0) { /* Notify backend of the error. Note: If there was a partial line at the end, it is not flushed through. */ @@ -7817,271 +8095,271 @@ AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr) FreeProcInstance(lpProc); } -/* [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 */ +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; +} + VOID DisplayIcsInteractionTitle(char *str) @@ -8263,8 +8541,10 @@ DisplayWhiteClock(long timeRemaining, int highlight) { HDC hdc; hdc = GetDC(hwndMain); + char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + if (!IsIconic(hwndMain)) { - DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White"); + DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White", flag); } if (highlight && iconCurrent == iconBlack) { iconCurrent = iconWhite; @@ -8282,9 +8562,11 @@ void DisplayBlackClock(long timeRemaining, int highlight) { HDC hdc; + char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + hdc = GetDC(hwndMain); if (!IsIconic(hwndMain)) { - DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black"); + DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black", flag); } if (highlight && iconCurrent == iconWhite) { iconCurrent = iconBlack; @@ -8535,27 +8817,27 @@ DestroyChildProcess(ProcRef pr, int/*boolean*/ signal) we could arrange for this even though neither WinBoard nor the chess program uses a console for stdio? */ /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/ - - /* [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 ); - } - } - + + /* [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 ); + } + } + CloseHandle(cp->hProcess); break; @@ -8942,7 +9224,7 @@ InputSourceRef AddInputSource(ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure) { - InputSource *is, *is2 = NULL; + InputSource *is, *is2 = NULL; ChildProc *cp = (ChildProc *) pr; is = (InputSource *) calloc(1, sizeof(InputSource)); @@ -8956,18 +9238,18 @@ AddInputSource(ProcRef pr, int lineByLine, consoleInputSource = is; } else { is->kind = cp->kind; - /* - [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. - */ + /* + [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. + */ switch (cp->kind) { case CPReal: is->hFile = cp->hFrom; cp->hFrom = NULL; /* now owned by InputThread */ is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPComm: @@ -8975,14 +9257,14 @@ AddInputSource(ProcRef pr, int lineByLine, cp->hFrom = NULL; /* now owned by InputThread */ is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPSock: is->sock = cp->sock; is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPRcmd: @@ -8994,22 +9276,22 @@ AddInputSource(ProcRef pr, int lineByLine, is2->second = is2; is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); is2->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is2, CREATE_SUSPENDED, &is2->id); + (LPVOID) is2, CREATE_SUSPENDED, &is2->id); break; } - - if( is->hThread != NULL ) { - ResumeThread( is->hThread ); + + if( is->hThread != NULL ) { + ResumeThread( is->hThread ); + } + + if( is2 != NULL && is2->hThread != NULL ) { + ResumeThread( is2->hThread ); + } } - - if( is2 != NULL && is2->hThread != NULL ) { - ResumeThread( is2->hThread ); - } - } - + return (InputSourceRef) is; } @@ -9204,10 +9486,10 @@ AnalysisPopUp(char* title, char* str) FARPROC lpProc; char *p, *q; - /* [AS] */ - EngineOutputPopUp(); - return; - + /* [AS] */ + EngineOutputPopUp(); + return; + if (str == NULL) str = ""; p = (char *) malloc(2 * strlen(str) + 2); q = p; @@ -9368,11 +9650,11 @@ ScreenSquare(column, row, pt) int column; int row; POINT * pt; { if (flipView) { - pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap); + pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap); pt->y = lineGap + row * (squareSize + lineGap); } else { pt->x = lineGap + column * (squareSize + lineGap); - pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap); + pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap); } } @@ -9420,32 +9702,32 @@ Tween(start, mid, finish, factor, frames, nFrames) } void -HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current ) -{ -#if 0 - char buf[256]; - - sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n", - first, last, current, current >= 0 ? movelist[current] : "n/a" ); - - 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 ) +{ +#if 0 + char buf[256]; + + sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n", + first, last, current, current >= 0 ? movelist[current] : "n/a" ); + + 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 ); +} diff --git a/winboard/winboard.rc b/winboard/winboard.rc index 07926b7..452d2cc 100644 --- a/winboard/winboard.rc +++ b/winboard/winboard.rc @@ -30,27 +30,28 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Dialog // -ABOUTBOX DIALOG DISCARDABLE 22, 17, 167, 67 +ABOUTBOX DIALOG DISCARDABLE 22, 17, 180, 108 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About WinBoard" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,131,47,32,14,WS_GROUP -#if __GNUC__ - ICON icon_white,-1,3,6,20,20,0 -#else - ICON "icon_white",-1,3,6,20,20,0 -#endif + DEFPUSHBUTTON "OK",IDOK,126,89,50,14,WS_GROUP + ICON "icon_white",IDC_STATIC,3,6,20,20 LTEXT "Chessboard for Windows",400,25,15,121,8 LTEXT "Copyright 1991 Digital Equipment Corporation",201,6,34, 149,8 LTEXT "Enhancements Copyright 1992-2003 Free Software Foundation", - OPT_TCtext1,6,44,121,17 + OPT_TCtext1,6,45,121,17 CONTROL "",OPT_TCTime,"Static",SS_BLACKRECT,4,28,159,1 - LTEXT "WinBoard 0.0.0",ABOUTBOX_Version,25,5,142,8 + LTEXT "WinBoard 0.0.0",ABOUTBOX_Version,25,5,61,8 + LTEXT "Enhancements Copyright 2005\r\nAlessandro Scotti", + IDC_STATIC,6,65,120,24 + LTEXT "Enhancements Copyright 2007\r\nH.G. Muller", + IDC_STATIC,6,85,120,24 + LTEXT "(unofficial version ""F"")",IDC_STATIC,88,4,71,8 END -DLG_TimeControl DIALOG DISCARDABLE 6, 18, 147, 113 +DLG_TimeControl DIALOG DISCARDABLE 6, 18, 174, 98 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Time Control" FONT 8, "MS Sans Serif" @@ -59,33 +60,32 @@ BEGIN BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,6,6,113,10 CONTROL "Incremental clock",OPT_TCUseInc,"Button", BS_AUTORADIOBUTTON | WS_TABSTOP,6,42,107,10 - EDITTEXT OPT_TCMoves,14,20,22,12,ES_AUTOHSCROLL | WS_GROUP - LTEXT "moves in",OPT_TCtext1,40,22,30,8,NOT WS_GROUP - EDITTEXT OPT_TCTime,74,20,32,12,ES_AUTOHSCROLL - LTEXT "minutes",OPT_TCtext2,111,22,26,8,NOT WS_GROUP - EDITTEXT OPT_TCTime2,14,56,32,12,ES_AUTOHSCROLL | WS_GROUP - LTEXT "minutes initially,",405,51,57,73,8,NOT WS_GROUP - LTEXT "plus",406,19,74,15,8,NOT WS_GROUP - EDITTEXT OPT_TCInc,37,72,32,12,ES_AUTOHSCROLL - LTEXT "seconds per move",408,74,74,67,8,NOT WS_GROUP - PUSHBUTTON "OK",IDOK,32,95,40,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,88,95,40,14 + EDITTEXT OPT_TCMoves,14,20,26,12,ES_AUTOHSCROLL | WS_GROUP + LTEXT "moves in",OPT_TCtext1,44,22,30,8,NOT WS_GROUP + EDITTEXT OPT_TCTime,78,20,26,12,ES_AUTOHSCROLL + LTEXT "minutes",OPT_TCtext2,108,22,26,8,NOT WS_GROUP + EDITTEXT OPT_TCTime2,14,56,26,12,ES_AUTOHSCROLL | WS_GROUP + LTEXT "minutes +",405,44,58,34,8,NOT WS_GROUP + EDITTEXT OPT_TCInc,78,56,26,12,ES_AUTOHSCROLL + LTEXT "seconds per move",408,108,58,62,8,NOT WS_GROUP + PUSHBUTTON "OK",IDOK,64,80,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,120,80,50,14 END -DLG_LoadOptions DIALOG DISCARDABLE 10, 18, 144, 55 +DLG_LoadOptions DIALOG DISCARDABLE 10, 18, 136, 55 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Load Game Options" FONT 8, "MS Sans Serif" BEGIN CONTROL "Load games with automatic stepping",OPT_Autostep,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,136,10 - EDITTEXT OPT_ASTimeDelay,23,18,28,12,ES_AUTOHSCROLL - LTEXT "seconds per move",OPT_AStext1,57,20,60,8,NOT WS_GROUP - PUSHBUTTON "OK",IDOK,24,37,40,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,80,37,40,14 + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,130,10 + EDITTEXT OPT_ASTimeDelay,16,16,28,14,ES_AUTOHSCROLL + LTEXT "seconds per move",OPT_AStext1,46,20,60,8,NOT WS_GROUP + PUSHBUTTON "OK",IDOK,26,36,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,82,36,50,14 END -DLG_SaveOptions DIALOG DISCARDABLE 6, 17, 133, 119 +DLG_SaveOptions DIALOG DISCARDABLE 6, 17, 178, 119 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Save Game Options" FONT 8, "MS Sans Serif" @@ -93,17 +93,19 @@ BEGIN CONTROL "Save games automatically",OPT_Autosave,"Button", BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,97,10 CONTROL "Prompt for filename",OPT_AVPrompt,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,18,75,10 - CONTROL "To file:",OPT_AVToFile,"Button",BS_AUTORADIOBUTTON,18, - 31,36,10 - EDITTEXT OPT_AVFilename,18,44,97,12,ES_AUTOHSCROLL - GROUPBOX "Save Style",801,4,63,125,28,WS_GROUP - CONTROL "PGN",OPT_PGN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP, - 18,75,39,10 - CONTROL "Old",OPT_Old,"Button",BS_AUTORADIOBUTTON,73,75,39,10 - PUSHBUTTON "OK",IDOK,18,98,40,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,74,98,40,14 - PUSHBUTTON "Browse...",OPT_AVBrowse,76,31,39,14 + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,18,75,10 + CONTROL "To file:",OPT_AVToFile,"Button",BS_AUTORADIOBUTTON,16, + 32,36,10 + EDITTEXT OPT_AVFilename,54,30,97,14,ES_AUTOHSCROLL + PUSHBUTTON "...",OPT_AVBrowse,156,30,18,14 + GROUPBOX "Save As: ",801,4,48,170,28,WS_GROUP + CONTROL "PGN",OPT_PGN,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8, + 60,39,10 + CONTROL "Old",OPT_Old,"Button",BS_AUTORADIOBUTTON,54,60,114,10 + CONTROL "Save out of book info in PGN",OPT_OutOfBookInfo,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,82,109,10 + PUSHBUTTON "OK",IDOK,68,100,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,124,100,50,14 END 1536 DIALOG DISCARDABLE 36, 24, 264, 134 @@ -136,46 +138,46 @@ BEGIN PUSHBUTTON "Net&work...",1037,208,113,50,14,WS_GROUP END -DLG_CommPort DIALOG DISCARDABLE 25, 30, 159, 98 +DLG_CommPort DIALOG DISCARDABLE 25, 30, 220, 79 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Communication Port Settings" FONT 8, "Helv" BEGIN + PUSHBUTTON "OK",IDOK,106,60,50,14 + PUSHBUTTON "Cancel",IDCANCEL,164,60,50,14 RTEXT "&Port:",-1,4,6,40,10 COMBOBOX OPT_Port,49,4,55,60,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP - RTEXT "Data &Rate:",-1,4,21,40,10,NOT WS_GROUP - COMBOBOX OPT_DataRate,49,19,55,100,CBS_DROPDOWN | WS_VSCROLL | + RTEXT "Data &Rate:",-1,114,6,40,10,NOT WS_GROUP + COMBOBOX OPT_DataRate,159,4,55,100,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP - RTEXT "Data &Bits:",-1,4,36,40,10,NOT WS_GROUP - COMBOBOX OPT_Bits,49,34,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | + RTEXT "Data &Bits:",-1,4,25,40,10,NOT WS_GROUP + COMBOBOX OPT_Bits,49,22,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP - RTEXT "P&arity:",-1,4,51,40,10,NOT WS_GROUP - COMBOBOX OPT_Parity,49,49,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | - WS_VSCROLL | WS_TABSTOP - RTEXT "&Stop Bits:",-1,4,66,40,10,NOT WS_GROUP - COMBOBOX OPT_StopBits,49,64,55,60,CBS_DROPDOWNLIST | + RTEXT "P&arity:",-1,114,24,40,10,NOT WS_GROUP + COMBOBOX OPT_Parity,159,22,55,60,CBS_DROPDOWNLIST | + CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + RTEXT "&Stop Bits:",-1,4,42,40,10,NOT WS_GROUP + COMBOBOX OPT_StopBits,49,40,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP - RTEXT "F&low:",-1,4,81,40,10,NOT WS_GROUP - DEFPUSHBUTTON "OK",IDOK,115,4,40,14 - PUSHBUTTON "Cancel",IDCANCEL,115,24,40,14 - COMBOBOX OPT_Flow,49,79,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | + RTEXT "F&low:",-1,114,42,40,10,NOT WS_GROUP + COMBOBOX OPT_Flow,159,40,55,60,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Help",OPT_SerialHelp,115,80,40,14,NOT WS_VISIBLE + PUSHBUTTON "Help",OPT_SerialHelp,4,60,50,14,NOT WS_VISIBLE END -DLG_EditComment DIALOG DISCARDABLE 6, 18, 306, 104 +DLG_EditComment DIALOG DISCARDABLE 6, 18, 302, 102 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Edit Comment" FONT 8, "MS Sans Serif" BEGIN - PUSHBUTTON "OK",IDOK,61,86,40,14 - PUSHBUTTON "Cancel",OPT_CancelComment,109,86,40,14 - PUSHBUTTON "&Clear",OPT_ClearComment,157,86,40,14 - PUSHBUTTON "&Edit",OPT_EditComment,205,86,40,14 + PUSHBUTTON "OK",IDOK,194,84,50,14 + PUSHBUTTON "Cancel",OPT_CancelComment,250,84,50,14 CONTROL "",OPT_CommentText,"RICHEDIT",ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_BORDER | WS_VSCROLL | - WS_HSCROLL | WS_TABSTOP,4,4,298,78 + WS_HSCROLL | WS_TABSTOP,2,2,298,78 + PUSHBUTTON "&Clear",OPT_ClearComment,2,84,50,14 + PUSHBUTTON "&Edit",OPT_EditComment,58,84,50,14 END DLG_PromotionKing DIALOG DISCARDABLE 98, 90, 183, 41 @@ -189,70 +191,71 @@ BEGIN PUSHBUTTON "&Rook",PB_Rook,49,23,40,14,NOT WS_TABSTOP PUSHBUTTON "&Bishop",PB_Bishop,94,23,40,14,NOT WS_TABSTOP PUSHBUTTON "K&night",PB_Knight,139,23,40,14,NOT WS_TABSTOP - PUSHBUTTON "&King",PB_King,4,4,40,14,NOT WS_TABSTOP + PUSHBUTTON "&King",PB_King,94,4,40,14,NOT WS_TABSTOP PUSHBUTTON "&Cancel",IDCANCEL,139,4,40,14,WS_GROUP END -ABOUTBOX2 DIALOG DISCARDABLE 22, 17, 281, 198 +ABOUTBOX2 DIALOG DISCARDABLE 22, 17, 281, 223 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About WinBoard" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,244,180,32,14,WS_GROUP RTEXT "Chessboard for Windows",DLG_TimeControl,196,154,80,8 - LTEXT "Copyright 1991 Digital Equipment Corporation",201,4,167, + LTEXT "Copyright 1991 Digital Equipment Corporation",201,4,168, 151,8 LTEXT "Enhancements Copyright 1992-2003 Free Software Foundation", - OPT_TCtext1,4,177,126,17 + OPT_TCtext1,4,179,126,17 CONTROL "",OPT_TCTime,"Static",SS_BLACKRECT,4,164,272,1 LTEXT "WinBoard 0.0.0",ABOUTBOX_Version,4,154,64,8 CONTROL "galactic",IDC_STATIC,"Static",SS_BITMAP,4,4,15,13 + DEFPUSHBUTTON "OK",IDOK,226,204,50,14 + LTEXT "Enhancements Copyright 2005\r\nAlessandro Scotti", + IDC_STATIC,4,199,183,20 + LTEXT "(unofficial version ""X"")",IDC_STATIC,68,154,71,8 END -DLG_GameList DIALOG DISCARDABLE 6, 18, 307, 159 +DLG_GameList DIALOG DISCARDABLE 6, 18, 259, 153 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Game List" FONT 8, "MS Sans Serif" BEGIN - LISTBOX OPT_GameListText,4,4,299,130,LBS_NOINTEGRALHEIGHT | + LISTBOX OPT_GameListText,2,2,254,130,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP - PUSHBUTTON "&Load",OPT_GameListLoad,61,138,40,15 - PUSHBUTTON "&Prev",OPT_GameListPrev,109,138,40,15 - PUSHBUTTON "&Next",OPT_GameListNext,157,138,40,15 - PUSHBUTTON "&Close",OPT_GameListClose,205,138,40,15 + PUSHBUTTON "&Load",OPT_GameListLoad,2,135,32,15 + PUSHBUTTON "&<",OPT_GameListPrev,38,135,22,15 + PUSHBUTTON "&>",OPT_GameListNext,64,135,24,15 + PUSHBUTTON "&Close",OPT_GameListClose,92,135,32,15 + PUSHBUTTON "Filter",IDC_GameListDoFilter,144,136,30,14 + EDITTEXT IDC_GameListFilter,178,136,78,14,ES_AUTOHSCROLL END -DLG_EditTags DIALOG DISCARDABLE 6, 18, 160, 141 +DLG_EditTags DIALOG DISCARDABLE 6, 18, 167, 140 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Edit Tags" FONT 8, "MS Sans Serif" BEGIN - PUSHBUTTON "OK",IDOK,12,123,40,14 - PUSHBUTTON "Cancel",OPT_TagsCancel,60,123,40,14 - PUSHBUTTON "&Edit",OPT_EditTags,108,123,40,14 + PUSHBUTTON "OK",IDOK,58,122,50,14 + PUSHBUTTON "Cancel",OPT_TagsCancel,114,122,50,14 CONTROL "",OPT_TagsText,"RICHEDIT",ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_BORDER | WS_VSCROLL | - WS_HSCROLL | WS_TABSTOP,4,4,152,115 + WS_HSCROLL | WS_TABSTOP,2,2,162,115 + PUSHBUTTON "&Edit",OPT_EditTags,2,122,50,14 END WBCONSOLE DIALOG DISCARDABLE 0, 0, 335, 133 STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "ICS Interaction" -#if __GNUC__ -CLASS WBConsole -#else CLASS "WBConsole" -#endif FONT 8, "Courier New" BEGIN CONTROL "",OPT_ConsoleText,"RICHEDIT",ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL | ES_READONLY | ES_NUMBER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,0,0,335,119 - CONTROL "",OPT_ConsoleInput,"RICHEDIT",ES_AUTOHSCROLL | - ES_AUTOVSCROLL | ES_MULTILINE | - ES_NOHIDESEL | ES_NUMBER | WS_BORDER | - WS_TABSTOP,0,120,335,13,WS_EX_TRANSPARENT + CONTROL "",OPT_ConsoleInput,"RICHEDIT",ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | + ES_NUMBER | WS_BORDER | WS_TABSTOP,0,120,335,13, + WS_EX_TRANSPARENT END DLG_Analysis DIALOG DISCARDABLE 0, 0, 294, 62 @@ -266,47 +269,47 @@ BEGIN WS_HSCROLL | WS_TABSTOP,4,4,286,54 END -DLG_Error DIALOG DISCARDABLE 0, 0, 183, 33 +DLG_Error DIALOG DISCARDABLE 0, 0, 220, 66 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Error" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,163,9,16,14 - ICON 32515,IDC_STATIC,4,6,20,20 - LTEXT "Sorry Charlie",OPT_ErrorText,27,4,130,25 + ICON 32515,IDC_STATIC,4,4,21,20 + LTEXT "Sorry Charlie",OPT_ErrorText,28,4,188,36 + DEFPUSHBUTTON "OK",IDOK,84,48,50,14 END -DLG_Colorize DIALOGEX 0, 0, 183, 52 +DLG_Colorize DIALOGEX 0, 0, 174, 61 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ICS Interaction Colors" FONT 8, "MS Sans Serif" BEGIN - PUSHBUTTON "&Choose Color...",OPT_ChooseColor,15,29,51,14,WS_GROUP + DEFPUSHBUTTON "OK",IDOK,64,42,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,120,42,50,14 + PUSHBUTTON "&Color...",OPT_ChooseColor,119,4,51,14,WS_GROUP CONTROL "&Bold",OPT_Bold,"Button",BS_AUTOCHECKBOX | WS_GROUP | - WS_TABSTOP,93,4,30,10 - CONTROL "&Italic",OPT_Italic,"Button",BS_AUTOCHECKBOX,93,14,30, + WS_TABSTOP,4,24,30,10 + CONTROL "&Italic",OPT_Italic,"Button",BS_AUTOCHECKBOX,40,24,30, 10 - CONTROL "&Underline",OPT_Underline,"Button",BS_AUTOCHECKBOX,93, + CONTROL "&Underline",OPT_Underline,"Button",BS_AUTOCHECKBOX,76, 24,45,10 - CONTROL "&Strikeout",OPT_Strikeout,"Button",BS_AUTOCHECKBOX,93, - 34,42,10 - DEFPUSHBUTTON "OK",IDOK,145,7,31,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,145,24,31,14 + CONTROL "&Strikeout",OPT_Strikeout,"Button",BS_AUTOCHECKBOX,128, + 24,42,10 CONTROL "",OPT_Sample,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_GROUP,4,9,75,15,WS_EX_CLIENTEDGE + ES_READONLY | WS_GROUP,4,4,106,15,WS_EX_CLIENTEDGE END -DLG_Question DIALOG DISCARDABLE 0, 0, 187, 60 +DLG_Question DIALOG DISCARDABLE 0, 0, 187, 77 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Question" FONT 8, "MS Sans Serif" BEGIN - EDITTEXT OPT_QuestionInput,4,44,179,12,ES_AUTOHSCROLL - DEFPUSHBUTTON "Enter",IDOK,133,4,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,133,24,50,14 + EDITTEXT OPT_QuestionInput,4,36,179,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,78,58,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,134,58,50,14 LTEXT "Enter a chess engine command or just type something stupid that will completely screw things up.", - OPT_QuestionText,27,4,101,33 - ICON 32514,IDC_STATIC,4,13,20,20 + OPT_QuestionText,30,2,153,28 + ICON 32514,IDC_STATIC,4,4,20,20 END DLG_Startup DIALOG DISCARDABLE 0, 0, 276, 127 @@ -337,292 +340,427 @@ BEGIN END DLG_IndexNumber DIALOG DISCARDABLE 0, 0, 236, 18 -#if __GNUC__ -STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | - NOT WS_POPUP | NOT WS_BORDER | NOT WS_SYSMENU -#else STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS -#endif FONT 8, "MS Sans Serif" BEGIN LTEXT "Inde&x number:",IDC_STATIC,5,2,46,8 EDITTEXT OPT_IndexNumber,54,0,155,13,ES_AUTOHSCROLL END -DLG_TypeInMove DIALOG DISCARDABLE 0, 0, 186, 46 +DLG_TypeInMove DIALOG DISCARDABLE 0, 0, 206, 23 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Type in a move" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,129,7,50,14 - PUSHBUTTON "Cancel",IDCANCEL,129,24,50,14 - EDITTEXT OPT_Move,7,16,109,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,96,4,50,14 + PUSHBUTTON "Cancel",IDCANCEL,152,4,50,14 + EDITTEXT OPT_Move,4,4,86,13,ES_AUTOHSCROLL END -DLG_Sound DIALOG DISCARDABLE 0, 0, 257, 95 +DLG_Sound DIALOG DISCARDABLE 0, 0, 242, 105 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Sounds" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,199,6,47,14 - PUSHBUTTON "Cancel",IDCANCEL,199,23,47,14 - PUSHBUTTON "Defaults",OPT_DefaultSounds,199,40,47,14 - COMBOBOX CBO_Sounds,52,6,128,110,CBS_DROPDOWNLIST | WS_VSCROLL | + DEFPUSHBUTTON "OK",IDOK,132,86,50,14 + PUSHBUTTON "Cancel",IDCANCEL,188,86,50,14 + LTEXT "Event:",IDC_STATIC,4,7,26,9 + COMBOBOX CBO_Sounds,30,4,208,110,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "No sound",OPT_NoSound,"Button",BS_AUTORADIOBUTTON | - WS_GROUP | WS_TABSTOP,11,25,47,10 + WS_GROUP | WS_TABSTOP,12,22,47,10 CONTROL "Default beep",OPT_DefaultBeep,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,11,42,57,10 + BS_AUTORADIOBUTTON | WS_TABSTOP,12,36,57,10 CONTROL "Built-in sound:",OPT_BuiltInSound,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,11,59,60,10 - COMBOBOX OPT_BuiltInSoundName,78,58,103,109,CBS_DROPDOWNLIST | + BS_AUTORADIOBUTTON | WS_TABSTOP,12,51,60,10 + COMBOBOX OPT_BuiltInSoundName,76,48,103,109,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Play",OPT_PlaySound,200,57,47,14 + PUSHBUTTON "Play",OPT_PlaySound,188,47,50,14 CONTROL "WAV file:",OPT_WavFile,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,11,76,45,10 - EDITTEXT OPT_WavFileName,78,75,103,12,ES_AUTOHSCROLL - PUSHBUTTON "Browse...",OPT_BrowseSound,200,74,47,14 - LTEXT "Event:",IDC_STATIC,19,9,26,9 + WS_TABSTOP,12,66,45,10 + EDITTEXT OPT_WavFileName,76,65,103,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",OPT_BrowseSound,188,64,50,14 + PUSHBUTTON "Defaults",OPT_DefaultSounds,6,86,50,14 END -DLG_GeneralOptions DIALOG DISCARDABLE 0, 0, 271, 135 +DLG_GeneralOptions DIALOG DISCARDABLE 0, 0, 220, 183 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "General Options" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,207,7,50,14 - PUSHBUTTON "Cancel",IDCANCEL,208,25,50,14 + DEFPUSHBUTTON "OK",IDOK,110,164,50,14 + PUSHBUTTON "Cancel",IDCANCEL,166,164,50,14 CONTROL "Always on &Top",OPT_AlwaysOnTop,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,6,79,10 - CONTROL "Always &Queen",OPT_AlwaysQueen,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,16,20,79,10 - CONTROL "Animate &Dragging",OPT_AnimateDragging,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,34,79,10 - CONTROL "&Animate Moving",OPT_AnimateMoving,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,48,79,10 - CONTROL "Auto &Flag",OPT_AutoFlag,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,16,62,79,10 - CONTROL "Auto Flip &View",OPT_AutoFlipView,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,76,79,10 - CONTROL "Auto &Raise Board",OPT_AutoRaiseBoard,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,90,71,10 - CONTROL "&Blindfold",OPT_Blindfold,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,16,104,79,10 - CONTROL "&Highlight Dragging",OPT_HighlightDragging,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,118,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,4,6,79,10 CONTROL "Highlight Last &Move",OPT_HighlightLastMove,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,6,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,6,79,10 + CONTROL "Always &Queen",OPT_AlwaysQueen,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,20,79,10 CONTROL "Periodic &Updates",OPT_PeriodicUpdates,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,20,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,20,79,10 + CONTROL "Animate &Dragging",OPT_AnimateDragging,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,34,79,10 CONTROL "Ponder &Next Move",OPT_PonderNextMove,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,34,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,34,79,10 + CONTROL "&Animate Moving",OPT_AnimateMoving,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,48,79,10 CONTROL "&Popup Exit Message",OPT_PopupExitMessage,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,48,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,48,79,10 + CONTROL "Auto &Flag",OPT_AutoFlag,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,62,79,10 CONTROL "Popup Move &Errors",OPT_PopupMoveErrors,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,62,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,62,79,10 + CONTROL "Auto Flip &View",OPT_AutoFlipView,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,76,79,10 CONTROL "Show Butt&on Bar",OPT_ShowButtonBar,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,76,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,76,79,10 + CONTROL "Auto &Raise Board",OPT_AutoRaiseBoard,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,90,71,10 CONTROL "Show &Coordinates",OPT_ShowCoordinates,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,90,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,90,79,10 + CONTROL "&Blindfold",OPT_Blindfold,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,104,79,10 CONTROL "&Show Thinking",OPT_ShowThinking,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,104,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,104,79,10 + CONTROL "&Highlight Dragging",OPT_HighlightDragging,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,118,79,10 CONTROL "Test &Legality",OPT_TestLegality,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,109,118,79,10 + BS_AUTOCHECKBOX | WS_TABSTOP,116,118,79,10 + CONTROL "Extended PGN Info",OPT_SaveExtPGN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,132,79,10 + CONTROL "Hide Thinking from Human",OPT_HideThinkFromHuman,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,116,132,100,10 + CONTROL "Extra Info in Move History",OPT_ExtraInfoInMoveHistory, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,146,97,10 + CONTROL "Highlight Move with Arrow",OPT_HighlightMoveArrow, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,116,145,100,9 END -DLG_IcsOptions DIALOGEX 0, 0, 318, 271 +DLG_IcsOptions DIALOGEX 0, 0, 302, 255 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ICS Options" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,88,250,60,15 - PUSHBUTTON "Cancel",IDCANCEL,168,250,60,15 + DEFPUSHBUTTON "OK",IDOK,194,237,50,15 + PUSHBUTTON "Cancel",IDCANCEL,248,237,50,15 CONTROL "&Auto Comment",OPT_AutoComment,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,20,12,63,8 + WS_TABSTOP,10,12,63,8 CONTROL "Auto &Observe",OPT_AutoObserve,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,20,27,63,8 + WS_TABSTOP,10,25,63,8 CONTROL "&Get Move List",OPT_GetMoveList,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,20,42,63,8 + BS_AUTOCHECKBOX | WS_TABSTOP,10,38,63,8 CONTROL "&Local Line Editing",OPT_LocalLineEditing,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,20,57,84,8 + BS_AUTOCHECKBOX | WS_TABSTOP,10,51,84,8 CONTROL "&Quiet Play",OPT_QuietPlay,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,20,72,63,8 + WS_TABSTOP,10,64,63,8 CONTROL "&Premove",OPT_Premove,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,171,12,46,10 + WS_TABSTOP,160,12,46,10 CONTROL "&White first move",OPT_PremoveWhite,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,171,27,67,10 - EDITTEXT OPT_PremoveWhiteText,252,23,25,14,ES_AUTOHSCROLL + BS_AUTOCHECKBOX | WS_TABSTOP,160,26,67,10 + EDITTEXT OPT_PremoveWhiteText,236,22,25,14,ES_AUTOHSCROLL CONTROL "&Black first move",OPT_PremoveBlack,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,171,42,66,10 - EDITTEXT OPT_PremoveBlackText,252,39,25,14,ES_AUTOHSCROLL + BS_AUTOCHECKBOX | WS_TABSTOP,160,40,66,10 + EDITTEXT OPT_PremoveBlackText,236,38,25,14,ES_AUTOHSCROLL CONTROL "&Sound alarm at",OPT_IcsAlarm,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,171,72,63,10 - EDITTEXT OPT_IcsAlarmTime,243,70,20,13,ES_AUTOHSCROLL - LTEXT "seconds",IDC_STATIC,270,72,28,8 - PUSHBUTTON "Choose...",OPT_ChooseShoutColor,104,101,45,15 - PUSHBUTTON "Choose...",OPT_ChooseSShoutColor,104,121,45,15 - PUSHBUTTON "Choose...",OPT_ChooseChannel1Color,104,141,45,15 - PUSHBUTTON "Choose...",OPT_ChooseChannelColor,104,161,45,15 - PUSHBUTTON "Choose...",OPT_ChooseKibitzColor,104,181,45,15 - PUSHBUTTON "Choose...",OPT_ChooseTellColor,252,101,45,15 - PUSHBUTTON "Choose...",OPT_ChooseChallengeColor,252,121,45,15 - PUSHBUTTON "Choose...",OPT_ChooseRequestColor,252,141,45,15 - PUSHBUTTON "Choose...",OPT_ChooseSeekColor,252,161,45,15 - PUSHBUTTON "Choose...",OPT_ChooseNormalColor,252,181,45,15 - PUSHBUTTON "&Choose Background Color...",OPT_ChooseBackgroundColor, - 17,204,132,16 - PUSHBUTTON "&Default ICS Colors",OPT_DefaultColors,165,204,132,16 + WS_TABSTOP,160,72,63,10 + EDITTEXT OPT_IcsAlarmTime,236,68,26,14,ES_AUTOHSCROLL + LTEXT "seconds",IDC_STATIC,264,72,28,8 + PUSHBUTTON "Choose...",OPT_ChooseShoutColor,97,101,45,15 + PUSHBUTTON "Choose...",OPT_ChooseSShoutColor,97,121,45,15 + PUSHBUTTON "Choose...",OPT_ChooseChannel1Color,97,141,45,15 + PUSHBUTTON "Choose...",OPT_ChooseChannelColor,97,161,45,15 + PUSHBUTTON "Choose...",OPT_ChooseKibitzColor,97,181,45,15 + PUSHBUTTON "Choose...",OPT_ChooseTellColor,246,101,45,15 + PUSHBUTTON "Choose...",OPT_ChooseChallengeColor,246,121,45,15 + PUSHBUTTON "Choose...",OPT_ChooseRequestColor,246,141,45,15 + PUSHBUTTON "Choose...",OPT_ChooseSeekColor,246,161,45,15 + PUSHBUTTON "Choose...",OPT_ChooseNormalColor,246,181,45,15 + PUSHBUTTON "Background...",OPT_ChooseBackgroundColor,10,208,50,16 + PUSHBUTTON "&Defaults...",OPT_DefaultColors,68,208,50,16 CONTROL "Do ¬ colorize messages",OPT_DontColorize,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,110,225,97,10 + BS_AUTOCHECKBOX | WS_TABSTOP,192,212,97,10 CONTROL "",OPT_SampleShout,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,17,101,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,10,101,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleSShout,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,17,121,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,10,121,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleChannel1,"RICHEDIT",ES_CENTER | - ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,17, + ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,10, 141,75,15,WS_EX_CLIENTEDGE CONTROL "",OPT_SampleChannel,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,17,161,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,10,161,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleKibitz,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_GROUP,17,181,75,15,WS_EX_CLIENTEDGE + ES_READONLY | WS_GROUP,10,181,75,15,WS_EX_CLIENTEDGE CONTROL "",OPT_SampleTell,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,165,101,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,159,101,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleChallenge,"RICHEDIT",ES_CENTER | - ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,165, + ES_MULTILINE | ES_READONLY | WS_DISABLED | WS_GROUP,159, 121,75,15,WS_EX_CLIENTEDGE CONTROL "",OPT_SampleRequest,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,165,141,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,159,141,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleSeek,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,165,161,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,159,161,75,15, WS_EX_CLIENTEDGE CONTROL "",OPT_SampleNormal,"RICHEDIT",ES_CENTER | ES_MULTILINE | - ES_READONLY | WS_DISABLED | WS_GROUP,165,181,75,15, + ES_READONLY | WS_DISABLED | WS_GROUP,159,181,75,15, WS_EX_CLIENTEDGE - GROUPBOX "Interaction Colors",IDC_STATIC,10,90,295,150 - GROUPBOX "Premove",IDC_STATIC,159,0,146,59 - GROUPBOX "",IDC_STATIC,10,0,146,89 - GROUPBOX "Alarm",IDC_STATIC,159,59,146,30 + GROUPBOX "Interaction Colors",IDC_STATIC,4,90,294,140 + GROUPBOX "Premove",IDC_STATIC,154,0,144,56 + GROUPBOX "General",IDC_STATIC,4,0,146,88 + GROUPBOX "Alarm",IDC_STATIC,154,58,144,30 END -DLG_BoardOptions DIALOG DISCARDABLE 0, 0, 262, 250 +DLG_BoardOptions DIALOG DISCARDABLE 0, 0, 194, 250 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Board Options" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,205,10,50,14 - PUSHBUTTON "Cancel",IDCANCEL,205,35,50,14 + DEFPUSHBUTTON "OK",IDOK,84,232,50,14 + PUSHBUTTON "Cancel",IDCANCEL,140,232,50,14 CONTROL "&Tiny",OPT_SizeTiny,"Button",BS_AUTORADIOBUTTON | - WS_GROUP | WS_TABSTOP,15,15,50,10 - CONTROL "T&eeny",OPT_SizeTeeny,"Button",BS_AUTORADIOBUTTON,15,25, - 50,10 - CONTROL "&Dinky",OPT_SizeDinky,"Button",BS_AUTORADIOBUTTON,15,35, + WS_GROUP | WS_TABSTOP,9,14,50,10 + CONTROL "T&eeny",OPT_SizeTeeny,"Button",BS_AUTORADIOBUTTON,9,24, 50,10 - CONTROL "&Petite",OPT_SizePetite,"Button",BS_AUTORADIOBUTTON,15, - 45,50,10 - CONTROL "Sl&im",OPT_SizeSlim,"Button",BS_AUTORADIOBUTTON,15,55, + CONTROL "&Dinky",OPT_SizeDinky,"Button",BS_AUTORADIOBUTTON,9,34, 50,10 - CONTROL "&Small",OPT_SizeSmall,"Button",BS_AUTORADIOBUTTON,15,65, + CONTROL "&Petite",OPT_SizePetite,"Button",BS_AUTORADIOBUTTON,9, + 44,50,10 + CONTROL "Sl&im",OPT_SizeSlim,"Button",BS_AUTORADIOBUTTON,9,54,50, + 10 + CONTROL "&Small",OPT_SizeSmall,"Button",BS_AUTORADIOBUTTON,9,64, 50,10 CONTROL "Medi&ocre",OPT_SizeMediocre,"Button",BS_AUTORADIOBUTTON, - 76,15,50,10 + 70,14,50,10 CONTROL "&Middling",OPT_SizeMiddling,"Button",BS_AUTORADIOBUTTON, - 76,25,50,10 + 70,24,50,10 CONTROL "&Average",OPT_SizeAverage,"Button",BS_AUTORADIOBUTTON, - 76,35,50,10 + 70,34,50,10 CONTROL "Mode&rate",OPT_SizeModerate,"Button",BS_AUTORADIOBUTTON, - 76,45,50,10 - CONTROL "Medi&um",OPT_SizeMedium,"Button",BS_AUTORADIOBUTTON,76, - 55,50,10 - CONTROL "Bul&ky",OPT_SizeBulky,"Button",BS_AUTORADIOBUTTON,76,65, + 70,44,50,10 + CONTROL "Medi&um",OPT_SizeMedium,"Button",BS_AUTORADIOBUTTON,70, + 54,50,10 + CONTROL "Bul&ky",OPT_SizeBulky,"Button",BS_AUTORADIOBUTTON,70,64, 50,10 - CONTROL "&Large",OPT_SizeLarge,"Button",BS_AUTORADIOBUTTON,140, - 15,50,10 - CONTROL "&Big",OPT_SizeBig,"Button",BS_AUTORADIOBUTTON,140,25,50, + CONTROL "&Large",OPT_SizeLarge,"Button",BS_AUTORADIOBUTTON,134, + 14,50,10 + CONTROL "&Big",OPT_SizeBig,"Button",BS_AUTORADIOBUTTON,134,24,50, 10 - CONTROL "&Huge",OPT_SizeHuge,"Button",BS_AUTORADIOBUTTON,140,35, + CONTROL "&Huge",OPT_SizeHuge,"Button",BS_AUTORADIOBUTTON,134,34, 50,10 - CONTROL "&Giant",OPT_SizeGiant,"Button",BS_AUTORADIOBUTTON,140, - 45,50,10 + CONTROL "&Giant",OPT_SizeGiant,"Button",BS_AUTORADIOBUTTON,134, + 44,50,10 CONTROL "&Colossal",OPT_SizeColossal,"Button",BS_AUTORADIOBUTTON, - 140,55,50,10 + 134,54,50,10 CONTROL "Tita&nic",OPT_SizeTitanic,"Button",BS_AUTORADIOBUTTON, - 140,65,50,10 - PUSHBUTTON "Choose...",OPT_ChooseLightSquareColor,144,100,40,15 - PUSHBUTTON "Choose...",OPT_ChooseDarkSquareColor,144,120,40,15 - PUSHBUTTON "Choose...",OPT_ChooseWhitePieceColor,144,140,40,15 - PUSHBUTTON "Choose...",OPT_ChooseBlackPieceColor,144,160,40,15 - PUSHBUTTON "Choose...",OPT_ChooseHighlightSquareColor,144,180,40,15 - PUSHBUTTON "Choose...",OPT_ChoosePremoveHighlightColor,144,200,40, - 15 - PUSHBUTTON "Defaults",OPT_DefaultBoardColors,118,225,65,15 - EDITTEXT OPT_DarkSquareColor,104,120,25,15,ES_READONLY | + 134,64,50,10 + PUSHBUTTON "...",OPT_ChooseLightSquareColor,110,94,20,15 + PUSHBUTTON "...",OPT_ChooseDarkSquareColor,110,112,20,15 + PUSHBUTTON "...",OPT_ChooseWhitePieceColor,110,130,20,15 + PUSHBUTTON "...",OPT_ChooseBlackPieceColor,110,148,20,15 + PUSHBUTTON "...",OPT_ChooseHighlightSquareColor,110,166,20,15 + PUSHBUTTON "...",OPT_ChoosePremoveHighlightColor,110,184,20,15 + CONTROL "Monochrome",OPT_Monochrome,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,210,64,10 + PUSHBUTTON "Defaults",OPT_DefaultBoardColors,80,206,50,15 + EDITTEXT OPT_DarkSquareColor,80,112,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - EDITTEXT OPT_LightSquareColor,104,100,25,15,ES_READONLY | + EDITTEXT OPT_LightSquareColor,80,94,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - EDITTEXT OPT_WhitePieceColor,104,140,25,15,ES_READONLY | + EDITTEXT OPT_WhitePieceColor,80,130,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - EDITTEXT OPT_BlackPieceColor,104,160,25,15,ES_READONLY | + EDITTEXT OPT_BlackPieceColor,80,148,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - EDITTEXT OPT_HighlightSquareColor,104,180,25,15,ES_READONLY | + EDITTEXT OPT_HighlightSquareColor,80,166,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - GROUPBOX "Colors",IDC_STATIC,10,90,185,155 - EDITTEXT OPT_PremoveHighlightColor,104,200,25,15,ES_READONLY | + GROUPBOX "Colors",IDC_STATIC,4,84,185,142 + EDITTEXT OPT_PremoveHighlightColor,80,184,25,15,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - LTEXT "Light Squares",IDC_STATIC,25,105,60,10 - LTEXT "Dark Squares",IDC_STATIC,25,124,60,10 - LTEXT "White Pieces",IDC_STATIC,25,145,60,10 - LTEXT "Black Pieces",IDC_STATIC,25,165,60,10 - LTEXT "Square Highlights",IDC_STATIC,25,185,60,10 - LTEXT "Premove Highlights",IDC_STATIC,25,205,70,10 - GROUPBOX "Size",IDC_STATIC,10,5,185,75 - CONTROL "Monochrome",OPT_Monochrome,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,25,225,75,10 - EDITTEXT OPT_SampleLightSquare,205,110,39,36,ES_READONLY | + LTEXT "Light Squares",IDC_STATIC,10,98,60,10 + LTEXT "Dark Squares",IDC_STATIC,10,116,60,10 + LTEXT "White Pieces",IDC_STATIC,10,134,60,10 + LTEXT "Black Pieces",IDC_STATIC,10,152,60,10 + LTEXT "Square Highlights",IDC_STATIC,10,170,60,10 + LTEXT "Premove Highlights",IDC_STATIC,10,188,70,10 + GROUPBOX "Size",IDC_STATIC,4,4,185,75 + EDITTEXT OPT_SampleLightSquare,144,96,39,36,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - EDITTEXT OPT_SampleDarkSquare,205,165,39,36,ES_READONLY | + EDITTEXT OPT_SampleDarkSquare,144,138,39,36,ES_READONLY | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP END -DLG_Fonts DIALOG DISCARDABLE 0, 0, 280, 231 +DLG_Fonts DIALOG DISCARDABLE 0, 0, 266, 226 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Fonts" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,155,210,50,14 - PUSHBUTTON "Cancel",IDCANCEL,215,210,50,14 - PUSHBUTTON "Choose...",OPT_ChooseClockFont,221,17,45,15 - PUSHBUTTON "Choose...",OPT_ChooseMessageFont,221,47,45,15 - PUSHBUTTON "Choose...",OPT_ChooseCoordFont,221,77,45,15 - PUSHBUTTON "Choose...",OPT_ChooseTagFont,221,120,45,15 - PUSHBUTTON "Choose...",OPT_ChooseCommentsFont,221,150,45,15 - PUSHBUTTON "Choose...",OPT_ChooseConsoleFont,221,180,45,15 - PUSHBUTTON "&Revert to Defaults",OPT_DefaultFonts,15,210,80,15 + DEFPUSHBUTTON "OK",IDOK,156,208,50,14 + PUSHBUTTON "Cancel",IDCANCEL,212,208,50,14 + PUSHBUTTON "Choose...",OPT_ChooseClockFont,212,15,45,15 + PUSHBUTTON "Choose...",OPT_ChooseMessageFont,212,39,45,15 + PUSHBUTTON "Choose...",OPT_ChooseCoordFont,212,63,45,15 + PUSHBUTTON "Choose...",OPT_ChooseTagFont,212,101,45,15 + PUSHBUTTON "Choose...",OPT_ChooseCommentsFont,212,125,45,15 + PUSHBUTTON "Choose...",OPT_ChooseConsoleFont,212,149,45,15 + PUSHBUTTON "Choose...",OPT_ChooseMoveHistoryFont,212,174,45,15 + PUSHBUTTON "&Defaults",OPT_DefaultFonts,3,208,50,15 CONTROL "",OPT_SampleCoordFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,72,140,20 + WS_DISABLED | WS_BORDER,67,59,140,20 CONTROL "",OPT_SampleTagFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,115,140,20 + WS_DISABLED | WS_BORDER,67,97,140,20 CONTROL "",OPT_SampleCommentsFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,145,140,20 + WS_DISABLED | WS_BORDER,67,121,140,20 CONTROL "",OPT_SampleConsoleFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,175,140,20 - LTEXT "Clocks",OPT_ClockFont,16,17,45,10,NOT WS_GROUP - LTEXT "Messages",OPT_MessageFont,16,47,45,10,NOT WS_GROUP - LTEXT "Coordinates",OPT_CoordFont,16,77,45,10,NOT WS_GROUP - LTEXT "Tags",OPT_EditTagsFont,16,120,45,10,NOT WS_GROUP - LTEXT "Comments",OPT_CommentsFont,16,150,45,10,NOT WS_GROUP - LTEXT "ICS Interaction",OPT_MessageFont5,16,180,50,10,NOT + WS_DISABLED | WS_BORDER,67,145,140,20 + LTEXT "Clocks",OPT_ClockFont,13,16,45,10,NOT WS_GROUP + LTEXT "Messages",OPT_MessageFont,13,40,45,10,NOT WS_GROUP + LTEXT "Coordinates",OPT_CoordFont,13,64,45,10,NOT WS_GROUP + LTEXT "Tags",OPT_EditTagsFont,13,102,45,10,NOT WS_GROUP + LTEXT "Comments",OPT_CommentsFont,13,126,45,10,NOT WS_GROUP + LTEXT "ICS Interaction",OPT_MessageFont5,13,150,50,10,NOT WS_GROUP CONTROL "",OPT_SampleClockFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,12,140,20 + WS_DISABLED | WS_BORDER,67,11,140,20 CONTROL "",OPT_SampleMessageFont,"RICHEDIT",ES_READONLY | - WS_DISABLED | WS_BORDER,70,42,140,20 - GROUPBOX "Current Board Size",IDC_STATIC,5,2,270,100 - GROUPBOX "All Board Sizes",IDC_STATIC,5,105,270,100 + WS_DISABLED | WS_BORDER,67,35,140,20 + GROUPBOX "Current Board Size",IDC_STATIC,3,1,259,83 + GROUPBOX "All Board Sizes",IDC_STATIC,3,87,259,109 + CONTROL "",OPT_SampleMoveHistoryFont,"RICHEDIT",ES_READONLY | + WS_DISABLED | WS_BORDER,67,170,140,20 + LTEXT "Move History",OPT_MessageFont6,13,175,50,10,NOT + WS_GROUP +END + +DLG_NewGameFRC DIALOG DISCARDABLE 0, 0, 176, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New FRC (Chess960) Game" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "&Start Position Number:",IDC_NFG_Label,4,7,71,8 + EDITTEXT IDC_NFG_Edit,76,4,42,14,ES_AUTOHSCROLL + PUSHBUTTON "Random",IDC_NFG_Random,122,4,50,14 + DEFPUSHBUTTON "OK",IDOK,64,28,50,14 + PUSHBUTTON "Cancel",IDCANCEL,122,28,50,14 +END + +DLG_GameListOptions DIALOG DISCARDABLE 0, 0, 206, 140 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Game List Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "PGN &Tags:",IDC_GLT,2,2,36,8 + LISTBOX IDC_GameListTags,2,12,158,102,LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Up",IDC_GLT_Up,164,12,38,14 + PUSHBUTTON "&Down",IDC_GLT_Down,164,30,38,14 + PUSHBUTTON "Default",IDC_GLT_Restore,164,82,38,14 + PUSHBUTTON "Factory",IDC_GLT_Default,164,100,38,14 + DEFPUSHBUTTON "OK",IDOK,96,122,50,14 + PUSHBUTTON "Cancel",IDCANCEL,152,122,50,14 + LTEXT "Restore to:",IDC_GLT_RestoreTo,164,70,36,8 +END + +DLG_MoveHistory DIALOGEX 0, 0, 225, 130 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Move History" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_MoveHistory,"RICHEDIT",ES_MULTILINE | ES_READONLY | + WS_BORDER | WS_VSCROLL | WS_TABSTOP,2,2,222,128 +END + +DLG_EvalGraph DIALOGEX 0, 0, 216, 75 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Evaluation Diagram" +FONT 8, "MS Sans Serif" +BEGIN +END + +DLG_EngineOutput DIALOGEX 0, 0, 266, 167 +STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Engine output" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Engine #1",IDC_EngineLabel1,14,2,84,8 + RTEXT "NPS",IDC_Engine1_NPS,206,2,57,8 + CONTROL "",IDC_EngineMemo1,"RICHEDIT",ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL | + WS_HSCROLL | WS_TABSTOP,0,10,262,72 + LTEXT "Engine #2",IDC_EngineLabel2,15,84,103,8 + RTEXT "NPS",IDC_Engine2_NPS,210,84,55,8 + CONTROL "",IDC_EngineMemo2,"RICHEDIT",ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL | + WS_HSCROLL | WS_TABSTOP,2,92,262,74 + ICON IDI_UNKNOWN_14,IDC_Color1,2,4,20,20,SS_REALSIZEIMAGE + ICON IDI_UNKNOWN_14,IDC_Color2,4,84,20,20 + LTEXT "Static",IDC_StateData1,120,2,83,8 + ICON IDI_TRANS_14,IDC_StateIcon1,98,0,21,20 + LTEXT "Static",IDC_StateData2,186,84,19,8 + ICON IDI_TRANS_14,IDC_StateIcon2,164,84,20,20 +END + +DLG_EnginePlayOptions DIALOG DISCARDABLE 0, 0, 208, 129 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Engine Options" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Ponder Next Move",IDC_EpPonder,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,4,75,10 + CONTROL "Enable and Show Thinking (recommended)", + IDC_EpShowThinking,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 4,16,152,10 + CONTROL "Hide Thinking when Playing against Human", + IDC_EpHideThinkingHuman,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,28,153,10 + CONTROL "Periodic Updates (for Analysis Mode)", + IDC_EpPeriodicUpdates,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,40,131,10 + GROUPBOX "Engine-engine matches",IDC_STATIC,4,56,200,50 + LTEXT "Adjudicate draw after:",IDC_STATIC,10,72,70,8 + EDITTEXT IDC_EpDrawMoveCount,116,68,40,14,ES_AUTOHSCROLL + LTEXT "moves",IDC_STATIC,158,72,22,8 + LTEXT "Win/loss adjudication threshold:",IDC_STATIC,10,90,102, + 8 + EDITTEXT IDC_EpAdjudicationThreshold,116,86,40,14,ES_AUTOHSCROLL + LTEXT "centipawns",IDC_STATIC,158,90,37,8 + DEFPUSHBUTTON "OK",IDOK,98,112,50,14 + PUSHBUTTON "Cancel",IDCANCEL,154,112,50,14 +END + +DLG_OptionsUCI DIALOG DISCARDABLE 0, 0, 228, 124 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "UCI Options" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Polyglot Directory:",IDC_STATIC,2,8,58,8 + EDITTEXT IDC_PolyglotDir,62,4,140,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BrowseForPolyglotDir,206,4,17,14 + LTEXT "Hash Size (MB):",IDC_STATIC,2,26,52,8 + EDITTEXT IDC_HashSize,62,22,40,14,ES_AUTOHSCROLL + LTEXT "EGTB Path:",IDC_STATIC,2,44,39,8 + EDITTEXT IDC_PathToEGTB,62,40,140,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BrowseForEGTB,206,40,17,14 + LTEXT "EGTB Size (MB):",IDC_STATIC,2,62,54,8 + EDITTEXT IDC_SizeOfEGTB,62,58,40,14,ES_AUTOHSCROLL + CONTROL "Use Book:",IDC_UseBook,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,2,80,49,10 + EDITTEXT IDC_BookFile,62,76,140,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BrowseForBook,206,76,17,14 + DEFPUSHBUTTON "OK",IDOK,118,106,50,14 + PUSHBUTTON "Cancel",IDCANCEL,174,106,50,14 +END + +DLG_Null DIALOG DISCARDABLE 0, 0, 186, 50 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Null" +FONT 8, "MS Sans Serif" +BEGIN END @@ -634,26 +772,36 @@ END #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN + DLG_GameList, DIALOG + BEGIN + RIGHTMARGIN, 258 + END + + DLG_Colorize, DIALOG + BEGIN + BOTTOMMARGIN, 32 + END + DLG_GeneralOptions, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 264 + RIGHTMARGIN, 213 TOPMARGIN, 7 - BOTTOMMARGIN, 128 + BOTTOMMARGIN, 176 END DLG_IcsOptions, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 311 + RIGHTMARGIN, 295 TOPMARGIN, 7 - BOTTOMMARGIN, 264 + BOTTOMMARGIN, 248 END DLG_BoardOptions, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 255 + RIGHTMARGIN, 187 TOPMARGIN, 7 BOTTOMMARGIN, 243 END @@ -661,9 +809,73 @@ BEGIN DLG_Fonts, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 273 + RIGHTMARGIN, 259 + TOPMARGIN, 7 + BOTTOMMARGIN, 219 + END + + DLG_NewGameFRC, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 169 + TOPMARGIN, 7 + BOTTOMMARGIN, 40 + END + + DLG_GameListOptions, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 199 TOPMARGIN, 7 - BOTTOMMARGIN, 224 + BOTTOMMARGIN, 133 + END + + DLG_MoveHistory, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 218 + TOPMARGIN, 7 + BOTTOMMARGIN, 123 + END + + DLG_EvalGraph, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 209 + TOPMARGIN, 7 + BOTTOMMARGIN, 68 + END + + DLG_EngineOutput, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 259 + TOPMARGIN, 7 + BOTTOMMARGIN, 160 + END + + DLG_EnginePlayOptions, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 201 + TOPMARGIN, 7 + BOTTOMMARGIN, 122 + END + + DLG_OptionsUCI, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 221 + TOPMARGIN, 7 + BOTTOMMARGIN, 117 + END + + DLG_Null, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 43 END END #endif // APSTUDIO_INVOKED @@ -676,9 +888,18 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. +IDI_WHITE_14 ICON DISCARDABLE "bitmaps\\white_14.ico" ICON_BLACK ICON DISCARDABLE "bitmaps\\icon_ob.ico" ICON_BOARD ICON DISCARDABLE "bitmaps\\board.ico" ICON_WHITE ICON DISCARDABLE "bitmaps\\icon_whi.ico" +IDI_BLACK_14 ICON DISCARDABLE "bitmaps\\black_14.ico" +IDI_PONDER_14 ICON DISCARDABLE "bitmaps\\ponder_14.ico" +IDI_TRANS_14 ICON DISCARDABLE "bitmaps\\trans_14.ico" +IDI_CLOCK_14 ICON DISCARDABLE "bitmaps\\clock_14.ico" +IDI_UNKNOWN_14 ICON DISCARDABLE "bitmaps\\unknown_14.ico" +IDI_BALOON_14 ICON DISCARDABLE "bitmaps\\baloon_14.ico" +IDI_ANALYZE_14 ICON DISCARDABLE "bitmaps\\analyze_14.ico" +IDI_ANALYZE2_14 ICON DISCARDABLE "bitmaps\\analyze2_14.ico" ///////////////////////////////////////////////////////////////////////////// // @@ -689,17 +910,20 @@ WINBOARD MENU DISCARDABLE BEGIN POPUP "&File" BEGIN - MENUITEM "Reset &Game", IDM_NewGame + MENUITEM "New &Game\tCtrl-N", IDM_NewGame + MENUITEM "New FRC Game...", IDM_NewGameFRC MENUITEM SEPARATOR - MENUITEM "&Load Game...", IDM_LoadGame + MENUITEM "&Load Game...\tCtrl-O", IDM_LoadGame MENUITEM "Load &Next Game\tAlt+PgDn", IDM_LoadNextGame MENUITEM "Load &Previous Game\tAlt+PgUp", IDM_LoadPrevGame MENUITEM "&Reload Same Game", IDM_ReloadGame - MENUITEM "&Save Game...", IDM_SaveGame + MENUITEM "&Save Game...\tCtrl-S", IDM_SaveGame MENUITEM SEPARATOR MENUITEM "&Copy Game To Clipboard\tAlt+C", IDM_CopyGame MENUITEM "Paste Game &From Clipboard\tAlt+V", IDM_PasteGame MENUITEM SEPARATOR + MENUITEM "Copy Game List to Clipboard", IDM_CopyGameListToClipboard + MENUITEM SEPARATOR MENUITEM "L&oad Position...", IDM_LoadPosition MENUITEM "Load N&ext Position\tAlt+Shift+PgDn", IDM_LoadNextPosition MENUITEM "Load Pre&vious Position\tAlt+Shift+PgUp", @@ -726,7 +950,11 @@ BEGIN MENUITEM "&Edit Position", IDM_EditPosition MENUITEM "Trai&ning", IDM_Training, GRAYED MENUITEM SEPARATOR - MENUITEM "Show Game &List...", IDM_ShowGameList + MENUITEM "Show Engine Output", IDM_ShowEngineOutput + MENUITEM "Show Evaluation Graph", IDM_ShowEvalGraph + MENUITEM "Show Game &List", IDM_ShowGameList + MENUITEM "Show Move History", IDM_ShowMoveHistory + MENUITEM SEPARATOR MENUITEM "Edit &Tags...", IDM_EditTags MENUITEM "Edit &Comment...", IDM_EditComment MENUITEM "&Pause\tPause", IDM_Pause @@ -745,6 +973,10 @@ BEGIN MENUITEM SEPARATOR MENUITEM "Stop &Observing\tF10", IDM_StopObserving MENUITEM "Stop E&xamining\tF11", IDM_StopExamining + MENUITEM SEPARATOR + MENUITEM "Adjudicate to White", IDM_UserAdjudication_White + MENUITEM "Adjudicate to Black", IDM_UserAdjudication_Black + MENUITEM "Adjudicate Draw", IDM_UserAdjudication_Draw END POPUP "&Step" BEGIN @@ -766,9 +998,12 @@ BEGIN MENUITEM SEPARATOR MENUITEM "&General...", IDM_GeneralOptions MENUITEM "&Board...", IDM_BoardOptions + MENUITEM "Engines...", IDM_EnginePlayOptions + MENUITEM "UCI...", IDM_OptionsUCI MENUITEM "&ICS...", IDM_IcsOptions, GRAYED MENUITEM "&Fonts...", IDM_Fonts MENUITEM "Soun&ds...", IDM_Sounds + MENUITEM "Game List...", IDM_GameListOptions MENUITEM SEPARATOR MENUITEM "Comm&unications...", IDM_CommPort MENUITEM "&Load Game...", IDM_LoadOptions @@ -902,12 +1137,17 @@ WINBOARD ACCELERATORS MOVEABLE PURE BEGIN "1", IDM_DirectCommand1, VIRTKEY, ALT, NOINVERT "2", IDM_DirectCommand2, VIRTKEY, ALT, NOINVERT + "B", IDM_Book, VIRTKEY, ALT, NOINVERT "C", IDM_CopyGame, VIRTKEY, ALT, NOINVERT - "C", IDM_CopyPosition, VIRTKEY, SHIFT, ALT, - NOINVERT + "C", IDM_CopyPosition, VIRTKEY, SHIFT, ALT, NOINVERT + "I", IDM_TypeInMove, VIRTKEY, ALT, NOINVERT + "N", IDM_NewGame, VIRTKEY, CONTROL, NOINVERT + "O", IDM_LoadGame, VIRTKEY, CONTROL, NOINVERT + "S", IDM_SaveGame, VIRTKEY, CONTROL, NOINVERT "V", IDM_PasteGame, VIRTKEY, ALT, NOINVERT - "V", IDM_PastePosition, VIRTKEY, SHIFT, ALT, + "V", IDM_PasteAny, VIRTKEY, SHIFT, CONTROL, NOINVERT + "V", IDM_PastePosition, VIRTKEY, SHIFT, ALT, NOINVERT VK_DELETE, IDM_RetractMove, VIRTKEY, ALT, NOINVERT VK_DOWN, IDM_ToEnd, VIRTKEY, ALT, NOINVERT VK_END, IDM_TruncateGame, VIRTKEY, ALT, NOINVERT @@ -954,6 +1194,13 @@ BEGIN VK_UP, IDM_ToStart, VIRTKEY, NOINVERT END +NO_ICS ACCELERATORS DISCARDABLE +BEGIN + "A", IDM_AnalysisMode, VIRTKEY, CONTROL, NOINVERT + "V", IDM_PasteAny, VIRTKEY, CONTROL, NOINVERT +END + + ///////////////////////////////////////////////////////////////////////////// // // Bitmap @@ -1285,6 +1532,62 @@ Q129W BITMAP MOVEABLE PURE "bitmaps\\q129w.bmp" K129W BITMAP MOVEABLE PURE "bitmaps\\k129w.bmp" GALACTIC BITMAP MOVEABLE PURE "bitmaps\\galactic.bmp" TIM BITMAP MOVEABLE PURE "bitmaps\\tim.bmp" +// [HGM] Some fairy symbols: +E72O BITMAP MOVEABLE PURE "bitmaps\\e72o.bmp" +A72O BITMAP MOVEABLE PURE "bitmaps\\a72o.bmp" +C72O BITMAP MOVEABLE PURE "bitmaps\\c72o.bmp" +F72O BITMAP MOVEABLE PURE "bitmaps\\f72o.bmp" +M72O BITMAP MOVEABLE PURE "bitmaps\\m72o.bmp" +W72O BITMAP MOVEABLE PURE "bitmaps\\w72o.bmp" +O72O BITMAP MOVEABLE PURE "bitmaps\\o72o.bmp" +U72O BITMAP MOVEABLE PURE "bitmaps\\u72o.bmp" +H72O BITMAP MOVEABLE PURE "bitmaps\\h72o.bmp" +E72S BITMAP MOVEABLE PURE "bitmaps\\e72s.bmp" +A72S BITMAP MOVEABLE PURE "bitmaps\\a72s.bmp" +C72S BITMAP MOVEABLE PURE "bitmaps\\c72s.bmp" +F72S BITMAP MOVEABLE PURE "bitmaps\\f72s.bmp" +M72S BITMAP MOVEABLE PURE "bitmaps\\m72s.bmp" +W72S BITMAP MOVEABLE PURE "bitmaps\\w72s.bmp" +O72S BITMAP MOVEABLE PURE "bitmaps\\o72s.bmp" +U72S BITMAP MOVEABLE PURE "bitmaps\\u72s.bmp" +H72S BITMAP MOVEABLE PURE "bitmaps\\h72s.bmp" +E72W BITMAP MOVEABLE PURE "bitmaps\\e72w.bmp" +A72W BITMAP MOVEABLE PURE "bitmaps\\a72w.bmp" +C72W BITMAP MOVEABLE PURE "bitmaps\\c72w.bmp" +F72W BITMAP MOVEABLE PURE "bitmaps\\f72w.bmp" +M72W BITMAP MOVEABLE PURE "bitmaps\\m72w.bmp" +W72W BITMAP MOVEABLE PURE "bitmaps\\w72w.bmp" +O72W BITMAP MOVEABLE PURE "bitmaps\\o72w.bmp" +U72W BITMAP MOVEABLE PURE "bitmaps\\u72w.bmp" +H72W BITMAP MOVEABLE PURE "bitmaps\\h72w.bmp" +E49O BITMAP MOVEABLE PURE "bitmaps\\e49o.bmp" +A49O BITMAP MOVEABLE PURE "bitmaps\\a49o.bmp" +C49O BITMAP MOVEABLE PURE "bitmaps\\c49o.bmp" +F49O BITMAP MOVEABLE PURE "bitmaps\\f49o.bmp" +M49O BITMAP MOVEABLE PURE "bitmaps\\m49o.bmp" +W49O BITMAP MOVEABLE PURE "bitmaps\\w49o.bmp" +O49O BITMAP MOVEABLE PURE "bitmaps\\o49o.bmp" +U49O BITMAP MOVEABLE PURE "bitmaps\\u49o.bmp" +H49O BITMAP MOVEABLE PURE "bitmaps\\h49o.bmp" +E49S BITMAP MOVEABLE PURE "bitmaps\\e49s.bmp" +A49S BITMAP MOVEABLE PURE "bitmaps\\a49s.bmp" +C49S BITMAP MOVEABLE PURE "bitmaps\\c49s.bmp" +F49S BITMAP MOVEABLE PURE "bitmaps\\f49s.bmp" +M49S BITMAP MOVEABLE PURE "bitmaps\\m49s.bmp" +W49S BITMAP MOVEABLE PURE "bitmaps\\w49s.bmp" +O49S BITMAP MOVEABLE PURE "bitmaps\\o49s.bmp" +U49S BITMAP MOVEABLE PURE "bitmaps\\u49s.bmp" +H49S BITMAP MOVEABLE PURE "bitmaps\\h49s.bmp" +E49W BITMAP MOVEABLE PURE "bitmaps\\e49w.bmp" +A49W BITMAP MOVEABLE PURE "bitmaps\\a49w.bmp" +C49W BITMAP MOVEABLE PURE "bitmaps\\c49w.bmp" +F49W BITMAP MOVEABLE PURE "bitmaps\\f49w.bmp" +M49W BITMAP MOVEABLE PURE "bitmaps\\m49w.bmp" +W49W BITMAP MOVEABLE PURE "bitmaps\\w49w.bmp" +O49W BITMAP MOVEABLE PURE "bitmaps\\o49w.bmp" +U49W BITMAP MOVEABLE PURE "bitmaps\\u49w.bmp" +H49W BITMAP MOVEABLE PURE "bitmaps\\h49w.bmp" + #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -1321,39 +1624,38 @@ END // WAVE // -DING WAVE DISCARDABLE "..\\sounds\\ding1.wav" -CHING WAVE DISCARDABLE "..\\sounds\\ching.wav" -CLICK WAVE DISCARDABLE "..\\sounds\\click.wav" -CYMBAL WAVE DISCARDABLE "..\\sounds\\cymbal.wav" -DRIP WAVE DISCARDABLE "..\\sounds\\drip.wav" -GONG WAVE DISCARDABLE "..\\sounds\\gong.wav" -BEEPBEEP WAVE DISCARDABLE "..\\sounds\\honkhonk.wav" -LASER WAVE DISCARDABLE "..\\sounds\\laser.wav" -PENALTY WAVE DISCARDABLE "..\\sounds\\penalty.wav" -PHONE WAVE DISCARDABLE "..\\sounds\\phone.wav" -POP WAVE DISCARDABLE "..\\sounds\\pop.wav" -POP2 WAVE DISCARDABLE "..\\sounds\\pop2.wav" -SLAP WAVE DISCARDABLE "..\\sounds\\slap.wav" -SQUEAK WAVE DISCARDABLE "..\\sounds\\squeak.wav" -SWISH WAVE DISCARDABLE "..\\sounds\\swish.wav" -THUD WAVE DISCARDABLE "..\\sounds\\thud.wav" -WHIPCRACK WAVE DISCARDABLE "..\\sounds\\whipcrak.wav" -MOVE WAVE DISCARDABLE "..\\sounds\\move.wav" -ALARM WAVE DISCARDABLE "..\\sounds\\alarm.wav" -CHALLENGE WAVE DISCARDABLE "..\\sounds\\challenge.wav" -CHANNEL WAVE DISCARDABLE "..\\sounds\\channel.wav" -CHANNEL1 WAVE DISCARDABLE "..\\sounds\\channel1.wav" -DRAW WAVE DISCARDABLE "..\\sounds\\draw.wav" -KIBITZ WAVE DISCARDABLE "..\\sounds\\kibitz.wav" -LOSE WAVE DISCARDABLE "..\\sounds\\lose.wav" -REQUEST WAVE DISCARDABLE "..\\sounds\\request.wav" -SEEK WAVE DISCARDABLE "..\\sounds\\seek.wav" -SHOUT WAVE DISCARDABLE "..\\sounds\\shout.wav" -SSHOUT WAVE DISCARDABLE "..\\sounds\\sshout.wav" -TELL WAVE DISCARDABLE "..\\sounds\\tell.wav" -UNFINISHED WAVE DISCARDABLE "..\\sounds\\unfinished.wav" -WIN WAVE DISCARDABLE "..\\sounds\\win.wav" - +DING WAVE DISCARDABLE "sounds\\ding1.wav" +CHING WAVE DISCARDABLE "sounds\\ching.wav" +CLICK WAVE DISCARDABLE "sounds\\click.wav" +CYMBAL WAVE DISCARDABLE "sounds\\cymbal.wav" +DRIP WAVE DISCARDABLE "sounds\\drip.wav" +GONG WAVE DISCARDABLE "sounds\\gong.wav" +BEEPBEEP WAVE DISCARDABLE "sounds\\honkhonk.wav" +LASER WAVE DISCARDABLE "sounds\\laser.wav" +PENALTY WAVE DISCARDABLE "sounds\\penalty.wav" +PHONE WAVE DISCARDABLE "sounds\\phone.wav" +POP WAVE DISCARDABLE "sounds\\pop.wav" +POP2 WAVE DISCARDABLE "sounds\\pop2.wav" +SLAP WAVE DISCARDABLE "sounds\\slap.wav" +SQUEAK WAVE DISCARDABLE "sounds\\squeak.wav" +SWISH WAVE DISCARDABLE "sounds\\swish.wav" +THUD WAVE DISCARDABLE "sounds\\thud.wav" +WHIPCRACK WAVE DISCARDABLE "sounds\\whipcrak.wav" +MOVE WAVE DISCARDABLE "sounds\\move.wav" +ALARM WAVE DISCARDABLE "sounds\\alarm.wav" +CHALLENGE WAVE DISCARDABLE "sounds\\challenge.wav" +CHANNEL WAVE DISCARDABLE "sounds\\channel.wav" +CHANNEL1 WAVE DISCARDABLE "sounds\\channel1.wav" +DRAW WAVE DISCARDABLE "sounds\\draw.wav" +KIBITZ WAVE DISCARDABLE "sounds\\kibitz.wav" +LOSE WAVE DISCARDABLE "sounds\\lose.wav" +REQUEST WAVE DISCARDABLE "sounds\\request.wav" +SEEK WAVE DISCARDABLE "sounds\\seek.wav" +SHOUT WAVE DISCARDABLE "sounds\\shout.wav" +SSHOUT WAVE DISCARDABLE "sounds\\sshout.wav" +TELL WAVE DISCARDABLE "sounds\\tell.wav" +UNFINISHED WAVE DISCARDABLE "sounds\\unfinished.wav" +WIN WAVE DISCARDABLE "sounds\\win.wav" #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// -- 1.7.0.4