X-Git-Url: http://winboard.nl/cgi-bin?p=xboard.git;a=blobdiff_plain;f=moves.c;h=b4572feeeb2ead49b375a6c23ab0f1282430bbc3;hp=64dbae9071e60d55eb46bc4880ea31bcb098bf16;hb=d016fb202fe45795e630e22ba516e754cf694ea6;hpb=91d8e5853ca580769cc130aa6ea004869118d171 diff --git a/moves.c b/moves.c index 64dbae9..b4572fe 100644 --- a/moves.c +++ b/moves.c @@ -1,11 +1,13 @@ /* * moves.c - Move generation and checking - * $Id: moves.c,v 2.1 2003/10/27 19:21:00 mann Exp $ * * Copyright 1991 by Digital Equipment Corporation, Maynard, - * Massachusetts. Enhancements Copyright - * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software - * Foundation, Inc. + * Massachusetts. + * + * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, + * 2007, 2008, 2009 Free Software Foundation, Inc. + * + * Enhancements Copyright 2005 Alessandro Scotti * * The following terms apply to Digital Equipment Corporation's copyright * interest in XBoard: @@ -65,8 +67,9 @@ 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 */ +extern signed char initialRights[BOARD_SIZE]; /* [HGM] all rights enabled, set in InitPosition */ int WhitePiece(piece) @@ -137,7 +140,7 @@ ChessSquare PromoPiece(moveType) char pieceToChar[] = { 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', - 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 's', 'U', 'K', + 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K', 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm', 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k', 'x' }; @@ -583,7 +586,7 @@ void GenPseudoLegal(board, flags, epfile, callback, closure) if (board[rt][ft] != EmptySquare) break; } if(m==1) goto mounted; - if(m==2) goto finishGold; + if(m==2) goto finishSilver; break; case WhiteQueen: @@ -647,7 +650,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 +729,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; @@ -1005,7 +1009,7 @@ int CheckTest(board, flags, rf, ff, rt, ft, enPassant) } } - return cl.check; + return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king } @@ -1025,6 +1029,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 +1060,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 */ @@ -1130,15 +1140,45 @@ int MateTest(board, flags, epfile, castlingRights) char castlingRights[]; { MateTestClosure cl; - int inCheck; + int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0; + ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing; + for(r=0; r= (int)king - (int)WhiteKing + (int)WhitePawn) + myPieces++; + else hisPieces++; + } + } + if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces); + switch(gameInfo.variant) { // [HGM] losers: extinction wins + case VariantShatranj: + if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW; + default: + break; + case VariantAtomic: + if(nrKing == 0) return MT_NOKING; + break; + case VariantLosers: + if(myPieces == 1) return MT_BARE; + } cl.count = 0; inCheck = GenLegal(board, flags, epfile, castlingRights, MateTestCallback, (VOIDSTAR) &cl); + // [HGM] 3check: yet to do! if (cl.count > 0) { return inCheck ? MT_CHECK : MT_NONE; } else { - return inCheck || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj ? - MT_CHECKMATE : MT_STALEMATE; + if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies + return myPieces == hisPieces ? MT_STALEMATE : + myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE; + else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE; + else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win + + return inCheck ? MT_CHECKMATE + : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ? + MT_STAINMATE : MT_STALEMATE; } } @@ -1590,4 +1630,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