DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦1026de1e0⟧ TextFile

    Length: 24364 (0x5f2c)
    Types: TextFile
    Names: »cmd.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cchess/cmd.c« 

TextFile

/*    CCHESS USER INTERFACE					version 1.00
 *
 *    (C) Copyright - Jan D. Wolter - Apr 1986
 *
 *    Mostly routines to talk to the user.
 */

#include "cchess.h"

#ifndef NOTERMCAP
extern char *DL;
#endif NOTERMCAP

int mredraw;	/* flag used for redrawing while reading in move */

/* DOMOVE()
 *
 * This is the move mode driver.  Show board and command menu.  Accept
 * move, forfiet, draw, and playback commands.  This is the real center of
 * the program.
 *
 * If <drawp> is RE_DRAW, then a draw has been proposed, and the options are
 * to accept or reject the draw.  If <drawp> is RE_CANCEL, then a cancelation
 * has been proposed, and the same commands are available.
 */

domove(drawp)
int drawp;
{
int fr,fc,tr,tc;
int row,col;
char new_move[15];
char ch;
schar took,p;
boolean thru,himcheck,checked,firsttime=TRUE;
int i;

    new_move[C_CMD] = 'M';
    new_move[C_MAT] = ' ';

    myillmoves = 0;
    scrolled = TRUE;
    mredraw = 0;
    must_take = Musttake && canmove(b,mycolor,TRUE);

    /* Loop until he is satisfied with a command */
    for (;;)
    {
#ifndef NOTERMCAP
	/* make sure there is room for an error message */
	if (moveok && errcode != E_OK && errcode != E_SY)
	{
	    if (!DL && cy == LINES)
		wputchar('\n');
	}
	else
	    errcode = E_OK;
#endif NOTERMCAP

	/* draw board - "scrolled" basically just tells us to do so */
	if (scrolled)
	{
	    disp(b,kibitz?WHITE:mycolor);
	    printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid);
	    printcom();
	    wputchar('\n');
	    if (cy >= LINES)
		msgarea = LINES - 1;
	    else
		msgarea = cy;
	}

	/* Prompt for a command */
	if (!moveok)
	{
	    /* Not allowed to move - Playback or nothing */
	    if (!kibitz) wprint("It's not your turn to move.\n");
	    wprint("Do you want a playback? ");
	    ch = xenter("YNVOL\014");
	    if (ch == 'Y')
	    {
		ch = 'P';
		wprint("Yes\n");
	    }
	    if (ch == 'N')
	    {
		ch = 'Z';
		wprint("No\n");
	    }
	}
	else if (drawp == RE_NONE)
	{
	    /* Normal case - Get a move command */
	    if (errcode != E_OK)
	    {
		prterror();
		wputchar('\n');
	    }
	    errcode = E_OK;
	    if (firsttime)
	    {
		if (movecnt < 2)
		    wprint("Help, ");
		else if (tamemoves >= MAXTAME)
		    wprint("Under the 50-move rule, you may unilaterally declare a draw.\n");
		firsttime = FALSE;
	    }
	    wprint("Move, Playback, Forfeit or Draw? ");
	    if (mredraw > 0)
		ch = 'M';
	    else
		ch = xenter("MPFDECQVH?OL\014");
	}
	else
	{
	    /* A draw or cancellation has been proposed */
	    if (drawp == RE_DRAW)
		wprint("A draw");
	    else
		wprint("A cancellation");
	    wprint(" has been proposed.\nAccept or Reject? ");
	    ch = xenter("PARVEQOL\014");
	}

	switch (ch)
	{
	case 'H':
	case '?':
	    wprint("Help\n");

	    initmore();
	    mprint("Move -     Enter a chess move\n");
	    mprint("Playback - View a replay of previous moves\n");
	    mprint("Forfeit -  Admit that the other player won\n");
	    mprint("Draw -     Propose that the game be a draw\n");
	    mprint("Last -     Display date of last move made\n");
	    mprint("Options -  Display the current game options\n");
	    mprint("^L -       Redraw the screen\n");
	    mprint("Exit -     Exit the program without moving\n");
	    mprint("Cancel -   Propose that the game be abandoned\n");
	    break;

	case 'L':
	    wprintf("Last move made on %s\n", cday(lastday));
	    break;

	case 'O':
	    wprint("Options\n");
	    initmore();
	    printopts(mprintf);
	    break;

	case 'M':
	    wprint("Move\n");
	    ++consmade;
	    took = taken;
	    checked = incheck;

	    /* Get his move */
	    mget(mycolor,new_move+C_MOV,&fr,&fc,&tr,&tc);
	    if (mredraw > 0)
	    {
		scrolled = TRUE;
		--consmade;
		break;
	    }

	    /* If it's an illegal move, give it up */
	    if (!(thru = (errcode==E_MC && consmade < consmoves && Thrucheck)))
	    {
		if (errcode == E_OK)
		    wputchar('\n');
		else
		{
		    consmade--;
		    break;
		}
	    }

	    /* Test if other player is in check */
	    if (Kingpiece(-mycolor) != SQ)
	    {
		find(bc,Kingpiece(-mycolor),&row,&col);
		himcheck = check(bc,mycolor,row,col);
	    }
	    else
		himcheck = FALSE;

	    /* Has enemy been stripped of all forces? */
	    if (Stripresult != RE_NONE && taken != SQ && !forces(-mycolor,bc))
	    {
		stripped = TRUE;
		wprint("Capture of all enemy pieces ");
		switch (Stripresult)
		{
		case RE_LOSE:
		    wprintf("is a win for you.\n");
		    break;
		case RE_WIN:
		    wprintf("is a win for the other player.\n");
		    break;
		case RE_DRAW:
		    wprintf("forces a draw.\n");
		    break;
		}
	    }
	    else if (consmade < consmoves)
	    {
		/* May not apply check until last move */
		if (himcheck)
		{
		    errcode = E_MM;
		    consmade--;
		    break;
		}
		repeated = inmate = FALSE;
		incheck = thru;
	    }
	    else
	    {
		/* Test if other player is in mate */
		incheck = himcheck;
		inmate = !canmove(bc,-mycolor,FALSE);

		/* How often has this position has occured? */
		if (inmate || !istame || tamemoves >= MAXTAME-1)
		    repeated = FALSE;
		else
		    repeated = isrepeat(b,bc);
	    }

dispnew:    /* Display the new position */
#ifndef NOTERMCAP
	    if (!scrolled && issmart && (!Kriegspiel || !inmate))
		doupdate(b,bc,new_move,TRUE);
	    else
	    {
#endif NOTERMCAP
		if (inmate) hidden = FALSE;
		disp(bc, kibitz ? WHITE : mycolor);
		printmov(new_move,myillmoves,hisid);
		printcom();
		wputchar('\n');
#ifndef NOTERMCAP
	    }
#endif NOTERMCAP
	    /* Warn him about effects of stalemate */
	    if (inmate && !incheck)
	    {
		wprint("Stalemate ");
		switch(Staleresult)
		{
		case RE_WIN:
		    wprint("is a win for the other player.\n");
		    break;
		case RE_LOSE:
		    wprint("is a win for you.\n");
		    break;
		case RE_DRAW:
		    wprint("forces a draw.\n");
		    break;
		}
	    }

	    /* Let him declare a draw under fifty move rule */
	    if (repeated)
	    {
		wprint("This position has occurred twice before.\n");
		wprint("Would you like to declare a draw?");
		repeated = (enteryn());
	    }

	    /* Give him a chance to change his mind */
	    if (!Kriegspiel)
	    {
		if (Help_me && (consmade == consmoves) &&
		               (p = compkib(bc,mycolor)) != SQ)
		    wprintf("Your %s may be at risk.\n", piecename[p]);

		wprint("Are you sure? ");
		if ((ch = enter("YN\014")) == '\014')
		{
		    /* Redraw the screen */
		    scrolled = TRUE;
		    goto dispnew;
		}
		if (ch == 'N')
		{
		    /* Don't make this move after all */
		    wprint("o\n");
		    taken = took;
		    incheck = checked;
		    consmade--;
		    inmate = FALSE;
		    stripped = FALSE;
		    errcode = E_OK;
#ifndef NOTERMCAP
		    if (issmart)
			doupdate(bc,b,mbuffer,FALSE);
		    else
#endif NOTERMCAP
			scrolled = TRUE;
		    break;
		}
		else
		    wprint("es\n");
	    }

	    savemove((consmoves == consmade || stripped)?'M':'m',
		new_move+C_MOV);

	    /* Finish off ended games */
	    if (inmate || repeated || stripped)
	    {
		copy(bc,b);	/* So playbacks will work right */
		if (incheck && inmate)
		    askplayback(RE_WIN,EV_CHECKMATE);
		else if (inmate)
		    askplayback(flip(Staleresult),EV_STALEMATE);
		else if (stripped)
		    askplayback(flip(Stripresult),EV_FORCES);
		else
		    askplayback(RE_DRAW,EV_REPEATED);
		asktrans();
		return;
	    }
	    if (consmoves == consmade)
		return;
	    else
	    {
		/* Multi-move game: ask him for another */
		errcode = E_OK;
		copy(bc,b);
		must_take = Musttake && canmove(b,mycolor,TRUE);
		break;
	    }

	case 'F':
	    wprint("Forfeit\nDo you want to forfeit? ");
	    if (!enteryn()) break;

	    if (Kriegspiel)
	    {
		hidden = FALSE;
		disp(bc, kibitz ? WHITE : mycolor);
		printmov(new_move,myillmoves,hisid);
		printcom();
		wputchar('\n');
	    }

	    savemove('F',"forf ");
	    askplayback(RE_LOSE,EV_FORFIET);
	    asktrans();
	    return;

	case 'D':  /* Propose Draw */
	    wprintf("Draw\nDo you want to %s a draw? ",
		    (tamemoves >= MAXTAME)?"declare":"propose");
	    if (!enteryn()) break;

	    savemove('D',"draw?");

	    /* Unilateral if more than fifty moves have been made */
	    if (tamemoves >= MAXTAME)
	    {
		askplayback(RE_DRAW,EV_FIFTYMOVES);
		asktrans();
	    }
	    return;

	case 'C':  /* Propose Cancelation */
	    wprint("Cancel\n");
	    wprint("Do you want to propose that this game be discarded? ");
	    if (!enteryn()) break;
	    savemove('K',"canc?");
	    return;

	case 'A':  /* Accept Draw or Cancellation */
	    wprint("Accept\n");
	    savemove('Y',"yes  ");
	    askplayback(drawp,EV_AGREEMENT);
	    asktrans();
	    return;

	case 'R': /* Reject Draw or Cancellation */
	    wprint("Reject\n");
	    savemove('N',"no   ");
	    return;

	case 'Q': /* Quit */
	    wprint("Quit\n");
	case 'E': /* Exit */
	    if (ch == 'E') wprint("Exit\n");
	    if (!interupt)
	    {
		wprint("Sorry, not until you've moved...");
		break;
	    }
	    wprint("Do you really want to ");
	    if (eachgame)
		wprint("skip to the next game? ");
	    else
		wprint("exit the program? ");
	    if (!enteryn()) break;

	case 'Z': /* Exit without asking for confirmation */
	    return;

	case 'P': /* Playback previous positions */
	    if (moveok) wprint("Playback\n");
	    if (playcnt == 0)
	    {
		wprint("No moves to Playback.\n");
		break;
	    }

	    wprintf("How many moves (1-%d) do you want replayed? ",
		playcnt+(consmade > 0));
	    if ((i=readint(playcnt + (consmade > 0))) > 0)
	    {
		if (!issmart) wputchar('\n');
		playback(playcnt - i + (consmade > 0));
	    }

	    wputchar('\n');
	    /* If move arrives during replay...experiment */
	    if (!moveok && !kibitz && mycolor == toplay)
	    {
		moveok = TRUE;
		fseek(wfp,0L,2);
	    }
	    break;

	case '\014': /* Control-L redraws the screen */
	    wprint("Redraw\n");
	    scrolled = TRUE;
	    break;

	case 'V':  /* Version */
	    versprint();
	    break;
	}
    }
}



/* DOMATE()
 *
 * End a game.  There has been a checkmate, or the game has been drawn.
 *
 * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what
 *	     became of the current player.
 *
 * <why>  is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED,
 *	     or EV_AGREEMENT depending on why the game ended.
 */

domate(what,why)
char what,why;
{
	disp(b,kibitz?WHITE:mycolor);
	printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid);
	printcom();

	wputchar('\n');
	askplayback(what,why);

	if (moveok)
	{
		/* MUST do the endgame *before* the transcript, */
		/* curious though it seems.                     */
		endgame(what);
		asktrans();
	}
}

/*  ASKPLAYBACK()
 *
 * Ask the player for a playback, and keep asking him until he doesn't want one.
 * Print the message before asking each time.  It displays both sides, even if
 * the game is kriegspiel.  It is only meant to be called after a game is over.
 *
 * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what
 *	     became of the current player.
 *
 * <why>  is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED,
 *	     or EV_AGREEMENT depending on why the game ended.
 */

askplayback(what,why)
char what,why;
{
static char tbuf[70];
boolean flag;
int i;

	if (hidden) scrolled = TRUE;
	hidden = FALSE;		/* No reason to hide moves anymore */

	switch (what)
	{
	case RE_WIN:
		if (solo && !kibitz)
			strcpy(tbuf,"Congratulations!\nYou have won");
		else
			sprintf(tbuf,"%s has won",
				(mycolor == toplay) ? myid : hisid);
		break;
	case RE_LOSE:
		if (solo && !kibitz)
			strcpy(tbuf,"You have lost");
		else
			sprintf(tbuf,"%s has won",
				(mycolor != toplay) ? myid : hisid);
		break;
	case RE_DRAW:
		strcpy(tbuf,"The game is drawn");
		break;
	case RE_CANCEL:
		strcpy(tbuf,"The game is canceled");
		break;
	}
	switch (why)
	{
	case EV_CHECKMATE:
		strcat(tbuf," by a checkmate.");
		break;
	case EV_STALEMATE:
		strcat(tbuf," by a stalemate.");
		break;
	case EV_FORCES:
		strcat(tbuf," by lose of forces.");
		break;
	case EV_FIFTYMOVES:
		strcat(tbuf," under the fifty move rule.");
		break;
	case EV_REPEATED:
		strcat(tbuf," because of repeated positions.");
		break;
	case EV_AGREEMENT:
		strcat(tbuf," by mutual agreement.");
		break;
	case EV_FORFIET:
		strcat(tbuf," by a forfieting.");
		break;
	}

	do
	{
		wprint(tbuf);
		if (playcnt == 0 && consmade == 0)
		{
			wputchar('\n');
			return;
		}
		wprint("\nDo you want a playback? ");
		if (flag = enteryn())
		{
			wprintf("How many moves (1-%d) do you want replayed? ",
				playcnt+(consmade > 0));
			if ((i=readint(playcnt + (consmade > 0))) > 0)
			{
				if (!issmart) wputchar('\n');
				playback(playcnt - i + (consmade > 0));
				wputchar('\n');
			}
		}
	} while (flag);
}

#ifndef NOTERMCAP
/* DOUPDATE()
 *
 *Update() plus update the labels on the display.
 *
 */

doupdate(bo,bn,move,mine)
schar bo[R_SIZE][C_SIZE], bn[R_SIZE][C_SIZE];
char *move;
boolean mine;
{
short ox=cx, oy=cy;

	update(bo,bn);
	printmov(move,mine?myillmoves:illmoves,mine?myid:hisid);
	cursor(ox,oy);
}
#endif NOTERMCAP

/* MGET()
 *
 *   Read a possibly illegal move from the player.  Set the error code if he
 *   Gave an illegal response.  Note that E_SY can only result if he hits
 *   return prematurely.  It is not really an error; it is used as a command
 *   cancellation.
 */

mget(co,buffer,fr,fc,tr,tc)
char *buffer;
int *fr,*fc,*tr,*tc;		/* from (row, column) to (row, column) */
int co;
{
char ch;

	if (consmoves > 1)
		wprintf("Enter your move (%d of %d): ",consmade,consmoves);
	else
		wprint("Enter your move: ");
	if (mread(buffer))
		if (mconv(buffer,fr,fc,tr,tc) &&
		    legal(co,*fr,*fc,*tr,*tc))
		{
			if (promote)
			{
			    /* Patch up pawn promotion */
			    if (Promotion == PR_MINISTER)
				/* Only Ministers can be promoted to */
				bc[*tr][*tc] = promote = WM*co;
			    else if (Promotion == PR_NONE)
				/* Can't Promote */
				bc[*tr][*tc] = promote = WP*co;
			    else
			    {
				/* Ask the player what to promote to */
				for(;;)
				{
				    wputchar('\n');
				    wprint("Promote pawn to: ");
				    if ((ch = xenter("QRBNK\014"))=='\014')
				    {
					errcode = E_OK;
					mredraw = 5;
					return;
				    }
				    promote = PIECES-(index(pc,ch)-pc);
				    wprint(piecename[promote]);
				    if (promote != Kingtype(co))
					break;
				    wprint("\nCan't promote to mateable piece");
				}

				bc[*tr][*tc] = promote *= co;
			    }
			}
			errcode = E_OK;
			return;
		}
		else    /* Illegal move */
		{	
			/* Unless he should have know better, record it */
			if (Kriegspiel &&
			    ( (errcode == E_CP) || (errcode == E_CT) ||
			      (errcode == E_PI) || (errcode == E_QB) ||
			      (errcode == E_BB) || (errcode == E_BR) ||
			      (errcode == E_MC) || (errcode == E_MM) ) )
			{
				interupt = FALSE;
				myillmoves++;
				fprintf(wfp,"%5ld:I %5.5s\n",day(),buffer);
			}
			wputchar('\n');
		}
	else
		if (mredraw == 0)
			errcode = E_SY;
	return;
}

/* PERROR()
 *
 *   Print error messages.  Current error is given by the global errcode.
 */

/* Three common messages */
static char kierrmsg[] = "Illegal Move.";
static char kcerrmsg[] = "Can't Castle.";
static char scerrmsg[] = "Castling has not been invented.";

prterror()
{
	/* If no error, do nothing */
	if (errcode == E_OK) return;

	wprint("Error:  ");

	switch (errcode)
	{
	case E_MT:		/* move from empty square */
		wprint("No friendly piece in starting square.");
		break;
	case E_AF:		/* attack on friendly piece */
		wprint("Can't capture your own pieces.");
		break;
	case E_EQ:		/* source and destination square are equal */
		wprint("Null move.");
		break;
	case E_SY:		/* Move syntax error */
		wprint("Command syntax unrecognizable.");
		break;
	case E_CR:
		wprint("Must capture if possible.");
		break;
	case E_NC:
		wprint("Piece may not capture.");
		break;
	case E_CP:
		if (Oldcastle)
			wprint(scerrmsg);
		else if (hidden)
			wprint(kcerrmsg);
		else
			wprint("Not in position to castle.");
		break;
	case E_CM:
		if (Oldcastle)
			wprint(scerrmsg);
		else
			wprint("Can't castle after moving king or rook.");
		break;
	case E_CT:
		if (Oldcastle)
			wprint(scerrmsg);
		else if (hidden)
			wprint(kcerrmsg);
		else
			wprint("Can't castle through or out of check.");
		break;
	case E_MC:		/* Move into Check */
		if (hidden)
			wprint(kierrmsg);
		else
			wprint("Move leaves you in check.");
		break;
	case E_MM:		/* No Check on Multi-Moves */
		if (hidden)
			wprint(kierrmsg);
		else
			wprint("Can't apply check until last move.");
		break;
	case E_CC:		/* Ta'biyat violation */
		wprint("Can't cross center on first turn.");
		break;
	case E_IL:		/* Illegal move */
		wprint(kierrmsg);
		break;
	case E_KI:		/* Illegal move of King or Jester */
	case E_JI:
		wprintf("%s moves only to adjacent squares.",
			piecename[errcode & 033]);
		break;
	case E_QI:		/* Illegal move of Queen */
		wprintf("%s moves only along rows, columns and diagonals.",
			piecename[WQ]);
		break;
	case E_BI:		/* Illegal move of Bishop */
		wprintf("%s moves only along diagonals.",piecename[WB]);
		break;
	case E_NI:		/* Illegal move of Knight */
		wprintf("%s moves two squares straight and one to the side.",
			piecename[WN]);
		break;
	case E_RI:		/* Illegal move of Rook */
		wprintf("%s moves only along rows or columns.",piecename[WR]);
		break;
	case E_PI:		/* Illegal move of Pawn */
		if (hidden)
			wprint(kierrmsg);
		else
			wprintf("%s moves forward or captures diagonally.",
				piecename[WP]);
		break;
	case E_MI:		/* Illegal move of Minister */
		wprintf("%s moves only to diagonally adjacent squares.",
			piecename[WM]);
		break;
	case E_EI:		/* Illegal move of Elephant */
		wprintf("%s moves two squares diagonally.",piecename[WE]);
		break;
	case E_DI:		/* Illegal move of Duke */
		wprintf("%s moves only to orthogonally adjacent squares.",
			piecename[WD]);
		break;
	case E_HI:		/* Illegal move of Maharajah */
		wprintf("%s moves like knight or like queen.",piecename[WH]);
		break;
	case E_KB:		/* Blocked move of Kamikazi King */
		if (hidden)
			wprint(kierrmsg);
		else
			wprintf("%s can't capture in this version of chess.",
				piecename[WK]);
		break;
	case E_QB:		/* Blocked move of Queen */
	case E_BB:		/* Blocked move of Bishop */
	case E_BR:		/* Blocked move of Rook */
	case E_BH:		/* Blocked move of Maharajah */
		if (hidden)
			wprint(kierrmsg);
		else
			wprintf("%s can't jump over other pieces.",
				piecename[errcode & 033]);
		break;
	default:
		wprintf("Weird Error %d.",errcode);
	}
}

/* MREAD()
 *
 *   Read a move from the terminal, return FALSE if player chickens out
 *   or asks for a redraw.  In the latter case, mredraw is set.  Supports
 *   backspaces.  Sorry Edsger.
 */

char nums[] = "87654321\b\n\014";
char lets[] = "LKJIHGFEDCBA\b\n\014";

mread(buffer)
REGISTER char *buffer;
{
register int m;
short ox=cx;

	if (mredraw > 0)
	{
		m = mredraw;
		mredraw = 0;
		if (m == 1) goto n0;
		wputchar(buffer[0]);
		if (m == 2) goto n1;
		wputchar(buffer[1]);
		wputchar('-');
		if (m == 3) goto n2;
		wputchar(buffer[3]);
		if (m == 4) goto n3;
		wputchar(buffer[4]);
		return(TRUE);
	}

n0:	buffer[0] = enter(&lets[C_SIZE-1-Cols]);
	switch (buffer[0])
	{
	case '\b':
		bell();
		wputchar(' ');
		goto n0;
	case '\014':
		mredraw = 1;
	case '\n':
		return (FALSE);
	}

n1:	buffer[1] = enter(&nums[R_SIZE-1-Rows]);
	switch (buffer[1])
	{
	case '\b':
		if (erases)
		{
			wputchar(' ');
			backspace();
		}
		else
			indent(ox-1);
		goto n0;
	case '\014':
		mredraw = 2;
	case '\n':
		return (FALSE);
	}

	buffer[2] = '-';
	wputchar('-');

n2:	buffer[3] = enter(&lets[C_SIZE-1-Cols]);
	switch (buffer[3])
	{
	case '\b':
		if (erases)
		{
			backspace();
			wprint(blanks(2));
			backspace();
			backspace();
		}
		else
		{
			indent(ox-1);
			wputchar(buffer[0]);
		}
		goto n1;
	case '\014':
		mredraw = 3;
	case '\n':
		return (FALSE);
	}

n3:	buffer[4] = enter(&nums[R_SIZE-1-Rows]);
	switch (buffer[4])
	{
	case '\b':
		if (erases)
		{
			wputchar(' ');
			backspace();
		}
		else
		{
			indent(ox-1);
			wputchar(buffer[0]);
			wputchar(buffer[1]);
			wputchar(buffer[2]);
		}
		goto n2;
	case '\014':
		mredraw = 4;
	case '\n':
		return (FALSE);
	}

	return(TRUE);
}


/* PREAD()
 *
 *   Read a position from the terminal, return FALSE if player chickens out.
 *   Supports backspaces.  Sorry Edsger.
 */

pread(buffer)
char *buffer;
{
short ox=cx;

n0:	buffer[0] = enter(&lets[C_SIZE-1-Cols]);
	if (buffer[0] == '\b')
	{
		bell();
		wputchar(' ');
		goto n0;
	}
	if (buffer[0] == '\n') return (FALSE);

	buffer[1] = enter(&nums[R_SIZE-1-Rows]);
	if (buffer[1] == '\n') return (FALSE);
	if (buffer[1] == '\b')
	{
		if (erases)
		{
			wputchar(' ');
			backspace();
		}
		else
			indent(ox-1);
		goto n0;
	}
	return(TRUE);
}


/* PLAYBACK()
 *
 *   Display a playback of the game.
 */

playback(n)
REGISTER int n;
{
int fr,fc,tr,tc;
register int cnt,subcnt=0;
int ill = 0;
boolean wasmoveok;
char probuf[15];
char c;
boolean prompt = FALSE;
boolean didmove = FALSE;

    if (n < 0)
    {
	wputchar('\n');
	return;
    }
    rewind(rfp);
    wasmoveok = moveok;
    moveok = FALSE;
    /* Read in the initial placement */
    do
    {
	if (fgetl(mbuffer,MB_LEN,rfp)==NULL)
	{
		printf("Panic: No accept line\n");
		done(1);
	}
	if (mbuffer[C_CMD] == 'C')
		copy(ib,bc);
	else if (mbuffer[C_CMD] == 'P')
		place(bc,mbuffer[C_MOV],mbuffer+C_MOV+2);
    } while (mbuffer[C_CMD] != 'A');

    cnt = 0;
    toplay = WHITE;
    for (;;)
    {
	/* Prompt the user */
	if (prompt)
	{
		cprint("*Hit return for next board:");
		c=xenter("\nQ\014");
		if (!issmart) wputchar('\n');
		if (c != '\n')
			if (c == 'Q')
			{
				/* No more updates or prompts */
				n = playcnt + (consmade > 0);
				prompt = FALSE;
			}
			else
			{
				/* Redraw the screen */
				disp(b,kibitz?WHITE:mycolor);
				wprint("\n\n");
				continue;
			}
	}

	/* Display the position */
	if (cnt >= n || (cnt == playcnt && consmade == subcnt))
	{
#ifndef NOTERMCAP
		if (cleardown && !scrolled)
		{
			update(b,bc);
			if (didmove)
				printmov(mbuffer,ill,
					toplay == mycolor ? hisid : myid);
			clbot();
		}
		else
		{
#endif NOTERMCAP
			disp(bc,kibitz?WHITE:mycolor);
			if (didmove)
				printmov(mbuffer,ill,
					toplay == mycolor ? hisid : myid);
#ifndef NOTERMCAP
		}
#endif NOTERMCAP
		prompt = TRUE;		/* Prompt after a redraw */
		copy(bc,b);
		if (cnt != 0)
			printcom();
		else
			wputchar('\n');
	}

	ill = 0;
	/* Read until we find a move line */
	for( ; ; )
	{
		if (fgetl(mbuffer,MB_LEN,rfp)==NULL)
		{
		    moveok = wasmoveok;
		    return;
		}
		if (mbuffer[C_CMD] == 'I')
		    ill++;
		else if (mbuffer[C_CMD] != 'R')
		    break;
	}
	didmove = TRUE;

	if (mbuffer[C_CMD] == 'm' || mbuffer[C_CMD] == 'M')
	{
		/* Checked or Mated? */
		incheck = (mbuffer[C_MAT] == 'C' || mbuffer[C_MAT] == 'M');
		inmate  = (mbuffer[C_MAT] == 'M' || mbuffer[C_MAT] == 'S');

		/* Play it on the board */
		if (mconv(mbuffer+C_MOV,&fr,&fc,&tr,&tc))
		{
			makemove(bc,toplay,fr,fc,tr,tc,FALSE);
			if (promote)
			{
				fgetl(probuf,14,rfp);
				bc[tr][tc] = promote =
				    toplay*(PIECES-(index(pc,probuf[C_MOV])-pc));
			}
		}
		else
		{
			wprint("Format error:  Bad move syntax.\n");
			done(1);
		}
	}
	if (mbuffer[C_CMD] != 'm')
	{
		cnt++;
		subcnt = 0;
		toplay = -toplay;
	}
	else
		subcnt++;
    }
}

/* ASKTRANS()
 *
 *  Give him a transcript, if he wants one.
 */

asktrans()
{
	wprint("Do you want a transcript of the game? ");
	if (enteryn()) trans(hidden);
}

/* TRANS()
 *
 *   Ask what sort of transcript he wants.  In visual mode.
 *   If <masked> is true, don't print the other players moves.
 */

trans(masked)
{
FILE *tfp;
char tfile[80];
boolean comments;

	clr();
	wprintf("Printing transcript for game between %s and %s\n",
		myid,hisid);

	/* Revert to self so output files can be opened */
	/* Note:  Can't open game files any more after this */
	setuid(getuid());
	setgid(getgid());

	/* Get output device */
	for (;;)
	{
		wprint("\nDo you want it in a file? ");
		if (!enteryn())
		{
			tfp = stdout;
			break;
		}
		else
		{
			wprint("Enter the filename: ");
			if (!readstr(tfile,0,79,TRUE)) continue;
			if ((tfp = fopen(tfile,"w")) == NULL)
				wprintf("Cannot open %s\n",tfile);
			else
				break;
		}
	}

	wprint("Do you want comments included? ");
	comments = enteryn();

	/* In the future, ask about format */

	cctrans(tfp,comments,masked);
}