* 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, 2016 Free
+ * Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
int SameColor P((ChessSquare, ChessSquare));
int PosFlags(int index);
-extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
int quickFlag;
char *pieceDesc[EmptySquare];
char *defaultDesc[EmptySquare] = {
#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];
+int promoPartner[EmptySquare];
char
PieceToChar (ChessSquare p)
{
- if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
- return pieceToChar[(int) p];
+ int c;
+ if((int)p < 0 || (int)p >= (int)EmptySquare) return('?'); /* [HGM] for safety */
+ 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
{
int i, j;
- for (i = 0; i < BOARD_HEIGHT; i++)
+ for (i = 0; i < handSize; i++)
for (j = 0; j < BOARD_WIDTH; j++)
to[i][j] = from[i][j];
for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
// dump all engine defined pieces, and pieces with non-standard names,
// but suppress black pieces that are the same as their white counterpart
ChessSquare p;
- static char buf[MSG_SIZ];
- char *m, c, d, *pieceName = defaultName;
- int len;
+ static char buf[MSG_SIZ], s[2];
+ char *m, *pieceName = defaultName;
+ int len, c, d;
*buf = NULLCHAR;
if(!pieceDefs) return "";
if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
for(p=WhitePawn; p<EmptySquare; p++) {
if((c = pieceToChar[p]) == '.' || c == '~') continue; // does not participate
- m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED p] : c);
- if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c)
- && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair
+ m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c);
+ if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32)
+ && (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
- if((p < BlackPawn || CharToPiece(toupper(d)) != EmptySquare) && // white or lone black
+ if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) && // white or lone black
!pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
// TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
if(!m) m = defaultDesc[p];
+ if(!m) continue;
len = strlen(buf);
- snprintf(buf+len, MSG_SIZ-len, "%s%s%c:%s", len ? ";" : "", c == '+' ? "+" : "", d, m);
+ *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63);
+ snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m);
}
return buf;
}
+int
+LoadPieceDesc (char *s)
+{
+ ChessSquare piece;
+ static char suf[] = SUFFIXES;
+ char *r, *p, *q = s;
+ int ok = TRUE, promoted, c;
+ while(q && *s) {
+ p = s;
+ q = strchr(s, ';');
+ if(q) *q = 0, s = q+1;
+ if(*p == '+') promoted = 1, p++; else promoted = 0;
+ c = *p++;
+ if(!c) { ok = FALSE; continue; } // bad syntax
+ if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++;
+ if(*p++ != ':') { ok = FALSE; continue; } // bad syntax
+ if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0
+ piece = CharToPiece(c);
+ if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece
+ if(promoted) {
+ piece = promoPartner[piece];
+ if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist
+ }
+ ASSIGN(pieceDesc[piece], p);
+ if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) {
+ ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p);
+ }
+ pieceDefs = TRUE;
+ if(q) *q = ';';
+ }
+ return ok;
+}
+
// [HGM] gen: configurable move generation from Betza notation sent by engine.
// Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
// square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
}
void
-MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
+MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, int range, char *desc, MoveCallback cb, VOIDSTAR cl)
{
char buf[80], *p = desc, *atom = NULL;
- int mine, his, dir, bit, occup, i, promoRank = -1;
+ 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, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 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
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 && (board[r][f] != initialPosition[r][f] ||
+ if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; initial = 0; } else
+ if(initial && !range) {
+ if( (board[r][f] != initialPosition[r][f] ||
r == 0 && board[TOUCHED_W] & 1<<f ||
- r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f ) ) continue;
- if(expo > 1 && dx == 0 && dy == 0) { // castling indicated by O + number
+ r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f )) continue;
+ initial = 0;
+ }
+ if(expo > 0 && dx == 0 && dy == 0) { // castling indicated by O + number
mode |= 1024; dy = 1;
}
+ if(expo < 0) expo = 1; // use 1 for default
if(!cont) {
if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc
} else {
for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
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
if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
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(board[y][x] == DarkSquare) break; // black squares are supposed to be off board
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 && !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(initial && expo - i + 1 != range) { if(occup == 4) continue; else break; }
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;
+ int rg = (expo != 1 ? expo - i + 1 : range); // pass length of last *slider* leg
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
+ MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
+ if(occup & mode & 3 && (killX < 0 || kill2X < 0 && (legNr > 1 || killX == x && killY == y) ||
+ (legNr == 1 ? kill2X == x && kill2Y == y : killX == x && killY == y))) { // destructive first leg
int cnt = 0;
- MovesFromString(board, flags, f, r, x, y, dir, cont, &OK, &cnt); // count possible continuations
- if(cnt) { // and if there are
- if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
+ legNr <<= 1;
+ MovesFromString(board, flags, f, r, x, y, dir, rg, cont, &OK, &cnt); // count possible continuations
+ legNr >>= 1;
+ if(cnt) { // and if there are
+ if(legNr & 1 ? killX < 0 : kill2X < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
legNr <<= 1;
- MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
+ MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
legNr >>= 1;
}
}
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 & 1024) { // castling
i = 2; // kludge to elongate move indefinitely
if(occup == 4) continue; // skip empty squares
- if((x == BOARD_LEFT || vx < 0 && board[y][x-1] == DarkSquare) && 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 || expo == 1) { // 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 || vx > 0 && board[y][x+1] == DarkSquare) && 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 || expo == 1) {
+ 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(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
void
Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
-{ // Lion-like move of Horned Falcon and Souring Eagle
+{ // Lion-like move of Horned Falcon and Soaring Eagle
int ft = ff + dx, rt = rf + dy;
if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
legNr += 2;
if (!SameColor(board[rf][ff], board[rt][ft]))
- callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
+ callback(board, flags, killX < 0 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
legNr -= 2;
ft += dx; rt += dy;
if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
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);
+ MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], callback, closure);
continue;
}
if(IS_SHOGI(gameInfo.variant))
rf, ff, rf + 1, ff + s, closure);
}
if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
+ int victimFile = (board[LAST_TO] & 0x40 ? ff + s : board[LAST_TO] & 255);
if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
- board[rf][ff + s] == BlackPawn &&
+ (board[EP_FILE] == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
+ (board[rf][victimFile] == BlackPawn || board[rf][victimFile] == BlackLance) &&
board[rf+1][ff + s] == EmptySquare) {
callback(board, flags, WhiteCapturesEnPassant,
rf, ff, rf+1, ff + s, closure);
rf, ff, rf - 1, ff + s, closure);
}
if (rf < BOARD_HEIGHT>>1) {
+ int victimFile = (board[LAST_TO] & 0x40 ? ff + s : board[LAST_TO] & 255);
if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
- (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
- board[rf][ff + s] == WhitePawn &&
+ (board[EP_FILE] == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
+ (board[rf][victimFile] == WhitePawn || board[rf][victimFile] == WhiteLance) &&
board[rf-1][ff + s] == EmptySquare) {
callback(board, flags, BlackCapturesEnPassant,
rf, ff, rf-1, ff + s, 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;
+ case SHOGI WhiteAxe:
+ case SHOGI BlackAxe:
SlideVertical(board, flags, rf, ff, callback, closure);
break;
- case SHOGI (PROMOTED WhiteKnight):
+ case SHOGI (PROMO WhiteKnight):
if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ case SHOGI WhiteClaw:
case SHOGI BlackDrunk:
case SHOGI BlackAlfil:
Ferz(board, flags, rf, ff, callback, 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 BlackClaw:
case SHOGI WhiteDrunk:
case SHOGI WhiteAlfil:
Ferz(board, flags, rf, ff, callback, closure);
break;
- case SHOGI WhiteStag:
- case SHOGI BlackStag:
+ case SHOGI WhiteGnu:
+ case SHOGI BlackGnu:
if(gameInfo.variant == VariantShogi) goto BlackGold;
SlideVertical(board, flags, rf, ff, callback, closure);
Ferz(board, flags, rf, ff, callback, closure);
StepSideways(board, flags, rf, ff, callback, closure);
break;
- case SHOGI (PROMOTED WhiteQueen):
+ case SHOGI (PROMO WhiteQueen):
case SHOGI WhiteTokin:
case SHOGI WhiteWazir:
WhiteGold:
Wazir(board, flags, rf, ff, callback, closure);
break;
- case SHOGI (PROMOTED BlackQueen):
+ case SHOGI (PROMO BlackQueen):
case SHOGI BlackTokin:
case SHOGI BlackWazir:
BlackGold:
case WhiteWazir:
case BlackWazir:
+ if(gameInfo.variant == VariantXiangqi) {
+ int palace = (piece == WhiteWazir ? 1 : BOARD_HEIGHT-2); // Palace center
+ if(ff <= BOARD_WIDTH/2 && !SameColor(board[rf][ff+1], piece)) callback(board, flags, NormalMove, rf, ff, rf, ff+1, closure);
+ if(ff >= BOARD_WIDTH/2 && !SameColor(board[rf][ff-1], piece)) callback(board, flags, NormalMove, rf, ff, rf, ff-1, closure);
+ if(rf >= palace && !SameColor(board[rf-1][ff], piece)) callback(board, flags, NormalMove, rf, ff, rf-1, ff, closure);
+ if(rf <= palace && !SameColor(board[rf+1][ff], piece)) callback(board, flags, NormalMove, rf, ff, rf+1, ff, closure);
+ break;
+ }
Wazir(board, flags, rf, ff, callback, closure);
break;
/* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
case WhiteDragon:
case BlackDragon:
- if(gameInfo.variant == VariantChuChess) goto DragonKing;
+ if(gameInfo.variant == VariantChuChess || gameInfo.variant == VariantSpartan) goto DragonKing;
for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
for (s = -2; s <= 2; s += 4) {
rt = rf + s * d;
ft = ff + s * (1 - d);
if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
- if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
+ if (board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
if (SameColor(board[rf][ff], board[rt][ft])) continue;
callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
}
- if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
- Wazir(board, flags, rf, ff, callback, closure);
- else
Rook(board, flags, rf, ff, callback, closure);
break;
Knight(board, flags, rf, ff, callback, closure);
break;
+ case WhiteTower:
+ case BlackTower:
+ for (d = 0; d <= 1; d++) // Dababba moves
+ for (s = -2; s <= 2; s += 4) {
+ rt = rf + s * d;
+ ft = ff + s * (1 - d);
+ if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+ if (SameColor(board[rf][ff], board[rt][ft])) continue;
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ }
+ Wazir(board, flags, rf, ff, callback, closure);
+ break;
+
/* Shogi Rooks are ordinary Rooks */
case SHOGI WhiteRook:
case SHOGI BlackRook:
case WhiteFerz:
case BlackFerz:
+ if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
+ int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
+ int ft = BOARD_WIDTH>>1;
+ if(!SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ } else
/* [HGM] support Shatranj pieces */
Ferz(board, flags, rf, ff, callback, closure);
break;
if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
- callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
+ callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
rf, ff, rt, ft, closure);
legNr -= 2*i;
}
break;
- case SHOGI WhiteFalcon:
- case SHOGI BlackFalcon:
+ case SHOGI WhiteDagger:
+ case SHOGI BlackDagger:
case SHOGI WhitePDagger:
case SHOGI BlackPDagger:
SlideSideways(board, flags, rf, ff, callback, 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 WhiteSword:
+ case SHOGI BlackSword:
case SHOGI WhitePSword:
case SHOGI BlackPSword:
SlideVertical(board, flags, rf, ff, callback, closure);
StepSideways(board, flags, rf, ff, callback, closure);
break;
- case SHOGI WhiteUnicorn:
- case SHOGI BlackUnicorn:
+ case SHOGI WhiteCat:
+ case SHOGI BlackCat:
Ferz(board, flags, rf, ff, callback, closure);
StepVertical(board, flags, rf, ff, callback, closure);
break;
- case SHOGI WhiteMan:
+ case SHOGI WhiteCopper:
StepDiagForward(board, flags, rf, ff, callback, closure);
StepVertical(board, flags, rf, ff, callback, closure);
break;
- case SHOGI BlackMan:
+ case SHOGI BlackCopper:
StepDiagBackward(board, flags, rf, ff, callback, closure);
StepVertical(board, flags, rf, ff, callback, closure);
break;
SlideVertical(board, flags, rf, ff, callback, closure);
break;
- case SHOGI WhiteHorned:
+ case SHOGI WhiteUnicorn:
Sting(board, flags, rf, ff, 1, 0, callback, closure);
callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
if(killX >= 0) break;
SlideBackward(board, flags, rf, ff, callback, closure);
break;
- case SHOGI BlackHorned:
+ case SHOGI BlackUnicorn:
Sting(board, flags, rf, ff, -1, 0, callback, closure);
callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
if(killX >= 0) break;
SlideForward(board, flags, rf, ff, callback, closure);
break;
- case SHOGI WhiteEagle:
+ case SHOGI WhiteFalcon:
Sting(board, flags, rf, ff, 1, 1, callback, closure);
Sting(board, flags, rf, ff, 1, -1, callback, closure);
callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
SlideDiagBackward(board, flags, rf, ff, callback, closure);
break;
- case SHOGI BlackEagle:
+ case SHOGI BlackFalcon:
Sting(board, flags, rf, ff, -1, 1, callback, closure);
Sting(board, flags, rf, ff, -1, -1, callback, closure);
callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
- if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
+ if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
if (!(flags & F_IGNORE_CHECK) ) {
int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
register CheckTestClosure *cl = (CheckTestClosure *) closure;
if (rt == cl->rking && ft == cl->fking) {
- if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
+ if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
cl->check++;
xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
}
- if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
+ if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
&& (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
}
{
CheckTestClosure cl;
ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
- ChessSquare captured = EmptySquare, ep=0, trampled=0;
+ ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
int saveKill = killX;
/* Suppress warnings on uninitialized variables */
}
}
+ if(PieceToChar(king) == '.') return 0; // never in check if the royal piece does not participate
+
if (rt >= 0) {
if (enPassant) {
captured = board[rf][ft];
board[rf][ft] = EmptySquare;
} else {
captured = board[rt][ft];
- if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; }
+ if(killX >= 0) {
+ trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
+ if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
+ }
}
if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
board[rt][ft] = board[rf][ff];
}
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
}
}
/* For compatibility with ICS wild 9, we scan the board in the
order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
and we test only whether that one is in check. */
+ cl.check = 0;
for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
if (board[cl.rking][cl.fking] == king) {
- cl.check = 0;
if(gameInfo.variant == VariantXiangqi) {
/* [HGM] In Xiangqi opposing Kings means check as well */
int i, dir;
board[rf][ft] = captured;
board[rt][ft] = EmptySquare;
} else {
- if(saveKill >= 0) board[killY][killX] = trampled, killX = saveKill;
+ if(saveKill >= 0) {
+ if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
+ board[killY][killX = saveKill & 0xFFF] = trampled;
+ }
board[rt][ft] = captured;
}
board[EP_STATUS] = ep;
}
- return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
+ return cl.fking < BOARD_RGHT ? cl.check : (gameInfo.variant == VariantAtomic)*1000; // [HGM] atomic: return 1000 if we have no king
}
int
if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
n = PieceToNumber(piece);
- if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
+ if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[handSize-1-n][0]) != piece)
&& gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
return ImpossibleMove; // piece not available
if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
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?) */
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) ||
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;
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;
// 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) {
{
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 ||
piece == WhiteCobra || piece == BlackCobra)
- wildCard = TRUE;
+ wildCard = !pieceDefs; // no wildcards when engine defined pieces
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) &&
(cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
(cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
- if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
+ if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) 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) {
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; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
return;
}
}
- } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
+ } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
DisambiguateClosure spare = *closure;
+ if(gameInfo.variant == VariantXiangqi && closure->pieceIn == EmptySquare && closure->ffIn < 0) {
+ closure->ffIn = closure->ftIn; //closure->pieceIn = (flags & 1 ? BlackPawn : WhitePawn); // forward Pawn push has priority
+ Disambiguate(board, flags, closure);
+ return;
+ }
pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
/* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
if(closure->rfIn != 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) )
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;
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;
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)
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;
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) {
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:
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;
}
*/
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;
int r, f;
for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
c += (board[r][f] == piece); // count on-board pieces of given type
- *outp++ = ToUpper(PieceToChar(piece));
+ *outp = PieceToChar(piece);
+ if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
+ *outp++ = ToUpper(PieceToChar(piece));
+ if(*outp = PieceSuffix(piece)) outp++;
}
if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
*outp++ = ff + AAA;