--- a/src/chess/rules.h Thu Jul 30 18:34:15 2026 +0200 +++ b/src/chess/rules.h Thu Jul 30 19:17:38 2026 +0200 @@ -43,7 +43,11 @@ #define PIECE_PINNED 5 #define KING_IN_CHECK 6 #define KING_MOVES_INTO_CHECK 7 -#define RULES_VIOLATED 10 +#define MISSING_CHECK 8 +#define MISSING_CHECKMATE 9 +#define INVALID_CHECK 10 +#define INVALID_CHECKMATE 11 +#define RULES_VIOLATED 32 #define ENPASSANT_THREAT 0x40u @@ -83,7 +87,7 @@ struct movetimeval { uint64_t tv_sec; - int32_t tv_usec; // important that this is signed b/c potential carry + int32_t tv_usec; /* important that this is signed b/c potential carry */ }; typedef struct { @@ -92,9 +96,11 @@ Row fromrow; File tofile; Row torow; - uint8_t promotion; - uint8_t check; + Piece promotion; + uint8_t check; /* must always be set if checkmate is set */ + uint8_t checkmate; uint8_t capture; + uint8_t padding[7]; /* necessary for stable ABI across networks */ struct movetimeval timestamp; struct movetimeval movetime; char string[8]; @@ -189,7 +195,13 @@ } static inline bool is_game_running(const GameState *gamestate) { - return !(gamestate->checkmate || gamestate->wresign || gamestate->bresign || gamestate->threefold || gamestate->stalemate || gamestate->remis || gamestate->review); + return !(gamestate->checkmate || gamestate->wresign || gamestate->bresign + || gamestate->threefold || gamestate->stalemate || gamestate->remis + || gamestate->review); +} + +static inline bool is_check_position(const GameState *gamestate) { + return gamestate->moves[gamestate->movecount - 1].check; } /** @@ -242,7 +254,7 @@ * @param gamestate the current game state * @return the color of the player who is next to move */ -Color current_color(GameState *gamestate); +Color current_color(const GameState *gamestate); /** * Returns the piece at the specified position. @@ -276,7 +288,29 @@ } /** - * Checks, if a specified field is covered by a piece of a certain color. + * Determines a list of possible moves to the specified field. + * + * The out-parameters may both be NULL, but if any of them is set, the other + * must be set, too. + * + * @param gamestate the current game state + * @param row row of the field to check + * @param file file of the field to check + * @param color the color of the piece that should move to the field + * @param moves the array where to store the moves + * (must be large enough, 16 is always enough) + * @param movecount a pointer where the number of moves is stored + * @return true, if any piece of the specified color can move to the specified + * field + */ +bool get_candidates(const GameState *gamestate, Row row, File file, + Color color, Move* moves, size_t* movecount); + +/** + * Checks, if a specified field is threatened by a piece of a certain color. + * + * A field is threatened, if there is a piece of the specified color that could + * capture an opponent piece on this field, regardless of being pinned. * * The out-parameters may both be NULL, but if any of them is set, the other * must be set, too. @@ -285,17 +319,17 @@ * @param row row of the field to check * @param file file of the field to check * @param color the color of the piece that should threaten the field - * @param threats the array where to store the threats (should be able to hold - * the rare maximum of 16 elements) + * @param threats the array where to store the threats + * (must be large enough, 16 is always enough) * @param threatcount a pointer where the count of threats is stored * @return true, if any piece of the specified color threatens the specified - * field (i.e. could capture an opponent piece) + * field */ -bool get_threats(GameState *gamestate, Row row, File file, +bool get_threats(const GameState *gamestate, Row row, File file, Color color, Move* threats, size_t* threatcount); /** - * Checks, if a specified field is covered by a piece of a certain color AND + * Checks, if a specified field is threatened by a piece of a certain color AND * if this piece is not pinned and therefore able to perform the move. * * The out-parameters may both be NULL, but if any of them is set, the other @@ -305,17 +339,20 @@ * @param row row of the field to check * @param file file of the field to check * @param color the color of the piece that should threaten the field - * @param threats the array where to store the threats (should be able to hold - * the rare maximum of 16 elements) + * @param threats the array where to store the threats + * (must be large enough, 16 is always enough) * @param threatcount a pointer where the count of threats is stored * @return true, if any piece of the specified color threatens the specified - * field (i.e. could capture an opponent piece) + * field and is not pinned */ -bool get_real_threats(GameState *gamestate, Row row, File file, +bool get_real_threats(const GameState *gamestate, Row row, File file, Color color, Move* threats, size_t* threatcount); /** - * Checks, if a specified field is covered by a piece of a certain color. + * Checks, if a specified field is threatened by a piece of a certain color. + * + * A field is threatened, if there is a piece of the specified color that could + * capture an opponent piece on this field, regardless of being pinned. * * @param gamestate the current game state * @param row row of the field to check @@ -330,7 +367,7 @@ /** * Checks, if a specified field is attacked by a piece of a certain color. * - * I.e. the field is covered by a piece AND this piece is not pinned and + * I.e. the field is threatened by a piece AND this piece is not pinned and * therefore able to perform the move. * * @param gamestate the current game state @@ -346,8 +383,8 @@ /** * Checks, if a specified field is protected by a piece of a certain color. * - * I.e. the field is covered by a piece that is NOT the king AND this piece is - * not pinned and therefore able to perform the move. + * This is the same as is_attacked(), but only considers pieces that are not + * the king. * * @param gamestate the current game state * @param row row of the field to check @@ -356,7 +393,7 @@ * @return true, if any piece (excluding the king) of the specified color * threatens the specified field and could capture an opponent piece */ -bool is_protected(GameState *gamestate, Row row, File file, Color color); +bool is_protected(const GameState *gamestate, Row row, File file, Color color); /** * Checks, if the specified move cannot be performed, because the piece is @@ -372,25 +409,51 @@ * @return true, if the move cannot be performed because the king would be in * check after the move */ -bool is_pinned(GameState *gamestate, Move *move); +bool is_pinned(const GameState *gamestate, const Move *move); /** * 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. + * evaluating the allowed moves according to the current game state. + * + * This function expects correct notation of check and checkmate indicators. + * For a more lazy evaluation, use eval_move_lazy(). * * 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 + * @param color the color of the player to evaluate the move for * @param move a pointer to object where the move data shall be stored - * @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 eval_move(GameState *gamestate, const char *mstr, Move *move, Color color); +int eval_move(const GameState *gamestate, + const char *mstr, Color color, Move *move); + +/** + * 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 + * evaluating the allowed moves according to the current game state. + * + * This function automatically corrects missing or incorrect check/checkmate + * indicators. Use eval_move() if you want to keep the original notation. + * + * 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 + * @param color the color of the player to evaluate the move for + * @param move a pointer to object where the move data shall be stored + * @return status code (see macros in this file for the list of codes) + */ +int eval_move_lazy(const GameState *gamestate, + const char *mstr, Color color, Move *move); /** * Syntactically checks a move without verifying that a piece exists that is @@ -408,7 +471,7 @@ * @param move the move to validate * @return status code (see macros in this file for the list of codes) */ -int validate_move(GameState *gamestate, Move *move); +int validate_move(const GameState *gamestate, const Move *move); /** * Applies a move and deletes captured pieces. @@ -427,7 +490,7 @@ * @param move_number the half-move that would now be played * @param replay the struct to populate with the state at the specified move */ -void gamestate_at_move(GameState *gamestate, +void gamestate_at_move(const GameState *gamestate, unsigned move_number, GameState *replay); /** @@ -439,7 +502,7 @@ * @return the remaining time - if time control is disabled, this function * always returns zero */ -uint16_t remaining_movetime2(GameState *gamestate, unsigned move_number); +uint16_t remaining_movetime2(const GameState *gamestate, unsigned move_number); /** * Returns the remaining time on the clock for the specified player. @@ -449,7 +512,7 @@ * @return the remaining time - if time control is disabled, this function * always returns zero */ -uint16_t remaining_movetime(GameState *gamestate, Color color); +uint16_t remaining_movetime(const GameState *gamestate, Color color); /** * Converts clock time to string. @@ -472,7 +535,7 @@ * @param gamestate the current game state * @return true if the game is in a threefold repetition position */ -bool check_threefold_repetition(GameState *gamestate); +bool check_threefold_repetition(const GameState *gamestate); #endif /* RULES_H */