diff options
Diffstat (limited to 'FICS/algcheck.c')
-rw-r--r-- | FICS/algcheck.c | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/FICS/algcheck.c b/FICS/algcheck.c new file mode 100644 index 0000000..15642fd --- /dev/null +++ b/FICS/algcheck.c @@ -0,0 +1,567 @@ +/* algcheck.c + * + */ + +/* + fics - An internet chess server. + Copyright (C) 1993 Richard V. Nash + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +/* Revision history: + name email yy/mm/dd Change + Richard Nash 93/10/22 Created + Markus Uhlin 24/05/05 Revised + Markus Uhlin 25/04/05 alg_parse_move: + return ambiguous move on + out-of-bounds array read/write. +*/ + +#include "stdinclude.h" +#include "common.h" + +#include <err.h> + +#include "algcheck.h" +#include "board.h" +#include "maxxes-utils.h" +#include "movecheck.h" +#include "utils.h" + +#define ALG_UNKNOWN -1 + +/* + * Well, lets see if I can list the possibilities: + * + * Piece moves: + * Ne4 + * Nxe4 + * Nce4 + * Ncxe4 + * R2f3 + * R2xf3 + * + * Special pawn moves: + * e4 + * ed + * exd + * exd5 + * ed5 + * + * Drop moves (bughouse, board edit): + * P@f7 P*f7 + * #f7 #Nf7 + * (o-o, o-o-o) + * Castling is handled earlier, so don't worry about that. Of course + * any of these can have a + or ++ or = string on the end, just cut + * that off. + */ + +/* + * f - file + * r - rank + * p - piece + * x - x + * @ - drop character (bughouse) + */ +PRIVATE char *alg_list[] = { + "fxfr", "pxfr", // These two get confused in case of bishop + "ffr", "pfr", // These two get confused in case of bishop + "pffr", + "pfxfr", + "prfr", + "prxfr", + "fr", + "ff", + "fxf", + "p@fr", + "#fr", + "#pfr", + NULL +}; + +PRIVATE int +get_move_info(char *str, int *piece, int *ff, int *fr, int *tf, int *tr, + int *bishconfusion) +{ + char *s; + char c; + char tmp[1024] = { '\0' }; + int i, j, len; + int lpiece, lff, lfr, ltf, ltr; + int matchVal = -1; + + *bishconfusion = 0; + mstrlcpy(tmp, str, sizeof tmp); + + if ((s = strchr(tmp, '+'))) // Cut off any check marks + *s = '\0'; + if ((s = strchr(tmp, '='))) // Cut off any promotion marks + *s = '\0'; + + *piece = *ff = *fr = *tf = *tr = ALG_UNKNOWN; + len = strlen(tmp); + + for (i = 0; alg_list[i]; i++) { + lpiece = lff = lfr = ltf = ltr = ALG_UNKNOWN; + + if (strlen(alg_list[i]) != (size_t)len) + continue; + + for (j = len - 1; j >= 0; j--) { + switch (alg_list[i][j]) { + case 'f': + if ((tmp[j] < 'a') || (tmp[j] > 'h')) + goto nomatch; + if (ltf == ALG_UNKNOWN) + ltf = tmp[j] - 'a'; + else + lff = tmp[j] - 'a'; + break; + case 'r': + if ((tmp[j] < '1') || (tmp[j] > '8')) + goto nomatch; + if (ltr == ALG_UNKNOWN) + ltr = tmp[j] - '1'; + else + lfr = tmp[j] - '1'; + break; + case 'p': + if (isupper(tmp[j])) + c = tolower(tmp[j]); + else + c = tmp[j]; + if (c == 'k') + lpiece = KING; + else if (c == 'q') + lpiece = QUEEN; + else if (c == 'r') + lpiece = ROOK; + else if (c == 'b') + lpiece = BISHOP; + else if (c == 'n') + lpiece = KNIGHT; + else if (c == 'p') + lpiece = PAWN; + else + goto nomatch; + break; + case 'x': + if ((tmp[j] != 'x') && (tmp[j] != 'X')) + goto nomatch; + break; + case '@': + if (tmp[j] != '@' && tmp[j] != '*') + goto nomatch; + lff = lfr = ALG_DROP; + break; + case '#': + if (tmp[j] != '#') + goto nomatch; + lff = lfr = ALG_DROP; + break; + default: + fprintf(stderr, "Unknown character in " + "algebraic parsing\n"); + break; + } + } + + if (lpiece == ALG_UNKNOWN) + lpiece = PAWN; + if (lpiece == PAWN && (lfr == ALG_UNKNOWN)) { // ffr or ff + if (lff != ALG_UNKNOWN) { + if (lff == ltf) + goto nomatch; + if ((lff - ltf != 1) && (ltf - lff != 1)) + goto nomatch; + } + } + + *piece = lpiece; // We have a match + *tf = ltf; + *tr = ltr; + *ff = lff; + *fr = lfr; + + if (matchVal != -1) { + /* + * We have two matches, it must be that Bxc4 + * vs. bxc4 problem. Or it could be the Bc4 vs + * bc4 problem. + */ + *bishconfusion = 1; + } + + matchVal = i; + + nomatch:; + } /* for */ + + if (matchVal != -1) + return MS_ALG; + else + return MS_NOTMOVE; +} + +PUBLIC int +alg_is_move(char *mstr) +{ + int piece, ff, fr, tf, tr, bc; + + return get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc); +} + +/* + * We already know it is algebraic - get the move squares. + */ +PUBLIC int +alg_parse_move(char *mstr, game_state_t *gs, move_t *mt) +{ + int f, r, tmpr, posf, posr, posr2; + int piece, ff, fr, tf, tr, bc; + + if (get_move_info(mstr, &piece, &ff, &fr, &tf, &tr, &bc) != MS_ALG) { + fprintf(stderr, "FICS: Shouldn't try to algebraically parse " + "non-algebraic move string.\n"); + return MOVE_ILLEGAL; + } + + // Resolve ambiguities in to-ness + if (tf == ALG_UNKNOWN) + return MOVE_AMBIGUOUS; // Must always know to file + if (tr == ALG_UNKNOWN) { + posr = posr2 = ALG_UNKNOWN; + + if (piece != PAWN) + return MOVE_AMBIGUOUS; + if (ff == ALG_UNKNOWN) + return MOVE_AMBIGUOUS; + + /* + * Need to find pawn on ff that can take to tf and + * fill in ranks. + */ + for (InitPieceLoop(gs->board, &f, &r, gs->onMove); + NextPieceLoop(gs->board, &f, &r, gs->onMove);) { + if ((ff != ALG_UNKNOWN) && (ff != f)) + continue; + if (r < 0 || r >= 8) { + warnx("%s: out-of-bounds array read/write: " + "r=%d", __func__, r); + return MOVE_AMBIGUOUS; + } + if (piecetype(gs->board[f][r]) != piece) + continue; + if (gs->onMove == WHITE) { + tmpr = r + 1; + } else { + tmpr = r - 1; + } + if (tmpr < 0 || tmpr >= 8) { + warnx("%s: out-of-bounds array read/write: " + "tmpr=%d", __func__, tmpr); + return MOVE_AMBIGUOUS; + } + + if (gs->board[tf][tmpr] == NOPIECE) { + if ((gs->ep_possible[((gs->onMove == WHITE) ? + 0 : 1)][ff]) != (tf - ff)) + continue; + } else { + if (iscolor(gs->board[tf][tmpr], gs->onMove)) + continue; + } + + if (legal_andcheck_move(gs, f, r, tf, tmpr)) { + if ((posr != ALG_UNKNOWN) && + (posr2 != ALG_UNKNOWN)) + return MOVE_AMBIGUOUS; + posr = tmpr; + posr2 = r; + } + } + + tr = posr; + fr = posr2; + } else if (bc) { // Could be bxc4 or Bxc4, tr is known. + ff = ALG_UNKNOWN; + fr = ALG_UNKNOWN; + + for (InitPieceLoop(gs->board, &f, &r, gs->onMove); + NextPieceLoop(gs->board, &f, &r, gs->onMove);) { + if ((piecetype(gs->board[f][r]) != PAWN) && + (piecetype(gs->board[f][r]) != BISHOP)) + continue; + if (legal_andcheck_move(gs, f, r, tf, tr)) { + if ((piecetype(gs->board[f][r]) == PAWN) && + (f != 1)) + continue; + if ((ff != ALG_UNKNOWN) && (fr != ALG_UNKNOWN)) + return (MOVE_AMBIGUOUS); + ff = f; + fr = r; + } + } + } else { // The from position is unknown + posf = ALG_UNKNOWN; + posr = ALG_UNKNOWN; + + if ((ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) { + /* + * Need to find a piece that can go to tf, tr. + */ + for (InitPieceLoop(gs->board, &f, &r, gs->onMove); + NextPieceLoop(gs->board, &f, &r, gs->onMove);) { + if ((ff != ALG_UNKNOWN) && (ff != f)) + continue; + if ((fr != ALG_UNKNOWN) && (fr != r)) + continue; + if (piecetype(gs->board[f][r]) != piece) + continue; + if (legal_andcheck_move(gs, f, r, tf, tr)) { + if ((posf != ALG_UNKNOWN) && + (posr != ALG_UNKNOWN)) + return MOVE_AMBIGUOUS; + posf = f; + posr = r; + } + } + } else if (ff == ALG_DROP) { + if (legal_andcheck_move(gs, ALG_DROP, piece, tf, tr)) { + posf = ALG_DROP; + posr = piece; + } + } + + ff = posf; + fr = posr; + } + + if ((tf == ALG_UNKNOWN) || (tr == ALG_UNKNOWN) || + (ff == ALG_UNKNOWN) || (fr == ALG_UNKNOWN)) + return MOVE_ILLEGAL; + + mt->fromFile = ff; + mt->fromRank = fr; + mt->toFile = tf; + mt->toRank = tr; + + return MOVE_OK; +} + +/* + * A assumes the move has yet to be made on the board. (This is the + * old stupid function, we are testing one from soso...) + */ +#if 0 +PUBLIC char * +alg_unparse(game_state_t *gs, move_t *mt) +{ + static char mStr[20] = { '\0' }; + + if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) && + ((mt->fromFile == 4) && (mt->toFile == 6))) + return "o-o"; + if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) && + ((mt->fromFile == 4) && (mt->toFile == 2))) + return "o-o-o"; + + msnprintf(mStr, sizeof mStr, "%c%d%c%d", + mt->fromFile + 'a', + mt->fromRank + 1, + mt->toFile + 'a', + mt->toRank + 1); + return mStr; +} +#endif + +/* + * Soso: Rewrote the alg_unparse() function. + * + * Algebraic deparser - sets the 'mStr' variable with move description + * in short notation. Used in last move report and in 'moves' command. + */ +PUBLIC char * +alg_unparse(game_state_t *gs, move_t *mt) +{ + char tmp[20] = { '\0' }; + game_state_t fakeMove; + int ambig, r_ambig, f_ambig; + int piece, f, r; + static char mStr[20] = { '\0' }; + + if (mt->fromFile == ALG_DROP) { + piece = mt->fromRank; + } else { + piece = piecetype(gs->board[mt->fromFile][mt->fromRank]); + } + + if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 6))) { + mstrlcpy(mStr, "O-O", sizeof mStr); + goto check; + } + if ((piece == KING) && ((mt->fromFile == 4) && (mt->toFile == 2))) { + mstrlcpy(mStr, "O-O-O", sizeof mStr); + goto check; + } + + mstrlcpy(mStr, "", sizeof mStr); + + switch (piece) { + case PAWN: + if (mt->fromFile == ALG_DROP) { + mstrlcpy(mStr, "P", sizeof mStr); + } else if (mt->fromFile != mt->toFile) { + msnprintf(tmp, sizeof tmp, "%c", mt->fromFile + 'a'); + mstrlcpy(mStr, tmp, sizeof mStr); + } + break; + case KNIGHT: + mstrlcpy(mStr, "N", sizeof mStr); + break; + case BISHOP: + mstrlcpy(mStr, "B", sizeof mStr); + break; + case ROOK: + mstrlcpy(mStr, "R", sizeof mStr); + break; + case QUEEN: + mstrlcpy(mStr, "Q", sizeof mStr); + break; + case KING: + mstrlcpy(mStr, "K", sizeof mStr); + break; + default: + mstrlcpy(mStr, "", sizeof mStr); + break; + } + + if (mt->fromFile == ALG_DROP) { + mstrlcat(mStr, DROP_STR, sizeof mStr); + } else { + /* + * Checks for ambiguity in short notation (Ncb3, R8e8 + * or so). + */ + if (piece != PAWN) { + ambig = r_ambig = f_ambig = 0; + + for (r = 0; r < 8; r++) { + for (f = 0; f < 8; f++) { + if ((gs->board[f][r] != NOPIECE) && + iscolor(gs->board[f][r], gs->onMove) && + (piecetype(gs->board[f][r]) == piece) && + ((f != mt->fromFile) || + (r != mt->fromRank))) { + if (legal_move(gs, f, r, + mt->toFile, mt->toRank)) { + fakeMove = *gs; + fakeMove.board[f][r] = + NOPIECE; + fakeMove.onMove = + CToggle(fakeMove.onMove); + gs->onMove = + CToggle(gs->onMove); + + /* + * New + * bracketing + * below to + * try to fix + * 'ambiguous + * move' bug. + */ + if ((in_check(gs)) || + !in_check(&fakeMove)) + ambig++; + + if (f == mt->fromFile) { + f_ambig++; + ambig++; + } + if (r == mt->fromRank) { + r_ambig++; + ambig++; + } + + gs->onMove = + CToggle(gs->onMove); + } + } + } /* for */ + } /* for */ + + if (ambig > 0) { + /* + * Ambiguity in short notation, need + * to add file, rank or _both_ in + * notation. + */ + + if (f_ambig == 0) { + msnprintf(tmp, sizeof tmp, "%c", + mt->fromFile + 'a'); + mstrlcat(mStr, tmp, sizeof mStr); + } else if (r_ambig == 0) { + msnprintf(tmp, sizeof tmp, "%d", + mt->fromRank + 1); + mstrlcat(mStr, tmp, sizeof mStr); + } else { + msnprintf(tmp, sizeof tmp, "%c%d", + mt->fromFile + 'a', + mt->fromRank + 1); + mstrlcat(mStr, tmp, sizeof mStr); + } + } + } + + if ((gs->board[mt->toFile][mt->toRank] != NOPIECE) || + ((piece == PAWN) && (mt->fromFile != mt->toFile))) { + mstrlcat(mStr, "x", sizeof mStr); + } + } + + msnprintf(tmp, sizeof tmp, "%c%d", mt->toFile + 'a', mt->toRank + 1); + mstrlcat(mStr, tmp, sizeof mStr); + + if ((piece == PAWN) && (mt->piecePromotionTo != NOPIECE)) { + mstrlcat(mStr, "=", sizeof mStr); // = before promoting piece + + switch (piecetype(mt->piecePromotionTo)) { + case KNIGHT: + mstrlcat(mStr, "N", sizeof mStr); + break; + case BISHOP: + mstrlcat(mStr, "B", sizeof mStr); + break; + case ROOK: + mstrlcat(mStr, "R", sizeof mStr); + break; + case QUEEN: + mstrlcat(mStr, "Q", sizeof mStr); + break; + default: + break; + } + } + + check:; + + fakeMove = *gs; + execute_move(&fakeMove, mt, 0); + fakeMove.onMove = CToggle(fakeMove.onMove); + + if (in_check(&fakeMove)) + mstrlcat(mStr, "+", sizeof mStr); + return mStr; +} |