X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=moves.c;h=d0cc65c32d61e21ef445836241a80d6db22ebba7;hb=ff23924264802924788f2a15c73a8d058e90432e;hp=787ab57ab04f55a074909ac8f4aec91bebd3ec19;hpb=01c9a60a2a37ba6441eeacca3a81cbb3ae3d484d;p=xboard.git diff --git a/moves.c b/moves.c index 787ab57..d0cc65c 100644 --- a/moves.c +++ b/moves.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -114,19 +114,32 @@ SameColor (ChessSquare piece1, ChessSquare piece2) #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare) #endif -char pieceToChar[] = { +unsigned char pieceToChar[EmptySquare+1] = { 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', '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' }; -char pieceNickName[EmptySquare]; +unsigned char pieceNickName[EmptySquare]; char PieceToChar (ChessSquare p) { + int c; if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */ - return pieceToChar[(int) p]; + c = pieceToChar[(int) p]; + if(c & 128) c = c & 63 | 64; + return c; +} + +char +PieceSuffix (ChessSquare p) +{ + int c; + if((int)p < 0 || (int)p >= (int)EmptySquare) return 0; /* [HGM] for safety */ + c = pieceToChar[(int) p]; + if(c < 128) return 0; + return SUFFIXES[c - 128 >> 6]; } int @@ -201,9 +214,9 @@ CollectPieceDescriptors () if(gameInfo.variant == VariantXiangqi) pieceName = xqName; for(p=WhitePawn; p= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c) - && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair + && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair char *wm = pieceDesc[BLACK_TO_WHITE p]; if(!m && !wm || m && wm && !strcmp(wm, m)) continue; // moves as a white piece } else // white or unpaired black @@ -235,9 +248,14 @@ char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N"; char xStep[] = "2110.130.102.10.00....0..2"; char yStep[] = "2132.133.313.20.11....1..3"; char dirType[] = "01000104000200000260050000"; +char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z"; +char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z"; + // alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z " int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0, 0,0,0,0xF0,0,0,0,0,0,0x0F,0 ,0,0,0 ,0,0,0,0 }; int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 }; +int dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 }; +int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 }; int rot[][4] = { // rotation matrices for each direction { 1, 0, 0, 1 }, @@ -259,21 +277,27 @@ OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOID void MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl) { - char *p = desc; - int mine, his, dir, bit, occup, i; + char buf[80], *p = desc, *atom = NULL; + int mine, his, dir, bit, occup, i, ep, promoRank = -1; + ChessMove promo= NormalMove; ChessSquare pc = board[r][f]; + if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2; + if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else + if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0; while(*p) { // more moves to go - int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1, skip = 0; + int expo = 1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0; char *cont = NULL; - if(*p == 'i') initial = 1, desc = ++p; + while(*p == 'i') initial++, desc = ++p; while(islower(*p)) p++; // skip prefixes if(!isupper(*p)) return; // syntax error: no atom + dx = xStep[*p-'A'] - '0';// step vector of atom + dy = yStep[*p-'A'] - '0'; dirSet = 0; // build direction set based on atom symmetry switch(symmetry[*p-'A']) { case 'B': expo = 0; // bishop, slide - case 'F': // diagonal atom (degenerate 4-fold) - if(tx < 0) { // for continuation legs relative directions are orthogonal! - while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { + case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! + while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { int b = dirs1[*desc-'a']; // use wide version if( islower(desc[1]) && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim) @@ -281,17 +305,21 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle desc += 2; } else desc++; dirSet |= b; - } - dirSet &= 0x55; if(!dirSet) dirSet = 0x55; - break; } + dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA; + break; case 'R': expo = 0; // rook, slide - case 'W': // orthogonal atom (non-deg 4-fold) + case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a']; dirSet &= 0x55; if(!dirSet) dirSet = 0x55; dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system break; - case 'N': // oblique atom (degenerate 8-fold) + case 'N': all = 0xFF; // oblique atom (degenerate 8-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! + if(*desc == 'h') { // chiral direction sets 'hr' and 'hl' + dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2; + } else while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { int b = dirs2[*desc-'a']; // when alone, use narrow version if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version @@ -305,47 +333,73 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle if(!dirSet) dirSet = 0xFF; break; case 'Q': expo = 0; // queen, slide - case 'K': // non-deg (pseudo) 8-fold - dirSet=0x55; // start with orthogonal moves - retry = 1; // and schedule the diagonal moves for later + case 'K': all = 0xFF; // non-deg (pseudo) 8-fold + king: + while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { + int b = dirs4[*desc-'a']; // when alone, use narrow version + if(desc[1] == *desc) desc++; // doubling forces alone + else if(islower(desc[1]) && i < '4' + && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same) + b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide + desc += 2; + } else desc++; + dirSet |= b; + } + if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg + : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags + dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system + ds2 = dirSet & 0xAA; // extract diagonal directions + if(dirSet &= 0x55) // start with orthogonal moves, if present + retry = 1, dx = 0; // and schedule the diagonal moves for later + else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately break; // should not have direction indicators default: return; // syntax error: invalid atom } if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves mode = 0; // build mode mask - if(*desc == 'm') mode |= 4, desc++; - if(*desc == 'c') mode |= his, desc++; - if(*desc == 'd') mode |= mine, desc++; - if(*desc == 'e') mode |= 8, desc++; - if(*desc == 'p') mode |= 32, desc++; - if(*desc == 'g') mode |= 64, desc++; - if(*desc == 'o') mode |= 128, desc++; - if(*desc == 'n') jump = 0, desc++; - while(*desc == 'j') jump++, desc++; - if(*desc == 'a') cont = ++desc; - if(!cont) { - if(!(mode & 15)) mode = his + 4; // no mode spec, use default = mc - } else { - if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through' - if(!(mode & 0x10F)) mode = his + 0x104; // and default = mcp - } - dx = xStep[*p-'A'] - '0'; // step vector of atom - dy = yStep[*p-'A'] - '0'; + if(*desc == 'm') mode |= 4, desc++; // move to empty + if(*desc == 'c') mode |= his, desc++; // capture foe + if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend) + if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover + if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test') + if(*desc == 'p') mode |= 32, desc++; // hop over occupied + if(*desc == 'g') mode |= 64, desc++; // hop and toggle range + if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board + if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square + if(*desc == 'n') jump = 0, desc++; // non-jumping + while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square) + if(*desc == 'a') cont = ++desc; // move again after doing what preceded it if(isdigit(*++p)) expo = atoi(p++); // read exponent if(expo > 9) p++; // allow double-digit desc = p; // this is start of next move + if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; } else if(initial && (board[r][f] != initialPosition[r][f] || r == 0 && board[TOUCHED_W] & 1< 1 && dx == 0 && dy == 0) { // castling indicated by O + number - mode |= 16; dy = 1; + mode |= 1024; dy = 1; + } + if(!cont) { + if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc + } else { + strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify + atom = buf; while(islower(*atom)) atom++; // skip to atom + if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through' + if(mode & 64 + 512) { + mode |= 256; // and 'g' too, but converts leaper <-> slider + if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g' + *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK + atom[1] = atom[2] = '\0'; // make sure any old range is stripped off + if(expo == 1) atom[1] = '0'; // turn other leapers into riders + } + if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m } if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square do { for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions - int i = expo, j = skip, hop = mode, vx, vy; + int i = expo, j = skip, hop = mode, vx, vy, loop = 0; if(!(bit & dirSet)) continue; // does not move in this direction - if(dy != 1) j = 0; // + if(dy != 1 || mode & 1024) j = 0; // vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector vy = dx*rot[dir][2] + dy*rot[dir][3]; if(tx < 0) x = f, y = r; // start square @@ -353,17 +407,21 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle do { // traverse ray x += vx; y += vy; // step to next square if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done - if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT; else break; } - if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT; else break; } + if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; } + if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; } if(j) { j--; continue; } // skip irrespective of occupation + if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop - if(x == f && y == r) occup = 4; else // start square counts as empty + if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!) if(board[y][x] < BlackPawn) occup = 0x101; else if(board[y][x] < EmptySquare) occup = 0x102; else occup = 4; if(cont) { // non-final leg + if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode if(occup & mode) { // valid intermediate square, do continuation + char origAtom = *atom; + if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid if(occup & mode & 0x104) // no side effects, merge legs to one move MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl); if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) { // destructive first leg @@ -376,29 +434,44 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle legNr >>= 1; } } + *atom = origAtom; // undo any interconversion } if(occup != 4) break; // occupied squares always terminate the leg continue; } if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper - if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square + ep = board[EP_RANK]; + if(mode & 8 && occup == 4 && board[EP_FILE] == x && (y == (ep & 127) || y - vy == ep - 128)) { // to e.p. square (or 2nd e.p. square) cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl); } - if(mode & 16) { // castling + if(mode & 1024) { // castling i = 2; // kludge to elongate move indefinitely if(occup == 4) continue; // skip empty squares - if(x == BOARD_LEFT && board[y][x] == initialPosition[y][x]) // reached initial corner piece + if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare) + && board[y][x] == initialPosition[y][x]) { // reached initial corner piece + if(pc != WhiteKing && pc != BlackKing) { // non-royal castling (to be entered as two-leg move via 'Rook') + if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f) + legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1; + } else cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl); - if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x]) + } + if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare) + && board[y][x] == initialPosition[y][x]) { + if(pc != WhiteKing && pc != BlackKing) { + if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f) + legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1; + } else cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl); + } break; } - if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate + if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal + if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate if(occup != 4) break; // not valid transit square } while(--i); } - dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q - } while(retry--); // and start doing them + dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q + } while(retry-- && ds2); // and start doing them if(tx >= 0) break; // don't do other atoms in continuation legs } } // next atom @@ -636,7 +709,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color m = 0; piece = board[rf][ff]; if(PieceToChar(piece) == '~') - piece = (ChessSquare) ( DEMOTED piece ); + piece = (ChessSquare) ( DEMOTED(piece) ); if(filter != EmptySquare && piece != filter) continue; if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure); @@ -812,14 +885,14 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, /* Gold General (and all its promoted versions) . First do the */ /* diagonal forward steps, then proceed as normal Wazir */ - case SHOGI (PROMOTED WhitePawn): + case SHOGI (PROMO WhitePawn): if(gameInfo.variant == VariantShogi) goto WhiteGold; - case SHOGI (PROMOTED BlackPawn): + case SHOGI (PROMO BlackPawn): if(gameInfo.variant == VariantShogi) goto BlackGold; SlideVertical(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteKnight): + case SHOGI (PROMO WhiteKnight): if(gameInfo.variant == VariantShogi) goto WhiteGold; case SHOGI BlackDrunk: case SHOGI BlackAlfil: @@ -828,7 +901,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, StepBackward(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED BlackKnight): + case SHOGI (PROMO BlackKnight): if(gameInfo.variant == VariantShogi) goto BlackGold; case SHOGI WhiteDrunk: case SHOGI WhiteAlfil: @@ -846,7 +919,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, StepSideways(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteQueen): + case SHOGI (PROMO WhiteQueen): case SHOGI WhiteTokin: case SHOGI WhiteWazir: WhiteGold: @@ -854,7 +927,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, Wazir(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED BlackQueen): + case SHOGI (PROMO BlackQueen): case SHOGI BlackTokin: case SHOGI BlackWazir: BlackGold: @@ -1165,9 +1238,9 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, StepVertical(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteFerz): + case SHOGI (PROMO WhiteFerz): if(gameInfo.variant == VariantShogi) goto WhiteGold; - case SHOGI (PROMOTED BlackFerz): + case SHOGI (PROMO BlackFerz): if(gameInfo.variant == VariantShogi) goto BlackGold; case SHOGI WhitePSword: case SHOGI BlackPSword: @@ -1569,14 +1642,16 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant CheckTestClosure cl; ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing; ChessSquare captured = EmptySquare, ep=0, trampled=0; + int saveKill = killX; /* Suppress warnings on uninitialized variables */ if(gameInfo.variant == VariantXiangqi) king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir; if(gameInfo.variant == VariantKnightmate) king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn; - if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check + if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch; + if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon for(r=0; r 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!) @@ -1591,7 +1666,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant board[rf][ft] = EmptySquare; } else { captured = board[rt][ft]; - if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; } + if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; } } if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop board[rt][ft] = board[rf][ff]; @@ -1599,10 +1674,11 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant } ep = board[EP_STATUS]; if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules - ChessSquare victim = killX < 0 ? EmptySquare : trampled; + ChessSquare victim = saveKill < 0 ? EmptySquare : trampled; if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance - (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge' + (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge' + || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between) board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected } } @@ -1639,7 +1715,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant board[rf][ft] = captured; board[rt][ft] = EmptySquare; } else { - if(killX >= 0) board[killY][killX] = trampled; + if(saveKill >= 0) board[killY][killX = saveKill] = trampled; board[rt][ft] = captured; } board[EP_STATUS] = ep; @@ -1715,7 +1791,7 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move. if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft); piece = filterPiece = board[rf][ff]; - if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece; + if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece); /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */ /* (perhaps we should disallow moves that obviously leave us in check?) */ @@ -1755,13 +1831,17 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind; if(promoChar != '+') return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove; - if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove; + if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') { + if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.') + return ImpossibleMove; + } return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; } else if(gameInfo.variant == VariantShogi) { /* [HGM] Shogi promotions. '=' means defer */ if(rf != DROP_RANK && cl.kind == NormalMove) { ChessSquare piece = board[rf][ff]; + int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8); if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */ if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) || @@ -1773,7 +1853,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove; else if(flags & F_WHITE_ON_MOVE) { if( (int) piece < (int) WhiteWazir && - (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) { + (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) { if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 || piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */ cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion; @@ -1781,7 +1861,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion; } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove; } else { - if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) { + if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) { if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 || piece == BlackKnight && rt < 2 ) /* promotion obligatory */ cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion; @@ -1798,7 +1878,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo // should test if in zone, really if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags)) return IllegalMove; - if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; + if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; } else if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) { @@ -1911,6 +1991,7 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in { register DisambiguateClosure *cl = (DisambiguateClosure *) closure; int wildCard = FALSE; ChessSquare piece = board[rf][ff]; + extern int kifu; // in parser.c // [HGM] wild: for wild-card pieces rt and rf are dummies if(piece == WhiteFalcon || piece == BlackFalcon || @@ -1919,7 +2000,7 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff] || PieceToChar(board[rf][ff]) == '~' - && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff]) + && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff])) ) && (cl->rfIn == -1 || cl->rfIn == rf) && (cl->ffIn == -1 || cl->ffIn == ff) && @@ -1928,6 +2009,18 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move + if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required + int this = 1, other = 1; + if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf); + if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf); + if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf); + if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff); + if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff); + if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used + if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it + if(!this) return; // the current move does not satisfy the requested relative position, ignore it + } + cl->count++; if(cl->count == 1 || board[rt][ft] != EmptySquare) { // [HGM] oneclick: if multiple moves, be sure we remember capture @@ -1969,7 +2062,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); if (closure->count == 0) { /* No, it's not even that */ - if(!appData.testLegality && closure->pieceIn != EmptySquare) { + if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) { int f, r; // if there is only a single piece of the requested type on the board, use that closure->rt = closure->rtIn, closure->ft = closure->ftIn; for(r=0; rrfIn != DROP_RANK && closure->kind == NormalMove) { ChessSquare piece = closure->piece; + int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8); if (c == 'd' && (piece == WhiteRook || piece == BlackRook) || c == 'h' && (piece == WhiteBishop || piece == BlackBishop) || c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) ) @@ -2025,7 +2119,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal else if(flags & F_WHITE_ON_MOVE) { if( (int) piece < (int) WhiteWazir && - (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) { + (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) { if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 || piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */ closure->kind = c == '=' ? IllegalMove : WhitePromotion; @@ -2033,7 +2127,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; } else closure->kind = c == '+' ? IllegalMove : NormalMove; } else { - if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) { + if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) { if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 || piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */ closure->kind = c == '=' ? IllegalMove : BlackPromotion; @@ -2048,8 +2142,10 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) { if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) + gameInfo.variant == VariantMakruk) c = PieceToChar(BlackFerz); + else if(gameInfo.variant == VariantASEAN) + c = PieceToChar(BlackRook); else if(gameInfo.variant == VariantGreat) c = PieceToChar(BlackMan); else if(gameInfo.variant == VariantGrand) @@ -2060,7 +2156,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove; } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it ChessSquare p = closure->piece; - if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+') + if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+') closure->kind = ImpossibleMove; // used on non-promotable piece else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove; } else if (c != NULLCHAR) closure->kind = IllegalMove; @@ -2105,7 +2201,7 @@ CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int f if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare (board[rf][ff] == cl->piece || PieceToChar(board[rf][ff]) == '~' && - (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece) + (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece) ) { if (rf == cl->rf) { if (ff == cl->ff) { @@ -2149,7 +2245,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p if (promoChar == 'x') promoChar = NULLCHAR; piece = board[rf][ff]; - if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece); + if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece)); switch (piece) { case WhitePawn: @@ -2241,13 +2337,13 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p cl.kind = IllegalMove; cl.rank = cl.file = cl.either = 0; c = PieceToChar(piece) ; - GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed + GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { /* Generate pretty moves for moving into check, but still return IllegalMove. */ - GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); + GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); if (cl.kind == IllegalMove) break; cl.kind = IllegalMove; } @@ -2259,10 +2355,11 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p */ if( c == '~' || c == '+') { /* [HGM] print nonexistent piece as its demoted version */ - piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu)); + piece = (ChessSquare) (CHUDEMOTED(piece)); } if(c=='+') *outp++ = c; *outp++ = ToUpper(PieceToChar(piece)); + if(*outp = PieceSuffix(piece)) outp++; if (cl.file || (cl.either && !cl.rank)) { *outp++ = ff + AAA;