From f0a3d19d382cc25602660adb8c4046ea84e58360 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Sun, 10 Oct 2021 18:49:29 +0200 Subject: [PATCH 1/2] CountersAi: skip for only one against Vorinclex --- .../java/forge/ai/ability/CountersAi.java | 15 +++++++++++-- .../forge/ai/ability/CountersMultiplyAi.java | 4 +--- .../java/forge/ai/ability/CountersPutAi.java | 21 +++++++++---------- .../main/java/forge/ai/ability/PoisonAi.java | 4 ++-- .../game/replacement/ReplacementEffect.java | 1 - 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java index 2b73110bc43..a5849fb7902 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java @@ -22,13 +22,17 @@ import java.util.List; import com.google.common.base.Predicate; import forge.ai.ComputerUtilCard; +import forge.ai.SpellAbilityAi; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; +import forge.game.card.CardPredicates; import forge.game.card.CounterEnumType; import forge.game.card.CounterType; import forge.game.keyword.Keyword; +import forge.game.player.Player; +import forge.game.zone.ZoneType; import forge.util.Aggregates; @@ -40,7 +44,7 @@ import forge.util.Aggregates; * @author Forge * @version $Id$ */ -public abstract class CountersAi { +public abstract class CountersAi extends SpellAbilityAi { // An AbilityFactory subclass for Putting or Removing Counters on Cards. /** @@ -54,10 +58,16 @@ public abstract class CountersAi { * a {@link java.lang.String} object. * @param amount * a int. + * @param newParam TODO * @return a {@link forge.game.card.Card} object. */ - public static Card chooseCursedTarget(final CardCollectionView list, final String type, final int amount) { + public static Card chooseCursedTarget(final CardCollectionView list, final String type, final int amount, final Player ai) { Card choice; + + if (amount == 1 && !CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider")).isEmpty()) { + return null; + } + if (type.equals("M1M1")) { // try to kill the best killable creature, or reduce the best one // but try not to target a Undying Creature @@ -87,6 +97,7 @@ public abstract class CountersAi { */ public static Card chooseBoonTarget(final CardCollectionView list, final String type) { Card choice = null; + if (type.equals("P1P1")) { choice = ComputerUtilCard.getBestCreatureAI(list); diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java index 37222ecc21d..1b3f5d8c0e1 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java @@ -49,7 +49,6 @@ public class CountersMultiplyAi extends SpellAbilityAi { if (!c.canReceiveCounters(counterType)) { return false; } - } else { for (Map.Entry e : c.getCounters().entrySet()) { // has negative counter it would double @@ -146,7 +145,7 @@ public class CountersMultiplyAi extends SpellAbilityAi { CardCollection aiList = CardLists.filterControlledBy(list, ai); if (!aiList.isEmpty()) { // counter type list to check - // first loyalty, then P1P!, then Charge Counter + // first loyalty, then P1P1, then Charge Counter List typeList = Lists.newArrayList(CounterEnumType.LOYALTY, CounterEnumType.P1P1, CounterEnumType.CHARGE); for (CounterEnumType type : typeList) { // enough targets @@ -182,7 +181,6 @@ public class CountersMultiplyAi extends SpellAbilityAi { private void addTargetsByCounterType(final Player ai, final SpellAbility sa, final CardCollection list, final CounterType type) { - CardCollection newList = CardLists.filter(list, CardPredicates.hasCounter(type)); if (newList.isEmpty()) { return; diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java index 00a55dce474..06608e6a93b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -53,7 +53,7 @@ import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.MyRandom; -public class CountersPutAi extends SpellAbilityAi { +public class CountersPutAi extends CountersAi { /* * (non-Javadoc) @@ -179,7 +179,7 @@ public class CountersPutAi extends SpellAbilityAi { if (abTgt.canTgtCreature()) { // try to kill creature with -1/-1 counters if it can - // receive counters, execpt it has undying + // receive counters, except it has undying CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa); CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1)); oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING); @@ -549,7 +549,7 @@ public class CountersPutAi extends SpellAbilityAi { } if (sa.isCurse()) { - choice = CountersAi.chooseCursedTarget(list, type, amount); + choice = chooseCursedTarget(list, type, amount, ai); } else { if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) { for (Card c : list) { @@ -564,15 +564,15 @@ public class CountersPutAi extends SpellAbilityAi { if (abCost == null || (ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn().isOpponentOf(ai))) { // only use at opponent EOT unless it is free - choice = CountersAi.chooseBoonTarget(list, type); + choice = chooseBoonTarget(list, type); } } } if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Dromoka's Command")) { - choice = CountersAi.chooseBoonTarget(list, type); + choice = chooseBoonTarget(list, type); } } else { - choice = CountersAi.chooseBoonTarget(list, type); + choice = chooseBoonTarget(list, type); } } @@ -681,7 +681,6 @@ public class CountersPutAi extends SpellAbilityAi { sa.resetTargets(); // target loop while (sa.canAddMoreTarget()) { - if (list.isEmpty()) { if (!sa.isTargetNumberValid() || sa.getTargets().size() == 0) { @@ -693,7 +692,7 @@ public class CountersPutAi extends SpellAbilityAi { } if (sa.isCurse()) { - choice = CountersAi.chooseCursedTarget(list, type, amount); + choice = chooseCursedTarget(list, type, amount, ai); } else { CardCollection lands = CardLists.filter(list, CardPredicates.Presets.LANDS); SpellAbility animate = sa.findSubAbilityByType(ApiType.Animate); @@ -702,7 +701,7 @@ public class CountersPutAi extends SpellAbilityAi { } else if ("BoonCounterOnOppCreature".equals(logic)) { choice = ComputerUtilCard.getWorstCreatureAI(list); } else { - choice = CountersAi.chooseBoonTarget(list, type); + choice = chooseBoonTarget(list, type); } } @@ -838,7 +837,7 @@ public class CountersPutAi extends SpellAbilityAi { // Choose targets here: if (sa.isCurse()) { if (preferred) { - choice = CountersAi.chooseCursedTarget(list, type, amount); + choice = chooseCursedTarget(list, type, amount, ai); if (choice == null && mandatory) { choice = Aggregates.random(list); } @@ -852,7 +851,7 @@ public class CountersPutAi extends SpellAbilityAi { } else { if (preferred) { list = ComputerUtil.getSafeTargets(ai, sa, list); - choice = CountersAi.chooseBoonTarget(list, type); + choice = chooseBoonTarget(list, type); if (choice == null && mandatory) { choice = Aggregates.random(list); } diff --git a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java index 5761c79832c..b9547f2e875 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java @@ -104,7 +104,7 @@ public class PoisonAi extends SpellAbilityAi { if (!betterTgts.isEmpty()) { tgts = betterTgts; } else if (mandatory) { - // no better choice but better than hiting himself + // no better choice but better than hitting himself sa.getTargets().add(tgts.getFirst()); return true; } @@ -121,7 +121,7 @@ public class PoisonAi extends SpellAbilityAi { // need to target something, try to target allies PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa)); if (!allies.isEmpty()) { - // some ally would be uneffected + // some ally would be unaffected PlayerCollection betterAllies = allies.filter(new Predicate() { @Override public boolean apply(Player input) { diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java index 5d442c4ccb9..a0e2011a456 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java @@ -145,7 +145,6 @@ public abstract class ReplacementEffect extends TriggerReplacementBase { } public boolean requirementsCheck(Game game, Map params) { - if (this.isSuppressed()) { return false; // Effect removed by effect } From 72d0de96e28919120459c85d42111a7c14e03810 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Sun, 10 Oct 2021 20:51:35 +0200 Subject: [PATCH 2/2] Improve poison combat checks --- forge-ai/src/main/java/forge/ai/AiController.java | 4 +++- forge-ai/src/main/java/forge/ai/ComputerUtilCard.java | 4 ++-- .../src/main/java/forge/ai/ComputerUtilCombat.java | 10 +++++++++- .../src/main/java/forge/ai/ability/CountersAi.java | 4 +++- .../java/forge/ai/ability/CountersProliferateAi.java | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index fbfa76e144a..41871f29d8f 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -2253,6 +2253,7 @@ public class AiController { return true; } + // AI logic for choosing which replacement effect to apply happens here. public ReplacementEffect chooseSingleReplacementEffect(List list) { // no need to choose anything if (list.size() <= 1) { @@ -2291,7 +2292,8 @@ public class AiController { } } - // AI logic for choosing which replacement effect to apply happens here. + // TODO always lower counters with Vorinclex first, might turn it from 1 to 0 as final + return Iterables.getFirst(list, null); } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 1573293dae3..b710292a252 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1488,8 +1488,8 @@ public class ComputerUtilCard { if (combat.isAttacking(c) && opp.getLife() > 0) { int dmg = ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true); int pumpedDmg = ComputerUtilCombat.damageIfUnblocked(pumped, opp, pumpedCombat, true); - int poisonOrig = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0; - int poisonPumped = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0; + int poisonOrig = ComputerUtilCombat.poisonIfUnblocked(c, ai); + int poisonPumped = ComputerUtilCombat.poisonIfUnblocked(pumped, ai); // predict Infect if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index d2aff0d0aa2..4af5821452c 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -235,17 +235,25 @@ public class ComputerUtilCombat { * @return a int. */ public static int poisonIfUnblocked(final Card attacker, final Player attacked) { + if (!attacked.canReceiveCounters(CounterEnumType.POISON)) { + return 0; + } int damage = attacker.getNetCombatDamage(); int poison = 0; damage += predictPowerBonusOfAttacker(attacker, null, null, false); if (attacker.hasKeyword(Keyword.INFECT)) { int pd = predictDamageTo(attacked, damage, attacker, true); + // opponent can always order it so that he gets 0 + if (pd == 1 && Iterables.any(attacker.getController().getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) { + pd = 0; + } poison += pd; if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { poison += pd; } } - if (attacker.hasKeyword(Keyword.POISONOUS) && (damage > 0)) { + if (attacker.hasKeyword(Keyword.POISONOUS) && damage > 0) { + // TODO need to check for magnitude 1, each of their triggers could be replaced to 0 poison += attacker.getKeywordMagnitude(Keyword.POISONOUS); } return poison; diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java index a5849fb7902..c3e084dd0dc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java @@ -20,6 +20,7 @@ package forge.ai.ability; import java.util.List; import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import forge.ai.ComputerUtilCard; import forge.ai.SpellAbilityAi; @@ -64,7 +65,8 @@ public abstract class CountersAi extends SpellAbilityAi { public static Card chooseCursedTarget(final CardCollectionView list, final String type, final int amount, final Player ai) { Card choice; - if (amount == 1 && !CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider")).isEmpty()) { + // opponent can always order it so that he gets 0 + if (amount == 1 && Iterables.any(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) { return null; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java index 1521e80d944..86269e88bf7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java @@ -63,7 +63,7 @@ public class CountersProliferateAi extends SpellAbilityAi { boolean opponentPoison = false; for (final Player o : ai.getOpponents()) { - opponentPoison |= o.getPoisonCounters() >= 1; + opponentPoison |= o.getPoisonCounters() > 0 && o.canReceiveCounters(CounterEnumType.POISON); hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate() { @Override public boolean apply(final Card crd) {