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 if(gameInfo.variant == VariantXiangqi) {
990 int palace = (piece == WhiteWazir ? 1 : BOARD_HEIGHT-2); // Palace center
991 if(ff <= BOARD_WIDTH/2 && !SameColor(board[rf][ff+1], piece)) callback(board, flags, NormalMove, rf, ff, rf, ff+1, closure);
992 if(ff >= BOARD_WIDTH/2 && !SameColor(board[rf][ff-1], piece)) callback(board, flags, NormalMove, rf, ff, rf, ff-1, closure);
993 if(rf >= palace && !SameColor(board[rf-1][ff], piece)) callback(board, flags, NormalMove, rf, ff, rf-1, ff, closure);
994 if(rf <= palace && !SameColor(board[rf+1][ff], piece)) callback(board, flags, NormalMove, rf, ff, rf+1, ff, closure);
997 Wazir(board, flags, rf, ff, callback, closure);
1000 case SHOGI WhiteMarshall:
1001 case SHOGI BlackMarshall:
1002 Ferz(board, flags, rf, ff, callback, closure);
1003 for (d = 0; d <= 1; d++)
1004 for (s = -2; s <= 2; s += 4) {
1006 ft = ff + s * (1 - d);
1007 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1008 if (!SameColor(board[rf][ff], board[rt][ft]) )
1009 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1013 case SHOGI WhiteAngel:
1014 case SHOGI BlackAngel:
1015 Wazir(board, flags, rf, ff, callback, closure);
1019 /* [HGM] support Shatranj pieces */
1020 for (rs = -1; rs <= 1; rs += 2)
1021 for (fs = -1; fs <= 1; fs += 2) {
1024 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1025 && ( gameInfo.variant != VariantXiangqi ||
1026 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
1028 && !SameColor(board[rf][ff], board[rt][ft]))
1029 callback(board, flags, NormalMove,
1030 rf, ff, rt, ft, closure);
1031 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1032 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
1033 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
1035 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
1036 && !SameColor(board[rf][ff], board[rt][ft]))
1037 callback(board, flags, NormalMove,
1038 rf, ff, rt, ft, closure);
1040 if(gameInfo.variant == VariantSpartan)
1041 for(fs = -1; fs <= 1; fs += 2) {
1043 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
1044 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
1048 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
1051 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
1052 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1053 for (s = -2; s <= 2; s += 4) {
1055 ft = ff + s * (1 - d);
1056 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1057 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1058 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1061 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
1062 case SHOGI WhiteCardinal:
1063 case SHOGI BlackCardinal:
1064 case SHOGI WhitePCardinal:
1065 case SHOGI BlackPCardinal:
1067 Bishop(board, flags, rf, ff, callback, closure);
1068 Wazir(board, flags, rf, ff, callback, closure);
1071 /* Capablanca Archbishop continues as Knight */
1074 Knight(board, flags, rf, ff, callback, closure);
1076 /* Shogi Bishops are ordinary Bishops */
1077 case SHOGI WhiteBishop:
1078 case SHOGI BlackBishop:
1079 case SHOGI WhitePBishop:
1080 case SHOGI BlackPBishop:
1083 Bishop(board, flags, rf, ff, callback, closure);
1086 /* Shogi Lance is unlike anything, and asymmetric at that */
1087 case SHOGI WhiteQueen:
1088 if(gameInfo.variant == VariantChu) goto doQueen;
1092 if (rt >= BOARD_HEIGHT) break;
1093 if (SameColor(board[rf][ff], board[rt][ft])) break;
1094 callback(board, flags, NormalMove,
1095 rf, ff, rt, ft, closure);
1096 if (board[rt][ft] != EmptySquare) break;
1100 case SHOGI BlackQueen:
1101 if(gameInfo.variant == VariantChu) goto doQueen;
1106 if (SameColor(board[rf][ff], board[rt][ft])) break;
1107 callback(board, flags, NormalMove,
1108 rf, ff, rt, ft, closure);
1109 if (board[rt][ft] != EmptySquare) break;
1113 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
1116 if(gameInfo.variant == VariantChuChess || gameInfo.variant == VariantSpartan) goto DragonKing;
1117 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
1118 for (s = -2; s <= 2; s += 4) {
1120 ft = ff + s * (1 - d);
1121 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1122 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
1123 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1124 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1126 Rook(board, flags, rf, ff, callback, closure);
1129 /* Shogi Dragon King has to continue as Ferz after Rook moves */
1130 case SHOGI WhiteDragon:
1131 case SHOGI BlackDragon:
1132 case SHOGI WhitePDragon:
1133 case SHOGI BlackPDragon:
1135 Rook(board, flags, rf, ff, callback, closure);
1136 Ferz(board, flags, rf, ff, callback, closure);
1140 /* Capablanca Chancellor sets flag to continue as Knight */
1143 Rook(board, flags, rf, ff, callback, closure);
1144 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
1145 Ferz(board, flags, rf, ff, callback, closure);
1147 Knight(board, flags, rf, ff, callback, closure);
1152 for (d = 0; d <= 1; d++) // Dababba moves
1153 for (s = -2; s <= 2; s += 4) {
1155 ft = ff + s * (1 - d);
1156 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1157 if (SameColor(board[rf][ff], board[rt][ft])) continue;
1158 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1160 Wazir(board, flags, rf, ff, callback, closure);
1163 /* Shogi Rooks are ordinary Rooks */
1164 case SHOGI WhiteRook:
1165 case SHOGI BlackRook:
1166 case SHOGI WhitePRook:
1167 case SHOGI BlackPRook:
1170 Rook(board, flags, rf, ff, callback, closure);
1175 case SHOGI WhiteMother:
1176 case SHOGI BlackMother:
1178 Rook(board, flags, rf, ff, callback, closure);
1179 Bishop(board, flags, rf, ff, callback, closure);
1182 case SHOGI WhitePawn:
1183 StepForward(board, flags, rf, ff, callback, closure);
1186 case SHOGI BlackPawn:
1187 StepBackward(board, flags, rf, ff, callback, closure);
1191 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1192 case SHOGI WhiteFerz:
1193 Ferz(board, flags, rf, ff, callback, closure);
1194 StepForward(board, flags, rf, ff, callback, closure);
1198 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
1199 case SHOGI BlackFerz:
1200 StepBackward(board, flags, rf, ff, callback, closure);
1204 if(gameInfo.variant == VariantXiangqi && ff != BOARD_WIDTH>>1) {
1205 int rt = (piece == BlackFerz ? BOARD_HEIGHT-2 : 1);
1206 int ft = BOARD_WIDTH>>1;
1207 if(!SameColor(board[rf][ff], board[rt][ft]))
1208 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
1210 /* [HGM] support Shatranj pieces */
1211 Ferz(board, flags, rf, ff, callback, closure);
1216 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
1219 case SHOGI WhiteMonarch:
1220 case SHOGI BlackMonarch:
1221 case SHOGI WhiteKing:
1222 case SHOGI BlackKing:
1225 Ferz(board, flags, rf, ff, callback, closure);
1226 Wazir(board, flags, rf, ff, callback, closure);
1229 case WhiteNightrider:
1230 case BlackNightrider:
1231 for (i = -1; i <= 1; i += 2)
1232 for (j = -1; j <= 1; j += 2)
1233 for (s = 1; s <= 2; s++) { int k;
1236 ft = ff + k*j*(3-s);
1237 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
1238 if (SameColor(board[rf][ff], board[rt][ft])) break;
1239 callback(board, flags, NormalMove,
1240 rf, ff, rt, ft, closure);
1241 if (board[rt][ft] != EmptySquare) break;
1247 Bishop(board, flags, rf, ff, callback, closure);
1248 Rook(board, flags, rf, ff, callback, closure);
1249 Knight(board, flags, rf, ff, callback, closure);
1252 // Use Lance as Berolina / Spartan Pawn.
1254 if(gameInfo.variant == VariantSuper) goto Amazon;
1255 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
1256 callback(board, flags,
1257 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1258 rf, ff, rf + 1, ff, closure);
1259 for (s = -1; s <= 1; s += 2) {
1260 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
1261 callback(board, flags,
1262 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1263 rf, ff, rf + 1, ff + s, closure);
1264 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1265 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1270 if(gameInfo.variant == VariantSuper) goto Amazon;
1271 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1272 callback(board, flags,
1273 rf <= promoRank ? BlackPromotion : NormalMove,
1274 rf, ff, rf - 1, ff, closure);
1275 for (s = -1; s <= 1; s += 2) {
1276 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1277 callback(board, flags,
1278 rf <= promoRank ? BlackPromotion : NormalMove,
1279 rf, ff, rf - 1, ff + s, closure);
1280 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1281 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1285 case SHOGI WhiteNothing:
1286 case SHOGI BlackNothing:
1287 case SHOGI WhiteLion:
1288 case SHOGI BlackLion:
1291 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1292 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1293 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1294 i = (killX >= 0 && (rt-killY)*(rt-killY) + (killX-ft)*(killX-ft) < 3); legNr += 2*i;
1295 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare && !i ? FirstLeg : NormalMove,
1296 rf, ff, rt, ft, closure);
1301 case SHOGI WhiteDagger:
1302 case SHOGI BlackDagger:
1303 case SHOGI WhitePDagger:
1304 case SHOGI BlackPDagger:
1305 SlideSideways(board, flags, rf, ff, callback, closure);
1306 StepVertical(board, flags, rf, ff, callback, closure);
1309 case SHOGI WhiteCobra:
1310 case SHOGI BlackCobra:
1311 StepVertical(board, flags, rf, ff, callback, closure);
1314 case SHOGI (PROMO WhiteFerz):
1315 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1316 case SHOGI (PROMO BlackFerz):
1317 if(gameInfo.variant == VariantShogi) goto BlackGold;
1318 case SHOGI WhiteSword:
1319 case SHOGI BlackSword:
1320 case SHOGI WhitePSword:
1321 case SHOGI BlackPSword:
1322 SlideVertical(board, flags, rf, ff, callback, closure);
1323 StepSideways(board, flags, rf, ff, callback, closure);
1326 case SHOGI WhiteCat:
1327 case SHOGI BlackCat:
1328 Ferz(board, flags, rf, ff, callback, closure);
1329 StepVertical(board, flags, rf, ff, callback, closure);
1332 case SHOGI WhiteCopper:
1333 StepDiagForward(board, flags, rf, ff, callback, closure);
1334 StepVertical(board, flags, rf, ff, callback, closure);
1337 case SHOGI BlackCopper:
1338 StepDiagBackward(board, flags, rf, ff, callback, closure);
1339 StepVertical(board, flags, rf, ff, callback, closure);
1342 case SHOGI WhiteHCrown:
1343 case SHOGI BlackHCrown:
1344 Bishop(board, flags, rf, ff, callback, closure);
1345 SlideSideways(board, flags, rf, ff, callback, closure);
1348 case SHOGI WhiteCrown:
1349 case SHOGI BlackCrown:
1350 Bishop(board, flags, rf, ff, callback, closure);
1351 SlideVertical(board, flags, rf, ff, callback, closure);
1354 case SHOGI WhiteUnicorn:
1355 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1356 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1357 if(killX >= 0) break;
1358 Bishop(board, flags, rf, ff, callback, closure);
1359 SlideSideways(board, flags, rf, ff, callback, closure);
1360 SlideBackward(board, flags, rf, ff, callback, closure);
1363 case SHOGI BlackUnicorn:
1364 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1365 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1366 if(killX >= 0) break;
1367 Bishop(board, flags, rf, ff, callback, closure);
1368 SlideSideways(board, flags, rf, ff, callback, closure);
1369 SlideForward(board, flags, rf, ff, callback, closure);
1372 case SHOGI WhiteFalcon:
1373 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1374 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1375 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1376 if(killX >= 0) break;
1377 Rook(board, flags, rf, ff, callback, closure);
1378 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1381 case SHOGI BlackFalcon:
1382 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1383 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1384 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1385 if(killX >= 0) break;
1386 Rook(board, flags, rf, ff, callback, closure);
1387 SlideDiagForward(board, flags, rf, ff, callback, closure);
1390 case SHOGI WhiteDolphin:
1391 case SHOGI BlackHorse:
1392 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1393 SlideVertical(board, flags, rf, ff, callback, closure);
1396 case SHOGI BlackDolphin:
1397 case SHOGI WhiteHorse:
1398 SlideDiagForward(board, flags, rf, ff, callback, closure);
1399 SlideVertical(board, flags, rf, ff, callback, closure);
1402 case SHOGI WhiteLance:
1403 SlideForward(board, flags, rf, ff, callback, closure);
1406 case SHOGI BlackLance:
1407 SlideBackward(board, flags, rf, ff, callback, closure);
1410 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1414 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1427 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1428 Board xqCheckers, nullBoard;
1430 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1431 int rf, int ff, int rt, int ft,
1435 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1437 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1439 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1441 if ((int)board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1443 if (!(flags & F_IGNORE_CHECK) ) {
1444 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1447 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1448 kings += (board[r][f] == BlackKing);
1452 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1454 check = CheckTest(board, flags, rf, ff, rt, ft,
1455 kind == WhiteCapturesEnPassant ||
1456 kind == BlackCapturesEnPassant);
1457 if(promo) board[rf][ff] = BlackLance;
1460 if (flags & F_ATOMIC_CAPTURE) {
1461 if (board[rt][ft] != EmptySquare ||
1462 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1464 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1465 if (board[rf][ff] == king) return;
1466 for (r = rt-1; r <= rt+1; r++) {
1467 for (f = ft-1; f <= ft+1; f++) {
1468 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1469 board[r][f] == king) return;
1474 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1481 int captures; // [HGM] losers
1482 } LegalityTestClosure;
1485 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1486 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1487 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1488 moves that would destroy your own king. The CASTLE_OK flags are
1489 true if castling is not yet ruled out by a move of the king or
1490 rook. Return TRUE if the player on move is currently in check and
1491 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1493 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1496 int ff, ft, k, left, right, swap;
1497 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1498 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1499 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1504 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1505 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1506 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1508 if (inCheck) return TRUE;
1510 /* Generate castling moves */
1511 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1512 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1515 p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1516 if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1518 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1519 if ((flags & F_WHITE_ON_MOVE) &&
1520 (flags & F_WHITE_KCASTLE_OK) &&
1521 board[0][ff] == wKing &&
1522 board[0][ff + 1] == EmptySquare &&
1523 board[0][ff + 2] == EmptySquare &&
1524 board[0][BOARD_RGHT-3] == EmptySquare &&
1525 board[0][BOARD_RGHT-2] == EmptySquare &&
1526 board[0][BOARD_RGHT-1] == WhiteRook &&
1527 castlingRights[0] != NoRights && /* [HGM] check rights */
1528 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1530 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1531 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1532 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1533 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1535 callback(board, flags,
1536 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1537 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1539 if ((flags & F_WHITE_ON_MOVE) &&
1540 (flags & F_WHITE_QCASTLE_OK) &&
1541 board[0][ff] == wKing &&
1542 board[0][ff - 1] == EmptySquare &&
1543 board[0][ff - 2] == EmptySquare &&
1544 board[0][BOARD_LEFT+2] == EmptySquare &&
1545 board[0][BOARD_LEFT+1] == EmptySquare &&
1546 board[0][BOARD_LEFT+0] == WhiteRook &&
1547 castlingRights[1] != NoRights && /* [HGM] check rights */
1548 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1550 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1551 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1552 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1554 callback(board, flags,
1555 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1556 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1558 if (!(flags & F_WHITE_ON_MOVE) &&
1559 (flags & F_BLACK_KCASTLE_OK) &&
1560 board[BOARD_HEIGHT-1][ff] == bKing &&
1561 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1562 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1563 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1564 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1565 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1566 castlingRights[3] != NoRights && /* [HGM] check rights */
1567 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1569 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1570 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1571 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1572 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1574 callback(board, flags,
1575 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1576 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1578 if (!(flags & F_WHITE_ON_MOVE) &&
1579 (flags & F_BLACK_QCASTLE_OK) &&
1580 board[BOARD_HEIGHT-1][ff] == bKing &&
1581 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1582 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1583 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1584 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1585 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1586 castlingRights[4] != NoRights && /* [HGM] check rights */
1587 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1589 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1590 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1591 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1593 callback(board, flags,
1594 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1595 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1599 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1601 /* generate all potential FRC castling moves (KxR), ignoring flags */
1602 /* [HGM] test if the Rooks we find have castling rights */
1603 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1606 if ((flags & F_WHITE_ON_MOVE) != 0) {
1607 ff = castlingRights[2]; /* King file if we have any rights */
1608 if(ff != NoRights && board[0][ff] == WhiteKing) {
1609 if (appData.debugMode) {
1610 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1611 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1613 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1615 right = BOARD_RGHT-2;
1616 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1617 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1618 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1619 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1620 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1621 if(ft != NoRights && board[0][ft] == WhiteRook) {
1622 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1623 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1626 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1627 left = BOARD_LEFT+2;
1629 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1630 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1631 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1632 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1633 if(ff > BOARD_LEFT+2)
1634 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1635 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1636 if(ft != NoRights && board[0][ft] == WhiteRook) {
1637 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1638 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1642 ff = castlingRights[5]; /* King file if we have any rights */
1643 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1644 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1646 right = BOARD_RGHT-2;
1647 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1648 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1649 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1650 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1651 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1652 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1653 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1654 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1657 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1658 left = BOARD_LEFT+2;
1660 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1661 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1662 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1663 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1664 if(ff > BOARD_LEFT+2)
1665 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1666 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1667 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1668 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1669 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1686 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1687 int rf, int ff, int rt, int ft,
1692 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1694 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1696 if (rt == cl->rking && ft == cl->fking) {
1697 if((int)xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1699 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1701 if( (int)board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1702 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1703 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1707 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1708 he leave himself in check? Or if rf == -1, is the player on move
1709 in check now? enPassant must be TRUE if the indicated move is an
1710 e.p. capture. The possibility of castling out of a check along the
1711 back rank is not accounted for (i.e., we still return nonzero), as
1712 this is illegal anyway. Return value is the number of times the
1713 king is in check. */
1715 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1717 CheckTestClosure cl;
1718 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1719 ChessSquare captured = EmptySquare, ep=0, trampled=0, trampled2 = 0;
1720 int saveKill = killX;
1721 /* Suppress warnings on uninitialized variables */
1723 if(gameInfo.variant == VariantXiangqi)
1724 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1725 if(gameInfo.variant == VariantKnightmate)
1726 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1727 if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check
1728 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1729 if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon
1730 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1731 if(board[r][f] == k || board[r][f] == prince) {
1732 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1733 king = board[r][f]; // remember hich one we had
1738 if(PieceToChar(king) == '.') return 0; // never in check if the royal piece does not participate
1742 captured = board[rf][ft];
1743 board[rf][ft] = EmptySquare;
1745 captured = board[rt][ft];
1747 trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; saveKill += (kill2X << 16) + (1 << 30);
1748 if(kill2X >= 0) { trampled2 = board[kill2Y][kill2X]; board[kill2Y][kill2X] = EmptySquare; kill2X = -1; }
1751 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1752 board[rt][ft] = board[rf][ff];
1753 if(rf != rt || ff != ft) board[rf][ff] = EmptySquare;
1755 ep = board[EP_STATUS];
1756 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1757 ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
1758 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1759 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1760 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge'
1761 || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
1762 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1766 /* For compatibility with ICS wild 9, we scan the board in the
1767 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1768 and we test only whether that one is in check. */
1769 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1770 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1771 if (board[cl.rking][cl.fking] == king) {
1773 if(gameInfo.variant == VariantXiangqi) {
1774 /* [HGM] In Xiangqi opposing Kings means check as well */
1776 dir = (king >= BlackPawn) ? -1 : 1;
1777 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1778 board[i][cl.fking] == EmptySquare; i+=dir );
1779 if(i>=0 && i<BOARD_HEIGHT &&
1780 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1783 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1784 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1785 goto undo_move; /* 2-level break */
1792 if(rf != DROP_RANK) // [HGM] drop
1793 board[rf][ff] = board[rt][ft];
1795 board[rf][ft] = captured;
1796 board[rt][ft] = EmptySquare;
1799 if(saveKill & 1<<30) board[kill2Y][kill2X = saveKill >> 16 & 0xFFF] = trampled2;
1800 board[killY][killX = saveKill & 0xFFF] = trampled;
1802 board[rt][ft] = captured;
1804 board[EP_STATUS] = ep;
1807 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1811 HasLion (Board board, int flags)
1813 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1815 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1816 if(board[r][f] == lion) return 1;
1821 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1822 { // [HGM] put drop legality testing in separate routine for clarity
1824 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1825 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1826 n = PieceToNumber(piece);
1827 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1828 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1829 return ImpossibleMove; // piece not available
1830 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1831 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1832 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1833 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1834 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1835 if(piece == WhitePawn || piece == BlackPawn) {
1836 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1837 for(r=1; r<BOARD_HEIGHT-1; r++)
1838 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1839 // should still test if we mate with this Pawn
1841 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1842 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1844 if( (piece == WhitePawn || piece == BlackPawn) &&
1845 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1846 return IllegalMove; /* no pawn drops on 1st/8th */
1848 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1849 if (!(flags & F_IGNORE_CHECK) &&
1850 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1851 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1854 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1855 int rf, int ff, int rt, int ft,
1859 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1861 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1863 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1864 cl->captures++; // [HGM] losers: count legal captures
1865 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1870 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1872 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1874 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1875 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1876 piece = filterPiece = board[rf][ff];
1877 if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
1879 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1880 /* (perhaps we should disallow moves that obviously leave us in check?) */
1881 if((piece == WhiteFalcon || piece == BlackFalcon ||
1882 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu && !pieceDesc[piece])
1883 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1887 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1888 cl.ft = fFilter = ft;
1889 cl.kind = IllegalMove;
1890 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1891 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1892 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1893 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1894 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1895 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1897 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1898 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1899 if(board[rf][ff] < BlackPawn) { // white
1900 if(rf != 0) return IllegalMove; // must be on back rank
1901 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1902 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1903 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1904 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1906 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1907 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1908 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1909 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1910 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1913 if(gameInfo.variant == VariantChu) {
1914 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1915 if(promoChar != '+')
1916 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1917 if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
1918 if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
1919 return ImpossibleMove;
1921 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1923 if(gameInfo.variant == VariantShogi) {
1924 /* [HGM] Shogi promotions. '=' means defer */
1925 if(rf != DROP_RANK && cl.kind == NormalMove) {
1926 ChessSquare piece = board[rf][ff];
1927 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
1929 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1930 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1931 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1932 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1933 promoChar = '+'; // allowed ICS notations
1934 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1935 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1936 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1937 else if(flags & F_WHITE_ON_MOVE) {
1938 if( (int) piece < (int) WhiteWazir &&
1939 (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) {
1940 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1941 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1942 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1943 else /* promotion optional, default is defer */
1944 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1945 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1947 if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) {
1948 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1949 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1950 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1951 else /* promotion optional, default is defer */
1952 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1953 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1957 if (promoChar != NULLCHAR) {
1958 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1959 ChessSquare piece = board[rf][ff];
1960 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1961 // should test if in zone, really
1962 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1964 if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1966 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1967 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1968 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1969 if(piece == EmptySquare)
1970 cl.kind = ImpossibleMove; // non-existing piece
1971 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1972 cl.kind = IllegalMove; // no two Lions
1973 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1974 if(promoChar != PieceToChar(BlackKing)) {
1975 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1976 if(piece == BlackLance) cl.kind = ImpossibleMove;
1977 } else { // promotion to King allowed only if we do not have two yet
1978 int r, f, kings = 0;
1979 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1980 if(kings == 2) cl.kind = IllegalMove;
1982 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1983 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1984 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1985 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1986 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1987 cl.kind = IllegalMove; // promotion to King usually not allowed
1989 cl.kind = IllegalMove;
1999 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
2000 int rf, int ff, int rt, int ft,
2004 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2006 register MateTestClosure *cl = (MateTestClosure *) closure;
2011 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
2013 MateTest (Board board, int flags)
2016 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
2017 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
2019 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
2020 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
2021 nrKing += (board[r][f] == king); // stm has king
2022 if( board[r][f] != EmptySquare ) {
2023 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
2028 switch(gameInfo.variant) { // [HGM] losers: extinction wins
2029 case VariantShatranj:
2030 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
2034 if(nrKing == 0) return MT_NOKING;
2037 if(myPieces == 1) return MT_BARE;
2040 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
2041 // [HGM] 3check: yet to do!
2043 return inCheck ? MT_CHECK : MT_NONE;
2045 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
2046 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
2047 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
2048 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
2049 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
2050 if(board[n][holdings] != EmptySquare) {
2051 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
2052 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
2055 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
2056 return myPieces == hisPieces ? MT_STALEMATE :
2057 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
2058 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
2059 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
2061 return inCheck ? MT_CHECKMATE
2062 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
2063 MT_STAINMATE : MT_STALEMATE;
2068 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
2069 int rf, int ff, int rt, int ft,
2073 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2075 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
2076 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
2077 extern int kifu; // in parser.c
2079 // [HGM] wild: for wild-card pieces rt and rf are dummies
2080 if(piece == WhiteFalcon || piece == BlackFalcon ||
2081 piece == WhiteCobra || piece == BlackCobra)
2082 wildCard = !pieceDefs; // no wildcards when engine defined pieces
2084 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
2085 || PieceToChar(board[rf][ff]) == '~'
2086 && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
2088 (cl->rfIn == -1 || cl->rfIn == rf) &&
2089 (cl->ffIn == -1 || cl->ffIn == ff) &&
2090 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
2091 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
2093 if(cl->count && rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft) return; // duplicate move
2095 if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required
2096 int this = 1, other = 1;
2097 if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf);
2098 if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf);
2099 if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf);
2100 if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff);
2101 if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff);
2102 if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used
2103 if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it
2104 if(!this) return; // the current move does not satisfy the requested relative position, ignore it
2108 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
2109 // [HGM] oneclick: if multiple moves, be sure we remember capture
2110 cl->piece = board[rf][ff];
2113 cl->rt = wildCard ? cl->rtIn : rt;
2114 cl->ft = wildCard ? cl->ftIn : ft;
2117 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
2122 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
2124 int illegal = 0; char c = closure->promoCharIn;
2126 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
2127 closure->count = closure->captures = 0;
2128 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2129 closure->kind = ImpossibleMove;
2130 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
2131 fFilter = closure->ftIn;
2132 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
2133 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2134 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
2135 closure->count = closure->captures = 0;
2136 closure->rf = closure->ff = closure->rt = closure->ft = 0;
2137 closure->kind = ImpossibleMove;
2138 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2141 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
2142 if (closure->count == 0) {
2143 /* See if it's an illegal move due to check */
2145 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
2146 if (closure->count == 0) {
2147 /* No, it's not even that */
2148 if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) {
2149 int f, r; // if there is only a single piece of the requested type on the board, use that
2150 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
2151 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
2152 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
2153 if(closure->count > 1) illegal = 0; // ambiguous
2155 if(closure->count == 0) {
2156 if (appData.debugMode) { int i, j;
2157 for(i=BOARD_HEIGHT-1; i>=0; i--) {
2158 for(j=0; j<BOARD_WIDTH; j++)
2159 fprintf(debugFP, "%3d", (int) board[i][j]);
2160 fprintf(debugFP, "\n");
2166 } else if(pieceDefs && closure->count > 1 && closure->rtIn >=0) { // [HGM] gen: move is ambiguous under engine-defined rules (and not one-click)
2167 DisambiguateClosure spare = *closure;
2168 if(gameInfo.variant == VariantXiangqi && closure->pieceIn == EmptySquare && closure->ffIn < 0) {
2169 closure->ffIn = closure->ftIn; //closure->pieceIn = (flags & 1 ? BlackPawn : WhitePawn); // forward Pawn push has priority
2170 Disambiguate(board, flags, closure);
2173 pieceDefs = FALSE; spare.count = 0; // See if the (erroneous) built-in rules would resolve that
2174 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) &spare, closure->pieceIn);
2175 if(spare.count == 1) *closure = spare; // It does, so use those in stead (game from file saved before gen patch?)
2179 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
2180 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
2181 if(closure->piece < BlackPawn) { // white
2182 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
2183 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
2184 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
2185 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2186 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2188 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
2189 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
2190 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
2191 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
2192 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
2195 if(gameInfo.variant == VariantChu) {
2196 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
2198 if(gameInfo.variant == VariantShogi) {
2199 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
2200 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
2201 ChessSquare piece = closure->piece;
2202 int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8);
2203 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
2204 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
2205 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
2206 c = '+'; // allowed ICS notations
2207 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
2208 else if(flags & F_WHITE_ON_MOVE) {
2209 if( (int) piece < (int) WhiteWazir &&
2210 (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) {
2211 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
2212 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
2213 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
2214 else /* promotion optional, default is defer */
2215 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
2216 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2218 if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) {
2219 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
2220 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
2221 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
2222 else /* promotion optional, default is defer */
2223 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
2224 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
2227 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
2228 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
2230 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
2231 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
2232 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
2233 gameInfo.variant == VariantMakruk)
2234 c = PieceToChar(BlackFerz);
2235 else if(gameInfo.variant == VariantASEAN)
2236 c = PieceToChar(BlackRook);
2237 else if(gameInfo.variant == VariantGreat)
2238 c = PieceToChar(BlackMan);
2239 else if(gameInfo.variant == VariantGrand)
2240 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
2242 c = PieceToChar(BlackQueen);
2243 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
2244 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2245 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
2246 ChessSquare p = closure->piece;
2247 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
2248 closure->kind = ImpossibleMove; // used on non-promotable piece
2249 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
2250 } else if (c != NULLCHAR) closure->kind = IllegalMove;
2252 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
2253 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
2254 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
2255 if (closure->count > 1) {
2256 closure->kind = AmbiguousMove;
2259 /* Note: If more than one illegal move matches, but no legal
2260 moves, we return IllegalMove, not AmbiguousMove. Caller
2261 can look at closure->count to detect this.
2263 closure->kind = IllegalMove;
2277 } CoordsToAlgebraicClosure;
2279 extern void CoordsToAlgebraicCallback P((Board board, int flags,
2280 ChessMove kind, int rf, int ff,
2281 int rt, int ft, VOIDSTAR closure));
2284 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2286 register CoordsToAlgebraicClosure *cl =
2287 (CoordsToAlgebraicClosure *) closure;
2289 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
2290 (board[rf][ff] == cl->piece
2291 || PieceToChar(board[rf][ff]) == '~' &&
2292 (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
2296 cl->kind = kind; /* this is the move we want */
2298 cl->file++; /* need file to rule out this move */
2302 cl->rank++; /* need rank to rule out this move */
2304 cl->either++; /* rank or file will rule out this move */
2310 /* Convert coordinates to normal algebraic notation.
2311 promoChar must be NULLCHAR or 'x' if not a promotion.
2314 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2318 char *outp = out, c, capture;
2319 CoordsToAlgebraicClosure cl;
2321 if (rf == DROP_RANK) {
2322 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2323 /* Bughouse piece drop */
2324 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2329 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2331 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2334 if (promoChar == 'x') promoChar = NULLCHAR;
2335 piece = board[rf][ff];
2336 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
2341 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2342 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2343 /* Keep short notation if move is illegal only because it
2344 leaves the player in check, but still return IllegalMove */
2345 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2346 if (kind == IllegalMove) break;
2351 capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2352 if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2353 /* Non-capture; use style "e5" */
2356 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2358 /* Capture; use style "exd5" */
2360 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2364 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2366 /* Use promotion suffix style "=Q" */
2368 if (promoChar != NULLCHAR) {
2369 if(IS_SHOGI(gameInfo.variant)) {
2370 /* [HGM] ... but not in Shogi! */
2371 *outp++ = promoChar == '=' ? '=' : '+';
2374 *outp++ = ToUpper(promoChar);
2383 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2384 /* Code added by Tord: FRC castling. */
2385 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2386 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2388 safeStrCpy(out, "O-O", MOVE_LEN);
2390 safeStrCpy(out, "O-O-O", MOVE_LEN);
2391 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2393 /* End of code added by Tord */
2394 /* Test for castling or ICS wild castling */
2395 /* Use style "O-O" (oh-oh) for PGN compatibility */
2396 else if (rf == rt &&
2397 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2398 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2399 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2400 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2401 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2402 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2404 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2406 /* This notation is always unambiguous, unless there are
2407 kings on both the d and e files, with "wild castling"
2408 possible for the king on the d file and normal castling
2409 possible for the other. ICS rules for wild 9
2410 effectively make castling illegal for either king in
2411 this situation. So I am not going to worry about it;
2412 I'll just generate an ambiguous O-O in this case.
2414 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2417 /* else fall through */
2422 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2423 cl.ft = fFilter = ft;
2425 cl.kind = IllegalMove;
2426 cl.rank = cl.file = cl.either = 0;
2427 c = PieceToChar(piece) ;
2428 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
2430 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2431 /* Generate pretty moves for moving into check, but
2432 still return IllegalMove.
2434 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
2435 if (cl.kind == IllegalMove) break;
2436 cl.kind = IllegalMove;
2439 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2440 else "Ngf3" or "Ngxf7",
2441 else "N1f3" or "N5xf7",
2442 else "Ng1f3" or "Ng5xf7".
2444 if( c == '~' || c == '+') {
2445 /* [HGM] print nonexistent piece as its demoted version */
2446 piece = (ChessSquare) (CHUDEMOTED(piece));
2448 if(c=='+') *outp++ = c;
2449 *outp++ = ToUpper(PieceToChar(piece));
2450 if(*outp = PieceSuffix(piece)) outp++;
2452 if (cl.file || (cl.either && !cl.rank)) {
2458 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2461 if(board[rt][ft] != EmptySquare)
2467 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2468 if (IS_SHOGI(gameInfo.variant)) {
2469 /* [HGM] in Shogi non-pawns can promote */
2470 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2472 else if (gameInfo.variant == VariantChuChess && promoChar ||
2473 gameInfo.variant != VariantSuper && promoChar &&
2474 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2476 *outp++ = ToUpper(promoChar);
2478 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2480 *outp++ = ToUpper(promoChar);
2486 /* Moving a nonexistent piece */
2490 /* Not a legal move, even ignoring check.
2491 If there was a piece on the from square,
2492 use style "Ng1g3" or "Ng1xe8";
2493 if there was a pawn or nothing (!),
2494 use style "g1g3" or "g1xe8". Use "x"
2495 if a piece was on the to square, even
2496 a piece of the same color.
2500 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2502 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2503 c += (board[r][f] == piece); // count on-board pieces of given type
2504 *outp = PieceToChar(piece);
2505 if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
2506 *outp++ = ToUpper(PieceToChar(piece));
2507 if(*outp = PieceSuffix(piece)) outp++;
2509 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2513 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2515 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2519 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2520 /* Use promotion suffix style "=Q" */
2521 if (promoChar != NULLCHAR && promoChar != 'x') {
2523 *outp++ = ToUpper(promoChar);
2530 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2539 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2541 int preyStackPointer, chaseStackPointer;
2544 unsigned char rf, ff, rt, ft;
2548 unsigned char rank, file;
2554 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2556 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2557 int rf, int ff, int rt, int ft,
2561 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2562 { // For adding captures that can lead to chase indictment to the chaseStack
2563 if(board[rt][ft] == EmptySquare) return; // non-capture
2564 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2565 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2566 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2567 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2568 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2569 chaseStack[chaseStackPointer].rf = rf;
2570 chaseStack[chaseStackPointer].ff = ff;
2571 chaseStack[chaseStackPointer].rt = rt;
2572 chaseStack[chaseStackPointer].ft = ft;
2573 chaseStackPointer++;
2576 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2577 int rf, int ff, int rt, int ft,
2581 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2582 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2584 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2586 if(board[rt][ft] == EmptySquare) return; // no capture
2587 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2588 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2590 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2591 for(i=0; i<chaseStackPointer; i++) {
2592 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2593 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2594 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2595 chaseStack[i] = chaseStack[--chaseStackPointer];
2601 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2602 int rf, int ff, int rt, int ft,
2606 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2607 { // for determining if a piece (given through the closure) is protected
2608 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2610 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2611 if(appData.debugMode && board[rt][ft] != EmptySquare)
2612 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2615 extern char moveList[MAX_MOVES][MOVE_LEN];
2618 PerpetualChase (int first, int last)
2619 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2622 ChessSquare captured;
2624 preyStackPointer = 0; // clear stack of chased pieces
2625 for(i=first; i<last; i+=2) { // for all positions with same side to move
2626 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2627 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2628 // determine all captures possible after the move, and put them on chaseStack
2629 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2630 if(appData.debugMode) { int n;
2631 for(n=0; n<chaseStackPointer; n++)
2632 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2633 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2634 fprintf(debugFP, ": all capts\n");
2636 // determine all captures possible before the move, and delete them from chaseStack
2637 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2638 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2639 cl.rt = moveList[i][3]-ONE;
2640 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2641 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2642 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2643 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2644 if(appData.debugMode) { int n;
2645 for(n=0; n<chaseStackPointer; n++)
2646 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2647 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2648 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2650 // chaseSack now contains all captures made possible by the move
2651 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2652 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2653 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2655 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2656 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2658 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2659 continue; // C or H attack on R is always chase; leave on chaseStack
2661 if(attacker == victim) {
2662 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2663 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2664 // we can capture back with equal piece, so this is no chase but a sacrifice
2665 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2666 j--; /* ! */ continue;
2671 // the attack is on a lower piece, or on a pinned or blocked equal one
2672 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2673 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2674 // test if the victim is protected by a true protector. First make the capture.
2675 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2676 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2677 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2678 // Then test if the opponent can recapture
2679 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2680 cl.rt = chaseStack[j].rt;
2681 cl.ft = chaseStack[j].ft;
2682 if(appData.debugMode) {
2683 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2685 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2686 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2687 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2688 // unmake the capture
2689 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2690 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2691 // if a recapture was found, piece is protected, and we are not chasing it.
2692 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2693 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2697 // chaseStack now contains all moves that chased
2698 if(appData.debugMode) { int n;
2699 for(n=0; n<chaseStackPointer; n++)
2700 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2701 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2702 fprintf(debugFP, ": chases\n");
2704 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2705 for(j=0; j<chaseStackPointer; j++) {
2706 preyStack[j].rank = chaseStack[j].rt;
2707 preyStack[j].file = chaseStack[j].ft;
2709 preyStackPointer = chaseStackPointer;
2712 for(j=0; j<chaseStackPointer; j++) {
2713 for(k=0; k<preyStackPointer; k++) {
2714 // search the victim of each chase move on the preyStack (first occurrence)
2715 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2716 if(k < tail) break; // piece was already identified as still being chased
2717 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2718 preyStack[tail] = preyStack[k]; // by swapping
2719 preyStack[k] = preyStack[preyStackPointer];
2725 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2726 if(appData.debugMode) { int n;
2727 for(n=0; n<preyStackPointer; n++)
2728 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2729 fprintf(debugFP, "always chased upto ply %d\n", i);
2731 // now adjust the location of the chased pieces according to opponent move
2732 for(j=0; j<preyStackPointer; j++) {
2733 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2734 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2735 preyStack[j].rank = moveList[i+1][3]-ONE;
2736 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2741 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2742 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the