Add variant Spartan Chess
[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         CheckTest(board, flags, rf, ff, rt, ft,
728                   kind == WhiteCapturesEnPassant ||
729                   kind == BlackCapturesEnPassant)) return;
730     if (flags & F_ATOMIC_CAPTURE) {
731       if (board[rt][ft] != EmptySquare ||
732           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
733         int r, f;
734         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
735         if (board[rf][ff] == king) return;
736         for (r = rt-1; r <= rt+1; r++) {
737           for (f = ft-1; f <= ft+1; f++) {
738             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
739                 board[r][f] == king) return;
740           }
741         }
742       }
743     }
744     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
745 }
746
747
748 typedef struct {
749     int rf, ff, rt, ft;
750     ChessMove kind;
751     int captures; // [HGM] losers
752 } LegalityTestClosure;
753
754
755 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
756    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
757    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
758    moves that would destroy your own king.  The CASTLE_OK flags are
759    true if castling is not yet ruled out by a move of the king or
760    rook.  Return TRUE if the player on move is currently in check and
761    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
762 int GenLegal(board, flags, callback, closure)
763      Board board;
764      int flags;
765      MoveCallback callback;
766      VOIDSTAR closure;
767 {
768     GenLegalClosure cl;
769     int ff, ft, k, left, right, swap;
770     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
771     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
772
773     cl.cb = callback;
774     cl.cl = closure;
775     GenPseudoLegal(board, flags, GenLegalCallback, (VOIDSTAR) &cl);
776
777     if (!ignoreCheck &&
778         CheckTest(board, flags, -1, -1, -1, -1, FALSE)) return TRUE;
779
780     /* Generate castling moves */
781     if(gameInfo.variant == VariantKnightmate) { /* [HGM] Knightmate */
782         wKing = WhiteUnicorn; bKing = BlackUnicorn;
783     }
784
785     for (ff = BOARD_WIDTH>>1; ff >= (BOARD_WIDTH-1)>>1; ff-- /*ics wild 1*/) {
786         if ((flags & F_WHITE_ON_MOVE) &&
787             (flags & F_WHITE_KCASTLE_OK) &&
788             board[0][ff] == wKing &&
789             board[0][ff + 1] == EmptySquare &&
790             board[0][ff + 2] == EmptySquare &&
791             board[0][BOARD_RGHT-3] == EmptySquare &&
792             board[0][BOARD_RGHT-2] == EmptySquare &&
793             board[0][BOARD_RGHT-1] == WhiteRook &&
794             castlingRights[0] != NoRights && /* [HGM] check rights */
795             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
796             (ignoreCheck ||
797              (!CheckTest(board, flags, 0, ff, 0, ff + 1, FALSE) &&
798               !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-3, FALSE) &&
799               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, 0, ff, 0, BOARD_RGHT-2, FALSE)) &&
800               !CheckTest(board, flags, 0, ff, 0, ff + 2, FALSE)))) {
801
802             callback(board, flags,
803                      ff==BOARD_WIDTH>>1 ? WhiteKingSideCastle : WhiteKingSideCastleWild,
804                      0, ff, 0, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
805         }
806         if ((flags & F_WHITE_ON_MOVE) &&
807             (flags & F_WHITE_QCASTLE_OK) &&
808             board[0][ff] == wKing &&
809             board[0][ff - 1] == EmptySquare &&
810             board[0][ff - 2] == EmptySquare &&
811             board[0][BOARD_LEFT+2] == EmptySquare &&
812             board[0][BOARD_LEFT+1] == EmptySquare &&
813             board[0][BOARD_LEFT+0] == WhiteRook &&
814             castlingRights[1] != NoRights && /* [HGM] check rights */
815             ( castlingRights[2] == ff || castlingRights[6] == ff ) &&
816             (ignoreCheck ||
817              (!CheckTest(board, flags, 0, ff, 0, ff - 1, FALSE) &&
818               !CheckTest(board, flags, 0, ff, 0, BOARD_LEFT+3, FALSE) &&
819               !CheckTest(board, flags, 0, ff, 0, ff - 2, FALSE)))) {
820
821             callback(board, flags,
822                      ff==BOARD_WIDTH>>1 ? WhiteQueenSideCastle : WhiteQueenSideCastleWild,
823                      0, ff, 0, ff - ((gameInfo.boardWidth+2)>>2), closure);
824         }
825         if (!(flags & F_WHITE_ON_MOVE) &&
826             (flags & F_BLACK_KCASTLE_OK) &&
827             board[BOARD_HEIGHT-1][ff] == bKing &&
828             board[BOARD_HEIGHT-1][ff + 1] == EmptySquare &&
829             board[BOARD_HEIGHT-1][ff + 2] == EmptySquare &&
830             board[BOARD_HEIGHT-1][BOARD_RGHT-3] == EmptySquare &&
831             board[BOARD_HEIGHT-1][BOARD_RGHT-2] == EmptySquare &&
832             board[BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook &&
833             castlingRights[3] != NoRights && /* [HGM] check rights */
834             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
835             (ignoreCheck ||
836              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 1, FALSE) &&
837               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-3, FALSE) &&
838               (gameInfo.variant != VariantJanus || !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_RGHT-2, FALSE)) &&
839               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + 2, FALSE)))) {
840
841             callback(board, flags,
842                      ff==BOARD_WIDTH>>1 ? BlackKingSideCastle : BlackKingSideCastleWild,
843                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff + ((gameInfo.boardWidth+2)>>2) + (gameInfo.variant == VariantJanus), closure);
844         }
845         if (!(flags & F_WHITE_ON_MOVE) &&
846             (flags & F_BLACK_QCASTLE_OK) &&
847             board[BOARD_HEIGHT-1][ff] == bKing &&
848             board[BOARD_HEIGHT-1][ff - 1] == EmptySquare &&
849             board[BOARD_HEIGHT-1][ff - 2] == EmptySquare &&
850             board[BOARD_HEIGHT-1][BOARD_LEFT+2] == EmptySquare &&
851             board[BOARD_HEIGHT-1][BOARD_LEFT+1] == EmptySquare &&
852             board[BOARD_HEIGHT-1][BOARD_LEFT+0] == BlackRook &&
853             castlingRights[4] != NoRights && /* [HGM] check rights */
854             ( castlingRights[5] == ff || castlingRights[7] == ff ) &&
855             (ignoreCheck ||
856              (!CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 1, FALSE) &&
857               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, BOARD_LEFT+3, FALSE) &&
858               !CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - 2, FALSE)))) {
859
860             callback(board, flags,
861                      ff==BOARD_WIDTH>>1 ? BlackQueenSideCastle : BlackQueenSideCastleWild,
862                      BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, ff - ((gameInfo.boardWidth+2)>>2), closure);
863         }
864     }
865
866   if((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
867
868     /* generate all potential FRC castling moves (KxR), ignoring flags */
869     /* [HGM] test if the Rooks we find have castling rights */
870     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
871
872
873     if ((flags & F_WHITE_ON_MOVE) != 0) {
874         ff = castlingRights[2]; /* King file if we have any rights */
875         if(ff != NoRights && board[0][ff] == WhiteKing) {
876     if (appData.debugMode) {
877         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
878                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
879     }
880             ft = castlingRights[0]; /* Rook file if we have H-side rights */
881             left  = ff+1;
882             right = BOARD_RGHT-2;
883             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
884             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
885                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
886             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
887                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
888             if(ft != NoRights && board[0][ft] == WhiteRook)
889                 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
890
891             ft = castlingRights[1]; /* Rook file if we have A-side rights */
892             left  = BOARD_LEFT+2;
893             right = ff-1;
894             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
895             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
896                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
897             if(ff > BOARD_LEFT+2)
898             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
899                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
900             if(ft != NoRights && board[0][ft] == WhiteRook)
901                 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
902         }
903     } else {
904         ff = castlingRights[5]; /* King file if we have any rights */
905         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
906             ft = castlingRights[3]; /* Rook file if we have H-side rights */
907             left  = ff+1;
908             right = BOARD_RGHT-2;
909             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
910             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
911                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
912             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
913                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
914             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
915                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
916
917             ft = castlingRights[4]; /* Rook file if we have A-side rights */
918             left  = BOARD_LEFT+2;
919             right = ff-1;
920             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
921             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
922                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
923             if(ff > BOARD_LEFT+2)
924             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
925                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
926             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
927                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
928         }
929     }
930
931   }
932
933     return FALSE;
934 }
935
936
937 typedef struct {
938     int rking, fking;
939     int check;
940 } CheckTestClosure;
941
942
943 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
944                                  int rf, int ff, int rt, int ft,
945                                  VOIDSTAR closure));
946
947
948 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
949      Board board;
950      int flags;
951      ChessMove kind;
952      int rf, ff, rt, ft;
953      VOIDSTAR closure;
954 {
955     register CheckTestClosure *cl = (CheckTestClosure *) closure;
956
957     if (rt == cl->rking && ft == cl->fking) cl->check++;
958 }
959
960
961 /* If the player on move were to move from (rf, ff) to (rt, ft), would
962    he leave himself in check?  Or if rf == -1, is the player on move
963    in check now?  enPassant must be TRUE if the indicated move is an
964    e.p. capture.  The possibility of castling out of a check along the
965    back rank is not accounted for (i.e., we still return nonzero), as
966    this is illegal anyway.  Return value is the number of times the
967    king is in check. */
968 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
969      Board board;
970      int flags;
971      int rf, ff, rt, ft, enPassant;
972 {
973     CheckTestClosure cl;
974     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
975     ChessSquare captured = EmptySquare;
976     /*  Suppress warnings on uninitialized variables    */
977
978     if(gameInfo.variant == VariantXiangqi)
979         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
980     if(gameInfo.variant == VariantKnightmate)
981         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
982
983     if (rf >= 0) {
984         if (enPassant) {
985             captured = board[rf][ft];
986             board[rf][ft] = EmptySquare;
987         } else {
988             captured = board[rt][ft];
989         }
990         board[rt][ft] = board[rf][ff];
991         board[rf][ff] = EmptySquare;
992     } else board[rt][ft] = ff; // [HGM] drop
993
994     /* For compatibility with ICS wild 9, we scan the board in the
995        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
996        and we test only whether that one is in check. */
997     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
998         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
999           if (board[cl.rking][cl.fking] == king) {
1000               if(gameInfo.variant == VariantXiangqi) {
1001                   /* [HGM] In Xiangqi opposing Kings means check as well */
1002                   int i, dir;
1003                   dir = (king >= BlackPawn) ? -1 : 1;
1004                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
1005                                 board[i][cl.fking] == EmptySquare; i+=dir );
1006                   if(i>=0 && i<BOARD_HEIGHT &&
1007                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1008                           cl.check++;
1009               }
1010               cl.check = 0;
1011               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
1012               if(gameInfo.variant != VariantSpartan || cl.check == 0) // in Spartan Chess go on to test if other King is checked too
1013                  goto undo_move;  /* 2-level break */
1014           }
1015       }
1016
1017   undo_move:
1018
1019     if (rf >= 0) {
1020         board[rf][ff] = board[rt][ft];
1021         if (enPassant) {
1022             board[rf][ft] = captured;
1023             board[rt][ft] = EmptySquare;
1024         } else {
1025             board[rt][ft] = captured;
1026         }
1027     } else board[rt][ft] = EmptySquare; // [HGM] drop
1028
1029     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1030 }
1031
1032 ChessMove LegalDrop(board, flags, piece, rt, ft)
1033      Board board;
1034      int flags;
1035      ChessSquare piece;
1036      int rt, ft;
1037 {   // [HGM] put drop legality testing in separate routine for clarity
1038     int n;
1039 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1040     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1041     n = PieceToNumber(piece);
1042     if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1043         return ImpossibleMove; // piece not available
1044     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1045         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1046            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1047             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1048             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1049         if(piece == WhitePawn || piece == BlackPawn) {
1050             int r;
1051             for(r=1; r<BOARD_HEIGHT-1; r++)
1052                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1053             // should still test if we mate with this Pawn
1054         }
1055     } else if(gameInfo.variant == VariantSChess) { // only back-rank drops
1056         if (rt != (piece < BlackPawn ? 0 : BOARD_HEIGHT-1)) return IllegalMove;
1057     } else {
1058         if( (piece == WhitePawn || piece == BlackPawn) &&
1059             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1060             return IllegalMove; /* no pawn drops on 1st/8th */
1061     }
1062 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1063     if (!(flags & F_IGNORE_CHECK) &&
1064         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1065     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1066 }
1067
1068 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1069                                     int rf, int ff, int rt, int ft,
1070                                     VOIDSTAR closure));
1071
1072 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1073      Board board;
1074      int flags;
1075      ChessMove kind;
1076      int rf, ff, rt, ft;
1077      VOIDSTAR closure;
1078 {
1079     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1080
1081 //    if (appData.debugMode) {
1082 //        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1083 //    }
1084     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1085         cl->captures++; // [HGM] losers: count legal captures
1086     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1087       cl->kind = kind;
1088 }
1089
1090 ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
1091      Board board;
1092      int flags;
1093      int rf, ff, rt, ft, promoChar;
1094 {
1095     LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
1096
1097     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1098     piece = board[rf][ff];
1099
1100     if (appData.debugMode) {
1101         int i;
1102         for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1103         fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1104     }
1105     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1106     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1107     if(piece == WhiteFalcon || piece == BlackFalcon ||
1108        piece == WhiteCobra  || piece == BlackCobra)
1109         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1110
1111     cl.rf = rf;
1112     cl.ff = ff;
1113     cl.rt = rt;
1114     cl.ft = ft;
1115     cl.kind = IllegalMove;
1116     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1117     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
1118     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1119                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1120         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1121
1122     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1123     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1124         if(board[rf][ff] < BlackPawn) { // white
1125             if(rf != 0) return IllegalMove; // must be on back rank
1126             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1127         } else {
1128             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1129             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1130         }
1131     } else
1132     if(gameInfo.variant == VariantShogi) {
1133         /* [HGM] Shogi promotions. '=' means defer */
1134         if(rf != DROP_RANK && cl.kind == NormalMove) {
1135             ChessSquare piece = board[rf][ff];
1136
1137             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1138             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1139                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1140                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1141                   promoChar = '+'; // allowed ICS notations
1142 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1143             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1144                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1145             else if(flags & F_WHITE_ON_MOVE) {
1146                 if( (int) piece < (int) WhiteWazir &&
1147                      (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
1148                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1149                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1150                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1151                     else /* promotion optional, default is defer */
1152                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1153                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1154             } else {
1155                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1156                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1157                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1158                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1159                     else /* promotion optional, default is defer */
1160                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1161                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1162             }
1163         }
1164     } else
1165     if (promoChar != NULLCHAR) {
1166         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1167         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1168             if(CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar)) == EmptySquare)
1169                 cl.kind = ImpossibleMove; // non-existing piece
1170         } else {
1171             cl.kind = IllegalMove;
1172         }
1173     }
1174     return cl.kind;
1175 }
1176
1177 typedef struct {
1178     int count;
1179 } MateTestClosure;
1180
1181 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1182                                 int rf, int ff, int rt, int ft,
1183                                 VOIDSTAR closure));
1184
1185 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1186      Board board;
1187      int flags;
1188      ChessMove kind;
1189      int rf, ff, rt, ft;
1190      VOIDSTAR closure;
1191 {
1192     register MateTestClosure *cl = (MateTestClosure *) closure;
1193
1194     cl->count++;
1195 }
1196
1197 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1198 int MateTest(board, flags)
1199      Board board;
1200      int flags;
1201 {
1202     MateTestClosure cl;
1203     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1204     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1205
1206     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1207         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1208         nrKing += (board[r][f] == king);   // stm has king
1209         if( board[r][f] != EmptySquare ) {
1210             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1211                  myPieces++;
1212             else hisPieces++;
1213         }
1214     }
1215     if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1216     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1217         case VariantShatranj:
1218                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1219         default:
1220                 break;
1221         case VariantAtomic:
1222                 if(nrKing == 0) return MT_NOKING;
1223                 break;
1224         case VariantLosers:
1225                 if(myPieces == 1) return MT_BARE;
1226     }
1227     cl.count = 0;
1228     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
1229     // [HGM] 3check: yet to do!
1230     if (cl.count > 0) {
1231         return inCheck ? MT_CHECK : MT_NONE;
1232     } else {
1233         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat) { // drop game
1234             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1235             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1236                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1237                     if(board[n][holdings] != EmptySquare) {
1238                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1239                         if(moveType == WhiteDrop || moveType == BlackDrop) return (inCheck ? MT_CHECK : MT_NONE); // we have legal drop
1240                     }
1241         }
1242         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1243                 return myPieces == hisPieces ? MT_STALEMATE :
1244                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1245         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1246         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1247
1248         return inCheck ? MT_CHECKMATE
1249                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
1250                           MT_STAINMATE : MT_STALEMATE;
1251     }
1252 }
1253
1254
1255 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1256                                     int rf, int ff, int rt, int ft,
1257                                     VOIDSTAR closure));
1258
1259 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1260      Board board;
1261      int flags;
1262      ChessMove kind;
1263      int rf, ff, rt, ft;
1264      VOIDSTAR closure;
1265 {
1266     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1267     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1268
1269     // [HGM] wild: for wild-card pieces rt and rf are dummies
1270     if(piece == WhiteFalcon || piece == BlackFalcon ||
1271        piece == WhiteCobra  || piece == BlackCobra)
1272         wildCard = TRUE;
1273
1274     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1275          || PieceToChar(board[rf][ff]) == '~'
1276               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1277                                                                       ) &&
1278         (cl->rfIn == -1 || cl->rfIn == rf) &&
1279         (cl->ffIn == -1 || cl->ffIn == ff) &&
1280         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1281         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1282
1283         cl->count++;
1284         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1285           // [HGM] oneclick: if multiple moves, be sure we remember capture
1286           cl->piece = board[rf][ff];
1287           cl->rf = rf;
1288           cl->ff = ff;
1289           cl->rt = wildCard ? cl->rtIn : rt;
1290           cl->ft = wildCard ? cl->ftIn : ft;
1291           cl->kind = kind;
1292         }
1293         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1294     }
1295 }
1296
1297 void Disambiguate(board, flags, closure)
1298      Board board;
1299      int flags;
1300      DisambiguateClosure *closure;
1301 {
1302     int illegal = 0; char c = closure->promoCharIn;
1303
1304     closure->count = closure->captures = 0;
1305     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1306     closure->kind = ImpossibleMove;
1307     if (appData.debugMode) {
1308         fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
1309                              closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1310                              closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1311     }
1312     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
1313     if (closure->count == 0) {
1314         /* See if it's an illegal move due to check */
1315         illegal = 1;
1316         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
1317         if (closure->count == 0) {
1318             /* No, it's not even that */
1319     if (appData.debugMode) { int i, j;
1320         for(i=BOARD_HEIGHT-1; i>=0; i--) {
1321                 for(j=0; j<BOARD_WIDTH; j++)
1322                         fprintf(debugFP, "%3d", (int) board[i][j]);
1323                 fprintf(debugFP, "\n");
1324         }
1325     }
1326             return;
1327         }
1328     }
1329
1330     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1331     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1332         if(closure->piece < BlackPawn) { // white
1333             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1334             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1335         } else {
1336             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1337             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1338         }
1339     } else
1340     if(gameInfo.variant == VariantShogi) {
1341         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1342         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1343             ChessSquare piece = closure->piece;
1344             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1345                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1346                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1347                    c = '+'; // allowed ICS notations
1348             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1349             else if(flags & F_WHITE_ON_MOVE) {
1350                 if( (int) piece < (int) WhiteWazir &&
1351                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1352                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1353                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1354                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1355                     else /* promotion optional, default is defer */
1356                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; 
1357                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1358             } else {
1359                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1360                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1361                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1362                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1363                     else /* promotion optional, default is defer */
1364                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1365                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1366             }
1367         }
1368         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1369         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1370     } else
1371     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1372         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1373             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
1374                 c = PieceToChar(BlackFerz);
1375             else if(gameInfo.variant == VariantGreat)
1376                 c = PieceToChar(BlackMan);
1377             else
1378                 c = PieceToChar(BlackQueen);
1379         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1380     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1381
1382     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1383     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1384         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1385     if (closure->count > 1) {
1386         closure->kind = AmbiguousMove;
1387     }
1388     if (illegal) {
1389         /* Note: If more than one illegal move matches, but no legal
1390            moves, we return IllegalMove, not AmbiguousMove.  Caller
1391            can look at closure->count to detect this.
1392         */
1393         closure->kind = IllegalMove;
1394     }
1395     if (appData.debugMode) {
1396         fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1397         closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1398         closure->promoChar >= ' ' ? closure->promoChar:'-');
1399     }
1400 }
1401
1402
1403 typedef struct {
1404     /* Input */
1405     ChessSquare piece;
1406     int rf, ff, rt, ft;
1407     /* Output */
1408     ChessMove kind;
1409     int rank;
1410     int file;
1411     int either;
1412 } CoordsToAlgebraicClosure;
1413
1414 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1415                                          ChessMove kind, int rf, int ff,
1416                                          int rt, int ft, VOIDSTAR closure));
1417
1418 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1419      Board board;
1420      int flags;
1421      ChessMove kind;
1422      int rf, ff, rt, ft;
1423      VOIDSTAR closure;
1424 {
1425     register CoordsToAlgebraicClosure *cl =
1426       (CoordsToAlgebraicClosure *) closure;
1427
1428     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1429         (board[rf][ff] == cl->piece
1430          || PieceToChar(board[rf][ff]) == '~' &&
1431             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1432                                      ) {
1433         if (rf == cl->rf) {
1434             if (ff == cl->ff) {
1435                 cl->kind = kind; /* this is the move we want */
1436             } else {
1437                 cl->file++; /* need file to rule out this move */
1438             }
1439         } else {
1440             if (ff == cl->ff) {
1441                 cl->rank++; /* need rank to rule out this move */
1442             } else {
1443                 cl->either++; /* rank or file will rule out this move */
1444             }
1445         }
1446     }
1447 }
1448
1449 /* Convert coordinates to normal algebraic notation.
1450    promoChar must be NULLCHAR or 'x' if not a promotion.
1451 */
1452 ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
1453      Board board;
1454      int flags;
1455      int rf, ff, rt, ft;
1456      int promoChar;
1457      char out[MOVE_LEN];
1458 {
1459     ChessSquare piece;
1460     ChessMove kind;
1461     char *outp = out, c;
1462     CoordsToAlgebraicClosure cl;
1463
1464     if (rf == DROP_RANK) {
1465         /* Bughouse piece drop */
1466         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1467         *outp++ = '@';
1468         *outp++ = ft + AAA;
1469         if(rt+ONE <= '9')
1470            *outp++ = rt + ONE;
1471         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1472         *outp = NULLCHAR;
1473         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1474     }
1475
1476     if (promoChar == 'x') promoChar = NULLCHAR;
1477     piece = board[rf][ff];
1478     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1479
1480   if (appData.debugMode)
1481           fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1482     switch (piece) {
1483       case WhitePawn:
1484       case BlackPawn:
1485         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1486         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1487             /* Keep short notation if move is illegal only because it
1488                leaves the player in check, but still return IllegalMove */
1489             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1490             if (kind == IllegalMove) break;
1491             kind = IllegalMove;
1492         }
1493         /* Pawn move */
1494         *outp++ = ff + AAA;
1495         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1496             /* Non-capture; use style "e5" */
1497             if(rt+ONE <= '9')
1498                *outp++ = rt + ONE;
1499             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1500         } else {
1501             /* Capture; use style "exd5" */
1502             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1503             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1504             *outp++ = ft + AAA;
1505             if(rt+ONE <= '9')
1506                *outp++ = rt + ONE;
1507             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1508         }
1509         /* Use promotion suffix style "=Q" */
1510         *outp = NULLCHAR;
1511   if (appData.debugMode)
1512           fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1513         if (promoChar != NULLCHAR) {
1514             if(gameInfo.variant == VariantShogi) {
1515                 /* [HGM] ... but not in Shogi! */
1516                 *outp++ = promoChar == '=' ? '=' : '+';
1517             } else {
1518                 *outp++ = '=';
1519                 *outp++ = ToUpper(promoChar);
1520             }
1521             *outp = NULLCHAR;
1522         }
1523         return kind;
1524
1525
1526       case WhiteKing:
1527       case BlackKing:
1528         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1529         /* Code added by Tord:  FRC castling. */
1530         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1531            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1532           if(ft > ff)
1533             safeStrCpy(out, "O-O", MOVE_LEN);
1534           else
1535             safeStrCpy(out, "O-O-O", MOVE_LEN);
1536           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1537         }
1538         /* End of code added by Tord */
1539         /* Test for castling or ICS wild castling */
1540         /* Use style "O-O" (oh-oh) for PGN compatibility */
1541         else if (rf == rt &&
1542             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1543             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1544             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1545              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1546             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1547               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1548             else
1549               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1550
1551             /* This notation is always unambiguous, unless there are
1552                kings on both the d and e files, with "wild castling"
1553                possible for the king on the d file and normal castling
1554                possible for the other.  ICS rules for wild 9
1555                effectively make castling illegal for either king in
1556                this situation.  So I am not going to worry about it;
1557                I'll just generate an ambiguous O-O in this case.
1558             */
1559             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1560         }
1561
1562         /* else fall through */
1563       default:
1564         /* Piece move */
1565         cl.rf = rf;
1566         cl.ff = ff;
1567         cl.rt = rt;
1568         cl.ft = ft;
1569         cl.piece = piece;
1570         cl.kind = IllegalMove;
1571         cl.rank = cl.file = cl.either = 0;
1572         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1573
1574         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1575             /* Generate pretty moves for moving into check, but
1576                still return IllegalMove.
1577             */
1578             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1579             if (cl.kind == IllegalMove) break;
1580             cl.kind = IllegalMove;
1581         }
1582
1583         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1584            else "Ngf3" or "Ngxf7",
1585            else "N1f3" or "N5xf7",
1586            else "Ng1f3" or "Ng5xf7".
1587         */
1588         c = PieceToChar(piece) ;
1589         if( c == '~' || c == '+') {
1590            /* [HGM] print nonexistent piece as its demoted version */
1591            piece = (ChessSquare) (DEMOTED piece);
1592         }
1593         if(c=='+') *outp++ = c;
1594         *outp++ = ToUpper(PieceToChar(piece));
1595
1596         if (cl.file || (cl.either && !cl.rank)) {
1597             *outp++ = ff + AAA;
1598         }
1599         if (cl.rank) {
1600             if(rf+ONE <= '9')
1601                 *outp++ = rf + ONE;
1602             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1603         }
1604
1605         if(board[rt][ft] != EmptySquare)
1606           *outp++ = 'x';
1607
1608         *outp++ = ft + AAA;
1609         if(rt+ONE <= '9')
1610            *outp++ = rt + ONE;
1611         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1612         if (gameInfo.variant == VariantShogi) {
1613             /* [HGM] in Shogi non-pawns can promote */
1614             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1615         }
1616         else if (gameInfo.variant != VariantSuper && promoChar && 
1617                  (piece == WhiteLance || piece == BlackLance) ) { // Lance sometimes represents Pawn
1618             *outp++ = '=';
1619             *outp++ = ToUpper(promoChar);
1620         }
1621         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1622             *outp++ = '/';
1623             *outp++ = ToUpper(promoChar);
1624         }
1625         *outp = NULLCHAR;
1626         return cl.kind;
1627         
1628       case EmptySquare:
1629         /* Moving a nonexistent piece */
1630         break;
1631     }
1632
1633     /* Not a legal move, even ignoring check.
1634        If there was a piece on the from square,
1635        use style "Ng1g3" or "Ng1xe8";
1636        if there was a pawn or nothing (!),
1637        use style "g1g3" or "g1xe8".  Use "x"
1638        if a piece was on the to square, even
1639        a piece of the same color.
1640     */
1641     outp = out;
1642     c = 0;
1643     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1644         int r, f;
1645       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1646                 c += (board[r][f] == piece); // count on-board pieces of given type
1647         *outp++ = ToUpper(PieceToChar(piece));
1648     }
1649   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1650     *outp++ = ff + AAA;
1651     if(rf+ONE <= '9')
1652        *outp++ = rf + ONE;
1653     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1654   }
1655     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1656     *outp++ = ft + AAA;
1657     if(rt+ONE <= '9')
1658        *outp++ = rt + ONE;
1659     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1660     /* Use promotion suffix style "=Q" */
1661     if (promoChar != NULLCHAR && promoChar != 'x') {
1662         *outp++ = '=';
1663         *outp++ = ToUpper(promoChar);
1664     }
1665     *outp = NULLCHAR;
1666
1667     return IllegalMove;
1668 }
1669
1670 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1671
1672 typedef struct {
1673     /* Input */
1674     int rf, ff, rt, ft;
1675     /* Output */
1676     int recaptures;
1677 } ChaseClosure;
1678
1679 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1680
1681 int preyStackPointer, chaseStackPointer;
1682
1683 struct {
1684 char rf, ff, rt, ft;
1685 } chaseStack[100];
1686
1687 struct {
1688 char rank, file;
1689 } preyStack[100];
1690
1691
1692
1693
1694 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1695
1696 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1697                                 int rf, int ff, int rt, int ft,
1698                                 VOIDSTAR closure));
1699
1700 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1701      Board board;
1702      int flags;
1703      ChessMove kind;
1704      int rf, ff, rt, ft;
1705      VOIDSTAR closure;
1706 {   // For adding captures that can lead to chase indictment to the chaseStack
1707     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1708     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1709     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1710     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1711     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1712     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1713     chaseStack[chaseStackPointer].rf = rf;
1714     chaseStack[chaseStackPointer].ff = ff;
1715     chaseStack[chaseStackPointer].rt = rt;
1716     chaseStack[chaseStackPointer].ft = ft;
1717     chaseStackPointer++;
1718 }
1719
1720 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1721                                 int rf, int ff, int rt, int ft,
1722                                 VOIDSTAR closure));
1723
1724 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1725      Board board;
1726      int flags;
1727      ChessMove kind;
1728      int rf, ff, rt, ft;
1729      VOIDSTAR closure;
1730 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1731     int i;
1732     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1733
1734     if(board[rt][ft] == EmptySquare) return; // no capture
1735     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1736         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1737     }
1738     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1739     for(i=0; i<chaseStackPointer; i++) {
1740         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1741            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1742             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1743             chaseStack[i] = chaseStack[--chaseStackPointer];
1744             break;
1745         }
1746     }
1747 }
1748
1749 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1750                                 int rf, int ff, int rt, int ft,
1751                                 VOIDSTAR closure));
1752
1753 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1754      Board board;
1755      int flags;
1756      ChessMove kind;
1757      int rf, ff, rt, ft;
1758      VOIDSTAR closure;
1759 {   // for determining if a piece (given through the closure) is protected
1760     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1761
1762     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1763     if(appData.debugMode && board[rt][ft] != EmptySquare)
1764         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1765 }
1766
1767 extern char moveList[MAX_MOVES][MOVE_LEN];
1768
1769 int PerpetualChase(int first, int last)
1770 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1771     int i, j, k, tail;
1772     ChaseClosure cl;
1773     ChessSquare captured;
1774
1775     preyStackPointer = 0;        // clear stack of chased pieces
1776     for(i=first; i<last; i+=2) { // for all positions with same side to move
1777         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1778         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1779         // determine all captures possible after the move, and put them on chaseStack
1780         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
1781         if(appData.debugMode) { int n;
1782             for(n=0; n<chaseStackPointer; n++)
1783                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1784                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1785             fprintf(debugFP, ": all capts\n");
1786         }
1787         // determine all captures possible before the move, and delete them from chaseStack
1788         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1789         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1790         cl.rt = moveList[i][3]-ONE;
1791         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1792         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl);
1793         if(appData.debugMode) { int n;
1794             for(n=0; n<chaseStackPointer; n++)
1795                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1796                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1797             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1798         }
1799         // chaseSack now contains all captures made possible by the move
1800         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1801             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1802             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1803
1804             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1805             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1806
1807             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1808                 continue; // C or H attack on R is always chase; leave on chaseStack
1809
1810             if(attacker == victim) {
1811                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1812                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1813                         // we can capture back with equal piece, so this is no chase but a sacrifice
1814                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1815                         j--; /* ! */ continue;
1816                 }
1817
1818             }
1819
1820             // the attack is on a lower piece, or on a pinned or blocked equal one
1821             // test if the victim is protected by a true protector. First make the capture.
1822             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1823             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1824             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1825             // Then test if the opponent can recapture
1826             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1827             cl.rt = chaseStack[j].rt;
1828             cl.ft = chaseStack[j].ft;
1829             if(appData.debugMode) {
1830                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1831             }
1832             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
1833             // unmake the capture
1834             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1835             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1836             // if a recapture was found, piece is protected, and we are not chasing it.
1837             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1838                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1839                 j--; /* ! */
1840             }
1841         }
1842         // chaseStack now contains all moves that chased
1843         if(appData.debugMode) { int n;
1844             for(n=0; n<chaseStackPointer; n++)
1845                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1846                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1847             fprintf(debugFP, ": chases\n");
1848         }
1849         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1850             for(j=0; j<chaseStackPointer; j++) {
1851                 preyStack[j].rank = chaseStack[j].rt;
1852                 preyStack[j].file = chaseStack[j].ft;
1853             }
1854             preyStackPointer = chaseStackPointer;
1855         }
1856         tail = 0;
1857         for(j=0; j<chaseStackPointer; j++) {
1858             for(k=0; k<preyStackPointer; k++) {
1859                 // search the victim of each chase move on the preyStack (first occurrence)
1860                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1861                     if(k < tail) break; // piece was already identified as still being chased
1862                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1863                     preyStack[tail] = preyStack[k];                // by swapping
1864                     preyStack[k] = preyStack[preyStackPointer];
1865                     tail++;
1866                     break;
1867                 }
1868             }
1869         }
1870         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1871         if(appData.debugMode) { int n;
1872             for(n=0; n<preyStackPointer; n++)
1873                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1874             fprintf(debugFP, "always chased upto ply %d\n", i);
1875         }
1876         // now adjust the location of the chased pieces according to opponent move
1877         for(j=0; j<preyStackPointer; j++) {
1878             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1879                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1880                 preyStack[j].rank = moveList[i+1][3]-ONE;
1881                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1882                 break;
1883             }
1884         }
1885     }
1886     return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
1887 }