X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=moves.c;h=970393f3299c8901f85b04ac76151acbb3373af8;hb=9384069316e400042076bd749ba0c6e0b07427fa;hp=64dbae9071e60d55eb46bc4880ea31bcb098bf16;hpb=91d8e5853ca580769cc130aa6ea004869118d171;p=xboard.git diff --git a/moves.c b/moves.c index 64dbae9..970393f 100644 --- a/moves.c +++ b/moves.c @@ -65,6 +65,7 @@ int WhitePiece P((ChessSquare)); int BlackPiece P((ChessSquare)); int SameColor P((ChessSquare, ChessSquare)); +int PosFlags(int index); extern char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */ @@ -647,7 +648,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure) case SHOGI BlackKing: case WhiteKing: case BlackKing: - walking: +// walking: for (i = -1; i <= 1; i++) for (j = -1; j <= 1; j++) { if (i == 0 && j == 0) continue; @@ -726,6 +727,7 @@ void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure) typedef struct { int rf, ff, rt, ft; ChessMove kind; + int captures; // [HGM] losers } LegalityTestClosure; @@ -1025,6 +1027,8 @@ void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure) // if (appData.debugMode) { // fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE); // } + if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant) + cl->captures++; // [HGM] losers: count legal captures if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) cl->kind = kind; } @@ -1054,7 +1058,11 @@ ChessMove LegalityTest(board, flags, epfile, castlingRights, rf, ff, rt, ft, pro cl.rt = rt; cl.ft = ft; cl.kind = IllegalMove; + cl.captures = 0; // [HGM] losers: prepare to count legal captures. GenLegal(board, flags, epfile, castlingRights, LegalityTestCallback, (VOIDSTAR) &cl); + if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare + && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant) + return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal if(gameInfo.variant == VariantShogi) { /* [HGM] Shogi promotions. '=' means defer */ @@ -1137,8 +1145,9 @@ int MateTest(board, flags, epfile, castlingRights) if (cl.count > 0) { return inCheck ? MT_CHECK : MT_NONE; } else { - return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ? - MT_CHECKMATE : MT_STALEMATE; + return inCheck ? MT_CHECKMATE + : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ? + MT_STAINMATE : MT_STALEMATE; } } @@ -1590,4 +1599,221 @@ ChessMove CoordsToAlgebraic(board, flags, epfile, return IllegalMove; } +// [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules) +typedef struct { + /* Input */ + int rf, ff, rt, ft; + /* Output */ + int recaptures; +} ChaseClosure; + +// I guess the following variables logically belong in the closure too, but I was too lazy and used globals + +int preyStackPointer, chaseStackPointer; + +struct { +char rf, ff, rt, ft; +} chaseStack[100]; + +struct { +char rank, file; +} preyStack[100]; + + + + +// there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture + +extern void AtacksCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ // For adding captures that can lead to chase indictment to the chaseStack + if(board[rt][ft] == EmptySquare) return; // non-capture + if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased + if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased + if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase + if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase + // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack + chaseStack[chaseStackPointer].rf = rf; + chaseStack[chaseStackPointer].ff = ff; + chaseStack[chaseStackPointer].rt = rt; + chaseStack[chaseStackPointer].ft = ft; + chaseStackPointer++; +} + +extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind, + int rf, int ff, int rt, int ft, + VOIDSTAR closure)); + +void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure) + Board board; + int flags; + ChessMove kind; + int rf, ff, rt, ft; + VOIDSTAR closure; +{ // for removing pre-exsting captures from the chaseStack, to be left with newly created ones + int i; + register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop + + if(board[rt][ft] == EmptySquare) return; // no capture + if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new + rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack + } + // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture) + for(i=0; irt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square + if(appData.debugMode && board[rt][ft] != EmptySquare) + fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures); +} + +extern char moveList[MAX_MOVES][MOVE_LEN]; + +int PerpetualChase(int first, int last) +{ // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking) + int i, j, k, tail; + ChaseClosure cl; + ChessSquare captured; + + preyStackPointer = 0; // clear stack of chased pieces + for(i=first; i= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type + if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim; + + if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook) + continue; // C or H attack on R is always chase; leave on chaseStack + + if(attacker == victim) { + if(LegalityTest(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, chaseStack[j].rt, + chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) { + // we can capture back with equal piece, so this is no chase but a sacrifice + chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack + j--; /* ! */ continue; + } + + } + + // the attack is on a lower piece, or on a pinned or blocked equal one + // test if the victim is protected by a true protector. First make the capture. + captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft]; + boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff]; + boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare; + // Then test if the opponent can recapture + cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it + cl.rt = chaseStack[j].rt; + cl.ft = chaseStack[j].ft; + if(appData.debugMode) { + fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE); + } + GenLegal(boards[i+1], PosFlags(i+1), EP_NONE, initialRights, ProtectedCallback, &cl); // try all moves + // unmake the capture + boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft]; + boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured; + // if a recapture was found, piece is protected, and we are not chasing it. + if(cl.recaptures) { // attacked piece was defended by true protector, no chase + chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack + j--; /* ! */ + } + } + // chaseStack now contains all moves that chased + if(appData.debugMode) { int n; + for(n=0; n