diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index ec056f11d8..4f86502e5d 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -669,7 +669,7 @@ public class ComputerPlayer> extends PlayerImpl i } @Override - public boolean playXMana(VariableManaCost cost, Game game) { + public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { logger.debug("playXMana"); //put everything into X for (Permanent perm: this.getAvailableManaProducers(game)) { 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 7ced3b867f..ac1a85b247 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 @@ -42,9 +42,14 @@ import java.util.concurrent.TimeoutException; 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; +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.abilities.effects.Effect; import mage.abilities.effects.SearchEffect; import mage.cards.Cards; @@ -497,6 +502,20 @@ public class ComputerPlayer2 extends ComputerPlayer implements return true; } + @Override + public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { + //SimulatedPlayer.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; + } + public void playNext(Game game, UUID activePlayerId, SimulationNode node) { boolean skip = false; while (true) { @@ -611,12 +630,15 @@ public class ComputerPlayer2 extends ComputerPlayer implements @Override public void selectAttackers(Game game) { - logger.debug("selectAttackers"); + if (logger.isDebugEnabled() && combat == null || combat.getGroups().isEmpty()) + logger.debug("not attacking"); if (combat != null) { UUID opponentId = game.getCombat().getDefenders().iterator().next(); for (UUID attackerId: combat.getAttackers()) { this.declareAttacker(attackerId, opponentId, game); - } + if (logger.isDebugEnabled()) + logger.debug("attacking with:" + game.getPermanent(attackerId).getName()); + } } } @@ -629,6 +651,8 @@ public class ComputerPlayer2 extends ComputerPlayer implements if (i < combat.getGroups().size()) { for (UUID blockerId: combat.getGroups().get(i).getBlockers()) { this.declareBlocker(blockerId, groups.get(i).getAttackers().get(0), game); + if (logger.isDebugEnabled()) + logger.debug("blocking with:" + game.getPermanent(blockerId).getName()); } } } 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 5e02552540..8d9044bc46 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 @@ -1,6 +1,29 @@ /* - * To change this template, choose Tools | Templates - * and open the template in the editor. + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. */ package mage.player.ai; @@ -13,6 +36,8 @@ import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.TrampleAbility; import mage.abilities.mana.ManaAbility; +import mage.counters.BoostCounter; +import mage.counters.Counter; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -39,9 +64,9 @@ public class GameStateEvaluator { Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next()); if (game.isGameOver()) { if (player.hasLost() || opponent.hasWon()) - return Integer.MIN_VALUE; + return Integer.MIN_VALUE + 1; if (opponent.hasLost() || player.hasWon()) - return Integer.MAX_VALUE; + return Integer.MAX_VALUE - 1; } int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR; int permanentScore = 0; @@ -73,6 +98,11 @@ public class GameStateEvaluator { if (!(ability instanceof ManaAbility) && ability.canActivate(ability.getControllerId(), game)) value += ability.getEffects().size(); } + for (Counter counter: permanent.getCounters().values()) { + if (!(counter instanceof BoostCounter)) { + value += counter.getCount(); + } + } value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).size(); value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).size(); value += permanent.getManaCost().convertedManaCost(); 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 0bd6533cdf..5ae447975f 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 @@ -38,6 +38,10 @@ import java.util.concurrent.ConcurrentLinkedQueue; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; 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.abilities.mana.ManaOptions; import mage.choices.Choice; import mage.filter.FilterAbility; @@ -99,13 +103,23 @@ public class SimulatedPlayer extends ComputerPlayer { for (Ability ability: playables) { List options = game.getPlayer(playerId).getPlayableOptions(ability, game); if (options.size() == 0) { - allActions.add(ability); + if (ability.getManaCosts().getVariableCosts().size() > 0) { + simulateVariableCosts(ability, game); + } + else { + allActions.add(ability); + } // simulateAction(game, previousActions, ability); } else { // ExecutorService simulationExecutor = Executors.newFixedThreadPool(4); for (Ability option: options) { - allActions.add(option); + if (ability.getManaCosts().getVariableCosts().size() > 0) { + simulateVariableCosts(option, game); + } + else { + allActions.add(option); + } // SimulationWorker worker = new SimulationWorker(game, this, previousActions, option); // simulationExecutor.submit(worker); } @@ -126,6 +140,30 @@ public class SimulatedPlayer extends ComputerPlayer { // } // } + //add a generic mana cost for each amount possible + protected void simulateVariableCosts(Ability ability, Game game) { + int numAvailable = getAvailableManaProducers(game).size(); + for (int i = 0; i < numAvailable; i++) { + Ability newAbility = ability.copy(); + newAbility.addManaCost(new GenericManaCost(i)); + allActions.add(newAbility); + } + } + + @Override + public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { + //simulateVariableCosts method adds a generic mana cost for each option + for (ManaCost manaCost: costs) { + if (manaCost instanceof GenericManaCost) { + cost.setPayment(manaCost.getPayment()); + logger.debug("simulating -- X = " + cost.getPayment().count()); + break; + } + } + cost.setPaid(); + return true; + } + public List addAttackers(Game game) { Map engagements = new HashMap(); //useful only for two player games - will only attack first opponent diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9754d58fbe..b22c1df503 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -51,6 +51,7 @@ import mage.abilities.ActivatedAbility; import mage.abilities.SpecialAction; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.VariableManaCost; import mage.cards.decks.Deck; import mage.choices.ChoiceImpl; @@ -355,7 +356,7 @@ public class HumanPlayer extends PlayerImpl { } @Override - public boolean playXMana(VariableManaCost cost, Game game) { + public boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game) { game.firePlayXManaEvent(playerId, "Pay {X}: {X}=" + cost.getAmount()); waitForResponse(); if (response.getBoolean() != null) { diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index 1e7af9d1ad..1fa918fa87 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 cbb118befe..a436a53d19 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.Server/plugins/mage-player-human.jar b/Mage.Server/plugins/mage-player-human.jar index 4d68195bea..6546beb288 100644 Binary files a/Mage.Server/plugins/mage-player-human.jar and b/Mage.Server/plugins/mage-player-human.jar differ diff --git a/Mage.Tests/plugins/mage-player-aiminimax.jar b/Mage.Tests/plugins/mage-player-aiminimax.jar index cbb118befe..a436a53d19 100644 Binary files a/Mage.Tests/plugins/mage-player-aiminimax.jar and b/Mage.Tests/plugins/mage-player-aiminimax.jar differ diff --git a/Mage/src/mage/abilities/costs/mana/ManaCost.java b/Mage/src/mage/abilities/costs/mana/ManaCost.java index 31291b1629..2f04ef7d95 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCost.java @@ -39,6 +39,7 @@ public interface ManaCost extends Cost { public Mana getMana(); public Mana getPayment(); public void assignPayment(ManaPool pool); + public void setPayment(Mana mana); @Override public String getText(); public ManaCost getUnpaid(); diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java index 3729aaeddf..7a79ea11db 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java @@ -209,6 +209,11 @@ public abstract class ManaCostImpl> extends CostImpl extends ArrayList implements M for (ManaCost cost: this.getUnpaidVariableCosts()) { VariableManaCost vCost = (VariableManaCost) cost; while (!vCost.isPaid()) { - if (player.playXMana(vCost, game)) + if (player.playXMana(vCost, (ManaCosts) this, game)) vCost.assignPayment(player.getManaPool()); else return false; @@ -155,6 +155,9 @@ public class ManaCostsImpl extends ArrayList implements M return variableCosts; } + @Override + public void setPayment(Mana mana) { } + @Override public void assignPayment(ManaPool pool) { //attempt to pay colored costs first diff --git a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java index 94aece97b4..d916586721 100644 --- a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java @@ -38,7 +38,6 @@ import mage.players.ManaPool; */ public class VariableManaCost extends ManaCostImpl implements VariableCost { - protected Mana manaPaid = new Mana(); protected int multiplier; public VariableManaCost() { @@ -53,7 +52,6 @@ public class VariableManaCost extends ManaCostImpl implements public VariableManaCost(VariableManaCost cost) { super(cost); - this.manaPaid = cost.manaPaid.copy(); this.multiplier = cost.multiplier; } @@ -64,7 +62,7 @@ public class VariableManaCost extends ManaCostImpl implements @Override public void assignPayment(ManaPool pool) { - manaPaid.add(pool.getMana()); + payment.add(pool.getMana()); pool.emptyPool(); } @@ -87,19 +85,9 @@ public class VariableManaCost extends ManaCostImpl implements return this; } - @Override - public void clearPaid() { - paid = false; - manaPaid.clear(); - } - - public Mana getPaid() { - return manaPaid; - } - @Override public int getAmount() { - return manaPaid.count() / multiplier; + return payment.count() / multiplier; } @Override diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index 6cc6db99e7..6b339827f3 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -59,6 +59,7 @@ public interface Card extends MageObject { public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag); public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game); + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId); public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId); public void checkTriggers(Zone zone, GameEvent event, Game game); diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index d835ce3a66..c90f80b7b7 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -246,6 +246,28 @@ public abstract class CardImpl> extends MageObjectImpl return false; } + @Override + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { + ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, ability.getId(), controllerId, fromZone, Zone.STACK); + if (!game.replaceEvent(event)) { + if (event.getFromZone() != null) { + switch (event.getFromZone()) { + case GRAVEYARD: + game.getPlayer(ownerId).removeFromGraveyard(this, game); + break; + default: + //logger.warning("moveToZone, not fully implemented: from="+event.getFromZone() + ", to="+event.getToZone()); + } + game.rememberLKI(objectId, event.getFromZone(), this); + } + game.getStack().push(new Spell(this, ability.copy(), controllerId)); + game.setZone(objectId, event.getToZone()); + game.fireEvent(event); + return game.getZone(objectId) == Zone.STACK; + } + return false; + } + @Override public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game) { Zone fromZone = game.getZone(objectId); diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index a5ec7178f3..8b192a610e 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -297,17 +297,17 @@ public class Spell> implements StackObject, Card { @Override public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) { - throw new UnsupportedOperationException("Not supported yet."); + throw new UnsupportedOperationException("Unsupported operation"); } @Override public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game) { - throw new UnsupportedOperationException("Not supported yet."); + throw new UnsupportedOperationException("Unsupported operation"); } @Override public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId) { - throw new UnsupportedOperationException("Not supported yet."); + throw new UnsupportedOperationException("Unsupported operation"); } @Override @@ -322,6 +322,11 @@ public class Spell> implements StackObject, Card { @Override public List getMana() { - throw new UnsupportedOperationException("Not supported yet."); + return card.getMana(); + } + + @Override + public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { + throw new UnsupportedOperationException("Unsupported operation"); } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index a3fe979ca7..3cf0a99349 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -43,6 +43,7 @@ import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.ReplacementEffect; import mage.abilities.mana.ManaOptions; @@ -149,7 +150,7 @@ public interface Player extends MageItem, Copyable { public abstract boolean chooseUse(Outcome outcome, String message, Game game); public abstract boolean choose(Outcome outcome, Choice choice, Game game); public abstract boolean playMana(ManaCost unpaid, Game game); - public abstract boolean playXMana(VariableManaCost cost, Game game); + public abstract boolean playXMana(VariableManaCost cost, ManaCosts costs, Game game); public abstract int chooseEffect(List rEffects, Game game); public abstract TriggeredAbility chooseTriggeredAbility(TriggeredAbilities abilities, Game game); public abstract void selectAttackers(Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 2138df47df..0821edb05a 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -375,7 +375,7 @@ public abstract class PlayerImpl> implements Player, Ser if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getId(), playerId))) { game.bookmarkState(); removeFromHand(card, game); - card.moveToZone(Zone.STACK, ability.getId(), game, false); + card.cast(game, Zone.HAND, ability, playerId); Ability spellAbility = game.getStack().getSpell(ability.getId()).getSpellAbility(); if (spellAbility.activate(game, noMana)) { for (KickerAbility kicker: card.getAbilities().getKickerAbilities()) {