Give Lance moves of Amazon in variant super
[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             /* Make Dragon-Horse also do Dababba moves outside Shogi, for better disambiguation in variant Fairy */
432             case WhiteCardinal:
433             case BlackCardinal:
434               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
435                 for (s = -2; s <= 2; s += 4) {
436                       rt = rf + s * d;
437                       ft = ff + s * (1 - d);
438                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
439                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
440                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
441                   }
442
443             /* Shogi Dragon Horse has to continue with Wazir after Bishop */
444             case SHOGI WhiteCardinal:
445             case SHOGI BlackCardinal:
446               m++;
447
448             /* Capablanca Archbishop continues as Knight                  */
449             case WhiteAngel:
450             case BlackAngel:
451               m++;
452
453             /* Shogi Bishops are ordinary Bishops */
454             case SHOGI WhiteBishop:
455             case SHOGI BlackBishop:
456             case WhiteBishop:
457             case BlackBishop:
458               for (rs = -1; rs <= 1; rs += 2)
459                 for (fs = -1; fs <= 1; fs += 2)
460                   for (i = 1;; i++) {
461                       rt = rf + (i * rs);
462                       ft = ff + (i * fs);
463                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
464                       if (SameColor(board[rf][ff], board[rt][ft])) break;
465                       callback(board, flags, NormalMove,
466                                rf, ff, rt, ft, closure);
467                       if (board[rt][ft] != EmptySquare) break;
468                   }
469                 if(m==1) goto mounted;
470                 if(m==2) goto finishGold;
471                 /* Bishop falls through */
472               break;
473
474             /* Shogi Lance is unlike anything, and asymmetric at that */
475             case SHOGI WhiteQueen:
476               for(i = 1;; i++) {
477                       rt = rf + i;
478                       ft = ff;
479                       if (rt >= BOARD_HEIGHT) 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             case SHOGI BlackQueen:
488               for(i = 1;; i++) {
489                       rt = rf - i;
490                       ft = ff;
491                       if (rt < 0) break;
492                       if (SameColor(board[rf][ff], board[rt][ft])) break;
493                       callback(board, flags, NormalMove,
494                                rf, ff, rt, ft, closure);
495                       if (board[rt][ft] != EmptySquare) break;
496               }
497               break;
498
499             /* Make Dragon-King Dababba & Rook-like outside Shogi, for better disambiguation in variant Fairy */
500             case WhiteDragon:
501             case BlackDragon:
502               for (d = 0; d <= 1; d++) // Dababba moves that Rook cannot do
503                 for (s = -2; s <= 2; s += 4) {
504                       rt = rf + s * d;
505                       ft = ff + s * (1 - d);
506                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT || board[rf+rt>>1][ff+ft>>1] == EmptySquare) continue;
507                       if (SameColor(board[rf][ff], board[rt][ft])) continue;
508                       callback(board, flags, NormalMove, rf, ff, rt, ft, closure);
509                   }
510               goto doRook;
511               
512             /* Shogi Dragon King has to continue as Ferz after Rook moves */
513             case SHOGI WhiteDragon:
514             case SHOGI BlackDragon:
515               m++;
516
517             /* Capablanca Chancellor sets flag to continue as Knight      */
518             case WhiteMarshall:
519             case BlackMarshall:
520               m++;
521
522             /* Shogi Rooks are ordinary Rooks */
523             case SHOGI WhiteRook:
524             case SHOGI BlackRook:
525             case WhiteRook:
526             case BlackRook:
527           doRook:
528               for (d = 0; d <= 1; d++)
529                 for (s = -1; s <= 1; s += 2)
530                   for (i = 1;; i++) {
531                       rt = rf + (i * s) * d;
532                       ft = ff + (i * s) * (1 - d);
533                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
534                       if (SameColor(board[rf][ff], board[rt][ft])) break;
535                       callback(board, flags, NormalMove,
536                                rf, ff, rt, ft, closure);
537                       if (board[rt][ft] != EmptySquare) break;
538                   }
539                 if(m==1) goto mounted;
540                 if(m==2) goto finishSilver;
541               break;
542
543             case WhiteQueen:
544             case BlackQueen:
545               for (rs = -1; rs <= 1; rs++)
546                 for (fs = -1; fs <= 1; fs++) {
547                     if (rs == 0 && fs == 0) continue;
548                     for (i = 1;; i++) {
549                         rt = rf + (i * rs);
550                         ft = ff + (i * fs);
551                         if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
552                         if (SameColor(board[rf][ff], board[rt][ft])) break;
553                         callback(board, flags, NormalMove,
554                                  rf, ff, rt, ft, closure);
555                         if (board[rt][ft] != EmptySquare) break;
556                     }
557                 }
558               break;
559
560             /* Shogi Pawn and Silver General: first the Pawn move,    */
561             /* then the General continues like a Ferz                 */
562             case WhiteMan:
563                 if(gameInfo.variant != VariantMakruk) goto commoner;
564             case SHOGI WhitePawn:
565             case SHOGI WhiteFerz:
566                   if (rf < BOARD_HEIGHT-1 &&
567                            !SameColor(board[rf][ff], board[rf + 1][ff]) )
568                            callback(board, flags, NormalMove,
569                                     rf, ff, rf + 1, ff, closure);
570               if(piece != SHOGI WhitePawn) goto finishSilver;
571               break;
572
573             case BlackMan:
574                 if(gameInfo.variant != VariantMakruk) goto commoner;
575             case SHOGI BlackPawn:
576             case SHOGI BlackFerz:
577                   if (rf > 0 &&
578                            !SameColor(board[rf][ff], board[rf - 1][ff]) )
579                            callback(board, flags, NormalMove,
580                                     rf, ff, rf - 1, ff, closure);
581               if(piece == SHOGI BlackPawn) break;
582
583             case WhiteFerz:
584             case BlackFerz:
585             finishSilver:
586                 /* [HGM] support Shatranj pieces */
587                 for (rs = -1; rs <= 1; rs += 2)
588                   for (fs = -1; fs <= 1; fs += 2) {
589                       rt = rf + rs;
590                       ft = ff + fs;
591                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
592                       if (!SameColor(board[rf][ff], board[rt][ft]) &&
593                           (gameInfo.variant != VariantXiangqi || InPalace(rt, ft) ) )
594                                callback(board, flags, NormalMove,
595                                         rf, ff, rt, ft, closure);
596                   }
597                 break;
598
599             case WhiteSilver:
600             case BlackSilver:
601                 m++; // [HGM] superchess: use for Centaur
602             commoner:
603             case SHOGI WhiteKing:
604             case SHOGI BlackKing:
605             case WhiteKing:
606             case BlackKing:
607 //            walking:
608               for (i = -1; i <= 1; i++)
609                 for (j = -1; j <= 1; j++) {
610                     if (i == 0 && j == 0) continue;
611                     rt = rf + i;
612                     ft = ff + j;
613                     if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
614                     if (SameColor(board[rf][ff], board[rt][ft])) continue;
615                     callback(board, flags, NormalMove,
616                              rf, ff, rt, ft, closure);
617                 }
618                 if(m==1) goto mounted;
619               break;
620
621             case WhiteNightrider:
622             case BlackNightrider:
623               for (i = -1; i <= 1; i += 2)
624                 for (j = -1; j <= 1; j += 2)
625                   for (s = 1; s <= 2; s++) {  int k;
626                     for(k=1;; k++) {
627                       rt = rf + k*i*s;
628                       ft = ff + k*j*(3-s);
629                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
630                       if (SameColor(board[rf][ff], board[rt][ft])) break;
631                       callback(board, flags, NormalMove,
632                                rf, ff, rt, ft, closure);
633                       if (board[rt][ft] != EmptySquare) break;
634                     }
635                   }
636               break;
637
638             Amazon:
639               /* First do Bishop,then continue like Chancellor */
640               for (rs = -1; rs <= 1; rs += 2)
641                 for (fs = -1; fs <= 1; fs += 2)
642                   for (i = 1;; i++) {
643                       rt = rf + (i * rs);
644                       ft = ff + (i * fs);
645                       if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) break;
646                       if (SameColor(board[rf][ff], board[rt][ft])) break;
647                       callback(board, flags, NormalMove,
648                                rf, ff, rt, ft, closure);
649                       if (board[rt][ft] != EmptySquare) break;
650                   }
651               m++;
652               goto doRook;
653
654             // Use Lance as Berolina / Spartan Pawn.
655             case WhiteLance:
656               if(gameInfo.variant == VariantSuper) goto Amazon;
657               if (rf < BOARD_HEIGHT-1 && BlackPiece(board[rf + 1][ff]))
658                   callback(board, flags,
659                            rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
660                            rf, ff, rf + 1, ff, closure);
661               for (s = -1; s <= 1; s += 2) {
662                   if (rf < BOARD_HEIGHT-1 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf + 1][ff + s] == EmptySquare)
663                       callback(board, flags, 
664                                rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove,
665                                rf, ff, rf + 1, ff + s, closure);
666                   if (rf == 1 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[3][ff + 2*s] == EmptySquare )
667                       callback(board, flags, NormalMove, rf, ff, 3, ff + 2*s, closure);
668               }
669               break;
670                 
671             case BlackLance:
672               if(gameInfo.variant == VariantSuper) goto Amazon;
673               if (rf > 0 && WhitePiece(board[rf - 1][ff]))
674                   callback(board, flags,
675                            rf <= promoRank ? BlackPromotion : NormalMove,
676                            rf, ff, rf - 1, ff, closure);
677               for (s = -1; s <= 1; s += 2) {
678                   if (rf > 0 && ff + s >= BOARD_LEFT && ff + s < BOARD_RGHT && board[rf - 1][ff + s] == EmptySquare)
679                       callback(board, flags, 
680                                rf <= promoRank ? BlackPromotion : NormalMove,
681                                rf, ff, rf - 1, ff + s, closure);
682                   if (rf == BOARD_HEIGHT-2 && ff + 2*s >= BOARD_LEFT && ff + 2*s < BOARD_RGHT && board[rf-2][ff + 2*s] == EmptySquare )
683                       callback(board, flags, NormalMove, rf, ff, rf-2, ff + 2*s, closure);
684               }
685             break;
686
687             case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
688             case BlackFalcon:
689             case WhiteCobra:
690             case BlackCobra:
691               callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
692               break;
693
694           }
695       }
696 }
697
698
699 typedef struct {
700     MoveCallback cb;
701     VOIDSTAR cl;
702 } GenLegalClosure;
703
704 extern void GenLegalCallback P((Board board, int flags, ChessMove kind,
705                                 int rf, int ff, int rt, int ft,
706                                 VOIDSTAR closure));
707
708 void GenLegalCallback(board, flags, kind, rf, ff, rt, ft, closure)
709      Board board;
710      int flags;
711      ChessMove kind;
712      int rf, ff, rt, ft;
713      VOIDSTAR closure;
714 {
715     register GenLegalClosure *cl = (GenLegalClosure *) closure;
716
717     if (!(flags & F_IGNORE_CHECK) &&
718         CheckTest(board, flags, rf, ff, rt, ft,
719                   kind == WhiteCapturesEnPassant ||
720                   kind == BlackCapturesEnPassant)) return;
721     if (flags & F_ATOMIC_CAPTURE) {
722       if (board[rt][ft] != EmptySquare ||
723           kind == WhiteCapturesEnPassant || kind == BlackCapturesEnPassant) {
724         int r, f;
725         ChessSquare king = (flags & F_WHITE_ON_MOVE) ? WhiteKing : BlackKing;
726         if (board[rf][ff] == king) return;
727         for (r = rt-1; r <= rt+1; r++) {
728           for (f = ft-1; f <= ft+1; f++) {
729             if (r >= 0 && r < BOARD_HEIGHT && f >= BOARD_LEFT && f < BOARD_RGHT &&
730                 board[r][f] == king) return;
731           }
732         }
733       }
734     }
735     cl->cb(board, flags, kind, rf, ff, rt, ft, cl->cl);
736 }
737
738
739 typedef struct {
740     int rf, ff, rt, ft;
741     ChessMove kind;
742     int captures; // [HGM] losers
743 } LegalityTestClosure;
744
745
746 /* Like GenPseudoLegal, but (1) include castling moves, (2) unless
747    F_IGNORE_CHECK is set in the flags, omit moves that would leave the
748    king in check, and (3) if F_ATOMIC_CAPTURE is set in the flags, omit
749    moves that would destroy your own king.  The CASTLE_OK flags are
750    true if castling is not yet ruled out by a move of the king or
751    rook.  Return TRUE if the player on move is currently in check and
752    F_IGNORE_CHECK is not set.  [HGM] add castlingRights parameter */
753 int GenLegal(board, flags, callback, closure)
754      Board board;
755      int flags;
756      MoveCallback callback;
757      VOIDSTAR closure;
758 {
759     GenLegalClosure cl;
760     int ff, ft, k, left, right, swap;
761     int ignoreCheck = (flags & F_IGNORE_CHECK) != 0;
762     ChessSquare wKing = WhiteKing, bKing = BlackKing, *castlingRights = board[CASTLING];
763
764     cl.cb = callback;
765     cl.cl = closure;
766     GenPseudoLegal(board, flags, 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] != NoRights && /* [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] != NoRights && /* [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] != NoRights && /* [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] != NoRights && /* [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((swap = gameInfo.variant == VariantSChess) || flags & F_FRC_TYPE_CASTLING) {
858
859     /* generate all potential FRC castling moves (KxR), ignoring flags */
860     /* [HGM] test if the Rooks we find have castling rights */
861     /* In S-Chess we generate RxK for allowed castlings, for gating at Rook square */
862
863
864     if ((flags & F_WHITE_ON_MOVE) != 0) {
865         ff = castlingRights[2]; /* King file if we have any rights */
866         if(ff != NoRights && board[0][ff] == WhiteKing) {
867     if (appData.debugMode) {
868         fprintf(debugFP, "FRC castling, %d %d %d %d %d %d\n",
869                 castlingRights[0],castlingRights[1],ff,castlingRights[3],castlingRights[4],castlingRights[5]);
870     }
871             ft = castlingRights[0]; /* Rook file if we have H-side rights */
872             left  = ff+1;
873             right = BOARD_RGHT-2;
874             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
875             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
876                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
877             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
878                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
879             if(ft != NoRights && board[0][ft] == WhiteRook)
880                 callback(board, flags, WhiteHSideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
881
882             ft = castlingRights[1]; /* Rook file if we have A-side rights */
883             left  = BOARD_LEFT+2;
884             right = ff-1;
885             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
886             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
887                 if(k != ft && board[0][k] != EmptySquare) ft = NoRights;
888             if(ff > BOARD_LEFT+2)
889             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
890                 if(!ignoreCheck && CheckTest(board, flags, 0, ff, 0, k, FALSE)) ft = NoRights;
891             if(ft != NoRights && board[0][ft] == WhiteRook)
892                 callback(board, flags, WhiteASideCastleFR, 0, swap ? ft : ff, 0, swap ? ff : ft, closure);
893         }
894     } else {
895         ff = castlingRights[5]; /* King file if we have any rights */
896         if(ff != NoRights && board[BOARD_HEIGHT-1][ff] == BlackKing) {
897             ft = castlingRights[3]; /* Rook file if we have H-side rights */
898             left  = ff+1;
899             right = BOARD_RGHT-2;
900             if(ff == BOARD_RGHT-2) left = right = ff-1;    /* special case */
901             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
902                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
903             for(k=left; k<right && ft != NoRights; k++) /* then if not checked */
904                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
905             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
906                 callback(board, flags, BlackHSideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
907
908             ft = castlingRights[4]; /* Rook file if we have A-side rights */
909             left  = BOARD_LEFT+2;
910             right = ff-1;
911             if(ff <= BOARD_LEFT+2) { left = ff+1; right = BOARD_LEFT+3; }
912             for(k=left; k<=right && ft != NoRights; k++) /* first test if blocked */
913                 if(k != ft && board[BOARD_HEIGHT-1][k] != EmptySquare) ft = NoRights;
914             if(ff > BOARD_LEFT+2)
915             for(k=left+1; k<=right && ft != NoRights; k++) /* then if not checked */
916                 if(!ignoreCheck && CheckTest(board, flags, BOARD_HEIGHT-1, ff, BOARD_HEIGHT-1, k, FALSE)) ft = NoRights;
917             if(ft != NoRights && board[BOARD_HEIGHT-1][ft] == BlackRook)
918                 callback(board, flags, BlackASideCastleFR, BOARD_HEIGHT-1, swap ? ft : ff, BOARD_HEIGHT-1, swap ? ff : ft, closure);
919         }
920     }
921
922   }
923
924     return FALSE;
925 }
926
927
928 typedef struct {
929     int rking, fking;
930     int check;
931 } CheckTestClosure;
932
933
934 extern void CheckTestCallback P((Board board, int flags, ChessMove kind,
935                                  int rf, int ff, int rt, int ft,
936                                  VOIDSTAR closure));
937
938
939 void CheckTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
940      Board board;
941      int flags;
942      ChessMove kind;
943      int rf, ff, rt, ft;
944      VOIDSTAR closure;
945 {
946     register CheckTestClosure *cl = (CheckTestClosure *) closure;
947
948     if (rt == cl->rking && ft == cl->fking) cl->check++;
949 }
950
951
952 /* If the player on move were to move from (rf, ff) to (rt, ft), would
953    he leave himself in check?  Or if rf == -1, is the player on move
954    in check now?  enPassant must be TRUE if the indicated move is an
955    e.p. capture.  The possibility of castling out of a check along the
956    back rank is not accounted for (i.e., we still return nonzero), as
957    this is illegal anyway.  Return value is the number of times the
958    king is in check. */
959 int CheckTest(board, flags, rf, ff, rt, ft, enPassant)
960      Board board;
961      int flags;
962      int rf, ff, rt, ft, enPassant;
963 {
964     CheckTestClosure cl;
965     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
966     ChessSquare captured = EmptySquare;
967     /*  Suppress warnings on uninitialized variables    */
968
969     if(gameInfo.variant == VariantXiangqi)
970         king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir;
971     if(gameInfo.variant == VariantKnightmate)
972         king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn;
973
974     if (rf >= 0) {
975         if (enPassant) {
976             captured = board[rf][ft];
977             board[rf][ft] = EmptySquare;
978         } else {
979             captured = board[rt][ft];
980         }
981         board[rt][ft] = board[rf][ff];
982         board[rf][ff] = EmptySquare;
983     } else board[rt][ft] = ff; // [HGM] drop
984
985     /* For compatibility with ICS wild 9, we scan the board in the
986        order a1, a2, a3, ... b1, b2, ..., h8 to find the first king,
987        and we test only whether that one is in check. */
988     cl.check = 0;
989     for (cl.fking = BOARD_LEFT+0; cl.fking < BOARD_RGHT; cl.fking++)
990         for (cl.rking = 0; cl.rking < BOARD_HEIGHT; cl.rking++) {
991           if (board[cl.rking][cl.fking] == king) {
992               if(gameInfo.variant == VariantXiangqi) {
993                   /* [HGM] In Xiangqi opposing Kings means check as well */
994                   int i, dir;
995                   dir = (king >= BlackPawn) ? -1 : 1;
996                   for( i=cl.rking+dir; i>=0 && i<BOARD_HEIGHT &&
997                                 board[i][cl.fking] == EmptySquare; i+=dir );
998                   if(i>=0 && i<BOARD_HEIGHT &&
999                       board[i][cl.fking] == (dir>0 ? BlackWazir : WhiteWazir) )
1000                           cl.check++;
1001               }
1002
1003               GenPseudoLegal(board, flags ^ F_WHITE_ON_MOVE, CheckTestCallback, (VOIDSTAR) &cl);
1004               goto undo_move;  /* 2-level break */
1005           }
1006       }
1007
1008   undo_move:
1009
1010     if (rf >= 0) {
1011         board[rf][ff] = board[rt][ft];
1012         if (enPassant) {
1013             board[rf][ft] = captured;
1014             board[rt][ft] = EmptySquare;
1015         } else {
1016             board[rt][ft] = captured;
1017         }
1018     } else board[rt][ft] = EmptySquare; // [HGM] drop
1019
1020     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
1021 }
1022
1023 ChessMove LegalDrop(board, flags, piece, rt, ft)
1024      Board board;
1025      int flags;
1026      ChessSquare piece;
1027      int rt, ft;
1028 {   // [HGM] put drop legality testing in separate routine for clarity
1029     int n;
1030 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1031     if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square
1032     n = PieceToNumber(piece);
1033     if(gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[BOARD_HEIGHT-1-n][0]) != piece)
1034         return ImpossibleMove; // piece not available
1035     if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden!
1036         if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 ||
1037            (piece == BlackPawn || piece == BlackQueen) && rt == 0 ||
1038             piece == WhiteKnight && rt > BOARD_HEIGHT-3 ||
1039             piece == BlackKnight && rt < 2 ) return IllegalMove; // e.g. where dropped piece has no moves
1040         if(piece == WhitePawn || piece == BlackPawn) {
1041             int r;
1042             for(r=1; r<BOARD_HEIGHT-1; r++)
1043                 if(board[r][ft] == piece) return IllegalMove; // or there already is a Pawn in file
1044             // should still test if we mate with this Pawn
1045         }
1046     } else {
1047         if( (piece == WhitePawn || piece == BlackPawn) &&
1048             (rt == 0 || rt == BOARD_HEIGHT -1 ) )
1049             return IllegalMove; /* no pawn drops on 1st/8th */
1050     }
1051 if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt);
1052     if (!(flags & F_IGNORE_CHECK) &&
1053         CheckTest(board, flags, DROP_RANK, piece, rt, ft, FALSE) ) return IllegalMove;
1054     return flags & F_WHITE_ON_MOVE ? WhiteDrop : BlackDrop;
1055 }
1056
1057 extern void LegalityTestCallback P((Board board, int flags, ChessMove kind,
1058                                     int rf, int ff, int rt, int ft,
1059                                     VOIDSTAR closure));
1060
1061 void LegalityTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1062      Board board;
1063      int flags;
1064      ChessMove kind;
1065      int rf, ff, rt, ft;
1066      VOIDSTAR closure;
1067 {
1068     register LegalityTestClosure *cl = (LegalityTestClosure *) closure;
1069
1070 //    if (appData.debugMode) {
1071 //        fprintf(debugFP, "Legality test: %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1072 //    }
1073     if(board[rt][ft] != EmptySquare || kind==WhiteCapturesEnPassant || kind==BlackCapturesEnPassant)
1074         cl->captures++; // [HGM] losers: count legal captures
1075     if (rf == cl->rf && ff == cl->ff && rt == cl->rt && ft == cl->ft)
1076       cl->kind = kind;
1077 }
1078
1079 ChessMove LegalityTest(board, flags, rf, ff, rt, ft, promoChar)
1080      Board board;
1081      int flags;
1082      int rf, ff, rt, ft, promoChar;
1083 {
1084     LegalityTestClosure cl; ChessSquare piece, *castlingRights = board[CASTLING];
1085
1086     if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
1087     piece = board[rf][ff];
1088
1089     if (appData.debugMode) {
1090         int i;
1091         for(i=0; i<6; i++) fprintf(debugFP, "%d ", castlingRights[i]);
1092         fprintf(debugFP, "Legality test? %c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);
1093     }
1094     /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
1095     /* (perhaps we should disallow moves that obviously leave us in check?)              */
1096     if(piece == WhiteFalcon || piece == BlackFalcon ||
1097        piece == WhiteCobra  || piece == BlackCobra)
1098         return CheckTest(board, flags, rf, ff, rt, ft, FALSE) ? IllegalMove : NormalMove;
1099
1100     cl.rf = rf;
1101     cl.ff = ff;
1102     cl.rt = rt;
1103     cl.ft = ft;
1104     cl.kind = IllegalMove;
1105     cl.captures = 0; // [HGM] losers: prepare to count legal captures.
1106     GenLegal(board, flags, LegalityTestCallback, (VOIDSTAR) &cl);
1107     if((flags & F_MANDATORY_CAPTURE) && cl.captures && board[rt][ft] == EmptySquare
1108                 && cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)
1109         return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal
1110
1111     if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case?
1112     if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) {
1113         if(board[rf][ff] < BlackPawn) { // white
1114             if(rf != 0) return IllegalMove; // must be on back rank
1115             if(board[PieceToNumber(CharToPiece(ToUpper(promoChar)))][BOARD_WIDTH-2] == 0) return ImpossibleMove;// must be in stock
1116         } else {
1117             if(rf != BOARD_HEIGHT-1) return IllegalMove;
1118             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(promoChar)))][1] == 0) return ImpossibleMove;
1119         }
1120     } else
1121     if(gameInfo.variant == VariantShogi) {
1122         /* [HGM] Shogi promotions. '=' means defer */
1123         if(rf != DROP_RANK && cl.kind == NormalMove) {
1124             ChessSquare piece = board[rf][ff];
1125
1126             if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */
1127             if(promoChar == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1128                promoChar == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1129                promoChar == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1130                   promoChar = '+'; // allowed ICS notations
1131 if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promoChar : '-');
1132             if(promoChar != NULLCHAR && promoChar != '+' && promoChar != '=')
1133                 return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
1134             else if(flags & F_WHITE_ON_MOVE) {
1135                 if( (int) piece < (int) WhiteWazir &&
1136                      (rf >= BOARD_HEIGHT*2/3 || rt >= BOARD_HEIGHT*2/3) ) {
1137                     if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 ||
1138                          piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */
1139                        cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion;
1140                     else /* promotion optional, default is defer */
1141                        cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion;
1142                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1143             } else {
1144                 if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) {
1145                     if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 ||
1146                          piece == BlackKnight && rt < 2 ) /* promotion obligatory */
1147                        cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion;
1148                     else /* promotion optional, default is defer */
1149                        cl.kind = promoChar == '+' ? BlackPromotion : BlackNonPromotion;
1150                 } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove;
1151             }
1152         }
1153     } else
1154     if (promoChar != NULLCHAR) {
1155         if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
1156         if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
1157             if(CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(promoChar) : ToLower(promoChar)) == EmptySquare)
1158                 cl.kind = ImpossibleMove; // non-existing piece
1159         } else {
1160             cl.kind = IllegalMove;
1161         }
1162     }
1163     return cl.kind;
1164 }
1165
1166 typedef struct {
1167     int count;
1168 } MateTestClosure;
1169
1170 extern void MateTestCallback P((Board board, int flags, ChessMove kind,
1171                                 int rf, int ff, int rt, int ft,
1172                                 VOIDSTAR closure));
1173
1174 void MateTestCallback(board, flags, kind, rf, ff, rt, ft, closure)
1175      Board board;
1176      int flags;
1177      ChessMove kind;
1178      int rf, ff, rt, ft;
1179      VOIDSTAR closure;
1180 {
1181     register MateTestClosure *cl = (MateTestClosure *) closure;
1182
1183     cl->count++;
1184 }
1185
1186 /* Return MT_NONE, MT_CHECK, MT_CHECKMATE, or MT_STALEMATE */
1187 int MateTest(board, flags)
1188      Board board;
1189      int flags;
1190 {
1191     MateTestClosure cl;
1192     int inCheck, r, f, myPieces=0, hisPieces=0, nrKing=0;
1193     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
1194
1195     for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
1196         // [HGM] losers: Count pieces and kings, to detect other unorthodox winning conditions
1197         nrKing += (board[r][f] == king);   // stm has king
1198         if( board[r][f] != EmptySquare ) {
1199             if((int)board[r][f] <= (int)king && (int)board[r][f] >= (int)king - (int)WhiteKing + (int)WhitePawn)
1200                  myPieces++;
1201             else hisPieces++;
1202         }
1203     }
1204     if(appData.debugMode) fprintf(debugFP, "MateTest: K=%d, my=%d, his=%d\n", nrKing, myPieces, hisPieces);
1205     switch(gameInfo.variant) { // [HGM] losers: extinction wins
1206         case VariantShatranj:
1207                 if(hisPieces == 1) return myPieces > 1 ? MT_BARE : MT_DRAW;
1208         default:
1209                 break;
1210         case VariantAtomic:
1211                 if(nrKing == 0) return MT_NOKING;
1212                 break;
1213         case VariantLosers:
1214                 if(myPieces == 1) return MT_BARE;
1215     }
1216     cl.count = 0;
1217     inCheck = GenLegal(board, flags, MateTestCallback, (VOIDSTAR) &cl);
1218     // [HGM] 3check: yet to do!
1219     if (cl.count > 0) {
1220         return inCheck ? MT_CHECK : MT_NONE;
1221     } else {
1222         if(gameInfo.holdingsWidth && gameInfo.variant != VariantSuper || gameInfo.variant != VariantGreat) { // drop game
1223             int r, f, n, holdings = flags & F_WHITE_ON_MOVE ? BOARD_WIDTH-1 : 0;
1224             for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) if(board[r][f] == EmptySquare) // all empty squares
1225                 for(n=0; n<BOARD_HEIGHT; n++) // all pieces in hand
1226                     if(board[n][holdings] != EmptySquare) {
1227                         int moveType = LegalDrop(board, flags, board[n][holdings], r, f);
1228                         if(moveType == WhiteDrop || moveType == BlackDrop) return MT_CHECK; // we can resolve check by legal drop
1229                     }
1230         }
1231         if(gameInfo.variant == VariantSuicide) // [HGM] losers: always stalemate, since no check, but result varies
1232                 return myPieces == hisPieces ? MT_STALEMATE :
1233                                         myPieces > hisPieces ? MT_STAINMATE : MT_STEALMATE;
1234         else if(gameInfo.variant == VariantLosers) return inCheck ? MT_TRICKMATE : MT_STEALMATE;
1235         else if(gameInfo.variant == VariantGiveaway) return MT_STEALMATE; // no check exists, stalemated = win
1236
1237         return inCheck ? MT_CHECKMATE
1238                        : (gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShatranj) ?
1239                           MT_STAINMATE : MT_STALEMATE;
1240     }
1241 }
1242
1243
1244 extern void DisambiguateCallback P((Board board, int flags, ChessMove kind,
1245                                     int rf, int ff, int rt, int ft,
1246                                     VOIDSTAR closure));
1247
1248 void DisambiguateCallback(board, flags, kind, rf, ff, rt, ft, closure)
1249      Board board;
1250      int flags;
1251      ChessMove kind;
1252      int rf, ff, rt, ft;
1253      VOIDSTAR closure;
1254 {
1255     register DisambiguateClosure *cl = (DisambiguateClosure *) closure;
1256     int wildCard = FALSE; ChessSquare piece = board[rf][ff];
1257
1258     // [HGM] wild: for wild-card pieces rt and rf are dummies
1259     if(piece == WhiteFalcon || piece == BlackFalcon ||
1260        piece == WhiteCobra  || piece == BlackCobra)
1261         wildCard = TRUE;
1262
1263     if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
1264          || PieceToChar(board[rf][ff]) == '~'
1265               && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
1266                                                                       ) &&
1267         (cl->rfIn == -1 || cl->rfIn == rf) &&
1268         (cl->ffIn == -1 || cl->ffIn == ff) &&
1269         (cl->rtIn == -1 || cl->rtIn == rt || wildCard) &&
1270         (cl->ftIn == -1 || cl->ftIn == ft || wildCard)) {
1271
1272         cl->count++;
1273         if(cl->count == 1 || board[rt][ft] != EmptySquare) {
1274           // [HGM] oneclick: if multiple moves, be sure we remember capture
1275           cl->piece = board[rf][ff];
1276           cl->rf = rf;
1277           cl->ff = ff;
1278           cl->rt = wildCard ? cl->rtIn : rt;
1279           cl->ft = wildCard ? cl->ftIn : ft;
1280           cl->kind = kind;
1281         }
1282         cl->captures += (board[rt][ft] != EmptySquare); // [HGM] oneclick: count captures
1283     }
1284 }
1285
1286 void Disambiguate(board, flags, closure)
1287      Board board;
1288      int flags;
1289      DisambiguateClosure *closure;
1290 {
1291     int illegal = 0; char c = closure->promoCharIn;
1292
1293     closure->count = closure->captures = 0;
1294     closure->rf = closure->ff = closure->rt = closure->ft = 0;
1295     closure->kind = ImpossibleMove;
1296     if (appData.debugMode) {
1297         fprintf(debugFP, "Disambiguate in:  %d(%d,%d)-(%d,%d) = %d (%c)\n",
1298                              closure->pieceIn,closure->ffIn,closure->rfIn,closure->ftIn,closure->rtIn,
1299                              closure->promoCharIn, closure->promoCharIn >= ' ' ? closure->promoCharIn : '-');
1300     }
1301     GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure);
1302     if (closure->count == 0) {
1303         /* See if it's an illegal move due to check */
1304         illegal = 1;
1305         GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure);
1306         if (closure->count == 0) {
1307             /* No, it's not even that */
1308     if (appData.debugMode) { int i, j;
1309         for(i=BOARD_HEIGHT-1; i>=0; i--) {
1310                 for(j=0; j<BOARD_WIDTH; j++)
1311                         fprintf(debugFP, "%3d", (int) board[i][j]);
1312                 fprintf(debugFP, "\n");
1313         }
1314     }
1315             return;
1316         }
1317     }
1318
1319     if (c == 'x') c = NULLCHAR; // get rid of any 'x' (which should never happen?)
1320     if(gameInfo.variant == VariantSChess && c && c != '=' && closure->piece != WhitePawn && closure->piece != BlackPawn) {
1321         if(closure->piece < BlackPawn) { // white
1322             if(closure->rf != 0) closure->kind = IllegalMove; // must be on back rank
1323             if(board[PieceToNumber(CharToPiece(ToUpper(c)))][BOARD_WIDTH-2] == 0) closure->kind = ImpossibleMove;// must be in stock
1324         } else {
1325             if(closure->rf != BOARD_HEIGHT-1) closure->kind = IllegalMove;
1326             if(board[BOARD_HEIGHT-1-PieceToNumber(CharToPiece(ToLower(c)))][1] == 0) closure->kind = ImpossibleMove;
1327         }
1328     } else
1329     if(gameInfo.variant == VariantShogi) {
1330         /* [HGM] Shogi promotions. On input, '=' means defer, '+' promote. Afterwards, c is set to '+' for promotions, NULL other */
1331         if(closure->rfIn != DROP_RANK && closure->kind == NormalMove) {
1332             ChessSquare piece = closure->piece;
1333             if (c == 'd' && (piece == WhiteRook   || piece == BlackRook)   ||
1334                 c == 'h' && (piece == WhiteBishop || piece == BlackBishop) ||
1335                 c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) )
1336                    c = '+'; // allowed ICS notations
1337             if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal
1338             else if(flags & F_WHITE_ON_MOVE) {
1339                 if( (int) piece < (int) WhiteWazir &&
1340                      (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) {
1341                     if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 ||
1342                          piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */
1343                        closure->kind = c == '=' ? IllegalMove : WhitePromotion;
1344                     else /* promotion optional, default is defer */
1345                        closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; 
1346                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1347             } else {
1348                 if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) {
1349                     if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 ||
1350                          piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */
1351                        closure->kind = c == '=' ? IllegalMove : BlackPromotion;
1352                     else /* promotion optional, default is defer */
1353                        closure->kind = c == '+' ? BlackPromotion : BlackNonPromotion;
1354                 } else closure->kind = c == '+' ? IllegalMove : NormalMove;
1355             }
1356         }
1357         if(closure->kind == WhitePromotion || closure->kind == BlackPromotion) c = '+'; else
1358         if(closure->kind == WhiteNonPromotion || closure->kind == BlackNonPromotion) c = '=';
1359     } else
1360     if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) {
1361         if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant
1362             if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
1363                 c = PieceToChar(BlackFerz);
1364             else if(gameInfo.variant == VariantGreat)
1365                 c = PieceToChar(BlackMan);
1366             else
1367                 c = PieceToChar(BlackQueen);
1368         } else if(c == '=') closure->kind = IllegalMove; // no deferral outside Shogi
1369     } else if (c != NULLCHAR) closure->kind = IllegalMove;
1370
1371     closure->promoChar = ToLower(c); // this can be NULLCHAR! Note we keep original promoChar even if illegal.
1372     if(c != '+' && c != '=' && c != NULLCHAR && CharToPiece(flags & F_WHITE_ON_MOVE ? ToUpper(c) : ToLower(c)) == EmptySquare)
1373         closure->kind = ImpossibleMove; // but we cannot handle non-existing piece types!
1374     if (closure->count > 1) {
1375         closure->kind = AmbiguousMove;
1376     }
1377     if (illegal) {
1378         /* Note: If more than one illegal move matches, but no legal
1379            moves, we return IllegalMove, not AmbiguousMove.  Caller
1380            can look at closure->count to detect this.
1381         */
1382         closure->kind = IllegalMove;
1383     }
1384     if (appData.debugMode) {
1385         fprintf(debugFP, "Disambiguate out: %d(%d,%d)-(%d,%d) = %d (%c)\n",
1386         closure->piece,closure->ff,closure->rf,closure->ft,closure->rt,closure->promoChar,
1387         closure->promoChar >= ' ' ? closure->promoChar:'-');
1388     }
1389 }
1390
1391
1392 typedef struct {
1393     /* Input */
1394     ChessSquare piece;
1395     int rf, ff, rt, ft;
1396     /* Output */
1397     ChessMove kind;
1398     int rank;
1399     int file;
1400     int either;
1401 } CoordsToAlgebraicClosure;
1402
1403 extern void CoordsToAlgebraicCallback P((Board board, int flags,
1404                                          ChessMove kind, int rf, int ff,
1405                                          int rt, int ft, VOIDSTAR closure));
1406
1407 void CoordsToAlgebraicCallback(board, flags, kind, rf, ff, rt, ft, closure)
1408      Board board;
1409      int flags;
1410      ChessMove kind;
1411      int rf, ff, rt, ft;
1412      VOIDSTAR closure;
1413 {
1414     register CoordsToAlgebraicClosure *cl =
1415       (CoordsToAlgebraicClosure *) closure;
1416
1417     if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
1418         (board[rf][ff] == cl->piece
1419          || PieceToChar(board[rf][ff]) == '~' &&
1420             (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
1421                                      ) {
1422         if (rf == cl->rf) {
1423             if (ff == cl->ff) {
1424                 cl->kind = kind; /* this is the move we want */
1425             } else {
1426                 cl->file++; /* need file to rule out this move */
1427             }
1428         } else {
1429             if (ff == cl->ff) {
1430                 cl->rank++; /* need rank to rule out this move */
1431             } else {
1432                 cl->either++; /* rank or file will rule out this move */
1433             }
1434         }
1435     }
1436 }
1437
1438 /* Convert coordinates to normal algebraic notation.
1439    promoChar must be NULLCHAR or 'x' if not a promotion.
1440 */
1441 ChessMove CoordsToAlgebraic(board, flags, rf, ff, rt, ft, promoChar, out)
1442      Board board;
1443      int flags;
1444      int rf, ff, rt, ft;
1445      int promoChar;
1446      char out[MOVE_LEN];
1447 {
1448     ChessSquare piece;
1449     ChessMove kind;
1450     char *outp = out, c;
1451     CoordsToAlgebraicClosure cl;
1452
1453     if (rf == DROP_RANK) {
1454         /* Bughouse piece drop */
1455         *outp++ = ToUpper(PieceToChar((ChessSquare) ff));
1456         *outp++ = '@';
1457         *outp++ = ft + AAA;
1458         if(rt+ONE <= '9')
1459            *outp++ = rt + ONE;
1460         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1461         *outp = NULLCHAR;
1462         return (flags & F_WHITE_ON_MOVE) ? WhiteDrop : BlackDrop;
1463     }
1464
1465     if (promoChar == 'x') promoChar = NULLCHAR;
1466     piece = board[rf][ff];
1467     if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
1468
1469   if (appData.debugMode)
1470           fprintf(debugFP, "CoordsToAlgebraic, piece=%d (%d,%d)-(%d,%d) %c\n", (int)piece,ff,rf,ft,rt,promoChar >= ' ' ? promoChar : '-');
1471     switch (piece) {
1472       case WhitePawn:
1473       case BlackPawn:
1474         kind = LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1475         if (kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1476             /* Keep short notation if move is illegal only because it
1477                leaves the player in check, but still return IllegalMove */
1478             kind = LegalityTest(board, flags|F_IGNORE_CHECK, rf, ff, rt, ft, promoChar);
1479             if (kind == IllegalMove) break;
1480             kind = IllegalMove;
1481         }
1482         /* Pawn move */
1483         *outp++ = ff + AAA;
1484         if (ff == ft && board[rt][ft] == EmptySquare) { /* [HGM] Xiangqi has straight noncapts! */
1485             /* Non-capture; use style "e5" */
1486             if(rt+ONE <= '9')
1487                *outp++ = rt + ONE;
1488             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1489         } else {
1490             /* Capture; use style "exd5" */
1491             if(gameInfo.variant != VariantXiangqi || board[rt][ft] != EmptySquare )
1492             *outp++ = 'x';  /* [HGM] Xiangqi has sideway noncaptures across river! */
1493             *outp++ = ft + AAA;
1494             if(rt+ONE <= '9')
1495                *outp++ = rt + ONE;
1496             else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1497         }
1498         /* Use promotion suffix style "=Q" */
1499         *outp = NULLCHAR;
1500   if (appData.debugMode)
1501           fprintf(debugFP, "movetype=%d, promochar=%d=%c\n", (int)kind, promoChar, promoChar >= ' ' ? promoChar : '-');
1502         if (promoChar != NULLCHAR) {
1503             if(gameInfo.variant == VariantShogi) {
1504                 /* [HGM] ... but not in Shogi! */
1505                 *outp++ = promoChar == '=' ? '=' : '+';
1506             } else {
1507                 *outp++ = '=';
1508                 *outp++ = ToUpper(promoChar);
1509             }
1510             *outp = NULLCHAR;
1511         }
1512         return kind;
1513
1514
1515       case WhiteKing:
1516       case BlackKing:
1517         /* Fabien moved code: FRC castling first (if KxR), wild castling second */
1518         /* Code added by Tord:  FRC castling. */
1519         if((piece == WhiteKing && board[rt][ft] == WhiteRook) ||
1520            (piece == BlackKing && board[rt][ft] == BlackRook)) {
1521           if(ft > ff)
1522             safeStrCpy(out, "O-O", MOVE_LEN);
1523           else
1524             safeStrCpy(out, "O-O-O", MOVE_LEN);
1525           return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1526         }
1527         /* End of code added by Tord */
1528         /* Test for castling or ICS wild castling */
1529         /* Use style "O-O" (oh-oh) for PGN compatibility */
1530         else if (rf == rt &&
1531             rf == ((piece == WhiteKing) ? 0 : BOARD_HEIGHT-1) &&
1532             (ft - ff > 1 || ff - ft > 1) &&  // No castling if legal King move (on narrow boards!)
1533             ((ff == BOARD_WIDTH>>1 && (ft == BOARD_LEFT+2 || ft == BOARD_RGHT-2)) ||
1534              (ff == (BOARD_WIDTH-1)>>1 && (ft == BOARD_LEFT+1 || ft == BOARD_RGHT-3)))) {
1535             if(ft==BOARD_LEFT+1 || ft==BOARD_RGHT-2)
1536               snprintf(out, MOVE_LEN, "O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1537             else
1538               snprintf(out, MOVE_LEN, "O-O-O%c%c", promoChar ? '/' : 0, ToUpper(promoChar));
1539
1540             /* This notation is always unambiguous, unless there are
1541                kings on both the d and e files, with "wild castling"
1542                possible for the king on the d file and normal castling
1543                possible for the other.  ICS rules for wild 9
1544                effectively make castling illegal for either king in
1545                this situation.  So I am not going to worry about it;
1546                I'll just generate an ambiguous O-O in this case.
1547             */
1548             return LegalityTest(board, flags, rf, ff, rt, ft, promoChar);
1549         }
1550
1551         /* else fall through */
1552       default:
1553         /* Piece move */
1554         cl.rf = rf;
1555         cl.ff = ff;
1556         cl.rt = rt;
1557         cl.ft = ft;
1558         cl.piece = piece;
1559         cl.kind = IllegalMove;
1560         cl.rank = cl.file = cl.either = 0;
1561         GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1562
1563         if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
1564             /* Generate pretty moves for moving into check, but
1565                still return IllegalMove.
1566             */
1567             GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl);
1568             if (cl.kind == IllegalMove) break;
1569             cl.kind = IllegalMove;
1570         }
1571
1572         /* Style is "Nf3" or "Nxf7" if this is unambiguous,
1573            else "Ngf3" or "Ngxf7",
1574            else "N1f3" or "N5xf7",
1575            else "Ng1f3" or "Ng5xf7".
1576         */
1577         c = PieceToChar(piece) ;
1578         if( c == '~' || c == '+') {
1579            /* [HGM] print nonexistent piece as its demoted version */
1580            piece = (ChessSquare) (DEMOTED piece);
1581         }
1582         if(c=='+') *outp++ = c;
1583         *outp++ = ToUpper(PieceToChar(piece));
1584
1585         if (cl.file || (cl.either && !cl.rank)) {
1586             *outp++ = ff + AAA;
1587         }
1588         if (cl.rank) {
1589             if(rf+ONE <= '9')
1590                 *outp++ = rf + ONE;
1591             else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1592         }
1593
1594         if(board[rt][ft] != EmptySquare)
1595           *outp++ = 'x';
1596
1597         *outp++ = ft + AAA;
1598         if(rt+ONE <= '9')
1599            *outp++ = rt + ONE;
1600         else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1601         if (gameInfo.variant == VariantShogi) {
1602             /* [HGM] in Shogi non-pawns can promote */
1603             *outp++ = promoChar; // Don't bother to correct move type, return value is never used!
1604         }
1605         else if (gameInfo.variant == VariantSChess && promoChar) { // and in S-Chess we have gating
1606             *outp++ = '/';
1607             *outp++ = ToUpper(promoChar);
1608         }
1609         *outp = NULLCHAR;
1610         return cl.kind;
1611         
1612       case EmptySquare:
1613         /* Moving a nonexistent piece */
1614         break;
1615     }
1616
1617     /* Not a legal move, even ignoring check.
1618        If there was a piece on the from square,
1619        use style "Ng1g3" or "Ng1xe8";
1620        if there was a pawn or nothing (!),
1621        use style "g1g3" or "g1xe8".  Use "x"
1622        if a piece was on the to square, even
1623        a piece of the same color.
1624     */
1625     outp = out;
1626     c = 0;
1627     if (piece != EmptySquare && piece != WhitePawn && piece != BlackPawn) {
1628         int r, f;
1629       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
1630                 c += (board[r][f] == piece); // count on-board pieces of given type
1631         *outp++ = ToUpper(PieceToChar(piece));
1632     }
1633   if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
1634     *outp++ = ff + AAA;
1635     if(rf+ONE <= '9')
1636        *outp++ = rf + ONE;
1637     else { *outp++ = (rf+ONE-'0')/10 + '0';*outp++ = (rf+ONE-'0')%10 + '0'; }
1638   }
1639     if (board[rt][ft] != EmptySquare) *outp++ = 'x';
1640     *outp++ = ft + AAA;
1641     if(rt+ONE <= '9')
1642        *outp++ = rt + ONE;
1643     else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; }
1644     /* Use promotion suffix style "=Q" */
1645     if (promoChar != NULLCHAR && promoChar != 'x') {
1646         *outp++ = '=';
1647         *outp++ = ToUpper(promoChar);
1648     }
1649     *outp = NULLCHAR;
1650
1651     return IllegalMove;
1652 }
1653
1654 // [HGM] XQ: the following code serves to detect perpetual chasing (Asian rules)
1655
1656 typedef struct {
1657     /* Input */
1658     int rf, ff, rt, ft;
1659     /* Output */
1660     int recaptures;
1661 } ChaseClosure;
1662
1663 // I guess the following variables logically belong in the closure too, but I was too lazy and used globals
1664
1665 int preyStackPointer, chaseStackPointer;
1666
1667 struct {
1668 char rf, ff, rt, ft;
1669 } chaseStack[100];
1670
1671 struct {
1672 char rank, file;
1673 } preyStack[100];
1674
1675
1676
1677
1678 // there are three new callbacks for use with GenLegal: for adding captures, deleting them, and finding a recapture
1679
1680 extern void AtacksCallback P((Board board, int flags, ChessMove kind,
1681                                 int rf, int ff, int rt, int ft,
1682                                 VOIDSTAR closure));
1683
1684 void AttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1685      Board board;
1686      int flags;
1687      ChessMove kind;
1688      int rf, ff, rt, ft;
1689      VOIDSTAR closure;
1690 {   // For adding captures that can lead to chase indictment to the chaseStack
1691     if(board[rt][ft] == EmptySquare) return;                               // non-capture
1692     if(board[rt][ft] == WhitePawn && rt <  BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1693     if(board[rt][ft] == BlackPawn && rt >= BOARD_HEIGHT/2) return;         // Pawn before river can be chased
1694     if(board[rf][ff] == WhitePawn  || board[rf][ff] == BlackPawn)  return; // Pawns are allowed to chase
1695     if(board[rf][ff] == WhiteWazir || board[rf][ff] == BlackWazir) return; // King is allowed to chase
1696     // move cannot be excluded from being a chase trivially (based on attacker and victim); save it on chaseStack
1697     chaseStack[chaseStackPointer].rf = rf;
1698     chaseStack[chaseStackPointer].ff = ff;
1699     chaseStack[chaseStackPointer].rt = rt;
1700     chaseStack[chaseStackPointer].ft = ft;
1701     chaseStackPointer++;
1702 }
1703
1704 extern void ExistingAtacksCallback P((Board board, int flags, ChessMove kind,
1705                                 int rf, int ff, int rt, int ft,
1706                                 VOIDSTAR closure));
1707
1708 void ExistingAttacksCallback(board, flags, kind, rf, ff, rt, ft, closure)
1709      Board board;
1710      int flags;
1711      ChessMove kind;
1712      int rf, ff, rt, ft;
1713      VOIDSTAR closure;
1714 {   // for removing pre-exsting captures from the chaseStack, to be left with newly created ones
1715     int i;
1716     register ChaseClosure *cl = (ChaseClosure *) closure; //closure tells us the move played in the repeat loop
1717
1718     if(board[rt][ft] == EmptySquare) return; // no capture
1719     if(rf == cl->rf && ff == cl->ff) { // attacks with same piece from new position are not considered new
1720         rf = cl->rt; ff = cl->ft;      // doctor their fromSquare so they will be recognized in chaseStack
1721     }
1722     // search move in chaseStack, and delete it if it occurred there (as we know now it is not a new capture)
1723     for(i=0; i<chaseStackPointer; i++) {
1724         if(chaseStack[i].rf == rf && chaseStack[i].ff == ff &&
1725            chaseStack[i].rt == rt && chaseStack[i].ft == ft   ) {
1726             // move found on chaseStack, delete it by overwriting with move popped from top of chaseStack
1727             chaseStack[i] = chaseStack[--chaseStackPointer];
1728             break;
1729         }
1730     }
1731 }
1732
1733 extern void ProtectedCallback P((Board board, int flags, ChessMove kind,
1734                                 int rf, int ff, int rt, int ft,
1735                                 VOIDSTAR closure));
1736
1737 void ProtectedCallback(board, flags, kind, rf, ff, rt, ft, closure)
1738      Board board;
1739      int flags;
1740      ChessMove kind;
1741      int rf, ff, rt, ft;
1742      VOIDSTAR closure;
1743 {   // for determining if a piece (given through the closure) is protected
1744     register ChaseClosure *cl = (ChaseClosure *) closure; // closure tells us where to recapture
1745
1746     if(rt == cl->rt && ft == cl->ft) cl->recaptures++;    // count legal recaptures to this square
1747     if(appData.debugMode && board[rt][ft] != EmptySquare)
1748         fprintf(debugFP, "try %c%c%c%c=%d\n", ff+AAA, rf+ONE,ft+AAA, rt+ONE, cl->recaptures);
1749 }
1750
1751 extern char moveList[MAX_MOVES][MOVE_LEN];
1752
1753 int PerpetualChase(int first, int last)
1754 {   // this routine detects if the side to move in the 'first' position is perpetually chasing (when not checking)
1755     int i, j, k, tail;
1756     ChaseClosure cl;
1757     ChessSquare captured;
1758
1759     preyStackPointer = 0;        // clear stack of chased pieces
1760     for(i=first; i<last; i+=2) { // for all positions with same side to move
1761         if(appData.debugMode) fprintf(debugFP, "judge position %i\n", i);
1762         chaseStackPointer = 0;   // clear stack that is going to hold possible chases
1763         // determine all captures possible after the move, and put them on chaseStack
1764         GenLegal(boards[i+1], PosFlags(i), AttacksCallback, &cl);
1765         if(appData.debugMode) { int n;
1766             for(n=0; n<chaseStackPointer; n++)
1767                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1768                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1769             fprintf(debugFP, ": all capts\n");
1770         }
1771         // determine all captures possible before the move, and delete them from chaseStack
1772         cl.rf = moveList[i][1]-ONE; // prepare closure to pass move that led from i to i+1
1773         cl.ff = moveList[i][0]-AAA+BOARD_LEFT;
1774         cl.rt = moveList[i][3]-ONE;
1775         cl.ft = moveList[i][2]-AAA+BOARD_LEFT;
1776         GenLegal(boards[i],   PosFlags(i), ExistingAttacksCallback, &cl);
1777         if(appData.debugMode) { int n;
1778             for(n=0; n<chaseStackPointer; n++)
1779                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1780                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1781             fprintf(debugFP, ": new capts after %c%c%c%c\n", cl.ff+AAA, cl.rf+ONE, cl.ft+AAA, cl.rt+ONE);
1782         }
1783         // chaseSack now contains all captures made possible by the move
1784         for(j=0; j<chaseStackPointer; j++) { // run through chaseStack to identify true chases
1785             int attacker = (int)boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1786             int victim   = (int)boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1787
1788             if(attacker >= (int) BlackPawn) attacker = BLACK_TO_WHITE attacker; // convert to white, as piecee type
1789             if(victim   >= (int) BlackPawn) victim   = BLACK_TO_WHITE victim;
1790
1791             if((attacker == WhiteKnight || attacker == WhiteCannon) && victim == WhiteRook)
1792                 continue; // C or H attack on R is always chase; leave on chaseStack
1793
1794             if(attacker == victim) {
1795                 if(LegalityTest(boards[i+1], PosFlags(i+1), chaseStack[j].rt,
1796                    chaseStack[j].ft, chaseStack[j].rf, chaseStack[j].ff, NULLCHAR) == NormalMove) {
1797                         // we can capture back with equal piece, so this is no chase but a sacrifice
1798                         chaseStack[j] = chaseStack[--chaseStackPointer]; // delete the capture from the chaseStack
1799                         j--; /* ! */ continue;
1800                 }
1801
1802             }
1803
1804             // the attack is on a lower piece, or on a pinned or blocked equal one
1805             // test if the victim is protected by a true protector. First make the capture.
1806             captured = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1807             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = boards[i+1][chaseStack[j].rf][chaseStack[j].ff];
1808             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = EmptySquare;
1809             // Then test if the opponent can recapture
1810             cl.recaptures = 0;         // prepare closure to pass recapture square and count moves to it
1811             cl.rt = chaseStack[j].rt;
1812             cl.ft = chaseStack[j].ft;
1813             if(appData.debugMode) {
1814                 fprintf(debugFP, "test if we can recapture %c%c\n", cl.ft+AAA, cl.rt+ONE);
1815             }
1816             GenLegal(boards[i+1], PosFlags(i+1), ProtectedCallback, &cl); // try all moves
1817             // unmake the capture
1818             boards[i+1][chaseStack[j].rf][chaseStack[j].ff] = boards[i+1][chaseStack[j].rt][chaseStack[j].ft];
1819             boards[i+1][chaseStack[j].rt][chaseStack[j].ft] = captured;
1820             // if a recapture was found, piece is protected, and we are not chasing it.
1821             if(cl.recaptures) { // attacked piece was defended by true protector, no chase
1822                 chaseStack[j] = chaseStack[--chaseStackPointer]; // so delete from chaseStack
1823                 j--; /* ! */
1824             }
1825         }
1826         // chaseStack now contains all moves that chased
1827         if(appData.debugMode) { int n;
1828             for(n=0; n<chaseStackPointer; n++)
1829                 fprintf(debugFP, "%c%c%c%c ", chaseStack[n].ff+AAA, chaseStack[n].rf+ONE,
1830                                               chaseStack[n].ft+AAA, chaseStack[n].rt+ONE);
1831             fprintf(debugFP, ": chases\n");
1832         }
1833         if(i == first) { // copy all people chased by first move of repeat cycle to preyStack
1834             for(j=0; j<chaseStackPointer; j++) {
1835                 preyStack[j].rank = chaseStack[j].rt;
1836                 preyStack[j].file = chaseStack[j].ft;
1837             }
1838             preyStackPointer = chaseStackPointer;
1839         }
1840         tail = 0;
1841         for(j=0; j<chaseStackPointer; j++) {
1842             for(k=0; k<preyStackPointer; k++) {
1843                 // search the victim of each chase move on the preyStack (first occurrence)
1844                 if(chaseStack[j].ft == preyStack[k].file && chaseStack[j].rt == preyStack[k].rank ) {
1845                     if(k < tail) break; // piece was already identified as still being chased
1846                     preyStack[preyStackPointer] = preyStack[tail]; // move chased piece to bottom part of preyStack
1847                     preyStack[tail] = preyStack[k];                // by swapping
1848                     preyStack[k] = preyStack[preyStackPointer];
1849                     tail++;
1850                     break;
1851                 }
1852             }
1853         }
1854         preyStackPointer = tail; // keep bottom part of preyStack, popping pieces unchased on move i.
1855         if(appData.debugMode) { int n;
1856             for(n=0; n<preyStackPointer; n++)
1857                 fprintf(debugFP, "%c%c ", preyStack[n].file+AAA, preyStack[n].rank+ONE);
1858             fprintf(debugFP, "always chased upto ply %d\n", i);
1859         }
1860         // now adjust the location of the chased pieces according to opponent move
1861         for(j=0; j<preyStackPointer; j++) {
1862             if(preyStack[j].rank == moveList[i+1][1]-ONE &&
1863                preyStack[j].file == moveList[i+1][0]-AAA+BOARD_LEFT) {
1864                 preyStack[j].rank = moveList[i+1][3]-ONE;
1865                 preyStack[j].file = moveList[i+1][2]-AAA+BOARD_LEFT;
1866                 break;
1867             }
1868         }
1869     }
1870     return preyStackPointer; // if any piece was left on preyStack, it has been perpetually chased
1871 }