fixes for monte carlo ai

This commit is contained in:
BetaSteward 2012-02-13 09:05:01 -05:00
parent 10eb7955eb
commit 48f5a469d4
33 changed files with 196 additions and 49 deletions

View file

@ -36,6 +36,7 @@ import mage.abilities.Ability;
import mage.cards.Card;
import mage.game.Game;
import mage.game.GameState;
import mage.game.permanent.Permanent;
/**
*
@ -63,7 +64,7 @@ public class CardsView extends HashMap<UUID, CardView> {
case BATTLEFIELD:
sourceCard = game.getPermanent(ability.getSourceId());
if (sourceCard == null)
sourceCard = game.getLastKnownInformation(ability.getSourceId(), Zone.BATTLEFIELD);
sourceCard = (Permanent)game.getLastKnownInformation(ability.getSourceId(), Zone.BATTLEFIELD);
break;
}
if (sourceCard != null) {

View file

@ -46,6 +46,7 @@ public class GameClientMessage implements Serializable {
private String message;
private AbilityPickerView abilityView;
private boolean flag;
private boolean cancel;
private String[] strings;
private Set<UUID> targets;
private int min;

View file

@ -216,6 +216,72 @@ public class ComputerPlayer<T extends ComputerPlayer<T>> extends PlayerImpl<T> i
return true;
}
}
if (target instanceof TargetCreatureOrPlayer) {
List<Permanent> targets;
TargetCreatureOrPlayer t = ((TargetCreatureOrPlayer)target);
if (outcome.isGood()) {
targets = threats(playerId, ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets());
}
else {
targets = threats(opponentId, ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets());
}
for (Permanent permanent : targets) {
List<UUID> alreadyTargetted = target.getTargets();
if (t.canTarget(playerId, permanent.getId(), null, game)) {
if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) {
target.add(permanent.getId(), game);
return true;
}
}
}
if (outcome.isGood()) {
if (target.canTarget(playerId, null, game)) {
target.add(playerId, game);
return true;
}
}
else {
if (target.canTarget(opponentId, null, game)) {
target.add(opponentId, game);
return true;
}
}
if (!target.isRequired())
return false;
}
if (target instanceof TargetPermanentOrPlayer) {
List<Permanent> targets;
TargetPermanentOrPlayer t = ((TargetPermanentOrPlayer)target);
if (outcome.isGood()) {
targets = threats(playerId, ((FilterPermanentOrPlayer)t.getFilter()).getPermanentFilter(), game, target.getTargets());
}
else {
targets = threats(opponentId, ((FilterPermanentOrPlayer)t.getFilter()).getPermanentFilter(), game, target.getTargets());
}
for (Permanent permanent : targets) {
List<UUID> alreadyTargetted = target.getTargets();
if (t.canTarget(permanent.getId(), game)) {
if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) {
target.add(permanent.getId(), game);
return true;
}
}
}
if (outcome.isGood()) {
if (target.canTarget(playerId, null, game)) {
target.add(playerId, game);
return true;
}
}
else {
if (target.canTarget(opponentId, null, game)) {
target.add(opponentId, game);
return true;
}
}
if (!target.isRequired())
return false;
}
return false;
}

View file

@ -60,7 +60,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer<ComputerPlayerMCTS> imple
private static final int THINK_MIN_RATIO = 40;
private static final int THINK_MAX_RATIO = 100;
private static final double THINK_TIME_MULTIPLIER = 2.0;
private static final boolean USE_MULTIPLE_THREADS = false;
private static final boolean USE_MULTIPLE_THREADS = true;
protected transient MCTSNode root;
protected int maxThinkTime;

View file

@ -118,9 +118,9 @@ public class MCTSNode {
double uct;
if (node.visits > 0)
if (isTarget)
uct = (node.wins / (node.visits + 1.0)) + (selectionCoefficient * Math.sqrt(Math.log(visits + 1.0) / (node.visits + 1.0)));
uct = (node.wins / (node.visits)) + (selectionCoefficient * Math.sqrt(Math.log(visits) / (node.visits)));
else
uct = ((node.visits - node.wins) / (node.visits + 1.0)) + (selectionCoefficient * Math.sqrt(Math.log(visits + 1.0) / (node.visits + 1.0)));
uct = ((node.visits - node.wins) / (node.visits)) + (selectionCoefficient * Math.sqrt(Math.log(visits) / (node.visits)));
else
// ensure that a random unvisited node is played first
uct = 10000 + 1000 * Math.random();

View file

@ -130,8 +130,10 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
}
if (ability.getManaCosts().getVariableCosts().size() > 0) {
int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost();
if (amount > 0)
if (amount > 0) {
ability = ability.copy();
ability.getManaCostsToPay().add(new GenericManaCost(rnd.nextInt(amount)));
}
}
// check if ability kills player, if not then it's ok to play
// if (ability.isUsesStack()) {

View file

@ -6,7 +6,7 @@
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
<playerType name="Computer - minimax" jar="mage-player-aiminimax.jar" className="mage.player.ai.ComputerPlayer3"/>
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayer6"/>
<!--playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/-->
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
</playerTypes>
<gameTypes>
<gameType name="Two Player Duel" jar="mage-game-twoplayerduel.jar" className="mage.game.TwoPlayerMatch" typeName="mage.game.TwoPlayerDuelType"/>

View file

@ -35,6 +35,7 @@ import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.Constants.WatcherScope;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
@ -96,7 +97,7 @@ class ZuberasDiedWatcher extends WatcherImpl<ZuberasDiedWatcher> {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) {
Card card = game.getLastKnownInformation(event.getTargetId(), Constants.Zone.BATTLEFIELD);
MageObject card = game.getLastKnownInformation(event.getTargetId(), Constants.Zone.BATTLEFIELD);
if (card != null && card.hasSubtype("Zubera")) {
zuberasDiedThisTurn++;
}

View file

@ -36,6 +36,7 @@ import mage.Constants.Rarity;
import mage.Constants.SubLayer;
import mage.Constants.Zone;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
@ -103,7 +104,7 @@ class GutterGrimeTriggeredAbility extends TriggeredAbilityImpl<GutterGrimeTrigge
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == EventType.ZONE_CHANGE) {
UUID targetId = event.getTargetId();
Card card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
MageObject card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
if (card != null && card instanceof Permanent && !(card instanceof PermanentToken)) {
Permanent permanent = (Permanent) card;
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;

View file

@ -33,6 +33,7 @@ import java.util.UUID;
import mage.Constants;
import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
@ -89,14 +90,14 @@ public class DivineOffering extends CardImpl<DivineOffering> {
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getLastKnownInformation(source.getFirstTarget(), Constants.Zone.BATTLEFIELD);
if (card != null) {
MageObject card = game.getLastKnownInformation(source.getFirstTarget(), Constants.Zone.BATTLEFIELD);
if (card != null) {
int cost = card.getManaCost().get(0).convertedManaCost();
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
player.gainLife(cost, game);
}
}
}
return true;
}

View file

@ -31,6 +31,7 @@ import java.util.UUID;
import mage.Constants;
import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.Cost;
@ -134,10 +135,10 @@ class MirrorworksEffect extends OneShotEffect<MirrorworksEffect> {
if (cost.pay(source, game, source.getId(), source.getControllerId(), false)) {
UUID targetId = targetPointer.getFirst(source);
if (targetId != null) {
Card target = game.getLastKnownInformation(targetId, Constants.Zone.BATTLEFIELD);
if (target != null) {
MageObject target = game.getLastKnownInformation(targetId, Constants.Zone.BATTLEFIELD);
if (target != null && target instanceof Permanent) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(target);
CardUtil.copyTo(token).from((Permanent)target);
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
return true;
}

View file

@ -32,6 +32,7 @@ import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.Constants.WatcherScope;
import mage.Constants.Zone;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.common.CreateTokenEffect;
@ -96,8 +97,8 @@ class FreshMeatWatcher extends WatcherImpl<FreshMeatWatcher> {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent) event).isDiesEvent()) {
Card card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
if (card != null && card.getOwnerId().equals(this.controllerId) && card.getCardType().contains(CardType.CREATURE)) {
MageObject card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
if (card != null && ((Card)card).getOwnerId().equals(this.controllerId) && card.getCardType().contains(CardType.CREATURE)) {
creaturesCount++;
}
}

View file

@ -32,6 +32,7 @@ import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.Constants.Zone;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.Card;
@ -92,7 +93,7 @@ class PawnOfUlamogTriggeredAbility extends TriggeredAbilityImpl<PawnOfUlamogTrig
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == EventType.ZONE_CHANGE) {
UUID targetId = event.getTargetId();
Card card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
MageObject card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
if (card != null && card instanceof Permanent && !(card instanceof PermanentToken)) {
Permanent permanent = (Permanent) card;
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;

View file

@ -34,6 +34,7 @@ import mage.Constants;
import mage.Constants.CardType;
import mage.Constants.Rarity;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BlocksOrBecomesBlockedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -85,7 +86,7 @@ class EngulfingSlagwurmEffect extends OneShotEffect<EngulfingSlagwurmEffect> {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Card c = game.getLastKnownInformation(targetPointer.getFirst(source), Constants.Zone.BATTLEFIELD);
MageObject c = game.getLastKnownInformation(targetPointer.getFirst(source), Constants.Zone.BATTLEFIELD);
if (c != null && controller != null) {
controller.gainLife(c.getPower().getValue(), game);
}

View file

@ -35,6 +35,7 @@ import mage.Constants.Outcome;
import mage.Constants.Rarity;
import mage.Constants.WatcherScope;
import mage.Constants.Zone;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.OneShotEffect;
@ -99,7 +100,7 @@ class FleshAllergyWatcher extends WatcherImpl<FleshAllergyWatcher> {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) {
Card card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
MageObject card = game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
if (card != null && card.getCardType().contains(CardType.CREATURE)) {
creaturesDiedThisTurn++;
}
@ -133,7 +134,7 @@ class FleshAllergyEffect extends OneShotEffect<FleshAllergyEffect> {
@Override
public boolean apply(Game game, Ability source) {
FleshAllergyWatcher watcher = (FleshAllergyWatcher) game.getState().getWatchers().get("CreaturesDied");
Card card = game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD);
MageObject card = game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD);
if (card != null && watcher != null) {
Player player = game.getPlayer(((Permanent)card).getControllerId());
if (player != null) {

View file

@ -44,6 +44,7 @@ import mage.sets.tokens.EmptyToken;
import mage.util.CardUtil;
import java.util.UUID;
import mage.MageObject;
/**
*
@ -93,10 +94,10 @@ class MyrPropagatorCreateTokenEffect extends OneShotEffect<MyrPropagatorCreateTo
@Override
public boolean apply(Game game, Ability source) {
Card thisCard = game.getLastKnownInformation(source.getSourceId(), Constants.Zone.BATTLEFIELD);
if (thisCard != null) {
MageObject thisCard = game.getLastKnownInformation(source.getSourceId(), Constants.Zone.BATTLEFIELD);
if (thisCard != null && thisCard instanceof Permanent) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(thisCard);
CardUtil.copyTo(token).from((Permanent)thisCard);
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId());
return true;
} else { // maybe it's token

View file

@ -105,7 +105,7 @@ public class RazorHippogriff extends CardImpl<RazorHippogriff> {
if (player != null) {
Card card = player.getGraveyard().get(source.getFirstTarget(), game);
if (card == null) {
card = game.getLastKnownInformation(source.getFirstTarget(), Zone.GRAVEYARD);
card = (Card)game.getLastKnownInformation(source.getFirstTarget(), Zone.GRAVEYARD);
}
if (card != null) {
player.gainLife(card.getManaCost().convertedManaCost(), game);

View file

@ -1,6 +1,7 @@
package mage.abilities.common;
import mage.Constants;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.cards.Card;
@ -31,8 +32,8 @@ public class DiesAndDealtDamageThisTurnTriggeredAbility extends TriggeredAbility
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) {
Card card = game.getLastKnownInformation(event.getTargetId(), Constants.Zone.BATTLEFIELD);
if (card instanceof Permanent && ((Permanent)card).getDealtDamageByThisTurn().contains(this.sourceId)) {
MageObject object = game.getLastKnownInformation(event.getTargetId(), Constants.Zone.BATTLEFIELD);
if (object instanceof Permanent && ((Permanent)object).getDealtDamageByThisTurn().contains(this.sourceId)) {
return true;
}
}

View file

@ -32,6 +32,7 @@ import java.util.UUID;
import mage.Constants;
import mage.Constants.Zone;
import mage.MageObject;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.game.Game;
@ -60,7 +61,7 @@ public class DiesTriggeredAbility extends ZoneChangeTriggeredAbility<DiesTrigger
@Override
public boolean isInUseableZone(Game game) {
// check it was previously on battlefield
Card before = game.getLastKnownInformation(sourceId, Constants.Zone.BATTLEFIELD);
MageObject before = game.getLastKnownInformation(sourceId, Constants.Zone.BATTLEFIELD);
// check now it is in graveyard
Zone after = game.getState().getZone(sourceId);
return before != null && after != null && Zone.GRAVEYARD.match(after);

View file

@ -64,7 +64,7 @@ public class LoseLifeControllerEffect extends OneShotEffect<LoseLifeControllerEf
@Override
public boolean apply(Game game, Ability source) {
Card targetCard = game.getLastKnownInformation(targetPointer.getFirst(source), Zone.BATTLEFIELD);
MageObject targetCard = game.getLastKnownInformation(targetPointer.getFirst(source), Zone.BATTLEFIELD);
if ( targetCard == null ) {
MageObject obj = game.getObject(targetPointer.getFirst(source));

View file

@ -111,8 +111,8 @@ public interface Game extends MageItem, Serializable {
public Game copy();
public boolean isSimulation();
public void setSimulation(boolean simulation);
public Card getLastKnownInformation(UUID objectId, Zone zone);
public void rememberLKI(UUID objectId, Zone zone, Card card);
public MageObject getLastKnownInformation(UUID objectId, Zone zone);
public void rememberLKI(UUID objectId, Zone zone, MageObject object);
public void resetLKI();
public void setLosingPlayer(Player player);
public Player getLosingPlayer();

View file

@ -95,7 +95,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource();
protected Map<UUID, Card> gameCards = new HashMap<UUID, Card>();
protected Map<UUID, Card> lki = new HashMap<UUID, Card>();
protected Map<UUID, MageObject> lki = new HashMap<UUID, MageObject>();
protected GameState state;
protected Date startTime;
@ -1206,13 +1206,13 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
* @return
*/
@Override
public Card getLastKnownInformation(UUID objectId, Zone zone) {
public MageObject getLastKnownInformation(UUID objectId, Zone zone) {
/*if (!lki.containsKey(objectId)) {
return getCard(objectId);
}*/
Card card = lki.get(objectId);
if (card != null) {
return card.copy();
MageObject object = lki.get(objectId);
if (object != null) {
return object.copy();
}
return null;
}
@ -1225,9 +1225,11 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
* @param card
*/
@Override
public void rememberLKI(UUID objectId, Zone zone, Card card) {
Card copy = card.copy();
lki.put(objectId, copy);
public void rememberLKI(UUID objectId, Zone zone, MageObject object) {
if (object instanceof Permanent || object instanceof StackObject) {
MageObject copy = object.copy();
lki.put(objectId, copy);
}
}
/**

View file

@ -71,6 +71,8 @@ import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.StackAbility;
import mage.players.net.UserData;
import mage.target.Target;
import mage.target.TargetAmount;
import mage.target.common.TargetCardInLibrary;
import mage.target.common.TargetDiscard;
import mage.watchers.common.BloodthirstWatcher;
@ -1278,15 +1280,25 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
}
private void addTargetOptions(List<Ability> options, Ability option, int targetNum, Game game) {
for (UUID targetId: option.getTargets().getUnchosen().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) {
Ability newOption = option.copy();
newOption.getTargets().get(targetNum).addTarget(targetId, option, game, true);
if (targetNum < option.getTargets().size() - 2) {
for (Target target: option.getTargets().getUnchosen().get(targetNum).getTargetOptions(option, game)) {
Ability newOption = option.copy();
if (target instanceof TargetAmount) {
for (UUID targetId: target.getTargets()) {
int amount = target.getTargetAmount(targetId);
newOption.getTargets().get(targetNum).addTarget(targetId, amount, newOption, game, true);
}
}
else {
for (UUID targetId: target.getTargets()) {
newOption.getTargets().get(targetNum).addTarget(targetId, newOption, game, true);
}
}
if (targetNum < option.getTargets().size() - 2) {
//addTargetOptions(options, newOption, targetNum + 1, game);
// ayrat: bug fix
addTargetOptions(options, newOption, targetNum + 1, game);
}
else {
else {
if (option.getChoices().size() > 0)
addChoiceOptions(options, newOption, 0, game);
else if (option.getCosts().getTargets().size() > 0)

View file

@ -61,6 +61,7 @@ public interface Target extends Serializable {
public boolean canTarget(UUID id, Game game);
public boolean canTarget(UUID id, Ability source, Game game);
public boolean isLegal(Ability source, Game game);
public List<? extends Target> getTargetOptions(Ability source, Game game);
//methods for non-targets
public boolean canChoose(UUID sourceControllerId, Game game);

View file

@ -28,7 +28,7 @@
package mage.target;
import java.util.UUID;
import java.util.*;
import mage.Constants.Outcome;
import mage.abilities.Ability;
import mage.game.Game;
@ -95,5 +95,36 @@ public abstract class TargetAmount<T extends TargetAmount<T>> extends TargetImpl
}
return chosen = true;
}
@Override
public List<T> getTargetOptions(Ability source, Game game) {
List<T> options = new ArrayList<T>();
Set<UUID> possibleTargets = possibleTargets(source.getSourceId(), source.getControllerId(), game);
addTargets(this, possibleTargets, options, source, game);
return options;
}
protected void addTargets(TargetAmount<T> target, Set<UUID> targets, List<T> options, Ability source, Game game) {
for (UUID targetId: targets) {
for (int n = 1; n <= target.remainingAmount; n++) {
T t = target.copy();
t.addTarget(targetId, n, source, game, true);
if (t.remainingAmount > 0) {
if (targets.size() > 1) {
Set<UUID> newTargets = new HashSet<UUID>();
for (UUID newTarget: targets) {
if (!newTarget.equals(targetId))
newTargets.add(newTarget);
}
addTargets(t, newTargets, options, source, game);
}
}
else {
options.add(t);
}
}
}
}
}

View file

@ -28,12 +28,8 @@
package mage.target;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.UUID;
import mage.Constants.Outcome;
import mage.Constants.Zone;
import mage.abilities.Ability;
@ -260,6 +256,30 @@ public abstract class TargetImpl<T extends TargetImpl<T>> implements Target {
return true;
}
@Override
public List<T> getTargetOptions(Ability source, Game game) {
List<T> options = new ArrayList<T>();
Set<UUID> possibleTargets = possibleTargets(source.getSourceId(), source.getControllerId(), game);
possibleTargets.removeAll(getTargets());
Iterator<UUID> it = possibleTargets.iterator();
while (it.hasNext()) {
UUID targetId = it.next();
T target = this.copy();
target.clearChosen();
target.addTarget(targetId, source, game, true);
if (!target.isChosen()) {
Iterator<UUID> it2 = possibleTargets.iterator();
while (it2.hasNext()&& !target.isChosen()) {
UUID nextTargetId = it2.next();
target.addTarget(nextTargetId, source, game, true);
}
}
if (target.isChosen())
options.add(target);
}
return options;
}
@Override
public List<UUID> getTargets() {
return new ArrayList<UUID>(targets.keySet());