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