| 262 case 'K': return KING; |
262 case 'K': return KING; |
| 263 default: return 0; |
263 default: return 0; |
| 264 } |
264 } |
| 265 } |
265 } |
| 266 |
266 |
| |
267 // TODO: check if we really need this "simulate" flag for performance |
| 267 static void apply_move_impl(GameState *gamestate, Move *move, bool simulate) { |
268 static void apply_move_impl(GameState *gamestate, Move *move, bool simulate) { |
| 268 /* format move before moving (s.t. ambiguities can be resolved) */ |
269 /* format move before moving (s.t. ambiguities can be resolved) */ |
| 269 if (!simulate) { |
270 if (!simulate) { |
| 270 if (!move->string[0]) { |
271 if (!move->string[0]) { |
| 271 format_move(gamestate, move); |
272 format_move(gamestate, move); |
| 450 if (move->check) { |
451 if (move->check) { |
| 451 /* determine possible escape fields */ |
452 /* determine possible escape fields */ |
| 452 bool canescape = false; |
453 bool canescape = false; |
| 453 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { |
454 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { |
| 454 for (int df = -1 ; df <= 1 && !canescape ; df++) { |
455 for (int df = -1 ; df <= 1 && !canescape ; df++) { |
| 455 if (!(dr == 0 && df == 0) && |
456 if (dr == 0 && df == 0) continue; |
| 456 isidx(opkingrow + dr) && isidx(opkingfile + df)) { |
457 if (!isidx(opkingrow + dr)) continue; |
| 457 /* escape field neither occupied nor covered */ |
458 if (!isidx(opkingfile + df)) continue; |
| 458 canescape = |
459 uint8_t er = opkingrow + dr; |
| 459 !simulation.board[opkingrow + dr][opkingfile + df] && |
460 uint8_t ef = opkingfile + df; |
| 460 !is_covered(&simulation, |
461 |
| 461 opkingrow + dr, opkingfile + df, piececolor); |
462 /* check if escape field is already covered (threatened) */ |
| |
463 if (is_covered(&simulation, er, ef, piececolor)) |
| |
464 continue; |
| |
465 |
| |
466 /* check if piece of the king's color blocks the field */ |
| |
467 if ((simulation.board[er][ef] & COLOR_MASK) == oppcolor) |
| |
468 continue; |
| |
469 |
| |
470 /* check if an attacking piece blocks the field */ |
| |
471 if ((simulation.board[er][ef] & COLOR_MASK) == piececolor) { |
| |
472 /* test if the king can fight back */ |
| |
473 GameState sim_retaliate = gamestate_copy_sim(&simulation); |
| |
474 Move move_retaliate = {0}; |
| |
475 move_retaliate.piece = (oppcolor|KING); |
| |
476 move_retaliate.fromrow = opkingrow; |
| |
477 move_retaliate.fromfile = opkingfile; |
| |
478 move_retaliate.torow = er; |
| |
479 move_retaliate.tofile = ef; |
| |
480 move_retaliate.capture = 1; |
| |
481 apply_move_impl(&sim_retaliate, &move_retaliate, true); |
| |
482 canescape = !is_covered(&sim_retaliate, er, ef, piececolor); |
| |
483 gamestate_cleanup(&sim_retaliate); |
| |
484 } else { |
| |
485 /* the field is not covered and unoccupied */ |
| |
486 canescape = true; |
| 462 } |
487 } |
| 463 } |
488 } |
| 464 } |
489 } |
| 465 /* can't escape, can he capture? */ |
490 |
| |
491 /* can't escape, can the king be rescued? */ |
| 466 if (!canescape && threatcount == 1) { |
492 if (!canescape && threatcount == 1) { |
| 467 /* TODO: this is bugged - we actually need distinguish two cases |
493 canescape = is_protected(&simulation, |
| 468 * 1. another piece can rescue the king |
|
| 469 * (this is what is implemented now) |
|
| 470 * 2. the king can kill the attacker |
|
| 471 * - in this case, the threatcount is irrelevant, because |
|
| 472 * the king moves out of the current threat |
|
| 473 * - we must verify that the king can actually kill the |
|
| 474 * attacker without running into a new check |
|
| 475 */ |
|
| 476 canescape = is_attacked(&simulation, |
|
| 477 threats[0].fromrow, threats[0].fromfile, oppcolor); |
494 threats[0].fromrow, threats[0].fromfile, oppcolor); |
| 478 } |
495 } |
| 479 |
496 |
| 480 /* can't capture, can he block? */ |
497 /* can't capture, can he block? */ |
| 481 if (!canescape && threatcount == 1) { |
498 if (!canescape && threatcount == 1) { |