From b19b5daf48b4920e8edb18e54cc2a3fcf3d3ae65 Mon Sep 17 00:00:00 2001 From: drdev Date: Fri, 13 Dec 2013 02:02:54 +0000 Subject: [PATCH] Refactor confirmTrigger into controller class --- .../forge/game/player/PlayerController.java | 23 +- .../forge/game/player/PlayerControllerAi.java | 119 +++++-- .../game/player/PlayerControllerHuman.java | 38 ++- .../forge/game/trigger/WrappedAbility.java | 71 +--- .../util/PlayerControllerForTests.java | 320 +++++++++--------- 5 files changed, 297 insertions(+), 274 deletions(-) diff --git a/forge-gui/src/main/java/forge/game/player/PlayerController.java b/forge-gui/src/main/java/forge/game/player/PlayerController.java index 1a53c2eaee5..f26492bedd2 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerController.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerController.java @@ -10,6 +10,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import com.google.common.base.Predicate; + import forge.card.ColorSet; import forge.deck.Deck; import forge.game.Game; @@ -27,6 +28,7 @@ import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.TargetChoices; +import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.item.PaperCard; @@ -43,19 +45,19 @@ public abstract class PlayerController { DeclareBlocker, Echo, Multikicker, - Replicate, + Replicate, CumulativeUpkeep; } - + public static enum BinaryChoiceType { HeadsOrTails, // coin TapOrUntap, PlayOrDraw, OddsOrEvens } - + protected final Game game; - + private PhaseType autoPassUntil = null; protected final Player player; protected final LobbyPlayer lobbyPlayer; @@ -95,14 +97,14 @@ public abstract class PlayerController { // End of Triggers preliminary choice public LobbyPlayer getLobbyPlayer() { return lobbyPlayer; } - + /** * Uses GUI to learn which spell the player (human in our case) would like to play */ public SpellAbility getAbilityToPlay(List abilities) { return getAbilityToPlay(abilities, null); } - + /** * Uses GUI to learn which spell the player (human in our case) would like to play */ @@ -135,8 +137,9 @@ public abstract class PlayerController { public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title); public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message); - public abstract boolean getWillPlayOnFirstTurn(boolean isFirstGame); public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message); + public abstract boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map triggerParams, boolean isMandatory); + public abstract boolean getWillPlayOnFirstTurn(boolean isFirstGame); public abstract List orderBlockers(Card attacker, List blockers); public abstract List orderAttackers(Card blocker, List attackers); @@ -148,7 +151,7 @@ public abstract class PlayerController { public abstract ImmutablePair, List> arrangeForScry(List topN); public abstract boolean willPutCardOnTop(Card c); public abstract List orderMoveToZoneList(List cards, ZoneType destinationZone); - + /** p = target player, validCards - possible discards, min cards to discard */ public abstract List chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, List validCards, int min, int max); public abstract Card chooseCardToDredge(List dredgers); @@ -168,7 +171,7 @@ public abstract class PlayerController { public abstract void declareAttackers(Player attacker, Combat combat); public abstract void declareBlockers(Player defender, Combat combat); public abstract void takePriority(); - + public abstract List chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose); @@ -181,7 +184,7 @@ public abstract class PlayerController { public abstract List chooseModeForAbility(SpellAbility sa, int min, int num); public abstract byte chooseColor(String message, SpellAbility sa, ColorSet colors); - + public abstract PaperCard chooseSinglePaperCard(SpellAbility sa, String message, Predicate cpp, String name); public abstract List chooseColors(String message, SpellAbility sa, int min, int max, List options); public abstract CounterType chooseCounterType(Collection options, SpellAbility sa, String prompt); diff --git a/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java b/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java index ffa729cb0a9..f5f6645d017 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java @@ -47,6 +47,7 @@ import forge.game.spellability.Spell; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.TargetChoices; +import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.item.PaperCard; import forge.util.Aggregates; @@ -64,7 +65,7 @@ public class PlayerControllerAi extends PlayerController { public PlayerControllerAi(Game game, Player p, LobbyPlayer lp) { super(game, p, lp); - brains = new AiController(p, game); + brains = new AiController(p, game); } /** @@ -73,11 +74,13 @@ public class PlayerControllerAi extends PlayerController { public SpellAbility getAbilityToPlay(List abilities, MouseEvent triggerEvent) { if (abilities.size() == 0) { return null; - } else + } + else { return abilities.get(0); -// } else { -// return GuiChoose.oneOrNone("Choose ability for AI to play", abilities); // some day network interaction will be here -// } + } +// else { +// return GuiChoose.oneOrNone("Choose ability for AI to play", abilities); // some day network interaction will be here +// } } /** @@ -140,11 +143,11 @@ public class PlayerControllerAi extends PlayerController { } return chosen; } - + @Override public Card chooseSingleCardForEffect(Collection options, SpellAbility sa, String title, boolean isOptional) { ApiType api = sa.getApi(); - if ( null == api ) { + if (null == api) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } return api.getAi().chooseSingleCard(player, sa, options, isOptional); @@ -153,7 +156,7 @@ public class PlayerControllerAi extends PlayerController { @Override public Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title) { ApiType api = sa.getApi(); - if ( null == api ) { + if (null == api) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } return api.getAi().chooseSinglePlayer(player, sa, options); @@ -162,27 +165,67 @@ public class PlayerControllerAi extends PlayerController { @Override public SpellAbility chooseSingleSpellForEffect(java.util.List spells, SpellAbility sa, String title) { ApiType api = sa.getApi(); - if ( null == api ) { + if (null == api) { throw new InvalidParameterException("SA is not api-based, this is not supported yet"); } return api.getAi().chooseSingleSpellAbility(player, sa, spells); } - - + @Override public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { return getAi().confirmAction(sa, mode, message); } - @Override - public boolean getWillPlayOnFirstTurn(boolean isFirstGame) { - return true; // AI is brave :) - } @Override public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { return getAi().confirmStaticApplication(hostCard, affected, logic, message); } + @Override + public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map triggerParams, boolean isMandatory) { + if (triggerParams.containsKey("DelayedTrigger")) { + //TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker, + // needs to be expanded when a more difficult cards comes up + return true; + } + // Store/replace target choices more properly to get this SA cleared. + TargetChoices tc = null; + TargetChoices subtc = null; + boolean storeChoices = sa.getTargetRestrictions() != null; + final SpellAbility sub = sa.getSubAbility(); + boolean storeSubChoices = sub != null && sub.getTargetRestrictions() != null; + boolean ret = true; + + if (storeChoices) { + tc = sa.getTargets(); + sa.resetTargets(); + } + if (storeSubChoices) { + subtc = sub.getTargets(); + sub.resetTargets(); + } + // There is no way this doTrigger here will have the same target as stored above + // So it's possible it's making a different decision here than will actually happen + if (!sa.doTrigger(isMandatory, player)) { + ret = false; + } + if (storeChoices) { + sa.resetTargets(); + sa.setTargets(tc); + } + if (storeSubChoices) { + sub.resetTargets(); + sub.setTargets(subtc); + } + + return ret; + } + + @Override + public boolean getWillPlayOnFirstTurn(boolean isFirstGame) { + return true; // AI is brave :) + } + @Override public List orderBlockers(Card attacker, List blockers) { return AiBlockController.orderBlockers(attacker, blockers); @@ -207,10 +250,12 @@ public class PlayerControllerAi extends PlayerController { List toTop = new ArrayList(); for (Card c: topN) { - if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c)) + if (ComputerUtil.scryWillMoveCardToBottomOfLibrary(player, c)) { toBottom.add(c); - else - toTop.add(c); + } + else { + toTop.add(c); + } } // put the rest on top in random order @@ -218,7 +263,6 @@ public class PlayerControllerAi extends PlayerController { return ImmutablePair.of(toTop, toBottom); } - @Override public boolean willPutCardOnTop(Card c) { return true; // AI does not know what will happen next (another clash or that would become his topdeck) @@ -232,11 +276,12 @@ public class PlayerControllerAi extends PlayerController { @Override public List chooseCardsToDiscardFrom(Player p, SpellAbility sa, List validCards, int min, int max) { - if ( p == player ) + if (p == player) { return brains.getCardsToDiscard(min, max, validCards, sa); - + } + boolean isTargetFriendly = !p.isOpponentOf(player); - + return isTargetFriendly ? ComputerUtil.getCardsToDiscardFromFriend(player, p, sa, validCards, min, max) : ComputerUtil.getCardsToDiscardFromOpponent(player, p, sa, validCards, min, max); @@ -253,13 +298,14 @@ public class PlayerControllerAi extends PlayerController { @Override public void playSpellAbilityForFree(SpellAbility copySA, boolean mayChooseNewTargets) { // Ai is known to set targets in doTrigger, so if it cannot choose new targets, we won't call canPlays - if( mayChooseNewTargets ) { + if (mayChooseNewTargets) { if (copySA instanceof Spell) { Spell spell = (Spell) copySA; if (!spell.canPlayFromEffectAI(player, true, true)) { return; // is this legal at all? } - } else { + } + else { copySA.canPlayAI(player); } } @@ -268,7 +314,7 @@ public class PlayerControllerAi extends PlayerController { @Override public void playSpellAbilityNoStack(SpellAbility effectSA, boolean canSetupTargets) { - if ( canSetupTargets ) + if (canSetupTargets) effectSA.doTrigger(true, player); // first parameter does not matter, since return value won't be used ComputerUtil.playNoStack(player, effectSA, game); } @@ -320,10 +366,10 @@ public class PlayerControllerAi extends PlayerController { @Override public String chooseSomeType(String kindOfType, SpellAbility sa, List validTypes, List invalidTypes) { String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), invalidTypes); - if( StringUtils.isBlank(chosen) && !validTypes.isEmpty() ) + if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) { chosen = validTypes.get(0); - Log.warn("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen" ); + Log.warn("AI has no idea how to choose " + kindOfType +", defaulting to 1st element: chosen"); } game.getAction().nofityOfValue(sa, null, "Computer picked: " + chosen, player); return chosen; @@ -338,14 +384,17 @@ public class PlayerControllerAi extends PlayerController { } @Override - public List getCardsToMulligan(boolean isCommander, Player firstPlayer) { - if( !ComputerUtil.wantMulligan(player) ) + public List getCardsToMulligan(boolean isCommander, Player firstPlayer) { + if (!ComputerUtil.wantMulligan(player)) { return null; + } - if (!isCommander) + if (!isCommander) { return player.getCardsIn(ZoneType.Hand); - else + } + else { return ComputerUtil.getPartialParisCandidates(player); + } } @Override @@ -353,7 +402,6 @@ public class PlayerControllerAi extends PlayerController { brains.declareAttackers(attacker, combat); } - @Override public void declareBlockers(Player defender, Combat combat) { brains.declareBlockersFor(defender, combat); @@ -361,8 +409,9 @@ public class PlayerControllerAi extends PlayerController { @Override public void takePriority() { - if ( !game.isGameOver() ) + if (!game.isGameOver()) { brains.onPriorityRecieved(); + } // use separate thread for AI? } @@ -436,7 +485,7 @@ public class PlayerControllerAi extends PlayerController { @Override public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { - if(kindOfChoice == BinaryChoiceType.TapOrUntap) return true; + if (kindOfChoice == BinaryChoiceType.TapOrUntap) { return true; } return MyRandom.getRandom().nextBoolean(); } @@ -518,7 +567,7 @@ public class PlayerControllerAi extends PlayerController { @Override public CounterType chooseCounterType(Collection options, SpellAbility sa, String prompt) { // may write a smarter AI if you need to (with calls to AI-clas for given API ability) - + // TODO: ArsenalNut (06 Feb 12)computer needs // better logic to pick a counter type and probably // an initial target diff --git a/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java index 62092aaabef..b0a853e2f82 100644 --- a/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -49,6 +49,7 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.TargetChoices; import forge.game.spellability.TargetSelection; +import forge.game.trigger.Trigger; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; @@ -403,6 +404,36 @@ public class PlayerControllerHuman extends PlayerController { return GuiDialog.confirm(hostCard, message); } + @Override + public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map triggerParams, boolean isMandatory) { + if (this.shouldAlwaysAcceptTrigger(regtrig.getId())) { + return true; + } + if (this.shouldAlwaysDeclineTrigger(regtrig.getId())) { + return false; + } + + String triggerDesc = triggerParams.get("TriggerDescription").replace("CARDNAME", regtrig.getHostCard().getName()); + final StringBuilder buildQuestion = new StringBuilder("Use triggered ability of "); + buildQuestion.append(regtrig.getHostCard().toString()).append("?"); + buildQuestion.append("\r\n(").append(triggerDesc).append(")\r\n"); + HashMap tos = sa.getTriggeringObjects(); + if (tos.containsKey("Attacker")) { + buildQuestion.append("[Attacker: " + tos.get("Attacker") + "]"); + } + if (tos.containsKey("Card")) { + Card card = (Card) tos.get("Card"); + if (card != null && (card.getController() == player || game.getZoneOf(card) == null + || game.getZoneOf(card).getZoneType().isKnown())) { + buildQuestion.append("[Triggering card: " + tos.get("Card") + "]"); + } + } + if (!GuiDialog.confirm(regtrig.getHostCard(), buildQuestion.toString())) { + return false; + } + return true; + } + @Override public boolean getWillPlayOnFirstTurn(boolean isFirstGame) { InputPlayOrDraw inp = new InputPlayOrDraw(player, isFirstGame); @@ -869,10 +900,10 @@ public class PlayerControllerHuman extends PlayerController { default: String[] colorNames = new String[cntColors]; int i = 0; - for(byte b : colors) { + for (byte b : colors) { colorNames[i++] = MagicColor.toLongString(b); } - if(colorNames.length > 2) { + if (colorNames.length > 2) { return MagicColor.fromName(GuiChoose.one(message, colorNames)); } int idxChosen = GuiDialog.confirm(sa.getSourceCard(), message, colorNames) ? 0 : 1; @@ -890,8 +921,9 @@ public class PlayerControllerHuman extends PlayerController { @Override public CounterType chooseCounterType(Collection options, SpellAbility sa, String prompt) { - if( options.size() <= 1) + if (options.size() <= 1) { return Iterables.getFirst(options, null); + } return GuiChoose.one(prompt, options); } } diff --git a/forge-gui/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-gui/src/main/java/forge/game/trigger/WrappedAbility.java index 07e5ea19565..d7b19254a92 100644 --- a/forge-gui/src/main/java/forge/game/trigger/WrappedAbility.java +++ b/forge-gui/src/main/java/forge/game/trigger/WrappedAbility.java @@ -18,7 +18,6 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityRestriction; import forge.game.spellability.TargetChoices; import forge.game.spellability.TargetRestrictions; -import forge.gui.GuiDialog; // Wrapper ability that checks the requirements again just before // resolving, for intervening if clauses. @@ -356,8 +355,9 @@ public class WrappedAbility extends Ability implements ISpellAbility { TriggerHandler th = game.getTriggerHandler(); Map triggerParams = regtrig.getMapParams(); - if (decider != null && !confirmTrigger(decider, triggerParams)) + if (decider != null && !decider.getController().confirmTrigger(sa, regtrig, triggerParams, this.isMandatory())) { return; + } getActivatingPlayer().getController().playSpellAbilityNoStack(sa, false); @@ -370,71 +370,4 @@ public class WrappedAbility extends Ability implements ISpellAbility { th.registerDelayedTrigger(deltrig); } } - - private boolean confirmTrigger(Player decider, Map triggerParams) { - if (decider.isHuman()) { - if(decider.getController().shouldAlwaysAcceptTrigger(regtrig.getId())) - return true; - else if(decider.getController().shouldAlwaysDeclineTrigger(regtrig.getId())) - return false; - - String triggerDesc = triggerParams.get("TriggerDescription").replace("CARDNAME", regtrig.getHostCard().getName()); - final StringBuilder buildQuestion = new StringBuilder("Use triggered ability of "); - buildQuestion.append(regtrig.getHostCard().toString()).append("?"); - buildQuestion.append("\r\n(").append(triggerDesc).append(")\r\n"); - HashMap tos = sa.getTriggeringObjects(); - if (tos.containsKey("Attacker")) { - buildQuestion.append("[Attacker: " + tos.get("Attacker") + "]"); - } - if (tos.containsKey("Card")) { - Card card = (Card) tos.get("Card"); - if (card != null && (card.getController() == decider || decider.getGame().getZoneOf(card) == null - || decider.getGame().getZoneOf(card).getZoneType().isKnown())) { - buildQuestion.append("[Triggering card: " + tos.get("Card") + "]"); - } - } - if (!GuiDialog.confirm(regtrig.getHostCard(), buildQuestion.toString())) { - return false; - } - return true; - } // human end - - if (triggerParams.containsKey("DelayedTrigger")) { - //TODO: The only card with an optional delayed trigger is Shirei, Shizo's Caretaker, - // needs to be expanded when a more difficult cards comes up - return true; - } - // Store/replace target choices more properly to get this SA cleared. - TargetChoices tc = null; - TargetChoices subtc = null; - boolean storeChoices = sa.getTargetRestrictions() != null; - final SpellAbility sub = sa.getSubAbility(); - boolean storeSubChoices = sub != null && sub.getTargetRestrictions() != null; - boolean ret = true; - - if (storeChoices) { - tc = sa.getTargets(); - sa.resetTargets(); - } - if (storeSubChoices) { - subtc = sub.getTargets(); - sub.resetTargets(); - } - // There is no way this doTrigger here will have the same target as stored above - // So it's possible it's making a different decision here than will actually happen - if (!sa.doTrigger(this.isMandatory(), decider)) { - ret = false; - } - if (storeChoices) { - sa.resetTargets(); - sa.setTargets(tc); - } - if (storeSubChoices) { - sub.resetTargets(); - sub.setTargets(subtc); - } - - return ret; - } - } diff --git a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index f47b49989a3..ccd84726de5 100644 --- a/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -41,6 +41,7 @@ import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.TargetChoices; +import forge.game.trigger.Trigger; import forge.game.zone.ZoneType; import forge.gamesimulationtests.util.card.CardSpecification; import forge.gamesimulationtests.util.card.CardSpecificationHandler; @@ -63,387 +64,392 @@ import forge.item.PaperCard; */ public class PlayerControllerForTests extends PlayerController { private PlayerActions playerActions; - - public PlayerControllerForTests( Game game, Player player, LobbyPlayer lobbyPlayer ) { - super( game, player, lobbyPlayer ); + + public PlayerControllerForTests(Game game, Player player, LobbyPlayer lobbyPlayer) { + super(game, player, lobbyPlayer); } - - public void setPlayerActions( PlayerActions playerActions ) { + + public void setPlayerActions(PlayerActions playerActions) { this.playerActions = playerActions; } - + public PlayerActions getPlayerActions() { return playerActions; } - + public Player getPlayer() { return player; } - + public Game getGame() { return game; } - + @Override - public SpellAbility getAbilityToPlay( List abilities ) { - System.out.println( "getAbilityToPlay " + StringUtils.join( abilities, ", " ) ); + public SpellAbility getAbilityToPlay(List abilities) { + System.out.println("getAbilityToPlay " + StringUtils.join(abilities, ", ")); return null; } @Override - public void playSpellAbilityForFree( SpellAbility copySA, boolean mayChoseNewTargets ) { - throw new IllegalStateException( "Callers of this method currently assume that it performs extra functionality!" ); + public void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); } @Override - public void playSpellAbilityNoStack( SpellAbility effectSA, boolean mayChoseNewTargets ) { + public void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets) { //TODO: eventually (when the real code is refactored) this should be handled normally... - if( effectSA.getDescription().equals( "At the beginning of your upkeep, if you have exactly 1 life, you win the game." ) ) {//test_104_2b_effect_may_state_that_player_wins_the_game - HumanPlay.playSpellAbilityNoStack( player, effectSA, !mayChoseNewTargets ); + if (effectSA.getDescription().equals("At the beginning of your upkeep, if you have exactly 1 life, you win the game.")) {//test_104_2b_effect_may_state_that_player_wins_the_game + HumanPlay.playSpellAbilityNoStack(player, effectSA, !mayChoseNewTargets); return; } - if( - ( effectSA.getSourceCard().getName().equals( "Nefarious Lich" ) && effectSA.getApi().getAi() instanceof DrawAi ) || - ( effectSA.getSourceCard().getName().equals( "Laboratory Maniac" ) && effectSA.getApi().getAi() instanceof GameWinAi ) || - ( effectSA.getSourceCard().getName().equals( "Nefarious Lich" ) && effectSA.getApi().getAi() instanceof ChangeZoneAi ) + if ( + (effectSA.getSourceCard().getName().equals("Nefarious Lich") && effectSA.getApi().getAi() instanceof DrawAi) || + (effectSA.getSourceCard().getName().equals("Laboratory Maniac") && effectSA.getApi().getAi() instanceof GameWinAi) || + (effectSA.getSourceCard().getName().equals("Nefarious Lich") && effectSA.getApi().getAi() instanceof ChangeZoneAi) ) {//test_104_3f_if_a_player_would_win_and_lose_simultaneously_he_loses - HumanPlay.playSpellAbilityNoStack( player, effectSA, !mayChoseNewTargets ); + HumanPlay.playSpellAbilityNoStack(player, effectSA, !mayChoseNewTargets); return; } - throw new IllegalStateException( "Callers of this method currently assume that it performs extra functionality!" ); + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); } @Override - public Deck sideboard( Deck deck, GameType gameType ) { + public Deck sideboard(Deck deck, GameType gameType) { return deck; } @Override - public Map assignCombatDamage( Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder ) { - if( blockers.size() == 1 && damageDealt == 2 && ( - ( attacker.getName().equals( "Grizzly Bears" ) && blockers.get( 0 ).getName().equals( "Ajani's Sunstriker" ) ) || - ( attacker.getName().equals( "Ajani's Sunstriker" ) && blockers.get( 0 ).getName().equals( "Grizzly Bears" ) ) - ) ) {//test_104_3b_player_with_less_than_zero_life_loses_the_game_only_when_a_player_receives_priority_variant_with_combat + public Map assignCombatDamage(Card attacker, List blockers, int damageDealt, GameEntity defender, boolean overrideOrder) { + if (blockers.size() == 1 && damageDealt == 2 && ( + (attacker.getName().equals("Grizzly Bears") && blockers.get(0).getName().equals("Ajani's Sunstriker")) || + (attacker.getName().equals("Ajani's Sunstriker") && blockers.get(0).getName().equals("Grizzly Bears")) + )) {//test_104_3b_player_with_less_than_zero_life_loses_the_game_only_when_a_player_receives_priority_variant_with_combat Map result = new HashMap(); - result.put( blockers.get( 0 ), damageDealt ); + result.put(blockers.get(0), damageDealt); return result; } - throw new IllegalStateException( "Erring on the side of caution here..." ); + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public Integer announceRequirements( SpellAbility ability, String announce, boolean allowZero ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public Integer announceRequirements(SpellAbility ability, String announce, boolean allowZero) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public List choosePermanentsToSacrifice( SpellAbility sa, int min, int max, List validTargets, String message ) { - return chooseItems( validTargets, min ); + public List choosePermanentsToSacrifice(SpellAbility sa, int min, int max, List validTargets, String message) { + return chooseItems(validTargets, min); } @Override - public List choosePermanentsToDestroy( SpellAbility sa, int min, int max, List validTargets, String message ) { - return chooseItems( validTargets, min ); + public List choosePermanentsToDestroy(SpellAbility sa, int min, int max, List validTargets, String message) { + return chooseItems(validTargets, min); } @Override - public TargetChoices chooseNewTargetsFor( SpellAbility ability ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public TargetChoices chooseNewTargetsFor(SpellAbility ability) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public Pair chooseTarget( SpellAbility sa, List> allTargets ) { - return chooseItem( allTargets ); + public Pair chooseTarget(SpellAbility sa, List> allTargets) { + return chooseItem(allTargets); } @Override - public List chooseCardsForEffect( List sourceList, SpellAbility sa, String title, int amount, boolean isOptional ) { - return chooseItems( sourceList, amount ); + public List chooseCardsForEffect(List sourceList, SpellAbility sa, String title, int amount, boolean isOptional) { + return chooseItems(sourceList, amount); } @Override - public Card chooseSingleCardForEffect( Collection sourceList, SpellAbility sa, String title, boolean isOptional ) { - return chooseItem( sourceList ); + public Card chooseSingleCardForEffect(Collection sourceList, SpellAbility sa, String title, boolean isOptional) { + return chooseItem(sourceList); } @Override - public Player chooseSinglePlayerForEffect( List options, SpellAbility sa, String title ) { - return chooseItem( options ); + public Player chooseSinglePlayerForEffect(List options, SpellAbility sa, String title) { + return chooseItem(options); } @Override - public SpellAbility chooseSingleSpellForEffect( List spells, SpellAbility sa, String title ) { - return chooseItem( spells ); + public SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title) { + return chooseItem(spells); } @Override - public boolean confirmAction( SpellAbility sa, PlayerActionConfirmMode mode, String message ) { + public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { return true; } @Override - public boolean getWillPlayOnFirstTurn( boolean isFirstGame ) { + public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) { return true; } @Override - public boolean confirmStaticApplication( Card hostCard, GameEntity affected, String logic, String message ) { - return true; - } + public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map triggerParams, boolean isMandatory) { + return true; + } + + @Override + public boolean getWillPlayOnFirstTurn(boolean isFirstGame) { + return true; + } @Override - public List orderBlockers( Card attacker, List blockers ) { + public List orderBlockers(Card attacker, List blockers) { return blockers; } @Override - public List orderAttackers( Card blocker, List attackers ) { + public List orderAttackers(Card blocker, List attackers) { return attackers; } @Override - public void reveal( String string, Collection cards, ZoneType zone, Player owner ) { + public void reveal(String string, Collection cards, ZoneType zone, Player owner) { //nothing needs to be done here } @Override - public void notifyOfValue( SpellAbility saSource, GameObject realtedTarget, String value ) { + public void notifyOfValue(SpellAbility saSource, GameObject realtedTarget, String value) { //nothing needs to be done here } @Override - public ImmutablePair, List> arrangeForScry( List topN ) { - return ImmutablePair.of( topN, null ); + public ImmutablePair, List> arrangeForScry(List topN) { + return ImmutablePair.of(topN, null); } @Override - public boolean willPutCardOnTop( Card c ) { + public boolean willPutCardOnTop(Card c) { return false; } @Override - public List orderMoveToZoneList( List cards, ZoneType destinationZone ) { + public List orderMoveToZoneList(List cards, ZoneType destinationZone) { return cards; } @Override - public List chooseCardsToDiscardFrom( Player playerDiscard, SpellAbility sa, List validCards, int min, int max ) { - return chooseItems( validCards, min ); + public List chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, List validCards, int min, int max) { + return chooseItems(validCards, min); } @Override - public Card chooseCardToDredge( List dredgers ) { + public Card chooseCardToDredge(List dredgers) { return null; } @Override - public void playMiracle( SpellAbility miracle, Card card ) { - throw new IllegalStateException( "Callers of this method currently assume that it performs extra functionality!" ); + public void playMiracle(SpellAbility miracle, Card card) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); } @Override - public List chooseCardsToDelve( int colorLessAmount, List grave ) { + public List chooseCardsToDelve(int colorLessAmount, List grave) { return Collections.emptyList(); } @Override - public List chooseCardsToRevealFromHand( int min, int max, List valid ) { - return chooseItems( valid, min ); + public List chooseCardsToRevealFromHand(int min, int max, List valid) { + return chooseItems(valid, min); } @Override - public List chooseCardsToDiscardUnlessType( int min, List hand, String param, SpellAbility sa ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public List chooseCardsToDiscardUnlessType(int min, List hand, String param, SpellAbility sa) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public List chooseSaToActivateFromOpeningHand( List usableFromOpeningHand ) { + public List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand) { return usableFromOpeningHand; } @Override - public Mana chooseManaFromPool( List manaChoices ) { - return chooseItem( manaChoices ); + public Mana chooseManaFromPool(List manaChoices) { + return chooseItem(manaChoices); } @Override - public Pair chooseAndRemoveOrPutCounter( Card cardWithCounter ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public Pair chooseAndRemoveOrPutCounter(Card cardWithCounter) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public boolean confirmReplacementEffect( ReplacementEffect replacementEffect, SpellAbility effectSA, String question ) { + public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) { return true; } @Override - public List getCardsToMulligan( boolean isCommander, Player firstPlayer ) { + public List getCardsToMulligan(boolean isCommander, Player firstPlayer) { return null; } @Override - public void declareAttackers( Player attacker, Combat combat ) { + public void declareAttackers(Player attacker, Combat combat) { //Doing nothing is safe in most cases, but not all (creatures that must attack etc). TODO: introduce checks? - if( playerActions == null ) { + if (playerActions == null) { return; } - DeclareAttackersAction declareAttackers = playerActions.getNextActionIfApplicable( player, game, DeclareAttackersAction.class ); - if( declareAttackers == null ) { + DeclareAttackersAction declareAttackers = playerActions.getNextActionIfApplicable(player, game, DeclareAttackersAction.class); + if (declareAttackers == null) { return; } - + //TODO: check that the chosen attack configuration is legal? (Including creatures that did not attack but should) //TODO: check that the chosen attack configuration was a complete match to what was requested? //TODO: banding (don't really care at the moment...) - - for( Map.Entry playerAttackAssignment : declareAttackers.getPlayerAttackAssignments().entrySet() ) { - Player defender = getPlayerBeingAttacked( game, player, playerAttackAssignment.getValue() ); - attack( combat, playerAttackAssignment.getKey(), defender ); + + for (Map.Entry playerAttackAssignment : declareAttackers.getPlayerAttackAssignments().entrySet()) { + Player defender = getPlayerBeingAttacked(game, player, playerAttackAssignment.getValue()); + attack(combat, playerAttackAssignment.getKey(), defender); } - for( Map.Entry planeswalkerAttackAssignment: declareAttackers.getPlaneswalkerAttackAssignments().entrySet() ) { - Card defender = CardSpecificationHandler.INSTANCE.find( game.getCardsInGame(), planeswalkerAttackAssignment.getKey() ); - attack( combat, planeswalkerAttackAssignment.getKey(), defender ); + for (Map.Entry planeswalkerAttackAssignment: declareAttackers.getPlaneswalkerAttackAssignments().entrySet()) { + Card defender = CardSpecificationHandler.INSTANCE.find(game.getCardsInGame(), planeswalkerAttackAssignment.getKey()); + attack(combat, planeswalkerAttackAssignment.getKey(), defender); } } - - private Player getPlayerBeingAttacked( Game game, Player attacker, PlayerSpecification defenderSpecification ) { - if( defenderSpecification != null ) { - return PlayerSpecificationHandler.INSTANCE.find( game.getPlayers(), defenderSpecification ); + + private Player getPlayerBeingAttacked(Game game, Player attacker, PlayerSpecification defenderSpecification) { + if (defenderSpecification != null) { + return PlayerSpecificationHandler.INSTANCE.find(game.getPlayers(), defenderSpecification); } - if( game.getPlayers().size() != 2 ) { - throw new IllegalStateException( "Can't use implicit defender specification in this situation!" ); + if (game.getPlayers().size() != 2) { + throw new IllegalStateException("Can't use implicit defender specification in this situation!"); } - for( Player player : game.getPlayers() ) { - if( !attacker.equals( player ) ) { + for (Player player : game.getPlayers()) { + if (!attacker.equals(player)) { return player; } } - throw new IllegalStateException( "Couldn't find implicit defender!" ); + throw new IllegalStateException("Couldn't find implicit defender!"); } - - private void attack( Combat combat, CardSpecification attackerSpecification, GameEntity defender ) { - Card attacker = CardSpecificationHandler.INSTANCE.find( combat.getAttackingPlayer().getCreaturesInPlay(), attackerSpecification ); - if ( !CombatUtil.canAttack( attacker, defender, combat ) ) { - throw new IllegalStateException( attacker + " can't attack " + defender ); + + private void attack(Combat combat, CardSpecification attackerSpecification, GameEntity defender) { + Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackingPlayer().getCreaturesInPlay(), attackerSpecification); + if (!CombatUtil.canAttack(attacker, defender, combat)) { + throw new IllegalStateException(attacker + " can't attack " + defender); } - combat.addAttacker( attacker, defender ); + combat.addAttacker(attacker, defender); } @Override - public void declareBlockers( Player defender, Combat combat ) { + public void declareBlockers(Player defender, Combat combat) { //Doing nothing is safe in most cases, but not all (creatures that must block, attackers that must be blocked etc). TODO: legality checks? - if( playerActions == null ) { + if (playerActions == null) { return; } - DeclareBlockersAction declareBlockers = playerActions.getNextActionIfApplicable( player, game, DeclareBlockersAction.class ); - if( declareBlockers == null ) { + DeclareBlockersAction declareBlockers = playerActions.getNextActionIfApplicable(player, game, DeclareBlockersAction.class); + if (declareBlockers == null) { return; } - + //TODO: check that the chosen block configuration is 100% legal? //TODO: check that the chosen block configuration was a 100% match to what was requested? //TODO: where do damage assignment orders get handled? - - for( Map.Entry> blockingAssignment : declareBlockers.getBlockingAssignments().asMap().entrySet() ) { - Card attacker = CardSpecificationHandler.INSTANCE.find( combat.getAttackers(), blockingAssignment.getKey() ); - for( CardSpecification blockerSpecification : blockingAssignment.getValue() ) { - Card blocker = CardSpecificationHandler.INSTANCE.find( game, blockerSpecification ); - if( !CombatUtil.canBlock( attacker, blocker ) ) { - throw new IllegalStateException( blocker + " can't block " + blocker ); + + for (Map.Entry> blockingAssignment : declareBlockers.getBlockingAssignments().asMap().entrySet()) { + Card attacker = CardSpecificationHandler.INSTANCE.find(combat.getAttackers(), blockingAssignment.getKey()); + for (CardSpecification blockerSpecification : blockingAssignment.getValue()) { + Card blocker = CardSpecificationHandler.INSTANCE.find(game, blockerSpecification); + if (!CombatUtil.canBlock(attacker, blocker)) { + throw new IllegalStateException(blocker + " can't block " + blocker); } - combat.addBlocker( attacker, blocker ); + combat.addBlocker(attacker, blocker); } } - String blockValidation = CombatUtil.validateBlocks( combat, player ); - if( blockValidation != null ) { - throw new IllegalStateException( blockValidation ); + String blockValidation = CombatUtil.validateBlocks(combat, player); + if (blockValidation != null) { + throw new IllegalStateException(blockValidation); } } @Override public void takePriority() { //TODO: just about everything... - if( playerActions != null ) { - CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable( player, game, CastSpellFromHandAction.class ); - if( castSpellFromHand != null ) { - castSpellFromHand.castSpellFromHand( player, game ); + if (playerActions != null) { + CastSpellFromHandAction castSpellFromHand = playerActions.getNextActionIfApplicable(player, game, CastSpellFromHandAction.class); + if (castSpellFromHand != null) { + castSpellFromHand.castSpellFromHand(player, game); } - - ActivateAbilityAction activateAbilityAction = playerActions.getNextActionIfApplicable( player, game, ActivateAbilityAction.class ); - if( activateAbilityAction != null ) { - activateAbilityAction.activateAbility( player, game ); + + ActivateAbilityAction activateAbilityAction = playerActions.getNextActionIfApplicable(player, game, ActivateAbilityAction.class); + if (activateAbilityAction != null) { + activateAbilityAction.activateAbility(player, game); } } } @Override - public List chooseCardsToDiscardToMaximumHandSize( int numDiscard ) { - return chooseItems( player.getZone( ZoneType.Hand ).getCards(), numDiscard ); + public List chooseCardsToDiscardToMaximumHandSize(int numDiscard) { + return chooseItems(player.getZone(ZoneType.Hand).getCards(), numDiscard); } @Override - public boolean payManaOptional( Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose ) { - throw new IllegalStateException( "Callers of this method currently assume that it performs extra functionality!" ); + public boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose) { + throw new IllegalStateException("Callers of this method currently assume that it performs extra functionality!"); } @Override - public int chooseNumber( SpellAbility sa, String title, int min, int max ) { + public int chooseNumber(SpellAbility sa, String title, int min, int max) { return min; } @Override - public boolean chooseBinary( SpellAbility sa, String question, BinaryChoiceType kindOfChoice ) { + public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return true; } @Override - public boolean chooseFlipResult( SpellAbility sa, Player flipper, boolean[] results, boolean call ) { + public boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call) { return true; } @Override - public Card chooseProtectionShield( GameEntity entityBeingDamaged, List options, Map choiceMap ) { - return choiceMap.get( options.get( 0 ) ); + public Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap) { + return choiceMap.get(options.get(0)); } @Override - public List chooseModeForAbility( SpellAbility sa, int min, int num ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public List chooseModeForAbility(SpellAbility sa, int min, int num) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override - public byte chooseColor( String message, SpellAbility sa, ColorSet colors ) { + public byte chooseColor(String message, SpellAbility sa, ColorSet colors) { return Iterables.getFirst(colors, MagicColor.WHITE); } - - private List chooseItems( Collection items, int amount ) { - if( items == null || items.isEmpty() ) { - return new ArrayList( items ); + + private List chooseItems(Collection items, int amount) { + if (items == null || items.isEmpty()) { + return new ArrayList(items); } - return new ArrayList( items ).subList( 0, Math.max( amount, items.size() ) ); + return new ArrayList(items).subList(0, Math.max(amount, items.size())); } - - private T chooseItem( Collection items ) { - if( items == null || items.isEmpty() ) { + + private T chooseItem(Collection items) { + if (items == null || items.isEmpty()) { return null; } - return Iterables.getFirst( items, null ); + return Iterables.getFirst(items, null); } @Override - public SpellAbility getAbilityToPlay( List abilities, MouseEvent triggerEvent ) { - return getAbilityToPlay( abilities ); + public SpellAbility getAbilityToPlay(List abilities, MouseEvent triggerEvent) { + return getAbilityToPlay(abilities); } - + @Override - public String chooseSomeType( String kindOfType, SpellAbility sa, List validTypes, List invalidTypes ) { - return chooseItem( validTypes ); + public String chooseSomeType(String kindOfType, SpellAbility sa, List validTypes, List invalidTypes) { + return chooseItem(validTypes); } @Override - public PaperCard chooseSinglePaperCard( SpellAbility sa, String message, Predicate cpp, String name ) { - throw new IllegalStateException( "Erring on the side of caution here..." ); + public PaperCard chooseSinglePaperCard(SpellAbility sa, String message, Predicate cpp, String name) { + throw new IllegalStateException("Erring on the side of caution here..."); } @Override