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 Free Software Foundation, Inc.
10 * Enhancements Copyright 2005 Alessandro Scotti
12 * The following terms apply to Digital Equipment Corporation's copyright
14 * ------------------------------------------------------------------------
17 * Permission to use, copy, modify, and distribute this software and its
18 * documentation for any purpose and without fee is hereby granted,
19 * provided that the above copyright notice appear in all copies and that
20 * both that copyright notice and this permission notice appear in
21 * supporting documentation, and that the name of Digital not be
22 * used in advertising or publicity pertaining to distribution of the
23 * software without specific, written prior permission.
25 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
26 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
27 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
30 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
32 * ------------------------------------------------------------------------
34 * The following terms apply to the enhanced version of XBoard
35 * distributed by the Free Software Foundation:
36 * ------------------------------------------------------------------------
38 * GNU XBoard is free software: you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation, either version 3 of the License, or (at
41 * your option) any later version.
43 * GNU XBoard is distributed in the hope that it will be useful, but
44 * WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46 * General Public License for more details.
48 * You should have received a copy of the GNU General Public License
49 * along with this program. If not, see http://www.gnu.org/licenses/. *
51 *------------------------------------------------------------------------
52 ** See the file ChangeLog for a revision history. */
61 #else /* not HAVE_STRING_H */
63 #endif /* not HAVE_STRING_H */
69 int WhitePiece P((ChessSquare));
70 int BlackPiece P((ChessSquare));
71 int SameColor P((ChessSquare, ChessSquare));
72 int PosFlags(int index);
74 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
76 char *pieceDesc[EmptySquare];
79 WhitePiece (ChessSquare piece)
81 return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
85 BlackPiece (ChessSquare piece)
87 return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
92 SameColor (ChessSquare piece1, ChessSquare piece2)
94 return ((int) piece1 >= (int) WhitePawn && /* [HGM] can be > King ! */
95 (int) piece1 < (int) BlackPawn &&
96 (int) piece2 >= (int) WhitePawn &&
97 (int) piece2 < (int) BlackPawn)
98 || ((int) piece1 >= (int) BlackPawn &&
99 (int) piece1 < (int) EmptySquare &&
100 (int) piece2 >= (int) BlackPawn &&
101 (int) piece2 < (int) EmptySquare);
104 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
107 char pieceToChar[] = {
108 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
109 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
110 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
111 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
113 char pieceNickName[EmptySquare];
116 PieceToChar (ChessSquare p)
118 if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
119 return pieceToChar[(int) p];
123 PieceToNumber (ChessSquare p) /* [HGM] holdings: count piece type, ignoring non-participating piece types */
126 ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
128 while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
136 if(c == '.') return EmptySquare;
137 for(i=0; i< (int) EmptySquare; i++)
138 if(pieceNickName[i] == c) return (ChessSquare) i;
139 for(i=0; i< (int) EmptySquare; i++)
140 if(pieceToChar[i] == c) return (ChessSquare) i;
145 CopyBoard (Board to, Board from)
149 for (i = 0; i < BOARD_HEIGHT; i++)
150 for (j = 0; j < BOARD_WIDTH; j++)
151 to[i][j] = from[i][j];
152 for (j = 0; j < BOARD_FILES; j++) // [HGM] gamestate: copy castling rights and ep status
153 to[VIRGIN][j] = from[VIRGIN][j],
154 to[CASTLING][j] = from[CASTLING][j];
155 to[HOLDINGS_SET] = 0; // flag used in ICS play
159 CompareBoards (Board board1, Board board2)
163 for (i = 0; i < BOARD_HEIGHT; i++)
164 for (j = 0; j < BOARD_WIDTH; j++) {
165 if (board1[i][j] != board2[i][j])
171 // [HGM] gen: configurable move generation from Betza notation sent by engine.
173 // alphabet "abcdefghijklmnopqrstuvwxyz"
174 char symmetry[] = "FBNW.F.WFNKN.N..QR....W..N";
175 char xStep[] = "2110.1.03102.10.00....0..2";
176 char yStep[] = "2132.1.33313.20.11....1..3";
177 char dirType[] = "01000104000200000260050000";
178 // 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 "
179 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 };
180 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 };
182 int rot[][4] = { // rotation matrices for each direction
194 MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
197 int mine, his, dir, bit, occup, i;
198 if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
199 while(*p) { // more moves to go
200 int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0;
201 if(*p == 'i') initial = 1, p++;
202 while(islower(*p)) p++; // skip prefixes
203 if(!isupper(*p)) return; // syntax error: no atom
204 dirSet = 0; // build direction set based on atom symmetry
205 switch(symmetry[*p-'A']) {
206 case 'B': expo = 0; // bishop, slide
207 case 'F': // diagonal atom (degenerate 4-fold)
208 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
209 int b = dirs1[*desc-'a']; // use wide version
210 if( islower(desc[1]) &&
211 ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
212 b = dirs1[*desc-'a'] & dirs1[desc[1]-'a']; // intersect wide & perp wide
217 dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
219 case 'R': expo = 0; // rook, slide
220 case 'W': // orthogonal atom (non-deg 4-fold)
221 while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a'];
222 dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
224 case 'N': // oblique atom (degenerate 8-fold)
225 while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
226 int b = dirs2[*desc-'a']; // when alone, use narrow version
227 if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
228 else if(islower(desc[1]) && i < '4'
229 && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim)
230 b = dirs1[*desc-'a'] & dirs2[desc[1]-'a']; // intersect wide & perp narrow
235 if(!dirSet) dirSet = 0xFF;
237 case 'Q': expo = 0; // queen, slide
238 case 'K': // non-deg (pseudo) 8-fold
239 dirSet=0x55; // start with orthogonal moves
240 retry = 1; // and schedule the diagonal moves for later
241 break; // should not have direction indicators
242 default: return; // syntax error: invalid atom
244 if(mine == 2) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves
245 mode = 0; // build mode mask
246 if(*desc == 'm') mode |= 4, desc++;
247 if(*desc == 'c') mode |= his, desc++;
248 if(*desc == 'd') mode |= mine, desc++;
249 if(!mode) mode = his + 4;// no mode spec, use default = mc
250 dx = xStep[*p-'A'] - '0'; // step vector of atom
251 dy = yStep[*p-'A'] - '0';
252 if(isdigit(*++p)) expo = atoi(p++); // read exponent
253 if(expo > 9) p++; // allow double-digit
254 desc = p; // this is start of next move
255 if(initial && (mine == 1 ? r > 1 : r < BOARD_HEIGHT - 2)) continue;
257 for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
259 if(!(bit & dirSet)) continue; // does not move in this direction
260 x = f; y = r; // start square
262 x += dx*rot[dir][0] + dy*rot[dir][1]; // step to next square
263 y += dx*rot[dir][2] + dy*rot[dir][3];
264 if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
265 if(board[y][x] < BlackPawn) occup = 1; else
266 if(board[y][x] < EmptySquare) occup = 2; else
268 if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
269 if(occup != 4) break; // not valid transit square
272 dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
273 } while(retry--); // and start doing them
277 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
280 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
285 if (rt >= BOARD_HEIGHT) break;
286 if (SameColor(board[rf][ff], board[rt][ft])) break;
287 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
288 if (board[rt][ft] != EmptySquare) break;
293 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
299 if (SameColor(board[rf][ff], board[rt][ft])) break;
300 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
301 if (board[rt][ft] != EmptySquare) break;
306 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
308 SlideForward(board, flags, rf, ff, callback, closure);
309 SlideBackward(board, flags, rf, ff, callback, closure);
313 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
315 int i, s, rt = rf, ft;
316 for(s = -1; s <= 1; s+= 2) {
319 if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
320 if (SameColor(board[rf][ff], board[rt][ft])) break;
321 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
322 if (board[rt][ft] != EmptySquare) break;
328 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
331 for(s = -1; s <= 1; s+= 2) {
335 if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
336 if (SameColor(board[rf][ff], board[rt][ft])) break;
337 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
338 if (board[rt][ft] != EmptySquare) break;
344 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
347 for(s = -1; s <= 1; s+= 2) {
351 if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
352 if (SameColor(board[rf][ff], board[rt][ft])) break;
353 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
354 if (board[rt][ft] != EmptySquare) break;
360 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
362 SlideVertical(board, flags, rf, ff, callback, closure);
363 SlideSideways(board, flags, rf, ff, callback, closure);
367 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
369 SlideDiagForward(board, flags, rf, ff, callback, closure);
370 SlideDiagBackward(board, flags, rf, ff, callback, closure);
374 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
375 { // Lion-like move of Horned Falcon and Souring Eagle
376 int ft = ff + dx, rt = rf + dy;
377 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
378 if (!SameColor(board[rf][ff], board[rt][ft]))
379 callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
381 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
382 if (!SameColor(board[rf][ff], board[rt][ft]))
383 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
387 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
389 int ft = ff, rt = rf + 1;
390 if (rt >= BOARD_HEIGHT) return;
391 if (SameColor(board[rf][ff], board[rt][ft])) return;
392 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
396 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
398 int ft = ff, rt = rf - 1;
400 if (SameColor(board[rf][ff], board[rt][ft])) return;
401 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
405 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
409 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
410 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
412 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
413 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
417 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
420 if (rt >= BOARD_HEIGHT) return;
422 if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
423 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
425 if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
426 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
430 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
435 if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
436 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
438 if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
439 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
443 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
445 StepForward(board, flags, rf, ff, callback, closure);
446 StepBackward(board, flags, rf, ff, callback, closure);
450 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
452 StepDiagForward(board, flags, rf, ff, callback, closure);
453 StepDiagBackward(board, flags, rf, ff, callback, closure);
457 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
459 StepVertical(board, flags, rf, ff, callback, closure);
460 StepSideways(board, flags, rf, ff, callback, closure);
464 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
467 for (i = -1; i <= 1; i += 2)
468 for (j = -1; j <= 1; j += 2)
469 for (s = 1; s <= 2; s++) {
472 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
473 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
474 && !SameColor(board[rf][ff], board[rt][ft]))
475 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
479 /* Call callback once for each pseudo-legal move in the given
480 position, except castling moves. A move is pseudo-legal if it is
481 legal, or if it would be legal except that it leaves the king in
482 check. In the arguments, epfile is EP_NONE if the previous move
483 was not a double pawn push, or the file 0..7 if it was, or
484 EP_UNKNOWN if we don't know and want to allow all e.p. captures.
485 Promotion moves generated are to Queen only.
488 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
489 // speed: only do moves with this piece type
492 int i, j, d, s, fs, rs, rt, ft, m;
493 int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
494 int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
496 for (rf = 0; rf < BOARD_HEIGHT; rf++)
497 for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
500 if(board[rf][ff] == EmptySquare) continue;
501 if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
502 m = 0; piece = board[rf][ff];
503 if(PieceToChar(piece) == '~')
504 piece = (ChessSquare) ( DEMOTED piece );
505 if(filter != EmptySquare && piece != filter) continue;
506 if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
507 if(IS_SHOGI(gameInfo.variant))
508 piece = (ChessSquare) ( SHOGI piece );
510 switch ((int)piece) {
511 /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
513 /* can't happen ([HGM] except for faries...) */
517 if(gameInfo.variant == VariantXiangqi) {
518 /* [HGM] capture and move straight ahead in Xiangqi */
519 if (rf < BOARD_HEIGHT-1 &&
520 !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
521 callback(board, flags, NormalMove,
522 rf, ff, rf + 1, ff, closure);
524 /* and move sideways when across the river */
525 for (s = -1; s <= 1; s += 2) {
526 if (rf >= BOARD_HEIGHT>>1 &&
527 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
528 !WhitePiece(board[rf][ff+s]) ) {
529 callback(board, flags, NormalMove,
530 rf, ff, rf, ff+s, closure);
535 if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
536 callback(board, flags,
537 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
538 rf, ff, rf + 1, ff, closure);
540 if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
541 gameInfo.variant != VariantShatranj && /* [HGM] */
542 gameInfo.variant != VariantCourier && /* [HGM] */
543 board[rf+2][ff] == EmptySquare ) {
544 callback(board, flags, NormalMove,
545 rf, ff, rf+2, ff, closure);
547 for (s = -1; s <= 1; s += 2) {
548 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
549 ((flags & F_KRIEGSPIEL_CAPTURE) ||
550 BlackPiece(board[rf + 1][ff + s]))) {
551 callback(board, flags,
552 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
553 rf, ff, rf + 1, ff + s, closure);
555 if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
556 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
557 (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
558 board[rf][ff + s] == BlackPawn &&
559 board[rf+1][ff + s] == EmptySquare) {
560 callback(board, flags, WhiteCapturesEnPassant,
561 rf, ff, rf+1, ff + s, closure);
568 if(gameInfo.variant == VariantXiangqi) {
569 /* [HGM] capture straight ahead in Xiangqi */
570 if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
571 callback(board, flags, NormalMove,
572 rf, ff, rf - 1, ff, closure);
574 /* and move sideways when across the river */
575 for (s = -1; s <= 1; s += 2) {
576 if (rf < BOARD_HEIGHT>>1 &&
577 ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
578 !BlackPiece(board[rf][ff+s]) ) {
579 callback(board, flags, NormalMove,
580 rf, ff, rf, ff+s, closure);
585 if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
586 callback(board, flags,
587 rf <= promoRank ? BlackPromotion : NormalMove,
588 rf, ff, rf - 1, ff, closure);
590 if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
591 gameInfo.variant != VariantShatranj && /* [HGM] */
592 gameInfo.variant != VariantCourier && /* [HGM] */
593 board[rf-2][ff] == EmptySquare) {
594 callback(board, flags, NormalMove,
595 rf, ff, rf-2, ff, closure);
597 for (s = -1; s <= 1; s += 2) {
598 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
599 ((flags & F_KRIEGSPIEL_CAPTURE) ||
600 WhitePiece(board[rf - 1][ff + s]))) {
601 callback(board, flags,
602 rf <= promoRank ? BlackPromotion : NormalMove,
603 rf, ff, rf - 1, ff + s, closure);
605 if (rf < BOARD_HEIGHT>>1) {
606 if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
607 (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
608 board[rf][ff + s] == WhitePawn &&
609 board[rf-1][ff + s] == EmptySquare) {
610 callback(board, flags, BlackCapturesEnPassant,
611 rf, ff, rf-1, ff + s, closure);
621 for (i = -1; i <= 1; i += 2)
622 for (j = -1; j <= 1; j += 2)
623 for (s = 1; s <= 2; s++) {
626 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
627 && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
628 && !SameColor(board[rf][ff], board[rt][ft]))
629 callback(board, flags, NormalMove,
630 rf, ff, rt, ft, closure);
634 case SHOGI WhiteKnight:
635 for (s = -1; s <= 1; s += 2) {
636 if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
637 !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
638 callback(board, flags, NormalMove,
639 rf, ff, rf + 2, ff + s, closure);
644 case SHOGI BlackKnight:
645 for (s = -1; s <= 1; s += 2) {
646 if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
647 !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
648 callback(board, flags, NormalMove,
649 rf, ff, rf - 2, ff + s, closure);
656 for (d = 0; d <= 1; d++)
657 for (s = -1; s <= 1; s += 2) {
660 rt = rf + (i * s) * d;
661 ft = ff + (i * s) * (1 - d);
662 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
663 if (m == 0 && board[rt][ft] == EmptySquare)
664 callback(board, flags, NormalMove,
665 rf, ff, rt, ft, closure);
666 if (m == 1 && board[rt][ft] != EmptySquare &&
667 !SameColor(board[rf][ff], board[rt][ft]) )
668 callback(board, flags, NormalMove,
669 rf, ff, rt, ft, closure);
670 if (board[rt][ft] != EmptySquare && m++) break;
675 /* Gold General (and all its promoted versions) . First do the */
676 /* diagonal forward steps, then proceed as normal Wazir */
677 case SHOGI (PROMOTED WhitePawn):
678 if(gameInfo.variant == VariantShogi) goto WhiteGold;
679 case SHOGI (PROMOTED BlackPawn):
680 if(gameInfo.variant == VariantShogi) goto BlackGold;
681 SlideVertical(board, flags, rf, ff, callback, closure);
684 case SHOGI (PROMOTED WhiteKnight):
685 if(gameInfo.variant == VariantShogi) goto WhiteGold;
686 case SHOGI BlackDrunk:
687 case SHOGI BlackAlfil:
688 Ferz(board, flags, rf, ff, callback, closure);
689 StepSideways(board, flags, rf, ff, callback, closure);
690 StepBackward(board, flags, rf, ff, callback, closure);
693 case SHOGI (PROMOTED BlackKnight):
694 if(gameInfo.variant == VariantShogi) goto BlackGold;
695 case SHOGI WhiteDrunk:
696 case SHOGI WhiteAlfil:
697 Ferz(board, flags, rf, ff, callback, closure);
698 StepSideways(board, flags, rf, ff, callback, closure);
699 StepForward(board, flags, rf, ff, callback, closure);
703 case SHOGI WhiteStag:
704 case SHOGI BlackStag:
705 if(gameInfo.variant == VariantShogi) goto BlackGold;
706 SlideVertical(board, flags, rf, ff, callback, closure);
707 Ferz(board, flags, rf, ff, callback, closure);
708 StepSideways(board, flags, rf, ff, callback, closure);
711 case SHOGI (PROMOTED WhiteQueen):
712 case SHOGI WhiteTokin:
713 case SHOGI WhiteWazir:
715 StepDiagForward(board, flags, rf, ff, callback, closure);
716 Wazir(board, flags, rf, ff, callback, closure);
719 case SHOGI (PROMOTED BlackQueen):
720 case SHOGI BlackTokin:
721 case SHOGI BlackWazir:
723 StepDiagBackward(board, flags, rf, ff, callback, closure);
724 Wazir(board, flags, rf, ff, callback, closure);
729 Wazir(board, flags, rf, ff, callback, closure);
732 case SHOGI WhiteMarshall:
733 case SHOGI BlackMarshall:
734 Ferz(board, flags, rf, ff, callback, closure);
735 for (d = 0; d <= 1; d++)
736 for (s = -2; s <= 2; s += 4) {
738 ft = ff + s * (1 - d);
739 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
740 if (!SameColor(board[rf][ff], board[rt][ft]) )
741 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
745 case SHOGI WhiteAngel:
746 case SHOGI BlackAngel:
747 Wazir(board, flags, rf, ff, callback, closure);
751 /* [HGM] support Shatranj pieces */
752 for (rs = -1; rs <= 1; rs += 2)
753 for (fs = -1; fs <= 1; fs += 2) {
756 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
757 && ( gameInfo.variant != VariantXiangqi ||
758 board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
760 && !SameColor(board[rf][ff], board[rt][ft]))
761 callback(board, flags, NormalMove,
762 rf, ff, rt, ft, closure);
763 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
764 gameInfo.variant == VariantChu || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
765 rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
767 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
768 && !SameColor(board[rf][ff], board[rt][ft]))
769 callback(board, flags, NormalMove,
770 rf, ff, rt, ft, closure);
772 if(gameInfo.variant == VariantSpartan)
773 for(fs = -1; fs <= 1; fs += 2) {
775 if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
776 callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
780 /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
783 if(gameInfo.variant == VariantChuChess) goto DragonHorse;
784 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
785 for (s = -2; s <= 2; s += 4) {
787 ft = ff + s * (1 - d);
788 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
789 if (SameColor(board[rf][ff], board[rt][ft])) continue;
790 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
793 /* Shogi Dragon Horse has to continue with Wazir after Bishop */
794 case SHOGI WhiteCardinal:
795 case SHOGI BlackCardinal:
796 case SHOGI WhitePCardinal:
797 case SHOGI BlackPCardinal:
799 Bishop(board, flags, rf, ff, callback, closure);
800 Wazir(board, flags, rf, ff, callback, closure);
803 /* Capablanca Archbishop continues as Knight */
806 Knight(board, flags, rf, ff, callback, closure);
808 /* Shogi Bishops are ordinary Bishops */
809 case SHOGI WhiteBishop:
810 case SHOGI BlackBishop:
811 case SHOGI WhitePBishop:
812 case SHOGI BlackPBishop:
815 Bishop(board, flags, rf, ff, callback, closure);
818 /* Shogi Lance is unlike anything, and asymmetric at that */
819 case SHOGI WhiteQueen:
820 if(gameInfo.variant == VariantChu) goto doQueen;
824 if (rt >= BOARD_HEIGHT) break;
825 if (SameColor(board[rf][ff], board[rt][ft])) break;
826 callback(board, flags, NormalMove,
827 rf, ff, rt, ft, closure);
828 if (board[rt][ft] != EmptySquare) break;
832 case SHOGI BlackQueen:
833 if(gameInfo.variant == VariantChu) goto doQueen;
838 if (SameColor(board[rf][ff], board[rt][ft])) break;
839 callback(board, flags, NormalMove,
840 rf, ff, rt, ft, closure);
841 if (board[rt][ft] != EmptySquare) break;
845 /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
848 if(gameInfo.variant == VariantChuChess) goto DragonKing;
849 for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
850 for (s = -2; s <= 2; s += 4) {
852 ft = ff + s * (1 - d);
853 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
854 if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
855 if (SameColor(board[rf][ff], board[rt][ft])) continue;
856 callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
858 if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
859 Wazir(board, flags, rf, ff, callback, closure);
861 Rook(board, flags, rf, ff, callback, closure);
864 /* Shogi Dragon King has to continue as Ferz after Rook moves */
865 case SHOGI WhiteDragon:
866 case SHOGI BlackDragon:
867 case SHOGI WhitePDragon:
868 case SHOGI BlackPDragon:
870 Rook(board, flags, rf, ff, callback, closure);
871 Ferz(board, flags, rf, ff, callback, closure);
875 /* Capablanca Chancellor sets flag to continue as Knight */
878 Rook(board, flags, rf, ff, callback, closure);
879 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
880 Ferz(board, flags, rf, ff, callback, closure);
882 Knight(board, flags, rf, ff, callback, closure);
885 /* Shogi Rooks are ordinary Rooks */
886 case SHOGI WhiteRook:
887 case SHOGI BlackRook:
888 case SHOGI WhitePRook:
889 case SHOGI BlackPRook:
892 Rook(board, flags, rf, ff, callback, closure);
897 case SHOGI WhiteMother:
898 case SHOGI BlackMother:
900 Rook(board, flags, rf, ff, callback, closure);
901 Bishop(board, flags, rf, ff, callback, closure);
904 case SHOGI WhitePawn:
905 StepForward(board, flags, rf, ff, callback, closure);
908 case SHOGI BlackPawn:
909 StepBackward(board, flags, rf, ff, callback, closure);
913 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
914 case SHOGI WhiteFerz:
915 Ferz(board, flags, rf, ff, callback, closure);
916 StepForward(board, flags, rf, ff, callback, closure);
920 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
921 case SHOGI BlackFerz:
922 StepBackward(board, flags, rf, ff, callback, closure);
926 /* [HGM] support Shatranj pieces */
927 Ferz(board, flags, rf, ff, callback, closure);
932 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
935 case SHOGI WhiteMonarch:
936 case SHOGI BlackMonarch:
937 case SHOGI WhiteKing:
938 case SHOGI BlackKing:
941 Ferz(board, flags, rf, ff, callback, closure);
942 Wazir(board, flags, rf, ff, callback, closure);
945 case WhiteNightrider:
946 case BlackNightrider:
947 for (i = -1; i <= 1; i += 2)
948 for (j = -1; j <= 1; j += 2)
949 for (s = 1; s <= 2; s++) { int k;
953 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
954 if (SameColor(board[rf][ff], board[rt][ft])) break;
955 callback(board, flags, NormalMove,
956 rf, ff, rt, ft, closure);
957 if (board[rt][ft] != EmptySquare) break;
963 Bishop(board, flags, rf, ff, callback, closure);
964 Rook(board, flags, rf, ff, callback, closure);
965 Knight(board, flags, rf, ff, callback, closure);
968 // Use Lance as Berolina / Spartan Pawn.
970 if(gameInfo.variant == VariantSuper) goto Amazon;
971 if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
972 callback(board, flags,
973 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
974 rf, ff, rf + 1, ff, closure);
975 for (s = -1; s <= 1; s += 2) {
976 if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
977 callback(board, flags,
978 rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
979 rf, ff, rf + 1, ff + s, closure);
980 if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
981 callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
986 if(gameInfo.variant == VariantSuper) goto Amazon;
987 if (rf > 0 && WhitePiece(board[rf - 1][ff]))
988 callback(board, flags,
989 rf <= promoRank ? BlackPromotion : NormalMove,
990 rf, ff, rf - 1, ff, closure);
991 for (s = -1; s <= 1; s += 2) {
992 if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
993 callback(board, flags,
994 rf <= promoRank ? BlackPromotion : NormalMove,
995 rf, ff, rf - 1, ff + s, closure);
996 if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
997 callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1001 case SHOGI WhiteNothing:
1002 case SHOGI BlackNothing:
1003 case SHOGI WhiteLion:
1004 case SHOGI BlackLion:
1007 for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1008 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1009 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1010 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1011 rf, ff, rt, ft, closure);
1015 case SHOGI WhiteFalcon:
1016 case SHOGI BlackFalcon:
1017 case SHOGI WhitePDagger:
1018 case SHOGI BlackPDagger:
1019 SlideSideways(board, flags, rf, ff, callback, closure);
1020 StepVertical(board, flags, rf, ff, callback, closure);
1023 case SHOGI WhiteCobra:
1024 case SHOGI BlackCobra:
1025 StepVertical(board, flags, rf, ff, callback, closure);
1028 case SHOGI (PROMOTED WhiteFerz):
1029 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1030 case SHOGI (PROMOTED BlackFerz):
1031 if(gameInfo.variant == VariantShogi) goto BlackGold;
1032 case SHOGI WhitePSword:
1033 case SHOGI BlackPSword:
1034 SlideVertical(board, flags, rf, ff, callback, closure);
1035 StepSideways(board, flags, rf, ff, callback, closure);
1038 case SHOGI WhiteUnicorn:
1039 case SHOGI BlackUnicorn:
1040 Ferz(board, flags, rf, ff, callback, closure);
1041 StepVertical(board, flags, rf, ff, callback, closure);
1044 case SHOGI WhiteMan:
1045 StepDiagForward(board, flags, rf, ff, callback, closure);
1046 StepVertical(board, flags, rf, ff, callback, closure);
1049 case SHOGI BlackMan:
1050 StepDiagBackward(board, flags, rf, ff, callback, closure);
1051 StepVertical(board, flags, rf, ff, callback, closure);
1054 case SHOGI WhiteHCrown:
1055 case SHOGI BlackHCrown:
1056 Bishop(board, flags, rf, ff, callback, closure);
1057 SlideSideways(board, flags, rf, ff, callback, closure);
1060 case SHOGI WhiteCrown:
1061 case SHOGI BlackCrown:
1062 Bishop(board, flags, rf, ff, callback, closure);
1063 SlideVertical(board, flags, rf, ff, callback, closure);
1066 case SHOGI WhiteHorned:
1067 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1068 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1069 if(killX >= 0) break;
1070 Bishop(board, flags, rf, ff, callback, closure);
1071 SlideSideways(board, flags, rf, ff, callback, closure);
1072 SlideBackward(board, flags, rf, ff, callback, closure);
1075 case SHOGI BlackHorned:
1076 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1077 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1078 if(killX >= 0) break;
1079 Bishop(board, flags, rf, ff, callback, closure);
1080 SlideSideways(board, flags, rf, ff, callback, closure);
1081 SlideForward(board, flags, rf, ff, callback, closure);
1084 case SHOGI WhiteEagle:
1085 Sting(board, flags, rf, ff, 1, 1, callback, closure);
1086 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1087 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1088 if(killX >= 0) break;
1089 Rook(board, flags, rf, ff, callback, closure);
1090 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1093 case SHOGI BlackEagle:
1094 Sting(board, flags, rf, ff, -1, 1, callback, closure);
1095 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1096 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1097 if(killX >= 0) break;
1098 Rook(board, flags, rf, ff, callback, closure);
1099 SlideDiagForward(board, flags, rf, ff, callback, closure);
1102 case SHOGI WhiteDolphin:
1103 case SHOGI BlackHorse:
1104 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1105 SlideVertical(board, flags, rf, ff, callback, closure);
1108 case SHOGI BlackDolphin:
1109 case SHOGI WhiteHorse:
1110 SlideDiagForward(board, flags, rf, ff, callback, closure);
1111 SlideVertical(board, flags, rf, ff, callback, closure);
1114 case SHOGI WhiteLance:
1115 SlideForward(board, flags, rf, ff, callback, closure);
1118 case SHOGI BlackLance:
1119 SlideBackward(board, flags, rf, ff, callback, closure);
1122 case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1126 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1139 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1140 Board xqCheckers, nullBoard;
1142 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1143 int rf, int ff, int rt, int ft,
1147 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1149 register GenLegalClosure *cl = (GenLegalClosure *) closure;
1151 if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1153 if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1155 if (!(flags & F_IGNORE_CHECK) ) {
1156 int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1159 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1160 kings += (board[r][f] == BlackKing);
1164 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1166 check = CheckTest(board, flags, rf, ff, rt, ft,
1167 kind == WhiteCapturesEnPassant ||
1168 kind == BlackCapturesEnPassant);
1169 if(promo) board[rf][ff] = BlackLance;
1172 if (flags & F_ATOMIC_CAPTURE) {
1173 if (board[rt][ft] != EmptySquare ||
1174 kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1176 ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1177 if (board[rf][ff] == king) return;
1178 for (r = rt-1; r <= rt+1; r++) {
1179 for (f = ft-1; f <= ft+1; f++) {
1180 if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1181 board[r][f] == king) return;
1186 cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1193 int captures; // [HGM] losers
1194 } LegalityTestClosure;
1197 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1198 F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1199 king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1200 moves that would destroy your own king. The CASTLE_OK flags are
1201 true if castling is not yet ruled out by a move of the king or
1202 rook. Return TRUE if the player on move is currently in check and
1203 F_IGNORE_CHECK is not set. [HGM] add castlingRights parameter */
1205 GenLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1208 int ff, ft, k, left, right, swap;
1209 int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1210 ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1211 int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1215 xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1216 if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1217 GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1219 if (inCheck) return TRUE;
1221 /* Generate castling moves */
1222 if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1223 wKing = WhiteUnicorn; bKing = BlackUnicorn;
1226 for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1227 if ((flags & F_WHITE_ON_MOVE) &&
1228 (flags & F_WHITE_KCASTLE_OK) &&
1229 board[0][ff] == wKing &&
1230 board[0][ff + 1] == EmptySquare &&
1231 board[0][ff + 2] == EmptySquare &&
1232 board[0][BOARD_RGHT-3] == EmptySquare &&
1233 board[0][BOARD_RGHT-2] == EmptySquare &&
1234 board[0][BOARD_RGHT-1] == WhiteRook &&
1235 castlingRights[0] != NoRights && /* [HGM] check rights */
1236 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1238 (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1239 !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1240 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1241 !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1243 callback(board, flags,
1244 ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1245 0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1247 if ((flags & F_WHITE_ON_MOVE) &&
1248 (flags & F_WHITE_QCASTLE_OK) &&
1249 board[0][ff] == wKing &&
1250 board[0][ff - 1] == EmptySquare &&
1251 board[0][ff - 2] == EmptySquare &&
1252 board[0][BOARD_LEFT+2] == EmptySquare &&
1253 board[0][BOARD_LEFT+1] == EmptySquare &&
1254 board[0][BOARD_LEFT+0] == WhiteRook &&
1255 castlingRights[1] != NoRights && /* [HGM] check rights */
1256 ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1258 (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1259 !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1260 !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1262 callback(board, flags,
1263 ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1264 0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1266 if (!(flags & F_WHITE_ON_MOVE) &&
1267 (flags & F_BLACK_KCASTLE_OK) &&
1268 board[BOARD_HEIGHT-1][ff] == bKing &&
1269 board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1270 board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1271 board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1272 board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1273 board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1274 castlingRights[3] != NoRights && /* [HGM] check rights */
1275 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1277 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1278 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1279 (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1280 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1282 callback(board, flags,
1283 ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1284 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1286 if (!(flags & F_WHITE_ON_MOVE) &&
1287 (flags & F_BLACK_QCASTLE_OK) &&
1288 board[BOARD_HEIGHT-1][ff] == bKing &&
1289 board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1290 board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1291 board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1292 board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1293 board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1294 castlingRights[4] != NoRights && /* [HGM] check rights */
1295 ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1297 (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1298 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1299 !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1301 callback(board, flags,
1302 ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1303 BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1307 if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1309 /* generate all potential FRC castling moves (KxR), ignoring flags */
1310 /* [HGM] test if the Rooks we find have castling rights */
1311 /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1314 if ((flags & F_WHITE_ON_MOVE) != 0) {
1315 ff = castlingRights[2]; /* King file if we have any rights */
1316 if(ff != NoRights && board[0][ff] == WhiteKing) {
1317 if (appData.debugMode) {
1318 fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1319 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1321 ft = castlingRights[0]; /* Rook file if we have H-side rights */
1323 right = BOARD_RGHT-2;
1324 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1325 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1326 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1327 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1328 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1329 if(ft != NoRights && board[0][ft] == WhiteRook) {
1330 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1331 if(swap) callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1334 ft = castlingRights[1]; /* Rook file if we have A-side rights */
1335 left = BOARD_LEFT+2;
1337 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1338 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1339 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1340 if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1341 if(ff > BOARD_LEFT+2)
1342 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1343 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1344 if(ft != NoRights && board[0][ft] == WhiteRook) {
1345 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1346 if(swap) callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1350 ff = castlingRights[5]; /* King file if we have any rights */
1351 if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1352 ft = castlingRights[3]; /* Rook file if we have H-side rights */
1354 right = BOARD_RGHT-2;
1355 if(ff == BOARD_RGHT-2) left = right = ff-1; /* special case */
1356 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1357 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1358 for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1359 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1360 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1361 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1362 if(swap) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1365 ft = castlingRights[4]; /* Rook file if we have A-side rights */
1366 left = BOARD_LEFT+2;
1368 if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1369 for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1370 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1371 if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1372 if(ff > BOARD_LEFT+2)
1373 for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1374 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1375 if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1376 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1377 if(swap) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1394 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1395 int rf, int ff, int rt, int ft,
1400 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1402 register CheckTestClosure *cl = (CheckTestClosure *) closure;
1404 if (rt == cl->rking && ft == cl->fking) {
1405 if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1407 xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1409 if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1410 && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1411 cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1415 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1416 he leave himself in check? Or if rf == -1, is the player on move
1417 in check now? enPassant must be TRUE if the indicated move is an
1418 e.p. capture. The possibility of castling out of a check along the
1419 back rank is not accounted for (i.e., we still return nonzero), as
1420 this is illegal anyway. Return value is the number of times the
1421 king is in check. */
1423 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1425 CheckTestClosure cl;
1426 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1427 ChessSquare captured = EmptySquare, ep=0, trampled=0;
1428 /* Suppress warnings on uninitialized variables */
1430 if(gameInfo.variant == VariantXiangqi)
1431 king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1432 if(gameInfo.variant == VariantKnightmate)
1433 king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1434 if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1435 int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1436 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1437 if(board[r][f] == k || board[r][f] == prince) {
1438 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1439 king = board[r][f]; // remember hich one we had
1446 captured = board[rf][ft];
1447 board[rf][ft] = EmptySquare;
1449 captured = board[rt][ft];
1450 if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1452 if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1453 board[rt][ft] = board[rf][ff];
1454 board[rf][ff] = EmptySquare;
1456 ep = board[EP_STATUS];
1457 if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1458 ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1459 if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion
1460 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance
1461 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1462 board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1466 /* For compatibility with ICS wild 9, we scan the board in the
1467 order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1468 and we test only whether that one is in check. */
1469 for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1470 for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1471 if (board[cl.rking][cl.fking] == king) {
1473 if(gameInfo.variant == VariantXiangqi) {
1474 /* [HGM] In Xiangqi opposing Kings means check as well */
1476 dir = (king >= BlackPawn) ? -1 : 1;
1477 for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1478 board[i][cl.fking] == EmptySquare; i+=dir );
1479 if(i>=0 && i<BOARD_HEIGHT &&
1480 board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1483 GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1484 if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1485 goto undo_move; /* 2-level break */
1492 if(rf != DROP_RANK) // [HGM] drop
1493 board[rf][ff] = board[rt][ft];
1495 board[rf][ft] = captured;
1496 board[rt][ft] = EmptySquare;
1498 if(killX >= 0) board[killY][killX] = trampled;
1499 board[rt][ft] = captured;
1501 board[EP_STATUS] = ep;
1504 return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1508 HasLion (Board board, int flags)
1510 int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1512 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1513 if(board[r][f] == lion) return 1;
1518 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1519 { // [HGM] put drop legality testing in separate routine for clarity
1521 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1522 if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1523 n = PieceToNumber(piece);
1524 if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1525 && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1526 return ImpossibleMove; // piece not available
1527 if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1528 if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1529 (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1530 piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1531 piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1532 if(piece == WhitePawn || piece == BlackPawn) {
1533 int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1534 for(r=1; r<BOARD_HEIGHT-1; r++)
1535 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1536 // should still test if we mate with this Pawn
1538 } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1539 if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1541 if( (piece == WhitePawn || piece == BlackPawn) &&
1542 (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1543 return IllegalMove; /* no pawn drops on 1st/8th */
1545 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1546 if (!(flags & F_IGNORE_CHECK) &&
1547 CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1548 return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1551 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1552 int rf, int ff, int rt, int ft,
1556 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1558 register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1560 if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1561 cl->captures++; // [HGM] losers: count legal captures
1562 if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1567 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1569 LegalityTestClosure cl; ChessSquare piece, filterPiece;
1571 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1572 if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1573 piece = filterPiece = board[rf][ff];
1574 if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1576 /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1577 /* (perhaps we should disallow moves that obviously leave us in check?) */
1578 if((piece == WhiteFalcon || piece == BlackFalcon ||
1579 piece == WhiteCobra || piece == BlackCobra) && gameInfo.variant != VariantChu)
1580 return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1584 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1585 cl.ft = fFilter = ft;
1586 cl.kind = IllegalMove;
1587 cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1588 if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1589 GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1590 if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1591 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1592 return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1594 if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1595 if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1596 if(board[rf][ff] < BlackPawn) { // white
1597 if(rf != 0) return IllegalMove; // must be on back rank
1598 if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1599 if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1600 if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1601 if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1603 if(rf != BOARD_HEIGHT-1) return IllegalMove;
1604 if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1605 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1606 if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1607 if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1610 if(gameInfo.variant == VariantChu) {
1611 if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1612 if(promoChar != '+')
1613 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1614 if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1615 return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1617 if(gameInfo.variant == VariantShogi) {
1618 /* [HGM] Shogi promotions. '=' means defer */
1619 if(rf != DROP_RANK && cl.kind == NormalMove) {
1620 ChessSquare piece = board[rf][ff];
1622 if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1623 if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1624 promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1625 promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1626 promoChar = '+'; // allowed ICS notations
1627 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1628 if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1629 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1630 else if(flags & F_WHITE_ON_MOVE) {
1631 if( (int) piece < (int) WhiteWazir &&
1632 (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1633 if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1634 piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1635 cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1636 else /* promotion optional, default is defer */
1637 cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1638 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1640 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1641 if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1642 piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1643 cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1644 else /* promotion optional, default is defer */
1645 cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1646 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1650 if (promoChar != NULLCHAR) {
1651 if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1652 ChessSquare piece = board[rf][ff];
1653 if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1654 // should test if in zone, really
1655 if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1657 if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1659 if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1660 if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1661 ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1662 if(piece == EmptySquare)
1663 cl.kind = ImpossibleMove; // non-existing piece
1664 if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1665 cl.kind = IllegalMove; // no two Lions
1666 } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1667 if(promoChar != PieceToChar(BlackKing)) {
1668 if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1669 if(piece == BlackLance) cl.kind = ImpossibleMove;
1670 } else { // promotion to King allowed only if we do not have two yet
1671 int r, f, kings = 0;
1672 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1673 if(kings == 2) cl.kind = IllegalMove;
1675 } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1676 piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1677 else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1678 cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1679 else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1680 cl.kind = IllegalMove; // promotion to King usually not allowed
1682 cl.kind = IllegalMove;
1692 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1693 int rf, int ff, int rt, int ft,
1697 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1699 register MateTestClosure *cl = (MateTestClosure *) closure;
1704 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1706 MateTest (Board board, int flags)
1709 int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1710 ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1712 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1713 // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1714 nrKing += (board[r][f] == king); // stm has king
1715 if( board[r][f] != EmptySquare ) {
1716 if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1721 switch(gameInfo.variant) { // [HGM] losers: extinction wins
1722 case VariantShatranj:
1723 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1727 if(nrKing == 0) return MT_NOKING;
1730 if(myPieces == 1) return MT_BARE;
1733 inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1734 // [HGM] 3check: yet to do!
1736 return inCheck ? MT_CHECK : MT_NONE;
1738 if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1739 && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1740 int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1741 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1742 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1743 if(board[n][holdings] != EmptySquare) {
1744 int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1745 if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1748 if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1749 return myPieces == hisPieces ? MT_STALEMATE :
1750 myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1751 else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1752 else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1754 return inCheck ? MT_CHECKMATE
1755 : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1756 MT_STAINMATE : MT_STALEMATE;
1761 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1762 int rf, int ff, int rt, int ft,
1766 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1768 register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1769 int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1771 // [HGM] wild: for wild-card pieces rt and rf are dummies
1772 if(piece == WhiteFalcon || piece == BlackFalcon ||
1773 piece == WhiteCobra || piece == BlackCobra)
1776 if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1777 || PieceToChar(board[rf][ff]) == '~'
1778 && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1780 (cl->rfIn == -1 || cl->rfIn == rf) &&
1781 (cl->ffIn == -1 || cl->ffIn == ff) &&
1782 (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1783 (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1786 if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1787 // [HGM] oneclick: if multiple moves, be sure we remember capture
1788 cl->piece = board[rf][ff];
1791 cl->rt = wildCard ? cl->rtIn : rt;
1792 cl->ft = wildCard ? cl->ftIn : ft;
1795 cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1800 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1802 int illegal = 0; char c = closure->promoCharIn;
1804 if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1805 closure->count = closure->captures = 0;
1806 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1807 closure->kind = ImpossibleMove;
1808 rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1809 fFilter = closure->ftIn;
1810 if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1811 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1812 if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1813 closure->count = closure->captures = 0;
1814 closure->rf = closure->ff = closure->rt = closure->ft = 0;
1815 closure->kind = ImpossibleMove;
1816 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1819 GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1820 if (closure->count == 0) {
1821 /* See if it's an illegal move due to check */
1823 GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1824 if (closure->count == 0) {
1825 /* No, it's not even that */
1826 if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1827 int f, r; // if there is only a single piece of the requested type on the board, use that
1828 closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1829 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1830 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1831 if(closure->count > 1) illegal = 0; // ambiguous
1833 if(closure->count == 0) {
1834 if (appData.debugMode) { int i, j;
1835 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1836 for(j=0; j<BOARD_WIDTH; j++)
1837 fprintf(debugFP, "%3d", (int) board[i][j]);
1838 fprintf(debugFP, "\n");
1846 if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1847 if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1848 if(closure->piece < BlackPawn) { // white
1849 if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1850 if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1851 if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1852 if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1853 if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1855 if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1856 if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1857 if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1858 if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1859 if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1862 if(gameInfo.variant == VariantChu) {
1863 if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1865 if(gameInfo.variant == VariantShogi) {
1866 /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1867 if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1868 ChessSquare piece = closure->piece;
1869 if (c == 'd' && (piece == WhiteRook || piece == BlackRook) ||
1870 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1871 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1872 c = '+'; // allowed ICS notations
1873 if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1874 else if(flags & F_WHITE_ON_MOVE) {
1875 if( (int) piece < (int) WhiteWazir &&
1876 (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1877 if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1878 piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1879 closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1880 else /* promotion optional, default is defer */
1881 closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1882 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1884 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1885 if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1886 piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1887 closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1888 else /* promotion optional, default is defer */
1889 closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1890 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1893 if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1894 if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1896 if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1897 if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1898 if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1899 gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1900 c = PieceToChar(BlackFerz);
1901 else if(gameInfo.variant == VariantGreat)
1902 c = PieceToChar(BlackMan);
1903 else if(gameInfo.variant == VariantGrand)
1904 closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1906 c = PieceToChar(BlackQueen);
1907 } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1908 else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1909 } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
1910 ChessSquare p = closure->piece;
1911 if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
1912 closure->kind = ImpossibleMove; // used on non-promotable piece
1913 else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1914 } else if (c != NULLCHAR) closure->kind = IllegalMove;
1916 closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1917 if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1918 closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1919 if (closure->count > 1) {
1920 closure->kind = AmbiguousMove;
1923 /* Note: If more than one illegal move matches, but no legal
1924 moves, we return IllegalMove, not AmbiguousMove. Caller
1925 can look at closure->count to detect this.
1927 closure->kind = IllegalMove;
1941 } CoordsToAlgebraicClosure;
1943 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1944 ChessMove kind, int rf, int ff,
1945 int rt, int ft, VOIDSTAR closure));
1948 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1950 register CoordsToAlgebraicClosure *cl =
1951 (CoordsToAlgebraicClosure *) closure;
1953 if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1954 (board[rf][ff] == cl->piece
1955 || PieceToChar(board[rf][ff]) == '~' &&
1956 (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1960 cl->kind = kind; /* this is the move we want */
1962 cl->file++; /* need file to rule out this move */
1966 cl->rank++; /* need rank to rule out this move */
1968 cl->either++; /* rank or file will rule out this move */
1974 /* Convert coordinates to normal algebraic notation.
1975 promoChar must be NULLCHAR or 'x' if not a promotion.
1978 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
1982 char *outp = out, c;
1983 CoordsToAlgebraicClosure cl;
1985 if (rf == DROP_RANK) {
1986 if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
1987 /* Bughouse piece drop */
1988 *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1993 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1995 return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1998 if (promoChar == 'x') promoChar = NULLCHAR;
1999 piece = board[rf][ff];
2000 if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2005 kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2006 if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2007 /* Keep short notation if move is illegal only because it
2008 leaves the player in check, but still return IllegalMove */
2009 kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2010 if (kind == IllegalMove) break;
2015 if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
2016 /* Non-capture; use style "e5" */
2019 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2021 /* Capture; use style "exd5" */
2022 if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
2023 *outp++ = 'x'; /* [HGM] Xiangqi has sideway noncaptures across river! */
2027 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2029 /* Use promotion suffix style "=Q" */
2031 if (promoChar != NULLCHAR) {
2032 if(IS_SHOGI(gameInfo.variant)) {
2033 /* [HGM] ... but not in Shogi! */
2034 *outp++ = promoChar == '=' ? '=' : '+';
2037 *outp++ = ToUpper(promoChar);
2046 /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2047 /* Code added by Tord: FRC castling. */
2048 if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2049 (piece == BlackKing && board[rt][ft] == BlackRook)) {
2051 safeStrCpy(out, "O-O", MOVE_LEN);
2053 safeStrCpy(out, "O-O-O", MOVE_LEN);
2054 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2056 /* End of code added by Tord */
2057 /* Test for castling or ICS wild castling */
2058 /* Use style "O-O" (oh-oh) for PGN compatibility */
2059 else if (rf == rt &&
2060 rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2061 (ft - ff > 1 || ff - ft > 1) && // No castling if legal King move (on narrow boards!)
2062 ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2063 (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2064 if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2065 snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2067 snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2069 /* This notation is always unambiguous, unless there are
2070 kings on both the d and e files, with "wild castling"
2071 possible for the king on the d file and normal castling
2072 possible for the other. ICS rules for wild 9
2073 effectively make castling illegal for either king in
2074 this situation. So I am not going to worry about it;
2075 I'll just generate an ambiguous O-O in this case.
2077 return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2080 /* else fall through */
2085 cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2086 cl.ft = fFilter = ft;
2088 cl.kind = IllegalMove;
2089 cl.rank = cl.file = cl.either = 0;
2090 c = PieceToChar(piece) ;
2091 GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2093 if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2094 /* Generate pretty moves for moving into check, but
2095 still return IllegalMove.
2097 GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2098 if (cl.kind == IllegalMove) break;
2099 cl.kind = IllegalMove;
2102 /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2103 else "Ngf3" or "Ngxf7",
2104 else "N1f3" or "N5xf7",
2105 else "Ng1f3" or "Ng5xf7".
2107 if( c == '~' || c == '+') {
2108 /* [HGM] print nonexistent piece as its demoted version */
2109 piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2111 if(c=='+') *outp++ = c;
2112 *outp++ = ToUpper(PieceToChar(piece));
2114 if (cl.file || (cl.either && !cl.rank)) {
2120 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2123 if(board[rt][ft] != EmptySquare)
2129 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2130 if (IS_SHOGI(gameInfo.variant)) {
2131 /* [HGM] in Shogi non-pawns can promote */
2132 *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2134 else if (gameInfo.variant == VariantChuChess && promoChar ||
2135 gameInfo.variant != VariantSuper && promoChar &&
2136 (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2138 *outp++ = ToUpper(promoChar);
2140 else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2142 *outp++ = ToUpper(promoChar);
2148 /* Moving a nonexistent piece */
2152 /* Not a legal move, even ignoring check.
2153 If there was a piece on the from square,
2154 use style "Ng1g3" or "Ng1xe8";
2155 if there was a pawn or nothing (!),
2156 use style "g1g3" or "g1xe8". Use "x"
2157 if a piece was on the to square, even
2158 a piece of the same color.
2162 if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2164 for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2165 c += (board[r][f] == piece); // count on-board pieces of given type
2166 *outp++ = ToUpper(PieceToChar(piece));
2168 if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2172 else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2174 if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2178 else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2179 /* Use promotion suffix style "=Q" */
2180 if (promoChar != NULLCHAR && promoChar != 'x') {
2182 *outp++ = ToUpper(promoChar);
2189 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2198 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2200 int preyStackPointer, chaseStackPointer;
2203 unsigned char rf, ff, rt, ft;
2207 unsigned char rank, file;
2213 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2215 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2216 int rf, int ff, int rt, int ft,
2220 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2221 { // For adding captures that can lead to chase indictment to the chaseStack
2222 if(board[rt][ft] == EmptySquare) return; // non-capture
2223 if(board[rt][ft] == WhitePawn && rt < BOARD_HEIGHT/2) return; // Pawn before river can be chased
2224 if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return; // Pawn before river can be chased
2225 if(board[rf][ff] == WhitePawn || board[rf][ff] == BlackPawn) return; // Pawns are allowed to chase
2226 if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2227 // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2228 chaseStack[chaseStackPointer].rf = rf;
2229 chaseStack[chaseStackPointer].ff = ff;
2230 chaseStack[chaseStackPointer].rt = rt;
2231 chaseStack[chaseStackPointer].ft = ft;
2232 chaseStackPointer++;
2235 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2236 int rf, int ff, int rt, int ft,
2240 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2241 { // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2243 register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2245 if(board[rt][ft] == EmptySquare) return; // no capture
2246 if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2247 rf = cl->rt; ff = cl->ft; // doctor their fromSquare so they will be recognized in chaseStack
2249 // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2250 for(i=0; i<chaseStackPointer; i++) {
2251 if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2252 chaseStack[i].rt == rt && chaseStack[i].ft == ft ) {
2253 // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2254 chaseStack[i] = chaseStack[--chaseStackPointer];
2260 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2261 int rf, int ff, int rt, int ft,
2265 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2266 { // for determining if a piece (given through the closure) is protected
2267 register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2269 if(rt == cl->rt && ft == cl->ft) cl->recaptures++; // count legal recaptures to this square
2270 if(appData.debugMode && board[rt][ft] != EmptySquare)
2271 fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2274 extern char moveList[MAX_MOVES][MOVE_LEN];
2277 PerpetualChase (int first, int last)
2278 { // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2281 ChessSquare captured;
2283 preyStackPointer = 0; // clear stack of chased pieces
2284 for(i=first; i<last; i+=2) { // for all positions with same side to move
2285 if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2286 chaseStackPointer = 0; // clear stack that is going to hold possible chases
2287 // determine all captures possible after the move, and put them on chaseStack
2288 GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2289 if(appData.debugMode) { int n;
2290 for(n=0; n<chaseStackPointer; n++)
2291 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2292 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2293 fprintf(debugFP, ": all capts\n");
2295 // determine all captures possible before the move, and delete them from chaseStack
2296 cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2297 cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2298 cl.rt = moveList[i][3]-ONE;
2299 cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2300 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2301 GenLegal(boards[i], PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2302 xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2303 if(appData.debugMode) { int n;
2304 for(n=0; n<chaseStackPointer; n++)
2305 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2306 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2307 fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2309 // chaseSack now contains all captures made possible by the move
2310 for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2311 int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2312 int victim = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2314 if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2315 if(victim >= (int) BlackPawn) victim = BLACK_TO_WHITE victim;
2317 if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2318 continue; // C or H attack on R is always chase; leave on chaseStack
2320 if(attacker == victim) {
2321 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2322 chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2323 // we can capture back with equal piece, so this is no chase but a sacrifice
2324 chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2325 j--; /* ! */ continue;
2330 // the attack is on a lower piece, or on a pinned or blocked equal one
2331 CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2332 CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2333 // test if the victim is protected by a true protector. First make the capture.
2334 captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2335 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2336 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2337 // Then test if the opponent can recapture
2338 cl.recaptures = 0; // prepare closure to pass recapture square and count moves to it
2339 cl.rt = chaseStack[j].rt;
2340 cl.ft = chaseStack[j].ft;
2341 if(appData.debugMode) {
2342 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2344 xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2345 GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2346 xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2347 // unmake the capture
2348 boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2349 boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2350 // if a recapture was found, piece is protected, and we are not chasing it.
2351 if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2352 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2356 // chaseStack now contains all moves that chased
2357 if(appData.debugMode) { int n;
2358 for(n=0; n<chaseStackPointer; n++)
2359 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2360 chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2361 fprintf(debugFP, ": chases\n");
2363 if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2364 for(j=0; j<chaseStackPointer; j++) {
2365 preyStack[j].rank = chaseStack[j].rt;
2366 preyStack[j].file = chaseStack[j].ft;
2368 preyStackPointer = chaseStackPointer;
2371 for(j=0; j<chaseStackPointer; j++) {
2372 for(k=0; k<preyStackPointer; k++) {
2373 // search the victim of each chase move on the preyStack (first occurrence)
2374 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2375 if(k < tail) break; // piece was already identified as still being chased
2376 preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2377 preyStack[tail] = preyStack[k]; // by swapping
2378 preyStack[k] = preyStack[preyStackPointer];
2384 preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2385 if(appData.debugMode) { int n;
2386 for(n=0; n<preyStackPointer; n++)
2387 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2388 fprintf(debugFP, "always chased upto ply %d\n", i);
2390 // now adjust the location of the chased pieces according to opponent move
2391 for(j=0; j<preyStackPointer; j++) {
2392 if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2393 preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2394 preyStack[j].rank = moveList[i+1][3]-ONE;
2395 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2400 return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2401 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the