[comp.sources.games] v03i087: cchess - corrispondence chess, Part02/05

games-request@tekred.TEK.COM (03/02/88)

Submitted by: Jan Wolter <janc@crim.eecs.umich.edu>
Comp.sources.games: Volume 3, Issue 87
Archive-name: cchess/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 5)."
# Contents:  cmd.c play.c
# Wrapped by billr@tekred on Tue Jan 12 12:09:52 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f cmd.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cmd.c\"
else
echo shar: Extracting \"cmd.c\" \(24364 characters\)
sed "s/^X//" >cmd.c <<'END_OF_cmd.c'
X/*    CCHESS USER INTERFACE					version 1.00
X *
X *    (C) Copyright - Jan D. Wolter - Apr 1986
X *
X *    Mostly routines to talk to the user.
X */
X
X#include "cchess.h"
X
X#ifndef NOTERMCAP
Xextern char *DL;
X#endif NOTERMCAP
X
Xint mredraw;	/* flag used for redrawing while reading in move */
X
X/* DOMOVE()
X *
X * This is the move mode driver.  Show board and command menu.  Accept
X * move, forfiet, draw, and playback commands.  This is the real center of
X * the program.
X *
X * If <drawp> is RE_DRAW, then a draw has been proposed, and the options are
X * to accept or reject the draw.  If <drawp> is RE_CANCEL, then a cancelation
X * has been proposed, and the same commands are available.
X */
X
Xdomove(drawp)
Xint drawp;
X{
Xint fr,fc,tr,tc;
Xint row,col;
Xchar new_move[15];
Xchar ch;
Xschar took,p;
Xboolean thru,himcheck,checked,firsttime=TRUE;
Xint i;
X
X    new_move[C_CMD] = 'M';
X    new_move[C_MAT] = ' ';
X
X    myillmoves = 0;
X    scrolled = TRUE;
X    mredraw = 0;
X    must_take = Musttake && canmove(b,mycolor,TRUE);
X
X    /* Loop until he is satisfied with a command */
X    for (;;)
X    {
X#ifndef NOTERMCAP
X	/* make sure there is room for an error message */
X	if (moveok && errcode != E_OK && errcode != E_SY)
X	{
X	    if (!DL && cy == LINES)
X		wputchar('\n');
X	}
X	else
X	    errcode = E_OK;
X#endif NOTERMCAP
X
X	/* draw board - "scrolled" basically just tells us to do so */
X	if (scrolled)
X	{
X	    disp(b,kibitz?WHITE:mycolor);
X	    printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid);
X	    printcom();
X	    wputchar('\n');
X	    if (cy >= LINES)
X		msgarea = LINES - 1;
X	    else
X		msgarea = cy;
X	}
X
X	/* Prompt for a command */
X	if (!moveok)
X	{
X	    /* Not allowed to move - Playback or nothing */
X	    if (!kibitz) wprint("It's not your turn to move.\n");
X	    wprint("Do you want a playback? ");
X	    ch = xenter("YNVOL\014");
X	    if (ch == 'Y')
X	    {
X		ch = 'P';
X		wprint("Yes\n");
X	    }
X	    if (ch == 'N')
X	    {
X		ch = 'Z';
X		wprint("No\n");
X	    }
X	}
X	else if (drawp == RE_NONE)
X	{
X	    /* Normal case - Get a move command */
X	    if (errcode != E_OK)
X	    {
X		prterror();
X		wputchar('\n');
X	    }
X	    errcode = E_OK;
X	    if (firsttime)
X	    {
X		if (movecnt < 2)
X		    wprint("Help, ");
X		else if (tamemoves >= MAXTAME)
X		    wprint("Under the 50-move rule, you may unilaterally declare a draw.\n");
X		firsttime = FALSE;
X	    }
X	    wprint("Move, Playback, Forfeit or Draw? ");
X	    if (mredraw > 0)
X		ch = 'M';
X	    else
X		ch = xenter("MPFDECQVH?OL\014");
X	}
X	else
X	{
X	    /* A draw or cancellation has been proposed */
X	    if (drawp == RE_DRAW)
X		wprint("A draw");
X	    else
X		wprint("A cancellation");
X	    wprint(" has been proposed.\nAccept or Reject? ");
X	    ch = xenter("PARVEQOL\014");
X	}
X
X	switch (ch)
X	{
X	case 'H':
X	case '?':
X	    wprint("Help\n");
X
X	    initmore();
X	    mprint("Move -     Enter a chess move\n");
X	    mprint("Playback - View a replay of previous moves\n");
X	    mprint("Forfeit -  Admit that the other player won\n");
X	    mprint("Draw -     Propose that the game be a draw\n");
X	    mprint("Last -     Display date of last move made\n");
X	    mprint("Options -  Display the current game options\n");
X	    mprint("^L -       Redraw the screen\n");
X	    mprint("Exit -     Exit the program without moving\n");
X	    mprint("Cancel -   Propose that the game be abandoned\n");
X	    break;
X
X	case 'L':
X	    wprintf("Last move made on %s\n", cday(lastday));
X	    break;
X
X	case 'O':
X	    wprint("Options\n");
X	    initmore();
X	    printopts(mprintf);
X	    break;
X
X	case 'M':
X	    wprint("Move\n");
X	    ++consmade;
X	    took = taken;
X	    checked = incheck;
X
X	    /* Get his move */
X	    mget(mycolor,new_move+C_MOV,&fr,&fc,&tr,&tc);
X	    if (mredraw > 0)
X	    {
X		scrolled = TRUE;
X		--consmade;
X		break;
X	    }
X
X	    /* If it's an illegal move, give it up */
X	    if (!(thru = (errcode==E_MC && consmade < consmoves && Thrucheck)))
X	    {
X		if (errcode == E_OK)
X		    wputchar('\n');
X		else
X		{
X		    consmade--;
X		    break;
X		}
X	    }
X
X	    /* Test if other player is in check */
X	    if (Kingpiece(-mycolor) != SQ)
X	    {
X		find(bc,Kingpiece(-mycolor),&row,&col);
X		himcheck = check(bc,mycolor,row,col);
X	    }
X	    else
X		himcheck = FALSE;
X
X	    /* Has enemy been stripped of all forces? */
X	    if (Stripresult != RE_NONE && taken != SQ && !forces(-mycolor,bc))
X	    {
X		stripped = TRUE;
X		wprint("Capture of all enemy pieces ");
X		switch (Stripresult)
X		{
X		case RE_LOSE:
X		    wprintf("is a win for you.\n");
X		    break;
X		case RE_WIN:
X		    wprintf("is a win for the other player.\n");
X		    break;
X		case RE_DRAW:
X		    wprintf("forces a draw.\n");
X		    break;
X		}
X	    }
X	    else if (consmade < consmoves)
X	    {
X		/* May not apply check until last move */
X		if (himcheck)
X		{
X		    errcode = E_MM;
X		    consmade--;
X		    break;
X		}
X		repeated = inmate = FALSE;
X		incheck = thru;
X	    }
X	    else
X	    {
X		/* Test if other player is in mate */
X		incheck = himcheck;
X		inmate = !canmove(bc,-mycolor,FALSE);
X
X		/* How often has this position has occured? */
X		if (inmate || !istame || tamemoves >= MAXTAME-1)
X		    repeated = FALSE;
X		else
X		    repeated = isrepeat(b,bc);
X	    }
X
Xdispnew:    /* Display the new position */
X#ifndef NOTERMCAP
X	    if (!scrolled && issmart && (!Kriegspiel || !inmate))
X		doupdate(b,bc,new_move,TRUE);
X	    else
X	    {
X#endif NOTERMCAP
X		if (inmate) hidden = FALSE;
X		disp(bc, kibitz ? WHITE : mycolor);
X		printmov(new_move,myillmoves,hisid);
X		printcom();
X		wputchar('\n');
X#ifndef NOTERMCAP
X	    }
X#endif NOTERMCAP
X	    /* Warn him about effects of stalemate */
X	    if (inmate && !incheck)
X	    {
X		wprint("Stalemate ");
X		switch(Staleresult)
X		{
X		case RE_WIN:
X		    wprint("is a win for the other player.\n");
X		    break;
X		case RE_LOSE:
X		    wprint("is a win for you.\n");
X		    break;
X		case RE_DRAW:
X		    wprint("forces a draw.\n");
X		    break;
X		}
X	    }
X
X	    /* Let him declare a draw under fifty move rule */
X	    if (repeated)
X	    {
X		wprint("This position has occurred twice before.\n");
X		wprint("Would you like to declare a draw?");
X		repeated = (enteryn());
X	    }
X
X	    /* Give him a chance to change his mind */
X	    if (!Kriegspiel)
X	    {
X		if (Help_me && (consmade == consmoves) &&
X		               (p = compkib(bc,mycolor)) != SQ)
X		    wprintf("Your %s may be at risk.\n", piecename[p]);
X
X		wprint("Are you sure? ");
X		if ((ch = enter("YN\014")) == '\014')
X		{
X		    /* Redraw the screen */
X		    scrolled = TRUE;
X		    goto dispnew;
X		}
X		if (ch == 'N')
X		{
X		    /* Don't make this move after all */
X		    wprint("o\n");
X		    taken = took;
X		    incheck = checked;
X		    consmade--;
X		    inmate = FALSE;
X		    stripped = FALSE;
X		    errcode = E_OK;
X#ifndef NOTERMCAP
X		    if (issmart)
X			doupdate(bc,b,mbuffer,FALSE);
X		    else
X#endif NOTERMCAP
X			scrolled = TRUE;
X		    break;
X		}
X		else
X		    wprint("es\n");
X	    }
X
X	    savemove((consmoves == consmade || stripped)?'M':'m',
X		new_move+C_MOV);
X
X	    /* Finish off ended games */
X	    if (inmate || repeated || stripped)
X	    {
X		copy(bc,b);	/* So playbacks will work right */
X		if (incheck && inmate)
X		    askplayback(RE_WIN,EV_CHECKMATE);
X		else if (inmate)
X		    askplayback(flip(Staleresult),EV_STALEMATE);
X		else if (stripped)
X		    askplayback(flip(Stripresult),EV_FORCES);
X		else
X		    askplayback(RE_DRAW,EV_REPEATED);
X		asktrans();
X		return;
X	    }
X	    if (consmoves == consmade)
X		return;
X	    else
X	    {
X		/* Multi-move game: ask him for another */
X		errcode = E_OK;
X		copy(bc,b);
X		must_take = Musttake && canmove(b,mycolor,TRUE);
X		break;
X	    }
X
X	case 'F':
X	    wprint("Forfeit\nDo you want to forfeit? ");
X	    if (!enteryn()) break;
X
X	    if (Kriegspiel)
X	    {
X		hidden = FALSE;
X		disp(bc, kibitz ? WHITE : mycolor);
X		printmov(new_move,myillmoves,hisid);
X		printcom();
X		wputchar('\n');
X	    }
X
X	    savemove('F',"forf ");
X	    askplayback(RE_LOSE,EV_FORFIET);
X	    asktrans();
X	    return;
X
X	case 'D':  /* Propose Draw */
X	    wprintf("Draw\nDo you want to %s a draw? ",
X		    (tamemoves >= MAXTAME)?"declare":"propose");
X	    if (!enteryn()) break;
X
X	    savemove('D',"draw?");
X
X	    /* Unilateral if more than fifty moves have been made */
X	    if (tamemoves >= MAXTAME)
X	    {
X		askplayback(RE_DRAW,EV_FIFTYMOVES);
X		asktrans();
X	    }
X	    return;
X
X	case 'C':  /* Propose Cancelation */
X	    wprint("Cancel\n");
X	    wprint("Do you want to propose that this game be discarded? ");
X	    if (!enteryn()) break;
X	    savemove('K',"canc?");
X	    return;
X
X	case 'A':  /* Accept Draw or Cancellation */
X	    wprint("Accept\n");
X	    savemove('Y',"yes  ");
X	    askplayback(drawp,EV_AGREEMENT);
X	    asktrans();
X	    return;
X
X	case 'R': /* Reject Draw or Cancellation */
X	    wprint("Reject\n");
X	    savemove('N',"no   ");
X	    return;
X
X	case 'Q': /* Quit */
X	    wprint("Quit\n");
X	case 'E': /* Exit */
X	    if (ch == 'E') wprint("Exit\n");
X	    if (!interupt)
X	    {
X		wprint("Sorry, not until you've moved...");
X		break;
X	    }
X	    wprint("Do you really want to ");
X	    if (eachgame)
X		wprint("skip to the next game? ");
X	    else
X		wprint("exit the program? ");
X	    if (!enteryn()) break;
X
X	case 'Z': /* Exit without asking for confirmation */
X	    return;
X
X	case 'P': /* Playback previous positions */
X	    if (moveok) wprint("Playback\n");
X	    if (playcnt == 0)
X	    {
X		wprint("No moves to Playback.\n");
X		break;
X	    }
X
X	    wprintf("How many moves (1-%d) do you want replayed? ",
X		playcnt+(consmade > 0));
X	    if ((i=readint(playcnt + (consmade > 0))) > 0)
X	    {
X		if (!issmart) wputchar('\n');
X		playback(playcnt - i + (consmade > 0));
X	    }
X
X	    wputchar('\n');
X	    /* If move arrives during replay...experiment */
X	    if (!moveok && !kibitz && mycolor == toplay)
X	    {
X		moveok = TRUE;
X		fseek(wfp,0L,2);
X	    }
X	    break;
X
X	case '\014': /* Control-L redraws the screen */
X	    wprint("Redraw\n");
X	    scrolled = TRUE;
X	    break;
X
X	case 'V':  /* Version */
X	    versprint();
X	    break;
X	}
X    }
X}
X
X
X
X/* DOMATE()
X *
X * End a game.  There has been a checkmate, or the game has been drawn.
X *
X * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what
X *	     became of the current player.
X *
X * <why>  is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED,
X *	     or EV_AGREEMENT depending on why the game ended.
X */
X
Xdomate(what,why)
Xchar what,why;
X{
X	disp(b,kibitz?WHITE:mycolor);
X	printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid);
X	printcom();
X
X	wputchar('\n');
X	askplayback(what,why);
X
X	if (moveok)
X	{
X		/* MUST do the endgame *before* the transcript, */
X		/* curious though it seems.                     */
X		endgame(what);
X		asktrans();
X	}
X}
X
X/*  ASKPLAYBACK()
X *
X * Ask the player for a playback, and keep asking him until he doesn't want one.
X * Print the message before asking each time.  It displays both sides, even if
X * the game is kriegspiel.  It is only meant to be called after a game is over.
X *
X * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what
X *	     became of the current player.
X *
X * <why>  is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED,
X *	     or EV_AGREEMENT depending on why the game ended.
X */
X
Xaskplayback(what,why)
Xchar what,why;
X{
Xstatic char tbuf[70];
Xboolean flag;
Xint i;
X
X	if (hidden) scrolled = TRUE;
X	hidden = FALSE;		/* No reason to hide moves anymore */
X
X	switch (what)
X	{
X	case RE_WIN:
X		if (solo && !kibitz)
X			strcpy(tbuf,"Congratulations!\nYou have won");
X		else
X			sprintf(tbuf,"%s has won",
X				(mycolor == toplay) ? myid : hisid);
X		break;
X	case RE_LOSE:
X		if (solo && !kibitz)
X			strcpy(tbuf,"You have lost");
X		else
X			sprintf(tbuf,"%s has won",
X				(mycolor != toplay) ? myid : hisid);
X		break;
X	case RE_DRAW:
X		strcpy(tbuf,"The game is drawn");
X		break;
X	case RE_CANCEL:
X		strcpy(tbuf,"The game is canceled");
X		break;
X	}
X	switch (why)
X	{
X	case EV_CHECKMATE:
X		strcat(tbuf," by a checkmate.");
X		break;
X	case EV_STALEMATE:
X		strcat(tbuf," by a stalemate.");
X		break;
X	case EV_FORCES:
X		strcat(tbuf," by lose of forces.");
X		break;
X	case EV_FIFTYMOVES:
X		strcat(tbuf," under the fifty move rule.");
X		break;
X	case EV_REPEATED:
X		strcat(tbuf," because of repeated positions.");
X		break;
X	case EV_AGREEMENT:
X		strcat(tbuf," by mutual agreement.");
X		break;
X	case EV_FORFIET:
X		strcat(tbuf," by a forfieting.");
X		break;
X	}
X
X	do
X	{
X		wprint(tbuf);
X		if (playcnt == 0 && consmade == 0)
X		{
X			wputchar('\n');
X			return;
X		}
X		wprint("\nDo you want a playback? ");
X		if (flag = enteryn())
X		{
X			wprintf("How many moves (1-%d) do you want replayed? ",
X				playcnt+(consmade > 0));
X			if ((i=readint(playcnt + (consmade > 0))) > 0)
X			{
X				if (!issmart) wputchar('\n');
X				playback(playcnt - i + (consmade > 0));
X				wputchar('\n');
X			}
X		}
X	} while (flag);
X}
X
X#ifndef NOTERMCAP
X/* DOUPDATE()
X *
X *Update() plus update the labels on the display.
X *
X */
X
Xdoupdate(bo,bn,move,mine)
Xschar bo[R_SIZE][C_SIZE], bn[R_SIZE][C_SIZE];
Xchar *move;
Xboolean mine;
X{
Xshort ox=cx, oy=cy;
X
X	update(bo,bn);
X	printmov(move,mine?myillmoves:illmoves,mine?myid:hisid);
X	cursor(ox,oy);
X}
X#endif NOTERMCAP
X
X/* MGET()
X *
X *   Read a possibly illegal move from the player.  Set the error code if he
X *   Gave an illegal response.  Note that E_SY can only result if he hits
X *   return prematurely.  It is not really an error; it is used as a command
X *   cancellation.
X */
X
Xmget(co,buffer,fr,fc,tr,tc)
Xchar *buffer;
Xint *fr,*fc,*tr,*tc;		/* from (row, column) to (row, column) */
Xint co;
X{
Xchar ch;
X
X	if (consmoves > 1)
X		wprintf("Enter your move (%d of %d): ",consmade,consmoves);
X	else
X		wprint("Enter your move: ");
X	if (mread(buffer))
X		if (mconv(buffer,fr,fc,tr,tc) &&
X		    legal(co,*fr,*fc,*tr,*tc))
X		{
X			if (promote)
X			{
X			    /* Patch up pawn promotion */
X			    if (Promotion == PR_MINISTER)
X				/* Only Ministers can be promoted to */
X				bc[*tr][*tc] = promote = WM*co;
X			    else if (Promotion == PR_NONE)
X				/* Can't Promote */
X				bc[*tr][*tc] = promote = WP*co;
X			    else
X			    {
X				/* Ask the player what to promote to */
X				for(;;)
X				{
X				    wputchar('\n');
X				    wprint("Promote pawn to: ");
X				    if ((ch = xenter("QRBNK\014"))=='\014')
X				    {
X					errcode = E_OK;
X					mredraw = 5;
X					return;
X				    }
X				    promote = PIECES-(index(pc,ch)-pc);
X				    wprint(piecename[promote]);
X				    if (promote != Kingtype(co))
X					break;
X				    wprint("\nCan't promote to mateable piece");
X				}
X
X				bc[*tr][*tc] = promote *= co;
X			    }
X			}
X			errcode = E_OK;
X			return;
X		}
X		else    /* Illegal move */
X		{	
X			/* Unless he should have know better, record it */
X			if (Kriegspiel &&
X			    ( (errcode == E_CP) || (errcode == E_CT) ||
X			      (errcode == E_PI) || (errcode == E_QB) ||
X			      (errcode == E_BB) || (errcode == E_BR) ||
X			      (errcode == E_MC) || (errcode == E_MM) ) )
X			{
X				interupt = FALSE;
X				myillmoves++;
X				fprintf(wfp,"%5ld:I %5.5s\n",day(),buffer);
X			}
X			wputchar('\n');
X		}
X	else
X		if (mredraw == 0)
X			errcode = E_SY;
X	return;
X}
X
X/* PERROR()
X *
X *   Print error messages.  Current error is given by the global errcode.
X */
X
X/* Three common messages */
Xstatic char kierrmsg[] = "Illegal Move.";
Xstatic char kcerrmsg[] = "Can't Castle.";
Xstatic char scerrmsg[] = "Castling has not been invented.";
X
Xprterror()
X{
X	/* If no error, do nothing */
X	if (errcode == E_OK) return;
X
X	wprint("Error:  ");
X
X	switch (errcode)
X	{
X	case E_MT:		/* move from empty square */
X		wprint("No friendly piece in starting square.");
X		break;
X	case E_AF:		/* attack on friendly piece */
X		wprint("Can't capture your own pieces.");
X		break;
X	case E_EQ:		/* source and destination square are equal */
X		wprint("Null move.");
X		break;
X	case E_SY:		/* Move syntax error */
X		wprint("Command syntax unrecognizable.");
X		break;
X	case E_CR:
X		wprint("Must capture if possible.");
X		break;
X	case E_NC:
X		wprint("Piece may not capture.");
X		break;
X	case E_CP:
X		if (Oldcastle)
X			wprint(scerrmsg);
X		else if (hidden)
X			wprint(kcerrmsg);
X		else
X			wprint("Not in position to castle.");
X		break;
X	case E_CM:
X		if (Oldcastle)
X			wprint(scerrmsg);
X		else
X			wprint("Can't castle after moving king or rook.");
X		break;
X	case E_CT:
X		if (Oldcastle)
X			wprint(scerrmsg);
X		else if (hidden)
X			wprint(kcerrmsg);
X		else
X			wprint("Can't castle through or out of check.");
X		break;
X	case E_MC:		/* Move into Check */
X		if (hidden)
X			wprint(kierrmsg);
X		else
X			wprint("Move leaves you in check.");
X		break;
X	case E_MM:		/* No Check on Multi-Moves */
X		if (hidden)
X			wprint(kierrmsg);
X		else
X			wprint("Can't apply check until last move.");
X		break;
X	case E_CC:		/* Ta'biyat violation */
X		wprint("Can't cross center on first turn.");
X		break;
X	case E_IL:		/* Illegal move */
X		wprint(kierrmsg);
X		break;
X	case E_KI:		/* Illegal move of King or Jester */
X	case E_JI:
X		wprintf("%s moves only to adjacent squares.",
X			piecename[errcode & 033]);
X		break;
X	case E_QI:		/* Illegal move of Queen */
X		wprintf("%s moves only along rows, columns and diagonals.",
X			piecename[WQ]);
X		break;
X	case E_BI:		/* Illegal move of Bishop */
X		wprintf("%s moves only along diagonals.",piecename[WB]);
X		break;
X	case E_NI:		/* Illegal move of Knight */
X		wprintf("%s moves two squares straight and one to the side.",
X			piecename[WN]);
X		break;
X	case E_RI:		/* Illegal move of Rook */
X		wprintf("%s moves only along rows or columns.",piecename[WR]);
X		break;
X	case E_PI:		/* Illegal move of Pawn */
X		if (hidden)
X			wprint(kierrmsg);
X		else
X			wprintf("%s moves forward or captures diagonally.",
X				piecename[WP]);
X		break;
X	case E_MI:		/* Illegal move of Minister */
X		wprintf("%s moves only to diagonally adjacent squares.",
X			piecename[WM]);
X		break;
X	case E_EI:		/* Illegal move of Elephant */
X		wprintf("%s moves two squares diagonally.",piecename[WE]);
X		break;
X	case E_DI:		/* Illegal move of Duke */
X		wprintf("%s moves only to orthogonally adjacent squares.",
X			piecename[WD]);
X		break;
X	case E_HI:		/* Illegal move of Maharajah */
X		wprintf("%s moves like knight or like queen.",piecename[WH]);
X		break;
X	case E_KB:		/* Blocked move of Kamikazi King */
X		if (hidden)
X			wprint(kierrmsg);
X		else
X			wprintf("%s can't capture in this version of chess.",
X				piecename[WK]);
X		break;
X	case E_QB:		/* Blocked move of Queen */
X	case E_BB:		/* Blocked move of Bishop */
X	case E_BR:		/* Blocked move of Rook */
X	case E_BH:		/* Blocked move of Maharajah */
X		if (hidden)
X			wprint(kierrmsg);
X		else
X			wprintf("%s can't jump over other pieces.",
X				piecename[errcode & 033]);
X		break;
X	default:
X		wprintf("Weird Error %d.",errcode);
X	}
X}
X
X/* MREAD()
X *
X *   Read a move from the terminal, return FALSE if player chickens out
X *   or asks for a redraw.  In the latter case, mredraw is set.  Supports
X *   backspaces.  Sorry Edsger.
X */
X
Xchar nums[] = "87654321\b\n\014";
Xchar lets[] = "LKJIHGFEDCBA\b\n\014";
X
Xmread(buffer)
XREGISTER char *buffer;
X{
Xregister int m;
Xshort ox=cx;
X
X	if (mredraw > 0)
X	{
X		m = mredraw;
X		mredraw = 0;
X		if (m == 1) goto n0;
X		wputchar(buffer[0]);
X		if (m == 2) goto n1;
X		wputchar(buffer[1]);
X		wputchar('-');
X		if (m == 3) goto n2;
X		wputchar(buffer[3]);
X		if (m == 4) goto n3;
X		wputchar(buffer[4]);
X		return(TRUE);
X	}
X
Xn0:	buffer[0] = enter(&lets[C_SIZE-1-Cols]);
X	switch (buffer[0])
X	{
X	case '\b':
X		bell();
X		wputchar(' ');
X		goto n0;
X	case '\014':
X		mredraw = 1;
X	case '\n':
X		return (FALSE);
X	}
X
Xn1:	buffer[1] = enter(&nums[R_SIZE-1-Rows]);
X	switch (buffer[1])
X	{
X	case '\b':
X		if (erases)
X		{
X			wputchar(' ');
X			backspace();
X		}
X		else
X			indent(ox-1);
X		goto n0;
X	case '\014':
X		mredraw = 2;
X	case '\n':
X		return (FALSE);
X	}
X
X	buffer[2] = '-';
X	wputchar('-');
X
Xn2:	buffer[3] = enter(&lets[C_SIZE-1-Cols]);
X	switch (buffer[3])
X	{
X	case '\b':
X		if (erases)
X		{
X			backspace();
X			wprint(blanks(2));
X			backspace();
X			backspace();
X		}
X		else
X		{
X			indent(ox-1);
X			wputchar(buffer[0]);
X		}
X		goto n1;
X	case '\014':
X		mredraw = 3;
X	case '\n':
X		return (FALSE);
X	}
X
Xn3:	buffer[4] = enter(&nums[R_SIZE-1-Rows]);
X	switch (buffer[4])
X	{
X	case '\b':
X		if (erases)
X		{
X			wputchar(' ');
X			backspace();
X		}
X		else
X		{
X			indent(ox-1);
X			wputchar(buffer[0]);
X			wputchar(buffer[1]);
X			wputchar(buffer[2]);
X		}
X		goto n2;
X	case '\014':
X		mredraw = 4;
X	case '\n':
X		return (FALSE);
X	}
X
X	return(TRUE);
X}
X
X
X/* PREAD()
X *
X *   Read a position from the terminal, return FALSE if player chickens out.
X *   Supports backspaces.  Sorry Edsger.
X */
X
Xpread(buffer)
Xchar *buffer;
X{
Xshort ox=cx;
X
Xn0:	buffer[0] = enter(&lets[C_SIZE-1-Cols]);
X	if (buffer[0] == '\b')
X	{
X		bell();
X		wputchar(' ');
X		goto n0;
X	}
X	if (buffer[0] == '\n') return (FALSE);
X
X	buffer[1] = enter(&nums[R_SIZE-1-Rows]);
X	if (buffer[1] == '\n') return (FALSE);
X	if (buffer[1] == '\b')
X	{
X		if (erases)
X		{
X			wputchar(' ');
X			backspace();
X		}
X		else
X			indent(ox-1);
X		goto n0;
X	}
X	return(TRUE);
X}
X
X
X/* PLAYBACK()
X *
X *   Display a playback of the game.
X */
X
Xplayback(n)
XREGISTER int n;
X{
Xint fr,fc,tr,tc;
Xregister int cnt,subcnt=0;
Xint ill = 0;
Xboolean wasmoveok;
Xchar probuf[15];
Xchar c;
Xboolean prompt = FALSE;
Xboolean didmove = FALSE;
X
X    if (n < 0)
X    {
X	wputchar('\n');
X	return;
X    }
X    rewind(rfp);
X    wasmoveok = moveok;
X    moveok = FALSE;
X    /* Read in the initial placement */
X    do
X    {
X	if (fgetl(mbuffer,MB_LEN,rfp)==NULL)
X	{
X		printf("Panic: No accept line\n");
X		done(1);
X	}
X	if (mbuffer[C_CMD] == 'C')
X		copy(ib,bc);
X	else if (mbuffer[C_CMD] == 'P')
X		place(bc,mbuffer[C_MOV],mbuffer+C_MOV+2);
X    } while (mbuffer[C_CMD] != 'A');
X
X    cnt = 0;
X    toplay = WHITE;
X    for (;;)
X    {
X	/* Prompt the user */
X	if (prompt)
X	{
X		cprint("*Hit return for next board:");
X		c=xenter("\nQ\014");
X		if (!issmart) wputchar('\n');
X		if (c != '\n')
X			if (c == 'Q')
X			{
X				/* No more updates or prompts */
X				n = playcnt + (consmade > 0);
X				prompt = FALSE;
X			}
X			else
X			{
X				/* Redraw the screen */
X				disp(b,kibitz?WHITE:mycolor);
X				wprint("\n\n");
X				continue;
X			}
X	}
X
X	/* Display the position */
X	if (cnt >= n || (cnt == playcnt && consmade == subcnt))
X	{
X#ifndef NOTERMCAP
X		if (cleardown && !scrolled)
X		{
X			update(b,bc);
X			if (didmove)
X				printmov(mbuffer,ill,
X					toplay == mycolor ? hisid : myid);
X			clbot();
X		}
X		else
X		{
X#endif NOTERMCAP
X			disp(bc,kibitz?WHITE:mycolor);
X			if (didmove)
X				printmov(mbuffer,ill,
X					toplay == mycolor ? hisid : myid);
X#ifndef NOTERMCAP
X		}
X#endif NOTERMCAP
X		prompt = TRUE;		/* Prompt after a redraw */
X		copy(bc,b);
X		if (cnt != 0)
X			printcom();
X		else
X			wputchar('\n');
X	}
X
X	ill = 0;
X	/* Read until we find a move line */
X	for( ; ; )
X	{
X		if (fgetl(mbuffer,MB_LEN,rfp)==NULL)
X		{
X		    moveok = wasmoveok;
X		    return;
X		}
X		if (mbuffer[C_CMD] == 'I')
X		    ill++;
X		else if (mbuffer[C_CMD] != 'R')
X		    break;
X	}
X	didmove = TRUE;
X
X	if (mbuffer[C_CMD] == 'm' || mbuffer[C_CMD] == 'M')
X	{
X		/* Checked or Mated? */
X		incheck = (mbuffer[C_MAT] == 'C' || mbuffer[C_MAT] == 'M');
X		inmate  = (mbuffer[C_MAT] == 'M' || mbuffer[C_MAT] == 'S');
X
X		/* Play it on the board */
X		if (mconv(mbuffer+C_MOV,&fr,&fc,&tr,&tc))
X		{
X			makemove(bc,toplay,fr,fc,tr,tc,FALSE);
X			if (promote)
X			{
X				fgetl(probuf,14,rfp);
X				bc[tr][tc] = promote =
X				    toplay*(PIECES-(index(pc,probuf[C_MOV])-pc));
X			}
X		}
X		else
X		{
X			wprint("Format error:  Bad move syntax.\n");
X			done(1);
X		}
X	}
X	if (mbuffer[C_CMD] != 'm')
X	{
X		cnt++;
X		subcnt = 0;
X		toplay = -toplay;
X	}
X	else
X		subcnt++;
X    }
X}
X
X/* ASKTRANS()
X *
X *  Give him a transcript, if he wants one.
X */
X
Xasktrans()
X{
X	wprint("Do you want a transcript of the game? ");
X	if (enteryn()) trans(hidden);
X}
X
X/* TRANS()
X *
X *   Ask what sort of transcript he wants.  In visual mode.
X *   If <masked> is true, don't print the other players moves.
X */
X
Xtrans(masked)
X{
XFILE *tfp;
Xchar tfile[80];
Xboolean comments;
X
X	clr();
X	wprintf("Printing transcript for game between %s and %s\n",
X		myid,hisid);
X
X	/* Revert to self so output files can be opened */
X	/* Note:  Can't open game files any more after this */
X	setuid(getuid());
X	setgid(getgid());
X
X	/* Get output device */
X	for (;;)
X	{
X		wprint("\nDo you want it in a file? ");
X		if (!enteryn())
X		{
X			tfp = stdout;
X			break;
X		}
X		else
X		{
X			wprint("Enter the filename: ");
X			if (!readstr(tfile,0,79,TRUE)) continue;
X			if ((tfp = fopen(tfile,"w")) == NULL)
X				wprintf("Cannot open %s\n",tfile);
X			else
X				break;
X		}
X	}
X
X	wprint("Do you want comments included? ");
X	comments = enteryn();
X
X	/* In the future, ask about format */
X
X	cctrans(tfp,comments,masked);
X}
END_OF_cmd.c
if test 24364 -ne `wc -c <cmd.c`; then
    echo shar: \"cmd.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f play.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"play.c\"
else
echo shar: Extracting \"play.c\" \(26495 characters\)
sed "s/^X//" >play.c <<'END_OF_play.c'
X/* CCHESS CHESS PLAYING ROUTINES.				Version 1.00
X *
X *   Routines to enforce rules and test for mate and such fun stuff.  These
X *   routines could be integrated much more, I think, but too much time elapsed
X *   between phases of development for me to do things right.
X *
X *   (C) Copyright Jan Wolter - Apr 1986.
X */
X
X#include "cchess.h"
X
X/* Values of pieces    ( ,K,Q,B,N,R,P,M,E,D,J,H) */
Xchar value[PIECES+1] = {0,7,6,4,4,5,1,2,2,2,3,7};
X
X/* Number of pieces relocated in last move */
Xstatic int ndiff;
X
Xboolean xcheck();
Xint mcheck();
X
X/* MAKEMOVE()
X *
X *   Make a move, assuming it is legal.  Handle promotion, en passeunt, and
X *   castling.  If <record> is true, note rook and king moves for future
X *   castle legality checking, and also save the move on the stack for
X *   repeated position testing.
X */
X 
Xmakemove(bd,co,fr,fc,tr,tc,record)
Xschar bd[R_SIZE][C_SIZE];
XREGISTER int fr,fc,tr,tc;
Xschar co;
Xboolean record;
X{
Xschar p;
Xint wastaken = taken;
Xint caprule;
X
X	p = bd[fr][fc];			/* Piece moved */
X	taken = bd[tr][tc];		/* Piece taken */
X	promote = 0;			/* No promotion */
X	ndiff = 1;			/* One piece changed */
X	caprule = (p == Whiteking || p == -Blackking) ? Kcapture : Capture;
X
X	switch (p)
X	{
X	case WP:
X	case BP:
X		/* Whenever a pawn moves, we can flush the stack */
X
X		/* Pawn Promotion - for now to goofy piece - Fix up later */
X		if (tr == tside(co) && (taken == SQ ||
X		    (caprule != CA_RIFLE && caprule != CA_CONVERSION)))
X			promote = p = co * XX;
X
X		/* En passeunt capture */
X		if ((fc != tc) && (taken == SQ))
X		{
X			taken = bd[fr][tc];
X			bd[fr][tc] = SQ;
X		}
X		break;
X	case WK:
X	case BK:
X		/* Castling - Move the rook too */
X		if (fc == tc + 2)
X		{
X			bd[fr][tc+1] = bd[fr][0];
X			bd[fr][0] = SQ;
X			ndiff = 2;
X			if (record) pushmove(fr,0,fr,tc+1);
X		}
X		if (fc == tc - 2)
X		{
X			bd[fr][tc-1] = bd[fr][Cols];
X			bd[fr][Cols] = SQ;
X			ndiff = 2;
X			if (record) pushmove(fr,Cols,fr,tc-1);
X		}
X		/* After any king move, can never castle again */
X		if (record)
X		{
X			if (co == WHITE)
X				wcck = wccq = FALSE;
X			else
X				bcck = bccq = FALSE;
X		}
X		break;
X	case WR:
X		/* Rook moves may make future castling impossible */
X		if (record)
X		{
X			if (fc == 0)    wccq = FALSE;
X			if (fc == Cols) wcck = FALSE;
X		}
X		break;
X	case BR:
X		/* Rook moves may make future castling impossible */
X		if (record)
X		{
X			if (fc == 0)    bccq = FALSE;
X			if (fc == Cols) bcck = FALSE;
X		}
X		break;
X	}
X
X	/* Move the piece */
X	bd[tr][tc] = p;
X	bd[fr][fc] = SQ;
X
X	if (taken == SQ)
X		/* If not a capture or pawn move, it is a tame move */
X		istame = (p != WP && p != BP);
X	else
X	{	
X		istame = FALSE;
X
X		/* Handle kamikazi, karma, rifle and conversion captures */
X		switch ((p == Whiteking || p == -Blackking) ?
X			Kcapture : Capture)
X		{
X		case CA_KARMA:
X			/* Capturing piece takes rank of captured one */
X			bd[tr][tc] = -taken;
X			break;
X		case CA_KAMIKAZI:
X			/* Capturing piece destroyed */
X			bd[tr][tc] = SQ;
X			break;
X		case CA_RIFLE:
X			/* Capturing piece stays where it came from */
X			bd[fr][fc] = bd[tr][tc];
X			bd[tr][tc] = SQ;
X			break;
X		case CA_CONVERSION:
X			/* Capturing piece stays where it came from */
X			bd[fr][fc] = bd[tr][tc];
X			/* Captured piece changes sides if not a traitor */
X			bd[tr][tc] = (wastaken != SQ &&
X				      ((co==WHITE && btr==tr && btc==tc) ||
X				       (co==BLACK && wtr==tr && wtc==tc)))
X				      ? SQ : -taken;
X			break;
X		}
X	}
X
X	/* Put tame moves on the stack, flush the stack after others */
X	if (record) 
X	{
X		if (istame)
X			pushmove(fr,fc,tr,tc);
X		else
X			flushmoves();
X	}
X
X	/* Record last move for en passaunt legality checking */
X	if (co == WHITE) { wfr = fr; wfc = fc; wtr = tr; wtc = tc; }
X	            else { bfr = fr; bfc = fc; btr = tr; btc = tc; }
X}
X
X
X/* CHECK()
X *
X *   Is there any piece of color "co" which can attack a square?
X *   Normally, the square in question is the king's location, so this
X *   is a check detector.  Note that it doesn't test if the move leaves
X *   co's king in check.  This would have to be done, if we used it for
X *   checking anything other than attacks on kings.  However, the only
X *   place it is used that way is by the computer kibitzer, which needn't
X *   be that smart.
X */
X
Xboolean check(bd,co,r,c)
Xschar bd[R_SIZE][C_SIZE];
Xschar co;
XREGISTER int r,c;
X{
Xregister int x,y;
X
X	/* If piece is off board, it can't be checked. */
X	if (r < 0 || c < 0 || r > Rows || c > Cols) return(FALSE);
X
X	for (x=0;x<=Rows;x++)
X		for (y=0;y<=Cols;y++)
X			if (xcheck(bd,co,r,c,x,y)) return(TRUE);
X	return(FALSE);
X}
X
X/* Can bd[x][y] attack bd[r][c]? */
X
Xboolean xcheck(bd,co,r,c,x,y)
Xschar bd[R_SIZE][C_SIZE];
Xschar co;
XREGISTER int r,c;
XREGISTER int x,y;
X{
Xregister schar p;
X
X	/* Spaces can't attack, and you can't attack yourself */
X	if ((p = co*bd[x][y]) == SQ || (r == x && c == y))
X		return(FALSE);
X
X	if ((p = co*bd[x][y]) == Kingtype(co))
X	{
X		/* If king can't capture, that's it for it */
X		if (Kcapture == CA_NONE)
X			return(FALSE);
X	}
X	else
X	{
X		/* If piece can't capture other pieces, that's that */
X		if (Capture == CA_NONE && abs(b[r][c]) != Kingtype(-co))
X			return(FALSE);
X	}
X
X	switch (p)
X	{
X	case WK:   /* Enemy King */
X		if ((abs(x-r) <= 1) && (abs(y-c) <= 1)) return (TRUE);
X		break;
X
X	case WQ:   /* Enemy Queen */
X		if (cart(bd,x,y,r,c) || diag(bd,x,y,r,c)) return(TRUE);
X		break;
X
X	case WN:    /* Enemy Knight */
X		if (((abs(x-r) == 2) && (abs(y-c) == 1)) ||
X		    ((abs(x-r) == 1) && (abs(y-c) == 2))) return(TRUE);
X		break;
X
X	case WB:    /* Enemy Bishop */
X		if (diag(bd,x,y,r,c)) return(TRUE);
X		break;
X
X	case WR:    /* Enemy Rook */
X		if (cart(bd,x,y,r,c)) return(TRUE);
X		break;
X
X	case WP:    /* Enemy Pawn */
X		if ((r==x+co) && (abs(y-c)==1)) return(TRUE);
X		break;
X
X	case WM:    /* Enemy Minister */
X		if ((abs(x-r) == 1) && (abs(y-c) == 1)) return(TRUE);
X		break;
X
X	case WE:    /* Enemy Elephant */
X		if ((abs(x-r) == 2) && (abs(y-c) == 2)) return(TRUE);
X		break;
X
X	case WD:    /* Enemy Duke */
X		if ((abs(x-r) + abs(y-c) == 1)) return(TRUE);
X		break;
X
X	case WJ:   /* Enemy Jester */
X		if ((abs(x-r) <= 1) && (abs(y-c) <= 1)) return(TRUE);
X		break;
X
X	case WH:   /* Enemy Maharajah */
X		if (((abs(x-r) == 1) && (abs(y-c) == 2)) ||
X		    ((abs(x-r) == 2) && (abs(y-c) == 1)) ||
X		    cart(bd,x,y,r,c) || diag(bd,x,y,r,c)) return(TRUE);
X		break;
X
X	default: 			/* Friendly or Blank */
X		break;
X	}
X	return(FALSE);
X}
X
X/* COMPKIB()
X *
X *   Computer Kibbitzer.  This is delibrately stupid.  It is ignorant of
X *   pinned pieces and fancy combinations.  It returns the piece code
X *   of the first piece it finds that looks like it may be in danger.  If
X *   it finds none, it returns the code for an empty space.  "co" is the
X *   player we are kibbitzing for.  This implementation is a little boneheaded
X *   too, but not as bad as it looks at first glance.  
X *   
X *   The Computer Kibbitzer finds major pieces which can be taken but are
X *   uncovered or which can be taken by a less valuable piece.  It doesn't
X *   worry about any piece that is no more valuable than the one you took
X *   this turn, since you are probably making a reasonable exchange.
X */
X
Xschar compkib(bd,co)
Xschar bd[R_SIZE][C_SIZE];
Xschar co;
X{
Xregister int x,y;
Xregister int x1,y1;
Xregister schar p;
X
X    /* Look for a major friendly piece */
X    for (x=0;x<=Rows;x++)
X	for (y=0;y<=Cols;y++)
X	    if ((p = bd[x][y]*co) > 0 && p != WP && p != Kingtype(co)
X	    		&& value[p] > value[abs(taken)])
X	    {
X		/* can it be attacked? */
X		for (x1=0;x1<=Rows;x1++)
X		    for (y1=0;y1<=Cols;y1++)
X			if (xcheck(bd,-co,x,y,x1,y1))
X			{
X			    /* Does it need cover? */
X			    if (value[p] > value[-bd[x1][y1]*co])
X				return(p);
X			    else
X				/* Is it covered? */
X				if (!check(bd,co,x,y))
X				    return(p);
X			}
X	    }
X    return(SQ);
X}
X
X
X/* LEGAL()
X *
X *   Is a move legal?  Return FALSE if not, and set error code.
X *   Board "b" gives current position.  If move is legal, board bc has
X *   the new position.  If the global must_take is set, only captures
X *   are considered legal.
X */
X
Xboolean legal(co,fr,fc,tr,tc)
Xint co;				/* player's color */
XREGISTER int fr,fc,tr,tc;	/* from (row, column) to (row, column) */
X{
Xint r,c;
Xint pf, pt;
X
X	pt = b[tr][tc];
X	pf = b[fr][fc];
X
X	/* Is there a friendly piece at start? */
X	if (pf*co <= 0)
X	{
X		errcode = E_MT;
X		return(FALSE);
X	}
X
X	/* Is there any motion? */
X	if ((fr == tr) && (fc == tc))
X	{
X		errcode = E_EQ;
X		return(FALSE);
X	}
X
X	/* Is the move legal? */
X	switch (pf)
X	{
X	case WK:	/* Kings */
X	case BK:
X		/* Castling Queen Side */
X		if (tc == fc-2)
X		{
X			/* Is there a rook in position? */
X			if (fc <= 2 || b[fr][0] != co*WR)
X			{
X				errcode = E_KI;
X				return (FALSE);
X			}
X			/* Has King or Rook been Previously moved? */
X			if (!((co+1)?wccq:bccq))
X			{
X				errcode = E_CM;
X				return (FALSE);
X			}
X			/* Are there any Intervening pieces */
X			if (!cart(b,fr,0,fr,fc))
X			{
X				errcode = E_CP;
X				return (FALSE);
X			}
X			/* Is intervening square under attack? */
X			if ((!Thrucheck || consmoves == consmade) &&
X			    (incheck || check(b,-co,fr,fc-1)))
X			{
X				errcode = E_CT;
X				return (FALSE);
X			}
X			break;
X		}
X
X		/* Castling King Side */
X		if (tc == fc + 2)
X		{
X			/* Is there a rook in position? */
X			if (fc >= Cols - 2 || b[fr][Cols] != co*WR)
X			{
X				errcode = E_KI;
X				return (FALSE);
X			}
X			/* Are King and Rook in their original squares? */
X			if (!((co+1)?wcck:bcck))
X			{
X				errcode = E_CM;
X				return (FALSE);
X			}
X			/* Are intervening squares clear? */
X			if (!cart(b,fr,fc,fr,Cols))
X			{
X				errcode = E_CP;
X				return (FALSE);
X			}
X			/* Is intervening square under attack? */
X			if ((!Thrucheck || consmoves == consmade) &&
X			    (incheck || check(b,-co,fr,fc+1)))
X			{
X				errcode = E_CT;
X				return (FALSE);
X			}
X			break;
X		}
X
X	case WJ:
X	case BJ:
X		/* Ordinary King Move */
X		if ((abs(fr-tr) > 1) || (abs(fc-tc) > 1))
X		{
X			errcode = E_IL | abs(pf);
X			return (FALSE);
X		}
X		break;
X	
X	case WQ:	/* Queens */
X	case BQ:
X		errcode = WQ;
X		if (cart(b,fr,fc,tr,tc) || diag(b,fr,fc,tr,tc))
X			break;
X		else
X			return(FALSE);
X	
X	case WB:	/* Bishops */
X	case BB:
X		errcode = WB;
X		if (diag(b,fr,fc,tr,tc))
X			break;
X		else
X			return(FALSE);
X		
X	case WN:	/* Knights */
X	case BN:
X		if (((abs(fr-tr) == 2) && (abs(fc-tc) == 1)) ||
X		    ((abs(fr-tr) == 1) && (abs(fc-tc) == 2)))
X			break;
X		else
X		{
X			errcode = E_NI;
X			return(FALSE);
X		}
X
X	case WR:	/* Rooks */
X	case BR:
X		errcode = WR;
X		if (cart(b,fr,fc,tr,tc))
X			break;
X		else
X			return(FALSE);
X	
X	case WP:	/* White Pawns */
X
X		errcode = E_PI;
X		if (fc == tc) /* move */
X			if ((pt==SQ) && ((tr==fr+1) ||
X			    ((!Oldpawn)&&(fr==1)&&(tr==3)&&(b[fr+1][fc]==SQ))))
X				break;
X			else
X				return(FALSE);
X
X		if (pt==SQ) /* capture en-passeunt */
X			if ((btr==fr) && (btc==tc) && (bfr==btr+2) &&
X			    (b[btr][btc]==BP) && (tr==fr+1) && (abs(fc-tc)==1))
X				break;
X			else
X				return(FALSE);
X
X		if ((tr==fr+1) && (abs(fc-tc)==1)) /* capture */
X			break;
X		else
X			return(FALSE);
X
X	case BP:	/* Black Pawns */
X
X		errcode = E_PI;
X		if (fc == tc) /* move */
X			if ((pt == SQ) && ((tr==fr-1) ||
X			    ((!Oldpawn)&&(fr==Rows-1)&&(tr==Rows-3)&&(b[fr-1][fc]==SQ))))
X				break;
X			else
X				return(FALSE);
X
X		if (pt==SQ) /* capture en-passeunt */
X			if ((wtr==fr) && (wtc==tc) && (wfr==wtr-2) &&
X			    (b[wtr][wtc]==WP) && (tr==fr-1) && (abs(fc-tc)==1))
X				break;
X			else
X				return(FALSE);
X
X		if ((tr==fr-1) && (abs(fc-tc)==1)) /* capture */
X			break;
X		else
X			return(FALSE);
X
X	case WM:	/* Ministers */
X	case BM:
X		if ((abs(fr-tr) == 1) && (abs(fc-tc) == 1))
X			break;
X		else
X		{
X			errcode = E_MI;
X			return(FALSE);
X		}
X
X	case WE:	/* Elephants */
X	case BE:
X		if ((abs(fr-tr) == 2) && (abs(fc-tc) == 2))
X			break;
X		else
X		{
X			errcode = E_EI;
X			return(FALSE);
X		}
X	
X	case WD:	/* Dukes */
X	case BD:
X		if ((abs(fr-tr) + abs(fc-tc) == 1))
X			break;
X		else
X		{
X			errcode = E_DI;
X			return(FALSE);
X		}
X
X	case WH:	/* Maharajahs */
X	case BH:
X		errcode = WR;
X		if (((abs(fr-tr) == 1) && (abs(fc-tc) == 2)) ||
X		    ((abs(fr-tr) == 2) && (abs(fc-tc) == 1)) ||
X		    cart(b,fr,fc,tr,tc) || diag(b,fr,fc,tr,tc))
X			break;
X		else
X		{
X			if (errcode == WR)
X				errcode = E_HI;
X			return(FALSE);
X		}
X	}
X
X	/* Is there an friendly piece at the destination? */
X	if (pt*co > 0)
X	{
X		errcode = E_AF;
X		return(FALSE);
X	}
X
X	/* Is the piece forbidden to capture? */
X	if (pt*co < 0 && ((pf==Kingpiece(co)) ? Kcapture : Capture) == CA_NONE)
X	{
X		errcode = E_NC;
X		return(FALSE);
X	}
X
X	/* May we cross the center? */
X	if (Tabiyat && movecnt <= 1 &&
X	    ((co == WHITE && tr >= (Rows+1)/2) ||
X	     (co == BLACK && tr <= Rows/2)))
X	{
X		errcode = E_CC;
X		return(FALSE);
X	}
X
X	/* Should it have been a capture? */
X	if (must_take && pt == SQ)
X	{
X		errcode = E_CR;
X		return(FALSE);
X	}
X
X	copy(b,bc);
X	makemove(bc,co,fr,fc,tr,tc,FALSE);
X
X	/* Does this move leave me in check? */
X	if (Kingpiece(co) != SQ)
X	{
X		find(bc,Kingpiece(co),&r,&c);	/* find the friendly king. */
X		if (check(bc,-co,r,c))		/* can an enemy attack him? */
X		{
X			errcode = E_MC;
X			return (FALSE);
X		}
X	}
X	return(TRUE);
X}
X
X/* CART()
X *
X *   Can a legal cartesian (rook) move be made between the two positions?
X */
X
Xboolean cart(bd,r1,c1,r2,c2)
Xschar bd[R_SIZE][C_SIZE];
Xint r1,c1,r2,c2;
X{
Xregister int i,x,y;
X
X	if (r1 == r2)
X	{
X		x = ((c1 < c2) ? c1 : c2) + 1;
X		y = ((c1 < c2) ? c2 : c1) - 1;
X		for (i = x; i<=y; i++)
X			if (bd[r1][i] != SQ)
X			{
X				errcode |= E_MB;
X				return(FALSE);
X			}
X	}
X
X	else if (c1 == c2)
X	{
X		x = ((r1 < r2) ? r1 : r2) + 1;
X		y = ((r1 < r2) ? r2 : r1) - 1;
X		for (i = x; i<=y; i++)
X			if (bd[i][c1] != SQ)
X			{
X				errcode |= E_MB;
X				return(FALSE);
X			}
X	}
X	
X	else
X	{
X		errcode |= E_IL;
X		return(FALSE);
X	}
X
X	return(TRUE);
X}
X
X/* DIAG()
X *
X *   Can a legal diagonal (bishop) move be made between the two positions?
X */
X
Xboolean diag(bd,r1,c1,r2,c2)
Xschar bd[R_SIZE][C_SIZE];
Xint r1,c1,r2,c2;
X{
Xregister int i,x,y,n;
X
X	if (r1+c1 == r2+c2)
X	{
X		x = ((r1 < r2) ? r1 : r2) + 1;
X		y = ((r1 < r2) ? c1 : c2) - 1;
X		n = abs(r1-r2) - 1;
X		for (i=0; i<n; i++)
X			if (bd[x+i][y-i] != SQ)
X			{
X				errcode |= E_MB;
X				return(FALSE);
X			}
X	}
X	else if (r1-c1 == r2-c2)
X	{
X		x = ((r1 < r2) ? r1 : r2) + 1;
X		y = ((r1 < r2) ? c1 : c2) + 1;
X		n = abs(r1-r2) - 1;
X		for (i=0; i<n; i++)
X			if (bd[x+i][y+i] != SQ)
X			{
X				errcode |= E_MB;
X				return(FALSE);
X			}
X	}
X	else
X	{
X		errcode |= E_IL;
X		return(FALSE);
X	}
X	return(TRUE);
X}
X
X/* MCHECK()
X *
X *  Is it Ok for a piece of color co to move from bd[fr][fc] to bd[tr][tc]?
X *   Return one of the following:
X *	0 = move legal
X *	1 = check - move doesn't block check (check before move)
X *	2 = check - piece was pinned (no check before move)
X *	3 = move blocked or off board
X * This is for use by the canmove() routine.  It assumes that the move is
X * of a type that is legal for the piece (e.g. along a diagonal for a
X * bishop).  If <cap> is set, only captures are considered legal, and 1
X * is returned for legal moves which are not captures.
X */
X
Xmcheck(bd,co,fr,fc,tr,tc,cap)
Xschar bd[R_SIZE][C_SIZE];
Xschar co;
XREGISTER int fr,fc,tr,tc;
Xboolean cap;
X{
Xregister schar t,f;
Xboolean flag;
Xint kr,kc;
Xschar king = Kingpiece(co);
X
X	/* Check if destination is legal */
X	if (tr < 0 || tc < 0 || tr > Rows || tc > Cols || (t=bd[tr][tc])*co > 0)
X		return (3);
X
X	/* Make move */
X	f = bd[tr][tc] = bd[fr][fc];
X	bd[fr][fc] = SQ;
X
X	/* Fix up capture in funny Capture modes */
X	/* What type of friendly piece is in a square doesn't matter */
X	if (t*co != SQ)
X		switch ((f == king) ? Kcapture : Capture)
X		{
X		case CA_NONE:
X			bd[tr][tc] = t;		/* Unmake move and abort */
X			bd[fr][fc] = f;
X			return(3);
X		case CA_RIFLE:
X			bd[fr][fc] = f;
X			/* no break here! */
X		case CA_KAMIKAZI:
X			bd[tr][tc] = SQ;
X			break;
X		case CA_CONVERSION:
X			bd[fr][fc] = f;
X			if (taken != SQ &&
X			    ((co==WHITE && btr==tr && btc==tc) ||
X			     (co==BLACK && wtr==tr && wtc==tc)))
X				bd[tr][tc] = SQ;
X			break;
X		}
X
X	/* Check if move leaves you in check */
X	if (king != SQ)
X	{
X		if (f != king)
X		{
X			find(bd,king,&kr,&kc);
X			flag = check(bd,-co,kr,kc);
X		}
X		else
X		{
X			flag = check(bd,-co,tr,tc);
X			if (flag && t != SQ)
X			{
X				bd[tr][tc] = t;
X				bd[fr][fc] = f;
X				return(3);
X			}
X		}
X	}
X	else
X		flag = FALSE;
X
X	/* Unmake move */
X	bd[tr][tc] = t;
X	bd[fr][fc] = f;
X
X	if (flag)
X		return (incheck ? 1 : 2);
X	else
X		return((cap && t==SQ) ? 1 : 0);
X}
X
X/* CANMOVE()
X *
X *  Can any piece of color <co> move on board <bd>?  For Mate detection.
X *  incheck should be set first using the check detection routine.
X *  if <cap> is set, only captures are considered.
X */
X
Xboolean canmove(bd,co,cap)
Xschar bd[R_SIZE][C_SIZE];
Xschar co;
Xboolean cap;
X{
Xregister int r,c;
Xregister int tr,tc;
Xint sr,sc;
Xint code;
X
X/* This bit of weirdness with the return statement is for debugging */
X#define Return(a,b,c,d) return(TRUE);
X/* #define Return(a,b,c,d) {printf("canmove: from %x %x to %x %x\r\n",a,b,c,d);return(TRUE);} */
X
X    for (c=0; c <= Cols; c++)
X	for (r=0; r <= Rows; r++)
X	    switch (co*bd[r][c])
X	    {
X	    case WK:	/* Kings and Jesters */
X	    case WJ:
X		for (tr = r-1; tr <= r+1; tr++)
X		    for (tc = c-1; tc <= c+1; tc++)
X			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
X			    Return (r,c,tr,tc)
X		break;
X
X	    case WQ:	/* Rooks and Queens and Maharajahs */
X	    case WR:
X	    case WH:
X		for (tr = r-1; tr >= 0; tr--)
X		{
X			if ((code = mcheck(bd,co,r,c,tr,c,cap))==0)
X				Return(r,c,tr,c)
X			else
X				if (code != 1) break;
X		}
X
X		for (tr = r+1; tr <= Rows; tr++)
X		{
X			if ((code = mcheck(bd,co,r,c,tr,c,cap))==0)
X				Return(r,c,tr,c)
X			else
X				if (code != 1) break;
X		}
X
X		for (tc = c-1; tc >= 0; tc--)
X		{
X			if ((code = mcheck(bd,co,r,c,r,tc,cap))==0)
X				Return(r,c,r,tc)
X			else
X				if (code != 1) break;
X		}
X
X		for (tr = c+1; tc <= Cols; tc++)
X		{
X			if ((code = mcheck(bd,co,r,c,r,tc,cap))==0)
X				Return(r,c,r,tc)
X			else
X				if (code != 1) break;
X		}
X
X		if (bd[r][c]*co == WR) break;
X
X	    case WB:	/* Bishops and Queens and Maharajahs */
X		for (sr = -1; sr <= 1; sr += 2)
X		    for (sc = -1; sc <= 1; sc += 2)
X			for (tr = 1; tr <= Rows; tr++)
X			{
X			    if ((code = mcheck(bd,co,r,c,
X						r+sr*tr,c+sc*tr,cap))==0)
X				Return(r,c,r+sr*tr,c+sc*tr)
X			    else
X				if (code != 1) break;
X			}
X
X		if (bd[r][c]*co != WH) break;
X
X	    case WN:	/* Knights and Maharajahs */
X		if ((code = mcheck(bd,co,r,c,r+1,c+2,cap)) == 0)
X			Return(r,c,r+1,c+2)
X		else	if (code == 2) break;
X
X
X		if ((code = mcheck(bd,co,r,c,r-1,c-2,cap)) == 0)
X			Return(r,c,r-1,c-2)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r-2,c+1,cap)) == 0)
X			Return(r,c,r-2,c+1)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r+2,c-1,cap)) == 0)
X			Return(r,c,r+2,c-1)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r-1,c+2,cap)) == 0)
X			Return(r,c,r-1,c+2)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r+1,c-2,cap)) == 0)
X			Return(r,c,r+1,c-2)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r-2,c-1,cap)) == 0)
X			Return(r,c,r-2,c-1)
X		else	if (code == 2) break;
X
X		if ((code = mcheck(bd,co,r,c,r+2,c+1,cap)) == 0)
X			Return(r,c,r+2,c+1)
X		break;
X
X	    case WP:	/* White Pawns */
X		if (co == WHITE)
X		{
X		    if (bd[r+1][c] == SQ && !cap)
X		    {
X			if ((code = mcheck(bd,co,r,c,r+1,c,cap)) == 0)
X				Return(r,c,r+1,c)
X			else
X				if (!Oldpawn && code == 1 && r == 1 &&
X				    bd[3][c]==SQ &&
X				    mcheck(bd,co,1,c,3,c,cap) == 0)
X					Return(1,c,3,c)
X		    }
X		    if (c > 0)
X			if (bd[r+1][c-1] < 0)
X				if (mcheck(bd,co,r,c,r+1,c-1,cap) == 0)
X					Return(r,c,r+1,c-1)
X			else /* En Passaunt out of check?  That's a new one. */
X				if (r == btr && c-1 == btc && bfr == btr + 2 &&
X				    bd[btr][btc] == BP)
X				{
X					bd[btr][btc] = SQ;
X					code = mcheck(bd,co,r,c,r+1,c-1,cap);
X					bd[btr][btc] = BP;
X					if (code == 0) Return (r,c,r+1,c-1)
X				}
X		    if (c < Cols)
X			if (bd[r+1][c+1] < 0)
X				if (mcheck(bd,co,r,c,r+1,c+1,cap) == 0)
X					Return(r,c,r+1,c+1)
X			else /* En Passaunt, would you believe? */
X				if (r == btr && c+1 == btc && bfr == btr + 2 &&
X				    bd[btr][btc] == BP)
X				{
X					bd[btr][btc] = SQ;
X					code = mcheck(bd,co,r,c,r+1,c+1,cap);
X					bd[btr][btc] = BP;
X					if (code == 0) Return (r,c,r+1,c+1)
X				}
X	        }
X
X		else  	/* Black Pawns */
X		{
X		    if (bd[r-1][c] == SQ && !cap)
X			if ((code = mcheck(bd,co,r,c,r-1,c,cap)) == 0)
X				Return(r,c,r-1,c)
X			else
X				if (!Oldpawn && code == 1 && r == Rows-1 &&
X				    bd[Rows-3][c]==SQ &&
X				    mcheck(bd,co,Rows-1,c,Rows-3,c,cap) == 0)
X					Return(Rows-1,c,Rows-3,c)
X		    if (c > 0)
X			if (bd[r-1][c-1] > 0)
X				if (mcheck(bd,co,r,c,r-1,c-1,cap) == 0)
X					Return(r,c,r-1,c-1)
X			else /* En Passaunt out of check?  That's a new one. */
X				if (r == wtr && c-1 == wtc && wfr == wtr - 2 &&
X				    bd[wtr][wtc] == WP)
X				{
X					bd[wtr][wtc] = SQ;
X					code = mcheck(bd,co,r,c,r-1,c-1,cap);
X					bd[wtr][wtc] = WP;
X					if (code == 0) Return (r,c,r-1,c-1)
X				}
X		    if (c < Cols)
X			if (bd[r-1][c+1] > 0)
X				if (mcheck(bd,co,r,c,r-1,c+1,cap) == 0)
X					Return(r,c,r-1,c+1)
X			else /* En Passaunt, would you believe? */
X				if (r == btr && c+1 == wtc && wfr == wtr - 2 &&
X				    bd[wtr][wtc] == WP)
X				{
X					bd[wtr][wtc] = SQ;
X					code = mcheck(bd,co,r,c,r-1,c+1,cap);
X					bd[wtr][wtc] = WP;
X					if (code == 0) Return (r,c,r-1,c+1)
X				}
X		}
X		break;
X
X	    case WM:	/* Ministers */
X		for (tr = r-1; tr <= r+1; tr+=2)
X		    for (tc = c-1; tc <= c+1; tc+=2)
X			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
X			    Return (r,c,tr,tc)
X		break;
X
X	    case WE:	/* Elephants */
X		for (tr = r-2; tr <= r+2; tr+=4)
X		    for (tc = c-2; tc <= c+2; tc+=4)
X			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
X			     Return (r,c,tr,tc)
X		break;
X
X	    case WD:	/* Dukes */
X		if (mcheck(bd,co,r,c,r+1,c,cap)==0) Return(r,c,r+1,c)
X		if (mcheck(bd,co,r,c,r-1,c,cap)==0) Return(r,c,r-1,c)
X		if (mcheck(bd,co,r,c,r,c+1,cap)==0) Return(r,c,r,c+1)
X		if (mcheck(bd,co,r,c,r,c-1,cap)==0) Return(r,c,r,c-1)
X		break;
X	    }
X    return(FALSE);
X}
X
X
X/* COPY()
X *
X *   Copy board <b1> into board <b2>
X */
X
Xcopy(b1,b2)
Xschar b1[R_SIZE][C_SIZE], b2[R_SIZE][C_SIZE];
X{
Xint x,y;
X
X	for (x=0;x<=Rows;x++)
X		for (y=0; y<=Cols; y++)
X			b2[x][y] = b1[x][y];
X}
X
X/* COMPARE()
X *
X *   Compare board <b1> to board <b2> and return true if they are equal
X */
X
Xboolean compare(b1,b2)
Xschar b1[R_SIZE][C_SIZE], b2[R_SIZE][C_SIZE];
X{
Xint x,y;
X
X	for (x=0;x<=Rows;x++)
X		for (y=0; y<=Cols; y++)
X			if (b2[x][y] != b1[x][y])
X				return(FALSE);
X	return(TRUE);
X}
X
X
X/* FIND()
X *
X *   Find a piece of type <man> on the board.  Mostly used to locate kings.
X *   return -1,-1 if not on board.
X */
X
Xfind(bd,man,r,c)
Xschar bd[R_SIZE][C_SIZE];
Xschar man;
Xint *r, *c;
X{
X	for (*r=0;*r<=Rows;(*r)++)
X		for (*c=0; *c<=Cols; (*c)++)
X			if (bd[*r][*c] == man) return;
X	*r = *c = -1;
X}
X
X/* FORCES()
X *
X *   Does the player of color <co> have any forces left on <bd> beside his king?
X *   This is used in shatranj and courier only.
X */
X
Xboolean forces(co,bd)
Xschar co;
Xschar bd[R_SIZE][C_SIZE];
X{
Xint r,c;
X	for (r=0; r<=Rows; r++)
X		for (c=0; c<=Cols; c++)
X			if ( bd[r][c]*co > 0 && bd[r][c] != Kingpiece(co))
X				return(TRUE);
X	return(FALSE);
X}
X
X/* FLUSHMOVES()
X *
X *    The move stack is used to detect repeated positions.  It starts out
X *    empty and is flushed every time a pawn is moved or a piece is captured.
X *    The global variable mvssize gives the number of moves currently on this
X *    stack.  Note that when a castling move is made, both the move of the
X *    king and the move of the rook should be pushed on the stack.
X *
X *    The call flushmoves() empties the stack.
X *
X *    The call pushmove() adds a move to the stack.  At most 50 moves are put
X *    on the stack, since beyond that point the 50-move rule dominates anyways.
X *
X *    The call isrepeat() returns true if the current position has occured
X *    twice before.  It doesn't count beyond 2, since that's all that matters.
X *    Isrepeat() should be called before the new move is placed on the stack.
X *    There should be a call to makemove() between any two calls to isrepeat(),
X *    since makemove() initializes the ndiffs variable.
X */
X
Xstatic struct stackamoves {
X	char fr,fc,tr,tc;
X	} mvstack[MAXTAME+2];	/* Two extra slots for castling */
X
Xflushmoves()
X{
X	mvssize = 0;
X}
X
Xpushmove(fr,fc,tr,tc)
Xint fr,fc,tr,tc;
X{
X	if (mvssize < MAXTAME+2)
X	{
X		mvstack[mvssize].fr = (char) fr;
X		mvstack[mvssize].fc = (char) fc;
X		mvstack[mvssize].tr = (char) tr;
X		mvstack[mvssize].tc = (char) tc;
X	}
X	mvssize++;
X}
X
Xboolean isrepeat(bd,nbd)
Xschar bd[R_SIZE][C_SIZE];	/* Board before current move */
Xschar nbd[R_SIZE][C_SIZE];	/* Current after current move */
X{
Xschar obd[R_SIZE][C_SIZE];	/* Previous board */
Xschar p;
Xint mvs;
Xint nreps;		/* Number of times this position has been repeated */
X
X    /*
X     * If I haven't moved since last flush, this is no repeat.
X     * If the game is stale, just return any old thing.
X     */
X    if (mvssize < 1 || mvssize >= MAXTAME+2)
X	return(FALSE);
X
X    /* Each piece on the board was where it is */
X    copy(bd,obd);
X
X    /*
X     * Back through the stack, unmaking the moves on the old board,
X     * and counting the number of pieces out of place.  This
X     * is easy, since none of the moves on the stack are captures.
X     */
X    nreps = 0;	/* ndiff initialized by makemove() */
X    mvs = mvssize;
X    while (mvs-- >= 0)
X    {
X	/* Unmake the move on the old board */
X	p = obd[mvstack[mvs].tr][mvstack[mvs].tc];
X	obd[mvstack[mvs].fr][mvstack[mvs].fc] = p;
X
X	/* Does this make or break any similarities? */
X	if (nbd[mvstack[mvs].tr][mvstack[mvs].tc] == p)
X	    ndiff++;
X	else if (nbd[mvstack[mvs].fr][mvstack[mvs].fc] == p)
X	    ndiff--;
X
X	/* If there are no differences, count a repeat */
X	if (ndiff == 0)
X	    if (++nreps == 2)
X		return(TRUE);
X	}
X	return(FALSE);
X}
X
X
X/* SETUP()
X *
X *    Make the default initial setup for various variants games.
X *    The default initial setup is returned in <bd>.
X */
X
Xsetup(bd)
Xschar bd[R_SIZE][C_SIZE];
X{
X	copy(ib,bd);
X
X	switch (Game_type)
X	{
X	case TY_SHATRANJ:
X		bd[0][2] = bd[0][5] = WE;
X		bd[0][3] = WM;
X		bd[7][2] = bd[7][5] = BE;
X		bd[7][3] = BM;
X		break;
X
X	case TY_COURIER:
X		bd[7][2] = -(bd[0][2] = WE);
X		bd[7][3] = -(bd[0][3] = WB);
X		bd[7][4] = -(bd[0][4] = WJ);
X		bd[7][5] = -(bd[0][5] = WK);
X		bd[7][6] = -(bd[0][6] = WM);
X		bd[7][7] = -(bd[0][7] = WD);
X		break;
X
X	case TY_HALFBOARD:
X		bd[0][3] = WK;
X		bd[7][3] = BK;
X		break;
X
X	case TY_MAHARAJAH:
X		bd[0][4] = WH;
X		bd[1][2] = bd[1][3] = bd[1][4] = bd[1][5] = SQ;
X	case TY_STEAMROLLER:
X		bd[0][0] = bd[0][1] = bd[0][2] = bd[0][3] =
X		bd[0][5] = bd[0][6] = bd[0][7] =
X		bd[1][0] = bd[1][1] = bd[1][6] = bd[1][7] = SQ;
X		break;
X	}
X}
END_OF_play.c
if test 26495 -ne `wc -c <play.c`; then
    echo shar: \"play.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 5\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 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