--- a/src/chess/rules.c Sun Aug 02 15:08:43 2026 +0200 +++ b/src/chess/rules.c Sun Aug 02 15:19:47 2026 +0200 @@ -138,11 +138,10 @@ /* resolve ambiguities, if any */ Move candidates[16]; size_t ccount; - if (get_candidates(gamestate, move->torow, move->tofile, + if (get_real_candidates(gamestate, move->torow, move->tofile, piece_color(move->piece), candidates, &ccount)) { unsigned int ambrows = 0, ambfiles = 0, ambpiece = 0; for (size_t i = 0 ; i < ccount ; i++) { - // TODO: check if we need to discard pinned pieces here if (candidates[i].piece == move->piece) { ambpiece++; if (candidates[i].fromrow == move->fromrow) { @@ -668,7 +667,6 @@ Color color, Move *threats, size_t *threatcount) { /* simulate a capturing move on the target position */ - // TODO: this does NOT disregard the pin, as stated in the spec! Color opcolor = opponent_color(color); GameState simulation = gamestate_copy_sim(gamestate); if (piece_color(piece_at(&simulation, row, file)) != opcolor) { @@ -730,7 +728,7 @@ Row kingrow = 0; for (Row row = 0 ; row < 8 ; row++) { for (File file = 0 ; file < 8 ; file++) { - if (simulation.board[row][file] == (color|KING)) { + if (piece_at(&simulation, row, file) == mkpiece(KING, color)) { kingfile = file; kingrow = row; } @@ -744,6 +742,31 @@ return covered; } +bool get_real_candidates(const GameState *gamestate, Row row, File file, + Color color, Move *moves, size_t *movecount) { + + if (movecount) { + *movecount = 0; + } + + Move candidates[16]; + size_t ccount; + if (get_candidates(gamestate, row, file, color, candidates, &ccount)) { + bool result = false; + for (size_t i = 0 ; i < ccount ; i++) { + if (!is_pinned(gamestate, &candidates[i])) { + result = true; + if (moves && movecount) { + moves[(*movecount)++] = candidates[i]; + } + } + } + return result; + } else { + return false; + } +} + bool get_real_threats(const GameState *gamestate, Row row, File file, Color color, Move *threats, size_t *threatcount) { @@ -752,36 +775,17 @@ } Move candidates[16]; - size_t candidatecount; - if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { - + size_t ccount; + if (get_threats(gamestate, row, file, color, candidates, &ccount)) { bool result = false; - File kingfile = 0; - Row kingrow = 0; - for (Row r = 0 ; r < 8 ; r++) { - for (File f = 0 ; f < 8 ; f++) { - if (gamestate->board[r][f] == (color|KING)) { - kingfile = f; - kingrow = r; - } - } - } - - for (size_t i = 0 ; i < candidatecount ; i++) { - // TODO: check if we really need full-blown simulations here - GameState simulation = gamestate_copy_sim(gamestate); - Move simmove = candidates[i]; - apply_move(&simulation, &simmove); - if (!is_covered(&simulation, kingrow, kingfile, - opponent_color(color))) { + for (size_t i = 0 ; i < ccount ; i++) { + if (!is_pinned(gamestate, &candidates[i])) { result = true; if (threats && threatcount) { threats[(*threatcount)++] = candidates[i]; } } - gamestate_cleanup(&simulation); } - return result; } else { return false;