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