88 case KING: return king_getlocation(gamestate, move); |
88 case KING: return king_getlocation(gamestate, move); |
89 default: return INVALID_MOVE_SYNTAX; |
89 default: return INVALID_MOVE_SYNTAX; |
90 } |
90 } |
91 } |
91 } |
92 |
92 |
93 _Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) { |
93 _Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, |
|
94 uint8_t color, Move *threat) { |
94 Move threats[16]; |
95 Move threats[16]; |
95 int threatcount = 0; |
96 int threatcount = 0; |
96 for (uint8_t r = 0 ; r < 8 ; r++) { |
97 for (uint8_t r = 0 ; r < 8 ; r++) { |
97 for (uint8_t f = 0 ; f < 8 ; f++) { |
98 for (uint8_t f = 0 ; f < 8 ; f++) { |
98 if ((gamestate->board[r][f] & COLOR_MASK) == color) { |
99 if ((gamestate->board[r][f] & COLOR_MASK) == color) { |
|
100 memset(&(threats[threatcount]), 0, sizeof(Move)); |
99 threats[threatcount].piece = gamestate->board[r][f]; |
101 threats[threatcount].piece = gamestate->board[r][f]; |
100 threats[threatcount].fromrow = r; |
102 threats[threatcount].fromrow = r; |
101 threats[threatcount].fromfile = f; |
103 threats[threatcount].fromfile = f; |
102 threats[threatcount].torow = row; |
104 threats[threatcount].torow = row; |
103 threats[threatcount].tofile = file; |
105 threats[threatcount].tofile = file; |
156 } else if (move->tofile == fileidx('c')) { |
161 } else if (move->tofile == fileidx('c')) { |
157 gamestate->board[move->torow][fileidx('a')] = 0; |
162 gamestate->board[move->torow][fileidx('a')] = 0; |
158 gamestate->board[move->torow][fileidx('d')] = color|ROOK; |
163 gamestate->board[move->torow][fileidx('d')] = color|ROOK; |
159 } |
164 } |
160 } |
165 } |
161 |
166 |
162 addmove(gamestate, move); |
167 addmove(gamestate, move); |
163 } |
168 } |
164 |
169 |
165 _Bool validate_move(GameState *gamestate, Move *move) { |
170 _Bool validate_move(GameState *gamestate, Move *move) { |
166 _Bool result; |
171 _Bool result; |
177 |
182 |
178 /* does piece exist */ |
183 /* does piece exist */ |
179 result = msrc(gamestate->board, move) == move->piece; |
184 result = msrc(gamestate->board, move) == move->piece; |
180 |
185 |
181 /* can't capture own pieces */ |
186 /* can't capture own pieces */ |
182 if ((mdst(gamestate->board, move) & COLOR_MASK) |
187 uint8_t piececolor = (move->piece & COLOR_MASK); |
183 == (move->piece & COLOR_MASK)) { |
188 if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { |
184 return 0; |
189 return 0; |
185 } |
190 } |
186 |
191 |
187 /* validate individual rules */ |
192 /* validate individual rules */ |
188 switch (move->piece & PIECE_MASK) { |
193 switch (move->piece & PIECE_MASK) { |
217 /* cancel processing to avoid recursion overflow with is_covered() */ |
222 /* cancel processing to avoid recursion overflow with is_covered() */ |
218 if (!result) { |
223 if (!result) { |
219 return 0; |
224 return 0; |
220 } |
225 } |
221 |
226 |
222 /* is piece pinned */ |
227 /* find kings for check validation */ |
223 // TODO: make it so |
228 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; |
|
229 for (uint8_t row = 0 ; row < 8 ; row++) { |
|
230 for (uint8_t file = 0 ; file < 8 ; file++) { |
|
231 if (gamestate->board[row][file] == |
|
232 (piececolor == WHITE?WKING:BKING)) { |
|
233 mykingfile = file; |
|
234 mykingrow = row; |
|
235 } else if (gamestate->board[row][file] == |
|
236 (piececolor == WHITE?BKING:WKING)) { |
|
237 opkingfile = file; |
|
238 opkingrow = row; |
|
239 } |
|
240 } |
|
241 } |
|
242 |
|
243 /* simulation move for check validation */ |
|
244 GameState simulation; |
|
245 memcpy(&simulation, gamestate, sizeof(GameState)); |
|
246 apply_move(&simulation, move); |
|
247 |
|
248 /* don't move into or stay in check position */ |
|
249 if (is_covered(&simulation, mykingrow, mykingfile, |
|
250 opponent_color(piececolor))) { |
|
251 return 0; |
|
252 } |
224 |
253 |
225 /* correct check and checkmate flags (move is still valid) */ |
254 /* correct check and checkmate flags (move is still valid) */ |
226 // TODO: make it so |
255 Move threat; |
227 |
256 move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, |
228 return result; |
257 piececolor, &threat); |
|
258 |
|
259 if (move->check) { |
|
260 /* determine possible escape fields */ |
|
261 _Bool canescape = 0; |
|
262 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { |
|
263 for (int df = -1 ; df <= 1 && !canescape ; df++) { |
|
264 if (!(dr == 0 && df == 0) && |
|
265 isidx(opkingrow + dr) && isidx(opkingfile + df)) { |
|
266 |
|
267 /* escape field neither blocked nor covered */ |
|
268 if ((simulation.board[opkingrow + dr][opkingfile + df] |
|
269 & COLOR_MASK) != opponent_color(piececolor)) { |
|
270 canescape |= !is_covered(&simulation, |
|
271 opkingrow + dr, opkingfile + df, piececolor); |
|
272 } |
|
273 } |
|
274 } |
|
275 } |
|
276 /* can't escape, can we capture? */ |
|
277 if (!canescape) { |
|
278 canescape = is_covered(&simulation, threat.fromrow, |
|
279 threat.fromfile, opponent_color(piececolor)); |
|
280 |
|
281 /* can't capture, can we block? */ |
|
282 // TODO: make it so |
|
283 |
|
284 if (!canescape) { |
|
285 gamestate->checkmate = 1; |
|
286 } |
|
287 } |
|
288 } |
|
289 |
|
290 return 1; |
229 } |
291 } |
230 |
292 |
231 int eval_move(GameState *gamestate, char *mstr, Move *move) { |
293 int eval_move(GameState *gamestate, char *mstr, Move *move) { |
232 memset(move, 0, sizeof(Move)); |
294 memset(move, 0, sizeof(Move)); |
233 move->fromfile = POS_UNSPECIFIED; |
295 move->fromfile = POS_UNSPECIFIED; |