Finish implementation of Shogi
[capablanca.git] / lasker-2.2.3 / src / algcheck.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 #include "includes.h"
23
24 /* Well, lets see if I can list the possibilities
25  * Piece moves
26  * Ne4
27  * Nxe4
28  * Nce4
29  * Ncxe4
30  * R2f3
31  * R2xf3
32  * Special pawn moves
33  * e4
34  * ed
35  * exd
36  * exd5
37  * ed5
38  * Drop moves (bughouse, board edit)
39  * P@f7 P*f7
40  * #f7 #Nf7
41  * (o-o, o-o-o) Castling is handled earlier, so don't worry about that
42  * Of course any of these can have a + or ++ or = string on the end, just
43  * cut that off.
44  */
45
46 /* f - file
47  * r - rank
48  * p - piece
49  * x - x
50  * @ - drop character (bughouse)
51  */
52 static char *alg_list[] = {
53   "fxfr", "pxfr",               /* These two get confused in case of bishop */
54   "ffr", "pfr",                 /* These two get confused in case of bishop */
55   "pffr",
56   "pfxfr",
57   "prfr",
58   "prxfr",
59   "fr",
60   "ff",
61   "fxf",
62   "p@fr",
63   "#fr",
64   "#pfr",
65   NULL
66 };
67
68 #define ALG_UNKNOWN -1
69
70 static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf, int *tr, int *bishconfusion)
71 {
72   char tmp[1024];
73   char *s;
74   int i, j, len;
75   char c;
76   int matchVal = -1;
77   int lpiece, lff, lfr, ltf, ltr;
78
79   *bishconfusion = 0;
80   strlcpy(tmp, str, sizeof(tmp));
81   if ((s = strchr(tmp, '+'))) { /* Cut off any check marks */
82     *s = '\0';
83   }
84   if ((s = strchr(tmp, '='))) { /* Cut off any promotion marks */
85     *s = '\0';
86   }
87   if ((s = strchr(tmp, '#'))) { /* Cut off any 'mates' marks */
88     *s = '\0';
89   }
90   *piece = *ff = *fr = *tf = *tr = ALG_UNKNOWN;
91   len = strlen(tmp);
92   for (i = 0; alg_list[i]; i++) {
93     lpiece = lff = lfr = ltf = ltr = ALG_UNKNOWN;
94     if (strlen(alg_list[i]) != len)
95       continue;
96     for (j = len - 1; j >= 0; j--) {
97       switch (alg_list[i][j]) {
98       case 'f':
99         if ((tmp[j] < 'a') || (tmp[j] > 'l')) // [HGM] upto l-file
100           goto nomatch;
101         if (ltf == ALG_UNKNOWN)
102           ltf = tmp[j] - 'a';
103         else
104           lff = tmp[j] - 'a';
105         break;
106       case 'r':
107         if ((tmp[j] < '0') || (tmp[j] > '9')) // [HGM] also match 0- and 9-rank
108           goto nomatch;
109         if (ltr == ALG_UNKNOWN)\r
110           ltr = tmp[j] - '0'; // [HGM] allow 0-rank for Xiangqi, correct later\r
111         else\r
112           lfr = tmp[j] - '0';
113         break;
114       case 'p':
115         if (isupper(tmp[j]))
116           c = tolower(tmp[j]);
117         else
118           c = tmp[j];
119         if (c == 'k')
120           lpiece = KING;
121         else if (c == 'e')   // [HGM] note that som piece indicators are ambiguous,
122           lpiece = ELEPHANT; //       and their true meaning depends on the variant,
123         else if (c == 'v')   //       which we do not know at this point.
124           lpiece = CENTAUR;
125         else if (c == 's')
126           lpiece = SILVER;
127         else if (c == 'g')
128           lpiece = GOLD;
129         else if (c == 'l')
130           lpiece = LANCE;
131         else if (c == 'f')
132           lpiece = FERZ;
133         else if (c == 'h')
134           lpiece = HORSE;
135         else if (c == 'w')
136           lpiece = WAZIR;
137         else if (c == 'o')
138           lpiece = SQUIRREL;
139         else if (c == 'q')
140           lpiece = QUEEN;
141         else if (c == 'c')
142           lpiece = MARSHALL;
143         else if (c == 'a')
144           lpiece = CARDINAL;
145         else if (c == 'm')
146           lpiece = MAN;
147         else if (c == 'r')
148           lpiece = ROOK;
149         else if (c == 'b')
150           lpiece = BISHOP;
151         else if (c == 'n')
152           lpiece = KNIGHT;
153         else if (c == 'p')
154           lpiece = PAWN;
155         else
156           goto nomatch;
157         break;
158       case 'x':
159         if ((tmp[j] != 'x') && (tmp[j] != 'X'))
160           goto nomatch;
161         break;
162       case '@':
163         if (tmp[j] != '@' && tmp[j] != '*')
164           goto nomatch;
165         lff = lfr = ALG_DROP;
166         break;
167       case '#':
168         if (tmp[j] != '#')
169           goto nomatch;
170         lff = lfr = ALG_DROP;
171         break;
172       default:
173         d_printf( "Unknown character in algebraic parsing\n");
174         break;
175       }
176     }
177     if (lpiece == ALG_UNKNOWN)
178       lpiece = PAWN;
179     if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) {       /* ffr or ff */
180       if (lff != ALG_UNKNOWN) {
181         if (lff == ltf)
182           goto nomatch;
183         if ((lff - ltf != 1) && (ltf - lff != 1))
184           goto nomatch;
185       }
186     }
187     *piece = lpiece;            /* We have a match */
188     *tf = ltf;
189     *tr = ltr;
190     *ff = lff;
191     *fr = lfr;
192     if (matchVal != -1) {
193       /* We have two matches, it must be that Bxc4 vs. bxc4 problem */
194       /* Or it could be the Bc4 vs bc4 problem */
195       *bishconfusion = 1;
196     }
197     matchVal = i;
198 nomatch:;
199   }
200   if (matchVal != -1)
201     return MS_ALG;
202   else
203     return MS_NOTMOVE;
204 }
205
206 int alg_is_move(const char *mstr)
207 {
208         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
209
210         return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
211 }
212
213 /* add any promotion qualifier from a move string */
214 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
215 {
216         char *s;
217         int piece, i;
218         s = strchr(mstr, '=');
219         if (s == NULL) {
220                 return;
221         }
222         
223         if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
224                 return;
225         }
226         if (mt->toRank != gs->ranks-1 && mt->toRank != 0) {
227                 return;
228         }
229
230         switch (tolower(s[1])) {
231         case 'f':
232                 piece = FERZ2;
233                 break;
234         case 'q':
235                 piece = QUEEN;
236                 break;
237         case 'c':\r
238                 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask\r
239                 piece = MARSHALL;\r
240                 break;\r
241         case 'a':\r
242                 if(!gs->capablancaPieces) return;\r
243                 piece = CARDINAL;\r
244                 break;\r
245         case 'm':\r
246                 if(!gs->royalKnight) return; // [HGM] only in knightmate\r
247                 piece = MAN;\r
248                 break;\r
249         case 'r':
250                 piece = ROOK;
251                 break;
252         case 'b':
253                 piece = BISHOP;
254                 break;
255         case 'n':
256                 if(gs->royalKnight) return; // [HGM] not in knightmate\r
257                 piece = KNIGHT;
258                 break;
259         // Superchess promotons: filtered out later by promoType
260         case 'g':
261                 piece = MASTODON;
262                 break;
263         case 'o':
264                 piece = SQUIRREL;
265                 break;
266         case 'w':
267                 piece = WOODY;
268                 break;
269         case 'v':
270                 piece = CENTAUR;
271                 break;
272         case 'e':\r
273                 piece = EMPRESS;
274                 break;
275         case 's':
276                 piece = PRINCESS;
277                 break;
278         default:
279                 return;
280         }
281
282         i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
283         if(piece >= WOODY && (gs->promoType != 2 || gs->holding[i][piece-1] == 0)) return;
284
285         mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
286 }
287
288 /* We already know it is algebraic, get the move squares */
289 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
290 {
291         int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
292         int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
293
294   if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
295     d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
296     return MOVE_ILLEGAL;
297   }
298   \r
299   // [HGM] check if move does not stray off board\r
300   if(gs->ranks < 10) { \r
301     if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank\r
302     if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1\r
303   }\r
304   if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)\r
305     return MOVE_ILLEGAL;\r
306
307   // [HGM] resolve ambiguity in piece, type based on variant
308   switch(piece) {
309     case ELEPHANT:
310       if(strstr(gs->variant, "super"))   piece = EMPRESS; else
311       if(strstr(gs->variant, "great"))   piece = MODERNELEPHANT; else
312       if(strstr(gs->variant, "courier")) piece = ALFIL2;
313       break;
314     case CARDINAL:
315       if(strstr(gs->variant, "super")) piece = AMAZON; else
316       if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
317       break;
318     case MARSHALL:
319       if(strstr(gs->variant, "xiangqi")) piece = CANNON;
320       break;
321     case SILVER:
322       if(strstr(gs->variant, "super")) piece = PRINCESS;
323       if(strstr(gs->variant, "great")) piece = MAN2;
324       break;
325     case BISHOP:
326       if(strstr(gs->variant, "shatranj")) piece = ALFIL;
327       break;
328     case QUEEN:
329       if(strstr(gs->variant, "shatranj")) piece = FERZ;
330       break;
331     case WAZIR:
332       if(strstr(gs->variant, "super")) piece = WOODY;
333       break;
334     case KNIGHT:
335       if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
336       break;
337     case MAN:
338       if(strstr(gs->variant, "great")) piece = MINISTER;
339       break;
340     case HORSE:
341       if(strstr(gs->variant, "great")) piece = PRIESTESS;
342       break;
343     case GOLD:
344       if(strstr(gs->variant, "great")) piece = MASTODON;
345       break;
346   }
347 \r
348   /* Resolve ambiguities in to-ness */
349   if (tf == ALG_UNKNOWN) {
350           d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
351           return MOVE_AMBIGUOUS;        /* Must always know to file */
352   }
353   if (tr == ALG_UNKNOWN) {
354     posr = posr2 = ALG_UNKNOWN;
355     if (piece != PAWN) {
356             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
357             return MOVE_AMBIGUOUS;
358     }
359     if (ff == ALG_UNKNOWN) {
360             d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
361             return MOVE_AMBIGUOUS;
362     }
363     /* Need to find pawn on ff that can take to tf and fill in ranks */
364     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
365          NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
366       if ((ff != ALG_UNKNOWN) && (ff != f))
367         continue;
368       if (piecetype(gs->board[f][r]) != piece)
369         continue;
370       if (gs->onMove == WHITE) {
371         tmpr = r + 1;
372       } else {
373         tmpr = r - 1;
374       }
375 /*      if ((gs->board[tf][tmpr] == NOPIECE) ||
376           (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
377 /* patch from Soso, added by Sparky 3/16/95                    */
378       if (gs->board[tf][tmpr] == NOPIECE) {
379         if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
380           continue;
381       } else {
382         if (iscolor(gs->board[tf][tmpr], gs->onMove))
383           continue;
384       }
385
386       if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
387               if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
388                       d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
389                       return MOVE_AMBIGUOUS;
390               }
391         posr = tmpr;
392         posr2 = r;
393       }
394     }
395     tr = posr;
396     fr = posr2;
397   } else if (bc) {              /* Could be bxc4 or Bxc4, tr is known */
398     ff = ALG_UNKNOWN;
399     fr = ALG_UNKNOWN;
400     for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
401          NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
402             if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
403                     // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
404                     continue;
405             }
406
407             if (legal_andcheck_move(gs, f, r, tf, tr)) {
408                     if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
409                             continue;
410                     }
411
412                     /* if its a lowercase 'b' then prefer the pawn move if there is one */
413                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
414                         piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
415                             ff = f;
416                             fr = r;
417                             continue;
418                     }
419
420                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
421                         piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
422                             continue;
423                     }
424
425                     if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
426                             d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
427                             return (MOVE_AMBIGUOUS);
428                     }
429                     ff = f;
430                     fr = r;
431             }
432     }
433   } else {                      /* The from position is unknown */
434     posf = ALG_UNKNOWN;
435     posr = ALG_UNKNOWN;
436     if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
437       /* Need to find a piece that can go to tf, tr */
438       for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
439            NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
440         if ((ff != ALG_UNKNOWN) && (ff != f))
441           continue;
442         if ((fr != ALG_UNKNOWN) && (fr != r))
443           continue;
444         if (piecetype(gs->board[f][r]) != piece)
445           continue;
446         if (legal_andcheck_move(gs, f, r, tf, tr)) {
447                 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
448                         d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
449                         return MOVE_AMBIGUOUS;
450                 }
451           posf = f;
452           posr = r;
453         }
454       }
455     } else if (ff == ALG_DROP) {
456       if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
457         posf = ALG_DROP;
458         posr = piece;
459       }
460     }
461     ff = posf;
462     fr = posr;
463   }
464   if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
465       (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
466     return MOVE_ILLEGAL;
467   mt->fromFile = ff;
468   mt->fromRank = fr;
469   mt->toFile = tf;
470   mt->toRank = tr;
471
472   add_promotion(gs, mstr, mt);
473
474   return MOVE_OK;
475 }
476
477 /* A assumes the move has yet to be made on the board */
478
479 /* Soso: rewrote alg_unparse function.
480  * Algebraic deparser - sets the mStr variable with move description
481  * in short notation. Used in last move report and in 'moves' command.
482  */
483
484 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
485 {
486   static char mStr[20];
487   char tmp[20];
488   int piece, f, r;
489   int ambig, r_ambig, f_ambig;
490   struct game_state_t fakeMove;
491
492   if (mt->fromFile == ALG_DROP) {
493     piece = mt->fromRank;
494   } else {
495     piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
496   }
497
498   if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R\r
499     strcpy(mStr, "O-O");\r
500     goto check;\r
501   }\r
502   if ((mt->fromFile == ALG_CASTLE) && (mt->toFile < mt->toRank)) { // [HGM] castle: K ends left of R\r
503     strcpy(mStr, "O-O-O");\r
504     goto check;\r
505   }\r
506   strcpy(mStr, "");
507   switch (piece) {
508   case PAWN:
509     if (mt->fromFile == ALG_DROP) {
510       strcpy(mStr,"P");
511     } else if (mt->fromFile != mt->toFile 
512             || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
513       sprintf(tmp, "%c", mt->fromFile + 'a');
514       strcpy(mStr, tmp);
515     }
516     break;
517   case KNIGHT:
518     strcpy(mStr, "N");
519     break;
520   case BISHOP:
521     strcpy(mStr, "B");
522     break;
523   case ROOK:
524     strcpy(mStr, "R");
525     break;
526   case ALFIL2:
527   case AMAZON:
528   case CARDINAL:\r
529     strcpy(mStr, "A");\r
530     break;\r
531   case CANNON:
532   case MARSHALL:\r
533     strcpy(mStr, "C");\r
534     break;\r
535   case MAN:\r
536     strcpy(mStr, "M");\r
537     break;\r
538   case FERZ:
539   case QUEEN:
540     strcpy(mStr, "Q");
541     break;
542   case EMPRESS:
543   case ELEPHANT:
544     strcpy(mStr, "E");
545     break;
546   case ALFIL:
547     strcpy(mStr, "B");
548     break;
549   case FERZ2:
550     strcpy(mStr, "F");
551     break;
552   case WOODY:
553   case WAZIR:
554     strcpy(mStr, "W");
555     break;
556   case SQUIRREL:
557     strcpy(mStr, "O");
558     break;
559   case CENTAUR:
560     strcpy(mStr, "V");
561     break;
562   case HORSE:
563     strcpy(mStr, "H");
564     break;
565   case HONORABLEHORSE:
566     strcpy(mStr, "N");
567     break;
568   case DRAGONKING:
569     strcpy(mStr, "J");
570     break;
571   case DRAGONHORSE:
572     strcpy(mStr, "I");
573     break;
574   case LANCE:
575     strcpy(mStr, "L");
576     break;
577   case PRINCESS:
578   case SILVER:
579     strcpy(mStr, "S");
580     break;
581   case MASTODON:
582   case GOLD:
583     strcpy(mStr, "G");
584     break;
585   case MANDARIN:
586     strcpy(mStr, "A");
587     break;
588   case KING:
589     strcpy(mStr, "K");
590     break;
591   default:
592     strcpy(mStr, "");
593     break;
594   }
595
596   if (mt->fromFile == ALG_DROP) {
597     strcat(mStr, "@");
598   } else {
599   /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
600   if (piece != PAWN) {
601     ambig = r_ambig = f_ambig = 0;
602     for (r = 0; r < gs->ranks; r++)\r
603       for (f = 0; f < gs->files; f++) {\r
604         if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
605             && (piecetype(gs->board[f][r]) == piece) &&
606             ((f != mt->fromFile) || (r != mt->fromRank))) {
607           if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
608             fakeMove = *gs;
609             fakeMove.board[f][r] = NOPIECE;
610             fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
611             fakeMove.onMove = CToggle(fakeMove.onMove);
612             gs->onMove = CToggle(gs->onMove);
613
614 #if 0
615             d_printf("possible move %c%d%c%d against %c%d%c%d\n",
616                      'a' + f, r+1,
617                      'a' + mt->toFile, mt->toRank+1,
618                      'a' + mt->fromFile, mt->fromRank+1,
619                      'a' + mt->toFile, mt->toRank+1);
620 #endif
621
622             if (!in_check(&fakeMove)) {
623                     ambig++;
624                     if (f == mt->fromFile) {
625                             ambig++;
626                             f_ambig++;
627                     }
628                     if (r == mt->fromRank) {
629                             ambig++;
630                             r_ambig++;
631                     }
632             }
633             gs->onMove = CToggle(gs->onMove);
634           }
635         }
636       }
637     if (ambig > 0) {\r
638       /* Ambiguity in short notation, need to add file,rank or _both_ in\r
639          notation */\r
640       if (f_ambig == 0) {\r
641         sprintf(tmp, "%c", mt->fromFile + 'a');\r
642         strcat(mStr, tmp);\r
643       } else if (r_ambig == 0) {\r
644         sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));\r
645         strcat(mStr, tmp);\r
646       } else {\r
647         sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));\r
648         strcat(mStr, tmp);\r
649       }\r
650     }\r
651   }\r
652   if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||\r
653       ((piece == PAWN) && (mt->fromFile != mt->toFile))) {\r
654     strcat(mStr, "x");\r
655   }\r
656   }\r
657   sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));\r
658   strcat(mStr, tmp);\r
659
660   if ((piece == PAWN || gs->promoType == 3) && (mt->piecePromotionTo != NOPIECE)) {\r
661     strcat(mStr, "=");          /* = before promoting piece */\r
662     switch (piecetype(mt->piecePromotionTo)) {\r
663     case KNIGHT:\r
664       strcat(mStr, "N");\r
665       break;\r
666     case BISHOP:\r
667       strcat(mStr, "B");\r
668       break;\r
669     case ROOK:\r
670       strcat(mStr, "R");\r
671       break;\r
672     case CARDINAL:\r
673       strcat(mStr, "A");\r
674       break;\r
675     case MARSHALL:\r
676       strcat(mStr, "C");\r
677       break;\r
678     case MAN:\r
679       strcat(mStr, "M");\r
680       break;\r
681     case QUEEN:\r
682       strcat(mStr, "Q");\r
683       break;\r
684     case FERZ2:\r
685       strcat(mStr, "F");\r
686       break;\r
687     case WOODY:\r
688       strcat(mStr, "W");\r
689       break;\r
690     case EMPRESS:\r
691       strcat(mStr, "E");\r
692       break;\r
693     case CENTAUR:\r
694       strcat(mStr, "V");\r
695       break;\r
696     case PRINCESS:\r
697       strcat(mStr, "S");\r
698       break;\r
699     case SQUIRREL:\r
700       strcat(mStr, "O");\r
701       break;\r
702     case MASTODON:\r
703       strcat(mStr, "G");\r
704       break;\r
705     case GOLD: // [HGM] Shogi promotions: avoid use of '+'\r
706       strcat(mStr, "G");\r
707       break;\r
708     case DRAGONHORSE:\r
709       strcat(mStr, "I");\r
710       break;\r
711     case DRAGONKING:\r
712       strcat(mStr, "J");\r
713       break;\r
714     default:\r
715       break;\r
716     }\r
717   }\r
718 check:;
719   fakeMove = *gs;
720   execute_move(&fakeMove, mt, 0);
721   fakeMove.onMove = CToggle(fakeMove.onMove);
722   if (in_check(&fakeMove)) {
723     strcat(mStr, "+");
724   }
725   return mStr;
726 }