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.
159 if ((tmp[j] != 'x') && (tmp[j] != 'X'))
163 if (tmp[j] != '@' && tmp[j] != '*')
165 lff = lfr = ALG_DROP;
170 lff = lfr = ALG_DROP;
173 d_printf( "Unknown character in algebraic parsing\n");
177 if (lpiece == ALG_UNKNOWN)
179 if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) { /* ffr or ff */
180 if (lff != ALG_UNKNOWN) {
183 if ((lff - ltf != 1) && (ltf - lff != 1))
187 *piece = lpiece; /* We have a match */
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 */
206 int alg_is_move(const char *mstr)
208 int piece=0, ff=0, fr=0, tf=0, tr=0, bc=0;
210 return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc);
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)
218 s = strchr(mstr, '=');
223 if (piecetype(gs->board[mt->fromFile][mt->fromRank]) != PAWN) {
226 if (mt->toRank != gs->ranks-1 && mt->toRank != 0) {
230 switch (tolower(s[1])) {
238 if(!gs->capablancaPieces) return; // [HGM] should make variant-dependent piece mask
\r
242 if(!gs->capablancaPieces) return;
\r
246 if(!gs->royalKnight) return; // [HGM] only in knightmate
\r
256 if(gs->royalKnight) return; // [HGM] not in knightmate
\r
259 // Superchess promotons: filtered out later by promoType
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;
285 mt->piecePromotionTo = piece | colorval(gs->board[mt->fromFile][mt->fromRank]);
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)
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;
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");
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
304 if(tr >= gs->ranks || fr >= gs->ranks || tf >= gs->files || ff >= gs->files)
\r
305 return MOVE_ILLEGAL;
\r
307 // [HGM] resolve ambiguity in piece, type based on variant
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;
315 if(strstr(gs->variant, "super")) piece = AMAZON; else
316 if(strstr(gs->variant, "xiangqi")) piece = MANDARIN;
319 if(strstr(gs->variant, "xiangqi")) piece = CANNON;
322 if(strstr(gs->variant, "super")) piece = PRINCESS;
323 if(strstr(gs->variant, "great")) piece = MAN2;
326 if(strstr(gs->variant, "shatranj")) piece = ALFIL;
329 if(strstr(gs->variant, "shatranj")) piece = FERZ;
332 if(strstr(gs->variant, "super")) piece = WOODY;
335 if(strstr(gs->variant, "shogi")) piece = HONORABLEHORSE;
338 if(strstr(gs->variant, "great")) piece = MINISTER;
341 if(strstr(gs->variant, "great")) piece = PRIESTESS;
344 if(strstr(gs->variant, "great")) piece = MASTODON;
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 */
353 if (tr == ALG_UNKNOWN) {
354 posr = posr2 = ALG_UNKNOWN;
356 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
357 return MOVE_AMBIGUOUS;
359 if (ff == ALG_UNKNOWN) {
360 d_printf("Ambiguous %s(%d)\n", __FUNCTION__, __LINE__);
361 return MOVE_AMBIGUOUS;
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))
368 if (piecetype(gs->board[f][r]) != piece)
370 if (gs->onMove == WHITE) {
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))
382 if (iscolor(gs->board[tf][tmpr], gs->onMove))
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;
397 } else if (bc) { /* Could be bxc4 or Bxc4, tr is known */
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
407 if (legal_andcheck_move(gs, f, r, tf, tr)) {
408 if ((piecetype(gs->board[f][r]) == PAWN) && (f != tolower(mstr[0]) - 'a')) {
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') {
420 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN) &&
421 piecetype(gs->board[ff][fr]) == PAWN && mstr[0] >= 'a') {
425 if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) {
426 d_printf("Ambiguous %s(%d) mstr=%s\n", __FUNCTION__, __LINE__, mstr);
427 return (MOVE_AMBIGUOUS);
433 } else { /* The from position is 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))
442 if ((fr != ALG_UNKNOWN) && (fr != r))
444 if (piecetype(gs->board[f][r]) != piece)
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;
455 } else if (ff == ALG_DROP) {
456 if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) {
464 if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) ||
465 (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN))
472 add_promotion(gs, mstr, mt);
477 /* A assumes the move has yet to be made on the board */
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.
484 char *alg_unparse(struct game_state_t * gs, struct move_t * mt)
486 static char mStr[20];
489 int ambig, r_ambig, f_ambig;
490 struct game_state_t fakeMove;
492 if (mt->fromFile == ALG_DROP) {
493 piece = mt->fromRank;
495 piece = piecetype(gs->board[mt->fromFile][mt->fromRank]);
498 if ((mt->fromFile == ALG_CASTLE) && (mt->toFile > mt->toRank)) { // [HGM] castle: K ends right of R
\r
499 strcpy(mStr, "O-O");
\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
509 if (mt->fromFile == ALG_DROP) {
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');
596 if (mt->fromFile == ALG_DROP) {
599 /* Checks for ambiguity in short notation ( Ncb3, R8e8 or so) */
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)) {
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);
615 d_printf("possible move %c%d%c%d against %c%d%c%d\n",
617 'a' + mt->toFile, mt->toRank+1,
618 'a' + mt->fromFile, mt->fromRank+1,
619 'a' + mt->toFile, mt->toRank+1);
622 if (!in_check(&fakeMove)) {
624 if (f == mt->fromFile) {
628 if (r == mt->fromRank) {
633 gs->onMove = CToggle(gs->onMove);
638 /* Ambiguity in short notation, need to add file,rank or _both_ in
\r
640 if (f_ambig == 0) {
\r
641 sprintf(tmp, "%c", mt->fromFile + 'a');
\r
643 } else if (r_ambig == 0) {
\r
644 sprintf(tmp, "%d", mt->fromRank + 1 - (gs->ranks > 9));
\r
647 sprintf(tmp, "%c%d", mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks > 9));
\r
652 if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) ||
\r
653 ((piece == PAWN) && (mt->fromFile != mt->toFile))) {
\r
657 sprintf(tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1 - (gs->ranks > 9));
\r
660 if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) {
\r
661 strcat(mStr, "="); /* = before promoting piece */
\r
662 switch (piecetype(mt->piecePromotionTo)) {
\r
711 execute_move(&fakeMove, mt, 0);
712 fakeMove.onMove = CToggle(fakeMove.onMove);
713 if (in_check(&fakeMove)) {