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