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