Example #1
0
  /**
   * chooseBoonTarget.
   *
   * @param list a {@link forge.CardList} object.
   * @param type a {@link java.lang.String} object.
   * @return a {@link forge.game.card.Card} object.
   */
  public static Card chooseBoonTarget(final CardCollectionView list, final String type) {
    Card choice = null;
    if (type.equals("P1P1")) {
      choice = ComputerUtilCard.getBestCreatureAI(list);

      if (choice == null) {
        // We'd only get here if list isn't empty, maybe we're trying to animate a land?
        choice = ComputerUtilCard.getBestLandToAnimate(list);
      }
    } else if (type.equals("DIVINITY")) {
      final CardCollection boon =
          CardLists.filter(
              list,
              new Predicate<Card>() {
                @Override
                public boolean apply(final Card c) {
                  return c.getCounters(CounterType.DIVINITY) == 0;
                }
              });
      choice = ComputerUtilCard.getMostExpensivePermanentAI(boon, null, false);
    } else {
      // The AI really should put counters on cards that can use it.
      // Charge counters on things with Charge abilities, etc. Expand
      // these above
      choice = Aggregates.random(list);
    }
    return choice;
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
   * forge.Card, forge.Player, forge.card.cost.Cost)
   */
  @Override
  public final boolean canPay(final SpellAbility ability) {
    final Player activator = ability.getActivatingPlayer();
    final Card source = ability.getHostCard();
    CardCollectionView validCards = activator.getCardsIn(ZoneType.Battlefield);
    validCards = CardLists.getValidCards(validCards, this.getType().split(";"), activator, source);
    validCards =
        CardLists.filter(
            validCards,
            new Predicate<Card>() {
              @Override
              public boolean apply(final Card c) {
                return c.hasCounters();
              }
            });
    if (validCards.isEmpty()) {
      return false;
    }
    Integer i = this.convertAmount();

    if (i == null) {
      i = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
    }
    int allCounters = 0;
    for (Card c : validCards) {
      final Map<CounterType, Integer> tgtCounters = c.getCounters();
      for (Integer value : tgtCounters.values()) {
        allCounters += value;
      }
    }

    return i <= allCounters;
  }
Example #3
0
 /**
  * chooseCursedTarget.
  *
  * @param list a {@link forge.CardList} object.
  * @param type a {@link java.lang.String} object.
  * @param amount a int.
  * @return a {@link forge.game.card.Card} object.
  */
 public static Card chooseCursedTarget(
     final CardCollectionView list, final String type, final int amount) {
   Card choice;
   if (type.equals("M1M1")) {
     // try to kill the best killable creature, or reduce the best one
     final List<Card> killable =
         CardLists.filter(
             list,
             new Predicate<Card>() {
               @Override
               public boolean apply(final Card c) {
                 return c.getNetToughness() <= amount;
               }
             });
     if (killable.size() > 0) {
       choice = ComputerUtilCard.getBestCreatureAI(killable);
     } else {
       choice = ComputerUtilCard.getBestCreatureAI(list);
     }
   } else {
     // improve random choice here
     choice = Aggregates.random(list);
   }
   return choice;
 }
Example #4
0
  @Override
  public PaymentDecision visit(CostUntapType cost) {
    final String amount = cost.getAmount();
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(amount);
      if (sVar.equals("XChoice")) {
        List<Card> typeList = player.getGame().getCardsIn(ZoneType.Battlefield);
        typeList =
            CardLists.getValidCards(
                typeList, cost.getType().split(";"), player, ability.getHostCard());
        if (!cost.canUntapSource) {
          typeList.remove(source);
        }
        typeList = CardLists.filter(typeList, Presets.TAPPED);
        c = typeList.size();
        source.setSVar("ChosenX", "Number$" + Integer.toString(c));
      } else {
        c = AbilityUtils.calculateAmount(source, amount, ability);
      }
    }

    List<Card> list =
        ComputerUtil.chooseUntapType(player, cost.getType(), source, cost.canUntapSource, c);

    if (list == null) {
      System.out.println("Couldn't find a valid card to untap for: " + source.getName());
      return null;
    }

    return PaymentDecision.card(list);
  }
Example #5
0
  @Override
  public PaymentDecision visit(CostTapType cost) {
    final String amount = cost.getAmount();
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(amount);
      if (sVar.equals("XChoice")) {
        List<Card> typeList =
            CardLists.getValidCards(
                player.getCardsIn(ZoneType.Battlefield),
                cost.getType().split(";"),
                ability.getActivatingPlayer(),
                ability.getHostCard());
        typeList = CardLists.filter(typeList, Presets.UNTAPPED);
        c = typeList.size();
        source.setSVar("ChosenX", "Number$" + Integer.toString(c));
      } else {
        c = AbilityUtils.calculateAmount(source, amount, ability);
      }
    }
    if (cost.getType().contains("sharesCreatureTypeWith")
        || cost.getType().contains("withTotalPowerGE")) {
      return null;
    }

    List<Card> totap =
        ComputerUtil.chooseTapType(player, cost.getType(), source, !cost.canTapSource, c);

    if (totap == null) {
      System.out.println("Couldn't find a valid card to tap for: " + source.getName());
      return null;
    }

    return PaymentDecision.card(totap);
  }
Example #6
0
  @Override
  public PaymentDecision visit(CostPutCardToLib cost) {
    Integer c = cost.convertAmount();
    final Game game = player.getGame();
    List<Card> chosen = new ArrayList<Card>();
    List<Card> list;

    if (cost.isSameZone()) {
      list = new ArrayList<Card>(game.getCardsIn(cost.getFrom()));
    } else {
      list = new ArrayList<Card>(player.getCardsIn(cost.getFrom()));
    }

    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      // Generalize cost
      if (sVar.equals("XChoice")) {
        return null;
      }

      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    list = CardLists.getValidCards(list, cost.getType().split(";"), player, source);

    if (cost.isSameZone()) {
      // Jotun Grunt
      // TODO: improve AI
      final List<Player> players = game.getPlayers();
      for (Player p : players) {
        List<Card> enoughType = CardLists.filter(list, CardPredicates.isOwner(p));
        if (enoughType.size() >= c) {
          chosen.addAll(enoughType);
          break;
        }
      }
      chosen = chosen.subList(0, c);
    } else {
      chosen =
          ComputerUtil.choosePutToLibraryFrom(
              player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c);
    }
    return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
  }
  @Override
  public Card chooseSingleCard(
      final Player aiChoser,
      SpellAbility sa,
      Iterable<Card> options,
      boolean isOptional,
      Player targetedPlayer) {
    if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
      final Player ai = sa.getActivatingPlayer();
      final Game game = ai.getGame();
      if (!game.getStack().isEmpty()) {
        Card choseCard = chooseCardOnStack(sa, ai, game);
        if (choseCard != null) {
          return choseCard;
        }
      }

      final Combat combat = game.getCombat();

      List<Card> permanentSources =
          CardLists.filter(
              options,
              new Predicate<Card>() {
                @Override
                public boolean apply(final Card c) {
                  if (c == null
                      || c.getZone() == null
                      || c.getZone().getZoneType() != ZoneType.Battlefield
                      || combat == null
                      || !combat.isAttacking(c, ai)
                      || !combat.isUnblocked(c)) {
                    return false;
                  }
                  return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
                }
              });
      return ComputerUtilCard.getBestCreatureAI(permanentSources);

    } else {
      return ComputerUtilCard.getBestAI(options);
    }
  }
Example #8
0
  @Override
  public PaymentDecision visit(CostRemoveAnyCounter cost) {
    final String amount = cost.getAmount();
    final int c = AbilityUtils.calculateAmount(source, amount, ability);
    final String type = cost.getType();

    List<Card> typeList =
        CardLists.getValidCards(
            player.getCardsIn(ZoneType.Battlefield), type.split(";"), player, source);
    List<Card> hperms =
        CardLists.filter(
            typeList,
            new Predicate<Card>() {
              @Override
              public boolean apply(final Card crd) {
                for (final CounterType c1 : CounterType.values()) {
                  if (crd.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, crd)) {
                    return true;
                  }
                }
                return false;
              }
            });

    if (hperms.isEmpty()) return null;

    PaymentDecision result = PaymentDecision.card(hperms);
    Card valid = hperms.get(0);
    for (CounterType c1 : valid.getCounters().keySet()) {
      if (valid.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, valid)) {
        result.ct = c1;
        break;
      }
    }
    // Only find cards with enough negative counters
    // TODO: add ai for Chisei, Heart of Oceans
    return result;
  }
Example #9
0
  private boolean pumpTgtAI(
      final Player ai,
      final SpellAbility sa,
      final int defense,
      final int attack,
      final boolean mandatory) {
    final List<String> keywords =
        sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>();
    final Game game = ai.getGame();
    final Card source = sa.getHostCard();

    if (!mandatory
        && !sa.isTrigger()
        && game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
        && !(sa.isCurse() && defense < 0)
        && !this.containsNonCombatKeyword(keywords)
        && !sa.hasParam("UntilYourNextTurn")) {
      return false;
    }

    final Player opp = ai.getOpponent();
    final TargetRestrictions tgt = sa.getTargetRestrictions();
    sa.resetTargets();
    if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) {
      Player targetingPlayer =
          AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0);
      sa.setTargetingPlayer(targetingPlayer);
      return targetingPlayer.getController().chooseTargetsFor(sa);
    }

    List<Card> list = new ArrayList<Card>();
    if (sa.hasParam("AILogic")) {
      if (sa.getParam("AILogic").equals("HighestPower")) {
        list =
            CardLists.getValidCards(
                CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES),
                tgt.getValidTgts(),
                ai,
                source);
        list = CardLists.getTargetableCards(list, sa);
        CardLists.sortByPowerDesc(list);
        if (!list.isEmpty()) {
          sa.getTargets().add(list.get(0));
          return true;
        } else {
          return false;
        }
      }
      if (sa.getParam("AILogic").equals("Fight") || sa.getParam("AILogic").equals("PowerDmg")) {
        final AbilitySub tgtFight = sa.getSubAbility();
        List<Card> aiCreatures = ai.getCreaturesInPlay();
        aiCreatures = CardLists.getTargetableCards(aiCreatures, sa);
        aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures);
        ComputerUtilCard.sortByEvaluateCreature(aiCreatures);
        // sort is suboptimal due to conflicting needs depending on game state:
        //  -deathtouch for deal damage
        //  -max power for damage to player
        //  -survivability for generic "fight"
        //  -no support for "heroic"

        List<Card> humCreatures = ai.getOpponent().getCreaturesInPlay();
        humCreatures = CardLists.getTargetableCards(humCreatures, tgtFight);
        ComputerUtilCard.sortByEvaluateCreature(humCreatures);
        if (humCreatures.isEmpty() || aiCreatures.isEmpty()) {
          return false;
        }
        int buffedAtk = attack, buffedDef = defense;
        for (Card humanCreature : humCreatures) {
          for (Card aiCreature : aiCreatures) {
            if (sa.isSpell()) { // heroic triggers adding counters
              for (Trigger t : aiCreature.getTriggers()) {
                if (t.getMode() == TriggerType.SpellCast) {
                  final Map<String, String> params = t.getMapParams();
                  if ("Card.Self".equals(params.get("TargetsValid"))
                      && "You".equals(params.get("ValidActivatingPlayer"))
                      && params.containsKey("Execute")) {
                    SpellAbility heroic =
                        AbilityFactory.getAbility(
                            aiCreature.getSVar(params.get("Execute")), aiCreature);
                    if ("Self".equals(heroic.getParam("Defined"))
                        && "P1P1".equals(heroic.getParam("CounterType"))) {
                      int amount =
                          AbilityUtils.calculateAmount(
                              aiCreature, heroic.getParam("CounterNum"), heroic);
                      buffedAtk += amount;
                      buffedDef += amount;
                    }
                    break;
                  }
                }
              }
            }
            if (sa.getParam("AILogic").equals("PowerDmg")) {
              if (FightAi.canKill(aiCreature, humanCreature, buffedAtk)) {
                sa.getTargets().add(aiCreature);
                tgtFight.getTargets().add(humanCreature);
                return true;
              }
            } else {
              if (FightAi.shouldFight(aiCreature, humanCreature, buffedAtk, buffedDef)) {
                sa.getTargets().add(aiCreature);
                tgtFight.getTargets().add(humanCreature);
                return true;
              }
            }
          }
        }
      }
      return false;
    } else if (sa.isCurse()) {
      if (sa.canTarget(opp)) {
        sa.getTargets().add(opp);
        return true;
      }
      list = this.getCurseCreatures(ai, sa, defense, attack, keywords);
    } else {
      if (!tgt.canTgtCreature()) {
        ZoneType zone = tgt.getZone().get(0);
        list = game.getCardsIn(zone);
      } else {
        list = this.getPumpCreatures(ai, sa, defense, attack, keywords);
      }
      if (sa.canTarget(ai)) {
        sa.getTargets().add(ai);
        return true;
      }
    }

    list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source);
    if (game.getStack().isEmpty()) {
      // If the cost is tapping, don't activate before declare
      // attack/block
      if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) {
        if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
            && game.getPhaseHandler().isPlayerTurn(ai)) {
          list.remove(sa.getHostCard());
        }
        if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)
            && game.getPhaseHandler().isPlayerTurn(opp)) {
          list.remove(sa.getHostCard());
        }
      }
    }

    if (list.isEmpty()) {
      return mandatory && this.pumpMandatoryTarget(ai, sa, mandatory);
    }

    if (!sa.isCurse()) {
      // Don't target cards that will die.
      list = ComputerUtil.getSafeTargets(ai, sa, list);
    }

    while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
      Card t = null;
      // boolean goodt = false;

      if (list.isEmpty()) {
        if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa))
            || (sa.getTargets().getNumTargeted() == 0)) {
          if (mandatory) {
            return this.pumpMandatoryTarget(ai, sa, mandatory);
          }

          sa.resetTargets();
          return false;
        } else {
          // TODO is this good enough? for up to amounts?
          break;
        }
      }

      t = ComputerUtilCard.getBestAI(list);
      // option to hold removal instead only applies for single targeted removal
      if (!sa.isTrigger() && tgt.getMaxTargets(source, sa) == 1 && sa.isCurse()) {
        if (!ComputerUtilCard.useRemovalNow(sa, t, -defense, ZoneType.Graveyard)) {
          return false;
        }
      }
      sa.getTargets().add(t);
      list.remove(t);
    }

    return true;
  } // pumpTgtAI()
  private boolean sacrificeTgtAI(final Player ai, final SpellAbility sa) {

    final Card source = sa.getHostCard();
    final TargetRestrictions tgt = sa.getTargetRestrictions();
    final boolean destroy = sa.hasParam("Destroy");

    Player opp = ai.getOpponent();
    if (tgt != null) {
      sa.resetTargets();
      if (!opp.canBeTargetedBy(sa)) {
        return false;
      }
      sa.getTargets().add(opp);
      final String valid = sa.getParam("SacValid");
      String num = sa.getParam("Amount");
      num = (num == null) ? "1" : num;
      final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);

      List<Card> list =
          CardLists.getValidCards(
              ai.getOpponent().getCardsIn(ZoneType.Battlefield),
              valid.split(","),
              sa.getActivatingPlayer(),
              sa.getHostCard());
      for (Card c : list) {
        if (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) > 3) {
          return false;
        }
      }
      if (!destroy) {
        list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(sa));
      } else {
        if (!CardLists.getKeyword(list, "Indestructible").isEmpty()) {
          // human can choose to destroy indestructibles
          return false;
        }
      }

      if (list.isEmpty()) {
        return false;
      }

      if (num.equals("X") && source.getSVar(num).equals("Count$xPaid")) {
        // Set PayX here to maximum value.
        final int xPay = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount);
        source.setSVar("PayX", Integer.toString(xPay));
      }

      final int half = (amount / 2) + (amount % 2); // Half of amount
      // rounded up

      // If the Human has at least half rounded up of the amount to be
      // sacrificed, cast the spell
      if (!sa.isTrigger() && list.size() < half) {
        return false;
      }
    }

    final String defined = sa.getParam("Defined");
    final String valid = sa.getParam("SacValid");
    if (defined == null) {
      // Self Sacrifice.
    } else if (defined.equals("Each") || (defined.equals("Opponent") && !sa.isTrigger())) {
      // If Sacrifice hits both players:
      // Only cast it if Human has the full amount of valid
      // Only cast it if AI doesn't have the full amount of Valid
      // TODO: Cast if the type is favorable: my "worst" valid is
      // worse than his "worst" valid
      final String num = sa.hasParam("Amount") ? sa.getParam("Amount") : "1";
      int amount = AbilityUtils.calculateAmount(source, num, sa);

      if (num.equals("X") && source.getSVar(num).equals("Count$xPaid")) {
        // Set PayX here to maximum value.
        amount = Math.min(ComputerUtilMana.determineLeftoverMana(sa, ai), amount);
      }

      List<Card> humanList =
          CardLists.getValidCards(
              opp.getCardsIn(ZoneType.Battlefield),
              valid.split(","),
              sa.getActivatingPlayer(),
              sa.getHostCard());

      // Since all of the cards have remAIDeck:True, I enabled 1 for 1
      // (or X for X) trades for special decks
      if (humanList.size() < amount) {
        return false;
      }
    } else if (defined.equals("You")) {
      List<Card> computerList =
          CardLists.getValidCards(
              ai.getCardsIn(ZoneType.Battlefield),
              valid.split(","),
              sa.getActivatingPlayer(),
              sa.getHostCard());
      for (Card c : computerList) {
        if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) <= 135) {
          return true;
        }
      }
      return false;
    }

    return true;
  }
  /* (non-Javadoc)
   * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
   */
  @Override
  protected boolean canPlayAI(final Player ai, SpellAbility sa) {
    // TODO: AI Support! Currently this is copied from AF ChooseCard.
    //       When implementing AI, I believe AI also needs to be made aware of the damage sources
    // chosen
    //       to be prevented (e.g. so the AI doesn't attack with a creature that will not deal any
    // damage
    //       to the player because a CoP was pre-activated on it - unless, of course, there's
    // another
    //       possible reason to attack with that creature).
    final Card host = sa.getHostCard();
    final Cost abCost = sa.getPayCosts();
    final Card source = sa.getHostCard();

    if (abCost != null) {
      // AI currently disabled for these costs
      if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
        return false;
      }

      if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
        return false;
      }

      if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
        return false;
      }

      if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
        return false;
      }
    }

    final TargetRestrictions tgt = sa.getTargetRestrictions();
    if (tgt != null) {
      sa.resetTargets();
      if (sa.canTarget(ai.getOpponent())) {
        sa.getTargets().add(ai.getOpponent());
      } else {
        return false;
      }
    }
    if (sa.hasParam("AILogic")) {
      final Game game = ai.getGame();
      if (sa.getParam("AILogic").equals("NeedsPrevention")) {
        if (!game.getStack().isEmpty()) {
          final SpellAbility topStack = game.getStack().peekAbility();
          if (sa.hasParam("Choices")
              && !topStack.getHostCard().isValid(sa.getParam("Choices"), ai, source)) {
            return false;
          }
          final ApiType threatApi = topStack.getApi();
          if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
            return false;
          }

          final Card threatSource = topStack.getHostCard();
          List<? extends GameObject> objects = getTargets(topStack);
          if (!topStack.usesTargeting()
              && topStack.hasParam("ValidPlayers")
              && !topStack.hasParam("Defined")) {
            objects =
                AbilityUtils.getDefinedPlayers(
                    threatSource, topStack.getParam("ValidPlayers"), topStack);
          }

          if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
            return false;
          }
          int dmg =
              AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
          if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
            return false;
          }
          return true;
        }
        if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
          return false;
        }
        CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
        if (sa.hasParam("Choices")) {
          choices =
              CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
        }
        final Combat combat = game.getCombat();
        choices =
            CardLists.filter(
                choices,
                new Predicate<Card>() {
                  @Override
                  public boolean apply(final Card c) {
                    if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
                      return false;
                    }
                    return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
                  }
                });
        if (choices.isEmpty()) {
          return false;
        }
      }
    }

    return true;
  }