hasInsufficientMaterial method

  1. @override
bool hasInsufficientMaterial(
  1. Side side
)
override

Tests if a Side has insufficient winning material.

Implementation

@override
bool hasInsufficientMaterial(Side side) {
  // side with king can always win by capturing the horde
  if (board.piecesOf(side, Role.king).isNotEmpty) {
    return false;
  }

  // now color represents horde and color.opposite is pieces
  final hordeNum = board.piecesOf(side, Role.pawn).size +
      board.piecesOf(side, Role.rook).size +
      board.piecesOf(side, Role.queen).size +
      board.piecesOf(side, Role.knight).size +
      math.min(_hordeBishops(side, SquareColor.light), 2) +
      math.min(_hordeBishops(side, SquareColor.dark), 2);

  if (hordeNum == 0) {
    return true;
  }

  if (hordeNum >= 4) {
    // 4 or more pieces can always deliver mate.
    return false;
  }

  final hordeMap = board.materialCount(side);
  final hordeBishopColor = _hordeBishopColor(side);
  final piecesMap = board.materialCount(side.opposite);
  final piecesNum = board.bySide(side.opposite).size;

  if ((hordeMap[Role.pawn]! >= 1 || hordeMap[Role.queen]! >= 1) &&
      hordeNum >= 2) {
    // Pawns/queens are never insufficient material when paired with any other
    // piece (a pawn promotes to a queen and delivers mate).
    return false;
  }

  if (hordeMap[Role.rook]! >= 1 && hordeNum >= 2) {
    // A rook is insufficient material only when it is paired with a bishop
    // against a lone king. The horde can mate in any other case.
    // A rook on A1 and a bishop on C3 mate a king on B1 when there is a
    // friendly pawn/opposite-color-bishop/rook/queen on C2.
    // A rook on B8 and a bishop C3 mate a king on A1 when there is a friendly
    // knight on A2.
    return hordeNum == 2 &&
        hordeMap[Role.rook]! == 1 &&
        hordeMap[Role.bishop]! == 1 &&
        (_pieceOfRoleNot(
                piecesNum, _hordeBishops(side.opposite, hordeBishopColor)) ==
            1);
  }

  if (hordeNum == 1) {
    if (piecesNum == 1) {
      // lone piece cannot mate a lone king
      return true;
    } else if (hordeMap[Role.queen] == 1) {
      // The horde has a lone queen.
      // A lone queen mates a king on A1 bounded by:
      //  -- a pawn/rook on A2
      //  -- two same color bishops on A2, B1
      // We ignore every other mating case, since it can be reduced to
      // the two previous cases (e.g. a black pawn on A2 and a black
      // bishop on B1).

      return !(piecesMap[Role.pawn]! >= 1 ||
          piecesMap[Role.rook]! >= 1 ||
          _hordeBishops(side.opposite, SquareColor.light) >= 2 ||
          _hordeBishops(side, SquareColor.dark) >= 2);
    } else if (hordeMap[Role.pawn] == 1) {
      // Promote the pawn to a queen or a knight and check whether white can mate.
      final pawnSquare = board.piecesOf(side, Role.pawn).last;

      final promoteToQueen = copyWith();
      promoteToQueen.board
          .setPieceAt(pawnSquare!, Piece(color: side, role: Role.queen));
      final promoteToKnight = copyWith();
      promoteToKnight.board
          .setPieceAt(pawnSquare, Piece(color: side, role: Role.knight));
      return promoteToQueen.hasInsufficientMaterial(side) &&
          promoteToKnight.hasInsufficientMaterial(side);
    } else if (hordeMap[Role.rook] == 1) {
      // A lone rook mates a king on A8 bounded by a pawn/rook on A7 and a
      // pawn/knight on B7. We ignore every other case, since it can be
      // reduced to the two previous cases.
      // (e.g. three pawns on A7, B7, C7)

      return !(piecesMap[Role.pawn]! >= 2 ||
          (piecesMap[Role.rook]! >= 1 && piecesMap[Role.pawn]! >= 1) ||
          (piecesMap[Role.rook]! >= 1 && piecesMap[Role.knight]! >= 1) ||
          (piecesMap[Role.pawn]! >= 1 && piecesMap[Role.knight]! >= 1));
    } else if (hordeMap[Role.bishop] == 1) {
      // horde has a lone bishop
      // The king can be mated on A1 if there is a pawn/opposite-color-bishop
      // on A2 and an opposite-color-bishop on B1.
      // If black has two or more pawns, white gets the benefit of the doubt;
      // there is an outside chance that white promotes its pawns to
      // opposite-color-bishops and selfmates theirself.
      // Every other case that the king is mated by the bishop requires that
      // black has two pawns or two opposite-color-bishop or a pawn and an
      // opposite-color-bishop.
      // For example a king on A3 can be mated if there is
      // a pawn/opposite-color-bishop on A4, a pawn/opposite-color-bishop on
      // B3, a pawn/bishop/rook/queen on A2 and any other piece on B2.

      return !(_hordeBishops(side.opposite, hordeBishopColor.opposite) >= 2 ||
          (_hordeBishops(side.opposite, hordeBishopColor.opposite) >= 1 &&
              piecesMap[Role.pawn]! >= 1) ||
          piecesMap[Role.pawn]! >= 2);
    } else if (hordeMap[Role.knight] == 1) {
      // horde has a lone knight
      // The king on A1 can be smother mated by a knight on C2 if there is
      // a pawn/knight/bishop on B2, a knight/rook on B1 and any other piece
      // on A2.
      // Moreover, when black has four or more pieces and two of them are
      // pawns, black can promote their pawns and selfmate theirself.

      return !(piecesNum >= 4 &&
          (piecesMap[Role.knight]! >= 2 ||
              piecesMap[Role.pawn]! >= 2 ||
              (piecesMap[Role.rook]! >= 1 && piecesMap[Role.knight]! >= 1) ||
              (piecesMap[Role.rook]! >= 1 && piecesMap[Role.bishop]! >= 1) ||
              (piecesMap[Role.knight]! >= 1 &&
                  piecesMap[Role.bishop]! >= 1) ||
              (piecesMap[Role.rook]! >= 1 && piecesMap[Role.pawn]! >= 1) ||
              (piecesMap[Role.knight]! >= 1 && piecesMap[Role.pawn]! >= 1) ||
              (piecesMap[Role.bishop]! >= 1 && piecesMap[Role.pawn]! >= 1) ||
              (_hasBishopPair(side.opposite) &&
                  piecesMap[Role.pawn]! >= 1)) &&
          (_hordeBishops(side.opposite, SquareColor.light) < 2 ||
              (_pieceOfRoleNot(piecesNum,
                      _hordeBishops(side.opposite, SquareColor.light)) >=
                  3)) &&
          (_hordeBishops(side.opposite, SquareColor.dark) < 2 ||
              (_pieceOfRoleNot(piecesNum,
                      _hordeBishops(side.opposite, SquareColor.dark)) >=
                  3)));
    }
  } else if (hordeNum == 2) {
    if (piecesNum == 1) {
      // two minor pieces cannot mate a lone king
      return true;
    } else if (hordeMap[Role.knight] == 2) {
      // A king on A1 is mated by two knights, if it is obstructed by a
      // pawn/bishop/knight on B2. On the other hand, if black only has
      // major pieces it is a draw.

      return piecesMap[Role.pawn]! +
              piecesMap[Role.bishop]! +
              piecesMap[Role.knight]! <
          1;
    } else if (_hasBishopPair(side)) {
      return !(piecesMap[Role.pawn]! >= 1 ||
          piecesMap[Role.bishop]! >= 1 ||
          (piecesMap[Role.knight]! >= 1 &&
              piecesMap[Role.rook]! + piecesMap[Role.queen]! >= 1));
    } else if (hordeMap[Role.bishop]! >= 1 && hordeMap[Role.knight]! >= 1) {
      // horde has a bishop and a knight
      return !(piecesMap[Role.pawn]! >= 1 ||
          _hordeBishops(side.opposite, hordeBishopColor.opposite) >= 1 ||
          (_pieceOfRoleNot(piecesNum,
                  _hordeBishops(side.opposite, hordeBishopColor)) >=
              3));
    } else {
      // The horde has two or more bishops on the same color.
      // White can only win if black has enough material to obstruct
      // the squares of the opposite color around the king.
      //
      // A king on A1 obstructed by a pawn/opposite-bishop/knight
      // on A2 and a opposite-bishop/knight on B1 is mated by two
      // bishops on B2 and C3. This position is theoretically
      // achievable even when black has two pawns or when they
      // have a pawn and an opposite color bishop.

      return !((piecesMap[Role.pawn]! >= 1 &&
              _hordeBishops(side.opposite, hordeBishopColor.opposite) >= 1) ||
          (piecesMap[Role.pawn]! >= 1 && piecesMap[Role.knight]! >= 1) ||
          (_hordeBishops(side.opposite, hordeBishopColor.opposite) >= 1 &&
              piecesMap[Role.knight]! >= 1) ||
          (_hordeBishops(side.opposite, hordeBishopColor.opposite) >= 2) ||
          piecesMap[Role.knight]! >= 2 ||
          piecesMap[Role.pawn]! >= 2);
    }
  } else if (hordeNum == 3) {
    // A king in the corner is mated by two knights and a bishop or three
    // knights or the bishop pair and a knight/bishop.

    if ((hordeMap[Role.knight] == 2 && hordeMap[Role.bishop] == 1) ||
        hordeMap[Role.knight] == 3 ||
        _hasBishopPair(side)) {
      return false;
    } else {
      return piecesNum == 1;
    }
  }

  return true;
}