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