[net.sources.games] network chess

toma@tc.fluke.COM (Tom Anderson) (01/09/87)

This is a five-part distribution of "nchess", a network chess game I wrote
a few months ago during a chess fad at work.  It runs on Sun Microsystems'
workstations, and would be quite difficult to port to other machines due
to heavy reliance on Sun's window libraries.  It bears no relation to the
existing chesstool program; i.e., it was written from scratch without any
knowledge of how chesstool is implemented (since we don't have the source).

Since the manual page is included in the following shell archives, no more
need be said.  Please mail any comments, bugs, etc. directly to me, since
I very rarely read net news.

Any commercial use of this software is strictly prohibited, INCLUDING usage
as a demo vehicle for Sun workstations.

Enjoy!

Tom Anderson, (206) 356-5895
John Fluke Mfg. Co., Inc.,  P.O. Box C9090 M/S 245F, Everett, Wa. 98206
{ hplsla, microsoft, uw-beaver, sun }!fluke!toma

toma@tc.fluke.COM (Tom Anderson) (01/09/87)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by spock!toma on Thu Jan  8 15:37:19 PST 1987
# Contents:  Icons/ Makefile nchess.6 decls.h nchess.h daemon.c xdr.c
#	Icons/bishop.icon Icons/bishopStencil.icon Icons/king.icon
#	Icons/kingStencil.icon Icons/knight.icon Icons/knightStencil.icon
#	Icons/pawn.icon Icons/pawnStencil.icon Icons/queen.icon
#	Icons/queenStencil.icon Icons/rook.icon Icons/rookStencil.icon
#	Icons/blackSquare.icon Icons/whiteSquare.icon Icons/nchess.icon
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
#
# Makefile for nchess package.
#

ALLOBJS = board.o boardsw.o main.o ipc.o controlsw.o daemon.o \
	tool.o xdr.o msgsw.o talksw.o 
CLIENTOBJS = board.o main.o ipc.o xdr.o tool.o controlsw.o boardsw.o \
	msgsw.o talksw.o chessprocess.o
SERVEROBJS = daemon.o xdr.o

CLIENTSRCS1 = board.c main.c 
CLIENTSRCS2 = ipc.c tool.c controlsw.c 
CLIENTSRCS3 = boardsw.c msgsw.c talksw.c chessprocess.c
CLIENTSRCS4 = xdr.c
CLIENTSRCS = $(CLIENTSRCS1) $(CLIENTSRCS2) $(CLIENTSRCS3) $(CLIENTSRCS4)

SERVERSRCS = daemon.c xdr.c

HEADERS = decls.h nchess.h

ICONS = Icons/bishop.icon Icons/bishopStencil.icon \
	Icons/king.icon Icons/kingStencil.icon \
	Icons/knight.icon Icons/knightStencil.icon \
	Icons/pawn.icon Icons/pawnStencil.icon \
	Icons/queen.icon Icons/queenStencil.icon \
	Icons/rook.icon Icons/rookStencil.icon \
	Icons/blackSquare.icon Icons/whiteSquare.icon \
	Icons/nchess.icon

# CFLAGS = -g
CFLAGS = -O

all : nchessd nchess

nchessd : $(SERVEROBJS)
	cc $(CFLAGS) -o nchessd $(SERVEROBJS)

nchess : $(CLIENTOBJS)
	cc $(CFLAGS) -o nchess $(CLIENTOBJS) -lsuntool -lsunwindow -lpixrect

shar : $(SERVERSRCS) $(CLIENTSRCS) $(HEADERS) $(ICONS)
	shar -c Makefile nchess.6 $(HEADERS) $(SERVERSRCS) Icons $(ICONS) > shar.1
	shar -c $(CLIENTSRCS1) > shar.2
	shar -c $(CLIENTSRCS2) > shar.3
	shar -c $(CLIENTSRCS3) > shar.4

lint : $(SERVERSRCS) $(CLIENTSRCS)
	lint $(SERVERSRCS)
	lint $(CLIENTSRCS)

list : $(SERVERSRCS) $(CLIENTSRCS) $(HEADERS)
	lpr -x2 -p -J"nchess" *.h *.c

clean : 
	rm -f *.o nchess nchessd

$(ALLOBJS) : $(HEADERS)

@//E*O*F Makefile//
chmod u=rw,g=r,o=r Makefile
 
echo x - nchess.6
sed 's/^@//' > "nchess.6" <<'@//E*O*F nchess.6//'
@.TH NCHESS 6 "14 October 1986"
@.SH NAME
nchess \- general-purpose chess tool
@.SH SYNOPSIS
@.B nchess
[\fB-c\fP]
@.br
	[\fB-e\fP | \fB-r\fP\fIrestoreFileName\fP]
@.br
	[\fB-s\fP\fIsaveFileName\fP]
@.br
	[\fB-d\fP\fIpieceIconDirectory\fP]
@.br
	[\fB-t\fP\fItranscriptFileName\fP]
@.br
	[\fB-x\fP\fIn\fP]
@.br

	\fB-m\fP\fIchessProgram\fP [\fB-m\fP\fIarg\fP ...] | \fB-n\fP\fIchessProgram\fP [\fB-n\fP\fIarg\fP ...]
@.br
	or
@.br
	\fB-m\fP\fIchessProgram\fP [\fB-m\fP\fIarg\fP ...] \fB-n\fP\fIchessProgram\fP [\fB-n\fP\fIarg\fP ...]
@.br	
	or
@.br
	[\fB-b | -w\fP] \fIuser@host\fP
@.SH DESCRIPTION
@.I Nchess
is a general-purpose chess interface suitable for playing any 
combination of { human, machine } vs. { human, machine }.
Some of the more notable features of nchess 
are facilities for generating transcript files
in various formats,
for saving and restoring games,
and for setting up initial board situations.
@.SH "INVOCATION"
@.LP
Nchess accepts numerous command line arguments:
@.IP "\fB-c\fP"
don't keep an internal copy of the board's display image
(the default is to keep one)
@.IP "\fB-b\fP"
ask to play the black pieces
@.IP "\fB-w\fP"
ask to play the white pieces
@.IP "\fB-e\fI"
edit the board (i.e., set up a non-standard board configuration)
prior to starting the game
@.IP "\fB-r\fP \fIrestoreFileName\fP"
resume a game from the game saved in \fIrestoreFileName\fP
@.IP "\fB-s\fP \fIsaveFileName\fP"
use \fIsaveFileName\fP for saving games instead of the 
default \fB"nchess.save"\fP.
@.IP "\fB-t\fP \fItranscriptFileName\fP"
use \fItranscriptFileName\fP for writing transcripts instead of the 
default \fB"nchess.transcript"\fP.
@.IP "\fB-x\fP\fIn\fP"
generate transcripts in format \fIn\fP, where \fIn\fP is one of:
@.nf

		0	formal; e.g., P/K2-K4, R/KB3xP
		1	minimal; e.g., P-K4, RxP (the default)
		2	algebraic; e.g., E2E4, F3F5
@.fi
@.IP "\fB-d\fP \fIpieceIconDirectory\fP"
look in \fIpieceIconDirectory\fP for piece icons to use instead
of the default ones supplied by \fInchess\fP.
@.IP "\fB-m\fP \fIchessProgram\fP"
start up \fIchessProgram\fP playing the white pieces
@.IP "\fB-n\fP \fIchessProgram\fP"
start up \fIchessProgram\fP playing the black pieces
@.LP
Specifying \fB-c\fP forces \fInchess\fP to recompute the board
image whenever any portion of it must be made visible again.
However, the memory savings it entails may justify its use 
for systems which are running a bit short.
@.LP
The \fB-m\fP and \fB-n\fP arguments are cumulative.
The first of either is interpreted as the name of an 
executable chess program;
subsequent instances are collected and passed as arguments 
to the chess program.
@.SH "HUMAN VS. HUMAN"
@.LP
Human vs. human games are started by
invoking \fInchess\fP with an argument of
the form "\fIuser@host\fP" and
neither of the \fB-m\fP, \fB-n\fP arguments.
Both the local machine and the host specified by the \fIhost\fP
argument must be running \fInchessd(6)\fP (hereafter referred
to as the daemon).
\fINchess\fP first checks the local daemon for an invitation 
registered by \fIuser\fP at \fIhost\fP.
If none is found, \fInchess\fP sends an invitation to the 
daemon running on \fIhost\fP,
prints something like "Waiting for \fIuser\fP to respond...",
then idles waiting for a message from a 
responding \fInchess\fP process.
In turn, the remote daemon prints something useful 
and sufficiently distracting on the remote console.
@.LP
If an invitation was found waiting in the local daemon,
\fInchess\fP sends a response message to the 
peer \fInchess\fP process.
Subsequent communication is peer-to-peer; 
i.e., the daemon is no longer consulted for anything.
If either user specified \fB-b\fP or \fB-w\fP,
the two peers attempt to satisfy color preferences;
if both users requested the same color,
a single round of arbitration results.
If this arbitration fails, 
both \fInchess\fP processes exit, 
awaiting a less contentious session.
@.SH "HUMAN VS. MACHINE"
@.LP
Human vs. machine games are started by 
invoking \fInchess\fP with one of 
the \fI-m\fP or \fI-n\fP arguments.
The existence of the daemon is not required.
The chess program used is expected to conform to 
the interface protocol described below.
@.SH "MACHINE VS. MACHINE"
@.LP
Machine vs. machine games are started by 
invoking \fInchess\fP with both of 
the \fI-m\fP and \fI-n\fP arguments.
The existence of the daemon is not required.
Both chess programs used are expected to conform to 
the interface protocol described below.
@.SH "MESSAGE SUBWINDOW"
@.LP
During the course of the game, 
various messages appear in a one-line subwindow
directly over the button panel.
These messages may direct you to press certain buttons
to confirm various actions (e.g., resigning, allowing an undo).
The cursor must be positioned in the board subwindow
when buttons are pressed in response to a message.
@.SH "NORMAL PLAY"
@.LP
In normal play, pieces are moved by positioning the cursor
over the square a piece resides on,
depressing the left mouse button,
moving the piece to the desired square,
and releasing the button.
After moving, activity in the board subwindow is 
suppressed until a move is made by the opponent.
@.LP
Certain move types deserve special mention.
En passant pawn captures are performed by moving the 
capturing pawn to the third rank square;
i.e., the square it will reside on after the move.
\fINchess\fP enforces the restriction that en passant
capturing rights expire after the move immediately 
following the pawn advance.
Castling to either side is performed by moving the
king two squares in the appropriate direction.
Finally, pawn "queening" is a three-step process:
First, the pawn is moved to the eighth rank, where
it is initially polymorphed to a queen.
Second, the pawn is rotated through the set of legal
pawn morphs for every left mouse button click (the legal
pawn morphs are queen, bishop, knight, and rook).
Finally, the move is sent to the opponent when the middle 
button is pressed.
(Note: If the opponent is a machine,
pawns may only be promoted to queens
and step two is skipped.)
@.LP
At any time during the game,
clicking the left mouse button on one of
several buttons above the board subwindow 
will have the following effects:
@.IP "(Resign)"
Resignation is final; and after confirming
that you really wanted to resign,
it's curtains...
@.IP "(Undo)"
Against a human opponent, this button causes \fInchess\fP
to forward a request to your opponent to undo your last move.
Assuming your opponent agrees, your last move will no longer
haunt you and it will be your turn again.
If it was your turn to play, 
your opponent's last move will be undone as well.
Against a machine opponent, 
acquiescence is implicit.
Undos can be repeated,
constrained only by reaching the initial game state and
the limits of your opponent's compassion.
@.IP "(Last Play)"
The last play made by either side will be illustrated by
briefly undoing the move in the board subwindow.
@.IP "(Transcript)"
A transcript of the game is written to the transcript file
specified on the command line; if no file was specified,
"nchess.transcript" is used.
@.IP "(Save)"
The game state (including the move history) is saved in the
file specified on the command line; if no file was specified,
"nchess.save" is used.
@.LP
When both players are machines, the "Resign" and "Undo"
buttons are omitted from the window.
@.SH "EDITING THE BOARD"
@.LP
When \fInchess\fP is invoked with the \fB-e\fP flag,
the tool starts up as a board editor with initial play suspended.
Pieces are deleted by positioning the cursor over the 
square the piece resides on and clicking the middle button.
Pieces are created by positioning the mouse over the square
a piece of the desired type and color would reside on at the 
beginning of a normal game, 
depressing the left button, 
moving the piece to the destination square,
and releasing the button.
When editing is complete and play is about to commence, 
the right button is clicked,
followed by another mouse click
to confirm exiting the board editor (once the board editor is
exited, it cannot be re-entered).
If this is a human vs. human game, the user is then asked
to specify which side moves first (via yet another mouse click).
Following that, the game is afoot.
@.LP
Boards can be edited for any of the { human, machine } combinations
described above.
After setting up a board for a machine vs. machine session, 
the human is reduced to being a mere spectator.
Also, in any combination involving machines the white pieces
always move first, due to a deficiency in the existing 
Unix chess program.
@.SH "BADGERING YOUR OPPONENT"
@.LP
\fINchess\fP provides a one-line talk subwindow in human vs. human
games for sending pithy messages to your opponent.
To send a message, position the cursor to the right of the "\fBSend:\fP"
field, type the message, and hit carriage return.
The message will not be sent until you've typed the carriage return,
allowing you to use the normal Unix line editing features.
@.SH "CREATING YOUR OWN PIECES"
@.LP 
\fINchess\fP allows you to supplant any or all of the pictorial piece 
representations (icons) with pieces of your own design.
A few guidelines concerning icon creation are outlined below.
For starters, you might want to take a look at the 
icons used by \fInchess\fP.
@.LP
All icons used by \fInchess\fP are drawn using \fIicontool(1)\fP,
and are (\fIicontool\fP's default) 64 pixels wide, 64 pixels high,
and 1 pixel deep.
Only the black pieces are drawn - the white pieces are created
by \fInchess\fP by inverting the black pieces.
You must leave a blank border of at 
least three pixels around the piece,
and the piece should be centered in \fIicontool's\fP drawing subwindow.
@.LP
After you have drawn the piece, you need to create the 
corresponding "stencil" image.
The stencil is used by \fInchess\fP to limit drawing the piece on
the board to only those areas within the 64 by 64 square that
actually represent the piece image.
Also, in order to create the white pieces correctly, the stencil
border needs to be grown one pixel past the piece image border - this
gives the white piece a thin black line border.
An easy way to create the stencil is to use the
following procedure:
@.IP
Load the piece image
@.IP
Select "Fill: black" and "Fill: xor" modes.
@.IP
Fill the entire drawing subwindow.  You should now have a reverse
video image of the piece.
@.IP
Select "Fill: white" and "Fill: src" modes, and get rid of all 
black pixels except for a one-line border 
around the white piece image.
@.IP
Select "Fill: black" mode and fill inside the border.
@.LP
You should now have a solid black image of the piece which is one
pixel bigger in all directions than the image you drew earlier.  
Store this image as the stencil.
If you want any parts of the piece to be transparent 
(e.g., windows in the rook),
simply leave those parts white in the stencil.
@.LP
The file names to use for the various pieces and stencils are listed
below in the "FILES" section.
Both the piece and stencil images must be accessible in the 
specified icon directory before \fInchess\fP will attempt to use them.
@.LP
In case you are concerned,
using your own pieces does not cause \fInchess\fP to use any more
memory than it already does.
@.SH "CHESS PROGRAM INTERFACE PROTOCOL"
@.LP
The protocol for communication with chess programs is derived
in bottom-up fashion from the syntax and semantics
used by the existing Unix chess program.
When the program starts up, 
it is expected to make a short one-line announcement (e.g., "Chess"),
which \fInchess\fP simply throws away.
Next, \fInchess\fP sends a single line consisting of the keyword "alg",
which informs the chess program that algebraic notation is desired.
@.LP
If \fInchess\fP was invoked with the \fB-e\fP flag,
the next communication with the chess program is to deliver the 
setup information.
This is done by sending a single line consisting 
of the keyword "setup",
followed by eight lines of eight characters each.
Each character represents the piece on a square, 
with space characters representing empty squares.
For the white pieces, 
the characters used are { p, n, b, r, q, k } for 
pawn, knight, bishop, rook, queen, and king, respectively.
Black pieces are represented by the upper case equivalent.
Lines are transmitted starting with the eighth rank (i.e., a8-h8).
After receiving the eighth setup line (a1-h1),
the chess program is expected to respond with a single line 
consisting of either the key 
phrase "Setup successful" or "Illegal setup".
@.LP
Next, the chess program will be sent either the first move by
its opponent (which will be white), 
or a single line consisting of the keyword "first",
which it should interpret to mean that it should play the white pieces
and that it should make the first move.
Moves sent to the chess program will always be in the format
implied by the printf string "%c%d%c%d\\n",
where the character specifications describe the file [a-h]
and the decimal digit specifications describe the rank [1-8].
En passant captures are encoded as a horizontal move; e.g., d5e5.
Castling moves are encoded as the king move; e.g., e1g1.
Pawns implicitly turn into queens; thus, d7d8 implies P-Q8(Q).
@.LP
After receiving a move, the chess program must echo a single line.
The standard Unix chess program uses this line to re-format and 
echo the move it received; 
however, \fInchess\fP does not interpret the echo and thus 
places no restrictions on its format.

@.SH "FILES"
@.br
bishop.icon
@.br
bishopStencil.icon
@.br
king.icon
@.br
kingStencil.icon
@.br
knight.icon
@.br
knightStencil.icon
@.br
pawn.icon
@.br
pawnStencil.icon
@.br
queen.icon
@.br
queenStencil.icon
@.br
rook.icon
@.br
rookStencil.icon
@.br
@.SH "SEE ALSO"
chess(6), chesstool(6), nchessd(6)
@.SH "BUGS"
@.LP
There aren't any clocks (yet).
@.LP
The board editor cannot be re-entered.  This is intentional, in order
to keep the middle of the game orderly.  However, some of the author's
cohorts think it is a bad idea.
@.LP
Transcript files do not show the initial board state, which makes
it necessary to separately document non-standard initial positions.
This will be corrected in a future release.
@.LP
The algebraic notation used for transcripts is not really what you
might expect.
@.LP
When either player is a machine, pawns may be promoted only to queens.
This bug is inherited from the existing Unix chess program.
@.LP
When either player is a machine, the white pieces must always make 
the first move in a game.
This is another bug inherited from the existing Unix chess program.
@.LP
Saved games must be re-started with the same configuration of human
and machine players.
This bug is due to the unknown format of the existing Unix chess
program's save files and to the lack of any known standard for 
the format of such files.
@.LP
The talk subwindow has no facility for queueing lines for paced
review by the receiver (as in games like rogue and hack).
Also, there is no way to shut the door if the opponent's messages
are a source of irritation instead of amusement or enlightenment.
@.LP
The rules for offering, accepting, and detecting draws are not
implemented.  These apparently have ramifications with regard to 
handling clocks, and are the principal reason clocks are not 
implemented yet.  All this will be implemented in a future release.
For now, the talk subwindow is expected to suffice.
@.LP
Checkmate and stalemate are not explicitly detected 
by \fInchess\fP, since it doesn't have an internal 
legal move generator.
Again, the talk subwindow is expected to suffice.
@.LP
There is currently no way to play Blitz chess rules (e.g., don't 
announce check, not forced to alleviate check, etc.).
@.LP
Multiple kings are allowed in human vs. human setups; however,
only the first one found is examined for being in check.
@.LP
In order to guarantee the ability to write background
chess process save files,
\fInchess\fP changes its working directory to /tmp following
the call to fork() and before the call to execvp().
Thus, chess programs in the former working directory
which were executable only via the '.'
component in the PATH environment variable 
will not be found by execvp().
This bug can be traced to the Unix chess program, which (apparently)
does not allow one to specify an alternative file name for its
save file.

@//E*O*F nchess.6//
chmod u=rw,g=r,o=r nchess.6
 
echo x - decls.h
sed 's/^@//' > "decls.h" <<'@//E*O*F decls.h//'
/*
 * global declarations 
 */

/* board.c */

extern void InitBoard(), DoMove(), UnDoMove(), ShowLastPlay(), 
    SaveGame(), WriteTranscript(), RestoreGame(), DoSetupChange(),
    DoResignation();
extern BOOL InitialTurn, IsOurPieceAt(), IsSrcPieceAt(), 
    IsMoveLegal(), IHaveMoved(), InCheck(), GameOver;
extern Square * GetSquare(), * GetSrcSquare();
extern int PromotePawn(), Turn;

/* boardsw.c */

extern void InitBoardSW(), DrawBoard(), DrawSquare(), AddVictim(),
    DeleteVictim(), KillMouseActivity(), RequestUndo();
extern MouseState Mouse;
extern BOOL Flashing;

/* chessprocess.c */

extern void InitChessProcess(), ReapChessProcesses(), KillChessProcesses(),
    SendMachineMove(), MachineUndo(), MachineRestore(), MachineFirst();
extern BOOL MachineSetup(), MachineDebug;
extern int GetMachineMove(), MachineSave(), ChessProcessFDs[];

/* controlsw.c */

extern void InitControlSW();
extern BOOL SaveWanted;

/* ipc.c */

extern int MyColor, PeerColor;
extern unsigned long PeerProgNum; 
extern void InitRPC(), SendResignation(), SendUndoRequest(), SendTalkMsg(),
    SendRestoreMove(), SendEndRestore(), SendSetupChange(),
    SendUndoAcknowledgement();
extern BOOL UndoWanted, SendMove(), RestoringGame;
extern char * PeerUserName;

/* main.c */

#ifdef FILE
extern FILE * RestoreFile;
#endif
extern int errno, TranscriptType;
extern char * TranscriptFileName, * SaveFileName, * PlayerName[];
extern BOOL SetupMode, IsMachine[2];
extern struct passwd * UserPWEntry;

/* msgsw.c */

extern void InitMsgSW(), Message(), ClearMessage(), WhoseMoveMessage();

/* rpcsw.c */

extern void AddRPCSubwindow(), DeleteRPCSubwindow();

/* select.c */

extern void SelectAll();

/* talksw.c */

extern void RecvTalkMsg(), InitTalkSW();

/* tool.c */

/* the following is a kludge, but hides enormous organizational problems
 * with Sun's headers */
#ifdef TOOL_NULL
extern Tool * NchessTool;
#endif
extern void RunTool(), InitTool(), ParseToolArgs();

/* xdr.c */

extern int XdrGameReq(), XdrMove(), XdrString(), XdrSetup();

/* undeclared system calls and library fxns */

extern int getpid();
extern long random();
extern char * malloc();
@//E*O*F decls.h//
chmod u=r,g=r,o=r decls.h
 
echo x - nchess.h
sed 's/^@//' > "nchess.h" <<'@//E*O*F nchess.h//'
/*
 * network chess header
 */

#define BOOL 			int
#ifndef TRUE
#define TRUE			1
#endif
#ifndef FALSE
#define FALSE			0
#endif

#define	SERVERPROGNUM	((u_long) 0x31233216)	/* RPC daemon program number */
#define VERSNUM			((u_long) 1)	/* RPC version number */

/*
 * daemon procedure numbers
 */
#define	REQPROCNUM		((u_long) 1)	/* game request */
#define	ACKPROCNUM		((u_long) 2)	/* game acknowledge */
#define	CANCELPROCNUM		((u_long) 3)	/* cancel a game request */

/*
 * peer procedure numbers 
 */
#define	ACCEPTPROCNUM		((u_long) 1)	/* game accepted */
#define	MOVEPROCNUM		((u_long) 2)	/* chess move */
#define	COLORFAILPROCNUM	((u_long) 3)	/* color arbitration failure */
#define	UNDOPROCNUM		((u_long) 4)	/* undo request */
#define	RESIGNPROCNUM		((u_long) 5)	/* resignation */
#define	UNDOACKPROCNUM		((u_long) 6)	/* undo request response */
#define	MSGPROCNUM		((u_long) 7)	/* one-liner antagonism */
#define	RESTOREMOVEPROCNUM	((u_long) 8)	/* move during game restoration */
#define	ENDRESTOREPROCNUM	((u_long) 9)	/* restoration/setup complete */
#define	SETUPPROCNUM		((u_long) 10)	/* board setup change */
#define	GOODBYEPROCNUM		((u_long) 11)	/* player killed his tool */

/*
 * color arbitration values - the values are important, as the 
 * game can proceed when the sum of the two players' colors is 
 * WANTSWHITE+WANTSBLACK; also, the values are used as indices 
 * into arrays.
 */
#define	WANTSBLACK		0		/* user wants black */
#define	BLACK			WANTSBLACK	/* user has black */
#define	WANTSWHITE		1		/* user wants white */
#define	WHITE			WANTSWHITE	/* user has white */
#define	EITHERCOLOR		2		/* user doesn't care */

#define	OTHERCOLOR(a)	((a) == WHITE ? BLACK : WHITE)
/*
 * rendezvous information 
 */
typedef struct {
    unsigned long progNum;			/* RPC program number of requester */
    int color;					/* requested color */
    int resumeGame;				/* boolean: wants to resume a game */
    char hostName[256];				/* host name of requester */
    char userName[256];				/* name of requesting user */
} GameRequest;

/*
 * piece move information 
 */
typedef struct {
    int x1;					/* origin square */
    int y1;
    int x2; 					/* destination square */
    int y2;
    int newPieceType;				/* new piece type for 8th rank pawns */
} Move;

/*
 * board square dimensions
 */
#define	SQUARE_WIDTH	64
#define	SQUARE_HEIGHT	SQUARE_WIDTH

/*
 * board coordinates
 */
typedef struct {
    int x;
    int y;
} BoardCoordinate;

/*
 * piece types
 */
typedef enum {
    PAWN = 0,
    KNIGHT,
    BISHOP,
    ROOK,
    QUEEN,
    KING,
    NULLPC,
} PieceType;

/*
 * square state
 */
typedef struct {
    PieceType type;
    int color;
} Square;

/*
 * setup change information 
 */
typedef struct {
    int x;
    int y;
    PieceType type;
    int color;
} SetupChange;

/*
 * mouse (in the board subwindow) activity states
 */
typedef enum {
    IDLE,		/* nada */
    MOVING_PIECE,	/* animating a piece with the left button down */
    PROMOTING_PAWN,	/* selecting a piece type for a pawn promotion */
    CONFIRMING,		/* borrowed for confirmation of something */
    LOCKED,		/* locked (ignored until unlocked) */
    SETUP,		/* setting up a board */
} MouseState;

/*
 * transcript types
 */
#define	TR_MIN_TYPE		0
#define	TR_FORMAL_NORMAL	0	/* P/K2-K4, R/KB3xP/KB5, etc. */
#define	TR_MIN_NORMAL		1	/* P-K4, RxP, etc. */
#define	TR_ALGEBRAIC		2	/* D2D4, etc. */
#define	TR_MAX_TYPE		2

#include "decls.h"

@//E*O*F nchess.h//
chmod u=r,g=r,o=r nchess.h
 
echo x - daemon.c
sed 's/^@//' > "daemon.c" <<'@//E*O*F daemon.c//'
/*
 * chess invitation rendezvous server
 *
 * there are three entry points: 
 * 	- request a game 
 *	- reply to a request
 *	- cancel a request
 */

#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/time.h>
#include <strings.h>

#include "nchess.h"

char LocalHostName[256];		/* local host name string */

#define	MAXINVITES	32
typedef struct {			/* registered invitations with call-back information */
    GameRequest gr;			/* call-back information */
    BOOL active;			/* is this slot active? */
    unsigned long time;			/* time invitation was registered */
} Invitation;

Invitation Invitations[MAXINVITES];

/*
 * register a game invitation and print a message on the console.
 */
void
gameRequest(gameReq)
    GameRequest * gameReq;
{
    struct timeval tv;
    struct tm * timep;
    register int i, oldSlot, oldTime;
    register Invitation * invp;
    int avail = -1;
    FILE * console;


    (void) gettimeofday(&tv, (struct timezone *) 0);
    timep = localtime((long *) &tv.tv_sec);
    oldTime = tv.tv_sec;
    /* find an empty invitation slot, keeping track of the oldest active one */
    for (i = 0 ; i < MAXINVITES ; i++) {
	invp = &Invitations[i];
	if ( ! invp->active) {
	    avail = i;
	    break;
	} else if (invp->time < oldTime) {
	    oldSlot = i;
	    oldTime = invp->time;
	}
    }
    /* if no slots are empty, re-use the oldest one */
    if (avail < 0) 
	avail = oldSlot;
    /* fill out the invitation slot */
    invp = &Invitations[avail];
    invp->active = TRUE;
    invp->time = tv.tv_sec;
    invp->gr.progNum = gameReq->progNum;
    invp->gr.color = gameReq->color;
    invp->gr.resumeGame = gameReq->resumeGame;
    strncpy(invp->gr.hostName, gameReq->hostName, sizeof(gameReq->hostName));
    strncpy(invp->gr.userName, gameReq->userName, sizeof(gameReq->userName));
    /* 
     * print the invitation message on the local console 
     */
    if ((console = fopen("/dev/console", "w")) != (FILE *) 0) {
	fprintf(console, "\n\007\007\007Message from Chess Daemon@%s at %d:%02d ...\n", 
	    LocalHostName, timep->tm_hour, timep->tm_min);
	if (gameReq->resumeGame) 
	    fprintf(console, "%s wants to resume a game of chess.\n", 
		gameReq->userName);
	else
	    fprintf(console, "%s wants to play a game of chess.\n", 
		gameReq->userName);
	fprintf(console, "reply with: nchess %s@%s\n", 
	    gameReq->userName, gameReq->hostName);
	fflush (console);
	fclose(console);
    }
}

/*
 * attempt to respond to a game invitation 
 */
GameRequest *
gameAcknowledge(gameAck)
    GameRequest * gameAck;
{
    static GameRequest gr;
    register int i, newestTime = 0, newestIndex = -1;
    
    /* 
     * look for the most recently registered invitation
     * if one exists, return the prog. number and the color,
     *		then remove the entry.
     * else return an entry with a program number of zero.
     */
    gr.progNum = 0;
    for (i = 0 ; i < MAXINVITES && Invitations[i].active ; i++) {
	if (strcmp(Invitations[i].gr.hostName, gameAck->hostName) == 0
	&& strcmp(Invitations[i].gr.userName, gameAck->userName) == 0
	&& Invitations[i].time > newestTime) {
	    newestIndex = i;
	    newestTime = Invitations[i].time;
	}
    }
    if (newestIndex >= 0) {
	gr.progNum = Invitations[newestIndex].gr.progNum;
	gr.color = Invitations[newestIndex].gr.color;
	gr.resumeGame = Invitations[newestIndex].gr.resumeGame;
	Invitations[newestIndex].active = FALSE;
    }
    return(&gr);
}

/*
 * cancel a game invitation 
 */
void
cancelRequest(gr)
    GameRequest * gr;
{
    register int i;
    
    for (i = 0 ; i < MAXINVITES && Invitations[i].active ; i++) {
	if (strcmp(Invitations[i].gr.hostName, gr->hostName) == 0
	&& strcmp(Invitations[i].gr.userName, gr->userName) == 0
	&& Invitations[i].gr.progNum == gr->progNum) {
	    Invitations[i].active = FALSE;
	    break;
	}
    }
}

/*ARGSUSED*/
main(argc, argv)
    int argc;
    char ** argv;
{
    if (gethostname(LocalHostName, sizeof(LocalHostName)) != 0) {
	fprintf(stderr, "%s: can't determine the local host name\n", argv[0]);
	exit(1);
    }
    pmap_unset(SERVERPROGNUM, VERSNUM);
    /* register the entry points */
    registerrpc(SERVERPROGNUM, VERSNUM, REQPROCNUM, 
	gameRequest, XdrGameReq, xdr_void);
    registerrpc(SERVERPROGNUM, VERSNUM, ACKPROCNUM, 
	gameAcknowledge, XdrGameReq, XdrGameReq);
    registerrpc(SERVERPROGNUM, VERSNUM, CANCELPROCNUM, 
	cancelRequest, XdrGameReq, xdr_void);
    svc_run();
    fprintf(stderr, "%s: exiting - svc_run returned\n", argv[0]);
    exit(1);
}

@//E*O*F daemon.c//
chmod u=r,g=r,o=r daemon.c
 
echo x - xdr.c
sed 's/^@//' > "xdr.c" <<'@//E*O*F xdr.c//'
/*
 * specialized xdr stuff
 */

#include <rpc/rpc.h>
#include <strings.h>

#include "nchess.h"

int
XdrGameReq(xdrsp, grp)
    XDR * xdrsp;
    GameRequest * grp;
{
    unsigned int size;
    char * cp;

    if ( ! xdr_u_long(xdrsp, &grp->progNum))
	return(0);
    if ( ! xdr_int(xdrsp, &grp->color))
	return(0);
    if ( ! xdr_int(xdrsp, &grp->resumeGame))
	return(0);
    size = sizeof(grp->hostName);
    cp = grp->hostName;
    if ( ! xdr_bytes(xdrsp, &cp, &size, size))
	return(0);
    size = sizeof(grp->userName);
    cp = grp->userName;
    if ( ! xdr_bytes(xdrsp, &cp, &size, size))
	return(0);
    return(1);
}

int
XdrMove(xdrsp, movep)
    XDR * xdrsp;
    Move * movep;
{
    if ( ! xdr_int(xdrsp, &movep->x1))
	return(0);
    if ( ! xdr_int(xdrsp, &movep->y1))
	return(0);
    if ( ! xdr_int(xdrsp, &movep->x2))
	return(0);
    if ( ! xdr_int(xdrsp, &movep->y2))
	return(0);
    if ( ! xdr_int(xdrsp, &movep->newPieceType))
	return(0);
    return(1);
}

int
XdrSetup(xdrsp, setup)
    XDR * xdrsp;
    SetupChange * setup;
{
    if ( ! xdr_int(xdrsp, &setup->x))
	return(0);
    if ( ! xdr_int(xdrsp, &setup->y))
	return(0);
    if ( ! xdr_int(xdrsp, (int *) &setup->type))
	return(0);
    if ( ! xdr_int(xdrsp, &setup->color))
	return(0);
    return(1);
}

int
XdrString(xdrsp, cp)
    XDR * xdrsp;
    char * cp;
{
    char c[128];
    strncpy(c, cp, sizeof(c));
    c[127] = '\0';
    if ( ! xdr_string(xdrsp, &cp, sizeof(c)))
	return(0);
    return(1);
}
@//E*O*F xdr.c//
chmod u=r,g=r,o=r xdr.c
 
echo mkdir - Icons
mkdir Icons
chmod u=rwx,g=rx,o=rx Icons
 
echo x - Icons/bishop.icon
sed 's/^@//' > "Icons/bishop.icon" <<'@//E*O*F Icons/bishop.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x000C,0x3000,0x0000,0x0000,0x000C,0x3000,0x0000,
	0x0000,0x001E,0x7800,0x0000,0x0000,0x003F,0x7C00,0x0000,
	0x0000,0x003F,0xBC00,0x0000,0x0000,0x007F,0xDE00,0x0000,
	0x0000,0x007F,0xEE00,0x0000,0x0000,0x00FF,0xEF00,0x0000,
	0x0000,0x01FF,0xF780,0x0000,0x0000,0x01FF,0xFB80,0x0000,
	0x0000,0x03FF,0xFBC0,0x0000,0x0000,0x03FF,0xFDC0,0x0000,
	0x0000,0x07FF,0xFEE0,0x0000,0x0000,0x07FF,0xFEE0,0x0000,
	0x0000,0x0FFF,0xFF70,0x0000,0x0000,0x0FFC,0x7FB0,0x0000,
	0x0000,0x1FFC,0x7FB8,0x0000,0x0000,0x1FFC,0x7FD8,0x0000,
	0x0000,0x1FFC,0x7FD8,0x0000,0x0000,0x1FFC,0x7FE8,0x0000,
	0x0000,0x3FFC,0x7FEC,0x0000,0x0000,0x3FFC,0x7FF4,0x0000,
	0x0000,0x3F80,0x03F4,0x0000,0x0000,0x3F80,0x03F8,0x0000,
	0x0000,0x3FFC,0x7FF8,0x0000,0x0000,0x3FFC,0x7FFC,0x0000,
	0x0000,0x3FFC,0x7FFC,0x0000,0x0000,0x3FFC,0x7FFC,0x0000,
	0x0000,0x3FFC,0x7FFC,0x0000,0x0000,0x3FFC,0x7FFC,0x0000,
	0x0000,0x3FFC,0x7FFC,0x0000,0x0000,0x1FFC,0x7FF8,0x0000,
	0x0000,0x1FFC,0x7FF8,0x0000,0x0000,0x1FFC,0x7FF8,0x0000,
	0x0000,0x1FFC,0x7FF8,0x0000,0x0000,0x1FFC,0x7FF8,0x0000,
	0x0000,0x0FFC,0x7FF0,0x0000,0x0000,0x0FFC,0x7FF0,0x0000,
	0x0000,0x0FFC,0x7FF0,0x0000,0x0000,0x07FF,0xFFE0,0x0000,
	0x0000,0x07FF,0xFFE0,0x0000,0x0000,0x07FF,0xFFE0,0x0000,
	0x0000,0x03FF,0xFFC0,0x0000,0x0000,0x03FF,0xFFC0,0x0000,
	0x0000,0x03FF,0xFFC0,0x0000,0x0000,0x03FF,0xFFC0,0x0000,
	0x0000,0x03FF,0xFFC0,0x0000,0x0000,0x03FF,0xFFC0,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/bishop.icon//
chmod u=rw,g=r,o=r Icons/bishop.icon
 
echo x - Icons/bishopStencil.icon
sed 's/^@//' > "Icons/bishopStencil.icon" <<'@//E*O*F Icons/bishopStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x000C,0x3000,0x0000,
	0x0000,0x001E,0x7800,0x0000,0x0000,0x001E,0x7800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x00FF,0xFF00,0x0000,
	0x0000,0x00FF,0xFF00,0x0000,0x0000,0x01FF,0xFF80,0x0000,
	0x0000,0x03FF,0xFFC0,0x0000,0x0000,0x03FF,0xFFC0,0x0000,
	0x0000,0x07FF,0xFFE0,0x0000,0x0000,0x07FF,0xFFE0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x07FF,0xFFE0,0x0000,0x0000,0x07FF,0xFFE0,0x0000,
	0x0000,0x07FF,0xFFE0,0x0000,0x0000,0x07FF,0xFFE0,0x0000,
	0x0000,0x07FF,0xFFE0,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/bishopStencil.icon//
chmod u=rw,g=r,o=r Icons/bishopStencil.icon
 
echo x - Icons/king.icon
sed 's/^@//' > "Icons/king.icon" <<'@//E*O*F Icons/king.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0006,0x6000,0x0000,0x0000,0x0006,0x6000,0x0000,
	0x0000,0x0006,0x6000,0x0000,0x0000,0x0F83,0xC1F0,0x0000,
	0x0000,0x7FE3,0xC7FE,0x0000,0x0001,0xFFF9,0x9FFF,0x8000,
	0x0003,0xFFFD,0xBFFF,0xC000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x001F,0xFFFF,0xFFFF,0xF800,0x001F,0xFFFF,0xFFFF,0xF800,
	0x003F,0xFFFF,0xFFFF,0xFC00,0x003F,0xFC7F,0xFE3F,0xFC00,
	0x003F,0xF00F,0xF00F,0xFC00,0x007F,0xF1E7,0xE78F,0xFE00,
	0x007F,0xE3FB,0xDFC7,0xFE00,0x007F,0xE7FF,0xFFE7,0xFE00,
	0x007F,0xE7FF,0xFFE7,0xFE00,0x007F,0xE7FF,0xFFE7,0xFE00,
	0x007F,0xE7FF,0xFFE7,0xFE00,0x007F,0xE7FF,0xFFE7,0xFE00,
	0x007F,0xE3FF,0xFFC7,0xFE00,0x007F,0xF3FF,0xFFCF,0xFE00,
	0x007F,0xF3FF,0xFFCF,0xFE00,0x003F,0xF9FF,0xFF9F,0xFC00,
	0x003F,0xF9FF,0xFF9F,0xFC00,0x003F,0xFCFF,0xFF3F,0xFC00,
	0x001F,0xFCFF,0xFF3F,0xF800,0x001F,0xFE7F,0xFE7F,0xF800,
	0x000F,0xFF7F,0xFEFF,0xF000,0x0007,0xFFBF,0xFDFF,0xE000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/king.icon//
chmod u=rw,g=r,o=r Icons/king.icon
 
echo x - Icons/kingStencil.icon
sed 's/^@//' > "Icons/kingStencil.icon" <<'@//E*O*F Icons/kingStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x0F8F,0xF1F0,0x0000,0x0000,0x7FE7,0xE7FE,0x0000,
	0x0001,0xFFFB,0xDFFF,0x8000,0x0003,0xFFFD,0xBFFF,0xC000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x001F,0xFFFF,0xFFFF,0xF800,0x001F,0xFFFF,0xFFFF,0xF800,
	0x003F,0xFFFF,0xFFFF,0xFC00,0x003F,0xFFFF,0xFFFF,0xFC00,
	0x007F,0xFFFF,0xFFFF,0xFE00,0x007F,0xFFFF,0xFFFF,0xFE00,
	0x007F,0xFFFF,0xFFFF,0xFE00,0x00FF,0xFFFF,0xFFFF,0xFF00,
	0x00FF,0xFFFF,0xFFFF,0xFF00,0x00FF,0xFFFF,0xFFFF,0xFF00,
	0x00FF,0xFFFF,0xFFFF,0xFF00,0x00FF,0xFFFF,0xFFFF,0xFF00,
	0x00FF,0xFFFF,0xFFFF,0xFF00,0x00FF,0xFFFF,0xFFFF,0xFF00,
	0x00FF,0xFFFF,0xFFFF,0xFF00,0x00FF,0xFFFF,0xFFFF,0xFF00,
	0x00FF,0xFFFF,0xFFFF,0xFF00,0x007F,0xFFFF,0xFFFF,0xFE00,
	0x007F,0xFFFF,0xFFFF,0xFE00,0x007F,0xFFFF,0xFFFF,0xFE00,
	0x003F,0xFFFF,0xFFFF,0xFC00,0x003F,0xFFFF,0xFFFF,0xFC00,
	0x001F,0xFFFF,0xFFFF,0xF800,0x000F,0xFFFF,0xFFFF,0xF000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0xC000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/kingStencil.icon//
chmod u=rw,g=r,o=r Icons/kingStencil.icon
 
echo x - Icons/knight.icon
sed 's/^@//' > "Icons/knight.icon" <<'@//E*O*F Icons/knight.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0ADA,0xC000,0x0000,
	0x0000,0xBE55,0x8018,0x0000,0x0000,0xCDFB,0x70F8,0x0000,
	0x0001,0x77FF,0xDBD0,0x0000,0x0002,0xDFFF,0x3FA0,0x0000,
	0x0004,0xBFFF,0xFE40,0x0000,0x000E,0xB7FF,0xFC80,0x0000,
	0x0017,0x7FFF,0xFD80,0x0000,0x0012,0xFFFF,0xFFE0,0x0000,
	0x003A,0xDFFF,0xFFF0,0x0000,0x004F,0xFFFF,0xFFF8,0x0000,
	0x0076,0xFFFF,0xFFFC,0x0000,0x005D,0xFFFF,0xFFFE,0x0000,
	0x004D,0xFFFF,0xFFBC,0x0000,0x006D,0xFFFF,0xFF9C,0x0000,
	0x007F,0xFFFF,0xFFBC,0x0000,0x00D7,0xFFFF,0xFFFE,0x0000,
	0x005D,0xFFFF,0xFFFF,0x0000,0x0077,0xFFEF,0xFFFF,0x8000,
	0x00D5,0xFFEF,0xFFFF,0xC000,0x005F,0xFFF7,0xFFFF,0xE000,
	0x01B7,0xFFFB,0xFFFF,0xF000,0x00EF,0xFFFD,0xFFF7,0xF000,
	0x0077,0xFFFE,0x7FFB,0xF800,0x00DF,0xFFFE,0x1FFD,0xF800,
	0x00B5,0xFFFF,0x007E,0xF800,0x00DF,0xFFFF,0x801F,0x7800,
	0x0077,0xFFFF,0x800F,0xB000,0x00DF,0xFFFF,0xC007,0xC000,
	0x00BB,0xFFFF,0xC003,0x8000,0x00AF,0xFFFF,0xE000,0x0000,
	0x0077,0xFFFF,0xF000,0x0000,0x00DF,0xFFFF,0xF800,0x0000,
	0x00BB,0xFFFF,0xFC00,0x0000,0x00EF,0xFFFF,0xFC00,0x0000,
	0x0057,0xFFFF,0xFE00,0x0000,0x0035,0xFFFF,0xFF00,0x0000,
	0x005B,0xFFFF,0xFF00,0x0000,0x002E,0xFFFF,0xFF80,0x0000,
	0x0027,0x7FFF,0xFF80,0x0000,0x001D,0xFFFF,0xFFC0,0x0000,
	0x000B,0x7FFF,0xFFE0,0x0000,0x000A,0xFFFF,0xFFF0,0x0000,
	0x000F,0xBFFF,0xFFF0,0x0000,0x0002,0xFFFF,0xFFF8,0x0000,
	0x0001,0x3FFF,0xFFFC,0x0000,0x0000,0xFFFF,0xFFFC,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/knight.icon//
chmod u=rw,g=r,o=r Icons/knight.icon
 
echo x - Icons/knightStencil.icon
sed 's/^@//' > "Icons/knightStencil.icon" <<'@//E*O*F Icons/knightStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0DB6,0xC000,0x0000,0x0000,0x1FFF,0xE01C,0x0000,
	0x0001,0x7FFF,0xF8FC,0x0000,0x0001,0xFFFF,0xFBFC,0x0000,
	0x0002,0xFFFF,0xFFF8,0x0000,0x0005,0xFFFF,0xFFF0,0x0000,
	0x000F,0xFFFF,0xFFE0,0x0000,0x0017,0xFFFF,0xFFC0,0x0000,
	0x000F,0xFFFF,0xFFE0,0x0000,0x002F,0xFFFF,0xFFF0,0x0000,
	0x005F,0xFFFF,0xFFF8,0x0000,0x00BF,0xFFFF,0xFFFC,0x0000,
	0x00FF,0xFFFF,0xFFFE,0x0000,0x007F,0xFFFF,0xFFFF,0x0000,
	0x00FF,0xFFFF,0xFFFE,0x0000,0x007F,0xFFFF,0xFFFE,0x0000,
	0x00FF,0xFFFF,0xFFFE,0x0000,0x017F,0xFFFF,0xFFFF,0x0000,
	0x00FF,0xFFFF,0xFFFF,0x8000,0x01FF,0xFFFF,0xFFFF,0xC000,
	0x007F,0xFFFF,0xFFFF,0xE000,0x01FF,0xFFFF,0xFFFF,0xF000,
	0x00FF,0xFFFF,0xFFFF,0xF800,0x01FF,0xFFFF,0xFFFF,0xF800,
	0x01FF,0xFFFF,0xFFFF,0xFC00,0x00FF,0xFFFF,0x7FFF,0xFC00,
	0x01FF,0xFFFF,0x9FFF,0xFC00,0x01FF,0xFFFF,0xC07F,0xFC00,
	0x00FF,0xFFFF,0xC01F,0xF800,0x01FF,0xFFFF,0xE00F,0xF000,
	0x01FF,0xFFFF,0xE007,0xC000,0x00FF,0xFFFF,0xF003,0x8000,
	0x01FF,0xFFFF,0xF800,0x0000,0x01FF,0xFFFF,0xFC00,0x0000,
	0x00FF,0xFFFF,0xFE00,0x0000,0x01FF,0xFFFF,0xFE00,0x0000,
	0x01FF,0xFFFF,0xFF00,0x0000,0x00FF,0xFFFF,0xFF80,0x0000,
	0x00BF,0xFFFF,0xFF80,0x0000,0x007F,0xFFFF,0xFFC0,0x0000,
	0x003F,0xFFFF,0xFFC0,0x0000,0x002F,0xFFFF,0xFFE0,0x0000,
	0x003F,0xFFFF,0xFFF0,0x0000,0x000F,0xFFFF,0xFFF8,0x0000,
	0x001F,0xFFFF,0xFFF8,0x0000,0x001F,0xFFFF,0xFFFC,0x0000,
	0x0007,0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/knightStencil.icon//
chmod u=rw,g=r,o=r Icons/knightStencil.icon
 
echo x - Icons/pawn.icon
sed 's/^@//' > "Icons/pawn.icon" <<'@//E*O*F Icons/pawn.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x00FF,0xFF00,0x0000,0x0000,0x01FF,0xFF80,0x0000,
	0x0000,0x03FF,0xFFC0,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7000,0x000E,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7000,0x000E,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/pawn.icon//
chmod u=rw,g=r,o=r Icons/pawn.icon
 
echo x - Icons/pawnStencil.icon
sed 's/^@//' > "Icons/pawnStencil.icon" <<'@//E*O*F Icons/pawnStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0001,0x8000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x00FF,0xFF00,0x0000,
	0x0000,0x01FF,0xFF80,0x0000,0x0000,0x03FF,0xFFC0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/pawnStencil.icon//
chmod u=rw,g=r,o=r Icons/pawnStencil.icon
 
echo x - Icons/queen.icon
sed 's/^@//' > "Icons/queen.icon" <<'@//E*O*F Icons/queen.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x00FF,0xFF00,0x0000,0x0020,0x00FF,0xFF00,0x0400,
	0x0030,0x01FF,0xFF80,0x0C00,0x0038,0x01FF,0xFF80,0x1C00,
	0x003C,0x03FF,0xFFC0,0x3C00,0x003C,0x03FF,0xFFC0,0x3C00,
	0x001E,0x07FE,0x7FE0,0x7800,0x001F,0x07FE,0x7FE0,0xF800,
	0x001F,0x8FFC,0x3FF1,0xF800,0x001F,0x8FFC,0x3FF1,0xF800,
	0x000F,0xDFF8,0x1FFB,0xF000,0x000F,0xFFF9,0x9FFF,0xF000,
	0x000F,0xFFF1,0x8FFF,0xF000,0x000F,0xFFF3,0xCFFF,0xF000,
	0x0007,0xFFE3,0xC7FF,0xE000,0x0007,0xFFE7,0xE7FF,0xE000,
	0x0007,0xFFC7,0xE3FF,0xE000,0x0007,0xFFCF,0xF3FF,0xE000,
	0x0007,0xFFE7,0xE7FF,0xE000,0x0003,0xFFE7,0xE7FF,0xC000,
	0x0003,0xFFF3,0xCFFF,0xC000,0x0003,0xFFF3,0xCFFF,0xC000,
	0x0003,0xFFF9,0x9FFF,0xC000,0x0001,0xFFF9,0x9FFF,0x8000,
	0x0001,0xFFFC,0x3FFF,0x8000,0x0001,0xFFFC,0x3FFF,0x8000,
	0x0001,0xFFFE,0x7FFF,0x8000,0x0000,0xFFFE,0x7FFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/queen.icon//
chmod u=rw,g=r,o=r Icons/queen.icon
 
echo x - Icons/queenStencil.icon
sed 's/^@//' > "Icons/queenStencil.icon" <<'@//E*O*F Icons/queenStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x8000,0x0000,
	0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,
	0x0000,0x0007,0xE000,0x0000,0x0000,0x0007,0xE000,0x0000,
	0x0000,0x000F,0xF000,0x0000,0x0000,0x000F,0xF000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x007F,0xFE00,0x0000,0x0000,0x007F,0xFE00,0x0000,
	0x0000,0x00FF,0xFF00,0x0000,0x0040,0x00FF,0xFF00,0x0200,
	0x0060,0x01FF,0xFF80,0x0600,0x0070,0x01FF,0xFF80,0x0E00,
	0x0078,0x03FF,0xFFC0,0x1E00,0x007C,0x03FF,0xFFC0,0x3E00,
	0x007E,0x07FF,0xFFE0,0x7E00,0x007E,0x07FF,0xFFE0,0x7E00,
	0x003F,0x0FFF,0xFFF0,0xFC00,0x003F,0x8FFF,0xFFF1,0xFC00,
	0x003F,0xDFFF,0xFFFB,0xFC00,0x003F,0xDFFF,0xFFFB,0xFC00,
	0x001F,0xFFFF,0xFFFF,0xF800,0x001F,0xFFFF,0xFFFF,0xF800,
	0x001F,0xFFFF,0xFFFF,0xF800,0x001F,0xFFFF,0xFFFF,0xF800,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/queenStencil.icon//
chmod u=rw,g=r,o=r Icons/queenStencil.icon
 
echo x - Icons/rook.icon
sed 's/^@//' > "Icons/rook.icon" <<'@//E*O*F Icons/rook.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x001F,0xF800,0x0000,0x0000,0x003F,0xFC00,0x0000,
	0x0000,0x783F,0xFC1E,0x0000,0x0001,0xF83F,0xFC1F,0x8000,
	0x0003,0xF83F,0xFC1F,0xC000,0x0007,0xF83F,0xFC1F,0xE000,
	0x0007,0xF83F,0xFC1F,0xE000,0x0007,0xF83F,0xFC1F,0xE000,
	0x0007,0xF9FF,0xFF9F,0xE000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0007,0xFFFF,0xFFFF,0xE000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x0D5F,0xFFF0,0x0000,
	0x0000,0x0D5F,0xFFF0,0x0000,0x0000,0x0D5F,0xFFF0,0x0000,
	0x0000,0x0D5F,0xFFF0,0x0000,0x0000,0x0D5F,0xFFF0,0x0000,
	0x0000,0x0D5F,0xFFF0,0x0000,0x0000,0x0D5A,0xBFF0,0x0000,
	0x0000,0x0D5A,0xBFF0,0x0000,0x0000,0x0FFA,0xBFF0,0x0000,
	0x0000,0x0FFA,0xBFF0,0x0000,0x0000,0x0FFA,0xBFF0,0x0000,
	0x0000,0x0FFA,0xBFF0,0x0000,0x0000,0x0FFA,0xB570,0x0000,
	0x0000,0x0FFA,0xB570,0x0000,0x0000,0x0FFF,0xF570,0x0000,
	0x0000,0x0FFF,0xF570,0x0000,0x0000,0x0FFF,0xF570,0x0000,
	0x0000,0x0FFF,0xF570,0x0000,0x0000,0x0FFF,0xF570,0x0000,
	0x0000,0x0FFF,0xF570,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFF0,0x0000,
	0x0000,0x0FFF,0xFFF0,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x3FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFE,0x0000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0001,0x8000,0x0001,0x8000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/rook.icon//
chmod u=rw,g=r,o=r Icons/rook.icon
 
echo x - Icons/rookStencil.icon
sed 's/^@//' > "Icons/rookStencil.icon" <<'@//E*O*F Icons/rookStencil.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x001F,0xF800,0x0000,
	0x0000,0x003F,0xFC00,0x0000,0x0000,0x787F,0xFE1E,0x0000,
	0x0001,0xFC7F,0xFE3F,0x8000,0x0003,0xFC7F,0xFE3F,0xC000,
	0x0007,0xFC7F,0xFE3F,0xE000,0x000F,0xFC7F,0xFE3F,0xF000,
	0x000F,0xFC7F,0xFE3F,0xF000,0x000F,0xFDFF,0xFFBF,0xF000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x000F,0xFFFF,0xFFFF,0xF000,0x000F,0xFFFF,0xFFFF,0xF000,
	0x0007,0xFFFF,0xFFFF,0xE000,0x0001,0xFFFF,0xFFFF,0x8000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x1FFF,0xFFF8,0x0000,
	0x0000,0x1FFF,0xFFF8,0x0000,0x0000,0x3FFF,0xFFFC,0x0000,
	0x0000,0x7FFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0003,0xFFFF,0xFFFF,0xC000,0x0003,0xFFFF,0xFFFF,0xC000,
	0x0001,0xFFFF,0xFFFF,0x8000,0x0000,0xFFFF,0xFFFF,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
@//E*O*F Icons/rookStencil.icon//
chmod u=rw,g=r,o=r Icons/rookStencil.icon
 
echo x - Icons/blackSquare.icon
sed 's/^@//' > "Icons/blackSquare.icon" <<'@//E*O*F Icons/blackSquare.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0x9111,0x1111,0x1111,0x1111,
	0x8888,0x8888,0x8888,0x8889,0xA222,0x2222,0x2222,0x2223,
	0xC444,0x4444,0x4444,0x4445,0xFFFF,0xFFFF,0xFFFF,0xFFFF
@//E*O*F Icons/blackSquare.icon//
chmod u=rw,g=r,o=r Icons/blackSquare.icon
 
echo x - Icons/whiteSquare.icon
sed 's/^@//' > "Icons/whiteSquare.icon" <<'@//E*O*F Icons/whiteSquare.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0x8000,0x0000,0x0000,0x0001,
	0x8000,0x0000,0x0000,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF
@//E*O*F Icons/whiteSquare.icon//
chmod u=rw,g=r,o=r Icons/whiteSquare.icon
 
echo x - Icons/nchess.icon
sed 's/^@//' > "Icons/nchess.icon" <<'@//E*O*F Icons/nchess.icon//'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0000,0x0000,0x0003,0xC000,0x0000,0x0000,0x0003,
	0xC000,0x0003,0xF000,0x0003,0xC000,0x0073,0x5380,0x0003,
	0xC000,0x01F2,0xB3C0,0x0003,0xC000,0x01F3,0x5B40,0x0003,
	0xC000,0x017B,0xFA40,0x0003,0xC000,0x010E,0x0A40,0x0003,
	0xC000,0x008E,0x0C80,0x0003,0xC000,0x0066,0x0300,0x0003,
	0xC000,0x0010,0x0C00,0x0003,0xC000,0x0012,0xA400,0x0003,
	0xC000,0x0018,0x0400,0x0003,0xC000,0x0018,0x0400,0x0003,
	0xC000,0x0019,0x0400,0x0003,0xC000,0x0019,0x0400,0x0003,
	0xC000,0x0019,0x0400,0x0003,0xC000,0x0011,0x2400,0x0003,
	0xC000,0x0011,0x2400,0x0003,0xC000,0x0010,0x2400,0x0003,
	0xC000,0x0030,0x27FF,0xFFFF,0xC000,0x0070,0x27FF,0xFFFF,
	0xC000,0x00E0,0x0200,0x0247,0xC000,0x01C0,0x0180,0x0D13,
	0xC000,0x0740,0x0140,0x188B,0xC000,0x0D18,0x0C40,0x2223,
	0xC000,0x1903,0x5040,0x4447,0xC000,0x3100,0x0040,0x9113,
	0xC000,0xE100,0x0043,0x888B,0xC001,0x80C0,0x0186,0x2223,
	0xC003,0x0038,0x0E0C,0x4447,0xC006,0x0007,0xF011,0x1113,
	0xC00C,0x0000,0x0028,0x888B,0xC038,0x0000,0x0062,0x2223,
	0xC07F,0xFFFF,0xFFFF,0xFFFF,0xC0D1,0x1111,0x1200,0x0003,
	0xC188,0x8888,0x8400,0x0003,0xC322,0x2222,0x2800,0x0003,
	0xCE44,0x4444,0x5000,0x0003,0xD911,0x1111,0x6000,0x0003,
	0xF888,0x8888,0x8000,0x0003,0xE222,0x2223,0x0000,0x0003,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
@//E*O*F Icons/nchess.icon//
chmod u=rw,g=r,o=r Icons/nchess.icon
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      58     183    1533 Makefile
     434    2596   16236 nchess.6
      87     234    2150 decls.h
     140     526    3434 nchess.h
     162     551    4501 daemon.c
      80     183    1474 xdr.c
      34      39    1933 bishop.icon
      34      39    1933 bishopStencil.icon
      34      39    1933 king.icon
      34      39    1933 kingStencil.icon
      34      39    1933 knight.icon
      34      39    1933 knightStencil.icon
      34      39    1933 pawn.icon
      34      39    1933 pawnStencil.icon
      34      39    1933 queen.icon
      34      39    1933 queenStencil.icon
      34      39    1933 rook.icon
      34      39    1933 rookStencil.icon
      34      39    1933 blackSquare.icon
      34      39    1933 whiteSquare.icon
      34      39    1933 nchess.icon
    1471    4858   58323 total
!!!
wc  Makefile nchess.6 decls.h nchess.h daemon.c xdr.c Icons/bishop.icon Icons/bishopStencil.icon Icons/king.icon Icons/kingStencil.icon Icons/knight.icon Icons/knightStencil.icon Icons/pawn.icon Icons/pawnStencil.icon Icons/queen.icon Icons/queenStencil.icon Icons/rook.icon Icons/rookStencil.icon Icons/blackSquare.icon Icons/whiteSquare.icon Icons/nchess.icon | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

toma@tc.fluke.COM (Tom Anderson) (01/09/87)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by spock!toma on Thu Jan  8 14:33:34 PST 1987
# Contents:  board.c main.c
 
echo x - board.c
sed 's/^@//' > "board.c" <<'@//E*O*F board.c//'
/*
 * manage the board state
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>

#include "nchess.h"

#define	TABSIZE		8

BOOL GameOver = FALSE;			/* game is over */
int MyColor = EITHERCOLOR;		/* my color */
int PeerColor = EITHERCOLOR;		/* opponent's color */
int Turn;				/* whose turn it is */
int InitialTurn;			/* whose turn it was initially */

BOOL KingMoved = FALSE,			/* has the king moved ? */
    KingRookMoved = FALSE,		/* has the king's rook moved ? */
    QueenRookMoved = FALSE;		/* has the queen's rook moved ? */

struct moveListNode {			/* move record list node */
    struct moveListNode * prev;		/* previous move */
    struct moveListNode * next;		/* next move */
    Move move;				/* the mechanics of the move */
    int tookX;				/* x coordinate of captured piece */
    int tookY;				/* y coordinate of captured piece */
    PieceType tookPiece;		/* captured piece type */
    BOOL movedQueenRook;		/* did this move the queen rook for the first time? */
    BOOL movedKingRook;			/* did this move the king rook for the first time? */
    BOOL movedKing;			/* did this move the king for the first time? */
};

#define MOVELISTNODE	struct moveListNode

MOVELISTNODE 
    * firstMove = (MOVELISTNODE *) 0,	/* head of the move list */
    * lastMove = (MOVELISTNODE *) 0,	/* tail of the move list */
    ** nextLink = &firstMove;		/* next link */

PieceType SourceTypes[8] = {
    ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
};

Square InitialBoard[8][8] = {
    {ROOK,BLACK}, {KNIGHT,BLACK}, {BISHOP,BLACK}, {QUEEN,BLACK}, {KING,BLACK}, {BISHOP,BLACK}, {KNIGHT,BLACK}, {ROOK,BLACK},
    {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK},
    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
    {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE},
    {ROOK,WHITE}, {KNIGHT,WHITE}, {BISHOP,WHITE}, {QUEEN,WHITE}, {KING,WHITE}, {BISHOP,WHITE}, {KNIGHT,WHITE}, {ROOK,WHITE},
};

Square MainBoard[8][8];

/*
 * are there any pieces between the start and the destination on 
 * board 'board' ?
 * (the start and destination must be on the same diagonal, the 
 * same rank, or the same file.)
 */
BOOL
interveningPieces(board, from, to)
    Square board[][8];
    BoardCoordinate * from, * to;
{
    register int x, y, xmax, ymax, xdelt, ydelt;
    int xmin, ymin;

    /*
     * if there are no intervening squares
     */
    if (abs(from->x - to->x) <= 1 && abs(from->y - to->y) <= 1) {
	return(FALSE);
    }
    /*
     * build the intervening path information 
     */
    if (from->x < to->x) {
	xmin = from->x + 1;
	xmax = to->x - 1;
	xdelt = 1;
    } else if (from->x > to->x) {
	xmin = to->x + 1;
	xmax = from->x - 1;
	xdelt = -1;
    } else {
	xmin = xmax = from->x;
	xdelt = 0;
    }
    if (from->y < to->y) {
	ymin = from->y + 1;
	ymax = to->y - 1;
	ydelt = 1;
    } else if (from->y > to->y) {
	ymin = to->y + 1;
	ymax = from->y - 1;
	ydelt = -1;
    } else {
	ymin = ymax = from->y;
	ydelt = 0;
    }
    x = from->x + xdelt ; y = from->y + ydelt ;
    while (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
	if (board[y][x].type != NULLPC) 
	    return(TRUE);
	x += xdelt ; y += ydelt ;
    }
    return(FALSE);
}

/*
 * set up a new board 
 */
void
initBoard(board) 
    Square board[][8];
{
    register int i, j;
    
    for (i = 0 ; i < 8 ; i++) {
	for (j = 0 ; j < 8 ; j++) {
	    board[i][j].type = InitialBoard[i][j].type;
	    board[i][j].color = InitialBoard[i][j].color;
	}
    }
}

/*
 * set up the visible playing surface at the beginning of the game
 */
void
InitBoard()
{
    InitialTurn = Turn = WHITE;
    initBoard(MainBoard);
}

/*
 * describe the state of the board square at x, y
 */
Square *
GetSquare(x, y)
{
    return (&MainBoard[y][x]);
}

/*
 * describe the state of the source board square at x, y
 */
Square *
GetSrcSquare(x, y)
{
    static Square square;

    square.color = (y <= 3 ? BLACK : WHITE);
    if (y == 1 || y == 6)
	square.type = PAWN;
    else if (y > 1 && y < 6)
	square.type = NULLPC;
    else 
	square.type = SourceTypes[x];
    return (&square);
}

/*
 * does the user have a piece at bp?
 */
BOOL
IsOurPieceAt(bp)
    BoardCoordinate * bp;
{
    return (MainBoard[bp->y][bp->x].color == MyColor
    && MainBoard[bp->y][bp->x].type != NULLPC);
}

/*
 * is this a setup source square?
 */
BOOL 
IsSrcPieceAt(bp)
    BoardCoordinate * bp;
{
    return(bp->y < 2 || bp->y > 5);
}

void
DoSetupChange(setup)
    SetupChange * setup;
{
    InitialBoard[setup->y][setup->x].type = MainBoard[setup->y][setup->x].type = setup->type;
    InitialBoard[setup->y][setup->x].color = MainBoard[setup->y][setup->x].color = setup->color;
    DrawSquare(setup->x, setup->y, &MainBoard[setup->y][setup->x]);
}

/*
 * is the square at 'x','y' on the board 'board' threatened by any 
 * pieces of the opposite color of 'myColor'?
 */
BOOL
threatened(board, xt, yt, myColor)
    Square board[][8];
    int xt, yt, myColor;
{
    register int x, y;
    BoardCoordinate from, to;

    to.x = xt; to.y = yt;
    for (x = 0 ; x < 8 ; x++) {
	for (y = 0 ; y < 8 ; y++) {
	    if (board[y][x].type == NULLPC || board[y][x].color == myColor)
		continue;
	    from.x = x; from.y = y;
	    switch (board[y][x].type) {
	    case KNIGHT:
		if (xt == x - 2 && (yt == y + 1 || yt == y - 1)
		|| xt == x - 1 && (yt == y + 2 || yt == y - 2)
		|| xt == x + 1 && (yt == y + 2 || yt == y - 2)
		|| xt == x + 2 && (yt == y + 1 || yt == y - 1))
		    return(TRUE);
		break;
	    case ROOK:
		if ((x == xt || y == yt) && ! interveningPieces(board, &from, &to))
		    return(TRUE);
		break;
	    case BISHOP:
		if (abs(x - xt) == abs(y - yt) && ! interveningPieces(board, &from, &to))
		    return(TRUE);
		break;
	    case QUEEN:
		if ((abs(x - xt) == abs(y - yt) || x == xt || y == yt)
		&& ! interveningPieces(board, &from, &to))
		    return(TRUE);
		break;
	    case PAWN:
		if ((xt == x - 1 || xt == x + 1)
		&& (myColor == WHITE && yt == y + 1 
		    || myColor == BLACK && yt == y - 1)
		)
		    return(TRUE);
		break;
	    case KING:
		if (abs(x - xt) <= 1 && abs(y - yt) <= 1)
		    return(TRUE);
		break;
	    }
	}
    }
    return(FALSE);
}

/*
 * is this a legal move?
 */
BOOL
isMoveLegal(board, color, from, to)
    Square board[][8];
    int color;
    BoardCoordinate * from, * to;
{
    Square oldS1, oldS2, oldS3, * s1, * s2, * s3 = (Square *) 0;
    BoardCoordinate bc;
    BOOL ok;

    s1 = &board[from->y][from->x];
    s2 = &board[to->y][to->x];
    /* illegal: if there is a piece of my color on the destination */
    if (s2->type != NULLPC && s2->color == color)
	return(FALSE);
    switch (s1->type) {
    case KNIGHT:
	/* illegal: if this is not a probable knight move */
	if (to->x == from->x || to->x > from->x + 2 || to->x < from->x - 2
	|| to->x == from->x - 2 && to->y != from->y + 1 && to->y != from->y - 1
	|| to->x == from->x - 1 && to->y != from->y + 2 && to->y != from->y - 2
	|| to->x == from->x + 1 && to->y != from->y + 2 && to->y != from->y - 2
	|| to->x == from->x + 2 && to->y != from->y + 1 && to->y != from->y - 1)
	    return(FALSE);
	break;
    case ROOK:
	/* 
	 * illegal: if this is not a probable rook move 
	 * or there is a piece between start and destination
	 */
	if (from->x != to->x && from->y != to->y
	|| interveningPieces(board, from, to))
	    return(FALSE);
	break;
    case BISHOP:
	/* 
	 * illegal: if this is not a probable bishop move 
	 * or there is a piece between start and destination
	 */
	if (abs(from->x - to->x) != abs(from->y - to->y)
	|| interveningPieces(board, from, to))
	    return(FALSE);
	break;
    case QUEEN:
	/* 
	 * illegal: if this is not a probable queen move 
	 * or there is a piece between start and destination
	 */
	if (abs(from->x - to->x) != abs(from->y - to->y)
	&& from->x != to->x && from->y != to->y
	|| interveningPieces(board, from, to)) 
	    return(FALSE);
	break;
    case PAWN:
	if (color == WHITE) {
	    /* 
	     * potentially legal: moves forward onto an empty square 
	     */
	    if (to->x == from->x && s2->type == NULLPC
	    && (to->y == from->y - 1 
		|| to->y == 4 && from->y == 6 && ! interveningPieces(board, from, to)))
		break;
	    /* 
	     * potentially legal: diagonal captures (including en passant)
	     */
	    if (to->y == from->y - 1 && (to->x == from->x + 1 || to->x == from->x - 1)) {
		if (s2->type != NULLPC)
		    break;
		else if (to->y == 2
		&& lastMove != (MOVELISTNODE *) 0
		&& lastMove->move.x1 == to->x 
		&& lastMove->move.x2 == to->x 
		&& lastMove->move.y1 == 1
		&& lastMove->move.y2 == 3 
		&& board[3][to->x].type == PAWN) {
		    s3 = &board[3][to->x];
		    break;
		}
	    }
	} else {
	    /* 
	     * potentially legal: moves forward onto an empty square 
	     */
	    if (to->x == from->x && s2->type == NULLPC
	    && (to->y == from->y + 1 
		|| to->y == 3 && from->y == 1 && ! interveningPieces(board, from, to)))
		break;
	    /* 
	     * potentially legal: diagonal captures (including en passant) 
	     */
	    if (to->y == from->y + 1 && (to->x == from->x + 1 || to->x == from->x - 1)) {
		if (s2->type != NULLPC)
		    break;
		else if (to->y == 5
		&& lastMove != (MOVELISTNODE *) 0
		&& lastMove->move.x1 == to->x 
		&& lastMove->move.x2 == to->x 
		&& lastMove->move.y1 == 6
		&& lastMove->move.y2 == 4
		&& board[4][to->x].type == PAWN) {
		    s3 = &board[4][to->x];
		    break;
		}
	    }
	}
	/* illegal: any other pawn moves */
	return(FALSE);
    case KING:
	/*
	 * potentially legal: single space moves
	 */
	if (abs(to->x - from->x) <= 1 && abs(to->y - from->y) <= 1)
	    break;
	/*
	 * potentially legal: castling moves with unmoved king and rook
	 * with no pieces between king and rook
	 *
	 * note: need to verify that the king and rook are still on their
	 * original squares in addition to not having moved, since a setup
	 * that finds them there assumes they were always there, but does
	 * not void the moved flags if they are not initially there (to 
	 * avoid having to store that state information in the save file).
	 * this is compatible with the unix chess game's interpretation of
	 * setups.
	 */
	if ( ! KingMoved && s1->type == KING && s1->color == color
	&& ! threatened(board, from->x, from->y, color)) {
	    if (to->x == 6 && ! KingRookMoved 
	    && board[to->y][7].type == ROOK && board[to->y][7].color == color) {
		bc.x = 7;
		bc.y = from->y;
		if ( ! interveningPieces(board, from, &bc)
		&& ! threatened(board, 5, from->y, color))
		    break;
	    } else if (to->x == 2 && ! QueenRookMoved
	    && board[to->y][0].type == ROOK && board[to->y][0].color == color) {
		bc.x = 0;
		bc.y = from->y;
		if ( ! interveningPieces(board, from, &bc)
		&& ! threatened(board, 3, from->y, color))
		    break;
	    }
	}
	/* illegal: any other king move */
	return(FALSE);
    }
    /* 
     * single remaining constraint: the king must not be in check after 
     * the move 
     */
    /* temporarily stuff the move in the board state */
    oldS1.type = s1->type; oldS1.color = s1->color;
    s1->type = NULLPC;
    oldS2.type = s2->type; oldS2.color = s2->color;
    s2->type = oldS1.type; s2->color = oldS1.color;
    if (s3 != (Square *) 0) {
	oldS3.type = s3->type; oldS3.color = s3->color;
	s3->type = NULLPC;
    }
    ok = ! inCheck(board, color);
    /* back out the move */
    s1->type = oldS1.type; s1->color = oldS1.color;
    s2->type = oldS2.type; s2->color = oldS2.color;
    if (s3 != (Square *) 0) {
	s3->type = oldS3.type; s3->color = oldS3.color;
    }
    return(ok);
}

/*
 * is this move (of ours) legal?
 */
BOOL
IsMoveLegal(from, to)
    BoardCoordinate * from, * to;
{
    return (isMoveLegal(MainBoard, MyColor, from, to));
}

/*
 * perform a move (either ours or the opponent's)
 * the move is assumed to be legal; hence, checks for certain move types
 * are minimal.
 */
void 
DoMove(move, drawIt)
    register Move * move;
    BOOL drawIt;
{
    register Square * from, * to;
    register MOVELISTNODE * mlnp;

    from = &MainBoard[move->y1][move->x1];
    to = &MainBoard[move->y2][move->x2];
    /* 
     * create the new move list node 
     */
    if ((mlnp = (MOVELISTNODE *) malloc(sizeof(MOVELISTNODE))) == (MOVELISTNODE *) 0) {
	fprintf(stderr, "can't create move list node\n");
	exit(1);
    }
    * nextLink = mlnp;			/* stuff the forward link */
    mlnp->prev = lastMove;		/* and the backward link */
    mlnp->next = (MOVELISTNODE *) 0;	/* set up the next forward link */
    nextLink = &(mlnp->next);
    lastMove = mlnp;			/* and the new tail pointer */
    mlnp->move.x1 = move->x1;		/* store the basic move mechanics */
    mlnp->move.y1 = move->y1;
    mlnp->move.x2 = move->x2;
    mlnp->move.y2 = move->y2;
    mlnp->move.newPieceType = (int) (mlnp->tookPiece = NULLPC);
    mlnp->movedQueenRook = mlnp->movedKingRook = mlnp->movedKing = FALSE;
    /* if this is an en passant capture */
    if (from->type == PAWN && to->type == NULLPC && move->x1 != move->x2) {
	AddVictim(PAWN, MainBoard[move->y1][move->x2].color, drawIt);
	mlnp->tookPiece = PAWN;
	mlnp->tookX = move->x2;
	mlnp->tookY = move->y1;
	MainBoard[move->y1][move->x2].type = NULLPC;
	if (drawIt) 
	    DrawSquare(move->x2, move->y1, &MainBoard[move->y1][move->x2]);
    /* else if it is a normal capture */
    } else if (to->type != NULLPC) {
	AddVictim(to->type, to->color, drawIt);
	mlnp->tookPiece = to->type;
	mlnp->tookX = move->x2;
	mlnp->tookY = move->y2;
    }
    /* move the piece from the origin square to the dest. square */
    to->type = from->type;
    to->color = from->color;
    /* put a null piece in the origin square */
    from->type = NULLPC;
    /* re-draw the origin and destination squares */
    if (drawIt) {
	DrawSquare(move->x1, move->y1, from);
	DrawSquare(move->x2, move->y2, to);
    }
    switch(to->type) {
    case KING:
	/* if this is my king, void the castling option */
	if (to->color == MyColor && ! KingMoved) 
	    KingMoved = mlnp->movedKing = TRUE;
	/* if this is a king-side castling move, move the king's rook */
	if (move->x1 == 4 && move->x2 == 6) {
	    from = &MainBoard[move->y2][7];
	    to = &MainBoard[move->y2][5];
	    to->type = ROOK;
	    to->color = from->color;
	    from->type = NULLPC;
	    if (drawIt) {
		DrawSquare(7, move->y1, from);
		DrawSquare(5, move->y1, to);
	    }
	/* else if this is a queen-side castling move, move the queen's rook */
	} else if (move->x1 == 4 && move->x2 == 2) {
	    from = &MainBoard[move->y2][0];
	    to = &MainBoard[move->y2][3];
	    to->type = ROOK;
	    to->color = from->color;
	    from->type = NULLPC;
	    if (drawIt) {
		DrawSquare(0, move->y1, from);
		DrawSquare(3, move->y2, to);
	    }
	}
	break;
    case PAWN:
	/* if this is a pawn reaching the 8th rank, polymorph it */
	if (move->y2 == 0 || move->y2 == 7) {
	    to->type = 
		(PieceType) (mlnp->move.newPieceType = move->newPieceType);
	    if (drawIt)
		DrawSquare(move->x2, move->y2, to);
	}
	break;
    case ROOK:
	/* check for first rook moves (to void the castling option) */
	if (to->color == MyColor) {
	    if (MyColor == BLACK) {
		if (move->x1 == 0 && move->y1 == 0 && ! QueenRookMoved) 
		    QueenRookMoved = mlnp->movedQueenRook = TRUE;
		else if (move->x1 == 7 && move->y1 == 0 && ! KingRookMoved)
		    KingRookMoved = mlnp->movedKingRook = TRUE;
	    } else {
		if (move->x1 == 0 && move->y1 == 7 && ! QueenRookMoved) 
		    QueenRookMoved = mlnp->movedQueenRook = TRUE;
		else if (move->x1 == 7 && move->y1 == 7 && ! KingRookMoved)
		    KingRookMoved = mlnp->movedKingRook = TRUE;
	    }
	} 
	break;
    }
}

/*
 * add a resignation as the last move in the move list.
 * this is marked as a move with a negative y destination coordinate
 * the color of the resigning player is stored in the x destination
 * coordinate.
 *
 * note: the last play pointer is not updated, so that the "Last Play"
 * button will show the move that preceded the resignation.
 */
void 
DoResignation(color)
    int color;
{
    register MOVELISTNODE * mlnp;

    /* 
     * create the new move list node 
     */
    if ((mlnp = (MOVELISTNODE *) malloc(sizeof(MOVELISTNODE))) == (MOVELISTNODE *) 0) {
	fprintf(stderr, "can't create move list node\n");
	exit(1);
    }
    * nextLink = mlnp;			/* stuff the forward link */
    mlnp->prev = lastMove;		/* and the backward link */
    mlnp->next = (MOVELISTNODE *) 0;	/* set up the next forward link */
    mlnp->move.x2 = color;
    mlnp->move.y2 = -1;
    GameOver = TRUE;
}

/*
 * undo the last move (either ours or the opponent's).
 */
void 
UnDoMove()
{
    register Square * from, * to;
    register Move * move;
    register MOVELISTNODE * mlnp;

    if ((mlnp = lastMove) == (MOVELISTNODE *) 0) {
	Message("You are already undone.");
	return;
    }
    move = &(mlnp->move);
    if (move->y2 < 0) {
	fputs("internal botch: tried to undo a resignation\n", stderr);
	return;
    }
    from = &MainBoard[move->y1][move->x1];
    to = &MainBoard[move->y2][move->x2];
    /* move the piece from the destination square to the origin square */
    from->type = to->type;
    from->color = to->color;
    to->type = NULLPC;
    /* if this move was a pawn reaching the eighth rank, turn it back into a lowly pawn */
    if (move->newPieceType != (int) NULLPC) 
	from->type = PAWN;
    /* re-draw the origin and destination squares */
    DrawSquare(move->x1, move->y1, from);
    DrawSquare(move->x2, move->y2, to);
    /* if this was a capture, re-draw the captured piece */
    if (mlnp->tookPiece != NULLPC) {
	DeleteVictim(
	    MainBoard[mlnp->tookY][mlnp->tookX].type = mlnp->tookPiece,
	    MainBoard[mlnp->tookY][mlnp->tookX].color = (from->color == BLACK ? WHITE : BLACK));
	DrawSquare(mlnp->tookX, mlnp->tookY, &MainBoard[mlnp->tookY][mlnp->tookX]);
    }
    /* if this move affected the ability to castle, restore it */
    if (mlnp->movedKing) 
	KingMoved = FALSE;
    if (mlnp->movedKingRook)
	KingRookMoved = FALSE;
    if (mlnp->movedQueenRook)
	QueenRookMoved = FALSE;
    switch(from->type) {
    case KING:
	/* if this was a king-side castling move, move the king's rook back */
	if (move->x1 == 4 && move->x2 == 6) {
	    from = &MainBoard[move->y2][7];
	    to = &MainBoard[move->y2][5];
	    from->type = ROOK;
	    from->color = to->color;
	    to->type = NULLPC;
	    DrawSquare(7, move->y1, from);
	    DrawSquare(5, move->y1, to);
	/* else if this is a queen-side castling move, move the queen's rook back */
	} else if (move->x1 == 4 && move->x2 == 2) {
	    from = &MainBoard[move->y2][0];
	    to = &MainBoard[move->y2][3];
	    from->type = ROOK;
	    from->color = to->color;
	    to->type = NULLPC;
	    DrawSquare(0, move->y1, from);
	    DrawSquare(3, move->y2, to);
	}
	break;
    }
    /* peel off the tail */
    if (mlnp->prev != (MOVELISTNODE *) 0) {
	lastMove = mlnp->prev;
	lastMove->next = (MOVELISTNODE *) 0;
	nextLink = &(lastMove->next);
    } else {
	nextLink = &firstMove;
	firstMove = lastMove = (MOVELISTNODE *) 0;
    }
    free((char *) mlnp);
}

/*
 * flash the last play by either me or my opponent
 * (NOTE: this sleeps for 1-2 seconds while showing the previous position - 
 * this should not be extended much longer, since this may overlap with
 * an RPC timeout if the opponent sends us his next move in the interim.)
 */
void
ShowLastPlay()
{
    register Move * move;
    register MOVELISTNODE * mlnp;
#define	FROM		0
#define	TO		1
    Square pre[4], post[4];
    BoardCoordinate pos[4];
    int lastSquareIndex = 1;
    register int i;

    /* ignore bogus last play requests */
    if ((mlnp = lastMove) == (MOVELISTNODE *) 0) {
	return;
    }
    move = &(mlnp->move);
    pos[FROM].x = move->x1; pos[FROM].y = move->y1;
    pos[TO].x = move->x2; pos[TO].y = move->y2;
    post[FROM].type = NULLPC;
    post[TO].type = MainBoard[pos[TO].y][pos[TO].x].type;
    post[TO].color = MainBoard[pos[TO].y][pos[TO].x].color;
    pre[FROM].type = post[TO].type;
    pre[FROM].color = post[TO].color;
    pre[TO].type = NULLPC;
    /* if this move was a pawn reaching the eighth rank */
    if (move->newPieceType != (int) NULLPC) 
	pre[FROM].type = PAWN;
    /* if this was a capture */
    if (mlnp->tookPiece != NULLPC) {
	/* if this was an en passant capture */
	if (pos[TO].y != mlnp->tookY) {
	    pos[++lastSquareIndex].x = mlnp->tookX;
	    pos[lastSquareIndex].y = mlnp->tookY;
	    post[lastSquareIndex].type = NULLPC;
	}
	pre[lastSquareIndex].type = mlnp->tookPiece;
	pre[lastSquareIndex].color = (pre[FROM].color == BLACK ? WHITE : BLACK);
    } else if (post[TO].type == KING) {
	/* if this was a king-side castling move */
	if (pos[FROM].x == 4 && pos[TO].x == 6) {
	    ++lastSquareIndex;
	    pos[lastSquareIndex].x = 7;
	    pos[lastSquareIndex + 1].x = 5;
	    pos[lastSquareIndex].y = pos[lastSquareIndex + 1].y = pos[FROM].y;
	    pre[lastSquareIndex].type = post[lastSquareIndex + 1].type = ROOK;
	    pre[lastSquareIndex].color = post[lastSquareIndex + 1].color = pre[FROM].color;
	    post[lastSquareIndex].type = pre[lastSquareIndex + 1].type = NULLPC;
	    ++lastSquareIndex;
	/* else if this is a queen-side castling move */
	} else if (move->x1 == 4 && move->x2 == 2) {
	    ++lastSquareIndex;
	    pos[lastSquareIndex].x = 0;
	    pos[lastSquareIndex + 1].x = 3;
	    pos[lastSquareIndex].y = pos[lastSquareIndex + 1].y = pos[FROM].y;
	    pre[lastSquareIndex].type = post[lastSquareIndex + 1].type = ROOK;
	    pre[lastSquareIndex].color = post[lastSquareIndex + 1].color = pre[FROM].color;
	    post[lastSquareIndex].type = pre[lastSquareIndex + 1].type = NULLPC;
	    ++lastSquareIndex;
	}
    }
    /* re-draw the pre-move configuration */
    for ( i = 0 ; i <= lastSquareIndex ; i++ ) 
	DrawSquare(pos[i].x, pos[i].y, &pre[i]);
    sleep((unsigned) 2);
    /* re-draw the post-move configuration */
    for ( i = 0 ; i <= lastSquareIndex ; i++ ) 
	DrawSquare(pos[i].x, pos[i].y, &post[i]);
}

/*
 * have I moved yet? 
 */
BOOL IHaveMoved()
{
    return (firstMove != (MOVELISTNODE *) 0
    && (MyColor == WHITE || firstMove != lastMove));
}

PieceType
pawnMorphs[] = { 
    QUEEN,			/* pawn -> queen */
    QUEEN,			/* knight -> queen */
    KNIGHT,			/* bishop -> knight */
    BISHOP,			/* rook -> bishop */
    ROOK			/* queen -> rook */
};

/*
 * select the next possible pawn promotion option 
 */
int
PromotePawn(blocp)
    BoardCoordinate * blocp;
{
    register Square * sp;

    sp = &MainBoard[blocp->y][blocp->x];
    lastMove->move.newPieceType = (int) (sp->type = pawnMorphs[(int) sp->type]);
    DrawSquare(blocp->x, blocp->y, sp);
    return((int) sp->type);
}

/*
 * is the 'color' king in check?
 */
BOOL 
inCheck(board, color)
    Square board[][8];
    int color;
{
    register int x, y;

    /* search for the king */
    for (x = 0 ; x < 8 ; x++) 
	for (y = 0 ; y < 8 ; y++) 
	    if (board[y][x].type == KING && board[y][x].color == color) 
		return (threatened(board, x, y, color));
    return(FALSE);
}

/*
 * am I in check?
 */
BOOL
InCheck() 
{
    return(inCheck(MainBoard, MyColor));
}

/* 
 * piece type characters
 */
char 
pieceChars[] = { 'P', 'N', 'B', 'R', 'Q', 'K' };

/*
 * normal board file descriptions
 */
char *
fileStrings[] = { "QR", "QN", "QB", "Q", "K", "KB", "KN", "KR" };

/*
 * write a string describing the board location 
 */
void
locString(cp, color, x, y)
    char * cp;
    int color, x, y;
{
    if (color == WHITE) 
	sprintf(cp, "%s%d", fileStrings[x], 8 - y);
    else 
	sprintf(cp, "%s%d", fileStrings[x], 1 + y);
}

/*
 * alias positions for alternative interpretations of move descriptions
 */
typedef struct {
    BoardCoordinate pos;	/* piece position */
    BOOL valid;			/* "currently under consideration" flag */
} Alias;

/*
 * mark aliases as valid which lie on a particular file
 * return the number qualified.
 * the file to use is always the first the first entry in the aliases array.
 */
void
markSpecificFile(aliases, aliasCount)
    Alias aliases[];
    int aliasCount;
{
    register int i, file;

    file = aliases[0].pos.x;
    for (i = 1 ; i < aliasCount ; i++) 
	aliases[i].valid = (aliases[i].pos.x == file);
}

/*
 * mark aliases as valid which lie on a generic file (b, n, or r)
 * or a specific file (q, k).
 * the file to use is always the first the first entry in the aliases array.
 */
void
markGenericFile(aliases, aliasCount)
    Alias aliases[];
    int aliasCount;
{
    register int i, file;

    file = aliases[0].pos.x;
    if (file == 3 || file == 4)
	markSpecificFile(aliases, aliasCount);
    else 
	for (i = 1 ; i < aliasCount ; i++)
	    aliases[i].valid = 
		(aliases[i].pos.x == file || aliases[i].pos.x == 7 - file);
}

/*
 * mark all aliases as valid
 */
void 
markAllAsValid(aliases, aliasCount)
    Alias aliases[];
    int aliasCount;
{
    register int i;

    for (i = 0 ; i < aliasCount ; i++) 
	aliases[i].valid = TRUE;
}

#define	NULLSIDE	0
#define	KINGSIDE	1
#define	QUEENSIDE	2

/*
 * determine if an alias list can be disambiguated by a kingside vs.
 * queenside distinction; if so, return KINGSIDE or QUEENSIDE and
 * limit the alias list to the first entry - otherwise, return
 * NULLSIDE.
 * Note: only bishops, knights, and rooks can be distinguished this
 * way - it would be too wierd to allow queens specified as "QQ".
 */
int
markBySide(aliases, aliasCount, pieceType, color)
    Alias aliases[];
    int aliasCount, color;
    PieceType pieceType;
{
    /* can only disambiguate two pieces this way */
    if (aliasCount > 2)
	return(NULLSIDE);
    if (pieceType == BISHOP 
	&& ((aliases[0].pos.x + aliases[0].pos.y) & 0x01) 
	!= ((aliases[1].pos.x + aliases[1].pos.y) & 0x01) 
    || (pieceType == ROOK || pieceType == KNIGHT)
	&& (aliases[0].pos.x != aliases[1].pos.x)) 
    {
	aliases[0].valid = TRUE;
	aliases[1].valid = FALSE;
	switch(pieceType) {
	case BISHOP:
	    if (((aliases[0].pos.x + aliases[0].pos.y) & 0x01) != 0 && color == BLACK
	    || ((aliases[0].pos.x + aliases[0].pos.y) & 0x01) == 0 && color == WHITE)
		return(KINGSIDE);
	    else
		return(QUEENSIDE);
	default:
	    return(aliases[0].pos.x < aliases[1].pos.x ? QUEENSIDE : KINGSIDE);
	}
    }
    return(NULLSIDE);
}

/*
 * return the number of possible interpretations
 * of the srcs[] array combined with the dests[] array.
 * entries that are under consideration have their valid flag set.
 */
int
findPossibles(board, color, srcs, srcCount, dests, destCount)
    Square board[][8];
    Alias srcs[], dests[];
    int color, srcCount, destCount;
{
    register int i, j;
    int possibles = 0;

    for (i = 0 ; i < srcCount ; i++) 
	for (j = 0 ; j < destCount ; j++) 
	    if (srcs[i].valid && dests[j].valid 
	    && isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos))
		possibles++;
    return(possibles);
}

/*
 * print the minimal normal notation for a capture (except en passant).
 * return the number of characters printed.
 *
 * note that we can find up to a maximum of ten pieces of the same type
 * in a "real" game where all eight pawns have been promoted to the
 * same piece type.
 */
int
printMinCapture(tfile, board, color, move, loc1, loc2, from, to)
    FILE * tfile;
    Square board[][8];
    register Square * from, * to;
    Move * move;
    char * loc1, * loc2;
    int color;
{
    register int srcCount = 1, destCount = 1, i, j;
    Alias srcs[10], dests[10];
    int possibles, moveCount;

    /* disambiguation state flags */
    BOOL 
    daSrcSingle = FALSE,	/* fully spec. src.; e.g., r(kr4)xp */
    daSrcBrdSide = NULLSIDE,  	/* side of the board/bishop color; e.g. krxp */
    daSrcSpecificFile = FALSE,	/* specific file; e.g. krpxr */
    daSrcGenericFile = FALSE,	/* generic file; e.g. rpxr */
    daDestSingle = FALSE,	/* fully spec. dest.; e.g., rxp(kr4) */
    daDestBrdSide = NULLSIDE,  	/* side of the board/bishop color; e.g. krxp */
    daDestSpecificFile = FALSE,  	/* specific file; e.g. rxkrp */
    daDestGenericFile = FALSE;	/* generic file; e.g. qxrp */

    /*
     * install the actual move as the first entries 
     */
    srcs[0].pos.x = move->x1; srcs[0].pos.y = move->y1; srcs[0].valid = TRUE;
    dests[0].pos.x = move->x2; dests[0].pos.y = move->y2; dests[0].valid = TRUE;
    /*
     * find the other pieces of the same color and type 
     * as the piece being moved,
     * along with the other pieces of the same type and color as the
     * piece being captured.
     */
    for (i = 0 ; i < 8 ; i++) {
	for (j = 0 ; j < 8 ; j++) {
	    if (board[j][i].type == from->type
	    && board[j][i].color == from->color
	    && ! (i == move->x1 && j == move->y1)) {
		srcs[srcCount].valid = FALSE;
		srcs[srcCount].pos.x = i;
		srcs[srcCount++].pos.y = j;
	    }
	    if (board[j][i].type == to->type
	    && board[j][i].color == to->color
	    && ! (i == move->x2 && j == move->y2)) {
		dests[destCount].valid = FALSE;
		dests[destCount].pos.x = i;
		dests[destCount++].pos.y = j;
	    }
	}
    }
    /*
     * reduce this to only the pieces that figure in ambiguous
     * captures.
     */
    possibles = 0;
    for (i = 0 ; i < srcCount ; i++) {
	for (j = 0 ; j < destCount ; j++) {
	    if (isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos)) {
		possibles++;
		srcs[i].valid = dests[j].valid = TRUE;
	    }
	}
    }
    /* condense the piece lists */
    for (i = j = 0 ; i < srcCount ; i++) {
	if (srcs[i].valid) {
	    if (i > j) {
		srcs[j].pos.x = srcs[i].pos.x;
		srcs[j].pos.y = srcs[i].pos.y;
		srcs[j].valid = srcs[i].valid;
	    }
	    j++;
	}
    }
    srcCount = j;
    for (i = j = 0 ; i < destCount ; i++) {
	if (dests[i].valid) {
	    if (i > j) {
		dests[j].pos.x = dests[i].pos.x;
		dests[j].pos.y = dests[i].pos.y;
		dests[j].valid = dests[i].valid;
	    }
	    j++;
	}
    }
    destCount = j;
    /* if there are ambiguous source pieces */
    if (possibles > 1 && srcCount > 1) {
	/*
	 * if only the source is ambiguous 
	 * or if the source is a pawn
	 * or if the destination is not an ambiguous pawn
	 * try to disambiguate the source alone
	 */
	if (destCount == 1 || from->type == PAWN || to->type != PAWN) {
	    /* if the source is a pawn */
	    if (from->type == PAWN) {
		/* try specifying the generic file source pawn; e.g., RPxP. */
		markGenericFile(srcs, srcCount);
		daSrcGenericFile = TRUE;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		/* try specifying the specific file source pawn; e.g., KRPxP.
		 * (note - if there is only one destination, 
		 * this will always succeed) */
		if (possibles > 1) {
		    markSpecificFile(srcs, srcCount);
		    daSrcSpecificFile = TRUE;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		/* if the above didn't work, back out the restrictions */
		if (possibles > 1) {
		    daSrcSpecificFile = daSrcGenericFile = FALSE;
		    markAllAsValid(srcs, srcCount);
		}
	    } 
	    /* else the source is a piece */
	    else {
		/* try using kingside or queenside prefixes.  */
		if ((daSrcBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) {
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		/* if there is only one destination,
		 * punt and fully specify the source square.  */
		if (possibles > 1 && destCount == 1) {
		    daSrcSingle = TRUE;
		    srcCount = 1;
		    if ((possibles = findPossibles(board, color, srcs, srcCount, dests, destCount)) != 1)
		    {
			fprintf(stderr, "transcript error: assertion #1 failed\n");
			possibles = 1;
		    }
		}
		/* if the above didn't work, back out the restrictions */
		if (possibles > 1) {
		    daSrcBrdSide = FALSE;
		    markAllAsValid(srcs, srcCount);
		}
	    }
	}
	/* if unsolved at this point, 
	 * there must be ambiguous destination pieces;
	 * otherwise, the previous clause would have solved it.  */
	if (possibles > 1 && destCount == 1) {
	    fprintf(stderr, "transcript error: assertion #2 failed\n");
	    fprintf(tfile, "%cx%c", 
		pieceChars[(int) from->type],
		pieceChars[(int) to->type]);
	    return(3);
	}
	/*
	 * if the destination is a pawn 
	 */
	if (possibles > 1 && to->type == PAWN) {
	    /* try specifying the generic file destination pawn; e.g., PxRP. */
	    markGenericFile(dests, destCount);
	    daDestGenericFile = TRUE;
	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    /* try specifying the specific file destination pawn; e.g., PxKRP */
	    if (possibles > 1) {
		markSpecificFile(dests, destCount);
		daDestSpecificFile = TRUE;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    }
	    /* if the source is a pawn */
	    if (possibles > 1 && from->type == PAWN) {
		/* back the destination spec. down to a generic file
		 * and specify the source generic file.  */
		markAllAsValid(dests, destCount);
		daDestGenericFile = daDestSpecificFile = FALSE;
		markGenericFile(dests, destCount);
		daDestGenericFile = TRUE;
		markGenericFile(srcs, srcCount); 
		daSrcGenericFile = TRUE;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		/*
		 * specify the specific source file.
		 * for example, consider the 
		 * case of two bishop pawns and two rook pawns 
		 * attacking two knight pawns and the queen and king
		 * pawns - - we will eventually wind up here
		 * with something like: "KBPxNP" (since "KBPxP" and
		 * "PxKNP" are both ambiguous.
		 * we may STILL have an ambiguity if there is more than 
		 * one pawn on the file.
		 */
		if (possibles > 1) {
		    markSpecificFile(srcs, srcCount);
		    daSrcSpecificFile = TRUE;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		/* fully specify the source square and back out all 
		 * destination specs.  */
		if (possibles > 1) {
		    markAllAsValid(dests, destCount);
		    daDestGenericFile = daDestSpecificFile = FALSE;
		    daSrcSingle = TRUE;
		    srcCount = 1;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		/* fully specify the source square and the generic
		 * destination file (will always be enough) */
		if (possibles > 1) {
		    markGenericFile(dests, destCount);
		    daDestGenericFile = TRUE;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		if (possibles > 1)
		    fprintf(stderr, "transcript error: assertion #3 failed\n");
		
	    }
	    /* else the source is a piece */
	    else if (possibles > 1) {
		/* back out the pawn file specs.
		 * try using kingside or queenside prefixes.  */
		markAllAsValid(dests, destCount);
		daDestGenericFile = daDestSpecificFile = FALSE;
		if ((daSrcBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) 
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		/* try fully specifying the source square */
		if (possibles > 1) {
		    daSrcSingle = TRUE;
		    srcCount = 1;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		/* specify only the generic dest. pawn file (will always be 
		 * enough; for example, consider two bishops of 
		 * the same color attacking two pawns) */
		if (possibles > 1) {
		    markGenericFile(dests, destCount);
		    daDestGenericFile = TRUE;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		if (possibles > 1)
		    fprintf(stderr, "transcript error: assertion #4 failed\n");
	    }
	}
	/* else the destination is a piece */
	else if (possibles > 1) {
	    /* if the source is a pawn, we know from the above that 
	    * specifying the source pawn file alone doesn't work.  */
	    if (from->type == PAWN) {
		/* try using kingside or queenside prefixes.  */
		if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) {
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		    /* try specifying the generic file src. pawn; e.g., RPxKB. */
		    if (possibles > 1) {
			markGenericFile(srcs, srcCount);
			daSrcGenericFile = TRUE;
			possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		    }
		    /* try specifying the specific file source pawn; e.g., KRPxKB */
		    if (possibles > 1) {
			markSpecificFile(srcs, srcCount);
			daSrcSpecificFile = TRUE;
			possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		    }
		}
		if (possibles > 1) {
		    /* back out any pawn limitations.
		     * try fully specifying the destination square.  */
		    daSrcSpecificFile = daSrcGenericFile = FALSE;
		    daDestSingle = TRUE;
		    destCount = 1;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		if (possibles > 1) {
		    /* specify the generic source file pawn.
		     * will always work, since at most one pawn can capture
		     * a specific square along a particular file.  */
		    daSrcGenericFile = TRUE;
		    markGenericFile(srcs, srcCount);
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		if (possibles > 1)
		    fprintf(stderr, "transcript error: assertion #5 failed\n");
	    } 
	    /* else both source and destination are pieces.  we know from 
	     * above that there are multiple destinations and that specifying 
	     * source kingside/queenside alone won't work.  */
	    else {
		/* try using dest. kingside or queenside prefixes.  */
		if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) 
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		/* try using src. kingside or queenside prefixes.  */
		if (possibles > 1 
		&& (daDestBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) 
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		if (possibles > 1) {
		    /* back out dest. k/q-side and fully specify the source. */
		    daDestBrdSide = NULLSIDE;
		    markAllAsValid(dests, destCount);
		    daSrcSingle = TRUE;
		    srcCount = 1;
		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		}
		if (possibles > 1) {
		    /* punt: fully specify the destination.  */
		    daDestSingle = TRUE;
		    /* no need to check the assertion that this is it,
		     * as it is really obvious. */
		}
	    }
	}
    } 
    /*
     * else only the destination is ambiguous
     */
    else if (possibles > 1) {
	if (to->type == PAWN) {
	    /* try generic file spec. */
	    markGenericFile(dests, destCount);
	    daDestGenericFile = TRUE;
	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    /* try specific file spec. */
	    if (possibles > 1) {
		markSpecificFile(dests, destCount);
		daDestSpecificFile = TRUE;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    }
	    /* try fully specifying the destination square */
	    if (possibles > 1) {
		daDestSingle = TRUE;
		destCount = 1;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    }
	    if (possibles > 1)
		fprintf(stderr, "transcript error: assertion #6 failed\n");
	} else {
	    /* try kingside/queenside */
	    if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) 
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    /* fully specify the destination square */
	    if (possibles > 1) {
		daDestSingle = TRUE;
		destCount = 1;
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    }
	    if (possibles > 1)
		fprintf(stderr, "transcript error: assertion #7 failed\n");
	}
    }
    /* now print the capture */
    /* if we must fully specify the source */
    if (daSrcSingle) {
	fprintf(tfile, "%c(%s)", pieceChars[(int) from->type], loc1);
	moveCount = 3 + strlen(loc1);
    } else {
	moveCount = 0;
	loc1 = fileStrings[move->x1];
	/* if we must specify queenside source */
	if (daSrcBrdSide == QUEENSIDE) {
	    fputc('Q', tfile);
	    moveCount = 1;
	/* else if we must specify kingside source */
	} else if (daSrcBrdSide == KINGSIDE) {
	    fputc('K', tfile);
	    moveCount = 1;
	/* else if we must specify the specific file */
	} else if (daSrcSpecificFile) {
	    fprintf(tfile, "%s", loc1);
	    moveCount = strlen(loc1);
	/* else if we must specify the generic file */
	} else if (daSrcGenericFile) {
	    fprintf(tfile, "%s", strlen(loc1) == 2 ? loc1 + 1 : loc1);
	    moveCount = 1;
	}
	fputc(pieceChars[(int) from->type], tfile);
	moveCount++;
    }
    fputc('x', tfile);
    moveCount++;
    /* if we must fully specify the destination */
    if (daDestSingle) {
	fprintf(tfile, "%c(%s)", pieceChars[(int) to->type], loc2);
	moveCount += 3 + strlen(loc2);
    } else {
	loc2 = fileStrings[move->x2];
	/* if we must specify queenside dest. */
	if (daDestBrdSide == QUEENSIDE) {
	    fputc('Q', tfile);
	    moveCount++ ;
	/* else if we must specify kingside dest. */
	} else if (daDestBrdSide == KINGSIDE) {
	    fputc('K', tfile);
	    moveCount++ ;
	/* else if we must specify the specific file */
	} else if (daDestSpecificFile) {
	    fprintf(tfile, "%s", loc2);
	    moveCount += strlen(loc2);
	/* else if we must specify the generic file */
	} else if (daDestGenericFile) {
	    fprintf(tfile, "%s", strlen(loc2) == 2 ? loc2 + 1 : loc2);
	    moveCount++;
	}
	fputc(pieceChars[(int) to->type], tfile);
	moveCount++;
    }
    return(moveCount);
}

/*
 * print the minimal normal notation for a non-special move.
 * return the number of characters printed.
 *
 * note that we can find up to a maximum of ten pieces of the same type
 * in a real game where all eight pawns have been promoted to the
 * same piece type.
 */
int
printMinMove(tfile, board, color, move, loc1, loc2, from)
    FILE * tfile;
    Square board[][8];
    register Square * from;
    Move * move;
    char * loc1, * loc2;
    int color;
{
    register int i, j;
    Alias srcs[10], dests[2];
    int srcCount = 1, destCount = 1, possibles, moveCount;
    /* disambiguation state flags */
    BOOL 
    daSingleDest = FALSE,	/* fully spec. dest.; e.g., r - kr4 */
    daBoardSide = NULLSIDE,  	/* specific side of the board/bishop color; 
				 * e.g. kr-r4 */
    daSingleSrc = FALSE;	/* fully spec. src.; e.g. r(kr4) - r4 */

    /*
     * install the actual move as the first entries 
     */
    srcs[0].pos.x = move->x1; srcs[0].pos.y = move->y1; srcs[0].valid = TRUE;
    dests[0].pos.x = move->x2; dests[0].pos.y = move->y2; dests[0].valid = TRUE;
    /*
     * find the other pieces of the same color and type 
     * as the piece being moved.
     */
    for (i = 0 ; i < 8 ; i++) {
	for (j = 0 ; j < 8 ; j++) {
	    if (board[j][i].type == from->type
	    && board[j][i].color == from->color
	    && ! (i == move->x1 && j == move->y1)) {
		srcs[srcCount].valid = FALSE;
		srcs[srcCount].pos.x = i;
		srcs[srcCount++].pos.y = j;
	    }
	}
    }
    /*
     * if the destination is a rook, knight, or bishop file
     * and there is no piece on the mirrored square,
     * double the possible destinations (e.g., "n3" means "kn3" or "qn3")
     */
    if (move->x2 < 3 || move->x2 > 4
    && board[move->y2][7-move->x2].type == NULLPC) {
	dests[1].pos.x = 7 - move->x2;
	dests[1].pos.y = move->y2;
	destCount = 2;
    }
    /*
     * reduce this to only the srcs and dests that figure in ambiguous
     * moves.
     */
    possibles = 0;
    for (i = 0 ; i < srcCount ; i++) {
	for (j = 0 ; j < destCount ; j++) {
	    if (isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos)) {
		possibles++;
		srcs[i].valid = dests[j].valid = TRUE;
	    }
	}
    }
    /* condense the src. piece list */
    for (i = j = 0 ; i < srcCount ; i++) {
	if (srcs[i].valid) {
	    if (i > j) {
		srcs[j].pos.x = srcs[i].pos.x;
		srcs[j].pos.y = srcs[i].pos.y;
		srcs[j].valid = srcs[i].valid;
	    }
	    j++;
	}
    }
    srcCount = j;
    /* condense the destination list */
    for (i = j = 0 ; i < destCount ; i++) {
	if (dests[i].valid) {
	    if (i > j) {
		dests[j].pos.x = dests[i].pos.x;
		dests[j].pos.y = dests[i].pos.y;
		dests[j].valid = dests[i].valid;
	    }
	    j++;
	}
    }
    destCount = j;
    if (possibles > 1) {
	/* try reducing to only one destination */
	if (destCount == 2) {
	    daSingleDest = TRUE;
	    dests[1].valid = FALSE;
	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    if (possibles > 1) {
		dests[1].valid = TRUE;
		daSingleDest = FALSE;
	    }
	}
	/* try specifying kingside/queenside */
	if (possibles > 1 
	&& (daBoardSide = markBySide(srcs, srcCount, from->type, color)) 
	    != NULLSIDE) 
	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	/* try restricting to only one destination */
	if (possibles > 1 && destCount == 2) {
	    daSingleDest = TRUE;
	    dests[1].valid = FALSE;
	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
	    if (possibles > 1) {
		dests[1].valid = TRUE;
		daSingleDest = FALSE;
	    }
	}
	/* try fully specifying the source */
	if (possibles > 1) {
	    daSingleSrc = TRUE;
	    srcCount = 1;
	    if (destCount > 1) {
		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
		if (possibles > 1) {
		    dests[1].valid = FALSE;
		    daSingleDest = TRUE;
		}
	    }
	}
    } 
    /* now print the move */
    /* if we must fully specify the source */
    if (daSingleSrc) {
	fprintf(tfile, "%c(%s)", pieceChars[(int) from->type], loc1);
	moveCount = 3 + strlen(loc1);
    } else {
	moveCount = 0;
	/* if we must specify queenside source */
	if (daBoardSide == QUEENSIDE) {
	    fputc('Q', tfile);
	    moveCount = 1;
	/* else if we must specify kingside source */
	} else if (daBoardSide == KINGSIDE) {
	    fputc('K', tfile);
	    moveCount = 1;
	} 
	fputc(pieceChars[(int) from->type], tfile);
	moveCount++;
    }
    fputc('-', tfile);
    moveCount++;
    /* if we must fully specify the destination */
    if (daSingleDest) {
	fprintf(tfile, "%s", loc2);
	moveCount += strlen(loc2);
    /* else print only the generic rank and file */
    } else {
	fprintf(tfile, "%s", strlen(loc2) == 2 ? loc2 : loc2 + 1);
	moveCount += 2;
    }
    return(moveCount);
}

/*
 * write a transcript file
 */
void 
WriteTranscript(filename, trType)
    char * filename;
    int trType;
{
    register Square * from, * to;
    register Move * move;
    FILE * tfile;
    MOVELISTNODE * mlnp;
    int moveSize, color = WHITE, moveNum = 1;
    Square board[8][8]; 
    char loc1[5], loc2[5];

    if ((tfile = fopen(filename, "w")) == (FILE *) 0) {
	Message("Can't open transcript file!");
	return;
    } else {
	Message("Writing transcript...");
    }
    /* create a new initial board layout */
    initBoard(board);
    fprintf(tfile, "\n\t%s", PlayerName[WHITE]);
    moveSize = strlen(PlayerName[WHITE]);
    if (moveSize < 2 * TABSIZE) {
	fputc('\t', tfile);
	if (moveSize < TABSIZE) 
	    fputc('\t', tfile);
    }
    fprintf(tfile, "%s\n\n", PlayerName[BLACK]);
    for (color = WHITE , mlnp = firstMove , moveSize = 0 ; 
    mlnp != (MOVELISTNODE *) 0 ; 
    mlnp = mlnp->next, color = (color == BLACK ? WHITE : BLACK)) 
    {
	move = &mlnp->move;
	/* if this is a resignation */
	if (move->y2 < 0) {
	    fputs(move->x2 == WHITE ? 
		"\n\tresigns\n" : 
		"\n\t\t\tresigns\n", tfile);
	    break;
	}
	if (color == WHITE) {
	    fprintf(tfile, "%d.\t", moveNum++);
	} else {
	    fputc('\t', tfile);
	    if (moveSize < TABSIZE) 
		fputc('\t', tfile);
	}
	/* if we are using algebraic notation */
	if (trType == TR_ALGEBRAIC) {
	    moveSize = 4;
	    fprintf(tfile, "%c%d%c%d",
		'a' + move->x1, 8 - move->y1,
		'a' + move->x2, 8 - move->y2);
	/* else normal notation */
	} else {
	    from = &board[move->y1][move->x1];
	    to = &board[move->y2][move->x2];
	    locString(loc1, color, move->x1, move->y1);
	    locString(loc2, color, move->x2, move->y2);
	    /* if this was an en passant capture */
	    if (from->type == PAWN && to->type == NULLPC && move->x1 != move->x2) {
		switch (trType) {
		case TR_FORMAL_NORMAL:
		    fprintf(tfile, "p/%sxp/%s", loc1, loc2);
		    moveSize = 5 + strlen(loc1) + strlen(loc2);
		    break;
		case TR_MIN_NORMAL:
		    fprintf(tfile, "PxP(ep)");
		    moveSize = 7;
		    break;
		}
		board[move->y1][move->x2].type = NULLPC;
	    /* else if this was a normal capture */
	    } else if (to->type != NULLPC) {
		switch (trType) {
		case TR_FORMAL_NORMAL:
		    fprintf(tfile, "%c/%sx%c/%s", 
			pieceChars[(int) from->type], loc1, 
			pieceChars[(int) to->type], loc2);
		    moveSize = 5 + strlen(loc1) + strlen(loc2);
		    break;
		case TR_MIN_NORMAL:
		    moveSize = printMinCapture(tfile, board, color, move, loc1, loc2, from, to);
		    break;
		}
	    /* else if this was a king move */
	    } else if (from->type == KING) {
		/* 
		 * if this was a king-side castling move
		 */
		if (move->x1 == 4 && move->x2 == 6) {
		    /* move the rook */
		    board[move->y2][5].type = ROOK;
		    board[move->y2][5].color = board[move->y2][7].color;
		    board[move->y2][7].type = NULLPC;
		    fprintf(tfile, "O-O");
		    moveSize = 3;
		/* 
		 * else if this was a queen-side castling move
		 */
		} else if (move->x1 == 4 && move->x2 == 2) {
		    /* move the rook */
		    board[move->y2][3].type = ROOK;
		    board[move->y2][3].color = board[move->y2][0].color;
		    board[move->y2][0].type = NULLPC;
		    fprintf(tfile, "O-O-O");
		    moveSize = 5;
		/* else this was a normal king move */
		} else {
		    switch (trType) {
		    case TR_FORMAL_NORMAL:
			fprintf(tfile, "k/%s-%s", loc1, loc2);
			moveSize = 3 + strlen(loc1) + strlen(loc2);
			break;
		    case TR_MIN_NORMAL:
			fprintf(tfile, "K-%s", 
			    strlen(loc2) == 2 ? loc2 : loc2 + 1);
			moveSize = 4;
			break;
		    }
		}
	    /* else this was a normal piece move */
	    } else {
		switch (trType) {
		case TR_FORMAL_NORMAL:
		    fprintf(tfile, "%c/%s-%s", 
			pieceChars[(int) from->type], loc1, loc2);
		    moveSize = 3 + strlen(loc1) + strlen(loc2);
		    break;
		case TR_MIN_NORMAL:
		    moveSize = printMinMove(tfile, board, color, move, loc1, loc2, from);
		    break;
		}
	    }
	}
	/* move the piece from the origin square to the dest. square */
	to->type = from->type;
	to->color = from->color;
	/* put a null piece in the origin square */
	from->type = NULLPC;
	/* 
	 * if this was a pawn reaching the 8th rank
	 */
	if (to->type == PAWN 
	&& (move->y2 == 0 || move->y2 == 7)) {
	    to->type = (PieceType) move->newPieceType;
	    fprintf(tfile, "(%c)", pieceChars[move->newPieceType]);
	    moveSize += 3;
	}
	/* if this put the opposite side in check */
	if (inCheck(board, color == WHITE ? BLACK : WHITE)) {
	    switch(trType) {
	    case TR_FORMAL_NORMAL:
		fputc('+', tfile);
		moveSize++;
		break;
	    case TR_MIN_NORMAL:
		fputs("ch", tfile);
		moveSize += 2;
		break;
	    }
	}
	if (color == BLACK)
	    fputc('\n', tfile);
    }
    WhoseMoveMessage("Transcript written");
    fclose(tfile);
}

/*
 * save game file format:
 *
 * int MyColor			color of player saving game
 * int InitialTurn		whose turn it was initially
 * int IsMachine[2]		whether each color is a machine
 * Square board[8][8]		initial board state
 * if (IsMachine[0])
 *	int chessSaveFileSize	# of bytes for embedded chess.out
 * 	char[...]		chess.out
 * endif
 * if (IsMachine[1])
 *	int chessSaveFileSize	# of bytes for embedded chess.out
 * 	char[...]		chess.out
 * endif
 * Move[...]			list of moves
 */

/*
 * save the game 
 */
void 
SaveGame(filename)
    char * filename;
{
    FILE * gameFile, * chessOutFile;
    register MOVELISTNODE * mlnp;
    register int i, j;
    int chessOutSize;

    if ((gameFile = fopen(filename, "w")) == (FILE *) 0) {
	Message("Can't open save file!");
	return;
    } else {
	Message("Saving game...");
    }
    if (fwrite((caddr_t) &MyColor, sizeof(MyColor), 1, gameFile) != 1
    || fwrite((caddr_t) &InitialTurn, sizeof(InitialTurn), 1, gameFile) != 1
    || fwrite((caddr_t) IsMachine, sizeof(IsMachine), 1, gameFile) != 1
    || fwrite((caddr_t) InitialBoard, sizeof(InitialBoard), 1, gameFile) != 1)
    {
	Message("Write error - save aborted");
	return;
    }
    for (i = 0 ; i < 2 ; i++) {
	if (IsMachine[i]) {
	    chessOutSize = MachineSave(i);
	    if ((chessOutFile = fopen("/tmp/chess.out", "r")) == (FILE *) 0) {
		Message("Can't open chess.out!");
		return;
	    }
	    if (fwrite((caddr_t) &chessOutSize, sizeof(chessOutSize), 1, gameFile) != 1) {
		Message("Write error - save aborted");
		return;
	    }
	    while ((j = fgetc(chessOutFile)) != EOF) {
		fputc(j, gameFile);
		chessOutSize--;
	    }
	    fclose(chessOutFile);
	    unlink("/tmp/chess.out");
	    if (chessOutSize != 0) {
		Message("Write error - save aborted");
		return;
	    }
	}
    }
    /*
     * save everything except trailing resignations
     */
    for (mlnp = firstMove ; 
    mlnp != (MOVELISTNODE *) 0 && mlnp->move.x2 >= 0 ; 
    mlnp = mlnp->next) {
	if (fwrite((caddr_t) &mlnp->move, sizeof(mlnp->move), 1, gameFile) != 1)
	{
	    Message("Write error - save aborted");
	    return;
	}
    }
    fclose(gameFile);
    WhoseMoveMessage("Game saved");
}

/*
 * note: my color has been previously read
 */
void
RestoreGame()
{
    Move move;
    SetupChange setup;
    register int i, j;
    BOOL wasMachine[2];
    FILE * chessOutFile;
    int chessOutSize;

    if (fread((caddr_t) &InitialTurn, sizeof(InitialTurn), 1, RestoreFile) != 1
    || fread((caddr_t) wasMachine, sizeof(wasMachine), 1, RestoreFile) != 1) {
	Message("Read error - restore aborted");
	goto abortRestore;
    }
    if (wasMachine[0] != IsMachine[0] || wasMachine[1] != IsMachine[1]) {
	Message("Restore file type mismatch - restore aborted");
	goto abortRestore;
    }
    Turn = InitialTurn;
    for (i = 0 ; i < 8 ; i++) {
	for (j = 0 ; j < 8 ; j++) {
	    if (fread((caddr_t) &InitialBoard[i][j].type, 
		sizeof(InitialBoard[i][j].type), 
		1, RestoreFile) != 1
	    || fread((caddr_t) &InitialBoard[i][j].color, 
		sizeof(InitialBoard[i][j].color), 
		1, RestoreFile) != 1)
	    {
		Message("Read error - restore truncated");
		goto abortRestore;
	    } else {
		setup.type = MainBoard[i][j].type = InitialBoard[i][j].type;
		setup.color = MainBoard[i][j].color = InitialBoard[i][j].color;
		setup.x = j;
		setup.y = i;
		SendSetupChange(&setup, PeerColor);
	    }
	}
    }
    for (i = 0 ; i < 2 ; i++) {
	if (wasMachine[i]) {
	    if ((chessOutFile = fopen("/tmp/chess.out", "w")) == (FILE *) 0) {
		Message("Can't create /tmp/chess.out - restore aborted");
		goto abortRestore;
	    }
	    if (fread((caddr_t) &chessOutSize, sizeof(chessOutSize), 1, RestoreFile) != 1) {
		Message("Read error - restore aborted");
		fclose(chessOutFile);
		goto abortRestore;
	    }
	    while (chessOutSize--) {
		if ((j = fgetc(RestoreFile)) == EOF) {
		    Message("Read error - restore aborted");
		    fclose(chessOutFile);
		    goto abortRestore;
		} 
		fputc(j, chessOutFile);
	    }
	    fclose(chessOutFile);
	    MachineRestore(i);
	    sleep(4);
	    unlink("/tmp/chess.out");
	}
    }
    while (fread((caddr_t) &move, sizeof(move), 1, RestoreFile)) {
	DoMove(&move, FALSE);
	SendRestoreMove(&move, PeerColor);
	Turn = OTHERCOLOR(Turn);
    }
    /*
     * if this was a machine vs. machine game, resubmit the last move
     * to the program whose turn it was.
     */
    if (wasMachine[BLACK] && wasMachine[WHITE]) {
	SendMachineMove(&lastMove->move, Turn);
    /*
     * else if the peer is not a machine, turn him loose
     */
    } else if ( ! IsMachine[PeerColor]) {
	SendEndRestore();
    }
abortRestore:
    fclose(RestoreFile);
    WhoseMoveMessage((char *) 0);
}

@//E*O*F board.c//
chmod u=r,g=r,o=r board.c
 
echo x - main.c
sed 's/^@//' > "main.c" <<'@//E*O*F main.c//'
/*
 * main client part of network chess.
 */

#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <suntool/tool_hs.h>
#include <pwd.h>

#include "nchess.h"

int errno;				/* global error number */

char * TranscriptFileName = "nchess.transcript";
int TranscriptType = TR_MIN_NORMAL;
char * SaveFileName = "nchess.save";
char * PlayerName[2];
BOOL SetupMode = FALSE;
BOOL IsMachine[2] = { FALSE, FALSE };
FILE * RestoreFile;			/* restoration file */
struct passwd * UserPWEntry;

#define	MAXCHESSPROCARGS	16
char * ChessProcessArgs[2][MAXCHESSPROCARGS];

/*
 * walk away from the game in the middle on receipt of an appropriate
 * signal
 */
bobbyFischer()
{
    /* kill any and all chess processes */
    KillChessProcesses();
    /* send a goodbye to the peer */
    SendGoodbye();
    exit(1);
}

main(argc, argv)
    int argc;
    char ** argv;
{
    register int i, j;
    char * cp = (char *) 0, * iconDirectory = (char *) 0;
    int chessArgIndex[2];
    BOOL useRetained = TRUE;

    if ((UserPWEntry = getpwuid(getuid())) == (struct passwd *) 0) {
	fputs("Can't determine who you are\n", stderr);
	exit(1);
    }
    chessArgIndex[BLACK] = chessArgIndex[WHITE] = 0;
    /* randomize things a bit */
    (void) srandom((long) getpid());
    /* parse and strip out the tool-related arguments */
    ParseToolArgs(&argc, argv);
    /* scan the remaining argument list */
    for (i = 1 ; i < argc ; i++) {
	if (argv[i][0] == '-') {
	    switch (argv[i][1]) {
	    case 'c':
		useRetained = FALSE;	/* use a retained pixrect for the board */
		break;
	    case 'v':
		MachineDebug = TRUE;	/* turn on chess program i/f debugging */
		break;
	    case 'd':			/* custom piece icon directory name */
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)) {
		    fprintf(stderr, "%s: bad piece icon directory name arg\n", argv[0]);
		    exit(1);
		} 
		iconDirectory = &argv[i][j];
		break;
	    case 'b':			/* wants to play the black pieces */
		MyColor = WANTSBLACK;
		break;
	    case 'w':			/* wants to play the white pieces */
		MyColor = WANTSWHITE;
		break;
	    case 'x':			/* specify transcript notation type */
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)
		|| sscanf(&argv[i][j], "%d", &TranscriptType) != 1
		|| TranscriptType < TR_MIN_TYPE
		|| TranscriptType > TR_MAX_TYPE) {
		    fprintf(stderr, "%s: bad transcript mode arg\n", argv[0]);
		    exit(1);
		}
		break;
	    case 't':			/* specify transcript file name */
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)) {
		    fprintf(stderr, "%s: bad transcript filename arg\n", argv[0]);
		    exit(1);
		} 
		TranscriptFileName = &argv[i][j];
		break;
	    case 's':			/* specify save file name */
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)) {
		    fprintf(stderr, "%s: bad save filename arg\n", argv[0]);
		    exit(1);
		} 
		SaveFileName = &argv[i][j];
		break;
	    case 'r':
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)
		|| ((RestoreFile = fopen(&argv[i][j], "r")) == (FILE *) 0)) {
		    fprintf(stderr, "%s: bad restore file arg\n", argv[0]);
		    exit(1);
		}
		if (fread((caddr_t) &MyColor, sizeof(MyColor), 1, RestoreFile) != 1) {
		    fputs("Bad save file\n", stderr);
		    exit(1);
		}
		break;
	    case 'e':
		SetupMode = TRUE;
		Mouse = SETUP;
		break;
	    case 'm':
		IsMachine[WHITE] = TRUE;
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)) {
		    fprintf(stderr, "%s: bad chess program arg\n", argv[0]);
		    exit(1);
		}
		if (chessArgIndex[WHITE] >= MAXCHESSPROCARGS - 1) {
		    fprintf(stderr, "%s: too many chess program args\n", argv[0]);
		    exit(1);
		}
		ChessProcessArgs[WHITE][chessArgIndex[WHITE]++] = &argv[i][j];
		ChessProcessArgs[WHITE][chessArgIndex[WHITE]] = (char *) 0;
		break;
	    case 'n':
		IsMachine[BLACK] = TRUE;
		if (argv[i][j=2] == '\0' && (j = 0 , ++i >= argc)) {
		    fprintf(stderr, "%s: bad chess program arg\n", argv[0]);
		    exit(1);
		}
		if (chessArgIndex[BLACK] >= MAXCHESSPROCARGS - 1) {
		    fprintf(stderr, "%s: too many chess program args\n", argv[0]);
		    exit(1);
		}
		ChessProcessArgs[BLACK][chessArgIndex[BLACK]++] = &argv[i][j];
		ChessProcessArgs[BLACK][chessArgIndex[BLACK]] = (char *) 0;
		break;
	    }
	} else 
	    cp = argv[i];
    }
    if (SetupMode && RestoreFile != (FILE *) 0) {
	fprintf(stderr, "%s: can only specify one of -r and -s\n", argv[0]);
	exit(1);
    }
    /*
     * if white is a machine, start up the white chess process and
     * force us to play the black pieces (if we get to play at all)
     */
    if (IsMachine[WHITE]) {
	InitChessProcess(ChessProcessArgs[WHITE], WHITE, 
	    ! SetupMode && RestoreFile == (FILE *) 0);
	PlayerName[WHITE] = ChessProcessArgs[WHITE][0];
	MyColor = BLACK;
    }
    /*
     * if black is a machine, start up the black chess process and
     * force us to play the white pieces (if we get to play at all)
     */
    if (IsMachine[BLACK]) {
	InitChessProcess(ChessProcessArgs[BLACK], BLACK, FALSE);
	PlayerName[BLACK] = ChessProcessArgs[BLACK][0];
	MyColor = WHITE;
    }
    /*
     * if both players are human, initialize the peer-to-peer RPC 
     * "connection" 
     */
    if ( ! IsMachine[WHITE] && ! IsMachine[BLACK]) {
	InitRPC(cp, argv[0]);
    /*
     * else if both players are machines, lock the mouse to avoid
     * spectator interference (unless the spectator is going to set
     * up the cannon fodder first)
     */
    } else if (IsMachine[WHITE] && IsMachine[BLACK]) {
	if ( ! SetupMode)
	    Mouse = LOCKED;			/* lock out the mouse */
    /*
     * else the user is playing a machine
     */
    } else {
	PlayerName[MyColor] = UserPWEntry->pw_name;
	PeerColor = OTHERCOLOR(MyColor);
    }
    signal(SIGINT, bobbyFischer);
    signal(SIGQUIT, bobbyFischer);
    signal(SIGHUP, bobbyFischer);
    /* initialize the board state */
    InitBoard();
    /* 
     * if we want to restore the game and our opponent hasn't beaten
     * us to it, try to do it now 
     */
    if (RestoreFile != (FILE *) 0 && ! RestoringGame)
	RestoreGame();
    /* initialize and install the tool */
    InitTool(useRetained, iconDirectory);
    tool_install(NchessTool);
    /* now play the game */
    RunTool();
    /* post-game cleanup */
    KillChessProcesses();
    SendGoodbye();
}

@//E*O*F main.c//
chmod u=r,g=r,o=r main.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    1873    8106   55842 board.c
     213     871    6252 main.c
    2086    8977   62094 total
!!!
wc  board.c main.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

toma@tc.fluke.COM (Tom Anderson) (01/09/87)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by spock!toma on Thu Jan  8 14:33:58 PST 1987
# Contents:  ipc.c tool.c controlsw.c
 
echo x - ipc.c
sed 's/^@//' > "ipc.c" <<'@//E*O*F ipc.c//'
/*
 * manage communication with the peer chess tool, if one exists.
 * some communication functions with a background chess process are
 * relayed by functions in this file.
 */

#include <stdio.h>
#include <rpc/rpc.h>
#include <strings.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <pwd.h>
#include <signal.h>

#include "nchess.h"

unsigned long MyProgNum = (u_long) 0;	/* my program number */
unsigned long PeerProgNum;		/* program number of peer */
char * PeerHostName;			/* host name of peer */
char * PeerUserName;			/* user name of peer */
BOOL peerDead = FALSE;			/* peer connection lost or chess program died */

BOOL UndoWanted = FALSE;		/* undo was requested locally */
BOOL RestoringGame = FALSE;		/* restoring a saved game via the peer */
SVCXPRT * Xprt;				/* RPC service transport handle */

/* 
 * get a transient program number 
 */
unsigned long
getTransient(vers, sockp)
    unsigned long vers; 
    int * sockp;
{
    unsigned long progNum = 0x52000000L;
    int s, len;
    struct sockaddr_in addr;

    /* create a socket */
    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	fprintf(stderr, "can't create a socket\n");
	exit(1);
    }
    * sockp = s;
    addr.sin_addr.s_addr = 0;
    addr.sin_family = AF_INET;
    addr.sin_port = 0;
    len = sizeof(addr);
    bind(s, (struct sockaddr *) &addr, len);
    if (getsockname(s, (caddr_t) &addr, &len) < 0) {
	fprintf(stderr, "getsockname failed\n");
	exit(1);
    }
    /* now find and reserve an available transient program number */
    while ( ! pmap_set(progNum++, vers, IPPROTO_UDP, addr.sin_port))
	continue;
    return(progNum - 1);
}

/*
 * timed out the peer
 */
void 
peerDied()
{
    Message("Lost connection to your opponent");
    peerDead = GameOver = TRUE;
    Mouse = LOCKED;
}

/*
 * RPC procedure dispatch routine
 */
void
dispatch(request, xprt)
    struct svc_req * request;
    SVCXPRT * xprt;
{
    Move move;
    SetupChange setup;
    GameRequest gr;
    char ans[80];
    int isUndoOK;

    switch (request->rq_proc) {
    /* 
     * are you there? 
     */
    case NULLPROC:
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    fprintf(stderr, "can't reply to remote peer\n");
	    exit(1);
	}
	break;
    /*  
     * game invitation accepted 
     */
    case ACCEPTPROCNUM:
	if ( ! svc_getargs(xprt, XdrGameReq, &gr)) {
	    svcerr_decode(xprt);
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    fprintf(stderr, "can't reply to remote peer\n");
	    exit(1);
	}
	PeerProgNum = gr.progNum;
	PeerColor = gr.color;
	if (MyColor + PeerColor != WANTSWHITE + WANTSBLACK) {
	    switch(MyColor) {
	    /* I don't care - pick a color based on what the peer wants */
	    case EITHERCOLOR:
		switch(PeerColor) {
		case WANTSBLACK:
		    MyColor = WANTSWHITE;
		    break;
		case WANTSWHITE:
		    MyColor = WANTSBLACK;
		    break;
		default:
		    fprintf(stderr, "color arbitration botch\n");
		    exit(1);
		}
		break;
	    case WANTSBLACK:
		if (PeerColor == WANTSBLACK) {
		    printf("Your opponent insists on playing black - is that OK? ");
		    scanf("%s", ans);
		    if (ans[0] == 'y' || ans[0] == 'Y') 
			MyColor = WANTSWHITE;
		    else {
			fprintf(stderr, "couldn't agree on colors - bye\n");
			(void) callrpc(PeerHostName, PeerProgNum, VERSNUM, COLORFAILPROCNUM,
			    xdr_void, (caddr_t) 0,
			    xdr_void, (caddr_t) 0);
			exit(1);
		    }
		}
		break;
	    case WANTSWHITE:
		if (PeerColor == WANTSWHITE) {
		    printf("Your opponent insists on playing white - is that OK? ");
		    scanf("%s", ans);
		    if (ans[0] == 'y' || ans[0] == 'Y') 
			MyColor = WANTSBLACK;
		    else {
			fprintf(stderr, "couldn't agree on colors - bye\n");
			(void) callrpc(PeerHostName, PeerProgNum, VERSNUM, COLORFAILPROCNUM,
			    xdr_void, (caddr_t) 0,
			    xdr_void, (caddr_t) 0);
			exit(1);
		    }
		}
		break;
	    }
	}
	break;
    /* 
     * the other player didn't ameliorate our insistence on color 
     */
    case COLORFAILPROCNUM:
	fprintf(stderr, "couldn't agree on colors - bye\n");
	(void) svc_sendreply(xprt, xdr_void, (caddr_t) 0);
	exit(1);
    /* 
     * receive a move from the other player 
     */
    case MOVEPROCNUM:
	if ( ! svc_getargs(xprt, XdrMove, &move)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    DoMove(&move, TRUE); 
	    Turn = MyColor;
	    Flashing = TRUE;
	    if (Mouse != CONFIRMING)
		WhoseMoveMessage((char *) 0);
	}
	break;
    /* 
     * receive a setup change from the other player 
     */
    case SETUPPROCNUM:
	if ( ! svc_getargs(xprt, XdrSetup, &setup)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    DoSetupChange(&setup);
	}
	break;
    /* 
     * receive a restoration move from the other player's process
     */
    case RESTOREMOVEPROCNUM:
	if ( ! svc_getargs(xprt, XdrMove, &move)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    DoMove(&move, TRUE); 
	    Turn = OTHERCOLOR(Turn);
	}
	break;
    /*
     * restoration complete 
     */
    case ENDRESTOREPROCNUM:
	if ( ! svc_getargs(xprt, xdr_int, &Turn)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    WhoseMoveMessage((char *) 0);
	    RestoringGame = FALSE;
	    Mouse = IDLE;	/* unlock the mouse */
	    Flashing = (Turn == MyColor);
	}
	break;
    /* 
     * other player wants to undo his previous move 
     */
    case UNDOPROCNUM:
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    RequestUndo();
	    Flashing = TRUE;
	}
	break;
    /*
     * the other player blew away his tool.
     */
    case GOODBYEPROCNUM:
	svc_sendreply(xprt, xdr_void, (caddr_t) 0);
	KillMouseActivity();
	peerDead = GameOver = TRUE;
	Mouse = LOCKED;
	Message("Your opponent killed his tool process");
	break;
    /*
     * other player is acknowledging our request to undo.
     */
    case UNDOACKPROCNUM:
	if ( ! svc_getargs(xprt, xdr_int, &isUndoOK)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    if (isUndoOK) {
		UnDoMove();
		if (Turn == MyColor)
		    UnDoMove();
		else
		    Turn = MyColor;
		WhoseMoveMessage("Undo accepted");
	    } else {
		WhoseMoveMessage("Undo refused");
	    }
	    UndoWanted = FALSE;
	    Mouse = IDLE;
	    Flashing = TRUE;
	}
	break;
    /*
     * other player resigned 
     */
    case RESIGNPROCNUM:
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0))
	    peerDead = TRUE;
	Message(PeerColor == BLACK ? "Black resigns" : "White resigns");
	KillMouseActivity();
	Mouse = LOCKED;		/* game is over */
	DoResignation(OTHERCOLOR(MyColor));
	Flashing = TRUE;
	break;
    case MSGPROCNUM:
	if ( ! svc_getargs(xprt, XdrString, ans)) {
	    fprintf(stderr, "can't get RPC args\n");
	    exit(1);
	}
	if ( ! svc_sendreply(xprt, xdr_void, (caddr_t) 0)) {
	    peerDied();
	} else {
	    RecvTalkMsg(ans);
	    Flashing = TRUE;
	}
	break;
    }
}


/*
 * outstanding invitation - shared by InitRPC() and quitEarly().
 */
GameRequest gr;

/*
 * cancel our invitation with the remote daemon if we receive a signal
 * before the invitee responds.
 */
quitEarly()
{
    callrpc(PeerHostName, SERVERPROGNUM, VERSNUM, CANCELPROCNUM, 
	XdrGameReq, (caddr_t) &gr, 
	xdr_void, (caddr_t) 0);
    exit(1);
}

/*
 * initialize the RPC connection, using the user's "user@host" arg.
 */
void
InitRPC(cp, progName)
    char * cp, * progName;
{
    GameRequest gr2;
    int sock, rfds; 
    char localHostName[256], ans[80];
    BOOL resumeGame = FALSE;

    /*
     * decipher the remote host and user strings 
     */
    if (cp == (char *) 0 || (PeerHostName = index(cp, '@')) == (char *) 0) {
	fprintf(stderr, "%s: illegal/non-existent user@host arg\n", progName);
	exit(1);
    }
    *PeerHostName++ = '\0';
    PeerUserName = cp;
    /* 
     * determine the local host and user names 
     */
    if (gethostname(localHostName, sizeof(localHostName)) != 0) {
	fprintf(stderr, "%s: can't determine local host name\n", progName);
	exit(1);
    }
    /*
     * generate the transient RPC program number used by this player.
     */
    MyProgNum = getTransient(VERSNUM, &sock);
    if ((Xprt = svcudp_create(sock)) == (SVCXPRT *) 0) {
	fprintf(stderr, "svcudp_create failed\n");
	exit(1);
    }
    /*
     * register the peer-to-peer RPC dispatch procedure
     */
    (void) svc_register(Xprt, MyProgNum, VERSNUM, dispatch, 0L);
    /*
     * check for an invitation registered with the local chess
     * daemon, indicating that we are responding to an invitation.
     */
    (void) strcpy(gr.userName, PeerUserName);
    (void) strcpy(gr.hostName, PeerHostName);
    if (callrpc(localHostName, SERVERPROGNUM, VERSNUM, ACKPROCNUM,
	XdrGameReq, (caddr_t) &gr, 
	XdrGameReq, (caddr_t) &gr2) != 0) 
    {
	fprintf(stderr, "error: callrpc of local daemon (%s)\n", localHostName);
	exit(1);
    }
    PeerProgNum = gr2.progNum;
    PeerColor = gr2.color;
    resumeGame = gr2.resumeGame;
    do {
	/* 
	 * if no invitation was registered or the remote program 
	 * is no longer running,
	 * 	 then register an invitation with the remote daemon.
	 */
	if (PeerProgNum == 0L
	|| callrpc(PeerHostName, PeerProgNum, VERSNUM, NULLPROC, 
	    xdr_void, (caddr_t) 0, 
	    xdr_void, (caddr_t) 0) != 0) 
	{
	    gr.progNum = MyProgNum;
	    gr.color = MyColor;
	    gr.resumeGame = (RestoreFile != (FILE *) 0 || SetupMode);
	    (void) strcpy(gr.userName, UserPWEntry->pw_name);
	    (void) strcpy(gr.hostName, localHostName);
	    signal(SIGINT, quitEarly);
	    signal(SIGQUIT, quitEarly);
	    signal(SIGHUP, quitEarly);
	    if (callrpc(PeerHostName, SERVERPROGNUM, VERSNUM, REQPROCNUM, 
		XdrGameReq, (caddr_t) &gr, 
		xdr_void, (caddr_t) 0) != 0) 
	    {
		fprintf(stderr, "error: callrpc of remote daemon\n");
		exit(1);
	    }
	    printf("waiting for %s to respond...\n", PeerUserName);
	    /* 
	     * wait for the remote peer to send us his program number and color 
	     */
	    do {
		rfds = svc_fds;
		switch(select(32, &rfds, (int *) 0, (int *) 0, (struct timeval *) 0)) {
		case -1:
		    if (errno == EINTR)
			continue;
		    fprintf(stderr, "RPC select botch\n");
		    exit(1);
		case 0:
		    break;
		default:
		    svc_getreq(rfds);
		    break;
		}
	    } while(PeerProgNum == 0L);
	    signal(SIGINT, SIG_DFL);
	    signal(SIGQUIT, SIG_DFL);
	    signal(SIGHUP, SIG_DFL);
	}
	/*
	 * else accept the invitation, sending the remote program 
	 * our program number and color.
	 */
	else {
	    if (resumeGame) {
		MyColor = EITHERCOLOR;
		RestoringGame = TRUE;
		SetupMode = FALSE;
	    }
	    /* try to pre-arrange colors */
	    if (MyColor + PeerColor != WANTSWHITE + WANTSBLACK) {
		switch(MyColor) {
		/* I don't care - pick a color based on what the peer wants */
		case EITHERCOLOR:
		    switch(PeerColor) {
		    case EITHERCOLOR:
			MyColor = (random() & 0x01) ? WANTSBLACK : WANTSWHITE;
			break;
		    case WANTSBLACK:
			MyColor = WANTSWHITE;
			break;
		    case WANTSWHITE:
			MyColor = WANTSBLACK;
			break;
		    }
		    break;
		case WANTSBLACK:
		    if (PeerColor == WANTSBLACK) {
			printf("%s also wants to play black - is that OK? ", PeerUserName);
			scanf("%s", ans);
			if (ans[0] == 'y' || ans[0] == 'Y') 
			    MyColor = WANTSWHITE;
		    }
		    break;
		case WANTSWHITE:
		    if (PeerColor == WANTSWHITE) {
			printf("%s also wants to play white - is that OK? ", PeerUserName);
			scanf("%s", ans);
			if (ans[0] == 'y' || ans[0] == 'Y') 
			    MyColor = WANTSBLACK;
		    }
		    break;
		}
	    }
	    gr.progNum = MyProgNum;
	    gr.color = MyColor;
	    if (callrpc(PeerHostName, PeerProgNum, VERSNUM, ACCEPTPROCNUM, 
		XdrGameReq, (caddr_t) &gr, 
		xdr_void, (caddr_t) 0) != 0)
	    {
		PeerProgNum = 0L;
	    }
	}
    } while(PeerProgNum == 0L);
    PeerColor = OTHERCOLOR(MyColor);
    PlayerName[MyColor] = UserPWEntry->pw_name;
    PlayerName[PeerColor] = PeerUserName;
    /*
     * if the other player is shipping us the game state, lock the
     * mouse in the meantime.
     */
    if (RestoringGame)
	Mouse = LOCKED;
}

/*
 * send a move to the 'color' player
 */
BOOL
SendMove(move, color)
    Move * move;
    int color;
{
    if ( ! peerDead) {
	if (IsMachine[color]) {
	    SendMachineMove(move, color);
	} else if (callrpc(PeerHostName, PeerProgNum, VERSNUM, MOVEPROCNUM,
	    XdrMove, (caddr_t) move, 
	    xdr_void, (caddr_t) 0) != 0) 
	{
	    peerDied();
	}
    }
    return( ! peerDead);
}

void
SendRestoreMove(move, color)
    Move * move;
    int color;
{
    if ( ! IsMachine[color] 
    && ! peerDead
    && callrpc(PeerHostName, PeerProgNum, VERSNUM, RESTOREMOVEPROCNUM,
	XdrMove, (caddr_t) move, 
	xdr_void, (caddr_t) 0) != 0)
    {
	peerDied();
    }
}

void
SendEndRestore()
{
    if ( ! peerDead
    && callrpc(PeerHostName, PeerProgNum, VERSNUM, ENDRESTOREPROCNUM,
	xdr_int, (caddr_t) &Turn, 
	xdr_void, (caddr_t) 0) != 0)
    {
	peerDied();
    }
}

void
SendUndoRequest(color)
    int color;
{
    if ( ! peerDead) {
	if (IsMachine[color]) {
	    if (Turn != MyColor) {
		Message("Will undo after machine moves...");
	    } else {
		MachineUndo(color);
		UndoWanted = FALSE;
		Mouse = IDLE;
	    }
	} else {
	    Message("Sending undo request to your opponent (please wait)...");
	    if (callrpc(PeerHostName, PeerProgNum, VERSNUM, UNDOPROCNUM,
		xdr_void, (caddr_t) 0, 
		xdr_void, (caddr_t) 0) != 0)
	    {
		peerDied();
	    }
	}
    }
}

void
SendResignation(color)
    int color;
{
    if ( ! peerDead) {
	if (IsMachine[color]) {
	    KillChessProcesses();
	} else if (callrpc(PeerHostName, PeerProgNum, VERSNUM, RESIGNPROCNUM,
	    xdr_void, (caddr_t) 0, 
	    xdr_void, (caddr_t) 0) != 0)
	{
	    peerDied();
	}
    }
}

void
SendTalkMsg(cp)
    char * cp;
{
    if ( ! peerDead
    && callrpc(PeerHostName, PeerProgNum, VERSNUM, MSGPROCNUM,
	XdrString, (caddr_t) cp, 
	xdr_void, (caddr_t) 0) != 0)
    {
	peerDied();
    }
}

void
SendSetupChange(setup, color)
    SetupChange * setup;
    int color;
{
    if ( ! IsMachine[color] 
    && ! peerDead
    && callrpc(PeerHostName, PeerProgNum, VERSNUM, SETUPPROCNUM,
	XdrSetup, (caddr_t) setup, 
	xdr_void, (caddr_t) 0) != 0)
    {
	peerDied();
    }
}

void
SendUndoAcknowledgement(isUndoOK)
    int isUndoOK;
{
    if (callrpc(PeerHostName, PeerProgNum, VERSNUM, UNDOACKPROCNUM,
	xdr_int, (caddr_t) &isUndoOK, 
	xdr_void, (caddr_t) 0) != 0)
    {
	peerDied();
    } else if (isUndoOK) {
	UnDoMove();
	if (Turn == MyColor) 
	    Turn = OTHERCOLOR(MyColor);
	else 
	    UnDoMove();
	Message(MyColor == WHITE ?
	    "Waiting for black to re-play..." :
	    "Waiting for white to re-play...");
    } else 
	WhoseMoveMessage((char *) 0);
}

void
SendGoodbye()
{
    if ( ! IsMachine[PeerColor] && ! peerDead) {
	callrpc(PeerHostName, PeerProgNum, VERSNUM, GOODBYEPROCNUM,
	    xdr_void, (caddr_t) 0,
	    xdr_void, (caddr_t) 0);
    }
    if (MyProgNum != 0) {
	/* unregister the RPC transport handle */
	xprt_unregister(Xprt);
	/* release my program number for re-use */
	pmap_unset(MyProgNum, VERSNUM);
    }
}
@//E*O*F ipc.c//
chmod u=r,g=r,o=r ipc.c
 
echo x - tool.c
sed 's/^@//' > "tool.c" <<'@//E*O*F tool.c//'
/*
 * handle the tool environment 
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <suntool/gfxsw.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <strings.h>

#include "nchess.h"

Tool * NchessTool;
char ** ToolAttrs = (char **) NULL;
char * ToolName;

/*
 * iconic form of window
 */
unsigned short IconImage[] = {
#include "Icons/nchess.icon"
};

DEFINE_ICON_FROM_IMAGE(WindowIcon, IconImage);

sigwinched()
{
    tool_sigwinch(NchessTool);
}

/*
 * parse the tool args.
 */
void 
ParseToolArgs(argc, argv)
    int *argc;
    char **argv;
{
    ToolName = argv[0];
    (*argc)--;
    argv++;
    if (tool_parse_all(argc, argv, &ToolAttrs, ToolName) == -1) {
	tool_usage(ToolName);
	exit(1);
    }
    (*argc)++;
}

/*
 * set up the tool environment
 */
void
InitTool(useRetained, iconDirectory)
    BOOL useRetained;		/* use a retained pixrect for the board */
    char * iconDirectory;	/* custom piece icon directory */
{
    register unsigned int height;
    register Toolsw * twsp;
    char c[256];

    /* create the tool marquee */
    strcpy(c, " ");
    strcat(c, PlayerName[WHITE]);
    strcat(c, " vs. ");
    strcat(c, PlayerName[BLACK]);
    if ((NchessTool = tool_make( 
	WIN_LABEL, c, 
	WIN_ICON, &WindowIcon, 
	WIN_ATTR_LIST, ToolAttrs,
	WIN_WIDTH, 8 * (SQUARE_WIDTH-1) + 2 * tool_borderwidth(NchessTool) - 1,
	/*
	 * NOTE: The following line was unnecessary in Sun release 2.2,
	 * but is necessary in release 3.0.  For some unknown reason, the
	 * call to tool_set_attributes following the call to InitBoardSW
	 * now fails to uncover the board area that was obscured by the 
	 * default tool height being too small (note that the tool has not 
	 * been installed yet).
	 */
	WIN_HEIGHT, 2000,
	0)) == (Tool *) NULL) 
    {
	fputs("Can't make tool\n", stderr);
	exit(1);
    }
    tool_free_attribute_list(ToolAttrs);
    /* initialize the subwindows */
    InitTalkSW();
    InitMsgSW();
    InitControlSW();
    InitBoardSW(useRetained, iconDirectory);
    /* 
     * add up subwindow heights and force the tool to the resulting size 
     */
    height = tool_stripeheight(NchessTool) + tool_borderwidth(NchessTool) - tool_subwindowspacing(NchessTool);
    for (twsp = NchessTool->tl_sw ; twsp != (Toolsw *) 0 ; twsp = twsp->ts_next) 
	height += twsp->ts_height + tool_subwindowspacing(NchessTool);
    /*
     * NOTE: under 2.2, the above calculation yielded the correct height.
     * under 3.0, we need to add a few pixels to make it come out right (the
     * reason is not yet known).
     */
    height += 2;
    tool_set_attributes(NchessTool, WIN_HEIGHT, height, 0); 
    signal(SIGWINCH, sigwinched);
}

void
RunTool()
{
    /*
     * NOTE: this is another difference between release 2.2 and 3.0:
     * in release 2.2, the SIGWINCH handler would get called once at the
     * outset to draw the board area; in release 3.0, this doesn't happen.
     */
    DrawBoard();
    tool_select(NchessTool, 0);
    tool_destroy(NchessTool);
}

@//E*O*F tool.c//
chmod u=r,g=r,o=r tool.c
 
echo x - controlsw.c
sed 's/^@//' > "controlsw.c" <<'@//E*O*F controlsw.c//'
/*
 * control panel subwindow handling
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <suntool/menu.h>

#include "nchess.h"

BOOL SaveWanted = FALSE;		/* machine opponent and game save deferred */

struct toolsw * ControlPanelSW;
Panel ControlPanel;

/* button items */
Panel_item UndoButton;
Panel_item LastPlayButton;
Panel_item ResignButton;
Panel_item TranscriptButton;
Panel_item SaveButton;

/*
 * undo move button event handler
 */
/*ARGSUSED*/
undoProc(item, event)
    Panel_item item;
    struct inputevent * event;
{
    if ( ! IHaveMoved()) {
	Message("You haven't moved yet!");
    } else if (Mouse == IDLE) {
	UndoWanted = TRUE;
	Mouse = LOCKED;		/* lock the mouse until the reply arrives */
	SendUndoRequest(PeerColor);
    }
}

/*
 * last play button event handler 
 */
/*ARGSUSED*/
lastPlayProc(item, event)
    Panel_item item;
    struct inputevent * event;
{
    ShowLastPlay();
}

/*
 * resign button event handler 
 */
/*ARGSUSED*/
resignProc(item, event)
    Panel_item item;
    struct inputevent * event;
{
    /* make sure this is what the user wants to do */
    if (Mouse == IDLE) {
	ConfirmResignation();
    }
}

/*
 * write a transcript 
 */
/*ARGSUSED*/
transcriptProc(item, event)
    Panel_item item;
    struct inputevent * event;
{
    WriteTranscript(TranscriptFileName, TranscriptType);
}

/*
 * save the game
 */
/*ARGSUSED*/
saveProc(item, event)
    Panel_item item;
    struct inputevent * event;
{
    /*
     * caveats: can't save games while we're still setting them up,
     * and cannot save a game involving machines after the game is over.
     */
    if (SetupMode 
    || GameOver && (IsMachine[WHITE] || IsMachine[BLACK]))
	return;
    if (IsMachine[WHITE] && IsMachine[BLACK]
    || IsMachine[PeerColor] && Turn != MyColor) {
	Message("Will save game when machine finishes move...");
	SaveWanted = TRUE;
    } else {
	SaveGame(SaveFileName);
    }
}

/*
 * set up the control subwindow
 */
void
InitControlSW()
{
    if ((ControlPanelSW = panel_create(NchessTool, 0)) == NULL) {
	fprintf(stderr, "Can't create control panel\n");
	exit(1);
    }
    ControlPanel = ControlPanelSW->ts_data;
    /* set up the buttons */
    if ( ! IsMachine[WHITE] || ! IsMachine[BLACK]) {
	ResignButton = panel_create_item(ControlPanel, PANEL_BUTTON,
	    PANEL_LABEL_STRING, "(Resign)",
	    PANEL_NOTIFY_PROC, resignProc,
	    0);
	UndoButton = panel_create_item(ControlPanel, PANEL_BUTTON,
	    PANEL_LABEL_STRING, "(Undo)",
	    PANEL_NOTIFY_PROC, undoProc,
	    0);
    }
    LastPlayButton = panel_create_item(ControlPanel, PANEL_BUTTON,
	PANEL_LABEL_STRING, "(Last Play)",
	PANEL_NOTIFY_PROC, lastPlayProc,
	0);
    TranscriptButton = panel_create_item(ControlPanel, PANEL_BUTTON,
	PANEL_LABEL_STRING, "(Transcript)",
	PANEL_NOTIFY_PROC, transcriptProc,
	0);
    SaveButton = panel_create_item(ControlPanel, PANEL_BUTTON,
	PANEL_LABEL_STRING, "(Save)",
	PANEL_NOTIFY_PROC, saveProc,
	0);
    panel_fit_height(ControlPanel);
}
@//E*O*F controlsw.c//
chmod u=r,g=r,o=r controlsw.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     646    1975   15392 ipc.c
     121     392    3019 tool.c
     136     347    3010 controlsw.c
     903    2714   21421 total
!!!
wc  ipc.c tool.c controlsw.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

toma@tc.fluke.COM (Tom Anderson) (01/09/87)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by spock!toma on Thu Jan  8 14:34:19 PST 1987
# Contents:  boardsw.c msgsw.c talksw.c chessprocess.c
 
echo x - boardsw.c
sed 's/^@//' > "boardsw.c" <<'@//E*O*F boardsw.c//'
/*
 * handle the board subwindow (as well as fielding RPC and chess game
 * process selects)
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <suntool/gfxsw.h>
#include <suntool/icon_load.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <strings.h>

#include "nchess.h"

#define	MOVE_PR_WIDTH	(SQUARE_WIDTH * 2)
#define	MOVE_PR_HEIGHT	(SQUARE_HEIGHT * 2)
#define	MOVE_X_OFFSET	(MOVE_PR_WIDTH/2 - SQUARE_WIDTH/2)
#define	MOVE_Y_OFFSET	(MOVE_PR_HEIGHT/2 - SQUARE_HEIGHT/2)

extern int svc_fds;			/* RPC service file descriptor(s) */
int BoardSWMask;			/* board gfx sw file des. */
BOOL Flashing = FALSE;			/* tool icon is flashing */

enum {					/* confirmation state using mouse */
    CONFIRM_WANTED,
    CONFIRM_SETUP_END,
    CONFIRM_WHOSE_TURN,
    CONFIRM_UNDO,
    CONFIRM_RESIGNATION,
} confirmState;

MouseState Mouse = IDLE;

char * PieceIconFileNames[] = {
    "pawn.icon",
    "knight.icon",
    "bishop.icon",
    "rook.icon",
    "queen.icon",
    "king.icon",
};

char * PieceStencilFileNames[] = {
    "pawnStencil.icon",
    "knightStencil.icon",
    "bishopStencil.icon",
    "rookStencil.icon",
    "queenStencil.icon",
    "kingStencil.icon",
};

/*
 * piece icons and stencils
 */
unsigned short RookBlackImage[] = {
#include "Icons/rook.icon"
};
unsigned short RookWhiteImage[] = {
#include "Icons/rook.icon"
};
unsigned short RookStencilImage[] = {
#include "Icons/rookStencil.icon"
};
mpr_static(RookBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookBlackImage);
mpr_static(RookWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookWhiteImage);
mpr_static(RookStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookStencilImage);

unsigned short KnightBlackImage[] = {
#include "Icons/knight.icon"
};
unsigned short KnightWhiteImage[] = {
#include "Icons/knight.icon"
};
unsigned short KnightStencilImage[] = {
#include "Icons/knightStencil.icon"
};
mpr_static(KnightBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightBlackImage);
mpr_static(KnightWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightWhiteImage);
mpr_static(KnightStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightStencilImage);

unsigned short BishopBlackImage[] = {
#include "Icons/bishop.icon"
};
unsigned short BishopWhiteImage[] = {
#include "Icons/bishop.icon"
};
unsigned short BishopStencilImage[] = {
#include "Icons/bishopStencil.icon"
};
mpr_static(BishopBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopBlackImage);
mpr_static(BishopWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopWhiteImage);
mpr_static(BishopStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopStencilImage);

unsigned short KingBlackImage[] = {
#include "Icons/king.icon"
};
unsigned short KingWhiteImage[] = {
#include "Icons/king.icon"
};
unsigned short KingStencilImage[] = {
#include "Icons/kingStencil.icon"
};
mpr_static(KingBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingBlackImage);
mpr_static(KingWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingWhiteImage);
mpr_static(KingStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingStencilImage);

unsigned short QueenBlackImage[] = {
#include "Icons/queen.icon"
};
unsigned short QueenWhiteImage[] = {
#include "Icons/queen.icon"
};
unsigned short QueenStencilImage[] = {
#include "Icons/queenStencil.icon"
};
mpr_static(QueenBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenBlackImage);
mpr_static(QueenWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenWhiteImage);
mpr_static(QueenStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenStencilImage);

unsigned short PawnBlackImage[] = {
#include "Icons/pawn.icon"
};
unsigned short PawnWhiteImage[] = {
#include "Icons/pawn.icon"
};
unsigned short PawnStencilImage[] = {
#include "Icons/pawnStencil.icon"
};
mpr_static(PawnBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnBlackImage);
mpr_static(PawnWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnWhiteImage);
mpr_static(PawnStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnStencilImage);

struct pixrect * PieceStencils[6] = {
    &PawnStencilPR,
    &KnightStencilPR,
    &BishopStencilPR,
    &RookStencilPR,
    &QueenStencilPR,
    &KingStencilPR,
};

struct pixrect * PieceIcons[6][2] = {
    &PawnBlackPR,	&PawnWhitePR,
    &KnightBlackPR,	&KnightWhitePR,
    &BishopBlackPR,	&BishopWhitePR,
    &RookBlackPR,	&RookWhitePR,
    &QueenBlackPR,	&QueenWhitePR,
    &KingBlackPR,	&KingWhitePR,
};

/*
 * blank square pixrects
 */
unsigned short WhiteSquareImage[] = {
#include "Icons/whiteSquare.icon"
};
mpr_static(WhiteSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, WhiteSquareImage);

unsigned short BlackSquareImage[] = {
#include "Icons/blackSquare.icon"
};
mpr_static(BlackSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BlackSquareImage);

/* board subwindow handles */
struct toolsw * BoardSW;
struct gfxsubwindow * Board;

/* pixrects used for piece animation */
struct pixrect * MoveFromPR, * MoveToPR;

/* pixrect used for victim drawing */
struct pixrect * VictimPR;

/*
 * board sigwinch handler 
 */
/*ARGSUSED*/
boardSigwinch(sw)
    caddr_t sw;
{
    gfxsw_interpretesigwinch(Board);
    gfxsw_handlesigwinch(Board);
    if (Board->gfx_flags & GFX_RESTART) {
	Board->gfx_flags &= ~ GFX_RESTART;
	DrawBoard();
    }
}

/*
 * map a mouse coordinate to a board coordinate
 */
void
mapMouseToBoard(mlocp, blocp)
    struct pr_pos * mlocp;
    BoardCoordinate * blocp;
{
    if (MyColor == WHITE) {
	blocp->x = mlocp->x / (SQUARE_WIDTH-1);
	blocp->y = mlocp->y / (SQUARE_HEIGHT-1);
    } else {
	blocp->x = (8 * (SQUARE_WIDTH-1) - mlocp->x)/(SQUARE_WIDTH-1);
	blocp->y = (8 * (SQUARE_HEIGHT-1) - mlocp->y)/(SQUARE_HEIGHT-1);
    }
}

/* 
 * map a board coordinate to a mouse coordinate
 */
void
mapBoardToMouse(blocp, mlocp)
    BoardCoordinate * blocp;
    struct pr_pos * mlocp;
{
    if (MyColor == WHITE) {
	mlocp->x = blocp->x * (SQUARE_WIDTH-1) - 1;
	mlocp->y = blocp->y * (SQUARE_HEIGHT-1) - 1;
    } else {
	mlocp->x = (7 - blocp->x) * (SQUARE_WIDTH-1) - 1;
	mlocp->y = (7 - blocp->y) * (SQUARE_WIDTH-1) - 1;
    }
}

/*
 * put a piece back where we got it (used to abort piece animation for 
 * various reasons)
 */
void
putPieceBack(from, stencil, icon)
    BoardCoordinate * from;
    struct pixrect * stencil, * icon;
{
    struct pr_pos loc;

    mapBoardToMouse(from, &loc);
    pw_stencil(Board->gfx_pixwin, 
	loc.x, loc.y,
	SQUARE_WIDTH, SQUARE_HEIGHT,
	PIX_SRC, stencil, 0, 0, icon, 0, 0);
}

/*
 * parameters which belong in boardSelected(), but which are shared
 * with RequestUndo() so that we can abort piece animation if the 
 * klutz at the other end panics (via an undo request or a resignation).  
 * all of them need to be static, anyway.
 */

BoardCoordinate from, to;
struct pixrect * pieceIcon, * pieceStencil;
struct pr_pos lastMouseLoc;

/*
 * abort any mouse activity - this really only means aborting piece 
 * animation.
 */
void
KillMouseActivity()
{
    switch (Mouse) {
    case PROMOTING_PAWN:
	UnDoMove();
	break;
    case MOVING_PIECE:
	/* repaint the background */
	pw_rop(Board->gfx_pixwin,
	    lastMouseLoc.x - MOVE_X_OFFSET,
	    lastMouseLoc.y - MOVE_Y_OFFSET,
	    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
	    PIX_SRC, MoveFromPR, 0, 0);
	putPieceBack(&from, pieceStencil, pieceIcon);
	break;
    }
}

/*
 * relay a request for an undo by the opponent.
 *
 * this has the side-effect of aborting any mouse activity that the 
 * user had going at the time.  however, setups will not be interrupted 
 * by undo requests since the (spurious) undo request will be prevented 
 * when the opponent's program detects that he hasn't moved yet.
 */
void
RequestUndo()
{
    Message("Your opponent wants to undo - left OK, other not OK");
    KillMouseActivity();
    Mouse = CONFIRMING;
    confirmState = CONFIRM_UNDO;
}

/*
 * confirm a wish to resign
 */
void
ConfirmResignation()
{

    Message("Sure you want to resign? - left yes, other no");
    Mouse = CONFIRMING;
    confirmState = CONFIRM_RESIGNATION;
}

/*
 * board select() handler 
 */
/*ARGSUSED*/
boardSelected(nullsw, ibits, obits, ebits, timer)
    caddr_t * nullsw;
    int * ibits, * obits, * ebits;
    struct timeval ** timer;
{
    static tickCount = 0;
    static Move move;
    static SetupChange setup;
    struct inputevent ie;
    struct pr_pos newMouseLoc;
    Square * sqp;
    long nbytes;
    BOOL clamped;
    int color, i;
    struct pixrect * pr;

    /*
     * 1-second ticker.  this cannot use SIGALRM, since the RPC
     * package already has dibs on that signal for implementing timeouts.
     * hence, it uses the subwindow select timeout mechanism.
     * the ticker is used to flash the window every 5 seconds if 
     * there is some event the user hasn't seen yet (opening the 
     * tool window turns the flasher off).
     *
     * note - timeouts render the file descriptor masks undefined, 
     * so we clean up and return w/o checking them.
     */
    if ((*timer)->tv_sec == 0L && (*timer)->tv_usec == 0L) {
	if (Flashing) {
	    if (wmgr_iswindowopen(NchessTool->tl_windowfd)) {
		tickCount = 0;
		Flashing = FALSE;
	    } else if (tickCount-- <= 0
	    && (pr = (struct pixrect *) tool_get_attribute(NchessTool, 
		WIN_ICON_IMAGE)) != (struct pixrect *) -1)
	    {
		tickCount = 5;
		pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, 
		    PIX_NOT(PIX_SRC), pr, 0, 0);
		tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0);
		pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, 
		    PIX_NOT(PIX_SRC), pr, 0, 0);
		tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0);
		tool_free_attribute(WIN_ICON_IMAGE, pr);
	    }
	}
	/* reset the timer and the file des. masks */
	(*timer)->tv_sec = 1L;
	(*timer)->tv_usec = 0L;
	* ibits = svc_fds | BoardSWMask 
	    | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE];
	* obits = * ebits = 0;
	return;
    }
    /* 
     * check for RPC service required 
     */
    if (*ibits & svc_fds) {
	svc_getreq(*ibits & svc_fds);
    } 
    /*
     * check for machine move received
     *
     * note: it is possible in some circumstances to receive a move
     * from a machine player after the game is over (for example, 
     * the opponent of the machine which is on move dies).
     * in that event, we simply throw the move away.
     */
    for (color = BLACK , i = 0  ; i < 2 ; color = WHITE , i++) {
	if ((*ibits & ChessProcessFDs[color]) 
	&& GetMachineMove(&move, color)
	&& ! GameOver) {
	    BOOL updateMsgWindow = TRUE;

	    Flashing = TRUE;
	    DoMove(&move, TRUE);
	    Turn = OTHERCOLOR(Turn);
	    /* 
	     * if we are trying to save a game of machine vs. machine,
	     * hold up sending the move until both machine states 
	     * have been saved.  when subsequently restoring the 
	     * game, this move will be resubmitted.
	     */
	    if (SaveWanted) {
		SaveGame(SaveFileName);
		SaveWanted = FALSE;
		updateMsgWindow = FALSE;
	    }
	    /* 
	     * if the player not moving is a machine, send the move 
	     */
	    if (IsMachine[OTHERCOLOR(color)]) {
		SendMove(&move, OTHERCOLOR(color));
	    /*
	     * else if the player not moving is a human that wants an
	     * undo, back it out
	     */
	    } else if (UndoWanted) {
		MachineUndo(color);
		UndoWanted = FALSE;
		Mouse = IDLE;
	    }
	    if (updateMsgWindow)
		WhoseMoveMessage((char *) 0);
	}
    }
    /*
     * check for board subwindow service required
     */
    if (*ibits & BoardSWMask) {
	if (input_readevent(BoardSW->ts_windowfd, &ie) == -1) {
	    perror("input failed");
	    abort();
	}
	switch (Mouse) {
	/*
	 * locked: ignore all mouse activity
	 */
	case LOCKED:
	    break;
	/* 
	 * we are using the mouse to confirm something 
	 */
	case CONFIRMING:
	    if (win_inputposevent(&ie) 
	    && ie.ie_code >= BUT_FIRST
	    && ie.ie_code <= BUT_LAST) {
		Mouse = IDLE;
		switch(confirmState) {
		case CONFIRM_RESIGNATION:
		    if (ie.ie_code == MS_LEFT) {
			SendResignation(PeerColor);
			Mouse = LOCKED;		/* game is over */
			DoResignation(MyColor);
			Message("Resigned");
		    } else {
			WhoseMoveMessage((char *) 0);
		    }
		    break;
		case CONFIRM_SETUP_END:
		    if (ie.ie_code == MS_LEFT) {
			/* 
			 * if either player is a machine, white always moves
			 * first following a setup (another brain-damaged
			 * attribute of the unix chess program).
			 */
			if (IsMachine[WHITE] || IsMachine[BLACK]) {
			    BOOL legalSetup = TRUE;

			    Turn = WHITE;
			    if (IsMachine[BLACK]) {
				legalSetup = MachineSetup(BLACK);
			    }
			    if (legalSetup && IsMachine[WHITE]) {
				if (legalSetup = MachineSetup(WHITE))
				    MachineFirst(WHITE);
			    }
			    if ( ! legalSetup) {
				Message("Illegal setup - try again");
				Mouse = SETUP;
			    } else {
				/*
				 * if both players are machines, the human part
				 * is over.
				 */
				if (IsMachine[BLACK] && IsMachine[WHITE]) 
				    Mouse = LOCKED;
				/*
				 * else we get to play
				 */
				else 
				    InitialTurn = WHITE;
				WhoseMoveMessage((char *) 0);
				SetupMode = FALSE;
			    }
			/*
			 * else the opponent is a human, and we can specify
			 * who moves first.
			 */
			} else {
			    Message("Left button to move first, other to move second");
			    Mouse = CONFIRMING;
			    confirmState = CONFIRM_WHOSE_TURN;
			    SetupMode = FALSE;
			}
		    } else {
			Message("Setup: left - source, middle - delete, right - end");
			Mouse = SETUP;
		    }
		    break;
		case CONFIRM_WHOSE_TURN:
		    Turn = InitialTurn = (ie.ie_code == MS_LEFT ? 
			MyColor : 
			OTHERCOLOR(MyColor));
		    WhoseMoveMessage((char *) 0);
		    SendEndRestore();
		    break;
		case CONFIRM_UNDO:
		    SendUndoAcknowledgement(ie.ie_code == MS_LEFT);
		    break;
		}
	    }
	    break;
	/*
	 * we are setting up an initial board layout
	 */
	case SETUP:
	    switch (ie.ie_code) {
	    /*
	     * generate and pick up a source piece
	     */
	    case MS_LEFT:
		if (win_inputposevent(&ie)) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    /* if this a source square */
		    if (IsSrcPieceAt(&from)) {
			Mouse = MOVING_PIECE;
			sqp = GetSrcSquare(from.x, from.y);
			setup.type = sqp->type;
			setup.color = sqp->color;
			/*
			 * create the first background pixrect,
			 * centered on the selected board square
			 */
			mapBoardToMouse(&from, &lastMouseLoc);
			/* grab the currently displayed image */
			pw_read(MoveFromPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC,
			    Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET);
			/* repaint the blank square */
			pr_rop(MoveFromPR,
			    MOVE_X_OFFSET,
			    MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    ((from.x + from.y) & 0x01) ? 
				&BlackSquarePR :
				&WhiteSquarePR,
			    0, 0);
			/* 
			 * remember the pixrect used to paint the piece 
			 * being moved 
			 */
			pieceIcon = PieceIcons[(int) sqp->type][sqp->color];
			pieceStencil = PieceStencils[(int) sqp->type];
			/* 
			 * if there is a piece at the source square, repaint 
			 * the piece on the background pixrect 
			 */
			sqp = GetSquare(from.x, from.y);
			if (sqp->type != NULLPC) {
			    pr_stencil(MoveFromPR,
				MOVE_X_OFFSET,
				MOVE_Y_OFFSET,
				SQUARE_WIDTH, SQUARE_HEIGHT,
				PIX_SRC, 
				PieceStencils[(int)sqp->type], 0, 0, 
				PieceIcons[(int) sqp->type][sqp->color], 0, 0);
			}
		    }
		}
		break;
	    /*
	     * delete a piece 
	     */
	    case MS_MIDDLE:
		if (win_inputposevent(&ie)) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    setup.x = from.x;
		    setup.y = from.y;
		    setup.type = NULLPC;
		    DoSetupChange(&setup);
		    SendSetupChange(&setup, PeerColor);
		}
		break;
	    /*
	     * exit setup
	     */
	    case MS_RIGHT:
		if (win_inputposevent(&ie)) {
		    Message("Sure you want to end setup? left - yes, other - no");
		    Mouse = CONFIRMING;
		    confirmState = CONFIRM_SETUP_END;
		}
		break;
	    }
	    break;
	/*
	 * we are promoting a pawn
	 */
	case PROMOTING_PAWN:
	    switch(ie.ie_code) {
	    /*
	     * select the next pawn morph
	     */
	    case MS_LEFT:
		if (win_inputposevent(&ie))
		    move.newPieceType = PromotePawn(&to);
		break;
	    /*
	     * go for the current morph
	     */
	    case MS_MIDDLE:
		if (win_inputposevent(&ie) && SendMove(&move, PeerColor)) {
		    Turn = OTHERCOLOR(Turn);
		    WhoseMoveMessage((char *) 0);
		    Mouse = IDLE;
		}
		break;
	    /*
	     * we exited the window - back out the pawn move 
	     */
	    case LOC_WINEXIT:
		UnDoMove();
		Mouse = IDLE;
		break;
	    }
	    break;
	/* 
	 * we aren't currently doing anything
	 */
	case IDLE:
	    switch(ie.ie_code) {
	    case MS_LEFT:
		/*
		 * if this a left button press 
		 * and it is our turn
		 * and we aren't waiting for an undo acknowledgement
		 */
		if (win_inputposevent(&ie) && Turn == MyColor && ! UndoWanted) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    /* 
		     * if there is one of our pieces on this square 
		     */
		    if (IsOurPieceAt(&from)) {
			Mouse = MOVING_PIECE;
			sqp = GetSquare(from.x, from.y);
			/*
			 * create the first background pixrect,
			 * centered on the selected board square
			 */
			mapBoardToMouse(&from, &lastMouseLoc);
			/* grab the currently displayed image */
			pw_read(MoveFromPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC,
			    Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET);
			/* repaint the blank square */
			pr_rop(MoveFromPR,
			    MOVE_X_OFFSET,
			    MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    ((from.x + from.y) & 0x01) ? 
				&BlackSquarePR :
				&WhiteSquarePR,
			    0, 0);
			/* 
			 * remember the pixrect used to paint the piece 
			 * being moved 
			 */
			pieceIcon = PieceIcons[(int) sqp->type][sqp->color];
			pieceStencil = PieceStencils[(int) sqp->type];
			/*
			 * a bit un-structured, but we are forced to do this
			 * if we want the piece to "jump" to the cursor when
			 * the button is initially depressed ("forced" in the
			 * sense that we are inside a switch statement).
			 */
			goto moveIt;
		    }
		}
		break;
	    }
	    break;
	/*
	 * we are animating a piece 
	 */
	case MOVING_PIECE:
	    switch(ie.ie_code) {
	    case MS_LEFT:
		/*
		 * if we are putting down a piece
		 */
		if (win_inputnegevent(&ie)) {
		    BOOL legal;

		    Mouse = IDLE;
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &to);
		    legal = TRUE;
		    move.x1 = from.x; move.y1 = from.y;
		    move.x2 = to.x; move.y2 = to.y;
		    if (SetupMode) {
			/* repaint the background */
			pw_rop(Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			/* put the piece down no matter what */
			setup.x = to.x;
			setup.y = to.y;
			DoSetupChange(&setup);
			SendSetupChange(&setup, PeerColor);
			Mouse = SETUP;
		    } else if ( ! (from.x == to.x && from.y == to.y) 
		    && Turn == MyColor 
		    && (legal = IsMoveLegal(&from, &to))) {
			/* if this is a pawn promotion */
			if (GetSquare(from.x, from.y)->type == PAWN
			&& (to.y == 0 || to.y == 7)) {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    move.newPieceType = (int) QUEEN;
			    DoMove(&move, TRUE);
			    /* if we are playing the brain-damaged unix chess
			     * program, can only promote to queens */
			    if (IsMachine[OTHERCOLOR(MyColor)]) {
				Turn = OTHERCOLOR(Turn);
				SendMove(&move, PeerColor);
				WhoseMoveMessage((char *) 0);
			    /* else need to have the user select the promoted 
			     * piece type */
			    } else {
				Message("Left button: select piece type, middle: send move.");
				Mouse = PROMOTING_PAWN;
			    }
			/* else if we can send the move */
			} else if (SendMove(&move, PeerColor)) {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    DoMove(&move, TRUE);
			    Turn = OTHERCOLOR(Turn);
			    WhoseMoveMessage((char *) 0);
			/* else the peer is dead */
			} else {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    putPieceBack(&from, pieceStencil, pieceIcon);
			}
		    } else {
			/* repaint the background */
			pw_rop(Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			putPieceBack(&from, pieceStencil, pieceIcon);
			if ( ! legal)
			    Message("Illegal move");
		    }
		}
		break;
	    /*
	     * exited the window - clean it up.
	     */
	    case LOC_WINEXIT:
		/* repaint the background */
		pw_rop(Board->gfx_pixwin,
		    lastMouseLoc.x - MOVE_X_OFFSET,
		    lastMouseLoc.y - MOVE_Y_OFFSET,
		    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
		    PIX_SRC, MoveFromPR, 0, 0);
		if (SetupMode) {
		    Mouse = SETUP;
		} else {
		    putPieceBack(&from, pieceStencil, pieceIcon);
		    Mouse = IDLE;
		}
		break;
	    /* 
	     * animate the piece
	     */
	    case LOC_MOVEWHILEBUTDOWN:
	moveIt:
		/* ignore old motion events */
		ioctl(Board->gfx_windowfd, (int) FIONREAD, (char *) &nbytes);
		if (nbytes/sizeof(struct inputevent) <= 3) {
		    do {
			newMouseLoc.x = ie.ie_locx - SQUARE_WIDTH/2;
			newMouseLoc.y = ie.ie_locy - SQUARE_HEIGHT/2;
			clamped = FALSE;
			/*
			 * clamp motion if necessary
			 */
			if (newMouseLoc.x - lastMouseLoc.x >= MOVE_X_OFFSET) {
			    newMouseLoc.x = lastMouseLoc.x + (MOVE_X_OFFSET-1);
			    clamped = TRUE;
			} else if (newMouseLoc.x - lastMouseLoc.x <= - MOVE_X_OFFSET) {
			    newMouseLoc.x = lastMouseLoc.x - (MOVE_X_OFFSET-1);
			    clamped = TRUE;
			} 
			if (newMouseLoc.y - lastMouseLoc.y >= MOVE_Y_OFFSET) {
			    newMouseLoc.y = lastMouseLoc.y + (MOVE_Y_OFFSET-1);
			    clamped = TRUE;
			} else if (newMouseLoc.y - lastMouseLoc.y <= - MOVE_Y_OFFSET) {
			    newMouseLoc.y = lastMouseLoc.y - (MOVE_Y_OFFSET-1);
			    clamped = TRUE;
			}
			/* grab the new area */
			pw_read(MoveToPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, Board->gfx_pixwin,
			    newMouseLoc.x - MOVE_X_OFFSET,
			    newMouseLoc.y - MOVE_Y_OFFSET);
			/* paste the old background over the new area */
			pr_rop(MoveToPR,
			    lastMouseLoc.x - newMouseLoc.x,
			    lastMouseLoc.y - newMouseLoc.y,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			/* save the new background */
			pr_rop(MoveFromPR, 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveToPR, 0, 0);
			/* paint the piece on the new area */
			pr_stencil(MoveToPR, 
			    MOVE_X_OFFSET, MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC, pieceStencil, 0, 0, pieceIcon, 0, 0);
			/* now paint the new area on the screen */
			pw_rop(Board->gfx_pixwin,
			    newMouseLoc.x - MOVE_X_OFFSET,
			    newMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveToPR, 0, 0);
			lastMouseLoc.x = newMouseLoc.x;
			lastMouseLoc.y = newMouseLoc.y;
		    } while (clamped);
		}
		break;
	    }
	    break;
	}
    }
    * ibits = svc_fds | BoardSWMask | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE];
    * obits = * ebits = 0;
}

/*
 * initialize the board subwindow
 */
void
InitBoardSW(useRetained, iconDirectory)
    BOOL useRetained;			/* use a retained pixrect */
    char * iconDirectory;		/* custom piece icon directory */
{
    static struct timeval tickValue;
    struct inputmask mask;
    register unsigned int i;
    int height = (SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10;

    /*
     * initialize the subwindow
     */
    if ((BoardSW = gfxsw_createtoolsubwindow(NchessTool, "",
	TOOL_SWEXTENDTOEDGE, 
	/* playing surface    +  victim area */
/*	(SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10,  */
	height,
	NULL)) == NULL) 
    {
	fprintf(stderr, "Can't create board subwindow\n");
	exit(1);
    }
    Board = (struct gfxsubwindow *) BoardSW->ts_data;
    if (useRetained)
	gfxsw_getretained(Board); 
    BoardSW->ts_io.tio_handlesigwinch = boardSigwinch;
    BoardSW->ts_io.tio_selected = boardSelected;
    input_imnull(&mask);
    win_setinputcodebit(&mask, MS_LEFT);
    win_setinputcodebit(&mask, MS_MIDDLE);
    win_setinputcodebit(&mask, MS_RIGHT);
    win_setinputcodebit(&mask, LOC_MOVEWHILEBUTDOWN);
    win_setinputcodebit(&mask, LOC_WINEXIT);
    mask.im_flags |= IM_NEGEVENT;
    win_setinputmask(BoardSW->ts_windowfd, &mask, NULL, WIN_NULLLINK);
    /* 
     * add the RPC service and chess process file descriptor select
     * masks to this subwindow.
     */
    BoardSW->ts_io.tio_inputmask = svc_fds 
	| (BoardSWMask = 1 << BoardSW->ts_windowfd) 
	| ChessProcessFDs[BLACK] 
	| ChessProcessFDs[WHITE] ;
    /*
     * set the timeout to 1 second 
     */
    tickValue.tv_sec = 1L;
    tickValue.tv_usec = 0L;
    BoardSW->ts_io.tio_timer = &tickValue;
    /*
     * create the white pieces by inverting the black pieces, using custom
     * pieces where appropriate.
     */
    for ( i = 0 ; i < 6 ; i++ ) {
	if (iconDirectory != (char *) 0) {
	    FILE * iconFile, * stencilFile;
	    icon_header_object iconHeader, stencilHeader;
	    char fileName[512], errorMsg[IL_ERRORMSG_SIZE + 2];

	    strcpy(fileName, iconDirectory);
	    strcat(fileName, "/");
	    strcat(fileName, PieceIconFileNames[i]);
	    if ((iconFile = icon_open_header(fileName, errorMsg, 
		&iconHeader)) != (FILE *) 0) 
	    {
		if (iconHeader.width != SQUARE_WIDTH 
		|| iconHeader.height != SQUARE_HEIGHT
		|| iconHeader.depth != 1) {
		    fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName);
		} else {
		    strcpy(fileName, iconDirectory);
		    strcat(fileName, "/");
		    strcat(fileName, PieceStencilFileNames[i]);
		    if ((stencilFile = icon_open_header(fileName, errorMsg, 
			&stencilHeader)) != (FILE *) 0) 
		    {
			if (stencilHeader.width != SQUARE_WIDTH 
			|| stencilHeader.height != SQUARE_HEIGHT
			|| stencilHeader.depth != 1) {
			    fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName);
			} else {
			    icon_read_pr(iconFile, &iconHeader, PieceIcons[i][BLACK]);
			    icon_read_pr(stencilFile, &stencilHeader, PieceStencils[i]);
			}
			fclose(stencilFile);
		    }
		}
		fclose(iconFile);
	    }
	}
	pr_rop(PieceIcons[i][WHITE], 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_NOT(PIX_SRC),
	    PieceIcons[i][BLACK], 0, 0);
    }
    /* 
     * create the pixrects used for piece animation and victim drawing
     */
    if ((MoveFromPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0
    || (MoveToPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0
    || (VictimPR = mem_create(SQUARE_WIDTH, SQUARE_HEIGHT, 1)) == (struct pixrect *) 0) {
	fprintf(stderr, "can't create the animation pixrects\n");
	exit(1);
    }
}

/*
 * draw a square, including the piece (if any)
 */
void
DrawSquare(x, y, sqp)
    int x, y;
    register Square * sqp;
{
    BoardCoordinate bloc;
    struct pr_pos mloc;

    bloc.x = x; bloc.y = y;
    mapBoardToMouse(&bloc, &mloc);
    /* paint the blank square */
    pw_rop(Board->gfx_pixwin, 
	mloc.x, mloc.y,
	SQUARE_WIDTH, SQUARE_HEIGHT,
	PIX_SRC, 
	(((x + y) & 0x01) ? &BlackSquarePR : &WhiteSquarePR), 
	0, 0);
    /* paint the piece, if there is one */
    if (sqp->type != NULLPC) {
	pw_stencil(Board->gfx_pixwin, mloc.x, mloc.y,
	    SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_SRC,
	    PieceStencils[(int) sqp->type], 0, 0,
	    PieceIcons[(int) sqp->type][sqp->color], 0, 0);
    }
}

/*
 * draw the playing surface and victim area
 */
void
DrawBoard()
{
    register int x, y;
    register Square * sqp;

    /* clear the board area */
    pw_rop(Board->gfx_pixwin,
	0, 0, Board->gfx_rect.r_width, Board->gfx_rect.r_height,
	PIX_CLR, (struct pixrect *) 0, 0, 0);
    /* draw the playing area */
    for (x = 0 ; x < 8 ; x++) {
	for (y = 0 ; y < 8 ; y++) {
	    sqp = GetSquare(x, y);
	    DrawSquare(x, y, sqp);
	}
    }
    /* draw the victims */
    drawVictims();
}

/*
 * your basic victim 
 */
typedef struct {
    PieceType type;			/* victim type */
    BOOL active;			/* victim slot state */
    int count;				/* victim overflow count */
} Victim;

#define	NUM_VICTIM_SLOTS 8

Victim victims[2 /* pawns(1) vs. pieces(0) */][2 /* color */ ][NUM_VICTIM_SLOTS /* victim slot # */];

/*
 * map the victim specified by the triple 
 * [ isPawn { 0, 1 }, color { BLACK, WHITE }, slot { 0 .. NUM_VICTIM_SLOTS-1 } ]
 * to a mouse coordinate (relative to the board subwindow)
 */
void
mapVictimToMouse(isPawn, color, slot, mlocp)
    BOOL isPawn;
    int color, slot;
    struct pr_pos * mlocp;
{
    mlocp->x = slot * (3 * SQUARE_WIDTH/4) + color * (3 * SQUARE_WIDTH/8);
    mlocp->y = 8 * (SQUARE_HEIGHT-1) + isPawn * (SQUARE_HEIGHT/2) + color * (SQUARE_HEIGHT/4) + 5;
}

/* 
 * draw the victims
 */
drawVictims()
{
    register int i, j, k;
    register Victim * victim;
    struct pr_pos victimOrigin;

    for (i = 0 ; i < 2 ; i++) {
	for (j = 0 ; j < 2 ; j++) {
	    for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
		victim = &victims[i][j][k];
		if (victim->active) {
		    mapVictimToMouse(i, j, k, &victimOrigin);
		    pw_stencil(Board->gfx_pixwin, 
			victimOrigin.x, victimOrigin.y,
			SQUARE_WIDTH, SQUARE_HEIGHT,
			PIX_SRC,
			PieceStencils[(int) victim->type], 0, 0,
			PieceIcons[(int) victim->type][j], 0, 0);
		}
	    }
	}
    }
}

/* 
 * add a piece to the set of victims 
 */
void
AddVictim(type, color, drawIt) 
    PieceType type;
    int color;
    BOOL drawIt;
{
    register int i, j, k;
    int empty, lastMatch, extras;
    register Victim * victim;
    BOOL isPawn = (type == PAWN);
    struct pr_pos victimOrigin, othersOrigin;

    /* 
     * look for the first empty slot and the last slot which 
     * contains a piece of the same type
     */
    for (lastMatch = empty = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) {
	victim = &victims[isPawn][color][i];
	if (empty < 0 && ! victim->active)
	    empty = i;
	if (victim->active && victim->type == type)
	    lastMatch = i;
    }
    /*
     * if there were no empty slots 
     */
    if (empty == -1) {
	/* 
	 * if there was one or more pieces of the same type, update
	 * the last instance's overflow count (includes coalescing
	 * all overflows of that type at the last instance)
	 */
	if (lastMatch >= 0) {
	    for (extras = i = 0 ; i < lastMatch ; i++) {
		victim = &victims[isPawn][color][i];
		if (victim->active && victim->type == type && victim->count > 1) {
		    extras += victim->count - 1;
		    victim->count = 1;
		}
	    }
	    victims[isPawn][color][lastMatch].count += extras;
	}
    /*
     * else install the victim in the empty slot 
     */
    } else {
	victim = &victims[isPawn][color][empty];
	victim->type = type;
	victim->active = TRUE;
	victim->count = 1;
	if (drawIt) {
	    /*
	     * re-draw all the pieces in the victim's slot 
	     */
	    mapVictimToMouse(isPawn, color, empty, &victimOrigin);
	    pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
		PIX_CLR, (struct pixrect *) 0, 0, 0);
	    for (i = 0 ; i < 2 ; i++) {
		for (j = 0 ; j < 2 ; j++) {
		    for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
			victim = &victims[i][j][k];
			if (victim->active) {
			    mapVictimToMouse(i, j, k, &othersOrigin);
			    pr_stencil(VictimPR,
				othersOrigin.x - victimOrigin.x,
				othersOrigin.y - victimOrigin.y,
				SQUARE_WIDTH, SQUARE_HEIGHT,
				PIX_SRC,
				PieceStencils[(int) victim->type], 0, 0,
				PieceIcons[(int) victim->type][j], 0, 0);
			}
		    }
		}
	    }
	    /*
	     * now re-draw the slot 
	     */
	    pw_rop(Board->gfx_pixwin,
		victimOrigin.x, victimOrigin.y, 
		SQUARE_WIDTH, SQUARE_HEIGHT,
		PIX_SRC, VictimPR, 0, 0);
	}
    }
}

/*
 * reincarnate a victim (via an undo)
 */
void
DeleteVictim(type, color)
    PieceType type;
    int color;
{
    register int i, j, k;
    int lastMatch, extras;
    register Victim * victim;
    BOOL isPawn = (type == PAWN);
    struct pr_pos victimOrigin, othersOrigin;

    /* 
     * look for the last slot which contains a piece of this type
     */
    for (lastMatch = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) {
	victim = &victims[isPawn][color][i];
	if (victim->active && victim->type == type)
	    lastMatch = i;
    }
    /*
     * if there were no matches, don't do anything
     */
    if (lastMatch == -1) {
	/* do nothing */
    /*
     * else if the last match slot contains overflows, simply 
     * decrement the overflow count
     */
    } else if ((victim = &victims[isPawn][color][lastMatch])->count > 1) {
	victim->count--;
    /*
     * else zero out the slot and re-draw it as empty
     */
    } else {
	victim->active = FALSE;
	/*
	 * re-draw all the remaining pieces in the victim's slot 
	 */
	mapVictimToMouse(isPawn, color, lastMatch, &victimOrigin);
	pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_CLR, (struct pixrect *) 0, 0, 0);
	for (i = 0 ; i < 2 ; i++) {
	    for (j = 0 ; j < 2 ; j++) {
		for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
		    victim = &victims[i][j][k];
		    if (victim->active) {
			mapVictimToMouse(i, j, k, &othersOrigin);
			pr_stencil(VictimPR,
			    othersOrigin.x - victimOrigin.x,
			    othersOrigin.y - victimOrigin.y,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    PieceStencils[(int) victim->type], 0, 0,
			    PieceIcons[(int) victim->type][j], 0, 0);
		    }
		}
	    }
	}
	/*
	 * now re-draw the slot 
	 */
	pw_rop(Board->gfx_pixwin,
	    victimOrigin.x, victimOrigin.y, 
	    SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_SRC, VictimPR, 0, 0);
    }
}

@//E*O*F boardsw.c//
chmod u=r,g=r,o=r boardsw.c
 
echo x - msgsw.c
sed 's/^@//' > "msgsw.c" <<'@//E*O*F msgsw.c//'
/*
 * message subwindow handling
 */

#include <stdio.h>
#include <ctype.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <strings.h>

#include "nchess.h"

struct toolsw * MessageSW;
Panel MessagePanel;

Panel_item MessageItem;

/*
 * set up the message subwindow
 */
void
InitMsgSW()
{
    if ((MessageSW = panel_create(NchessTool, 0)) == NULL) {
	fprintf(stderr, "Can't create message panel\n");
	exit(1);
    }
    MessagePanel = MessageSW->ts_data;
    /* create the message panel item */
    MessageItem = panel_create_item(MessagePanel, PANEL_MESSAGE,
	PANEL_LABEL_STRING, 
	    RestoringGame ? "Please wait..." :
	    SetupMode ? "Setup: left - source, middle - delete, right - end" :
	    IsMachine[Turn] || Turn != MyColor ? 
		(Turn == WHITE ?
		    "Waiting for white to play..." : 
		    "Waiting for black to play...") :
		"Your move" ,
	PANEL_SHOW_ITEM, TRUE,
	0);
    panel_fit_height(MessagePanel); 
}

void 
Message(cp)
    char * cp;
{
    panel_set(MessageItem,
	PANEL_LABEL_STRING, cp,
	PANEL_SHOW_ITEM, TRUE,
	0);
}

/*
 * write the standard "your move", "waiting ..." message
 * pre-pended with the passed string (if non-null)
 */
void
WhoseMoveMessage(cp)
    char * cp;
{
    char c1[256], c2[256];

    if (GameOver) 
	strcpy(c2, "Game over");
    else if (IsMachine[Turn] || Turn != MyColor)
	strcpy(c2, Turn == WHITE ? 
	    "Waiting for white to play..." :
	    "Waiting for black to play...");
    else if (InCheck())
	strcpy(c2, "Your move (check)");
    else
	strcpy(c2, "Your move");

    if (cp != (char *) 0) {
	c2[0] = tolower(c2[0]);
	strcpy(c1, cp);
	strcat(c1, " - ");
	Message(strcat(c1, c2));
    } else 
	Message(c2);
}

@//E*O*F msgsw.c//
chmod u=r,g=r,o=r msgsw.c
 
echo x - talksw.c
sed 's/^@//' > "talksw.c" <<'@//E*O*F talksw.c//'
/*
 * talk subwindow handling
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <strings.h>

#include "nchess.h"

#define	MAX_SEND_LENGTH		60

struct toolsw * TalkSW;
Panel TalkPanel;

Panel_item SendItem;
Panel_item RecvItem;

/*ARGSUSED*/
sendProc(item, event)
    Panel_item item;
    struct inputevent *event;
{
    char c[MAX_SEND_LENGTH+1];

    strcpy(c, (char *) panel_get_value(SendItem));
    SendTalkMsg(c);
    /* set the command text to nil */
    panel_set_value(SendItem, "");
}

/*
 * set up the talk send and receive subwindows 
 * (if we are playing against the machine, leave them out)
 */
void
InitTalkSW()
{
    if (IsMachine[PeerColor])
	return;
    if ((TalkSW = panel_create(NchessTool, 0)) == NULL) {
	fprintf(stderr, "Can't create talk subwindows\n");
	exit(1);
    }
    TalkPanel = TalkSW->ts_data;
    RecvItem = panel_create_item(TalkPanel, PANEL_MESSAGE,
	PANEL_LABEL_STRING, "Recv:",
	PANEL_SHOW_ITEM, TRUE,
	0);
    SendItem = panel_create_item(TalkPanel, PANEL_TEXT,
	PANEL_LABEL_STRING, "Send:",
	PANEL_NOTIFY_STRING, "\n\r",
	PANEL_NOTIFY_LEVEL, PANEL_SPECIFIED,
	PANEL_NOTIFY_PROC, sendProc,
	PANEL_VALUE_STORED_LENGTH, MAX_SEND_LENGTH,
	0);
    panel_fit_height(TalkPanel); 
}

/*
 * receive a pithy banality from the opponent
 */
void
RecvTalkMsg(cp)
    char *cp;
{
    char ncp[128];

    strcpy(ncp, "Recv: ");
    panel_set(RecvItem, 
	PANEL_LABEL_STRING, strcat(ncp, cp),
	0);
}
@//E*O*F talksw.c//
chmod u=r,g=r,o=r talksw.c
 
echo x - chessprocess.c
sed 's/^@//' > "chessprocess.c" <<'@//E*O*F chessprocess.c//'
/*
 * manage one or two chess game processes
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>

#include "nchess.h"

extern char * gets();

int ChessPid[2],
    ChessProcessFDs[2];
FILE * ReadChessProcess[2], * WriteChessProcess[2];
BOOL SigChildInterceptOn = FALSE;
BOOL MachineDebug = FALSE;
char * colorStrings[] = { "black", "white" };

/*
 * intercept SIGCHLD
 *
 * print a message for the first chess process that dies and abort the 
 * game.  (note: any change in child status is interpreted as death).
 */
handleSigChild()
{
    int pid;
    union wait status;

    while(1) {
	pid = wait3(&status, WNOHANG, (struct rusage *) 0);
	if (pid <= 0)
	    return;
	if ( ! GameOver && (pid == ChessPid[BLACK] || pid == ChessPid[WHITE])) {
	    Message (pid == ChessPid[WHITE] ?
		"White's process died" :
		"Black's process died" );
	    KillMouseActivity();
	    GameOver = TRUE;
	    Mouse = LOCKED;
	}
    }
}

/*
 * have a chess process make the first move.
 *
 * note: with the wonderful unix chess program, "first" also implies
 * playing the white pieces.  nonetheless, color is an argument to this
 * function in case we ever find out how to get around this problem.
 */
void
MachineFirst(color)
    int color;
{
    fputs("first\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: first\n", colorStrings[color]);
}

/*
 * open a pipe to a chess game process
 *
 * startIt indicates whether the process should be sent the "first"
 * command.
 */
void
InitChessProcess(cp, color, startIt)
    char ** cp;
    int color;
    BOOL startIt;
{
    int sv[2];
    char c[128];

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
	fputs("socketpair failed\n", stderr);
	exit(1);
    }
    if ((ChessPid[color] = fork()) == 0) {
	dup2(sv[0], 0);
	dup2(sv[0], 1); 
	chdir("/tmp");		/* so that "chess.out" can always be written */
	execvp(cp[0], cp);
	puts("exec");
	fflush(stdout);
	_exit(1);
    }
/*    dup2(sv[1], 0);
    dup2(sv[1], 1); */
    /* want to intercept SIGCHLD */
    if ( ! SigChildInterceptOn) {
	SigChildInterceptOn = TRUE;
	signal(SIGCHLD, handleSigChild);
    }
    ChessProcessFDs[color] = (1 << sv[1]);
    ReadChessProcess[color] = fdopen(sv[1], "r");
    WriteChessProcess[color] = fdopen(sv[1], "w");
    setbuf(ReadChessProcess[color], (char *) 0);
    /* 
     * eat the "Chess" prompt 
     * note: if the child's exec failed,
     * catch the "exec" prompt and give up
     */
    fgets(c, sizeof(c), ReadChessProcess[color]);
    if (strcmp(c, "exec\n") == 0) {
	KillChessProcesses();
	fprintf(stderr, "exec of %s failed\n", cp[0]);
	exit(1);
    }
    /* set algebraic notation mode */
    fputs("alg\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: alg\n", colorStrings[color]);
    if (startIt)
	MachineFirst(color);
}
 

/*
 * reap one or more chess process
 */
void
ReapChessProcesses()
{
    union wait status;

    while (wait3(&status, WNOHANG, (struct rusage *) 0) >= 0)
	;
}

/*
 * kill any and all chess processes
 */
void
KillChessProcesses()
{
    signal(SIGCHLD, SIG_IGN);
    if (ChessPid[BLACK])
	kill(ChessPid[BLACK], SIGKILL);
    if (ChessPid[WHITE])
	kill(ChessPid[WHITE], SIGKILL);
    ReapChessProcesses();
}

/*
 * get a move from a chess process 
 * 
 * return 1 if a move was successfully obtained, 0 if not.
 */
int
GetMachineMove(move, color)
    Move * move;
    int color;
{
    char c[256], c2[256];
    char file1, file2;
    int rank1, rank2;

    if (fgets(c, sizeof(c), ReadChessProcess[color]) == (char *) 0) 
	return(0);
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
    /* look for special announcements */
    if (strcmp(c, "Forced mate\n") == 0) {
	Message(color == WHITE ? 
	    "White announces forced mate" :
	    "Black announces forced mate");
	return(0);
    } 
    if (strcmp(c, "Resign\n") == 0) {
	Message(color == WHITE ? "White resigns" : "Black resigns");
	DoResignation(color);
	Mouse = LOCKED;
	return(0);
    } 
    if (strcmp(c, "Illegal move\n") == 0) {
	Message("Internal botch - illegal move detected");
	return(0);
    } 
    if (strncmp(c, "done", 4) == 0) {
	return(0);
    }
    if (strncmp(c, "White wins", 10) == 0
    || strncmp(c, "Black wins", 10) == 0) {
	c[10] = '\0';
	Message(c);
	GameOver = TRUE;
	Mouse = LOCKED;
	return(0);
    }
    /* e.g., 1. d2d4 */
    if (sscanf(c, "%*d. %c%d%c%d", &file1, &rank1, &file2, &rank2) == 4
    /* e.g., 1. ... d2d4 */
    || sscanf(c, "%*d. ... %c%d%c%d", &file1, &rank1, &file2, &rank2) == 4) {
	move->x1 = file1 - (isupper(file1) ? 'A' : 'a');
	move->y1 = 8 - rank1;
	move->x2 = file2 - (isupper(file2) ? 'A' : 'a');
	move->y2 = 8 - rank2;
	/* TBD: possible non-queen pawn promotion 
	 * (the existing chess program doesn't implement this) */
	move->newPieceType = (int) QUEEN;
	return(1);
    } else {
	strcpy(c2, color == WHITE ? "White announces: " : "Black announces: ");
	Message(strncat(c2, c, sizeof(c2) - strlen(c2) - 2));
	if (strncmp(c, "Draw", 4) == 0) {
	    GameOver = TRUE;
	    Mouse = LOCKED;
	}
	return(0);
    }
}

/*
 * send a move to a chess process
 *
 * note: if the process responds with a move too quickly, we won't 
 * get another select() trigger to get its move.  thus, we need to
 * check for the availability of a move when the echo is received.
 */
void
SendMachineMove(move, color)
    Move * move;
    int color;
{
    char c[128];

    if (move->x1 != move->x2
    && GetSquare(move->x1, move->y1)->type == PAWN
    && GetSquare(move->x2, move->y2)->type == NULLPC) {
	/* re-encode en passant captures as horizontal moves */
	fprintf(WriteChessProcess[color], "%c%d%c%d\n", 
	    move->x1 + 'a', 8 - move->y1, 
	    move->x2 + 'a', 8 - move->y1);
	if (MachineDebug)
	    fprintf(stderr, "sent move to %s: %c%d%c%d\n", 
		colorStrings[color],
		move->x1 + 'a', 8 - move->y1, 
		move->x2 + 'a', 8 - move->y1);
    } else {
	fprintf(WriteChessProcess[color], "%c%d%c%d\n", 
	    move->x1 + 'a', 8 - move->y1, 
	    move->x2 + 'a', 8 - move->y2);
	if (MachineDebug)
	    fprintf(stderr, "sent move to %s: %c%d%c%d\n", 
		colorStrings[color],
		move->x1 + 'a', 8 - move->y1, 
		move->x2 + 'a', 8 - move->y2);
    }
    fflush(WriteChessProcess[color]);
    fgets(c, sizeof(c), ReadChessProcess[color]); /* eat the move echo */
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
}

/*
 * undo the last machine move and our move 
 */
void
MachineUndo(color)
{
    fputs("remove\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: remove\n", colorStrings[color]);
    UnDoMove();
    UnDoMove();
}

/*
 * set up the board 
 * (as noted above: with the existing unix chess program, there is no 
 * apparent way (short of deciphering the chess.out format, which is 
 * totally un-fun) to inform the chess program whose turn it is and 
 * which color it is supposed to play - it assumes that white always 
 * is the first to move.  
 */

char whitePieceChars[] = { 'p', 'n', 'b', 'r', 'q', 'k', ' ' };
char blackPieceChars[] = { 'P', 'N', 'B', 'R', 'Q', 'K', ' ' };

/*
 * set up a board state against the machine.
 * returns TRUE if successful, FALSE otherwise
 */
BOOL
MachineSetup(color)
    int color;
{
    register int x, y;
    Square * sqp;
    char c[128];

    fputs("setup\n", WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s:\nsetup\n", colorStrings[color]);
    for (y = 0 ; y < 8 ; y++) {
	for (x = 0 ; x < 8 ; x++) {
	    sqp = GetSquare(x, y);
	    putc(sqp->color == WHITE ? 
		whitePieceChars[(int) sqp->type] :
		blackPieceChars[(int) sqp->type], 
		WriteChessProcess[color]);
	    if (MachineDebug)
		fputc(sqp->color == WHITE ? 
		    whitePieceChars[(int) sqp->type] :
		    blackPieceChars[(int) sqp->type], 
		    stderr);
	}
	putc('\n', WriteChessProcess[color]);
	if (MachineDebug)
	    fputc('\n', stderr);
    }
    fflush(WriteChessProcess[color]);
    fgets(c, sizeof(c), ReadChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
    return(strcmp(c, "Setup successful\n") == 0);
}

/*
 * have a chess process save the game as "/tmp/chess.out".
 * return the size of chess.out.
 */
int
MachineSave(color)
    int color;
{
    FILE * saveFile;
    struct stat saveFileStatus;
    int saveFileSize;
    int retryCount = 0;

    fputs("save\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: save\n", colorStrings[color]);
    sleep((unsigned) 1);
    /* wait for the chess process to create chess.out */
    do {
	if ((saveFile = fopen("/tmp/chess.out", "r")) == (FILE *) 0) 
	    sleep((unsigned) 2); 
    } while(saveFile == (FILE *) 0 && ++retryCount < 10);
    if (saveFile == (FILE *) 0) {
	Message("Can't open chess.out!");
	return(-1);
    }
    saveFileStatus.st_size = -1;
    /* wait until chess.out stops growing */
    do {
	sleep((unsigned) 1);
	saveFileSize = saveFileStatus.st_size;
	fstat(fileno(saveFile), &saveFileStatus);
    } while (saveFileSize != saveFileStatus.st_size);
    fclose(saveFile);
    return(saveFileSize);
}

/*
 * restore the game 
 */
void
MachineRestore(color)
    int color;
{
    fputs("restore\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: restore\n", colorStrings[color]);
}

@//E*O*F chessprocess.c//
chmod u=r,g=r,o=r chessprocess.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    1249    4199   34033 boardsw.c
      83     217    1681 msgsw.c
      74     154    1462 talksw.c
     382    1283    9700 chessprocess.c
    1788    5853   46876 total
!!!
wc  boardsw.c msgsw.c talksw.c chessprocess.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

toma@tc.fluke.COM (Tom Anderson) (01/19/87)

As some of you may have noticed, the sources posted for network chess
run into a snag on release 2.2 of Sun Unix.  Replacements for "decls.h"
and "tool.c" appended below should fix this, as well as suffice for
3.2.  However, if memory serves correctly (perhaps someone from Sun
can confirm this), release 3.0 does not define "struct tool" and 
"struct toolsw" (they are typedefs Tool and Toolsw, respectively);
if this is true and you are running 3.0, don't use these files.

Tom Anderson, (206) 356-5895
John Fluke Mfg. Co., Inc.,  P.O. Box C9090 M/S 245F, Everett, Wa. 98206
{ hplsla, microsoft, uw-beaver, sun }!fluke!toma

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by toma on Mon Jan 19 11:36:24 PST 1987
# Contents:  decls.h tool.c
 
echo x - decls.h
sed 's/^@//' > "decls.h" <<'@//E*O*F decls.h//'
/*
 * global declarations 
 */

/* board.c */

extern void InitBoard(), DoMove(), UnDoMove(), ShowLastPlay(), 
    SaveGame(), WriteTranscript(), RestoreGame(), DoSetupChange(),
    DoResignation();
extern BOOL InitialTurn, IsOurPieceAt(), IsSrcPieceAt(), 
    IsMoveLegal(), IHaveMoved(), InCheck(), GameOver;
extern Square * GetSquare(), * GetSrcSquare();
extern int PromotePawn(), Turn;

/* boardsw.c */

extern void InitBoardSW(), DrawBoard(), DrawSquare(), AddVictim(),
    DeleteVictim(), KillMouseActivity(), RequestUndo();
extern MouseState Mouse;
extern BOOL Flashing;

/* chessprocess.c */

extern void InitChessProcess(), ReapChessProcesses(), KillChessProcesses(),
    SendMachineMove(), MachineUndo(), MachineRestore(), MachineFirst();
extern BOOL MachineSetup(), MachineDebug;
extern int GetMachineMove(), MachineSave(), ChessProcessFDs[];

/* controlsw.c */

extern void InitControlSW();
extern BOOL SaveWanted;

/* ipc.c */

extern int MyColor, PeerColor;
extern unsigned long PeerProgNum; 
extern void InitRPC(), SendResignation(), SendUndoRequest(), SendTalkMsg(),
    SendRestoreMove(), SendEndRestore(), SendSetupChange(),
    SendUndoAcknowledgement();
extern BOOL UndoWanted, SendMove(), RestoringGame;
extern char * PeerUserName;

/* main.c */

#ifdef FILE
extern FILE * RestoreFile;
#endif
extern int errno, TranscriptType;
extern char * TranscriptFileName, * SaveFileName, * PlayerName[];
extern BOOL SetupMode, IsMachine[2];
extern struct passwd * UserPWEntry;

/* msgsw.c */

extern void InitMsgSW(), Message(), ClearMessage(), WhoseMoveMessage();

/* rpcsw.c */

extern void AddRPCSubwindow(), DeleteRPCSubwindow();

/* select.c */

extern void SelectAll();

/* talksw.c */

extern void RecvTalkMsg(), InitTalkSW();

/* tool.c */

/* the following is a kludge, but hides enormous organizational problems
 * with Sun's headers */
extern struct tool * NchessTool;
extern void RunTool(), InitTool(), ParseToolArgs();

/* xdr.c */

extern int XdrGameReq(), XdrMove(), XdrString(), XdrSetup();

/* undeclared system calls and library fxns */

extern int getpid();
extern long random();
extern char * malloc();
@//E*O*F decls.h//
chmod u=r,g=r,o=r decls.h
 
echo x - tool.c
sed 's/^@//' > "tool.c" <<'@//E*O*F tool.c//'
/*
 * handle the tool environment 
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <suntool/gfxsw.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <strings.h>

#include "nchess.h"

struct tool * NchessTool;
char ** ToolAttrs = (char **) NULL;
char * ToolName;

/*
 * iconic form of window
 */
unsigned short IconImage[] = {
#include "Icons/nchess.icon"
};

DEFINE_ICON_FROM_IMAGE(WindowIcon, IconImage);

sigwinched()
{
    tool_sigwinch(NchessTool);
}

/*
 * parse the tool args.
 */
void 
ParseToolArgs(argc, argv)
    int *argc;
    char **argv;
{
    ToolName = argv[0];
    (*argc)--;
    argv++;
    if (tool_parse_all(argc, argv, &ToolAttrs, ToolName) == -1) {
	tool_usage(ToolName);
	exit(1);
    }
    (*argc)++;
}

/*
 * set up the tool environment
 */
void
InitTool(useRetained, iconDirectory)
    BOOL useRetained;		/* use a retained pixrect for the board */
    char * iconDirectory;	/* custom piece icon directory */
{
    register unsigned int height;
    register struct toolsw * twsp;
    char c[256];

    /* create the tool marquee */
    strcpy(c, " ");
    strcat(c, PlayerName[WHITE]);
    strcat(c, " vs. ");
    strcat(c, PlayerName[BLACK]);
    if ((NchessTool = tool_make( 
	WIN_LABEL, c, 
	WIN_ICON, &WindowIcon, 
	WIN_ATTR_LIST, ToolAttrs,
	WIN_WIDTH, 8 * (SQUARE_WIDTH-1) + 2 * tool_borderwidth(NchessTool) - 1,
	/*
	 * NOTE: The following line was unnecessary in Sun release 2.2,
	 * but is necessary in release 3.0.  For some unknown reason, the
	 * call to tool_set_attributes following the call to InitBoardSW
	 * now fails to uncover the board area that was obscured by the 
	 * default tool height being too small (note that the tool has not 
	 * been installed yet).
	 */
	WIN_HEIGHT, 2000,
	0)) == (struct tool *) NULL) 
    {
	fputs("Can't make tool\n", stderr);
	exit(1);
    }
    tool_free_attribute_list(ToolAttrs);
    /* initialize the subwindows */
    InitTalkSW();
    InitMsgSW();
    InitControlSW();
    InitBoardSW(useRetained, iconDirectory);
    /* 
     * add up subwindow heights and force the tool to the resulting size 
     */
    height = tool_stripeheight(NchessTool) + tool_borderwidth(NchessTool) - tool_subwindowspacing(NchessTool);
    for (twsp = NchessTool->tl_sw ; twsp != (struct toolsw *) 0 ; twsp = twsp->ts_next) 
	height += twsp->ts_height + tool_subwindowspacing(NchessTool);
    /*
     * NOTE: under 2.2, the above calculation yielded the correct height.
     * under 3.0, we need to add a few pixels to make it come out right (the
     * reason is not yet known).
     */
    height += 2;
    tool_set_attributes(NchessTool, WIN_HEIGHT, height, 0); 
    signal(SIGWINCH, sigwinched);
}

void
RunTool()
{
    /*
     * NOTE: this is another difference between release 2.2 and 3.0:
     * in release 2.2, the SIGWINCH handler would get called once at the
     * outset to draw the board area; in release 3.0, this doesn't happen.
     */
    DrawBoard();
    tool_select(NchessTool, 0);
    tool_destroy(NchessTool);
}

@//E*O*F tool.c//
chmod u=r,g=r,o=r tool.c
 
exit 0
-- 

Tom Anderson, (206) 356-5895
John Fluke Mfg. Co., Inc.,  P.O. Box C9090 M/S 245F, Everett, Wa. 98206
{ hplsla, microsoft, uw-beaver, sun }!fluke!toma