diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java index a72cc2aba8..60fd7d0be7 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java @@ -40,11 +40,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.logging.Level; import mage.Constants.Outcome; import mage.Constants.PhaseStep; import mage.Constants.RangeOfInfluence; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.PassAbility; @@ -55,7 +53,6 @@ import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.Effect; import mage.abilities.effects.SearchEffect; import mage.cards.Cards; -import mage.cards.decks.Deck; import mage.choices.Choice; import mage.filter.FilterAbility; import mage.game.Game; @@ -98,6 +95,8 @@ public class ComputerPlayer2 extends ComputerPlayer implements protected int maxDepth; protected int maxNodes; + protected int nodeCount = 0; + protected long thinkTime = 0; protected LinkedList actions = new LinkedList(); protected List targets = new ArrayList(); protected List choices = new ArrayList(); @@ -190,6 +189,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements addActionsTimed(new FilterAbility()); else addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); + logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s"); if (root.children.size() > 0) { root = root.children.get(0); actions = new LinkedList(root.abilities); @@ -319,9 +319,15 @@ public class ComputerPlayer2 extends ComputerPlayer implements return addActions(root, filter, Integer.MIN_VALUE, Integer.MAX_VALUE); } }); + long startTime = System.nanoTime(); pool.execute(task); try { task.get(Config.maxThinkSeconds, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + long duration = endTime - startTime; + logger.info("Calculated " + root.nodeCount + " nodes in " + duration/1000000000.0 + "s"); + nodeCount += root.nodeCount; + thinkTime += duration; } catch (TimeoutException e) { logger.debug("simulating - timed out"); task.cancel(true); @@ -331,6 +337,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements } catch (InterruptedException ex) { logger.fatal("can't sleep"); } + logger.info("Calculated " + root.nodeCount + " nodes in 30s"); } catch (ExecutionException e) { logger.fatal("Simulation error", e); task.cancel(true); @@ -712,6 +719,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements newPlayer.restore(origPlayer); sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer); } + sim.setSimulation(true); return sim; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java index cb8c57f9f5..980c5ddf1e 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java @@ -164,6 +164,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { addActionsTimed(new FilterAbility()); else addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); + logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s"); if (root.children.size() > 0) { root = root.children.get(0); actions = new LinkedList(root.abilities); @@ -187,6 +188,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player { addActionsTimed(new FilterAbility()); else addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); + logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s"); if (root.children.size() > 0) { root = root.children.get(0); actions = new LinkedList(root.abilities); diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java index 59aa730248..095afd46ad 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java @@ -38,6 +38,7 @@ import mage.abilities.keyword.TrampleAbility; import mage.abilities.mana.ManaAbility; import mage.counters.BoostCounter; import mage.counters.Counter; +import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -76,6 +77,7 @@ public class GameStateEvaluator { return WIN_SCORE; } int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR; + int poisonScore = (opponent.getCounters().getCount(CounterType.POISON) - player.getCounters().getCount(CounterType.POISON)) * LIFE_FACTOR * 2; int permanentScore = 0; for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { permanentScore += evaluatePermanent(permanent, game, ignoreTapped); @@ -89,7 +91,7 @@ public class GameStateEvaluator { handScore = player.getHand().size() - opponent.getHand().size(); handScore *= HAND_FACTOR; - int score = lifeScore + permanentScore + handScore; + int score = lifeScore + poisonScore + permanentScore + handScore; if (logger.isDebugEnabled()) logger.debug("game state for player " + player.getName() + " evaluated to- lifeScore:" + lifeScore + " permanentScore:" + permanentScore + " handScore:" + handScore + " total:" + score); return score; diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index bada7a347e..9d46b2ab6e 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-aiminimax.jar b/Mage.Server/plugins/mage-player-aiminimax.jar index df53d832ba..a654a2533d 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/condition/common/Metalcraft.java b/Mage/src/mage/abilities/condition/common/Metalcraft.java index 67ab0eb3e2..ad96714bcf 100644 --- a/Mage/src/mage/abilities/condition/common/Metalcraft.java +++ b/Mage/src/mage/abilities/condition/common/Metalcraft.java @@ -27,6 +27,6 @@ public class Metalcraft implements Condition { @Override public boolean apply(Game game, Ability source) { - return game.getBattlefield().countAll(filter, source.getControllerId()) >= 3; + return game.getBattlefield().contains(filter, source.getControllerId(), 3); } } diff --git a/Mage/src/mage/abilities/costs/common/MetalcraftCost.java b/Mage/src/mage/abilities/costs/common/MetalcraftCost.java index bec7ab86e0..a9a6687c5e 100644 --- a/Mage/src/mage/abilities/costs/common/MetalcraftCost.java +++ b/Mage/src/mage/abilities/costs/common/MetalcraftCost.java @@ -61,7 +61,7 @@ public class MetalcraftCost extends CostImpl { @Override public boolean canPay(UUID sourceId, UUID controllerId, Game game) { - return game.getBattlefield().countAll(filter, controllerId) >= 3; + return game.getBattlefield().contains(filter, controllerId, 3); } @Override diff --git a/Mage/src/mage/abilities/keyword/LandwalkAbility.java b/Mage/src/mage/abilities/keyword/LandwalkAbility.java index 449244c32f..d49b27ce89 100644 --- a/Mage/src/mage/abilities/keyword/LandwalkAbility.java +++ b/Mage/src/mage/abilities/keyword/LandwalkAbility.java @@ -73,7 +73,7 @@ class LandwalkEffect extends RestrictionEffect { @Override public boolean canBlock(Permanent attacker, Permanent blocker, Game game) { - return game.getBattlefield().countAll(filter, blocker.getControllerId()) == 0; + return !game.getBattlefield().contains(filter, blocker.getControllerId(), 1); } @Override diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index b960cea9bb..b9fe2123ef 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -107,6 +107,8 @@ public interface Game extends MageItem, Serializable { public GameStates getGameStates(); public void loadGameStates(GameStates states); 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 void resetLKI(); @@ -119,7 +121,6 @@ public interface Game extends MageItem, Serializable { public void fireSelectTargetEvent(UUID playerId, String message, Set targets, boolean required, Map options); public void fireSelectTargetEvent(UUID playerId, String message, Cards cards, boolean required); public void fireSelectTargetEvent(UUID playerId, String message, TriggeredAbilities abilities, boolean required); -// public void fireRevealCardsEvent(String message, Cards cards); public void fireSelectEvent(UUID playerId, String message); public void fireLookAtCardsEvent(UUID playerId, String message, Cards cards); public void firePriorityEvent(UUID playerId); @@ -136,7 +137,6 @@ public interface Game extends MageItem, Serializable { public boolean replaceEvent(GameEvent event); //game play methods - //public void init(UUID choosingPlayerId); public void start(UUID choosingPlayerId); public void start(UUID choosingPlayerId, GameOptions options); public void end(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index dd6e8b0ef0..f8bed3cbc3 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -79,6 +79,7 @@ public abstract class GameImpl> implements Game, Serializa private transient Stack savedStates = new Stack(); private transient Object customData; + protected boolean simulation = false; protected final UUID id; protected boolean ready; @@ -113,10 +114,18 @@ public abstract class GameImpl> implements Game, Serializa this.range = game.range; this.attackOption = game.attackOption; this.state = game.state.copy(); -// for (Map.Entry entry: game.gameCards.entrySet()) { -// this.gameCards.put(entry.getKey(), entry.getValue().copy()); -// } this.gameCards = game.gameCards; + this.simulation = game.simulation; + } + + @Override + public boolean isSimulation() { + return simulation; + } + + @Override + public void setSimulation(boolean simulation) { + this.simulation = simulation; } @Override @@ -273,21 +282,27 @@ public abstract class GameImpl> implements Game, Serializa @Override public void bookmarkState() { - saveState(); - logger.fine("Bookmarking state: " + gameStates.getSize()); - savedStates.push(gameStates.getSize() - 1); + if (!simulation) { + saveState(); + logger.fine("Bookmarking state: " + gameStates.getSize()); + savedStates.push(gameStates.getSize() - 1); + } } @Override public void restoreState() { - GameState restore = gameStates.rollback(savedStates.pop()); - if (restore != null) - state.restore(restore); + if (!simulation) { + GameState restore = gameStates.rollback(savedStates.pop()); + if (restore != null) + state.restore(restore); + } } @Override public void removeLastBookmark() { - savedStates.pop(); + if (!simulation) { + savedStates.pop(); + } } @Override @@ -622,13 +637,13 @@ public abstract class GameImpl> implements Game, Serializa } } //20091005 - 704.5j, 801.14 - if (getBattlefield().countAll(filterPlaneswalker) > 1) { //don't bother checking if less than 2 planeswalkers in play + if (getBattlefield().contains(filterPlaneswalker, 2)) { //don't bother checking if less than 2 planeswalkers in play for (Permanent planeswalker: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) { for (String planeswalkertype: planeswalker.getSubtype()) { filterPlaneswalker.getSubtype().clear(); filterPlaneswalker.getSubtype().add(planeswalkertype); filterPlaneswalker.setScopeSubtype(ComparisonScope.Any); - if (getBattlefield().count(filterPlaneswalker, planeswalker.getControllerId(), this) > 1) { + if (getBattlefield().contains(filterPlaneswalker, planeswalker.getControllerId(), this, 2)) { for (Permanent perm: getBattlefield().getActivePermanents(filterPlaneswalker, planeswalker.getControllerId(), this)) { perm.moveToZone(Zone.GRAVEYARD, null, this, false); } @@ -660,11 +675,11 @@ public abstract class GameImpl> implements Game, Serializa } } //20091005 - 704.5k, 801.12 - if (getBattlefield().countAll(filterLegendary) > 1) { //don't bother checking if less than 2 legends in play + if (getBattlefield().contains(filterLegendary, 2)) { //don't bother checking if less than 2 legends in play for (Permanent legend: getBattlefield().getAllActivePermanents(filterLegendary)) { filterLegendName.getName().clear(); filterLegendName.getName().add(legend.getName()); - if (getBattlefield().count(filterLegendName, legend.getControllerId(), this) > 1) { + if (getBattlefield().contains(filterLegendName, legend.getControllerId(), this, 2)) { for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) { dupLegend.moveToZone(Zone.GRAVEYARD, null, this, false); } diff --git a/Mage/src/mage/game/permanent/Battlefield.java b/Mage/src/mage/game/permanent/Battlefield.java index 22e76ba0c1..96da8a41fe 100644 --- a/Mage/src/mage/game/permanent/Battlefield.java +++ b/Mage/src/mage/game/permanent/Battlefield.java @@ -138,6 +138,81 @@ public class Battlefield implements Serializable { return count; } + /** + * Returns true if the battlefield contains at least 1 {@link Permanent} + * that matches the filter. + * This method ignores the range of influence. + * + * @param filter + * @return boolean + */ + public boolean contains(FilterPermanent filter, int num) { + int count = 0; + for (Permanent permanent: field.values()) { + if (filter.match(permanent)) { + count++; + if (num == count) + return true; + } + } + return false; + } + + /** + * Returns true if the battlefield contains at least 1 {@link Permanent} + * that matches the filter and is controlled by controllerId. + * This method ignores the range of influence. + * + * @param filter + * @param controllerId + * @return boolean + */ + public boolean contains(FilterPermanent filter, UUID controllerId, int num) { + int count = 0; + for (Permanent permanent: field.values()) { + if (permanent.getControllerId().equals(controllerId) && filter.match(permanent)) { + count++; + if (num == count) + return true; + } + } + return false; + } + + /** + * Returns true if the battlefield contains at least 1 {@link Permanent} + * that is within the range of influence of the specified player id + * and that matches the supplied filter. + * + * @param filter + * @param sourcePlayerId + * @param game + * @return boolean + */ + public boolean contains(FilterPermanent filter, UUID sourcePlayerId, Game game, int num) { + int count = 0; + if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) { + for (Permanent permanent: field.values()) { + if (filter.match(permanent, sourcePlayerId, game)) { + count++; + if (num == count) + return true; + } + } + } + else { + Set range = game.getPlayer(sourcePlayerId).getInRange(); + for (Permanent permanent: field.values()) { + if (range.contains(permanent.getControllerId()) && filter.match(permanent, sourcePlayerId, game)) { + count++; + if (num == count) + return true; + } + } + } + return false; + } + public void addPermanent(Permanent permanent) { field.put(permanent.getId(), permanent); } diff --git a/Mage/src/mage/target/TargetPermanent.java b/Mage/src/mage/target/TargetPermanent.java index f5963cf01f..a5b2ec24ae 100644 --- a/Mage/src/mage/target/TargetPermanent.java +++ b/Mage/src/mage/target/TargetPermanent.java @@ -105,12 +105,15 @@ public class TargetPermanent> extends TargetObject< */ @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + int remainingTargets = this.minNumberOfTargets - targets.size(); + if (remainingTargets == 0) + return true; int count = 0; MageObject targetSource = game.getObject(sourceId); for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { - if (permanent.canBeTargetedBy(targetSource)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource)) { count++; - if (count >= this.minNumberOfTargets) + if (count >= remainingTargets) return true; } } @@ -127,9 +130,18 @@ public class TargetPermanent> extends TargetObject< */ @Override public boolean canChoose(UUID sourceControllerId, Game game) { - int possibleTargets = game.getBattlefield().count(filter, sourceControllerId, game); - return possibleTargets >= this.minNumberOfTargets && - this.getTargets().size() < possibleTargets; + int remainingTargets = this.minNumberOfTargets - targets.size(); + if (remainingTargets == 0) + return true; + int count = 0; + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { + if (!targets.containsKey(permanent.getId())) { + count++; + if (count >= remainingTargets) + return true; + } + } + return false; } @Override @@ -137,7 +149,7 @@ public class TargetPermanent> extends TargetObject< Set possibleTargets = new HashSet(); MageObject targetSource = game.getObject(sourceId); for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { - if (permanent.canBeTargetedBy(targetSource)) { + if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource)) { possibleTargets.add(permanent.getId()); } } @@ -148,7 +160,9 @@ public class TargetPermanent> extends TargetObject< public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet(); for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { - possibleTargets.add(permanent.getId()); + if (!targets.containsKey(permanent.getId())) { + possibleTargets.add(permanent.getId()); + } } return possibleTargets; }