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 get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file, |
|
94 uint8_t color, Move *threat) { |
|
95 Move threats[16]; |
|
96 int threatcount = 0; |
|
97 for (uint8_t r = 0 ; r < 8 ; r++) { |
|
98 for (uint8_t f = 0 ; f < 8 ; f++) { |
|
99 if ((gamestate->board[r][f] & COLOR_MASK) == color) { |
|
100 memset(&(threats[threatcount]), 0, sizeof(Move)); |
|
101 threats[threatcount].piece = gamestate->board[r][f]; |
|
102 threats[threatcount].fromrow = r; |
|
103 threats[threatcount].fromfile = f; |
|
104 threats[threatcount].torow = row; |
|
105 threats[threatcount].tofile = file; |
|
106 threatcount++; |
|
107 } |
|
108 } |
|
109 } |
|
110 |
|
111 for (int i = 0 ; i < threatcount ; i++) { |
|
112 if (validate_move(gamestate, &(threats[i]))) { |
|
113 if (threat) { |
|
114 *threat = threats[i]; |
|
115 } |
|
116 return 1; |
|
117 } |
|
118 } |
|
119 |
|
120 return 0; |
|
121 } |
|
122 |
|
123 void apply_move(GameState *gamestate, Move *move) { |
93 void apply_move(GameState *gamestate, Move *move) { |
124 uint8_t piece = move->piece & PIECE_MASK; |
94 uint8_t piece = move->piece & PIECE_MASK; |
125 uint8_t color = move->piece & COLOR_MASK; |
95 uint8_t color = move->piece & COLOR_MASK; |
126 |
96 |
127 /* en passant capture */ |
97 /* en passant capture */ |
179 if (move->fromfile == move->tofile && move->fromrow == move->torow) { |
147 if (move->fromfile == move->tofile && move->fromrow == move->torow) { |
180 return 0; |
148 return 0; |
181 } |
149 } |
182 |
150 |
183 /* does piece exist */ |
151 /* does piece exist */ |
184 result = msrc(gamestate->board, move) == move->piece; |
152 if (msrc(gamestate->board, move) != move->piece) { |
|
153 return 0; |
|
154 } |
185 |
155 |
186 /* can't capture own pieces */ |
156 /* can't capture own pieces */ |
187 uint8_t piececolor = (move->piece & COLOR_MASK); |
157 if ((mdst(gamestate->board, move) & COLOR_MASK) |
188 if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) { |
158 == (move->piece & COLOR_MASK)) { |
189 return 0; |
159 return 0; |
190 } |
160 } |
191 |
161 |
192 /* validate individual rules */ |
162 /* validate individual rules */ |
193 switch (move->piece & PIECE_MASK) { |
163 switch (move->piece & PIECE_MASK) { |
194 case PAWN: |
164 case PAWN: |
195 result = result && pawn_chkrules(gamestate, move); |
165 return pawn_chkrules(gamestate, move) && |
196 result = result && !pawn_isblocked(gamestate, move); |
166 !pawn_isblocked(gamestate, move); |
197 break; |
|
198 case ROOK: |
167 case ROOK: |
199 result = result && rook_chkrules(move); |
168 return rook_chkrules(move) && |
200 result = result && !rook_isblocked(gamestate, move); |
169 !rook_isblocked(gamestate, move); |
201 break; |
|
202 case KNIGHT: |
170 case KNIGHT: |
203 result = result && knight_chkrules(move); |
171 return knight_chkrules(move); /* knight is never blocked */ |
204 result = result && !knight_isblocked(gamestate, move); |
|
205 break; |
|
206 case BISHOP: |
172 case BISHOP: |
207 result = result && bishop_chkrules(move); |
173 return bishop_chkrules(move) && |
208 result = result && !bishop_isblocked(gamestate, move); |
174 !bishop_isblocked(gamestate, move); |
209 break; |
|
210 case QUEEN: |
175 case QUEEN: |
211 result = result && queen_chkrules(move); |
176 return queen_chkrules(move) && |
212 result = result && !queen_isblocked(gamestate, move); |
177 !queen_isblocked(gamestate, move); |
213 break; |
|
214 case KING: |
178 case KING: |
215 result = result && king_chkrules(gamestate, move); |
179 return king_chkrules(gamestate, move) && |
216 result = result && !king_isblocked(gamestate, move); |
180 !king_isblocked(gamestate, move); |
217 break; |
|
218 default: |
181 default: |
219 result = 0; |
182 return 0; |
220 } |
183 } |
221 |
184 } |
222 /* cancel processing to avoid recursion overflow with is_covered() */ |
185 |
|
186 _Bool validate_move(GameState *gamestate, Move *move) { |
|
187 |
|
188 _Bool result = validate_move_rules(gamestate, move); |
|
189 |
|
190 /* cancel processing to save resources */ |
223 if (!result) { |
191 if (!result) { |
224 return 0; |
192 return 0; |
225 } |
193 } |
226 |
194 |
227 /* find kings for check validation */ |
195 /* find kings for check validation */ |
|
196 uint8_t piececolor = (move->piece & COLOR_MASK); |
|
197 |
228 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; |
198 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; |
229 for (uint8_t row = 0 ; row < 8 ; row++) { |
199 for (uint8_t row = 0 ; row < 8 ; row++) { |
230 for (uint8_t file = 0 ; file < 8 ; file++) { |
200 for (uint8_t file = 0 ; file < 8 ; file++) { |
231 if (gamestate->board[row][file] == |
201 if (gamestate->board[row][file] == |
232 (piececolor == WHITE?WKING:BKING)) { |
202 (piececolor == WHITE?WKING:BKING)) { |
250 opponent_color(piececolor))) { |
220 opponent_color(piececolor))) { |
251 return 0; |
221 return 0; |
252 } |
222 } |
253 |
223 |
254 /* correct check and checkmate flags (move is still valid) */ |
224 /* correct check and checkmate flags (move is still valid) */ |
255 Move threat; |
225 Move threats[16]; |
256 move->check = get_any_threat_for(&simulation, opkingrow, opkingfile, |
226 uint8_t threatcount; |
257 piececolor, &threat); |
227 move->check = get_threats(&simulation, opkingrow, opkingfile, |
|
228 piececolor, threats, &threatcount); |
258 |
229 |
259 if (move->check) { |
230 if (move->check) { |
260 /* determine possible escape fields */ |
231 /* determine possible escape fields */ |
261 _Bool canescape = 0; |
232 _Bool canescape = 0; |
262 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { |
233 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { |
271 opkingrow + dr, opkingfile + df, piececolor); |
242 opkingrow + dr, opkingfile + df, piececolor); |
272 } |
243 } |
273 } |
244 } |
274 } |
245 } |
275 } |
246 } |
276 /* can't escape, can we capture? */ |
247 /* can't escape, can he capture? */ |
|
248 if (!canescape && threatcount == 1) { |
|
249 canescape = is_attacked(&simulation, threats[0].fromrow, |
|
250 threats[0].fromfile, opponent_color(piececolor)); |
|
251 } |
|
252 |
|
253 /* can't capture, can he block? */ |
|
254 if (!canescape && threatcount == 1) { |
|
255 Move *threat = &(threats[0]); |
|
256 uint8_t threatpiece = threat->piece & PIECE_MASK; |
|
257 |
|
258 /* knight, pawns and the king cannot be blocked */ |
|
259 if (threatpiece == BISHOP || threatpiece == ROOK |
|
260 || threatpiece == QUEEN) { |
|
261 if (threat->fromrow == threat->torow) { |
|
262 /* rook aspect (on row) */ |
|
263 int d = threat->tofile > threat->fromfile ? 1 : -1; |
|
264 uint8_t file = threat->fromfile; |
|
265 while (!canescape && file != threat->tofile - d) { |
|
266 file += d; |
|
267 canescape |= is_protected(&simulation, |
|
268 threat->torow, file, opponent_color(piececolor)); |
|
269 } |
|
270 } else if (threat->fromfile == threat->tofile) { |
|
271 /* rook aspect (on file) */ |
|
272 int d = threat->torow > threat->fromrow ? 1 : -1; |
|
273 uint8_t row = threat->fromrow; |
|
274 while (!canescape && row != threat->torow - d) { |
|
275 row += d; |
|
276 canescape |= is_protected(&simulation, |
|
277 row, threat->tofile, opponent_color(piececolor)); |
|
278 } |
|
279 } else { |
|
280 /* bishop aspect */ |
|
281 int dr = move->torow > move->fromrow ? 1 : -1; |
|
282 int df = move->tofile > move->fromfile ? 1 : -1; |
|
283 |
|
284 uint8_t row = move->fromrow; |
|
285 uint8_t file = move->fromfile; |
|
286 while (!canescape && file != move->tofile - df |
|
287 && row != move->torow - dr) { |
|
288 row += dr; |
|
289 file += df; |
|
290 canescape |= is_protected(&simulation, row, file, |
|
291 opponent_color(piececolor)); |
|
292 } |
|
293 } |
|
294 } |
|
295 } |
|
296 |
277 if (!canescape) { |
297 if (!canescape) { |
278 canescape = is_covered(&simulation, threat.fromrow, |
298 gamestate->checkmate = 1; |
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 } |
299 } |
288 } |
300 } |
289 |
301 |
290 return 1; |
302 return 1; |
291 } |
303 } |
416 } |
428 } |
417 } else { |
429 } else { |
418 return INVALID_MOVE_SYNTAX; |
430 return INVALID_MOVE_SYNTAX; |
419 } |
431 } |
420 } |
432 } |
|
433 |
|
434 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, |
|
435 uint8_t color, Move *threats, uint8_t *threatcount) { |
|
436 Move candidates[16]; |
|
437 int candidatecount = 0; |
|
438 for (uint8_t r = 0 ; r < 8 ; r++) { |
|
439 for (uint8_t f = 0 ; f < 8 ; f++) { |
|
440 if ((gamestate->board[r][f] & COLOR_MASK) == color) { |
|
441 memset(&(candidates[candidatecount]), 0, sizeof(Move)); |
|
442 candidates[candidatecount].piece = gamestate->board[r][f]; |
|
443 candidates[candidatecount].fromrow = r; |
|
444 candidates[candidatecount].fromfile = f; |
|
445 candidates[candidatecount].torow = row; |
|
446 candidates[candidatecount].tofile = file; |
|
447 candidatecount++; |
|
448 } |
|
449 } |
|
450 } |
|
451 |
|
452 if (threatcount) { |
|
453 *threatcount = 0; |
|
454 } |
|
455 |
|
456 |
|
457 _Bool result = 0; |
|
458 |
|
459 for (int i = 0 ; i < candidatecount ; i++) { |
|
460 if (validate_move_rules(gamestate, &(candidates[i]))) { |
|
461 result = 1; |
|
462 if (threats && threatcount) { |
|
463 threats[(*threatcount)++] = candidates[i]; |
|
464 } |
|
465 } |
|
466 } |
|
467 |
|
468 return result; |
|
469 } |
|
470 |
|
471 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, |
|
472 uint8_t color, Move *threats, uint8_t *threatcount) { |
|
473 |
|
474 Move candidates[16]; |
|
475 uint8_t candidatecount; |
|
476 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { |
|
477 |
|
478 if (threatcount) { |
|
479 *threatcount = 0; |
|
480 } |
|
481 _Bool result = 0; |
|
482 uint8_t kingfile = 0, kingrow = 0; |
|
483 for (uint8_t row = 0 ; row < 8 ; row++) { |
|
484 for (uint8_t file = 0 ; file < 8 ; file++) { |
|
485 if ((gamestate->board[row][file] & COLOR_MASK) == color) { |
|
486 kingfile = file; |
|
487 kingrow = row; |
|
488 } |
|
489 } |
|
490 } |
|
491 |
|
492 for (uint8_t i = 0 ; i < candidatecount ; i++) { |
|
493 GameState simulation; |
|
494 memcpy(&simulation, gamestate, sizeof(GameState)); |
|
495 apply_move(&simulation, &(candidates[i])); |
|
496 if (!is_covered(&simulation, kingrow, kingfile, |
|
497 opponent_color(color))) { |
|
498 result = 1; |
|
499 if (threats && threatcount) { |
|
500 threats[(*threatcount)++] = candidates[i]; |
|
501 } |
|
502 } |
|
503 } |
|
504 |
|
505 return result; |
|
506 } else { |
|
507 return 0; |
|
508 } |
|
509 } |
|
510 |
|
511 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, |
|
512 uint8_t color) { |
|
513 |
|
514 Move threats[16]; |
|
515 uint8_t threatcount; |
|
516 if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { |
|
517 for (int i = 0 ; i < threatcount ; i++) { |
|
518 if (threats[i].piece != (color|KING)) { |
|
519 return 1; |
|
520 } |
|
521 } |
|
522 return 0; |
|
523 } else { |
|
524 return 0; |
|
525 } |
|
526 } |