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