1.1 --- a/src/chess/rules.c Thu Apr 17 12:16:14 2014 +0200 1.2 +++ b/src/chess/rules.c Wed May 28 15:47:57 2014 +0200 1.3 @@ -98,19 +98,6 @@ 1.4 } 1.5 } 1.6 1.7 -static int getlocation(GameState *gamestate, Move *move) { 1.8 - uint8_t piece = move->piece & PIECE_MASK; 1.9 - switch (piece) { 1.10 - case PAWN: return pawn_getlocation(gamestate, move); 1.11 - case ROOK: return rook_getlocation(gamestate, move); 1.12 - case KNIGHT: return knight_getlocation(gamestate, move); 1.13 - case BISHOP: return bishop_getlocation(gamestate, move); 1.14 - case QUEEN: return queen_getlocation(gamestate, move); 1.15 - case KING: return king_getlocation(gamestate, move); 1.16 - default: return INVALID_MOVE_SYNTAX; 1.17 - } 1.18 -} 1.19 - 1.20 void apply_move(GameState *gamestate, Move *move) { 1.21 uint8_t piece = move->piece & PIECE_MASK; 1.22 uint8_t color = move->piece & COLOR_MASK; 1.23 @@ -170,13 +157,20 @@ 1.24 } 1.25 1.26 /* does piece exist */ 1.27 - if (msrc(gamestate->board, move) != move->piece) { 1.28 + if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 1.29 + != (move->piece&(PIECE_MASK|COLOR_MASK))) { 1.30 return 0; 1.31 } 1.32 1.33 /* can't capture own pieces */ 1.34 if ((mdst(gamestate->board, move) & COLOR_MASK) 1.35 - == (move->piece & COLOR_MASK)) { 1.36 + == (move->piece & COLOR_MASK)) { 1.37 + return 0; 1.38 + } 1.39 + 1.40 + /* must capture, if and only if destination is occupied */ 1.41 + if ((mdst(gamestate->board, move) == 0 && move->capture) || 1.42 + (mdst(gamestate->board, move) != 0 && !move->capture)) { 1.43 return 0; 1.44 } 1.45 1.46 @@ -205,6 +199,7 @@ 1.47 } 1.48 1.49 _Bool validate_move(GameState *gamestate, Move *move) { 1.50 + // TODO: provide more details via a return code 1.51 1.52 _Bool result = validate_move_rules(gamestate, move); 1.53 1.54 @@ -232,9 +227,9 @@ 1.55 } 1.56 1.57 /* simulation move for check validation */ 1.58 - GameState simulation; 1.59 - memcpy(&simulation, gamestate, sizeof(GameState)); 1.60 - apply_move(&simulation, move); 1.61 + GameState simulation = *gamestate; 1.62 + Move simmove = *move; 1.63 + apply_move(&simulation, &simmove); 1.64 1.65 /* don't move into or stay in check position */ 1.66 if (is_covered(&simulation, mykingrow, mykingfile, 1.67 @@ -323,6 +318,155 @@ 1.68 return 1; 1.69 } 1.70 1.71 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.72 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.73 + Move candidates[32]; 1.74 + int candidatecount = 0; 1.75 + for (uint8_t r = 0 ; r < 8 ; r++) { 1.76 + for (uint8_t f = 0 ; f < 8 ; f++) { 1.77 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.78 + // non-capturing move 1.79 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 1.80 + candidates[candidatecount].piece = gamestate->board[r][f]; 1.81 + candidates[candidatecount].fromrow = r; 1.82 + candidates[candidatecount].fromfile = f; 1.83 + candidates[candidatecount].torow = row; 1.84 + candidates[candidatecount].tofile = file; 1.85 + candidatecount++; 1.86 + 1.87 + // capturing move 1.88 + memcpy(&(candidates[candidatecount]), 1.89 + &(candidates[candidatecount-1]), sizeof(Move)); 1.90 + candidates[candidatecount].capture = 1; 1.91 + candidatecount++; 1.92 + } 1.93 + } 1.94 + } 1.95 + 1.96 + if (threatcount) { 1.97 + *threatcount = 0; 1.98 + } 1.99 + 1.100 + 1.101 + _Bool result = 0; 1.102 + 1.103 + for (int i = 0 ; i < candidatecount ; i++) { 1.104 + if (validate_move_rules(gamestate, &(candidates[i]))) { 1.105 + result = 1; 1.106 + if (threats && threatcount) { 1.107 + threats[(*threatcount)++] = candidates[i]; 1.108 + } 1.109 + } 1.110 + } 1.111 + 1.112 + return result; 1.113 +} 1.114 + 1.115 +_Bool is_pinned(GameState *gamestate, Move *move) { 1.116 + uint8_t color = move->piece & COLOR_MASK; 1.117 + 1.118 + uint8_t kingfile = 0, kingrow = 0; 1.119 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.120 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.121 + if (gamestate->board[row][file] == (color|KING)) { 1.122 + kingfile = file; 1.123 + kingrow = row; 1.124 + } 1.125 + } 1.126 + } 1.127 + 1.128 + GameState simulation = *gamestate; 1.129 + Move simmove = *move; 1.130 + apply_move(&simulation, &simmove); 1.131 + return is_covered(&simulation, kingrow, kingfile, opponent_color(color)); 1.132 +} 1.133 + 1.134 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.135 + uint8_t color, Move *threats, uint8_t *threatcount) { 1.136 + 1.137 + if (threatcount) { 1.138 + *threatcount = 0; 1.139 + } 1.140 + 1.141 + Move candidates[16]; 1.142 + uint8_t candidatecount; 1.143 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 1.144 + 1.145 + _Bool result = 0; 1.146 + uint8_t kingfile = 0, kingrow = 0; 1.147 + for (uint8_t row = 0 ; row < 8 ; row++) { 1.148 + for (uint8_t file = 0 ; file < 8 ; file++) { 1.149 + if (gamestate->board[row][file] == (color|KING)) { 1.150 + kingfile = file; 1.151 + kingrow = row; 1.152 + } 1.153 + } 1.154 + } 1.155 + 1.156 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 1.157 + GameState simulation = *gamestate; 1.158 + Move simmove = candidates[i]; 1.159 + apply_move(&simulation, &simmove); 1.160 + if (!is_covered(&simulation, kingrow, kingfile, 1.161 + opponent_color(color))) { 1.162 + result = 1; 1.163 + if (threats && threatcount) { 1.164 + threats[(*threatcount)++] = candidates[i]; 1.165 + } 1.166 + } 1.167 + } 1.168 + 1.169 + return result; 1.170 + } else { 1.171 + return 0; 1.172 + } 1.173 +} 1.174 +#include <ncurses.h> 1.175 +static int getlocation(GameState *gamestate, Move *move) { 1.176 + 1.177 + uint8_t color = move->piece & COLOR_MASK; 1.178 + _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 1.179 + 1.180 + Move threats[16], *threat = NULL; 1.181 + uint8_t threatcount; 1.182 + 1.183 + if (get_threats(gamestate, move->torow, move->tofile, color, 1.184 + threats, &threatcount)) { 1.185 + 1.186 + // find threats for the specified position 1.187 + for (uint8_t i = 0 ; i < threatcount ; i++) { 1.188 + if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 1.189 + == move->piece && 1.190 + (move->fromrow == POS_UNSPECIFIED || 1.191 + move->fromrow == threats[i].fromrow) && 1.192 + (move->fromfile == POS_UNSPECIFIED || 1.193 + move->fromfile == threats[i].fromfile)) { 1.194 + 1.195 + if (threat) { 1.196 + return AMBIGUOUS_MOVE; 1.197 + } else { 1.198 + threat = &(threats[i]); 1.199 + } 1.200 + } 1.201 + } 1.202 + 1.203 + // can't threaten specified position 1.204 + if (!threat) { 1.205 + return INVALID_POSITION; 1.206 + } 1.207 + 1.208 + // found threat is no real threat 1.209 + if (is_pinned(gamestate, threat)) { 1.210 + return incheck?KING_IN_CHECK:PIECE_PINNED; 1.211 + } else { 1.212 + memcpy(move, threat, sizeof(Move)); 1.213 + return VALID_MOVE_SYNTAX; 1.214 + } 1.215 + } else { 1.216 + return INVALID_POSITION; 1.217 + } 1.218 +} 1.219 + 1.220 int eval_move(GameState *gamestate, char *mstr, Move *move) { 1.221 memset(move, 0, sizeof(Move)); 1.222 move->fromfile = POS_UNSPECIFIED; 1.223 @@ -452,83 +596,6 @@ 1.224 } 1.225 } 1.226 1.227 -_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.228 - uint8_t color, Move *threats, uint8_t *threatcount) { 1.229 - Move candidates[16]; 1.230 - int candidatecount = 0; 1.231 - for (uint8_t r = 0 ; r < 8 ; r++) { 1.232 - for (uint8_t f = 0 ; f < 8 ; f++) { 1.233 - if ((gamestate->board[r][f] & COLOR_MASK) == color) { 1.234 - memset(&(candidates[candidatecount]), 0, sizeof(Move)); 1.235 - candidates[candidatecount].piece = gamestate->board[r][f]; 1.236 - candidates[candidatecount].fromrow = r; 1.237 - candidates[candidatecount].fromfile = f; 1.238 - candidates[candidatecount].torow = row; 1.239 - candidates[candidatecount].tofile = file; 1.240 - candidatecount++; 1.241 - } 1.242 - } 1.243 - } 1.244 - 1.245 - if (threatcount) { 1.246 - *threatcount = 0; 1.247 - } 1.248 - 1.249 - 1.250 - _Bool result = 0; 1.251 - 1.252 - for (int i = 0 ; i < candidatecount ; i++) { 1.253 - if (validate_move_rules(gamestate, &(candidates[i]))) { 1.254 - result = 1; 1.255 - if (threats && threatcount) { 1.256 - threats[(*threatcount)++] = candidates[i]; 1.257 - } 1.258 - } 1.259 - } 1.260 - 1.261 - return result; 1.262 -} 1.263 - 1.264 -_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 1.265 - uint8_t color, Move *threats, uint8_t *threatcount) { 1.266 - 1.267 - Move candidates[16]; 1.268 - uint8_t candidatecount; 1.269 - if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 1.270 - 1.271 - if (threatcount) { 1.272 - *threatcount = 0; 1.273 - } 1.274 - _Bool result = 0; 1.275 - uint8_t kingfile = 0, kingrow = 0; 1.276 - for (uint8_t row = 0 ; row < 8 ; row++) { 1.277 - for (uint8_t file = 0 ; file < 8 ; file++) { 1.278 - if ((gamestate->board[row][file] & COLOR_MASK) == color) { 1.279 - kingfile = file; 1.280 - kingrow = row; 1.281 - } 1.282 - } 1.283 - } 1.284 - 1.285 - for (uint8_t i = 0 ; i < candidatecount ; i++) { 1.286 - GameState simulation; 1.287 - memcpy(&simulation, gamestate, sizeof(GameState)); 1.288 - apply_move(&simulation, &(candidates[i])); 1.289 - if (!is_covered(&simulation, kingrow, kingfile, 1.290 - opponent_color(color))) { 1.291 - result = 1; 1.292 - if (threats && threatcount) { 1.293 - threats[(*threatcount)++] = candidates[i]; 1.294 - } 1.295 - } 1.296 - } 1.297 - 1.298 - return result; 1.299 - } else { 1.300 - return 0; 1.301 - } 1.302 -} 1.303 - 1.304 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 1.305 uint8_t color) { 1.306