[comp.sources.games] v02i092: nchess - network chess game for Suns, Part02/04

games-request@tekred.UUCP (11/19/87)

Submitted by: (Tom Anderson) toma@tc.fluke.com
Comp.sources.games: Volume 2, Issue 92
Archive-name: nchess/Part02

#! /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 <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 4)."
# Contents:  board.c
# Wrapped by billr@tekred on Thu Nov 19 10:49:20 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f board.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"board.c\"
else
echo shar: Extracting \"board.c\" \(55954 characters\)
sed "s/^X//" >board.c <<'END_OF_board.c'
X/*
X * Copyright 1987 Tom Anderson; 20831 Frank Waters Road;
X * Stanwood, WA  98282.   All rights reserved.
X */
X
X/*
X * manage the board state
X */
X
X#include <stdio.h>
X#include <strings.h>
X#include <sys/types.h>
X
X#include "nchess.h"
X
X#define	TABSIZE		8
X
XBOOL GameOver = FALSE;			/* game is over */
Xint MyColor = EITHERCOLOR;		/* my color */
Xint PeerColor = EITHERCOLOR;		/* opponent's color */
Xint Turn;				/* whose turn it is */
Xint InitialTurn;			/* whose turn it was initially */
X
XBOOL KingMoved = FALSE,			/* has the king moved ? */
X    KingRookMoved = FALSE,		/* has the king's rook moved ? */
X    QueenRookMoved = FALSE;		/* has the queen's rook moved ? */
X
Xstruct moveListNode {			/* move record list node */
X    struct moveListNode * prev;		/* previous move */
X    struct moveListNode * next;		/* next move */
X    Move move;				/* the mechanics of the move */
X    int tookX;				/* x coordinate of captured piece */
X    int tookY;				/* y coordinate of captured piece */
X    PieceType tookPiece;		/* captured piece type */
X    BOOL movedQueenRook;		/* did this move the queen rook for the first time? */
X    BOOL movedKingRook;			/* did this move the king rook for the first time? */
X    BOOL movedKing;			/* did this move the king for the first time? */
X};
X
X#define MOVELISTNODE	struct moveListNode
X
XMOVELISTNODE 
X    * firstMove = (MOVELISTNODE *) 0,	/* head of the move list */
X    * lastMove = (MOVELISTNODE *) 0,	/* tail of the move list */
X    ** nextLink = &firstMove;		/* next link */
X
XPieceType SourceTypes[8] = {
X    ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
X};
X
XSquare InitialBoard[8][8] = {
X    {ROOK,BLACK}, {KNIGHT,BLACK}, {BISHOP,BLACK}, {QUEEN,BLACK}, {KING,BLACK}, {BISHOP,BLACK}, {KNIGHT,BLACK}, {ROOK,BLACK},
X    {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK}, {PAWN,BLACK},
X    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
X    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
X    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
X    {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC}, {NULLPC},
X    {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE}, {PAWN,WHITE},
X    {ROOK,WHITE}, {KNIGHT,WHITE}, {BISHOP,WHITE}, {QUEEN,WHITE}, {KING,WHITE}, {BISHOP,WHITE}, {KNIGHT,WHITE}, {ROOK,WHITE},
X};
X
XSquare MainBoard[8][8];
X
X/*
X * are there any pieces between the start and the destination on 
X * board 'board' ?
X * (the start and destination must be on the same diagonal, the 
X * same rank, or the same file.)
X */
XBOOL
XinterveningPieces(board, from, to)
X    Square board[][8];
X    BoardCoordinate * from, * to;
X{
X    register int x, y, xmax, ymax, xdelt, ydelt;
X    int xmin, ymin;
X
X    /*
X     * if there are no intervening squares
X     */
X    if (abs(from->x - to->x) <= 1 && abs(from->y - to->y) <= 1) {
X	return(FALSE);
X    }
X    /*
X     * build the intervening path information 
X     */
X    if (from->x < to->x) {
X	xmin = from->x + 1;
X	xmax = to->x - 1;
X	xdelt = 1;
X    } else if (from->x > to->x) {
X	xmin = to->x + 1;
X	xmax = from->x - 1;
X	xdelt = -1;
X    } else {
X	xmin = xmax = from->x;
X	xdelt = 0;
X    }
X    if (from->y < to->y) {
X	ymin = from->y + 1;
X	ymax = to->y - 1;
X	ydelt = 1;
X    } else if (from->y > to->y) {
X	ymin = to->y + 1;
X	ymax = from->y - 1;
X	ydelt = -1;
X    } else {
X	ymin = ymax = from->y;
X	ydelt = 0;
X    }
X    x = from->x + xdelt ; y = from->y + ydelt ;
X    while (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
X	if (board[y][x].type != NULLPC) 
X	    return(TRUE);
X	x += xdelt ; y += ydelt ;
X    }
X    return(FALSE);
X}
X
X/*
X * set up a new board 
X */
Xvoid
XinitBoard(board) 
X    Square board[][8];
X{
X    register int i, j;
X    
X    for (i = 0 ; i < 8 ; i++) {
X	for (j = 0 ; j < 8 ; j++) {
X	    board[i][j].type = InitialBoard[i][j].type;
X	    board[i][j].color = InitialBoard[i][j].color;
X	}
X    }
X}
X
X/*
X * set up the visible playing surface at the beginning of the game
X */
Xvoid
XInitBoard()
X{
X    InitialTurn = Turn = WHITE;
X    initBoard(MainBoard);
X}
X
X/*
X * describe the state of the board square at x, y
X */
XSquare *
XGetSquare(x, y)
X{
X    return (&MainBoard[y][x]);
X}
X
X/*
X * describe the state of the source board square at x, y
X */
XSquare *
XGetSrcSquare(x, y)
X{
X    static Square square;
X
X    square.color = (y <= 3 ? BLACK : WHITE);
X    if (y == 1 || y == 6)
X	square.type = PAWN;
X    else if (y > 1 && y < 6)
X	square.type = NULLPC;
X    else 
X	square.type = SourceTypes[x];
X    return (&square);
X}
X
X/*
X * does the user have a piece at bp?
X */
XBOOL
XIsOurPieceAt(bp)
X    BoardCoordinate * bp;
X{
X    return (MainBoard[bp->y][bp->x].color == MyColor
X    && MainBoard[bp->y][bp->x].type != NULLPC);
X}
X
X/*
X * is this a setup source square?
X */
XBOOL 
XIsSrcPieceAt(bp)
X    BoardCoordinate * bp;
X{
X    return(bp->y < 2 || bp->y > 5);
X}
X
Xvoid
XDoSetupChange(setup)
X    SetupChange * setup;
X{
X    InitialBoard[setup->y][setup->x].type = MainBoard[setup->y][setup->x].type = setup->type;
X    InitialBoard[setup->y][setup->x].color = MainBoard[setup->y][setup->x].color = setup->color;
X    DrawSquare(setup->x, setup->y, &MainBoard[setup->y][setup->x]);
X}
X
X/*
X * is the square at 'x','y' on the board 'board' threatened by any 
X * pieces of the opposite color of 'myColor'?
X */
XBOOL
Xthreatened(board, xt, yt, myColor)
X    Square board[][8];
X    int xt, yt, myColor;
X{
X    register int x, y;
X    BoardCoordinate from, to;
X
X    to.x = xt; to.y = yt;
X    for (x = 0 ; x < 8 ; x++) {
X	for (y = 0 ; y < 8 ; y++) {
X	    if (board[y][x].type == NULLPC || board[y][x].color == myColor)
X		continue;
X	    from.x = x; from.y = y;
X	    switch (board[y][x].type) {
X	    case KNIGHT:
X		if (xt == x - 2 && (yt == y + 1 || yt == y - 1)
X		|| xt == x - 1 && (yt == y + 2 || yt == y - 2)
X		|| xt == x + 1 && (yt == y + 2 || yt == y - 2)
X		|| xt == x + 2 && (yt == y + 1 || yt == y - 1))
X		    return(TRUE);
X		break;
X	    case ROOK:
X		if ((x == xt || y == yt) && ! interveningPieces(board, &from, &to))
X		    return(TRUE);
X		break;
X	    case BISHOP:
X		if (abs(x - xt) == abs(y - yt) && ! interveningPieces(board, &from, &to))
X		    return(TRUE);
X		break;
X	    case QUEEN:
X		if ((abs(x - xt) == abs(y - yt) || x == xt || y == yt)
X		&& ! interveningPieces(board, &from, &to))
X		    return(TRUE);
X		break;
X	    case PAWN:
X		if ((xt == x - 1 || xt == x + 1)
X		&& (myColor == WHITE && yt == y + 1 
X		    || myColor == BLACK && yt == y - 1)
X		)
X		    return(TRUE);
X		break;
X	    case KING:
X		if (abs(x - xt) <= 1 && abs(y - yt) <= 1)
X		    return(TRUE);
X		break;
X	    }
X	}
X    }
X    return(FALSE);
X}
X
X/*
X * is this a legal move?
X */
XBOOL
XisMoveLegal(board, color, from, to)
X    Square board[][8];
X    int color;
X    BoardCoordinate * from, * to;
X{
X    Square oldS1, oldS2, oldS3, * s1, * s2, * s3 = (Square *) 0;
X    BoardCoordinate bc;
X    BOOL ok;
X
X    s1 = &board[from->y][from->x];
X    s2 = &board[to->y][to->x];
X    /* illegal: if there is a piece of my color on the destination */
X    if (s2->type != NULLPC && s2->color == color)
X	return(FALSE);
X    switch (s1->type) {
X    case KNIGHT:
X	/* illegal: if this is not a probable knight move */
X	if (to->x == from->x || to->x > from->x + 2 || to->x < from->x - 2
X	|| to->x == from->x - 2 && to->y != from->y + 1 && to->y != from->y - 1
X	|| to->x == from->x - 1 && to->y != from->y + 2 && to->y != from->y - 2
X	|| to->x == from->x + 1 && to->y != from->y + 2 && to->y != from->y - 2
X	|| to->x == from->x + 2 && to->y != from->y + 1 && to->y != from->y - 1)
X	    return(FALSE);
X	break;
X    case ROOK:
X	/* 
X	 * illegal: if this is not a probable rook move 
X	 * or there is a piece between start and destination
X	 */
X	if (from->x != to->x && from->y != to->y
X	|| interveningPieces(board, from, to))
X	    return(FALSE);
X	break;
X    case BISHOP:
X	/* 
X	 * illegal: if this is not a probable bishop move 
X	 * or there is a piece between start and destination
X	 */
X	if (abs(from->x - to->x) != abs(from->y - to->y)
X	|| interveningPieces(board, from, to))
X	    return(FALSE);
X	break;
X    case QUEEN:
X	/* 
X	 * illegal: if this is not a probable queen move 
X	 * or there is a piece between start and destination
X	 */
X	if (abs(from->x - to->x) != abs(from->y - to->y)
X	&& from->x != to->x && from->y != to->y
X	|| interveningPieces(board, from, to)) 
X	    return(FALSE);
X	break;
X    case PAWN:
X	if (color == WHITE) {
X	    /* 
X	     * potentially legal: moves forward onto an empty square 
X	     */
X	    if (to->x == from->x && s2->type == NULLPC
X	    && (to->y == from->y - 1 
X		|| to->y == 4 && from->y == 6 && ! interveningPieces(board, from, to)))
X		break;
X	    /* 
X	     * potentially legal: diagonal captures (including en passant)
X	     */
X	    if (to->y == from->y - 1 && (to->x == from->x + 1 || to->x == from->x - 1)) {
X		if (s2->type != NULLPC)
X		    break;
X		else if (to->y == 2
X		&& lastMove != (MOVELISTNODE *) 0
X		&& lastMove->move.x1 == to->x 
X		&& lastMove->move.x2 == to->x 
X		&& lastMove->move.y1 == 1
X		&& lastMove->move.y2 == 3 
X		&& board[3][to->x].type == PAWN) {
X		    s3 = &board[3][to->x];
X		    break;
X		}
X	    }
X	} else {
X	    /* 
X	     * potentially legal: moves forward onto an empty square 
X	     */
X	    if (to->x == from->x && s2->type == NULLPC
X	    && (to->y == from->y + 1 
X		|| to->y == 3 && from->y == 1 && ! interveningPieces(board, from, to)))
X		break;
X	    /* 
X	     * potentially legal: diagonal captures (including en passant) 
X	     */
X	    if (to->y == from->y + 1 && (to->x == from->x + 1 || to->x == from->x - 1)) {
X		if (s2->type != NULLPC)
X		    break;
X		else if (to->y == 5
X		&& lastMove != (MOVELISTNODE *) 0
X		&& lastMove->move.x1 == to->x 
X		&& lastMove->move.x2 == to->x 
X		&& lastMove->move.y1 == 6
X		&& lastMove->move.y2 == 4
X		&& board[4][to->x].type == PAWN) {
X		    s3 = &board[4][to->x];
X		    break;
X		}
X	    }
X	}
X	/* illegal: any other pawn moves */
X	return(FALSE);
X    case KING:
X	/*
X	 * potentially legal: single space moves
X	 */
X	if (abs(to->x - from->x) <= 1 && abs(to->y - from->y) <= 1)
X	    break;
X	/*
X	 * potentially legal: castling moves with unmoved king and rook
X	 * with no pieces between king and rook
X	 *
X	 * note: need to verify that the king and rook are still on their
X	 * original squares in addition to not having moved, since a setup
X	 * that finds them there assumes they were always there, but does
X	 * not void the moved flags if they are not initially there (to 
X	 * avoid having to store that state information in the save file).
X	 * this is compatible with the unix chess game's interpretation of
X	 * setups.
X	 */
X	if ( ! KingMoved && s1->type == KING && s1->color == color
X	&& ! threatened(board, from->x, from->y, color)) {
X	    if (to->x == 6 && ! KingRookMoved 
X	    && board[to->y][7].type == ROOK && board[to->y][7].color == color) {
X		bc.x = 7;
X		bc.y = from->y;
X		if ( ! interveningPieces(board, from, &bc)
X		&& ! threatened(board, 5, from->y, color))
X		    break;
X	    } else if (to->x == 2 && ! QueenRookMoved
X	    && board[to->y][0].type == ROOK && board[to->y][0].color == color) {
X		bc.x = 0;
X		bc.y = from->y;
X		if ( ! interveningPieces(board, from, &bc)
X		&& ! threatened(board, 3, from->y, color))
X		    break;
X	    }
X	}
X	/* illegal: any other king move */
X	return(FALSE);
X    }
X    /* 
X     * single remaining constraint: the king must not be in check after 
X     * the move 
X     */
X    /* temporarily stuff the move in the board state */
X    oldS1.type = s1->type; oldS1.color = s1->color;
X    s1->type = NULLPC;
X    oldS2.type = s2->type; oldS2.color = s2->color;
X    s2->type = oldS1.type; s2->color = oldS1.color;
X    if (s3 != (Square *) 0) {
X	oldS3.type = s3->type; oldS3.color = s3->color;
X	s3->type = NULLPC;
X    }
X    ok = ! inCheck(board, color);
X    /* back out the move */
X    s1->type = oldS1.type; s1->color = oldS1.color;
X    s2->type = oldS2.type; s2->color = oldS2.color;
X    if (s3 != (Square *) 0) {
X	s3->type = oldS3.type; s3->color = oldS3.color;
X    }
X    return(ok);
X}
X
X/*
X * is this move (of ours) legal?
X */
XBOOL
XIsMoveLegal(from, to)
X    BoardCoordinate * from, * to;
X{
X    return (isMoveLegal(MainBoard, MyColor, from, to));
X}
X
X/*
X * perform a move (either ours or the opponent's)
X * the move is assumed to be legal; hence, checks for certain move types
X * are minimal.
X */
Xvoid 
XDoMove(move, drawIt)
X    register Move * move;
X    BOOL drawIt;
X{
X    register Square * from, * to;
X    register MOVELISTNODE * mlnp;
X
X    from = &MainBoard[move->y1][move->x1];
X    to = &MainBoard[move->y2][move->x2];
X    /* 
X     * create the new move list node 
X     */
X    if ((mlnp = (MOVELISTNODE *) malloc(sizeof(MOVELISTNODE))) == (MOVELISTNODE *) 0) {
X	fprintf(stderr, "can't create move list node\n");
X	exit(1);
X    }
X    * nextLink = mlnp;			/* stuff the forward link */
X    mlnp->prev = lastMove;		/* and the backward link */
X    mlnp->next = (MOVELISTNODE *) 0;	/* set up the next forward link */
X    nextLink = &(mlnp->next);
X    lastMove = mlnp;			/* and the new tail pointer */
X    mlnp->move.x1 = move->x1;		/* store the basic move mechanics */
X    mlnp->move.y1 = move->y1;
X    mlnp->move.x2 = move->x2;
X    mlnp->move.y2 = move->y2;
X    mlnp->move.newPieceType = (int) (mlnp->tookPiece = NULLPC);
X    mlnp->movedQueenRook = mlnp->movedKingRook = mlnp->movedKing = FALSE;
X    /* if this is an en passant capture */
X    if (from->type == PAWN && to->type == NULLPC && move->x1 != move->x2) {
X	AddVictim(PAWN, MainBoard[move->y1][move->x2].color, drawIt);
X	mlnp->tookPiece = PAWN;
X	mlnp->tookX = move->x2;
X	mlnp->tookY = move->y1;
X	MainBoard[move->y1][move->x2].type = NULLPC;
X	if (drawIt) 
X	    DrawSquare(move->x2, move->y1, &MainBoard[move->y1][move->x2]);
X    /* else if it is a normal capture */
X    } else if (to->type != NULLPC) {
X	AddVictim(to->type, to->color, drawIt);
X	mlnp->tookPiece = to->type;
X	mlnp->tookX = move->x2;
X	mlnp->tookY = move->y2;
X    }
X    /* move the piece from the origin square to the dest. square */
X    to->type = from->type;
X    to->color = from->color;
X    /* put a null piece in the origin square */
X    from->type = NULLPC;
X    /* re-draw the origin and destination squares */
X    if (drawIt) {
X	DrawSquare(move->x1, move->y1, from);
X	DrawSquare(move->x2, move->y2, to);
X    }
X    switch(to->type) {
X    case KING:
X	/* if this is my king, void the castling option */
X	if (to->color == MyColor && ! KingMoved) 
X	    KingMoved = mlnp->movedKing = TRUE;
X	/* if this is a king-side castling move, move the king's rook */
X	if (move->x1 == 4 && move->x2 == 6) {
X	    from = &MainBoard[move->y2][7];
X	    to = &MainBoard[move->y2][5];
X	    to->type = ROOK;
X	    to->color = from->color;
X	    from->type = NULLPC;
X	    if (drawIt) {
X		DrawSquare(7, move->y1, from);
X		DrawSquare(5, move->y1, to);
X	    }
X	/* else if this is a queen-side castling move, move the queen's rook */
X	} else if (move->x1 == 4 && move->x2 == 2) {
X	    from = &MainBoard[move->y2][0];
X	    to = &MainBoard[move->y2][3];
X	    to->type = ROOK;
X	    to->color = from->color;
X	    from->type = NULLPC;
X	    if (drawIt) {
X		DrawSquare(0, move->y1, from);
X		DrawSquare(3, move->y2, to);
X	    }
X	}
X	break;
X    case PAWN:
X	/* if this is a pawn reaching the 8th rank, polymorph it */
X	if (move->y2 == 0 || move->y2 == 7) {
X	    to->type = 
X		(PieceType) (mlnp->move.newPieceType = move->newPieceType);
X	    if (drawIt)
X		DrawSquare(move->x2, move->y2, to);
X	}
X	break;
X    case ROOK:
X	/* check for first rook moves (to void the castling option) */
X	if (to->color == MyColor) {
X	    if (MyColor == BLACK) {
X		if (move->x1 == 0 && move->y1 == 0 && ! QueenRookMoved) 
X		    QueenRookMoved = mlnp->movedQueenRook = TRUE;
X		else if (move->x1 == 7 && move->y1 == 0 && ! KingRookMoved)
X		    KingRookMoved = mlnp->movedKingRook = TRUE;
X	    } else {
X		if (move->x1 == 0 && move->y1 == 7 && ! QueenRookMoved) 
X		    QueenRookMoved = mlnp->movedQueenRook = TRUE;
X		else if (move->x1 == 7 && move->y1 == 7 && ! KingRookMoved)
X		    KingRookMoved = mlnp->movedKingRook = TRUE;
X	    }
X	} 
X	break;
X    }
X}
X
X/*
X * add a resignation as the last move in the move list.
X * this is marked as a move with a negative y destination coordinate
X * the color of the resigning player is stored in the x destination
X * coordinate.
X *
X * note: the last play pointer is not updated, so that the "Last Play"
X * button will show the move that preceded the resignation.
X */
Xvoid 
XDoResignation(color)
X    int color;
X{
X    register MOVELISTNODE * mlnp;
X
X    /* 
X     * create the new move list node 
X     */
X    if ((mlnp = (MOVELISTNODE *) malloc(sizeof(MOVELISTNODE))) == (MOVELISTNODE *) 0) {
X	fprintf(stderr, "can't create move list node\n");
X	exit(1);
X    }
X    * nextLink = mlnp;			/* stuff the forward link */
X    mlnp->prev = lastMove;		/* and the backward link */
X    mlnp->next = (MOVELISTNODE *) 0;	/* set up the next forward link */
X    mlnp->move.x2 = color;
X    mlnp->move.y2 = -1;
X    GameOver = TRUE;
X}
X
X/*
X * undo the last move (either ours or the opponent's).
X */
Xvoid 
XUnDoMove()
X{
X    register Square * from, * to;
X    register Move * move;
X    register MOVELISTNODE * mlnp;
X
X    if ((mlnp = lastMove) == (MOVELISTNODE *) 0) {
X	Message("You are already undone.");
X	return;
X    }
X    move = &(mlnp->move);
X    if (move->y2 < 0) {
X	fputs("internal botch: tried to undo a resignation\n", stderr);
X	return;
X    }
X    from = &MainBoard[move->y1][move->x1];
X    to = &MainBoard[move->y2][move->x2];
X    /* move the piece from the destination square to the origin square */
X    from->type = to->type;
X    from->color = to->color;
X    to->type = NULLPC;
X    /* if this move was a pawn reaching the eighth rank, turn it back into a lowly pawn */
X    if (move->newPieceType != (int) NULLPC) 
X	from->type = PAWN;
X    /* re-draw the origin and destination squares */
X    DrawSquare(move->x1, move->y1, from);
X    DrawSquare(move->x2, move->y2, to);
X    /* if this was a capture, re-draw the captured piece */
X    if (mlnp->tookPiece != NULLPC) {
X	DeleteVictim(
X	    MainBoard[mlnp->tookY][mlnp->tookX].type = mlnp->tookPiece,
X	    MainBoard[mlnp->tookY][mlnp->tookX].color = (from->color == BLACK ? WHITE : BLACK));
X	DrawSquare(mlnp->tookX, mlnp->tookY, &MainBoard[mlnp->tookY][mlnp->tookX]);
X    }
X    /* if this move affected the ability to castle, restore it */
X    if (mlnp->movedKing) 
X	KingMoved = FALSE;
X    if (mlnp->movedKingRook)
X	KingRookMoved = FALSE;
X    if (mlnp->movedQueenRook)
X	QueenRookMoved = FALSE;
X    switch(from->type) {
X    case KING:
X	/* if this was a king-side castling move, move the king's rook back */
X	if (move->x1 == 4 && move->x2 == 6) {
X	    from = &MainBoard[move->y2][7];
X	    to = &MainBoard[move->y2][5];
X	    from->type = ROOK;
X	    from->color = to->color;
X	    to->type = NULLPC;
X	    DrawSquare(7, move->y1, from);
X	    DrawSquare(5, move->y1, to);
X	/* else if this is a queen-side castling move, move the queen's rook back */
X	} else if (move->x1 == 4 && move->x2 == 2) {
X	    from = &MainBoard[move->y2][0];
X	    to = &MainBoard[move->y2][3];
X	    from->type = ROOK;
X	    from->color = to->color;
X	    to->type = NULLPC;
X	    DrawSquare(0, move->y1, from);
X	    DrawSquare(3, move->y2, to);
X	}
X	break;
X    }
X    /* peel off the tail */
X    if (mlnp->prev != (MOVELISTNODE *) 0) {
X	lastMove = mlnp->prev;
X	lastMove->next = (MOVELISTNODE *) 0;
X	nextLink = &(lastMove->next);
X    } else {
X	nextLink = &firstMove;
X	firstMove = lastMove = (MOVELISTNODE *) 0;
X    }
X    free((char *) mlnp);
X}
X
X/*
X * flash the last play by either me or my opponent
X * (NOTE: this sleeps for 1-2 seconds while showing the previous position - 
X * this should not be extended much longer, since this may overlap with
X * an RPC timeout if the opponent sends us his next move in the interim.)
X */
Xvoid
XShowLastPlay()
X{
X    register Move * move;
X    register MOVELISTNODE * mlnp;
X#define	FROM		0
X#define	TO		1
X    Square pre[4], post[4];
X    BoardCoordinate pos[4];
X    int lastSquareIndex = 1;
X    register int i;
X
X    /* ignore bogus last play requests */
X    if ((mlnp = lastMove) == (MOVELISTNODE *) 0) {
X	return;
X    }
X    move = &(mlnp->move);
X    pos[FROM].x = move->x1; pos[FROM].y = move->y1;
X    pos[TO].x = move->x2; pos[TO].y = move->y2;
X    post[FROM].type = NULLPC;
X    post[TO].type = MainBoard[pos[TO].y][pos[TO].x].type;
X    post[TO].color = MainBoard[pos[TO].y][pos[TO].x].color;
X    pre[FROM].type = post[TO].type;
X    pre[FROM].color = post[TO].color;
X    pre[TO].type = NULLPC;
X    /* if this move was a pawn reaching the eighth rank */
X    if (move->newPieceType != (int) NULLPC) 
X	pre[FROM].type = PAWN;
X    /* if this was a capture */
X    if (mlnp->tookPiece != NULLPC) {
X	/* if this was an en passant capture */
X	if (pos[TO].y != mlnp->tookY) {
X	    pos[++lastSquareIndex].x = mlnp->tookX;
X	    pos[lastSquareIndex].y = mlnp->tookY;
X	    post[lastSquareIndex].type = NULLPC;
X	}
X	pre[lastSquareIndex].type = mlnp->tookPiece;
X	pre[lastSquareIndex].color = (pre[FROM].color == BLACK ? WHITE : BLACK);
X    } else if (post[TO].type == KING) {
X	/* if this was a king-side castling move */
X	if (pos[FROM].x == 4 && pos[TO].x == 6) {
X	    ++lastSquareIndex;
X	    pos[lastSquareIndex].x = 7;
X	    pos[lastSquareIndex + 1].x = 5;
X	    pos[lastSquareIndex].y = pos[lastSquareIndex + 1].y = pos[FROM].y;
X	    pre[lastSquareIndex].type = post[lastSquareIndex + 1].type = ROOK;
X	    pre[lastSquareIndex].color = post[lastSquareIndex + 1].color = pre[FROM].color;
X	    post[lastSquareIndex].type = pre[lastSquareIndex + 1].type = NULLPC;
X	    ++lastSquareIndex;
X	/* else if this is a queen-side castling move */
X	} else if (move->x1 == 4 && move->x2 == 2) {
X	    ++lastSquareIndex;
X	    pos[lastSquareIndex].x = 0;
X	    pos[lastSquareIndex + 1].x = 3;
X	    pos[lastSquareIndex].y = pos[lastSquareIndex + 1].y = pos[FROM].y;
X	    pre[lastSquareIndex].type = post[lastSquareIndex + 1].type = ROOK;
X	    pre[lastSquareIndex].color = post[lastSquareIndex + 1].color = pre[FROM].color;
X	    post[lastSquareIndex].type = pre[lastSquareIndex + 1].type = NULLPC;
X	    ++lastSquareIndex;
X	}
X    }
X    /* re-draw the pre-move configuration */
X    for ( i = 0 ; i <= lastSquareIndex ; i++ ) 
X	DrawSquare(pos[i].x, pos[i].y, &pre[i]);
X    sleep((unsigned) 2);
X    /* re-draw the post-move configuration */
X    for ( i = 0 ; i <= lastSquareIndex ; i++ ) 
X	DrawSquare(pos[i].x, pos[i].y, &post[i]);
X}
X
X/*
X * have I moved yet? 
X */
XBOOL IHaveMoved()
X{
X    return (firstMove != (MOVELISTNODE *) 0
X    && (MyColor == WHITE || firstMove != lastMove));
X}
X
XPieceType
XpawnMorphs[] = { 
X    QUEEN,			/* pawn -> queen */
X    QUEEN,			/* knight -> queen */
X    KNIGHT,			/* bishop -> knight */
X    BISHOP,			/* rook -> bishop */
X    ROOK			/* queen -> rook */
X};
X
X/*
X * select the next possible pawn promotion option 
X */
Xint
XPromotePawn(blocp)
X    BoardCoordinate * blocp;
X{
X    register Square * sp;
X
X    sp = &MainBoard[blocp->y][blocp->x];
X    lastMove->move.newPieceType = (int) (sp->type = pawnMorphs[(int) sp->type]);
X    DrawSquare(blocp->x, blocp->y, sp);
X    return((int) sp->type);
X}
X
X/*
X * is the 'color' king in check?
X */
XBOOL 
XinCheck(board, color)
X    Square board[][8];
X    int color;
X{
X    register int x, y;
X
X    /* search for the king */
X    for (x = 0 ; x < 8 ; x++) 
X	for (y = 0 ; y < 8 ; y++) 
X	    if (board[y][x].type == KING && board[y][x].color == color) 
X		return (threatened(board, x, y, color));
X    return(FALSE);
X}
X
X/*
X * am I in check?
X */
XBOOL
XInCheck() 
X{
X    return(inCheck(MainBoard, MyColor));
X}
X
X/* 
X * piece type characters
X */
Xchar 
XpieceChars[] = { 'P', 'N', 'B', 'R', 'Q', 'K' };
X
X/*
X * normal board file descriptions
X */
Xchar *
XfileStrings[] = { "QR", "QN", "QB", "Q", "K", "KB", "KN", "KR" };
X
X/*
X * write a string describing the board location 
X */
Xvoid
XlocString(cp, color, x, y)
X    char * cp;
X    int color, x, y;
X{
X    if (color == WHITE) 
X	sprintf(cp, "%s%d", fileStrings[x], 8 - y);
X    else 
X	sprintf(cp, "%s%d", fileStrings[x], 1 + y);
X}
X
X/*
X * alias positions for alternative interpretations of move descriptions
X */
Xtypedef struct {
X    BoardCoordinate pos;	/* piece position */
X    BOOL valid;			/* "currently under consideration" flag */
X} Alias;
X
X/*
X * mark aliases as valid which lie on a particular file
X * return the number qualified.
X * the file to use is always the first the first entry in the aliases array.
X */
Xvoid
XmarkSpecificFile(aliases, aliasCount)
X    Alias aliases[];
X    int aliasCount;
X{
X    register int i, file;
X
X    file = aliases[0].pos.x;
X    for (i = 1 ; i < aliasCount ; i++) 
X	aliases[i].valid = (aliases[i].pos.x == file);
X}
X
X/*
X * mark aliases as valid which lie on a generic file (b, n, or r)
X * or a specific file (q, k).
X * the file to use is always the first the first entry in the aliases array.
X */
Xvoid
XmarkGenericFile(aliases, aliasCount)
X    Alias aliases[];
X    int aliasCount;
X{
X    register int i, file;
X
X    file = aliases[0].pos.x;
X    if (file == 3 || file == 4)
X	markSpecificFile(aliases, aliasCount);
X    else 
X	for (i = 1 ; i < aliasCount ; i++)
X	    aliases[i].valid = 
X		(aliases[i].pos.x == file || aliases[i].pos.x == 7 - file);
X}
X
X/*
X * mark all aliases as valid
X */
Xvoid 
XmarkAllAsValid(aliases, aliasCount)
X    Alias aliases[];
X    int aliasCount;
X{
X    register int i;
X
X    for (i = 0 ; i < aliasCount ; i++) 
X	aliases[i].valid = TRUE;
X}
X
X#define	NULLSIDE	0
X#define	KINGSIDE	1
X#define	QUEENSIDE	2
X
X/*
X * determine if an alias list can be disambiguated by a kingside vs.
X * queenside distinction; if so, return KINGSIDE or QUEENSIDE and
X * limit the alias list to the first entry - otherwise, return
X * NULLSIDE.
X * Note: only bishops, knights, and rooks can be distinguished this
X * way - it would be too wierd to allow queens specified as "QQ".
X */
Xint
XmarkBySide(aliases, aliasCount, pieceType, color)
X    Alias aliases[];
X    int aliasCount, color;
X    PieceType pieceType;
X{
X    /* can only disambiguate two pieces this way */
X    if (aliasCount > 2)
X	return(NULLSIDE);
X    if (pieceType == BISHOP 
X	&& ((aliases[0].pos.x + aliases[0].pos.y) & 0x01) 
X	!= ((aliases[1].pos.x + aliases[1].pos.y) & 0x01) 
X    || (pieceType == ROOK || pieceType == KNIGHT)
X	&& (aliases[0].pos.x != aliases[1].pos.x)) 
X    {
X	aliases[0].valid = TRUE;
X	aliases[1].valid = FALSE;
X	switch(pieceType) {
X	case BISHOP:
X	    if (((aliases[0].pos.x + aliases[0].pos.y) & 0x01) != 0 && color == BLACK
X	    || ((aliases[0].pos.x + aliases[0].pos.y) & 0x01) == 0 && color == WHITE)
X		return(KINGSIDE);
X	    else
X		return(QUEENSIDE);
X	default:
X	    return(aliases[0].pos.x < aliases[1].pos.x ? QUEENSIDE : KINGSIDE);
X	}
X    }
X    return(NULLSIDE);
X}
X
X/*
X * return the number of possible interpretations
X * of the srcs[] array combined with the dests[] array.
X * entries that are under consideration have their valid flag set.
X */
Xint
XfindPossibles(board, color, srcs, srcCount, dests, destCount)
X    Square board[][8];
X    Alias srcs[], dests[];
X    int color, srcCount, destCount;
X{
X    register int i, j;
X    int possibles = 0;
X
X    for (i = 0 ; i < srcCount ; i++) 
X	for (j = 0 ; j < destCount ; j++) 
X	    if (srcs[i].valid && dests[j].valid 
X	    && isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos))
X		possibles++;
X    return(possibles);
X}
X
X/*
X * print the minimal normal notation for a capture (except en passant).
X * return the number of characters printed.
X *
X * note that we can find up to a maximum of ten pieces of the same type
X * in a "real" game where all eight pawns have been promoted to the
X * same piece type.
X */
Xint
XprintMinCapture(tfile, board, color, move, loc1, loc2, from, to)
X    FILE * tfile;
X    Square board[][8];
X    register Square * from, * to;
X    Move * move;
X    char * loc1, * loc2;
X    int color;
X{
X    register int srcCount = 1, destCount = 1, i, j;
X    Alias srcs[10], dests[10];
X    int possibles, moveCount;
X
X    /* disambiguation state flags */
X    BOOL 
X    daSrcSingle = FALSE,	/* fully spec. src.; e.g., r(kr4)xp */
X    daSrcBrdSide = NULLSIDE,  	/* side of the board/bishop color; e.g. krxp */
X    daSrcSpecificFile = FALSE,	/* specific file; e.g. krpxr */
X    daSrcGenericFile = FALSE,	/* generic file; e.g. rpxr */
X    daDestSingle = FALSE,	/* fully spec. dest.; e.g., rxp(kr4) */
X    daDestBrdSide = NULLSIDE,  	/* side of the board/bishop color; e.g. krxp */
X    daDestSpecificFile = FALSE,  	/* specific file; e.g. rxkrp */
X    daDestGenericFile = FALSE;	/* generic file; e.g. qxrp */
X
X    /*
X     * install the actual move as the first entries 
X     */
X    srcs[0].pos.x = move->x1; srcs[0].pos.y = move->y1; srcs[0].valid = TRUE;
X    dests[0].pos.x = move->x2; dests[0].pos.y = move->y2; dests[0].valid = TRUE;
X    /*
X     * find the other pieces of the same color and type 
X     * as the piece being moved,
X     * along with the other pieces of the same type and color as the
X     * piece being captured.
X     */
X    for (i = 0 ; i < 8 ; i++) {
X	for (j = 0 ; j < 8 ; j++) {
X	    if (board[j][i].type == from->type
X	    && board[j][i].color == from->color
X	    && ! (i == move->x1 && j == move->y1)) {
X		srcs[srcCount].valid = FALSE;
X		srcs[srcCount].pos.x = i;
X		srcs[srcCount++].pos.y = j;
X	    }
X	    if (board[j][i].type == to->type
X	    && board[j][i].color == to->color
X	    && ! (i == move->x2 && j == move->y2)) {
X		dests[destCount].valid = FALSE;
X		dests[destCount].pos.x = i;
X		dests[destCount++].pos.y = j;
X	    }
X	}
X    }
X    /*
X     * reduce this to only the pieces that figure in ambiguous
X     * captures.
X     */
X    possibles = 0;
X    for (i = 0 ; i < srcCount ; i++) {
X	for (j = 0 ; j < destCount ; j++) {
X	    if (isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos)) {
X		possibles++;
X		srcs[i].valid = dests[j].valid = TRUE;
X	    }
X	}
X    }
X    /* condense the piece lists */
X    for (i = j = 0 ; i < srcCount ; i++) {
X	if (srcs[i].valid) {
X	    if (i > j) {
X		srcs[j].pos.x = srcs[i].pos.x;
X		srcs[j].pos.y = srcs[i].pos.y;
X		srcs[j].valid = srcs[i].valid;
X	    }
X	    j++;
X	}
X    }
X    srcCount = j;
X    for (i = j = 0 ; i < destCount ; i++) {
X	if (dests[i].valid) {
X	    if (i > j) {
X		dests[j].pos.x = dests[i].pos.x;
X		dests[j].pos.y = dests[i].pos.y;
X		dests[j].valid = dests[i].valid;
X	    }
X	    j++;
X	}
X    }
X    destCount = j;
X    /* if there are ambiguous source pieces */
X    if (possibles > 1 && srcCount > 1) {
X	/*
X	 * if only the source is ambiguous 
X	 * or if the source is a pawn
X	 * or if the destination is not an ambiguous pawn
X	 * try to disambiguate the source alone
X	 */
X	if (destCount == 1 || from->type == PAWN || to->type != PAWN) {
X	    /* if the source is a pawn */
X	    if (from->type == PAWN) {
X		/* try specifying the generic file source pawn; e.g., RPxP. */
X		markGenericFile(srcs, srcCount);
X		daSrcGenericFile = TRUE;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		/* try specifying the specific file source pawn; e.g., KRPxP.
X		 * (note - if there is only one destination, 
X		 * this will always succeed) */
X		if (possibles > 1) {
X		    markSpecificFile(srcs, srcCount);
X		    daSrcSpecificFile = TRUE;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		/* if the above didn't work, back out the restrictions */
X		if (possibles > 1) {
X		    daSrcSpecificFile = daSrcGenericFile = FALSE;
X		    markAllAsValid(srcs, srcCount);
X		}
X	    } 
X	    /* else the source is a piece */
X	    else {
X		/* try using kingside or queenside prefixes.  */
X		if ((daSrcBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) {
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		/* if there is only one destination,
X		 * punt and fully specify the source square.  */
X		if (possibles > 1 && destCount == 1) {
X		    daSrcSingle = TRUE;
X		    srcCount = 1;
X		    if ((possibles = findPossibles(board, color, srcs, srcCount, dests, destCount)) != 1)
X		    {
X			fprintf(stderr, "transcript error: assertion #1 failed\n");
X			possibles = 1;
X		    }
X		}
X		/* if the above didn't work, back out the restrictions */
X		if (possibles > 1) {
X		    daSrcBrdSide = FALSE;
X		    markAllAsValid(srcs, srcCount);
X		}
X	    }
X	}
X	/* if unsolved at this point, 
X	 * there must be ambiguous destination pieces;
X	 * otherwise, the previous clause would have solved it.  */
X	if (possibles > 1 && destCount == 1) {
X	    fprintf(stderr, "transcript error: assertion #2 failed\n");
X	    fprintf(tfile, "%cx%c", 
X		pieceChars[(int) from->type],
X		pieceChars[(int) to->type]);
X	    return(3);
X	}
X	/*
X	 * if the destination is a pawn 
X	 */
X	if (possibles > 1 && to->type == PAWN) {
X	    /* try specifying the generic file destination pawn; e.g., PxRP. */
X	    markGenericFile(dests, destCount);
X	    daDestGenericFile = TRUE;
X	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    /* try specifying the specific file destination pawn; e.g., PxKRP */
X	    if (possibles > 1) {
X		markSpecificFile(dests, destCount);
X		daDestSpecificFile = TRUE;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    }
X	    /* if the source is a pawn */
X	    if (possibles > 1 && from->type == PAWN) {
X		/* back the destination spec. down to a generic file
X		 * and specify the source generic file.  */
X		markAllAsValid(dests, destCount);
X		daDestGenericFile = daDestSpecificFile = FALSE;
X		markGenericFile(dests, destCount);
X		daDestGenericFile = TRUE;
X		markGenericFile(srcs, srcCount); 
X		daSrcGenericFile = TRUE;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		/*
X		 * specify the specific source file.
X		 * for example, consider the 
X		 * case of two bishop pawns and two rook pawns 
X		 * attacking two knight pawns and the queen and king
X		 * pawns - - we will eventually wind up here
X		 * with something like: "KBPxNP" (since "KBPxP" and
X		 * "PxKNP" are both ambiguous.
X		 * we may STILL have an ambiguity if there is more than 
X		 * one pawn on the file.
X		 */
X		if (possibles > 1) {
X		    markSpecificFile(srcs, srcCount);
X		    daSrcSpecificFile = TRUE;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		/* fully specify the source square and back out all 
X		 * destination specs.  */
X		if (possibles > 1) {
X		    markAllAsValid(dests, destCount);
X		    daDestGenericFile = daDestSpecificFile = FALSE;
X		    daSrcSingle = TRUE;
X		    srcCount = 1;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		/* fully specify the source square and the generic
X		 * destination file (will always be enough) */
X		if (possibles > 1) {
X		    markGenericFile(dests, destCount);
X		    daDestGenericFile = TRUE;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		if (possibles > 1)
X		    fprintf(stderr, "transcript error: assertion #3 failed\n");
X		
X	    }
X	    /* else the source is a piece */
X	    else if (possibles > 1) {
X		/* back out the pawn file specs.
X		 * try using kingside or queenside prefixes.  */
X		markAllAsValid(dests, destCount);
X		daDestGenericFile = daDestSpecificFile = FALSE;
X		if ((daSrcBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) 
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		/* try fully specifying the source square */
X		if (possibles > 1) {
X		    daSrcSingle = TRUE;
X		    srcCount = 1;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		/* specify only the generic dest. pawn file (will always be 
X		 * enough; for example, consider two bishops of 
X		 * the same color attacking two pawns) */
X		if (possibles > 1) {
X		    markGenericFile(dests, destCount);
X		    daDestGenericFile = TRUE;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		if (possibles > 1)
X		    fprintf(stderr, "transcript error: assertion #4 failed\n");
X	    }
X	}
X	/* else the destination is a piece */
X	else if (possibles > 1) {
X	    /* if the source is a pawn, we know from the above that 
X	    * specifying the source pawn file alone doesn't work.  */
X	    if (from->type == PAWN) {
X		/* try using kingside or queenside prefixes.  */
X		if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) {
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		    /* try specifying the generic file src. pawn; e.g., RPxKB. */
X		    if (possibles > 1) {
X			markGenericFile(srcs, srcCount);
X			daSrcGenericFile = TRUE;
X			possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		    }
X		    /* try specifying the specific file source pawn; e.g., KRPxKB */
X		    if (possibles > 1) {
X			markSpecificFile(srcs, srcCount);
X			daSrcSpecificFile = TRUE;
X			possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		    }
X		}
X		if (possibles > 1) {
X		    /* back out any pawn limitations.
X		     * try fully specifying the destination square.  */
X		    daSrcSpecificFile = daSrcGenericFile = FALSE;
X		    daDestSingle = TRUE;
X		    destCount = 1;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		if (possibles > 1) {
X		    /* specify the generic source file pawn.
X		     * will always work, since at most one pawn can capture
X		     * a specific square along a particular file.  */
X		    daSrcGenericFile = TRUE;
X		    markGenericFile(srcs, srcCount);
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		if (possibles > 1)
X		    fprintf(stderr, "transcript error: assertion #5 failed\n");
X	    } 
X	    /* else both source and destination are pieces.  we know from 
X	     * above that there are multiple destinations and that specifying 
X	     * source kingside/queenside alone won't work.  */
X	    else {
X		/* try using dest. kingside or queenside prefixes.  */
X		if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) 
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		/* try using src. kingside or queenside prefixes.  */
X		if (possibles > 1 
X		&& (daDestBrdSide = markBySide(srcs, srcCount, from->type, color)) != NULLSIDE) 
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		if (possibles > 1) {
X		    /* back out dest. k/q-side and fully specify the source. */
X		    daDestBrdSide = NULLSIDE;
X		    markAllAsValid(dests, destCount);
X		    daSrcSingle = TRUE;
X		    srcCount = 1;
X		    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		}
X		if (possibles > 1) {
X		    /* punt: fully specify the destination.  */
X		    daDestSingle = TRUE;
X		    /* no need to check the assertion that this is it,
X		     * as it is really obvious. */
X		}
X	    }
X	}
X    } 
X    /*
X     * else only the destination is ambiguous
X     */
X    else if (possibles > 1) {
X	if (to->type == PAWN) {
X	    /* try generic file spec. */
X	    markGenericFile(dests, destCount);
X	    daDestGenericFile = TRUE;
X	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    /* try specific file spec. */
X	    if (possibles > 1) {
X		markSpecificFile(dests, destCount);
X		daDestSpecificFile = TRUE;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    }
X	    /* try fully specifying the destination square */
X	    if (possibles > 1) {
X		daDestSingle = TRUE;
X		destCount = 1;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    }
X	    if (possibles > 1)
X		fprintf(stderr, "transcript error: assertion #6 failed\n");
X	} else {
X	    /* try kingside/queenside */
X	    if ((daDestBrdSide = markBySide(dests, destCount, to->type, color == WHITE ? BLACK : WHITE)) != NULLSIDE) 
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    /* fully specify the destination square */
X	    if (possibles > 1) {
X		daDestSingle = TRUE;
X		destCount = 1;
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    }
X	    if (possibles > 1)
X		fprintf(stderr, "transcript error: assertion #7 failed\n");
X	}
X    }
X    /* now print the capture */
X    /* if we must fully specify the source */
X    if (daSrcSingle) {
X	fprintf(tfile, "%c(%s)", pieceChars[(int) from->type], loc1);
X	moveCount = 3 + strlen(loc1);
X    } else {
X	moveCount = 0;
X	loc1 = fileStrings[move->x1];
X	/* if we must specify queenside source */
X	if (daSrcBrdSide == QUEENSIDE) {
X	    fputc('Q', tfile);
X	    moveCount = 1;
X	/* else if we must specify kingside source */
X	} else if (daSrcBrdSide == KINGSIDE) {
X	    fputc('K', tfile);
X	    moveCount = 1;
X	/* else if we must specify the specific file */
X	} else if (daSrcSpecificFile) {
X	    fprintf(tfile, "%s", loc1);
X	    moveCount = strlen(loc1);
X	/* else if we must specify the generic file */
X	} else if (daSrcGenericFile) {
X	    fprintf(tfile, "%s", strlen(loc1) == 2 ? loc1 + 1 : loc1);
X	    moveCount = 1;
X	}
X	fputc(pieceChars[(int) from->type], tfile);
X	moveCount++;
X    }
X    fputc('x', tfile);
X    moveCount++;
X    /* if we must fully specify the destination */
X    if (daDestSingle) {
X	fprintf(tfile, "%c(%s)", pieceChars[(int) to->type], loc2);
X	moveCount += 3 + strlen(loc2);
X    } else {
X	loc2 = fileStrings[move->x2];
X	/* if we must specify queenside dest. */
X	if (daDestBrdSide == QUEENSIDE) {
X	    fputc('Q', tfile);
X	    moveCount++ ;
X	/* else if we must specify kingside dest. */
X	} else if (daDestBrdSide == KINGSIDE) {
X	    fputc('K', tfile);
X	    moveCount++ ;
X	/* else if we must specify the specific file */
X	} else if (daDestSpecificFile) {
X	    fprintf(tfile, "%s", loc2);
X	    moveCount += strlen(loc2);
X	/* else if we must specify the generic file */
X	} else if (daDestGenericFile) {
X	    fprintf(tfile, "%s", strlen(loc2) == 2 ? loc2 + 1 : loc2);
X	    moveCount++;
X	}
X	fputc(pieceChars[(int) to->type], tfile);
X	moveCount++;
X    }
X    return(moveCount);
X}
X
X/*
X * print the minimal normal notation for a non-special move.
X * return the number of characters printed.
X *
X * note that we can find up to a maximum of ten pieces of the same type
X * in a real game where all eight pawns have been promoted to the
X * same piece type.
X */
Xint
XprintMinMove(tfile, board, color, move, loc1, loc2, from)
X    FILE * tfile;
X    Square board[][8];
X    register Square * from;
X    Move * move;
X    char * loc1, * loc2;
X    int color;
X{
X    register int i, j;
X    Alias srcs[10], dests[2];
X    int srcCount = 1, destCount = 1, possibles, moveCount;
X    /* disambiguation state flags */
X    BOOL 
X    daSingleDest = FALSE,	/* fully spec. dest.; e.g., r - kr4 */
X    daBoardSide = NULLSIDE,  	/* specific side of the board/bishop color; 
X				 * e.g. kr-r4 */
X    daSingleSrc = FALSE;	/* fully spec. src.; e.g. r(kr4) - r4 */
X
X    /*
X     * install the actual move as the first entries 
X     */
X    srcs[0].pos.x = move->x1; srcs[0].pos.y = move->y1; srcs[0].valid = TRUE;
X    dests[0].pos.x = move->x2; dests[0].pos.y = move->y2; dests[0].valid = TRUE;
X    /*
X     * find the other pieces of the same color and type 
X     * as the piece being moved.
X     */
X    for (i = 0 ; i < 8 ; i++) {
X	for (j = 0 ; j < 8 ; j++) {
X	    if (board[j][i].type == from->type
X	    && board[j][i].color == from->color
X	    && ! (i == move->x1 && j == move->y1)) {
X		srcs[srcCount].valid = FALSE;
X		srcs[srcCount].pos.x = i;
X		srcs[srcCount++].pos.y = j;
X	    }
X	}
X    }
X    /*
X     * if the destination is a rook, knight, or bishop file
X     * and there is no piece on the mirrored square,
X     * double the possible destinations (e.g., "n3" means "kn3" or "qn3")
X     */
X    if (move->x2 < 3 || move->x2 > 4
X    && board[move->y2][7-move->x2].type == NULLPC) {
X	dests[1].pos.x = 7 - move->x2;
X	dests[1].pos.y = move->y2;
X	destCount = 2;
X    }
X    /*
X     * reduce this to only the srcs and dests that figure in ambiguous
X     * moves.
X     */
X    possibles = 0;
X    for (i = 0 ; i < srcCount ; i++) {
X	for (j = 0 ; j < destCount ; j++) {
X	    if (isMoveLegal(board, color, &srcs[i].pos, &dests[j].pos)) {
X		possibles++;
X		srcs[i].valid = dests[j].valid = TRUE;
X	    }
X	}
X    }
X    /* condense the src. piece list */
X    for (i = j = 0 ; i < srcCount ; i++) {
X	if (srcs[i].valid) {
X	    if (i > j) {
X		srcs[j].pos.x = srcs[i].pos.x;
X		srcs[j].pos.y = srcs[i].pos.y;
X		srcs[j].valid = srcs[i].valid;
X	    }
X	    j++;
X	}
X    }
X    srcCount = j;
X    /* condense the destination list */
X    for (i = j = 0 ; i < destCount ; i++) {
X	if (dests[i].valid) {
X	    if (i > j) {
X		dests[j].pos.x = dests[i].pos.x;
X		dests[j].pos.y = dests[i].pos.y;
X		dests[j].valid = dests[i].valid;
X	    }
X	    j++;
X	}
X    }
X    destCount = j;
X    if (possibles > 1) {
X	/* try reducing to only one destination */
X	if (destCount == 2) {
X	    daSingleDest = TRUE;
X	    dests[1].valid = FALSE;
X	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    if (possibles > 1) {
X		dests[1].valid = TRUE;
X		daSingleDest = FALSE;
X	    }
X	}
X	/* try specifying kingside/queenside */
X	if (possibles > 1 
X	&& (daBoardSide = markBySide(srcs, srcCount, from->type, color)) 
X	    != NULLSIDE) 
X	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	/* try restricting to only one destination */
X	if (possibles > 1 && destCount == 2) {
X	    daSingleDest = TRUE;
X	    dests[1].valid = FALSE;
X	    possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X	    if (possibles > 1) {
X		dests[1].valid = TRUE;
X		daSingleDest = FALSE;
X	    }
X	}
X	/* try fully specifying the source */
X	if (possibles > 1) {
X	    daSingleSrc = TRUE;
X	    srcCount = 1;
X	    if (destCount > 1) {
X		possibles = findPossibles(board, color, srcs, srcCount, dests, destCount);
X		if (possibles > 1) {
X		    dests[1].valid = FALSE;
X		    daSingleDest = TRUE;
X		}
X	    }
X	}
X    } 
X    /* now print the move */
X    /* if we must fully specify the source */
X    if (daSingleSrc) {
X	fprintf(tfile, "%c(%s)", pieceChars[(int) from->type], loc1);
X	moveCount = 3 + strlen(loc1);
X    } else {
X	moveCount = 0;
X	/* if we must specify queenside source */
X	if (daBoardSide == QUEENSIDE) {
X	    fputc('Q', tfile);
X	    moveCount = 1;
X	/* else if we must specify kingside source */
X	} else if (daBoardSide == KINGSIDE) {
X	    fputc('K', tfile);
X	    moveCount = 1;
X	} 
X	fputc(pieceChars[(int) from->type], tfile);
X	moveCount++;
X    }
X    fputc('-', tfile);
X    moveCount++;
X    /* if we must fully specify the destination */
X    if (daSingleDest) {
X	fprintf(tfile, "%s", loc2);
X	moveCount += strlen(loc2);
X    /* else print only the generic rank and file */
X    } else {
X	fprintf(tfile, "%s", strlen(loc2) == 2 ? loc2 : loc2 + 1);
X	moveCount += 2;
X    }
X    return(moveCount);
X}
X
X/*
X * write a transcript file
X */
Xvoid 
XWriteTranscript(filename, trType)
X    char * filename;
X    int trType;
X{
X    register Square * from, * to;
X    register Move * move;
X    FILE * tfile;
X    MOVELISTNODE * mlnp;
X    int moveSize, color = WHITE, moveNum = 1;
X    Square board[8][8]; 
X    char loc1[5], loc2[5];
X
X    if ((tfile = fopen(filename, "w")) == (FILE *) 0) {
X	Message("Can't open transcript file!");
X	return;
X    } else {
X	Message("Writing transcript...");
X    }
X    /* create a new initial board layout */
X    initBoard(board);
X    fprintf(tfile, "\n\t%s", PlayerName[WHITE]);
X    moveSize = strlen(PlayerName[WHITE]);
X    if (moveSize < 2 * TABSIZE) {
X	fputc('\t', tfile);
X	if (moveSize < TABSIZE) 
X	    fputc('\t', tfile);
X    }
X    fprintf(tfile, "%s\n\n", PlayerName[BLACK]);
X    for (color = WHITE , mlnp = firstMove , moveSize = 0 ; 
X    mlnp != (MOVELISTNODE *) 0 ; 
X    mlnp = mlnp->next, color = (color == BLACK ? WHITE : BLACK)) 
X    {
X	move = &mlnp->move;
X	/* if this is a resignation */
X	if (move->y2 < 0) {
X	    fputs(move->x2 == WHITE ? 
X		"\n\tresigns\n" : 
X		"\n\t\t\tresigns\n", tfile);
X	    break;
X	}
X	if (color == WHITE) {
X	    fprintf(tfile, "%d.\t", moveNum++);
X	} else {
X	    fputc('\t', tfile);
X	    if (moveSize < TABSIZE) 
X		fputc('\t', tfile);
X	}
X	/* if we are using algebraic notation */
X	if (trType == TR_ALGEBRAIC) {
X	    moveSize = 4;
X	    fprintf(tfile, "%c%d%c%d",
X		'a' + move->x1, 8 - move->y1,
X		'a' + move->x2, 8 - move->y2);
X	/* else normal notation */
X	} else {
X	    from = &board[move->y1][move->x1];
X	    to = &board[move->y2][move->x2];
X	    locString(loc1, color, move->x1, move->y1);
X	    locString(loc2, color, move->x2, move->y2);
X	    /* if this was an en passant capture */
X	    if (from->type == PAWN && to->type == NULLPC && move->x1 != move->x2) {
X		switch (trType) {
X		case TR_FORMAL_NORMAL:
X		    fprintf(tfile, "p/%sxp/%s", loc1, loc2);
X		    moveSize = 5 + strlen(loc1) + strlen(loc2);
X		    break;
X		case TR_MIN_NORMAL:
X		    fprintf(tfile, "PxP(ep)");
X		    moveSize = 7;
X		    break;
X		}
X		board[move->y1][move->x2].type = NULLPC;
X	    /* else if this was a normal capture */
X	    } else if (to->type != NULLPC) {
X		switch (trType) {
X		case TR_FORMAL_NORMAL:
X		    fprintf(tfile, "%c/%sx%c/%s", 
X			pieceChars[(int) from->type], loc1, 
X			pieceChars[(int) to->type], loc2);
X		    moveSize = 5 + strlen(loc1) + strlen(loc2);
X		    break;
X		case TR_MIN_NORMAL:
X		    moveSize = printMinCapture(tfile, board, color, move, loc1, loc2, from, to);
X		    break;
X		}
X	    /* else if this was a king move */
X	    } else if (from->type == KING) {
X		/* 
X		 * if this was a king-side castling move
X		 */
X		if (move->x1 == 4 && move->x2 == 6) {
X		    /* move the rook */
X		    board[move->y2][5].type = ROOK;
X		    board[move->y2][5].color = board[move->y2][7].color;
X		    board[move->y2][7].type = NULLPC;
X		    fprintf(tfile, "O-O");
X		    moveSize = 3;
X		/* 
X		 * else if this was a queen-side castling move
X		 */
X		} else if (move->x1 == 4 && move->x2 == 2) {
X		    /* move the rook */
X		    board[move->y2][3].type = ROOK;
X		    board[move->y2][3].color = board[move->y2][0].color;
X		    board[move->y2][0].type = NULLPC;
X		    fprintf(tfile, "O-O-O");
X		    moveSize = 5;
X		/* else this was a normal king move */
X		} else {
X		    switch (trType) {
X		    case TR_FORMAL_NORMAL:
X			fprintf(tfile, "k/%s-%s", loc1, loc2);
X			moveSize = 3 + strlen(loc1) + strlen(loc2);
X			break;
X		    case TR_MIN_NORMAL:
X			fprintf(tfile, "K-%s", 
X			    strlen(loc2) == 2 ? loc2 : loc2 + 1);
X			moveSize = 4;
X			break;
X		    }
X		}
X	    /* else this was a normal piece move */
X	    } else {
X		switch (trType) {
X		case TR_FORMAL_NORMAL:
X		    fprintf(tfile, "%c/%s-%s", 
X			pieceChars[(int) from->type], loc1, loc2);
X		    moveSize = 3 + strlen(loc1) + strlen(loc2);
X		    break;
X		case TR_MIN_NORMAL:
X		    moveSize = printMinMove(tfile, board, color, move, loc1, loc2, from);
X		    break;
X		}
X	    }
X	}
X	/* move the piece from the origin square to the dest. square */
X	to->type = from->type;
X	to->color = from->color;
X	/* put a null piece in the origin square */
X	from->type = NULLPC;
X	/* 
X	 * if this was a pawn reaching the 8th rank
X	 */
X	if (to->type == PAWN 
X	&& (move->y2 == 0 || move->y2 == 7)) {
X	    to->type = (PieceType) move->newPieceType;
X	    fprintf(tfile, "(%c)", pieceChars[move->newPieceType]);
X	    moveSize += 3;
X	}
X	/* if this put the opposite side in check */
X	if (inCheck(board, color == WHITE ? BLACK : WHITE)) {
X	    switch(trType) {
X	    case TR_FORMAL_NORMAL:
X		fputc('+', tfile);
X		moveSize++;
X		break;
X	    case TR_MIN_NORMAL:
X		fputs("ch", tfile);
X		moveSize += 2;
X		break;
X	    }
X	}
X	if (color == BLACK)
X	    fputc('\n', tfile);
X    }
X    WhoseMoveMessage("Transcript written");
X    fclose(tfile);
X}
X
X/*
X * save game file format:
X *
X * int MyColor			color of player saving game
X * int InitialTurn		whose turn it was initially
X * int IsMachine[2]		whether each color is a machine
X * Square board[8][8]		initial board state
X * if (IsMachine[0])
X *	int chessSaveFileSize	# of bytes for embedded chess.out
X * 	char[...]		chess.out
X * endif
X * if (IsMachine[1])
X *	int chessSaveFileSize	# of bytes for embedded chess.out
X * 	char[...]		chess.out
X * endif
X * Move[...]			list of moves
X */
X
X/*
X * save the game 
X */
Xvoid 
XSaveGame(filename)
X    char * filename;
X{
X    FILE * gameFile, * chessOutFile;
X    register MOVELISTNODE * mlnp;
X    register int i, j;
X    int chessOutSize;
X
X    if ((gameFile = fopen(filename, "w")) == (FILE *) 0) {
X	Message("Can't open save file!");
X	return;
X    } else {
X	Message("Saving game...");
X    }
X    if (fwrite((caddr_t) &MyColor, sizeof(MyColor), 1, gameFile) != 1
X    || fwrite((caddr_t) &InitialTurn, sizeof(InitialTurn), 1, gameFile) != 1
X    || fwrite((caddr_t) IsMachine, sizeof(IsMachine), 1, gameFile) != 1
X    || fwrite((caddr_t) InitialBoard, sizeof(InitialBoard), 1, gameFile) != 1)
X    {
X	Message("Write error - save aborted");
X	return;
X    }
X    for (i = 0 ; i < 2 ; i++) {
X	if (IsMachine[i]) {
X	    chessOutSize = MachineSave(i);
X	    if ((chessOutFile = fopen("/tmp/chess.out", "r")) == (FILE *) 0) {
X		Message("Can't open chess.out!");
X		return;
X	    }
X	    if (fwrite((caddr_t) &chessOutSize, sizeof(chessOutSize), 1, gameFile) != 1) {
X		Message("Write error - save aborted");
X		return;
X	    }
X	    while ((j = fgetc(chessOutFile)) != EOF) {
X		fputc(j, gameFile);
X		chessOutSize--;
X	    }
X	    fclose(chessOutFile);
X	    unlink("/tmp/chess.out");
X	    if (chessOutSize != 0) {
X		Message("Write error - save aborted");
X		return;
X	    }
X	}
X    }
X    /*
X     * save everything except trailing resignations
X     */
X    for (mlnp = firstMove ; 
X    mlnp != (MOVELISTNODE *) 0 && mlnp->move.x2 >= 0 ; 
X    mlnp = mlnp->next) {
X	if (fwrite((caddr_t) &mlnp->move, sizeof(mlnp->move), 1, gameFile) != 1)
X	{
X	    Message("Write error - save aborted");
X	    return;
X	}
X    }
X    fclose(gameFile);
X    WhoseMoveMessage("Game saved");
X}
X
X/*
X * note: my color has been previously read
X */
Xvoid
XRestoreGame()
X{
X    Move move;
X    SetupChange setup;
X    register int i, j;
X    BOOL wasMachine[2];
X    FILE * chessOutFile;
X    int chessOutSize;
X
X    if (fread((caddr_t) &InitialTurn, sizeof(InitialTurn), 1, RestoreFile) != 1
X    || fread((caddr_t) wasMachine, sizeof(wasMachine), 1, RestoreFile) != 1) {
X	Message("Read error - restore aborted");
X	goto abortRestore;
X    }
X    if (wasMachine[0] != IsMachine[0] || wasMachine[1] != IsMachine[1]) {
X	Message("Restore file type mismatch - restore aborted");
X	goto abortRestore;
X    }
X    Turn = InitialTurn;
X    for (i = 0 ; i < 8 ; i++) {
X	for (j = 0 ; j < 8 ; j++) {
X	    if (fread((caddr_t) &InitialBoard[i][j].type, 
X		sizeof(InitialBoard[i][j].type), 
X		1, RestoreFile) != 1
X	    || fread((caddr_t) &InitialBoard[i][j].color, 
X		sizeof(InitialBoard[i][j].color), 
X		1, RestoreFile) != 1)
X	    {
X		Message("Read error - restore truncated");
X		goto abortRestore;
X	    } else {
X		setup.type = MainBoard[i][j].type = InitialBoard[i][j].type;
X		setup.color = MainBoard[i][j].color = InitialBoard[i][j].color;
X		setup.x = j;
X		setup.y = i;
X		SendSetupChange(&setup, PeerColor);
X	    }
X	}
X    }
X    for (i = 0 ; i < 2 ; i++) {
X	if (wasMachine[i]) {
X	    if ((chessOutFile = fopen("/tmp/chess.out", "w")) == (FILE *) 0) {
X		Message("Can't create /tmp/chess.out - restore aborted");
X		goto abortRestore;
X	    }
X	    if (fread((caddr_t) &chessOutSize, sizeof(chessOutSize), 1, RestoreFile) != 1) {
X		Message("Read error - restore aborted");
X		fclose(chessOutFile);
X		goto abortRestore;
X	    }
X	    while (chessOutSize--) {
X		if ((j = fgetc(RestoreFile)) == EOF) {
X		    Message("Read error - restore aborted");
X		    fclose(chessOutFile);
X		    goto abortRestore;
X		} 
X		fputc(j, chessOutFile);
X	    }
X	    fclose(chessOutFile);
X	    MachineRestore(i);
X	    sleep(4);
X	    unlink("/tmp/chess.out");
X	}
X    }
X    while (fread((caddr_t) &move, sizeof(move), 1, RestoreFile)) {
X	DoMove(&move, FALSE);
X	SendRestoreMove(&move, PeerColor);
X	Turn = OTHERCOLOR(Turn);
X    }
X    /*
X     * if this was a machine vs. machine game, resubmit the last move
X     * to the program whose turn it was.
X     */
X    if (wasMachine[BLACK] && wasMachine[WHITE]) {
X	SendMachineMove(&lastMove->move, Turn);
X    /*
X     * else if the peer is not a machine, turn him loose
X     */
X    } else if ( ! IsMachine[PeerColor]) {
X	SendEndRestore();
X    }
XabortRestore:
X    fclose(RestoreFile);
X    WhoseMoveMessage((char *) 0);
X}
X
END_OF_board.c
if test 55954 -ne `wc -c <board.c`; then
    echo shar: \"board.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0