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