I decided to put the following code to generate moves from a simple Betza-notation text string in the public domain.
Code: Select all
/****************************************************/
/* Configurable move generation from Betza notation */
/* Placed in the public domain by H.G.Muller */
/****************************************************/
// 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 per 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)
// board is the current board position
// f, r are the file and rank coordinate of the piece to generate moves for
// desc is a text string describing the piece in Betza notation
// cb is the function to be called (with argument cl) for each pseudo-legal move
{
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); // next step
} // next direction
dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
} while(retry--); // and start doing them
} // next atom
}