#include "config.h"
#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
#if HAVE_STRING_H
# include <string.h>
#else /* not HAVE_STRING_H */
extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
int quickFlag;
+char *pieceDesc[EmptySquare];
int
WhitePiece (ChessSquare piece)
return TRUE;
}
+// [HGM] gen: configurable move generation from Betza notation sent by engine.
+
+// alphabet "abcdefghijklmnopqrstuvwxyz"
+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";
+// 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 rot[][4] = { // rotation matrices for each direction
+ { 1, 0, 0, 1 },
+ { 0, 1, 1, 0 },
+ { 0, 1,-1, 0 },
+ { 1, 0, 0,-1 },
+ {-1, 0, 0,-1 },
+ { 0,-1,-1, 0 },
+ { 0,-1, 1, 0 },
+ {-1, 0, 0, 1 }
+};
+
+void
+MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
+{
+ char *p = desc;
+ int mine, his, dir, bit, occup, i;
+ if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
+ while(*p) { // more moves to go
+ int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1;
+ if(*p == 'i') initial = 1, desc = ++p;
+ while(islower(*p)) p++; // skip prefixes
+ if(!isupper(*p)) return; // syntax error: no atom
+ 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)
+ 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)
+ b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
+ desc += 2;
+ } else desc++;
+ dirSet |= b;
+ }
+ dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
+ break;
+ case 'R': expo = 0; // rook, slide
+ case 'W': // orthogonal atom (non-deg 4-fold)
+ while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
+ dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+ break;
+ case 'N': // oblique atom (degenerate 8-fold)
+ 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
+ else if(islower(desc[1]) && i < '4'
+ && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
+ b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
+ desc += 2;
+ } else desc++;
+ dirSet |= b;
+ }
+ 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
+ break; // should not have direction indicators
+ default: return; // syntax error: invalid atom
+ }
+ if(mine == 2) 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(!mode) mode = his + 4;// no mode spec, use default = mc
+ if(*desc == 'p') mode |= 32, desc++;
+ if(*desc == 'g') mode |= 64, desc++;
+ if(*desc == 'n') jump = 0, desc++;
+ while(*desc == 'j') jump++, desc++;
+ dx = xStep[*p-'A'] - '0'; // step vector of atom
+ dy = yStep[*p-'A'] - '0';
+ 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] ||
+ 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
+ mode |= 16; dy = 1;
+ }
+ do {
+ for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
+ int i = expo, hop = mode, vx, vy;
+ if(!(bit & dirSet)) continue; // does not move in this direction
+ vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
+ vy = dx*rot[dir][2] + dy*rot[dir][3];
+ x = f; y = r; // start square
+ do {
+ x += vx; y += vy; // step to next square
+ if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
+ 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 = 1; else
+ if(board[y][x] < EmptySquare) occup = 2; else
+ occup = 4;
+ 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
+ cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
+ }
+ if(mode & 16) { // 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
+ cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
+ if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
+ 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(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
+ }
+} // next atom
+
// [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
void
for (rf = 0; rf < BOARD_HEIGHT; rf++)
for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
ChessSquare piece;
- int rookRange;
if(board[rf][ff] == EmptySquare) continue;
if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
- rookRange = 1000;
m = 0; piece = board[rf][ff];
if(PieceToChar(piece) == '~')
piece = (ChessSquare) ( DEMOTED piece );
if(filter != EmptySquare && piece != filter) continue;
+ if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
if(IS_SHOGI(gameInfo.variant))
piece = (ChessSquare) ( SHOGI piece );
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 || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
+ 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 (SameColor(board[rf][ff], board[rt][ft])) continue;
callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
}
- if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
- goto doRook;
+ 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;
/* Shogi Dragon King has to continue as Ferz after Rook moves */
case SHOGI WhiteDragon:
case SHOGI BlackPRook:
case WhiteRook:
case BlackRook:
- doRook:
Rook(board, flags, rf, ff, callback, closure);
break;
int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
+ char *p;
cl.cb = callback;
cl.cl = closure;
wKing = WhiteUnicorn; bKing = BlackUnicorn;
}
+ p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
+ if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
+
for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
if ((flags & F_WHITE_ON_MOVE) &&
(flags & F_WHITE_KCASTLE_OK) &&
if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[0][ft] == WhiteRook)
- callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+ if(ft != NoRights && board[0][ft] == WhiteRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
+ if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
+ }
ft = castlingRights[1]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
if(ff > BOARD_LEFT+2)
for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[0][ft] == WhiteRook)
- callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
+ if(ft != NoRights && board[0][ft] == WhiteRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
+ if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
+ }
}
} else {
ff = castlingRights[5]; /* King file if we have any rights */
if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
- callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+ if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+ if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+ }
ft = castlingRights[4]; /* Rook file if we have A-side rights */
left = BOARD_LEFT+2;
if(ff > BOARD_LEFT+2)
for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
- if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
- callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
+ if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
+ if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
+ if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
+ }
}
}
{
CheckTestClosure cl;
ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
- ChessSquare captured = EmptySquare, ep, trampled;
+ ChessSquare captured = EmptySquare, ep=0, trampled=0;
/* Suppress warnings on uninitialized variables */
if(gameInfo.variant == VariantXiangqi)
piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
if(piece == WhitePawn || piece == BlackPawn) {
- int r;
+ int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
for(r=1; r<BOARD_HEIGHT-1; r++)
- if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
+ if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
// should still test if we mate with this Pawn
}
} else if(gameInfo.variant == VariantSChess) { // only back-rank drops
/* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
/* (perhaps we should disallow moves that obviously leave us in check?) */
- if(piece == WhiteFalcon || piece == BlackFalcon ||
- piece == WhiteCobra || piece == BlackCobra)
+ if((piece == WhiteFalcon || piece == BlackFalcon ||
+ piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu)
return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
cl.rf = rf;
if(rf != 0) return IllegalMove; // must be on back rank
if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
+ if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
+ if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
} else {
if(rf != BOARD_HEIGHT-1) return IllegalMove;
if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
+ 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(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
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(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) {
{
ChessSquare piece;
ChessMove kind;
- char *outp = out, c;
+ char *outp = out, c, capture;
CoordsToAlgebraicClosure cl;
if (rf == DROP_RANK) {
}
/* Pawn move */
*outp++ = ff + AAA;
- if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
+ capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
+ if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
/* Non-capture; use style "e5" */
if(rt+ONE <= '9')
*outp++ = rt + ONE;
else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
} else {
/* Capture; use style "exd5" */
- if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
+ if(capture)
*outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
*outp++ = ft + AAA;
if(rt+ONE <= '9')