Fix Spartan promotion to King
[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 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 #if HAVE_STRING_H
58 # include <string.h>
59 #else /* not HAVE_STRING_H */
60 # include <strings.h>
61 #endif /* not HAVE_STRING_H */
62 #include "common.h"
63 #include "backend.h"
64 #include "moves.h"
65 #include "parser.h"
66
67 int WhitePiece P((ChessSquare));
68 int BlackPiece P((ChessSquare));
69 int SameColor P((ChessSquare, ChessSquare));
70 int PosFlags(int index);
71
72 extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
73
74
75 int WhitePiece(piece)
76      ChessSquare piece;
77 {
78     return (int) piece >= (int) WhitePawn && (int) piece < (int) BlackPawn;
79 }
80
81 int BlackPiece(piece)
82      ChessSquare piece;
83 {
84     return (int) piece >= (int) BlackPawn && (int) piece < (int) EmptySquare;
85 }
86
87 int SameColor(piece1, piece2)
88      ChessSquare piece1, piece2;
89 {
90     return ((int) piece1 >= (int) WhitePawn &&   /* [HGM] can be > King ! */
91             (int) piece1 <  (int) BlackPawn &&
92             (int) piece2 >= (int) WhitePawn &&
93             (int) piece2 <  (int) BlackPawn)
94       ||   ((int) piece1 >= (int) BlackPawn &&
95             (int) piece1 <  (int) EmptySquare &&
96             (int) piece2 >= (int) BlackPawn &&
97             (int) piece2 <  (int) EmptySquare);
98 }
99
100 char pieceToChar[] = {
101                         'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M',
102                         'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K',
103                         'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm',
104                         'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
105                         'x' };
106 char pieceNickName[EmptySquare];
107
108 char PieceToChar(p)
109      ChessSquare p;
110 {
111     if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
112     return pieceToChar[(int) p];
113 }
114
115 int PieceToNumber(p)  /* [HGM] holdings: count piece type, ignoring non-participating piece types */
116      ChessSquare p;
117 {
118     int i=0;
119     ChessSquare start = (int)p >= (int)BlackPawn ? BlackPawn : WhitePawn;
120
121     while(start++ != p) if(pieceToChar[(int)start-1] != '.') i++;
122     return i;
123 }
124
125 ChessSquare CharToPiece(c)
126      int c;
127 {
128      int i;
129      for(i=0; i< (int) EmptySquare; i++)
130           if(pieceNickName[i] == c) return (ChessSquare) i;
131      for(i=0; i< (int) EmptySquare; i++)
132           if(pieceToChar[i] == c) return (ChessSquare) i;
133      return EmptySquare;
134 }
135
136 void CopyBoard(to, from)
137      Board to, from;
138 {
139     int i, j;
140
141     for (i = 0; i < BOARD_HEIGHT; i++)
142       for (j = 0; j < BOARD_WIDTH; j++)
143         to[i][j] = from[i][j];
144     for (j = 0; j < BOARD_FILES-1; j++) // [HGM] gamestate: copy castling rights and ep status
145         to[CASTLING][j] = from[CASTLING][j];
146     to[HOLDINGS_SET] = 0; // flag used in ICS play
147 }
148
149 int CompareBoards(board1, board2)
150      Board board1, board2;
151 {
152     int i, j;
153
154     for (i = 0; i < BOARD_HEIGHT; i++)
155       for (j = 0; j < BOARD_WIDTH; j++) {
156           if (board1[i][j] != board2[i][j])
157             return FALSE;
158     }
159     return TRUE;
160 }
161
162
163 /* Call callback once for each pseudo-legal move in the given
164    position, except castling moves. A move is pseudo-legal if it is
165    legal, or if it would be legal except that it leaves the king in
166    check.  In the arguments, epfile is EP_NONE if the previous move
167    was not a double pawn push, or the file 0..7 if it was, or
168    EP_UNKNOWN if we don't know and want to allow all e.p. captures.
169    Promotion moves generated are to Queen only.
170 */
171 void GenPseudoLegal(board, flags, callback, closure)
172      Board board;
173      int flags;
174      MoveCallback callback;
175      VOIDSTAR closure;
176 {
177     int rf, ff;
178     int i, j, d, s, fs, rs, rt, ft, m;
179     int epfile = (signed char)board[EP_STATUS]; // [HGM] gamestate: extract ep status from board
180     int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
181
182     for (rf = 0; rf < BOARD_HEIGHT; rf++)
183       for (ff = BOARD_LEFT; ff < BOARD_RGHT; ff++) {
184           ChessSquare piece;
185           int rookRange = 1000;
186
187           if (flags & F_WHITE_ON_MOVE) {
188               if (!WhitePiece(board[rf][ff])) continue;
189           } else {
190               if (!BlackPiece(board[rf][ff])) continue;
191           }
192           m = 0; piece = board[rf][ff];
193           if(PieceToChar(piece) == '~')
194                  piece = (ChessSquare) ( DEMOTED piece );
195           if(gameInfo.variant == VariantShogi)
196                  piece = (ChessSquare) ( SHOGI piece );
197
198           switch (piece) {
199             /* case EmptySquare: [HGM] this is nonsense, and conflicts with Shogi cases */
200             default:
201               /* can't happen ([HGM] except for faries...) */
202               break;
203
204              case WhitePawn:
205               if(gameInfo.variant == VariantXiangqi) {
206                   /* [HGM] capture and move straight ahead in Xiangqi */
207                   if (rf < BOARD_HEIGHT-1 &&
208                            !SameColor(board[rf][ff], board[rf + 1][ff]) ) {
209                            callback(board, flags, NormalMove,
210                                     rf, ff, rf + 1, ff, closure);
211                   }
212                   /* and move sideways when across the river */
213                   for (s = -1; s <= 1; s += 2) {
214                       if (rf >= BOARD_HEIGHT>>1 &&
215                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
216                           !WhitePiece(board[rf][ff+s]) ) {
217                            callback(board, flags, NormalMove,
218                                     rf, ff, rf, ff+s, closure);
219                       }
220                   }
221                   break;
222               }
223               if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) {
224                   callback(board, flags,
225                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
226                            rf, ff, rf + 1, ff, closure);
227               }
228               if (rf == 1 && board[2][ff] == EmptySquare &&
229                   gameInfo.variant != VariantShatranj && /* [HGM] */
230                   gameInfo.variant != VariantCourier  && /* [HGM] */
231                   board[3][ff] == EmptySquare ) {
232                       callback(board, flags, NormalMove,
233                                rf, ff, 3, ff, closure);
234               }
235               for (s = -1; s <= 1; s += 2) {
236                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
237                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
238                        BlackPiece(board[rf + 1][ff + s]))) {
239                       callback(board, flags,
240                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
241                                rf, ff, rf + 1, ff + s, closure);
242                   }
243                   if (rf == BOARD_HEIGHT-4) {
244                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
245                           (epfile == ff + s || epfile == EP_UNKNOWN) &&
246                           board[BOARD_HEIGHT-4][ff + s] == BlackPawn &&
247                           board[BOARD_HEIGHT-3][ff + s] == EmptySquare) {
248                           callback(board, flags, WhiteCapturesEnPassant,
249                                    rf, ff, 5, ff + s, closure);
250                       }
251                   }
252               }
253               break;
254
255             case BlackPawn:
256               if(gameInfo.variant == VariantXiangqi) {
257                   /* [HGM] capture straight ahead in Xiangqi */
258                   if (rf > 0 && !SameColor(board[rf][ff], board[rf - 1][ff]) ) {
259                            callback(board, flags, NormalMove,
260                                     rf, ff, rf - 1, ff, closure);
261                   }
262                   /* and move sideways when across the river */
263                   for (s = -1; s <= 1; s += 2) {
264                       if (rf < BOARD_HEIGHT>>1 &&
265                           ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
266                           !BlackPiece(board[rf][ff+s]) ) {
267                            callback(board, flags, NormalMove,
268                                     rf, ff, rf, ff+s, closure);
269                       }
270                   }
271                   break;
272               }
273               if (rf > 0 && board[rf - 1][ff] == EmptySquare) {
274                   callback(board, flags,
275                            rf <= promoRank ? BlackPromotion : NormalMove,
276                            rf, ff, rf - 1, ff, closure);
277               }
278               if (rf == BOARD_HEIGHT-2 && board[BOARD_HEIGHT-3][ff] == EmptySquare &&
279                   gameInfo.variant != VariantShatranj && /* [HGM] */
280                   gameInfo.variant != VariantCourier  && /* [HGM] */
281                   board[BOARD_HEIGHT-4][ff] == EmptySquare) {
282                   callback(board, flags, NormalMove,
283                            rf, ff, BOARD_HEIGHT-4, ff, closure);
284               }
285               for (s = -1; s <= 1; s += 2) {
286                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
287                       ((flags & F_KRIEGSPIEL_CAPTURE) ||
288                        WhitePiece(board[rf - 1][ff + s]))) {
289                       callback(board, flags,
290                                rf <= promoRank ? BlackPromotion : NormalMove,
291                                rf, ff, rf - 1, ff + s, closure);
292                   }
293                   if (rf == 3) {
294                       if (ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
295                           (epfile == ff + s || epfile == EP_UNKNOWN) &&
296                           board[3][ff + s] == WhitePawn &&
297                           board[2][ff + s] == EmptySquare) {
298                           callback(board, flags, BlackCapturesEnPassant,
299                                    rf, ff, 2, ff + s, closure);
300                       }
301                   }
302               }
303               break;
304
305             case WhiteUnicorn:
306             case BlackUnicorn:
307             case WhiteKnight:
308             case BlackKnight:
309             mounted:
310               for (i = -1; i <= 1; i += 2)
311                 for (j = -1; j <= 1; j += 2)
312                   for (s = 1; s <= 2; s++) {
313                       rt = rf + i*s;
314                       ft = ff + j*(3-s);
315                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
316                           && ( gameInfo.variant != VariantXiangqi || board[rf+i*(s-1)][ff+j*(2-s)] == EmptySquare)
317                           && !SameColor(board[rf][ff], board[rt][ft]))
318                       callback(board, flags, NormalMove,
319                                rf, ff, rt, ft, closure);
320                   }
321               break;
322
323             case SHOGI WhiteKnight:
324               for (s = -1; s <= 1; s += 2) {
325                   if (rf < BOARD_HEIGHT-2 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
326                       !SameColor(board[rf][ff], board[rf + 2][ff + s])) {
327                       callback(board, flags, NormalMove,
328                                rf, ff, rf + 2, ff + s, closure);
329                   }
330               }
331               break;
332
333             case SHOGI BlackKnight:
334               for (s = -1; s <= 1; s += 2) {
335                   if (rf > 1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
336                       !SameColor(board[rf][ff], board[rf - 2][ff + s])) {
337                       callback(board, flags, NormalMove,
338                                rf, ff, rf - 2, ff + s, closure);
339                   }
340               }
341               break;
342
343             case WhiteCannon:
344             case BlackCannon:
345               for (d = 0; d <= 1; d++)
346                 for (s = -1; s <= 1; s += 2) {
347                   m = 0;
348                   for (i = 1;; i++) {
349                       rt = rf + (i * s) * d;
350                       ft = ff + (i * s) * (1 - d);
351                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
352                       if (m == 0 && board[rt][ft] == EmptySquare)
353                                  callback(board, flags, NormalMove,
354                                           rf, ff, rt, ft, closure);
355                       if (m == 1 && board[rt][ft] != EmptySquare &&
356                           !SameColor(board[rf][ff], board[rt][ft]) )
357                                  callback(board, flags, NormalMove,
358                                           rf, ff, rt, ft, closure);
359                       if (board[rt][ft] != EmptySquare && m++) break;
360                   }
361                 }
362               break;
363
364             /* Gold General (and all its promoted versions) . First do the */
365             /* diagonal forward steps, then proceed as normal Wazir        */
366             case SHOGI WhiteWazir:
367             case SHOGI (PROMOTED WhitePawn):
368             case SHOGI (PROMOTED WhiteKnight):
369             case SHOGI (PROMOTED WhiteQueen):
370             case SHOGI (PROMOTED WhiteFerz):
371               for (s = -1; s <= 1; s += 2) {
372                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
373                       !SameColor(board[rf][ff], board[rf + 1][ff + s])) {
374                       callback(board, flags, NormalMove,
375                                rf, ff, rf + 1, ff + s, closure);
376                   }
377               }
378               goto finishGold;
379
380             case SHOGI BlackWazir:
381             case SHOGI (PROMOTED BlackPawn):
382             case SHOGI (PROMOTED BlackKnight):
383             case SHOGI (PROMOTED BlackQueen):
384             case SHOGI (PROMOTED BlackFerz):
385               for (s = -1; s <= 1; s += 2) {
386                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT &&
387                       !SameColor(board[rf][ff], board[rf - 1][ff + s])) {
388                       callback(board, flags, NormalMove,
389                                rf, ff, rf - 1, ff + s, closure);
390                   }
391               }
392
393             case WhiteWazir:
394             case BlackWazir:
395             finishGold:
396               for (d = 0; d <= 1; d++)
397                 for (s = -1; s <= 1; s += 2) {
398                       rt = rf + s * d;
399                       ft = ff + s * (1 - d);
400                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
401                           && !SameColor(board[rf][ff], board[rt][ft]) &&
402                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
403                                callback(board, flags, NormalMove,
404                                         rf, ff, rt, ft, closure);
405                       }
406               break;
407
408             case WhiteAlfil:
409             case BlackAlfil:
410                 /* [HGM] support Shatranj pieces */
411                 for (rs = -1; rs <= 1; rs += 2)
412                   for (fs = -1; fs <= 1; fs += 2) {
413                       rt = rf + 2 * rs;
414                       ft = ff + 2 * fs;
415                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
416                           && ( gameInfo.variant != VariantXiangqi ||
417                                board[rf+rs][ff+fs] == EmptySquare && (2*rf < BOARD_HEIGHT) == (2*rt < BOARD_HEIGHT) )
418
419                           && !SameColor(board[rf][ff], board[rt][ft]))
420                                callback(board, flags, NormalMove,
421                                         rf, ff, rt, ft, closure);
422                       if(gameInfo.variant == VariantShatranj && gameInfo.variant == VariantCourier) continue; // classical Alfil
423                       rt = rf + rs; // in unknown variant we assume Modern Elephant, which can also do one step
424                       ft = ff + fs;
425                       if (!(rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT)
426                           && !SameColor(board[rf][ff], board[rt][ft]))
427                                callback(board, flags, NormalMove,
428                                         rf, ff, rt, ft, closure);
429                   }
430                 if(gameInfo.variant == VariantSpartan)
431                    for(fs = -1; fs <= 1; fs += 2) {
432                       ft = ff + fs;
433                       if (!(ft < BOARD_LEFT || ft >= BOARD_RGHT) && board[rf][ft] == EmptySquare)
434                                callback(board, flags, NormalMove, rf, ff, rf, ft, closure);
435                    }
436                 break;
437
438             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
439             case WhiteCardinal:
440             case BlackCardinal:
441               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
442                 for (s = -2; s <= 2; s += 4) {
443                       rt = rf + s * d;
444                       ft = ff + s * (1 - d);
445                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
446                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
447                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
448                   }
449
450             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
451             case SHOGI WhiteCardinal:
452             case SHOGI BlackCardinal:
453               m++;
454
455             /* Capablanca Archbishop continues as Knight                  */
456             case WhiteAngel:
457             case BlackAngel:
458               m++;
459
460             /* Shogi Bishops are ordinary Bishops */
461             case SHOGI WhiteBishop:
462             case SHOGI BlackBishop:
463             case WhiteBishop:
464             case BlackBishop:
465               for (rs = -1; rs <= 1; rs += 2)
466                 for (fs = -1; fs <= 1; fs += 2)
467                   for (i = 1;; i++) {
468                       rt = rf + (i * rs);
469                       ft = ff + (i * fs);
470                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
471                       if (SameColor(board[rf][ff], board[rt][ft])) break;
472                       callback(board, flags, NormalMove,
473                                rf, ff, rt, ft, closure);
474                       if (board[rt][ft] != EmptySquare) break;
475                   }
476                 if(m==1) goto mounted;
477                 if(m==2) goto finishGold;
478                 /* Bishop falls through */
479               break;
480
481             /* Shogi Lance is unlike anything, and asymmetric at that */
482             case SHOGI WhiteQueen:
483               for(i = 1;; i++) {
484                       rt = rf + i;
485                       ft = ff;
486                       if (rt >= BOARD_HEIGHT) break;
487                       if (SameColor(board[rf][ff], board[rt][ft])) break;
488                       callback(board, flags, NormalMove,
489                                rf, ff, rt, ft, closure);
490                       if (board[rt][ft] != EmptySquare) break;
491               }
492               break;
493
494             case SHOGI BlackQueen:
495               for(i = 1;; i++) {
496                       rt = rf - i;
497                       ft = ff;
498                       if (rt < 0) break;
499                       if (SameColor(board[rf][ff], board[rt][ft])) break;
500                       callback(board, flags, NormalMove,
501                                rf, ff, rt, ft, closure);
502                       if (board[rt][ft] != EmptySquare) break;
503               }
504               break;
505
506             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
507             case WhiteDragon:
508             case BlackDragon:
509               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
510                 for (s = -2; s <= 2; s += 4) {
511                       rt = rf + s * d;
512                       ft = ff + s * (1 - d);
513                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
514                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
515                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
516                   }
517               if(gameInfo.variant == VariantSpartan) rookRange = 2; // in Spartan Chess restrict range to modern Dababba
518               goto doRook;
519               
520             /* Shogi Dragon King has to continue as Ferz after Rook moves */
521             case SHOGI WhiteDragon:
522             case SHOGI BlackDragon:
523               m++;
524
525             /* Capablanca Chancellor sets flag to continue as Knight      */
526             case WhiteMarshall:
527             case BlackMarshall:
528               m++;
529               m += (gameInfo.variant == VariantSpartan); // in Spartan Chess Chancellor is used for Dragon King.
530
531             /* Shogi Rooks are ordinary Rooks */
532             case SHOGI WhiteRook:
533             case SHOGI BlackRook:
534             case WhiteRook:
535             case BlackRook:
536           doRook:
537               for (d = 0; d <= 1; d++)
538                 for (s = -1; s <= 1; s += 2)
539                   for (i = 1;; i++) {
540                       rt = rf + (i * s) * d;
541                       ft = ff + (i * s) * (1 - d);
542                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
543                       if (SameColor(board[rf][ff], board[rt][ft])) break;
544                       callback(board, flags, NormalMove,
545                                rf, ff, rt, ft, closure);
546                       if (board[rt][ft] != EmptySquare || i == rookRange) break;
547                   }
548                 if(m==1) goto mounted;
549                 if(m==2) goto finishSilver;
550               break;
551
552             case WhiteQueen:
553             case BlackQueen:
554               for (rs = -1; rs <= 1; rs++)
555                 for (fs = -1; fs <= 1; fs++) {
556                     if (rs == 0 && fs == 0) continue;
557                     for (i = 1;; i++) {
558                         rt = rf + (i * rs);
559                         ft = ff + (i * fs);
560                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
561                         if (SameColor(board[rf][ff], board[rt][ft])) break;
562                         callback(board, flags, NormalMove,
563                                  rf, ff, rt, ft, closure);
564                         if (board[rt][ft] != EmptySquare) break;
565                     }
566                 }
567               break;
568
569             /* Shogi Pawn and Silver General: first the Pawn move,    */
570             /* then the General continues like a Ferz                 */
571             case WhiteMan:
572                 if(gameInfo.variant != VariantMakruk) goto commoner;
573             case SHOGI WhitePawn:
574             case SHOGI WhiteFerz:
575                   if (rf < BOARD_HEIGHT-1 &&
576                            !SameColor(board[rf][ff], board[rf + 1][ff]) )
577                            callback(board, flags, NormalMove,
578                                     rf, ff, rf + 1, ff, closure);
579               if(piece != SHOGI WhitePawn) goto finishSilver;
580               break;
581
582             case BlackMan:
583                 if(gameInfo.variant != VariantMakruk) goto commoner;
584             case SHOGI BlackPawn:
585             case SHOGI BlackFerz:
586                   if (rf > 0 &&
587                            !SameColor(board[rf][ff], board[rf - 1][ff]) )
588                            callback(board, flags, NormalMove,
589                                     rf, ff, rf - 1, ff, closure);
590               if(piece == SHOGI BlackPawn) break;
591
592             case WhiteFerz:
593             case BlackFerz:
594             finishSilver:
595                 /* [HGM] support Shatranj pieces */
596                 for (rs = -1; rs <= 1; rs += 2)
597                   for (fs = -1; fs <= 1; fs += 2) {
598                       rt = rf + rs;
599                       ft = ff + fs;
600                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
601                       if (!SameColor(board[rf][ff], board[rt][ft]) &&
602                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
603                                callback(board, flags, NormalMove,
604                                         rf, ff, rt, ft, closure);
605                   }
606                 break;
607
608             case WhiteSilver:
609             case BlackSilver:
610                 m++; // [HGM] superchess: use for Centaur
611             commoner:
612             case SHOGI WhiteKing:
613             case SHOGI BlackKing:
614             case WhiteKing:
615             case BlackKing:
616 //            walking:
617               for (i = -1; i <= 1; i++)
618                 for (j = -1; j <= 1; j++) {
619                     if (i == 0 && j == 0) continue;
620                     rt = rf + i;
621                     ft = ff + j;
622                     if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
623                     if (SameColor(board[rf][ff], board[rt][ft])) continue;
624                     callback(board, flags, NormalMove,
625                              rf, ff, rt, ft, closure);
626                 }
627                 if(m==1) goto mounted;
628               break;
629
630             case WhiteNightrider:
631             case BlackNightrider:
632               for (i = -1; i <= 1; i += 2)
633                 for (j = -1; j <= 1; j += 2)
634                   for (s = 1; s <= 2; s++) {  int k;
635                     for(k=1;; k++) {
636                       rt = rf + k*i*s;
637                       ft = ff + k*j*(3-s);
638                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
639                       if (SameColor(board[rf][ff], board[rt][ft])) break;
640                       callback(board, flags, NormalMove,
641                                rf, ff, rt, ft, closure);
642                       if (board[rt][ft] != EmptySquare) break;
643                     }
644                   }
645               break;
646
647             Amazon:
648               /* First do Bishop,then continue like Chancellor */
649               for (rs = -1; rs <= 1; rs += 2)
650                 for (fs = -1; fs <= 1; fs += 2)
651                   for (i = 1;; i++) {
652                       rt = rf + (i * rs);
653                       ft = ff + (i * fs);
654                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
655                       if (SameColor(board[rf][ff], board[rt][ft])) break;
656                       callback(board, flags, NormalMove,
657                                rf, ff, rt, ft, closure);
658                       if (board[rt][ft] != EmptySquare) break;
659                   }
660               m++;
661               goto doRook;
662
663             // Use Lance as Berolina / Spartan Pawn.
664             case WhiteLance:
665               if(gameInfo.variant == VariantSuper) goto Amazon;
666               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
667                   callback(board, flags,
668                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
669                            rf, ff, rf + 1, ff, closure);
670               for (s = -1; s <= 1; s += 2) {
671                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
672                       callback(board, flags, 
673                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
674                                rf, ff, rf + 1, ff + s, closure);
675                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
676                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
677               }
678               break;
679                 
680             case BlackLance:
681               if(gameInfo.variant == VariantSuper) goto Amazon;
682               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
683                   callback(board, flags,
684                            rf <= promoRank ? BlackPromotion : NormalMove,
685                            rf, ff, rf - 1, ff, closure);
686               for (s = -1; s <= 1; s += 2) {
687                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
688                       callback(board, flags, 
689                                rf <= promoRank ? BlackPromotion : NormalMove,
690                                rf, ff, rf - 1, ff + s, closure);
691                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
692                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
693               }
694             break;
695
696             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
697             case BlackFalcon:
698             case WhiteCobra:
699             case BlackCobra:
700               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
701               break;
702
703           }
704       }
705 }
706
707
708 typedef struct {
709     MoveCallback cb;
710     VOIDSTAR cl;
711 } GenLegalClosure;
712
713 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
714                                 int rf, int ff, int rt, int ft,
715                                 VOIDSTAR closure));
716
717 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
718      Board board;
719      int flags;
720      ChessMove kind;
721      int rf, ff, rt, ft;
722      VOIDSTAR closure;
723 {
724     register GenLegalClosure *cl = (GenLegalClosure *) closure;
725
726     if (!(flags & F_IGNORE_CHECK) ) {
727       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
728       if(promo) board[rf][ff] = BlackKing; // [HGM] spartan: promote to King before check-test
729         check = CheckTest(board, flags, rf, ff, rt, ft,
730                   kind == WhiteCapturesEnPassant ||
731                   kind == BlackCapturesEnPassant);
732         if(promo) board[rf][ff] = BlackLance;
733       if(check) return;
734     }
735     if (flags & F_ATOMIC_CAPTURE) {
736       if (board[rt][ft] != EmptySquare ||
737           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
738         int r, f;
739         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
740         if (board[rf][ff] == king) return;
741         for (r = rt-1; r <= rt+1; r++) {
742           for (f = ft-1; f <= ft+1; f++) {
743             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
744                 board[r][f] == king) return;
745           }
746         }
747       }
748     }
749     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
750 }
751
752
753 typedef struct {
754     int rf, ff, rt, ft;
755     ChessMove kind;
756     int captures; // [HGM] losers
757 } LegalityTestClosure;
758
759
760 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
761    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
762    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
763    moves that would destroy your own king.  The CASTLE_OK flags are
764    true if castling is not yet ruled out by a move of the king or
765    rook.  Return TRUE if the player on move is currently in check and
766    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
767 int GenLegal(board, flags, callback, closure)
768      Board board;
769      int flags;
770      MoveCallback callback;
771      VOIDSTAR closure;
772 {
773     GenLegalClosure cl;
774     int ff, ft, k, left, right, swap;
775     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
776     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
777
778     cl.cb = callback;
779     cl.cl = closure;
780     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
781
782     if (!ignoreCheck &&
783         CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
784
785     /* Generate castling moves */
786     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
787         wKing = WhiteUnicorn; bKing = BlackUnicorn;
788     }
789
790     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
791         if ((flags & F_WHITE_ON_MOVE) &&
792             (flags & F_WHITE_KCASTLE_OK) &&
793             board[0][ff] == wKing &&
794             board[0][ff + 1] == EmptySquare &&
795             board[0][ff + 2] == EmptySquare &&
796             board[0][BOARD_RGHT-3] == EmptySquare &&
797             board[0][BOARD_RGHT-2] == EmptySquare &&
798             board[0][BOARD_RGHT-1] == WhiteRook &&
799             castlingRights[0] != NoRights && /* [HGM] check rights */
800             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
801             (ignoreCheck ||
802              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
803               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
804               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
805               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
806
807             callback(board, flags,
808                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
809                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
810         }
811         if ((flags & F_WHITE_ON_MOVE) &&
812             (flags & F_WHITE_QCASTLE_OK) &&
813             board[0][ff] == wKing &&
814             board[0][ff - 1] == EmptySquare &&
815             board[0][ff - 2] == EmptySquare &&
816             board[0][BOARD_LEFT+2] == EmptySquare &&
817             board[0][BOARD_LEFT+1] == EmptySquare &&
818             board[0][BOARD_LEFT+0] == WhiteRook &&
819             castlingRights[1] != NoRights && /* [HGM] check rights */
820             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
821             (ignoreCheck ||
822              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
823               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
824               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
825
826             callback(board, flags,
827                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
828                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
829         }
830         if (!(flags & F_WHITE_ON_MOVE) &&
831             (flags & F_BLACK_KCASTLE_OK) &&
832             board[BOARD_HEIGHT-1][ff] == bKing &&
833             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
834             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
835             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
836             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
837             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
838             castlingRights[3] != NoRights && /* [HGM] check rights */
839             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
840             (ignoreCheck ||
841              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
842               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
843               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
844               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
845
846             callback(board, flags,
847                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
848                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
849         }
850         if (!(flags & F_WHITE_ON_MOVE) &&
851             (flags & F_BLACK_QCASTLE_OK) &&
852             board[BOARD_HEIGHT-1][ff] == bKing &&
853             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
854             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
855             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
856             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
857             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
858             castlingRights[4] != NoRights && /* [HGM] check rights */
859             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
860             (ignoreCheck ||
861              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
862               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
863               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
864
865             callback(board, flags,
866                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
867                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
868         }
869     }
870
871   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
872
873     /* generate all potential FRC castling moves (KxR), ignoring flags */
874     /* [HGM] test if the Rooks we find have castling rights */
875     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
876
877
878     if ((flags & F_WHITE_ON_MOVE) != 0) {
879         ff = castlingRights[2]; /* King file if we have any rights */
880         if(ff != NoRights && board[0][ff] == WhiteKing) {
881     if (appData.debugMode) {
882         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
883                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
884     }
885             ft = castlingRights[0]; /* Rook file if we have H-side rights */
886             left  = ff+1;
887             right = BOARD_RGHT-2;
888             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
889             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
890                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
891             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
892                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
893             if(ft != NoRights && board[0][ft] == WhiteRook)
894                 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
895
896             ft = castlingRights[1]; /* Rook file if we have A-side rights */
897             left  = BOARD_LEFT+2;
898             right = ff-1;
899             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
900             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
901                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
902             if(ff > BOARD_LEFT+2)
903             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
904                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
905             if(ft != NoRights && board[0][ft] == WhiteRook)
906                 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
907         }
908     } else {
909         ff = castlingRights[5]; /* King file if we have any rights */
910         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
911             ft = castlingRights[3]; /* Rook file if we have H-side rights */
912             left  = ff+1;
913             right = BOARD_RGHT-2;
914             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
915             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
916                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
917             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
918                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
919             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
920                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
921
922             ft = castlingRights[4]; /* Rook file if we have A-side rights */
923             left  = BOARD_LEFT+2;
924             right = ff-1;
925             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
926             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
927                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
928             if(ff > BOARD_LEFT+2)
929             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
930                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
931             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
932                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
933         }
934     }
935
936   }
937
938     return FALSE;
939 }
940
941
942 typedef struct {
943     int rking, fking;
944     int check;
945 } CheckTestClosure;
946
947
948 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
949                                  int rf, int ff, int rt, int ft,
950                                  VOIDSTAR closure));
951
952
953 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
954      Board board;
955      int flags;
956      ChessMove kind;
957      int rf, ff, rt, ft;
958      VOIDSTAR closure;
959 {
960     register CheckTestClosure *cl = (CheckTestClosure *) closure;
961
962     if (rt == cl->rking && ft == cl->fking) cl->check++;
963 }
964
965
966 /* If the player on move were to move from (rf, ff) to (rt, ft), would
967    he leave himself in check?  Or if rf == -1, is the player on move
968    in check now?  enPassant must be TRUE if the indicated move is an
969    e.p. capture.  The possibility of castling out of a check along the
970    back rank is not accounted for (i.e., we still return nonzero), as
971    this is illegal anyway.  Return value is the number of times the
972    king is in check. */
973 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
974      Board board;
975      int flags;
976      int rf, ff, rt, ft, enPassant;
977 {
978     CheckTestClosure cl;
979     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
980     ChessSquare captured = EmptySquare;
981     /*  Suppress warnings on uninitialized variables    */
982
983     if(gameInfo.variant == VariantXiangqi)
984         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
985     if(gameInfo.variant == VariantKnightmate)
986         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
987
988     if (rf >= 0) {
989         if (enPassant) {
990             captured = board[rf][ft];
991             board[rf][ft] = EmptySquare;
992         } else {
993             captured = board[rt][ft];
994         }
995         board[rt][ft] = board[rf][ff];
996         board[rf][ff] = EmptySquare;
997     } else board[rt][ft] = ff; // [HGM] drop
998
999     /* For compatibility with ICS wild 9, we scan the board in the
1000        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
1001        and we test only whether that one is in check. */
1002     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
1003         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
1004           if (board[cl.rking][cl.fking] == king) {
1005               if(gameInfo.variant == VariantXiangqi) {
1006                   /* [HGM] In Xiangqi opposing Kings means check as well */
1007                   int i, dir;
1008                   dir = (king >= BlackPawn) ? -1 : 1;
1009                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1010                                 board[i][cl.fking] == EmptySquare; i+=dir );
1011                   if(i>=0 && i<BOARD_HEIGHT &&
1012                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1013                           cl.check++;
1014               }
1015               cl.check = 0;
1016               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
1017               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1018                  goto undo_move;  /* 2-level break */
1019           }
1020       }
1021
1022   undo_move:
1023
1024     if (rf >= 0) {
1025         board[rf][ff] = board[rt][ft];
1026         if (enPassant) {
1027             board[rf][ft] = captured;
1028             board[rt][ft] = EmptySquare;
1029         } else {
1030             board[rt][ft] = captured;
1031         }
1032     } else board[rt][ft] = EmptySquare; // [HGM] drop
1033
1034     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1035 }
1036
1037 ChessMove LegalDrop(board, flags, piece, rt, ft)
1038      Board board;
1039      int flags;
1040      ChessSquare piece;
1041      int rt, ft;
1042 {   // [HGM] put drop legality testing in separate routine for clarity
1043     int n;
1044 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1045     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1046     n = PieceToNumber(piece);
1047     if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1048         return ImpossibleMove; // piece not available
1049     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1050         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1051            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1052             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1053             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1054         if(piece == WhitePawn || piece == BlackPawn) {
1055             int r;
1056             for(r=1; r<BOARD_HEIGHT-1; r++)
1057                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1058             // should still test if we mate with this Pawn
1059         }
1060     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1061         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1062     } else {
1063         if( (piece == WhitePawn || piece == BlackPawn) &&
1064             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1065             return IllegalMove; /* no pawn drops on 1st/8th */
1066     }
1067 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1068     if (!(flags & F_IGNORE_CHECK) &&
1069         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1070     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1071 }
1072
1073 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1074                                     int rf, int ff, int rt, int ft,
1075                                     VOIDSTAR closure));
1076
1077 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1078      Board board;
1079      int flags;
1080      ChessMove kind;
1081      int rf, ff, rt, ft;
1082      VOIDSTAR closure;
1083 {
1084     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1085
1086 //    if (appData.debugMode) {
1087 //        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1088 //    }
1089     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1090         cl->captures++; // [HGM] losers: count legal captures
1091     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1092       cl->kind = kind;
1093 }
1094
1095 ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
1096      Board board;
1097      int flags;
1098      int rf, ff, rt, ft, promoChar;
1099 {
1100     LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
1101
1102     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1103     piece = board[rf][ff];
1104
1105     if (appData.debugMode) {
1106         int i;
1107         for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1108         fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1109     }
1110     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1111     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1112     if(piece == WhiteFalcon || piece == BlackFalcon ||
1113        piece == WhiteCobra  || piece == BlackCobra)
1114         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1115
1116     cl.rf = rf;
1117     cl.ff = ff;
1118     cl.rt = rt;
1119     cl.ft = ft;
1120     cl.kind = IllegalMove;
1121     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1122     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
1123     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1124                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1125         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1126
1127     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1128     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1129         if(board[rf][ff] < BlackPawn) { // white
1130             if(rf != 0) return IllegalMove; // must be on back rank
1131             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1132         } else {
1133             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1134             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1135         }
1136     } else
1137     if(gameInfo.variant == VariantShogi) {
1138         /* [HGM] Shogi promotions. '=' means defer */
1139         if(rf != DROP_RANK && cl.kind == NormalMove) {
1140             ChessSquare piece = board[rf][ff];
1141
1142             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1143             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1144                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1145                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1146                   promoChar = '+'; // allowed ICS notations
1147 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1148             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1149                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1150             else if(flags & F_WHITE_ON_MOVE) {
1151                 if( (int) piece < (int) WhiteWazir &&
1152                      (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
1153                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1154                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1155                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1156                     else /* promotion optional, default is defer */
1157                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1158                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1159             } else {
1160                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1161                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1162                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1163                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1164                     else /* promotion optional, default is defer */
1165                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1166                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1167             }
1168         }
1169     } else
1170     if (promoChar != NULLCHAR) {
1171         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1172         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1173             if(CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar)) == EmptySquare)
1174                 cl.kind = ImpossibleMove; // non-existing piece
1175             if(gameInfo.variant == VariantSpartan && cl.kind == BlackPromotion && promoChar != PieceToChar(BlackKing) &&
1176                CheckTest(board, flags, rf, ff, rt, ft, FALSE)) cl.kind = IllegalMove; // [HGM] spartan: only promotion to King was possible
1177         } else {
1178             cl.kind = IllegalMove;
1179         }
1180     }
1181     return cl.kind;
1182 }
1183
1184 typedef struct {
1185     int count;
1186 } MateTestClosure;
1187
1188 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1189                                 int rf, int ff, int rt, int ft,
1190                                 VOIDSTAR closure));
1191
1192 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1193      Board board;
1194      int flags;
1195      ChessMove kind;
1196      int rf, ff, rt, ft;
1197      VOIDSTAR closure;
1198 {
1199     register MateTestClosure *cl = (MateTestClosure *) closure;
1200
1201     cl->count++;
1202 }
1203
1204 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1205 int MateTest(board, flags)
1206      Board board;
1207      int flags;
1208 {
1209     MateTestClosure cl;
1210     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1211     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1212
1213     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1214         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1215         nrKing += (board[r][f] == king);   // stm has king
1216         if( board[r][f] != EmptySquare ) {
1217             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1218                  myPieces++;
1219             else hisPieces++;
1220         }
1221     }
1222     if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1223     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1224         case VariantShatranj:
1225                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1226         default:
1227                 break;
1228         case VariantAtomic:
1229                 if(nrKing == 0) return MT_NOKING;
1230                 break;
1231         case VariantLosers:
1232                 if(myPieces == 1) return MT_BARE;
1233     }
1234     cl.count = 0;
1235     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
1236     // [HGM] 3check: yet to do!
1237     if (cl.count > 0) {
1238         return inCheck ? MT_CHECK : MT_NONE;
1239     } else {
1240         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // drop game
1241             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1242             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1243                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1244                     if(board[n][holdings] != EmptySquare) {
1245                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1246                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1247                     }
1248         }
1249         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1250                 return myPieces == hisPieces ? MT_STALEMATE :
1251                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1252         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1253         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1254
1255         return inCheck ? MT_CHECKMATE
1256                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
1257                           MT_STAINMATE : MT_STALEMATE;
1258     }
1259 }
1260
1261
1262 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1263                                     int rf, int ff, int rt, int ft,
1264                                     VOIDSTAR closure));
1265
1266 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1267      Board board;
1268      int flags;
1269      ChessMove kind;
1270      int rf, ff, rt, ft;
1271      VOIDSTAR closure;
1272 {
1273     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1274     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1275
1276     // [HGM] wild: for wild-card pieces rt and rf are dummies
1277     if(piece == WhiteFalcon || piece == BlackFalcon ||
1278        piece == WhiteCobra  || piece == BlackCobra)
1279         wildCard = TRUE;
1280
1281     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1282          || PieceToChar(board[rf][ff]) == '~'
1283               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1284                                                                       ) &&
1285         (cl->rfIn == -1 || cl->rfIn == rf) &&
1286         (cl->ffIn == -1 || cl->ffIn == ff) &&
1287         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1288         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1289
1290         cl->count++;
1291         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1292           // [HGM] oneclick: if multiple moves, be sure we remember capture
1293           cl->piece = board[rf][ff];
1294           cl->rf = rf;
1295           cl->ff = ff;
1296           cl->rt = wildCard ? cl->rtIn : rt;
1297           cl->ft = wildCard ? cl->ftIn : ft;
1298           cl->kind = kind;
1299         }
1300         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1301     }
1302 }
1303
1304 void Disambiguate(board, flags, closure)
1305      Board board;
1306      int flags;
1307      DisambiguateClosure *closure;
1308 {
1309     int illegal = 0; char c = closure->promoCharIn;
1310
1311     closure->count = closure->captures = 0;
1312     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1313     closure->kind = ImpossibleMove;
1314     if (appData.debugMode) {
1315         fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
1316                              closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1317                              closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1318     }
1319     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
1320     if (closure->count == 0) {
1321         /* See if it's an illegal move due to check */
1322         illegal = 1;
1323         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
1324         if (closure->count == 0) {
1325             /* No, it's not even that */
1326     if (appData.debugMode) { int i, j;
1327         for(i=BOARD_HEIGHT-1; i>=0; i--) {
1328                 for(j=0; j<BOARD_WIDTH; j++)
1329                         fprintf(debugFP, "%3d", (int) board[i][j]);
1330                 fprintf(debugFP, "\n");
1331         }
1332     }
1333             return;
1334         }
1335     }
1336
1337     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1338     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1339         if(closure->piece < BlackPawn) { // white
1340             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1341             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1342         } else {
1343             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1344             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1345         }
1346     } else
1347     if(gameInfo.variant == VariantShogi) {
1348         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1349         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1350             ChessSquare piece = closure->piece;
1351             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1352                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1353                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1354                    c = '+'; // allowed ICS notations
1355             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1356             else if(flags & F_WHITE_ON_MOVE) {
1357                 if( (int) piece < (int) WhiteWazir &&
1358                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1359                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1360                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1361                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1362                     else /* promotion optional, default is defer */
1363                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; 
1364                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1365             } else {
1366                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1367                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1368                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1369                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1370                     else /* promotion optional, default is defer */
1371                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1372                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1373             }
1374         }
1375         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1376         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1377     } else
1378     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1379         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1380             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
1381                 c = PieceToChar(BlackFerz);
1382             else if(gameInfo.variant == VariantGreat)
1383                 c = PieceToChar(BlackMan);
1384             else
1385                 c = PieceToChar(BlackQueen);
1386         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1387     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1388
1389     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1390     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1391         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1392     if (closure->count > 1) {
1393         closure->kind = AmbiguousMove;
1394     }
1395     if (illegal) {
1396         /* Note: If more than one illegal move matches, but no legal
1397            moves, we return IllegalMove, not AmbiguousMove.  Caller
1398            can look at closure->count to detect this.
1399         */
1400         closure->kind = IllegalMove;
1401     }
1402     if (appData.debugMode) {
1403         fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1404         closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1405         closure->promoChar >= ' ' ? closure->promoChar:'-');
1406     }
1407 }
1408
1409
1410 typedef struct {
1411     /* Input */
1412     ChessSquare piece;
1413     int rf, ff, rt, ft;
1414     /* Output */
1415     ChessMove kind;
1416     int rank;
1417     int file;
1418     int either;
1419 } CoordsToAlgebraicClosure;
1420
1421 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1422                                          ChessMove kind, int rf, int ff,
1423                                          int rt, int ft, VOIDSTAR closure));
1424
1425 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1426      Board board;
1427      int flags;
1428      ChessMove kind;
1429      int rf, ff, rt, ft;
1430      VOIDSTAR closure;
1431 {
1432     register CoordsToAlgebraicClosure *cl =
1433       (CoordsToAlgebraicClosure *) closure;
1434
1435     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1436         (board[rf][ff] == cl->piece
1437          || PieceToChar(board[rf][ff]) == '~' &&
1438             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1439                                      ) {
1440         if (rf == cl->rf) {
1441             if (ff == cl->ff) {
1442                 cl->kind = kind; /* this is the move we want */
1443             } else {
1444                 cl->file++; /* need file to rule out this move */
1445             }
1446         } else {
1447             if (ff == cl->ff) {
1448                 cl->rank++; /* need rank to rule out this move */
1449             } else {
1450                 cl->either++; /* rank or file will rule out this move */
1451             }
1452         }
1453     }
1454 }
1455
1456 /* Convert coordinates to normal algebraic notation.
1457    promoChar must be NULLCHAR or 'x' if not a promotion.
1458 */
1459 ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
1460      Board board;
1461      int flags;
1462      int rf, ff, rt, ft;
1463      int promoChar;
1464      char out[MOVE_LEN];
1465 {
1466     ChessSquare piece;
1467     ChessMove kind;
1468     char *outp = out, c;
1469     CoordsToAlgebraicClosure cl;
1470
1471     if (rf == DROP_RANK) {
1472         /* Bughouse piece drop */
1473         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1474         *outp++ = '@';
1475         *outp++ = ft + AAA;
1476         if(rt+ONE <= '9')
1477            *outp++ = rt + ONE;
1478         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1479         *outp = NULLCHAR;
1480         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1481     }
1482
1483     if (promoChar == 'x') promoChar = NULLCHAR;
1484     piece = board[rf][ff];
1485     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1486
1487   if (appData.debugMode)
1488           fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1489     switch (piece) {
1490       case WhitePawn:
1491       case BlackPawn:
1492         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1493         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1494             /* Keep short notation if move is illegal only because it
1495                leaves the player in check, but still return IllegalMove */
1496             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1497             if (kind == IllegalMove) break;
1498             kind = IllegalMove;
1499         }
1500         /* Pawn move */
1501         *outp++ = ff + AAA;
1502         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1503             /* Non-capture; use style "e5" */
1504             if(rt+ONE <= '9')
1505                *outp++ = rt + ONE;
1506             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1507         } else {
1508             /* Capture; use style "exd5" */
1509             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1510             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1511             *outp++ = ft + AAA;
1512             if(rt+ONE <= '9')
1513                *outp++ = rt + ONE;
1514             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1515         }
1516         /* Use promotion suffix style "=Q" */
1517         *outp = NULLCHAR;
1518   if (appData.debugMode)
1519           fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1520         if (promoChar != NULLCHAR) {
1521             if(gameInfo.variant == VariantShogi) {
1522                 /* [HGM] ... but not in Shogi! */
1523                 *outp++ = promoChar == '=' ? '=' : '+';
1524             } else {
1525                 *outp++ = '=';
1526                 *outp++ = ToUpper(promoChar);
1527             }
1528             *outp = NULLCHAR;
1529         }
1530         return kind;
1531
1532
1533       case WhiteKing:
1534       case BlackKing:
1535         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1536         /* Code added by Tord:  FRC castling. */
1537         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1538            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1539           if(ft > ff)
1540             safeStrCpy(out, "O-O", MOVE_LEN);
1541           else
1542             safeStrCpy(out, "O-O-O", MOVE_LEN);
1543           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1544         }
1545         /* End of code added by Tord */
1546         /* Test for castling or ICS wild castling */
1547         /* Use style "O-O" (oh-oh) for PGN compatibility */
1548         else if (rf == rt &&
1549             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1550             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1551             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1552              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1553             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1554               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1555             else
1556               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1557
1558             /* This notation is always unambiguous, unless there are
1559                kings on both the d and e files, with "wild castling"
1560                possible for the king on the d file and normal castling
1561                possible for the other.  ICS rules for wild 9
1562                effectively make castling illegal for either king in
1563                this situation.  So I am not going to worry about it;
1564                I'll just generate an ambiguous O-O in this case.
1565             */
1566             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1567         }
1568
1569         /* else fall through */
1570       default:
1571         /* Piece move */
1572         cl.rf = rf;
1573         cl.ff = ff;
1574         cl.rt = rt;
1575         cl.ft = ft;
1576         cl.piece = piece;
1577         cl.kind = IllegalMove;
1578         cl.rank = cl.file = cl.either = 0;
1579         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1580
1581         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1582             /* Generate pretty moves for moving into check, but
1583                still return IllegalMove.
1584             */
1585             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1586             if (cl.kind == IllegalMove) break;
1587             cl.kind = IllegalMove;
1588         }
1589
1590         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1591            else "Ngf3" or "Ngxf7",
1592            else "N1f3" or "N5xf7",
1593            else "Ng1f3" or "Ng5xf7".
1594         */
1595         c = PieceToChar(piece) ;
1596         if( c == '~' || c == '+') {
1597            /* [HGM] print nonexistent piece as its demoted version */
1598            piece = (ChessSquare) (DEMOTED piece);
1599         }
1600         if(c=='+') *outp++ = c;
1601         *outp++ = ToUpper(PieceToChar(piece));
1602
1603         if (cl.file || (cl.either && !cl.rank)) {
1604             *outp++ = ff + AAA;
1605         }
1606         if (cl.rank) {
1607             if(rf+ONE <= '9')
1608                 *outp++ = rf + ONE;
1609             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1610         }
1611
1612         if(board[rt][ft] != EmptySquare)
1613           *outp++ = 'x';
1614
1615         *outp++ = ft + AAA;
1616         if(rt+ONE <= '9')
1617            *outp++ = rt + ONE;
1618         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1619         if (gameInfo.variant == VariantShogi) {
1620             /* [HGM] in Shogi non-pawns can promote */
1621             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1622         }
1623         else if (gameInfo.variant != VariantSuper && promoChar && 
1624                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1625             *outp++ = '=';
1626             *outp++ = ToUpper(promoChar);
1627         }
1628         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1629             *outp++ = '/';
1630             *outp++ = ToUpper(promoChar);
1631         }
1632         *outp = NULLCHAR;
1633         return cl.kind;
1634         
1635       case EmptySquare:
1636         /* Moving a nonexistent piece */
1637         break;
1638     }
1639
1640     /* Not a legal move, even ignoring check.
1641        If there was a piece on the from square,
1642        use style "Ng1g3" or "Ng1xe8";
1643        if there was a pawn or nothing (!),
1644        use style "g1g3" or "g1xe8".  Use "x"
1645        if a piece was on the to square, even
1646        a piece of the same color.
1647     */
1648     outp = out;
1649     c = 0;
1650     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1651         int r, f;
1652       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1653                 c += (board[r][f] == piece); // count on-board pieces of given type
1654         *outp++ = ToUpper(PieceToChar(piece));
1655     }
1656   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1657     *outp++ = ff + AAA;
1658     if(rf+ONE <= '9')
1659        *outp++ = rf + ONE;
1660     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1661   }
1662     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1663     *outp++ = ft + AAA;
1664     if(rt+ONE <= '9')
1665        *outp++ = rt + ONE;
1666     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1667     /* Use promotion suffix style "=Q" */
1668     if (promoChar != NULLCHAR && promoChar != 'x') {
1669         *outp++ = '=';
1670         *outp++ = ToUpper(promoChar);
1671     }
1672     *outp = NULLCHAR;
1673
1674     return IllegalMove;
1675 }
1676
1677 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1678
1679 typedef struct {
1680     /* Input */
1681     int rf, ff, rt, ft;
1682     /* Output */
1683     int recaptures;
1684 } ChaseClosure;
1685
1686 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1687
1688 int preyStackPointer, chaseStackPointer;
1689
1690 struct {
1691 char rf, ff, rt, ft;
1692 } chaseStack[100];
1693
1694 struct {
1695 char rank, file;
1696 } preyStack[100];
1697
1698
1699
1700
1701 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1702
1703 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1704                                 int rf, int ff, int rt, int ft,
1705                                 VOIDSTAR closure));
1706
1707 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1708      Board board;
1709      int flags;
1710      ChessMove kind;
1711      int rf, ff, rt, ft;
1712      VOIDSTAR closure;
1713 {   // For adding captures that can lead to chase indictment to the chaseStack
1714     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1715     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1716     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1717     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1718     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1719     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1720     chaseStack[chaseStackPointer].rf = rf;
1721     chaseStack[chaseStackPointer].ff = ff;
1722     chaseStack[chaseStackPointer].rt = rt;
1723     chaseStack[chaseStackPointer].ft = ft;
1724     chaseStackPointer++;
1725 }
1726
1727 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1728                                 int rf, int ff, int rt, int ft,
1729                                 VOIDSTAR closure));
1730
1731 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1732      Board board;
1733      int flags;
1734      ChessMove kind;
1735      int rf, ff, rt, ft;
1736      VOIDSTAR closure;
1737 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1738     int i;
1739     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1740
1741     if(board[rt][ft] == EmptySquare) return; // no capture
1742     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1743         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1744     }
1745     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1746     for(i=0; i<chaseStackPointer; i++) {
1747         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1748            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1749             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1750             chaseStack[i] = chaseStack[--chaseStackPointer];
1751             break;
1752         }
1753     }
1754 }
1755
1756 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1757                                 int rf, int ff, int rt, int ft,
1758                                 VOIDSTAR closure));
1759
1760 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1761      Board board;
1762      int flags;
1763      ChessMove kind;
1764      int rf, ff, rt, ft;
1765      VOIDSTAR closure;
1766 {   // for determining if a piece (given through the closure) is protected
1767     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1768
1769     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1770     if(appData.debugMode && board[rt][ft] != EmptySquare)
1771         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1772 }
1773
1774 extern char moveList[MAX_MOVES][MOVE_LEN];
1775
1776 int PerpetualChase(int first, int last)
1777 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1778     int i, j, k, tail;
1779     ChaseClosure cl;
1780     ChessSquare captured;
1781
1782     preyStackPointer = 0;        // clear stack of chased pieces
1783     for(i=first; i<last; i+=2) { // for all positions with same side to move
1784         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1785         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1786         // determine all captures possible after the move, and put them on chaseStack
1787         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
1788         if(appData.debugMode) { int n;
1789             for(n=0; n<chaseStackPointer; n++)
1790                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1791                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1792             fprintf(debugFP, ": all capts\n");
1793         }
1794         // determine all captures possible before the move, and delete them from chaseStack
1795         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1796         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1797         cl.rt = moveList[i][3]-ONE;
1798         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1799         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl);
1800         if(appData.debugMode) { int n;
1801             for(n=0; n<chaseStackPointer; n++)
1802                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1803                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1804             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1805         }
1806         // chaseSack now contains all captures made possible by the move
1807         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1808             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1809             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1810
1811             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1812             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1813
1814             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1815                 continue; // C or H attack on R is always chase; leave on chaseStack
1816
1817             if(attacker == victim) {
1818                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1819                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1820                         // we can capture back with equal piece, so this is no chase but a sacrifice
1821                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1822                         j--; /* ! */ continue;
1823                 }
1824
1825             }
1826
1827             // the attack is on a lower piece, or on a pinned or blocked equal one
1828             // test if the victim is protected by a true protector. First make the capture.
1829             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1830             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1831             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1832             // Then test if the opponent can recapture
1833             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1834             cl.rt = chaseStack[j].rt;
1835             cl.ft = chaseStack[j].ft;
1836             if(appData.debugMode) {
1837                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1838             }
1839             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
1840             // unmake the capture
1841             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1842             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1843             // if a recapture was found, piece is protected, and we are not chasing it.
1844             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1845                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1846                 j--; /* ! */
1847             }
1848         }
1849         // chaseStack now contains all moves that chased
1850         if(appData.debugMode) { int n;
1851             for(n=0; n<chaseStackPointer; n++)
1852                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1853                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1854             fprintf(debugFP, ": chases\n");
1855         }
1856         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1857             for(j=0; j<chaseStackPointer; j++) {
1858                 preyStack[j].rank = chaseStack[j].rt;
1859                 preyStack[j].file = chaseStack[j].ft;
1860             }
1861             preyStackPointer = chaseStackPointer;
1862         }
1863         tail = 0;
1864         for(j=0; j<chaseStackPointer; j++) {
1865             for(k=0; k<preyStackPointer; k++) {
1866                 // search the victim of each chase move on the preyStack (first occurrence)
1867                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1868                     if(k < tail) break; // piece was already identified as still being chased
1869                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1870                     preyStack[tail] = preyStack[k];                // by swapping
1871                     preyStack[k] = preyStack[preyStackPointer];
1872                     tail++;
1873                     break;
1874                 }
1875             }
1876         }
1877         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1878         if(appData.debugMode) { int n;
1879             for(n=0; n<preyStackPointer; n++)
1880                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1881             fprintf(debugFP, "always chased upto ply %d\n", i);
1882         }
1883         // now adjust the location of the chased pieces according to opponent move
1884         for(j=0; j<preyStackPointer; j++) {
1885             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1886                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1887                 preyStack[j].rank = moveList[i+1][3]-ONE;
1888                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1889                 break;
1890             }
1891         }
1892     }
1893     return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
1894 }