mirror of
https://github.com/correl/mage.git
synced 2025-03-12 17:00:08 -09:00
fixes for monte carlo ai
This commit is contained in:
parent
10eb7955eb
commit
48f5a469d4
33 changed files with 196 additions and 49 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Reference in a new issue