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 |
1.1 --- a/src/chess/rules.c Thu Apr 03 16:07:04 2014 +0200 1.2 +++ b/src/chess/rules.c Fri Apr 04 17:36:42 2014 +0200 1.3 @@ -90,12 +90,14 @@ 1.4 } 1.5 } 1.6 1.7 -_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) { 1.8 +_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, 1.9 + uint8_t color, Move *threat) { 1.10 Move threats[16]; 1.11 int threatcount = 0; 1.12 for (uint8_t r = 0 ; r < 8 ; r++) { 1.13 for (uint8_t f = 0 ; f < 8 ; f++) { 1.14 if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.15 + memset(&(threats[threatcount]), 0, sizeof(Move)); 1.16 threats[threatcount].piece = gamestate->board[r][f]; 1.17 threats[threatcount].fromrow = r; 1.18 threats[threatcount].fromfile = f; 1.19 @@ -108,6 +110,9 @@ 1.20 1.21 for (int i = 0 ; i < threatcount ; i++) { 1.22 if (validate_move(gamestate, &(threats[i]))) { 1.23 + if (threat) { 1.24 + *threat = threats[i]; 1.25 + } 1.26 return 1; 1.27 } 1.28 } 1.29 @@ -158,7 +163,7 @@ 1.30 gamestate->board[move->torow][fileidx('d')] = color|ROOK; 1.31 } 1.32 } 1.33 - 1.34 + 1.35 addmove(gamestate, move); 1.36 } 1.37 1.38 @@ -179,8 +184,8 @@ 1.39 result = msrc(gamestate->board, move) == move->piece; 1.40 1.41 /* can't capture own pieces */ 1.42 - if ((mdst(gamestate->board, move) & COLOR_MASK) 1.43 - == (move->piece & COLOR_MASK)) { 1.44 + uint8_t piececolor = (move->piece & COLOR_MASK); 1.45 + if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { 1.46 return 0; 1.47 } 1.48 1.49 @@ -219,13 +224,70 @@ 1.50 return 0; 1.51 } 1.52 1.53 - /* is piece pinned */ 1.54 - // TODO: make it so 1.55 + /* find kings for check validation */ 1.56 + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 1.57 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.58 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.59 + if (gamestate->board[row][file] == 1.60 + (piececolor == WHITE?WKING:BKING)) { 1.61 + mykingfile = file; 1.62 + mykingrow = row; 1.63 + } else if (gamestate->board[row][file] == 1.64 + (piececolor == WHITE?BKING:WKING)) { 1.65 + opkingfile = file; 1.66 + opkingrow = row; 1.67 + } 1.68 + } 1.69 + } 1.70 + 1.71 + /* simulation move for check validation */ 1.72 + GameState simulation; 1.73 + memcpy(&simulation, gamestate, sizeof(GameState)); 1.74 + apply_move(&simulation, move); 1.75 + 1.76 + /* don't move into or stay in check position */ 1.77 + if (is_covered(&simulation, mykingrow, mykingfile, 1.78 + opponent_color(piececolor))) { 1.79 + return 0; 1.80 + } 1.81 1.82 /* correct check and checkmate flags (move is still valid) */ 1.83 - // TODO: make it so 1.84 + Move threat; 1.85 + move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, 1.86 + piececolor, &threat); 1.87 1.88 - return result; 1.89 + if (move->check) { 1.90 + /* determine possible escape fields */ 1.91 + _Bool canescape = 0; 1.92 + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 1.93 + for (int df = -1 ; df <= 1 && !canescape ; df++) { 1.94 + if (!(dr == 0 && df == 0) && 1.95 + isidx(opkingrow + dr) && isidx(opkingfile + df)) { 1.96 + 1.97 + /* escape field neither blocked nor covered */ 1.98 + if ((simulation.board[opkingrow + dr][opkingfile + df] 1.99 + & COLOR_MASK) != opponent_color(piececolor)) { 1.100 + canescape |= !is_covered(&simulation, 1.101 + opkingrow + dr, opkingfile + df, piececolor); 1.102 + } 1.103 + } 1.104 + } 1.105 + } 1.106 + /* can't escape, can we capture? */ 1.107 + if (!canescape) { 1.108 + canescape = is_covered(&simulation, threat.fromrow, 1.109 + threat.fromfile, opponent_color(piececolor)); 1.110 + 1.111 + /* can't capture, can we block? */ 1.112 + // TODO: make it so 1.113 + 1.114 + if (!canescape) { 1.115 + gamestate->checkmate = 1; 1.116 + } 1.117 + } 1.118 + } 1.119 + 1.120 + return 1; 1.121 } 1.122 1.123 int eval_move(GameState *gamestate, char *mstr, Move *move) {
2.1 --- a/src/chess/rules.h Thu Apr 03 16:07:04 2014 +0200 2.2 +++ b/src/chess/rules.h Fri Apr 04 17:36:42 2014 +0200 2.3 @@ -102,7 +102,7 @@ 2.4 #define mdst(b,m) b[(m)->torow][(m)->tofile] 2.5 #define msrc(b,m) b[(m)->fromrow][(m)->fromfile] 2.6 2.7 -#define isidx(idx) ((uint8_t)idx < 8) 2.8 +#define isidx(idx) ((uint8_t)(idx) < 8) 2.9 2.10 #define isfile(file) (file >= 'a' && file <= 'h') 2.11 #define isrow(row) (row >= '1' && row <= '8') 2.12 @@ -145,6 +145,24 @@ 2.13 /** 2.14 * Checks, if a specified field is covered by a piece of a certain color. 2.15 * 2.16 + * Note: when the field is covered by multiple pieces, this function returns 2.17 + * the first piece it can find. 2.18 + * 2.19 + * @param gamestate the current game state 2.20 + * @param row row of the field to check 2.21 + * @param file file of the field to check 2.22 + * @param color the color of the piece that should threaten the field 2.23 + * @param threat if not NULL: a pointer to the move structure where 2.24 + * the move that could be performed to capture the field should be stored 2.25 + * @return TRUE, if any piece of the specified color threatens the specified 2.26 + * field (i.e. could capture an opponent piece) 2.27 + */ 2.28 +_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, 2.29 + uint8_t color, Move *threat); 2.30 + 2.31 +/** 2.32 + * Checks, if a specified field is covered by a piece of a certain color. 2.33 + * 2.34 * @param gamestate the current game state 2.35 * @param row row of the field to check 2.36 * @param file file of the field to check 2.37 @@ -152,7 +170,8 @@ 2.38 * @return TRUE, if any piece of the specified color threatens the specified 2.39 * field (i.e. could capture an opponent piece) 2.40 */ 2.41 -_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color); 2.42 +#define is_covered(gamestate, row, file, color) \ 2.43 + get_any_threat_for(gamestate, row, file, color, NULL) 2.44 2.45 /** 2.46 * Evaluates a move syntactically and stores the move data in the specified
3.1 --- a/src/game.c Thu Apr 03 16:07:04 2014 +0200 3.2 +++ b/src/game.c Fri Apr 04 17:36:42 2014 +0200 3.3 @@ -106,7 +106,7 @@ 3.4 } 3.5 if (!logelem->next) { 3.6 if (gamestate->checkmate) { 3.7 - addch('#'); 3.8 + addstr("\b#"); 3.9 } else if (gamestate->stalemate) { 3.10 addstr(" stalemate"); 3.11 } 3.12 @@ -343,9 +343,9 @@ 3.13 3.14 void game_start_singlemachine(Settings *settings) { 3.15 GameState gamestate; 3.16 + memset(&gamestate, 0, sizeof(GameState)); 3.17 init_board(&gamestate); 3.18 gamestate.mycolor = WHITE; 3.19 - gamestate.movelist = gamestate.lastmove = NULL; 3.20 // TODO: time limit 3.21 _Bool running; 3.22 do { 3.23 @@ -354,6 +354,8 @@ 3.24 running = !domove_singlemachine(&gamestate); 3.25 gamestate.mycolor = opponent_color(gamestate.mycolor); 3.26 } while (running); 3.27 + move(0,0); 3.28 + draw_board(&gamestate); 3.29 3.30 gamestate_cleanup(&gamestate); 3.31 3.32 @@ -369,9 +371,9 @@ 3.33 3.34 // TODO: time limit 3.35 GameState gamestate; 3.36 + memset(&gamestate, 0, sizeof(GameState)); 3.37 init_board(&gamestate); 3.38 gamestate.mycolor = myturn ? WHITE:BLACK; 3.39 - gamestate.movelist = gamestate.lastmove = NULL; 3.40 3.41 _Bool running; 3.42 do { 3.43 @@ -386,6 +388,9 @@ 3.44 myturn ^= TRUE; 3.45 } while (running); 3.46 3.47 + move(0,0); 3.48 + draw_board(&gamestate); 3.49 + 3.50 gamestate_cleanup(&gamestate); 3.51 3.52 mvaddstr(getmaxy(stdscr)-1, 0,