public static boolean destroyDoTriggerAI(
      final AbilityFactory af, SpellAbility sa, boolean mandatory) {
    if (!ComputerUtil.canPayCost(sa)) return false;

    Target tgt = sa.getTarget();
    final Card source = sa.getSourceCard();
    final boolean noRegen = af.getMapParams().containsKey("NoRegen");

    if (tgt != null) {
      CardList list;
      list = AllZoneUtil.getCardsInPlay();
      list = list.getTargetableCards(source);
      list = list.getValidCards(tgt.getValidTgts(), source.getController(), source);

      if (list.size() == 0 || list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) return false;

      tgt.resetTargets();

      CardList preferred = list.getNotKeyword("Indestructible");
      preferred = list.getController(AllZone.HumanPlayer);

      // If NoRegen is not set, filter out creatures that have a regeneration shield
      if (!noRegen) {
        // TODO: filter out things that could regenerate in response? might be tougher?
        preferred =
            preferred.filter(
                new CardListFilter() {
                  public boolean addCard(Card c) {
                    return c.getShield() == 0;
                  }
                });
      }

      for (Card c : preferred) list.remove(c);

      while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
        if (preferred.size() == 0) {
          if (tgt.getNumTargeted() == 0
              || tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
            if (!mandatory) {
              tgt.resetTargets();
              return false;
            } else break;
          } else {
            break;
          }
        } else {
          Card c;
          if (preferred.getNotType("Creature").size() == 0) {
            c = CardFactoryUtil.AI_getBestCreature(preferred);
          } else if (preferred.getNotType("Land").size() == 0) {
            c = CardFactoryUtil.AI_getBestLand(preferred);
          } else {
            c = CardFactoryUtil.AI_getMostExpensivePermanent(preferred, source, false);
          }
          tgt.addTarget(c);
          preferred.remove(c);
        }
      }

      while (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
        if (list.size() == 0) {
          break;
        } else {
          Card c;
          if (list.getNotType("Creature").size() == 0) {
            c = CardFactoryUtil.AI_getWorstCreature(list);
          } else {
            c = CardFactoryUtil.AI_getCheapestPermanent(list, source, false);
          }
          tgt.addTarget(c);
          list.remove(c);
        }
      }

      if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) return false;
    } else {
      if (!mandatory) return false;
    }

    Ability_Sub subAb = sa.getSubAbility();
    if (subAb != null) return subAb.doTrigger(mandatory);

    return true;
  }
  public static boolean destroyCanPlayAI(final AbilityFactory af, final SpellAbility sa) {
    // AI needs to be expanded, since this function can be pretty complex based on what the expected
    // targets could be
    Random r = MyRandom.random;
    Cost abCost = sa.getPayCosts();
    Target abTgt = sa.getTarget();
    final Card source = sa.getSourceCard();
    final boolean noRegen = af.getMapParams().containsKey("NoRegen");

    CardList list;
    list = AllZoneUtil.getPlayerCardsInPlay(AllZone.HumanPlayer);
    list = list.getTargetableCards(source);

    if (abTgt != null) {
      list = list.getValidCards(abTgt.getValidTgts(), source.getController(), source);
      list = list.getNotKeyword("Indestructible");

      // If NoRegen is not set, filter out creatures that have a regeneration shield
      if (!noRegen) {
        // TODO: filter out things that could regenerate in response? might be tougher?
        list =
            list.filter(
                new CardListFilter() {
                  public boolean addCard(Card c) {
                    return (c.getShield() == 0 && !ComputerUtil.canRegenerate(c));
                  }
                });
      }

      if (list.size() == 0) return false;
    }

    if (abCost != null) {
      // AI currently disabled for some costs
      if (abCost.getSacCost() && !abCost.getSacThis()) {
        // only sacrifice something that's supposed to be sacrificed
        String sacType = abCost.getSacType();
        CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.ComputerPlayer);
        typeList = typeList.getValidCards(sacType.split(","), source.getController(), source);
        if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) return false;
      }
      if (abCost.getLifeCost()) {
        if (AllZone.ComputerPlayer.getLife() - abCost.getLifeAmount() < 4) return false;
      }
      if (abCost.getDiscardCost()) return false;

      if (abCost.getSubCounter()) {
        // OK
      }
    }

    if (!ComputerUtil.canPayCost(sa)) return false;

    // prevent run-away activations - first time will always return true
    boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());

    // Targeting
    if (abTgt != null) {
      abTgt.resetTargets();
      // target loop
      while (abTgt.getNumTargeted() < abTgt.getMaxTargets(sa.getSourceCard(), sa)) {
        if (list.size() == 0) {
          if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa)
              || abTgt.getNumTargeted() == 0) {
            abTgt.resetTargets();
            return false;
          } else {
            // TODO is this good enough? for up to amounts?
            break;
          }
        }

        Card choice = null;
        if (list.getNotType("Creature").size() == 0)
          choice =
              CardFactoryUtil.AI_getBestCreature(
                  list); // if the targets are only creatures, take the best
        else choice = CardFactoryUtil.AI_getMostExpensivePermanent(list, af.getHostCard(), true);

        if (choice == null) { // can't find anything left
          if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa)
              || abTgt.getNumTargeted() == 0) {
            abTgt.resetTargets();
            return false;
          } else {
            // TODO is this good enough? for up to amounts?
            break;
          }
        }
        list.remove(choice);
        abTgt.addTarget(choice);
      }

    } else {
      return false;
    }

    Ability_Sub subAb = sa.getSubAbility();
    if (subAb != null) chance &= subAb.chkAI_Drawback();

    return ((r.nextFloat() < .6667) && chance);
  }