From mipos3!intelca!oliveb!ames!hao!husc6!mit-eddie!uw-beaver!tektronix!tekgen!tekred!games-request Sat Nov 21 11:47:54 PST 1987 Article 28 of net.sources.games: Path: td2cad!mipos3!intelca!oliveb!ames!hao!husc6!mit-eddie!uw-beaver!tektronix!tekgen!tekred!games-request From: games-request@tekred.TEK.COM Newsgroups: net.sources.games Subject: v02i091: nchess - network chess game for Suns, Part01/04 Message-ID: <1846@tekred.TEK.COM> Date: 19 Nov 87 18:57:45 GMT Sender: billr@tekred.TEK.COM Lines: 1985 Approved: billr@tekred.TEK.COM Submitted by: (Tom Anderson) toma@tc.fluke.com Comp.sources.games: Volume 2, Issue 91 Archive-name: nchess/Part01 [This was originally posted to net.sources.games, but I had a request to repost it here so it could be archived. I checked with Tom (the author) and he had no objections to posting and had not made any significant changes to the source since it was first posted. -br] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh README <<'END_OF_README' XThis is a five-part distribution of "nchess", a network chess game I wrote Xa few months ago during a chess fad at work. It runs on Sun Microsystems' Xworkstations, and would be quite difficult to port to other machines due Xto heavy reliance on Sun's window libraries. It bears no relation to the Xexisting chesstool program; i.e., it was written from scratch without any Xknowledge of how chesstool is implemented (since we don't have the source). X XSince the manual page is included in the following shell archives, no more Xneed be said. Please mail any comments, bugs, etc. directly to me, since XI very rarely read net news. X XAny commercial use of this software is strictly prohibited, INCLUDING usage Xas a demo vehicle for Sun workstations. X XEnjoy! X XTom Anderson, (206) 356-5895 XJohn Fluke Mfg. Co., Inc., P.O. Box C9090 M/S 245F, Everett, Wa. 98206 X{ hplsla, microsoft, uw-beaver, sun }!fluke!toma X X END_OF_README if test 904 -ne `wc -c MANIFEST <<'END_OF_MANIFEST' X File Name Archive # Description X----------------------------------------------------------- X Icons 1 X Icons/bishop.icon 4 X Icons/bishopStencil.icon 3 X Icons/blackSquare.icon 4 X Icons/king.icon 4 X Icons/kingStencil.icon 4 X Icons/knight.icon 4 X Icons/knightStencil.icon 3 X Icons/nchess.icon 4 X Icons/pawn.icon 4 X Icons/pawnStencil.icon 4 X Icons/queen.icon 4 X Icons/queenStencil.icon 3 X Icons/rook.icon 4 X Icons/rookStencil.icon 3 X Icons/whiteSquare.icon 4 X MANIFEST 1 X Makefile 4 X README 1 X board.c 2 X boardsw.c 1 X chessprocess.c 3 X controlsw.c 3 X daemon.c 3 X decls.h 3 X ipc.c 3 X main.c 3 X msgsw.c 3 X nchess.6 1 X nchess.h 1 X talksw.c 4 X tool.c 3 X xdr.c 4 END_OF_MANIFEST if test 1086 -ne `wc -c boardsw.c <<'END_OF_boardsw.c' X/* X * Copyright 1987 Tom Anderson; 20831 Frank Waters Road; X * Stanwood, WA 98282. All rights reserved. X */ X X/* X * handle the board subwindow (as well as fielding RPC and chess game X * process selects) X */ X X#include X#include X#include X#include X#include X#include X#include X#include X X#include "nchess.h" X X#define MOVE_PR_WIDTH (SQUARE_WIDTH * 2) X#define MOVE_PR_HEIGHT (SQUARE_HEIGHT * 2) X#define MOVE_X_OFFSET (MOVE_PR_WIDTH/2 - SQUARE_WIDTH/2) X#define MOVE_Y_OFFSET (MOVE_PR_HEIGHT/2 - SQUARE_HEIGHT/2) X Xextern int svc_fds; /* RPC service file descriptor(s) */ Xint BoardSWMask; /* board gfx sw file des. */ XBOOL Flashing = FALSE; /* tool icon is flashing */ X Xenum { /* confirmation state using mouse */ X CONFIRM_WANTED, X CONFIRM_SETUP_END, X CONFIRM_WHOSE_TURN, X CONFIRM_UNDO, X CONFIRM_RESIGNATION, X} confirmState; X XMouseState Mouse = IDLE; X Xchar * PieceIconFileNames[] = { X "pawn.icon", X "knight.icon", X "bishop.icon", X "rook.icon", X "queen.icon", X "king.icon", X}; X Xchar * PieceStencilFileNames[] = { X "pawnStencil.icon", X "knightStencil.icon", X "bishopStencil.icon", X "rookStencil.icon", X "queenStencil.icon", X "kingStencil.icon", X}; X X/* X * piece icons and stencils X */ Xunsigned short RookBlackImage[] = { X#include "Icons/rook.icon" X}; Xunsigned short RookWhiteImage[] = { X#include "Icons/rook.icon" X}; Xunsigned short RookStencilImage[] = { X#include "Icons/rookStencil.icon" X}; Xmpr_static(RookBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookBlackImage); Xmpr_static(RookWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookWhiteImage); Xmpr_static(RookStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookStencilImage); X Xunsigned short KnightBlackImage[] = { X#include "Icons/knight.icon" X}; Xunsigned short KnightWhiteImage[] = { X#include "Icons/knight.icon" X}; Xunsigned short KnightStencilImage[] = { X#include "Icons/knightStencil.icon" X}; Xmpr_static(KnightBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightBlackImage); Xmpr_static(KnightWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightWhiteImage); Xmpr_static(KnightStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightStencilImage); X Xunsigned short BishopBlackImage[] = { X#include "Icons/bishop.icon" X}; Xunsigned short BishopWhiteImage[] = { X#include "Icons/bishop.icon" X}; Xunsigned short BishopStencilImage[] = { X#include "Icons/bishopStencil.icon" X}; Xmpr_static(BishopBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopBlackImage); Xmpr_static(BishopWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopWhiteImage); Xmpr_static(BishopStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopStencilImage); X Xunsigned short KingBlackImage[] = { X#include "Icons/king.icon" X}; Xunsigned short KingWhiteImage[] = { X#include "Icons/king.icon" X}; Xunsigned short KingStencilImage[] = { X#include "Icons/kingStencil.icon" X}; Xmpr_static(KingBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingBlackImage); Xmpr_static(KingWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingWhiteImage); Xmpr_static(KingStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingStencilImage); X Xunsigned short QueenBlackImage[] = { X#include "Icons/queen.icon" X}; Xunsigned short QueenWhiteImage[] = { X#include "Icons/queen.icon" X}; Xunsigned short QueenStencilImage[] = { X#include "Icons/queenStencil.icon" X}; Xmpr_static(QueenBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenBlackImage); Xmpr_static(QueenWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenWhiteImage); Xmpr_static(QueenStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenStencilImage); X Xunsigned short PawnBlackImage[] = { X#include "Icons/pawn.icon" X}; Xunsigned short PawnWhiteImage[] = { X#include "Icons/pawn.icon" X}; Xunsigned short PawnStencilImage[] = { X#include "Icons/pawnStencil.icon" X}; Xmpr_static(PawnBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnBlackImage); Xmpr_static(PawnWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnWhiteImage); Xmpr_static(PawnStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnStencilImage); X Xstruct pixrect * PieceStencils[6] = { X &PawnStencilPR, X &KnightStencilPR, X &BishopStencilPR, X &RookStencilPR, X &QueenStencilPR, X &KingStencilPR, X}; X Xstruct pixrect * PieceIcons[6][2] = { X &PawnBlackPR, &PawnWhitePR, X &KnightBlackPR, &KnightWhitePR, X &BishopBlackPR, &BishopWhitePR, X &RookBlackPR, &RookWhitePR, X &QueenBlackPR, &QueenWhitePR, X &KingBlackPR, &KingWhitePR, X}; X X/* X * blank square pixrects X */ Xunsigned short WhiteSquareImage[] = { X#include "Icons/whiteSquare.icon" X}; Xmpr_static(WhiteSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, WhiteSquareImage); X Xunsigned short BlackSquareImage[] = { X#include "Icons/blackSquare.icon" X}; Xmpr_static(BlackSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BlackSquareImage); X X/* board subwindow handles */ Xstruct toolsw * BoardSW; Xstruct gfxsubwindow * Board; X X/* pixrects used for piece animation */ Xstruct pixrect * MoveFromPR, * MoveToPR; X X/* pixrect used for victim drawing */ Xstruct pixrect * VictimPR; X X/* X * board sigwinch handler X */ X/*ARGSUSED*/ XboardSigwinch(sw) X caddr_t sw; X{ X gfxsw_interpretesigwinch(Board); X gfxsw_handlesigwinch(Board); X if (Board->gfx_flags & GFX_RESTART) { X Board->gfx_flags &= ~ GFX_RESTART; X DrawBoard(); X } X} X X/* X * map a mouse coordinate to a board coordinate X */ Xvoid XmapMouseToBoard(mlocp, blocp) X struct pr_pos * mlocp; X BoardCoordinate * blocp; X{ X if (MyColor == WHITE) { X blocp->x = mlocp->x / (SQUARE_WIDTH-1); X blocp->y = mlocp->y / (SQUARE_HEIGHT-1); X } else { X blocp->x = (8 * (SQUARE_WIDTH-1) - mlocp->x)/(SQUARE_WIDTH-1); X blocp->y = (8 * (SQUARE_HEIGHT-1) - mlocp->y)/(SQUARE_HEIGHT-1); X } X} X X/* X * map a board coordinate to a mouse coordinate X */ Xvoid XmapBoardToMouse(blocp, mlocp) X BoardCoordinate * blocp; X struct pr_pos * mlocp; X{ X if (MyColor == WHITE) { X mlocp->x = blocp->x * (SQUARE_WIDTH-1) - 1; X mlocp->y = blocp->y * (SQUARE_HEIGHT-1) - 1; X } else { X mlocp->x = (7 - blocp->x) * (SQUARE_WIDTH-1) - 1; X mlocp->y = (7 - blocp->y) * (SQUARE_WIDTH-1) - 1; X } X} X X/* X * put a piece back where we got it (used to abort piece animation for X * various reasons) X */ Xvoid XputPieceBack(from, stencil, icon) X BoardCoordinate * from; X struct pixrect * stencil, * icon; X{ X struct pr_pos loc; X X mapBoardToMouse(from, &loc); X pw_stencil(Board->gfx_pixwin, X loc.x, loc.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, stencil, 0, 0, icon, 0, 0); X} X X/* X * parameters which belong in boardSelected(), but which are shared X * with RequestUndo() so that we can abort piece animation if the X * klutz at the other end panics (via an undo request or a resignation). X * all of them need to be static, anyway. X */ X XBoardCoordinate from, to; Xstruct pixrect * pieceIcon, * pieceStencil; Xstruct pr_pos lastMouseLoc; X X/* X * abort any mouse activity - this really only means aborting piece X * animation. X */ Xvoid XKillMouseActivity() X{ X switch (Mouse) { X case PROMOTING_PAWN: X UnDoMove(); X break; X case MOVING_PIECE: X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X putPieceBack(&from, pieceStencil, pieceIcon); X break; X } X} X X/* X * relay a request for an undo by the opponent. X * X * this has the side-effect of aborting any mouse activity that the X * user had going at the time. however, setups will not be interrupted X * by undo requests since the (spurious) undo request will be prevented X * when the opponent's program detects that he hasn't moved yet. X */ Xvoid XRequestUndo() X{ X Message("Your opponent wants to undo - left OK, other not OK"); X KillMouseActivity(); X Mouse = CONFIRMING; X confirmState = CONFIRM_UNDO; X} X X/* X * confirm a wish to resign X */ Xvoid XConfirmResignation() X{ X X Message("Sure you want to resign? - left yes, other no"); X Mouse = CONFIRMING; X confirmState = CONFIRM_RESIGNATION; X} X X/* X * board select() handler X */ X/*ARGSUSED*/ XboardSelected(nullsw, ibits, obits, ebits, timer) X caddr_t * nullsw; X int * ibits, * obits, * ebits; X struct timeval ** timer; X{ X static tickCount = 0; X static Move move; X static SetupChange setup; X struct inputevent ie; X struct pr_pos newMouseLoc; X Square * sqp; X long nbytes; X BOOL clamped; X int color, i; X struct pixrect * pr; X X /* X * 1-second ticker. this cannot use SIGALRM, since the RPC X * package already has dibs on that signal for implementing timeouts. X * hence, it uses the subwindow select timeout mechanism. X * the ticker is used to flash the window every 5 seconds if X * there is some event the user hasn't seen yet (opening the X * tool window turns the flasher off). X * X * note - timeouts render the file descriptor masks undefined, X * so we clean up and return w/o checking them. X */ X if ((*timer)->tv_sec == 0L && (*timer)->tv_usec == 0L) { X if (Flashing) { X if (wmgr_iswindowopen(NchessTool->tl_windowfd)) { X tickCount = 0; X Flashing = FALSE; X } else if (tickCount-- <= 0 X && (pr = (struct pixrect *) tool_get_attribute(NchessTool, X WIN_ICON_IMAGE)) != (struct pixrect *) -1) X { X tickCount = 5; X pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, X PIX_NOT(PIX_SRC), pr, 0, 0); X tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0); X pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, X PIX_NOT(PIX_SRC), pr, 0, 0); X tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0); X tool_free_attribute(WIN_ICON_IMAGE, pr); X } X } X /* reset the timer and the file des. masks */ X (*timer)->tv_sec = 1L; X (*timer)->tv_usec = 0L; X * ibits = svc_fds | BoardSWMask X | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE]; X * obits = * ebits = 0; X return; X } X /* X * check for RPC service required X */ X if (*ibits & svc_fds) { X svc_getreq(*ibits & svc_fds); X } X /* X * check for machine move received X * X * note: it is possible in some circumstances to receive a move X * from a machine player after the game is over (for example, X * the opponent of the machine which is on move dies). X * in that event, we simply throw the move away. X */ X for (color = BLACK , i = 0 ; i < 2 ; color = WHITE , i++) { X if ((*ibits & ChessProcessFDs[color]) X && GetMachineMove(&move, color) X && ! GameOver) { X BOOL updateMsgWindow = TRUE; X X Flashing = TRUE; X DoMove(&move, TRUE); X Turn = OTHERCOLOR(Turn); X /* X * if we are trying to save a game of machine vs. machine, X * hold up sending the move until both machine states X * have been saved. when subsequently restoring the X * game, this move will be resubmitted. X */ X if (SaveWanted) { X SaveGame(SaveFileName); X SaveWanted = FALSE; X updateMsgWindow = FALSE; X } X /* X * if the player not moving is a machine, send the move X */ X if (IsMachine[OTHERCOLOR(color)]) { X SendMove(&move, OTHERCOLOR(color)); X /* X * else if the player not moving is a human that wants an X * undo, back it out X */ X } else if (UndoWanted) { X MachineUndo(color); X UndoWanted = FALSE; X Mouse = IDLE; X } X if (updateMsgWindow) X WhoseMoveMessage((char *) 0); X } X } X /* X * check for board subwindow service required X */ X if (*ibits & BoardSWMask) { X if (input_readevent(BoardSW->ts_windowfd, &ie) == -1) { X perror("input failed"); X abort(); X } X switch (Mouse) { X /* X * locked: ignore all mouse activity X */ X case LOCKED: X break; X /* X * we are using the mouse to confirm something X */ X case CONFIRMING: X if (win_inputposevent(&ie) X && ie.ie_code >= BUT_FIRST X && ie.ie_code <= BUT_LAST) { X Mouse = IDLE; X switch(confirmState) { X case CONFIRM_RESIGNATION: X if (ie.ie_code == MS_LEFT) { X SendResignation(PeerColor); X Mouse = LOCKED; /* game is over */ X DoResignation(MyColor); X Message("Resigned"); X } else { X WhoseMoveMessage((char *) 0); X } X break; X case CONFIRM_SETUP_END: X if (ie.ie_code == MS_LEFT) { X /* X * if either player is a machine, white always moves X * first following a setup (another brain-damaged X * attribute of the unix chess program). X */ X if (IsMachine[WHITE] || IsMachine[BLACK]) { X BOOL legalSetup = TRUE; X X Turn = WHITE; X if (IsMachine[BLACK]) { X legalSetup = MachineSetup(BLACK); X } X if (legalSetup && IsMachine[WHITE]) { X if (legalSetup = MachineSetup(WHITE)) X MachineFirst(WHITE); X } X if ( ! legalSetup) { X Message("Illegal setup - try again"); X Mouse = SETUP; X } else { X /* X * if both players are machines, the human part X * is over. X */ X if (IsMachine[BLACK] && IsMachine[WHITE]) X Mouse = LOCKED; X /* X * else we get to play X */ X else X InitialTurn = WHITE; X WhoseMoveMessage((char *) 0); X SetupMode = FALSE; X } X /* X * else the opponent is a human, and we can specify X * who moves first. X */ X } else { X Message("Left button to move first, other to move second"); X Mouse = CONFIRMING; X confirmState = CONFIRM_WHOSE_TURN; X SetupMode = FALSE; X } X } else { X Message("Setup: left - source, middle - delete, right - end"); X Mouse = SETUP; X } X break; X case CONFIRM_WHOSE_TURN: X Turn = InitialTurn = (ie.ie_code == MS_LEFT ? X MyColor : X OTHERCOLOR(MyColor)); X WhoseMoveMessage((char *) 0); X SendEndRestore(); X break; X case CONFIRM_UNDO: X SendUndoAcknowledgement(ie.ie_code == MS_LEFT); X break; X } X } X break; X /* X * we are setting up an initial board layout X */ X case SETUP: X switch (ie.ie_code) { X /* X * generate and pick up a source piece X */ X case MS_LEFT: X if (win_inputposevent(&ie)) { X newMouseLoc.x = ie.ie_locx; X newMouseLoc.y = ie.ie_locy; X mapMouseToBoard(&newMouseLoc, &from); X /* if this a source square */ X if (IsSrcPieceAt(&from)) { X Mouse = MOVING_PIECE; X sqp = GetSrcSquare(from.x, from.y); X setup.type = sqp->type; X setup.color = sqp->color; X /* X * create the first background pixrect, X * centered on the selected board square X */ X mapBoardToMouse(&from, &lastMouseLoc); X /* grab the currently displayed image */ X pw_read(MoveFromPR, X 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, X Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET); X /* repaint the blank square */ X pr_rop(MoveFromPR, X MOVE_X_OFFSET, X MOVE_Y_OFFSET, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X ((from.x + from.y) & 0x01) ? X &BlackSquarePR : X &WhiteSquarePR, X 0, 0); X /* X * remember the pixrect used to paint the piece X * being moved X */ X pieceIcon = PieceIcons[(int) sqp->type][sqp->color]; X pieceStencil = PieceStencils[(int) sqp->type]; X /* X * if there is a piece at the source square, repaint X * the piece on the background pixrect X */ X sqp = GetSquare(from.x, from.y); X if (sqp->type != NULLPC) { X pr_stencil(MoveFromPR, X MOVE_X_OFFSET, X MOVE_Y_OFFSET, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X PieceStencils[(int)sqp->type], 0, 0, X PieceIcons[(int) sqp->type][sqp->color], 0, 0); X } X } X } X break; X /* X * delete a piece X */ X case MS_MIDDLE: X if (win_inputposevent(&ie)) { X newMouseLoc.x = ie.ie_locx; X newMouseLoc.y = ie.ie_locy; X mapMouseToBoard(&newMouseLoc, &from); X setup.x = from.x; X setup.y = from.y; X setup.type = NULLPC; X DoSetupChange(&setup); X SendSetupChange(&setup, PeerColor); X } X break; X /* X * exit setup X */ X case MS_RIGHT: X if (win_inputposevent(&ie)) { X Message("Sure you want to end setup? left - yes, other - no"); X Mouse = CONFIRMING; X confirmState = CONFIRM_SETUP_END; X } X break; X } X break; X /* X * we are promoting a pawn X */ X case PROMOTING_PAWN: X switch(ie.ie_code) { X /* X * select the next pawn morph X */ X case MS_LEFT: X if (win_inputposevent(&ie)) X move.newPieceType = PromotePawn(&to); X break; X /* X * go for the current morph X */ X case MS_MIDDLE: X if (win_inputposevent(&ie) && SendMove(&move, PeerColor)) { X Turn = OTHERCOLOR(Turn); X WhoseMoveMessage((char *) 0); X Mouse = IDLE; X } X break; X /* X * we exited the window - back out the pawn move X */ X case LOC_WINEXIT: X UnDoMove(); X Mouse = IDLE; X break; X } X break; X /* X * we aren't currently doing anything X */ X case IDLE: X switch(ie.ie_code) { X case MS_LEFT: X /* X * if this a left button press X * and it is our turn X * and we aren't waiting for an undo acknowledgement X */ X if (win_inputposevent(&ie) && Turn == MyColor && ! UndoWanted) { X newMouseLoc.x = ie.ie_locx; X newMouseLoc.y = ie.ie_locy; X mapMouseToBoard(&newMouseLoc, &from); X /* X * if there is one of our pieces on this square X */ X if (IsOurPieceAt(&from)) { X Mouse = MOVING_PIECE; X sqp = GetSquare(from.x, from.y); X /* X * create the first background pixrect, X * centered on the selected board square X */ X mapBoardToMouse(&from, &lastMouseLoc); X /* grab the currently displayed image */ X pw_read(MoveFromPR, X 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, X Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET); X /* repaint the blank square */ X pr_rop(MoveFromPR, X MOVE_X_OFFSET, X MOVE_Y_OFFSET, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X ((from.x + from.y) & 0x01) ? X &BlackSquarePR : X &WhiteSquarePR, X 0, 0); X /* X * remember the pixrect used to paint the piece X * being moved X */ X pieceIcon = PieceIcons[(int) sqp->type][sqp->color]; X pieceStencil = PieceStencils[(int) sqp->type]; X /* X * a bit un-structured, but we are forced to do this X * if we want the piece to "jump" to the cursor when X * the button is initially depressed ("forced" in the X * sense that we are inside a switch statement). X */ X goto moveIt; X } X } X break; X } X break; X /* X * we are animating a piece X */ X case MOVING_PIECE: X switch(ie.ie_code) { X case MS_LEFT: X /* X * if we are putting down a piece X */ X if (win_inputnegevent(&ie)) { X BOOL legal; X X Mouse = IDLE; X newMouseLoc.x = ie.ie_locx; X newMouseLoc.y = ie.ie_locy; X mapMouseToBoard(&newMouseLoc, &to); X legal = TRUE; X move.x1 = from.x; move.y1 = from.y; X move.x2 = to.x; move.y2 = to.y; X if (SetupMode) { X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X /* put the piece down no matter what */ X setup.x = to.x; X setup.y = to.y; X DoSetupChange(&setup); X SendSetupChange(&setup, PeerColor); X Mouse = SETUP; X } else if ( ! (from.x == to.x && from.y == to.y) X && Turn == MyColor X && (legal = IsMoveLegal(&from, &to))) { X /* if this is a pawn promotion */ X if (GetSquare(from.x, from.y)->type == PAWN X && (to.y == 0 || to.y == 7)) { X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X move.newPieceType = (int) QUEEN; X DoMove(&move, TRUE); X /* if we are playing the brain-damaged unix chess X * program, can only promote to queens */ X if (IsMachine[OTHERCOLOR(MyColor)]) { X Turn = OTHERCOLOR(Turn); X SendMove(&move, PeerColor); X WhoseMoveMessage((char *) 0); X /* else need to have the user select the promoted X * piece type */ X } else { X Message("Left button: select piece type, middle: send move."); X Mouse = PROMOTING_PAWN; X } X /* else if we can send the move */ X } else if (SendMove(&move, PeerColor)) { X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X DoMove(&move, TRUE); X Turn = OTHERCOLOR(Turn); X WhoseMoveMessage((char *) 0); X /* else the peer is dead */ X } else { X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X putPieceBack(&from, pieceStencil, pieceIcon); X } X } else { X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X putPieceBack(&from, pieceStencil, pieceIcon); X if ( ! legal) X Message("Illegal move"); X } X } X break; X /* X * exited the window - clean it up. X */ X case LOC_WINEXIT: X /* repaint the background */ X pw_rop(Board->gfx_pixwin, X lastMouseLoc.x - MOVE_X_OFFSET, X lastMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X if (SetupMode) { X Mouse = SETUP; X } else { X putPieceBack(&from, pieceStencil, pieceIcon); X Mouse = IDLE; X } X break; X /* X * animate the piece X */ X case LOC_MOVEWHILEBUTDOWN: X moveIt: X /* ignore old motion events */ X ioctl(Board->gfx_windowfd, (int) FIONREAD, (char *) &nbytes); X if (nbytes/sizeof(struct inputevent) <= 3) { X do { X newMouseLoc.x = ie.ie_locx - SQUARE_WIDTH/2; X newMouseLoc.y = ie.ie_locy - SQUARE_HEIGHT/2; X clamped = FALSE; X /* X * clamp motion if necessary X */ X if (newMouseLoc.x - lastMouseLoc.x >= MOVE_X_OFFSET) { X newMouseLoc.x = lastMouseLoc.x + (MOVE_X_OFFSET-1); X clamped = TRUE; X } else if (newMouseLoc.x - lastMouseLoc.x <= - MOVE_X_OFFSET) { X newMouseLoc.x = lastMouseLoc.x - (MOVE_X_OFFSET-1); X clamped = TRUE; X } X if (newMouseLoc.y - lastMouseLoc.y >= MOVE_Y_OFFSET) { X newMouseLoc.y = lastMouseLoc.y + (MOVE_Y_OFFSET-1); X clamped = TRUE; X } else if (newMouseLoc.y - lastMouseLoc.y <= - MOVE_Y_OFFSET) { X newMouseLoc.y = lastMouseLoc.y - (MOVE_Y_OFFSET-1); X clamped = TRUE; X } X /* grab the new area */ X pw_read(MoveToPR, X 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, Board->gfx_pixwin, X newMouseLoc.x - MOVE_X_OFFSET, X newMouseLoc.y - MOVE_Y_OFFSET); X /* paste the old background over the new area */ X pr_rop(MoveToPR, X lastMouseLoc.x - newMouseLoc.x, X lastMouseLoc.y - newMouseLoc.y, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveFromPR, 0, 0); X /* save the new background */ X pr_rop(MoveFromPR, 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveToPR, 0, 0); X /* paint the piece on the new area */ X pr_stencil(MoveToPR, X MOVE_X_OFFSET, MOVE_Y_OFFSET, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, pieceStencil, 0, 0, pieceIcon, 0, 0); X /* now paint the new area on the screen */ X pw_rop(Board->gfx_pixwin, X newMouseLoc.x - MOVE_X_OFFSET, X newMouseLoc.y - MOVE_Y_OFFSET, X MOVE_PR_WIDTH, MOVE_PR_HEIGHT, X PIX_SRC, MoveToPR, 0, 0); X lastMouseLoc.x = newMouseLoc.x; X lastMouseLoc.y = newMouseLoc.y; X } while (clamped); X } X break; X } X break; X } X } X * ibits = svc_fds | BoardSWMask | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE]; X * obits = * ebits = 0; X} X X/* X * initialize the board subwindow X */ Xvoid XInitBoardSW(useRetained, iconDirectory) X BOOL useRetained; /* use a retained pixrect */ X char * iconDirectory; /* custom piece icon directory */ X{ X static struct timeval tickValue; X struct inputmask mask; X register unsigned int i; X int height = (SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10; X X /* X * initialize the subwindow X */ X if ((BoardSW = gfxsw_createtoolsubwindow(NchessTool, "", X TOOL_SWEXTENDTOEDGE, X /* playing surface + victim area */ X/* (SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10, */ X height, X NULL)) == NULL) X { X fprintf(stderr, "Can't create board subwindow\n"); X exit(1); X } X Board = (struct gfxsubwindow *) BoardSW->ts_data; X if (useRetained) X gfxsw_getretained(Board); X BoardSW->ts_io.tio_handlesigwinch = boardSigwinch; X BoardSW->ts_io.tio_selected = boardSelected; X input_imnull(&mask); X win_setinputcodebit(&mask, MS_LEFT); X win_setinputcodebit(&mask, MS_MIDDLE); X win_setinputcodebit(&mask, MS_RIGHT); X win_setinputcodebit(&mask, LOC_MOVEWHILEBUTDOWN); X win_setinputcodebit(&mask, LOC_WINEXIT); X mask.im_flags |= IM_NEGEVENT; X win_setinputmask(BoardSW->ts_windowfd, &mask, NULL, WIN_NULLLINK); X /* X * add the RPC service and chess process file descriptor select X * masks to this subwindow. X */ X BoardSW->ts_io.tio_inputmask = svc_fds X | (BoardSWMask = 1 << BoardSW->ts_windowfd) X | ChessProcessFDs[BLACK] X | ChessProcessFDs[WHITE] ; X /* X * set the timeout to 1 second X */ X tickValue.tv_sec = 1L; X tickValue.tv_usec = 0L; X BoardSW->ts_io.tio_timer = &tickValue; X /* X * create the white pieces by inverting the black pieces, using custom X * pieces where appropriate. X */ X for ( i = 0 ; i < 6 ; i++ ) { X if (iconDirectory != (char *) 0) { X FILE * iconFile, * stencilFile; X icon_header_object iconHeader, stencilHeader; X char fileName[512], errorMsg[IL_ERRORMSG_SIZE + 2]; X X strcpy(fileName, iconDirectory); X strcat(fileName, "/"); X strcat(fileName, PieceIconFileNames[i]); X if ((iconFile = icon_open_header(fileName, errorMsg, X &iconHeader)) != (FILE *) 0) X { X if (iconHeader.width != SQUARE_WIDTH X || iconHeader.height != SQUARE_HEIGHT X || iconHeader.depth != 1) { X fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName); X } else { X strcpy(fileName, iconDirectory); X strcat(fileName, "/"); X strcat(fileName, PieceStencilFileNames[i]); X if ((stencilFile = icon_open_header(fileName, errorMsg, X &stencilHeader)) != (FILE *) 0) X { X if (stencilHeader.width != SQUARE_WIDTH X || stencilHeader.height != SQUARE_HEIGHT X || stencilHeader.depth != 1) { X fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName); X } else { X icon_read_pr(iconFile, &iconHeader, PieceIcons[i][BLACK]); X icon_read_pr(stencilFile, &stencilHeader, PieceStencils[i]); X } X fclose(stencilFile); X } X } X fclose(iconFile); X } X } X pr_rop(PieceIcons[i][WHITE], 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_NOT(PIX_SRC), X PieceIcons[i][BLACK], 0, 0); X } X /* X * create the pixrects used for piece animation and victim drawing X */ X if ((MoveFromPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0 X || (MoveToPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0 X || (VictimPR = mem_create(SQUARE_WIDTH, SQUARE_HEIGHT, 1)) == (struct pixrect *) 0) { X fprintf(stderr, "can't create the animation pixrects\n"); X exit(1); X } X} X X/* X * draw a square, including the piece (if any) X */ Xvoid XDrawSquare(x, y, sqp) X int x, y; X register Square * sqp; X{ X BoardCoordinate bloc; X struct pr_pos mloc; X X bloc.x = x; bloc.y = y; X mapBoardToMouse(&bloc, &mloc); X /* paint the blank square */ X pw_rop(Board->gfx_pixwin, X mloc.x, mloc.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X (((x + y) & 0x01) ? &BlackSquarePR : &WhiteSquarePR), X 0, 0); X /* paint the piece, if there is one */ X if (sqp->type != NULLPC) { X pw_stencil(Board->gfx_pixwin, mloc.x, mloc.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X PieceStencils[(int) sqp->type], 0, 0, X PieceIcons[(int) sqp->type][sqp->color], 0, 0); X } X} X X/* X * draw the playing surface and victim area X */ Xvoid XDrawBoard() X{ X register int x, y; X register Square * sqp; X X /* clear the board area */ X pw_rop(Board->gfx_pixwin, X 0, 0, Board->gfx_rect.r_width, Board->gfx_rect.r_height, X PIX_CLR, (struct pixrect *) 0, 0, 0); X /* draw the playing area */ X for (x = 0 ; x < 8 ; x++) { X for (y = 0 ; y < 8 ; y++) { X sqp = GetSquare(x, y); X DrawSquare(x, y, sqp); X } X } X /* draw the victims */ X drawVictims(); X} X X/* X * your basic victim X */ Xtypedef struct { X PieceType type; /* victim type */ X BOOL active; /* victim slot state */ X int count; /* victim overflow count */ X} Victim; X X#define NUM_VICTIM_SLOTS 8 X XVictim victims[2 /* pawns(1) vs. pieces(0) */][2 /* color */ ][NUM_VICTIM_SLOTS /* victim slot # */]; X X/* X * map the victim specified by the triple X * [ isPawn { 0, 1 }, color { BLACK, WHITE }, slot { 0 .. NUM_VICTIM_SLOTS-1 } ] X * to a mouse coordinate (relative to the board subwindow) X */ Xvoid XmapVictimToMouse(isPawn, color, slot, mlocp) X BOOL isPawn; X int color, slot; X struct pr_pos * mlocp; X{ X mlocp->x = slot * (3 * SQUARE_WIDTH/4) + color * (3 * SQUARE_WIDTH/8); X mlocp->y = 8 * (SQUARE_HEIGHT-1) + isPawn * (SQUARE_HEIGHT/2) + color * (SQUARE_HEIGHT/4) + 5; X} X X/* X * draw the victims X */ XdrawVictims() X{ X register int i, j, k; X register Victim * victim; X struct pr_pos victimOrigin; X X for (i = 0 ; i < 2 ; i++) { X for (j = 0 ; j < 2 ; j++) { X for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) { X victim = &victims[i][j][k]; X if (victim->active) { X mapVictimToMouse(i, j, k, &victimOrigin); X pw_stencil(Board->gfx_pixwin, X victimOrigin.x, victimOrigin.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X PieceStencils[(int) victim->type], 0, 0, X PieceIcons[(int) victim->type][j], 0, 0); X } X } X } X } X} X X/* X * add a piece to the set of victims X */ Xvoid XAddVictim(type, color, drawIt) X PieceType type; X int color; X BOOL drawIt; X{ X register int i, j, k; X int empty, lastMatch, extras; X register Victim * victim; X BOOL isPawn = (type == PAWN); X struct pr_pos victimOrigin, othersOrigin; X X /* X * look for the first empty slot and the last slot which X * contains a piece of the same type X */ X for (lastMatch = empty = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) { X victim = &victims[isPawn][color][i]; X if (empty < 0 && ! victim->active) X empty = i; X if (victim->active && victim->type == type) X lastMatch = i; X } X /* X * if there were no empty slots X */ X if (empty == -1) { X /* X * if there was one or more pieces of the same type, update X * the last instance's overflow count (includes coalescing X * all overflows of that type at the last instance) X */ X if (lastMatch >= 0) { X for (extras = i = 0 ; i < lastMatch ; i++) { X victim = &victims[isPawn][color][i]; X if (victim->active && victim->type == type && victim->count > 1) { X extras += victim->count - 1; X victim->count = 1; X } X } X victims[isPawn][color][lastMatch].count += extras; X } X /* X * else install the victim in the empty slot X */ X } else { X victim = &victims[isPawn][color][empty]; X victim->type = type; X victim->active = TRUE; X victim->count = 1; X if (drawIt) { X /* X * re-draw all the pieces in the victim's slot X */ X mapVictimToMouse(isPawn, color, empty, &victimOrigin); X pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_CLR, (struct pixrect *) 0, 0, 0); X for (i = 0 ; i < 2 ; i++) { X for (j = 0 ; j < 2 ; j++) { X for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) { X victim = &victims[i][j][k]; X if (victim->active) { X mapVictimToMouse(i, j, k, &othersOrigin); X pr_stencil(VictimPR, X othersOrigin.x - victimOrigin.x, X othersOrigin.y - victimOrigin.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X PieceStencils[(int) victim->type], 0, 0, X PieceIcons[(int) victim->type][j], 0, 0); X } X } X } X } X /* X * now re-draw the slot X */ X pw_rop(Board->gfx_pixwin, X victimOrigin.x, victimOrigin.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, VictimPR, 0, 0); X } X } X} X X/* X * reincarnate a victim (via an undo) X */ Xvoid XDeleteVictim(type, color) X PieceType type; X int color; X{ X register int i, j, k; X int lastMatch, extras; X register Victim * victim; X BOOL isPawn = (type == PAWN); X struct pr_pos victimOrigin, othersOrigin; X X /* X * look for the last slot which contains a piece of this type X */ X for (lastMatch = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) { X victim = &victims[isPawn][color][i]; X if (victim->active && victim->type == type) X lastMatch = i; X } X /* X * if there were no matches, don't do anything X */ X if (lastMatch == -1) { X /* do nothing */ X /* X * else if the last match slot contains overflows, simply X * decrement the overflow count X */ X } else if ((victim = &victims[isPawn][color][lastMatch])->count > 1) { X victim->count--; X /* X * else zero out the slot and re-draw it as empty X */ X } else { X victim->active = FALSE; X /* X * re-draw all the remaining pieces in the victim's slot X */ X mapVictimToMouse(isPawn, color, lastMatch, &victimOrigin); X pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_CLR, (struct pixrect *) 0, 0, 0); X for (i = 0 ; i < 2 ; i++) { X for (j = 0 ; j < 2 ; j++) { X for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) { X victim = &victims[i][j][k]; X if (victim->active) { X mapVictimToMouse(i, j, k, &othersOrigin); X pr_stencil(VictimPR, X othersOrigin.x - victimOrigin.x, X othersOrigin.y - victimOrigin.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, X PieceStencils[(int) victim->type], 0, 0, X PieceIcons[(int) victim->type][j], 0, 0); X } X } X } X } X /* X * now re-draw the slot X */ X pw_rop(Board->gfx_pixwin, X victimOrigin.x, victimOrigin.y, X SQUARE_WIDTH, SQUARE_HEIGHT, X PIX_SRC, VictimPR, 0, 0); X } X} X END_OF_boardsw.c if test 34145 -ne `wc -c nchess.6 <<'END_OF_nchess.6' X.TH NCHESS 6 "14 October 1986" X.SH NAME Xnchess \- general-purpose chess tool X.SH SYNOPSIS X.B nchess X[\fB-c\fP] X.br X [\fB-e\fP | \fB-r\fP\fIrestoreFileName\fP] X.br X [\fB-s\fP\fIsaveFileName\fP] X.br X [\fB-d\fP\fIpieceIconDirectory\fP] X.br X [\fB-t\fP\fItranscriptFileName\fP] X.br X [\fB-x\fP\fIn\fP] X.br X X \fB-m\fP\fIchessProgram\fP [\fB-m\fP\fIarg\fP ...] | \fB-n\fP\fIchessProgram\fP [\fB-n\fP\fIarg\fP ...] X.br X or X.br X \fB-m\fP\fIchessProgram\fP [\fB-m\fP\fIarg\fP ...] \fB-n\fP\fIchessProgram\fP [\fB-n\fP\fIarg\fP ...] X.br X or X.br X [\fB-b | -w\fP] \fIuser@host\fP X.SH DESCRIPTION X.I Nchess Xis a general-purpose chess interface suitable for playing any Xcombination of { human, machine } vs. { human, machine }. XSome of the more notable features of nchess Xare facilities for generating transcript files Xin various formats, Xfor saving and restoring games, Xand for setting up initial board situations. X.SH "INVOCATION" X.LP XNchess accepts numerous command line arguments: X.IP "\fB-c\fP" Xdon't keep an internal copy of the board's display image X(the default is to keep one) X.IP "\fB-b\fP" Xask to play the black pieces X.IP "\fB-w\fP" Xask to play the white pieces X.IP "\fB-e\fI" Xedit the board (i.e., set up a non-standard board configuration) Xprior to starting the game X.IP "\fB-r\fP \fIrestoreFileName\fP" Xresume a game from the game saved in \fIrestoreFileName\fP X.IP "\fB-s\fP \fIsaveFileName\fP" Xuse \fIsaveFileName\fP for saving games instead of the Xdefault \fB"nchess.save"\fP. X.IP "\fB-t\fP \fItranscriptFileName\fP" Xuse \fItranscriptFileName\fP for writing transcripts instead of the Xdefault \fB"nchess.transcript"\fP. X.IP "\fB-x\fP\fIn\fP" Xgenerate transcripts in format \fIn\fP, where \fIn\fP is one of: X.nf X X 0 formal; e.g., P/K2-K4, R/KB3xP X 1 minimal; e.g., P-K4, RxP (the default) X 2 algebraic; e.g., E2E4, F3F5 X.fi X.IP "\fB-d\fP \fIpieceIconDirectory\fP" Xlook in \fIpieceIconDirectory\fP for piece icons to use instead Xof the default ones supplied by \fInchess\fP. X.IP "\fB-m\fP \fIchessProgram\fP" Xstart up \fIchessProgram\fP playing the white pieces X.IP "\fB-n\fP \fIchessProgram\fP" Xstart up \fIchessProgram\fP playing the black pieces X.LP XSpecifying \fB-c\fP forces \fInchess\fP to recompute the board Ximage whenever any portion of it must be made visible again. XHowever, the memory savings it entails may justify its use Xfor systems which are running a bit short. X.LP XThe \fB-m\fP and \fB-n\fP arguments are cumulative. XThe first of either is interpreted as the name of an Xexecutable chess program; Xsubsequent instances are collected and passed as arguments Xto the chess program. X.SH "HUMAN VS. HUMAN" X.LP XHuman vs. human games are started by Xinvoking \fInchess\fP with an argument of Xthe form "\fIuser@host\fP" and Xneither of the \fB-m\fP, \fB-n\fP arguments. XBoth the local machine and the host specified by the \fIhost\fP Xargument must be running \fInchessd(6)\fP (hereafter referred Xto as the daemon). X\fINchess\fP first checks the local daemon for an invitation Xregistered by \fIuser\fP at \fIhost\fP. XIf none is found, \fInchess\fP sends an invitation to the Xdaemon running on \fIhost\fP, Xprints something like "Waiting for \fIuser\fP to respond...", Xthen idles waiting for a message from a Xresponding \fInchess\fP process. XIn turn, the remote daemon prints something useful Xand sufficiently distracting on the remote console. X.LP XIf an invitation was found waiting in the local daemon, X\fInchess\fP sends a response message to the Xpeer \fInchess\fP process. XSubsequent communication is peer-to-peer; Xi.e., the daemon is no longer consulted for anything. XIf either user specified \fB-b\fP or \fB-w\fP, Xthe two peers attempt to satisfy color preferences; Xif both users requested the same color, Xa single round of arbitration results. XIf this arbitration fails, Xboth \fInchess\fP processes exit, Xawaiting a less contentious session. X.SH "HUMAN VS. MACHINE" X.LP XHuman vs. machine games are started by Xinvoking \fInchess\fP with one of Xthe \fI-m\fP or \fI-n\fP arguments. XThe existence of the daemon is not required. XThe chess program used is expected to conform to Xthe interface protocol described below. X.SH "MACHINE VS. MACHINE" X.LP XMachine vs. machine games are started by Xinvoking \fInchess\fP with both of Xthe \fI-m\fP and \fI-n\fP arguments. XThe existence of the daemon is not required. XBoth chess programs used are expected to conform to Xthe interface protocol described below. X.SH "MESSAGE SUBWINDOW" X.LP XDuring the course of the game, Xvarious messages appear in a one-line subwindow Xdirectly over the button panel. XThese messages may direct you to press certain buttons Xto confirm various actions (e.g., resigning, allowing an undo). XThe cursor must be positioned in the board subwindow Xwhen buttons are pressed in response to a message. X.SH "NORMAL PLAY" X.LP XIn normal play, pieces are moved by positioning the cursor Xover the square a piece resides on, Xdepressing the left mouse button, Xmoving the piece to the desired square, Xand releasing the button. XAfter moving, activity in the board subwindow is Xsuppressed until a move is made by the opponent. X.LP XCertain move types deserve special mention. XEn passant pawn captures are performed by moving the Xcapturing pawn to the third rank square; Xi.e., the square it will reside on after the move. X\fINchess\fP enforces the restriction that en passant Xcapturing rights expire after the move immediately Xfollowing the pawn advance. XCastling to either side is performed by moving the Xking two squares in the appropriate direction. XFinally, pawn "queening" is a three-step process: XFirst, the pawn is moved to the eighth rank, where Xit is initially polymorphed to a queen. XSecond, the pawn is rotated through the set of legal Xpawn morphs for every left mouse button click (the legal Xpawn morphs are queen, bishop, knight, and rook). XFinally, the move is sent to the opponent when the middle Xbutton is pressed. X(Note: If the opponent is a machine, Xpawns may only be promoted to queens Xand step two is skipped.) X.LP XAt any time during the game, Xclicking the left mouse button on one of Xseveral buttons above the board subwindow Xwill have the following effects: X.IP "(Resign)" XResignation is final; and after confirming Xthat you really wanted to resign, Xit's curtains... X.IP "(Undo)" XAgainst a human opponent, this button causes \fInchess\fP Xto forward a request to your opponent to undo your last move. XAssuming your opponent agrees, your last move will no longer Xhaunt you and it will be your turn again. XIf it was your turn to play, Xyour opponent's last move will be undone as well. XAgainst a machine opponent, Xacquiescence is implicit. XUndos can be repeated, Xconstrained only by reaching the initial game state and Xthe limits of your opponent's compassion. X.IP "(Last Play)" XThe last play made by either side will be illustrated by Xbriefly undoing the move in the board subwindow. X.IP "(Transcript)" XA transcript of the game is written to the transcript file Xspecified on the command line; if no file was specified, X"nchess.transcript" is used. X.IP "(Save)" XThe game state (including the move history) is saved in the Xfile specified on the command line; if no file was specified, X"nchess.save" is used. X.LP XWhen both players are machines, the "Resign" and "Undo" Xbuttons are omitted from the window. X.SH "EDITING THE BOARD" X.LP XWhen \fInchess\fP is invoked with the \fB-e\fP flag, Xthe tool starts up as a board editor with initial play suspended. XPieces are deleted by positioning the cursor over the Xsquare the piece resides on and clicking the middle button. XPieces are created by positioning the mouse over the square Xa piece of the desired type and color would reside on at the Xbeginning of a normal game, Xdepressing the left button, Xmoving the piece to the destination square, Xand releasing the button. XWhen editing is complete and play is about to commence, Xthe right button is clicked, Xfollowed by another mouse click Xto confirm exiting the board editor (once the board editor is Xexited, it cannot be re-entered). XIf this is a human vs. human game, the user is then asked Xto specify which side moves first (via yet another mouse click). XFollowing that, the game is afoot. X.LP XBoards can be edited for any of the { human, machine } combinations Xdescribed above. XAfter setting up a board for a machine vs. machine session, Xthe human is reduced to being a mere spectator. XAlso, in any combination involving machines the white pieces Xalways move first, due to a deficiency in the existing XUnix chess program. X.SH "BADGERING YOUR OPPONENT" X.LP X\fINchess\fP provides a one-line talk subwindow in human vs. human Xgames for sending pithy messages to your opponent. XTo send a message, position the cursor to the right of the "\fBSend:\fP" Xfield, type the message, and hit carriage return. XThe message will not be sent until you've typed the carriage return, Xallowing you to use the normal Unix line editing features. X.SH "CREATING YOUR OWN PIECES" X.LP X\fINchess\fP allows you to supplant any or all of the pictorial piece Xrepresentations (icons) with pieces of your own design. XA few guidelines concerning icon creation are outlined below. XFor starters, you might want to take a look at the Xicons used by \fInchess\fP. X.LP XAll icons used by \fInchess\fP are drawn using \fIicontool(1)\fP, Xand are (\fIicontool\fP's default) 64 pixels wide, 64 pixels high, Xand 1 pixel deep. XOnly the black pieces are drawn - the white pieces are created Xby \fInchess\fP by inverting the black pieces. XYou must leave a blank border of at Xleast three pixels around the piece, Xand the piece should be centered in \fIicontool's\fP drawing subwindow. X.LP XAfter you have drawn the piece, you need to create the Xcorresponding "stencil" image. XThe stencil is used by \fInchess\fP to limit drawing the piece on Xthe board to only those areas within the 64 by 64 square that Xactually represent the piece image. XAlso, in order to create the white pieces correctly, the stencil Xborder needs to be grown one pixel past the piece image border - this Xgives the white piece a thin black line border. XAn easy way to create the stencil is to use the Xfollowing procedure: X.IP XLoad the piece image X.IP XSelect "Fill: black" and "Fill: xor" modes. X.IP XFill the entire drawing subwindow. You should now have a reverse Xvideo image of the piece. X.IP XSelect "Fill: white" and "Fill: src" modes, and get rid of all Xblack pixels except for a one-line border Xaround the white piece image. X.IP XSelect "Fill: black" mode and fill inside the border. X.LP XYou should now have a solid black image of the piece which is one Xpixel bigger in all directions than the image you drew earlier. XStore this image as the stencil. XIf you want any parts of the piece to be transparent X(e.g., windows in the rook), Xsimply leave those parts white in the stencil. X.LP XThe file names to use for the various pieces and stencils are listed Xbelow in the "FILES" section. XBoth the piece and stencil images must be accessible in the Xspecified icon directory before \fInchess\fP will attempt to use them. X.LP XIn case you are concerned, Xusing your own pieces does not cause \fInchess\fP to use any more Xmemory than it already does. X.SH "CHESS PROGRAM INTERFACE PROTOCOL" X.LP XThe protocol for communication with chess programs is derived Xin bottom-up fashion from the syntax and semantics Xused by the existing Unix chess program. XWhen the program starts up, Xit is expected to make a short one-line announcement (e.g., "Chess"), Xwhich \fInchess\fP simply throws away. XNext, \fInchess\fP sends a single line consisting of the keyword "alg", Xwhich informs the chess program that algebraic notation is desired. X.LP XIf \fInchess\fP was invoked with the \fB-e\fP flag, Xthe next communication with the chess program is to deliver the Xsetup information. XThis is done by sending a single line consisting Xof the keyword "setup", Xfollowed by eight lines of eight characters each. XEach character represents the piece on a square, Xwith space characters representing empty squares. XFor the white pieces, Xthe characters used are { p, n, b, r, q, k } for Xpawn, knight, bishop, rook, queen, and king, respectively. XBlack pieces are represented by the upper case equivalent. XLines are transmitted starting with the eighth rank (i.e., a8-h8). XAfter receiving the eighth setup line (a1-h1), Xthe chess program is expected to respond with a single line Xconsisting of either the key Xphrase "Setup successful" or "Illegal setup". X.LP XNext, the chess program will be sent either the first move by Xits opponent (which will be white), Xor a single line consisting of the keyword "first", Xwhich it should interpret to mean that it should play the white pieces Xand that it should make the first move. XMoves sent to the chess program will always be in the format Ximplied by the printf string "%c%d%c%d\\n", Xwhere the character specifications describe the file [a-h] Xand the decimal digit specifications describe the rank [1-8]. XEn passant captures are encoded as a horizontal move; e.g., d5e5. XCastling moves are encoded as the king move; e.g., e1g1. XPawns implicitly turn into queens; thus, d7d8 implies P-Q8(Q). X.LP XAfter receiving a move, the chess program must echo a single line. XThe standard Unix chess program uses this line to re-format and Xecho the move it received; Xhowever, \fInchess\fP does not interpret the echo and thus Xplaces no restrictions on its format. X X.SH "FILES" X.br Xbishop.icon X.br XbishopStencil.icon X.br Xking.icon X.br XkingStencil.icon X.br Xknight.icon X.br XknightStencil.icon X.br Xpawn.icon X.br XpawnStencil.icon X.br Xqueen.icon X.br XqueenStencil.icon X.br Xrook.icon X.br XrookStencil.icon X.br X.SH "SEE ALSO" Xchess(6), chesstool(6), nchessd(6) X.SH "BUGS" X.LP XThere aren't any clocks (yet). X.LP XThe board editor cannot be re-entered. This is intentional, in order Xto keep the middle of the game orderly. However, some of the author's Xcohorts think it is a bad idea. X.LP XTranscript files do not show the initial board state, which makes Xit necessary to separately document non-standard initial positions. XThis will be corrected in a future release. X.LP XThe algebraic notation used for transcripts is not really what you Xmight expect. X.LP XWhen either player is a machine, pawns may be promoted only to queens. XThis bug is inherited from the existing Unix chess program. X.LP XWhen either player is a machine, the white pieces must always make Xthe first move in a game. XThis is another bug inherited from the existing Unix chess program. X.LP XSaved games must be re-started with the same configuration of human Xand machine players. XThis bug is due to the unknown format of the existing Unix chess Xprogram's save files and to the lack of any known standard for Xthe format of such files. X.LP XThe talk subwindow has no facility for queueing lines for paced Xreview by the receiver (as in games like rogue and hack). XAlso, there is no way to shut the door if the opponent's messages Xare a source of irritation instead of amusement or enlightenment. X.LP XThe rules for offering, accepting, and detecting draws are not Ximplemented. These apparently have ramifications with regard to Xhandling clocks, and are the principal reason clocks are not Ximplemented yet. All this will be implemented in a future release. XFor now, the talk subwindow is expected to suffice. X.LP XCheckmate and stalemate are not explicitly detected Xby \fInchess\fP, since it doesn't have an internal Xlegal move generator. XAgain, the talk subwindow is expected to suffice. X.LP XThere is currently no way to play Blitz chess rules (e.g., don't Xannounce check, not forced to alleviate check, etc.). X.LP XMultiple kings are allowed in human vs. human setups; however, Xonly the first one found is examined for being in check. X.LP XIn order to guarantee the ability to write background Xchess process save files, X\fInchess\fP changes its working directory to /tmp following Xthe call to fork() and before the call to execvp(). XThus, chess programs in the former working directory Xwhich were executable only via the '.' Xcomponent in the PATH environment variable Xwill not be found by execvp(). XThis bug can be traced to the Unix chess program, which (apparently) Xdoes not allow one to specify an alternative file name for its Xsave file. X END_OF_nchess.6 if test 16236 -ne `wc -c nchess.h <<'END_OF_nchess.h' X/* X * Copyright 1987 Tom Anderson; 20831 Frank Waters Road; X * Stanwood, WA 98282. All rights reserved. X */ X X/* X * network chess header X */ X X#define BOOL int X#ifndef TRUE X#define TRUE 1 X#endif X#ifndef FALSE X#define FALSE 0 X#endif X X#define SERVERPROGNUM ((u_long) 0x31233216) /* RPC daemon program number */ X#define VERSNUM ((u_long) 1) /* RPC version number */ X X/* X * daemon procedure numbers X */ X#define REQPROCNUM ((u_long) 1) /* game request */ X#define ACKPROCNUM ((u_long) 2) /* game acknowledge */ X#define CANCELPROCNUM ((u_long) 3) /* cancel a game request */ X X/* X * peer procedure numbers X */ X#define ACCEPTPROCNUM ((u_long) 1) /* game accepted */ X#define MOVEPROCNUM ((u_long) 2) /* chess move */ X#define COLORFAILPROCNUM ((u_long) 3) /* color arbitration failure */ X#define UNDOPROCNUM ((u_long) 4) /* undo request */ X#define RESIGNPROCNUM ((u_long) 5) /* resignation */ X#define UNDOACKPROCNUM ((u_long) 6) /* undo request response */ X#define MSGPROCNUM ((u_long) 7) /* one-liner antagonism */ X#define RESTOREMOVEPROCNUM ((u_long) 8) /* move during game restoration */ X#define ENDRESTOREPROCNUM ((u_long) 9) /* restoration/setup complete */ X#define SETUPPROCNUM ((u_long) 10) /* board setup change */ X#define GOODBYEPROCNUM ((u_long) 11) /* player killed his tool */ X X/* X * color arbitration values - the values are important, as the X * game can proceed when the sum of the two players' colors is X * WANTSWHITE+WANTSBLACK; also, the values are used as indices X * into arrays. X */ X#define WANTSBLACK 0 /* user wants black */ X#define BLACK WANTSBLACK /* user has black */ X#define WANTSWHITE 1 /* user wants white */ X#define WHITE WANTSWHITE /* user has white */ X#define EITHERCOLOR 2 /* user doesn't care */ X X#define OTHERCOLOR(a) ((a) == WHITE ? BLACK : WHITE) X/* X * rendezvous information X */ Xtypedef struct { X unsigned long progNum; /* RPC program number of requester */ X int color; /* requested color */ X int resumeGame; /* boolean: wants to resume a game */ X char hostName[256]; /* host name of requester */ X char userName[256]; /* name of requesting user */ X} GameRequest; X X/* X * piece move information X */ Xtypedef struct { X int x1; /* origin square */ X int y1; X int x2; /* destination square */ X int y2; X int newPieceType; /* new piece type for 8th rank pawns */ X} Move; X X/* X * board square dimensions X */ X#define SQUARE_WIDTH 64 X#define SQUARE_HEIGHT SQUARE_WIDTH X X/* X * board coordinates X */ Xtypedef struct { X int x; X int y; X} BoardCoordinate; X X/* X * piece types X */ Xtypedef enum { X PAWN = 0, X KNIGHT, X BISHOP, X ROOK, X QUEEN, X KING, X NULLPC, X} PieceType; X X/* X * square state X */ Xtypedef struct { X PieceType type; X int color; X} Square; X X/* X * setup change information X */ Xtypedef struct { X int x; X int y; X PieceType type; X int color; X} SetupChange; X X/* X * mouse (in the board subwindow) activity states X */ Xtypedef enum { X IDLE, /* nada */ X MOVING_PIECE, /* animating a piece with the left button down */ X PROMOTING_PAWN, /* selecting a piece type for a pawn promotion */ X CONFIRMING, /* borrowed for confirmation of something */ X LOCKED, /* locked (ignored until unlocked) */ X SETUP, /* setting up a board */ X} MouseState; X X/* X * transcript types X */ X#define TR_MIN_TYPE 0 X#define TR_FORMAL_NORMAL 0 /* P/K2-K4, R/KB3xP/KB5, etc. */ X#define TR_MIN_NORMAL 1 /* P-K4, RxP, etc. */ X#define TR_ALGEBRAIC 2 /* D2D4, etc. */ X#define TR_MAX_TYPE 2 X X#include "decls.h" X END_OF_nchess.h if test 3546 -ne `wc -c