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;
939 SlideVertical(board, flags, rf, ff, callback, closure);
942 case SHOGI (PROMO WhiteKnight):
943 if(gameInfo.variant == VariantShogi) goto WhiteGold;
944 case SHOGI WhiteClaw:
945 case SHOGI BlackDrunk:
946 case SHOGI BlackAlfil:
947 Ferz(board, flags, rf, ff, callback, closure);
948 StepSideways(board, flags, rf, ff, callback, closure);
949 StepBackward(board, flags, rf, ff, callback, closure);
952 case SHOGI (PROMO BlackKnight):
953 if(gameInfo.variant == VariantShogi) goto BlackGold;
954 case SHOGI BlackClaw:
955 case SHOGI WhiteDrunk:
956 case SHOGI WhiteAlfil:
957 Ferz(board, flags, rf, ff, callback, closure);
958 StepSideways(board, flags, rf, ff, callback, closure);
959 StepForward(board, flags, rf, ff, callback, closure);
965 if(gameInfo.variant == VariantShogi) goto BlackGold;
966 SlideVertical(board, flags, rf, ff, callback, closure);
967 Ferz(board, flags, rf, ff, callback, closure);
968 StepSideways(board, flags, rf, ff, callback, closure);
971 case SHOGI (PROMO WhiteQueen):
972 case SHOGI WhiteTokin:
973 case SHOGI WhiteWazir:
975 StepDiagForward(board, flags, rf, ff, callback, closure);
976 Wazir(board, flags, rf, ff, callback, closure);
979 case SHOGI (PROMO BlackQueen):
980 case SHOGI BlackTokin:
981 case SHOGI BlackWazir:
983 StepDiagBackward(board, flags, rf, ff, callback, closure);
984 Wazir(board, flags, rf, ff, callback, closure);
989 Wazir(board, flags, rf, ff, callback, closure);
992 case SHOGI WhiteMarshall:
993 case SHOGI BlackMarshall:
994 Ferz(board, flags, rf, ff, callback, closure);
995 for (d = 0; d <= 1; d++)
996 for (s = -2; s <= 2; s += 4) {
998 ft = ff + s * (1 - d);
999 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1000 if (!SameColor(board[rf][ff], board[rt][ft]) )
1001 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1005 case SHOGI WhiteAngel:
1006 case SHOGI BlackAngel:
1007 Wazir(board, flags, rf, ff, callback, closure);
1011 /* [HGM] support Shatranj pieces */
1012 for (rs = -1; rs <= 1; rs += 2)
1013 for (fs = -1; fs <= 1; fs += 2) {
1016 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1017 && ( gameInfo.variant != VariantXiangqi ||
1018 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
1020 && !SameColor(board[rf][ff], board[rt][ft]))
1021 callback(board, flags, NormalMove,
1022 rf, ff, rt, ft, closure);
1023 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1024 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
1025 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
1027 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1028 && !SameColor(board[rf][ff], board[rt][ft]))
1029 callback(board, flags, NormalMove,
1030 rf, ff, rt, ft, closure);
1032 if(gameInfo.variant == VariantSpartan)
1033 for(fs = -1; fs <= 1; fs += 2) {
1035 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
1036 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
1040 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
1043 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
1044 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1045 for (s = -2; s <= 2; s += 4) {
1047 ft = ff + s * (1 - d);
1048 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1049 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1050 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1053 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
1054 case SHOGI WhiteCardinal:
1055 case SHOGI BlackCardinal:
1056 case SHOGI WhitePCardinal:
1057 case SHOGI BlackPCardinal:
1059 Bishop(board, flags, rf, ff, callback, closure);
1060 Wazir(board, flags, rf, ff, callback, closure);
1063 /* Capablanca Archbishop continues as Knight */
1066 Knight(board, flags, rf, ff, callback, closure);
1068 /* Shogi Bishops are ordinary Bishops */
1069 case SHOGI WhiteBishop:
1070 case SHOGI BlackBishop:
1071 case SHOGI WhitePBishop:
1072 case SHOGI BlackPBishop:
1075 Bishop(board, flags, rf, ff, callback, closure);
1078 /* Shogi Lance is unlike anything, and asymmetric at that */
1079 case SHOGI WhiteQueen:
1080 if(gameInfo.variant == VariantChu) goto doQueen;
1084 if (rt >= BOARD_HEIGHT) break;
1085 if (SameColor(board[rf][ff], board[rt][ft])) break;
1086 callback(board, flags, NormalMove,
1087 rf, ff, rt, ft, closure);
1088 if (board[rt][ft] != EmptySquare) break;
1092 case SHOGI BlackQueen:
1093 if(gameInfo.variant == VariantChu) goto doQueen;
1098 if (SameColor(board[rf][ff], board[rt][ft])) break;
1099 callback(board, flags, NormalMove,
1100 rf, ff, rt, ft, closure);
1101 if (board[rt][ft] != EmptySquare) break;
1105 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1108 if(gameInfo.variant == VariantChuChess) goto DragonKing;
1109 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1110 for (s = -2; s <= 2; s += 4) {
1112 ft = ff + s * (1 - d);
1113 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1114 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
1115 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1116 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1118 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
1119 Wazir(board, flags, rf, ff, callback, closure);
1121 Rook(board, flags, rf, ff, callback, closure);
1124 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1125 case SHOGI WhiteDragon:
1126 case SHOGI BlackDragon:
1127 case SHOGI WhitePDragon:
1128 case SHOGI BlackPDragon:
1130 Rook(board, flags, rf, ff, callback, closure);
1131 Ferz(board, flags, rf, ff, callback, closure);
1135 /* Capablanca Chancellor sets flag to continue as Knight */
1138 Rook(board, flags, rf, ff, callback, closure);
1139 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1140 Ferz(board, flags, rf, ff, callback, closure);
1142 Knight(board, flags, rf, ff, callback, closure);
1145 /* Shogi Rooks are ordinary Rooks */
1146 case SHOGI WhiteRook:
1147 case SHOGI BlackRook:
1148 case SHOGI WhitePRook:
1149 case SHOGI BlackPRook:
1152 Rook(board, flags, rf, ff, callback, closure);
1157 case SHOGI WhiteMother:
1158 case SHOGI BlackMother:
1160 Rook(board, flags, rf, ff, callback, closure);
1161 Bishop(board, flags, rf, ff, callback, closure);
1164 case SHOGI WhitePawn:
1165 StepForward(board, flags, rf, ff, callback, closure);
1168 case SHOGI BlackPawn:
1169 StepBackward(board, flags, rf, ff, callback, closure);
1173 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1174 case SHOGI WhiteFerz:
1175 Ferz(board, flags, rf, ff, callback, closure);
1176 StepForward(board, flags, rf, ff, callback, closure);
1180 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1181 case SHOGI BlackFerz:
1182 StepBackward(board, flags, rf, ff, callback, closure);
1186 if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
1187 int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
1188 int ft = BOARD_WIDTH>>1;
1189 if(!SameColor(board[rf][ff], board[rt][ft]))
1190 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1192 /* [HGM] support Shatranj pieces */
1193 Ferz(board, flags, rf, ff, callback, closure);
1198 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1201 case SHOGI WhiteMonarch:
1202 case SHOGI BlackMonarch:
1203 case SHOGI WhiteKing:
1204 case SHOGI BlackKing:
1207 Ferz(board, flags, rf, ff, callback, closure);
1208 Wazir(board, flags, rf, ff, callback, closure);
1211 case WhiteNightrider:
1212 case BlackNightrider:
1213 for (i = -1; i <= 1; i += 2)
1214 for (j = -1; j <= 1; j += 2)
1215 for (s = 1; s <= 2; s++) { int k;
1218 ft = ff + k*j*(3-s);
1219 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1220 if (SameColor(board[rf][ff], board[rt][ft])) break;
1221 callback(board, flags, NormalMove,
1222 rf, ff, rt, ft, closure);
1223 if (board[rt][ft] != EmptySquare) break;
1229 Bishop(board, flags, rf, ff, callback, closure);
1230 Rook(board, flags, rf, ff, callback, closure);
1231 Knight(board, flags, rf, ff, callback, closure);
1234 // Use Lance as Berolina / Spartan Pawn.
1236 if(gameInfo.variant == VariantSuper) goto Amazon;
1237 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1238 callback(board, flags,
1239 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1240 rf, ff, rf + 1, ff, closure);
1241 for (s = -1; s <= 1; s += 2) {
1242 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1243 callback(board, flags,
1244 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1245 rf, ff, rf + 1, ff + s, closure);
1246 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1247 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1252 if(gameInfo.variant == VariantSuper) goto Amazon;
1253 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1254 callback(board, flags,
1255 rf <= promoRank ? BlackPromotion : NormalMove,
1256 rf, ff, rf - 1, ff, closure);
1257 for (s = -1; s <= 1; s += 2) {
1258 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1259 callback(board, flags,
1260 rf <= promoRank ? BlackPromotion : NormalMove,
1261 rf, ff, rf - 1, ff + s, closure);
1262 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1263 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1267 case SHOGI WhiteNothing:
1268 case SHOGI BlackNothing:
1269 case SHOGI WhiteLion:
1270 case SHOGI BlackLion:
1273 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1274 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1275 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1276 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1277 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1278 rf, ff, rt, ft, closure);
1283 case SHOGI WhiteDagger:
1284 case SHOGI BlackDagger:
1285 case SHOGI WhitePDagger:
1286 case SHOGI BlackPDagger:
1287 SlideSideways(board, flags, rf, ff, callback, closure);
1288 StepVertical(board, flags, rf, ff, callback, closure);
1291 case SHOGI WhiteCobra:
1292 case SHOGI BlackCobra:
1293 StepVertical(board, flags, rf, ff, callback, closure);
1296 case SHOGI (PROMO WhiteFerz):
1297 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1298 case SHOGI (PROMO BlackFerz):
1299 if(gameInfo.variant == VariantShogi) goto BlackGold;
1300 case SHOGI WhiteSword:
1301 case SHOGI BlackSword:
1302 case SHOGI WhitePSword:
1303 case SHOGI BlackPSword:
1304 SlideVertical(board, flags, rf, ff, callback, closure);
1305 StepSideways(board, flags, rf, ff, callback, closure);
1308 case SHOGI WhiteCat:
1309 case SHOGI BlackCat:
1310 Ferz(board, flags, rf, ff, callback, closure);
1311 StepVertical(board, flags, rf, ff, callback, closure);
1314 case SHOGI WhiteCopper:
1315 StepDiagForward(board, flags, rf, ff, callback, closure);
1316 StepVertical(board, flags, rf, ff, callback, closure);
1319 case SHOGI BlackCopper:
1320 StepDiagBackward(board, flags, rf, ff, callback, closure);
1321 StepVertical(board, flags, rf, ff, callback, closure);
1324 case SHOGI WhiteHCrown:
1325 case SHOGI BlackHCrown:
1326 Bishop(board, flags, rf, ff, callback, closure);
1327 SlideSideways(board, flags, rf, ff, callback, closure);
1330 case SHOGI WhiteCrown:
1331 case SHOGI BlackCrown:
1332 Bishop(board, flags, rf, ff, callback, closure);
1333 SlideVertical(board, flags, rf, ff, callback, closure);
1336 case SHOGI WhiteUnicorn:
1337 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1338 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1339 if(killX >= 0) break;
1340 Bishop(board, flags, rf, ff, callback, closure);
1341 SlideSideways(board, flags, rf, ff, callback, closure);
1342 SlideBackward(board, flags, rf, ff, callback, closure);
1345 case SHOGI BlackUnicorn:
1346 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1347 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1348 if(killX >= 0) break;
1349 Bishop(board, flags, rf, ff, callback, closure);
1350 SlideSideways(board, flags, rf, ff, callback, closure);
1351 SlideForward(board, flags, rf, ff, callback, closure);
1354 case SHOGI WhiteFalcon:
1355 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1356 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1357 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1358 if(killX >= 0) break;
1359 Rook(board, flags, rf, ff, callback, closure);
1360 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1363 case SHOGI BlackFalcon:
1364 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1365 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1366 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1367 if(killX >= 0) break;
1368 Rook(board, flags, rf, ff, callback, closure);
1369 SlideDiagForward(board, flags, rf, ff, callback, closure);
1372 case SHOGI WhiteDolphin:
1373 case SHOGI BlackHorse:
1374 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1375 SlideVertical(board, flags, rf, ff, callback, closure);
1378 case SHOGI BlackDolphin:
1379 case SHOGI WhiteHorse:
1380 SlideDiagForward(board, flags, rf, ff, callback, closure);
1381 SlideVertical(board, flags, rf, ff, callback, closure);
1384 case SHOGI WhiteLance:
1385 SlideForward(board, flags, rf, ff, callback, closure);
1388 case SHOGI BlackLance:
1389 SlideBackward(board, flags, rf, ff, callback, closure);
1392 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1396 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1409 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1410 Board xqCheckers, nullBoard;
1412 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1413 int rf, int ff, int rt, int ft,
1417 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1419 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1421 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1423 if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1425 if (!(flags & F_IGNORE_CHECK) ) {
1426 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1429 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1430 kings += (board[r][f] == BlackKing);
1434 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1436 check = CheckTest(board, flags, rf, ff, rt, ft,
1437 kind == WhiteCapturesEnPassant ||
1438 kind == BlackCapturesEnPassant);
1439 if(promo) board[rf][ff] = BlackLance;
1442 if (flags & F_ATOMIC_CAPTURE) {
1443 if (board[rt][ft] != EmptySquare ||
1444 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1446 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1447 if (board[rf][ff] == king) return;
1448 for (r = rt-1; r <= rt+1; r++) {
1449 for (f = ft-1; f <= ft+1; f++) {
1450 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1451 board[r][f] == king) return;
1456 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1463 int captures; // [HGM] losers
1464 } LegalityTestClosure;
1467 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1468 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1469 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1470 moves that would destroy your own king. The CASTLE_OK flags are
1471 true if castling is not yet ruled out by a move of the king or
1472 rook. Return TRUE if the player on move is currently in check and
1473 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1475 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1478 int ff, ft, k, left, right, swap;
1479 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1480 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1481 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1486 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1487 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1488 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1490 if (inCheck) return TRUE;
1492 /* Generate castling moves */
1493 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1494 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1497 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1498 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1500 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1501 if ((flags & F_WHITE_ON_MOVE) &&
1502 (flags & F_WHITE_KCASTLE_OK) &&
1503 board[0][ff] == wKing &&
1504 board[0][ff + 1] == EmptySquare &&
1505 board[0][ff + 2] == EmptySquare &&
1506 board[0][BOARD_RGHT-3] == EmptySquare &&
1507 board[0][BOARD_RGHT-2] == EmptySquare &&
1508 board[0][BOARD_RGHT-1] == WhiteRook &&
1509 castlingRights[0] != NoRights && /* [HGM] check rights */
1510 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1512 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1513 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1514 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1515 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1517 callback(board, flags,
1518 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1519 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1521 if ((flags & F_WHITE_ON_MOVE) &&
1522 (flags & F_WHITE_QCASTLE_OK) &&
1523 board[0][ff] == wKing &&
1524 board[0][ff - 1] == EmptySquare &&
1525 board[0][ff - 2] == EmptySquare &&
1526 board[0][BOARD_LEFT+2] == EmptySquare &&
1527 board[0][BOARD_LEFT+1] == EmptySquare &&
1528 board[0][BOARD_LEFT+0] == WhiteRook &&
1529 castlingRights[1] != NoRights && /* [HGM] check rights */
1530 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1532 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1533 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1534 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1536 callback(board, flags,
1537 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1538 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1540 if (!(flags & F_WHITE_ON_MOVE) &&
1541 (flags & F_BLACK_KCASTLE_OK) &&
1542 board[BOARD_HEIGHT-1][ff] == bKing &&
1543 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1544 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1545 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1546 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1547 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1548 castlingRights[3] != NoRights && /* [HGM] check rights */
1549 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1551 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1552 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1553 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1554 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1556 callback(board, flags,
1557 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1558 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1560 if (!(flags & F_WHITE_ON_MOVE) &&
1561 (flags & F_BLACK_QCASTLE_OK) &&
1562 board[BOARD_HEIGHT-1][ff] == bKing &&
1563 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1564 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1565 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1566 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1567 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1568 castlingRights[4] != NoRights && /* [HGM] check rights */
1569 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1571 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1572 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1573 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1575 callback(board, flags,
1576 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1577 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1581 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1583 /* generate all potential FRC castling moves (KxR), ignoring flags */
1584 /* [HGM] test if the Rooks we find have castling rights */
1585 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1588 if ((flags & F_WHITE_ON_MOVE) != 0) {
1589 ff = castlingRights[2]; /* King file if we have any rights */
1590 if(ff != NoRights && board[0][ff] == WhiteKing) {
1591 if (appData.debugMode) {
1592 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1593 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1595 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1597 right = BOARD_RGHT-2;
1598 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1599 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1600 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1601 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1602 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1603 if(ft != NoRights && board[0][ft] == WhiteRook) {
1604 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1605 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1608 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1609 left = BOARD_LEFT+2;
1611 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1612 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1613 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1614 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1615 if(ff > BOARD_LEFT+2)
1616 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1617 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1618 if(ft != NoRights && board[0][ft] == WhiteRook) {
1619 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1620 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1624 ff = castlingRights[5]; /* King file if we have any rights */
1625 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1626 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1628 right = BOARD_RGHT-2;
1629 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1630 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1631 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1632 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1633 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1634 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1635 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1636 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1639 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1640 left = BOARD_LEFT+2;
1642 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1643 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1644 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1645 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1646 if(ff > BOARD_LEFT+2)
1647 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1648 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1649 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1650 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1651 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1668 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1669 int rf, int ff, int rt, int ft,
1674 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1676 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1678 if (rt == cl->rking && ft == cl->fking) {
1679 if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1681 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1683 if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1684 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1685 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1689 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1690 he leave himself in check? Or if rf == -1, is the player on move
1691 in check now? enPassant must be TRUE if the indicated move is an
1692 e.p. capture. The possibility of castling out of a check along the
1693 back rank is not accounted for (i.e., we still return nonzero), as
1694 this is illegal anyway. Return value is the number of times the
1695 king is in check. */
1697 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1699 CheckTestClosure cl;
1700 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1701 ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1702 int saveKill = killX;
1703 /* Suppress warnings on uninitialized variables */
1705 if(gameInfo.variant == VariantXiangqi)
1706 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1707 if(gameInfo.variant == VariantKnightmate)
1708 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1709 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1710 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1711 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1712 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1713 if(board[r][f] == k || board[r][f] == prince) {
1714 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1715 king = board[r][f]; // remember hich one we had
1722 captured = board[rf][ft];
1723 board[rf][ft] = EmptySquare;
1725 captured = board[rt][ft];
1727 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1728 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1731 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1732 board[rt][ft] = board[rf][ff];
1733 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1735 ep = board[EP_STATUS];
1736 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1737 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1738 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1739 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1740 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1741 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1742 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1746 /* For compatibility with ICS wild 9, we scan the board in the
1747 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1748 and we test only whether that one is in check. */
1749 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1750 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1751 if (board[cl.rking][cl.fking] == king) {
1753 if(gameInfo.variant == VariantXiangqi) {
1754 /* [HGM] In Xiangqi opposing Kings means check as well */
1756 dir = (king >= BlackPawn) ? -1 : 1;
1757 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1758 board[i][cl.fking] == EmptySquare; i+=dir );
1759 if(i>=0 && i<BOARD_HEIGHT &&
1760 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1763 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1764 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1765 goto undo_move; /* 2-level break */
1772 if(rf != DROP_RANK) // [HGM] drop
1773 board[rf][ff] = board[rt][ft];
1775 board[rf][ft] = captured;
1776 board[rt][ft] = EmptySquare;
1779 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1780 board[killY][killX = saveKill & 0xFFF] = trampled;
1782 board[rt][ft] = captured;
1784 board[EP_STATUS] = ep;
1787 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1791 HasLion (Board board, int flags)
1793 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1795 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1796 if(board[r][f] == lion) return 1;
1801 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1802 { // [HGM] put drop legality testing in separate routine for clarity
1804 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1805 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1806 n = PieceToNumber(piece);
1807 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1808 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1809 return ImpossibleMove; // piece not available
1810 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1811 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1812 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1813 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1814 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1815 if(piece == WhitePawn || piece == BlackPawn) {
1816 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1817 for(r=1; r<BOARD_HEIGHT-1; r++)
1818 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1819 // should still test if we mate with this Pawn
1821 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1822 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1824 if( (piece == WhitePawn || piece == BlackPawn) &&
1825 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1826 return IllegalMove; /* no pawn drops on 1st/8th */
1828 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1829 if (!(flags & F_IGNORE_CHECK) &&
1830 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1831 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1834 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1835 int rf, int ff, int rt, int ft,
1839 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1841 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1843 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1844 cl->captures++; // [HGM] losers: count legal captures
1845 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1850 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1852 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1854 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1855 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1856 piece = filterPiece = board[rf][ff];
1857 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1859 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1860 /* (perhaps we should disallow moves that obviously leave us in check?) */
1861 if((piece == WhiteFalcon || piece == BlackFalcon ||
1862 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1863 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1867 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1868 cl.ft = fFilter = ft;
1869 cl.kind = IllegalMove;
1870 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1871 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1872 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1873 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1874 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1875 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1877 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1878 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1879 if(board[rf][ff] < BlackPawn) { // white
1880 if(rf != 0) return IllegalMove; // must be on back rank
1881 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1882 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1883 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1884 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1886 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1887 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1888 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1889 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1890 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1893 if(gameInfo.variant == VariantChu) {
1894 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1895 if(promoChar != '+')
1896 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1897 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1898 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1899 return ImpossibleMove;
1901 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1903 if(gameInfo.variant == VariantShogi) {
1904 /* [HGM] Shogi promotions. '=' means defer */
1905 if(rf != DROP_RANK && cl.kind == NormalMove) {
1906 ChessSquare piece = board[rf][ff];
1907 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1909 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1910 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1911 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1912 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1913 promoChar = '+'; // allowed ICS notations
1914 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1915 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1916 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1917 else if(flags & F_WHITE_ON_MOVE) {
1918 if( (int) piece < (int) WhiteWazir &&
1919 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1920 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1921 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1922 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1923 else /* promotion optional, default is defer */
1924 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1925 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1927 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1928 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1929 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1930 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1931 else /* promotion optional, default is defer */
1932 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1933 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1937 if (promoChar != NULLCHAR) {
1938 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1939 ChessSquare piece = board[rf][ff];
1940 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1941 // should test if in zone, really
1942 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1944 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1946 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1947 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1948 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1949 if(piece == EmptySquare)
1950 cl.kind = ImpossibleMove; // non-existing piece
1951 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1952 cl.kind = IllegalMove; // no two Lions
1953 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1954 if(promoChar != PieceToChar(BlackKing)) {
1955 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1956 if(piece == BlackLance) cl.kind = ImpossibleMove;
1957 } else { // promotion to King allowed only if we do not have two yet
1958 int r, f, kings = 0;
1959 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1960 if(kings == 2) cl.kind = IllegalMove;
1962 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1963 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1964 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1965 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1966 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1967 cl.kind = IllegalMove; // promotion to King usually not allowed
1969 cl.kind = IllegalMove;
1979 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1980 int rf, int ff, int rt, int ft,
1984 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1986 register MateTestClosure *cl = (MateTestClosure *) closure;
1991 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1993 MateTest (Board board, int flags)
1996 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1997 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1999 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
2000 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
2001 nrKing += (board[r][f] == king); // stm has king
2002 if( board[r][f] != EmptySquare ) {
2003 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
2008 switch(gameInfo.variant) { // [HGM] losers: extinction wins
2009 case VariantShatranj:
2010 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2014 if(nrKing == 0) return MT_NOKING;
2017 if(myPieces == 1) return MT_BARE;
2020 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2021 // [HGM] 3check: yet to do!
2023 return inCheck ? MT_CHECK : MT_NONE;
2025 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2026 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2027 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2028 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2029 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2030 if(board[n][holdings] != EmptySquare) {
2031 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2032 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2035 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2036 return myPieces == hisPieces ? MT_STALEMATE :
2037 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2038 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2039 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2041 return inCheck ? MT_CHECKMATE
2042 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2043 MT_STAINMATE : MT_STALEMATE;
2048 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2049 int rf, int ff, int rt, int ft,
2053 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2055 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2056 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2057 extern int kifu; // in parser.c
2059 // [HGM] wild: for wild-card pieces rt and rf are dummies
2060 if(piece == WhiteFalcon || piece == BlackFalcon ||
2061 piece == WhiteCobra || piece == BlackCobra)
2062 wildCard = !pieceDefs; // no wildcards when engine defined pieces
2064 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2065 || PieceToChar(board[rf][ff]) == '~'
2066 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2068 (cl->rfIn == -1 || cl->rfIn == rf) &&
2069 (cl->ffIn == -1 || cl->ffIn == ff) &&
2070 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2071 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2073 if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
2075 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2076 int this = 1, other = 1;
2077 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2078 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2079 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2080 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2081 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2082 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2083 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2084 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2088 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2089 // [HGM] oneclick: if multiple moves, be sure we remember capture
2090 cl->piece = board[rf][ff];
2093 cl->rt = wildCard ? cl->rtIn : rt;
2094 cl->ft = wildCard ? cl->ftIn : ft;
2097 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2102 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2104 int illegal = 0; char c = closure->promoCharIn;
2106 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2107 closure->count = closure->captures = 0;
2108 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2109 closure->kind = ImpossibleMove;
2110 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2111 fFilter = closure->ftIn;
2112 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2113 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2114 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2115 closure->count = closure->captures = 0;
2116 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2117 closure->kind = ImpossibleMove;
2118 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2121 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2122 if (closure->count == 0) {
2123 /* See if it's an illegal move due to check */
2125 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2126 if (closure->count == 0) {
2127 /* No, it's not even that */
2128 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2129 int f, r; // if there is only a single piece of the requested type on the board, use that
2130 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2131 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2132 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2133 if(closure->count > 1) illegal = 0; // ambiguous
2135 if(closure->count == 0) {
2136 if (appData.debugMode) { int i, j;
2137 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2138 for(j=0; j<BOARD_WIDTH; j++)
2139 fprintf(debugFP, "%3d", (int) board[i][j]);
2140 fprintf(debugFP, "\n");
2146 } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
2147 DisambiguateClosure spare = *closure;
2148 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2149 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2150 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2154 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2155 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2156 if(closure->piece < BlackPawn) { // white
2157 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2158 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2159 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2160 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2161 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2163 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2164 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2165 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2166 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2167 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2170 if(gameInfo.variant == VariantChu) {
2171 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2173 if(gameInfo.variant == VariantShogi) {
2174 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2175 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2176 ChessSquare piece = closure->piece;
2177 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2178 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2179 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2180 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2181 c = '+'; // allowed ICS notations
2182 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2183 else if(flags & F_WHITE_ON_MOVE) {
2184 if( (int) piece < (int) WhiteWazir &&
2185 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2186 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2187 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2188 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2189 else /* promotion optional, default is defer */
2190 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2191 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2193 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2194 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2195 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2196 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2197 else /* promotion optional, default is defer */
2198 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2199 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2202 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2203 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2205 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2206 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2207 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2208 gameInfo.variant == VariantMakruk)
2209 c = PieceToChar(BlackFerz);
2210 else if(gameInfo.variant == VariantASEAN)
2211 c = PieceToChar(BlackRook);
2212 else if(gameInfo.variant == VariantGreat)
2213 c = PieceToChar(BlackMan);
2214 else if(gameInfo.variant == VariantGrand)
2215 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2217 c = PieceToChar(BlackQueen);
2218 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2219 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2220 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2221 ChessSquare p = closure->piece;
2222 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2223 closure->kind = ImpossibleMove; // used on non-promotable piece
2224 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2225 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2227 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2228 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2229 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2230 if (closure->count > 1) {
2231 closure->kind = AmbiguousMove;
2234 /* Note: If more than one illegal move matches, but no legal
2235 moves, we return IllegalMove, not AmbiguousMove. Caller
2236 can look at closure->count to detect this.
2238 closure->kind = IllegalMove;
2252 } CoordsToAlgebraicClosure;
2254 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2255 ChessMove kind, int rf, int ff,
2256 int rt, int ft, VOIDSTAR closure));
2259 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2261 register CoordsToAlgebraicClosure *cl =
2262 (CoordsToAlgebraicClosure *) closure;
2264 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2265 (board[rf][ff] == cl->piece
2266 || PieceToChar(board[rf][ff]) == '~' &&
2267 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2271 cl->kind = kind; /* this is the move we want */
2273 cl->file++; /* need file to rule out this move */
2277 cl->rank++; /* need rank to rule out this move */
2279 cl->either++; /* rank or file will rule out this move */
2285 /* Convert coordinates to normal algebraic notation.
2286 promoChar must be NULLCHAR or 'x' if not a promotion.
2289 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2293 char *outp = out, c, capture;
2294 CoordsToAlgebraicClosure cl;
2296 if (rf == DROP_RANK) {
2297 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2298 /* Bughouse piece drop */
2299 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2304 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2306 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2309 if (promoChar == 'x') promoChar = NULLCHAR;
2310 piece = board[rf][ff];
2311 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2316 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2317 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2318 /* Keep short notation if move is illegal only because it
2319 leaves the player in check, but still return IllegalMove */
2320 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2321 if (kind == IllegalMove) break;
2326 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2327 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2328 /* Non-capture; use style "e5" */
2331 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2333 /* Capture; use style "exd5" */
2335 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2339 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2341 /* Use promotion suffix style "=Q" */
2343 if (promoChar != NULLCHAR) {
2344 if(IS_SHOGI(gameInfo.variant)) {
2345 /* [HGM] ... but not in Shogi! */
2346 *outp++ = promoChar == '=' ? '=' : '+';
2349 *outp++ = ToUpper(promoChar);
2358 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2359 /* Code added by Tord: FRC castling. */
2360 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2361 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2363 safeStrCpy(out, "O-O", MOVE_LEN);
2365 safeStrCpy(out, "O-O-O", MOVE_LEN);
2366 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2368 /* End of code added by Tord */
2369 /* Test for castling or ICS wild castling */
2370 /* Use style "O-O" (oh-oh) for PGN compatibility */
2371 else if (rf == rt &&
2372 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2373 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2374 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2375 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2376 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2377 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2379 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2381 /* This notation is always unambiguous, unless there are
2382 kings on both the d and e files, with "wild castling"
2383 possible for the king on the d file and normal castling
2384 possible for the other. ICS rules for wild 9
2385 effectively make castling illegal for either king in
2386 this situation. So I am not going to worry about it;
2387 I'll just generate an ambiguous O-O in this case.
2389 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2392 /* else fall through */
2397 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2398 cl.ft = fFilter = ft;
2400 cl.kind = IllegalMove;
2401 cl.rank = cl.file = cl.either = 0;
2402 c = PieceToChar(piece) ;
2403 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2405 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2406 /* Generate pretty moves for moving into check, but
2407 still return IllegalMove.
2409 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2410 if (cl.kind == IllegalMove) break;
2411 cl.kind = IllegalMove;
2414 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2415 else "Ngf3" or "Ngxf7",
2416 else "N1f3" or "N5xf7",
2417 else "Ng1f3" or "Ng5xf7".
2419 if( c == '~' || c == '+') {
2420 /* [HGM] print nonexistent piece as its demoted version */
2421 piece = (ChessSquare) (CHUDEMOTED(piece));
2423 if(c=='+') *outp++ = c;
2424 *outp++ = ToUpper(PieceToChar(piece));
2425 if(*outp = PieceSuffix(piece)) outp++;
2427 if (cl.file || (cl.either && !cl.rank)) {
2433 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2436 if(board[rt][ft] != EmptySquare)
2442 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2443 if (IS_SHOGI(gameInfo.variant)) {
2444 /* [HGM] in Shogi non-pawns can promote */
2445 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2447 else if (gameInfo.variant == VariantChuChess && promoChar ||
2448 gameInfo.variant != VariantSuper && promoChar &&
2449 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2451 *outp++ = ToUpper(promoChar);
2453 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2455 *outp++ = ToUpper(promoChar);
2461 /* Moving a nonexistent piece */
2465 /* Not a legal move, even ignoring check.
2466 If there was a piece on the from square,
2467 use style "Ng1g3" or "Ng1xe8";
2468 if there was a pawn or nothing (!),
2469 use style "g1g3" or "g1xe8". Use "x"
2470 if a piece was on the to square, even
2471 a piece of the same color.
2475 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2477 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2478 c += (board[r][f] == piece); // count on-board pieces of given type
2479 *outp = PieceToChar(piece);
2480 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2481 *outp++ = ToUpper(PieceToChar(piece));
2482 if(*outp = PieceSuffix(piece)) outp++;
2484 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2488 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2490 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2494 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2495 /* Use promotion suffix style "=Q" */
2496 if (promoChar != NULLCHAR && promoChar != 'x') {
2498 *outp++ = ToUpper(promoChar);
2505 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2514 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2516 int preyStackPointer, chaseStackPointer;
2519 unsigned char rf, ff, rt, ft;
2523 unsigned char rank, file;
2529 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2531 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2532 int rf, int ff, int rt, int ft,
2536 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2537 { // For adding captures that can lead to chase indictment to the chaseStack
2538 if(board[rt][ft] == EmptySquare) return; // non-capture
2539 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2540 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2541 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2542 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2543 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2544 chaseStack[chaseStackPointer].rf = rf;
2545 chaseStack[chaseStackPointer].ff = ff;
2546 chaseStack[chaseStackPointer].rt = rt;
2547 chaseStack[chaseStackPointer].ft = ft;
2548 chaseStackPointer++;
2551 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2552 int rf, int ff, int rt, int ft,
2556 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2557 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2559 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2561 if(board[rt][ft] == EmptySquare) return; // no capture
2562 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2563 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2565 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2566 for(i=0; i<chaseStackPointer; i++) {
2567 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2568 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2569 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2570 chaseStack[i] = chaseStack[--chaseStackPointer];
2576 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2577 int rf, int ff, int rt, int ft,
2581 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2582 { // for determining if a piece (given through the closure) is protected
2583 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2585 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2586 if(appData.debugMode && board[rt][ft] != EmptySquare)
2587 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2590 extern char moveList[MAX_MOVES][MOVE_LEN];
2593 PerpetualChase (int first, int last)
2594 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2597 ChessSquare captured;
2599 preyStackPointer = 0; // clear stack of chased pieces
2600 for(i=first; i<last; i+=2) { // for all positions with same side to move
2601 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2602 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2603 // determine all captures possible after the move, and put them on chaseStack
2604 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2605 if(appData.debugMode) { int n;
2606 for(n=0; n<chaseStackPointer; n++)
2607 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2608 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2609 fprintf(debugFP, ": all capts\n");
2611 // determine all captures possible before the move, and delete them from chaseStack
2612 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2613 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2614 cl.rt = moveList[i][3]-ONE;
2615 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2616 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2617 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2618 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2619 if(appData.debugMode) { int n;
2620 for(n=0; n<chaseStackPointer; n++)
2621 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2622 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2623 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2625 // chaseSack now contains all captures made possible by the move
2626 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2627 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2628 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2630 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2631 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2633 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2634 continue; // C or H attack on R is always chase; leave on chaseStack
2636 if(attacker == victim) {
2637 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2638 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2639 // we can capture back with equal piece, so this is no chase but a sacrifice
2640 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2641 j--; /* ! */ continue;
2646 // the attack is on a lower piece, or on a pinned or blocked equal one
2647 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2648 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2649 // test if the victim is protected by a true protector. First make the capture.
2650 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2651 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2652 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2653 // Then test if the opponent can recapture
2654 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2655 cl.rt = chaseStack[j].rt;
2656 cl.ft = chaseStack[j].ft;
2657 if(appData.debugMode) {
2658 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2660 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2661 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2662 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2663 // unmake the capture
2664 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2665 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2666 // if a recapture was found, piece is protected, and we are not chasing it.
2667 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2668 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2672 // chaseStack now contains all moves that chased
2673 if(appData.debugMode) { int n;
2674 for(n=0; n<chaseStackPointer; n++)
2675 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2676 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2677 fprintf(debugFP, ": chases\n");
2679 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2680 for(j=0; j<chaseStackPointer; j++) {
2681 preyStack[j].rank = chaseStack[j].rt;
2682 preyStack[j].file = chaseStack[j].ft;
2684 preyStackPointer = chaseStackPointer;
2687 for(j=0; j<chaseStackPointer; j++) {
2688 for(k=0; k<preyStackPointer; k++) {
2689 // search the victim of each chase move on the preyStack (first occurrence)
2690 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2691 if(k < tail) break; // piece was already identified as still being chased
2692 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2693 preyStack[tail] = preyStack[k]; // by swapping
2694 preyStack[k] = preyStack[preyStackPointer];
2700 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2701 if(appData.debugMode) { int n;
2702 for(n=0; n<preyStackPointer; n++)
2703 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2704 fprintf(debugFP, "always chased upto ply %d\n", i);
2706 // now adjust the location of the chased pieces according to opponent move
2707 for(j=0; j<preyStackPointer; j++) {
2708 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2709 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2710 preyStack[j].rank = moveList[i+1][3]-ONE;
2711 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2716 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2717 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the