* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+ * Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
'x' };
unsigned char pieceNickName[EmptySquare];
int promoPartner[EmptySquare];
+unsigned char autoProm[EmptySquare];
char
PieceToChar (ChessSquare p)
{
int c;
- if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
+ 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;
{
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 == VariantShogi) pieceName = shogiName;
- if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
+ if(gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantJanggi) 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)
+ 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
(*(int*)cl)++;
}
+static int viaX = 100, viaY = 100, epFlag;
+
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, 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;
+ if(gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantJanggi) {
+ 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;
- char *cont = NULL;
+ int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0, put = 0, u = 0;
+ char *cont = NULL, *q;
while(*p == 'i') initial++, desc = ++p;
while(islower(*p)) p++; // skip prefixes
if(!isupper(*p)) return; // syntax error: no atom
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
+ if(dx && dx != dy) break; // oblique continuation
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
}
if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
mode = 0; // build mode mask
+ if(*desc == 'u') put++, desc++; // unload stuff at start of leg
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 == 'x') mode = mine|1<<12, desc++; // induction step
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(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] ||
+ if(tx == -2 && mode & 1<<12) continue; // prevent recursive move borrowing (flaky)
+ epFlag = // flags initial orthogonal and diagonal pawn non-capture multi-pushes (which have legacy meaning)
+ (initial && promo != NormalMove && !cont && mode == 4 && (!dx || dx == dy) && (dy > 1 ? !jump : expo > 1));
+ 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;
+ 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;
}
} else {
strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
atom = buf; while(islower(*atom)) atom++; // skip to atom
+ for(q=buf; q!=atom && *q != 'a'; q++) // test whether next leg unloads
+ if(*q == 'u') u = 1;
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(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(j) { j--; continue; } // skip irrespective of occupation
+ if(j > 0) { 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(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(skip < 0) mode |= 4; // 'n' = 'm' + rights creation in non-final step leg
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
+ int transp = (occup | 1<<12) & mode & 0x1104; // no side effect on intermediate square
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
+ if(transp && !u) { // no side effects, merge legs to one move
+ if(skip < 0 && occup == 4) { // create e.p. rights on this square
+ if(viaX != 100) { // second e.p. square!
+ if(viaX == x && viaY == y - vy) viaY = y | 128; // flag it when we can handle it
+ } else viaX = x, viaY = y;
+ }
+ if(mode & 1<<12) MovesFromString(board, flags, x, y, x, y, dir, rg, cont, cb, cl), occup = 0; else
+ MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
+ if(viaY & 128) viaY = y - vy; else viaX = viaY = 100;
+ }
+ if((occup & mode & 3 || transp && u) && (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;
}
}
break;
}
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 & mode) {
+ if(mode & 1<<12) {
+ ChessSquare neighbor = board[y][x];
+ char *borrow = (neighbor == pc ? NULL : pieceDesc[neighbor]); // do not borrow from equal type
+ if(borrow) MovesFromString(board, flags, f, r, -2, -2, dir, range, borrow, cb, cl); // borrow moves from neighbor
+ } else cb(board, flags, y == promoRank ? promo : put ? Swap : NormalMove, r, f, y, x, cl); // allowed, generate
+ }
if(occup != 4) break; // not valid transit square
} while(--i);
}
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;
}
}
+void
+Zebra (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
+{
+ int i, j, s, rt, ft;
+ for (i = -1; i <= 1; i += 2)
+ for (j = -1; j <= 1; j += 2)
+ for (s = 2; s <= 3; s++) {
+ rt = rf + i*s;
+ ft = ff + j*(5-s);
+ if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
+ && ( gameInfo.variant != VariantJanggi || board[rf+i*(s-2)][ff+j*(3-s)] == EmptySquare && board[rf+i*(s-1)][ff+j*(4-s)] == EmptySquare)
+ && !SameColor(board[rf][ff], board[rt][ft]))
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ }
+}
+
+void
+PalaceDiags (Board board, int flags, int rf, int ff, int isRook, MoveCallback callback, VOIDSTAR closure)
+{ // Janggi diagonal palace moves
+ int piece = board[rf][ff];
+ int middle = BOARD_WIDTH/2;
+ int palace = (rf < 3 ? 1 : BOARD_HEIGHT-2);
+ if(ff == middle) {
+ if(rf == palace && isRook) Ferz(board, flags, rf, ff, callback, closure);
+ } else if((ff == middle+1 || ff == middle-1) && (rf == palace+1 || rf == palace-1)) { // Palace corner
+ int rt = 2*palace - rf, ft = 2*middle - ff; // reflect
+ ChessSquare center = board[palace][middle];
+ if(isRook && !SameColor(piece, center))
+ callback(board, flags, NormalMove, rf, ff, palace, middle, closure);
+ if(center == WhiteCannon || center == BlackCannon) return;
+ if((center == EmptySquare) == isRook && !SameColor(piece, board[rt][ft])
+ && piece + board[rt][ft] != WhiteCannon + BlackCannon)
+ callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
+ }
+}
+
/* Call callback once for each pseudo-legal move in the given
position, except castling moves. A move is pseudo-legal if it is
legal, or if it would be legal except that it leaves the king in
{
int rf, ff;
int i, j, d, s, fs, rs, rt, ft, m;
+ int vari = gameInfo.variant;
int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
- int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
+ int dead = (vari == VariantSChess && !gameInfo.holdingsSize ? 1 : 0);
+ int promoRank = vari == VariantMakruk || vari == VariantGrand || vari == VariantChuChess ? 3 : 1;
- for (rf = 0; rf < BOARD_HEIGHT; rf++)
+ if(vari == VariantSChess && !gameInfo.holdingsSize) promoRank = 2;
+ for (rf = dead; rf < BOARD_HEIGHT - dead; rf++)
for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
ChessSquare 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))
+ if(IS_SHOGI(vari))
piece = (ChessSquare) ( SHOGI piece );
switch ((int)piece) {
break;
case WhitePawn:
- if(gameInfo.variant == VariantXiangqi) {
+ if(vari != VariantNormal && (vari == VariantXiangqi || vari == VariantJanggi)) {
/* [HGM] capture and move straight ahead in Xiangqi */
if (rf < BOARD_HEIGHT-1 &&
!SameColor(board[rf][ff], board[rf + 1][ff]) ) {
}
/* and move sideways when across the river */
for (s = -1; s <= 1; s += 2) {
- if (rf >= BOARD_HEIGHT>>1 &&
+ if ((rf >= BOARD_HEIGHT>>1 || vari == VariantJanggi) &&
ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
!WhitePiece(board[rf][ff+s]) ) {
callback(board, flags, NormalMove,
rf, ff, rf, ff+s, closure);
}
}
+ if(vari == VariantJanggi) { // diagonal moves in palace
+ int d = BOARD_HEIGHT - rf;
+ if(d == 3 || d == 2) {
+ if(ff == BOARD_WIDTH/2 - d + 2 && !WhitePiece(board[rf+1][ff+1]))
+ callback(board, flags, NormalMove, rf, ff, rf+1, ff+1, closure);
+ if(ff == BOARD_WIDTH/2 + d - 2 && !WhitePiece(board[rf+1][ff-1]))
+ callback(board, flags, NormalMove, rf, ff, rf+1, ff-1, closure);
+ }
+ }
break;
}
if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
callback(board, flags,
- rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
+ rf >= BOARD_HEIGHT-1-promoRank && !autoProm[WhitePawn] ? WhitePromotion : NormalMove,
rf, ff, rf + 1, ff, closure);
}
if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
- gameInfo.variant != VariantShatranj && /* [HGM] */
- gameInfo.variant != VariantCourier && /* [HGM] */
+ vari != VariantShatranj && /* [HGM] */
+ vari != VariantCourier && /* [HGM] */
board[rf+2][ff] == EmptySquare ) {
callback(board, flags, NormalMove,
rf, ff, rf+2, ff, closure);
((flags & F_KRIEGSPIEL_CAPTURE) ||
BlackPiece(board[rf + 1][ff + s]))) {
callback(board, flags,
- rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
+ rf >= BOARD_HEIGHT-1-promoRank && !autoProm[WhitePawn] ? WhitePromotion : NormalMove,
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);
break;
case BlackPawn:
- if(gameInfo.variant == VariantXiangqi) {
+ if(vari != VariantNormal && (vari == VariantXiangqi || vari == VariantJanggi)) {
/* [HGM] capture straight ahead in Xiangqi */
if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
callback(board, flags, NormalMove,
}
/* and move sideways when across the river */
for (s = -1; s <= 1; s += 2) {
- if (rf < BOARD_HEIGHT>>1 &&
+ if ((rf < BOARD_HEIGHT>>1 || vari == VariantJanggi) &&
ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
!BlackPiece(board[rf][ff+s]) ) {
callback(board, flags, NormalMove,
rf, ff, rf, ff+s, closure);
}
}
+ if(vari == VariantJanggi) { // diagonal moves in palace
+ if(rf == 1 || rf == 2) {
+ if(ff == BOARD_WIDTH/2 - rf + 1 && !BlackPiece(board[rf-1][ff+1]))
+ callback(board, flags, NormalMove, rf, ff, rf-1, ff+1, closure);
+ if(ff == BOARD_WIDTH/2 + rf - 1 && !BlackPiece(board[rf-1][ff-1]))
+ callback(board, flags, NormalMove, rf, ff, rf-1, ff-1, closure);
+ }
+ }
break;
}
if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
callback(board, flags,
- rf <= promoRank ? BlackPromotion : NormalMove,
+ rf <= promoRank && !autoProm[BlackPawn] ? BlackPromotion : NormalMove,
rf, ff, rf - 1, ff, closure);
}
if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
- gameInfo.variant != VariantShatranj && /* [HGM] */
- gameInfo.variant != VariantCourier && /* [HGM] */
+ vari != VariantShatranj && /* [HGM] */
+ vari != VariantCourier && /* [HGM] */
board[rf-2][ff] == EmptySquare) {
callback(board, flags, NormalMove,
rf, ff, rf-2, ff, closure);
((flags & F_KRIEGSPIEL_CAPTURE) ||
WhitePiece(board[rf - 1][ff + s]))) {
callback(board, flags,
- rf <= promoRank ? BlackPromotion : NormalMove,
+ rf <= promoRank && !autoProm[BlackPawn] ? BlackPromotion : NormalMove,
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);
rt = rf + i*s;
ft = ff + j*(3-s);
if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
- && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
+ && ( vari == VariantNormal ||
+ vari != VariantJanggi && vari != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
&& !SameColor(board[rf][ff], board[rt][ft]))
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
case WhiteCannon:
case BlackCannon:
+ if(vari == VariantJanggi) PalaceDiags(board, flags, rf, ff, FALSE, callback, closure);
for (d = 0; d <= 1; d++)
for (s = -1; s <= 1; s += 2) {
m = 0;
rt = rf + (i * s) * d;
ft = ff + (i * s) * (1 - d);
if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
+ if(vari == VariantJanggi) {
+ if(board[rt][ft] == WhiteCannon || board[rt][ft] == BlackCannon) break;
+ } else
if (m == 0 && board[rt][ft] == EmptySquare)
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
- if (m == 1 && board[rt][ft] != EmptySquare &&
+ if (m == 1 && (board[rt][ft] != EmptySquare || vari == VariantJanggi) &&
!SameColor(board[rf][ff], board[rt][ft]) )
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
/* Gold General (and all its promoted versions) . First do the */
/* diagonal forward steps, then proceed as normal Wazir */
case SHOGI (PROMO WhitePawn):
- if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ if(vari == VariantShogi) goto WhiteGold;
case SHOGI (PROMO BlackPawn):
- if(gameInfo.variant == VariantShogi) goto BlackGold;
+ if(vari == VariantShogi) goto BlackGold;
+ case SHOGI WhiteAxe:
+ case SHOGI BlackAxe:
SlideVertical(board, flags, rf, ff, callback, closure);
break;
case SHOGI (PROMO WhiteKnight):
- if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ if(vari == VariantShogi) goto WhiteGold;
+ case SHOGI WhiteClaw:
case SHOGI BlackDrunk:
case SHOGI BlackAlfil:
Ferz(board, flags, rf, ff, callback, closure);
break;
case SHOGI (PROMO BlackKnight):
- if(gameInfo.variant == VariantShogi) goto BlackGold;
+ if(vari == VariantShogi) goto BlackGold;
+ case SHOGI BlackClaw:
case SHOGI WhiteDrunk:
case SHOGI WhiteAlfil:
Ferz(board, flags, rf, ff, callback, closure);
case SHOGI WhiteGnu:
case SHOGI BlackGnu:
- if(gameInfo.variant == VariantShogi) goto BlackGold;
+ if(vari == VariantShogi) goto BlackGold;
SlideVertical(board, flags, rf, ff, callback, closure);
Ferz(board, flags, rf, ff, callback, closure);
StepSideways(board, flags, rf, ff, callback, closure);
case WhiteWazir:
case BlackWazir:
+ janggi:
+ if(vari == VariantXiangqi || vari == VariantJanggi) {
+ int palace = (piece < BlackPawn ? 1 : BOARD_HEIGHT-2); // Palace center
+ int middle = BOARD_WIDTH/2;
+ if(ff <= middle && !SameColor(board[rf][ff+1], piece)) callback(board, flags, NormalMove, rf, ff, rf, ff+1, closure);
+ if(ff >= middle && !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);
+ if(vari == VariantJanggi) {
+ if(ff == middle) {
+ if(rf == palace) Ferz(board, flags, rf, ff, callback, closure);
+ } else if(rf != palace && !SameColor(board[palace][middle], piece))
+ callback(board, flags, NormalMove, rf, ff, palace, middle, closure);
+ }
+ break;
+ }
Wazir(board, flags, rf, ff, callback, closure);
break;
case WhiteAlfil:
case BlackAlfil:
+ if(vari == VariantJanggi) { Zebra(board, flags, rf, ff, callback, closure); break; }
/* [HGM] support Shatranj pieces */
for (rs = -1; rs <= 1; rs += 2)
for (fs = -1; fs <= 1; fs += 2) {
rt = rf + 2 * rs;
ft = ff + 2 * fs;
if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
- && ( gameInfo.variant != VariantXiangqi ||
+ && ( vari != VariantXiangqi ||
board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
&& !SameColor(board[rf][ff], board[rt][ft]))
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
- if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
+ if(vari == VariantShatranj || vari == VariantCourier ||
+ vari == VariantChu || vari == VariantXiangqi) continue; // classical Alfil
rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
ft = ff + fs;
if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
callback(board, flags, NormalMove,
rf, ff, rt, ft, closure);
}
- if(gameInfo.variant == VariantSpartan)
+ if(vari == VariantSpartan)
for(fs = -1; fs <= 1; fs += 2) {
ft = ff + fs;
if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
/* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
case WhiteCardinal:
case BlackCardinal:
- if(gameInfo.variant == VariantChuChess) goto DragonHorse;
+ if(vari == VariantChuChess) goto DragonHorse;
for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
for (s = -2; s <= 2; s += 4) {
rt = rf + s * d;
/* Shogi Lance is unlike anything, and asymmetric at that */
case SHOGI WhiteQueen:
- if(gameInfo.variant == VariantChu) goto doQueen;
+ if(vari == VariantChu) goto doQueen;
for(i = 1;; i++) {
rt = rf + i;
ft = ff;
break;
case SHOGI BlackQueen:
- if(gameInfo.variant == VariantChu) goto doQueen;
+ if(vari == VariantChu) goto doQueen;
for(i = 1;; i++) {
rt = rf - i;
ft = ff;
/* 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(vari == VariantChuChess || vari == 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;
case WhiteMarshall:
case BlackMarshall:
Rook(board, flags, rf, ff, callback, closure);
- if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
+ if(vari == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
Ferz(board, flags, rf, ff, callback, closure);
else
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 WhiteRook:
+ case BlackRook:
+ if(vari == VariantJanggi) PalaceDiags(board, flags, rf, ff, TRUE, callback, closure);
case SHOGI WhiteRook:
case SHOGI BlackRook:
case SHOGI WhitePRook:
case SHOGI BlackPRook:
- case WhiteRook:
- case BlackRook:
Rook(board, flags, rf, ff, callback, closure);
break;
break;
case WhiteMan:
- if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
+ if(vari != VariantMakruk && vari != VariantASEAN) goto commoner;
case SHOGI WhiteFerz:
Ferz(board, flags, rf, ff, callback, closure);
StepForward(board, flags, rf, ff, callback, closure);
break;
case BlackMan:
- if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
+ if(vari != VariantMakruk && vari != VariantASEAN) goto commoner;
case SHOGI BlackFerz:
StepBackward(board, flags, rf, ff, callback, closure);
case WhiteFerz:
case BlackFerz:
+ if(vari == VariantJanggi) goto janggi;
+ if(vari == 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;
// Use Lance as Berolina / Spartan Pawn.
case WhiteLance:
- if(gameInfo.variant == VariantSuper) goto Amazon;
+ if(vari == VariantSuper) goto Amazon;
if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
callback(board, flags,
rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
break;
case BlackLance:
- if(gameInfo.variant == VariantSuper) goto Amazon;
+ if(vari == VariantSuper) goto Amazon;
if (rf > 0 && WhitePiece(board[rf - 1][ff]))
callback(board, flags,
rf <= promoRank ? BlackPromotion : NormalMove,
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);
break;
case SHOGI (PROMO WhiteFerz):
- if(gameInfo.variant == VariantShogi) goto WhiteGold;
+ if(vari == VariantShogi) goto WhiteGold;
case SHOGI (PROMO BlackFerz):
- if(gameInfo.variant == VariantShogi) goto BlackGold;
+ if(vari == 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 WhiteCat:
+ 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 BlackCat:
+ 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 WhiteDagger:
+ 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 BlackDagger:
+ 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(gameInfo.variant == VariantXiangqi)
+ if(gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantJanggi)
king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
if(gameInfo.variant == VariantKnightmate)
king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
}
}
+ 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];
/* 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 = saveKill] = trampled;
+ 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
+}
+
+typedef struct { int rt, ft, ep; } EnPassantClosure;
+
+void
+EnPassantCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
+{
+ EnPassantClosure *cl = (EnPassantClosure *) closure;
+ if(rt == cl->rt && ft == cl->ft) {
+ if(kind = WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) cl->ep = 2; // is e.p. capture
+ if(epFlag) cl->ep = 0; // for backward compatibility lets any imn pawn multi-push handle by old code
+ if(viaX != 100) board[EP_FILE] = viaX, board[EP_RANK] = viaY, cl->ep = 1; // generates e.p. rights
+ }
+}
+
+int
+EnPassantTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
+{ // sets e.p. square in board if move generated rights, returns whether move is an e.p. capture
+ EnPassantClosure cl;
+ ChessSquare piece = board[rf][ff];
+ cl.rt = rt; cl.ft = ft; cl.ep = 0;
+ MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], EnPassantCallback, (void *) &cl);
+ return cl.ep;
}
int
ChessMove
LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
{ // [HGM] put drop legality testing in separate routine for clarity
- int n;
+ int n, p = piece;
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(PieceToChar(piece) == '+') p = CHUDEMOTED(p);
+ n = PieceToNumber(p);
+ if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[handSize-1-n][0]) != p)
&& 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(gameInfo.variant == VariantShogi && !autoProm[piece]) { // in Shogi lots of drops are forbidden! (but not in Kyoto/micro-)
if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
(piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
- if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
+ if(autoProm[piece]) promoChar = NULLCHAR; // ignore promotion characters on auto-promoting pieces
+ if(gameInfo.variant == VariantSChess) {
+ if(!gameInfo.holdingsSize) { // holdingless Seirawan
+ if(promoChar == NULLCHAR) { // gating indicator could be missing
+ if(flags & F_WHITE_ON_MOVE) {
+ if(rf == 1) {
+ if(board[0][ff] != DarkSquare) return WhitePromotion; // mandatory gating, report it as promotion
+ if(cl.kind == WhiteQueenSideCastle && board[0][0] != DarkSquare) return WhitePromotion;
+ if(cl.kind == WhiteKingSideCastle && board[0][BOARD_RGHT-1] != DarkSquare) return WhitePromotion;
+ }
+ } else {
+ if(rf == BOARD_HEIGHT-2) {
+ if(board[BOARD_HEIGHT-1][ff] != DarkSquare) return BlackPromotion;
+ if(cl.kind == BlackQueenSideCastle && board[BOARD_HEIGHT-1][0] != DarkSquare) return BlackPromotion;
+ if(cl.kind == BlackKingSideCastle && board[BOARD_HEIGHT-1][BOARD_RGHT-1] != DarkSquare) return BlackPromotion;
+ }
+ }
+ }
+ } else if(promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
if(board[rf][ff] < BlackPawn) { // white
if(rf != 0) return IllegalMove; // must be on back rank
if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
}
+ }
} else
if(gameInfo.variant == VariantChu) {
if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
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
+ else if(gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantDuck) return MT_STEALMATE; // no check exists, stalemated = win
+ else if(gameInfo.variant == VariantJanggi && !inCheck) return MT_NONE; // in Janggi turn passing is always an option
return inCheck ? MT_CHECKMATE
: (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
// [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->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;
return;
}
}
- } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
+ } else if(closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
+ if((gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantJanggi) &&
+ (closure->pieceIn == WhitePawn || closure->pieceIn == BlackPawn) && closure->ffIn < 0) {
+ closure->ffIn = closure->ftIn; //closure->pieceIn = (flags & 1 ? BlackPawn : WhitePawn); // forward Pawn push has priority
+ Disambiguate(board, flags, closure);
+ return;
+ }
+ if(pieceDefs) {
DisambiguateClosure spare = *closure;
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?)
pieceDefs = TRUE;
+ }
}
if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
if(closure->piece < BlackPawn) { // white
- if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
- if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
+ if(closure->rf != !gameInfo.holdingsSize) closure->kind = IllegalMove; // must be on back rank
+ if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
+ if(gameInfo.holdingsSize) {
if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
+ }
} else {
- if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
- if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
+ if(closure->rf != BOARD_HEIGHT-1-!gameInfo.holdingsSize) closure->kind = IllegalMove;
+ if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
+ if(gameInfo.holdingsSize) {
if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
+ }
}
} else
if(gameInfo.variant == VariantChu) {
ChessMove kind;
char *outp = out, c, capture;
CoordsToAlgebraicClosure cl;
+ int d;
if (rf == DROP_RANK) {
if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
/* Bughouse piece drop */
- *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
+ c = PieceToChar((ChessSquare) ff);
+ if(c == '+') c = pieceNickName[ff]; // must have nickname for promote drop
+ *outp++ = ToUpper(c);
*outp++ = '@';
*outp++ = ft + AAA;
if(rt+ONE <= '9')
else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
} else {
/* Capture; use style "exd5" */
+ if(gameInfo.variant == VariantJanggi && (rt < 2 || rt > BOARD_HEIGHT-3) && ft > BOARD_WIDTH/2 - 2 && ft < BOARD_WIDTH/2 + 2) {
+ *outp++ = rf + ONE;
+ }
if(capture)
*outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
*outp++ = ft + AAA;
case WhiteKing:
case BlackKing:
+ d = (gameInfo.variant == VariantSChess && !gameInfo.holdingsSize);
/* Fabien moved code: FRC castling first (if KxR), wild castling second */
/* Code added by Tord: FRC castling. */
if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
/* Test for castling or ICS wild castling */
/* Use style "O-O" (oh-oh) for PGN compatibility */
else if (rf == rt &&
- rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
+ rf == ((piece == WhiteKing) ? d : BOARD_HEIGHT-1-d) &&
(ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
(ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
- snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
+ snprintf(out, MOVE_LEN, "O-O%c%c%c", promoChar ? '/' : 0, ToUpper(promoChar), ff + AAA);
else
- snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
+ snprintf(out, MOVE_LEN, "O-O-O%c%c%c", promoChar ? '/' : 0, ToUpper(promoChar), ff + AAA);
/* This notation is always unambiguous, unless there are
kings on both the d and e files, with "wild castling"
else "N1f3" or "N5xf7",
else "Ng1f3" or "Ng5xf7".
*/
+ if(c=='+') {
+ c = pieceNickName[piece]; // prefer any nick over +X notation
+ if(c < 'A') *outp++ = c = '+';
+ }
if( c == '~' || c == '+') {
/* [HGM] print nonexistent piece as its demoted version */
- piece = (ChessSquare) (CHUDEMOTED(piece));
+ c = PieceToChar((ChessSquare) (CHUDEMOTED(piece)));
}
- if(c=='+') *outp++ = c;
- *outp++ = ToUpper(PieceToChar(piece));
+ *outp++ = ToUpper(c);
if(*outp = PieceSuffix(piece)) outp++;
if (cl.file || (cl.either && !cl.rank)) {
if(rt+ONE <= '9')
*outp++ = rt + ONE;
else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
+ if(autoProm[piece]) promoChar = 0; // no promotion suffix for implied promotions
if (IS_SHOGI(gameInfo.variant)) {
/* [HGM] in Shogi non-pawns can promote */
*outp++ = promoChar; // Don't bother to correct move type, return value is never used!
*outp++ = ToUpper(promoChar);
}
else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
+ ChessSquare victim = board[rt][ft];
+ if(piece == WhiteRook && victim == WhiteKing ||
+ piece == BlackRook && victim == BlackKing) {
+ strncpy(out, "O-O-O", MOVE_LEN);
+ outp = out + 3 + 2*(ft > ff);
+ }
*outp++ = '/';
*outp++ = ToUpper(promoChar);
+ if(out[0] == 'O') *outp++ = ff + AAA;
}
*outp = NULLCHAR;
return cl.kind;
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;