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