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