|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 26495 (0x677f)
Types: TextFile
Names: »play.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Cchess/play.c«
/* 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;
}
}