From 0c0fe9984ed0900e1e45ad38a7522b50beb9a319 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 16 Jul 2015 13:10:11 +0200 Subject: [PATCH] Some fixes/improvements to AI target handling and tests. --- .../src/mage/player/ai/SimulatedPlayer2.java | 8 +- .../java/mage/player/ai/ComputerPlayer.java | 19 +++- .../mage/sets/dissension/NovijenSages.java | 8 +- .../test/AI/basic/TargetsAreChosenTest.java | 102 ++++++++++++++++++ .../mage/test/cards/watchers/BoseijuTest.java | 39 +++---- .../costs/common/RemoveCounterCost.java | 31 +++--- Mage/src/mage/target/TargetImpl.java | 102 ++++++++++-------- 7 files changed, 217 insertions(+), 92 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 42a7e062dd7..75d69b98518 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -302,10 +302,10 @@ public class SimulatedPlayer2 extends ComputerPlayer { } if (bad) { - // remove its own creatures, player itself for bad effects + // remove its own creatures, player itself for bad effects with one target while (iterator.hasNext()) { Ability ability1 = iterator.next(); - if (ability1.getTargets().size() == 1) { + if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) { Permanent permanent = game.getPermanent(ability1.getFirstTarget()); if (permanent != null && !game.getOpponents(playerId).contains(permanent.getControllerId())) { iterator.remove(); @@ -318,10 +318,10 @@ public class SimulatedPlayer2 extends ComputerPlayer { } } if (good) { - // remove opponent creatures and opponent for only good effects + // remove opponent creatures and opponent for only good effects with one target while (iterator.hasNext()) { Ability ability1 = iterator.next(); - if (ability1.getTargets().size() == 1) { + if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) { Permanent permanent = game.getPermanent(ability1.getFirstTarget()); if (permanent != null && game.getOpponents(playerId).contains(permanent.getControllerId())) { iterator.remove(); diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 28264fff1a5..952fe020cff 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -161,7 +161,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { private transient final static Logger log = Logger.getLogger(ComputerPlayer.class); protected int PASSIVITY_PENALTY = 5; // Penalty value for doing nothing if some actions are availble - protected boolean ALLOW_INTERRUPT = true; // change this for test purposes to switch off interrupts while debugging + protected boolean ALLOW_INTERRUPT = false; // change this for test / debugging purposes to false to switch off interrupts while debugging private transient Map unplayable = new TreeMap<>(); private transient List playableNonInstant = new ArrayList<>(); @@ -301,12 +301,25 @@ public class ComputerPlayer extends PlayerImpl implements Player { } for (Permanent permanent : targets) { if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { + // stop to add targets if not needed and outcome is no advantage for AI player + if (target.getNumberOfTargets() == target.getTargets().size()) { + if (outcome.isGood() && hasOpponent(permanent.getControllerId(), game)) { + return true; + } + if (!outcome.isGood() && !hasOpponent(permanent.getControllerId(), game)) { + return true; + } + } + // add the target target.add(permanent.getId(), game); - return true; + if (target.doneChosing()) { + return true; + } } } - return false; + return target.isChosen(); } + if (target instanceof TargetCardInHand) { List cards = new ArrayList<>(); for (UUID cardId : ((TargetCardInHand) target).possibleTargets(sourceId, this.getId(), game)) { diff --git a/Mage.Sets/src/mage/sets/dissension/NovijenSages.java b/Mage.Sets/src/mage/sets/dissension/NovijenSages.java index 67fb5ceb705..3b0c226850b 100644 --- a/Mage.Sets/src/mage/sets/dissension/NovijenSages.java +++ b/Mage.Sets/src/mage/sets/dissension/NovijenSages.java @@ -49,12 +49,12 @@ import mage.target.TargetPermanent; * @author JotaPeRL */ public class NovijenSages extends CardImpl { - + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control with a +1/+1 counter on it"); static { filter.add(new CounterPredicate(CounterType.P1P1)); - } + } public NovijenSages(UUID ownerId) { super(ownerId, 27, "Novijen Sages", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); @@ -67,10 +67,10 @@ public class NovijenSages extends CardImpl { // Graft 4 this.addAbility(new GraftAbility(this, 4)); - + // {1}, Remove two +1/+1 counters from among creatures you control: Draw a card. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new GenericManaCost(1)); - ability.addCost(new RemoveCounterCost(new TargetPermanent(1, Integer.MAX_VALUE, filter, true), CounterType.P1P1, 2)); + ability.addCost(new RemoveCounterCost(new TargetPermanent(1, 2, filter, true), CounterType.P1P1, 2)); this.addAbility(ability); } diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java new file mode 100644 index 00000000000..7b65f262232 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetsAreChosenTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.AI.basic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseAI; + +/** + * + * @author LevelX2 + */ +public class TargetsAreChosenTest extends CardTestPlayerBaseAI { + + /** + * Check that the AI selects a target from the own artifacts and also an + * artifact from the opponent artficats + */ + @Test + public void testRackAndRuin() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Destroy two target artifacts. + addCard(Zone.HAND, playerA, "Rack and Ruin"); // {2}{R} + + addCard(Zone.BATTLEFIELD, playerA, "Mox Emerald", 2); + addCard(Zone.BATTLEFIELD, playerB, "Juggernaut"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Rack and Ruin", 1); + assertGraveyardCount(playerA, "Mox Emerald", 1); + assertGraveyardCount(playerB, "Juggernaut", 1); + } + + /** + * Check that the AI does not cast Rack and Ruin if it would destroy the + * owly creature on the battlefield owned by the AI + */ + @Test + public void testRackAndRuin2() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Destroy two target artifacts. + addCard(Zone.HAND, playerA, "Rack and Ruin"); // {2}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Mox Emerald"); + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Rack and Ruin", 0); + } + + /** + * Check that the AI does cast Rack and Ruin if it would destroy two targets + * of the opponent + */ + @Test + public void testRackAndRuin3() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Destroy two target artifacts. + addCard(Zone.HAND, playerA, "Rack and Ruin"); // {2}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Mox Emerald", 2); + addCard(Zone.BATTLEFIELD, playerA, "Juggernaut"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Rack and Ruin", 1); + assertGraveyardCount(playerB, "Mox Emerald", 2); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java index 63340f54f37..e19a37ef2e4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/BoseijuTest.java @@ -14,30 +14,33 @@ public class BoseijuTest extends CardTestPlayerBase { * Boseiju, Who Shelters All * Legendary Land * Boseiju, Who Shelters All enters the battlefield tapped. - * {T}, Pay 2 life: Add {1} to your mana pool. If that mana is spent on an + * {T}, Pay 2 life: Add {1} to your mana pool. If that mana is spent on an * instant or sorcery spell, that spell can't be countered by spells or abilities. * - */ - + */ + // test that instants and soceries can't be countered when Boseiju mana is used @Test public void testCantCounter() { - addCard(Zone.BATTLEFIELD, playerA, "Island", 6); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.BATTLEFIELD, playerA, "Boseiju, Who Shelters All"); - addCard(Zone.HAND, playerA, "Brilliant Plan"); - addCard(Zone.HAND, playerA, "Counterspell"); - + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Draw three cards. + addCard(Zone.HAND, playerA, "Brilliant Plan"); // {4}{U} + + addCard(Zone.HAND, playerB, "Counterspell"); // {U}{U} + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brilliant Plan"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Brilliant Plan"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Brilliant Plan"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + this.assertHandCount(playerA, 3); - this.assertGraveyardCount(playerA, "Counterspell", 1); - + this.assertGraveyardCount(playerB, "Counterspell", 1); + } - + // test that instants and soceries can be countered when Boseiju mana is not used @Test public void testCanCounter() { @@ -45,16 +48,16 @@ public class BoseijuTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Boseiju, Who Shelters All"); addCard(Zone.HAND, playerA, "Mental Note"); addCard(Zone.HAND, playerA, "Counterspell"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mental Note"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Mental Note"); - + setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); - + this.assertHandCount(playerA, 0); this.assertGraveyardCount(playerA, 2); - + } - + } diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 28526853e3b..85f7676eb46 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,16 +20,14 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.abilities.costs.common; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.UUID; import mage.abilities.Ability; @@ -55,7 +53,7 @@ public class RemoveCounterCost extends CostImpl { private String name; private CounterType counterTypeToRemove; private int countersToRemove; - + public RemoveCounterCost(TargetPermanent target) { this(target, null); } @@ -88,7 +86,7 @@ public class RemoveCounterCost extends CostImpl { if (controller != null) { target.clearChosen(); if (target.choose(Outcome.UnboostCreature, controllerId, sourceId, game)) { - for (UUID targetId: (List)target.getTargets()) { + for (UUID targetId : target.getTargets()) { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { if (permanent.getCounters().size() > 0 && (counterTypeToRemove == null || permanent.getCounters().containsKey(counterTypeToRemove))) { @@ -123,18 +121,19 @@ public class RemoveCounterCost extends CostImpl { int numberOfCountersSelected = 1; if (countersLeft > 1 && countersOnPermanent > 1) { numberOfCountersSelected = controller.getAmount(1, Math.min(countersLeft, countersOnPermanent), - new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game); + new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game); } permanent.removeCounters(counterName, numberOfCountersSelected, game); - if (permanent.getCounters().getCount(counterName) == 0 ){ + if (permanent.getCounters().getCount(counterName) == 0) { permanent.getCounters().removeCounter(counterName); } countersRemoved += numberOfCountersSelected; - if (!game.isSimulation()) + if (!game.isSimulation()) { game.informPlayers(new StringBuilder(controller.getLogName()) - .append(" removes ").append(numberOfCountersSelected == 1 ? "a":numberOfCountersSelected).append(" ") - .append(counterName).append(numberOfCountersSelected == 1 ? " counter from ":" counters from ") - .append(permanent.getName()).toString()); + .append(" removes ").append(numberOfCountersSelected == 1 ? "a" : numberOfCountersSelected).append(" ") + .append(counterName).append(numberOfCountersSelected == 1 ? " counter from " : " counters from ") + .append(permanent.getName()).toString()); + } if (countersRemoved == countersToRemove) { this.paid = true; break; @@ -160,7 +159,7 @@ public class RemoveCounterCost extends CostImpl { if (counterTypeToRemove != null) { sb.append(counterTypeToRemove.getName()); } - sb.append(countersToRemove == 1 ? " counter from ":" counters from ").append(target.getMaxNumberOfTargets() == 1 ? "a ":"").append(target.getTargetName()); + sb.append(countersToRemove == 1 ? " counter from " : " counters from ").append(target.getMaxNumberOfTargets() == 1 ? "a " : "").append(target.getTargetName()); return sb.toString(); } diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index ab3eb6ef68e..3776a61446d 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -1,46 +1,53 @@ /* -* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are -* permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this list of -* conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this list -* of conditions and the following disclaimer in the documentation and/or other materials -* provided with the distribution. -* -* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED -* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR -* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* The views and conclusions contained in the software and documentation are those of the -* authors and should not be interpreted as representing official policies, either expressed -* or implied, of BetaSteward_at_googlemail.com. -*/ - + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.target; -import mage.constants.Outcome; -import mage.constants.Zone; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; +import mage.constants.AbilityType; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.players.Player; -import java.util.*; -import mage.MageObject; -import mage.constants.AbilityType; - /** * * @author BetaSteward_at_googlemail.com @@ -104,7 +111,7 @@ public abstract class TargetImpl implements Target { public void setMinNumberOfTargets(int minNumberOftargets) { this.minNumberOfTargets = minNumberOftargets; } - + @Override public void setMaxNumberOfTargets(int maxNumberOftargets) { this.maxNumberOfTargets = maxNumberOftargets; @@ -116,7 +123,7 @@ public abstract class TargetImpl implements Target { if (targetController != null) { // Hint for the selecting player that the targets must be valid from the point of the ability controller // e.g. select opponent text may be misleading otherwise - suffix = " (target controlling!)"; + suffix = " (target controlling!)"; } if (getMaxNumberOfTargets() != 1) { StringBuilder sb = new StringBuilder(); @@ -158,7 +165,7 @@ public abstract class TargetImpl implements Target { public Zone getZone() { return zone; } - + @Override public boolean isRequired(UUID sourceId, Game game) { MageObject object = game.getObject(sourceId); @@ -168,12 +175,12 @@ public abstract class TargetImpl implements Target { return isRequired(); } } - + @Override public boolean isRequired() { return required; } - + @Override public boolean isRequired(Ability ability) { return ability == null || ability.isActivated() || !(ability.getAbilityType().equals(AbilityType.SPELL) || ability.getAbilityType().equals(AbilityType.ACTIVATED)); @@ -217,6 +224,7 @@ public abstract class TargetImpl implements Target { if (!targets.containsKey(id)) { targets.put(id, 0); rememberZoneChangeCounter(id, game); + chosen = targets.size() >= getNumberOfTargets(); } } } @@ -254,7 +262,7 @@ public abstract class TargetImpl implements Target { } } } - + @Override public void updateTarget(UUID id, Game game) { rememberZoneChangeCounter(id, game); @@ -315,7 +323,7 @@ public abstract class TargetImpl implements Target { int i = 0; int rnd = new Random().nextInt(possibleTargets.size()); Iterator it = possibleTargets.iterator(); - while( i < rnd) { + while (i < rnd) { it.next(); i++; } @@ -336,9 +344,9 @@ public abstract class TargetImpl implements Target { @Override public boolean isLegal(Ability source, Game game) { //20101001 - 608.2b - Set illegalTargets = new HashSet<>(); + Set illegalTargets = new HashSet<>(); // int replacedTargets = 0; - for (UUID targetId: targets.keySet()) { + for (UUID targetId : targets.keySet()) { Card card = game.getCard(targetId); if (card != null) { if (zoneChangeCounters.containsKey(targetId) && zoneChangeCounters.get(targetId) != card.getZoneChangeCounter(game)) { @@ -356,7 +364,7 @@ public abstract class TargetImpl implements Target { } } // remove illegal targets, needed to handle if only a subset of targets was illegal - for (UUID targetId: illegalTargets) { + for (UUID targetId : illegalTargets) { targets.remove(targetId); } // if (replacedTargets > 0 && replacedTargets == targets.size()) { @@ -381,7 +389,7 @@ public abstract class TargetImpl implements Target { target.addTarget(targetId, source, game, true); if (!target.isChosen()) { Iterator it2 = possibleTargets.iterator(); - while (it2.hasNext()&& !target.isChosen()) { + while (it2.hasNext() && !target.isChosen()) { UUID nextTargetId = it2.next(); target.addTarget(nextTargetId, source, game, true); } @@ -389,7 +397,7 @@ public abstract class TargetImpl implements Target { if (target.isChosen()) { options.add(target); } - } + } return options; } @@ -458,6 +466,6 @@ public abstract class TargetImpl implements Target { } else { return game.getPlayer(playerId); } - } - + } + }