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