|
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; } }