universe@10: /* universe@10: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@10: * universe@55: * Copyright 2016 Mike Becker. All rights reserved. universe@10: * universe@10: * Redistribution and use in source and binary forms, with or without universe@10: * modification, are permitted provided that the following conditions are met: universe@10: * universe@10: * 1. Redistributions of source code must retain the above copyright universe@10: * notice, this list of conditions and the following disclaimer. universe@10: * universe@10: * 2. Redistributions in binary form must reproduce the above copyright universe@10: * notice, this list of conditions and the following disclaimer in the universe@10: * documentation and/or other materials provided with the distribution. universe@10: * universe@10: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@10: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@10: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@10: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@10: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@10: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@10: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@10: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@10: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@10: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@10: * POSSIBILITY OF SUCH DAMAGE. universe@10: * universe@10: */ universe@10: universe@10: #ifndef RULES_H universe@10: #define RULES_H universe@10: universe@19: #include universe@36: #include universe@10: universe@48: #define VALID_MOVE_SYNTAX 0 universe@48: #define VALID_MOVE_SEMANTICS 0 /* use same code for a success */ universe@48: #define INVALID_MOVE_SYNTAX 1 universe@48: #define INVALID_POSITION 2 universe@48: #define AMBIGUOUS_MOVE 3 universe@48: #define NEED_PROMOTION 4 universe@48: #define PIECE_PINNED 5 universe@48: #define KING_IN_CHECK 6 universe@48: #define KING_MOVES_INTO_CHECK 7 universe@48: #define RULES_VIOLATED 10 universe@16: universe@19: universe@19: #define PIECE_MASK 0x0F universe@19: #define COLOR_MASK 0x30 universe@19: #define ENPASSANT_THREAT 0x40 universe@19: universe@19: #define WHITE 0x10 universe@19: #define BLACK 0x20 universe@19: universe@19: #define PAWN 0x01 universe@19: #define ROOK 0x02 universe@19: #define KNIGHT 0x03 universe@19: #define BISHOP 0x04 universe@19: #define QUEEN 0x05 universe@19: #define KING 0x06 universe@19: universe@19: #define WPAWN (WHITE|PAWN) universe@19: #define WROOK (WHITE|ROOK) universe@19: #define WKNIGHT (WHITE|KNIGHT) universe@19: #define WBISHOP (WHITE|BISHOP) universe@19: #define WQUEEN (WHITE|QUEEN) universe@19: #define WKING (WHITE|KING) universe@19: #define BPAWN (BLACK|PAWN) universe@19: #define BROOK (BLACK|ROOK) universe@19: #define BKNIGHT (BLACK|KNIGHT) universe@19: #define BBISHOP (BLACK|BISHOP) universe@19: #define BQUEEN (BLACK|QUEEN) universe@19: #define BKING (BLACK|KING) universe@19: universe@19: typedef uint8_t Board[8][8]; universe@19: universe@40: struct movetimeval { universe@40: uint64_t tv_sec; universe@40: int32_t tv_usec; universe@40: }; universe@40: universe@19: typedef struct { universe@19: uint8_t piece; universe@19: uint8_t fromfile; universe@19: uint8_t fromrow; universe@19: uint8_t tofile; universe@19: uint8_t torow; universe@19: uint8_t promotion; universe@41: uint8_t check; universe@41: uint8_t capture; universe@40: struct movetimeval timestamp; universe@40: struct movetimeval movetime; universe@49: char string[8]; universe@19: } Move; universe@19: universe@23: typedef struct MoveList MoveList; universe@23: universe@23: struct MoveList { universe@23: Move move; universe@23: MoveList* next; universe@23: }; universe@23: universe@30: universe@30: typedef struct { universe@30: uint8_t servercolor; universe@40: uint8_t timecontrol; universe@30: uint16_t time; universe@30: uint16_t addtime; universe@30: } GameInfo; universe@30: universe@23: typedef struct { universe@23: Board board; universe@23: MoveList* movelist; universe@23: MoveList* lastmove; universe@63: unsigned int movecount; /* number of (half-)moves (counting BOTH colors) */ universe@27: _Bool checkmate; universe@27: _Bool stalemate; universe@50: _Bool remis; universe@50: _Bool resign; universe@23: } GameState; universe@23: universe@25: #define opponent_color(color) ((color)==WHITE?BLACK:WHITE) universe@25: universe@19: #define POS_UNSPECIFIED UINT8_MAX universe@19: #define mdst(b,m) b[(m)->torow][(m)->tofile] universe@19: #define msrc(b,m) b[(m)->fromrow][(m)->fromfile] universe@19: universe@28: #define isidx(idx) ((uint8_t)(idx) < 8) universe@19: universe@19: #define isfile(file) (file >= 'a' && file <= 'h') universe@19: #define isrow(row) (row >= '1' && row <= '8') universe@19: universe@19: #define rowidx(row) (row-'1') universe@19: #define fileidx(file) (file-'a') universe@19: universe@19: #define rowchr(row) (row+'1') universe@19: #define filechr(file) (file+'a') universe@19: universe@19: #define chkidx(move) (isidx((move)->fromfile) && isidx((move)->fromrow) && \ universe@19: isidx((move)->tofile) && isidx((move)->torow)) universe@19: universe@19: /* secure versions - use, if index is not checked with isidx() */ universe@19: #define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED) universe@19: #define rowidx_s(c) (isrow(c)?rowidx(c):POS_UNSPECIFIED) universe@19: universe@50: #define is_game_running(gamestate) !((gamestate)->checkmate || \ universe@50: (gamestate)->resign || (gamestate)->stalemate || (gamestate)->remis) universe@50: universe@50: universe@50: /** universe@50: * Initializes a game state and prepares the chess board. universe@50: * @param gamestate the game state to initialize universe@50: */ universe@50: void gamestate_init(GameState *gamestate); universe@50: universe@49: /** universe@49: * Cleans up a game state and frees the memory for the movement list. universe@49: * @param gamestate the game state to clean up universe@49: */ universe@23: void gamestate_cleanup(GameState *gamestate); universe@23: universe@19: /** universe@19: * Maps a character to a piece. universe@19: * universe@19: * Does not work for pawns, since they don't have a character. universe@19: * universe@19: * @param c one of R,N,B,Q,K universe@19: * @return numeric value for the specified piece universe@19: */ universe@19: uint8_t getpiece(char c); universe@19: universe@19: /** universe@19: * Maps a piece to a character. universe@19: * universe@19: * Does not work for pawns, scince they don't have a character. universe@19: * universe@19: * @param piece one of ROOK, KNIGHT, BISHOP, QUEEN, KING universe@19: * @return character value for the specified piece universe@19: */ universe@19: char getpiecechr(uint8_t piece); universe@19: universe@19: /** universe@25: * Checks, if a specified field is covered by a piece of a certain color. universe@25: * universe@29: * The out-parameters may both be NULL, but if any of them is set, the other universe@29: * must be set, too. universe@28: * universe@28: * @param gamestate the current game state universe@28: * @param row row of the field to check universe@28: * @param file file of the field to check universe@28: * @param color the color of the piece that should threaten the field universe@47: * @param threats the array where to store the threats (should be able to hold universe@47: * the rare maximum of 16 elements) universe@47: * @param threatcount a pointer to an uint8_t where the count of threats is universe@47: * stored universe@28: * @return TRUE, if any piece of the specified color threatens the specified universe@28: * field (i.e. could capture an opponent piece) universe@28: */ universe@29: _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, universe@29: uint8_t color, Move* threats, uint8_t* threatcount); universe@29: universe@29: /** universe@29: * Checks, if a specified field is covered by a piece of a certain color AND universe@29: * if this piece is not pinned and therefore able to perform the move. universe@29: * universe@29: * The out-parameters may both be NULL, but if any of them is set, the other universe@29: * must be set, too. universe@29: * universe@29: * @param gamestate the current game state universe@29: * @param row row of the field to check universe@29: * @param file file of the field to check universe@29: * @param color the color of the piece that should threaten the field universe@47: * @param threats the array where to store the threats (should be able to hold universe@47: * the rare maximum of 16 elements) universe@47: * @param threatcount a pointer to an uint8_t where the count of threats is universe@47: * stored universe@29: * @return TRUE, if any piece of the specified color threatens the specified universe@29: * field (i.e. could capture an opponent piece) universe@29: */ universe@29: _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, universe@29: uint8_t color, Move* threats, uint8_t* threatcount); universe@28: universe@28: /** universe@28: * Checks, if a specified field is covered by a piece of a certain color. universe@28: * universe@25: * @param gamestate the current game state universe@25: * @param row row of the field to check universe@25: * @param file file of the field to check universe@25: * @param color the color of the piece that should cover the field universe@25: * @return TRUE, if any piece of the specified color threatens the specified universe@29: * field universe@25: */ universe@28: #define is_covered(gamestate, row, file, color) \ universe@29: get_threats(gamestate, row, file, color, NULL, NULL) universe@29: universe@29: /** universe@29: * Checks, if a specified field is attacked by a piece of a certain color. universe@29: * universe@29: * I.e. the field is covered by a piece AND this piece is not pinned and universe@29: * therefore able to perform the move. universe@29: * universe@29: * @param gamestate the current game state universe@29: * @param row row of the field to check universe@29: * @param file file of the field to check universe@29: * @param color the color of the piece that should cover the field universe@29: * @return TRUE, if any piece of the specified color threatens the specified universe@29: * field and could capture an opponent piece universe@29: */ universe@29: #define is_attacked(gamestate, row, file, color) \ universe@61: get_real_threats(gamestate, row, file, color, NULL, NULL) universe@29: universe@29: /** universe@29: * Checks, if a specified field is protected by a piece of a certain color. universe@29: * universe@29: * I.e. the field is covered by a piece that is NOT the king AND this piece is universe@29: * not pinned and therefore able to perform the move. universe@29: * universe@29: * @param gamestate the current game state universe@29: * @param row row of the field to check universe@29: * @param file file of the field to check universe@29: * @param color the color of the piece that should cover the field universe@29: * @return TRUE, if any piece (excluding the king) of the specified color universe@29: * threatens the specified field and could capture an opponent piece universe@29: */ universe@29: _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, universe@29: uint8_t color); universe@25: universe@25: /** universe@47: * Checks, if the specified move cannot be performed, because the piece is universe@47: * either pinned or cannot remove the check. universe@47: * universe@47: * Note: in chess a piece is pinned, when it can't be moved because the move universe@47: * would result in a check position. But this function also returns true, universe@47: * if the king is already in check position and the specified move does not universe@47: * protect the king. universe@47: * universe@47: * @param gamestate the current game state universe@47: * @param move the move to check universe@47: * @return TRUE, if the move cannot be performed because the king would be in universe@47: * check after the move universe@47: */ universe@47: _Bool is_pinned(GameState *gamestate, Move *move); universe@47: universe@47: /** universe@19: * Evaluates a move syntactically and stores the move data in the specified universe@19: * object. universe@19: * universe@23: * @param gamestate the current game state universe@19: * @param mstr the input string to parse universe@19: * @param move a pointer to object where the move data shall be stored universe@50: * @param color the color of the player to evaluate the move for universe@48: * @return status code (see macros in this file for the list of codes) universe@19: */ universe@50: int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color); universe@19: universe@19: /** universe@19: * Validates move by applying chess rules. universe@23: * @param gamestate the current game state universe@19: * @param move the move to validate universe@48: * @return status code (see macros in this file for the list of codes) universe@19: */ universe@48: int validate_move(GameState *gamestate, Move *move); universe@19: universe@19: /** universe@19: * Applies a move and deletes captured pieces. universe@19: * universe@23: * @param gamestate the current game state universe@19: * @param move the move to apply universe@19: */ universe@23: void apply_move(GameState *gamestate, Move *move); universe@19: universe@33: universe@33: /** universe@33: * Returns the remaining time on the clock for the specified player. universe@33: * universe@33: * @param gameinfo the information about the game universe@33: * @param gamestate the current game state universe@33: * @param color either BLACK or WHITE universe@33: * @return the remaining time - if time control is disabled, this function universe@33: * always returns zero universe@33: */ universe@33: uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, universe@33: uint8_t color); universe@33: universe@10: #endif /* RULES_H */ universe@10: