From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Sep 2025 20:45:37 +0000 (+0200) Subject: Fix insufficient material detection for variants with multiple pawn types (#916) X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=4aef2cac0d4f570b679920b50a29b448e5863f63;p=fairystockfish.git Fix insufficient material detection for variants with multiple pawn types (#916) Closes #915 --- diff --git a/src/apiutil.h b/src/apiutil.h index 74bdc6d..d0657bc 100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@ -372,31 +372,53 @@ inline bool has_insufficient_material(Color c, const Position& pos) { || (pos.flag_region(c) && pos.count(c, pos.flag_piece(c)))) return false; - // Restricted pieces - Bitboard restricted = pos.pieces(~c, KING); - // Atomic kings can not help checkmating - if (pos.extinction_pseudo_royal() && pos.blast_on_capture() && (pos.extinction_piece_types() & COMMONER)) - restricted |= pos.pieces(c, COMMONER); + // Precalculate if any promotion pawn types have pieces + bool hasPromotingPawn = false; + for (PieceSet pawnTypes = pos.promotion_pawn_types(c); pawnTypes; ) + { + PieceType pawnType = pop_lsb(pawnTypes); + if (pos.count(c, pawnType) > 0) + { + hasPromotingPawn = true; + break; + } + } + + // Determine checkmating potential of present pieces + constexpr PieceSet MAJOR_PIECES = piece_set(ROOK) | QUEEN | ARCHBISHOP | CHANCELLOR + | SILVER | GOLD | COMMONER | CENTAUR | AMAZON | BERS; + constexpr PieceSet COLORBOUND_PIECES = piece_set(BISHOP) | FERS | FERS_ALFIL | ALFIL | ELEPHANT; + Bitboard restricted = pos.pieces(KING); + Bitboard colorbound = 0; for (PieceSet ps = pos.piece_types(); ps;) { PieceType pt = pop_lsb(ps); - if (pt == KING || !(pos.board_bb(c, pt) & pos.board_bb(~c, KING))) + + // Constrained pieces + if (pt == KING || !(pos.board_bb(c, pt) & pos.board_bb(~c, KING)) || (pos.extinction_pseudo_royal() && pos.blast_on_capture() && (pos.extinction_piece_types() & pt))) restricted |= pos.pieces(c, pt); - else if (is_custom(pt) && pos.count(c, pt) > 0) - // to be conservative, assume any custom piece has mating potential - return false; + + // If piece is a major piece or a custom piece we consider it sufficient for mate. + // To avoid false positives, we assume any custom piece has mating potential. + else if ((MAJOR_PIECES & pt) || is_custom(pt)) + { + // Check if piece is already on the board + if (pos.count(c, pt) > 0) + return false; + + // Check if any pawn can promote to this piece type + if (hasPromotingPawn && (pos.promotion_piece_types(c) & pt)) + return false; + } + + // Collect color-bound pieces + else if (COLORBOUND_PIECES & pt) + colorbound |= pos.pieces(pt); } - // Mating pieces - for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER, GOLD, COMMONER, CENTAUR, AMAZON, BERS }) - if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, pos.main_promotion_pawn_type(c)) && (pos.promotion_piece_types(c) & pt))) - return false; + Bitboard unbound = pos.pieces() ^ restricted ^ colorbound; // Color-bound pieces - Bitboard colorbound = 0, unbound; - for (PieceType pt : { BISHOP, FERS, FERS_ALFIL, ALFIL, ELEPHANT }) - colorbound |= pos.pieces(pt) & ~restricted; - unbound = pos.pieces() ^ restricted ^ colorbound; if ((colorbound & pos.pieces(c)) && (((DarkSquares & colorbound) && (~DarkSquares & colorbound)) || unbound || pos.stalemate_value() != VALUE_DRAW || pos.check_counting() || pos.makpong())) return false; diff --git a/test.py b/test.py index 8ac27ab..ca5d391 100644 --- a/test.py +++ b/test.py @@ -127,6 +127,10 @@ startFen = rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[Qq] w KQkq - 0 1 [cannonatomic:atomic] cannon = c + +[multipawn:chess] +soldier = s +pawnTypes = ps """ sf.load_variant_config(ini_text) @@ -243,6 +247,10 @@ variant_positions = { "wazirking": { "7k/6K1/8/8/8/8/8/8 b - - 0 1": (False, False), # K vs K }, + "multipawn": { + "k7/p7/8/8/8/8/8/K7 w - - 0 1": (True, False), # K vs KP + "k7/s7/8/8/8/8/8/K7 w - - 0 1": (True, False), # K vs KS + }, } invalid_variant_positions = {