diff --git a/src/main/java/forge/card/ability/SpellAbilityAi.java b/src/main/java/forge/card/ability/SpellAbilityAi.java index 9cf43e0c972..e05c1212bc1 100644 --- a/src/main/java/forge/card/ability/SpellAbilityAi.java +++ b/src/main/java/forge/card/ability/SpellAbilityAi.java @@ -1,6 +1,9 @@ package forge.card.ability; +import java.util.List; + +import forge.Card; import forge.card.spellability.AbilitySub; import forge.card.spellability.SpellAbility; import forge.game.ai.ComputerUtilCost; @@ -109,4 +112,14 @@ public abstract class SpellAbilityAi { System.err.println("Warning: default (ie. inherited from base class) implementation of confirmAction is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); return true; } + + public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); + return options.get(0); + } + + public Player chooseSinglePlayer(Player ai, SpellAbility sa, List options, boolean isOptional) { + System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used for " + this.getClass().getName() + ". Consider declaring an overloaded method"); + return options.get(0); + } } diff --git a/src/main/java/forge/card/ability/ai/BondAi.java b/src/main/java/forge/card/ability/ai/BondAi.java index ba79d9b2a23..f33a946f0eb 100644 --- a/src/main/java/forge/card/ability/ai/BondAi.java +++ b/src/main/java/forge/card/ability/ai/BondAi.java @@ -17,8 +17,12 @@ */ package forge.card.ability.ai; +import java.util.List; + +import forge.Card; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; +import forge.game.ai.ComputerUtilCard; import forge.game.player.Player; /** @@ -45,4 +49,10 @@ public final class BondAi extends SpellAbilityAi { protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { return true; } // end bondCanPlayAI() + + + @Override + public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + return ComputerUtilCard.getBestCreatureAI(options); + } } diff --git a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java index 5e90ab155e2..67b4d8df4e9 100644 --- a/src/main/java/forge/card/ability/ai/ChangeZoneAi.java +++ b/src/main/java/forge/card/ability/ai/ChangeZoneAi.java @@ -1382,4 +1382,11 @@ public class ChangeZoneAi extends SpellAbilityAi { return true; } + + @Override + public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + // Called when looking for creature to attach aura or equipment + return ComputerUtilCard.getBestAI(options); + } + } diff --git a/src/main/java/forge/card/ability/ai/ChooseCardAi.java b/src/main/java/forge/card/ability/ai/ChooseCardAi.java index 1de212d6e02..0af91f3301d 100644 --- a/src/main/java/forge/card/ability/ai/ChooseCardAi.java +++ b/src/main/java/forge/card/ability/ai/ChooseCardAi.java @@ -6,10 +6,12 @@ import com.google.common.base.Predicate; import forge.Card; import forge.CardLists; +import forge.CardPredicates.Presets; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.Game; +import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCombat; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -90,4 +92,57 @@ public class ChooseCardAi extends SpellAbilityAi { public boolean chkAIDrawback(SpellAbility sa, Player ai) { return canPlayAI(ai, sa); } + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean) + */ + @Override + public Card chooseSingleCard(final Player ai, SpellAbility sa, List options, boolean isOptional) { + final Card host = sa.getSourceCard(); + final String logic = sa.getParam("AILogic"); + Card choice = null; + if (logic == null) { + // Base Logic is choose "best" + choice = ComputerUtilCard.getBestAI(options); + } else if ("WorstCard".equals(logic)) { + choice = ComputerUtilCard.getWorstAI(options); + } else if (logic.equals("BestBlocker")) { + if (!CardLists.filter(options, Presets.UNTAPPED).isEmpty()) { + options = CardLists.filter(options, Presets.UNTAPPED); + } + choice = ComputerUtilCard.getBestCreatureAI(options); + } else if (logic.equals("Clone")) { + if (!CardLists.getValidCards(options, "Permanent.YouDontCtrl,Permanent.nonLegendary", host.getController(), host).isEmpty()) { + options = CardLists.getValidCards(options, "Permanent.YouDontCtrl,Permanent.nonLegendary", host.getController(), host); + } + choice = ComputerUtilCard.getBestAI(options); + } else if (logic.equals("Untap")) { + if (!CardLists.getValidCards(options, "Permanent.YouCtrl,Permanent.tapped", host.getController(), host).isEmpty()) { + options = CardLists.getValidCards(options, "Permanent.YouCtrl,Permanent.tapped", host.getController(), host); + } + choice = ComputerUtilCard.getBestAI(options); + } else if (logic.equals("NeedsPrevention")) { + List better = CardLists.filter(options, new Predicate() { + @Override + public boolean apply(final Card c) { + final Game game = ai.getGame(); + if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) { + return false; + } + if (host.getName().equals("Forcefield")) { + return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 1; + } + return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0; + } + }); + if (!better.isEmpty()) { + choice = ComputerUtilCard.getBestAI(better); + } else { + choice = ComputerUtilCard.getBestAI(options); + } + } else { + choice = ComputerUtilCard.getBestAI(options); + } + return choice; + } } diff --git a/src/main/java/forge/card/ability/ai/EncodeAi.java b/src/main/java/forge/card/ability/ai/EncodeAi.java index 50f64a9a2d4..681c186f315 100644 --- a/src/main/java/forge/card/ability/ai/EncodeAi.java +++ b/src/main/java/forge/card/ability/ai/EncodeAi.java @@ -17,8 +17,16 @@ */ package forge.card.ability.ai; +import java.util.List; + +import com.google.common.base.Predicate; + +import forge.Card; +import forge.CardLists; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; +import forge.game.ai.ComputerUtilCard; +import forge.game.phase.CombatUtil; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; @@ -58,4 +66,35 @@ public final class EncodeAi extends SpellAbilityAi { public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { return true; } + + /* (non-Javadoc) + * @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean) + */ + @Override + public Card chooseSingleCard(Player ai, SpellAbility sa, List options, boolean isOptional) { + Card choice = null; + final String logic = sa.getParam("AILogic"); + if (logic == null) { + final List attackers = CardLists.filter(options, new Predicate() { + @Override + public boolean apply(final Card c) { + return CombatUtil.canAttackNextTurn(c); + } + }); + final List unblockables = CardLists.filter(attackers, new Predicate() { + @Override + public boolean apply(final Card c) { + return !CombatUtil.canBeBlocked(c); + } + }); + if (!unblockables.isEmpty()) { + choice = ComputerUtilCard.getBestAI(unblockables); + } else if (!attackers.isEmpty()) { + choice = ComputerUtilCard.getBestAI(attackers); + } else { + choice = ComputerUtilCard.getBestAI(options); + } + } + return choice; + } } diff --git a/src/main/java/forge/game/ai/AiController.java b/src/main/java/forge/game/ai/AiController.java index a7ae13a4ee5..93ba058bef0 100644 --- a/src/main/java/forge/game/ai/AiController.java +++ b/src/main/java/forge/game/ai/AiController.java @@ -611,98 +611,6 @@ public class AiController { return discardList; } - // These methods might be moved into matching SpellAbilityAi classes just without all these switches here - public Card chooseSingleCardForEffect(List options, SpellAbility sa, String title, boolean isOptional) { - ApiType api = sa.getApi(); - if ( null == api ) { - throw new InvalidParameterException("SA is not api-based, this is not supported yet"); - } - - Card choice = null; - final Card host = sa.getSourceCard(); - final String logic = sa.getParam("AILogic"); - - switch(api) { - case Bond: - return ComputerUtilCard.getBestCreatureAI(options); - - case ChooseCard: - if (logic == null) { - // Base Logic is choose "best" - choice = ComputerUtilCard.getBestAI(options); - } else if ("WorstCard".equals(logic)) { - choice = ComputerUtilCard.getWorstAI(options); - } else if (logic.equals("BestBlocker")) { - if (!CardLists.filter(options, Presets.UNTAPPED).isEmpty()) { - options = CardLists.filter(options, Presets.UNTAPPED); - } - choice = ComputerUtilCard.getBestCreatureAI(options); - } else if (logic.equals("Clone")) { - if (!CardLists.getValidCards(options, "Permanent.YouDontCtrl,Permanent.nonLegendary", host.getController(), host).isEmpty()) { - options = CardLists.getValidCards(options, "Permanent.YouDontCtrl,Permanent.nonLegendary", host.getController(), host); - } - choice = ComputerUtilCard.getBestAI(options); - } else if (logic.equals("Untap")) { - if (!CardLists.getValidCards(options, "Permanent.YouCtrl,Permanent.tapped", host.getController(), host).isEmpty()) { - options = CardLists.getValidCards(options, "Permanent.YouCtrl,Permanent.tapped", host.getController(), host); - } - choice = ComputerUtilCard.getBestAI(options); - } else if (logic.equals("NeedsPrevention")) { - final Player ai = this.getPlayer(); - List better = CardLists.filter(options, new Predicate() { - @Override - public boolean apply(final Card c) { - if (!c.isAttacking(ai) || !game.getCombat().isUnblocked(c)) { - return false; - } - if (host.getName().equals("Forcefield")) { - return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 1; - } - return ComputerUtilCombat.damageIfUnblocked(c, ai, game.getCombat()) > 0; - } - }); - if (!better.isEmpty()) { - choice = ComputerUtilCard.getBestAI(better); - } else { - choice = ComputerUtilCard.getBestAI(options); - } - } else { - choice = ComputerUtilCard.getBestAI(options); - } - return choice; - - case Encode: - if (logic == null) { - final List attackers = CardLists.filter(options, new Predicate() { - @Override - public boolean apply(final Card c) { - return CombatUtil.canAttackNextTurn(c); - } - }); - final List unblockables = CardLists.filter(attackers, new Predicate() { - @Override - public boolean apply(final Card c) { - return !CombatUtil.canBeBlocked(c); - } - }); - if (!unblockables.isEmpty()) { - choice = ComputerUtilCard.getBestAI(unblockables); - } else if (!attackers.isEmpty()) { - choice = ComputerUtilCard.getBestAI(attackers); - } else { - choice = ComputerUtilCard.getBestAI(options); - } - } - return choice; - - case ChangeZone: // called when permanent ETB 'AttachedTo' something - return ComputerUtilCard.getBestAI(options); - - default: throw new InvalidParameterException("AI chooseSingleCard does not know how to choose card for " + api); - } - } - - @SuppressWarnings("incomplete-switch") public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { ApiType api = sa.getApi(); diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 3a1ab9cd90f..bf780fa778f 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -1,5 +1,6 @@ package forge.game.player; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -14,6 +15,7 @@ import forge.Card; import forge.CardLists; import forge.CardPredicates; import forge.GameEntity; +import forge.card.ability.ApiType; import forge.card.cost.Cost; import forge.card.mana.Mana; import forge.card.replacement.ReplacementEffect; @@ -134,7 +136,12 @@ public class PlayerControllerAi extends PlayerController { @Override public Card chooseSingleCardForEffect(List options, SpellAbility sa, String title, boolean isOptional) { - return getAi().chooseSingleCardForEffect(options, sa, title, isOptional); + ApiType api = sa.getApi(); + if ( null == api ) { + throw new InvalidParameterException("SA is not api-based, this is not supported yet"); + } + + return api.getAi().chooseSingleCard(player, sa, options, isOptional); } @Override