--- a/src/chess/rules.c Fri Jun 12 13:36:15 2026 +0200 +++ b/src/chess/rules.c Fri Jun 12 14:33:42 2026 +0200 @@ -539,6 +539,11 @@ candidates[candidatecount].fromfile = f; candidates[candidatecount].torow = row; candidates[candidatecount].tofile = file; + if ((gamestate->board[r][f]&PIECE_MASK) == PAWN + && (row == 0 || row == 7)) { + /* the exact piece for promotion does not matter */ + candidates[candidatecount].promotion = color|QUEEN; + } candidatecount++; /* capturing move */ @@ -643,7 +648,7 @@ Move threats[16], *threat = NULL; uint8_t threatcount; - + if (get_threats(gamestate, move->torow, move->tofile, color, threats, &threatcount)) { @@ -651,8 +656,7 @@ /* find threats for the specified position */ for (uint8_t i = 0 ; i < threatcount ; i++) { - if ((threats[i].piece & PIECE_MASK) == piece - && (threats[i].piece & COLOR_MASK) == color && + if (threats[i].piece == move->piece && (move->fromrow == POS_UNSPECIFIED || move->fromrow == threats[i].fromrow) && (move->fromfile == POS_UNSPECIFIED || @@ -662,6 +666,7 @@ return AMBIGUOUS_MOVE; } else { /* found threat is no real threat */ + // TODO: why didn't we call get_real_threats() ? if (is_pinned(gamestate, &(threats[i]))) { reason = incheck?KING_IN_CHECK: (piece==KING?KING_MOVES_INTO_CHECK:PIECE_PINNED); @@ -677,7 +682,9 @@ return reason; } - memcpy(move, threat, sizeof(Move)); + /* found a threat, copy the source location */ + move->fromrow = threat->fromrow; + move->fromfile = threat->fromfile; return VALID_MOVE_SYNTAX; } else { return INVALID_POSITION;