Tue, 28 Apr 2026 12:25:48 +0200
relax validation of premoves to allow retaking pieces
fixes #832
| src/chess/rules.c | file | annotate | diff | comparison | revisions | |
| src/chess/rules.h | file | annotate | diff | comparison | revisions | |
| src/game.c | file | annotate | diff | comparison | revisions |
--- a/src/chess/rules.c Mon Apr 27 18:06:00 2026 +0200 +++ b/src/chess/rules.c Tue Apr 28 12:25:48 2026 +0200 @@ -633,7 +633,7 @@ } } -int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { +static int eval_move1(char *mstr, Move *move, uint8_t color) { memset(move, 0, sizeof(Move)); move->fromfile = POS_UNSPECIFIED; move->fromrow = POS_UNSPECIFIED; @@ -745,23 +745,41 @@ } - if (move->piece) { - if (move->piece == PAWN - && move->torow == (color==WHITE?7:0) - && !move->promotion) { - return NEED_PROMOTION; - } - - move->piece |= color; + if (!move->piece) { + return INVALID_MOVE_SYNTAX; + } + + if (move->piece == PAWN + && move->torow == (color==WHITE?7:0) + && !move->promotion) { + return NEED_PROMOTION; + } + + move->piece |= color; + + if (!chkidx_to(move)) { + return INVALID_POSITION; + } + + return VALID_MOVE_SYNTAX; +} + +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { + int result = eval_move1(mstr, move, color); + if (result == VALID_MOVE_SYNTAX) { if (move->fromfile == POS_UNSPECIFIED || move->fromrow == POS_UNSPECIFIED) { return getlocation(gamestate, move); - } else { - return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; + } else if (!chkidx_from(move)) { + return INVALID_POSITION; } - } else { - return INVALID_MOVE_SYNTAX; } + return result; +} + +int check_move(char *mstr, uint8_t color) { + Move move; + return eval_move1(mstr, &move, color); } bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
--- a/src/chess/rules.h Mon Apr 27 18:06:00 2026 +0200 +++ b/src/chess/rules.h Tue Apr 28 12:25:48 2026 +0200 @@ -63,8 +63,9 @@ #define rowchr(row) (row+'1') #define filechr(file) (file+'a') -#define chkidx(move) (isidx((move)->fromfile) && isidx((move)->fromrow) && \ - isidx((move)->tofile) && isidx((move)->torow)) +#define chkidx_from(move) (isidx((move)->fromfile) && isidx((move)->fromrow)) +#define chkidx_to(move) (isidx((move)->tofile) && isidx((move)->torow)) +#define chkidx(move) (chkidx_from(move) && chkidx_to(move)) /* secure versions - use, if index is not checked with isidx() */ #define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED) @@ -206,6 +207,12 @@ /** * Evaluates a move syntactically and stores the move data in the specified * object. + * + * When short algebraic notation is used, the source position is determined by + * the evaluating the allowed moves according to the current game state. + * + * For a purely syntactic check, regardless of whether a piece exists that is + * allowed to move that way, use check_move(). * * @param gamestate the current game state * @param mstr the input string to parse @@ -216,6 +223,16 @@ int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color); /** + * Syntactically checks a move without verifying that a piece exists that is + * allowed to move that way. + * + * @param mstr the input string to parse + * @param color the color of the player to evaluate the move for + * @return status code (see macros in this file for the list of codes) + */ +int check_move(char *mstr, uint8_t color); + +/** * Validates move by applying chess rules. * @param gamestate the current game state * @param move the move to validate
--- a/src/game.c Mon Apr 27 18:06:00 2026 +0200 +++ b/src/game.c Tue Apr 28 12:25:48 2026 +0200 @@ -478,8 +478,7 @@ } else if (movestr[0] == 0) { memset(gamestate->premove, 0, sizeof(gamestate->premove)); } else { - Move move; - int res = eval_move(gamestate, movestr, &move, mycolor); + int res = check_move(movestr, mycolor); if (res == VALID_MOVE_SYNTAX) { strncpy(gamestate->premove, movestr, 8); memset(movestr, 0, MOVESTR_BUFLEN);