From: Fabian Fichter Date: Fri, 19 Jun 2020 22:50:22 +0000 (+0200) Subject: Generalize insufficient material calculation X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=9d59bf9016e8b93be95458f3cf87b5302f641cc6;p=fairystockfish.git Generalize insufficient material calculation Closes #139. --- diff --git a/src/pyffish.cpp b/src/pyffish.cpp index f4bab02..89bf194 100644 --- a/src/pyffish.cpp +++ b/src/pyffish.cpp @@ -285,42 +285,36 @@ const std::string move_to_san(Position& pos, Move m, Notation n) { bool hasInsufficientMaterial(Color c, const Position& pos) { - if ( pos.captures_to_hand() || pos.count_in_hand(c, ALL_PIECES) + // Other win rules + if ( pos.captures_to_hand() + || pos.count_in_hand(c, ALL_PIECES) + || pos.extinction_value() != VALUE_NONE || (pos.capture_the_flag_piece() && pos.count(c, pos.capture_the_flag_piece()))) return false; - for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER }) - if (pos.count(c, pt) || (pos.count(c, PAWN) && pos.promotion_piece_types().find(pt) != pos.promotion_piece_types().end())) - return false; - - // To avoid false positives, treat pawn + anything as sufficient mating material. - // This is too conservative for South-East Asian variants. - if (pos.count(c, PAWN) && pos.count() >= 4) - return false; - - if (pos.count(c, KNIGHT) >= 2 || (pos.count(c, KNIGHT) && (pos.count(c, BISHOP) || pos.count(c, FERS) || pos.count(c, FERS_ALFIL)))) - return false; + // Restricted pieces + Bitboard restricted = pos.pieces(~c, KING); + for (PieceType pt : pos.piece_types()) + if (pt == KING || !(pos.board_bb(c, pt) & pos.board_bb(~c, KING))) + restricted |= pos.pieces(c, pt); - // Check for opposite colored color-bound pieces - if ( (pos.count(c, BISHOP) || pos.count(c, FERS_ALFIL)) - && (DarkSquares & pos.pieces(BISHOP, FERS_ALFIL)) && (~DarkSquares & pos.pieces(BISHOP, FERS_ALFIL))) - return false; - - if (pos.count(c, FERS) && (DarkSquares & pos.pieces(FERS)) && (~DarkSquares & pos.pieces(FERS))) - return false; + // Mating pieces + for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER, GOLD, COMMONER, CENTAUR }) + if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, PAWN) && pos.promotion_piece_types().find(pt) != pos.promotion_piece_types().end())) + return false; - if (pos.pieces(c, CANNON, JANGGI_CANNON) && (pos.count(c, ALL_PIECES) > 2 || pos.count(~c, ALL_PIECES) > 1)) + // 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)) return false; - if (pos.count(c, JANGGI_ELEPHANT) >= 2) + // Unbound pieces require one helper piece of either color + if ((pos.pieces(c) & unbound) && (popcount(pos.pieces() ^ restricted) >= 2 || pos.stalemate_value() != VALUE_DRAW)) return false; - // Pieces sufficient for stalemate (Xiangqi) - if (pos.stalemate_value() != VALUE_DRAW) - for (PieceType pt : { HORSE, SOLDIER, JANGGI_ELEPHANT }) - if (pos.count(c, pt)) - return false; - return true; } diff --git a/test.py b/test.py index 2bce111..11ebe34 100644 --- a/test.py +++ b/test.py @@ -78,6 +78,7 @@ variant_positions = { "k7/b1b5/8/8/8/8/8/K7 w - - 0 1": (True, True), # K vs KBB same color "kb6/8/8/8/8/8/8/K1B6 w - - 0 1": (True, True), # KB vs KB same color "kb6/8/8/8/8/8/8/KB7 w - - 0 1": (False, False), # KB vs KB opp color + "8/8/8/8/8/6KN/8/6nk w - - 0 1": (False, False), # KN vs KN }, "seirawan": { "k7/8/8/8/8/8/8/K7[] w - - 0 1": (True, True), # K vs K