From 0df9763ec1f7e7976b6c5ba89b37bc1fdb099110 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Fri, 17 May 2013 18:45:06 +0000 Subject: [PATCH] AI & Human mana payment, ManaPool, ManaCostBeingPaid refactored. The game uses a byte with flags set instead of color names in more cases. --- .gitattributes | 3 + src/main/java/forge/CardUtil.java | 12 +- .../java/forge/card/ability/ai/CounterAi.java | 5 +- .../ability/effects/ManaReflectedEffect.java | 5 +- .../card/cardfactory/CardFactoryUtil.java | 2 +- src/main/java/forge/card/cost/CostDamage.java | 2 +- .../java/forge/card/cost/CostPayment.java | 5 - src/main/java/forge/card/mana/Mana.java | 85 +- src/main/java/forge/card/mana/ManaCost.java | 2 +- .../forge/card/mana/ManaCostBeingPaid.java | 410 +++------ .../java/forge/card/mana/ManaCostShard.java | 47 +- src/main/java/forge/card/mana/ManaPool.java | 449 +++------- .../card/spellability/AbilityManaPart.java | 35 +- .../forge/control/input/InputPayManaBase.java | 180 ++-- src/main/java/forge/game/GameActionUtil.java | 19 +- src/main/java/forge/game/ai/ComputerUtil.java | 2 +- .../java/forge/game/ai/ComputerUtilCard.java | 26 - .../java/forge/game/ai/ComputerUtilMana.java | 787 ++++++++---------- .../forge/game/player/PlayerController.java | 2 + .../forge/game/player/PlayerControllerAi.java | 7 + .../game/player/PlayerControllerHuman.java | 17 + .../forge/gui/match/nonsingleton/VField.java | 2 +- src/main/java/forge/item/ItemPool.java | 2 + .../java/forge/util/maps/EnumMapToAmount.java | 86 ++ .../java/forge/util/maps/MapToAmount.java | 18 + .../java/forge/util/maps/TreeMapToAmount.java | 85 ++ .../java/forge/card/mana/ManaPartTest.java | 340 ++++---- 27 files changed, 1148 insertions(+), 1487 deletions(-) create mode 100644 src/main/java/forge/util/maps/EnumMapToAmount.java create mode 100644 src/main/java/forge/util/maps/MapToAmount.java create mode 100644 src/main/java/forge/util/maps/TreeMapToAmount.java diff --git a/.gitattributes b/.gitattributes index 52a0e619726..74fde9c705d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14604,9 +14604,12 @@ src/main/java/forge/util/TextUtil.java -text src/main/java/forge/util/XmlUtil.java -text src/main/java/forge/util/maps/CollectionSuppliers.java -text src/main/java/forge/util/maps/EnumMapOfLists.java -text +src/main/java/forge/util/maps/EnumMapToAmount.java -text src/main/java/forge/util/maps/HashMapOfLists.java -text src/main/java/forge/util/maps/MapOfLists.java -text +src/main/java/forge/util/maps/MapToAmount.java -text src/main/java/forge/util/maps/TreeMapOfLists.java -text +src/main/java/forge/util/maps/TreeMapToAmount.java -text src/main/java/forge/util/maps/package-info.java -text src/main/java/forge/util/package-info.java -text src/main/java/forge/util/storage/IStorage.java -text diff --git a/src/main/java/forge/CardUtil.java b/src/main/java/forge/CardUtil.java index 0ff99c8a068..244381a0784 100644 --- a/src/main/java/forge/CardUtil.java +++ b/src/main/java/forge/CardUtil.java @@ -18,6 +18,7 @@ package forge; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -192,7 +193,12 @@ public final class CardUtil { return ret; } - public static Set getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa, + // a nice entry point with minimum parameters + public static Set getReflectableManaColors(final SpellAbility sa) { + return getReflectableManaColors(sa, sa, new HashSet(), new ArrayList()); + } + + private static Set getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa, Set colors, final List parents) { // Here's the problem with reflectable Mana. If more than one is out, // they need to Reflect each other, @@ -266,14 +272,14 @@ public final class CardUtil { colors.add(Constant.Color.COLORLESS); } } else if (reflectProperty.equals("Produce")) { - final ArrayList abilities = new ArrayList(); + final List abilities = new ArrayList(); for (final Card c : cards) { abilities.addAll(c.getManaAbility()); } // currently reflected mana will ignore other reflected mana // abilities - final ArrayList reflectAbilities = new ArrayList(); + final List reflectAbilities = new ArrayList(); for (final SpellAbility ab : abilities) { if (maxChoices == colors.size()) { diff --git a/src/main/java/forge/card/ability/ai/CounterAi.java b/src/main/java/forge/card/ability/ai/CounterAi.java index 4ba1397b441..24eb70741e8 100644 --- a/src/main/java/forge/card/ability/ai/CounterAi.java +++ b/src/main/java/forge/card/ability/ai/CounterAi.java @@ -8,7 +8,6 @@ import forge.card.cost.Cost; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.game.GameState; -import forge.game.ai.ComputerUtilCard; import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilMana; import forge.game.player.Player; @@ -63,7 +62,7 @@ public class CounterAi extends SpellAbilityAi { if (unlessCost != null && !unlessCost.endsWith(">")) { // Is this Usable Mana Sources? Or Total Available Mana? - final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent()); + final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size(); int toPay = 0; boolean setPayX = false; if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) { @@ -132,7 +131,7 @@ public class CounterAi extends SpellAbilityAi { final Card source = sa.getSourceCard(); if (unlessCost != null) { // Is this Usable Mana Sources? Or Total Available Mana? - final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent()); + final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size(); int toPay = 0; boolean setPayX = false; if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) { diff --git a/src/main/java/forge/card/ability/effects/ManaReflectedEffect.java b/src/main/java/forge/card/ability/effects/ManaReflectedEffect.java index 5a05cca579e..fa055d642b4 100644 --- a/src/main/java/forge/card/ability/effects/ManaReflectedEffect.java +++ b/src/main/java/forge/card/ability/effects/ManaReflectedEffect.java @@ -1,11 +1,8 @@ package forge.card.ability.effects; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; -import forge.Card; import forge.CardUtil; import forge.card.MagicColor; import forge.card.ability.AbilityUtils; @@ -27,7 +24,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect { AbilityManaPart ma = sa.getManaPart(); sa.setUndoable(sa.isAbility() && sa.isUndoable()); - final Collection colors = CardUtil.getReflectableManaColors(sa, sa, new HashSet(), new ArrayList()); + final Collection colors = CardUtil.getReflectableManaColors(sa); final List tgtPlayers = getTargetPlayers(sa); for (final Player player : tgtPlayers) { diff --git a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java index 4259b4de9a1..b18d35e7947 100644 --- a/src/main/java/forge/card/cardfactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardfactory/CardFactoryUtil.java @@ -1133,7 +1133,7 @@ public class CardFactoryUtil { if (color.equals("All")) { return cc.getManaPool().totalMana(); } else { - return cc.getManaPool().getAmountOfColor(color); + return cc.getManaPool().getAmountOfColor(MagicColor.fromName(color)); } } diff --git a/src/main/java/forge/card/cost/CostDamage.java b/src/main/java/forge/card/cost/CostDamage.java index 5d2bd1566fa..cea2ddfd6ca 100644 --- a/src/main/java/forge/card/cost/CostDamage.java +++ b/src/main/java/forge/card/cost/CostDamage.java @@ -65,7 +65,7 @@ public class CostDamage extends CostPart { */ @Override public final void payAI(final PaymentDecision decision, final Player ai, SpellAbility ability, Card source) { - ability.getActivatingPlayer().addDamage(decision.c, source); + ai.addDamage(decision.c, source); } /* diff --git a/src/main/java/forge/card/cost/CostPayment.java b/src/main/java/forge/card/cost/CostPayment.java index 10e3813ea01..a02afb793c6 100644 --- a/src/main/java/forge/card/cost/CostPayment.java +++ b/src/main/java/forge/card/cost/CostPayment.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import forge.Card; -import forge.card.mana.ManaCost; import forge.card.spellability.SpellAbility; import forge.game.GameState; import forge.game.player.Player; @@ -178,10 +177,6 @@ public class CostPayment { final Card source = this.ability.getSourceCard(); final List parts = this.cost.getCostParts(); - if (this.getCost().getCostMana() == null) { - parts.add(new CostPartMana(ManaCost.ZERO, null, false)); - } - Map, PaymentDecision> decisions = new HashMap, PaymentDecision>(); // Set all of the decisions before attempting to pay anything diff --git a/src/main/java/forge/card/mana/Mana.java b/src/main/java/forge/card/mana/Mana.java index 16a059d24b9..55742456e70 100644 --- a/src/main/java/forge/card/mana/Mana.java +++ b/src/main/java/forge/card/mana/Mana.java @@ -31,43 +31,19 @@ import forge.card.spellability.AbilityManaPart; * @version $Id$ */ public class Mana { - private byte color; - private Card sourceCard = null; - private AbilityManaPart manaAbility = null; - private boolean hasRestrictions = false; - private boolean pumpCounterMagic = false; - - /** - *

- * Constructor for Mana. - *

- * - * @param col - * a {@link java.lang.String} object. - * @param source - * a {@link forge.Card} object. - * @param manaAbility - * a {@link forge.card.spellability.AbilityMana} object - */ - public Mana(final String col, final Card source, final AbilityManaPart manaAbility) { - this.color = MagicColor.fromName(col); - if (manaAbility != null) { - this.manaAbility = manaAbility; - if (!manaAbility.getManaRestrictions().isEmpty()) { - this.hasRestrictions = true; - } - if (manaAbility.cannotCounterPaidWith()) { - this.pumpCounterMagic = true; - } - } - if (source == null) { - return; - } - - - this.sourceCard = source; + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + color; + result = prime * result + (hasRestrictions ? 1231 : 1237); + result = prime * result + ((manaAbility == null) ? 0 : manaAbility.hashCode()); + result = prime * result + (pumpCounterMagic ? 1231 : 1237); + result = prime * result + ((sourceCard == null) ? 0 : sourceCard.hashCode()); + return result; } + @Override public boolean equals(Object other) { if (!(other instanceof Mana)) { @@ -88,6 +64,45 @@ public class Mana { return mp == mp2 || mp.getManaRestrictions().equals(mp2.getManaRestrictions()); } + + private byte color; + private Card sourceCard = null; + private AbilityManaPart manaAbility = null; + private boolean hasRestrictions = false; + private boolean pumpCounterMagic = false; + + /** + *

+ * Constructor for Mana. + *

+ * + * @param col + * a {@link java.lang.String} object. + * @param source + * a {@link forge.Card} object. + * @param manaAbility + * a {@link forge.card.spellability.AbilityMana} object + */ + public Mana(final byte color, final Card source, final AbilityManaPart manaAbility) { + this.color = color; + if (manaAbility != null) { + this.manaAbility = manaAbility; + if (!manaAbility.getManaRestrictions().isEmpty()) { + this.hasRestrictions = true; + } + if (manaAbility.cannotCounterPaidWith()) { + this.pumpCounterMagic = true; + } + } + if (source == null) { + return; + } + + + this.sourceCard = source; + } + + /** *

* toString. diff --git a/src/main/java/forge/card/mana/ManaCost.java b/src/main/java/forge/card/mana/ManaCost.java index c4bf7c422b2..dfa4a35e7de 100644 --- a/src/main/java/forge/card/mana/ManaCost.java +++ b/src/main/java/forge/card/mana/ManaCost.java @@ -266,7 +266,7 @@ public final class ManaCost implements Comparable { public boolean canBePaidWithAvaliable(ColorSet color) { for (ManaCostShard shard : shards) { - if (!shard.canBePaidWithAvaliable(color)) { + if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) { return false; } } diff --git a/src/main/java/forge/card/mana/ManaCostBeingPaid.java b/src/main/java/forge/card/mana/ManaCostBeingPaid.java index 6412746d794..a8dcc7a2ca8 100644 --- a/src/main/java/forge/card/mana/ManaCostBeingPaid.java +++ b/src/main/java/forge/card/mana/ManaCostBeingPaid.java @@ -18,18 +18,20 @@ package forge.card.mana; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import forge.Card; import forge.CardLists; import forge.CardPredicates; import forge.CardUtil; -import forge.Constant; import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.spellability.SpellAbility; @@ -38,6 +40,9 @@ import forge.game.GameState; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.GuiChoose; +import forge.util.TextUtil; +import forge.util.maps.EnumMapToAmount; +import forge.util.maps.MapToAmount; /** *

@@ -48,7 +53,7 @@ import forge.gui.GuiChoose; * @version $Id$ */ public class ManaCostBeingPaid { - private class ManaCostBeingPaidIterator implements IParserManaCost { + private class ManaCostBeingPaidIterator implements IParserManaCost, Iterator { private Iterator mch; private ManaCostShard nextShard = null; private int remainingShards = 0; @@ -101,7 +106,7 @@ public class ManaCostBeingPaid { // holds Mana_Part objects // ManaPartColor is stored before ManaPartColorless - private final HashMap unpaidShards = new HashMap(); + private final MapToAmount unpaidShards = new EnumMapToAmount(ManaCostShard.class); private byte sunburstMap = 0; private int cntX = 0; private final String sourceRestriction; @@ -132,7 +137,7 @@ public class ManaCostBeingPaid { if (shard == ManaCostShard.X) { cntX++; } else { - increaseShard(shard, 1); + unpaidShards.add(shard); } } increaseColorlessMana(manaCost.getGenericCost()); @@ -149,45 +154,12 @@ public class ManaCostBeingPaid { return ColorSet.fromMask(sunburstMap).countColors(); } - /** - *

- * getColorsPaid. - *

- * - * @return a String. - */ + public final byte getColorsPaid() { return sunburstMap; } - /** - *

- * getUnpaidPhyrexianMana. - *

- * - * @return a {@link java.util.ArrayList} object. - */ - private List getUnpaidPhyrexianMana() { - ArrayList res = new ArrayList(); - for (final Entry part : this.unpaidShards.entrySet()) { - if (!part.getKey().isPhyrexian()) { - continue; - } - for (int i = 0; i < part.getValue(); i++) { - res.add(part.getKey()); - } - } - return res; - } - - /** - *

- * containsPhyrexianMana. - *

- * - * @return a boolean. - */ public final boolean containsPhyrexianMana() { for (ManaCostShard shard : unpaidShards.keySet()) { if (shard.isPhyrexian()) { @@ -197,92 +169,50 @@ public class ManaCostBeingPaid { return false; } - /** - *

- * payPhyrexian. - *

- * - * @return a boolean. - */ public final boolean payPhyrexian() { - final List phy = this.getUnpaidPhyrexianMana(); - - if (phy.size() > 0) { - Integer cnt = unpaidShards.get(phy.get(0)); - if (cnt <= 1) { - unpaidShards.remove(phy.get(0)); - } else { - unpaidShards.put(phy.get(0), Integer.valueOf(cnt - 1)); + ManaCostShard phy = null; + for(ManaCostShard mcs : unpaidShards.keySet()) { + if( mcs.isPhyrexian() ) { + phy = mcs; + break; } - - return true; } - return false; + if (phy == null ) + return false; + + decreaseShard(phy, 1); + return true; } + // takes a Short Color and returns true if it exists in the mana cost. // Easier for split costs - /** - *

- * isColor. - *

- * - * @param color - * a {@link java.lang.String} object. - * @return a boolean. - */ - public final boolean isColor(final String color) { - //if ( "1".equals(color) ) return getColorlessManaAmount() > 0; - if (color.matches("^\\d+$")) { - return getColorlessManaAmount() > 0; - } - + public final boolean needsColor(final byte colorMask) { for (ManaCostShard shard : unpaidShards.keySet()) { + if (shard == ManaCostShard.COLORLESS) + continue; + if (shard.isOr2Colorless()) { + if ((shard.getColorMask() & colorMask) != 0 ) + return true; + } else if (shard.canBePaidWithManaOfColor(colorMask)) + return true; + } + return false; + } - String ss = shard.toString(); - if (ss.contains(color)) { + // isNeeded(String) still used by the Computer, might have problems activating Snow abilities + public final boolean isAnyPartPayableWith(byte colorMask) { + for (ManaCostShard shard : unpaidShards.keySet()) { + if (shard.canBePaidWithManaOfColor(colorMask)) { return true; } } return false; } - // isNeeded(String) still used by the Computer, might have problems - // activating Snow abilities - /** - *

- * isNeeded. - *

- * - * @param mana - * a {@link java.lang.String} object. - * @return a boolean. - */ - public final boolean isNeeded(String mana) { - if (mana.length() > 1) { - mana = MagicColor.toShortString(mana); - } - for (ManaCostShard shard : unpaidShards.keySet()) { - if (canBePaidWith(shard, mana)) { - return true; - } - } - return false; - } - - /** - *

- * isNeeded. - *

- * - * @param paid - * a {@link forge.card.mana.Mana} object. - * @return a boolean. - */ public final boolean isNeeded(final Mana paid) { for (ManaCostShard shard : unpaidShards.keySet()) { - if (canBePaidWith(shard, paid)) { return true; } @@ -290,30 +220,11 @@ public class ManaCostBeingPaid { return false; } - /** - *

- * isPaid. - *

- * - * @return a boolean. - */ + public final boolean isPaid() { return unpaidShards.isEmpty(); } // isPaid() - /** - *

- * payMana. - *

- * - * @param mana - * a {@link forge.card.mana.Mana} object. - * @return a boolean. - */ - public final boolean payMana(final Mana mana) { - return this.addMana(mana); - } - /** *

* payMultipleMana. @@ -323,65 +234,35 @@ public class ManaCostBeingPaid { * a {@link java.lang.String} object. * @return a boolean. */ - public final void payMultipleMana(String mana) { - String[] manas = mana.split(" "); - for (String manaPart : manas) { - if (manaPart.matches("[0-9]+")) { - final int amount = Integer.parseInt(manaPart); - for (int i = 0; i < amount; i++) { - this.payMana(Constant.Color.COLORLESS); + public final String payMultipleMana(String mana) { + List unused = new ArrayList<>(4); + for (String manaPart : TextUtil.split(mana, ' ')) { + if (StringUtils.isNumeric(manaPart)) { + for(int i = Integer.parseInt(manaPart); i > 0; i--) { + boolean wasNeeded = this.payMana("1"); + if(!wasNeeded) { + unused.add(Integer.toString(i)); + break; + } } } else { - this.payMana(forge.card.MagicColor.toLongString(manaPart)); + String color = MagicColor.toShortString(manaPart); + boolean wasNeeded = this.payMana(color); + if(!wasNeeded) + unused.add(color); } } + return unused.isEmpty() ? null : StringUtils.join(unused, ' '); } - /** - *

- * payMana. - *

- * - * @param color - * a {@link java.lang.String} object. - * @return a boolean. - */ - public final boolean payMana(String color) { - color = MagicColor.toShortString(color); - return this.addMana(color); - } - - /** - *

- * increaseColorlessMana. - *

- * - * @param manaToAdd - * a int. - */ public final void increaseColorlessMana(final int manaToAdd) { increaseShard(ManaCostShard.COLORLESS, manaToAdd); } public final void increaseShard(final ManaCostShard shard, final int toAdd) { - if (toAdd <= 0) { - return; - } - - Integer cnt = unpaidShards.get(shard); - unpaidShards.put(shard, Integer.valueOf(cnt == null || cnt == 0 ? toAdd : toAdd + cnt)); + unpaidShards.add(shard, toAdd); } - /** - *

- * decreaseColorlessMana - *

- * . - * - * @param manaToSubtract - * an int. The amount of colorless mana to subtract from the - * cost.Used by Delve. - */ public final void decreaseColorlessMana(final int manaToSubtract) { decreaseShard(ManaCostShard.COLORLESS, manaToSubtract); } @@ -391,26 +272,15 @@ public class ManaCostBeingPaid { return; } - Integer genericCnt = unpaidShards.get(shard); - if (null == genericCnt || genericCnt - manaToSubtract <= 0) { - unpaidShards.remove(shard); - } else { - unpaidShards.put(shard, Integer.valueOf(genericCnt - manaToSubtract)); + if (!unpaidShards.containsKey(shard)) { + System.err.println("Tried to substract a " + shard.toString() + " shard that is not present in this ManaCostBeingPaid"); + return; } + unpaidShards.substract(shard, manaToSubtract); } - /** - *

- * getColorlessManaAmount - *

- * Returns how much colorless mana must be paid to pay the cost.Used by - * Delve AI. - * - * @return an int. - */ public final int getColorlessManaAmount() { - Integer genericCnt = unpaidShards.get(ManaCostShard.COLORLESS); - return genericCnt == null ? 0 : genericCnt; + return unpaidShards.count(ManaCostShard.COLORLESS); } /** @@ -422,40 +292,73 @@ public class ManaCostBeingPaid { * a {@link java.lang.String} object. * @return a boolean. */ - public final boolean addMana(final String mana) { - if (!this.isNeeded(mana)) { - System.out.println("ManaCost : addMana() error, mana not needed - " + mana); + public final boolean payMana(final String mana) { + final byte colorMask = MagicColor.fromName(mana); + if (!this.isAnyPartPayableWith(colorMask)) { + //System.out.println("ManaCost : addMana() error, mana not needed - " + mana); + return false; //throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana); } - byte colorMask = MagicColor.fromName(mana); + + Predicate predCanBePaid = new Predicate() { + @Override public boolean apply(ManaCostShard ms) { + return ms.canBePaidWithManaOfColor(colorMask); + } + }; + + return tryPayMana(colorMask, Iterables.filter(unpaidShards.keySet(), predCanBePaid)); + } + + /** + *

+ * addMana. + *

+ * + * @param mana + * a {@link forge.card.mana.Mana} object. + * @return a boolean. + */ + public final boolean payMana(final Mana mana) { + if (!this.isNeeded(mana)) { + throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana); + } + + Predicate predCanBePaid = new Predicate() { + @Override public boolean apply(ManaCostShard ms) { + return canBePaidWith(ms, mana); + } + }; + + return tryPayMana(mana.getColorCode(), Iterables.filter(unpaidShards.keySet(), predCanBePaid)); + } + + private boolean tryPayMana(final byte colorMask, Iterable payableShards) { ManaCostShard choice = null; - for (ManaCostShard toPay : unpaidShards.keySet()) { - if (canBePaidWith(toPay, mana)) { - // if m is a better to pay than choice - if (choice == null) { - choice = toPay; - continue; - } - if (isFirstChoiceBetter(toPay, choice, colorMask)) { - choice = toPay; - } + for (ManaCostShard toPay : payableShards) { + // if m is a better to pay than choice + if (choice == null) { + choice = toPay; + continue; + } + if (isFirstChoiceBetter(toPay, choice, colorMask)) { + choice = toPay; } } // for if (choice == null) { return false; } - + decreaseShard(choice, 1); if (choice.isOr2Colorless() && choice.getColorMask() != colorMask ) { this.increaseColorlessMana(1); } - + this.sunburstMap |= colorMask; return true; } - private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte b) { - return getPayPriority(s1, b) > getPayPriority(s2, b); + private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte colorMask) { + return getPayPriority(s1, colorMask) > getPayPriority(s2, colorMask); } private int getPayPriority(ManaCostShard bill, byte paymentColor) { @@ -472,66 +375,16 @@ public class ManaCostBeingPaid { } return 8; } - return 5; } private boolean canBePaidWith(ManaCostShard shard, Mana mana) { - if (shard.isSnow() && mana.isSnow()) { - return true; - } - //System.err.println(String.format("ManaPaid: paying for %s with %s" , shard, mana)); - // debug here even more; - return canBePaidWith(shard, MagicColor.toShortString(mana.getColor())); - } - - private boolean canBePaidWith(ManaCostShard shard, String mana) { - // most debug here!! - String sShard = shard.toString(); - boolean res = "1".equals(sShard) || sShard.contains(mana) || shard.isOr2Colorless(); - //System.out.println(String.format("Str: paying for %s with %s => %d" , shard, mana, res ? 1 : 0)); - return res; - } - - /** - *

- * addMana. - *

- * - * @param mana - * a {@link forge.card.mana.Mana} object. - * @return a boolean. - */ - public final boolean addMana(final Mana mana) { - if (!this.isNeeded(mana)) { - throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana); - } - - ManaCostShard choice = null; - for (ManaCostShard toPay : unpaidShards.keySet()) { - if (canBePaidWith(toPay, mana)) { - // if m is a better to pay than choice - if (choice == null) { - choice = toPay; - continue; - } - if (isFirstChoiceBetter(toPay, choice, mana.getColorCode())) { - choice = toPay; - } - } - } // for - if (choice == null) { + if (shard.isSnow() && !mana.isSnow()) { return false; } - - - decreaseShard(choice, 1); - if (choice.isOr2Colorless() && choice.getColorMask() != mana.getColorCode() ) { - this.increaseColorlessMana(1); - } - - this.sunburstMap |= mana.getColorCode(); - return true; + + byte color = mana.getColorCode(); + return shard.canBePaidWithManaOfColor(color); } public final void combineManaCost(final ManaCost extra) { @@ -545,10 +398,6 @@ public class ManaCostBeingPaid { increaseColorlessMana(extra.getGenericCost()); } - public final void combineManaCost(final String extra) { - combineManaCost(new ManaCost(new ManaCostParser(extra))); - } - /** * To string. * @@ -615,17 +464,24 @@ public class ManaCostBeingPaid { return new ManaCost(new ManaCostBeingPaidIterator()); } - /** - *

- * Getter for the field xcounter. - *

- * - * @return a int. - */ public final int getXcounter() { return cntX; } + + public final List getUnpaidShards() { + List result = new ArrayList(); + for(Entry kv : unpaidShards.entrySet()) { + for(int i = kv.getValue().intValue(); i > 0; i--) { + result.add(kv.getKey()); + } + } + for(int i = cntX; i > 0; i--) { + result.add(ManaCostShard.X); + } + return result; + } + /** *

* removeColorlessMana. @@ -782,5 +638,13 @@ public class ManaCostBeingPaid { public String getSourceRestriction() { return sourceRestriction; - } + } + + public Iterable getDistinctShards() { + return unpaidShards.keySet(); + } + + public int getUnpaidShards(ManaCostShard key) { + return unpaidShards.count(key); + } } diff --git a/src/main/java/forge/card/mana/ManaCostShard.java b/src/main/java/forge/card/mana/ManaCostShard.java index 0053473c683..1c97fe32d23 100644 --- a/src/main/java/forge/card/mana/ManaCostShard.java +++ b/src/main/java/forge/card/mana/ManaCostShard.java @@ -17,21 +17,14 @@ */ package forge.card.mana; -import forge.card.ColorSet; -import forge.card.MagicColor; import forge.util.BinaryUtil; /** * The Class CardManaCostShard. */ -public enum ManaCostShard { +public enum ManaCostShard implements Comparable { + // declaration order matters! Place the shards that offer least ways to be paid for first - - COLORLESS(ManaAtom.COLORLESS, "1"), - - X(ManaAtom.IS_X, "X"), - S(ManaAtom.IS_SNOW, "S"), - /* Pure colors */ WHITE(ManaAtom.WHITE, "W"), BLUE(ManaAtom.BLUE, "U"), @@ -51,6 +44,17 @@ public enum ManaCostShard { BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"), RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"), + /* Or 2 colorless */ + W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"), + U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"), + B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"), + R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"), + G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G"), + + // Snow and colorless + S(ManaAtom.IS_SNOW, "S"), + COLORLESS(ManaAtom.COLORLESS, "1"), + /* Phyrexian */ PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"), PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"), @@ -58,13 +62,7 @@ public enum ManaCostShard { PR(ManaAtom.RED | ManaAtom.OR_2_LIFE, "R/P", "PR"), PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "G/P", "PG"), - /* Or 2 colorless */ - W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"), - U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"), - B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"), - R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"), - G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G"); - + X(ManaAtom.IS_X, "X"); private final int shard; @@ -275,23 +273,6 @@ public enum ManaCostShard { public boolean isOr2Colorless() { return (this.shard & ManaAtom.OR_2_COLORLESS) != 0; } - /** - * TODO: Can pay for this shard with unlimited mana of given color combination? - * @param color - * @return - */ - public boolean canBePaidWithAvaliable(ColorSet color) { - // can pay with life? - if (this.isPhyrexian()) { - return true; - } - // can pay with any color? - if (this.isOr2Colorless()) { - return true; - } - // either colored part is empty, or there are same colors in shard and mana source - return (COLORS_SUPERPOSITION & this.shard) == 0 || (color.getColor() & this.shard) > 0; - } public boolean canBePaidWithManaOfColor(byte colorCode) { return this.isOr2Colorless() || (COLORS_SUPERPOSITION & this.shard) == 0 || (colorCode & this.shard) > 0; diff --git a/src/main/java/forge/card/mana/ManaPool.java b/src/main/java/forge/card/mana/ManaPool.java index a6115ac2c70..7ea029ef55c 100644 --- a/src/main/java/forge/card/mana/ManaPool.java +++ b/src/main/java/forge/card/mana/ManaPool.java @@ -18,21 +18,20 @@ package forge.card.mana; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; +import java.util.Collection; import java.util.List; -import java.util.Map; - +import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import forge.Constant; +import com.google.common.base.Supplier; +import forge.card.MagicColor; import forge.card.spellability.AbilityManaPart; import forge.card.spellability.SpellAbility; import forge.game.GlobalRuleChange; import forge.game.player.Player; -import forge.gui.GuiChoose; +import forge.util.maps.MapOfLists; +import forge.util.maps.TreeMapOfLists; /** *

@@ -43,14 +42,14 @@ import forge.gui.GuiChoose; * @version $Id$ */ public class ManaPool { - // current paying moved to SpellAbility - private final ArrayList floatingMana = new ArrayList(); - private final int[] floatingTotals = new int[6]; // WUBRGC - private final int[] floatingSnowTotals = new int[6]; // WUBRGC + private final static Supplier> listFactory = new Supplier>(){ + @Override public List get() { return new ArrayList(); } + }; + + private final MapOfLists floatingMana = new TreeMapOfLists(listFactory); /** Constant map. */ - private static final Map MAP = new HashMap(); private final Player owner; /** @@ -63,91 +62,15 @@ public class ManaPool { */ public ManaPool(final Player player) { owner = player; - ManaPool.MAP.put(Constant.Color.WHITE, 0); - ManaPool.MAP.put(Constant.Color.BLUE, 1); - ManaPool.MAP.put(Constant.Color.BLACK, 2); - ManaPool.MAP.put(Constant.Color.RED, 3); - ManaPool.MAP.put(Constant.Color.GREEN, 4); - ManaPool.MAP.put(Constant.Color.COLORLESS, 5); } - /** - *

- * calculatManaTotals for the Player panel. - *

- * - */ - private void calculateManaTotals() { - for (int i = 0; i < floatingTotals.length; i++) { - floatingTotals[i] = 0; - floatingSnowTotals[i] = 0; - } - - for (final Mana m : this.floatingMana) { - if (m.isSnow()) { - floatingSnowTotals[ManaPool.MAP.get(m.getColor())]++; - } else { - floatingTotals[ManaPool.MAP.get(m.getColor())]++; - } - } + public final int getAmountOfColor(final byte color) { + Collection ofColor = floatingMana.get(color); + return ofColor == null ? 0 : ofColor.size(); } - /** - *

- * getAmountOfColor. - *

- * - * @param color - * a {@link java.lang.String} object. - * @return a int. - */ - public final int getAmountOfColor(final String color) { - if (color.equals(Constant.Color.SNOW)) { - // If looking for Snow mana return total Snow - int total = 0; - for (int i : this.floatingSnowTotals) { - total += i; - } - return total; - } - - // If looking for Color/Colorless total Snow and non-Snow - int i = ManaPool.MAP.get(color); - return this.floatingTotals[i] + this.floatingSnowTotals[i]; - } - - /** - *

- * isEmpty. - *

- * - * @return a boolean. - */ - private boolean isEmpty() { - return this.floatingMana.size() == 0; - } - - /** - *

- * addManaToPool. - *

- * - * @param pool - * a {@link java.util.ArrayList} object. - * @param mana - * a {@link forge.card.mana.Mana} object. - */ - private void addManaToPool(final ArrayList pool, final Mana mana) { - pool.add(mana); - if (pool.equals(this.floatingMana)) { - int i = ManaPool.MAP.get(mana.getColor()); - if (mana.isSnow()) { - this.floatingSnowTotals[i]++; - } - else { - this.floatingTotals[i]++; - } - } + private void addMana(final Mana mana) { + floatingMana.add(mana.getColorCode(), mana); owner.updateObservers(); } @@ -159,9 +82,9 @@ public class ManaPool { * @param manaList * a {@link java.util.ArrayList} object. */ - public final void addManaToFloating(final ArrayList manaList) { + public final void add(final Iterable manaList) { for (final Mana m : manaList) { - this.addManaToPool(this.floatingMana, m); + this.addMana(m); } owner.getGame().getAction().checkStateEffects(); owner.updateObservers(); @@ -175,36 +98,24 @@ public class ManaPool { *

*/ public final int clearPool(boolean isEndOfPhase) { - int numRemoved = 0; + // isEndOfPhase parameter: true = end of phase, false = mana drain effect + if (this.floatingMana.isEmpty()) { return 0; } if (isEndOfPhase && owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty)) { - return numRemoved; + return 0; } - if (this.floatingMana.isEmpty()) { - this.calculateManaTotals(); - //this.owner.updateObservers(); - return numRemoved; - } + int numRemoved = 0; + boolean keepGreenMana = isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end."); - if (isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.")) { - // Omnath in play, clear all non-green mana - int i = 0; - while (i < this.floatingMana.size()) { - if (this.floatingMana.get(i).isColor(Constant.Color.GREEN)) { - i++; - continue; - } - numRemoved++; - this.floatingMana.remove(i); - } - } else { - numRemoved = this.floatingMana.size(); - this.floatingMana.clear(); + Set keys = floatingMana.keySet(); + if ( keepGreenMana ) + keys.remove(Byte.valueOf(MagicColor.GREEN)); + + for(Byte b : keys) { + numRemoved += floatingMana.get(b).size(); + floatingMana.get(b).clear(); } - this.calculateManaTotals(); - //this.owner.updateObservers(); - return numRemoved; } @@ -221,166 +132,86 @@ public class ManaPool { * a {@link forge.card.spellability.SpellAbility} object. * @return a {@link forge.card.mana.Mana} object. */ - private Mana getMana(final String manaStr, final SpellAbility saBeingPaidFor, String restriction) { - final ArrayList pool = this.floatingMana; - - //System.out.format("ManaStr='%s' ...", manaStr); - ManaCostShard shard = ManaCostShard.parseNonGeneric(manaStr); - //System.out.format("Shard=%s (%d)", shard.toString(), shard.getColorMask() ); - //System.out.println(); - - // What are the available options? - final List> weightedOptions = new ArrayList>(); - for (final Mana thisMana : pool) { - - if (!thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) { - continue; - } - - boolean canPay = shard.canBePaidWithManaOfColor(thisMana.getColorCode()); - if (!canPay || (shard.isSnow() && !thisMana.isSnow())) { - continue; - } - - if( StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().isType(restriction) ) - continue; - - // prefer colorless mana to spend - int weight = thisMana.isColorless() ? 5 : 0; - - // prefer restricted mana to spend - if (thisMana.isRestricted()) { - weight += 2; - } - - // Spend non-snow mana first - if (!thisMana.isSnow()) { - weight += 1; - } - - weightedOptions.add(Pair.of(thisMana, weight)); - } + private Mana getMana(final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) { + final List> weightedOptions = selectManaToPayFor(shard, saBeingPaidFor, restriction); // Exclude border case if (weightedOptions.isEmpty()) { - return null; // There is no matching mana in the pool } - // have at least one option at this moment - int maxWeight = Integer.MIN_VALUE; - int equalWeights = 0; - Mana toPay = null; + // select equal weight possibilities + List manaChoices = new ArrayList(); + int bestWeight = Integer.MIN_VALUE; for (Pair option : weightedOptions) { int thisWeight = option.getRight(); - if (thisWeight > maxWeight) { - maxWeight = thisWeight; - equalWeights = 1; - toPay = option.getLeft(); - } else if (thisWeight == maxWeight) { - equalWeights++; + Mana thisMana = option.getLeft(); + + if (thisWeight > bestWeight) { + manaChoices.clear(); + bestWeight = thisWeight; + } + + if (thisWeight == bestWeight) { + // add only distinct Mana-s + boolean haveDuplicate = false; + for(Mana m : manaChoices) { + if(m.equals(thisMana) ) { + haveDuplicate = true; + break; + } + } + if(!haveDuplicate) + manaChoices.add(thisMana); } } // got an only one best option? - if (equalWeights == 1) { - return toPay; + if (manaChoices.size() == 1) { + return manaChoices.get(0); } - // select equal weight possibilities - List options = new ArrayList(); - for (Pair option : weightedOptions) { - int thisWeight = option.getRight(); - if (maxWeight == thisWeight) { - options.add(option.getLeft()); - } - } + // Let them choose then + return owner.getController().chooseManaFromPool(manaChoices); + } - // if the options are equal, there is no difference on which to spend - toPay = options.get(0); - boolean allAreEqual = true; - for (int i = 1; i < options.size(); i++) { - if (!toPay.equals(options.get(i))) { - - allAreEqual = false; - break; - } - } - - if (allAreEqual) { - return toPay; - } - - // Not found a good one - then let them choose - final List manaChoices = options; - Mana payment = null; - - final int[] normalMana = { 0, 0, 0, 0, 0, 0 }; - final int[] snowMana = { 0, 0, 0, 0, 0, 0 }; - - // loop through manaChoices adding - for (final Mana m : manaChoices) { - if (m.isSnow()) { - snowMana[ManaPool.MAP.get(m.getColor())]++; - } else { - normalMana[ManaPool.MAP.get(m.getColor())]++; - } - } - - int totalMana = 0; - final ArrayList alChoice = new ArrayList(); - for (int i = 0; i < normalMana.length; i++) { - totalMana += normalMana[i]; - totalMana += snowMana[i]; - if (normalMana[i] > 0) { - alChoice.add(Constant.Color.COLORS.get(i) + "(" + normalMana[i] + ")"); - } - if (snowMana[i] > 0) { - alChoice.add("{S}" + Constant.Color.COLORS.get(i) + "(" + snowMana[i] + ")"); - } - } - - if (alChoice.size() == 1) { - payment = manaChoices.get(0); - return payment; - } - - int numColorless = 0; - if (StringUtils.isNumeric(manaStr)) { - numColorless = Integer.parseInt(manaStr); - } - if (numColorless >= totalMana) { - payment = manaChoices.get(0); - return payment; - } - - Object o; - - if (this.owner.isHuman()) { - o = GuiChoose.oneOrNone("Pay Mana from Mana Pool", alChoice); - } else { - o = alChoice.get(0); // owner is computer - } - - if (o != null) { - String ch = o.toString(); - final boolean grabSnow = ch.startsWith("{S}"); - ch = ch.replace("{S}", ""); - - ch = ch.substring(0, ch.indexOf("(")); - - for (final Mana m : manaChoices) { - if (m.isColor(ch) && (!grabSnow || (grabSnow && m.isSnow()))) { - if (payment == null) { - payment = m; - } else if (payment.isSnow() && !m.isSnow()) { - payment = m; - } + private List> selectManaToPayFor(final ManaCostShard shard, final SpellAbility saBeingPaidFor, + String restriction) { + final List> weightedOptions = new ArrayList>(); + for (final Byte manaKey : this.floatingMana.keySet()) { + if(!shard.canBePaidWithManaOfColor(manaKey.byteValue())) + continue; + + for(final Mana thisMana : this.floatingMana.get(manaKey)) { + if (!thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) { + continue; } + + boolean canPay = shard.canBePaidWithManaOfColor(thisMana.getColorCode()); + if (!canPay || (shard.isSnow() && !thisMana.isSnow())) { + continue; + } + + if( StringUtils.isNotBlank(restriction) && !thisMana.getSourceCard().isType(restriction) ) + continue; + + // prefer colorless mana to spend + int weight = thisMana.isColorless() ? 5 : 0; + + // prefer restricted mana to spend + if (thisMana.isRestricted()) { + weight += 2; + } + + // Spend non-snow mana first + if (!thisMana.isSnow()) { + weight += 1; + } + + weightedOptions.add(Pair.of(thisMana, weight)); } } - - return payment; + return weightedOptions; } /** @@ -393,87 +224,27 @@ public class ManaPool { * @param choice * a {@link forge.card.mana.Mana} object. */ - private void removeManaFrom(final ArrayList pool, final Mana choice) { - if (choice != null && pool.contains(choice)) { - pool.remove(choice); - if (pool.equals(this.floatingMana)) { - int i = ManaPool.MAP.get(choice.getColor()); - if (choice.isSnow()) { - this.floatingSnowTotals[i]--; - } - else { - this.floatingTotals[i]--; - } - } + private void removeMana(final Mana mana) { + Collection cm = floatingMana.get(mana.getColorCode()); + if (cm.remove(mana)) { owner.updateObservers(); } } - /** - *

- * payManaFromPool. - *

- * - * @param saBeingPaidFor - * a {@link forge.card.spellability.SpellAbility} object. - * @param manaCost - * a {@link forge.card.mana.ManaCostBeingPaid} object. - * @return a {@link forge.card.mana.ManaCostBeingPaid} object. - */ - public final void payManaFromPool(final SpellAbility saBeingPaidFor, ManaCostBeingPaid manaCost) { - - // paying from Mana Pool - if (manaCost.isPaid() || this.isEmpty()) { - return; - } - - final ArrayList manaPaid = saBeingPaidFor.getPayingMana(); - - List splitCost = Arrays.asList(manaCost.toString().replace("X ", "").replace("P", "").split(" ")); - Collections.reverse(splitCost); // reverse to pay colorful parts first with matching-color mana while it lasts. - for(String part : splitCost) { - int loops = StringUtils.isNumeric(part) ? Integer.parseInt(part) : 1; - for(int i = 0; i < loops; i++ ) { - final Mana mana = this.getMana(part, saBeingPaidFor, manaCost.getSourceRestriction()); - if (mana != null) { - manaCost.payMana(mana); - manaPaid.add(mana); - this.removeManaFrom(this.floatingMana, mana); - if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) { - saBeingPaidFor.getSourceCard().setCanCounter(false); - } - } - } - } - } - - /** - *

- * payManaFromPool. - *

- * - * @param saBeingPaidFor - * a {@link forge.card.spellability.SpellAbility} object. - * @param manaCost - * a {@link forge.card.mana.ManaCostBeingPaid} object. - * @param manaStr - * a {@link java.lang.String} object. - * @return a {@link forge.card.mana.ManaCostBeingPaid} object. - */ - public final void payManaFromPool(final SpellAbility saBeingPaidFor, final ManaCostBeingPaid manaCost, final String manaStr) { - if (manaStr.trim().equals("") || manaCost.isPaid()) { + public final void payManaFromPool(final SpellAbility saBeingPaidFor, final ManaCostBeingPaid manaCost, final ManaCostShard manaShard) { + if (manaCost.isPaid()) { return; } // get a mana of this type from floating, bail if none available - final Mana mana = this.getMana(manaStr, saBeingPaidFor, manaCost.getSourceRestriction()); + final Mana mana = this.getMana(manaShard, saBeingPaidFor, manaCost.getSourceRestriction()); if (mana == null) { return; // no matching mana in the pool } else if (manaCost.isNeeded(mana)) { manaCost.payMana(mana); saBeingPaidFor.getPayingMana().add(mana); - this.removeManaFrom(this.floatingMana, mana); + this.removeMana( mana); if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) { saBeingPaidFor.getSourceCard().setCanCounter(false); } @@ -508,11 +279,11 @@ public class ManaPool { } paidAbs.add(ma); // assumes some part on the mana produced by the ability will get used - for (final Mana mana : abManaPart.getLastProduced()) { + for (final Mana mana : abManaPart.getLastManaProduced()) { if (manaCost.isNeeded(mana)) { manaCost.payMana(mana); manaPaid.add(mana); - this.removeManaFrom(this.floatingMana, mana); + this.removeMana(mana); if (mana.addsNoCounterMagic() && sa.getSourceCard() != null) { sa.getSourceCard().setCanCounter(false); } @@ -551,33 +322,35 @@ public class ManaPool { ability.getSourceCard().setCanCounter(true); } for (final Mana m : manaPaid) { - this.addManaToPool(this.floatingMana, m); + this.addMana(m); } } manaPaid.clear(); - this.calculateManaTotals(); this.owner.updateObservers(); } private boolean accountFor(final SpellAbility sa, final AbilityManaPart ma) { - final ArrayList manaPaid = sa.getPayingMana(); + final List manaPaid = sa.getPayingMana(); - if ((manaPaid.size() == 0) && (this.floatingMana.size() == 0)) { + if (manaPaid.isEmpty() && this.floatingMana.isEmpty()) { return false; } final ArrayList removePaying = new ArrayList(); final ArrayList removeFloating = new ArrayList(); + boolean manaNotAccountedFor = false; // loop over mana produced by mana ability - for (Mana mana : ma.getLastProduced()) { + for (Mana mana : ma.getLastManaProduced()) { + Collection poolLane = this.floatingMana.get(mana.getColorCode()); + if (manaPaid.contains(mana)) { removePaying.add(mana); } - else if (this.floatingMana.contains(mana)) { + else if (poolLane != null && poolLane.contains(mana)) { removeFloating.add(mana); } else { @@ -593,10 +366,10 @@ public class ManaPool { } for (int k = 0; k < removePaying.size(); k++) { - this.removeManaFrom(manaPaid, removePaying.get(k)); + manaPaid.remove(removePaying.get(k)); } for (int k = 0; k < removeFloating.size(); k++) { - this.removeManaFrom(this.floatingMana, removeFloating.get(k)); + this.removeMana(removeFloating.get(k)); } return true; } diff --git a/src/main/java/forge/card/spellability/AbilityManaPart.java b/src/main/java/forge/card/spellability/AbilityManaPart.java index c617b2723b8..c4a1848ac57 100644 --- a/src/main/java/forge/card/spellability/AbilityManaPart.java +++ b/src/main/java/forge/card/spellability/AbilityManaPart.java @@ -22,7 +22,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + import forge.Card; +import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.Mana; import forge.card.mana.ManaPool; @@ -44,7 +47,7 @@ public class AbilityManaPart implements java.io.Serializable { private String origProduced; private String lastExpressChoice = ""; private String manaRestrictions = ""; - private transient ArrayList lastProduced = new ArrayList(); + private transient ArrayList lastManaProduced = new ArrayList(); /** The canceled. */ private boolean canceled = false; @@ -106,22 +109,20 @@ public class AbilityManaPart implements java.io.Serializable { final ManaPool manaPool = player.getManaPool(); //clear lastProduced - this.lastProduced.clear(); + this.lastManaProduced.clear(); // loop over mana produced string for (final String c : produced.split(" ")) { - try { - int colorlessAmount = Integer.parseInt(c); - for (int i = 0; i < colorlessAmount; i++) { - this.lastProduced.add(new Mana(c, source, this)); + if(StringUtils.isNumeric(c)) + for(int i = Integer.parseInt(c); i > 0; i--) { + this.lastManaProduced.add(new Mana((byte)0, source, this)); } - } catch (NumberFormatException e) { - this.lastProduced.add(new Mana(c, source, this)); - } + else + this.lastManaProduced.add(new Mana(MagicColor.fromName(c), source, this)); } // add the mana produced to the mana pool - manaPool.addManaToFloating(this.lastProduced); + manaPool.add(this.lastManaProduced); // Run triggers final HashMap runParams = new HashMap(); @@ -228,6 +229,16 @@ public class AbilityManaPart implements java.io.Serializable { this.lastExpressChoice = s; } + public void setExpressChoice(ColorSet cs) { + StringBuilder sb = new StringBuilder(); + if(cs.hasBlack()) sb.append("B "); + if(cs.hasBlue()) sb.append("U "); + if(cs.hasWhite()) sb.append("W "); + if(cs.hasRed()) sb.append("R "); + if(cs.hasGreen()) sb.append("G "); + this.lastExpressChoice = cs.toString(); + } + /** *

* Getter for the field lastAnyChoice. @@ -256,8 +267,8 @@ public class AbilityManaPart implements java.io.Serializable { * * @return a {@link java.lang.String} object. */ - public ArrayList getLastProduced() { - return this.lastProduced; + public ArrayList getLastManaProduced() { + return this.lastManaProduced; } /** diff --git a/src/main/java/forge/control/input/InputPayManaBase.java b/src/main/java/forge/control/input/InputPayManaBase.java index 9f3ba5b5f00..260383e1d7c 100644 --- a/src/main/java/forge/control/input/InputPayManaBase.java +++ b/src/main/java/forge/control/input/InputPayManaBase.java @@ -1,18 +1,18 @@ package forge.control.input; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import org.apache.commons.lang3.StringUtils; import forge.Card; import forge.CardUtil; -import forge.Constant; import forge.FThreads; +import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.ability.ApiType; import forge.card.mana.ManaCostBeingPaid; +import forge.card.mana.ManaCostShard; import forge.card.spellability.AbilityManaPart; import forge.card.spellability.SpellAbility; import forge.game.GameState; @@ -71,34 +71,26 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I * a {@link java.lang.String} object. * @return a boolean. */ - private static boolean canMake(final SpellAbility am, final String mana) { - if (mana.contains("1")) { + private static boolean canMake(final SpellAbility am, final byte colorMask) { + if (colorMask == 0) { return true; } AbilityManaPart m = am.getManaPart(); - if (mana.contains("S") && m.isSnow()) { - return true; - } + if (m.isAnyMana()) { return true; } if (am.getApi() == ApiType.ManaReflected) { - final Iterable reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet(), new ArrayList()); + final Iterable reflectableColors = CardUtil.getReflectableManaColors(am); for (final String color : reflectableColors) { - if (mana.contains(MagicColor.toShortString(color))) { + if (0 != (colorMask & MagicColor.fromName(color))) { return true; } } } else { - String[] colorsProduced; - if (m.isComboMana()) { - colorsProduced = m.getComboColors().split(" "); - } - else { - colorsProduced = m.getOrigProduced().split(" "); - } - for (final String color : colorsProduced) { - if (mana.contains(color)) { + String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced(); + for (final String color : colorsProduced.split(" ")) { + if (0 != (colorMask & MagicColor.fromName(color))) { return true; } } @@ -119,7 +111,7 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I protected void useManaFromPool(byte colorCode) { useManaFromPool(colorCode, manaCost); } protected void useManaFromPool(byte colorCode, ManaCostBeingPaid manaCost) { // Convert Color to short String - player.getManaPool().payManaFromPool(saPaidFor, manaCost, MagicColor.toShortString(colorCode)); + player.getManaPool().payManaFromPool(saPaidFor, manaCost, ManaCostShard.parseNonGeneric(MagicColor.toShortString(colorCode))); onManaAbilityPlayed(null); showMessage(); @@ -144,21 +136,15 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I return; } - final StringBuilder cneeded = new StringBuilder(); - final StringBuilder colorRequired = new StringBuilder(); - boolean choice = true; - boolean skipExpress = false; - - for (final String color : Constant.Color.MANA_COLORS) { - String shortColor = MagicColor.toShortString(color); - if (manaCost.isNeeded(color)) { - cneeded.append(shortColor); - } - if (manaCost.isColor(shortColor)) { - colorRequired.append(shortColor); - } + byte colorCanUse = 0; + byte colorNeeded = 0; + + for (final byte color : MagicColor.WUBRG) { + if (manaCost.isAnyPartPayableWith(color)) colorCanUse |= color; + if (manaCost.needsColor(color)) colorNeeded |= color; } - + boolean canUseColorless = manaCost.isAnyPartPayableWith((byte)0); + List abilities = new ArrayList(); // you can't remove unneeded abilities inside a for(am:abilities) loop :( @@ -166,108 +152,62 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I if( StringUtils.isNotBlank(typeRes) && !card.isType(typeRes)) return; + boolean guessAbilityWithRequiredColors = true; for (SpellAbility ma : card.getManaAbility()) { ma.setActivatingPlayer(player); + AbilityManaPart m = null; SpellAbility tail = ma; while (m == null && tail != null) { m = tail.getManaPart(); tail = tail.getSubAbility(); } - if (m == null) { - continue; - } else if (!ma.canPlay()) { - continue; - } else if (!InputPayManaBase.canMake(ma, cneeded.toString())) { - continue; - } else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) { - continue; - } else if (!m.meetsManaRestrictions(saPaidFor)) { - continue; - } + + if (m == null || !ma.canPlay()) continue; + if (!canUseColorless && !InputPayManaBase.canMake(ma, colorCanUse)) continue; + if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) continue; + if (!m.meetsManaRestrictions(saPaidFor)) continue; abilities.add(ma); - if (!skipExpress) { - // skip express mana if the ability is not undoable or reusable - if (!ma.isUndoable()) { - skipExpress = true; - continue; - } else if (!ma.getPayCosts().isRenewableResource()) { - skipExpress = true; - continue; - } - } + // skip express mana if the ability is not undoable or reusable + if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource()) + guessAbilityWithRequiredColors = false; } + if (abilities.isEmpty()) { return; } - + // Store some information about color costs to help with any mana choices - String colorsNeeded = colorRequired.toString(); - if ("1".equals(colorsNeeded)) { // only colorless left - if (saPaidFor.getSourceCard() != null - && saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) { - colorsNeeded = ""; + if (colorNeeded == 0) { // only colorless left + if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) { String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(","); for (String negColor : negEffects) { - // convert long color strings to short color strings - if (negColor.length() > 1) { - negColor = MagicColor.toShortString(negColor); - } - if (!colorsNeeded.contains(negColor)) { - colorsNeeded = colorsNeeded.concat(negColor); - } + byte col = MagicColor.fromName(negColor); + colorCanUse |= col; } } - else { - colorsNeeded = "W"; - } - } - else { - // remove colorless from colors needed - colorsNeeded = colorsNeeded.replace("1", ""); - } + } + + // If the card has sunburst or any other ability that tracks mana spent, // skip express Mana choice - if (saPaidFor.getSourceCard() != null - && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) { - colorsNeeded = "WUBRG"; - skipExpress = true; + if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) { + colorCanUse = MagicColor.ALL_COLORS; + guessAbilityWithRequiredColors = false; } - if (!skipExpress) { + boolean choice = true; + if (guessAbilityWithRequiredColors) { // express Mana Choice final ArrayList colorMatches = new ArrayList(); - for (final SpellAbility am : abilities) { - AbilityManaPart m = am.getManaPart(); - if (am.getApi() == ApiType.ManaReflected) { - final Iterable reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet(), new ArrayList()); - for (final String color : reflectableColors) { - if (manaCost.isColor(color)) { - // checking if color - colorMatches.add(am); - } - } - } else if (m.isAnyMana()) { - colorMatches.add(am); - } else { - String[] colorsProduced; - if (m.isComboMana()) { - colorsProduced = m.getComboColors().split(" "); - } - else { - colorsProduced = m.getOrigProduced().split(" "); - } - for (final String color : colorsProduced) { - if (manaCost.isColor(color)) { - // checking if color - colorMatches.add(am); - } - } - } + for (SpellAbility sa : abilities) { + if (abilityProducesManaOfColor(sa, colorNeeded)) + colorMatches.add(sa); } + if (colorMatches.isEmpty()) { // can only match colorless just grab the first and move on. @@ -286,7 +226,7 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I } // save off color needed for use by any mana and reflected mana - subchosen.getManaPart().setExpressChoice(colorsNeeded); + subchosen.getManaPart().setExpressChoice(ColorSet.fromMask(colorCanUse)); // System.out.println("Chosen sa=" + chosen + " of " + chosen.getSourceCard() + " to pay mana"); Runnable proc = new Runnable() { @@ -299,6 +239,30 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I FThreads.invokeInNewThread(proc, true); // EDT that removes lockUI from input stack will call our showMessage() method } + + + private static boolean abilityProducesManaOfColor(SpellAbility am, byte neededColor) { + AbilityManaPart m = am.getManaPart(); + if (am.getApi() == ApiType.ManaReflected) { + final Iterable reflectableColors = CardUtil.getReflectableManaColors(am); + for (final String color : reflectableColors) { + if ((neededColor & MagicColor.fromName(color)) != 0) { + return true; + } + } + } else if (m.isAnyMana()) { + return true; + } else { + String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced(); + for (final String color : colorsProduced.split(" ")) { + if ((neededColor & MagicColor.fromName(color)) != 0) { + // checking if color + return true; + } + } + } + return false; + } public void onManaAbilityPlayed(final SpellAbility saPaymentSrc) { if ( saPaymentSrc != null) // null comes when they've paid from pool diff --git a/src/main/java/forge/game/GameActionUtil.java b/src/main/java/forge/game/GameActionUtil.java index ba225b86531..e43db5db316 100644 --- a/src/main/java/forge/game/GameActionUtil.java +++ b/src/main/java/forge/game/GameActionUtil.java @@ -22,6 +22,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.lang.StringUtils; + import com.google.common.base.Function; import com.google.common.collect.Iterables; @@ -1082,17 +1084,12 @@ public final class GameActionUtil { sb.append(baseMana); } else { - try { - // if baseMana is an integer(colorless), just multiply amount - // and baseMana - final int base = Integer.parseInt(baseMana); - sb.append(base * amount); - } catch (final NumberFormatException e) { - for (int i = 0; i < amount; i++) { - if (i != 0) { - sb.append(" "); - } - sb.append(baseMana); + if(StringUtils.isNumeric(baseMana)) { + sb.append(amount * Integer.parseInt(baseMana)); + } else { + sb.append(baseMana); + for (int i = 1; i < amount; i++) { + sb.append(" ").append(baseMana); } } } diff --git a/src/main/java/forge/game/ai/ComputerUtil.java b/src/main/java/forge/game/ai/ComputerUtil.java index faaca09fd63..c2a0d0dd7f1 100644 --- a/src/main/java/forge/game/ai/ComputerUtil.java +++ b/src/main/java/forge/game/ai/ComputerUtil.java @@ -187,7 +187,7 @@ public class ComputerUtil { if (unless != null && !unless.endsWith(">")) { final int amount = AbilityUtils.calculateAmount(source, unless, sa); - final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent()); + final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size(); // If the Unless isn't enough, this should be less likely to be used if (amount > usableManaSources) { diff --git a/src/main/java/forge/game/ai/ComputerUtilCard.java b/src/main/java/forge/game/ai/ComputerUtilCard.java index b232b9502aa..1b8fca0c356 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCard.java +++ b/src/main/java/forge/game/ai/ComputerUtilCard.java @@ -29,7 +29,6 @@ import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; import forge.game.player.Player; -import forge.game.zone.ZoneType; import forge.item.CardPrinted; import forge.util.Aggregates; @@ -784,31 +783,6 @@ public class ComputerUtilCard { return result; } - /** - *

- * getUsableManaSources. - *

- * - * @param player - * a {@link forge.game.player.Player} object. - * @return a int. - */ - public static int getUsableManaSources(final Player player) { - List list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), new Predicate() { - @Override - public boolean apply(final Card c) { - for (final SpellAbility am : ComputerUtilMana.getAIPlayableMana(c)) { - am.setActivatingPlayer(player); - if (am.canPlay()) { - return true; - } - } - return false; - } - }); - - return list.size(); - } /** *

diff --git a/src/main/java/forge/game/ai/ComputerUtilMana.java b/src/main/java/forge/game/ai/ComputerUtilMana.java index f492da0cbcd..e5bd2e9d86e 100644 --- a/src/main/java/forge/game/ai/ComputerUtilMana.java +++ b/src/main/java/forge/game/ai/ComputerUtilMana.java @@ -1,14 +1,16 @@ package forge.game.ai; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; import com.google.common.base.Predicate; - import forge.Card; import forge.CardLists; import forge.CardUtil; @@ -19,6 +21,7 @@ import forge.card.ability.ApiType; import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.cost.CostPayment; +import forge.card.mana.ManaAtom; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostShard; @@ -30,6 +33,10 @@ import forge.card.spellability.SpellAbility; import forge.game.GameActionUtil; import forge.game.player.Player; import forge.game.zone.ZoneType; +import forge.util.maps.CollectionSuppliers; +import forge.util.maps.EnumMapOfLists; +import forge.util.maps.MapOfLists; +import forge.util.maps.TreeMapOfLists; /** * TODO: Write javadoc for this type. @@ -59,206 +66,286 @@ public class ComputerUtilMana { ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana); final Card card = sa.getSourceCard(); - // Make mana needed to avoid negative effect a mandatory cost for the AI - - final String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(","); - int amountAdded = 0; - for (int nStr = 0; nStr < negEffects.length; nStr++) { - // convert long color strings to short color strings - if (negEffects[nStr].length() > 1) { - negEffects[nStr] = MagicColor.toShortString(negEffects[nStr]); - } - // make mana mandatory for AI - if (!cost.isColor(negEffects[nStr]) && cost.getColorlessManaAmount() > amountAdded) { - cost.combineManaCost(negEffects[nStr]); - amountAdded++; - } + + adjustManaCostToAvoidNegEffects(cost, card); + + final ManaPool manapool = ai.getManaPool(); + List unpaidShards = cost.getUnpaidShards(); + Collections.sort(unpaidShards); // most difficult shards must come first + for(ManaCostShard part : unpaidShards) { + if( part != ManaCostShard.X) + manapool.payManaFromPool(sa, cost, part); } - // TODO: should it be an error condition if amountAdded is greater - // than the colorless in the original cost? (ArsenalNut - 120102) - // adjust colorless amount to account for added mana - cost.decreaseColorlessMana(amountAdded); - - - final ManaPool manapool = ai.getManaPool(); - - manapool.payManaFromPool(sa, cost); - if (cost.isPaid()) { // refund any mana taken from mana pool when test manapool.clearManaPaid(sa, test); return true; } - // get map of mana abilities - final Map> manaAbilityMap = ComputerUtilMana.mapManaSources(ai, checkPlayable); - // initialize ArrayList list for mana needed - final List> partSources = new ArrayList>(); - final List partPriority = new ArrayList(); - final String[] costParts = cost.toString().replace("X ", "").split(" "); - boolean foundAllSources = ComputerUtilMana.findManaSources(ai, manaAbilityMap, partSources, partPriority, costParts); - if (!foundAllSources) { - if (!test) { - // real payment should not arrive here - throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard()); - } - manapool.clearManaPaid(sa, test); // refund any mana taken from mana pool + // arrange all mana abilities by color produced. + final MapOfLists manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable); + if ( manaAbilityMap.isEmpty() ) { + manapool.clearManaPaid(sa, test); return false; } - - // Create array to keep track of sources used - final ArrayList usedSources = new ArrayList(); - // this is to prevent errors for mana sources that have abilities that - // cost mana. - usedSources.add(sa.getSourceCard()); + + // select which abilities may be used for each shard + Map> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost); + // Loop over mana needed - int nPriority = 0; - List negEffectPaid = new ArrayList(); - while (nPriority < partPriority.size()) { - final int nPart = partPriority.get(nPriority); - final ManaCostBeingPaid costPart = new ManaCostBeingPaid(costParts[nPart]); - // Loop over mana abilities that can be used to current mana cost part - for (final SpellAbility ma : partSources.get(nPart)) { - final Card sourceCard = ma.getSourceCard(); - - // Check if source has already been used - if (usedSources.contains(sourceCard)) { - continue; + ManaCostShard toPay = null; + while (!cost.isPaid()) { + toPay = getNextShardToPay(cost, sourcesForShards); + + Collection saList = sourcesForShards.get(toPay); + List payableSources = new ArrayList(); + if( saList != null ) { + for (final SpellAbility ma : saList) { + if( canPayShardWithSpellAbility(toPay, ai, ma, sa, checkPlayable || !test ) ) { + payableSources.add(ma); + } } - - // Check if AI can still play this mana ability - ma.setActivatingPlayer(ai); - // if the AI can't pay the additional costs skip the mana ability - if (ma.getPayCosts() != null && checkPlayable) { - if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) { + } + + if( payableSources.isEmpty() ) { + if(!toPay.isPhyrexian() || !ai.canPayLife(2)) + break; // cannot pay + + cost.payPhyrexian(); + if( !test ) + ai.payLife(2, sa.getSourceCard()); + continue; + } + + // choose the best SA. + SpellAbility saPayment = payableSources.get(0); + + setExpressColorChoice(sa, ai, cost, toPay, saPayment); + + if ( test ) { + String manaProduced = toPay.isSnow() ? "S" : GameActionUtil.generatedMana(saPayment); + //System.out.println(manaProduced); + /* String remainder = */ cost.payMultipleMana(manaProduced); + // add it to mana pool? + + // remove from available lists + for(Collection kv : sourcesForShards.values()) { + kv.remove(saPayment); + } + } else { + if (saPayment.getPayCosts() != null) { + final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment); + if (!pay.payComputerCosts(ai, ai.getGame())) { continue; } - } else if (sourceCard.isTapped() && checkPlayable) { - continue; - } - - AbilityManaPart m = ma.getManaPart(); - // Check for mana restrictions - if (!m.meetsManaRestrictions(sa)) { - continue; - } - - String manaProduced; - // Check if paying snow mana - if ("S".equals(costParts[nPart])) { - manaProduced = "S"; } else { - if (m.isComboMana()) { - String colorChoice = costParts[nPart]; - m.setExpressChoice(colorChoice); - colorChoice = ComputerUtilMana.getComboManaChoice(ai, ma, sa, cost); - m.setExpressChoice(colorChoice); - } else if (ma.getApi() == ApiType.ManaReflected) { - if (CardUtil.getReflectableManaColors(ma, ma, new HashSet(), new ArrayList()).contains(MagicColor.toLongString(costParts[nPart]))) { - m.setExpressChoice(costParts[nPart]); - } else { - m.setExpressChoice("0"); - } - } - // check if ability produces any color - else if (m.isAnyMana()) { - String colorChoice = costParts[nPart]; - // Check for - // 1) Colorless - // 2) Split e.g. 2/G - // 3) Hybrid e.g. UG - if (costParts[nPart].matches("[0-9]+")) { - colorChoice = "W"; - for (int n = 0; n < negEffects.length; n++) { - if (!negEffectPaid.contains(negEffects[n])) { - colorChoice = negEffects[n]; - break; - } - } - } else if (costParts[nPart].contains("2/")) { - colorChoice = costParts[nPart].replace("2/", ""); - } else if (costParts[nPart].length() > 1) { - colorChoice = costParts[nPart].substring(0, 1); - for (int n = 0; n < negEffects.length; n++) { - if (costParts[nPart].contains(negEffects[n]) && !negEffectPaid.contains(negEffects[n])) { - colorChoice = negEffects[n]; - break; - } - } - } - m.setExpressChoice(colorChoice); - } - // get produced mana - manaProduced = GameActionUtil.generatedMana(ma); - if (manaProduced.matches("0")) { - continue; - } + System.err.println("Ability " + saPayment + " from " + saPayment.getSourceCard() + " had NULL as payCost"); + saPayment.getSourceCard().tap(); } - - // add source card to used list - usedSources.add(sourceCard); - negEffectPaid.add(manaProduced); - costPart.payMultipleMana(manaProduced); - - if (!test) { - // Pay additional costs - if (ma.getPayCosts() != null) { - final CostPayment pay = new CostPayment(ma.getPayCosts(), ma); - if (!pay.payComputerCosts(ai, ai.getGame())) { - continue; - } - } else { - sourceCard.tap(); - } - // resolve mana ability - //ma.resolve(); - AbilityUtils.resolve(ma, false); - // subtract mana from mana pool - manapool.payManaFromAbility(sa, cost, ma); - } else { - cost.payMultipleMana(manaProduced); + + AbilityUtils.resolve(saPayment, false); + // subtract mana from mana pool + manapool.payManaFromAbility(sa, cost, saPayment); + + // no need remove abilities from resource map, once their costs are paid and consume resources, they can not be used again + } + } + + manapool.clearManaPaid(sa, test); + if(!cost.isPaid()) { + if( test ) + return false; + else + throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard().getName() + ". Didn't find what to pay for " + toPay); + } + + + // if (sa instanceof Spell_Permanent) // should probably add this + sa.getSourceCard().setColorsPaid(cost.getColorsPaid()); + sa.getSourceCard().setSunburstValue(cost.getSunburst()); + return true; + } // payManaCost() + + + private static void setExpressColorChoice(final SpellAbility sa, final Player ai, ManaCostBeingPaid cost, + ManaCostShard toPay, SpellAbility saPayment) { + if ( saPayment.getManaPart().isComboMana() ) + getComboManaChoice(ai, saPayment, sa, cost); + else if (saPayment.getApi() == ApiType.ManaReflected) { + System.out.println("Evaluate reflected mana of: " + saPayment.getSourceCard()); + Set reflected = CardUtil.getReflectableManaColors(saPayment); + + for(byte c : MagicColor.WUBRG) { + if (toPay.canBePaidWithManaOfColor(c) && reflected.contains(MagicColor.toLongString(c))) { + saPayment.getManaPart().setExpressChoice(MagicColor.toShortString(c)); + return; } - // check if cost part is paid - if (costPart.isPaid() || cost.isPaid()) { + } + } else if ( saPayment.getManaPart().isAnyMana()) { + byte colorChoice = 0; + if (toPay.isOr2Colorless()) + colorChoice = toPay.getColorMask(); + else for( byte c : MagicColor.WUBRG ) { + if ( toPay.canBePaidWithManaOfColor(c)) { + colorChoice = c; break; } - } // end of mana ability loop - - if (!costPart.isPaid() || cost.isPaid()) { - break; - } else { - nPriority++; } - } // end of cost parts loop - - //check for phyrexian mana - if (!cost.isPaid() && cost.containsPhyrexianMana() && ai.getLife() > 5 && ai.canPayLife(2)) { - cost.payPhyrexian(); - if (!test) { - ai.payLife(2, sa.getSourceCard()); + saPayment.getManaPart().setExpressChoice(MagicColor.toShortString(colorChoice)); + } + } + + private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) { + final Card sourceCard = ma.getSourceCard(); + + if (toPay.isSnow() && !sourceCard.isSnow() ) return false; + + AbilityManaPart m = ma.getManaPart(); + if (!m.meetsManaRestrictions(sa)) { + return false; + } + + if ( checkCosts ) { + // Check if AI can still play this mana ability + ma.setActivatingPlayer(ai); + if (ma.getPayCosts() != null ) { // if the AI can't pay the additional costs skip the mana ability + if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) { + return false; + } + } else if (sourceCard.isTapped() ) { + return false; } } - - manapool.clearManaPaid(sa, test); - // check if paid - if (cost.isPaid()) { - // if (sa instanceof Spell_Permanent) // should probably add this - sa.getSourceCard().setColorsPaid(cost.getColorsPaid()); - sa.getSourceCard().setSunburstValue(cost.getSunburst()); - return true; + + if (m.isComboMana()) { + for(String s : m.getComboColors().split(" ")) { + if ( "Any".equals(s) || toPay.canBePaidWithManaOfColor(MagicColor.fromName(s))) + return true; + } + return false; + + } else if (ma.getApi() == ApiType.ManaReflected) { + Set reflected = CardUtil.getReflectableManaColors(ma); + + for(byte c : MagicColor.WUBRG) { + if (toPay.canBePaidWithManaOfColor(c) && reflected.contains(MagicColor.toLongString(c))) { + m.setExpressChoice(MagicColor.toShortString(c)); + return true; + } + } + return false; } - - if (!test) { - final StringBuilder sb = new StringBuilder(); - sb.append("ComputerUtil : payManaCost() cost was not paid for "); - sb.append(sa.getSourceCard().getName()); - throw new RuntimeException(sb.toString()); + return true; + } + + + private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Map> sourcesForShards) { + // mind the priorities + // * Pay mono-colored first, + // * Pay 2/C with matching colors + // * pay hybrids + // * pay phyrexian, keep mana for colorless + // * pay colorless + + for(ManaCostShard s : cost.getDistinctShards()) { // should check in which order EnumMap enumerates keys. If it's same as enum member declaration, nothing else needs to be done. + return s; } + return null; + } + + private static void adjustManaCostToAvoidNegEffects(ManaCostBeingPaid cost, final Card card) { + // Make mana needed to avoid negative effect a mandatory cost for the AI + for (String manaPart : card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")) { + // convert long color strings to short color strings + byte mask = MagicColor.fromName(manaPart); + + // make mana mandatory for AI + if (!cost.needsColor(mask) && cost.getColorlessManaAmount() > 0) { + ManaCostShard shard = ManaCostShard.valueOf(mask); + cost.increaseShard(shard, 1); + cost.decreaseColorlessMana(1); + } + } + } + + /** + *

+ * getComboManaChoice. + *

+ * + * @param abMana + * a {@link forge.card.spellability.AbilityMana} object. + * @param saRoot + * a {@link forge.card.spellability.SpellAbility} object. + * @param cost + * a {@link forge.card.mana.ManaCostBeingPaid} object. + * @return String + */ + private static void getComboManaChoice(final Player ai, final SpellAbility manaAb, final SpellAbility saRoot, final ManaCostBeingPaid cost) { - return false; + final StringBuilder choiceString = new StringBuilder(); + final Card source = manaAb.getSourceCard(); + final AbilityManaPart abMana = manaAb.getManaPart(); - } // payManaCost() + if (abMana.isComboMana()) { + int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1; + final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost.toString().replace("X ", "")); + final String[] comboColors = abMana.getComboColors().split(" "); + for (int nMana = 1; nMana <= amount; nMana++) { + String choice = ""; + // Use expressChoice first + if (!abMana.getExpressChoice().isEmpty()) { + choice = abMana.getExpressChoice(); + abMana.clearExpressChoice(); + byte colorMask = MagicColor.fromName(choice); + if (abMana.canProduce(choice) && testCost.isAnyPartPayableWith(colorMask)) { + choiceString.append(choice); + testCost.payMultipleMana(choice); + continue; + } + } + // check colors needed for cost + if (!testCost.isPaid()) { + // Loop over combo colors + for (String color : comboColors) { + if (testCost.isAnyPartPayableWith(MagicColor.fromName(color))) { + testCost.payMultipleMana(color); + if (nMana != 1) { + choiceString.append(" "); + } + choiceString.append(color); + choice = color; + break; + } + } + if (!choice.isEmpty()) { + continue; + } + } + // check if combo mana can produce most common color in hand + String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn( + ZoneType.Hand)); + if (!commonColor.isEmpty() && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) { + choice = MagicColor.toShortString(commonColor); + } + else { + // default to first color + choice = comboColors[0]; + } + if (nMana != 1) { + choiceString.append(" "); + } + choiceString.append(choice); + } + } + if (choiceString.toString().isEmpty()) { + choiceString.append("0"); + } + + abMana.setExpressChoice(choiceString.toString()); + } + // TODO: this code is disconnected now, it was moved here from MagicStack, where X cost is not processed any more public static void computerPayX(final SpellAbility sa, Player player, int xCost) { @@ -297,134 +384,37 @@ public class ComputerUtilMana { * @param foundAllSources * @return Were all mana sources found? */ - private static boolean findManaSources(final Player ai, final Map> manaAbilityMap, - final List> partSources, final List partPriority, - final String[] costParts) { - final String[] shortColors = { "W", "U", "B", "R", "G" }; - boolean foundAllSources; - if (manaAbilityMap.isEmpty()) { - foundAllSources = false; - } else { - foundAllSources = true; - // loop over cost parts - for (int nPart = 0; nPart < costParts.length; nPart++) { - final List srcFound = new ArrayList(); - // Test for: - // 1) Colorless - // 2) Split e.g. 2/G - // 3) Hybrid e.g. U/G - // defaults to single short color - if (costParts[nPart].matches("[0-9]+")) { // Colorless - srcFound.addAll(manaAbilityMap.get("1")); - } else if (costParts[nPart].contains("2/")) { // Split - final String colorKey = costParts[nPart].replace("2/", ""); - // add specified color sources first - if (manaAbilityMap.containsKey(colorKey)) { - srcFound.addAll(manaAbilityMap.get(colorKey)); - } - // add other available colors - for (final String color : shortColors) { - if (!colorKey.contains(color)) { - // Is source available? - if (manaAbilityMap.containsKey(color)) { - srcFound.addAll(manaAbilityMap.get(color)); - } - } - } - } else if (costParts[nPart].contains("P")) { // Phyrexian - String newPart = costParts[nPart].replace("/P", ""); - if (manaAbilityMap.containsKey(newPart)) { - srcFound.addAll(manaAbilityMap.get(newPart)); - } else if (ai.getLife() > 8) { //Pay with life - partSources.add(nPart, srcFound); - continue; - } - } else if (costParts[nPart].length() > 1) { // Hybrid - final String firstColor = costParts[nPart].substring(0, 1); - final String secondColor = costParts[nPart].substring(2); - final boolean foundFirst = manaAbilityMap.containsKey(firstColor); - final boolean foundSecond = manaAbilityMap.containsKey(secondColor); - if (foundFirst || foundSecond) { - if (!foundFirst) { - srcFound.addAll(manaAbilityMap.get(secondColor)); - } else if (!foundSecond) { - srcFound.addAll(manaAbilityMap.get(firstColor)); - } else if (manaAbilityMap.get(firstColor).size() > manaAbilityMap.get(secondColor).size()) { - srcFound.addAll(manaAbilityMap.get(firstColor)); - srcFound.addAll(manaAbilityMap.get(secondColor)); - } else { - srcFound.addAll(manaAbilityMap.get(secondColor)); - srcFound.addAll(manaAbilityMap.get(firstColor)); - } - } - } else { // single color - if (manaAbilityMap.containsKey(costParts[nPart])) { - srcFound.addAll(manaAbilityMap.get(costParts[nPart])); - } - } - - // add sources to array lists - partSources.add(nPart, srcFound); - // add to sorted priority list - if (srcFound.size() > 0) { - int i; - for (i = 0; i < partPriority.size(); i++) { - if (srcFound.size() <= partSources.get(i).size()) { - break; - } - } - partPriority.add(i, nPart); - } else { - foundAllSources = false; - break; - } - } - } - return foundAllSources; - } + private static MapOfLists groupAndOrderToPayShards(final Player ai, final MapOfLists manaAbilityMap, final ManaCostBeingPaid cost) { + MapOfLists res = new EnumMapOfLists(ManaCostShard.class, CollectionSuppliers.hashSets()); - private static void increaseManaCostByX(SpellAbility sa, ManaCostBeingPaid cost, int xValue) { - String manaXColor = sa.hasParam("XColor") ? sa.getParam("XColor") : ""; - if (manaXColor.isEmpty()) { - cost.increaseColorlessMana(xValue); - } else { - if (manaXColor.equals("B")) { - cost.increaseShard(ManaCostShard.BLACK, xValue); - } else if (manaXColor.equals("G")) { - cost.increaseShard(ManaCostShard.GREEN, xValue); - } else if (manaXColor.equals("R")) { - cost.increaseShard(ManaCostShard.RED, xValue); - } else if (manaXColor.equals("U")) { - cost.increaseShard(ManaCostShard.BLUE, xValue); - } else if (manaXColor.equals("W")) { - cost.increaseShard(ManaCostShard.WHITE, xValue); - - /* Max: Never seen these options in real cards - } else if (manaXColor.contains("B") && manaXColor.contains("G")) { - cost.increaseShard(ManaCostShard.BG, xValue); - } else if (manaXColor.contains("B") && manaXColor.contains("R")) { - cost.increaseShard(ManaCostShard.BR, xValue); - } else if (manaXColor.contains("R") && manaXColor.contains("G")) { - cost.increaseShard(ManaCostShard.RG, xValue); - } else if (manaXColor.contains("U") && manaXColor.contains("B")) { - cost.increaseShard(ManaCostShard.UB, xValue); - } else if (manaXColor.contains("U") && manaXColor.contains("G")) { - cost.increaseShard(ManaCostShard.UG, xValue); - } else if (manaXColor.contains("U") && manaXColor.contains("R")) { - cost.increaseShard(ManaCostShard.UR, xValue); - } else if (manaXColor.contains("W") && manaXColor.contains("B")) { - cost.increaseShard(ManaCostShard.WB, xValue); - } else if (manaXColor.contains("W") && manaXColor.contains("G")) { - cost.increaseShard(ManaCostShard.WG, xValue); - } else if (manaXColor.contains("W") && manaXColor.contains("R")) { - cost.increaseShard(ManaCostShard.WR, xValue); - } else if (manaXColor.contains("W") && manaXColor.contains("U")) { - cost.increaseShard(ManaCostShard.WU, xValue); - */ - } + // loop over cost parts + for (ManaCostShard shard : cost.getDistinctShards() ) { + if ( shard == ManaCostShard.S ) { + res.put(shard, manaAbilityMap.get(ManaAtom.IS_SNOW)); + continue; + } + + if (shard.isOr2Colorless()) { + Integer colorKey = Integer.valueOf(shard.getColorMask()); + if (manaAbilityMap.containsKey(colorKey) ) + res.addAll(shard, manaAbilityMap.get(colorKey)); + if (manaAbilityMap.containsKey(ManaAtom.COLORLESS) ) + res.addAll(shard, manaAbilityMap.get(ManaAtom.COLORLESS)); + continue; + } + + for(Entry> kv : manaAbilityMap.entrySet()) { + if( shard.canBePaidWithManaOfColor(kv.getKey().byteValue()) ) + res.addAll(shard, kv.getValue()); } } + if (cost.getColorlessManaAmount() > 0 && manaAbilityMap.containsKey(ManaAtom.COLORLESS)) + res.addAll(ManaCostShard.COLORLESS, manaAbilityMap.get(ManaAtom.COLORLESS)); + + return res; + } + /** * Calculate the ManaCost for the given SpellAbility. * @param sa @@ -459,7 +449,9 @@ public class ComputerUtilMana { } } - increaseManaCostByX(sa, cost, manaToAdd); + String manaXColor = sa.getParam("XColor"); + ManaCostShard shardToGrow = ManaCostShard.parseNonGeneric(manaXColor == null ? "1" : manaXColor); + cost.increaseShard(shardToGrow, manaToAdd); if (!test) { card.setXManaCostPaid(manaToAdd / cost.getXcounter()); @@ -470,24 +462,19 @@ public class ComputerUtilMana { } //This method is currently used by AI to estimate human's available mana - private static List getAvailableMana(final Player ai, final boolean checkPlayable) { + public static List getAvailableMana(final Player ai, final boolean checkPlayable) { final List list = ai.getCardsIn(ZoneType.Battlefield); list.addAll(ai.getCardsIn(ZoneType.Hand)); final List manaSources = CardLists.filter(list, new Predicate() { @Override public boolean apply(final Card c) { - if (checkPlayable) { - for (final SpellAbility am : getAIPlayableMana(c)) { - am.setActivatingPlayer(ai); - if (am.canPlay()) { - return true; - } + for (final SpellAbility am : getAIPlayableMana(c)) { + am.setActivatingPlayer(ai); + if (am.canPlay() || !checkPlayable) { + return true; } - - return false; - } else { - return true; } + return false; } }); // CardListFilter @@ -507,8 +494,7 @@ public class ComputerUtilMana { // 2. Search for mana sources that have a certain number of abilities // 3. Use lands that produce any color many // 4. all other sources (creature, costs, drawback, etc.) - for (int i = 0; i < manaSources.size(); i++) { - final Card card = manaSources.get(i); + for (Card card : manaSources) { if (card.isCreature() || card.isEnchanted()) { otherManaSources.add(card); @@ -588,27 +574,12 @@ public class ComputerUtilMana { //This method is currently used by AI to estimate mana available to human - private static Map> mapManaSources(final Player ai, boolean checkPlayable) { - final Map> manaMap = new HashMap>(); - - final List whiteSources = new ArrayList(); - final List blueSources = new ArrayList(); - final List blackSources = new ArrayList(); - final List redSources = new ArrayList(); - final List greenSources = new ArrayList(); - final List colorlessSources = new ArrayList(); - final List snowSources = new ArrayList(); - - // Get list of current available mana sources - final List manaSources = getAvailableMana(ai, checkPlayable); - - // Loop over all mana sources - for (int i = 0; i < manaSources.size(); i++) { - final Card sourceCard = manaSources.get(i); - final ArrayList manaAbilities = getAIPlayableMana(sourceCard); - - // Loop over all mana abilities for a source - for (final SpellAbility m : manaAbilities) { + private static MapOfLists groupSourcesByManaColor(final Player ai, boolean checkPlayable) { + final MapOfLists manaMap = new TreeMapOfLists(CollectionSuppliers.arrayLists()); + + // Loop over all current available mana sources + for (final Card sourceCard : getAvailableMana(ai, checkPlayable)) { + for (final SpellAbility m : getAIPlayableMana(sourceCard)) { m.setActivatingPlayer(ai); if (!m.canPlay() && checkPlayable) { continue; @@ -621,60 +592,32 @@ public class ComputerUtilMana { continue; } } - - // add to colorless source list - colorlessSources.add(m); - + + manaMap.add(ManaAtom.COLORLESS, m); // add to colorless source list + + Set reflectedColors = CardUtil.getReflectableManaColors(m); // find possible colors - if (m.getManaPart().canProduce("W") - || CardUtil.getReflectableManaColors(m, m, new HashSet(), new ArrayList()).contains(Constant.Color.WHITE)) { - whiteSources.add(m); + if (m.getManaPart().canProduce("W") || reflectedColors.contains(Constant.Color.WHITE)) { + manaMap.add(ManaAtom.WHITE, m); } - if (m.getManaPart().canProduce("U") - || CardUtil.getReflectableManaColors(m, m, new HashSet(), new ArrayList()).contains(Constant.Color.BLUE)) { - blueSources.add(m); + if (m.getManaPart().canProduce("U") || reflectedColors.contains(Constant.Color.BLUE)) { + manaMap.add(ManaAtom.BLUE, m); } - if (m.getManaPart().canProduce("B") - || CardUtil.getReflectableManaColors(m, m, new HashSet(), new ArrayList()).contains(Constant.Color.BLACK)) { - blackSources.add(m); + if (m.getManaPart().canProduce("B") || reflectedColors.contains(Constant.Color.BLACK)) { + manaMap.add(ManaAtom.BLACK, m); } - if (m.getManaPart().canProduce("R") - || CardUtil.getReflectableManaColors(m, m, new HashSet(), new ArrayList()).contains(Constant.Color.RED)) { - redSources.add(m); + if (m.getManaPart().canProduce("R") || reflectedColors.contains(Constant.Color.RED)) { + manaMap.add(ManaAtom.RED, m); } - if (m.getManaPart().canProduce("G") - || CardUtil.getReflectableManaColors(m, m, new HashSet(), new ArrayList()).contains(Constant.Color.GREEN)) { - greenSources.add(m); + if (m.getManaPart().canProduce("G") || reflectedColors.contains(Constant.Color.GREEN)) { + manaMap.add(ManaAtom.GREEN, m); } if (m.getManaPart().isSnow()) { - snowSources.add(m); + manaMap.add(ManaAtom.IS_SNOW, m); } } // end of mana abilities loop } // end of mana sources loop - - // Add sources - if (!whiteSources.isEmpty()) { - manaMap.put("W", whiteSources); - } - if (!blueSources.isEmpty()) { - manaMap.put("U", blueSources); - } - if (!blackSources.isEmpty()) { - manaMap.put("B", blackSources); - } - if (!redSources.isEmpty()) { - manaMap.put("R", redSources); - } - if (!greenSources.isEmpty()) { - manaMap.put("G", greenSources); - } - if (!colorlessSources.isEmpty()) { - manaMap.put("1", colorlessSources); - } - if (!snowSources.isEmpty()) { - manaMap.put("S", snowSources); - } - + return manaMap; } @@ -704,83 +647,6 @@ public class ComputerUtilMana { return xMana; } - /** - *

- * getComboManaChoice. - *

- * - * @param abMana - * a {@link forge.card.spellability.AbilityMana} object. - * @param saRoot - * a {@link forge.card.spellability.SpellAbility} object. - * @param cost - * a {@link forge.card.mana.ManaCostBeingPaid} object. - * @return String - */ - public static String getComboManaChoice(final Player ai, final SpellAbility manaAb, final SpellAbility saRoot, final ManaCostBeingPaid cost) { - - final StringBuilder choiceString = new StringBuilder(); - - - final Card source = manaAb.getSourceCard(); - final AbilityManaPart abMana = manaAb.getManaPart(); - - if (abMana.isComboMana()) { - int amount = manaAb.hasParam("Amount") ? AbilityUtils.calculateAmount(source, manaAb.getParam("Amount"), saRoot) : 1; - final ManaCostBeingPaid testCost = new ManaCostBeingPaid(cost.toString().replace("X ", "")); - final String[] comboColors = abMana.getComboColors().split(" "); - for (int nMana = 1; nMana <= amount; nMana++) { - String choice = ""; - // Use expressChoice first - if (!abMana.getExpressChoice().isEmpty()) { - choice = abMana.getExpressChoice(); - abMana.clearExpressChoice(); - if (abMana.canProduce(choice) && testCost.isNeeded(choice)) { - choiceString.append(choice); - testCost.payMultipleMana(choice); - continue; - } - } - // check colors needed for cost - if (!testCost.isPaid()) { - // Loop over combo colors - for (String color : comboColors) { - if (testCost.isNeeded(color)) { - testCost.payMultipleMana(color); - if (nMana != 1) { - choiceString.append(" "); - } - choiceString.append(color); - choice = color; - break; - } - } - if (!choice.isEmpty()) { - continue; - } - } - // check if combo mana can produce most common color in hand - String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn( - ZoneType.Hand)); - if (!commonColor.isEmpty() && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) { - choice = MagicColor.toShortString(commonColor); - } - else { - // default to first color - choice = comboColors[0]; - } - if (nMana != 1) { - choiceString.append(" "); - } - choiceString.append(choice); - } - } - if (choiceString.toString().isEmpty()) { - choiceString.append("0"); - } - return choiceString.toString(); - } - // Returns basic mana abilities plus "reflected mana" abilities /** *

@@ -796,13 +662,12 @@ public class ComputerUtilMana { // if a mana ability has a mana cost the AI will miscalculate // if there is a parent ability the AI can't use it final Cost cost = a.getPayCosts(); - if (!cost.hasNoManaCost() - || (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) { + if (!cost.hasNoManaCost() || (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) { continue; } - AbilityManaPart am = a.getManaPart(); - if (am.isBasic() && !res.contains(a)) { + //AbilityManaPart am = a.getManaPart(); + if (/*am.isBasic() && */!res.contains(a)) { res.add(a); } diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index e76b9ac1379..1912cd24a5b 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -8,6 +8,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import forge.Card; import forge.GameEntity; +import forge.card.mana.Mana; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.control.input.Input; @@ -126,4 +127,5 @@ public abstract class PlayerController { public abstract void playMadness(SpellAbility madness); public abstract List chooseCardsToDelve(int colorLessAmount, List grave); public abstract List chooseCardsToDiscardUnlessType(int min, List hand, String param, SpellAbility sa); + public abstract Mana chooseManaFromPool(List manaChoices); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index ad73cc6fa42..d3dd93ac215 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -14,6 +14,7 @@ import forge.Card; import forge.CardLists; import forge.CardPredicates; import forge.GameEntity; +import forge.card.mana.Mana; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; @@ -318,4 +319,10 @@ public class PlayerControllerAi extends PlayerController { } + @Override + public Mana chooseManaFromPool(List manaChoices) { + return manaChoices.get(0); // no brains used + } + + } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 33f16875c26..0ddb7ef5b1a 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -14,6 +14,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import forge.Card; import forge.FThreads; import forge.GameEntity; +import forge.card.mana.Mana; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.card.spellability.TargetSelection; @@ -36,6 +37,7 @@ import forge.gui.GuiDialog; import forge.gui.GuiUtils; import forge.gui.match.CMatchUI; import forge.item.CardPrinted; +import forge.util.TextUtil; /** @@ -500,4 +502,19 @@ public class PlayerControllerHuman extends PlayerController { return target.getSelected(); } + /* (non-Javadoc) + * @see forge.game.player.PlayerController#chooseManaFromPool(java.util.List) + */ + @Override + public Mana chooseManaFromPool(List manaChoices) { + List options = new ArrayList(); + for(int i = 0; i < manaChoices.size(); i++) { + Mana m = manaChoices.get(i); + options.add(String.format("%d. %s mana from %s", 1+i, m.getColor(), m.getSourceCard() )); + } + String chosen = GuiChoose.one("Pay Mana from Mana Pool", options); + String idx = TextUtil.split(chosen, '.')[0]; + return manaChoices.get(Integer.parseInt(idx)-1); + } + } diff --git a/src/main/java/forge/gui/match/nonsingleton/VField.java b/src/main/java/forge/gui/match/nonsingleton/VField.java index 4bc44a6a9cf..cdfd24c513e 100644 --- a/src/main/java/forge/gui/match/nonsingleton/VField.java +++ b/src/main/java/forge/gui/match/nonsingleton/VField.java @@ -384,7 +384,7 @@ public class VField implements IVDoc { public void updateManaPool(final Player p0) { ManaPool m = p0.getManaPool(); for(Pair label : manaLabels) - label.getKey().setText(Integer.toString(m.getAmountOfColor(MagicColor.toLongString(label.getRight())))); + label.getKey().setText(Integer.toString(m.getAmountOfColor(label.getRight()))); } //========= Retrieval methods diff --git a/src/main/java/forge/item/ItemPool.java b/src/main/java/forge/item/ItemPool.java index 6502209c8f2..f03aa39a23e 100644 --- a/src/main/java/forge/item/ItemPool.java +++ b/src/main/java/forge/item/ItemPool.java @@ -31,6 +31,8 @@ import java.util.Map.Entry; */ public class ItemPool extends ItemPoolView { + + // Constructors here /** * diff --git a/src/main/java/forge/util/maps/EnumMapToAmount.java b/src/main/java/forge/util/maps/EnumMapToAmount.java new file mode 100644 index 00000000000..a7ec052a49a --- /dev/null +++ b/src/main/java/forge/util/maps/EnumMapToAmount.java @@ -0,0 +1,86 @@ +package forge.util.maps; + +import java.util.EnumMap; +import java.util.Map; + + + +/** + * TODO: Write javadoc for this type. + * + */ +public class EnumMapToAmount> extends EnumMap implements MapToAmount { + private static final long serialVersionUID = -4749796492075359368L; + + + public EnumMapToAmount(Class keyType) { + super(keyType); + } + + public EnumMapToAmount(EnumMap m) { + super(m); + } + + public EnumMapToAmount(Map m) { + super(m); + } + + @Override + public void add(T item) { + add(item, 1); + } + + @Override + public void add(T item, int amount) { + if (amount <= 0) return; // throw an exception maybe? + Integer cur = get(item); + int newVal = cur == null ? amount : amount + cur.intValue(); + put(item, Integer.valueOf(newVal)); + } + + @Override + public void addAll(Iterable item) { + for(T i : item) add(i, 1); + } + + @Override + public boolean substract(T item) { + return substract(item, 1); + } + + + @Override + public boolean substract(T item, int amount) { + Integer cur = get(item); + if( cur == null ) return false; + int newVal = cur.intValue() - amount; + if(newVal > 0) + put(item, Integer.valueOf(newVal)); + else + remove(item); + return true; + } + + + @Override + public void substractAll(Iterable item) { + for(T i : item) substract(i); + } + + @Override + public int countAll() { + int c = 0; + for(java.util.Map.Entry kv : this.entrySet()) { + c += kv.getValue().intValue(); + } + return c; + } + + + @Override + public int count(T item) { + Integer cur = get(item); + return cur == null ? 0 : cur.intValue(); + } + +} diff --git a/src/main/java/forge/util/maps/MapToAmount.java b/src/main/java/forge/util/maps/MapToAmount.java new file mode 100644 index 00000000000..80b1daa62ae --- /dev/null +++ b/src/main/java/forge/util/maps/MapToAmount.java @@ -0,0 +1,18 @@ +package forge.util.maps; + +import java.util.Map; + +/** + * TODO: Write javadoc for this type. + * + */ +public interface MapToAmount extends Map { + void add(K item); + void add(K item, int amount); + void addAll(Iterable item); + boolean substract(K item); + boolean substract(K item, int amount); + void substractAll(Iterable item); + int countAll(); + int count(K item); // just unboxes and returns zero instead of null +} diff --git a/src/main/java/forge/util/maps/TreeMapToAmount.java b/src/main/java/forge/util/maps/TreeMapToAmount.java new file mode 100644 index 00000000000..9cd967a4bbb --- /dev/null +++ b/src/main/java/forge/util/maps/TreeMapToAmount.java @@ -0,0 +1,85 @@ +package forge.util.maps; + +import java.util.Comparator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +public class TreeMapToAmount extends TreeMap implements MapToAmount { + private static final long serialVersionUID = 5001205073403776022L; + + public TreeMapToAmount() { + super(); + } + + public TreeMapToAmount(Comparator comparator) { + super(comparator); + } + + public TreeMapToAmount(Map m) { + super(m); + } + + public TreeMapToAmount(SortedMap m) { + super(m); + } + + + @Override + public void add(T item) { + add(item, 1); + } + + @Override + public void add(T item, int amount) { + if (amount <= 0) return; // throw an exception maybe? + Integer cur = get(item); + int newVal = cur == null ? amount : amount + cur.intValue(); + put(item, Integer.valueOf(newVal)); + } + + @Override + public void addAll(Iterable item) { + for(T i : item) add(i, 1); + } + + @Override + public boolean substract(T item) { + return substract(item, 1); + } + + + @Override + public boolean substract(T item, int amount) { + Integer cur = get(item); + if( cur == null ) return false; + int newVal = cur.intValue() - amount; + if(newVal > 0) + put(item, Integer.valueOf(newVal)); + else + remove(item); + return true; + } + + + @Override + public void substractAll(Iterable item) { + for(T i : item) substract(i); + } + + @Override + public int countAll() { + int c = 0; + for(java.util.Map.Entry kv : this.entrySet()) { + c += kv.getValue().intValue(); + } + return c; + } + + + @Override + public int count(T item) { + Integer cur = get(item); + return cur == null ? 0 : cur.intValue(); + } +} diff --git a/src/test/java/forge/card/mana/ManaPartTest.java b/src/test/java/forge/card/mana/ManaPartTest.java index d0de7549c64..b44de5428bf 100644 --- a/src/test/java/forge/card/mana/ManaPartTest.java +++ b/src/test/java/forge/card/mana/ManaPartTest.java @@ -3,7 +3,7 @@ package forge.card.mana; import org.testng.annotations.Test; import forge.Card; -import forge.Constant; +import forge.card.MagicColor; /** *

@@ -66,277 +66,277 @@ public class ManaPartTest { final ManaCostBeingPaid p1 = new ManaCostBeingPaid("2/U"); - this.check(0.3, p1.isNeeded("G")); - this.check(0.4, p1.isNeeded("U")); - this.check(0.5, p1.isNeeded("B")); - this.check(0.6, p1.isNeeded("W")); - this.check(0.7, p1.isNeeded("R")); - this.check(0.8, p1.isNeeded("1")); + this.check(0.3, p1.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(0.4, p1.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(0.5, p1.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(0.6, p1.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(0.7, p1.isAnyPartPayableWith(MagicColor.RED)); + this.check(0.8, p1.isAnyPartPayableWith((byte) 0)); - p1.addMana("U"); + p1.payMana("U"); this.check(0.9, p1.isPaid()); - this.check(0.91, !p1.isNeeded("R")); + this.check(0.91, !p1.isAnyPartPayableWith(MagicColor.RED)); final ManaCostBeingPaid p2 = new ManaCostBeingPaid("G"); - this.check(1, p2.isNeeded("G")); + this.check(1, p2.isAnyPartPayableWith(MagicColor.GREEN)); - this.check(1.1, !p2.isNeeded("U")); - this.check(1.2, !p2.isNeeded("B")); - this.check(1.3, !p2.isNeeded("W")); - this.check(1.4, !p2.isNeeded("R")); - this.check(1.5, !p2.isNeeded("1")); + this.check(1.1, !p2.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(1.2, !p2.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(1.3, !p2.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(1.4, !p2.isAnyPartPayableWith(MagicColor.RED)); + this.check(1.5, !p2.isAnyPartPayableWith((byte) 0)); - p2.addMana("G"); + p2.payMana("G"); this.check(2, p2.isPaid()); - this.check(2.1, !p2.isNeeded("G")); + this.check(2.1, !p2.isAnyPartPayableWith(MagicColor.GREEN)); final ManaCostBeingPaid p4 = new ManaCostBeingPaid("1"); - this.check(3, p4.isNeeded("G")); - this.check(4, p4.isNeeded("U")); - this.check(5, p4.isNeeded("B")); - this.check(6, p4.isNeeded("W")); - this.check(7, p4.isNeeded("R")); - this.check(8, p4.isNeeded("1")); + this.check(3, p4.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(4, p4.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(5, p4.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(6, p4.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(7, p4.isAnyPartPayableWith(MagicColor.RED)); + this.check(8, p4.isAnyPartPayableWith((byte) 0)); - p4.addMana("B"); + p4.payMana("B"); this.check(9, p4.isPaid()); - this.check(9.1, !p4.isNeeded("R")); + this.check(9.1, !p4.isAnyPartPayableWith(MagicColor.RED)); final ManaCostBeingPaid p5 = new ManaCostBeingPaid("GW"); - this.check(10, p5.isNeeded("G")); - this.check(13, p5.isNeeded("W")); + this.check(10, p5.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(13, p5.isAnyPartPayableWith(MagicColor.WHITE)); - this.check(11, !p5.isNeeded("U")); - this.check(12, !p5.isNeeded("B")); - this.check(14, !p5.isNeeded("R")); - this.check(15, !p5.isNeeded("1")); + this.check(11, !p5.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(12, !p5.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(14, !p5.isAnyPartPayableWith(MagicColor.RED)); + this.check(15, !p5.isAnyPartPayableWith((byte) 0)); - p5.addMana("W"); + p5.payMana("W"); this.check(16, p5.isPaid()); - this.check(17, !p5.isNeeded("W")); + this.check(17, !p5.isAnyPartPayableWith(MagicColor.WHITE)); final ManaCostBeingPaid p6 = new ManaCostBeingPaid("BR"); - this.check(17.1, p6.isNeeded("B")); - this.check(17.2, p6.isNeeded("R")); + this.check(17.1, p6.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(17.2, p6.isAnyPartPayableWith(MagicColor.RED)); - this.check(17.3, !p6.isNeeded("U")); - this.check(17.4, !p6.isNeeded("W")); - this.check(17.5, !p6.isNeeded("G")); - this.check(17.6, !p6.isNeeded("1")); + this.check(17.3, !p6.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(17.4, !p6.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(17.5, !p6.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(17.6, !p6.isAnyPartPayableWith((byte) 0)); - p6.addMana("R"); + p6.payMana("R"); this.check(17.7, p6.isPaid()); - this.check(17.8, !p6.isNeeded("R")); + this.check(17.8, !p6.isAnyPartPayableWith(MagicColor.RED)); final ManaCostBeingPaid p7 = new ManaCostBeingPaid("1 G G"); - p7.addMana("G"); + p7.payMana("G"); - this.check(18.1, p7.isNeeded("G")); - this.check(18.2, p7.isNeeded("W")); - this.check(18.3, p7.isNeeded("U")); - this.check(18.4, p7.isNeeded("B")); - this.check(18.5, p7.isNeeded("R")); - this.check(18.6, p7.isNeeded("1")); + this.check(18.1, p7.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(18.2, p7.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(18.3, p7.isAnyPartPayableWith(MagicColor.BLUE)); + this.check(18.4, p7.isAnyPartPayableWith(MagicColor.BLACK)); + this.check(18.5, p7.isAnyPartPayableWith(MagicColor.RED)); + this.check(18.6, p7.isAnyPartPayableWith((byte) 0)); - p7.addMana("1"); - p7.addMana("G"); + p7.payMana("1"); + p7.payMana("G"); this.check(18.7, p7.isPaid()); - this.check(18.8, !p7.isNeeded("W")); + this.check(18.8, !p7.isAnyPartPayableWith(MagicColor.WHITE)); final ManaCostBeingPaid p8 = new ManaCostBeingPaid("0"); - this.check(19.1, !p8.isNeeded("1")); - this.check(19.2, !p8.isNeeded("G")); - this.check(19.3, !p8.isNeeded("U")); + this.check(19.1, !p8.isAnyPartPayableWith((byte) 0)); + this.check(19.2, !p8.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(19.3, !p8.isAnyPartPayableWith(MagicColor.BLUE)); this.check(19.4, p8.isPaid()); - this.check(19.5, !p8.isNeeded("R")); + this.check(19.5, !p8.isAnyPartPayableWith(MagicColor.RED)); final ManaCostBeingPaid p9 = new ManaCostBeingPaid("G G"); - this.check(20.1, !p9.isNeeded("1")); - this.check(20.2, p9.isNeeded("G")); + this.check(20.1, !p9.isAnyPartPayableWith((byte) 0)); + this.check(20.2, p9.isAnyPartPayableWith(MagicColor.GREEN)); - this.check(20.3, !p9.isNeeded("U")); + this.check(20.3, !p9.isAnyPartPayableWith(MagicColor.BLUE)); - p9.addMana("G"); - p9.addMana("G"); + p9.payMana("G"); + p9.payMana("G"); this.check(20.4, p9.isPaid()); - this.check(20.5, !p9.isNeeded("B")); + this.check(20.5, !p9.isAnyPartPayableWith(MagicColor.BLACK)); final ManaCostBeingPaid p10 = new ManaCostBeingPaid("G G G"); - this.check(21.1, !p10.isNeeded("W")); - this.check(21.2, p10.isNeeded("G")); + this.check(21.1, !p10.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(21.2, p10.isAnyPartPayableWith(MagicColor.GREEN)); - this.check(21.3, !p10.isNeeded("R")); + this.check(21.3, !p10.isAnyPartPayableWith(MagicColor.RED)); - p10.addMana("G"); - p10.addMana("G"); - p10.addMana("G"); + p10.payMana("G"); + p10.payMana("G"); + p10.payMana("G"); this.check(21.4, p10.isPaid()); - this.check(21.5, !p10.isNeeded("U")); + this.check(21.5, !p10.isAnyPartPayableWith(MagicColor.BLUE)); final ManaCostBeingPaid p11 = new ManaCostBeingPaid("G G G G"); - this.check(22.1, !p11.isNeeded("W")); - this.check(22.2, p11.isNeeded("G")); + this.check(22.1, !p11.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(22.2, p11.isAnyPartPayableWith(MagicColor.GREEN)); - this.check(22.3, !p11.isNeeded("R")); + this.check(22.3, !p11.isAnyPartPayableWith(MagicColor.RED)); - p11.addMana("G"); - p11.addMana("G"); - p11.addMana("G"); - p11.addMana("G"); + p11.payMana("G"); + p11.payMana("G"); + p11.payMana("G"); + p11.payMana("G"); this.check(22.4, p11.isPaid()); - this.check(22.5, !p11.isNeeded("G")); + this.check(22.5, !p11.isAnyPartPayableWith(MagicColor.GREEN)); final ManaCostBeingPaid p12 = new ManaCostBeingPaid("GW"); - this.check(23.1, p12.isNeeded("W")); - this.check(23.2, p12.isNeeded("G")); - this.check(23.3, !p12.isNeeded("R")); + this.check(23.1, p12.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(23.2, p12.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(23.3, !p12.isAnyPartPayableWith(MagicColor.RED)); - p12.addMana("G"); + p12.payMana("G"); this.check(23.4, p12.isPaid()); - this.check(23.5, !p12.isNeeded("G")); + this.check(23.5, !p12.isAnyPartPayableWith(MagicColor.GREEN)); final ManaCostBeingPaid p13 = new ManaCostBeingPaid("GW"); - this.check(24.1, p13.isNeeded("W")); - this.check(24.2, p13.isNeeded("G")); - this.check(24.3, !p13.isNeeded("U")); + this.check(24.1, p13.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(24.2, p13.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(24.3, !p13.isAnyPartPayableWith(MagicColor.BLUE)); - p13.addMana("W"); + p13.payMana("W"); this.check(24.4, p13.isPaid()); - this.check(24.5, !p13.isNeeded("W")); + this.check(24.5, !p13.isAnyPartPayableWith(MagicColor.WHITE)); final ManaCostBeingPaid p14 = new ManaCostBeingPaid("3 GW GW"); - this.check(25.1, p14.isNeeded("W")); - this.check(25.2, p14.isNeeded("G")); - this.check(25.3, p14.isNeeded("U")); + this.check(25.1, p14.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(25.2, p14.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(25.3, p14.isAnyPartPayableWith(MagicColor.BLUE)); - p14.addMana("1"); - p14.addMana("1"); - p14.addMana("1"); + p14.payMana("1"); + p14.payMana("1"); + p14.payMana("1"); - this.check(25.4, p14.isNeeded("W")); - this.check(25.5, p14.isNeeded("G")); - this.check(25.6, !p14.isNeeded("U")); + this.check(25.4, p14.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(25.5, p14.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(25.6, !p14.isAnyPartPayableWith(MagicColor.BLUE)); - p14.addMana("G"); - p14.addMana("W"); + p14.payMana("G"); + p14.payMana("W"); this.check(25.7, p14.isPaid()); - this.check(25.8, !p14.isNeeded("W")); - this.check(25.9, !p14.isNeeded("G")); - this.check(25.10, !p14.isNeeded("1")); - this.check(25.11, !p14.isNeeded("R")); + this.check(25.8, !p14.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(25.9, !p14.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(25.10, !p14.isAnyPartPayableWith((byte) 0)); + this.check(25.11, !p14.isAnyPartPayableWith(MagicColor.RED)); final ManaCostBeingPaid p15 = new ManaCostBeingPaid("4"); - this.check(26.1, p15.isNeeded("W")); - this.check(26.2, p15.isNeeded("G")); - this.check(26.3, p15.isNeeded("U")); + this.check(26.1, p15.isAnyPartPayableWith(MagicColor.WHITE)); + this.check(26.2, p15.isAnyPartPayableWith(MagicColor.GREEN)); + this.check(26.3, p15.isAnyPartPayableWith(MagicColor.BLUE)); - p15.addMana("1"); - p15.addMana("1"); - p15.addMana("1"); - p15.addMana("1"); + p15.payMana("1"); + p15.payMana("1"); + p15.payMana("1"); + p15.payMana("1"); this.check(26.4, p15.isPaid()); final ManaCostBeingPaid p16 = new ManaCostBeingPaid("10"); - p16.addMana("G"); - p16.addMana("W"); - p16.addMana("R"); - p16.addMana("U"); - p16.addMana("B"); + p16.payMana("G"); + p16.payMana("W"); + p16.payMana("R"); + p16.payMana("U"); + p16.payMana("B"); - p16.addMana("1"); + p16.payMana("1"); - p16.addMana("W"); - p16.addMana("R"); - p16.addMana("U"); - p16.addMana("B"); + p16.payMana("W"); + p16.payMana("R"); + p16.payMana("U"); + p16.payMana("B"); this.check(27, p16.isPaid()); final ManaCostBeingPaid p17 = new ManaCostBeingPaid("12 G GW"); for (int i = 0; i < 12; i++) { - p17.addMana("R"); + p17.payMana("R"); } - p17.addMana("G"); - p17.addMana("W"); + p17.payMana("G"); + p17.payMana("W"); this.check(28, p17.isPaid()); final ManaCostBeingPaid p18 = new ManaCostBeingPaid("2 W B U R G"); for (int i = 0; i < 1; i++) { - p18.addMana("R"); + p18.payMana("R"); } for (int i = 0; i < 2; i++) { - p18.addMana("1"); + p18.payMana("1"); } for (int i = 0; i < 1; i++) { - p18.addMana("G"); - p18.addMana("W"); - p18.addMana("B"); - p18.addMana("U"); + p18.payMana("G"); + p18.payMana("W"); + p18.payMana("B"); + p18.payMana("U"); } this.check(29, p18.isPaid()); final ManaCostBeingPaid p19 = new ManaCostBeingPaid("W B U R G W"); - p19.addMana("R"); - p19.addMana("G"); - p19.addMana("B"); - p19.addMana("U"); + p19.payMana("R"); + p19.payMana("G"); + p19.payMana("B"); + p19.payMana("U"); - p19.addMana("W"); - p19.addMana("W"); + p19.payMana("W"); + p19.payMana("W"); this.check(30, p19.isPaid()); final ManaCostBeingPaid p20 = new ManaCostBeingPaid("W B U R G W B U R G"); for (int i = 0; i < 2; i++) { - p20.addMana("W"); - p20.addMana("R"); - p20.addMana("G"); - p20.addMana("B"); - p20.addMana("U"); + p20.payMana("W"); + p20.payMana("R"); + p20.payMana("G"); + p20.payMana("B"); + p20.payMana("U"); } this.check(31, p20.isPaid()); @@ -344,68 +344,68 @@ public class ManaPartTest { final ManaCostBeingPaid p21 = new ManaCostBeingPaid("2 W B U R G W B U R G G"); for (int i = 0; i < 2; i++) { - p21.addMana("W"); - p21.addMana("R"); - p21.addMana("G"); - p21.addMana("B"); - p21.addMana("U"); + p21.payMana("W"); + p21.payMana("R"); + p21.payMana("G"); + p21.payMana("B"); + p21.payMana("U"); } - p21.addMana("1"); - p21.addMana("1"); - p21.addMana("G"); + p21.payMana("1"); + p21.payMana("1"); + p21.payMana("G"); this.check(32, p21.isPaid()); final ManaCostBeingPaid p22 = new ManaCostBeingPaid("1 B R"); - p22.addMana("B"); - p22.addMana("1"); - p22.addMana("R"); + p22.payMana("B"); + p22.payMana("1"); + p22.payMana("R"); this.check(33, p22.isPaid()); final ManaCostBeingPaid p23 = new ManaCostBeingPaid("B R"); - p23.addMana("B"); - p23.addMana("R"); + p23.payMana("B"); + p23.payMana("R"); this.check(34, p23.isPaid()); final ManaCostBeingPaid p24 = new ManaCostBeingPaid("2/B 2/B 2/B"); - this.check(35, p24.isNeeded("G")); + this.check(35, p24.isAnyPartPayableWith(MagicColor.GREEN)); - p24.addMana("B"); + p24.payMana("B"); this.check(36, p24.toString().equals("2/B 2/B")); - p24.addMana("B"); + p24.payMana("B"); this.check(37, p24.toString().equals("2/B")); - p24.addMana("B"); + p24.payMana("B"); this.check(38, p24.isPaid()); final ManaCostBeingPaid p25 = new ManaCostBeingPaid("2/G"); - p25.addMana("1"); + p25.payMana("1"); this.check(39, p25.toString().equals("1")); - p25.addMana("W"); + p25.payMana("W"); this.check(40, p25.isPaid()); final ManaCostBeingPaid p27 = new ManaCostBeingPaid("2/R 2/R"); - p27.addMana("1"); + p27.payMana("1"); this.check(41, p27.toString().equals("2/R 1")); - p27.addMana("W"); + p27.payMana("W"); this.check(42, p27.toString().equals("2/R")); final ManaCostBeingPaid p26 = new ManaCostBeingPaid("2/W 2/W"); for (int i = 0; i < 4; i++) { this.check(43, !p26.isPaid()); - p26.addMana("1"); + p26.payMana("1"); } this.check(44, p26.isPaid()); @@ -413,11 +413,11 @@ public class ManaPartTest { final ManaCostBeingPaid p28 = new ManaCostBeingPaid("2/W 2/B 2/U 2/R 2/G"); this.check(45, !p28.isPaid()); - p28.addMana("B"); - p28.addMana("R"); - p28.addMana("G"); - p28.addMana("W"); - p28.addMana("U"); + p28.payMana("B"); + p28.payMana("R"); + p28.payMana("G"); + p28.payMana("W"); + p28.payMana("U"); this.check(45.1, p28.isPaid(), p28); @@ -426,11 +426,11 @@ public class ManaPartTest { final Card c = new Card(); - p29.addMana(new Mana(Constant.Color.BLACK, c, null)); - p29.addMana(new Mana(Constant.Color.RED, c, null)); - p29.addMana(new Mana(Constant.Color.GREEN, c, null)); - p29.addMana(new Mana(Constant.Color.WHITE, c, null)); - p29.addMana(new Mana(Constant.Color.BLUE, c, null)); + p29.payMana(new Mana(MagicColor.BLACK, c, null)); + p29.payMana(new Mana(MagicColor.RED, c, null)); + p29.payMana(new Mana(MagicColor.GREEN, c, null)); + p29.payMana(new Mana(MagicColor.WHITE, c, null)); + p29.payMana(new Mana(MagicColor.BLUE, c, null)); this.check(46.1, p29.isPaid(), p29);