Mon, 07 Apr 2014 14:08:57 +0200
fixed checkmate and completed implementation (more testing is still advised)
src/chess/rules.c | file | annotate | diff | comparison | revisions | |
src/chess/rules.h | file | annotate | diff | comparison | revisions | |
src/main.c | file | annotate | diff | comparison | revisions |
1.1 --- a/src/chess/rules.c Fri Apr 04 17:36:42 2014 +0200 1.2 +++ b/src/chess/rules.c Mon Apr 07 14:08:57 2014 +0200 1.3 @@ -90,36 +90,6 @@ 1.4 } 1.5 } 1.6 1.7 -_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, 1.8 - uint8_t color, Move *threat) { 1.9 - Move threats[16]; 1.10 - int threatcount = 0; 1.11 - for (uint8_t r = 0 ; r < 8 ; r++) { 1.12 - for (uint8_t f = 0 ; f < 8 ; f++) { 1.13 - if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.14 - memset(&(threats[threatcount]), 0, sizeof(Move)); 1.15 - threats[threatcount].piece = gamestate->board[r][f]; 1.16 - threats[threatcount].fromrow = r; 1.17 - threats[threatcount].fromfile = f; 1.18 - threats[threatcount].torow = row; 1.19 - threats[threatcount].tofile = file; 1.20 - threatcount++; 1.21 - } 1.22 - } 1.23 - } 1.24 - 1.25 - for (int i = 0 ; i < threatcount ; i++) { 1.26 - if (validate_move(gamestate, &(threats[i]))) { 1.27 - if (threat) { 1.28 - *threat = threats[i]; 1.29 - } 1.30 - return 1; 1.31 - } 1.32 - } 1.33 - 1.34 - return 0; 1.35 -} 1.36 - 1.37 void apply_move(GameState *gamestate, Move *move) { 1.38 uint8_t piece = move->piece & PIECE_MASK; 1.39 uint8_t color = move->piece & COLOR_MASK; 1.40 @@ -167,9 +137,7 @@ 1.41 addmove(gamestate, move); 1.42 } 1.43 1.44 -_Bool validate_move(GameState *gamestate, Move *move) { 1.45 - _Bool result; 1.46 - 1.47 +static _Bool validate_move_rules(GameState *gamestate, Move *move) { 1.48 /* validate indices (don't trust opponent) */ 1.49 if (!chkidx(move)) { 1.50 return 0; 1.51 @@ -181,50 +149,52 @@ 1.52 } 1.53 1.54 /* does piece exist */ 1.55 - result = msrc(gamestate->board, move) == move->piece; 1.56 + if (msrc(gamestate->board, move) != move->piece) { 1.57 + return 0; 1.58 + } 1.59 1.60 /* can't capture own pieces */ 1.61 - uint8_t piececolor = (move->piece & COLOR_MASK); 1.62 - if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { 1.63 + if ((mdst(gamestate->board, move) & COLOR_MASK) 1.64 + == (move->piece & COLOR_MASK)) { 1.65 return 0; 1.66 } 1.67 1.68 /* validate individual rules */ 1.69 switch (move->piece & PIECE_MASK) { 1.70 case PAWN: 1.71 - result = result && pawn_chkrules(gamestate, move); 1.72 - result = result && !pawn_isblocked(gamestate, move); 1.73 - break; 1.74 + return pawn_chkrules(gamestate, move) && 1.75 + !pawn_isblocked(gamestate, move); 1.76 case ROOK: 1.77 - result = result && rook_chkrules(move); 1.78 - result = result && !rook_isblocked(gamestate, move); 1.79 - break; 1.80 + return rook_chkrules(move) && 1.81 + !rook_isblocked(gamestate, move); 1.82 case KNIGHT: 1.83 - result = result && knight_chkrules(move); 1.84 - result = result && !knight_isblocked(gamestate, move); 1.85 - break; 1.86 + return knight_chkrules(move); /* knight is never blocked */ 1.87 case BISHOP: 1.88 - result = result && bishop_chkrules(move); 1.89 - result = result && !bishop_isblocked(gamestate, move); 1.90 - break; 1.91 + return bishop_chkrules(move) && 1.92 + !bishop_isblocked(gamestate, move); 1.93 case QUEEN: 1.94 - result = result && queen_chkrules(move); 1.95 - result = result && !queen_isblocked(gamestate, move); 1.96 - break; 1.97 + return queen_chkrules(move) && 1.98 + !queen_isblocked(gamestate, move); 1.99 case KING: 1.100 - result = result && king_chkrules(gamestate, move); 1.101 - result = result && !king_isblocked(gamestate, move); 1.102 - break; 1.103 + return king_chkrules(gamestate, move) && 1.104 + !king_isblocked(gamestate, move); 1.105 default: 1.106 - result = 0; 1.107 + return 0; 1.108 } 1.109 +} 1.110 + 1.111 +_Bool validate_move(GameState *gamestate, Move *move) { 1.112 1.113 - /* cancel processing to avoid recursion overflow with is_covered() */ 1.114 + _Bool result = validate_move_rules(gamestate, move); 1.115 + 1.116 + /* cancel processing to save resources */ 1.117 if (!result) { 1.118 return 0; 1.119 } 1.120 1.121 /* find kings for check validation */ 1.122 + uint8_t piececolor = (move->piece & COLOR_MASK); 1.123 + 1.124 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 1.125 for (uint8_t row = 0 ; row < 8 ; row++) { 1.126 for (uint8_t file = 0 ; file < 8 ; file++) { 1.127 @@ -252,9 +222,10 @@ 1.128 } 1.129 1.130 /* correct check and checkmate flags (move is still valid) */ 1.131 - Move threat; 1.132 - move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, 1.133 - piececolor, &threat); 1.134 + Move threats[16]; 1.135 + uint8_t threatcount; 1.136 + move->check = get_threats(&simulation, opkingrow, opkingfile, 1.137 + piececolor, threats, &threatcount); 1.138 1.139 if (move->check) { 1.140 /* determine possible escape fields */ 1.141 @@ -273,17 +244,58 @@ 1.142 } 1.143 } 1.144 } 1.145 - /* can't escape, can we capture? */ 1.146 + /* can't escape, can he capture? */ 1.147 + if (!canescape && threatcount == 1) { 1.148 + canescape = is_attacked(&simulation, threats[0].fromrow, 1.149 + threats[0].fromfile, opponent_color(piececolor)); 1.150 + } 1.151 + 1.152 + /* can't capture, can he block? */ 1.153 + if (!canescape && threatcount == 1) { 1.154 + Move *threat = &(threats[0]); 1.155 + uint8_t threatpiece = threat->piece & PIECE_MASK; 1.156 + 1.157 + /* knight, pawns and the king cannot be blocked */ 1.158 + if (threatpiece == BISHOP || threatpiece == ROOK 1.159 + || threatpiece == QUEEN) { 1.160 + if (threat->fromrow == threat->torow) { 1.161 + /* rook aspect (on row) */ 1.162 + int d = threat->tofile > threat->fromfile ? 1 : -1; 1.163 + uint8_t file = threat->fromfile; 1.164 + while (!canescape && file != threat->tofile - d) { 1.165 + file += d; 1.166 + canescape |= is_protected(&simulation, 1.167 + threat->torow, file, opponent_color(piececolor)); 1.168 + } 1.169 + } else if (threat->fromfile == threat->tofile) { 1.170 + /* rook aspect (on file) */ 1.171 + int d = threat->torow > threat->fromrow ? 1 : -1; 1.172 + uint8_t row = threat->fromrow; 1.173 + while (!canescape && row != threat->torow - d) { 1.174 + row += d; 1.175 + canescape |= is_protected(&simulation, 1.176 + row, threat->tofile, opponent_color(piececolor)); 1.177 + } 1.178 + } else { 1.179 + /* bishop aspect */ 1.180 + int dr = move->torow > move->fromrow ? 1 : -1; 1.181 + int df = move->tofile > move->fromfile ? 1 : -1; 1.182 + 1.183 + uint8_t row = move->fromrow; 1.184 + uint8_t file = move->fromfile; 1.185 + while (!canescape && file != move->tofile - df 1.186 + && row != move->torow - dr) { 1.187 + row += dr; 1.188 + file += df; 1.189 + canescape |= is_protected(&simulation, row, file, 1.190 + opponent_color(piececolor)); 1.191 + } 1.192 + } 1.193 + } 1.194 + } 1.195 + 1.196 if (!canescape) { 1.197 - canescape = is_covered(&simulation, threat.fromrow, 1.198 - threat.fromfile, opponent_color(piececolor)); 1.199 - 1.200 - /* can't capture, can we block? */ 1.201 - // TODO: make it so 1.202 - 1.203 - if (!canescape) { 1.204 - gamestate->checkmate = 1; 1.205 - } 1.206 + gamestate->checkmate = 1; 1.207 } 1.208 } 1.209 1.210 @@ -418,3 +430,97 @@ 1.211 return INVALID_MOVE_SYNTAX; 1.212 } 1.213 } 1.214 + 1.215 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.216 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.217 + Move candidates[16]; 1.218 + int candidatecount = 0; 1.219 + for (uint8_t r = 0 ; r < 8 ; r++) { 1.220 + for (uint8_t f = 0 ; f < 8 ; f++) { 1.221 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.222 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 1.223 + candidates[candidatecount].piece = gamestate->board[r][f]; 1.224 + candidates[candidatecount].fromrow = r; 1.225 + candidates[candidatecount].fromfile = f; 1.226 + candidates[candidatecount].torow = row; 1.227 + candidates[candidatecount].tofile = file; 1.228 + candidatecount++; 1.229 + } 1.230 + } 1.231 + } 1.232 + 1.233 + if (threatcount) { 1.234 + *threatcount = 0; 1.235 + } 1.236 + 1.237 + 1.238 + _Bool result = 0; 1.239 + 1.240 + for (int i = 0 ; i < candidatecount ; i++) { 1.241 + if (validate_move_rules(gamestate, &(candidates[i]))) { 1.242 + result = 1; 1.243 + if (threats && threatcount) { 1.244 + threats[(*threatcount)++] = candidates[i]; 1.245 + } 1.246 + } 1.247 + } 1.248 + 1.249 + return result; 1.250 +} 1.251 + 1.252 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.253 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.254 + 1.255 + Move candidates[16]; 1.256 + uint8_t candidatecount; 1.257 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 1.258 + 1.259 + if (threatcount) { 1.260 + *threatcount = 0; 1.261 + } 1.262 + _Bool result = 0; 1.263 + uint8_t kingfile = 0, kingrow = 0; 1.264 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.265 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.266 + if ((gamestate->board[row][file] & COLOR_MASK) == color) { 1.267 + kingfile = file; 1.268 + kingrow = row; 1.269 + } 1.270 + } 1.271 + } 1.272 + 1.273 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 1.274 + GameState simulation; 1.275 + memcpy(&simulation, gamestate, sizeof(GameState)); 1.276 + apply_move(&simulation, &(candidates[i])); 1.277 + if (!is_covered(&simulation, kingrow, kingfile, 1.278 + opponent_color(color))) { 1.279 + result = 1; 1.280 + if (threats && threatcount) { 1.281 + threats[(*threatcount)++] = candidates[i]; 1.282 + } 1.283 + } 1.284 + } 1.285 + 1.286 + return result; 1.287 + } else { 1.288 + return 0; 1.289 + } 1.290 +} 1.291 + 1.292 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 1.293 + uint8_t color) { 1.294 + 1.295 + Move threats[16]; 1.296 + uint8_t threatcount; 1.297 + if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 1.298 + for (int i = 0 ; i < threatcount ; i++) { 1.299 + if (threats[i].piece != (color|KING)) { 1.300 + return 1; 1.301 + } 1.302 + } 1.303 + return 0; 1.304 + } else { 1.305 + return 0; 1.306 + } 1.307 +}
2.1 --- a/src/chess/rules.h Fri Apr 04 17:36:42 2014 +0200 2.2 +++ b/src/chess/rules.h Mon Apr 07 14:08:57 2014 +0200 2.3 @@ -145,20 +145,41 @@ 2.4 /** 2.5 * Checks, if a specified field is covered by a piece of a certain color. 2.6 * 2.7 - * Note: when the field is covered by multiple pieces, this function returns 2.8 - * the first piece it can find. 2.9 + * The out-parameters may both be NULL, but if any of them is set, the other 2.10 + * must be set, too. 2.11 * 2.12 * @param gamestate the current game state 2.13 * @param row row of the field to check 2.14 * @param file file of the field to check 2.15 * @param color the color of the piece that should threaten the field 2.16 - * @param threat if not NULL: a pointer to the move structure where 2.17 - * the move that could be performed to capture the field should be stored 2.18 + * @param threats the array where to store the threats (should be able to the 2.19 + * rare maximum of 16 elements) 2.20 + * @param threatcount a pointer to an uint8_t where to store the amount of threats 2.21 * @return TRUE, if any piece of the specified color threatens the specified 2.22 * field (i.e. could capture an opponent piece) 2.23 */ 2.24 -_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, 2.25 - uint8_t color, Move *threat); 2.26 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 2.27 + uint8_t color, Move* threats, uint8_t* threatcount); 2.28 + 2.29 +/** 2.30 + * Checks, if a specified field is covered by a piece of a certain color AND 2.31 + * if this piece is not pinned and therefore able to perform the move. 2.32 + * 2.33 + * The out-parameters may both be NULL, but if any of them is set, the other 2.34 + * must be set, too. 2.35 + * 2.36 + * @param gamestate the current game state 2.37 + * @param row row of the field to check 2.38 + * @param file file of the field to check 2.39 + * @param color the color of the piece that should threaten the field 2.40 + * @param threats the array where to store the threats (should be able to the 2.41 + * rare maximum of 16 elements) 2.42 + * @param threatcount a pointer to an uint8_t where to store the amount of threats 2.43 + * @return TRUE, if any piece of the specified color threatens the specified 2.44 + * field (i.e. could capture an opponent piece) 2.45 + */ 2.46 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 2.47 + uint8_t color, Move* threats, uint8_t* threatcount); 2.48 2.49 /** 2.50 * Checks, if a specified field is covered by a piece of a certain color. 2.51 @@ -168,10 +189,42 @@ 2.52 * @param file file of the field to check 2.53 * @param color the color of the piece that should cover the field 2.54 * @return TRUE, if any piece of the specified color threatens the specified 2.55 - * field (i.e. could capture an opponent piece) 2.56 + * field 2.57 */ 2.58 #define is_covered(gamestate, row, file, color) \ 2.59 - get_any_threat_for(gamestate, row, file, color, NULL) 2.60 + get_threats(gamestate, row, file, color, NULL, NULL) 2.61 + 2.62 +/** 2.63 + * Checks, if a specified field is attacked by a piece of a certain color. 2.64 + * 2.65 + * I.e. the field is covered by a piece AND this piece is not pinned and 2.66 + * therefore able to perform the move. 2.67 + * 2.68 + * @param gamestate the current game state 2.69 + * @param row row of the field to check 2.70 + * @param file file of the field to check 2.71 + * @param color the color of the piece that should cover the field 2.72 + * @return TRUE, if any piece of the specified color threatens the specified 2.73 + * field and could capture an opponent piece 2.74 + */ 2.75 +#define is_attacked(gamestate, row, file, color) \ 2.76 + get_threats(gamestate, row, file, color, NULL, NULL) 2.77 + 2.78 +/** 2.79 + * Checks, if a specified field is protected by a piece of a certain color. 2.80 + * 2.81 + * I.e. the field is covered by a piece that is NOT the king AND this piece is 2.82 + * not pinned and therefore able to perform the move. 2.83 + * 2.84 + * @param gamestate the current game state 2.85 + * @param row row of the field to check 2.86 + * @param file file of the field to check 2.87 + * @param color the color of the piece that should cover the field 2.88 + * @return TRUE, if any piece (excluding the king) of the specified color 2.89 + * threatens the specified field and could capture an opponent piece 2.90 + */ 2.91 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 2.92 + uint8_t color); 2.93 2.94 /** 2.95 * Evaluates a move syntactically and stores the move data in the specified
3.1 --- a/src/main.c Fri Apr 04 17:36:42 2014 +0200 3.2 +++ b/src/main.c Mon Apr 07 14:08:57 2014 +0200 3.3 @@ -164,6 +164,7 @@ 3.4 } 3.5 initscr(); 3.6 cbreak(); 3.7 + keypad(stdscr, TRUE); 3.8 if (has_colors()) { 3.9 start_color(); 3.10 init_colorpairs();