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(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 (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
253 if (mt->toRank != gs->ranks-1 && mt->toRank != 0) {
257 switch (tolower(s[1])) {
265 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
\r
269 if(!gs->capablancaPieces) return;
\r
273 if(!gs->royalKnight) return; // [HGM] only in knightmate
\r
283 if(gs->royalKnight) return; // [HGM] not in knightmate
\r
286 // Superchess promotons: filtered out later by promoType
309 i = colorval(gs->board[mt->fromFile][mt->fromRank]) == WHITE ? 0 : 1;
310 if(piece >= WOODY && (gs->promoType != 2 || gs->holding[i][piece-1] == 0)) return;
312 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
315 /* We already know it is algebraic, get the move squares */
316 int alg_parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt)
318 int f=0, r=0, tmpr=0, posf=0, posr=0, posr2=0;
319 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
321 if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) {
322 d_printf( "CHESSD: Shouldn't try to algebraicly parse non-algabraic move string.\n");
326 // [HGM] check if move does not stray off board
\r
327 if(gs->ranks < 10) {
\r
328 if(tr == 0 || fr == 0) return MOVE_ILLEGAL; // used nonexistent 0-rank
\r
329 if(tr != ALG_UNKNOWN) tr--; if(fr != ALG_UNKNOWN) fr--; // shift to lowest rank = 1
\r
331 if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
\r
332 return MOVE_ILLEGAL;
\r
334 // [HGM] resolve ambiguity in piece, type based on variant
337 if(strstr(gs->variant, "super")) piece = EMPRESS; else
338 if(strstr(gs->variant, "great")) piece = MODERNELEPHANT; else
339 if(strstr(gs->variant, "courier")) piece = ALFIL2;
342 if(strstr(gs->variant, "super")) piece = AMAZON; else
343 if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
346 if(strstr(gs->variant, "xiangqi")) piece = CANNON;
349 if(strstr(gs->variant, "super")) piece = PRINCESS;
350 if(strstr(gs->variant, "great")) piece = MAN2;
353 if(strstr(gs->variant, "shatranj")) piece = ALFIL;
356 if(strstr(gs->variant, "shatranj")) piece = FERZ;
359 if(strstr(gs->variant, "super")) piece = WOODY;
362 if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
365 if(strstr(gs->variant, "great")) piece = MINISTER;
368 if(strstr(gs->variant, "great")) piece = PRIESTESS;
369 if(strstr(gs->variant, "shogi")) piece = DRAGONHORSE;
372 if(strstr(gs->variant, "great")) piece = MASTODON;
376 /* Resolve ambiguities in to-ness */
377 if (tf == ALG_UNKNOWN) {
378 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
379 return MOVE_AMBIGUOUS; /* Must always know to file */
381 if (tr == ALG_UNKNOWN) {
382 posr = posr2 = ALG_UNKNOWN;
384 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
385 return MOVE_AMBIGUOUS;
387 if (ff == ALG_UNKNOWN) {
388 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
389 return MOVE_AMBIGUOUS;
391 /* Need to find pawn on ff that can take to tf and fill in ranks */
392 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
393 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
394 if ((ff != ALG_UNKNOWN) && (ff != f))
396 if (piecetype(gs->board[f][r]) != piece)
398 if (gs->onMove == WHITE) {
403 /* if ((gs->board[tf][tmpr] == NOPIECE) ||
404 (iscolor(gs->board[tf][tmpr], gs->onMove))) continue;*/
405 /* patch from Soso, added by Sparky 3/16/95 */
406 if (gs->board[tf][tmpr] == NOPIECE) {
407 if ((gs->ep_possible[((gs->onMove == WHITE) ? 0 : 1)][ff]) != (tf - ff))
410 if (iscolor(gs->board[tf][tmpr], gs->onMove))
414 if (legal_andcheck_move(gs, f, r, tf, tmpr)) {
415 if ((posr != ALG_UNKNOWN) && (posr2 != ALG_UNKNOWN)) {
416 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
417 return MOVE_AMBIGUOUS;
425 } else if (bc) { /* Could be bxc4 or Bxc4, tr is known */
428 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
429 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
430 if ((piecetype(gs->board[f][r]) != PAWN) && (piecetype(gs->board[f][r]) != piece)) {
431 // note that the interpretation Bxc4 is matched last, and has set piece to BISHOP
435 if (legal_andcheck_move(gs, f, r, tf, tr)) {
436 if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
440 /* if its a lowercase 'b' then prefer the pawn move if there is one */
441 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
442 piecetype(gs->board[f][r]) == PAWN && mstr[0] >= 'a') {
448 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
449 piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
453 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
454 d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
455 return (MOVE_AMBIGUOUS);
461 } else { /* The from position is unknown */
464 if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) {
465 /* Need to find a piece that can go to tf, tr */
466 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
467 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
468 if ((ff != ALG_UNKNOWN) && (ff != f))
470 if ((fr != ALG_UNKNOWN) && (fr != r))
472 if (piecetype(gs->board[f][r]) != piece)
474 if (legal_andcheck_move(gs, f, r, tf, tr)) {
475 if ((posf != ALG_UNKNOWN) && (posr != ALG_UNKNOWN)) {
476 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
477 return MOVE_AMBIGUOUS;
483 } else if (ff == ALG_DROP) {
484 if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
492 if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
493 (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
500 add_promotion(gs, mstr, mt);
505 /* A assumes the move has yet to be made on the board */
507 /* Soso: rewrote alg_unparse function.
508 * Algebraic deparser - sets the mStr variable with move description
509 * in short notation. Used in last move report and in 'moves' command.
512 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
514 static char mStr[20];
517 int ambig, r_ambig, f_ambig;
518 struct game_state_t fakeMove;
520 if (mt->fromFile == ALG_DROP) {
521 piece = mt->fromRank;
523 piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
526 if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R
\r
527 strcpy(mStr, "O-O");
\r
530 if ((mt->fromFile == ALG_CASTLE) && (mt->toFile < mt->toRank)) { // [HGM] castle: K ends left of R
\r
531 strcpy(mStr, "O-O-O");
\r
537 if (mt->fromFile == ALG_DROP) {
539 } else if (mt->fromFile != mt->toFile
540 || gs->board[mt->toFile][mt->toRank] != NOPIECE) { // [HGM] XQ: forward captures as "axa6"
541 sprintf(tmp, "%c", mt->fromFile + 'a');
624 if (mt->fromFile == ALG_DROP) {
627 /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
629 ambig = r_ambig = f_ambig = 0;
630 for (r = 0; r < gs->ranks; r++)
\r
631 for (f = 0; f < gs->files; f++) {
\r
632 if ((gs->board[f][r] != NOPIECE) && iscolor(gs->board[f][r], gs->onMove)
633 && (piecetype(gs->board[f][r]) == piece) &&
634 ((f != mt->fromFile) || (r != mt->fromRank))) {
635 if (legal_move(gs, f, r, mt->toFile, mt->toRank)) {
637 fakeMove.board[f][r] = NOPIECE;
638 fakeMove.board[mt->toFile][mt->toRank] = piece | gs->onMove;
639 fakeMove.onMove = CToggle(fakeMove.onMove);
640 gs->onMove = CToggle(gs->onMove);
643 d_printf("possible move %c%d%c%d against %c%d%c%d\n",
645 'a' + mt->toFile, mt->toRank+1,
646 'a' + mt->fromFile, mt->fromRank+1,
647 'a' + mt->toFile, mt->toRank+1);
650 if (!in_check(&fakeMove)) {
652 if (f == mt->fromFile) {
656 if (r == mt->fromRank) {
661 gs->onMove = CToggle(gs->onMove);
666 /* Ambiguity in short notation, need to add file,rank or _both_ in
\r
668 if (f_ambig == 0) {
\r
669 sprintf(tmp, "%c", mt->fromFile + 'a');
\r
671 } else if (r_ambig == 0) {
\r
672 sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
\r
675 sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
\r
680 if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
\r
681 ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
\r
685 sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
\r
688 if ((piece == PAWN || gs->promoType == 3) && (mt->piecePromotionTo != NOPIECE)) {
\r
689 strcat(mStr, "="); /* = before promoting piece */
\r
690 switch (piecetype(mt->piecePromotionTo)) {
\r
733 case GOLD: // [HGM] Shogi promotions: avoid use of '+'
\r
748 execute_move(&fakeMove, mt, 0);
749 fakeMove.onMove = CToggle(fakeMove.onMove);
750 if (in_check(&fakeMove)) {