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);
269 // [HGM] gen: configurable move generation from Betza notation sent by engine.
270 // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
271 // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
272 // if the move has 1 or 2 legs. Only the marking of squares makes use of this info, by only marking
273 // target squares of leg 1 (rejecting null move). A dummy move with MoveType 'FirstLeg' to the relay square
274 // is generated, so a cyan marker can be put there, and other functions can ignore such a move. When the
275 // user selects this square, it becomes the kill-square. Once a kill-square is set, only 2-leg moves are
276 // generated that use that square as relay, plus 1-leg moves, so the 1-leg move that goes to the kill-square
277 // can be marked during 2nd-leg entry to terminate the move there. For judging the pseudo-legality of the
278 // 2nd leg, the from-square has to be considered empty, although the moving piece is still on it.
282 // alphabet "abcdefghijklmnopqrstuvwxyz"
283 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
284 char xStep[] = "2110.130.102.10.00....0..2";
285 char yStep[] = "2132.133.313.20.11....1..3";
286 char dirType[] = "01000104000200000260050000";
287 char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z";
288 char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z";
290 // 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 "
291 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 };
292 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 };
293 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 };
294 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 };
296 int rot[][4] = { // rotation matrices for each direction
308 OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR cl)
314 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, int range, char *desc, MoveCallback cb, VOIDSTAR cl)
316 char buf[80], *p = desc, *atom = NULL;
317 int mine, his, dir, bit, occup, i, ep, promoRank = -1;
318 ChessMove promo= NormalMove; ChessSquare pc = board[r][f];
319 if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board
320 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
321 if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
322 if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
323 while(*p) { // more moves to go
324 int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
326 while(*p == 'i') initial++, desc = ++p;
327 while(islower(*p)) p++; // skip prefixes
328 if(!isupper(*p)) return; // syntax error: no atom
329 dx = xStep[*p-'A'] - '0';// step vector of atom
330 dy = yStep[*p-'A'] - '0';
331 dirSet = 0; // build direction set based on atom symmetry
332 switch(symmetry[*p-'A']) {
333 case 'B': expo = 0; // bishop, slide
334 case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold)
335 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
336 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
337 int b = dirs1[*desc-'a']; // use wide version
338 if( islower(desc[1]) &&
339 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
340 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
345 dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
347 case 'R': expo = 0; // rook, slide
348 case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold)
349 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
350 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
351 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
352 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
354 case 'N': all = 0xFF; // oblique atom (degenerate 8-fold)
355 if(tx >= 0) goto king; // continuation legs specified in K/Q system!
356 if(*desc == 'h') { // chiral direction sets 'hr' and 'hl'
357 dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2;
359 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
360 int b = dirs2[*desc-'a']; // when alone, use narrow version
361 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
362 else if(*desc == desc[1] || islower(desc[1]) && i < '4'
363 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
364 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
369 if(!dirSet) dirSet = 0xFF;
371 case 'Q': expo = 0; // queen, slide
372 case 'K': all = 0xFF; // non-deg (pseudo) 8-fold
374 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
375 int b = dirs4[*desc-'a']; // when alone, use narrow version
376 if(desc[1] == *desc) desc++; // doubling forces alone
377 else if(islower(desc[1]) && i < '4'
378 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
379 b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide
384 if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg
385 : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags
386 dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system
387 ds2 = dirSet & 0xAA; // extract diagonal directions
388 if(dirSet &= 0x55) // start with orthogonal moves, if present
389 retry = 1, dx = 0; // and schedule the diagonal moves for later
390 else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately
391 break; // should not have direction indicators
392 default: return; // syntax error: invalid atom
394 if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
395 mode = 0; // build mode mask
396 if(*desc == 'm') mode |= 4, desc++; // move to empty
397 if(*desc == 'c') mode |= his, desc++; // capture foe
398 if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend)
399 if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover
400 if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test')
401 if(*desc == 'p') mode |= 32, desc++; // hop over occupied
402 if(*desc == 'g') mode |= 64, desc++; // hop and toggle range
403 if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board
404 if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square
405 if(*desc == 'n') jump = 0, desc++; // non-jumping
406 while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square)
407 if(*desc == 'a') cont = ++desc; // move again after doing what preceded it
408 if(isdigit(*++p)) expo = atoi(p++); // read exponent
409 if(expo > 9) p++; // allow double-digit
410 desc = p; // this is start of next move
411 if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; initial = 0; } else
412 if(initial && !range) {
413 if( (board[r][f] != initialPosition[r][f] ||
414 r == 0 && board[TOUCHED_W] & 1<<f ||
415 r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f )) continue;
418 if(expo > 0 && dx == 0 && dy == 0) { // castling indicated by O + number
419 mode |= 1024; dy = 1;
421 if(expo < 0) expo = 1; // use 1 for default
423 if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc
425 strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify
426 atom = buf; while(islower(*atom)) atom++; // skip to atom
427 if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through'
428 if(mode & 64 + 512) {
429 mode |= 256; // and 'g' too, but converts leaper <-> slider
430 if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g'
431 *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK
432 atom[1] = atom[2] = '\0'; // make sure any old range is stripped off
433 if(expo == 1) atom[1] = '0'; // turn other leapers into riders
435 if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m
437 if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square
439 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
440 int i = expo, j = skip, hop = mode, vx, vy, loop = 0;
441 if(!(bit & dirSet)) continue; // does not move in this direction
442 if(dy != 1 || mode & 1024) j = 0; //
443 vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector
444 vy = dx*rot[dir][2] + dy*rot[dir][3];
445 if(tx < 0) x = f, y = r; // start square
446 else x = tx, y = ty; // from previous to-square if continuation
448 x += vx; y += vy; // step to next square
449 if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
450 if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
451 if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
452 if(j) { j--; continue; } // skip irrespective of occupation
453 if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board
454 if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
455 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
456 if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!)
457 if(board[y][x] < BlackPawn) occup = 0x101; else
458 if(board[y][x] < EmptySquare) occup = 0x102; else
460 if(initial && expo - i + 1 != range) { if(occup == 4) continue; else break; }
461 if(cont) { // non-final leg
462 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
463 if(occup & mode) { // valid intermediate square, do continuation
464 char origAtom = *atom;
465 int rg = (expo != 1 ? expo - i + 1 : range); // pass length of last *slider* leg
466 if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid
467 if(occup & mode & 0x104) // no side effects, merge legs to one move
468 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
469 if(occup & mode & 3 && (killX < 0 || kill2X < 0 && (legNr > 1 || killX == x && killY == y) ||
470 (legNr == 1 ? kill2X == x && kill2Y == y : killX == x && killY == y))) { // destructive first leg
473 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, &OK, &cnt); // count possible continuations
475 if(cnt) { // and if there are
476 if(legNr & 1 ? killX < 0 : kill2X < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); // then generate their first leg
478 MovesFromString(board, flags, f, r, x, y, dir, rg, cont, cb, cl);
482 *atom = origAtom; // undo any interconversion
484 if(occup != 4) break; // occupied squares always terminate the leg
487 if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper
489 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)
490 cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
492 if(mode & 1024) { // castling
493 i = 2; // kludge to elongate move indefinitely
494 if(occup == 4) continue; // skip empty squares
495 if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
496 && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
497 if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook')
498 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f)
499 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
501 cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
503 if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
504 && board[y][x] == initialPosition[y][x]) {
505 if(pc != WhiteKing && pc != BlackKing || expo == 1) {
506 if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f)
507 legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
509 cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
513 if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal
514 if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate
515 if(occup != 4) break; // not valid transit square
518 dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q
519 } while(retry-- && ds2); // and start doing them
520 if(tx >= 0) break; // don't do other atoms in continuation legs
524 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
527 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
532 if (rt >= BOARD_HEIGHT) break;
533 if (SameColor(board[rf][ff], board[rt][ft])) break;
534 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
535 if (board[rt][ft] != EmptySquare) break;
540 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
546 if (SameColor(board[rf][ff], board[rt][ft])) break;
547 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
548 if (board[rt][ft] != EmptySquare) break;
553 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
555 SlideForward(board, flags, rf, ff, callback, closure);
556 SlideBackward(board, flags, rf, ff, callback, closure);
560 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
562 int i, s, rt = rf, ft;
563 for(s = -1; s <= 1; s+= 2) {
566 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
567 if (SameColor(board[rf][ff], board[rt][ft])) break;
568 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
569 if (board[rt][ft] != EmptySquare) break;
575 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
578 for(s = -1; s <= 1; s+= 2) {
582 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
583 if (SameColor(board[rf][ff], board[rt][ft])) break;
584 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
585 if (board[rt][ft] != EmptySquare) break;
591 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
594 for(s = -1; s <= 1; s+= 2) {
598 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
599 if (SameColor(board[rf][ff], board[rt][ft])) break;
600 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
601 if (board[rt][ft] != EmptySquare) break;
607 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
609 SlideVertical(board, flags, rf, ff, callback, closure);
610 SlideSideways(board, flags, rf, ff, callback, closure);
614 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
616 SlideDiagForward(board, flags, rf, ff, callback, closure);
617 SlideDiagBackward(board, flags, rf, ff, callback, closure);
621 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
622 { // Lion-like move of Horned Falcon and Soaring Eagle
623 int ft = ff + dx, rt = rf + dy;
624 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
626 if (!SameColor(board[rf][ff], board[rt][ft]))
627 callback(board, flags, killX < 0 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
630 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
632 if (!SameColor(board[rf][ff], board[rt][ft]))
633 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
634 if (!SameColor(board[rf][ff], board[rf+dy][ff+dx]))
635 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
640 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
642 int ft = ff, rt = rf + 1;
643 if (rt >= BOARD_HEIGHT) return;
644 if (SameColor(board[rf][ff], board[rt][ft])) return;
645 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
649 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
651 int ft = ff, rt = rf - 1;
653 if (SameColor(board[rf][ff], board[rt][ft])) return;
654 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
658 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
662 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
663 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
665 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
666 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
670 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
673 if (rt >= BOARD_HEIGHT) return;
675 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
676 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
678 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
679 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
683 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
688 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
689 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
691 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
692 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
696 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
698 StepForward(board, flags, rf, ff, callback, closure);
699 StepBackward(board, flags, rf, ff, callback, closure);
703 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
705 StepDiagForward(board, flags, rf, ff, callback, closure);
706 StepDiagBackward(board, flags, rf, ff, callback, closure);
710 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
712 StepVertical(board, flags, rf, ff, callback, closure);
713 StepSideways(board, flags, rf, ff, callback, closure);
717 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
720 for (i = -1; i <= 1; i += 2)
721 for (j = -1; j <= 1; j += 2)
722 for (s = 1; s <= 2; s++) {
725 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
726 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
727 && !SameColor(board[rf][ff], board[rt][ft]))
728 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
732 /* Call callback once for each pseudo-legal move in the given
733 position, except castling moves. A move is pseudo-legal if it is
734 legal, or if it would be legal except that it leaves the king in
735 check. In the arguments, epfile is EP_NONE if the previous move
736 was not a double pawn push, or the file 0..7 if it was, or
737 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
738 Promotion moves generated are to Queen only.
741 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
742 // speed: only do moves with this piece type
745 int i, j, d, s, fs, rs, rt, ft, m;
746 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
747 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
749 for (rf = 0; rf < BOARD_HEIGHT; rf++)
750 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
753 if(board[rf][ff] == EmptySquare) continue;
754 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
755 m = 0; piece = board[rf][ff];
756 if(PieceToChar(piece) == '~')
757 piece = (ChessSquare) ( DEMOTED(piece) );
758 if(filter != EmptySquare && piece != filter) continue;
759 if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
760 MovesFromString(board, flags, ff, rf, -1, -1, 0, 0, pieceDesc[piece], callback, closure);
763 if(IS_SHOGI(gameInfo.variant))
764 piece = (ChessSquare) ( SHOGI piece );
766 switch ((int)piece) {
767 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
769 /* can't happen ([HGM] except for faries...) */
773 if(gameInfo.variant == VariantXiangqi) {
774 /* [HGM] capture and move straight ahead in Xiangqi */
775 if (rf < BOARD_HEIGHT-1 &&
776 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
777 callback(board, flags, NormalMove,
778 rf, ff, rf + 1, ff, closure);
780 /* and move sideways when across the river */
781 for (s = -1; s <= 1; s += 2) {
782 if (rf >= BOARD_HEIGHT>>1 &&
783 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
784 !WhitePiece(board[rf][ff+s]) ) {
785 callback(board, flags, NormalMove,
786 rf, ff, rf, ff+s, closure);
791 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
792 callback(board, flags,
793 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
794 rf, ff, rf + 1, ff, closure);
796 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
797 gameInfo.variant != VariantShatranj && /* [HGM] */
798 gameInfo.variant != VariantCourier && /* [HGM] */
799 board[rf+2][ff] == EmptySquare ) {
800 callback(board, flags, NormalMove,
801 rf, ff, rf+2, ff, closure);
803 for (s = -1; s <= 1; s += 2) {
804 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
805 ((flags & F_KRIEGSPIEL_CAPTURE) ||
806 BlackPiece(board[rf + 1][ff + s]))) {
807 callback(board, flags,
808 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
809 rf, ff, rf + 1, ff + s, closure);
811 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
812 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
813 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
814 board[rf][ff + s] == BlackPawn &&
815 board[rf+1][ff + s] == EmptySquare) {
816 callback(board, flags, WhiteCapturesEnPassant,
817 rf, ff, rf+1, ff + s, closure);
824 if(gameInfo.variant == VariantXiangqi) {
825 /* [HGM] capture straight ahead in Xiangqi */
826 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
827 callback(board, flags, NormalMove,
828 rf, ff, rf - 1, ff, closure);
830 /* and move sideways when across the river */
831 for (s = -1; s <= 1; s += 2) {
832 if (rf < BOARD_HEIGHT>>1 &&
833 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
834 !BlackPiece(board[rf][ff+s]) ) {
835 callback(board, flags, NormalMove,
836 rf, ff, rf, ff+s, closure);
841 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
842 callback(board, flags,
843 rf <= promoRank ? BlackPromotion : NormalMove,
844 rf, ff, rf - 1, ff, closure);
846 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
847 gameInfo.variant != VariantShatranj && /* [HGM] */
848 gameInfo.variant != VariantCourier && /* [HGM] */
849 board[rf-2][ff] == EmptySquare) {
850 callback(board, flags, NormalMove,
851 rf, ff, rf-2, ff, closure);
853 for (s = -1; s <= 1; s += 2) {
854 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
855 ((flags & F_KRIEGSPIEL_CAPTURE) ||
856 WhitePiece(board[rf - 1][ff + s]))) {
857 callback(board, flags,
858 rf <= promoRank ? BlackPromotion : NormalMove,
859 rf, ff, rf - 1, ff + s, closure);
861 if (rf < BOARD_HEIGHT>>1) {
862 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
863 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
864 board[rf][ff + s] == WhitePawn &&
865 board[rf-1][ff + s] == EmptySquare) {
866 callback(board, flags, BlackCapturesEnPassant,
867 rf, ff, rf-1, ff + s, closure);
877 for (i = -1; i <= 1; i += 2)
878 for (j = -1; j <= 1; j += 2)
879 for (s = 1; s <= 2; s++) {
882 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
883 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
884 && !SameColor(board[rf][ff], board[rt][ft]))
885 callback(board, flags, NormalMove,
886 rf, ff, rt, ft, closure);
890 case SHOGI WhiteKnight:
891 for (s = -1; s <= 1; s += 2) {
892 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
893 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
894 callback(board, flags, NormalMove,
895 rf, ff, rf + 2, ff + s, closure);
900 case SHOGI BlackKnight:
901 for (s = -1; s <= 1; s += 2) {
902 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
903 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
904 callback(board, flags, NormalMove,
905 rf, ff, rf - 2, ff + s, closure);
912 for (d = 0; d <= 1; d++)
913 for (s = -1; s <= 1; s += 2) {
916 rt = rf + (i * s) * d;
917 ft = ff + (i * s) * (1 - d);
918 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
919 if (m == 0 && board[rt][ft] == EmptySquare)
920 callback(board, flags, NormalMove,
921 rf, ff, rt, ft, closure);
922 if (m == 1 && board[rt][ft] != EmptySquare &&
923 !SameColor(board[rf][ff], board[rt][ft]) )
924 callback(board, flags, NormalMove,
925 rf, ff, rt, ft, closure);
926 if (board[rt][ft] != EmptySquare && m++) break;
931 /* Gold General (and all its promoted versions) . First do the */
932 /* diagonal forward steps, then proceed as normal Wazir */
933 case SHOGI (PROMO WhitePawn):
934 if(gameInfo.variant == VariantShogi) goto WhiteGold;
935 case SHOGI (PROMO BlackPawn):
936 if(gameInfo.variant == VariantShogi) goto BlackGold;
937 SlideVertical(board, flags, rf, ff, callback, closure);
940 case SHOGI (PROMO WhiteKnight):
941 if(gameInfo.variant == VariantShogi) goto WhiteGold;
942 case SHOGI BlackDrunk:
943 case SHOGI BlackAlfil:
944 Ferz(board, flags, rf, ff, callback, closure);
945 StepSideways(board, flags, rf, ff, callback, closure);
946 StepBackward(board, flags, rf, ff, callback, closure);
949 case SHOGI (PROMO BlackKnight):
950 if(gameInfo.variant == VariantShogi) goto BlackGold;
951 case SHOGI WhiteDrunk:
952 case SHOGI WhiteAlfil:
953 Ferz(board, flags, rf, ff, callback, closure);
954 StepSideways(board, flags, rf, ff, callback, closure);
955 StepForward(board, flags, rf, ff, callback, closure);
961 if(gameInfo.variant == VariantShogi) goto BlackGold;
962 SlideVertical(board, flags, rf, ff, callback, closure);
963 Ferz(board, flags, rf, ff, callback, closure);
964 StepSideways(board, flags, rf, ff, callback, closure);
967 case SHOGI (PROMO WhiteQueen):
968 case SHOGI WhiteTokin:
969 case SHOGI WhiteWazir:
971 StepDiagForward(board, flags, rf, ff, callback, closure);
972 Wazir(board, flags, rf, ff, callback, closure);
975 case SHOGI (PROMO BlackQueen):
976 case SHOGI BlackTokin:
977 case SHOGI BlackWazir:
979 StepDiagBackward(board, flags, rf, ff, callback, closure);
980 Wazir(board, flags, rf, ff, callback, closure);
985 Wazir(board, flags, rf, ff, callback, closure);
988 case SHOGI WhiteMarshall:
989 case SHOGI BlackMarshall:
990 Ferz(board, flags, rf, ff, callback, closure);
991 for (d = 0; d <= 1; d++)
992 for (s = -2; s <= 2; s += 4) {
994 ft = ff + s * (1 - d);
995 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
996 if (!SameColor(board[rf][ff], board[rt][ft]) )
997 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1001 case SHOGI WhiteAngel:
1002 case SHOGI BlackAngel:
1003 Wazir(board, flags, rf, ff, callback, closure);
1007 /* [HGM] support Shatranj pieces */
1008 for (rs = -1; rs <= 1; rs += 2)
1009 for (fs = -1; fs <= 1; fs += 2) {
1012 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1013 && ( gameInfo.variant != VariantXiangqi ||
1014 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
1016 && !SameColor(board[rf][ff], board[rt][ft]))
1017 callback(board, flags, NormalMove,
1018 rf, ff, rt, ft, closure);
1019 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1020 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
1021 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
1023 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1024 && !SameColor(board[rf][ff], board[rt][ft]))
1025 callback(board, flags, NormalMove,
1026 rf, ff, rt, ft, closure);
1028 if(gameInfo.variant == VariantSpartan)
1029 for(fs = -1; fs <= 1; fs += 2) {
1031 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
1032 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
1036 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
1039 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
1040 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1041 for (s = -2; s <= 2; s += 4) {
1043 ft = ff + s * (1 - d);
1044 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1045 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1046 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1049 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
1050 case SHOGI WhiteCardinal:
1051 case SHOGI BlackCardinal:
1052 case SHOGI WhitePCardinal:
1053 case SHOGI BlackPCardinal:
1055 Bishop(board, flags, rf, ff, callback, closure);
1056 Wazir(board, flags, rf, ff, callback, closure);
1059 /* Capablanca Archbishop continues as Knight */
1062 Knight(board, flags, rf, ff, callback, closure);
1064 /* Shogi Bishops are ordinary Bishops */
1065 case SHOGI WhiteBishop:
1066 case SHOGI BlackBishop:
1067 case SHOGI WhitePBishop:
1068 case SHOGI BlackPBishop:
1071 Bishop(board, flags, rf, ff, callback, closure);
1074 /* Shogi Lance is unlike anything, and asymmetric at that */
1075 case SHOGI WhiteQueen:
1076 if(gameInfo.variant == VariantChu) goto doQueen;
1080 if (rt >= BOARD_HEIGHT) break;
1081 if (SameColor(board[rf][ff], board[rt][ft])) break;
1082 callback(board, flags, NormalMove,
1083 rf, ff, rt, ft, closure);
1084 if (board[rt][ft] != EmptySquare) break;
1088 case SHOGI BlackQueen:
1089 if(gameInfo.variant == VariantChu) goto doQueen;
1094 if (SameColor(board[rf][ff], board[rt][ft])) break;
1095 callback(board, flags, NormalMove,
1096 rf, ff, rt, ft, closure);
1097 if (board[rt][ft] != EmptySquare) break;
1101 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1104 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1105 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1106 for (s = -2; s <= 2; s += 4) {
1108 ft = ff + s * (1 - d);
1109 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1110 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1111 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1112 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1114 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1115 Wazir(board, flags, rf, ff, callback, closure);
1117 Rook(board, flags, rf, ff, callback, closure);
1120 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1121 case SHOGI WhiteDragon:
1122 case SHOGI BlackDragon:
1123 case SHOGI WhitePDragon:
1124 case SHOGI BlackPDragon:
1126 Rook(board, flags, rf, ff, callback, closure);
1127 Ferz(board, flags, rf, ff, callback, closure);
1131 /* Capablanca Chancellor sets flag to continue as Knight */
1134 Rook(board, flags, rf, ff, callback, closure);
1135 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1136 Ferz(board, flags, rf, ff, callback, closure);
1138 Knight(board, flags, rf, ff, callback, closure);
1141 /* Shogi Rooks are ordinary Rooks */
1142 case SHOGI WhiteRook:
1143 case SHOGI BlackRook:
1144 case SHOGI WhitePRook:
1145 case SHOGI BlackPRook:
1148 Rook(board, flags, rf, ff, callback, closure);
1153 case SHOGI WhiteMother:
1154 case SHOGI BlackMother:
1156 Rook(board, flags, rf, ff, callback, closure);
1157 Bishop(board, flags, rf, ff, callback, closure);
1160 case SHOGI WhitePawn:
1161 StepForward(board, flags, rf, ff, callback, closure);
1164 case SHOGI BlackPawn:
1165 StepBackward(board, flags, rf, ff, callback, closure);
1169 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1170 case SHOGI WhiteFerz:
1171 Ferz(board, flags, rf, ff, callback, closure);
1172 StepForward(board, flags, rf, ff, callback, closure);
1176 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1177 case SHOGI BlackFerz:
1178 StepBackward(board, flags, rf, ff, callback, closure);
1182 if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
1183 int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
1184 int ft = BOARD_WIDTH>>1;
1185 if(!SameColor(board[rf][ff], board[rt][ft]))
1186 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1188 /* [HGM] support Shatranj pieces */
1189 Ferz(board, flags, rf, ff, callback, closure);
1194 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1197 case SHOGI WhiteMonarch:
1198 case SHOGI BlackMonarch:
1199 case SHOGI WhiteKing:
1200 case SHOGI BlackKing:
1203 Ferz(board, flags, rf, ff, callback, closure);
1204 Wazir(board, flags, rf, ff, callback, closure);
1207 case WhiteNightrider:
1208 case BlackNightrider:
1209 for (i = -1; i <= 1; i += 2)
1210 for (j = -1; j <= 1; j += 2)
1211 for (s = 1; s <= 2; s++) { int k;
1214 ft = ff + k*j*(3-s);
1215 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1216 if (SameColor(board[rf][ff], board[rt][ft])) break;
1217 callback(board, flags, NormalMove,
1218 rf, ff, rt, ft, closure);
1219 if (board[rt][ft] != EmptySquare) break;
1225 Bishop(board, flags, rf, ff, callback, closure);
1226 Rook(board, flags, rf, ff, callback, closure);
1227 Knight(board, flags, rf, ff, callback, closure);
1230 // Use Lance as Berolina / Spartan Pawn.
1232 if(gameInfo.variant == VariantSuper) goto Amazon;
1233 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1234 callback(board, flags,
1235 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1236 rf, ff, rf + 1, ff, closure);
1237 for (s = -1; s <= 1; s += 2) {
1238 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1239 callback(board, flags,
1240 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1241 rf, ff, rf + 1, ff + s, closure);
1242 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1243 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1248 if(gameInfo.variant == VariantSuper) goto Amazon;
1249 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1250 callback(board, flags,
1251 rf <= promoRank ? BlackPromotion : NormalMove,
1252 rf, ff, rf - 1, ff, closure);
1253 for (s = -1; s <= 1; s += 2) {
1254 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1255 callback(board, flags,
1256 rf <= promoRank ? BlackPromotion : NormalMove,
1257 rf, ff, rf - 1, ff + s, closure);
1258 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1259 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1263 case SHOGI WhiteNothing:
1264 case SHOGI BlackNothing:
1265 case SHOGI WhiteLion:
1266 case SHOGI BlackLion:
1269 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1270 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1271 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1272 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1273 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1274 rf, ff, rt, ft, closure);
1279 case SHOGI WhiteFalcon:
1280 case SHOGI BlackFalcon:
1281 case SHOGI WhitePDagger:
1282 case SHOGI BlackPDagger:
1283 SlideSideways(board, flags, rf, ff, callback, closure);
1284 StepVertical(board, flags, rf, ff, callback, closure);
1287 case SHOGI WhiteCobra:
1288 case SHOGI BlackCobra:
1289 StepVertical(board, flags, rf, ff, callback, closure);
1292 case SHOGI (PROMO WhiteFerz):
1293 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1294 case SHOGI (PROMO BlackFerz):
1295 if(gameInfo.variant == VariantShogi) goto BlackGold;
1296 case SHOGI WhitePSword:
1297 case SHOGI BlackPSword:
1298 SlideVertical(board, flags, rf, ff, callback, closure);
1299 StepSideways(board, flags, rf, ff, callback, closure);
1302 case SHOGI WhiteUnicorn:
1303 case SHOGI BlackUnicorn:
1304 Ferz(board, flags, rf, ff, callback, closure);
1305 StepVertical(board, flags, rf, ff, callback, closure);
1308 case SHOGI WhiteMan:
1309 StepDiagForward(board, flags, rf, ff, callback, closure);
1310 StepVertical(board, flags, rf, ff, callback, closure);
1313 case SHOGI BlackMan:
1314 StepDiagBackward(board, flags, rf, ff, callback, closure);
1315 StepVertical(board, flags, rf, ff, callback, closure);
1318 case SHOGI WhiteHCrown:
1319 case SHOGI BlackHCrown:
1320 Bishop(board, flags, rf, ff, callback, closure);
1321 SlideSideways(board, flags, rf, ff, callback, closure);
1324 case SHOGI WhiteCrown:
1325 case SHOGI BlackCrown:
1326 Bishop(board, flags, rf, ff, callback, closure);
1327 SlideVertical(board, flags, rf, ff, callback, closure);
1330 case SHOGI WhiteCat:
1331 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1332 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1333 if(killX >= 0) break;
1334 Bishop(board, flags, rf, ff, callback, closure);
1335 SlideSideways(board, flags, rf, ff, callback, closure);
1336 SlideBackward(board, flags, rf, ff, callback, closure);
1339 case SHOGI BlackCat:
1340 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1341 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1342 if(killX >= 0) break;
1343 Bishop(board, flags, rf, ff, callback, closure);
1344 SlideSideways(board, flags, rf, ff, callback, closure);
1345 SlideForward(board, flags, rf, ff, callback, closure);
1348 case SHOGI WhiteDagger:
1349 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1350 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1351 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1352 if(killX >= 0) break;
1353 Rook(board, flags, rf, ff, callback, closure);
1354 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1357 case SHOGI BlackDagger:
1358 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1359 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1360 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1361 if(killX >= 0) break;
1362 Rook(board, flags, rf, ff, callback, closure);
1363 SlideDiagForward(board, flags, rf, ff, callback, closure);
1366 case SHOGI WhiteDolphin:
1367 case SHOGI BlackHorse:
1368 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1369 SlideVertical(board, flags, rf, ff, callback, closure);
1372 case SHOGI BlackDolphin:
1373 case SHOGI WhiteHorse:
1374 SlideDiagForward(board, flags, rf, ff, callback, closure);
1375 SlideVertical(board, flags, rf, ff, callback, closure);
1378 case SHOGI WhiteLance:
1379 SlideForward(board, flags, rf, ff, callback, closure);
1382 case SHOGI BlackLance:
1383 SlideBackward(board, flags, rf, ff, callback, closure);
1386 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1390 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1403 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1404 Board xqCheckers, nullBoard;
1406 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1407 int rf, int ff, int rt, int ft,
1411 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1413 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1415 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1417 if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1419 if (!(flags & F_IGNORE_CHECK) ) {
1420 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1423 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1424 kings += (board[r][f] == BlackKing);
1428 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1430 check = CheckTest(board, flags, rf, ff, rt, ft,
1431 kind == WhiteCapturesEnPassant ||
1432 kind == BlackCapturesEnPassant);
1433 if(promo) board[rf][ff] = BlackLance;
1436 if (flags & F_ATOMIC_CAPTURE) {
1437 if (board[rt][ft] != EmptySquare ||
1438 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1440 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1441 if (board[rf][ff] == king) return;
1442 for (r = rt-1; r <= rt+1; r++) {
1443 for (f = ft-1; f <= ft+1; f++) {
1444 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1445 board[r][f] == king) return;
1450 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1457 int captures; // [HGM] losers
1458 } LegalityTestClosure;
1461 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1462 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1463 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1464 moves that would destroy your own king. The CASTLE_OK flags are
1465 true if castling is not yet ruled out by a move of the king or
1466 rook. Return TRUE if the player on move is currently in check and
1467 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1469 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1472 int ff, ft, k, left, right, swap;
1473 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1474 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1475 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1480 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1481 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1482 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1484 if (inCheck) return TRUE;
1486 /* Generate castling moves */
1487 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1488 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1491 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1492 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1494 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1495 if ((flags & F_WHITE_ON_MOVE) &&
1496 (flags & F_WHITE_KCASTLE_OK) &&
1497 board[0][ff] == wKing &&
1498 board[0][ff + 1] == EmptySquare &&
1499 board[0][ff + 2] == EmptySquare &&
1500 board[0][BOARD_RGHT-3] == EmptySquare &&
1501 board[0][BOARD_RGHT-2] == EmptySquare &&
1502 board[0][BOARD_RGHT-1] == WhiteRook &&
1503 castlingRights[0] != NoRights && /* [HGM] check rights */
1504 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1506 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1507 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1508 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1509 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1511 callback(board, flags,
1512 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1513 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1515 if ((flags & F_WHITE_ON_MOVE) &&
1516 (flags & F_WHITE_QCASTLE_OK) &&
1517 board[0][ff] == wKing &&
1518 board[0][ff - 1] == EmptySquare &&
1519 board[0][ff - 2] == EmptySquare &&
1520 board[0][BOARD_LEFT+2] == EmptySquare &&
1521 board[0][BOARD_LEFT+1] == EmptySquare &&
1522 board[0][BOARD_LEFT+0] == WhiteRook &&
1523 castlingRights[1] != NoRights && /* [HGM] check rights */
1524 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1526 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1527 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1528 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1530 callback(board, flags,
1531 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1532 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1534 if (!(flags & F_WHITE_ON_MOVE) &&
1535 (flags & F_BLACK_KCASTLE_OK) &&
1536 board[BOARD_HEIGHT-1][ff] == bKing &&
1537 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1538 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1539 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1540 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1541 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1542 castlingRights[3] != NoRights && /* [HGM] check rights */
1543 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1545 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1546 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1547 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1548 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1550 callback(board, flags,
1551 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1552 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1554 if (!(flags & F_WHITE_ON_MOVE) &&
1555 (flags & F_BLACK_QCASTLE_OK) &&
1556 board[BOARD_HEIGHT-1][ff] == bKing &&
1557 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1558 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1559 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1560 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1561 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1562 castlingRights[4] != NoRights && /* [HGM] check rights */
1563 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1565 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1566 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1567 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1569 callback(board, flags,
1570 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1571 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1575 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1577 /* generate all potential FRC castling moves (KxR), ignoring flags */
1578 /* [HGM] test if the Rooks we find have castling rights */
1579 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1582 if ((flags & F_WHITE_ON_MOVE) != 0) {
1583 ff = castlingRights[2]; /* King file if we have any rights */
1584 if(ff != NoRights && board[0][ff] == WhiteKing) {
1585 if (appData.debugMode) {
1586 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1587 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1589 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1591 right = BOARD_RGHT-2;
1592 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1593 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1594 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1595 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1596 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1597 if(ft != NoRights && board[0][ft] == WhiteRook) {
1598 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1599 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1602 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1603 left = BOARD_LEFT+2;
1605 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1606 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1607 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1608 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1609 if(ff > BOARD_LEFT+2)
1610 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1611 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1612 if(ft != NoRights && board[0][ft] == WhiteRook) {
1613 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1614 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1618 ff = castlingRights[5]; /* King file if we have any rights */
1619 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1620 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1622 right = BOARD_RGHT-2;
1623 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1624 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1625 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1626 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1627 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1628 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1629 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1630 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1633 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1634 left = BOARD_LEFT+2;
1636 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1637 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1638 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1639 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1640 if(ff > BOARD_LEFT+2)
1641 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1642 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1643 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1644 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1645 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1662 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1663 int rf, int ff, int rt, int ft,
1668 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1670 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1672 if (rt == cl->rking && ft == cl->fking) {
1673 if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1675 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1677 if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1678 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1679 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1683 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1684 he leave himself in check? Or if rf == -1, is the player on move
1685 in check now? enPassant must be TRUE if the indicated move is an
1686 e.p. capture. The possibility of castling out of a check along the
1687 back rank is not accounted for (i.e., we still return nonzero), as
1688 this is illegal anyway. Return value is the number of times the
1689 king is in check. */
1691 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1693 CheckTestClosure cl;
1694 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1695 ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1696 int saveKill = killX;
1697 /* Suppress warnings on uninitialized variables */
1699 if(gameInfo.variant == VariantXiangqi)
1700 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1701 if(gameInfo.variant == VariantKnightmate)
1702 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1703 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1704 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1705 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1706 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1707 if(board[r][f] == k || board[r][f] == prince) {
1708 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1709 king = board[r][f]; // remember hich one we had
1716 captured = board[rf][ft];
1717 board[rf][ft] = EmptySquare;
1719 captured = board[rt][ft];
1721 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1722 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1725 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1726 board[rt][ft] = board[rf][ff];
1727 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1729 ep = board[EP_STATUS];
1730 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1731 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1732 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1733 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1734 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1735 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1736 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1740 /* For compatibility with ICS wild 9, we scan the board in the
1741 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1742 and we test only whether that one is in check. */
1743 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1744 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1745 if (board[cl.rking][cl.fking] == king) {
1747 if(gameInfo.variant == VariantXiangqi) {
1748 /* [HGM] In Xiangqi opposing Kings means check as well */
1750 dir = (king >= BlackPawn) ? -1 : 1;
1751 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1752 board[i][cl.fking] == EmptySquare; i+=dir );
1753 if(i>=0 && i<BOARD_HEIGHT &&
1754 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1757 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1758 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1759 goto undo_move; /* 2-level break */
1766 if(rf != DROP_RANK) // [HGM] drop
1767 board[rf][ff] = board[rt][ft];
1769 board[rf][ft] = captured;
1770 board[rt][ft] = EmptySquare;
1773 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1774 board[killY][killX = saveKill & 0xFFF] = trampled;
1776 board[rt][ft] = captured;
1778 board[EP_STATUS] = ep;
1781 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1785 HasLion (Board board, int flags)
1787 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1789 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1790 if(board[r][f] == lion) return 1;
1795 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1796 { // [HGM] put drop legality testing in separate routine for clarity
1798 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1799 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1800 n = PieceToNumber(piece);
1801 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1802 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1803 return ImpossibleMove; // piece not available
1804 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1805 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1806 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1807 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1808 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1809 if(piece == WhitePawn || piece == BlackPawn) {
1810 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1811 for(r=1; r<BOARD_HEIGHT-1; r++)
1812 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1813 // should still test if we mate with this Pawn
1815 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1816 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1818 if( (piece == WhitePawn || piece == BlackPawn) &&
1819 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1820 return IllegalMove; /* no pawn drops on 1st/8th */
1822 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1823 if (!(flags & F_IGNORE_CHECK) &&
1824 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1825 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1828 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1829 int rf, int ff, int rt, int ft,
1833 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1835 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1837 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1838 cl->captures++; // [HGM] losers: count legal captures
1839 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1844 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1846 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1848 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1849 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1850 piece = filterPiece = board[rf][ff];
1851 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1853 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1854 /* (perhaps we should disallow moves that obviously leave us in check?) */
1855 if((piece == WhiteFalcon || piece == BlackFalcon ||
1856 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1857 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1861 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1862 cl.ft = fFilter = ft;
1863 cl.kind = IllegalMove;
1864 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1865 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1866 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1867 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1868 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1869 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1871 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1872 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1873 if(board[rf][ff] < BlackPawn) { // white
1874 if(rf != 0) return IllegalMove; // must be on back rank
1875 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1876 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1877 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1878 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1880 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1881 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1882 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1883 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1884 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1887 if(gameInfo.variant == VariantChu) {
1888 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1889 if(promoChar != '+')
1890 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1891 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1892 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1893 return ImpossibleMove;
1895 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1897 if(gameInfo.variant == VariantShogi) {
1898 /* [HGM] Shogi promotions. '=' means defer */
1899 if(rf != DROP_RANK && cl.kind == NormalMove) {
1900 ChessSquare piece = board[rf][ff];
1901 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1903 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1904 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1905 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1906 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1907 promoChar = '+'; // allowed ICS notations
1908 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1909 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1910 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1911 else if(flags & F_WHITE_ON_MOVE) {
1912 if( (int) piece < (int) WhiteWazir &&
1913 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1914 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1915 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1916 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1917 else /* promotion optional, default is defer */
1918 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1919 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1921 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1922 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1923 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1924 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1925 else /* promotion optional, default is defer */
1926 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1927 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1931 if (promoChar != NULLCHAR) {
1932 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1933 ChessSquare piece = board[rf][ff];
1934 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1935 // should test if in zone, really
1936 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1938 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1940 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1941 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1942 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1943 if(piece == EmptySquare)
1944 cl.kind = ImpossibleMove; // non-existing piece
1945 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1946 cl.kind = IllegalMove; // no two Lions
1947 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1948 if(promoChar != PieceToChar(BlackKing)) {
1949 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1950 if(piece == BlackLance) cl.kind = ImpossibleMove;
1951 } else { // promotion to King allowed only if we do not have two yet
1952 int r, f, kings = 0;
1953 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1954 if(kings == 2) cl.kind = IllegalMove;
1956 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1957 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1958 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1959 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1960 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1961 cl.kind = IllegalMove; // promotion to King usually not allowed
1963 cl.kind = IllegalMove;
1973 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1974 int rf, int ff, int rt, int ft,
1978 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1980 register MateTestClosure *cl = (MateTestClosure *) closure;
1985 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1987 MateTest (Board board, int flags)
1990 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1991 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1993 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1994 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1995 nrKing += (board[r][f] == king); // stm has king
1996 if( board[r][f] != EmptySquare ) {
1997 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
2002 switch(gameInfo.variant) { // [HGM] losers: extinction wins
2003 case VariantShatranj:
2004 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2008 if(nrKing == 0) return MT_NOKING;
2011 if(myPieces == 1) return MT_BARE;
2014 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2015 // [HGM] 3check: yet to do!
2017 return inCheck ? MT_CHECK : MT_NONE;
2019 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2020 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2021 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2022 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2023 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2024 if(board[n][holdings] != EmptySquare) {
2025 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2026 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2029 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2030 return myPieces == hisPieces ? MT_STALEMATE :
2031 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2032 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2033 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2035 return inCheck ? MT_CHECKMATE
2036 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2037 MT_STAINMATE : MT_STALEMATE;
2042 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2043 int rf, int ff, int rt, int ft,
2047 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2049 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2050 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2051 extern int kifu; // in parser.c
2053 // [HGM] wild: for wild-card pieces rt and rf are dummies
2054 if(piece == WhiteFalcon || piece == BlackFalcon ||
2055 piece == WhiteCobra || piece == BlackCobra)
2056 wildCard = !pieceDefs; // no wildcards when engine defined pieces
2058 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2059 || PieceToChar(board[rf][ff]) == '~'
2060 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2062 (cl->rfIn == -1 || cl->rfIn == rf) &&
2063 (cl->ffIn == -1 || cl->ffIn == ff) &&
2064 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2065 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2067 if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
2069 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2070 int this = 1, other = 1;
2071 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2072 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2073 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2074 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2075 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2076 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2077 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2078 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2082 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2083 // [HGM] oneclick: if multiple moves, be sure we remember capture
2084 cl->piece = board[rf][ff];
2087 cl->rt = wildCard ? cl->rtIn : rt;
2088 cl->ft = wildCard ? cl->ftIn : ft;
2091 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2096 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2098 int illegal = 0; char c = closure->promoCharIn;
2100 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2101 closure->count = closure->captures = 0;
2102 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2103 closure->kind = ImpossibleMove;
2104 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2105 fFilter = closure->ftIn;
2106 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2107 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2108 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2109 closure->count = closure->captures = 0;
2110 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2111 closure->kind = ImpossibleMove;
2112 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2115 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2116 if (closure->count == 0) {
2117 /* See if it's an illegal move due to check */
2119 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2120 if (closure->count == 0) {
2121 /* No, it's not even that */
2122 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2123 int f, r; // if there is only a single piece of the requested type on the board, use that
2124 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2125 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2126 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2127 if(closure->count > 1) illegal = 0; // ambiguous
2129 if(closure->count == 0) {
2130 if (appData.debugMode) { int i, j;
2131 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2132 for(j=0; j<BOARD_WIDTH; j++)
2133 fprintf(debugFP, "%3d", (int) board[i][j]);
2134 fprintf(debugFP, "\n");
2140 } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
2141 DisambiguateClosure spare = *closure;
2142 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2143 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2144 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2148 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2149 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2150 if(closure->piece < BlackPawn) { // white
2151 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2152 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2153 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2154 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2155 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2157 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2158 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2159 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2160 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2161 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2164 if(gameInfo.variant == VariantChu) {
2165 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2167 if(gameInfo.variant == VariantShogi) {
2168 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2169 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2170 ChessSquare piece = closure->piece;
2171 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2172 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2173 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2174 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2175 c = '+'; // allowed ICS notations
2176 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2177 else if(flags & F_WHITE_ON_MOVE) {
2178 if( (int) piece < (int) WhiteWazir &&
2179 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2180 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2181 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2182 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2183 else /* promotion optional, default is defer */
2184 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2185 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2187 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2188 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2189 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2190 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2191 else /* promotion optional, default is defer */
2192 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2193 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2196 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2197 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2199 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2200 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2201 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2202 gameInfo.variant == VariantMakruk)
2203 c = PieceToChar(BlackFerz);
2204 else if(gameInfo.variant == VariantASEAN)
2205 c = PieceToChar(BlackRook);
2206 else if(gameInfo.variant == VariantGreat)
2207 c = PieceToChar(BlackMan);
2208 else if(gameInfo.variant == VariantGrand)
2209 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2211 c = PieceToChar(BlackQueen);
2212 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2213 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2214 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2215 ChessSquare p = closure->piece;
2216 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2217 closure->kind = ImpossibleMove; // used on non-promotable piece
2218 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2219 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2221 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2222 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2223 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2224 if (closure->count > 1) {
2225 closure->kind = AmbiguousMove;
2228 /* Note: If more than one illegal move matches, but no legal
2229 moves, we return IllegalMove, not AmbiguousMove. Caller
2230 can look at closure->count to detect this.
2232 closure->kind = IllegalMove;
2246 } CoordsToAlgebraicClosure;
2248 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2249 ChessMove kind, int rf, int ff,
2250 int rt, int ft, VOIDSTAR closure));
2253 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2255 register CoordsToAlgebraicClosure *cl =
2256 (CoordsToAlgebraicClosure *) closure;
2258 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2259 (board[rf][ff] == cl->piece
2260 || PieceToChar(board[rf][ff]) == '~' &&
2261 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2265 cl->kind = kind; /* this is the move we want */
2267 cl->file++; /* need file to rule out this move */
2271 cl->rank++; /* need rank to rule out this move */
2273 cl->either++; /* rank or file will rule out this move */
2279 /* Convert coordinates to normal algebraic notation.
2280 promoChar must be NULLCHAR or 'x' if not a promotion.
2283 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2287 char *outp = out, c, capture;
2288 CoordsToAlgebraicClosure cl;
2290 if (rf == DROP_RANK) {
2291 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2292 /* Bughouse piece drop */
2293 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2298 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2300 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2303 if (promoChar == 'x') promoChar = NULLCHAR;
2304 piece = board[rf][ff];
2305 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2310 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2311 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2312 /* Keep short notation if move is illegal only because it
2313 leaves the player in check, but still return IllegalMove */
2314 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2315 if (kind == IllegalMove) break;
2320 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2321 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2322 /* Non-capture; use style "e5" */
2325 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2327 /* Capture; use style "exd5" */
2329 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2333 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2335 /* Use promotion suffix style "=Q" */
2337 if (promoChar != NULLCHAR) {
2338 if(IS_SHOGI(gameInfo.variant)) {
2339 /* [HGM] ... but not in Shogi! */
2340 *outp++ = promoChar == '=' ? '=' : '+';
2343 *outp++ = ToUpper(promoChar);
2352 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2353 /* Code added by Tord: FRC castling. */
2354 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2355 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2357 safeStrCpy(out, "O-O", MOVE_LEN);
2359 safeStrCpy(out, "O-O-O", MOVE_LEN);
2360 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2362 /* End of code added by Tord */
2363 /* Test for castling or ICS wild castling */
2364 /* Use style "O-O" (oh-oh) for PGN compatibility */
2365 else if (rf == rt &&
2366 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2367 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2368 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2369 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2370 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2371 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2373 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2375 /* This notation is always unambiguous, unless there are
2376 kings on both the d and e files, with "wild castling"
2377 possible for the king on the d file and normal castling
2378 possible for the other. ICS rules for wild 9
2379 effectively make castling illegal for either king in
2380 this situation. So I am not going to worry about it;
2381 I'll just generate an ambiguous O-O in this case.
2383 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2386 /* else fall through */
2391 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2392 cl.ft = fFilter = ft;
2394 cl.kind = IllegalMove;
2395 cl.rank = cl.file = cl.either = 0;
2396 c = PieceToChar(piece) ;
2397 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2399 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2400 /* Generate pretty moves for moving into check, but
2401 still return IllegalMove.
2403 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2404 if (cl.kind == IllegalMove) break;
2405 cl.kind = IllegalMove;
2408 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2409 else "Ngf3" or "Ngxf7",
2410 else "N1f3" or "N5xf7",
2411 else "Ng1f3" or "Ng5xf7".
2413 if( c == '~' || c == '+') {
2414 /* [HGM] print nonexistent piece as its demoted version */
2415 piece = (ChessSquare) (CHUDEMOTED(piece));
2417 if(c=='+') *outp++ = c;
2418 *outp++ = ToUpper(PieceToChar(piece));
2419 if(*outp = PieceSuffix(piece)) outp++;
2421 if (cl.file || (cl.either && !cl.rank)) {
2427 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2430 if(board[rt][ft] != EmptySquare)
2436 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2437 if (IS_SHOGI(gameInfo.variant)) {
2438 /* [HGM] in Shogi non-pawns can promote */
2439 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2441 else if (gameInfo.variant == VariantChuChess && promoChar ||
2442 gameInfo.variant != VariantSuper && promoChar &&
2443 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2445 *outp++ = ToUpper(promoChar);
2447 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2449 *outp++ = ToUpper(promoChar);
2455 /* Moving a nonexistent piece */
2459 /* Not a legal move, even ignoring check.
2460 If there was a piece on the from square,
2461 use style "Ng1g3" or "Ng1xe8";
2462 if there was a pawn or nothing (!),
2463 use style "g1g3" or "g1xe8". Use "x"
2464 if a piece was on the to square, even
2465 a piece of the same color.
2469 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2471 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2472 c += (board[r][f] == piece); // count on-board pieces of given type
2473 *outp = PieceToChar(piece);
2474 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2475 *outp++ = ToUpper(PieceToChar(piece));
2476 if(*outp = PieceSuffix(piece)) outp++;
2478 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2482 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2484 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2488 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2489 /* Use promotion suffix style "=Q" */
2490 if (promoChar != NULLCHAR && promoChar != 'x') {
2492 *outp++ = ToUpper(promoChar);
2499 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2508 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2510 int preyStackPointer, chaseStackPointer;
2513 unsigned char rf, ff, rt, ft;
2517 unsigned char rank, file;
2523 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2525 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2526 int rf, int ff, int rt, int ft,
2530 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2531 { // For adding captures that can lead to chase indictment to the chaseStack
2532 if(board[rt][ft] == EmptySquare) return; // non-capture
2533 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2534 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2535 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2536 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2537 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2538 chaseStack[chaseStackPointer].rf = rf;
2539 chaseStack[chaseStackPointer].ff = ff;
2540 chaseStack[chaseStackPointer].rt = rt;
2541 chaseStack[chaseStackPointer].ft = ft;
2542 chaseStackPointer++;
2545 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2546 int rf, int ff, int rt, int ft,
2550 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2551 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2553 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2555 if(board[rt][ft] == EmptySquare) return; // no capture
2556 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2557 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2559 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2560 for(i=0; i<chaseStackPointer; i++) {
2561 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2562 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2563 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2564 chaseStack[i] = chaseStack[--chaseStackPointer];
2570 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2571 int rf, int ff, int rt, int ft,
2575 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2576 { // for determining if a piece (given through the closure) is protected
2577 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2579 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2580 if(appData.debugMode && board[rt][ft] != EmptySquare)
2581 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2584 extern char moveList[MAX_MOVES][MOVE_LEN];
2587 PerpetualChase (int first, int last)
2588 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2591 ChessSquare captured;
2593 preyStackPointer = 0; // clear stack of chased pieces
2594 for(i=first; i<last; i+=2) { // for all positions with same side to move
2595 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2596 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2597 // determine all captures possible after the move, and put them on chaseStack
2598 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2599 if(appData.debugMode) { int n;
2600 for(n=0; n<chaseStackPointer; n++)
2601 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2602 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2603 fprintf(debugFP, ": all capts\n");
2605 // determine all captures possible before the move, and delete them from chaseStack
2606 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2607 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2608 cl.rt = moveList[i][3]-ONE;
2609 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2610 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2611 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2612 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2613 if(appData.debugMode) { int n;
2614 for(n=0; n<chaseStackPointer; n++)
2615 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2616 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2617 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2619 // chaseSack now contains all captures made possible by the move
2620 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2621 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2622 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2624 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2625 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2627 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2628 continue; // C or H attack on R is always chase; leave on chaseStack
2630 if(attacker == victim) {
2631 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2632 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2633 // we can capture back with equal piece, so this is no chase but a sacrifice
2634 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2635 j--; /* ! */ continue;
2640 // the attack is on a lower piece, or on a pinned or blocked equal one
2641 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2642 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2643 // test if the victim is protected by a true protector. First make the capture.
2644 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2645 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2646 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2647 // Then test if the opponent can recapture
2648 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2649 cl.rt = chaseStack[j].rt;
2650 cl.ft = chaseStack[j].ft;
2651 if(appData.debugMode) {
2652 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2654 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2655 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2656 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2657 // unmake the capture
2658 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2659 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2660 // if a recapture was found, piece is protected, and we are not chasing it.
2661 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2662 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2666 // chaseStack now contains all moves that chased
2667 if(appData.debugMode) { int n;
2668 for(n=0; n<chaseStackPointer; n++)
2669 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2670 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2671 fprintf(debugFP, ": chases\n");
2673 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2674 for(j=0; j<chaseStackPointer; j++) {
2675 preyStack[j].rank = chaseStack[j].rt;
2676 preyStack[j].file = chaseStack[j].ft;
2678 preyStackPointer = chaseStackPointer;
2681 for(j=0; j<chaseStackPointer; j++) {
2682 for(k=0; k<preyStackPointer; k++) {
2683 // search the victim of each chase move on the preyStack (first occurrence)
2684 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2685 if(k < tail) break; // piece was already identified as still being chased
2686 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2687 preyStack[tail] = preyStack[k]; // by swapping
2688 preyStack[k] = preyStack[preyStackPointer];
2694 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2695 if(appData.debugMode) { int n;
2696 for(n=0; n<preyStackPointer; n++)
2697 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2698 fprintf(debugFP, "always chased upto ply %d\n", i);
2700 // now adjust the location of the chased pieces according to opponent move
2701 for(j=0; j<preyStackPointer; j++) {
2702 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2703 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2704 preyStack[j].rank = moveList[i+1][3]-ONE;
2705 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2710 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2711 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the