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 || gameInfo.variant == VariantSpartan) 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) continue;
1115 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1116 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1118 Rook(board, flags, rf, ff, callback, closure);
1121 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1122 case SHOGI WhiteDragon:
1123 case SHOGI BlackDragon:
1124 case SHOGI WhitePDragon:
1125 case SHOGI BlackPDragon:
1127 Rook(board, flags, rf, ff, callback, closure);
1128 Ferz(board, flags, rf, ff, callback, closure);
1132 /* Capablanca Chancellor sets flag to continue as Knight */
1135 Rook(board, flags, rf, ff, callback, closure);
1136 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1137 Ferz(board, flags, rf, ff, callback, closure);
1139 Knight(board, flags, rf, ff, callback, closure);
1144 for (d = 0; d <= 1; d++) // Dababba moves
1145 for (s = -2; s <= 2; s += 4) {
1147 ft = ff + s * (1 - d);
1148 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1149 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1150 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1152 Wazir(board, flags, rf, ff, callback, closure);
1155 /* Shogi Rooks are ordinary Rooks */
1156 case SHOGI WhiteRook:
1157 case SHOGI BlackRook:
1158 case SHOGI WhitePRook:
1159 case SHOGI BlackPRook:
1162 Rook(board, flags, rf, ff, callback, closure);
1167 case SHOGI WhiteMother:
1168 case SHOGI BlackMother:
1170 Rook(board, flags, rf, ff, callback, closure);
1171 Bishop(board, flags, rf, ff, callback, closure);
1174 case SHOGI WhitePawn:
1175 StepForward(board, flags, rf, ff, callback, closure);
1178 case SHOGI BlackPawn:
1179 StepBackward(board, flags, rf, ff, callback, closure);
1183 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1184 case SHOGI WhiteFerz:
1185 Ferz(board, flags, rf, ff, callback, closure);
1186 StepForward(board, flags, rf, ff, callback, closure);
1190 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1191 case SHOGI BlackFerz:
1192 StepBackward(board, flags, rf, ff, callback, closure);
1196 if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
1197 int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
1198 int ft = BOARD_WIDTH>>1;
1199 if(!SameColor(board[rf][ff], board[rt][ft]))
1200 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1202 /* [HGM] support Shatranj pieces */
1203 Ferz(board, flags, rf, ff, callback, closure);
1208 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1211 case SHOGI WhiteMonarch:
1212 case SHOGI BlackMonarch:
1213 case SHOGI WhiteKing:
1214 case SHOGI BlackKing:
1217 Ferz(board, flags, rf, ff, callback, closure);
1218 Wazir(board, flags, rf, ff, callback, closure);
1221 case WhiteNightrider:
1222 case BlackNightrider:
1223 for (i = -1; i <= 1; i += 2)
1224 for (j = -1; j <= 1; j += 2)
1225 for (s = 1; s <= 2; s++) { int k;
1228 ft = ff + k*j*(3-s);
1229 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1230 if (SameColor(board[rf][ff], board[rt][ft])) break;
1231 callback(board, flags, NormalMove,
1232 rf, ff, rt, ft, closure);
1233 if (board[rt][ft] != EmptySquare) break;
1239 Bishop(board, flags, rf, ff, callback, closure);
1240 Rook(board, flags, rf, ff, callback, closure);
1241 Knight(board, flags, rf, ff, callback, closure);
1244 // Use Lance as Berolina / Spartan Pawn.
1246 if(gameInfo.variant == VariantSuper) goto Amazon;
1247 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1248 callback(board, flags,
1249 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1250 rf, ff, rf + 1, ff, closure);
1251 for (s = -1; s <= 1; s += 2) {
1252 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1253 callback(board, flags,
1254 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1255 rf, ff, rf + 1, ff + s, closure);
1256 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1257 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1262 if(gameInfo.variant == VariantSuper) goto Amazon;
1263 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1264 callback(board, flags,
1265 rf <= promoRank ? BlackPromotion : NormalMove,
1266 rf, ff, rf - 1, ff, closure);
1267 for (s = -1; s <= 1; s += 2) {
1268 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1269 callback(board, flags,
1270 rf <= promoRank ? BlackPromotion : NormalMove,
1271 rf, ff, rf - 1, ff + s, closure);
1272 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1273 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1277 case SHOGI WhiteNothing:
1278 case SHOGI BlackNothing:
1279 case SHOGI WhiteLion:
1280 case SHOGI BlackLion:
1283 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1284 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1285 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1286 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1287 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1288 rf, ff, rt, ft, closure);
1293 case SHOGI WhiteDagger:
1294 case SHOGI BlackDagger:
1295 case SHOGI WhitePDagger:
1296 case SHOGI BlackPDagger:
1297 SlideSideways(board, flags, rf, ff, callback, closure);
1298 StepVertical(board, flags, rf, ff, callback, closure);
1301 case SHOGI WhiteCobra:
1302 case SHOGI BlackCobra:
1303 StepVertical(board, flags, rf, ff, callback, closure);
1306 case SHOGI (PROMO WhiteFerz):
1307 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1308 case SHOGI (PROMO BlackFerz):
1309 if(gameInfo.variant == VariantShogi) goto BlackGold;
1310 case SHOGI WhiteSword:
1311 case SHOGI BlackSword:
1312 case SHOGI WhitePSword:
1313 case SHOGI BlackPSword:
1314 SlideVertical(board, flags, rf, ff, callback, closure);
1315 StepSideways(board, flags, rf, ff, callback, closure);
1318 case SHOGI WhiteCat:
1319 case SHOGI BlackCat:
1320 Ferz(board, flags, rf, ff, callback, closure);
1321 StepVertical(board, flags, rf, ff, callback, closure);
1324 case SHOGI WhiteCopper:
1325 StepDiagForward(board, flags, rf, ff, callback, closure);
1326 StepVertical(board, flags, rf, ff, callback, closure);
1329 case SHOGI BlackCopper:
1330 StepDiagBackward(board, flags, rf, ff, callback, closure);
1331 StepVertical(board, flags, rf, ff, callback, closure);
1334 case SHOGI WhiteHCrown:
1335 case SHOGI BlackHCrown:
1336 Bishop(board, flags, rf, ff, callback, closure);
1337 SlideSideways(board, flags, rf, ff, callback, closure);
1340 case SHOGI WhiteCrown:
1341 case SHOGI BlackCrown:
1342 Bishop(board, flags, rf, ff, callback, closure);
1343 SlideVertical(board, flags, rf, ff, callback, closure);
1346 case SHOGI WhiteUnicorn:
1347 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1348 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1349 if(killX >= 0) break;
1350 Bishop(board, flags, rf, ff, callback, closure);
1351 SlideSideways(board, flags, rf, ff, callback, closure);
1352 SlideBackward(board, flags, rf, ff, callback, closure);
1355 case SHOGI BlackUnicorn:
1356 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1357 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1358 if(killX >= 0) break;
1359 Bishop(board, flags, rf, ff, callback, closure);
1360 SlideSideways(board, flags, rf, ff, callback, closure);
1361 SlideForward(board, flags, rf, ff, callback, closure);
1364 case SHOGI WhiteFalcon:
1365 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1366 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1367 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1368 if(killX >= 0) break;
1369 Rook(board, flags, rf, ff, callback, closure);
1370 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1373 case SHOGI BlackFalcon:
1374 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1375 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1376 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1377 if(killX >= 0) break;
1378 Rook(board, flags, rf, ff, callback, closure);
1379 SlideDiagForward(board, flags, rf, ff, callback, closure);
1382 case SHOGI WhiteDolphin:
1383 case SHOGI BlackHorse:
1384 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1385 SlideVertical(board, flags, rf, ff, callback, closure);
1388 case SHOGI BlackDolphin:
1389 case SHOGI WhiteHorse:
1390 SlideDiagForward(board, flags, rf, ff, callback, closure);
1391 SlideVertical(board, flags, rf, ff, callback, closure);
1394 case SHOGI WhiteLance:
1395 SlideForward(board, flags, rf, ff, callback, closure);
1398 case SHOGI BlackLance:
1399 SlideBackward(board, flags, rf, ff, callback, closure);
1402 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1406 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1419 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1420 Board xqCheckers, nullBoard;
1422 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1423 int rf, int ff, int rt, int ft,
1427 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1429 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1431 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1433 if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1435 if (!(flags & F_IGNORE_CHECK) ) {
1436 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1439 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1440 kings += (board[r][f] == BlackKing);
1444 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1446 check = CheckTest(board, flags, rf, ff, rt, ft,
1447 kind == WhiteCapturesEnPassant ||
1448 kind == BlackCapturesEnPassant);
1449 if(promo) board[rf][ff] = BlackLance;
1452 if (flags & F_ATOMIC_CAPTURE) {
1453 if (board[rt][ft] != EmptySquare ||
1454 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1456 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1457 if (board[rf][ff] == king) return;
1458 for (r = rt-1; r <= rt+1; r++) {
1459 for (f = ft-1; f <= ft+1; f++) {
1460 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1461 board[r][f] == king) return;
1466 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1473 int captures; // [HGM] losers
1474 } LegalityTestClosure;
1477 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1478 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1479 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1480 moves that would destroy your own king. The CASTLE_OK flags are
1481 true if castling is not yet ruled out by a move of the king or
1482 rook. Return TRUE if the player on move is currently in check and
1483 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1485 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1488 int ff, ft, k, left, right, swap;
1489 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1490 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1491 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1496 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1497 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1498 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1500 if (inCheck) return TRUE;
1502 /* Generate castling moves */
1503 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1504 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1507 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1508 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1510 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1511 if ((flags & F_WHITE_ON_MOVE) &&
1512 (flags & F_WHITE_KCASTLE_OK) &&
1513 board[0][ff] == wKing &&
1514 board[0][ff + 1] == EmptySquare &&
1515 board[0][ff + 2] == EmptySquare &&
1516 board[0][BOARD_RGHT-3] == EmptySquare &&
1517 board[0][BOARD_RGHT-2] == EmptySquare &&
1518 board[0][BOARD_RGHT-1] == WhiteRook &&
1519 castlingRights[0] != NoRights && /* [HGM] check rights */
1520 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1522 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1523 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1524 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1525 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1527 callback(board, flags,
1528 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1529 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1531 if ((flags & F_WHITE_ON_MOVE) &&
1532 (flags & F_WHITE_QCASTLE_OK) &&
1533 board[0][ff] == wKing &&
1534 board[0][ff - 1] == EmptySquare &&
1535 board[0][ff - 2] == EmptySquare &&
1536 board[0][BOARD_LEFT+2] == EmptySquare &&
1537 board[0][BOARD_LEFT+1] == EmptySquare &&
1538 board[0][BOARD_LEFT+0] == WhiteRook &&
1539 castlingRights[1] != NoRights && /* [HGM] check rights */
1540 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1542 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1543 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1544 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1546 callback(board, flags,
1547 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1548 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1550 if (!(flags & F_WHITE_ON_MOVE) &&
1551 (flags & F_BLACK_KCASTLE_OK) &&
1552 board[BOARD_HEIGHT-1][ff] == bKing &&
1553 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1554 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1555 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1556 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1557 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1558 castlingRights[3] != NoRights && /* [HGM] check rights */
1559 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1561 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1562 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1563 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1564 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1566 callback(board, flags,
1567 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1568 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1570 if (!(flags & F_WHITE_ON_MOVE) &&
1571 (flags & F_BLACK_QCASTLE_OK) &&
1572 board[BOARD_HEIGHT-1][ff] == bKing &&
1573 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1574 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1575 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1576 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1577 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1578 castlingRights[4] != NoRights && /* [HGM] check rights */
1579 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1581 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1582 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1583 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1585 callback(board, flags,
1586 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1587 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1591 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1593 /* generate all potential FRC castling moves (KxR), ignoring flags */
1594 /* [HGM] test if the Rooks we find have castling rights */
1595 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1598 if ((flags & F_WHITE_ON_MOVE) != 0) {
1599 ff = castlingRights[2]; /* King file if we have any rights */
1600 if(ff != NoRights && board[0][ff] == WhiteKing) {
1601 if (appData.debugMode) {
1602 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1603 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1605 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1607 right = BOARD_RGHT-2;
1608 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1609 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1610 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1611 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1612 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1613 if(ft != NoRights && board[0][ft] == WhiteRook) {
1614 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1615 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1618 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1619 left = BOARD_LEFT+2;
1621 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1622 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1623 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1624 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1625 if(ff > BOARD_LEFT+2)
1626 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1627 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1628 if(ft != NoRights && board[0][ft] == WhiteRook) {
1629 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1630 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1634 ff = castlingRights[5]; /* King file if we have any rights */
1635 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1636 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1638 right = BOARD_RGHT-2;
1639 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1640 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1641 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1642 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1643 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1644 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1645 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1646 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1649 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1650 left = BOARD_LEFT+2;
1652 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1653 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1654 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1655 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1656 if(ff > BOARD_LEFT+2)
1657 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1658 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1659 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1660 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1661 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1678 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1679 int rf, int ff, int rt, int ft,
1684 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1686 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1688 if (rt == cl->rking && ft == cl->fking) {
1689 if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1691 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1693 if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1694 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1695 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1699 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1700 he leave himself in check? Or if rf == -1, is the player on move
1701 in check now? enPassant must be TRUE if the indicated move is an
1702 e.p. capture. The possibility of castling out of a check along the
1703 back rank is not accounted for (i.e., we still return nonzero), as
1704 this is illegal anyway. Return value is the number of times the
1705 king is in check. */
1707 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1709 CheckTestClosure cl;
1710 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1711 ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1712 int saveKill = killX;
1713 /* Suppress warnings on uninitialized variables */
1715 if(gameInfo.variant == VariantXiangqi)
1716 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1717 if(gameInfo.variant == VariantKnightmate)
1718 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1719 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1720 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1721 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1722 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1723 if(board[r][f] == k || board[r][f] == prince) {
1724 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1725 king = board[r][f]; // remember hich one we had
1732 captured = board[rf][ft];
1733 board[rf][ft] = EmptySquare;
1735 captured = board[rt][ft];
1737 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1738 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1741 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1742 board[rt][ft] = board[rf][ff];
1743 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1745 ep = board[EP_STATUS];
1746 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1747 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1748 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1749 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1750 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1751 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1752 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1756 /* For compatibility with ICS wild 9, we scan the board in the
1757 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1758 and we test only whether that one is in check. */
1759 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1760 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1761 if (board[cl.rking][cl.fking] == king) {
1763 if(gameInfo.variant == VariantXiangqi) {
1764 /* [HGM] In Xiangqi opposing Kings means check as well */
1766 dir = (king >= BlackPawn) ? -1 : 1;
1767 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1768 board[i][cl.fking] == EmptySquare; i+=dir );
1769 if(i>=0 && i<BOARD_HEIGHT &&
1770 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1773 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1774 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1775 goto undo_move; /* 2-level break */
1782 if(rf != DROP_RANK) // [HGM] drop
1783 board[rf][ff] = board[rt][ft];
1785 board[rf][ft] = captured;
1786 board[rt][ft] = EmptySquare;
1789 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1790 board[killY][killX = saveKill & 0xFFF] = trampled;
1792 board[rt][ft] = captured;
1794 board[EP_STATUS] = ep;
1797 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1801 HasLion (Board board, int flags)
1803 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1805 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1806 if(board[r][f] == lion) return 1;
1811 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1812 { // [HGM] put drop legality testing in separate routine for clarity
1814 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1815 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1816 n = PieceToNumber(piece);
1817 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1818 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1819 return ImpossibleMove; // piece not available
1820 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1821 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1822 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1823 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1824 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1825 if(piece == WhitePawn || piece == BlackPawn) {
1826 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1827 for(r=1; r<BOARD_HEIGHT-1; r++)
1828 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1829 // should still test if we mate with this Pawn
1831 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1832 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1834 if( (piece == WhitePawn || piece == BlackPawn) &&
1835 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1836 return IllegalMove; /* no pawn drops on 1st/8th */
1838 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1839 if (!(flags & F_IGNORE_CHECK) &&
1840 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1841 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1844 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1845 int rf, int ff, int rt, int ft,
1849 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1851 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1853 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1854 cl->captures++; // [HGM] losers: count legal captures
1855 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1860 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1862 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1864 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1865 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1866 piece = filterPiece = board[rf][ff];
1867 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1869 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1870 /* (perhaps we should disallow moves that obviously leave us in check?) */
1871 if((piece == WhiteFalcon || piece == BlackFalcon ||
1872 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1873 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1877 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1878 cl.ft = fFilter = ft;
1879 cl.kind = IllegalMove;
1880 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1881 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1882 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1883 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1884 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1885 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1887 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1888 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1889 if(board[rf][ff] < BlackPawn) { // white
1890 if(rf != 0) return IllegalMove; // must be on back rank
1891 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1892 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1893 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1894 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1896 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1897 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1898 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1899 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1900 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1903 if(gameInfo.variant == VariantChu) {
1904 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1905 if(promoChar != '+')
1906 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1907 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1908 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1909 return ImpossibleMove;
1911 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1913 if(gameInfo.variant == VariantShogi) {
1914 /* [HGM] Shogi promotions. '=' means defer */
1915 if(rf != DROP_RANK && cl.kind == NormalMove) {
1916 ChessSquare piece = board[rf][ff];
1917 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1919 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1920 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1921 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1922 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1923 promoChar = '+'; // allowed ICS notations
1924 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1925 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1926 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1927 else if(flags & F_WHITE_ON_MOVE) {
1928 if( (int) piece < (int) WhiteWazir &&
1929 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1930 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1931 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1932 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1933 else /* promotion optional, default is defer */
1934 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1935 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1937 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1938 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1939 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1940 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1941 else /* promotion optional, default is defer */
1942 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1943 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1947 if (promoChar != NULLCHAR) {
1948 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1949 ChessSquare piece = board[rf][ff];
1950 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1951 // should test if in zone, really
1952 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1954 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1956 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1957 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1958 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1959 if(piece == EmptySquare)
1960 cl.kind = ImpossibleMove; // non-existing piece
1961 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1962 cl.kind = IllegalMove; // no two Lions
1963 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1964 if(promoChar != PieceToChar(BlackKing)) {
1965 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1966 if(piece == BlackLance) cl.kind = ImpossibleMove;
1967 } else { // promotion to King allowed only if we do not have two yet
1968 int r, f, kings = 0;
1969 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1970 if(kings == 2) cl.kind = IllegalMove;
1972 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1973 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1974 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1975 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1976 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1977 cl.kind = IllegalMove; // promotion to King usually not allowed
1979 cl.kind = IllegalMove;
1989 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1990 int rf, int ff, int rt, int ft,
1994 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1996 register MateTestClosure *cl = (MateTestClosure *) closure;
2001 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
2003 MateTest (Board board, int flags)
2006 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
2007 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
2009 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
2010 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
2011 nrKing += (board[r][f] == king); // stm has king
2012 if( board[r][f] != EmptySquare ) {
2013 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
2018 switch(gameInfo.variant) { // [HGM] losers: extinction wins
2019 case VariantShatranj:
2020 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2024 if(nrKing == 0) return MT_NOKING;
2027 if(myPieces == 1) return MT_BARE;
2030 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2031 // [HGM] 3check: yet to do!
2033 return inCheck ? MT_CHECK : MT_NONE;
2035 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2036 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2037 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2038 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2039 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2040 if(board[n][holdings] != EmptySquare) {
2041 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2042 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2045 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2046 return myPieces == hisPieces ? MT_STALEMATE :
2047 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2048 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2049 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2051 return inCheck ? MT_CHECKMATE
2052 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2053 MT_STAINMATE : MT_STALEMATE;
2058 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2059 int rf, int ff, int rt, int ft,
2063 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2065 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2066 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2067 extern int kifu; // in parser.c
2069 // [HGM] wild: for wild-card pieces rt and rf are dummies
2070 if(piece == WhiteFalcon || piece == BlackFalcon ||
2071 piece == WhiteCobra || piece == BlackCobra)
2072 wildCard = !pieceDefs; // no wildcards when engine defined pieces
2074 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2075 || PieceToChar(board[rf][ff]) == '~'
2076 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2078 (cl->rfIn == -1 || cl->rfIn == rf) &&
2079 (cl->ffIn == -1 || cl->ffIn == ff) &&
2080 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2081 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2083 if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
2085 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2086 int this = 1, other = 1;
2087 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2088 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2089 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2090 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2091 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2092 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2093 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2094 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2098 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2099 // [HGM] oneclick: if multiple moves, be sure we remember capture
2100 cl->piece = board[rf][ff];
2103 cl->rt = wildCard ? cl->rtIn : rt;
2104 cl->ft = wildCard ? cl->ftIn : ft;
2107 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2112 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2114 int illegal = 0; char c = closure->promoCharIn;
2116 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2117 closure->count = closure->captures = 0;
2118 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2119 closure->kind = ImpossibleMove;
2120 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2121 fFilter = closure->ftIn;
2122 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2123 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2124 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2125 closure->count = closure->captures = 0;
2126 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2127 closure->kind = ImpossibleMove;
2128 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2131 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2132 if (closure->count == 0) {
2133 /* See if it's an illegal move due to check */
2135 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2136 if (closure->count == 0) {
2137 /* No, it's not even that */
2138 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2139 int f, r; // if there is only a single piece of the requested type on the board, use that
2140 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2141 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2142 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2143 if(closure->count > 1) illegal = 0; // ambiguous
2145 if(closure->count == 0) {
2146 if (appData.debugMode) { int i, j;
2147 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2148 for(j=0; j<BOARD_WIDTH; j++)
2149 fprintf(debugFP, "%3d", (int) board[i][j]);
2150 fprintf(debugFP, "\n");
2156 } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
2157 DisambiguateClosure spare = *closure;
2158 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2159 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2160 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2164 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2165 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2166 if(closure->piece < BlackPawn) { // white
2167 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2168 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2169 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2170 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2171 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2173 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2174 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2175 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2176 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2177 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2180 if(gameInfo.variant == VariantChu) {
2181 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2183 if(gameInfo.variant == VariantShogi) {
2184 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2185 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2186 ChessSquare piece = closure->piece;
2187 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2188 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2189 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2190 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2191 c = '+'; // allowed ICS notations
2192 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2193 else if(flags & F_WHITE_ON_MOVE) {
2194 if( (int) piece < (int) WhiteWazir &&
2195 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2196 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2197 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2198 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2199 else /* promotion optional, default is defer */
2200 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2201 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2203 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2204 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2205 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2206 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2207 else /* promotion optional, default is defer */
2208 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2209 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2212 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2213 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2215 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2216 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2217 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2218 gameInfo.variant == VariantMakruk)
2219 c = PieceToChar(BlackFerz);
2220 else if(gameInfo.variant == VariantASEAN)
2221 c = PieceToChar(BlackRook);
2222 else if(gameInfo.variant == VariantGreat)
2223 c = PieceToChar(BlackMan);
2224 else if(gameInfo.variant == VariantGrand)
2225 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2227 c = PieceToChar(BlackQueen);
2228 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2229 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2230 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2231 ChessSquare p = closure->piece;
2232 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2233 closure->kind = ImpossibleMove; // used on non-promotable piece
2234 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2235 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2237 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2238 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2239 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2240 if (closure->count > 1) {
2241 closure->kind = AmbiguousMove;
2244 /* Note: If more than one illegal move matches, but no legal
2245 moves, we return IllegalMove, not AmbiguousMove. Caller
2246 can look at closure->count to detect this.
2248 closure->kind = IllegalMove;
2262 } CoordsToAlgebraicClosure;
2264 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2265 ChessMove kind, int rf, int ff,
2266 int rt, int ft, VOIDSTAR closure));
2269 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2271 register CoordsToAlgebraicClosure *cl =
2272 (CoordsToAlgebraicClosure *) closure;
2274 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2275 (board[rf][ff] == cl->piece
2276 || PieceToChar(board[rf][ff]) == '~' &&
2277 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2281 cl->kind = kind; /* this is the move we want */
2283 cl->file++; /* need file to rule out this move */
2287 cl->rank++; /* need rank to rule out this move */
2289 cl->either++; /* rank or file will rule out this move */
2295 /* Convert coordinates to normal algebraic notation.
2296 promoChar must be NULLCHAR or 'x' if not a promotion.
2299 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2303 char *outp = out, c, capture;
2304 CoordsToAlgebraicClosure cl;
2306 if (rf == DROP_RANK) {
2307 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2308 /* Bughouse piece drop */
2309 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2314 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2316 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2319 if (promoChar == 'x') promoChar = NULLCHAR;
2320 piece = board[rf][ff];
2321 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2326 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2327 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2328 /* Keep short notation if move is illegal only because it
2329 leaves the player in check, but still return IllegalMove */
2330 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2331 if (kind == IllegalMove) break;
2336 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2337 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2338 /* Non-capture; use style "e5" */
2341 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2343 /* Capture; use style "exd5" */
2345 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2349 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2351 /* Use promotion suffix style "=Q" */
2353 if (promoChar != NULLCHAR) {
2354 if(IS_SHOGI(gameInfo.variant)) {
2355 /* [HGM] ... but not in Shogi! */
2356 *outp++ = promoChar == '=' ? '=' : '+';
2359 *outp++ = ToUpper(promoChar);
2368 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2369 /* Code added by Tord: FRC castling. */
2370 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2371 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2373 safeStrCpy(out, "O-O", MOVE_LEN);
2375 safeStrCpy(out, "O-O-O", MOVE_LEN);
2376 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2378 /* End of code added by Tord */
2379 /* Test for castling or ICS wild castling */
2380 /* Use style "O-O" (oh-oh) for PGN compatibility */
2381 else if (rf == rt &&
2382 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2383 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2384 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2385 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2386 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2387 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2389 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2391 /* This notation is always unambiguous, unless there are
2392 kings on both the d and e files, with "wild castling"
2393 possible for the king on the d file and normal castling
2394 possible for the other. ICS rules for wild 9
2395 effectively make castling illegal for either king in
2396 this situation. So I am not going to worry about it;
2397 I'll just generate an ambiguous O-O in this case.
2399 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2402 /* else fall through */
2407 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2408 cl.ft = fFilter = ft;
2410 cl.kind = IllegalMove;
2411 cl.rank = cl.file = cl.either = 0;
2412 c = PieceToChar(piece) ;
2413 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2415 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2416 /* Generate pretty moves for moving into check, but
2417 still return IllegalMove.
2419 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2420 if (cl.kind == IllegalMove) break;
2421 cl.kind = IllegalMove;
2424 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2425 else "Ngf3" or "Ngxf7",
2426 else "N1f3" or "N5xf7",
2427 else "Ng1f3" or "Ng5xf7".
2429 if( c == '~' || c == '+') {
2430 /* [HGM] print nonexistent piece as its demoted version */
2431 piece = (ChessSquare) (CHUDEMOTED(piece));
2433 if(c=='+') *outp++ = c;
2434 *outp++ = ToUpper(PieceToChar(piece));
2435 if(*outp = PieceSuffix(piece)) outp++;
2437 if (cl.file || (cl.either && !cl.rank)) {
2443 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2446 if(board[rt][ft] != EmptySquare)
2452 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2453 if (IS_SHOGI(gameInfo.variant)) {
2454 /* [HGM] in Shogi non-pawns can promote */
2455 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2457 else if (gameInfo.variant == VariantChuChess && promoChar ||
2458 gameInfo.variant != VariantSuper && promoChar &&
2459 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2461 *outp++ = ToUpper(promoChar);
2463 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2465 *outp++ = ToUpper(promoChar);
2471 /* Moving a nonexistent piece */
2475 /* Not a legal move, even ignoring check.
2476 If there was a piece on the from square,
2477 use style "Ng1g3" or "Ng1xe8";
2478 if there was a pawn or nothing (!),
2479 use style "g1g3" or "g1xe8". Use "x"
2480 if a piece was on the to square, even
2481 a piece of the same color.
2485 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2487 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2488 c += (board[r][f] == piece); // count on-board pieces of given type
2489 *outp = PieceToChar(piece);
2490 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2491 *outp++ = ToUpper(PieceToChar(piece));
2492 if(*outp = PieceSuffix(piece)) outp++;
2494 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2498 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2500 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2504 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2505 /* Use promotion suffix style "=Q" */
2506 if (promoChar != NULLCHAR && promoChar != 'x') {
2508 *outp++ = ToUpper(promoChar);
2515 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2524 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2526 int preyStackPointer, chaseStackPointer;
2529 unsigned char rf, ff, rt, ft;
2533 unsigned char rank, file;
2539 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2541 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2542 int rf, int ff, int rt, int ft,
2546 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2547 { // For adding captures that can lead to chase indictment to the chaseStack
2548 if(board[rt][ft] == EmptySquare) return; // non-capture
2549 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2550 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2551 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2552 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2553 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2554 chaseStack[chaseStackPointer].rf = rf;
2555 chaseStack[chaseStackPointer].ff = ff;
2556 chaseStack[chaseStackPointer].rt = rt;
2557 chaseStack[chaseStackPointer].ft = ft;
2558 chaseStackPointer++;
2561 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2562 int rf, int ff, int rt, int ft,
2566 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2567 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2569 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2571 if(board[rt][ft] == EmptySquare) return; // no capture
2572 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2573 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2575 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2576 for(i=0; i<chaseStackPointer; i++) {
2577 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2578 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2579 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2580 chaseStack[i] = chaseStack[--chaseStackPointer];
2586 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2587 int rf, int ff, int rt, int ft,
2591 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2592 { // for determining if a piece (given through the closure) is protected
2593 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2595 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2596 if(appData.debugMode && board[rt][ft] != EmptySquare)
2597 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2600 extern char moveList[MAX_MOVES][MOVE_LEN];
2603 PerpetualChase (int first, int last)
2604 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2607 ChessSquare captured;
2609 preyStackPointer = 0; // clear stack of chased pieces
2610 for(i=first; i<last; i+=2) { // for all positions with same side to move
2611 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2612 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2613 // determine all captures possible after the move, and put them on chaseStack
2614 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2615 if(appData.debugMode) { int n;
2616 for(n=0; n<chaseStackPointer; n++)
2617 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2618 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2619 fprintf(debugFP, ": all capts\n");
2621 // determine all captures possible before the move, and delete them from chaseStack
2622 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2623 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2624 cl.rt = moveList[i][3]-ONE;
2625 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2626 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2627 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2628 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2629 if(appData.debugMode) { int n;
2630 for(n=0; n<chaseStackPointer; n++)
2631 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2632 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2633 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2635 // chaseSack now contains all captures made possible by the move
2636 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2637 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2638 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2640 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2641 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2643 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2644 continue; // C or H attack on R is always chase; leave on chaseStack
2646 if(attacker == victim) {
2647 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2648 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2649 // we can capture back with equal piece, so this is no chase but a sacrifice
2650 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2651 j--; /* ! */ continue;
2656 // the attack is on a lower piece, or on a pinned or blocked equal one
2657 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2658 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2659 // test if the victim is protected by a true protector. First make the capture.
2660 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2661 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2662 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2663 // Then test if the opponent can recapture
2664 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2665 cl.rt = chaseStack[j].rt;
2666 cl.ft = chaseStack[j].ft;
2667 if(appData.debugMode) {
2668 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2670 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2671 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2672 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2673 // unmake the capture
2674 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2675 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2676 // if a recapture was found, piece is protected, and we are not chasing it.
2677 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2678 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2682 // chaseStack now contains all moves that chased
2683 if(appData.debugMode) { int n;
2684 for(n=0; n<chaseStackPointer; n++)
2685 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2686 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2687 fprintf(debugFP, ": chases\n");
2689 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2690 for(j=0; j<chaseStackPointer; j++) {
2691 preyStack[j].rank = chaseStack[j].rt;
2692 preyStack[j].file = chaseStack[j].ft;
2694 preyStackPointer = chaseStackPointer;
2697 for(j=0; j<chaseStackPointer; j++) {
2698 for(k=0; k<preyStackPointer; k++) {
2699 // search the victim of each chase move on the preyStack (first occurrence)
2700 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2701 if(k < tail) break; // piece was already identified as still being chased
2702 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2703 preyStack[tail] = preyStack[k]; // by swapping
2704 preyStack[k] = preyStack[preyStackPointer];
2710 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2711 if(appData.debugMode) { int n;
2712 for(n=0; n<preyStackPointer; n++)
2713 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2714 fprintf(debugFP, "always chased upto ply %d\n", i);
2716 // now adjust the location of the chased pieces according to opponent move
2717 for(j=0; j<preyStackPointer; j++) {
2718 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2719 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2720 preyStack[j].rank = moveList[i+1][3]-ONE;
2721 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2726 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2727 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the