diff -r 724e8acff6f8 -r 5ef724e21702 src/chess/rules.c --- a/src/chess/rules.c Fri Aug 21 16:08:50 2026 +0200 +++ b/src/chess/rules.c Fri Aug 21 17:04:43 2026 +0200 @@ -297,6 +297,79 @@ return false; } +static bool check_no_material_color(Color color, const GameState *gamestate) { + /* count the available pieces */ + unsigned piece_count[7] = {0}; + unsigned op_piece_count[7] = {0}; + bool has_bbishop = false, has_wbishop = false; + bool op_has_bbishop = false, op_has_wbishop = false; + for (Row r = 0 ; r < 8 ; r++) { + for (File f = 0 ; f < 8 ; f++) { + Piece p = piece_at(gamestate, r, f); + if (piece_color(p) == color) { + piece_count[piece_type(p)]++; + if (piece_type(p) == BISHOP) { + if (field_color(r, f) == WHITE) { + has_wbishop = true; + } else { + has_bbishop = true; + } + } + } else { + op_piece_count[piece_type(p)]++; + if (piece_type(p) == BISHOP) { + if (field_color(r, f) == WHITE) { + op_has_wbishop = true; + } else { + op_has_bbishop = true; + } + } + } + } + } + + /* rooks and queens are always enough - don't test them below */ + if (piece_count[ROOK] > 0 || piece_count[QUEEN] > 0) + return false; + + /* only the king left */ + if (piece_count[PAWN] == 0 && piece_count[KNIGHT] == 0 + && piece_count[BISHOP] == 0) + return true; + + /* king + knight and the opponent has only king + queens */ + if (piece_count[PAWN] == 0 && piece_count[BISHOP] == 0 + && piece_count[KNIGHT] == 1 + && op_piece_count[ROOK] == 0 && op_piece_count[BISHOP] == 0 + && op_piece_count[KNIGHT] == 0 && op_piece_count[PAWN] == 0 + && op_piece_count[QUEEN] > 0) + return true; + + /* king + bishop and the opponent doesn't have + * opposite color bishops or knights or pawns */ + if (piece_count[PAWN] == 0 && piece_count[KNIGHT] == 0 + && piece_count[BISHOP] > 0) { + + if (op_piece_count[KNIGHT] > 0 || op_piece_count[PAWN] > 0) + return false; + + if (has_bbishop && op_has_wbishop) + return false; + + if (has_wbishop && op_has_bbishop) + return false; + + return true; + } + + return false; +} + +bool check_no_material(const GameState *gamestate) { + return check_no_material_color(WHITE, gamestate) + && check_no_material_color(BLACK, gamestate); +} + char getpiecechr(Piece piece) { switch (piece_type(piece)) { case ROOK: return 'R'; @@ -417,6 +490,8 @@ /* calculate gamestate flags in order of efficiency */ if (move->checkmate) { gamestate->checkmate = true; + } else if (check_no_material(gamestate)) { + gamestate->nomaterial = true; } else if (check_threefold_repetition(gamestate)) { gamestate->threefold = true; } else if (check_stalemate(gamestate)) {