Remove promotion-piece encoding from ChessMove type
[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 != NULLCHAR && promoChar != '+' && promoChar != '=' &&
1038                ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(promoChar) )
1039                     cl.kind = IllegalMove;
1040             else if(flags & F_WHITE_ON_MOVE) {
1041                 if( (int) piece < (int) WhiteWazir &&
1042                      (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
1043                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1044                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1045                              cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1046                     else /* promotion optional, default is promote */
1047                              cl.kind = promoChar == '=' ? WhiteNonPromotion : WhitePromotion;
1048                    
1049                 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1050                                             NormalMove : IllegalMove;
1051             } else {
1052                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1053                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1054                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1055                              cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1056                     else /* promotion optional, default is promote */
1057                              cl.kind = promoChar == '=' ? BlackNonPromotion : BlackPromotion;
1058
1059                 } else cl.kind = (promoChar == NULLCHAR || promoChar == '=') ? NormalMove : IllegalMove;
1060             }
1061         }
1062     } else
1063     if (promoChar != NULLCHAR) {
1064         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1065         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1066             if(CharToPiece(promoChar) == EmptySquare) cl.kind = ImpossibleMove; // non-existing piece
1067         } else {
1068             cl.kind = IllegalMove;
1069         }
1070     }
1071     return cl.kind;
1072 }
1073
1074 typedef struct {
1075     int count;
1076 } MateTestClosure;
1077
1078 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1079                                 int rf, int ff, int rt, int ft,
1080                                 VOIDSTAR closure));
1081
1082 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1083      Board board;
1084      int flags;
1085      ChessMove kind;
1086      int rf, ff, rt, ft;
1087      VOIDSTAR closure;
1088 {
1089     register MateTestClosure *cl = (MateTestClosure *) closure;
1090
1091     cl->count++;
1092 }
1093
1094 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1095 int MateTest(board, flags)
1096      Board board;
1097      int flags;
1098 {
1099     MateTestClosure cl;
1100     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1101     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1102
1103     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) { 
1104         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1105         nrKing += (board[r][f] == king);   // stm has king
1106         if( board[r][f] != EmptySquare ) {
1107             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1108                  myPieces++;
1109             else hisPieces++;
1110         }
1111     }
1112     if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1113     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1114         case VariantShatranj:
1115                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1116         default:
1117                 break;
1118         case VariantAtomic:
1119                 if(nrKing == 0) return MT_NOKING;
1120                 break;
1121         case VariantLosers:
1122                 if(myPieces == 1) return MT_BARE;
1123     }
1124     cl.count = 0;
1125     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
1126     // [HGM] 3check: yet to do!
1127     if (cl.count > 0) {
1128         return inCheck ? MT_CHECK : MT_NONE;
1129     } else {
1130         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper || gameInfo.variant != VariantGreat) { // drop game
1131             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1132             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1133                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1134                     if(board[n][holdings] != EmptySquare) {
1135                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1136                         if(moveType == WhiteDrop || moveType == BlackDrop) return MT_CHECK; // we can resolve check by legal drop
1137                     }
1138         }
1139         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1140                 return myPieces == hisPieces ? MT_STALEMATE :
1141                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1142         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1143         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1144                                             
1145         return inCheck ? MT_CHECKMATE 
1146                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ? 
1147                           MT_STAINMATE : MT_STALEMATE;
1148     }
1149 }
1150
1151      
1152 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1153                                     int rf, int ff, int rt, int ft,
1154                                     VOIDSTAR closure));
1155
1156 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1157      Board board;
1158      int flags;
1159      ChessMove kind;
1160      int rf, ff, rt, ft;
1161      VOIDSTAR closure;
1162 {
1163     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1164     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1165
1166     // [HGM] wild: for wild-card pieces rt and rf are dummies
1167     if(piece == WhiteFalcon || piece == BlackFalcon ||
1168        piece == WhiteCobra  || piece == BlackCobra  ||
1169        piece == WhiteLance  || piece == BlackLance)
1170         wildCard = TRUE;
1171
1172     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1173          || PieceToChar(board[rf][ff]) == '~'
1174               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1175                                                                       ) &&
1176         (cl->rfIn == -1 || cl->rfIn == rf) &&
1177         (cl->ffIn == -1 || cl->ffIn == ff) &&
1178         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1179         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1180
1181         cl->count++;
1182         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1183           // [HGM] oneclick: if multiple moves, be sure we remember capture
1184           cl->piece = board[rf][ff];
1185           cl->rf = rf;
1186           cl->ff = ff;
1187           cl->rt = wildCard ? cl->rtIn : rt;
1188           cl->ft = wildCard ? cl->ftIn : ft;
1189           cl->kind = kind;
1190         }
1191         cl->captures += (board[cl->rt][cl->ft] != EmptySquare); // [HGM] oneclick: count captures
1192     }
1193 }
1194
1195 void Disambiguate(board, flags, closure)
1196      Board board;
1197      int flags;
1198      DisambiguateClosure *closure;
1199 {
1200     int illegal = 0; char c = closure->promoCharIn;
1201
1202     closure->count = closure->captures = 0;
1203     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1204     closure->kind = ImpossibleMove;
1205     if (appData.debugMode) {
1206         fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
1207                              closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1208                              closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1209     }
1210     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
1211     if (closure->count == 0) {
1212         /* See if it's an illegal move due to check */
1213         illegal = 1;
1214         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);        
1215         if (closure->count == 0) {
1216             /* No, it's not even that */
1217     if (appData.debugMode) { int i, j;
1218         for(i=BOARD_HEIGHT-1; i>=0; i--) {
1219                 for(j=0; j<BOARD_WIDTH; j++)
1220                         fprintf(debugFP, "%3d", (int) board[i][j]);
1221                 fprintf(debugFP, "\n");
1222         }
1223     }
1224             return;
1225         }
1226     }
1227
1228     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1229     if(gameInfo.variant == VariantShogi) {
1230         /* [HGM] Shogi promotions. On input, '=' means defer, NULL promote. Afterwards, c is set to '+' for promotions, NULL other */
1231         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1232             ChessSquare piece = closure->piece;
1233             if(c != NULLCHAR && c != '+' && c != '=' &&
1234                ToUpper(PieceToChar(PROMOTED piece)) != ToUpper(c) ) 
1235                     closure->kind = IllegalMove; // the only allowed cases are '+', '=' and the promoted partner.
1236             else if(flags & F_WHITE_ON_MOVE) {
1237                 if( (int) piece < (int) WhiteWazir &&
1238                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1239                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1240                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1241                              closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1242                     else /* promotion optional, default is promote */
1243                              closure->kind = c == '=' ? WhiteNonPromotion  : WhitePromotion;
1244                 } else closure->kind = (c == NULLCHAR || c == '=') ? NormalMove : IllegalMove;
1245             } else {
1246                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1247                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1248                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1249                              closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1250                     else /* promotion optional, default is promote */
1251                              closure->kind = c == '=' ? BlackNonPromotion  : BlackPromotion;
1252                 } else closure->kind = (c == NULLCHAR || c == '=') ? NormalMove : IllegalMove;
1253             }
1254         }
1255         c = (closure->kind == WhitePromotion || closure->kind == BlackPromotion) ? '+' : NULLCHAR;
1256     } else
1257     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1258         if(c == NULLCHAR) c = 'q'; // even in Shatranj, Courier, Makruk, as Apply move corrects this to Ferz (how messy!)
1259     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1260
1261     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1262     if(c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare) closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types! 
1263     if (closure->count > 1) {
1264         closure->kind = AmbiguousMove;
1265     }
1266     if (illegal) {
1267         /* Note: If more than one illegal move matches, but no legal
1268            moves, we return IllegalMove, not AmbiguousMove.  Caller
1269            can look at closure->count to detect this.
1270         */
1271         closure->kind = IllegalMove;
1272     }
1273     if (appData.debugMode) {
1274         fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1275         closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1276         closure->promoChar >= ' ' ? closure->promoChar:'-');
1277     }
1278 }
1279
1280
1281 typedef struct {
1282     /* Input */
1283     ChessSquare piece;
1284     int rf, ff, rt, ft;
1285     /* Output */
1286     ChessMove kind;
1287     int rank;
1288     int file;
1289     int either;
1290 } CoordsToAlgebraicClosure;
1291
1292 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1293                                          ChessMove kind, int rf, int ff,
1294                                          int rt, int ft, VOIDSTAR closure));
1295
1296 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1297      Board board;
1298      int flags;
1299      ChessMove kind;
1300      int rf, ff, rt, ft;
1301      VOIDSTAR closure;
1302 {
1303     register CoordsToAlgebraicClosure *cl =
1304       (CoordsToAlgebraicClosure *) closure;
1305
1306     if (rt == cl->rt && ft == cl->ft &&
1307         (board[rf][ff] == cl->piece
1308          || PieceToChar(board[rf][ff]) == '~' &&
1309             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1310                                      ) {
1311         if (rf == cl->rf) {
1312             if (ff == cl->ff) {
1313                 cl->kind = kind; /* this is the move we want */
1314             } else {
1315                 cl->file++; /* need file to rule out this move */
1316             }
1317         } else {
1318             if (ff == cl->ff) {
1319                 cl->rank++; /* need rank to rule out this move */
1320             } else {
1321                 cl->either++; /* rank or file will rule out this move */
1322             }
1323         }           
1324     }
1325 }
1326
1327 /* Convert coordinates to normal algebraic notation.
1328    promoChar must be NULLCHAR or 'x' if not a promotion.
1329 */
1330 ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
1331      Board board;
1332      int flags;
1333      int rf, ff, rt, ft;
1334      int promoChar;
1335      char out[MOVE_LEN];
1336 {
1337     ChessSquare piece;
1338     ChessMove kind;
1339     char *outp = out, c;
1340     CoordsToAlgebraicClosure cl;
1341     
1342     if (rf == DROP_RANK) {
1343         /* Bughouse piece drop */
1344         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1345         *outp++ = '@';
1346         *outp++ = ft + AAA;
1347         if(rt+ONE <= '9')
1348            *outp++ = rt + ONE;
1349         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1350         *outp = NULLCHAR;
1351         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1352     }
1353
1354     if (promoChar == 'x') promoChar = NULLCHAR;
1355     piece = board[rf][ff];
1356     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1357
1358   if (appData.debugMode)
1359           fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1360     switch (piece) {
1361       case WhitePawn:
1362       case BlackPawn:
1363         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1364         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1365             /* Keep short notation if move is illegal only because it
1366                leaves the player in check, but still return IllegalMove */
1367             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1368             if (kind == IllegalMove) break;
1369             kind = IllegalMove;
1370         }
1371         /* Pawn move */
1372         *outp++ = ff + AAA;
1373         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1374             /* Non-capture; use style "e5" */
1375             if(rt+ONE <= '9')
1376                *outp++ = rt + ONE;
1377             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1378         } else {
1379             /* Capture; use style "exd5" */
1380             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1381             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1382             *outp++ = ft + AAA;
1383             if(rt+ONE <= '9')
1384                *outp++ = rt + ONE;
1385             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1386         }
1387         /* Use promotion suffix style "=Q" */
1388         *outp = NULLCHAR;
1389   if (appData.debugMode)
1390           fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1391         if (promoChar != NULLCHAR) {
1392             if(gameInfo.variant == VariantShogi) {
1393                 /* [HGM] ... but not in Shogi! */
1394                 *outp++ = promoChar == '=' ? '=' : '+';
1395             } else {
1396                 *outp++ = '=';
1397                 *outp++ = ToUpper(promoChar);
1398             }
1399             *outp = NULLCHAR;
1400         }
1401         return kind;
1402
1403         
1404       case WhiteKing:
1405       case BlackKing:
1406         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1407         /* Code added by Tord:  FRC castling. */
1408         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1409            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1410           if(ft > ff) strcpy(out, "O-O"); else strcpy(out, "O-O-O");
1411             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1412         }
1413         /* End of code added by Tord */
1414         /* Test for castling or ICS wild castling */
1415         /* Use style "O-O" (oh-oh) for PGN compatibility */
1416         else if (rf == rt &&
1417             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1418             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1419             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1420              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1421             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1422                 strcpy(out, "O-O");
1423             else
1424                 strcpy(out, "O-O-O");
1425
1426             /* This notation is always unambiguous, unless there are
1427                kings on both the d and e files, with "wild castling"
1428                possible for the king on the d file and normal castling
1429                possible for the other.  ICS rules for wild 9
1430                effectively make castling illegal for either king in
1431                this situation.  So I am not going to worry about it;
1432                I'll just generate an ambiguous O-O in this case.
1433             */
1434             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1435         }
1436
1437         /* else fall through */
1438       default:
1439         /* Piece move */
1440         cl.rf = rf;
1441         cl.ff = ff;
1442         cl.rt = rt;
1443         cl.ft = ft;
1444         cl.piece = piece;
1445         cl.kind = IllegalMove;
1446         cl.rank = cl.file = cl.either = 0;
1447         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1448
1449         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1450             /* Generate pretty moves for moving into check, but
1451                still return IllegalMove.
1452             */
1453             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1454             if (cl.kind == IllegalMove) break;
1455             cl.kind = IllegalMove;
1456         }
1457
1458         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1459            else "Ngf3" or "Ngxf7",
1460            else "N1f3" or "N5xf7",
1461            else "Ng1f3" or "Ng5xf7".
1462         */
1463         c = PieceToChar(piece) ;
1464         if( c == '~' || c == '+') {
1465            /* [HGM] print nonexistent piece as its demoted version */
1466            piece = (ChessSquare) (DEMOTED piece);
1467         }
1468         if(c=='+') *outp++ = c;
1469         *outp++ = ToUpper(PieceToChar(piece));
1470
1471         if (cl.file || (cl.either && !cl.rank)) {
1472             *outp++ = ff + AAA;
1473         }
1474         if (cl.rank) {
1475             if(rf+ONE <= '9')
1476                 *outp++ = rf + ONE;
1477             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1478         }
1479
1480         if(board[rt][ft] != EmptySquare)
1481           *outp++ = 'x';
1482
1483         *outp++ = ft + AAA;
1484         if(rt+ONE <= '9')
1485            *outp++ = rt + ONE;
1486         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1487         *outp = NULLCHAR;
1488         if (gameInfo.variant == VariantShogi) {
1489             /* [HGM] in Shogi non-pawns can promote */
1490             if(flags & F_WHITE_ON_MOVE) {
1491                 if( (int) cl.piece < (int) WhiteWazir &&
1492                      (rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1493                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1494                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1495                              cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1496                     else cl.kind =  WhitePromotion; /* promotion optional */
1497                    
1498                 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1499                                             NormalMove : IllegalMove;
1500             } else {
1501                 if( (int) cl.piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1502                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1503                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1504                              cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1505                     else cl.kind =  BlackPromotion; /* promotion optional */
1506                 } else cl.kind = (promoChar == NULLCHAR || promoChar == 'x' || promoChar == '=') ?
1507                                             NormalMove : IllegalMove;
1508             }
1509             if(cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1510                 /* for optional promotions append '+' or '=' */
1511                 if(promoChar == '=') {
1512                     *outp++ = '=';
1513                     cl.kind = NormalMove;
1514                 } else *outp++ = '+';
1515                 *outp = NULLCHAR;
1516             } else if(cl.kind == IllegalMove) {
1517                 /* Illegal move specifies any given promotion */
1518                 if(promoChar != NULLCHAR && promoChar != 'x') {
1519                     *outp++ = '=';
1520                     *outp++ = ToUpper(promoChar);
1521                     *outp = NULLCHAR;
1522                 }
1523             }
1524         }
1525         return cl.kind;
1526         
1527       /* [HGM] Always long notation for fairies we don't know */
1528       case WhiteFalcon:
1529       case BlackFalcon:
1530       case WhiteLance:
1531       case BlackLance:
1532       case WhiteGrasshopper:
1533       case BlackGrasshopper:
1534       case EmptySquare:
1535         /* Moving a nonexistent piece */
1536         break;
1537     }
1538     
1539     /* Not a legal move, even ignoring check.
1540        If there was a piece on the from square, 
1541        use style "Ng1g3" or "Ng1xe8";
1542        if there was a pawn or nothing (!),
1543        use style "g1g3" or "g1xe8".  Use "x"
1544        if a piece was on the to square, even
1545        a piece of the same color.
1546     */
1547     outp = out;
1548     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1549         *outp++ = ToUpper(PieceToChar(piece));
1550     }
1551     *outp++ = ff + AAA;
1552     if(rf+ONE <= '9')
1553        *outp++ = rf + ONE;
1554     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1555     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1556     *outp++ = ft + AAA;
1557     if(rt+ONE <= '9')
1558        *outp++ = rt + ONE;
1559     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1560     /* Use promotion suffix style "=Q" */
1561     if (promoChar != NULLCHAR && promoChar != 'x') {
1562         *outp++ = '=';
1563         *outp++ = ToUpper(promoChar);
1564     }
1565     *outp = NULLCHAR;
1566
1567     return IllegalMove;
1568 }
1569
1570 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1571
1572 typedef struct {
1573     /* Input */
1574     int rf, ff, rt, ft;
1575     /* Output */
1576     int recaptures;
1577 } ChaseClosure;
1578
1579 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1580
1581 int preyStackPointer, chaseStackPointer;
1582
1583 struct {
1584 char rf, ff, rt, ft;
1585 } chaseStack[100];
1586
1587 struct {
1588 char rank, file;
1589 } preyStack[100];
1590
1591
1592
1593
1594 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1595
1596 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1597                                 int rf, int ff, int rt, int ft,
1598                                 VOIDSTAR closure));
1599
1600 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1601      Board board;
1602      int flags;
1603      ChessMove kind;
1604      int rf, ff, rt, ft;
1605      VOIDSTAR closure;
1606 {   // For adding captures that can lead to chase indictment to the chaseStack
1607     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1608     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1609     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1610     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1611     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1612     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1613     chaseStack[chaseStackPointer].rf = rf;
1614     chaseStack[chaseStackPointer].ff = ff;
1615     chaseStack[chaseStackPointer].rt = rt;
1616     chaseStack[chaseStackPointer].ft = ft;
1617     chaseStackPointer++;
1618 }
1619
1620 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1621                                 int rf, int ff, int rt, int ft,
1622                                 VOIDSTAR closure));
1623
1624 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1625      Board board;
1626      int flags;
1627      ChessMove kind;
1628      int rf, ff, rt, ft;
1629      VOIDSTAR closure;
1630 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1631     int i;
1632     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1633
1634     if(board[rt][ft] == EmptySquare) return; // no capture
1635     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1636         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1637     }
1638     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1639     for(i=0; i<chaseStackPointer; i++) {
1640         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff && 
1641            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) { 
1642             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1643             chaseStack[i] = chaseStack[--chaseStackPointer];
1644             break;
1645         }
1646     }
1647 }
1648
1649 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1650                                 int rf, int ff, int rt, int ft,
1651                                 VOIDSTAR closure));
1652
1653 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1654      Board board;
1655      int flags;
1656      ChessMove kind;
1657      int rf, ff, rt, ft;
1658      VOIDSTAR closure;
1659 {   // for determining if a piece (given through the closure) is protected
1660     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1661
1662     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1663     if(appData.debugMode && board[rt][ft] != EmptySquare)
1664         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1665 }
1666
1667 extern char moveList[MAX_MOVES][MOVE_LEN];
1668
1669 int PerpetualChase(int first, int last)
1670 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1671     int i, j, k, tail;
1672     ChaseClosure cl;
1673     ChessSquare captured;
1674
1675     preyStackPointer = 0;        // clear stack of chased pieces
1676     for(i=first; i<last; i+=2) { // for all positions with same side to move
1677         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1678         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1679         // determine all captures possible after the move, and put them on chaseStack
1680         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
1681         if(appData.debugMode) { int n; 
1682             for(n=0; n<chaseStackPointer; n++) 
1683                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE, 
1684                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1685             fprintf(debugFP, ": all capts\n");
1686         }
1687         // determine all captures possible before the move, and delete them from chaseStack
1688         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1689         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1690         cl.rt = moveList[i][3]-ONE;
1691         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1692         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl);
1693         if(appData.debugMode) { int n; 
1694             for(n=0; n<chaseStackPointer; n++) 
1695                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE, 
1696                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1697             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1698         }
1699         // chaseSack now contains all captures made possible by the move
1700         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1701             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1702             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1703
1704             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1705             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1706
1707             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook) 
1708                 continue; // C or H attack on R is always chase; leave on chaseStack
1709
1710             if(attacker == victim) {
1711                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt, 
1712                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1713                         // we can capture back with equal piece, so this is no chase but a sacrifice
1714                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1715                         j--; /* ! */ continue;
1716                 }
1717
1718             }
1719
1720             // the attack is on a lower piece, or on a pinned or blocked equal one
1721             // test if the victim is protected by a true protector. First make the capture.
1722             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1723             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1724             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1725             // Then test if the opponent can recapture
1726             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1727             cl.rt = chaseStack[j].rt;
1728             cl.ft = chaseStack[j].ft;
1729             if(appData.debugMode) {
1730                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1731             }
1732             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
1733             // unmake the capture
1734             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1735             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1736             // if a recapture was found, piece is protected, and we are not chasing it.
1737             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1738                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1739                 j--; /* ! */ 
1740             }
1741         }
1742         // chaseStack now contains all moves that chased
1743         if(appData.debugMode) { int n; 
1744             for(n=0; n<chaseStackPointer; n++) 
1745                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE, 
1746                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1747             fprintf(debugFP, ": chases\n");
1748         }
1749         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1750             for(j=0; j<chaseStackPointer; j++) {
1751                 preyStack[j].rank = chaseStack[j].rt;
1752                 preyStack[j].file = chaseStack[j].ft;
1753             }
1754             preyStackPointer = chaseStackPointer;
1755         }
1756         tail = 0;
1757         for(j=0; j<chaseStackPointer; j++) {
1758             for(k=0; k<preyStackPointer; k++) {
1759                 // search the victim of each chase move on the preyStack (first occurrence)
1760                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1761                     if(k < tail) break; // piece was already identified as still being chased
1762                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1763                     preyStack[tail] = preyStack[k];                // by swapping
1764                     preyStack[k] = preyStack[preyStackPointer];
1765                     tail++;
1766                     break;
1767                 }
1768             }
1769         }
1770         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1771         if(appData.debugMode) { int n; 
1772             for(n=0; n<preyStackPointer; n++) 
1773                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1774             fprintf(debugFP, "always chased upto ply %d\n", i);
1775         }
1776         // now adjust the location of the chased pieces according to opponent move
1777         for(j=0; j<preyStackPointer; j++) {
1778             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1779                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1780                 preyStack[j].rank = moveList[i+1][3]-ONE;
1781                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1782                 break;
1783             }
1784         }
1785     }
1786     return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
1787 }