Sat, 13 Jun 2026 13:45:19 +0200
fix checkmate detection
issue #895 and issue #896
| src/chess/rules.c | file | annotate | diff | comparison | revisions |
--- a/src/chess/rules.c Sat Jun 13 13:24:54 2026 +0200 +++ b/src/chess/rules.c Sat Jun 13 13:45:19 2026 +0200 @@ -264,6 +264,7 @@ } } +// TODO: check if we really need this "simulate" flag for performance static void apply_move_impl(GameState *gamestate, Move *move, bool simulate) { /* format move before moving (s.t. ambiguities can be resolved) */ if (!simulate) { @@ -452,28 +453,44 @@ bool canescape = false; for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { for (int df = -1 ; df <= 1 && !canescape ; df++) { - if (!(dr == 0 && df == 0) && - isidx(opkingrow + dr) && isidx(opkingfile + df)) { - /* escape field neither occupied nor covered */ - canescape = - !simulation.board[opkingrow + dr][opkingfile + df] && - !is_covered(&simulation, - opkingrow + dr, opkingfile + df, piececolor); + if (dr == 0 && df == 0) continue; + if (!isidx(opkingrow + dr)) continue; + if (!isidx(opkingfile + df)) continue; + uint8_t er = opkingrow + dr; + uint8_t ef = opkingfile + df; + + /* check if escape field is already covered (threatened) */ + if (is_covered(&simulation, er, ef, piececolor)) + continue; + + /* check if piece of the king's color blocks the field */ + if ((simulation.board[er][ef] & COLOR_MASK) == oppcolor) + continue; + + /* check if an attacking piece blocks the field */ + if ((simulation.board[er][ef] & COLOR_MASK) == piececolor) { + /* test if the king can fight back */ + GameState sim_retaliate = gamestate_copy_sim(&simulation); + Move move_retaliate = {0}; + move_retaliate.piece = (oppcolor|KING); + move_retaliate.fromrow = opkingrow; + move_retaliate.fromfile = opkingfile; + move_retaliate.torow = er; + move_retaliate.tofile = ef; + move_retaliate.capture = 1; + apply_move_impl(&sim_retaliate, &move_retaliate, true); + canescape = !is_covered(&sim_retaliate, er, ef, piececolor); + gamestate_cleanup(&sim_retaliate); + } else { + /* the field is not covered and unoccupied */ + canescape = true; } } } - /* can't escape, can he capture? */ + + /* can't escape, can the king be rescued? */ if (!canescape && threatcount == 1) { - /* TODO: this is bugged - we actually need distinguish two cases - * 1. another piece can rescue the king - * (this is what is implemented now) - * 2. the king can kill the attacker - * - in this case, the threatcount is irrelevant, because - * the king moves out of the current threat - * - we must verify that the king can actually kill the - * attacker without running into a new check - */ - canescape = is_attacked(&simulation, + canescape = is_protected(&simulation, threats[0].fromrow, threats[0].fromfile, oppcolor); }