2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
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.
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.
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.
24 /* Well, lets see if I can list the possibilities
38 * Drop moves (bughouse, board edit)
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
50 * @ - drop character (bughouse)
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 */
68 #define ALG_UNKNOWN -1
70 static int get_move_info(const char *str, int *piece, int *ff, int *fr, int *tf, int *tr, int *bishconfusion)
77 int lpiece, lff, lfr, ltf, ltr;
80 strlcpy(tmp, str, sizeof(tmp));
81 if ((s = strchr(tmp, '+'))) { /* Cut off any check marks */
84 if ((s = strchr(tmp, '='))) { /* Cut off any promotion marks */
87 if ((s = strchr(tmp, '#'))) { /* Cut off any 'mates' marks */
90 *piece = *ff = *fr = *tf = *tr = ALG_UNKNOWN;
92 for (i = 0; alg_list[i]; i++) {
93 lpiece = lff = lfr = ltf = ltr = ALG_UNKNOWN;
94 if (strlen(alg_list[i]) != len)
96 for (j = len - 1; j >= 0; j--) {
97 switch (alg_list[i][j]) {
99 if ((tmp[j] < 'a') || (tmp[j] > 'l')) // [HGM] upto l-file
101 if (ltf == ALG_UNKNOWN)
107 if ((tmp[j] < '0') || (tmp[j] > '9')) // [HGM] also match 0- and 9-rank
109 if (ltr == ALG_UNKNOWN)
110 ltr = tmp[j] - '0'; // [HGM] allow 0-rank for Xiangqi, correct later
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.
161 if ((tmp[j] != 'x') && (tmp[j] != 'X'))
165 if (tmp[j] != '@' && tmp[j] != '*')
167 lff = lfr = ALG_DROP;
172 lff = lfr = ALG_DROP;
175 d_printf( "Unknown character in algebraic parsing\n");
179 if (lpiece == ALG_UNKNOWN)
181 if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) { /* ffr or ff */
182 if (lff != ALG_UNKNOWN) {
185 if ((lff - ltf != 1) && (ltf - lff != 1))
189 *piece = lpiece; /* We have a match */
194 if (matchVal != -1) {
195 /* We have two matches, it must be that Bxc4 vs. bxc4 problem */
196 /* Or it could be the Bc4 vs bc4 problem */
209 int alg_is_move(const char *mstr)
211 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
213 return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
216 /* add any promotion qualifier from a move string */
217 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
221 s = strchr(mstr, '=');
225 if(gs->promoType == 3) { // handle Shogi promotions
226 piece = gs->board[mt->fromFile][mt->fromRank];
227 if(colorval(piece) == WHITE && mt->fromRank < gs->ranks - gs->ranks/3
228 && mt->toRank < gs->ranks - gs->ranks/3 ) return;
229 if(colorval(piece) == BLACK && mt->fromRank >= gs->ranks/3
230 && mt->toRank >= gs->ranks/3 ) return;
231 switch(piecetype(piece)) {
236 if(s[1] != '+' && s[1] != '^' && s[1] != 'G' && s[1] != 'g') return;
239 if(s[1] != '+' && s[1] != '^' && s[1] != 'H' && s[1] != 'h') return;
240 piece = DRAGONHORSE; break;
242 if(s[1] != '+' && s[1] != '^' && s[1] != 'D' && s[1] != 'd') return;
243 piece = DRAGONKING; break;
244 default: return; // others do not promote, so ignore
246 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
250 if(gs->drops != 2 || (gs->onMove == WHITE ? 0 : gs->ranks-1) != mt->fromRank) { // [HGM] always accept if backrank mover in Seirawan
251 if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN &&
252 piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) {
255 if (mt->toRank < gs->ranks - gs->promoZone && mt->toRank >= gs->promoZone) {
260 switch (tolower(s[1])) {
268 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = CAPTAIN; else
269 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
273 if(!gs->capablancaPieces) return;
277 if(!gs->royalKnight) return; // [HGM] only in knightmate
287 if(gs->royalKnight) return; // [HGM] not in knightmate
290 // Superchess promotons: filtered out later by promoType
292 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = GENERAL; else
299 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) piece = WARLORD; else
303 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) return;
307 if(piecetype(gs->board[mt->fromFile][mt->fromRank]) != HOPLITE) return;
314 piece = gs->drops == 2 ? SELEPHANT : EMPRESS; // for Seirawan
320 if(gs->drops != 2) return;
326 i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
327 if(gs->promoType == 2 && gs->holding[i][piece-1] == 0) return; // only if piece was captured
328 if(piece >= WOODY && piece < KING && (gs->promoType != 2 || gs->promoZone == 3)) return; // reserved for Superchess
330 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
333 /* We already know it is algebraic, get the move squares */
334 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
336 int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
337 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
339 if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
340 d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
343 // [HGM] check if move does not stray off board
345 if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank
346 if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1
348 if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
351 // [HGM] resolve ambiguity in piece, type based on variant
354 if(strstr(gs->variant, "super")) piece = EMPRESS; else
355 if(strstr(gs->variant, "seirawan"))piece = SELEPHANT; else
356 if(strstr(gs->variant, "great")) piece = MODERNELEPHANT; else
357 if(strstr(gs->variant, "courier")) piece = ALFIL2;
360 if(strstr(gs->variant, "super")) piece = AMAZON; else
361 if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
364 if(strstr(gs->variant, "xiangqi")) piece = CANNON;
367 if(strstr(gs->variant, "super")) piece = PRINCESS;
368 if(strstr(gs->variant, "great")) piece = MAN2;
371 if(strstr(gs->variant, "shatranj")) piece = ALFIL;
374 if(strstr(gs->variant, "shatranj")) piece = FERZ;
377 if(strstr(gs->variant, "super")) piece = WOODY;
380 if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
383 if(strstr(gs->variant, "great")) piece = MINISTER;
386 if(strstr(gs->variant, "shogi")) piece = DRAGONHORSE; else
387 if(strstr(gs->variant, "seirawan")) piece = HAWK; else
388 if(strstr(gs->variant, "great")) piece = PRIESTESS;
391 if(strstr(gs->variant, "great")) piece = MASTODON;
395 /* Resolve ambiguities in to-ness */
396 if (tf == ALG_UNKNOWN) {
397 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
398 return MOVE_AMBIGUOUS; /* Must always know to file */
400 if (tr == ALG_UNKNOWN) {
401 posr = posr2 = ALG_UNKNOWN;
403 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
404 return MOVE_AMBIGUOUS;
406 if (ff == ALG_UNKNOWN) {
407 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
408 return MOVE_AMBIGUOUS;
410 /* Need to find pawn on ff that can take to tf and fill in ranks */
411 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
412 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
413 if ((ff != ALG_UNKNOWN) && (ff != f))
415 if (piecetype(gs->board[f][r]) != piece)
417 if (gs->onMove == WHITE) {
422 /* if ((gs->board[tf][tmpr] == NOPIECE) ||
423 (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
424 /* patch from Soso, added by Sparky 3/16/95 */
425 if (gs->board[tf][tmpr] == NOPIECE) {
426 if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
429 if (iscolor(gs->board[tf][tmpr], gs->onMove))
433 if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
434 if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
435 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
436 return MOVE_AMBIGUOUS;
444 } else if (bc) { /* Could be bxc4 or Bxc4, tr is known */
447 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
448 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
449 if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
450 // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
453 if (legal_andcheck_move(gs, f, r, tf, tr)) {
454 if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
458 /* if its a lowercase 'b' then prefer the pawn move if there is one */
459 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
460 piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
466 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
467 piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
471 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
472 d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
473 return (MOVE_AMBIGUOUS);
479 } else { /* The from position is unknown */
482 if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
483 /* Need to find a piece that can go to tf, tr */
484 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
485 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
486 if ((ff != ALG_UNKNOWN) && (ff != f))
488 if ((fr != ALG_UNKNOWN) && (fr != r))
490 if (piecetype(gs->board[f][r]) != piece)
492 if (legal_andcheck_move(gs, f, r, tf, tr)) {
493 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
494 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
495 return MOVE_AMBIGUOUS;
501 } else if (ff == ALG_DROP) {
502 if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
510 if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
511 (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
518 add_promotion(gs, mstr, mt);
523 /* A assumes the move has yet to be made on the board */
525 /* Soso: rewrote alg_unparse function.
526 * Algebraic deparser - sets the mStr variable with move description
527 * in short notation. Used in last move report and in 'moves' command.
530 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
532 static char mStr[20];
535 int ambig, r_ambig, f_ambig;
536 struct game_state_t fakeMove;
538 if (mt->fromFile == ALG_DROP) {
539 piece = mt->fromRank;
541 piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
544 if (mt->fromFile == ALG_CASTLE) {
545 int r = gs->onMove == WHITE ? 1 : gs->ranks;
546 if(mt->toFile > mt->toRank) { // [HGM] castle: K ends right of R
549 if (mt->toFile < mt->toRank) { // [HGM] castle: K ends left of R
550 strcpy(mStr, "O-O-O");
553 if(mt->piecePromotionTo < 0) snprintf(mStr, 20, "%c%de%d", mt->fromRank + 'a', r, r);
554 goto suffix; // [HGM] in Seirawan castling can have gating suffix
561 if (mt->fromFile == ALG_DROP) {
563 } else if (mt->fromFile != mt->toFile
564 || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
565 sprintf(tmp, "%c", mt->fromFile + 'a');
653 if (mt->fromFile == ALG_DROP) {
656 /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
658 ambig = r_ambig = f_ambig = 0;
659 for (r = 0; r < gs->ranks; r++)
660 for (f = 0; f < gs->files; f++) {
661 if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
662 && (piecetype(gs->board[f][r]) == piece) &&
663 ((f != mt->fromFile) || (r != mt->fromRank))) {
664 if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
666 fakeMove.board[f][r] = NOPIECE;
667 fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
668 fakeMove.onMove = CToggle(fakeMove.onMove);
669 gs->onMove = CToggle(gs->onMove);
672 d_printf("possible move %c%d%c%d against %c%d%c%d\n",
674 'a' + mt->toFile, mt->toRank+1,
675 'a' + mt->fromFile, mt->fromRank+1,
676 'a' + mt->toFile, mt->toRank+1);
679 if (!in_check(&fakeMove)) {
681 if (f == mt->fromFile) {
685 if (r == mt->fromRank) {
690 gs->onMove = CToggle(gs->onMove);
695 /* Ambiguity in short notation, need to add file,rank or _both_ in
698 sprintf(tmp, "%c", mt->fromFile + 'a');
700 } else if (r_ambig == 0) {
701 sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
704 sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
709 if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
710 ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
714 sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
717 if ((piece == PAWN || piece == HOPLITE || gs->promoType == 3 || gs->drops == 2) && (mt->piecePromotionTo != NOPIECE)) {
718 strcat(mStr, "="); /* = before promoting piece */
719 switch (piecetype(abs(mt->piecePromotionTo))) {
767 case GOLD: // [HGM] Shogi promotions: avoid use of '+'
786 execute_move(&fakeMove, mt, 0);
787 fakeMove.onMove = CToggle(fakeMove.onMove);
788 if (in_check(&fakeMove)) {