Allow definition of castling in piece command
[xboard.git] / moves.c
1 /*
2  * moves.c - Move generation and checking
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
9  *
10  * Enhancements Copyright 2005 Alessandro Scotti
11  *
12  * The following terms apply to Digital Equipment Corporation's copyright
13  * interest in XBoard:
14  * ------------------------------------------------------------------------
15  * All Rights Reserved
16  *
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.
24  *
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
31  * SOFTWARE.
32  * ------------------------------------------------------------------------
33  *
34  * The following terms apply to the enhanced version of XBoard
35  * distributed by the Free Software Foundation:
36  * ------------------------------------------------------------------------
37  *
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.
42  *
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.
47  *
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/.  *
50  *
51  *------------------------------------------------------------------------
52  ** See the file ChangeLog for a revision history.  */
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <stdlib.h>
59 #if HAVE_STRING_H
60 # include <string.h>
61 #else /* not HAVE_STRING_H */
62 # include <strings.h>
63 #endif /* not HAVE_STRING_H */
64 #include "common.h"
65 #include "backend.h"
66 #include "moves.h"
67 #include "parser.h"
68
69 int WhitePiece P((ChessSquare));
70 int BlackPiece P((ChessSquare));
71 int SameColor P((ChessSquare, ChessSquare));
72 int PosFlags(int index);
73
74 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
75 int quickFlag;
76 char *pieceDesc[EmptySquare];
77
78 int
79 WhitePiece (ChessSquare piece)
80 {
81     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
82 }
83
84 int
85 BlackPiece (ChessSquare piece)
86 {
87     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
88 }
89
90 #if 0
91 int
92 SameColor (ChessSquare piece1, ChessSquare piece2)
93 {
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);
102 }
103 #else
104 #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare)
105 #endif
106
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',
112                         'x' };
113 char pieceNickName[EmptySquare];
114
115 char
116 PieceToChar (ChessSquare p)
117 {
118     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
119     return pieceToChar[(int) p];
120 }
121
122 int
123 PieceToNumber (ChessSquare p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
124 {
125     int i=0;
126     ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
127
128     while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
129     return i;
130 }
131
132 ChessSquare
133 CharToPiece (int c)
134 {
135      int 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;
141      return EmptySquare;
142 }
143
144 void
145 CopyBoard (Board to, Board from)
146 {
147     int i, j;
148
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
156 }
157
158 int
159 CompareBoards (Board board1, Board board2)
160 {
161     int i, j;
162
163     for (i = 0; i < BOARD_HEIGHT; i++)
164       for (j = 0; j < BOARD_WIDTH; j++) {
165           if (board1[i][j] != board2[i][j])
166             return FALSE;
167     }
168     return TRUE;
169 }
170
171 // [HGM] gen: configurable move generation from Betza notation sent by engine.
172
173 //  alphabet      "abcdefghijklmnopqrstuvwxyz"
174 char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
175 char xStep[]    = "2110.130.102.10.00....0..2";
176 char yStep[]    = "2132.133.313.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 };
181
182 int rot[][4] = { // rotation matrices for each direction
183   { 1, 0, 0, 1 },
184   { 0, 1, 1, 0 },
185   { 0, 1,-1, 0 },
186   { 1, 0, 0,-1 },
187   {-1, 0, 0,-1 },
188   { 0,-1,-1, 0 },
189   { 0,-1, 1, 0 },
190   {-1, 0, 0, 1 }
191 };
192
193 void
194 MovesFromString (Board board, int flags, int f, int r, char *desc, MoveCallback cb, VOIDSTAR cl)
195 {
196     char *p = desc;
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, jump=1;
201         if(*p == 'i') initial = 1, desc = ++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
213                             desc += 2;
214                         } else desc++;
215                         dirSet |= b;
216                     }
217                     dirSet &= 0x99; if(!dirSet) dirSet = 0x99;
218                     break;
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;
223                     break;
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
231                             desc += 2;
232                         } else desc++;
233                         dirSet |= b;
234                     }
235                     if(!dirSet) dirSet = 0xFF;
236                     break;
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
243         }
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(*desc == 'e') mode |= 8, desc++;
250         if(*desc == 'n') jump = 0, desc++;
251         while(*desc == 'j') jump++, desc++;
252         if(!mode) mode = his + 4;// no mode spec, use default = mc
253         dx = xStep[*p-'A'] - '0';                     // step vector of atom
254         dy = yStep[*p-'A'] - '0';
255         if(isdigit(*++p)) expo = atoi(p++);           // read exponent
256         if(expo > 9) p++;                             // allow double-digit
257         desc = p;                                     // this is start of next move
258         if(initial && board[r][f] != initialPosition[r][f]) continue;
259         if(expo > 1 && dx == 0 && dy == 0) {          // castling indicated by O + number
260             mode |= 16; dy = 1;
261         }
262         do {
263           for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions
264             int i = expo, vx, vy;
265             if(!(bit & dirSet)) continue;             // does not move in this direction
266             vx = dx*rot[dir][0] + dy*rot[dir][1];     // rotate step vector
267             vy = dx*rot[dir][2] + dy*rot[dir][3];
268             x = f; y = r;                             // start square
269             do {
270                 x += vx; y += vy;                     // step to next square
271                 if(y < 0 || y >= BOARD_HEIGHT || x < BOARD_LEFT || x >= BOARD_RGHT) break;
272                 if(!jump    && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
273                 if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
274                 if(board[y][x] < BlackPawn)   occup = 1; else
275                 if(board[y][x] < EmptySquare) occup = 2; else
276                                               occup = 4;
277                 if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
278                     cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
279                 }
280                 if(mode & 16) {              // castling
281                     i = 2;                   // kludge to elongate move indefinitely
282                     if(occup == 4) continue; // skip empty squares
283                     if(x == BOARD_LEFT   && board[y][x] == initialPosition[y][x]) // reached initial corner piece
284                         cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl);
285                     if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x])
286                         cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl);
287                     break;
288                 }
289                 if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate
290                 if(occup != 4) break; // not valid transit square
291             } while(--i);
292           }
293           dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
294         } while(retry--);             // and start doing them
295     }
296 } // next atom
297
298 // [HGM] move generation now based on hierarchy of subroutines for rays and combinations of rays
299
300 void
301 SlideForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
302 {
303   int i, rt, ft = ff;
304   for (i = 1;; i++) {
305       rt = rf + i;
306       if (rt >= BOARD_HEIGHT) break;
307       if (SameColor(board[rf][ff], board[rt][ft])) break;
308       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
309       if (board[rt][ft] != EmptySquare) break;
310   }
311 }
312
313 void
314 SlideBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
315 {
316   int i, rt, ft = ff;
317   for (i = 1;; i++) {
318       rt = rf - i;
319       if (rt < 0) 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;
323   }
324 }
325
326 void
327 SlideVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
328 {
329   SlideForward(board, flags, rf, ff, callback, closure);
330   SlideBackward(board, flags, rf, ff, callback, closure);
331 }
332
333 void
334 SlideSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
335 {
336   int i, s, rt = rf, ft;
337   for(s = -1; s <= 1; s+= 2) {
338     for (i = 1;; i++) {
339       ft = ff + i*s;
340       if (ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
341       if (SameColor(board[rf][ff], board[rt][ft])) break;
342       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
343       if (board[rt][ft] != EmptySquare) break;
344     }
345   }
346 }
347
348 void
349 SlideDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
350 {
351   int i, s, rt, ft;
352   for(s = -1; s <= 1; s+= 2) {
353     for (i = 1;; i++) {
354       rt = rf + i;
355       ft = ff + i * s;
356       if (rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
357       if (SameColor(board[rf][ff], board[rt][ft])) break;
358       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
359       if (board[rt][ft] != EmptySquare) break;
360     }
361   }
362 }
363
364 void
365 SlideDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
366 {
367   int i, s, rt, ft;
368   for(s = -1; s <= 1; s+= 2) {
369     for (i = 1;; i++) {
370       rt = rf - i;
371       ft = ff + i * s;
372       if (rt < 0 || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
373       if (SameColor(board[rf][ff], board[rt][ft])) break;
374       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
375       if (board[rt][ft] != EmptySquare) break;
376     }
377   }
378 }
379
380 void
381 Rook (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
382 {
383   SlideVertical(board, flags, rf, ff, callback, closure);
384   SlideSideways(board, flags, rf, ff, callback, closure);
385 }
386
387 void
388 Bishop (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
389 {
390   SlideDiagForward(board, flags, rf, ff, callback, closure);
391   SlideDiagBackward(board, flags, rf, ff, callback, closure);
392 }
393
394 void
395 Sting (Board board, int flags, int rf, int ff, int dy, int dx, MoveCallback callback, VOIDSTAR closure)
396 { // Lion-like move of Horned Falcon and Souring Eagle
397   int ft = ff + dx, rt = rf + dy;
398   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
399   if (!SameColor(board[rf][ff], board[rt][ft]))
400     callback(board, flags, board[rt][ft] != EmptySquare ? FirstLeg : NormalMove, rf, ff, rt, ft, closure);
401   ft += dx; rt += dy;
402   if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) return;
403   if (!SameColor(board[rf][ff], board[rt][ft]))
404     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
405 }
406
407 void
408 StepForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
409 {
410   int ft = ff, rt = rf + 1;
411   if (rt >= BOARD_HEIGHT) return;
412   if (SameColor(board[rf][ff], board[rt][ft])) return;
413   callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
414 }
415
416 void
417 StepBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
418 {
419   int ft = ff, rt = rf - 1;
420   if (rt < 0) return;
421   if (SameColor(board[rf][ff], board[rt][ft])) return;
422   callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
423 }
424
425 void
426 StepSideways (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
427 {
428   int ft, rt = rf;
429   ft = ff + 1;
430   if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
431       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
432   ft = ff - 1;
433   if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
434       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
435 }
436
437 void
438 StepDiagForward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
439 {
440   int ft, rt = rf + 1;
441   if (rt >= BOARD_HEIGHT) return;
442   ft = ff + 1;
443   if (!(rt >= BOARD_HEIGHT || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
444       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
445   ft = ff - 1;
446   if (!(rt >= BOARD_HEIGHT || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
447       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
448 }
449
450 void
451 StepDiagBackward (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
452 {
453   int ft, rt = rf - 1;
454   if(rt < 0) return;
455   ft = ff + 1;
456   if (!(rt < 0 || ft >= BOARD_RGHT) && !SameColor(board[rf][ff], board[rt][ft]))
457       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
458   ft = ff - 1;
459   if (!(rt < 0 || ft < BOARD_LEFT) && !SameColor(board[rf][ff], board[rt][ft]))
460       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
461 }
462
463 void
464 StepVertical (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
465 {
466   StepForward(board, flags, rf, ff, callback, closure);
467   StepBackward(board, flags, rf, ff, callback, closure);
468 }
469
470 void
471 Ferz (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
472 {
473   StepDiagForward(board, flags, rf, ff, callback, closure);
474   StepDiagBackward(board, flags, rf, ff, callback, closure);
475 }
476
477 void
478 Wazir (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
479 {
480   StepVertical(board, flags, rf, ff, callback, closure);
481   StepSideways(board, flags, rf, ff, callback, closure);
482 }
483
484 void
485 Knight (Board board, int flags, int rf, int ff, MoveCallback callback, VOIDSTAR closure)
486 {
487     int i, j, s, rt, ft;
488     for (i = -1; i <= 1; i += 2)
489         for (j = -1; j <= 1; j += 2)
490             for (s = 1; s <= 2; s++) {
491                 rt = rf + i*s;
492                 ft = ff + j*(3-s);
493                 if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
494                     && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
495                     && !SameColor(board[rf][ff], board[rt][ft]))
496                     callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
497             }
498 }
499
500 /* Call callback once for each pseudo-legal move in the given
501    position, except castling moves. A move is pseudo-legal if it is
502    legal, or if it would be legal except that it leaves the king in
503    check.  In the arguments, epfile is EP_NONE if the previous move
504    was not a double pawn push, or the file 0..7 if it was, or
505    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
506    Promotion moves generated are to Queen only.
507 */
508 void
509 GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
510 // speed: only do moves with this piece type
511 {
512     int rf, ff;
513     int i, j, d, s, fs, rs, rt, ft, m;
514     int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
515     int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
516
517     for (rf = 0; rf < BOARD_HEIGHT; rf++)
518       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
519           ChessSquare piece;
520
521           if(board[rf][ff] == EmptySquare) continue;
522           if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
523           m = 0; piece = board[rf][ff];
524           if(PieceToChar(piece) == '~')
525                  piece = (ChessSquare) ( DEMOTED piece );
526           if(filter != EmptySquare && piece != filter) continue;
527           if(pieceDesc[piece]) { MovesFromString(board, flags, ff, rf, pieceDesc[piece], callback, closure); continue; } // [HGM] gen
528           if(IS_SHOGI(gameInfo.variant))
529                  piece = (ChessSquare) ( SHOGI piece );
530
531           switch ((int)piece) {
532             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
533             default:
534               /* can't happen ([HGM] except for faries...) */
535               break;
536
537              case WhitePawn:
538               if(gameInfo.variant == VariantXiangqi) {
539                   /* [HGM] capture and move straight ahead in Xiangqi */
540                   if (rf < BOARD_HEIGHT-1 &&
541                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
542                            callback(board, flags, NormalMove,
543                                     rf, ff, rf + 1, ff, closure);
544                   }
545                   /* and move sideways when across the river */
546                   for (s = -1; s <= 1; s += 2) {
547                       if (rf >= BOARD_HEIGHT>>1 &&
548                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
549                           !WhitePiece(board[rf][ff+s]) ) {
550                            callback(board, flags, NormalMove,
551                                     rf, ff, rf, ff+s, closure);
552                       }
553                   }
554                   break;
555               }
556               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
557                   callback(board, flags,
558                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
559                            rf, ff, rf + 1, ff, closure);
560               }
561               if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board
562                   gameInfo.variant != VariantShatranj && /* [HGM] */
563                   gameInfo.variant != VariantCourier  && /* [HGM] */
564                   board[rf+2][ff] == EmptySquare ) {
565                       callback(board, flags, NormalMove,
566                                rf, ff, rf+2, ff, closure);
567               }
568               for (s = -1; s <= 1; s += 2) {
569                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
570                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
571                        BlackPiece(board[rf + 1][ff + s]))) {
572                       callback(board, flags,
573                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
574                                rf, ff, rf + 1, ff + s, closure);
575                   }
576                   if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board
577                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
578                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf < BOARD_HEIGHT-3 &&
579                           board[rf][ff + s] == BlackPawn &&
580                           board[rf+1][ff + s] == EmptySquare) {
581                           callback(board, flags, WhiteCapturesEnPassant,
582                                    rf, ff, rf+1, ff + s, closure);
583                       }
584                   }
585               }
586               break;
587
588             case BlackPawn:
589               if(gameInfo.variant == VariantXiangqi) {
590                   /* [HGM] capture straight ahead in Xiangqi */
591                   if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
592                            callback(board, flags, NormalMove,
593                                     rf, ff, rf - 1, ff, closure);
594                   }
595                   /* and move sideways when across the river */
596                   for (s = -1; s <= 1; s += 2) {
597                       if (rf < BOARD_HEIGHT>>1 &&
598                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
599                           !BlackPiece(board[rf][ff+s]) ) {
600                            callback(board, flags, NormalMove,
601                                     rf, ff, rf, ff+s, closure);
602                       }
603                   }
604                   break;
605               }
606               if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
607                   callback(board, flags,
608                            rf <= promoRank ? BlackPromotion : NormalMove,
609                            rf, ff, rf - 1, ff, closure);
610               }
611               if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand
612                   gameInfo.variant != VariantShatranj && /* [HGM] */
613                   gameInfo.variant != VariantCourier  && /* [HGM] */
614                   board[rf-2][ff] == EmptySquare) {
615                   callback(board, flags, NormalMove,
616                            rf, ff, rf-2, ff, closure);
617               }
618               for (s = -1; s <= 1; s += 2) {
619                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
620                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
621                        WhitePiece(board[rf - 1][ff + s]))) {
622                       callback(board, flags,
623                                rf <= promoRank ? BlackPromotion : NormalMove,
624                                rf, ff, rf - 1, ff + s, closure);
625                   }
626                   if (rf < BOARD_HEIGHT>>1) {
627                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
628                           (epfile == ff + s || epfile == EP_UNKNOWN) && rf > 2 &&
629                           board[rf][ff + s] == WhitePawn &&
630                           board[rf-1][ff + s] == EmptySquare) {
631                           callback(board, flags, BlackCapturesEnPassant,
632                                    rf, ff, rf-1, ff + s, closure);
633                       }
634                   }
635               }
636               break;
637
638             case WhiteUnicorn:
639             case BlackUnicorn:
640             case WhiteKnight:
641             case BlackKnight:
642               for (i = -1; i <= 1; i += 2)
643                 for (j = -1; j <= 1; j += 2)
644                   for (s = 1; s <= 2; s++) {
645                       rt = rf + i*s;
646                       ft = ff + j*(3-s);
647                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
648                           && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
649                           && !SameColor(board[rf][ff], board[rt][ft]))
650                       callback(board, flags, NormalMove,
651                                rf, ff, rt, ft, closure);
652                   }
653               break;
654
655             case SHOGI WhiteKnight:
656               for (s = -1; s <= 1; s += 2) {
657                   if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
658                       !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
659                       callback(board, flags, NormalMove,
660                                rf, ff, rf + 2, ff + s, closure);
661                   }
662               }
663               break;
664
665             case SHOGI BlackKnight:
666               for (s = -1; s <= 1; s += 2) {
667                   if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
668                       !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
669                       callback(board, flags, NormalMove,
670                                rf, ff, rf - 2, ff + s, closure);
671                   }
672               }
673               break;
674
675             case WhiteCannon:
676             case BlackCannon:
677               for (d = 0; d <= 1; d++)
678                 for (s = -1; s <= 1; s += 2) {
679                   m = 0;
680                   for (i = 1;; i++) {
681                       rt = rf + (i * s) * d;
682                       ft = ff + (i * s) * (1 - d);
683                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
684                       if (m == 0 && board[rt][ft] == EmptySquare)
685                                  callback(board, flags, NormalMove,
686                                           rf, ff, rt, ft, closure);
687                       if (m == 1 && board[rt][ft] != EmptySquare &&
688                           !SameColor(board[rf][ff], board[rt][ft]) )
689                                  callback(board, flags, NormalMove,
690                                           rf, ff, rt, ft, closure);
691                       if (board[rt][ft] != EmptySquare && m++) break;
692                   }
693                 }
694               break;
695
696             /* Gold General (and all its promoted versions) . First do the */
697             /* diagonal forward steps, then proceed as normal Wazir        */
698             case SHOGI (PROMOTED WhitePawn):
699                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
700             case SHOGI (PROMOTED BlackPawn):
701                 if(gameInfo.variant == VariantShogi) goto BlackGold;
702                 SlideVertical(board, flags, rf, ff, callback, closure);
703                 break;
704
705             case SHOGI (PROMOTED WhiteKnight):
706                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
707             case SHOGI BlackDrunk:
708             case SHOGI BlackAlfil:
709                 Ferz(board, flags, rf, ff, callback, closure);
710                 StepSideways(board, flags, rf, ff, callback, closure);
711                 StepBackward(board, flags, rf, ff, callback, closure);
712                 break;
713
714             case SHOGI (PROMOTED BlackKnight):
715                 if(gameInfo.variant == VariantShogi) goto BlackGold;
716             case SHOGI WhiteDrunk:
717             case SHOGI WhiteAlfil:
718                 Ferz(board, flags, rf, ff, callback, closure);
719                 StepSideways(board, flags, rf, ff, callback, closure);
720                 StepForward(board, flags, rf, ff, callback, closure);
721                 break;
722
723
724             case SHOGI WhiteStag:
725             case SHOGI BlackStag:
726                 if(gameInfo.variant == VariantShogi) goto BlackGold;
727                 SlideVertical(board, flags, rf, ff, callback, closure);
728                 Ferz(board, flags, rf, ff, callback, closure);
729                 StepSideways(board, flags, rf, ff, callback, closure);
730                 break;
731
732             case SHOGI (PROMOTED WhiteQueen):
733             case SHOGI WhiteTokin:
734             case SHOGI WhiteWazir:
735             WhiteGold:
736                 StepDiagForward(board, flags, rf, ff, callback, closure);
737                 Wazir(board, flags, rf, ff, callback, closure);
738                 break;
739
740             case SHOGI (PROMOTED BlackQueen):
741             case SHOGI BlackTokin:
742             case SHOGI BlackWazir:
743             BlackGold:
744                 StepDiagBackward(board, flags, rf, ff, callback, closure);
745                 Wazir(board, flags, rf, ff, callback, closure);
746                 break;
747
748             case WhiteWazir:
749             case BlackWazir:
750                 Wazir(board, flags, rf, ff, callback, closure);
751                 break;
752
753             case SHOGI WhiteMarshall:
754             case SHOGI BlackMarshall:
755                 Ferz(board, flags, rf, ff, callback, closure);
756                 for (d = 0; d <= 1; d++)
757                     for (s = -2; s <= 2; s += 4) {
758                         rt = rf + s * d;
759                         ft = ff + s * (1 - d);
760                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
761                         if (!SameColor(board[rf][ff], board[rt][ft]) )
762                             callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
763                     }
764                 break;
765
766             case SHOGI WhiteAngel:
767             case SHOGI BlackAngel:
768                 Wazir(board, flags, rf, ff, callback, closure);
769
770             case WhiteAlfil:
771             case BlackAlfil:
772                 /* [HGM] support Shatranj pieces */
773                 for (rs = -1; rs <= 1; rs += 2)
774                   for (fs = -1; fs <= 1; fs += 2) {
775                       rt = rf + 2 * rs;
776                       ft = ff + 2 * fs;
777                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
778                           && ( gameInfo.variant != VariantXiangqi ||
779                                board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
780
781                           && !SameColor(board[rf][ff], board[rt][ft]))
782                                callback(board, flags, NormalMove,
783                                         rf, ff, rt, ft, closure);
784                       if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
785                          gameInfo.variant == VariantChu      || gameInfo.variant == VariantXiangqi) continue; // classical Alfil
786                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
787                       ft = ff + fs;
788                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
789                           && !SameColor(board[rf][ff], board[rt][ft]))
790                                callback(board, flags, NormalMove,
791                                         rf, ff, rt, ft, closure);
792                   }
793                 if(gameInfo.variant == VariantSpartan)
794                    for(fs = -1; fs <= 1; fs += 2) {
795                       ft = ff + fs;
796                       if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
797                                callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
798                    }
799                 break;
800
801             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
802             case WhiteCardinal:
803             case BlackCardinal:
804               if(gameInfo.variant == VariantChuChess) goto DragonHorse;
805               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
806                 for (s = -2; s <= 2; s += 4) {
807                       rt = rf + s * d;
808                       ft = ff + s * (1 - d);
809                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
810                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
811                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
812                   }
813
814             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
815             case SHOGI WhiteCardinal:
816             case SHOGI BlackCardinal:
817             case SHOGI WhitePCardinal:
818             case SHOGI BlackPCardinal:
819             DragonHorse:
820                 Bishop(board, flags, rf, ff, callback, closure);
821                 Wazir(board, flags, rf, ff, callback, closure);
822                 break;
823
824             /* Capablanca Archbishop continues as Knight                  */
825             case WhiteAngel:
826             case BlackAngel:
827                 Knight(board, flags, rf, ff, callback, closure);
828
829             /* Shogi Bishops are ordinary Bishops */
830             case SHOGI WhiteBishop:
831             case SHOGI BlackBishop:
832             case SHOGI WhitePBishop:
833             case SHOGI BlackPBishop:
834             case WhiteBishop:
835             case BlackBishop:
836                 Bishop(board, flags, rf, ff, callback, closure);
837                 break;
838
839             /* Shogi Lance is unlike anything, and asymmetric at that */
840             case SHOGI WhiteQueen:
841               if(gameInfo.variant == VariantChu) goto doQueen;
842               for(i = 1;; i++) {
843                       rt = rf + i;
844                       ft = ff;
845                       if (rt >= BOARD_HEIGHT) break;
846                       if (SameColor(board[rf][ff], board[rt][ft])) break;
847                       callback(board, flags, NormalMove,
848                                rf, ff, rt, ft, closure);
849                       if (board[rt][ft] != EmptySquare) break;
850               }
851               break;
852
853             case SHOGI BlackQueen:
854               if(gameInfo.variant == VariantChu) goto doQueen;
855               for(i = 1;; i++) {
856                       rt = rf - i;
857                       ft = ff;
858                       if (rt < 0) break;
859                       if (SameColor(board[rf][ff], board[rt][ft])) break;
860                       callback(board, flags, NormalMove,
861                                rf, ff, rt, ft, closure);
862                       if (board[rt][ft] != EmptySquare) break;
863               }
864               break;
865
866             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
867             case WhiteDragon:
868             case BlackDragon:
869               if(gameInfo.variant == VariantChuChess) goto DragonKing;
870               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
871                 for (s = -2; s <= 2; s += 4) {
872                       rt = rf + s * d;
873                       ft = ff + s * (1 - d);
874                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
875                       if (board[rf+rt>>1][ff+ft>>1] == EmptySquare && gameInfo.variant != VariantSpartan) continue;
876                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
877                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
878                   }
879               if(gameInfo.variant == VariantSpartan) // in Spartan Chess restrict range to modern Dababba
880                 Wazir(board, flags, rf, ff, callback, closure);
881               else
882                 Rook(board, flags, rf, ff, callback, closure);
883               break;
884
885             /* Shogi Dragon King has to continue as Ferz after Rook moves */
886             case SHOGI WhiteDragon:
887             case SHOGI BlackDragon:
888             case SHOGI WhitePDragon:
889             case SHOGI BlackPDragon:
890             DragonKing:
891                 Rook(board, flags, rf, ff, callback, closure);
892                 Ferz(board, flags, rf, ff, callback, closure);
893                 break;
894               m++;
895
896             /* Capablanca Chancellor sets flag to continue as Knight      */
897             case WhiteMarshall:
898             case BlackMarshall:
899                 Rook(board, flags, rf, ff, callback, closure);
900                 if(gameInfo.variant == VariantSpartan) // in Spartan Chess Chancellor is used for Dragon King.
901                     Ferz(board, flags, rf, ff, callback, closure);
902                 else
903                     Knight(board, flags, rf, ff, callback, closure);
904                 break;
905
906             /* Shogi Rooks are ordinary Rooks */
907             case SHOGI WhiteRook:
908             case SHOGI BlackRook:
909             case SHOGI WhitePRook:
910             case SHOGI BlackPRook:
911             case WhiteRook:
912             case BlackRook:
913                 Rook(board, flags, rf, ff, callback, closure);
914                 break;
915
916             case WhiteQueen:
917             case BlackQueen:
918             case SHOGI WhiteMother:
919             case SHOGI BlackMother:
920             doQueen:
921                 Rook(board, flags, rf, ff, callback, closure);
922                 Bishop(board, flags, rf, ff, callback, closure);
923                 break;
924
925            case SHOGI WhitePawn:
926                 StepForward(board, flags, rf, ff, callback, closure);
927                 break;
928
929             case SHOGI BlackPawn:
930                 StepBackward(board, flags, rf, ff, callback, closure);
931                 break;
932
933             case WhiteMan:
934                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
935             case SHOGI WhiteFerz:
936                 Ferz(board, flags, rf, ff, callback, closure);
937                 StepForward(board, flags, rf, ff, callback, closure);
938                 break;
939
940             case BlackMan:
941                 if(gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN) goto commoner;
942             case SHOGI BlackFerz:
943                 StepBackward(board, flags, rf, ff, callback, closure);
944
945             case WhiteFerz:
946             case BlackFerz:
947                 /* [HGM] support Shatranj pieces */
948                 Ferz(board, flags, rf, ff, callback, closure);
949                 break;
950
951             case WhiteSilver:
952             case BlackSilver:
953                 Knight(board, flags, rf, ff, callback, closure); // [HGM] superchess: use for Centaur
954
955             commoner:
956             case SHOGI WhiteMonarch:
957             case SHOGI BlackMonarch:
958             case SHOGI WhiteKing:
959             case SHOGI BlackKing:
960             case WhiteKing:
961             case BlackKing:
962                 Ferz(board, flags, rf, ff, callback, closure);
963                 Wazir(board, flags, rf, ff, callback, closure);
964                 break;
965
966             case WhiteNightrider:
967             case BlackNightrider:
968               for (i = -1; i <= 1; i += 2)
969                 for (j = -1; j <= 1; j += 2)
970                   for (s = 1; s <= 2; s++) {  int k;
971                     for(k=1;; k++) {
972                       rt = rf + k*i*s;
973                       ft = ff + k*j*(3-s);
974                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
975                       if (SameColor(board[rf][ff], board[rt][ft])) break;
976                       callback(board, flags, NormalMove,
977                                rf, ff, rt, ft, closure);
978                       if (board[rt][ft] != EmptySquare) break;
979                     }
980                   }
981               break;
982
983             Amazon:
984                 Bishop(board, flags, rf, ff, callback, closure);
985                 Rook(board, flags, rf, ff, callback, closure);
986                 Knight(board, flags, rf, ff, callback, closure);
987                 break;
988
989             // Use Lance as Berolina / Spartan Pawn.
990             case WhiteLance:
991               if(gameInfo.variant == VariantSuper) goto Amazon;
992               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
993                   callback(board, flags,
994                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
995                            rf, ff, rf + 1, ff, closure);
996               for (s = -1; s <= 1; s += 2) {
997                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
998                       callback(board, flags,
999                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
1000                                rf, ff, rf + 1, ff + s, closure);
1001                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
1002                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
1003               }
1004               break;
1005
1006             case BlackLance:
1007               if(gameInfo.variant == VariantSuper) goto Amazon;
1008               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
1009                   callback(board, flags,
1010                            rf <= promoRank ? BlackPromotion : NormalMove,
1011                            rf, ff, rf - 1, ff, closure);
1012               for (s = -1; s <= 1; s += 2) {
1013                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
1014                       callback(board, flags,
1015                                rf <= promoRank ? BlackPromotion : NormalMove,
1016                                rf, ff, rf - 1, ff + s, closure);
1017                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
1018                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
1019               }
1020             break;
1021
1022             case SHOGI WhiteNothing:
1023             case SHOGI BlackNothing:
1024             case SHOGI WhiteLion:
1025             case SHOGI BlackLion:
1026             case WhiteLion:
1027             case BlackLion:
1028               for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
1029                 if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
1030                 if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
1031                 callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
1032                          rf, ff, rt, ft, closure);
1033               }
1034               break;
1035
1036             case SHOGI WhiteFalcon:
1037             case SHOGI BlackFalcon:
1038             case SHOGI WhitePDagger:
1039             case SHOGI BlackPDagger:
1040                 SlideSideways(board, flags, rf, ff, callback, closure);
1041                 StepVertical(board, flags, rf, ff, callback, closure);
1042                 break;
1043
1044             case SHOGI WhiteCobra:
1045             case SHOGI BlackCobra:
1046                 StepVertical(board, flags, rf, ff, callback, closure);
1047                 break;
1048
1049             case SHOGI (PROMOTED WhiteFerz):
1050                 if(gameInfo.variant == VariantShogi) goto WhiteGold;
1051             case SHOGI (PROMOTED BlackFerz):
1052                 if(gameInfo.variant == VariantShogi) goto BlackGold;
1053             case SHOGI WhitePSword:
1054             case SHOGI BlackPSword:
1055                 SlideVertical(board, flags, rf, ff, callback, closure);
1056                 StepSideways(board, flags, rf, ff, callback, closure);
1057                 break;
1058
1059             case SHOGI WhiteUnicorn:
1060             case SHOGI BlackUnicorn:
1061                 Ferz(board, flags, rf, ff, callback, closure);
1062                 StepVertical(board, flags, rf, ff, callback, closure);
1063                 break;
1064
1065             case SHOGI WhiteMan:
1066                 StepDiagForward(board, flags, rf, ff, callback, closure);
1067                 StepVertical(board, flags, rf, ff, callback, closure);
1068                 break;
1069
1070             case SHOGI BlackMan:
1071                 StepDiagBackward(board, flags, rf, ff, callback, closure);
1072                 StepVertical(board, flags, rf, ff, callback, closure);
1073                 break;
1074
1075             case SHOGI WhiteHCrown:
1076             case SHOGI BlackHCrown:
1077                 Bishop(board, flags, rf, ff, callback, closure);
1078                 SlideSideways(board, flags, rf, ff, callback, closure);
1079                 break;
1080
1081             case SHOGI WhiteCrown:
1082             case SHOGI BlackCrown:
1083                 Bishop(board, flags, rf, ff, callback, closure);
1084                 SlideVertical(board, flags, rf, ff, callback, closure);
1085                 break;
1086
1087             case SHOGI WhiteHorned:
1088                 Sting(board, flags, rf, ff, 1, 0, callback, closure);
1089                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1090                 if(killX >= 0) break;
1091                 Bishop(board, flags, rf, ff, callback, closure);
1092                 SlideSideways(board, flags, rf, ff, callback, closure);
1093                 SlideBackward(board, flags, rf, ff, callback, closure);
1094                 break;
1095
1096             case SHOGI BlackHorned:
1097                 Sting(board, flags, rf, ff, -1, 0, callback, closure);
1098                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1099                 if(killX >= 0) break;
1100                 Bishop(board, flags, rf, ff, callback, closure);
1101                 SlideSideways(board, flags, rf, ff, callback, closure);
1102                 SlideForward(board, flags, rf, ff, callback, closure);
1103                 break;
1104
1105             case SHOGI WhiteEagle:
1106                 Sting(board, flags, rf, ff, 1,  1, callback, closure);
1107                 Sting(board, flags, rf, ff, 1, -1, callback, closure);
1108                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1109                 if(killX >= 0) break;
1110                 Rook(board, flags, rf, ff, callback, closure);
1111                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1112                 break;
1113
1114             case SHOGI BlackEagle:
1115                 Sting(board, flags, rf, ff, -1,  1, callback, closure);
1116                 Sting(board, flags, rf, ff, -1, -1, callback, closure);
1117                 callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1118                 if(killX >= 0) break;
1119                 Rook(board, flags, rf, ff, callback, closure);
1120                 SlideDiagForward(board, flags, rf, ff, callback, closure);
1121                 break;
1122
1123             case SHOGI WhiteDolphin:
1124             case SHOGI BlackHorse:
1125                 SlideDiagBackward(board, flags, rf, ff, callback, closure);
1126                 SlideVertical(board, flags, rf, ff, callback, closure);
1127                 break;
1128
1129             case SHOGI BlackDolphin:
1130             case SHOGI WhiteHorse:
1131                 SlideDiagForward(board, flags, rf, ff, callback, closure);
1132                 SlideVertical(board, flags, rf, ff, callback, closure);
1133                 break;
1134
1135             case SHOGI WhiteLance:
1136                 SlideForward(board, flags, rf, ff, callback, closure);
1137                 break;
1138
1139             case SHOGI BlackLance:
1140                 SlideBackward(board, flags, rf, ff, callback, closure);
1141                 break;
1142
1143             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
1144             case BlackFalcon:
1145             case WhiteCobra:
1146             case BlackCobra:
1147               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
1148               break;
1149
1150           }
1151       }
1152 }
1153
1154
1155 typedef struct {
1156     MoveCallback cb;
1157     VOIDSTAR cl;
1158 } GenLegalClosure;
1159
1160 int rFilter, fFilter; // [HGM] speed: sorry, but I get a bit tired of this closure madness
1161 Board xqCheckers, nullBoard;
1162
1163 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
1164                                 int rf, int ff, int rt, int ft,
1165                                 VOIDSTAR closure));
1166
1167 void
1168 GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1169 {
1170     register GenLegalClosure *cl = (GenLegalClosure *) closure;
1171
1172     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
1173
1174     if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
1175
1176     if (!(flags & F_IGNORE_CHECK) ) {
1177       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
1178       if(promo) {
1179             int r, f, kings=0;
1180             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT;       f++)
1181                 kings += (board[r][f] == BlackKing);
1182             if(kings >= 2)
1183               promo = 0;
1184             else
1185                 board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
1186         }
1187         check = CheckTest(board, flags, rf, ff, rt, ft,
1188                   kind == WhiteCapturesEnPassant ||
1189                   kind == BlackCapturesEnPassant);
1190         if(promo) board[rf][ff] = BlackLance;
1191       if(check) return;
1192     }
1193     if (flags & F_ATOMIC_CAPTURE) {
1194       if (board[rt][ft] != EmptySquare ||
1195           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
1196         int r, f;
1197         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
1198         if (board[rf][ff] == king) return;
1199         for (r = rt-1; r <= rt+1; r++) {
1200           for (f = ft-1; f <= ft+1; f++) {
1201             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
1202                 board[r][f] == king) return;
1203           }
1204         }
1205       }
1206     }
1207     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
1208 }
1209
1210
1211 typedef struct {
1212     int rf, ff, rt, ft;
1213     ChessMove kind;
1214     int captures; // [HGM] losers
1215 } LegalityTestClosure;
1216
1217
1218 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
1219    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
1220    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
1221    moves that would destroy your own king.  The CASTLE_OK flags are
1222    true if castling is not yet ruled out by a move of the king or
1223    rook.  Return TRUE if the player on move is currently in check and
1224    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
1225 int
1226 GenLegal (Board board, int  flags, MoveCallback callback, VOIDSTAR closure, ChessSquare filter)
1227 {
1228     GenLegalClosure cl;
1229     int ff, ft, k, left, right, swap;
1230     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
1231     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
1232     int inCheck = !ignoreCheck && CheckTest(board, flags, -1, -1, -1, -1, FALSE); // kludge alert: this would mark pre-existing checkers if status==1
1233     char *p;
1234
1235     cl.cb = callback;
1236     cl.cl = closure;
1237     xqCheckers[EP_STATUS] *= 2; // quasi: if previous CheckTest has been marking, we now set flag for suspending same checkers
1238     if(filter == EmptySquare) rFilter = fFilter = -1; // [HGM] speed: do not filter on square if we do not filter on piece
1239     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl, filter);
1240
1241     if (inCheck) return TRUE;
1242
1243     /* Generate castling moves */
1244     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
1245         wKing = WhiteUnicorn; bKing = BlackUnicorn;
1246     }
1247
1248     p = (flags & F_WHITE_ON_MOVE ? pieceDesc[wKing] : pieceDesc[bKing]);
1249     if(p && strchr(p, 'O')) return FALSE; // [HGM] gen: castlings were already generated from string
1250
1251     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
1252         if ((flags & F_WHITE_ON_MOVE) &&
1253             (flags & F_WHITE_KCASTLE_OK) &&
1254             board[0][ff] == wKing &&
1255             board[0][ff + 1] == EmptySquare &&
1256             board[0][ff + 2] == EmptySquare &&
1257             board[0][BOARD_RGHT-3] == EmptySquare &&
1258             board[0][BOARD_RGHT-2] == EmptySquare &&
1259             board[0][BOARD_RGHT-1] == WhiteRook &&
1260             castlingRights[0] != NoRights && /* [HGM] check rights */
1261             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1262             (ignoreCheck ||
1263              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
1264               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
1265               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
1266               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
1267
1268             callback(board, flags,
1269                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
1270                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1271         }
1272         if ((flags & F_WHITE_ON_MOVE) &&
1273             (flags & F_WHITE_QCASTLE_OK) &&
1274             board[0][ff] == wKing &&
1275             board[0][ff - 1] == EmptySquare &&
1276             board[0][ff - 2] == EmptySquare &&
1277             board[0][BOARD_LEFT+2] == EmptySquare &&
1278             board[0][BOARD_LEFT+1] == EmptySquare &&
1279             board[0][BOARD_LEFT+0] == WhiteRook &&
1280             castlingRights[1] != NoRights && /* [HGM] check rights */
1281             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
1282             (ignoreCheck ||
1283              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
1284               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
1285               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
1286
1287             callback(board, flags,
1288                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
1289                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
1290         }
1291         if (!(flags & F_WHITE_ON_MOVE) &&
1292             (flags & F_BLACK_KCASTLE_OK) &&
1293             board[BOARD_HEIGHT-1][ff] == bKing &&
1294             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
1295             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
1296             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
1297             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
1298             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
1299             castlingRights[3] != NoRights && /* [HGM] check rights */
1300             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1301             (ignoreCheck ||
1302              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
1303               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
1304               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
1305               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
1306
1307             callback(board, flags,
1308                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
1309                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
1310         }
1311         if (!(flags & F_WHITE_ON_MOVE) &&
1312             (flags & F_BLACK_QCASTLE_OK) &&
1313             board[BOARD_HEIGHT-1][ff] == bKing &&
1314             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
1315             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
1316             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
1317             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
1318             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
1319             castlingRights[4] != NoRights && /* [HGM] check rights */
1320             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
1321             (ignoreCheck ||
1322              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
1323               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
1324               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
1325
1326             callback(board, flags,
1327                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
1328                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
1329         }
1330     }
1331
1332   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
1333
1334     /* generate all potential FRC castling moves (KxR), ignoring flags */
1335     /* [HGM] test if the Rooks we find have castling rights */
1336     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
1337
1338
1339     if ((flags & F_WHITE_ON_MOVE) != 0) {
1340         ff = castlingRights[2]; /* King file if we have any rights */
1341         if(ff != NoRights && board[0][ff] == WhiteKing) {
1342     if (appData.debugMode) {
1343         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
1344                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
1345     }
1346             ft = castlingRights[0]; /* Rook file if we have H-side rights */
1347             left  = ff+1;
1348             right = BOARD_RGHT-2;
1349             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1350             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1351                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1352             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1353                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1354             if(ft != NoRights && board[0][ft] == WhiteRook) {
1355                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteHSideCastleFR, 0, ff, 0, ft, closure);
1356                 if(swap)                        callback(board, flags, WhiteHSideCastleFR, 0, ft, 0, ff, closure);
1357             }
1358
1359             ft = castlingRights[1]; /* Rook file if we have A-side rights */
1360             left  = BOARD_LEFT+2;
1361             right = ff-1;
1362             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1363             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1364                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
1365             if(ft == 0 && ff != 1 && board[0][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b1 */
1366             if(ff > BOARD_LEFT+2)
1367             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1368                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
1369             if(ft != NoRights && board[0][ft] == WhiteRook) {
1370                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, WhiteASideCastleFR, 0, ff, 0, ft, closure);
1371                 if(swap)                        callback(board, flags, WhiteASideCastleFR, 0, ft, 0, ff, closure);
1372             }
1373         }
1374     } else {
1375         ff = castlingRights[5]; /* King file if we have any rights */
1376         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
1377             ft = castlingRights[3]; /* Rook file if we have H-side rights */
1378             left  = ff+1;
1379             right = BOARD_RGHT-2;
1380             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
1381             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1382                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1383             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
1384                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1385             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1386                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1387                 if(swap)                        callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1388             }
1389
1390             ft = castlingRights[4]; /* Rook file if we have A-side rights */
1391             left  = BOARD_LEFT+2;
1392             right = ff-1;
1393             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
1394             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
1395                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
1396             if(ft == 0 && ff != 1 && board[BOARD_HEIGHT-1][1] != EmptySquare) ft = NoRights; /* Rook can be blocked on b8 */
1397             if(ff > BOARD_LEFT+2)
1398             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
1399                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
1400             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook) {
1401                 if(flags & F_FRC_TYPE_CASTLING) callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ft, closure);
1402                 if(swap)                        callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, ft, BOARD_HEIGHT-1, ff, closure);
1403             }
1404         }
1405     }
1406
1407   }
1408
1409     return FALSE;
1410 }
1411
1412
1413 typedef struct {
1414     int rking, fking;
1415     int check;
1416 } CheckTestClosure;
1417
1418
1419 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
1420                                  int rf, int ff, int rt, int ft,
1421                                  VOIDSTAR closure));
1422
1423
1424 void
1425 CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1426 {
1427     register CheckTestClosure *cl = (CheckTestClosure *) closure;
1428
1429     if (rt == cl->rking && ft == cl->fking) {
1430         if(xqCheckers[EP_STATUS] >= 2 && xqCheckers[rf][ff]) return; // checker is piece with suspended checking power
1431         cl->check++;
1432         xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
1433     }
1434     if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
1435         && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
1436         cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
1437 }
1438
1439
1440 /* If the player on move were to move from (rf, ff) to (rt, ft), would
1441    he leave himself in check?  Or if rf == -1, is the player on move
1442    in check now?  enPassant must be TRUE if the indicated move is an
1443    e.p. capture.  The possibility of castling out of a check along the
1444    back rank is not accounted for (i.e., we still return nonzero), as
1445    this is illegal anyway.  Return value is the number of times the
1446    king is in check. */
1447 int
1448 CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant)
1449 {
1450     CheckTestClosure cl;
1451     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1452     ChessSquare captured = EmptySquare, ep=0, trampled=0;
1453     /*  Suppress warnings on uninitialized variables    */
1454
1455     if(gameInfo.variant == VariantXiangqi)
1456         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
1457     if(gameInfo.variant == VariantKnightmate)
1458         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
1459     if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check
1460         int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch;
1461         for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1462             if(board[r][f] == k || board[r][f] == prince) {
1463                 if(++royals > 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!)
1464                 king = board[r][f]; // remember hich one we had
1465             }
1466         }
1467     }
1468
1469     if (rt >= 0) {
1470         if (enPassant) {
1471             captured = board[rf][ft];
1472             board[rf][ft] = EmptySquare;
1473         } else {
1474             captured = board[rt][ft];
1475             if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; }
1476         }
1477         if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop
1478             board[rt][ft] = board[rf][ff];
1479             board[rf][ff] = EmptySquare;
1480         }
1481         ep = board[EP_STATUS];
1482         if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
1483             ChessSquare victim = killX < 0 ? EmptySquare : trampled;
1484             if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
1485                 (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
1486                 (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
1487                      board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
1488         }
1489     }
1490
1491     /* For compatibility with ICS wild 9, we scan the board in the
1492        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1493        and we test only whether that one is in check. */
1494     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1495         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1496           if (board[cl.rking][cl.fking] == king) {
1497               cl.check = 0;
1498               if(gameInfo.variant == VariantXiangqi) {
1499                   /* [HGM] In Xiangqi opposing Kings means check as well */
1500                   int i, dir;
1501                   dir = (king >= BlackPawn) ? -1 : 1;
1502                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1503                                 board[i][cl.fking] == EmptySquare; i+=dir );
1504                   if(i>=0 && i<BOARD_HEIGHT &&
1505                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1506                           cl.check++;
1507               }
1508               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl, EmptySquare);
1509               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1510                  goto undo_move;  /* 2-level break */
1511           }
1512       }
1513
1514   undo_move:
1515
1516     if (rt >= 0) {
1517         if(rf != DROP_RANK) // [HGM] drop
1518             board[rf][ff] = board[rt][ft];
1519         if (enPassant) {
1520             board[rf][ft] = captured;
1521             board[rt][ft] = EmptySquare;
1522         } else {
1523             if(killX >= 0) board[killY][killX] = trampled;
1524             board[rt][ft] = captured;
1525         }
1526         board[EP_STATUS] = ep;
1527     }
1528
1529     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1530 }
1531
1532 int
1533 HasLion (Board board, int flags)
1534 {
1535     int lion = F_WHITE_ON_MOVE & flags ? WhiteLion : BlackLion;
1536     int r, f;
1537     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1538         if(board[r][f] == lion) return 1;
1539     return 0;
1540 }
1541
1542 ChessMove
1543 LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft)
1544 {   // [HGM] put drop legality testing in separate routine for clarity
1545     int n;
1546 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1547     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1548     n = PieceToNumber(piece);
1549     if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1550         && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us
1551         return ImpossibleMove; // piece not available
1552     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1553         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1554            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1555             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1556             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1557         if(piece == WhitePawn || piece == BlackPawn) {
1558             int r, max = 1 + (BOARD_HEIGHT == 7); // two Pawns per file in Tori!
1559             for(r=1; r<BOARD_HEIGHT-1; r++)
1560                 if(!(max -= (board[r][ft] == piece))) return IllegalMove; // or there already is a Pawn in file
1561             // should still test if we mate with this Pawn
1562         }
1563     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1564         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1565     } else {
1566         if( (piece == WhitePawn || piece == BlackPawn) &&
1567             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1568             return IllegalMove; /* no pawn drops on 1st/8th */
1569     }
1570 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1571     if (!(flags & F_IGNORE_CHECK) &&
1572         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1573     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1574 }
1575
1576 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1577                                     int rf, int ff, int rt, int ft,
1578                                     VOIDSTAR closure));
1579
1580 void
1581 LegalityTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1582 {
1583     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1584
1585     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1586         cl->captures++; // [HGM] losers: count legal captures
1587     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1588       cl->kind = kind;
1589 }
1590
1591 ChessMove
1592 LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar)
1593 {
1594     LegalityTestClosure cl; ChessSquare piece, filterPiece;
1595
1596     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1597     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1598     piece = filterPiece = board[rf][ff];
1599     if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
1600
1601     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1602     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1603     if((piece == WhiteFalcon || piece == BlackFalcon ||
1604         piece == WhiteCobra  || piece == BlackCobra) && gameInfo.variant != VariantChu)
1605         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1606
1607     cl.rf = rf;
1608     cl.ff = ff;
1609     cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
1610     cl.ft = fFilter = ft;
1611     cl.kind = IllegalMove;
1612     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1613     if(flags & F_MANDATORY_CAPTURE) filterPiece = EmptySquare; // [HGM] speed: do not filter in suicide, to find all captures
1614     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl, filterPiece);
1615     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1616                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1617         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1618
1619     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1620     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1621         if(board[rf][ff] < BlackPawn) { // white
1622             if(rf != 0) return IllegalMove; // must be on back rank
1623             if(!(board[VIRGIN][ff] & VIRGIN_W)) return IllegalMove; // non-virgin
1624             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1625             if(cl.kind == WhiteHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1626             if(cl.kind == WhiteASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1627         } else {
1628             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1629             if(!(board[VIRGIN][ff] & VIRGIN_B)) return IllegalMove; // non-virgin
1630             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1631             if(cl.kind == BlackHSideCastleFR && (ff == BOARD_RGHT-2 || ff == BOARD_RGHT-3)) return ImpossibleMove;
1632             if(cl.kind == BlackASideCastleFR && (ff == BOARD_LEFT+2 || ff == BOARD_LEFT+3)) return ImpossibleMove;
1633         }
1634     } else
1635     if(gameInfo.variant == VariantChu) {
1636         if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
1637         if(promoChar != '+')
1638             return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1639         if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove;
1640         return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1641     } else
1642     if(gameInfo.variant == VariantShogi) {
1643         /* [HGM] Shogi promotions. '=' means defer */
1644         if(rf != DROP_RANK && cl.kind == NormalMove) {
1645             ChessSquare piece = board[rf][ff];
1646
1647             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1648             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1649                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1650                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1651                   promoChar = '+'; // allowed ICS notations
1652 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1653             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1654                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1655             else if(flags & F_WHITE_ON_MOVE) {
1656                 if( (int) piece < (int) WhiteWazir &&
1657                      (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) {
1658                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1659                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1660                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1661                     else /* promotion optional, default is defer */
1662                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1663                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1664             } else {
1665                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1666                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1667                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1668                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1669                     else /* promotion optional, default is defer */
1670                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1671                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1672             }
1673         }
1674     } else
1675     if (promoChar != NULLCHAR) {
1676         if(cl.kind == NormalMove && promoChar == '+') { // allow shogi-style promotion is pieceToChar specifies them
1677             ChessSquare piece = board[rf][ff];
1678             if(piece < BlackPawn ? piece > WhiteMan : piece > BlackMan) return ImpossibleMove; // already promoted
1679             // should test if in zone, really
1680             if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
1681                 return IllegalMove;
1682             if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
1683         } else
1684         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1685         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1686             ChessSquare piece = CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar));
1687             if(piece == EmptySquare)
1688                 cl.kind = ImpossibleMove; // non-existing piece
1689             if(gameInfo.variant == VariantChuChess && promoChar == 'l' && HasLion(board, flags)) {
1690                 cl.kind = IllegalMove; // no two Lions
1691             } else if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion ) {
1692                 if(promoChar != PieceToChar(BlackKing)) {
1693                     if(CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1694                     if(piece == BlackLance) cl.kind = ImpossibleMove;
1695                 } else { // promotion to King allowed only if we do not have two yet
1696                     int r, f, kings = 0;
1697                     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) kings += (board[r][f] == BlackKing);
1698                     if(kings == 2) cl.kind = IllegalMove;
1699                 }
1700             } else if(piece == WhitePawn && rt == BOARD_HEIGHT-1 ||
1701                           piece == BlackPawn && rt == 0) cl.kind = IllegalMove; // cannot stay Pawn on last rank in any variant
1702             else if((piece == WhiteUnicorn || piece == BlackUnicorn) && gameInfo.variant == VariantKnightmate)
1703              cl.kind = IllegalMove; // promotion to Royal Knight not allowed
1704             else if((piece == WhiteKing || piece == BlackKing) && gameInfo.variant != VariantSuicide && gameInfo.variant != VariantGiveaway)
1705              cl.kind = IllegalMove; // promotion to King usually not allowed
1706         } else {
1707             cl.kind = IllegalMove;
1708         }
1709     }
1710     return cl.kind;
1711 }
1712
1713 typedef struct {
1714     int count;
1715 } MateTestClosure;
1716
1717 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1718                                 int rf, int ff, int rt, int ft,
1719                                 VOIDSTAR closure));
1720
1721 void
1722 MateTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1723 {
1724     register MateTestClosure *cl = (MateTestClosure *) closure;
1725
1726     cl->count++;
1727 }
1728
1729 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1730 int
1731 MateTest (Board board, int flags)
1732 {
1733     MateTestClosure cl;
1734     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1735     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1736
1737     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1738         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1739         nrKing += (board[r][f] == king);   // stm has king
1740         if( board[r][f] != EmptySquare ) {
1741             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1742                  myPieces++;
1743             else hisPieces++;
1744         }
1745     }
1746     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1747         case VariantShatranj:
1748                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1749         default:
1750                 break;
1751         case VariantAtomic:
1752                 if(nrKing == 0) return MT_NOKING;
1753                 break;
1754         case VariantLosers:
1755                 if(myPieces == 1) return MT_BARE;
1756     }
1757     cl.count = 0;
1758     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl, EmptySquare);
1759     // [HGM] 3check: yet to do!
1760     if (cl.count > 0) {
1761         return inCheck ? MT_CHECK : MT_NONE;
1762     } else {
1763         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
1764                                  && gameInfo.variant != VariantSChess && gameInfo.variant != VariantGrand) { // drop game
1765             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1766             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1767                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1768                     if(board[n][holdings] != EmptySquare) {
1769                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1770                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1771                     }
1772         }
1773         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1774                 return myPieces == hisPieces ? MT_STALEMATE :
1775                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1776         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1777         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1778
1779         return inCheck ? MT_CHECKMATE
1780                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj || IS_SHOGI(gameInfo.variant)) ?
1781                           MT_STAINMATE : MT_STALEMATE;
1782     }
1783 }
1784
1785
1786 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1787                                     int rf, int ff, int rt, int ft,
1788                                     VOIDSTAR closure));
1789
1790 void
1791 DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1792 {
1793     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1794     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1795
1796     // [HGM] wild: for wild-card pieces rt and rf are dummies
1797     if(piece == WhiteFalcon || piece == BlackFalcon ||
1798        piece == WhiteCobra  || piece == BlackCobra)
1799         wildCard = TRUE;
1800
1801     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1802          || PieceToChar(board[rf][ff]) == '~'
1803               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1804                                                                       ) &&
1805         (cl->rfIn == -1 || cl->rfIn == rf) &&
1806         (cl->ffIn == -1 || cl->ffIn == ff) &&
1807         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1808         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1809
1810         cl->count++;
1811         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1812           // [HGM] oneclick: if multiple moves, be sure we remember capture
1813           cl->piece = board[rf][ff];
1814           cl->rf = rf;
1815           cl->ff = ff;
1816           cl->rt = wildCard ? cl->rtIn : rt;
1817           cl->ft = wildCard ? cl->ftIn : ft;
1818           cl->kind = kind;
1819         }
1820         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1821     }
1822 }
1823
1824 void
1825 Disambiguate (Board board, int flags, DisambiguateClosure *closure)
1826 {
1827     int illegal = 0; char c = closure->promoCharIn;
1828
1829     if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
1830     closure->count = closure->captures = 0;
1831     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1832     closure->kind = ImpossibleMove;
1833     rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square
1834     fFilter = closure->ftIn;
1835     if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy
1836         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1837         if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity
1838             closure->count = closure->captures = 0;
1839             closure->rf = closure->ff = closure->rt = closure->ft = 0;
1840             closure->kind = ImpossibleMove;
1841             GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1842         }
1843     } else
1844     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type
1845     if (closure->count == 0) {
1846         /* See if it's an illegal move due to check */
1847         illegal = 1;
1848         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn);
1849         if (closure->count == 0) {
1850             /* No, it's not even that */
1851           if(!appData.testLegality && closure->pieceIn != EmptySquare) {
1852             int f, r; // if there is only a single piece of the requested type on the board, use that
1853             closure->rt = closure->rtIn, closure->ft = closure->ftIn;
1854             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
1855                 if(board[r][f] == closure->pieceIn) closure->count++, closure->rf = r, closure->ff = f;
1856             if(closure->count > 1) illegal = 0; // ambiguous
1857           }
1858           if(closure->count == 0) {
1859             if (appData.debugMode) { int i, j;
1860                 for(i=BOARD_HEIGHT-1; i>=0; i--) {
1861                     for(j=0; j<BOARD_WIDTH; j++)
1862                         fprintf(debugFP, "%3d", (int) board[i][j]);
1863                     fprintf(debugFP, "\n");
1864                 }
1865             }
1866             return;
1867           }
1868         }
1869     }
1870
1871     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1872     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1873         if(closure->piece < BlackPawn) { // white
1874             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1875             if(!(board[VIRGIN][closure->ff] & VIRGIN_W)) closure->kind = IllegalMove; // non-virgin
1876             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1877             if(closure->kind == WhiteHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1878             if(closure->kind == WhiteASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1879         } else {
1880             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1881             if(!(board[VIRGIN][closure->ff] & VIRGIN_B)) closure->kind = IllegalMove; // non-virgin
1882             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1883             if(closure->kind == BlackHSideCastleFR && (closure->ff == BOARD_RGHT-2 || closure->ff == BOARD_RGHT-3)) closure->kind = ImpossibleMove;
1884             if(closure->kind == BlackASideCastleFR && (closure->ff == BOARD_LEFT+2 || closure->ff == BOARD_LEFT+3)) closure->kind = ImpossibleMove;
1885         }
1886     } else
1887     if(gameInfo.variant == VariantChu) {
1888         if(c == '+') closure->kind = (flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion); // for now, accept any
1889     } else
1890     if(gameInfo.variant == VariantShogi) {
1891         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1892         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1893             ChessSquare piece = closure->piece;
1894             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1895                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1896                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1897                    c = '+'; // allowed ICS notations
1898             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1899             else if(flags & F_WHITE_ON_MOVE) {
1900                 if( (int) piece < (int) WhiteWazir &&
1901                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1902                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1903                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1904                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1905                     else /* promotion optional, default is defer */
1906                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion;
1907                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1908             } else {
1909                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1910                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1911                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1912                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1913                     else /* promotion optional, default is defer */
1914                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1915                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1916             }
1917         }
1918         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1919         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1920     } else
1921     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1922         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1923             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
1924                gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
1925                 c = PieceToChar(BlackFerz);
1926             else if(gameInfo.variant == VariantGreat)
1927                 c = PieceToChar(BlackMan);
1928             else if(gameInfo.variant == VariantGrand)
1929                     closure->kind = closure->rt != 0 && closure->rt != BOARD_HEIGHT-1 ? NormalMove : AmbiguousMove; // no default in Grand Chess
1930             else
1931                 c = PieceToChar(BlackQueen);
1932         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1933         else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1934     } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
1935         ChessSquare p = closure->piece;
1936         if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
1937             closure->kind = ImpossibleMove; // used on non-promotable piece
1938         else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
1939     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1940
1941     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1942     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1943         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1944     if (closure->count > 1) {
1945         closure->kind = AmbiguousMove;
1946     }
1947     if (illegal) {
1948         /* Note: If more than one illegal move matches, but no legal
1949            moves, we return IllegalMove, not AmbiguousMove.  Caller
1950            can look at closure->count to detect this.
1951         */
1952         closure->kind = IllegalMove;
1953     }
1954 }
1955
1956
1957 typedef struct {
1958     /* Input */
1959     ChessSquare piece;
1960     int rf, ff, rt, ft;
1961     /* Output */
1962     ChessMove kind;
1963     int rank;
1964     int file;
1965     int either;
1966 } CoordsToAlgebraicClosure;
1967
1968 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1969                                          ChessMove kind, int rf, int ff,
1970                                          int rt, int ft, VOIDSTAR closure));
1971
1972 void
1973 CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
1974 {
1975     register CoordsToAlgebraicClosure *cl =
1976       (CoordsToAlgebraicClosure *) closure;
1977
1978     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1979         (board[rf][ff] == cl->piece
1980          || PieceToChar(board[rf][ff]) == '~' &&
1981             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1982                                      ) {
1983         if (rf == cl->rf) {
1984             if (ff == cl->ff) {
1985                 cl->kind = kind; /* this is the move we want */
1986             } else {
1987                 cl->file++; /* need file to rule out this move */
1988             }
1989         } else {
1990             if (ff == cl->ff) {
1991                 cl->rank++; /* need rank to rule out this move */
1992             } else {
1993                 cl->either++; /* rank or file will rule out this move */
1994             }
1995         }
1996     }
1997 }
1998
1999 /* Convert coordinates to normal algebraic notation.
2000    promoChar must be NULLCHAR or 'x' if not a promotion.
2001 */
2002 ChessMove
2003 CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])
2004 {
2005     ChessSquare piece;
2006     ChessMove kind;
2007     char *outp = out, c, capture;
2008     CoordsToAlgebraicClosure cl;
2009
2010     if (rf == DROP_RANK) {
2011         if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass
2012         /* Bughouse piece drop */
2013         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
2014         *outp++ = '@';
2015         *outp++ = ft + AAA;
2016         if(rt+ONE <= '9')
2017            *outp++ = rt + ONE;
2018         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2019         *outp = NULLCHAR;
2020         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
2021     }
2022
2023     if (promoChar == 'x') promoChar = NULLCHAR;
2024     piece = board[rf][ff];
2025     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
2026
2027     switch (piece) {
2028       case WhitePawn:
2029       case BlackPawn:
2030         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2031         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2032             /* Keep short notation if move is illegal only because it
2033                leaves the player in check, but still return IllegalMove */
2034             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
2035             if (kind == IllegalMove) break;
2036             kind = IllegalMove;
2037         }
2038         /* Pawn move */
2039         *outp++ = ff + AAA;
2040         capture = board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant;
2041         if (ff == ft && !capture) { /* [HGM] Xiangqi has straight noncapts! */
2042             /* Non-capture; use style "e5" */
2043             if(rt+ONE <= '9')
2044                *outp++ = rt + ONE;
2045             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2046         } else {
2047             /* Capture; use style "exd5" */
2048             if(capture)
2049             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
2050             *outp++ = ft + AAA;
2051             if(rt+ONE <= '9')
2052                *outp++ = rt + ONE;
2053             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2054         }
2055         /* Use promotion suffix style "=Q" */
2056         *outp = NULLCHAR;
2057         if (promoChar != NULLCHAR) {
2058             if(IS_SHOGI(gameInfo.variant)) {
2059                 /* [HGM] ... but not in Shogi! */
2060                 *outp++ = promoChar == '=' ? '=' : '+';
2061             } else {
2062                 *outp++ = '=';
2063                 *outp++ = ToUpper(promoChar);
2064             }
2065             *outp = NULLCHAR;
2066         }
2067         return kind;
2068
2069
2070       case WhiteKing:
2071       case BlackKing:
2072         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
2073         /* Code added by Tord:  FRC castling. */
2074         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
2075            (piece == BlackKing && board[rt][ft] == BlackRook)) {
2076           if(ft > ff)
2077             safeStrCpy(out, "O-O", MOVE_LEN);
2078           else
2079             safeStrCpy(out, "O-O-O", MOVE_LEN);
2080           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2081         }
2082         /* End of code added by Tord */
2083         /* Test for castling or ICS wild castling */
2084         /* Use style "O-O" (oh-oh) for PGN compatibility */
2085         else if (rf == rt &&
2086             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
2087             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
2088             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
2089              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
2090             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
2091               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2092             else
2093               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
2094
2095             /* This notation is always unambiguous, unless there are
2096                kings on both the d and e files, with "wild castling"
2097                possible for the king on the d file and normal castling
2098                possible for the other.  ICS rules for wild 9
2099                effectively make castling illegal for either king in
2100                this situation.  So I am not going to worry about it;
2101                I'll just generate an ambiguous O-O in this case.
2102             */
2103             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
2104         }
2105
2106         /* else fall through */
2107       default:
2108         /* Piece move */
2109         cl.rf = rf;
2110         cl.ff = ff;
2111         cl.rt = rFilter = rt; // [HGM] speed: filter on to-square
2112         cl.ft = fFilter = ft;
2113         cl.piece = piece;
2114         cl.kind = IllegalMove;
2115         cl.rank = cl.file = cl.either = 0;
2116         c = PieceToChar(piece) ;
2117         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
2118
2119         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
2120             /* Generate pretty moves for moving into check, but
2121                still return IllegalMove.
2122             */
2123             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
2124             if (cl.kind == IllegalMove) break;
2125             cl.kind = IllegalMove;
2126         }
2127
2128         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
2129            else "Ngf3" or "Ngxf7",
2130            else "N1f3" or "N5xf7",
2131            else "Ng1f3" or "Ng5xf7".
2132         */
2133         if( c == '~' || c == '+') {
2134            /* [HGM] print nonexistent piece as its demoted version */
2135            piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
2136         }
2137         if(c=='+') *outp++ = c;
2138         *outp++ = ToUpper(PieceToChar(piece));
2139
2140         if (cl.file || (cl.either && !cl.rank)) {
2141             *outp++ = ff + AAA;
2142         }
2143         if (cl.rank) {
2144             if(rf+ONE <= '9')
2145                 *outp++ = rf + ONE;
2146             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2147         }
2148
2149         if(board[rt][ft] != EmptySquare)
2150           *outp++ = 'x';
2151
2152         *outp++ = ft + AAA;
2153         if(rt+ONE <= '9')
2154            *outp++ = rt + ONE;
2155         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2156         if (IS_SHOGI(gameInfo.variant)) {
2157             /* [HGM] in Shogi non-pawns can promote */
2158             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
2159         }
2160         else if (gameInfo.variant == VariantChuChess && promoChar ||
2161                  gameInfo.variant != VariantSuper && promoChar &&
2162                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
2163             *outp++ = '=';
2164             *outp++ = ToUpper(promoChar);
2165         }
2166         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
2167             *outp++ = '/';
2168             *outp++ = ToUpper(promoChar);
2169         }
2170         *outp = NULLCHAR;
2171         return cl.kind;
2172
2173       case EmptySquare:
2174         /* Moving a nonexistent piece */
2175         break;
2176     }
2177
2178     /* Not a legal move, even ignoring check.
2179        If there was a piece on the from square,
2180        use style "Ng1g3" or "Ng1xe8";
2181        if there was a pawn or nothing (!),
2182        use style "g1g3" or "g1xe8".  Use "x"
2183        if a piece was on the to square, even
2184        a piece of the same color.
2185     */
2186     outp = out;
2187     c = 0;
2188     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
2189         int r, f;
2190       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
2191                 c += (board[r][f] == piece); // count on-board pieces of given type
2192         *outp++ = ToUpper(PieceToChar(piece));
2193     }
2194   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
2195     *outp++ = ff + AAA;
2196     if(rf+ONE <= '9')
2197        *outp++ = rf + ONE;
2198     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
2199   }
2200     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
2201     *outp++ = ft + AAA;
2202     if(rt+ONE <= '9')
2203        *outp++ = rt + ONE;
2204     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
2205     /* Use promotion suffix style "=Q" */
2206     if (promoChar != NULLCHAR && promoChar != 'x') {
2207         *outp++ = '=';
2208         *outp++ = ToUpper(promoChar);
2209     }
2210     *outp = NULLCHAR;
2211
2212     return IllegalMove;
2213 }
2214
2215 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
2216
2217 typedef struct {
2218     /* Input */
2219     int rf, ff, rt, ft;
2220     /* Output */
2221     int recaptures;
2222 } ChaseClosure;
2223
2224 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
2225
2226 int preyStackPointer, chaseStackPointer;
2227
2228 struct {
2229 unsigned char rf, ff, rt, ft;
2230 } chaseStack[100];
2231
2232 struct {
2233 unsigned char rank, file;
2234 } preyStack[100];
2235
2236
2237
2238
2239 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
2240
2241 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
2242                                 int rf, int ff, int rt, int ft,
2243                                 VOIDSTAR closure));
2244
2245 void
2246 AttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2247 {   // For adding captures that can lead to chase indictment to the chaseStack
2248     if(board[rt][ft] == EmptySquare) return;                               // non-capture
2249     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2250     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
2251     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
2252     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
2253     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
2254     chaseStack[chaseStackPointer].rf = rf;
2255     chaseStack[chaseStackPointer].ff = ff;
2256     chaseStack[chaseStackPointer].rt = rt;
2257     chaseStack[chaseStackPointer].ft = ft;
2258     chaseStackPointer++;
2259 }
2260
2261 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
2262                                 int rf, int ff, int rt, int ft,
2263                                 VOIDSTAR closure));
2264
2265 void
2266 ExistingAttacksCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2267 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
2268     int i;
2269     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
2270
2271     if(board[rt][ft] == EmptySquare) return; // no capture
2272     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
2273         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
2274     }
2275     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
2276     for(i=0; i<chaseStackPointer; i++) {
2277         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
2278            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
2279             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
2280             chaseStack[i] = chaseStack[--chaseStackPointer];
2281             break;
2282         }
2283     }
2284 }
2285
2286 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
2287                                 int rf, int ff, int rt, int ft,
2288                                 VOIDSTAR closure));
2289
2290 void
2291 ProtectedCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
2292 {   // for determining if a piece (given through the closure) is protected
2293     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
2294
2295     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
2296     if(appData.debugMode && board[rt][ft] != EmptySquare)
2297         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
2298 }
2299
2300 extern char moveList[MAX_MOVES][MOVE_LEN];
2301
2302 int
2303 PerpetualChase (int first, int last)
2304 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
2305     int i, j, k, tail;
2306     ChaseClosure cl;
2307     ChessSquare captured;
2308
2309     preyStackPointer = 0;        // clear stack of chased pieces
2310     for(i=first; i<last; i+=2) { // for all positions with same side to move
2311         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
2312         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
2313         // determine all captures possible after the move, and put them on chaseStack
2314         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl, EmptySquare);
2315         if(appData.debugMode) { int n;
2316             for(n=0; n<chaseStackPointer; n++)
2317                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2318                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2319             fprintf(debugFP, ": all capts\n");
2320         }
2321         // determine all captures possible before the move, and delete them from chaseStack
2322         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
2323         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
2324         cl.rt = moveList[i][3]-ONE;
2325         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
2326         CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1; // giant kludge to make GenLegal ignore pre-existing checks
2327         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl, EmptySquare);
2328         xqCheckers[EP_STATUS] = 0; // disable the generation of quasi-legal moves again
2329         if(appData.debugMode) { int n;
2330             for(n=0; n<chaseStackPointer; n++)
2331                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2332                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2333             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
2334         }
2335         // chaseSack now contains all captures made possible by the move
2336         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
2337             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2338             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2339
2340             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
2341             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
2342
2343             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
2344                 continue; // C or H attack on R is always chase; leave on chaseStack
2345
2346             if(attacker == victim) {
2347                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
2348                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
2349                         // we can capture back with equal piece, so this is no chase but a sacrifice
2350                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
2351                         j--; /* ! */ continue;
2352                 }
2353
2354             }
2355
2356             // the attack is on a lower piece, or on a pinned or blocked equal one
2357             CopyBoard(xqCheckers, nullBoard); xqCheckers[EP_STATUS] = 1;
2358             CheckTest(boards[i+1], PosFlags(i+1), -1, -1, -1, -1, FALSE); // if we deliver check with our move, the checkers get marked
2359             // test if the victim is protected by a true protector. First make the capture.
2360             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2361             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
2362             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
2363             // Then test if the opponent can recapture
2364             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
2365             cl.rt = chaseStack[j].rt;
2366             cl.ft = chaseStack[j].ft;
2367             if(appData.debugMode) {
2368                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
2369             }
2370             xqCheckers[EP_STATUS] = 2; // causes GenLegal to ignore the checks we delivered with the move, in real life evaded before we captured
2371             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl, EmptySquare); // try all moves
2372             xqCheckers[EP_STATUS] = 0; // disable quasi-legal moves again
2373             // unmake the capture
2374             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
2375             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
2376             // if a recapture was found, piece is protected, and we are not chasing it.
2377             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
2378                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
2379                 j--; /* ! */
2380             }
2381         }
2382         // chaseStack now contains all moves that chased
2383         if(appData.debugMode) { int n;
2384             for(n=0; n<chaseStackPointer; n++)
2385                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
2386                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
2387             fprintf(debugFP, ": chases\n");
2388         }
2389         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
2390             for(j=0; j<chaseStackPointer; j++) {
2391                 preyStack[j].rank = chaseStack[j].rt;
2392                 preyStack[j].file = chaseStack[j].ft;
2393             }
2394             preyStackPointer = chaseStackPointer;
2395         }
2396         tail = 0;
2397         for(j=0; j<chaseStackPointer; j++) {
2398             for(k=0; k<preyStackPointer; k++) {
2399                 // search the victim of each chase move on the preyStack (first occurrence)
2400                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
2401                     if(k < tail) break; // piece was already identified as still being chased
2402                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
2403                     preyStack[tail] = preyStack[k];                // by swapping
2404                     preyStack[k] = preyStack[preyStackPointer];
2405                     tail++;
2406                     break;
2407                 }
2408             }
2409         }
2410         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
2411         if(appData.debugMode) { int n;
2412             for(n=0; n<preyStackPointer; n++)
2413                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
2414             fprintf(debugFP, "always chased upto ply %d\n", i);
2415         }
2416         // now adjust the location of the chased pieces according to opponent move
2417         for(j=0; j<preyStackPointer; j++) {
2418             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
2419                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
2420                 preyStack[j].rank = moveList[i+1][3]-ONE;
2421                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
2422                 break;
2423             }
2424         }
2425     }
2426     return preyStackPointer ? 256*(preyStack[preyStackPointer].file - BOARD_LEFT + AAA) + (preyStack[preyStackPointer].rank + ONE)
2427                                 : 0; // if any piece was left on preyStack, it has been perpetually chased,and we return the
2428 }