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)
\r
110 ltr = tmp[j] - '0'; // [HGM] allow 0-rank for Xiangqi, correct later
\r
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 */
208 int alg_is_move(const char *mstr)
210 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
212 return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
215 /* add any promotion qualifier from a move string */
216 static void add_promotion(struct game_state_t *gs, const char *mstr, struct move_t * mt)
220 s = strchr(mstr, '=');
225 if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
228 if (mt->toRank != gs->ranks-1 && mt->toRank != 0) {
232 switch (tolower(s[1])) {
240 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
\r
244 if(!gs->capablancaPieces) return;
\r
248 if(!gs->royalKnight) return; // [HGM] only in knightmate
\r
258 if(gs->royalKnight) return; // [HGM] not in knightmate
\r
261 // Superchess promotons: filtered out later by promoType
284 i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
285 if(piece >= WOODY && (gs->promoType != 2 || gs->holding[i][piece-1] == 0)) return;
287 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
290 /* We already know it is algebraic, get the move squares */
291 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
293 int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
294 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
296 if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
297 d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
301 // [HGM] check if move does not stray off board
\r
302 if(gs->ranks < 10) {
\r
303 if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank
\r
304 if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1
\r
306 if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
\r
307 return MOVE_ILLEGAL;
\r
309 // [HGM] resolve ambiguity in piece, type based on variant
312 if(strstr(gs->variant, "super")) piece = EMPRESS; else
313 if(strstr(gs->variant, "great")) piece = MODERNELEPHANT; else
314 if(strstr(gs->variant, "courier")) piece = ALFIL2;
317 if(strstr(gs->variant, "super")) piece = AMAZON; else
318 if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
321 if(strstr(gs->variant, "xiangqi")) piece = CANNON;
324 if(strstr(gs->variant, "super")) piece = PRINCESS;
325 if(strstr(gs->variant, "great")) piece = MAN2;
328 if(strstr(gs->variant, "shatranj")) piece = ALFIL;
331 if(strstr(gs->variant, "shatranj")) piece = FERZ;
334 if(strstr(gs->variant, "super")) piece = WOODY;
337 if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
340 if(strstr(gs->variant, "great")) piece = MINISTER;
343 if(strstr(gs->variant, "great")) piece = PRIESTESS;
344 if(strstr(gs->variant, "shogi")) piece = DRAGONHORSE;
347 if(strstr(gs->variant, "great")) piece = MASTODON;
351 /* Resolve ambiguities in to-ness */
352 if (tf == ALG_UNKNOWN) {
353 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
354 return MOVE_AMBIGUOUS; /* Must always know to file */
356 if (tr == ALG_UNKNOWN) {
357 posr = posr2 = ALG_UNKNOWN;
359 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
360 return MOVE_AMBIGUOUS;
362 if (ff == ALG_UNKNOWN) {
363 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
364 return MOVE_AMBIGUOUS;
366 /* Need to find pawn on ff that can take to tf and fill in ranks */
367 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
368 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
369 if ((ff != ALG_UNKNOWN) && (ff != f))
371 if (piecetype(gs->board[f][r]) != piece)
373 if (gs->onMove == WHITE) {
378 /* if ((gs->board[tf][tmpr] == NOPIECE) ||
379 (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
380 /* patch from Soso, added by Sparky 3/16/95 */
381 if (gs->board[tf][tmpr] == NOPIECE) {
382 if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
385 if (iscolor(gs->board[tf][tmpr], gs->onMove))
389 if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
390 if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
391 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
392 return MOVE_AMBIGUOUS;
400 } else if (bc) { /* Could be bxc4 or Bxc4, tr is known */
403 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
404 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
405 if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
406 // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
410 if (legal_andcheck_move(gs, f, r, tf, tr)) {
411 if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
415 /* if its a lowercase 'b' then prefer the pawn move if there is one */
416 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
417 piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
423 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
424 piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
428 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
429 d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
430 return (MOVE_AMBIGUOUS);
436 } else { /* The from position is unknown */
439 if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
440 /* Need to find a piece that can go to tf, tr */
441 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
442 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
443 if ((ff != ALG_UNKNOWN) && (ff != f))
445 if ((fr != ALG_UNKNOWN) && (fr != r))
447 if (piecetype(gs->board[f][r]) != piece)
449 if (legal_andcheck_move(gs, f, r, tf, tr)) {
450 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
451 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
452 return MOVE_AMBIGUOUS;
458 } else if (ff == ALG_DROP) {
459 if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
467 if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
468 (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
475 add_promotion(gs, mstr, mt);
480 /* A assumes the move has yet to be made on the board */
482 /* Soso: rewrote alg_unparse function.
483 * Algebraic deparser - sets the mStr variable with move description
484 * in short notation. Used in last move report and in 'moves' command.
487 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
489 static char mStr[20];
492 int ambig, r_ambig, f_ambig;
493 struct game_state_t fakeMove;
495 if (mt->fromFile == ALG_DROP) {
496 piece = mt->fromRank;
498 piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
501 if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R
\r
502 strcpy(mStr, "O-O");
\r
505 if ((mt->fromFile == ALG_CASTLE) && (mt->toFile < mt->toRank)) { // [HGM] castle: K ends left of R
\r
506 strcpy(mStr, "O-O-O");
\r
512 if (mt->fromFile == ALG_DROP) {
514 } else if (mt->fromFile != mt->toFile
515 || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
516 sprintf(tmp, "%c", mt->fromFile + 'a');
599 if (mt->fromFile == ALG_DROP) {
602 /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
604 ambig = r_ambig = f_ambig = 0;
605 for (r = 0; r < gs->ranks; r++)
\r
606 for (f = 0; f < gs->files; f++) {
\r
607 if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
608 && (piecetype(gs->board[f][r]) == piece) &&
609 ((f != mt->fromFile) || (r != mt->fromRank))) {
610 if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
612 fakeMove.board[f][r] = NOPIECE;
613 fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
614 fakeMove.onMove = CToggle(fakeMove.onMove);
615 gs->onMove = CToggle(gs->onMove);
618 d_printf("possible move %c%d%c%d against %c%d%c%d\n",
620 'a' + mt->toFile, mt->toRank+1,
621 'a' + mt->fromFile, mt->fromRank+1,
622 'a' + mt->toFile, mt->toRank+1);
625 if (!in_check(&fakeMove)) {
627 if (f == mt->fromFile) {
631 if (r == mt->fromRank) {
636 gs->onMove = CToggle(gs->onMove);
641 /* Ambiguity in short notation, need to add file,rank or _both_ in
\r
643 if (f_ambig == 0) {
\r
644 sprintf(tmp, "%c", mt->fromFile + 'a');
\r
646 } else if (r_ambig == 0) {
\r
647 sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
\r
650 sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
\r
655 if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
\r
656 ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
\r
660 sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
\r
663 if ((piece == PAWN || gs->promoType == 3) && (mt->piecePromotionTo != NOPIECE)) {
\r
664 strcat(mStr, "="); /* = before promoting piece */
\r
665 switch (piecetype(mt->piecePromotionTo)) {
\r
708 case GOLD: // [HGM] Shogi promotions: avoid use of '+'
\r
723 execute_move(&fakeMove, mt, 0);
724 fakeMove.onMove = CToggle(fakeMove.onMove);
725 if (in_check(&fakeMove)) {