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