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 p

⟦0a8640361⟧ TextFile

    Length: 26495 (0x677f)
    Types: TextFile
    Names: »play.c«

Derivation

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

TextFile

/* CCHESS CHESS PLAYING ROUTINES.				Version 1.00
 *
 *   Routines to enforce rules and test for mate and such fun stuff.  These
 *   routines could be integrated much more, I think, but too much time elapsed
 *   between phases of development for me to do things right.
 *
 *   (C) Copyright Jan Wolter - Apr 1986.
 */

#include "cchess.h"

/* Values of pieces    ( ,K,Q,B,N,R,P,M,E,D,J,H) */
char value[PIECES+1] = {0,7,6,4,4,5,1,2,2,2,3,7};

/* Number of pieces relocated in last move */
static int ndiff;

boolean xcheck();
int mcheck();

/* MAKEMOVE()
 *
 *   Make a move, assuming it is legal.  Handle promotion, en passeunt, and
 *   castling.  If <record> is true, note rook and king moves for future
 *   castle legality checking, and also save the move on the stack for
 *   repeated position testing.
 */
 
makemove(bd,co,fr,fc,tr,tc,record)
schar bd[R_SIZE][C_SIZE];
REGISTER int fr,fc,tr,tc;
schar co;
boolean record;
{
schar p;
int wastaken = taken;
int caprule;

	p = bd[fr][fc];			/* Piece moved */
	taken = bd[tr][tc];		/* Piece taken */
	promote = 0;			/* No promotion */
	ndiff = 1;			/* One piece changed */
	caprule = (p == Whiteking || p == -Blackking) ? Kcapture : Capture;

	switch (p)
	{
	case WP:
	case BP:
		/* Whenever a pawn moves, we can flush the stack */

		/* Pawn Promotion - for now to goofy piece - Fix up later */
		if (tr == tside(co) && (taken == SQ ||
		    (caprule != CA_RIFLE && caprule != CA_CONVERSION)))
			promote = p = co * XX;

		/* En passeunt capture */
		if ((fc != tc) && (taken == SQ))
		{
			taken = bd[fr][tc];
			bd[fr][tc] = SQ;
		}
		break;
	case WK:
	case BK:
		/* Castling - Move the rook too */
		if (fc == tc + 2)
		{
			bd[fr][tc+1] = bd[fr][0];
			bd[fr][0] = SQ;
			ndiff = 2;
			if (record) pushmove(fr,0,fr,tc+1);
		}
		if (fc == tc - 2)
		{
			bd[fr][tc-1] = bd[fr][Cols];
			bd[fr][Cols] = SQ;
			ndiff = 2;
			if (record) pushmove(fr,Cols,fr,tc-1);
		}
		/* After any king move, can never castle again */
		if (record)
		{
			if (co == WHITE)
				wcck = wccq = FALSE;
			else
				bcck = bccq = FALSE;
		}
		break;
	case WR:
		/* Rook moves may make future castling impossible */
		if (record)
		{
			if (fc == 0)    wccq = FALSE;
			if (fc == Cols) wcck = FALSE;
		}
		break;
	case BR:
		/* Rook moves may make future castling impossible */
		if (record)
		{
			if (fc == 0)    bccq = FALSE;
			if (fc == Cols) bcck = FALSE;
		}
		break;
	}

	/* Move the piece */
	bd[tr][tc] = p;
	bd[fr][fc] = SQ;

	if (taken == SQ)
		/* If not a capture or pawn move, it is a tame move */
		istame = (p != WP && p != BP);
	else
	{	
		istame = FALSE;

		/* Handle kamikazi, karma, rifle and conversion captures */
		switch ((p == Whiteking || p == -Blackking) ?
			Kcapture : Capture)
		{
		case CA_KARMA:
			/* Capturing piece takes rank of captured one */
			bd[tr][tc] = -taken;
			break;
		case CA_KAMIKAZI:
			/* Capturing piece destroyed */
			bd[tr][tc] = SQ;
			break;
		case CA_RIFLE:
			/* Capturing piece stays where it came from */
			bd[fr][fc] = bd[tr][tc];
			bd[tr][tc] = SQ;
			break;
		case CA_CONVERSION:
			/* Capturing piece stays where it came from */
			bd[fr][fc] = bd[tr][tc];
			/* Captured piece changes sides if not a traitor */
			bd[tr][tc] = (wastaken != SQ &&
				      ((co==WHITE && btr==tr && btc==tc) ||
				       (co==BLACK && wtr==tr && wtc==tc)))
				      ? SQ : -taken;
			break;
		}
	}

	/* Put tame moves on the stack, flush the stack after others */
	if (record) 
	{
		if (istame)
			pushmove(fr,fc,tr,tc);
		else
			flushmoves();
	}

	/* Record last move for en passaunt legality checking */
	if (co == WHITE) { wfr = fr; wfc = fc; wtr = tr; wtc = tc; }
	            else { bfr = fr; bfc = fc; btr = tr; btc = tc; }
}


/* CHECK()
 *
 *   Is there any piece of color "co" which can attack a square?
 *   Normally, the square in question is the king's location, so this
 *   is a check detector.  Note that it doesn't test if the move leaves
 *   co's king in check.  This would have to be done, if we used it for
 *   checking anything other than attacks on kings.  However, the only
 *   place it is used that way is by the computer kibitzer, which needn't
 *   be that smart.
 */

boolean check(bd,co,r,c)
schar bd[R_SIZE][C_SIZE];
schar co;
REGISTER int r,c;
{
register int x,y;

	/* If piece is off board, it can't be checked. */
	if (r < 0 || c < 0 || r > Rows || c > Cols) return(FALSE);

	for (x=0;x<=Rows;x++)
		for (y=0;y<=Cols;y++)
			if (xcheck(bd,co,r,c,x,y)) return(TRUE);
	return(FALSE);
}

/* Can bd[x][y] attack bd[r][c]? */

boolean xcheck(bd,co,r,c,x,y)
schar bd[R_SIZE][C_SIZE];
schar co;
REGISTER int r,c;
REGISTER int x,y;
{
register schar p;

	/* Spaces can't attack, and you can't attack yourself */
	if ((p = co*bd[x][y]) == SQ || (r == x && c == y))
		return(FALSE);

	if ((p = co*bd[x][y]) == Kingtype(co))
	{
		/* If king can't capture, that's it for it */
		if (Kcapture == CA_NONE)
			return(FALSE);
	}
	else
	{
		/* If piece can't capture other pieces, that's that */
		if (Capture == CA_NONE && abs(b[r][c]) != Kingtype(-co))
			return(FALSE);
	}

	switch (p)
	{
	case WK:   /* Enemy King */
		if ((abs(x-r) <= 1) && (abs(y-c) <= 1)) return (TRUE);
		break;

	case WQ:   /* Enemy Queen */
		if (cart(bd,x,y,r,c) || diag(bd,x,y,r,c)) return(TRUE);
		break;

	case WN:    /* Enemy Knight */
		if (((abs(x-r) == 2) && (abs(y-c) == 1)) ||
		    ((abs(x-r) == 1) && (abs(y-c) == 2))) return(TRUE);
		break;

	case WB:    /* Enemy Bishop */
		if (diag(bd,x,y,r,c)) return(TRUE);
		break;

	case WR:    /* Enemy Rook */
		if (cart(bd,x,y,r,c)) return(TRUE);
		break;

	case WP:    /* Enemy Pawn */
		if ((r==x+co) && (abs(y-c)==1)) return(TRUE);
		break;

	case WM:    /* Enemy Minister */
		if ((abs(x-r) == 1) && (abs(y-c) == 1)) return(TRUE);
		break;

	case WE:    /* Enemy Elephant */
		if ((abs(x-r) == 2) && (abs(y-c) == 2)) return(TRUE);
		break;

	case WD:    /* Enemy Duke */
		if ((abs(x-r) + abs(y-c) == 1)) return(TRUE);
		break;

	case WJ:   /* Enemy Jester */
		if ((abs(x-r) <= 1) && (abs(y-c) <= 1)) return(TRUE);
		break;

	case WH:   /* Enemy Maharajah */
		if (((abs(x-r) == 1) && (abs(y-c) == 2)) ||
		    ((abs(x-r) == 2) && (abs(y-c) == 1)) ||
		    cart(bd,x,y,r,c) || diag(bd,x,y,r,c)) return(TRUE);
		break;

	default: 			/* Friendly or Blank */
		break;
	}
	return(FALSE);
}

/* COMPKIB()
 *
 *   Computer Kibbitzer.  This is delibrately stupid.  It is ignorant of
 *   pinned pieces and fancy combinations.  It returns the piece code
 *   of the first piece it finds that looks like it may be in danger.  If
 *   it finds none, it returns the code for an empty space.  "co" is the
 *   player we are kibbitzing for.  This implementation is a little boneheaded
 *   too, but not as bad as it looks at first glance.  
 *   
 *   The Computer Kibbitzer finds major pieces which can be taken but are
 *   uncovered or which can be taken by a less valuable piece.  It doesn't
 *   worry about any piece that is no more valuable than the one you took
 *   this turn, since you are probably making a reasonable exchange.
 */

schar compkib(bd,co)
schar bd[R_SIZE][C_SIZE];
schar co;
{
register int x,y;
register int x1,y1;
register schar p;

    /* Look for a major friendly piece */
    for (x=0;x<=Rows;x++)
	for (y=0;y<=Cols;y++)
	    if ((p = bd[x][y]*co) > 0 && p != WP && p != Kingtype(co)
	    		&& value[p] > value[abs(taken)])
	    {
		/* can it be attacked? */
		for (x1=0;x1<=Rows;x1++)
		    for (y1=0;y1<=Cols;y1++)
			if (xcheck(bd,-co,x,y,x1,y1))
			{
			    /* Does it need cover? */
			    if (value[p] > value[-bd[x1][y1]*co])
				return(p);
			    else
				/* Is it covered? */
				if (!check(bd,co,x,y))
				    return(p);
			}
	    }
    return(SQ);
}


/* LEGAL()
 *
 *   Is a move legal?  Return FALSE if not, and set error code.
 *   Board "b" gives current position.  If move is legal, board bc has
 *   the new position.  If the global must_take is set, only captures
 *   are considered legal.
 */

boolean legal(co,fr,fc,tr,tc)
int co;				/* player's color */
REGISTER int fr,fc,tr,tc;	/* from (row, column) to (row, column) */
{
int r,c;
int pf, pt;

	pt = b[tr][tc];
	pf = b[fr][fc];

	/* Is there a friendly piece at start? */
	if (pf*co <= 0)
	{
		errcode = E_MT;
		return(FALSE);
	}

	/* Is there any motion? */
	if ((fr == tr) && (fc == tc))
	{
		errcode = E_EQ;
		return(FALSE);
	}

	/* Is the move legal? */
	switch (pf)
	{
	case WK:	/* Kings */
	case BK:
		/* Castling Queen Side */
		if (tc == fc-2)
		{
			/* Is there a rook in position? */
			if (fc <= 2 || b[fr][0] != co*WR)
			{
				errcode = E_KI;
				return (FALSE);
			}
			/* Has King or Rook been Previously moved? */
			if (!((co+1)?wccq:bccq))
			{
				errcode = E_CM;
				return (FALSE);
			}
			/* Are there any Intervening pieces */
			if (!cart(b,fr,0,fr,fc))
			{
				errcode = E_CP;
				return (FALSE);
			}
			/* Is intervening square under attack? */
			if ((!Thrucheck || consmoves == consmade) &&
			    (incheck || check(b,-co,fr,fc-1)))
			{
				errcode = E_CT;
				return (FALSE);
			}
			break;
		}

		/* Castling King Side */
		if (tc == fc + 2)
		{
			/* Is there a rook in position? */
			if (fc >= Cols - 2 || b[fr][Cols] != co*WR)
			{
				errcode = E_KI;
				return (FALSE);
			}
			/* Are King and Rook in their original squares? */
			if (!((co+1)?wcck:bcck))
			{
				errcode = E_CM;
				return (FALSE);
			}
			/* Are intervening squares clear? */
			if (!cart(b,fr,fc,fr,Cols))
			{
				errcode = E_CP;
				return (FALSE);
			}
			/* Is intervening square under attack? */
			if ((!Thrucheck || consmoves == consmade) &&
			    (incheck || check(b,-co,fr,fc+1)))
			{
				errcode = E_CT;
				return (FALSE);
			}
			break;
		}

	case WJ:
	case BJ:
		/* Ordinary King Move */
		if ((abs(fr-tr) > 1) || (abs(fc-tc) > 1))
		{
			errcode = E_IL | abs(pf);
			return (FALSE);
		}
		break;
	
	case WQ:	/* Queens */
	case BQ:
		errcode = WQ;
		if (cart(b,fr,fc,tr,tc) || diag(b,fr,fc,tr,tc))
			break;
		else
			return(FALSE);
	
	case WB:	/* Bishops */
	case BB:
		errcode = WB;
		if (diag(b,fr,fc,tr,tc))
			break;
		else
			return(FALSE);
		
	case WN:	/* Knights */
	case BN:
		if (((abs(fr-tr) == 2) && (abs(fc-tc) == 1)) ||
		    ((abs(fr-tr) == 1) && (abs(fc-tc) == 2)))
			break;
		else
		{
			errcode = E_NI;
			return(FALSE);
		}

	case WR:	/* Rooks */
	case BR:
		errcode = WR;
		if (cart(b,fr,fc,tr,tc))
			break;
		else
			return(FALSE);
	
	case WP:	/* White Pawns */

		errcode = E_PI;
		if (fc == tc) /* move */
			if ((pt==SQ) && ((tr==fr+1) ||
			    ((!Oldpawn)&&(fr==1)&&(tr==3)&&(b[fr+1][fc]==SQ))))
				break;
			else
				return(FALSE);

		if (pt==SQ) /* capture en-passeunt */
			if ((btr==fr) && (btc==tc) && (bfr==btr+2) &&
			    (b[btr][btc]==BP) && (tr==fr+1) && (abs(fc-tc)==1))
				break;
			else
				return(FALSE);

		if ((tr==fr+1) && (abs(fc-tc)==1)) /* capture */
			break;
		else
			return(FALSE);

	case BP:	/* Black Pawns */

		errcode = E_PI;
		if (fc == tc) /* move */
			if ((pt == SQ) && ((tr==fr-1) ||
			    ((!Oldpawn)&&(fr==Rows-1)&&(tr==Rows-3)&&(b[fr-1][fc]==SQ))))
				break;
			else
				return(FALSE);

		if (pt==SQ) /* capture en-passeunt */
			if ((wtr==fr) && (wtc==tc) && (wfr==wtr-2) &&
			    (b[wtr][wtc]==WP) && (tr==fr-1) && (abs(fc-tc)==1))
				break;
			else
				return(FALSE);

		if ((tr==fr-1) && (abs(fc-tc)==1)) /* capture */
			break;
		else
			return(FALSE);

	case WM:	/* Ministers */
	case BM:
		if ((abs(fr-tr) == 1) && (abs(fc-tc) == 1))
			break;
		else
		{
			errcode = E_MI;
			return(FALSE);
		}

	case WE:	/* Elephants */
	case BE:
		if ((abs(fr-tr) == 2) && (abs(fc-tc) == 2))
			break;
		else
		{
			errcode = E_EI;
			return(FALSE);
		}
	
	case WD:	/* Dukes */
	case BD:
		if ((abs(fr-tr) + abs(fc-tc) == 1))
			break;
		else
		{
			errcode = E_DI;
			return(FALSE);
		}

	case WH:	/* Maharajahs */
	case BH:
		errcode = WR;
		if (((abs(fr-tr) == 1) && (abs(fc-tc) == 2)) ||
		    ((abs(fr-tr) == 2) && (abs(fc-tc) == 1)) ||
		    cart(b,fr,fc,tr,tc) || diag(b,fr,fc,tr,tc))
			break;
		else
		{
			if (errcode == WR)
				errcode = E_HI;
			return(FALSE);
		}
	}

	/* Is there an friendly piece at the destination? */
	if (pt*co > 0)
	{
		errcode = E_AF;
		return(FALSE);
	}

	/* Is the piece forbidden to capture? */
	if (pt*co < 0 && ((pf==Kingpiece(co)) ? Kcapture : Capture) == CA_NONE)
	{
		errcode = E_NC;
		return(FALSE);
	}

	/* May we cross the center? */
	if (Tabiyat && movecnt <= 1 &&
	    ((co == WHITE && tr >= (Rows+1)/2) ||
	     (co == BLACK && tr <= Rows/2)))
	{
		errcode = E_CC;
		return(FALSE);
	}

	/* Should it have been a capture? */
	if (must_take && pt == SQ)
	{
		errcode = E_CR;
		return(FALSE);
	}

	copy(b,bc);
	makemove(bc,co,fr,fc,tr,tc,FALSE);

	/* Does this move leave me in check? */
	if (Kingpiece(co) != SQ)
	{
		find(bc,Kingpiece(co),&r,&c);	/* find the friendly king. */
		if (check(bc,-co,r,c))		/* can an enemy attack him? */
		{
			errcode = E_MC;
			return (FALSE);
		}
	}
	return(TRUE);
}

/* CART()
 *
 *   Can a legal cartesian (rook) move be made between the two positions?
 */

boolean cart(bd,r1,c1,r2,c2)
schar bd[R_SIZE][C_SIZE];
int r1,c1,r2,c2;
{
register int i,x,y;

	if (r1 == r2)
	{
		x = ((c1 < c2) ? c1 : c2) + 1;
		y = ((c1 < c2) ? c2 : c1) - 1;
		for (i = x; i<=y; i++)
			if (bd[r1][i] != SQ)
			{
				errcode |= E_MB;
				return(FALSE);
			}
	}

	else if (c1 == c2)
	{
		x = ((r1 < r2) ? r1 : r2) + 1;
		y = ((r1 < r2) ? r2 : r1) - 1;
		for (i = x; i<=y; i++)
			if (bd[i][c1] != SQ)
			{
				errcode |= E_MB;
				return(FALSE);
			}
	}
	
	else
	{
		errcode |= E_IL;
		return(FALSE);
	}

	return(TRUE);
}

/* DIAG()
 *
 *   Can a legal diagonal (bishop) move be made between the two positions?
 */

boolean diag(bd,r1,c1,r2,c2)
schar bd[R_SIZE][C_SIZE];
int r1,c1,r2,c2;
{
register int i,x,y,n;

	if (r1+c1 == r2+c2)
	{
		x = ((r1 < r2) ? r1 : r2) + 1;
		y = ((r1 < r2) ? c1 : c2) - 1;
		n = abs(r1-r2) - 1;
		for (i=0; i<n; i++)
			if (bd[x+i][y-i] != SQ)
			{
				errcode |= E_MB;
				return(FALSE);
			}
	}
	else if (r1-c1 == r2-c2)
	{
		x = ((r1 < r2) ? r1 : r2) + 1;
		y = ((r1 < r2) ? c1 : c2) + 1;
		n = abs(r1-r2) - 1;
		for (i=0; i<n; i++)
			if (bd[x+i][y+i] != SQ)
			{
				errcode |= E_MB;
				return(FALSE);
			}
	}
	else
	{
		errcode |= E_IL;
		return(FALSE);
	}
	return(TRUE);
}

/* MCHECK()
 *
 *  Is it Ok for a piece of color co to move from bd[fr][fc] to bd[tr][tc]?
 *   Return one of the following:
 *	0 = move legal
 *	1 = check - move doesn't block check (check before move)
 *	2 = check - piece was pinned (no check before move)
 *	3 = move blocked or off board
 * This is for use by the canmove() routine.  It assumes that the move is
 * of a type that is legal for the piece (e.g. along a diagonal for a
 * bishop).  If <cap> is set, only captures are considered legal, and 1
 * is returned for legal moves which are not captures.
 */

mcheck(bd,co,fr,fc,tr,tc,cap)
schar bd[R_SIZE][C_SIZE];
schar co;
REGISTER int fr,fc,tr,tc;
boolean cap;
{
register schar t,f;
boolean flag;
int kr,kc;
schar king = Kingpiece(co);

	/* Check if destination is legal */
	if (tr < 0 || tc < 0 || tr > Rows || tc > Cols || (t=bd[tr][tc])*co > 0)
		return (3);

	/* Make move */
	f = bd[tr][tc] = bd[fr][fc];
	bd[fr][fc] = SQ;

	/* Fix up capture in funny Capture modes */
	/* What type of friendly piece is in a square doesn't matter */
	if (t*co != SQ)
		switch ((f == king) ? Kcapture : Capture)
		{
		case CA_NONE:
			bd[tr][tc] = t;		/* Unmake move and abort */
			bd[fr][fc] = f;
			return(3);
		case CA_RIFLE:
			bd[fr][fc] = f;
			/* no break here! */
		case CA_KAMIKAZI:
			bd[tr][tc] = SQ;
			break;
		case CA_CONVERSION:
			bd[fr][fc] = f;
			if (taken != SQ &&
			    ((co==WHITE && btr==tr && btc==tc) ||
			     (co==BLACK && wtr==tr && wtc==tc)))
				bd[tr][tc] = SQ;
			break;
		}

	/* Check if move leaves you in check */
	if (king != SQ)
	{
		if (f != king)
		{
			find(bd,king,&kr,&kc);
			flag = check(bd,-co,kr,kc);
		}
		else
		{
			flag = check(bd,-co,tr,tc);
			if (flag && t != SQ)
			{
				bd[tr][tc] = t;
				bd[fr][fc] = f;
				return(3);
			}
		}
	}
	else
		flag = FALSE;

	/* Unmake move */
	bd[tr][tc] = t;
	bd[fr][fc] = f;

	if (flag)
		return (incheck ? 1 : 2);
	else
		return((cap && t==SQ) ? 1 : 0);
}

/* CANMOVE()
 *
 *  Can any piece of color <co> move on board <bd>?  For Mate detection.
 *  incheck should be set first using the check detection routine.
 *  if <cap> is set, only captures are considered.
 */

boolean canmove(bd,co,cap)
schar bd[R_SIZE][C_SIZE];
schar co;
boolean cap;
{
register int r,c;
register int tr,tc;
int sr,sc;
int code;

/* This bit of weirdness with the return statement is for debugging */
#define Return(a,b,c,d) return(TRUE);
/* #define Return(a,b,c,d) {printf("canmove: from %x %x to %x %x\r\n",a,b,c,d);return(TRUE);} */

    for (c=0; c <= Cols; c++)
	for (r=0; r <= Rows; r++)
	    switch (co*bd[r][c])
	    {
	    case WK:	/* Kings and Jesters */
	    case WJ:
		for (tr = r-1; tr <= r+1; tr++)
		    for (tc = c-1; tc <= c+1; tc++)
			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
			    Return (r,c,tr,tc)
		break;

	    case WQ:	/* Rooks and Queens and Maharajahs */
	    case WR:
	    case WH:
		for (tr = r-1; tr >= 0; tr--)
		{
			if ((code = mcheck(bd,co,r,c,tr,c,cap))==0)
				Return(r,c,tr,c)
			else
				if (code != 1) break;
		}

		for (tr = r+1; tr <= Rows; tr++)
		{
			if ((code = mcheck(bd,co,r,c,tr,c,cap))==0)
				Return(r,c,tr,c)
			else
				if (code != 1) break;
		}

		for (tc = c-1; tc >= 0; tc--)
		{
			if ((code = mcheck(bd,co,r,c,r,tc,cap))==0)
				Return(r,c,r,tc)
			else
				if (code != 1) break;
		}

		for (tr = c+1; tc <= Cols; tc++)
		{
			if ((code = mcheck(bd,co,r,c,r,tc,cap))==0)
				Return(r,c,r,tc)
			else
				if (code != 1) break;
		}

		if (bd[r][c]*co == WR) break;

	    case WB:	/* Bishops and Queens and Maharajahs */
		for (sr = -1; sr <= 1; sr += 2)
		    for (sc = -1; sc <= 1; sc += 2)
			for (tr = 1; tr <= Rows; tr++)
			{
			    if ((code = mcheck(bd,co,r,c,
						r+sr*tr,c+sc*tr,cap))==0)
				Return(r,c,r+sr*tr,c+sc*tr)
			    else
				if (code != 1) break;
			}

		if (bd[r][c]*co != WH) break;

	    case WN:	/* Knights and Maharajahs */
		if ((code = mcheck(bd,co,r,c,r+1,c+2,cap)) == 0)
			Return(r,c,r+1,c+2)
		else	if (code == 2) break;


		if ((code = mcheck(bd,co,r,c,r-1,c-2,cap)) == 0)
			Return(r,c,r-1,c-2)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r-2,c+1,cap)) == 0)
			Return(r,c,r-2,c+1)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r+2,c-1,cap)) == 0)
			Return(r,c,r+2,c-1)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r-1,c+2,cap)) == 0)
			Return(r,c,r-1,c+2)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r+1,c-2,cap)) == 0)
			Return(r,c,r+1,c-2)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r-2,c-1,cap)) == 0)
			Return(r,c,r-2,c-1)
		else	if (code == 2) break;

		if ((code = mcheck(bd,co,r,c,r+2,c+1,cap)) == 0)
			Return(r,c,r+2,c+1)
		break;

	    case WP:	/* White Pawns */
		if (co == WHITE)
		{
		    if (bd[r+1][c] == SQ && !cap)
		    {
			if ((code = mcheck(bd,co,r,c,r+1,c,cap)) == 0)
				Return(r,c,r+1,c)
			else
				if (!Oldpawn && code == 1 && r == 1 &&
				    bd[3][c]==SQ &&
				    mcheck(bd,co,1,c,3,c,cap) == 0)
					Return(1,c,3,c)
		    }
		    if (c > 0)
			if (bd[r+1][c-1] < 0)
				if (mcheck(bd,co,r,c,r+1,c-1,cap) == 0)
					Return(r,c,r+1,c-1)
			else /* En Passaunt out of check?  That's a new one. */
				if (r == btr && c-1 == btc && bfr == btr + 2 &&
				    bd[btr][btc] == BP)
				{
					bd[btr][btc] = SQ;
					code = mcheck(bd,co,r,c,r+1,c-1,cap);
					bd[btr][btc] = BP;
					if (code == 0) Return (r,c,r+1,c-1)
				}
		    if (c < Cols)
			if (bd[r+1][c+1] < 0)
				if (mcheck(bd,co,r,c,r+1,c+1,cap) == 0)
					Return(r,c,r+1,c+1)
			else /* En Passaunt, would you believe? */
				if (r == btr && c+1 == btc && bfr == btr + 2 &&
				    bd[btr][btc] == BP)
				{
					bd[btr][btc] = SQ;
					code = mcheck(bd,co,r,c,r+1,c+1,cap);
					bd[btr][btc] = BP;
					if (code == 0) Return (r,c,r+1,c+1)
				}
	        }

		else  	/* Black Pawns */
		{
		    if (bd[r-1][c] == SQ && !cap)
			if ((code = mcheck(bd,co,r,c,r-1,c,cap)) == 0)
				Return(r,c,r-1,c)
			else
				if (!Oldpawn && code == 1 && r == Rows-1 &&
				    bd[Rows-3][c]==SQ &&
				    mcheck(bd,co,Rows-1,c,Rows-3,c,cap) == 0)
					Return(Rows-1,c,Rows-3,c)
		    if (c > 0)
			if (bd[r-1][c-1] > 0)
				if (mcheck(bd,co,r,c,r-1,c-1,cap) == 0)
					Return(r,c,r-1,c-1)
			else /* En Passaunt out of check?  That's a new one. */
				if (r == wtr && c-1 == wtc && wfr == wtr - 2 &&
				    bd[wtr][wtc] == WP)
				{
					bd[wtr][wtc] = SQ;
					code = mcheck(bd,co,r,c,r-1,c-1,cap);
					bd[wtr][wtc] = WP;
					if (code == 0) Return (r,c,r-1,c-1)
				}
		    if (c < Cols)
			if (bd[r-1][c+1] > 0)
				if (mcheck(bd,co,r,c,r-1,c+1,cap) == 0)
					Return(r,c,r-1,c+1)
			else /* En Passaunt, would you believe? */
				if (r == btr && c+1 == wtc && wfr == wtr - 2 &&
				    bd[wtr][wtc] == WP)
				{
					bd[wtr][wtc] = SQ;
					code = mcheck(bd,co,r,c,r-1,c+1,cap);
					bd[wtr][wtc] = WP;
					if (code == 0) Return (r,c,r-1,c+1)
				}
		}
		break;

	    case WM:	/* Ministers */
		for (tr = r-1; tr <= r+1; tr+=2)
		    for (tc = c-1; tc <= c+1; tc+=2)
			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
			    Return (r,c,tr,tc)
		break;

	    case WE:	/* Elephants */
		for (tr = r-2; tr <= r+2; tr+=4)
		    for (tc = c-2; tc <= c+2; tc+=4)
			if (mcheck(bd,co,r,c,tr,tc,cap)==0)
			     Return (r,c,tr,tc)
		break;

	    case WD:	/* Dukes */
		if (mcheck(bd,co,r,c,r+1,c,cap)==0) Return(r,c,r+1,c)
		if (mcheck(bd,co,r,c,r-1,c,cap)==0) Return(r,c,r-1,c)
		if (mcheck(bd,co,r,c,r,c+1,cap)==0) Return(r,c,r,c+1)
		if (mcheck(bd,co,r,c,r,c-1,cap)==0) Return(r,c,r,c-1)
		break;
	    }
    return(FALSE);
}


/* COPY()
 *
 *   Copy board <b1> into board <b2>
 */

copy(b1,b2)
schar b1[R_SIZE][C_SIZE], b2[R_SIZE][C_SIZE];
{
int x,y;

	for (x=0;x<=Rows;x++)
		for (y=0; y<=Cols; y++)
			b2[x][y] = b1[x][y];
}

/* COMPARE()
 *
 *   Compare board <b1> to board <b2> and return true if they are equal
 */

boolean compare(b1,b2)
schar b1[R_SIZE][C_SIZE], b2[R_SIZE][C_SIZE];
{
int x,y;

	for (x=0;x<=Rows;x++)
		for (y=0; y<=Cols; y++)
			if (b2[x][y] != b1[x][y])
				return(FALSE);
	return(TRUE);
}


/* FIND()
 *
 *   Find a piece of type <man> on the board.  Mostly used to locate kings.
 *   return -1,-1 if not on board.
 */

find(bd,man,r,c)
schar bd[R_SIZE][C_SIZE];
schar man;
int *r, *c;
{
	for (*r=0;*r<=Rows;(*r)++)
		for (*c=0; *c<=Cols; (*c)++)
			if (bd[*r][*c] == man) return;
	*r = *c = -1;
}

/* FORCES()
 *
 *   Does the player of color <co> have any forces left on <bd> beside his king?
 *   This is used in shatranj and courier only.
 */

boolean forces(co,bd)
schar co;
schar bd[R_SIZE][C_SIZE];
{
int r,c;
	for (r=0; r<=Rows; r++)
		for (c=0; c<=Cols; c++)
			if ( bd[r][c]*co > 0 && bd[r][c] != Kingpiece(co))
				return(TRUE);
	return(FALSE);
}

/* FLUSHMOVES()
 *
 *    The move stack is used to detect repeated positions.  It starts out
 *    empty and is flushed every time a pawn is moved or a piece is captured.
 *    The global variable mvssize gives the number of moves currently on this
 *    stack.  Note that when a castling move is made, both the move of the
 *    king and the move of the rook should be pushed on the stack.
 *
 *    The call flushmoves() empties the stack.
 *
 *    The call pushmove() adds a move to the stack.  At most 50 moves are put
 *    on the stack, since beyond that point the 50-move rule dominates anyways.
 *
 *    The call isrepeat() returns true if the current position has occured
 *    twice before.  It doesn't count beyond 2, since that's all that matters.
 *    Isrepeat() should be called before the new move is placed on the stack.
 *    There should be a call to makemove() between any two calls to isrepeat(),
 *    since makemove() initializes the ndiffs variable.
 */

static struct stackamoves {
	char fr,fc,tr,tc;
	} mvstack[MAXTAME+2];	/* Two extra slots for castling */

flushmoves()
{
	mvssize = 0;
}

pushmove(fr,fc,tr,tc)
int fr,fc,tr,tc;
{
	if (mvssize < MAXTAME+2)
	{
		mvstack[mvssize].fr = (char) fr;
		mvstack[mvssize].fc = (char) fc;
		mvstack[mvssize].tr = (char) tr;
		mvstack[mvssize].tc = (char) tc;
	}
	mvssize++;
}

boolean isrepeat(bd,nbd)
schar bd[R_SIZE][C_SIZE];	/* Board before current move */
schar nbd[R_SIZE][C_SIZE];	/* Current after current move */
{
schar obd[R_SIZE][C_SIZE];	/* Previous board */
schar p;
int mvs;
int nreps;		/* Number of times this position has been repeated */

    /*
     * If I haven't moved since last flush, this is no repeat.
     * If the game is stale, just return any old thing.
     */
    if (mvssize < 1 || mvssize >= MAXTAME+2)
	return(FALSE);

    /* Each piece on the board was where it is */
    copy(bd,obd);

    /*
     * Back through the stack, unmaking the moves on the old board,
     * and counting the number of pieces out of place.  This
     * is easy, since none of the moves on the stack are captures.
     */
    nreps = 0;	/* ndiff initialized by makemove() */
    mvs = mvssize;
    while (mvs-- >= 0)
    {
	/* Unmake the move on the old board */
	p = obd[mvstack[mvs].tr][mvstack[mvs].tc];
	obd[mvstack[mvs].fr][mvstack[mvs].fc] = p;

	/* Does this make or break any similarities? */
	if (nbd[mvstack[mvs].tr][mvstack[mvs].tc] == p)
	    ndiff++;
	else if (nbd[mvstack[mvs].fr][mvstack[mvs].fc] == p)
	    ndiff--;

	/* If there are no differences, count a repeat */
	if (ndiff == 0)
	    if (++nreps == 2)
		return(TRUE);
	}
	return(FALSE);
}


/* SETUP()
 *
 *    Make the default initial setup for various variants games.
 *    The default initial setup is returned in <bd>.
 */

setup(bd)
schar bd[R_SIZE][C_SIZE];
{
	copy(ib,bd);

	switch (Game_type)
	{
	case TY_SHATRANJ:
		bd[0][2] = bd[0][5] = WE;
		bd[0][3] = WM;
		bd[7][2] = bd[7][5] = BE;
		bd[7][3] = BM;
		break;

	case TY_COURIER:
		bd[7][2] = -(bd[0][2] = WE);
		bd[7][3] = -(bd[0][3] = WB);
		bd[7][4] = -(bd[0][4] = WJ);
		bd[7][5] = -(bd[0][5] = WK);
		bd[7][6] = -(bd[0][6] = WM);
		bd[7][7] = -(bd[0][7] = WD);
		break;

	case TY_HALFBOARD:
		bd[0][3] = WK;
		bd[7][3] = BK;
		break;

	case TY_MAHARAJAH:
		bd[0][4] = WH;
		bd[1][2] = bd[1][3] = bd[1][4] = bd[1][5] = SQ;
	case TY_STEAMROLLER:
		bd[0][0] = bd[0][1] = bd[0][2] = bd[0][3] =
		bd[0][5] = bd[0][6] = bd[0][7] =
		bd[1][0] = bd[1][1] = bd[1][6] = bd[1][7] = SQ;
		break;
	}
}