diff --git a/.gitignore b/.gitignore index 0cb45be4dd8..e1956605db3 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target \.chg\..*$ \.rej$ \.conflict\~$ +/Mage.Server.Plugins/Mage.Player.AIMCTS/target/ +/Mage.Server.Console/target/ \ No newline at end of file diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java index edc2f617a7f..8714801e5b8 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/ComputerPlayerMCTS.java @@ -38,6 +38,10 @@ import mage.Constants.Zone; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.PassAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.VariableManaCost; import mage.cards.Card; import mage.game.Game; import mage.game.combat.Combat; @@ -52,8 +56,9 @@ import org.apache.log4j.Logger; */ public class ComputerPlayerMCTS extends ComputerPlayer implements Player { - private static final int THINK_MIN_RATIO = 20; + private static final int THINK_MIN_RATIO = 40; private static final int THINK_MAX_RATIO = 100; + private static final boolean USE_MULTIPLE_THREADS = false; protected transient MCTSNode root; protected int maxThinkTime; @@ -84,7 +89,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer imple @Override public boolean priority(Game game) { - if (game.getStep().getType() == PhaseStep.DRAW) + if (game.getStep().getType() == PhaseStep.PRECOMBAT_MAIN) logList("computer player " + name + " hand: ", new ArrayList(hand.getCards(game))); game.firePriorityEvent(playerId); getNextAction(game, NextAction.PRIORITY); @@ -111,7 +116,7 @@ public class ComputerPlayerMCTS extends ComputerPlayer imple protected void getNextAction(Game game, NextAction nextAction) { if (root != null) { - MCTSNode newRoot = null; + MCTSNode newRoot; newRoot = root.getMatchingState(game.getState().getValue(false, game)); if (newRoot != null) { newRoot.emancipate(); @@ -161,7 +166,8 @@ public class ComputerPlayerMCTS extends ComputerPlayer imple // // @Override // public boolean chooseUse(Outcome outcome, String message, Game game) { -// throw new UnsupportedOperationException("Not supported yet."); +// getNextAction(game, NextAction.CHOOSE_USE); +// return root.get // } // // @Override @@ -174,11 +180,20 @@ public class ComputerPlayerMCTS extends ComputerPlayer imple // throw new UnsupportedOperationException("Not supported yet."); // } -// @Override -// public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { -// throw new UnsupportedOperationException("Not supported yet."); -// } -// + @Override + public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { + //MCTSPlayer.simulateVariableCosts method adds a generic mana cost for each option + for (ManaCost manaCost: costs) { + if (manaCost instanceof GenericManaCost) { + cost.setPayment(manaCost.getPayment()); + logger.debug("using X = " + cost.getPayment().count()); + break; + } + } + cost.setPaid(); + return true; + } + // @Override // public int chooseEffect(List rEffects, Game game) { // throw new UnsupportedOperationException("Not supported yet."); @@ -261,68 +276,69 @@ public class ComputerPlayerMCTS extends ComputerPlayer imple logger.info("applyMCTS - Thinking for " + (endTime - startTime)/1000000000.0 + "s"); if (thinkTime > 0) { -// List tasks = new ArrayList(); -// for (int i = 0; i < cores; i++) { -// Game sim = createMCTSGame(game); -// MCTSPlayer player = (MCTSPlayer) sim.getPlayer(playerId); -// player.setNextAction(action); -// MCTSExecutor exec = new MCTSExecutor(sim, playerId, thinkTime); -// tasks.add(exec); -// } -// -// try { -// pool.invokeAll(tasks); -// } catch (InterruptedException ex) { -// logger.warn("applyMCTS interrupted"); -// } -// -// for (MCTSExecutor task: tasks) { -// root.merge(task.getRoot()); -// task.clear(); -// } -// tasks.clear(); - - MCTSNode current; - int simCount = 0; - while (true) { - long currentTime = System.nanoTime(); - if (currentTime > endTime) - break; - current = root; - - // Selection - while (!current.isLeaf()) { - current = current.select(this.playerId); + if (USE_MULTIPLE_THREADS) { + List tasks = new ArrayList(); + for (int i = 0; i < cores; i++) { + Game sim = createMCTSGame(game); + MCTSPlayer player = (MCTSPlayer) sim.getPlayer(playerId); + player.setNextAction(action); + MCTSExecutor exec = new MCTSExecutor(sim, playerId, thinkTime); + tasks.add(exec); } - int result; - if (!current.isTerminal()) { - // Expansion - current.expand(); + try { + pool.invokeAll(tasks); + } catch (InterruptedException ex) { + logger.warn("applyMCTS interrupted"); + } - // Simulation - current = current.select(this.playerId); - result = current.simulate(this.playerId); - simCount++; + for (MCTSExecutor task: tasks) { + root.merge(task.getRoot()); + task.clear(); } - else { - result = current.isWinner(this.playerId)?1:-1; - } - // Backpropagation - current.backpropagate(result); + tasks.clear(); } + else { + MCTSNode current; + int simCount = 0; + while (true) { + long currentTime = System.nanoTime(); + if (currentTime > endTime) + break; + current = root; - logger.info("Simulated " + simCount + " games - nodes in tree: " + root.size()); + // Selection + while (!current.isLeaf()) { + current = current.select(this.playerId); + } + + int result; + if (!current.isTerminal()) { + // Expansion + current.expand(); + + // Simulation + current = current.select(this.playerId); + result = current.simulate(this.playerId); + simCount++; + } + else { + result = current.isWinner(this.playerId)?1:-1; + } + // Backpropagation + current.backpropagate(result); + } + logger.info("Simulated " + simCount + " games - nodes in tree: " + root.size()); + } displayMemory(); } // root.print(1); - return; } //try to ensure that there are at least THINK_MIN_RATIO simulations per node at all times private int calculateThinkTime(Game game, NextAction action) { - int thinkTime = 0; + int thinkTime; int nodeSizeRatio = 0; if (root.getNumChildren() > 0) nodeSizeRatio = root.getVisits() / root.getNumChildren(); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSExecutor.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSExecutor.java index 7082daf098e..fe5247d1647 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSExecutor.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSExecutor.java @@ -57,16 +57,10 @@ public class MCTSExecutor implements Callable { long endTime = startTime + (thinkTime * 1000000000l); MCTSNode current; -// if (root.getNumChildren() == 1) -// //there is only one possible action - don't spend a lot of time thinking -// endTime = startTime + 1000000000l; -// logger.info("applyMCTS - Thinking for " + (endTime - startTime)/1000000000.0 + "s"); while (true) { long currentTime = System.nanoTime(); -// logger.info("Remaining time: " + (endTime - currentTime)/1000000000.0 + "s"); if (currentTime > endTime) -// if (root.getNodeCount() > 50) break; current = root; @@ -80,14 +74,17 @@ public class MCTSExecutor implements Callable { // Expansion current.expand(); -// if (current == root && current.getNumChildren() == 1) -// //there is only one possible action - don't spend a lot of time thinking -// endTime = startTime + 1000000000l; - - // Simulation - current = current.select(this.playerId); - result = current.simulate(this.playerId); - simCount++; + // only run simulations for nodes that have siblings + if (current.getNumChildren() > 1) { + // Simulation + current = current.select(this.playerId); + result = current.simulate(this.playerId); + simCount++; + } + else { + current = current.select(this.playerId); + result = 0; + } } else { result = current.isWinner(this.playerId)?1:-1; @@ -95,8 +92,7 @@ public class MCTSExecutor implements Callable { // Backpropagation current.backpropagate(result); } -// logger.info("Created " + root.getNodeCount() + " nodes"); - logger.info("Simulated " + simCount + " nodes"); + logger.info("Simulated " + simCount + " games - nodes in tree: " + root.size()); return true; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSNode.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSNode.java index d339e3acb80..15f8a5b4e76 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSNode.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSNode.java @@ -42,7 +42,6 @@ import mage.game.Game; import mage.game.combat.Combat; import mage.game.combat.CombatGroup; import mage.game.turn.Step.StepPart; -import mage.player.ai.MCTSPlayer.NextAction; import mage.players.Player; import org.apache.log4j.Logger; @@ -53,7 +52,7 @@ import org.apache.log4j.Logger; public class MCTSNode { private static final double selectionCoefficient = 1.0; - private static final double passRatioTolerance = 0.01; + private static final double passRatioTolerance = 0.0; private final static transient Logger logger = Logger.getLogger(MCTSNode.class); private int visits = 0; @@ -64,12 +63,14 @@ public class MCTSNode { private Game game; private String stateValue; private UUID playerId; + private boolean terminal = false; private static int nodeCount; public MCTSNode(Game game) { this.game = game; this.stateValue = game.getState().getValue(false, game); + this.terminal = game.isGameOver(); setPlayer(); nodeCount = 1; } @@ -77,6 +78,7 @@ public class MCTSNode { protected MCTSNode(MCTSNode parent, Game game, Ability action) { this.game = game; this.stateValue = game.getState().getValue(false, game); + this.terminal = game.isGameOver(); this.parent = parent; this.action = action; setPlayer(); @@ -86,6 +88,7 @@ public class MCTSNode { protected MCTSNode(MCTSNode parent, Game game) { this.game = game; this.stateValue = game.getState().getValue(false, game); + this.terminal = game.isGameOver(); this.parent = parent; setPlayer(); nodeCount++; @@ -144,6 +147,7 @@ public class MCTSNode { sim.resume(); children.add(new MCTSNode(this, sim, ability)); } + game = null; break; case SELECT_ATTACKERS: // logger.info("Select attackers:" + player.getName()); @@ -185,7 +189,7 @@ public class MCTSNode { Game sim = createSimulation(game, playerId); sim.resume(); // long duration = System.nanoTime() - startTime; - int retVal = 0; //anything other than a win is a loss + int retVal = -1; //anything other than a win is a loss for (Player simPlayer: sim.getPlayers().values()) { // logger.info(simPlayer.getName() + " calculated " + ((SimulatedPlayerMCTS)simPlayer).getActionCount() + " actions in " + duration/1000000000.0 + "s"); if (simPlayer.getId().equals(playerId) && simPlayer.hasWon()) { @@ -197,6 +201,8 @@ public class MCTSNode { } public void backpropagate(int result) { + if (result == 0) + return; if (result == 1) wins++; visits++; @@ -232,6 +238,7 @@ public class MCTSNode { //favour passing vs any other action if ratio is close double ratio = node.wins/(node.visits * 1.0); if (ratio > bestRatio - passRatioTolerance) { + logger.info("choosing pass over " + bestChild.getAction()); bestChild = node; bestCount = node.visits; bestRatio = ratio; @@ -329,7 +336,7 @@ public class MCTSNode { } public boolean isTerminal() { - return game.isGameOver(); + return terminal; } public boolean isWinner(UUID playerId) { diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java index 0760ba32440..6b499c13334 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/MCTSPlayer.java @@ -30,6 +30,7 @@ package mage.player.ai; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.PassAbility; @@ -115,7 +116,7 @@ public class MCTSPlayer extends ComputerPlayer { } for (int i = start; i < numAvailable; i++) { Ability newAbility = ability.copy(); - newAbility.addManaCost(new GenericManaCost(i)); + newAbility.getManaCostsToPay().add(new GenericManaCost(i)); options.add(newAbility); } } @@ -242,7 +243,9 @@ public class MCTSPlayer extends ComputerPlayer { // // @Override // public boolean chooseUse(Outcome outcome, String message, Game game) { -// game.end(); +// game.pause(); +// nextAction = NextAction.CHOOSE_USE; +// return false; // } // // @Override @@ -267,14 +270,12 @@ public class MCTSPlayer extends ComputerPlayer { @Override public void selectAttackers(Game game) { -// logger.info("Paused for select attackers for player:" + getName()); game.pause(); nextAction = NextAction.SELECT_ATTACKERS; } @Override public void selectBlockers(Game game) { -// logger.info("Paused for select blockers for player:" + getName()); game.pause(); nextAction = NextAction.SELECT_BLOCKERS; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 278b1a18e01..27182d89fae 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -51,13 +51,11 @@ import mage.abilities.effects.ReplacementEffect; import mage.abilities.mana.ManaAbility; import mage.cards.Card; import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.choices.Choice; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetAmount; @@ -133,7 +131,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { if (ability.getManaCosts().getVariableCosts().size() > 0) { int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); if (amount > 0) - ability.addManaCost(new GenericManaCost(rnd.nextInt(amount))); + ability.getManaCostsToPay().add(new GenericManaCost(rnd.nextInt(amount))); } // check if ability kills player, if not then it's ok to play // if (ability.isUsesStack()) { @@ -278,67 +276,82 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { - return chooseRandom(target, game); + if (this.isHuman()) + return chooseRandom(target, game); + return super.choose(outcome, target, sourceId, game); } @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { - return chooseRandom(target, game); + if (this.isHuman()) + return chooseRandom(target, game); + return super.choose(outcome, target, sourceId, game, options); } @Override public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { - if (cards.isEmpty()) - return !target.isRequired(); - Set possibleTargets = target.possibleTargets(playerId, cards, game); - if (possibleTargets.isEmpty()) - return !target.isRequired(); - Iterator it = possibleTargets.iterator(); - int targetNum = rnd.nextInt(possibleTargets.size()); - UUID targetId = it.next(); - for (int i = 0; i < targetNum; i++) { - targetId = it.next(); + if (this.isHuman()) { + if (cards.isEmpty()) + return !target.isRequired(); + Set possibleTargets = target.possibleTargets(playerId, cards, game); + if (possibleTargets.isEmpty()) + return !target.isRequired(); + Iterator it = possibleTargets.iterator(); + int targetNum = rnd.nextInt(possibleTargets.size()); + UUID targetId = it.next(); + for (int i = 0; i < targetNum; i++) { + targetId = it.next(); + } + target.add(targetId, game); + return true; } - target.add(targetId, game); - return true; + return super.choose(outcome, cards, target, game); } @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { - return chooseRandomTarget(target, source, game); + if (this.isHuman()) + return chooseRandomTarget(target, source, game); + return super.chooseTarget(outcome, target, source, game); } @Override public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { - if (cards.isEmpty()) - return !target.isRequired(); - Card card = cards.getRandom(game); - target.addTarget(card.getId(), source, game); - return true; + if (this.isHuman()) { + if (cards.isEmpty()) + return !target.isRequired(); + Card card = cards.getRandom(game); + target.addTarget(card.getId(), source, game); + return true; + } + return super.chooseTarget(outcome, cards, target, source, game); } @Override public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { - Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); - if (possibleTargets.isEmpty()) - return !target.isRequired(); - if (!target.isRequired()) { - if (rnd.nextInt(possibleTargets.size() + 1) == 0) { - return false; + if (this.isHuman()) { + Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); + if (possibleTargets.isEmpty()) + return !target.isRequired(); + if (!target.isRequired()) { + if (rnd.nextInt(possibleTargets.size() + 1) == 0) { + return false; + } } - } - if (possibleTargets.size() == 1) { - target.addTarget(possibleTargets.iterator().next(), target.getAmountRemaining(), source, game); + if (possibleTargets.size() == 1) { + target.addTarget(possibleTargets.iterator().next(), target.getAmountRemaining(), source, game); + return true; + } + Iterator it = possibleTargets.iterator(); + int targetNum = rnd.nextInt(possibleTargets.size()); + UUID targetId = it.next(); + for (int i = 0; i < targetNum; i++) { + targetId = it.next(); + } + target.addTarget(targetId, rnd.nextInt(target.getAmountRemaining()) + 1, source, game); return true; } - Iterator it = possibleTargets.iterator(); - int targetNum = rnd.nextInt(possibleTargets.size()); - UUID targetId = it.next(); - for (int i = 0; i < targetNum; i++) { - targetId = it.next(); - } - target.addTarget(targetId, rnd.nextInt(target.getAmountRemaining()) + 1, source, game); - return true; + return super.chooseTargetAmount(outcome, target, source, game); } @Override @@ -348,19 +361,24 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { @Override public boolean chooseUse(Outcome outcome, String message, Game game) { - return rnd.nextBoolean(); + if (this.isHuman()) + return rnd.nextBoolean(); + return super.chooseUse(outcome, message, game); } @Override public boolean choose(Outcome outcome, Choice choice, Game game) { - Iterator it = choice.getChoices().iterator(); - String sChoice = it.next(); - int choiceNum = rnd.nextInt(choice.getChoices().size()); - for (int i = 0; i < choiceNum; i++) { - sChoice = it.next(); + if (this.isHuman()) { + Iterator it = choice.getChoices().iterator(); + String sChoice = it.next(); + int choiceNum = rnd.nextInt(choice.getChoices().size()); + for (int i = 0; i < choiceNum; i++) { + sChoice = it.next(); + } + choice.setChoice(sChoice); + return true; } - choice.setChoice(sChoice); - return true; + return super.choose(outcome, choice, game); } @Override @@ -383,70 +401,87 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { @Override public int chooseEffect(List rEffects, Game game) { - return rnd.nextInt(rEffects.size()); + if (this.isHuman()) + return rnd.nextInt(rEffects.size()); + return super.chooseEffect(rEffects, game); } @Override public TriggeredAbility chooseTriggeredAbility(TriggeredAbilities abilities, Game game) { - return abilities.get(rnd.nextInt(abilities.size())); + if (this.isHuman()) + return abilities.get(rnd.nextInt(abilities.size())); + return super.chooseTriggeredAbility(abilities, game); } @Override public Mode chooseMode(Modes modes, Ability source, Game game) { - Iterator it = modes.values().iterator(); - Mode mode = it.next(); - if (modes.size() == 1) + if (this.isHuman()) { + Iterator it = modes.values().iterator(); + Mode mode = it.next(); + if (modes.size() == 1) + return mode; + int modeNum = rnd.nextInt(modes.values().size()); + for (int i = 0; i < modeNum; i++) { + mode = it.next(); + } return mode; - int modeNum = rnd.nextInt(modes.values().size()); - for (int i = 0; i < modeNum; i++) { - mode = it.next(); } - return mode; + return super.chooseMode(modes, source, game); } @Override public UUID chooseAttackerOrder(List attackers, Game game) { - return attackers.get(rnd.nextInt(attackers.size())).getId(); + if (this.isHuman()) + return attackers.get(rnd.nextInt(attackers.size())).getId(); + return super.chooseAttackerOrder(attackers, game); } @Override public UUID chooseBlockerOrder(List blockers, Game game) { - return blockers.get(rnd.nextInt(blockers.size())).getId(); + if (this.isHuman()) + return blockers.get(rnd.nextInt(blockers.size())).getId(); + return super.chooseBlockerOrder(blockers, game); } @Override public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { - int remainingDamage = damage; - UUID targetId; - int amount; - while (remainingDamage > 0) { - if (targets.size() == 1) { - targetId = targets.get(0); - amount = remainingDamage; - } - else { - targetId = targets.get(rnd.nextInt(targets.size())); - amount = rnd.nextInt(damage + 1); - } - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { - permanent.damage(amount, sourceId, game, true, false); - remainingDamage -= amount; - } - else { - Player player = game.getPlayer(targetId); - if (player != null) { - player.damage(amount, sourceId, game, false, true); + if (this.isHuman()) { + int remainingDamage = damage; + UUID targetId; + int amount; + while (remainingDamage > 0) { + if (targets.size() == 1) { + targetId = targets.get(0); + amount = remainingDamage; + } + else { + targetId = targets.get(rnd.nextInt(targets.size())); + amount = rnd.nextInt(damage + 1); + } + Permanent permanent = game.getPermanent(targetId); + if (permanent != null) { + permanent.damage(amount, sourceId, game, true, false); remainingDamage -= amount; } + else { + Player player = game.getPlayer(targetId); + if (player != null) { + player.damage(amount, sourceId, game, false, true); + remainingDamage -= amount; + } + } + targets.remove(targetId); } - targets.remove(targetId); } + else + super.assignDamage(damage, targets, singleTargetName, sourceId, game); } @Override public int getAmount(int min, int max, String message, Game game) { - return rnd.nextInt(max - min) + min; + if (this.isHuman()) + return rnd.nextInt(max - min) + min; + return super.getAmount(min, max, message, game); } } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index e795d96a575..93d96168a95 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -150,7 +150,7 @@ public class SimulatedPlayer extends ComputerPlayer { } for (int i = start; i < numAvailable; i++) { Ability newAbility = ability.copy(); - newAbility.addCost(new GenericManaCost(i)); + newAbility.getManaCostsToPay().add(new GenericManaCost(i)); allActions.add(newAbility); } } diff --git a/Mage.Server/plugins/mage-player-ai-ma.jar b/Mage.Server/plugins/mage-player-ai-ma.jar index b3bc0b4151f..d8c366969b7 100644 Binary files a/Mage.Server/plugins/mage-player-ai-ma.jar and b/Mage.Server/plugins/mage-player-ai-ma.jar differ diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index c17e2480315..5f1e107417d 100644 Binary files a/Mage.Server/plugins/mage-player-ai.jar and b/Mage.Server/plugins/mage-player-ai.jar differ diff --git a/Mage.Server/plugins/mage-player-aimcts.jar b/Mage.Server/plugins/mage-player-aimcts.jar index b6ab68d2a9b..d45723969c2 100644 Binary files a/Mage.Server/plugins/mage-player-aimcts.jar and b/Mage.Server/plugins/mage-player-aimcts.jar differ diff --git a/Mage.Server/plugins/mage-player-aiminimax.jar b/Mage.Server/plugins/mage-player-aiminimax.jar index a386de17138..9fedeec9919 100644 Binary files a/Mage.Server/plugins/mage-player-aiminimax.jar and b/Mage.Server/plugins/mage-player-aiminimax.jar differ diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 8a967bcd360..617700e6361 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -374,9 +374,14 @@ public abstract class AbilityImpl> implements Ability { @Override public void addCost(Cost cost) { - if (cost != null) { - this.costs.add(cost); - } + if (cost != null) { + if (cost instanceof ManaCost) { + this.addManaCost((ManaCost)cost); + } + else { + this.costs.add(cost); + } + } } @Override diff --git a/Mage/src/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/mage/abilities/ActivatedAbilityImpl.java index 7565e31e6e2..e10e2497863 100644 --- a/Mage/src/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/mage/abilities/ActivatedAbilityImpl.java @@ -99,8 +99,6 @@ public abstract class ActivatedAbilityImpl> ex if (cost != null) { if (cost instanceof PhyrexianManaCost) { this.addManaCost((PhyrexianManaCost)cost); - } else if (cost instanceof ManaCost) { - this.addManaCost((ManaCost) cost); } else { this.addCost(cost); } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 0e75d3a2e95..03ee51914df 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -45,6 +45,7 @@ import mage.Constants.Layer; import mage.Constants.SubLayer; import mage.Constants.Zone; import mage.abilities.Ability; +import mage.abilities.StaticAbility; import mage.cards.Card; import mage.game.Game; import mage.game.events.GameEvent; @@ -245,17 +246,20 @@ public class ContinuousEffects implements Serializable { public List getApplicableRequirementEffects(Permanent permanent, Game game) { List effects = new ArrayList(); //get all applicable Requirement effects on the battlefield - for (Permanent perm: game.getBattlefield().getActivePermanents(permanent.getControllerId(), game)) { - for (Entry entry: perm.getAbilities().getEffects(game, Zone.BATTLEFIELD, EffectType.REQUIREMENT).entrySet()) { - if (((RequirementEffect)entry.getKey()).applies(permanent, entry.getValue(), game)) { - effects.add((RequirementEffect)entry.getKey()); - abilityMap.put(entry.getKey().getId(), entry.getValue()); - } - } - } +// for (Permanent perm: game.getBattlefield().getActivePermanents(permanent.getControllerId(), game)) { +// for (Entry entry: perm.getAbilities().getEffects(game, Zone.BATTLEFIELD, EffectType.REQUIREMENT).entrySet()) { +// if (((RequirementEffect)entry.getKey()).applies(permanent, entry.getValue(), game)) { +// effects.add((RequirementEffect)entry.getKey()); +// abilityMap.put(entry.getKey().getId(), entry.getValue()); +// } +// } +// } for (RequirementEffect effect: requirementEffects) { - if (effect.applies(permanent, abilityMap.get(effect.getId()), game)) - effects.add(effect); + Ability ability = abilityMap.get(effect.getId()); + if (!(ability instanceof StaticAbility) || ability.getZone() == game.getZone(ability.getSourceId())) { + if (effect.applies(permanent, ability, game)) + effects.add(effect); + } } return effects; } @@ -263,17 +267,20 @@ public class ContinuousEffects implements Serializable { public List getApplicableRestrictionEffects(Permanent permanent, Game game) { List effects = new ArrayList(); //get all applicable Restriction effects on the battlefield - for (Permanent perm: game.getBattlefield().getActivePermanents(permanent.getControllerId(), game)) { - for (Entry entry: perm.getAbilities().getEffects(game, Zone.BATTLEFIELD, EffectType.RESTRICTION).entrySet()) { - if (((RestrictionEffect)entry.getKey()).applies(permanent, entry.getValue(), game)) { - effects.add((RestrictionEffect)entry.getKey()); - abilityMap.put(entry.getKey().getId(), entry.getValue()); - } - } - } +// for (Permanent perm: game.getBattlefield().getActivePermanents(permanent.getControllerId(), game)) { +// for (Entry entry: perm.getAbilities().getEffects(game, Zone.BATTLEFIELD, EffectType.RESTRICTION).entrySet()) { +// if (((RestrictionEffect)entry.getKey()).applies(permanent, entry.getValue(), game)) { +// effects.add((RestrictionEffect)entry.getKey()); +// abilityMap.put(entry.getKey().getId(), entry.getValue()); +// } +// } +// } for (RestrictionEffect effect: restrictionEffects) { - if (effect.applies(permanent, abilityMap.get(effect.getId()), game)) - effects.add(effect); + Ability ability = abilityMap.get(effect.getId()); + if (!(ability instanceof StaticAbility) || ability.getZone() == game.getZone(ability.getSourceId())) { + if (effect.applies(permanent, ability, game)) + effects.add(effect); + } } return effects; } @@ -289,49 +296,55 @@ public class ContinuousEffects implements Serializable { if (planeswalkerRedirectionEffect.applies(event, null, game)) replaceEffects.add(planeswalkerRedirectionEffect); //get all applicable Replacement effects in each players hand and graveyard - for (Card card: game.getCards()) { - Zone zone = game.getState().getZone(card.getId()); - if (zone == Zone.HAND || zone == Zone.GRAVEYARD) { - for (Entry entry: card.getAbilities().getReplacementEffects(zone).entrySet()) { - if (entry.getKey().applies(event, entry.getValue(), game)) { - replaceEffects.add(entry.getKey()); - abilityMap.put(entry.getKey().getId(), entry.getValue()); - } - } - } - } +// for (Card card: game.getCards()) { +// Zone zone = game.getState().getZone(card.getId()); +// if (zone == Zone.HAND || zone == Zone.GRAVEYARD) { +// for (Entry entry: card.getAbilities().getReplacementEffects(zone).entrySet()) { +// if (entry.getKey().applies(event, entry.getValue(), game)) { +// replaceEffects.add(entry.getKey()); +// abilityMap.put(entry.getKey().getId(), entry.getValue()); +// } +// } +// } +// } //get all applicable Replacement effects on the battlefield - for (Permanent permanent: game.getBattlefield().getAllPermanents()) { - for (Entry entry: permanent.getAbilities().getReplacementEffects(Zone.BATTLEFIELD).entrySet()) { - if (entry.getKey().applies(event, entry.getValue(), game)) { - replaceEffects.add(entry.getKey()); - abilityMap.put(entry.getKey().getId(), entry.getValue()); - } - } - } +// for (Permanent permanent: game.getBattlefield().getAllPermanents()) { +// for (Entry entry: permanent.getAbilities().getReplacementEffects(Zone.BATTLEFIELD).entrySet()) { +// if (entry.getKey().applies(event, entry.getValue(), game)) { +// replaceEffects.add(entry.getKey()); +// abilityMap.put(entry.getKey().getId(), entry.getValue()); +// } +// } +// } //get all applicable Replacement effects on players - for (Player player: game.getPlayers().values()) { - for (Entry entry: player.getAbilities().getReplacementEffects(Zone.BATTLEFIELD).entrySet()) { - if (entry.getKey().applies(event, entry.getValue(), game)) { - replaceEffects.add(entry.getKey()); - abilityMap.put(entry.getKey().getId(), entry.getValue()); - } - } - } +// for (Player player: game.getPlayers().values()) { +// for (Entry entry: player.getAbilities().getReplacementEffects(Zone.BATTLEFIELD).entrySet()) { +// if (entry.getKey().applies(event, entry.getValue(), game)) { +// replaceEffects.add(entry.getKey()); +// abilityMap.put(entry.getKey().getId(), entry.getValue()); +// } +// } +// } //get all applicable transient Replacement effects for (ReplacementEffect effect: replacementEffects) { - if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { - if (effect.applies(event, abilityMap.get(effect.getId()), game)) { - replaceEffects.add(effect); - } - } + Ability ability = abilityMap.get(effect.getId()); + if (!(ability instanceof StaticAbility) || ability.getZone() == game.getZone(ability.getSourceId())) { + if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { + if (effect.applies(event, ability, game)) { + replaceEffects.add(effect); + } + } + } } for (PreventionEffect effect: preventionEffects) { - if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { - if (effect.applies(event, abilityMap.get(effect.getId()), game)) { - replaceEffects.add(effect); - } - } + Ability ability = abilityMap.get(effect.getId()); + if (!(ability instanceof StaticAbility) || ability.getZone() == game.getZone(ability.getSourceId())) { + if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) { + if (effect.applies(event, ability, game)) { + replaceEffects.add(effect); + } + } + } } return replaceEffects; } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 930ac991c46..f683d6f5e07 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -72,6 +72,7 @@ import org.apache.log4j.Logger; import java.io.IOException; import java.io.Serializable; import java.util.*; +import mage.abilities.effects.Effect; import mage.watchers.common.PlayerDamagedBySourceWatcher; public abstract class GameImpl> implements Game, Serializable { @@ -192,6 +193,15 @@ public abstract class GameImpl> implements Game, Serializa watcher.setSourceId(card.getId()); state.getWatchers().add(watcher); } + for (StaticAbility ability: card.getAbilities().getStaticAbilities(Zone.ALL)) { + for (Mode mode: ability.getModes().values()) { + for (Effect effect: mode.getEffects()) { + if (effect instanceof ContinuousEffect) { + state.addEffect((ContinuousEffect)effect, ability); + } + } + } + } } } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index c27edaa56e9..ebe342dcb20 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1228,8 +1228,9 @@ public abstract class PlayerImpl> implements Player, Ser Map playableActivated = new HashMap(); for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { - if (canPlay(ability, available, game)) - playableActivated.put(ability.toString(), ability); + if (!playableActivated.containsKey(ability.toString())) + if (canPlay(ability, available, game)) + playableActivated.put(ability.toString(), ability); } } playable.addAll(playableActivated.values());