2 * moves.c - Move generation and checking
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
9 * Software Foundation, Inc.
11 * Enhancements Copyright 2005 Alessandro Scotti
13 * The following terms apply to Digital Equipment Corporation's copyright
15 * ------------------------------------------------------------------------
18 * Permission to use, copy, modify, and distribute this software and its
19 * documentation for any purpose and without fee is hereby granted,
20 * provided that the above copyright notice appear in all copies and that
21 * both that copyright notice and this permission notice appear in
22 * supporting documentation, and that the name of Digital not be
23 * used in advertising or publicity pertaining to distribution of the
24 * software without specific, written prior permission.
26 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
28 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
29 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
30 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
31 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
33 * ------------------------------------------------------------------------
35 * The following terms apply to the enhanced version of XBoard
36 * distributed by the Free Software Foundation:
37 * ------------------------------------------------------------------------
39 * GNU XBoard is free software: you can redistribute it and/or modify
40 * it under the terms of the GNU General Public License as published by
41 * the Free Software Foundation, either version 3 of the License, or (at
42 * your option) any later version.
44 * GNU XBoard is distributed in the hope that it will be useful, but
45 * WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47 * General Public License for more details.
49 * You should have received a copy of the GNU General Public License
50 * along with this program. If not, see http://www.gnu.org/licenses/. *
52 *------------------------------------------------------------------------
53 ** See the file ChangeLog for a revision history. */
62 #else /* not HAVE_STRING_H */
64 #endif /* not HAVE_STRING_H */
70 int WhitePiece P((ChessSquare));
71 int BlackPiece P((ChessSquare));
72 int SameColor P((ChessSquare, ChessSquare));
73 int PosFlags(int index);
76 char *pieceDesc[EmptySquare];
77 char *defaultDesc[EmptySquare] = {
78 "fmWfceFifmnD", "N", "B", "R", "Q",
79 "F", "A", "BN", "RN", "W", "K",
80 "mRcpR", "N0", "BW", "RF", "gQ",
81 "", "", "QN", "", "N", "",
83 "", "", "", "", "", "",
85 "", "", "", "", "", "K"
89 WhitePiece (ChessSquare piece)
91 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
95 BlackPiece (ChessSquare piece)
97 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
102 SameColor (ChessSquare piece1, ChessSquare piece2)
104 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
105 (int) piece1 < (int) BlackPawn &&
106 (int) piece2 >= (int) WhitePawn &&
107 (int) piece2 < (int) BlackPawn)
108 || ((int) piece1 >= (int) BlackPawn &&
109 (int) piece1 < (int) EmptySquare &&
110 (int) piece2 >= (int) BlackPawn &&
111 (int) piece2 < (int) EmptySquare);
114 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
117 unsigned char pieceToChar[EmptySquare+1] = {
118 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
119 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
120 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
121 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
123 unsigned char pieceNickName[EmptySquare];
124 int promoPartner[EmptySquare];
127 PieceToChar (ChessSquare p)
130 if((int)p < 0 || (int)p >= (int)EmptySquare) return('?'); /* [HGM] for safety */
131 c = pieceToChar[(int) p];
132 if(c & 128) c = c & 63 | 64;
137 PieceSuffix (ChessSquare p)
140 if((int)p < 0 || (int)p >= (int)EmptySquare) return 0; /* [HGM] for safety */
141 c = pieceToChar[(int) p];
142 if(c < 128) return 0;
143 return SUFFIXES[c - 128 >> 6];
147 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
150 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
152 while(start++ != p) if(pieceToChar[start-1] != '.' && pieceToChar[start-1] != '+') i++;
160 if(c == '.') return EmptySquare;
161 for(i=0; i< (int) EmptySquare; i++)
162 if(pieceNickName[i] == c) return (ChessSquare) i;
163 for(i=0; i< (int) EmptySquare; i++)
164 if(pieceToChar[i] == c) return (ChessSquare) i;
169 CopyBoard (Board to, Board from)
173 for (i = 0; i < BOARD_HEIGHT; i++)
174 for (j = 0; j < BOARD_WIDTH; j++)
175 to[i][j] = from[i][j];
176 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
177 to[VIRGIN][j] = from[VIRGIN][j],
178 to[CASTLING][j] = from[CASTLING][j];
179 to[HOLDINGS_SET] = 0; // flag used in ICS play
183 CompareBoards (Board board1, Board board2)
187 for (i = 0; i < BOARD_HEIGHT; i++)
188 for (j = 0; j < BOARD_WIDTH; j++) {
189 if (board1[i][j] != board2[i][j])
195 char defaultName[] = "PNBRQ......................................K" // white
196 "pnbrq......................................k"; // black
197 char shogiName[] = "PNBRLS...G.++++++..........................K" // white
198 "pnbrls...g.++++++..........................k"; // black
199 char xqName[] = "PH.R.AE..K.C................................" // white
200 "ph.r.ae..k.c................................"; // black
203 CollectPieceDescriptors ()
204 { // make a line of piece descriptions for use in the PGN Piece tag:
205 // dump all engine defined pieces, and pieces with non-standard names,
206 // but suppress black pieces that are the same as their white counterpart
208 static char buf[MSG_SIZ], s[2];
209 char *m, *pieceName = defaultName;
212 if(!pieceDefs) return "";
213 if(gameInfo.variant == VariantChu) return ""; // for now don't do this for Chu Shogi
214 if(gameInfo.variant == VariantShogi) pieceName = shogiName;
215 if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
216 for(p=WhitePawn; p<EmptySquare; p++) {
217 if((c = pieceToChar[p]) == '.' || c == '~') continue; // does not participate
218 m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c);
219 if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32)
220 && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair
221 char *wm = pieceDesc[BLACK_TO_WHITE p];
222 if(!m && !wm || m && wm && !strcmp(wm, m)) continue; // moves as a white piece
223 } else // white or unpaired black
224 if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) && // white or lone black
225 !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
226 // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
227 if(!m) m = defaultDesc[p];
230 *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63);
231 snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m);
237 LoadPieceDesc (char *s)
240 static char suf[] = SUFFIXES;
242 int ok = TRUE, promoted, c;
246 if(q) *q = 0, s = q+1;
247 if(*p == '+') promoted = 1, p++; else promoted = 0;
249 if(!c) { ok = FALSE; continue; } // bad syntax
250 if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++;
251 if(*p++ != ':') { ok = FALSE; continue; } // bad syntax
252 if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0
253 piece = CharToPiece(c);
254 if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece
256 piece = promoPartner[piece];
257 if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist
259 ASSIGN(pieceDesc[piece], p);
260 if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) {
261 ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p);
268 // [HGM] gen: configurable move generation from Betza notation sent by engine.
269 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
270 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
271 // if the move has 1 or 2 legs. Only the marking of squares makes use of this info, by only marking
272 // target squares of leg 1 (rejecting null move). A dummy move with MoveType 'FirstLeg' to the relay square
273 // is generated, so a cyan marker can be put there, and other functions can ignore such a move. When the
274 // user selects this square, it becomes the kill-square. Once a kill-square is set, only 2-leg moves are
275 // generated that use that square as relay, plus 1-leg moves, so the 1-leg move that goes to the kill-square
276 // can be marked during 2nd-leg entry to terminate the move there. For judging the pseudo-legality of the
277 // 2nd leg, the from-square has to be considered empty, although the moving piece is still on it.
281 // alphabet "abcdefghijklmnopqrstuvwxyz"
282 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
283 char xStep[] = "2110.130.102.10.00....0..2";
284 char yStep[] = "2132.133.313.20.11....1..3";
285 char dirType[] = "01000104000200000260050000";
286 char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z";
287 char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z";
289 // 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 "
290 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 };
291 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 };
292 int dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 };
293 int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 };
295 int rot[][4] = { // rotation matrices for each direction
307 OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
313 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, int range, char *desc, MoveCallback cb, VOIDSTAR cl)
315 char buf[80], *p = desc, *atom = NULL;
316 int mine, his, dir, bit, occup, i, ep, promoRank = -1;
317 ChessMove promo= NormalMove; ChessSquare pc = board[r][f];
318 if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board
319 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
320 if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
321 if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
322 while(*p) { // more moves to go
323 int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
325 while(*p == 'i') initial++, desc = ++p;
326 while(islower(*p)) p++; // skip prefixes
327 if(!isupper(*p)) return; // syntax error: no atom
328 dx = xStep[*p-'A'] - '0';// step vector of atom
329 dy = yStep[*p-'A'] - '0';
330 dirSet = 0; // build direction set based on atom symmetry
331 switch(symmetry[*p-'A']) {
332 case 'B': expo = 0; // bishop, slide
333 case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold)
334 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
335 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
336 int b = dirs1[*desc-'a']; // use wide version
337 if( islower(desc[1]) &&
338 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
339 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
344 dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
346 case 'R': expo = 0; // rook, slide
347 case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold)
348 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
349 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
350 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
351 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
353 case 'N': all = 0xFF; // oblique atom (degenerate 8-fold)
354 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
355 if(*desc == 'h') { // chiral direction sets 'hr' and 'hl'
356 dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2;
358 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
359 int b = dirs2[*desc-'a']; // when alone, use narrow version
360 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
361 else if(*desc == desc[1] || islower(desc[1]) && i < '4'
362 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
363 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
368 if(!dirSet) dirSet = 0xFF;
370 case 'Q': expo = 0; // queen, slide
371 case 'K': all = 0xFF; // non-deg (pseudo) 8-fold
373 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
374 int b = dirs4[*desc-'a']; // when alone, use narrow version
375 if(desc[1] == *desc) desc++; // doubling forces alone
376 else if(islower(desc[1]) && i < '4'
377 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
378 b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide
383 if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg
384 : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags
385 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
386 ds2 = dirSet & 0xAA; // extract diagonal directions
387 if(dirSet &= 0x55) // start with orthogonal moves, if present
388 retry = 1, dx = 0; // and schedule the diagonal moves for later
389 else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately
390 break; // should not have direction indicators
391 default: return; // syntax error: invalid atom
393 if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
394 mode = 0; // build mode mask
395 if(*desc == 'm') mode |= 4, desc++; // move to empty
396 if(*desc == 'c') mode |= his, desc++; // capture foe
397 if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend)
398 if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover
399 if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test')
400 if(*desc == 'p') mode |= 32, desc++; // hop over occupied
401 if(*desc == 'g') mode |= 64, desc++; // hop and toggle range
402 if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board
403 if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square
404 if(*desc == 'n') jump = 0, desc++; // non-jumping
405 while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square)
406 if(*desc == 'a') cont = ++desc; // move again after doing what preceded it
407 if(isdigit(*++p)) expo = atoi(p++); // read exponent
408 if(expo > 9) p++; // allow double-digit
409 desc = p; // this is start of next move
410 if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; initial = 0; } else
411 if(initial && !range) {
412 if( (board[r][f] != initialPosition[r][f] ||
413 r == 0 && board[TOUCHED_W] & 1<<f ||
414 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f )) continue;
417 if(expo > 0 && dx == 0 && dy == 0) { // castling indicated by O + number
418 mode |= 1024; dy = 1;
420 if(expo < 0) expo = 1; // use 1 for default
422 if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc
424 strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
425 atom = buf; while(islower(*atom)) atom++; // skip to atom
426 if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through'
427 if(mode & 64 + 512) {
428 mode |= 256; // and 'g' too, but converts leaper <-> slider
429 if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g'
430 *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK
431 atom[1] = atom[2] = '\0'; // make sure any old range is stripped off
432 if(expo == 1) atom[1] = '0'; // turn other leapers into riders
434 if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m
436 if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square
438 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
439 int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
440 if(!(bit & dirSet)) continue; // does not move in this direction
441 if(dy != 1 || mode & 1024) j = 0; //
442 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
443 vy = dx*rot[dir][2] + dy*rot[dir][3];
444 if(tx < 0) x = f, y = r; // start square
445 else x = tx, y = ty; // from previous to-square if continuation
447 x += vx; y += vy; // step to next square
448 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
449 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
450 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
451 if(j) { j--; continue; } // skip irrespective of occupation
452 if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board
453 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
454 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
455 if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!)
456 if(board[y][x] < BlackPawn) occup = 0x101; else
457 if(board[y][x] < EmptySquare) occup = 0x102; else
459 if(initial && expo - i + 1 != range) { if(occup == 4) continue; else break; }
460 if(cont) { // non-final leg
461 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
462 if(occup & mode) { // valid intermediate square, do continuation
463 char origAtom = *atom;
464 int rg = (expo != 1 ? expo - i + 1 : range); // pass length of last *slider* leg
465 if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
466 if(occup & mode & 0x104) // no side effects, merge legs to one move
467 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
468 if(occup & mode & 3 && (killX < 0 || kill2X < 0 && (legNr > 1 || killX == x && killY == y) ||
469 (legNr == 1 ? kill2X == x && kill2Y == y : killX == x && killY == y))) { // destructive first leg
472 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, &OK, &cnt); // count possible continuations
474 if(cnt) { // and if there are
475 if(legNr & 1 ? killX < 0 : kill2X < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
477 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
481 *atom = origAtom; // undo any interconversion
483 if(occup != 4) break; // occupied squares always terminate the leg
486 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
488 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)
489 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
491 if(mode & 1024) { // castling
492 i = 2; // kludge to elongate move indefinitely
493 if(occup == 4) continue; // skip empty squares
494 if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
495 && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
496 if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook')
497 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f)
498 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
500 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
502 if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
503 && board[y][x] == initialPosition[y][x]) {
504 if(pc != WhiteKing && pc != BlackKing || expo == 1) {
505 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f)
506 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
508 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
512 if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
513 if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
514 if(occup != 4) break; // not valid transit square
517 dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q
518 } while(retry-- && ds2); // and start doing them
519 if(tx >= 0) break; // don't do other atoms in continuation legs
523 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
526 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
531 if (rt >= BOARD_HEIGHT) break;
532 if (SameColor(board[rf][ff], board[rt][ft])) break;
533 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
534 if (board[rt][ft] != EmptySquare) break;
539 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
545 if (SameColor(board[rf][ff], board[rt][ft])) break;
546 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
547 if (board[rt][ft] != EmptySquare) break;
552 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
554 SlideForward(board, flags, rf, ff, callback, closure);
555 SlideBackward(board, flags, rf, ff, callback, closure);
559 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
561 int i, s, rt = rf, ft;
562 for(s = -1; s <= 1; s+= 2) {
565 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
566 if (SameColor(board[rf][ff], board[rt][ft])) break;
567 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
568 if (board[rt][ft] != EmptySquare) break;
574 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
577 for(s = -1; s <= 1; s+= 2) {
581 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
582 if (SameColor(board[rf][ff], board[rt][ft])) break;
583 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
584 if (board[rt][ft] != EmptySquare) break;
590 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
593 for(s = -1; s <= 1; s+= 2) {
597 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
598 if (SameColor(board[rf][ff], board[rt][ft])) break;
599 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
600 if (board[rt][ft] != EmptySquare) break;
606 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
608 SlideVertical(board, flags, rf, ff, callback, closure);
609 SlideSideways(board, flags, rf, ff, callback, closure);
613 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
615 SlideDiagForward(board, flags, rf, ff, callback, closure);
616 SlideDiagBackward(board, flags, rf, ff, callback, closure);
620 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
621 { // Lion-like move of Horned Falcon and Soaring Eagle
622 int ft = ff + dx, rt = rf + dy;
623 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
625 if (!SameColor(board[rf][ff], board[rt][ft]))
626 callback(board, flags, killX < 0 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
629 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
631 if (!SameColor(board[rf][ff], board[rt][ft]))
632 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
633 if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
634 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
639 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
641 int ft = ff, rt = rf + 1;
642 if (rt >= BOARD_HEIGHT) return;
643 if (SameColor(board[rf][ff], board[rt][ft])) return;
644 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
648 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
650 int ft = ff, rt = rf - 1;
652 if (SameColor(board[rf][ff], board[rt][ft])) return;
653 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
657 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
661 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
662 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
664 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
665 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
669 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
672 if (rt >= BOARD_HEIGHT) return;
674 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
675 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
677 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
678 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
682 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
687 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
688 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
690 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
691 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
695 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
697 StepForward(board, flags, rf, ff, callback, closure);
698 StepBackward(board, flags, rf, ff, callback, closure);
702 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
704 StepDiagForward(board, flags, rf, ff, callback, closure);
705 StepDiagBackward(board, flags, rf, ff, callback, closure);
709 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
711 StepVertical(board, flags, rf, ff, callback, closure);
712 StepSideways(board, flags, rf, ff, callback, closure);
716 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
719 for (i = -1; i <= 1; i += 2)
720 for (j = -1; j <= 1; j += 2)
721 for (s = 1; s <= 2; s++) {
724 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
725 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
726 && !SameColor(board[rf][ff], board[rt][ft]))
727 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
731 /* Call callback once for each pseudo-legal move in the given
732 position, except castling moves. A move is pseudo-legal if it is
733 legal, or if it would be legal except that it leaves the king in
734 check. In the arguments, epfile is EP_NONE if the previous move
735 was not a double pawn push, or the file 0..7 if it was, or
736 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
737 Promotion moves generated are to Queen only.
740 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
741 // speed: only do moves with this piece type
744 int i, j, d, s, fs, rs, rt, ft, m;
745 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
746 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
748 for (rf = 0; rf < BOARD_HEIGHT; rf++)
749 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
752 if(board[rf][ff] == EmptySquare) continue;
753 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
754 m = 0; piece = board[rf][ff];
755 if(PieceToChar(piece) == '~')
756 piece = (ChessSquare) ( DEMOTED(piece) );
757 if(filter != EmptySquare && piece != filter) continue;
758 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
759 MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], callback, closure);
762 if(IS_SHOGI(gameInfo.variant))
763 piece = (ChessSquare) ( SHOGI piece );
765 switch ((int)piece) {
766 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
768 /* can't happen ([HGM] except for faries...) */
772 if(gameInfo.variant == VariantXiangqi) {
773 /* [HGM] capture and move straight ahead in Xiangqi */
774 if (rf < BOARD_HEIGHT-1 &&
775 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
776 callback(board, flags, NormalMove,
777 rf, ff, rf + 1, ff, closure);
779 /* and move sideways when across the river */
780 for (s = -1; s <= 1; s += 2) {
781 if (rf >= BOARD_HEIGHT>>1 &&
782 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
783 !WhitePiece(board[rf][ff+s]) ) {
784 callback(board, flags, NormalMove,
785 rf, ff, rf, ff+s, closure);
790 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
791 callback(board, flags,
792 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
793 rf, ff, rf + 1, ff, closure);
795 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
796 gameInfo.variant != VariantShatranj && /* [HGM] */
797 gameInfo.variant != VariantCourier && /* [HGM] */
798 board[rf+2][ff] == EmptySquare ) {
799 callback(board, flags, NormalMove,
800 rf, ff, rf+2, ff, closure);
802 for (s = -1; s <= 1; s += 2) {
803 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
804 ((flags & F_KRIEGSPIEL_CAPTURE) ||
805 BlackPiece(board[rf + 1][ff + s]))) {
806 callback(board, flags,
807 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
808 rf, ff, rf + 1, ff + s, closure);
810 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
811 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
812 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
813 board[rf][ff + s] == BlackPawn &&
814 board[rf+1][ff + s] == EmptySquare) {
815 callback(board, flags, WhiteCapturesEnPassant,
816 rf, ff, rf+1, ff + s, closure);
823 if(gameInfo.variant == VariantXiangqi) {
824 /* [HGM] capture straight ahead in Xiangqi */
825 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
826 callback(board, flags, NormalMove,
827 rf, ff, rf - 1, ff, closure);
829 /* and move sideways when across the river */
830 for (s = -1; s <= 1; s += 2) {
831 if (rf < BOARD_HEIGHT>>1 &&
832 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
833 !BlackPiece(board[rf][ff+s]) ) {
834 callback(board, flags, NormalMove,
835 rf, ff, rf, ff+s, closure);
840 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
841 callback(board, flags,
842 rf <= promoRank ? BlackPromotion : NormalMove,
843 rf, ff, rf - 1, ff, closure);
845 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
846 gameInfo.variant != VariantShatranj && /* [HGM] */
847 gameInfo.variant != VariantCourier && /* [HGM] */
848 board[rf-2][ff] == EmptySquare) {
849 callback(board, flags, NormalMove,
850 rf, ff, rf-2, ff, closure);
852 for (s = -1; s <= 1; s += 2) {
853 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
854 ((flags & F_KRIEGSPIEL_CAPTURE) ||
855 WhitePiece(board[rf - 1][ff + s]))) {
856 callback(board, flags,
857 rf <= promoRank ? BlackPromotion : NormalMove,
858 rf, ff, rf - 1, ff + s, closure);
860 if (rf < BOARD_HEIGHT>>1) {
861 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
862 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
863 board[rf][ff + s] == WhitePawn &&
864 board[rf-1][ff + s] == EmptySquare) {
865 callback(board, flags, BlackCapturesEnPassant,
866 rf, ff, rf-1, ff + s, closure);
876 for (i = -1; i <= 1; i += 2)
877 for (j = -1; j <= 1; j += 2)
878 for (s = 1; s <= 2; s++) {
881 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
882 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
883 && !SameColor(board[rf][ff], board[rt][ft]))
884 callback(board, flags, NormalMove,
885 rf, ff, rt, ft, closure);
889 case SHOGI WhiteKnight:
890 for (s = -1; s <= 1; s += 2) {
891 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
892 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
893 callback(board, flags, NormalMove,
894 rf, ff, rf + 2, ff + s, closure);
899 case SHOGI BlackKnight:
900 for (s = -1; s <= 1; s += 2) {
901 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
902 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
903 callback(board, flags, NormalMove,
904 rf, ff, rf - 2, ff + s, closure);
911 for (d = 0; d <= 1; d++)
912 for (s = -1; s <= 1; s += 2) {
915 rt = rf + (i * s) * d;
916 ft = ff + (i * s) * (1 - d);
917 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
918 if (m == 0 && board[rt][ft] == EmptySquare)
919 callback(board, flags, NormalMove,
920 rf, ff, rt, ft, closure);
921 if (m == 1 && board[rt][ft] != EmptySquare &&
922 !SameColor(board[rf][ff], board[rt][ft]) )
923 callback(board, flags, NormalMove,
924 rf, ff, rt, ft, closure);
925 if (board[rt][ft] != EmptySquare && m++) break;
930 /* Gold General (and all its promoted versions) . First do the */
931 /* diagonal forward steps, then proceed as normal Wazir */
932 case SHOGI (PROMO WhitePawn):
933 if(gameInfo.variant == VariantShogi) goto WhiteGold;
934 case SHOGI (PROMO BlackPawn):
935 if(gameInfo.variant == VariantShogi) goto BlackGold;
936 SlideVertical(board, flags, rf, ff, callback, closure);
939 case SHOGI (PROMO WhiteKnight):
940 if(gameInfo.variant == VariantShogi) goto WhiteGold;
941 case SHOGI BlackDrunk:
942 case SHOGI BlackAlfil:
943 Ferz(board, flags, rf, ff, callback, closure);
944 StepSideways(board, flags, rf, ff, callback, closure);
945 StepBackward(board, flags, rf, ff, callback, closure);
948 case SHOGI (PROMO BlackKnight):
949 if(gameInfo.variant == VariantShogi) goto BlackGold;
950 case SHOGI WhiteDrunk:
951 case SHOGI WhiteAlfil:
952 Ferz(board, flags, rf, ff, callback, closure);
953 StepSideways(board, flags, rf, ff, callback, closure);
954 StepForward(board, flags, rf, ff, callback, closure);
960 if(gameInfo.variant == VariantShogi) goto BlackGold;
961 SlideVertical(board, flags, rf, ff, callback, closure);
962 Ferz(board, flags, rf, ff, callback, closure);
963 StepSideways(board, flags, rf, ff, callback, closure);
966 case SHOGI (PROMO WhiteQueen):
967 case SHOGI WhiteTokin:
968 case SHOGI WhiteWazir:
970 StepDiagForward(board, flags, rf, ff, callback, closure);
971 Wazir(board, flags, rf, ff, callback, closure);
974 case SHOGI (PROMO BlackQueen):
975 case SHOGI BlackTokin:
976 case SHOGI BlackWazir:
978 StepDiagBackward(board, flags, rf, ff, callback, closure);
979 Wazir(board, flags, rf, ff, callback, closure);
984 Wazir(board, flags, rf, ff, callback, closure);
987 case SHOGI WhiteMarshall:
988 case SHOGI BlackMarshall:
989 Ferz(board, flags, rf, ff, callback, closure);
990 for (d = 0; d <= 1; d++)
991 for (s = -2; s <= 2; s += 4) {
993 ft = ff + s * (1 - d);
994 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
995 if (!SameColor(board[rf][ff], board[rt][ft]) )
996 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1000 case SHOGI WhiteAngel:
1001 case SHOGI BlackAngel:
1002 Wazir(board, flags, rf, ff, callback, closure);
1006 /* [HGM] support Shatranj pieces */
1007 for (rs = -1; rs <= 1; rs += 2)
1008 for (fs = -1; fs <= 1; fs += 2) {
1011 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1012 && ( gameInfo.variant != VariantXiangqi ||
1013 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
1015 && !SameColor(board[rf][ff], board[rt][ft]))
1016 callback(board, flags, NormalMove,
1017 rf, ff, rt, ft, closure);
1018 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1019 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
1020 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
1022 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1023 && !SameColor(board[rf][ff], board[rt][ft]))
1024 callback(board, flags, NormalMove,
1025 rf, ff, rt, ft, closure);
1027 if(gameInfo.variant == VariantSpartan)
1028 for(fs = -1; fs <= 1; fs += 2) {
1030 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
1031 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
1035 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
1038 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
1039 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1040 for (s = -2; s <= 2; s += 4) {
1042 ft = ff + s * (1 - d);
1043 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1044 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1045 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1048 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
1049 case SHOGI WhiteCardinal:
1050 case SHOGI BlackCardinal:
1051 case SHOGI WhitePCardinal:
1052 case SHOGI BlackPCardinal:
1054 Bishop(board, flags, rf, ff, callback, closure);
1055 Wazir(board, flags, rf, ff, callback, closure);
1058 /* Capablanca Archbishop continues as Knight */
1061 Knight(board, flags, rf, ff, callback, closure);
1063 /* Shogi Bishops are ordinary Bishops */
1064 case SHOGI WhiteBishop:
1065 case SHOGI BlackBishop:
1066 case SHOGI WhitePBishop:
1067 case SHOGI BlackPBishop:
1070 Bishop(board, flags, rf, ff, callback, closure);
1073 /* Shogi Lance is unlike anything, and asymmetric at that */
1074 case SHOGI WhiteQueen:
1075 if(gameInfo.variant == VariantChu) goto doQueen;
1079 if (rt >= BOARD_HEIGHT) break;
1080 if (SameColor(board[rf][ff], board[rt][ft])) break;
1081 callback(board, flags, NormalMove,
1082 rf, ff, rt, ft, closure);
1083 if (board[rt][ft] != EmptySquare) break;
1087 case SHOGI BlackQueen:
1088 if(gameInfo.variant == VariantChu) goto doQueen;
1093 if (SameColor(board[rf][ff], board[rt][ft])) break;
1094 callback(board, flags, NormalMove,
1095 rf, ff, rt, ft, closure);
1096 if (board[rt][ft] != EmptySquare) break;
1100 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1103 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1104 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1105 for (s = -2; s <= 2; s += 4) {
1107 ft = ff + s * (1 - d);
1108 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1109 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1110 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1111 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1113 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1114 Wazir(board, flags, rf, ff, callback, closure);
1116 Rook(board, flags, rf, ff, callback, closure);
1119 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1120 case SHOGI WhiteDragon:
1121 case SHOGI BlackDragon:
1122 case SHOGI WhitePDragon:
1123 case SHOGI BlackPDragon:
1125 Rook(board, flags, rf, ff, callback, closure);
1126 Ferz(board, flags, rf, ff, callback, closure);
1130 /* Capablanca Chancellor sets flag to continue as Knight */
1133 Rook(board, flags, rf, ff, callback, closure);
1134 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1135 Ferz(board, flags, rf, ff, callback, closure);
1137 Knight(board, flags, rf, ff, callback, closure);
1140 /* Shogi Rooks are ordinary Rooks */
1141 case SHOGI WhiteRook:
1142 case SHOGI BlackRook:
1143 case SHOGI WhitePRook:
1144 case SHOGI BlackPRook:
1147 Rook(board, flags, rf, ff, callback, closure);
1152 case SHOGI WhiteMother:
1153 case SHOGI BlackMother:
1155 Rook(board, flags, rf, ff, callback, closure);
1156 Bishop(board, flags, rf, ff, callback, closure);
1159 case SHOGI WhitePawn:
1160 StepForward(board, flags, rf, ff, callback, closure);
1163 case SHOGI BlackPawn:
1164 StepBackward(board, flags, rf, ff, callback, closure);
1168 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1169 case SHOGI WhiteFerz:
1170 Ferz(board, flags, rf, ff, callback, closure);
1171 StepForward(board, flags, rf, ff, callback, closure);
1175 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1176 case SHOGI BlackFerz:
1177 StepBackward(board, flags, rf, ff, callback, closure);
1181 /* [HGM] support Shatranj pieces */
1182 Ferz(board, flags, rf, ff, callback, closure);
1187 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1190 case SHOGI WhiteMonarch:
1191 case SHOGI BlackMonarch:
1192 case SHOGI WhiteKing:
1193 case SHOGI BlackKing:
1196 Ferz(board, flags, rf, ff, callback, closure);
1197 Wazir(board, flags, rf, ff, callback, closure);
1200 case WhiteNightrider:
1201 case BlackNightrider:
1202 for (i = -1; i <= 1; i += 2)
1203 for (j = -1; j <= 1; j += 2)
1204 for (s = 1; s <= 2; s++) { int k;
1207 ft = ff + k*j*(3-s);
1208 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1209 if (SameColor(board[rf][ff], board[rt][ft])) break;
1210 callback(board, flags, NormalMove,
1211 rf, ff, rt, ft, closure);
1212 if (board[rt][ft] != EmptySquare) break;
1218 Bishop(board, flags, rf, ff, callback, closure);
1219 Rook(board, flags, rf, ff, callback, closure);
1220 Knight(board, flags, rf, ff, callback, closure);
1223 // Use Lance as Berolina / Spartan Pawn.
1225 if(gameInfo.variant == VariantSuper) goto Amazon;
1226 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1227 callback(board, flags,
1228 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1229 rf, ff, rf + 1, ff, closure);
1230 for (s = -1; s <= 1; s += 2) {
1231 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1232 callback(board, flags,
1233 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1234 rf, ff, rf + 1, ff + s, closure);
1235 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1236 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1241 if(gameInfo.variant == VariantSuper) goto Amazon;
1242 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1243 callback(board, flags,
1244 rf <= promoRank ? BlackPromotion : NormalMove,
1245 rf, ff, rf - 1, ff, closure);
1246 for (s = -1; s <= 1; s += 2) {
1247 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1248 callback(board, flags,
1249 rf <= promoRank ? BlackPromotion : NormalMove,
1250 rf, ff, rf - 1, ff + s, closure);
1251 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1252 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1256 case SHOGI WhiteNothing:
1257 case SHOGI BlackNothing:
1258 case SHOGI WhiteLion:
1259 case SHOGI BlackLion:
1262 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1263 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1264 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1265 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1266 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1267 rf, ff, rt, ft, closure);
1272 case SHOGI WhiteFalcon:
1273 case SHOGI BlackFalcon:
1274 case SHOGI WhitePDagger:
1275 case SHOGI BlackPDagger:
1276 SlideSideways(board, flags, rf, ff, callback, closure);
1277 StepVertical(board, flags, rf, ff, callback, closure);
1280 case SHOGI WhiteCobra:
1281 case SHOGI BlackCobra:
1282 StepVertical(board, flags, rf, ff, callback, closure);
1285 case SHOGI (PROMO WhiteFerz):
1286 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1287 case SHOGI (PROMO BlackFerz):
1288 if(gameInfo.variant == VariantShogi) goto BlackGold;
1289 case SHOGI WhitePSword:
1290 case SHOGI BlackPSword:
1291 SlideVertical(board, flags, rf, ff, callback, closure);
1292 StepSideways(board, flags, rf, ff, callback, closure);
1295 case SHOGI WhiteUnicorn:
1296 case SHOGI BlackUnicorn:
1297 Ferz(board, flags, rf, ff, callback, closure);
1298 StepVertical(board, flags, rf, ff, callback, closure);
1301 case SHOGI WhiteMan:
1302 StepDiagForward(board, flags, rf, ff, callback, closure);
1303 StepVertical(board, flags, rf, ff, callback, closure);
1306 case SHOGI BlackMan:
1307 StepDiagBackward(board, flags, rf, ff, callback, closure);
1308 StepVertical(board, flags, rf, ff, callback, closure);
1311 case SHOGI WhiteHCrown:
1312 case SHOGI BlackHCrown:
1313 Bishop(board, flags, rf, ff, callback, closure);
1314 SlideSideways(board, flags, rf, ff, callback, closure);
1317 case SHOGI WhiteCrown:
1318 case SHOGI BlackCrown:
1319 Bishop(board, flags, rf, ff, callback, closure);
1320 SlideVertical(board, flags, rf, ff, callback, closure);
1323 case SHOGI WhiteCat:
1324 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1325 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1326 if(killX >= 0) break;
1327 Bishop(board, flags, rf, ff, callback, closure);
1328 SlideSideways(board, flags, rf, ff, callback, closure);
1329 SlideBackward(board, flags, rf, ff, callback, closure);
1332 case SHOGI BlackCat:
1333 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1334 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1335 if(killX >= 0) break;
1336 Bishop(board, flags, rf, ff, callback, closure);
1337 SlideSideways(board, flags, rf, ff, callback, closure);
1338 SlideForward(board, flags, rf, ff, callback, closure);
1341 case SHOGI WhiteDagger:
1342 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1343 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1344 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1345 if(killX >= 0) break;
1346 Rook(board, flags, rf, ff, callback, closure);
1347 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1350 case SHOGI BlackDagger:
1351 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1352 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1353 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1354 if(killX >= 0) break;
1355 Rook(board, flags, rf, ff, callback, closure);
1356 SlideDiagForward(board, flags, rf, ff, callback, closure);
1359 case SHOGI WhiteDolphin:
1360 case SHOGI BlackHorse:
1361 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1362 SlideVertical(board, flags, rf, ff, callback, closure);
1365 case SHOGI BlackDolphin:
1366 case SHOGI WhiteHorse:
1367 SlideDiagForward(board, flags, rf, ff, callback, closure);
1368 SlideVertical(board, flags, rf, ff, callback, closure);
1371 case SHOGI WhiteLance:
1372 SlideForward(board, flags, rf, ff, callback, closure);
1375 case SHOGI BlackLance:
1376 SlideBackward(board, flags, rf, ff, callback, closure);
1379 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1383 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1396 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1397 Board xqCheckers, nullBoard;
1399 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1400 int rf, int ff, int rt, int ft,
1404 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1406 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1408 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1410 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1412 if (!(flags & F_IGNORE_CHECK) ) {
1413 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1416 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1417 kings += (board[r][f] == BlackKing);
1421 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1423 check = CheckTest(board, flags, rf, ff, rt, ft,
1424 kind == WhiteCapturesEnPassant ||
1425 kind == BlackCapturesEnPassant);
1426 if(promo) board[rf][ff] = BlackLance;
1429 if (flags & F_ATOMIC_CAPTURE) {
1430 if (board[rt][ft] != EmptySquare ||
1431 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1433 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1434 if (board[rf][ff] == king) return;
1435 for (r = rt-1; r <= rt+1; r++) {
1436 for (f = ft-1; f <= ft+1; f++) {
1437 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1438 board[r][f] == king) return;
1443 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1450 int captures; // [HGM] losers
1451 } LegalityTestClosure;
1454 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1455 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1456 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1457 moves that would destroy your own king. The CASTLE_OK flags are
1458 true if castling is not yet ruled out by a move of the king or
1459 rook. Return TRUE if the player on move is currently in check and
1460 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1462 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1465 int ff, ft, k, left, right, swap;
1466 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1467 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1468 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1473 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1474 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1475 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1477 if (inCheck) return TRUE;
1479 /* Generate castling moves */
1480 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1481 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1484 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1485 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1487 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1488 if ((flags & F_WHITE_ON_MOVE) &&
1489 (flags & F_WHITE_KCASTLE_OK) &&
1490 board[0][ff] == wKing &&
1491 board[0][ff + 1] == EmptySquare &&
1492 board[0][ff + 2] == EmptySquare &&
1493 board[0][BOARD_RGHT-3] == EmptySquare &&
1494 board[0][BOARD_RGHT-2] == EmptySquare &&
1495 board[0][BOARD_RGHT-1] == WhiteRook &&
1496 castlingRights[0] != NoRights && /* [HGM] check rights */
1497 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1499 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1500 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1501 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1502 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1504 callback(board, flags,
1505 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1506 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1508 if ((flags & F_WHITE_ON_MOVE) &&
1509 (flags & F_WHITE_QCASTLE_OK) &&
1510 board[0][ff] == wKing &&
1511 board[0][ff - 1] == EmptySquare &&
1512 board[0][ff - 2] == EmptySquare &&
1513 board[0][BOARD_LEFT+2] == EmptySquare &&
1514 board[0][BOARD_LEFT+1] == EmptySquare &&
1515 board[0][BOARD_LEFT+0] == WhiteRook &&
1516 castlingRights[1] != NoRights && /* [HGM] check rights */
1517 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1519 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1520 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1521 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1523 callback(board, flags,
1524 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1525 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1527 if (!(flags & F_WHITE_ON_MOVE) &&
1528 (flags & F_BLACK_KCASTLE_OK) &&
1529 board[BOARD_HEIGHT-1][ff] == bKing &&
1530 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1531 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1532 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1533 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1534 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1535 castlingRights[3] != NoRights && /* [HGM] check rights */
1536 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1538 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1539 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1540 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1541 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1543 callback(board, flags,
1544 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1545 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1547 if (!(flags & F_WHITE_ON_MOVE) &&
1548 (flags & F_BLACK_QCASTLE_OK) &&
1549 board[BOARD_HEIGHT-1][ff] == bKing &&
1550 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1551 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1552 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1553 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1554 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1555 castlingRights[4] != NoRights && /* [HGM] check rights */
1556 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1558 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1559 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1560 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1562 callback(board, flags,
1563 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1564 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1568 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1570 /* generate all potential FRC castling moves (KxR), ignoring flags */
1571 /* [HGM] test if the Rooks we find have castling rights */
1572 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1575 if ((flags & F_WHITE_ON_MOVE) != 0) {
1576 ff = castlingRights[2]; /* King file if we have any rights */
1577 if(ff != NoRights && board[0][ff] == WhiteKing) {
1578 if (appData.debugMode) {
1579 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1580 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1582 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1584 right = BOARD_RGHT-2;
1585 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1586 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1587 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1588 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1589 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1590 if(ft != NoRights && board[0][ft] == WhiteRook) {
1591 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1592 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1595 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1596 left = BOARD_LEFT+2;
1598 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1599 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1600 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1601 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1602 if(ff > BOARD_LEFT+2)
1603 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1604 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1605 if(ft != NoRights && board[0][ft] == WhiteRook) {
1606 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1607 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1611 ff = castlingRights[5]; /* King file if we have any rights */
1612 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1613 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1615 right = BOARD_RGHT-2;
1616 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1617 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1618 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1619 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1620 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1621 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1622 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1623 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1626 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1627 left = BOARD_LEFT+2;
1629 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1630 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1631 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1632 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1633 if(ff > BOARD_LEFT+2)
1634 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1635 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1636 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1637 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1638 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1655 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1656 int rf, int ff, int rt, int ft,
1661 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1663 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1665 if (rt == cl->rking && ft == cl->fking) {
1666 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1668 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1670 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1671 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1672 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1676 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1677 he leave himself in check? Or if rf == -1, is the player on move
1678 in check now? enPassant must be TRUE if the indicated move is an
1679 e.p. capture. The possibility of castling out of a check along the
1680 back rank is not accounted for (i.e., we still return nonzero), as
1681 this is illegal anyway. Return value is the number of times the
1682 king is in check. */
1684 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1686 CheckTestClosure cl;
1687 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1688 ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1689 int saveKill = killX;
1690 /* Suppress warnings on uninitialized variables */
1692 if(gameInfo.variant == VariantXiangqi)
1693 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1694 if(gameInfo.variant == VariantKnightmate)
1695 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1696 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1697 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1698 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1699 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1700 if(board[r][f] == k || board[r][f] == prince) {
1701 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1702 king = board[r][f]; // remember hich one we had
1709 captured = board[rf][ft];
1710 board[rf][ft] = EmptySquare;
1712 captured = board[rt][ft];
1714 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1715 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1718 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1719 board[rt][ft] = board[rf][ff];
1720 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1722 ep = board[EP_STATUS];
1723 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1724 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1725 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1726 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1727 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1728 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1729 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1733 /* For compatibility with ICS wild 9, we scan the board in the
1734 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1735 and we test only whether that one is in check. */
1736 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1737 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1738 if (board[cl.rking][cl.fking] == king) {
1740 if(gameInfo.variant == VariantXiangqi) {
1741 /* [HGM] In Xiangqi opposing Kings means check as well */
1743 dir = (king >= BlackPawn) ? -1 : 1;
1744 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1745 board[i][cl.fking] == EmptySquare; i+=dir );
1746 if(i>=0 && i<BOARD_HEIGHT &&
1747 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1750 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1751 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1752 goto undo_move; /* 2-level break */
1759 if(rf != DROP_RANK) // [HGM] drop
1760 board[rf][ff] = board[rt][ft];
1762 board[rf][ft] = captured;
1763 board[rt][ft] = EmptySquare;
1766 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1767 board[killY][killX = saveKill & 0xFFF] = trampled;
1769 board[rt][ft] = captured;
1771 board[EP_STATUS] = ep;
1774 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1778 HasLion (Board board, int flags)
1780 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1782 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1783 if(board[r][f] == lion) return 1;
1788 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1789 { // [HGM] put drop legality testing in separate routine for clarity
1791 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1792 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1793 n = PieceToNumber(piece);
1794 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1795 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1796 return ImpossibleMove; // piece not available
1797 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1798 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1799 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1800 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1801 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1802 if(piece == WhitePawn || piece == BlackPawn) {
1803 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1804 for(r=1; r<BOARD_HEIGHT-1; r++)
1805 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1806 // should still test if we mate with this Pawn
1808 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1809 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1811 if( (piece == WhitePawn || piece == BlackPawn) &&
1812 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1813 return IllegalMove; /* no pawn drops on 1st/8th */
1815 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1816 if (!(flags & F_IGNORE_CHECK) &&
1817 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1818 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1821 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1822 int rf, int ff, int rt, int ft,
1826 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1828 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1830 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1831 cl->captures++; // [HGM] losers: count legal captures
1832 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1837 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1839 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1841 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1842 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1843 piece = filterPiece = board[rf][ff];
1844 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1846 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1847 /* (perhaps we should disallow moves that obviously leave us in check?) */
1848 if((piece == WhiteFalcon || piece == BlackFalcon ||
1849 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1850 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1854 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1855 cl.ft = fFilter = ft;
1856 cl.kind = IllegalMove;
1857 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1858 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1859 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1860 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1861 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1862 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1864 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1865 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1866 if(board[rf][ff] < BlackPawn) { // white
1867 if(rf != 0) return IllegalMove; // must be on back rank
1868 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1869 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1870 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1871 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1873 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1874 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1875 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1876 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1877 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1880 if(gameInfo.variant == VariantChu) {
1881 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1882 if(promoChar != '+')
1883 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1884 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1885 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1886 return ImpossibleMove;
1888 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1890 if(gameInfo.variant == VariantShogi) {
1891 /* [HGM] Shogi promotions. '=' means defer */
1892 if(rf != DROP_RANK && cl.kind == NormalMove) {
1893 ChessSquare piece = board[rf][ff];
1894 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1896 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1897 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1898 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1899 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1900 promoChar = '+'; // allowed ICS notations
1901 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1902 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1903 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1904 else if(flags & F_WHITE_ON_MOVE) {
1905 if( (int) piece < (int) WhiteWazir &&
1906 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1907 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1908 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1909 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1910 else /* promotion optional, default is defer */
1911 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1912 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1914 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1915 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1916 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1917 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1918 else /* promotion optional, default is defer */
1919 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1920 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1924 if (promoChar != NULLCHAR) {
1925 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1926 ChessSquare piece = board[rf][ff];
1927 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1928 // should test if in zone, really
1929 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1931 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1933 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1934 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1935 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1936 if(piece == EmptySquare)
1937 cl.kind = ImpossibleMove; // non-existing piece
1938 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1939 cl.kind = IllegalMove; // no two Lions
1940 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1941 if(promoChar != PieceToChar(BlackKing)) {
1942 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1943 if(piece == BlackLance) cl.kind = ImpossibleMove;
1944 } else { // promotion to King allowed only if we do not have two yet
1945 int r, f, kings = 0;
1946 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1947 if(kings == 2) cl.kind = IllegalMove;
1949 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1950 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1951 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1952 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1953 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1954 cl.kind = IllegalMove; // promotion to King usually not allowed
1956 cl.kind = IllegalMove;
1966 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1967 int rf, int ff, int rt, int ft,
1971 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1973 register MateTestClosure *cl = (MateTestClosure *) closure;
1978 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1980 MateTest (Board board, int flags)
1983 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1984 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1986 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1987 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1988 nrKing += (board[r][f] == king); // stm has king
1989 if( board[r][f] != EmptySquare ) {
1990 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1995 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1996 case VariantShatranj:
1997 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2001 if(nrKing == 0) return MT_NOKING;
2004 if(myPieces == 1) return MT_BARE;
2007 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2008 // [HGM] 3check: yet to do!
2010 return inCheck ? MT_CHECK : MT_NONE;
2012 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2013 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2014 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2015 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2016 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2017 if(board[n][holdings] != EmptySquare) {
2018 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2019 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2022 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2023 return myPieces == hisPieces ? MT_STALEMATE :
2024 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2025 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2026 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2028 return inCheck ? MT_CHECKMATE
2029 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2030 MT_STAINMATE : MT_STALEMATE;
2035 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2036 int rf, int ff, int rt, int ft,
2040 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2042 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2043 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2044 extern int kifu; // in parser.c
2046 // [HGM] wild: for wild-card pieces rt and rf are dummies
2047 if(piece == WhiteFalcon || piece == BlackFalcon ||
2048 piece == WhiteCobra || piece == BlackCobra)
2051 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2052 || PieceToChar(board[rf][ff]) == '~'
2053 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2055 (cl->rfIn == -1 || cl->rfIn == rf) &&
2056 (cl->ffIn == -1 || cl->ffIn == ff) &&
2057 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2058 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2060 if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move
2062 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2063 int this = 1, other = 1;
2064 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2065 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2066 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2067 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2068 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2069 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2070 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2071 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2075 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2076 // [HGM] oneclick: if multiple moves, be sure we remember capture
2077 cl->piece = board[rf][ff];
2080 cl->rt = wildCard ? cl->rtIn : rt;
2081 cl->ft = wildCard ? cl->ftIn : ft;
2084 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2089 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2091 int illegal = 0; char c = closure->promoCharIn;
2093 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2094 closure->count = closure->captures = 0;
2095 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2096 closure->kind = ImpossibleMove;
2097 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2098 fFilter = closure->ftIn;
2099 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2100 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2101 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2102 closure->count = closure->captures = 0;
2103 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2104 closure->kind = ImpossibleMove;
2105 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2108 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2109 if (closure->count == 0) {
2110 /* See if it's an illegal move due to check */
2112 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2113 if (closure->count == 0) {
2114 /* No, it's not even that */
2115 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2116 int f, r; // if there is only a single piece of the requested type on the board, use that
2117 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2118 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2119 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2120 if(closure->count > 1) illegal = 0; // ambiguous
2122 if(closure->count == 0) {
2123 if (appData.debugMode) { int i, j;
2124 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2125 for(j=0; j<BOARD_WIDTH; j++)
2126 fprintf(debugFP, "%3d", (int) board[i][j]);
2127 fprintf(debugFP, "\n");
2133 } else if(pieceDefs && closure->count > 1) { // [HGM] gen: move is ambiguous under engine-defined rules
2134 DisambiguateClosure spare = *closure;
2135 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2136 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2137 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2141 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2142 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2143 if(closure->piece < BlackPawn) { // white
2144 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2145 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2146 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2147 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2148 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2150 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2151 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2152 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2153 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2154 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2157 if(gameInfo.variant == VariantChu) {
2158 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2160 if(gameInfo.variant == VariantShogi) {
2161 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2162 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2163 ChessSquare piece = closure->piece;
2164 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2165 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2166 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2167 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2168 c = '+'; // allowed ICS notations
2169 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2170 else if(flags & F_WHITE_ON_MOVE) {
2171 if( (int) piece < (int) WhiteWazir &&
2172 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2173 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2174 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2175 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2176 else /* promotion optional, default is defer */
2177 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2178 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2180 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2181 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2182 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2183 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2184 else /* promotion optional, default is defer */
2185 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2186 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2189 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2190 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2192 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2193 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2194 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2195 gameInfo.variant == VariantMakruk)
2196 c = PieceToChar(BlackFerz);
2197 else if(gameInfo.variant == VariantASEAN)
2198 c = PieceToChar(BlackRook);
2199 else if(gameInfo.variant == VariantGreat)
2200 c = PieceToChar(BlackMan);
2201 else if(gameInfo.variant == VariantGrand)
2202 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2204 c = PieceToChar(BlackQueen);
2205 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2206 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2207 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2208 ChessSquare p = closure->piece;
2209 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2210 closure->kind = ImpossibleMove; // used on non-promotable piece
2211 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2212 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2214 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2215 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2216 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2217 if (closure->count > 1) {
2218 closure->kind = AmbiguousMove;
2221 /* Note: If more than one illegal move matches, but no legal
2222 moves, we return IllegalMove, not AmbiguousMove. Caller
2223 can look at closure->count to detect this.
2225 closure->kind = IllegalMove;
2239 } CoordsToAlgebraicClosure;
2241 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2242 ChessMove kind, int rf, int ff,
2243 int rt, int ft, VOIDSTAR closure));
2246 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2248 register CoordsToAlgebraicClosure *cl =
2249 (CoordsToAlgebraicClosure *) closure;
2251 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2252 (board[rf][ff] == cl->piece
2253 || PieceToChar(board[rf][ff]) == '~' &&
2254 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2258 cl->kind = kind; /* this is the move we want */
2260 cl->file++; /* need file to rule out this move */
2264 cl->rank++; /* need rank to rule out this move */
2266 cl->either++; /* rank or file will rule out this move */
2272 /* Convert coordinates to normal algebraic notation.
2273 promoChar must be NULLCHAR or 'x' if not a promotion.
2276 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2280 char *outp = out, c, capture;
2281 CoordsToAlgebraicClosure cl;
2283 if (rf == DROP_RANK) {
2284 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2285 /* Bughouse piece drop */
2286 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2291 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2293 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2296 if (promoChar == 'x') promoChar = NULLCHAR;
2297 piece = board[rf][ff];
2298 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2303 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2304 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2305 /* Keep short notation if move is illegal only because it
2306 leaves the player in check, but still return IllegalMove */
2307 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2308 if (kind == IllegalMove) break;
2313 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2314 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2315 /* Non-capture; use style "e5" */
2318 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2320 /* Capture; use style "exd5" */
2322 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2326 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2328 /* Use promotion suffix style "=Q" */
2330 if (promoChar != NULLCHAR) {
2331 if(IS_SHOGI(gameInfo.variant)) {
2332 /* [HGM] ... but not in Shogi! */
2333 *outp++ = promoChar == '=' ? '=' : '+';
2336 *outp++ = ToUpper(promoChar);
2345 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2346 /* Code added by Tord: FRC castling. */
2347 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2348 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2350 safeStrCpy(out, "O-O", MOVE_LEN);
2352 safeStrCpy(out, "O-O-O", MOVE_LEN);
2353 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2355 /* End of code added by Tord */
2356 /* Test for castling or ICS wild castling */
2357 /* Use style "O-O" (oh-oh) for PGN compatibility */
2358 else if (rf == rt &&
2359 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2360 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2361 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2362 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2363 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2364 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2366 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2368 /* This notation is always unambiguous, unless there are
2369 kings on both the d and e files, with "wild castling"
2370 possible for the king on the d file and normal castling
2371 possible for the other. ICS rules for wild 9
2372 effectively make castling illegal for either king in
2373 this situation. So I am not going to worry about it;
2374 I'll just generate an ambiguous O-O in this case.
2376 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2379 /* else fall through */
2384 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2385 cl.ft = fFilter = ft;
2387 cl.kind = IllegalMove;
2388 cl.rank = cl.file = cl.either = 0;
2389 c = PieceToChar(piece) ;
2390 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2392 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2393 /* Generate pretty moves for moving into check, but
2394 still return IllegalMove.
2396 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2397 if (cl.kind == IllegalMove) break;
2398 cl.kind = IllegalMove;
2401 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2402 else "Ngf3" or "Ngxf7",
2403 else "N1f3" or "N5xf7",
2404 else "Ng1f3" or "Ng5xf7".
2406 if( c == '~' || c == '+') {
2407 /* [HGM] print nonexistent piece as its demoted version */
2408 piece = (ChessSquare) (CHUDEMOTED(piece));
2410 if(c=='+') *outp++ = c;
2411 *outp++ = ToUpper(PieceToChar(piece));
2412 if(*outp = PieceSuffix(piece)) outp++;
2414 if (cl.file || (cl.either && !cl.rank)) {
2420 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2423 if(board[rt][ft] != EmptySquare)
2429 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2430 if (IS_SHOGI(gameInfo.variant)) {
2431 /* [HGM] in Shogi non-pawns can promote */
2432 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2434 else if (gameInfo.variant == VariantChuChess && promoChar ||
2435 gameInfo.variant != VariantSuper && promoChar &&
2436 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2438 *outp++ = ToUpper(promoChar);
2440 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2442 *outp++ = ToUpper(promoChar);
2448 /* Moving a nonexistent piece */
2452 /* Not a legal move, even ignoring check.
2453 If there was a piece on the from square,
2454 use style "Ng1g3" or "Ng1xe8";
2455 if there was a pawn or nothing (!),
2456 use style "g1g3" or "g1xe8". Use "x"
2457 if a piece was on the to square, even
2458 a piece of the same color.
2462 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2464 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2465 c += (board[r][f] == piece); // count on-board pieces of given type
2466 *outp = PieceToChar(piece);
2467 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2468 *outp++ = ToUpper(PieceToChar(piece));
2469 if(*outp = PieceSuffix(piece)) outp++;
2471 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2475 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2477 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2481 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2482 /* Use promotion suffix style "=Q" */
2483 if (promoChar != NULLCHAR && promoChar != 'x') {
2485 *outp++ = ToUpper(promoChar);
2492 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2501 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2503 int preyStackPointer, chaseStackPointer;
2506 unsigned char rf, ff, rt, ft;
2510 unsigned char rank, file;
2516 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2518 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2519 int rf, int ff, int rt, int ft,
2523 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2524 { // For adding captures that can lead to chase indictment to the chaseStack
2525 if(board[rt][ft] == EmptySquare) return; // non-capture
2526 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2527 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2528 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2529 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2530 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2531 chaseStack[chaseStackPointer].rf = rf;
2532 chaseStack[chaseStackPointer].ff = ff;
2533 chaseStack[chaseStackPointer].rt = rt;
2534 chaseStack[chaseStackPointer].ft = ft;
2535 chaseStackPointer++;
2538 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2539 int rf, int ff, int rt, int ft,
2543 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2544 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2546 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2548 if(board[rt][ft] == EmptySquare) return; // no capture
2549 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2550 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2552 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2553 for(i=0; i<chaseStackPointer; i++) {
2554 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2555 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2556 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2557 chaseStack[i] = chaseStack[--chaseStackPointer];
2563 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2564 int rf, int ff, int rt, int ft,
2568 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2569 { // for determining if a piece (given through the closure) is protected
2570 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2572 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2573 if(appData.debugMode && board[rt][ft] != EmptySquare)
2574 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2577 extern char moveList[MAX_MOVES][MOVE_LEN];
2580 PerpetualChase (int first, int last)
2581 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2584 ChessSquare captured;
2586 preyStackPointer = 0; // clear stack of chased pieces
2587 for(i=first; i<last; i+=2) { // for all positions with same side to move
2588 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2589 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2590 // determine all captures possible after the move, and put them on chaseStack
2591 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2592 if(appData.debugMode) { int n;
2593 for(n=0; n<chaseStackPointer; n++)
2594 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2595 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2596 fprintf(debugFP, ": all capts\n");
2598 // determine all captures possible before the move, and delete them from chaseStack
2599 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2600 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2601 cl.rt = moveList[i][3]-ONE;
2602 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2603 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2604 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2605 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2606 if(appData.debugMode) { int n;
2607 for(n=0; n<chaseStackPointer; n++)
2608 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2609 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2610 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2612 // chaseSack now contains all captures made possible by the move
2613 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2614 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2615 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2617 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2618 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2620 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2621 continue; // C or H attack on R is always chase; leave on chaseStack
2623 if(attacker == victim) {
2624 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2625 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2626 // we can capture back with equal piece, so this is no chase but a sacrifice
2627 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2628 j--; /* ! */ continue;
2633 // the attack is on a lower piece, or on a pinned or blocked equal one
2634 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2635 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2636 // test if the victim is protected by a true protector. First make the capture.
2637 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2638 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2639 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2640 // Then test if the opponent can recapture
2641 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2642 cl.rt = chaseStack[j].rt;
2643 cl.ft = chaseStack[j].ft;
2644 if(appData.debugMode) {
2645 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2647 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2648 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2649 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2650 // unmake the capture
2651 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2652 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2653 // if a recapture was found, piece is protected, and we are not chasing it.
2654 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2655 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2659 // chaseStack now contains all moves that chased
2660 if(appData.debugMode) { int n;
2661 for(n=0; n<chaseStackPointer; n++)
2662 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2663 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2664 fprintf(debugFP, ": chases\n");
2666 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2667 for(j=0; j<chaseStackPointer; j++) {
2668 preyStack[j].rank = chaseStack[j].rt;
2669 preyStack[j].file = chaseStack[j].ft;
2671 preyStackPointer = chaseStackPointer;
2674 for(j=0; j<chaseStackPointer; j++) {
2675 for(k=0; k<preyStackPointer; k++) {
2676 // search the victim of each chase move on the preyStack (first occurrence)
2677 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2678 if(k < tail) break; // piece was already identified as still being chased
2679 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2680 preyStack[tail] = preyStack[k]; // by swapping
2681 preyStack[k] = preyStack[preyStackPointer];
2687 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2688 if(appData.debugMode) { int n;
2689 for(n=0; n<preyStackPointer; n++)
2690 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2691 fprintf(debugFP, "always chased upto ply %d\n", i);
2693 // now adjust the location of the chased pieces according to opponent move
2694 for(j=0; j<preyStackPointer; j++) {
2695 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2696 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2697 preyStack[j].rank = moveList[i+1][3]-ONE;
2698 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2703 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2704 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the