Fri, 04 Apr 2014 17:36:42 +0200
NEED TESTING: implemented check and checkmate - TODO: avoid checkmate by moving another piece in between
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 Thu Apr 03 16:07:04 2014 +0200 +++ b/src/chess/rules.c Fri Apr 04 17:36:42 2014 +0200 @@ -90,12 +90,14 @@ } } -_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) { +_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, + uint8_t color, Move *threat) { Move threats[16]; int threatcount = 0; for (uint8_t r = 0 ; r < 8 ; r++) { for (uint8_t f = 0 ; f < 8 ; f++) { if ((gamestate->board[r][f] & COLOR_MASK) == color) { + memset(&(threats[threatcount]), 0, sizeof(Move)); threats[threatcount].piece = gamestate->board[r][f]; threats[threatcount].fromrow = r; threats[threatcount].fromfile = f; @@ -108,6 +110,9 @@ for (int i = 0 ; i < threatcount ; i++) { if (validate_move(gamestate, &(threats[i]))) { + if (threat) { + *threat = threats[i]; + } return 1; } } @@ -158,7 +163,7 @@ gamestate->board[move->torow][fileidx('d')] = color|ROOK; } } - + addmove(gamestate, move); } @@ -179,8 +184,8 @@ result = msrc(gamestate->board, move) == move->piece; /* can't capture own pieces */ - if ((mdst(gamestate->board, move) & COLOR_MASK) - == (move->piece & COLOR_MASK)) { + uint8_t piececolor = (move->piece & COLOR_MASK); + if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { return 0; } @@ -219,13 +224,70 @@ return 0; } - /* is piece pinned */ - // TODO: make it so + /* find kings for check validation */ + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; + for (uint8_t row = 0 ; row < 8 ; row++) { + for (uint8_t file = 0 ; file < 8 ; file++) { + if (gamestate->board[row][file] == + (piececolor == WHITE?WKING:BKING)) { + mykingfile = file; + mykingrow = row; + } else if (gamestate->board[row][file] == + (piececolor == WHITE?BKING:WKING)) { + opkingfile = file; + opkingrow = row; + } + } + } + + /* simulation move for check validation */ + GameState simulation; + memcpy(&simulation, gamestate, sizeof(GameState)); + apply_move(&simulation, move); + + /* don't move into or stay in check position */ + if (is_covered(&simulation, mykingrow, mykingfile, + opponent_color(piececolor))) { + return 0; + } /* correct check and checkmate flags (move is still valid) */ - // TODO: make it so + Move threat; + move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, + piececolor, &threat); - return result; + if (move->check) { + /* determine possible escape fields */ + _Bool canescape = 0; + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { + for (int df = -1 ; df <= 1 && !canescape ; df++) { + if (!(dr == 0 && df == 0) && + isidx(opkingrow + dr) && isidx(opkingfile + df)) { + + /* escape field neither blocked nor covered */ + if ((simulation.board[opkingrow + dr][opkingfile + df] + & COLOR_MASK) != opponent_color(piececolor)) { + canescape |= !is_covered(&simulation, + opkingrow + dr, opkingfile + df, piececolor); + } + } + } + } + /* can't escape, can we capture? */ + if (!canescape) { + canescape = is_covered(&simulation, threat.fromrow, + threat.fromfile, opponent_color(piececolor)); + + /* can't capture, can we block? */ + // TODO: make it so + + if (!canescape) { + gamestate->checkmate = 1; + } + } + } + + return 1; } int eval_move(GameState *gamestate, char *mstr, Move *move) {
--- a/src/chess/rules.h Thu Apr 03 16:07:04 2014 +0200 +++ b/src/chess/rules.h Fri Apr 04 17:36:42 2014 +0200 @@ -102,7 +102,7 @@ #define mdst(b,m) b[(m)->torow][(m)->tofile] #define msrc(b,m) b[(m)->fromrow][(m)->fromfile] -#define isidx(idx) ((uint8_t)idx < 8) +#define isidx(idx) ((uint8_t)(idx) < 8) #define isfile(file) (file >= 'a' && file <= 'h') #define isrow(row) (row >= '1' && row <= '8') @@ -145,6 +145,24 @@ /** * Checks, if a specified field is covered by a piece of a certain color. * + * Note: when the field is covered by multiple pieces, this function returns + * the first piece it can find. + * + * @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 threaten the field + * @param threat if not NULL: a pointer to the move structure where + * the move that could be performed to capture the field should be stored + * @return TRUE, if any piece of the specified color threatens the specified + * field (i.e. could capture an opponent piece) + */ +_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, + uint8_t color, Move *threat); + +/** + * Checks, if a specified field is covered by a piece of a certain color. + * * @param gamestate the current game state * @param row row of the field to check * @param file file of the field to check @@ -152,7 +170,8 @@ * @return TRUE, if any piece of the specified color threatens the specified * field (i.e. could capture an opponent piece) */ -_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color); +#define is_covered(gamestate, row, file, color) \ + get_any_threat_for(gamestate, row, file, color, NULL) /** * Evaluates a move syntactically and stores the move data in the specified
--- a/src/game.c Thu Apr 03 16:07:04 2014 +0200 +++ b/src/game.c Fri Apr 04 17:36:42 2014 +0200 @@ -106,7 +106,7 @@ } if (!logelem->next) { if (gamestate->checkmate) { - addch('#'); + addstr("\b#"); } else if (gamestate->stalemate) { addstr(" stalemate"); } @@ -343,9 +343,9 @@ void game_start_singlemachine(Settings *settings) { GameState gamestate; + memset(&gamestate, 0, sizeof(GameState)); init_board(&gamestate); gamestate.mycolor = WHITE; - gamestate.movelist = gamestate.lastmove = NULL; // TODO: time limit _Bool running; do { @@ -354,6 +354,8 @@ running = !domove_singlemachine(&gamestate); gamestate.mycolor = opponent_color(gamestate.mycolor); } while (running); + move(0,0); + draw_board(&gamestate); gamestate_cleanup(&gamestate); @@ -369,9 +371,9 @@ // TODO: time limit GameState gamestate; + memset(&gamestate, 0, sizeof(GameState)); init_board(&gamestate); gamestate.mycolor = myturn ? WHITE:BLACK; - gamestate.movelist = gamestate.lastmove = NULL; _Bool running; do { @@ -386,6 +388,9 @@ myturn ^= TRUE; } while (running); + move(0,0); + draw_board(&gamestate); + gamestate_cleanup(&gamestate); mvaddstr(getmaxy(stdscr)-1, 0,