AI & Human mana payment, ManaPool, ManaCostBeingPaid refactored.

The game uses a byte with flags set instead of color names in more cases.
This commit is contained in:
Maxmtg
2013-05-17 18:45:06 +00:00
parent a5111fed3d
commit 0df9763ec1
27 changed files with 1148 additions and 1487 deletions

3
.gitattributes vendored
View File

@@ -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/XmlUtil.java -text
src/main/java/forge/util/maps/CollectionSuppliers.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/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/HashMapOfLists.java -text
src/main/java/forge/util/maps/MapOfLists.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/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/maps/package-info.java -text
src/main/java/forge/util/package-info.java -text src/main/java/forge/util/package-info.java -text
src/main/java/forge/util/storage/IStorage.java -text src/main/java/forge/util/storage/IStorage.java -text

View File

@@ -18,6 +18,7 @@
package forge; package forge;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -192,7 +193,12 @@ public final class CardUtil {
return ret; return ret;
} }
public static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa, // a nice entry point with minimum parameters
public static Set<String> getReflectableManaColors(final SpellAbility sa) {
return getReflectableManaColors(sa, sa, new HashSet<String>(), new ArrayList<Card>());
}
private static Set<String> getReflectableManaColors(final SpellAbility abMana, final SpellAbility sa,
Set<String> colors, final List<Card> parents) { Set<String> colors, final List<Card> parents) {
// Here's the problem with reflectable Mana. If more than one is out, // Here's the problem with reflectable Mana. If more than one is out,
// they need to Reflect each other, // they need to Reflect each other,
@@ -266,14 +272,14 @@ public final class CardUtil {
colors.add(Constant.Color.COLORLESS); colors.add(Constant.Color.COLORLESS);
} }
} else if (reflectProperty.equals("Produce")) { } else if (reflectProperty.equals("Produce")) {
final ArrayList<SpellAbility> abilities = new ArrayList<SpellAbility>(); final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
for (final Card c : cards) { for (final Card c : cards) {
abilities.addAll(c.getManaAbility()); abilities.addAll(c.getManaAbility());
} }
// currently reflected mana will ignore other reflected mana // currently reflected mana will ignore other reflected mana
// abilities // abilities
final ArrayList<SpellAbility> reflectAbilities = new ArrayList<SpellAbility>(); final List<SpellAbility> reflectAbilities = new ArrayList<SpellAbility>();
for (final SpellAbility ab : abilities) { for (final SpellAbility ab : abilities) {
if (maxChoices == colors.size()) { if (maxChoices == colors.size()) {

View File

@@ -8,7 +8,6 @@ import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.GameState; import forge.game.GameState;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilCost;
import forge.game.ai.ComputerUtilMana; import forge.game.ai.ComputerUtilMana;
import forge.game.player.Player; import forge.game.player.Player;
@@ -63,7 +62,7 @@ public class CounterAi extends SpellAbilityAi {
if (unlessCost != null && !unlessCost.endsWith(">")) { if (unlessCost != null && !unlessCost.endsWith(">")) {
// Is this Usable Mana Sources? Or Total Available Mana? // 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; int toPay = 0;
boolean setPayX = false; boolean setPayX = false;
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) { if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {
@@ -132,7 +131,7 @@ public class CounterAi extends SpellAbilityAi {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
if (unlessCost != null) { if (unlessCost != null) {
// Is this Usable Mana Sources? Or Total Available Mana? // 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; int toPay = 0;
boolean setPayX = false; boolean setPayX = false;
if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) { if (unlessCost.equals("X") && source.getSVar(unlessCost).equals("Count$xPaid")) {

View File

@@ -1,11 +1,8 @@
package forge.card.ability.effects; package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import forge.Card;
import forge.CardUtil; import forge.CardUtil;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
@@ -27,7 +24,7 @@ public class ManaReflectedEffect extends SpellAbilityEffect {
AbilityManaPart ma = sa.getManaPart(); AbilityManaPart ma = sa.getManaPart();
sa.setUndoable(sa.isAbility() && sa.isUndoable()); sa.setUndoable(sa.isAbility() && sa.isUndoable());
final Collection<String> colors = CardUtil.getReflectableManaColors(sa, sa, new HashSet<String>(), new ArrayList<Card>()); final Collection<String> colors = CardUtil.getReflectableManaColors(sa);
final List<Player> tgtPlayers = getTargetPlayers(sa); final List<Player> tgtPlayers = getTargetPlayers(sa);
for (final Player player : tgtPlayers) { for (final Player player : tgtPlayers) {

View File

@@ -1133,7 +1133,7 @@ public class CardFactoryUtil {
if (color.equals("All")) { if (color.equals("All")) {
return cc.getManaPool().totalMana(); return cc.getManaPool().totalMana();
} else { } else {
return cc.getManaPool().getAmountOfColor(color); return cc.getManaPool().getAmountOfColor(MagicColor.fromName(color));
} }
} }

View File

@@ -65,7 +65,7 @@ public class CostDamage extends CostPart {
*/ */
@Override @Override
public final void payAI(final PaymentDecision decision, final Player ai, SpellAbility ability, Card source) { 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);
} }
/* /*

View File

@@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import forge.Card; import forge.Card;
import forge.card.mana.ManaCost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
@@ -178,10 +177,6 @@ public class CostPayment {
final Card source = this.ability.getSourceCard(); final Card source = this.ability.getSourceCard();
final List<CostPart> parts = this.cost.getCostParts(); final List<CostPart> parts = this.cost.getCostParts();
if (this.getCost().getCostMana() == null) {
parts.add(new CostPartMana(ManaCost.ZERO, null, false));
}
Map<Class<? extends CostPart>, PaymentDecision> decisions = new HashMap<Class<? extends CostPart>, PaymentDecision>(); Map<Class<? extends CostPart>, PaymentDecision> decisions = new HashMap<Class<? extends CostPart>, PaymentDecision>();
// Set all of the decisions before attempting to pay anything // Set all of the decisions before attempting to pay anything

View File

@@ -31,43 +31,19 @@ import forge.card.spellability.AbilityManaPart;
* @version $Id$ * @version $Id$
*/ */
public class Mana { public class Mana {
private byte color; @Override
private Card sourceCard = null; public int hashCode() {
private AbilityManaPart manaAbility = null; final int prime = 31;
private boolean hasRestrictions = false; int result = 1;
private boolean pumpCounterMagic = false; result = prime * result + color;
result = prime * result + (hasRestrictions ? 1231 : 1237);
/** result = prime * result + ((manaAbility == null) ? 0 : manaAbility.hashCode());
* <p> result = prime * result + (pumpCounterMagic ? 1231 : 1237);
* Constructor for Mana. result = prime * result + ((sourceCard == null) ? 0 : sourceCard.hashCode());
* </p> return result;
*
* @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 @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (!(other instanceof Mana)) { if (!(other instanceof Mana)) {
@@ -88,6 +64,45 @@ public class Mana {
return mp == mp2 || mp.getManaRestrictions().equals(mp2.getManaRestrictions()); 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;
/**
* <p>
* Constructor for Mana.
* </p>
*
* @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;
}
/** /**
* <p> * <p>
* toString. * toString.

View File

@@ -266,7 +266,7 @@ public final class ManaCost implements Comparable<ManaCost> {
public boolean canBePaidWithAvaliable(ColorSet color) { public boolean canBePaidWithAvaliable(ColorSet color) {
for (ManaCostShard shard : shards) { for (ManaCostShard shard : shards) {
if (!shard.canBePaidWithAvaliable(color)) { if (!shard.isPhyrexian() && !shard.canBePaidWithManaOfColor(color.getColor())) {
return false; return false;
} }
} }

View File

@@ -18,18 +18,20 @@
package forge.card.mana; package forge.card.mana;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map.Entry; 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 com.google.common.collect.Lists;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
@@ -38,6 +40,9 @@ import forge.game.GameState;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.util.TextUtil;
import forge.util.maps.EnumMapToAmount;
import forge.util.maps.MapToAmount;
/** /**
* <p> * <p>
@@ -48,7 +53,7 @@ import forge.gui.GuiChoose;
* @version $Id$ * @version $Id$
*/ */
public class ManaCostBeingPaid { public class ManaCostBeingPaid {
private class ManaCostBeingPaidIterator implements IParserManaCost { private class ManaCostBeingPaidIterator implements IParserManaCost, Iterator<ManaCostShard> {
private Iterator<ManaCostShard> mch; private Iterator<ManaCostShard> mch;
private ManaCostShard nextShard = null; private ManaCostShard nextShard = null;
private int remainingShards = 0; private int remainingShards = 0;
@@ -101,7 +106,7 @@ public class ManaCostBeingPaid {
// holds Mana_Part objects // holds Mana_Part objects
// ManaPartColor is stored before ManaPartColorless // ManaPartColor is stored before ManaPartColorless
private final HashMap<ManaCostShard, Integer> unpaidShards = new HashMap<ManaCostShard, Integer>(); private final MapToAmount<ManaCostShard> unpaidShards = new EnumMapToAmount<ManaCostShard>(ManaCostShard.class);
private byte sunburstMap = 0; private byte sunburstMap = 0;
private int cntX = 0; private int cntX = 0;
private final String sourceRestriction; private final String sourceRestriction;
@@ -132,7 +137,7 @@ public class ManaCostBeingPaid {
if (shard == ManaCostShard.X) { if (shard == ManaCostShard.X) {
cntX++; cntX++;
} else { } else {
increaseShard(shard, 1); unpaidShards.add(shard);
} }
} }
increaseColorlessMana(manaCost.getGenericCost()); increaseColorlessMana(manaCost.getGenericCost());
@@ -149,45 +154,12 @@ public class ManaCostBeingPaid {
return ColorSet.fromMask(sunburstMap).countColors(); return ColorSet.fromMask(sunburstMap).countColors();
} }
/**
* <p>
* getColorsPaid.
* </p>
*
* @return a String.
*/
public final byte getColorsPaid() { public final byte getColorsPaid() {
return sunburstMap; return sunburstMap;
} }
/**
* <p>
* getUnpaidPhyrexianMana.
* </p>
*
* @return a {@link java.util.ArrayList} object.
*/
private List<ManaCostShard> getUnpaidPhyrexianMana() {
ArrayList<ManaCostShard> res = new ArrayList<ManaCostShard>();
for (final Entry<ManaCostShard, Integer> part : this.unpaidShards.entrySet()) {
if (!part.getKey().isPhyrexian()) {
continue;
}
for (int i = 0; i < part.getValue(); i++) {
res.add(part.getKey());
}
}
return res;
}
/**
* <p>
* containsPhyrexianMana.
* </p>
*
* @return a boolean.
*/
public final boolean containsPhyrexianMana() { public final boolean containsPhyrexianMana() {
for (ManaCostShard shard : unpaidShards.keySet()) { for (ManaCostShard shard : unpaidShards.keySet()) {
if (shard.isPhyrexian()) { if (shard.isPhyrexian()) {
@@ -197,92 +169,50 @@ public class ManaCostBeingPaid {
return false; return false;
} }
/**
* <p>
* payPhyrexian.
* </p>
*
* @return a boolean.
*/
public final boolean payPhyrexian() { public final boolean payPhyrexian() {
final List<ManaCostShard> phy = this.getUnpaidPhyrexianMana(); ManaCostShard phy = null;
for(ManaCostShard mcs : unpaidShards.keySet()) {
if (phy.size() > 0) { if( mcs.isPhyrexian() ) {
Integer cnt = unpaidShards.get(phy.get(0)); phy = mcs;
if (cnt <= 1) { break;
unpaidShards.remove(phy.get(0));
} else {
unpaidShards.put(phy.get(0), Integer.valueOf(cnt - 1));
} }
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. // takes a Short Color and returns true if it exists in the mana cost.
// Easier for split costs // Easier for split costs
/** public final boolean needsColor(final byte colorMask) {
* <p>
* isColor.
* </p>
*
* @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;
}
for (ManaCostShard shard : unpaidShards.keySet()) { 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(); // isNeeded(String) still used by the Computer, might have problems activating Snow abilities
if (ss.contains(color)) { public final boolean isAnyPartPayableWith(byte colorMask) {
for (ManaCostShard shard : unpaidShards.keySet()) {
if (shard.canBePaidWithManaOfColor(colorMask)) {
return true; return true;
} }
} }
return false; return false;
} }
// isNeeded(String) still used by the Computer, might have problems
// activating Snow abilities
/**
* <p>
* isNeeded.
* </p>
*
* @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;
}
/**
* <p>
* isNeeded.
* </p>
*
* @param paid
* a {@link forge.card.mana.Mana} object.
* @return a boolean.
*/
public final boolean isNeeded(final Mana paid) { public final boolean isNeeded(final Mana paid) {
for (ManaCostShard shard : unpaidShards.keySet()) { for (ManaCostShard shard : unpaidShards.keySet()) {
if (canBePaidWith(shard, paid)) { if (canBePaidWith(shard, paid)) {
return true; return true;
} }
@@ -290,30 +220,11 @@ public class ManaCostBeingPaid {
return false; return false;
} }
/**
* <p>
* isPaid.
* </p>
*
* @return a boolean.
*/
public final boolean isPaid() { public final boolean isPaid() {
return unpaidShards.isEmpty(); return unpaidShards.isEmpty();
} // isPaid() } // isPaid()
/**
* <p>
* payMana.
* </p>
*
* @param mana
* a {@link forge.card.mana.Mana} object.
* @return a boolean.
*/
public final boolean payMana(final Mana mana) {
return this.addMana(mana);
}
/** /**
* <p> * <p>
* payMultipleMana. * payMultipleMana.
@@ -323,65 +234,35 @@ public class ManaCostBeingPaid {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a boolean. * @return a boolean.
*/ */
public final void payMultipleMana(String mana) { public final String payMultipleMana(String mana) {
String[] manas = mana.split(" "); List<String> unused = new ArrayList<>(4);
for (String manaPart : manas) { for (String manaPart : TextUtil.split(mana, ' ')) {
if (manaPart.matches("[0-9]+")) { if (StringUtils.isNumeric(manaPart)) {
final int amount = Integer.parseInt(manaPart); for(int i = Integer.parseInt(manaPart); i > 0; i--) {
for (int i = 0; i < amount; i++) { boolean wasNeeded = this.payMana("1");
this.payMana(Constant.Color.COLORLESS); if(!wasNeeded) {
unused.add(Integer.toString(i));
break;
}
} }
} else { } 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, ' ');
} }
/**
* <p>
* payMana.
* </p>
*
* @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);
}
/**
* <p>
* increaseColorlessMana.
* </p>
*
* @param manaToAdd
* a int.
*/
public final void increaseColorlessMana(final int manaToAdd) { public final void increaseColorlessMana(final int manaToAdd) {
increaseShard(ManaCostShard.COLORLESS, manaToAdd); increaseShard(ManaCostShard.COLORLESS, manaToAdd);
} }
public final void increaseShard(final ManaCostShard shard, final int toAdd) { public final void increaseShard(final ManaCostShard shard, final int toAdd) {
if (toAdd <= 0) { unpaidShards.add(shard, toAdd);
return;
}
Integer cnt = unpaidShards.get(shard);
unpaidShards.put(shard, Integer.valueOf(cnt == null || cnt == 0 ? toAdd : toAdd + cnt));
} }
/**
* <p>
* decreaseColorlessMana
* </p>
* .
*
* @param manaToSubtract
* an int. The amount of colorless mana to subtract from the
* cost.Used by Delve.
*/
public final void decreaseColorlessMana(final int manaToSubtract) { public final void decreaseColorlessMana(final int manaToSubtract) {
decreaseShard(ManaCostShard.COLORLESS, manaToSubtract); decreaseShard(ManaCostShard.COLORLESS, manaToSubtract);
} }
@@ -391,26 +272,15 @@ public class ManaCostBeingPaid {
return; return;
} }
Integer genericCnt = unpaidShards.get(shard); if (!unpaidShards.containsKey(shard)) {
if (null == genericCnt || genericCnt - manaToSubtract <= 0) { System.err.println("Tried to substract a " + shard.toString() + " shard that is not present in this ManaCostBeingPaid");
unpaidShards.remove(shard); return;
} else {
unpaidShards.put(shard, Integer.valueOf(genericCnt - manaToSubtract));
} }
unpaidShards.substract(shard, manaToSubtract);
} }
/**
* <p>
* getColorlessManaAmount
* </p>
* Returns how much colorless mana must be paid to pay the cost.Used by
* Delve AI.
*
* @return an int.
*/
public final int getColorlessManaAmount() { public final int getColorlessManaAmount() {
Integer genericCnt = unpaidShards.get(ManaCostShard.COLORLESS); return unpaidShards.count(ManaCostShard.COLORLESS);
return genericCnt == null ? 0 : genericCnt;
} }
/** /**
@@ -422,40 +292,73 @@ public class ManaCostBeingPaid {
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a boolean. * @return a boolean.
*/ */
public final boolean addMana(final String mana) { public final boolean payMana(final String mana) {
if (!this.isNeeded(mana)) { final byte colorMask = MagicColor.fromName(mana);
System.out.println("ManaCost : addMana() error, mana not needed - " + 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); //throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana);
} }
byte colorMask = MagicColor.fromName(mana);
Predicate<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() {
@Override public boolean apply(ManaCostShard ms) {
return ms.canBePaidWithManaOfColor(colorMask);
}
};
return tryPayMana(colorMask, Iterables.filter(unpaidShards.keySet(), predCanBePaid));
}
/**
* <p>
* addMana.
* </p>
*
* @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<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() {
@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<ManaCostShard> payableShards) {
ManaCostShard choice = null; ManaCostShard choice = null;
for (ManaCostShard toPay : unpaidShards.keySet()) { for (ManaCostShard toPay : payableShards) {
if (canBePaidWith(toPay, mana)) { // if m is a better to pay than choice
// if m is a better to pay than choice if (choice == null) {
if (choice == null) { choice = toPay;
choice = toPay; continue;
continue; }
} if (isFirstChoiceBetter(toPay, choice, colorMask)) {
if (isFirstChoiceBetter(toPay, choice, colorMask)) { choice = toPay;
choice = toPay;
}
} }
} // for } // for
if (choice == null) { if (choice == null) {
return false; return false;
} }
decreaseShard(choice, 1); decreaseShard(choice, 1);
if (choice.isOr2Colorless() && choice.getColorMask() != colorMask ) { if (choice.isOr2Colorless() && choice.getColorMask() != colorMask ) {
this.increaseColorlessMana(1); this.increaseColorlessMana(1);
} }
this.sunburstMap |= colorMask; this.sunburstMap |= colorMask;
return true; return true;
} }
private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte b) { private boolean isFirstChoiceBetter(ManaCostShard s1, ManaCostShard s2, byte colorMask) {
return getPayPriority(s1, b) > getPayPriority(s2, b); return getPayPriority(s1, colorMask) > getPayPriority(s2, colorMask);
} }
private int getPayPriority(ManaCostShard bill, byte paymentColor) { private int getPayPriority(ManaCostShard bill, byte paymentColor) {
@@ -472,66 +375,16 @@ public class ManaCostBeingPaid {
} }
return 8; return 8;
} }
return 5; return 5;
} }
private boolean canBePaidWith(ManaCostShard shard, Mana mana) { private boolean canBePaidWith(ManaCostShard shard, Mana mana) {
if (shard.isSnow() && mana.isSnow()) { 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;
}
/**
* <p>
* addMana.
* </p>
*
* @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) {
return false; return false;
} }
byte color = mana.getColorCode();
decreaseShard(choice, 1); return shard.canBePaidWithManaOfColor(color);
if (choice.isOr2Colorless() && choice.getColorMask() != mana.getColorCode() ) {
this.increaseColorlessMana(1);
}
this.sunburstMap |= mana.getColorCode();
return true;
} }
public final void combineManaCost(final ManaCost extra) { public final void combineManaCost(final ManaCost extra) {
@@ -545,10 +398,6 @@ public class ManaCostBeingPaid {
increaseColorlessMana(extra.getGenericCost()); increaseColorlessMana(extra.getGenericCost());
} }
public final void combineManaCost(final String extra) {
combineManaCost(new ManaCost(new ManaCostParser(extra)));
}
/** /**
* To string. * To string.
* *
@@ -615,17 +464,24 @@ public class ManaCostBeingPaid {
return new ManaCost(new ManaCostBeingPaidIterator()); return new ManaCost(new ManaCostBeingPaidIterator());
} }
/**
* <p>
* Getter for the field <code>xcounter</code>.
* </p>
*
* @return a int.
*/
public final int getXcounter() { public final int getXcounter() {
return cntX; return cntX;
} }
public final List<ManaCostShard> getUnpaidShards() {
List<ManaCostShard> result = new ArrayList<ManaCostShard>();
for(Entry<ManaCostShard, Integer> 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;
}
/** /**
* <p> * <p>
* removeColorlessMana. * removeColorlessMana.
@@ -782,5 +638,13 @@ public class ManaCostBeingPaid {
public String getSourceRestriction() { public String getSourceRestriction() {
return sourceRestriction; return sourceRestriction;
} }
public Iterable<ManaCostShard> getDistinctShards() {
return unpaidShards.keySet();
}
public int getUnpaidShards(ManaCostShard key) {
return unpaidShards.count(key);
}
} }

View File

@@ -17,21 +17,14 @@
*/ */
package forge.card.mana; package forge.card.mana;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.util.BinaryUtil; import forge.util.BinaryUtil;
/** /**
* The Class CardManaCostShard. * The Class CardManaCostShard.
*/ */
public enum ManaCostShard { public enum ManaCostShard implements Comparable<ManaCostShard> {
// 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 */ /* Pure colors */
WHITE(ManaAtom.WHITE, "W"), WHITE(ManaAtom.WHITE, "W"),
BLUE(ManaAtom.BLUE, "U"), BLUE(ManaAtom.BLUE, "U"),
@@ -51,6 +44,17 @@ public enum ManaCostShard {
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"), BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"), 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 */ /* Phyrexian */
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"), PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"), 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"), PR(ManaAtom.RED | ManaAtom.OR_2_LIFE, "R/P", "PR"),
PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "G/P", "PG"), PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "G/P", "PG"),
/* Or 2 colorless */ X(ManaAtom.IS_X, "X");
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");
private final int shard; private final int shard;
@@ -275,23 +273,6 @@ public enum ManaCostShard {
public boolean isOr2Colorless() { public boolean isOr2Colorless() {
return (this.shard & ManaAtom.OR_2_COLORLESS) != 0; 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) { public boolean canBePaidWithManaOfColor(byte colorCode) {
return this.isOr2Colorless() || (COLORS_SUPERPOSITION & this.shard) == 0 || (colorCode & this.shard) > 0; return this.isOr2Colorless() || (COLORS_SUPERPOSITION & this.shard) == 0 || (colorCode & this.shard) > 0;

View File

@@ -18,21 +18,20 @@
package forge.card.mana; package forge.card.mana;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; 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.AbilityManaPart;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;
import forge.game.player.Player; import forge.game.player.Player;
import forge.gui.GuiChoose; import forge.util.maps.MapOfLists;
import forge.util.maps.TreeMapOfLists;
/** /**
* <p> * <p>
@@ -43,14 +42,14 @@ import forge.gui.GuiChoose;
* @version $Id$ * @version $Id$
*/ */
public class ManaPool { public class ManaPool {
// current paying moved to SpellAbility
private final ArrayList<Mana> floatingMana = new ArrayList<Mana>(); private final static Supplier<List<Mana>> listFactory = new Supplier<List<Mana>>(){
private final int[] floatingTotals = new int[6]; // WUBRGC @Override public List<Mana> get() { return new ArrayList<Mana>(); }
private final int[] floatingSnowTotals = new int[6]; // WUBRGC };
private final MapOfLists<Byte, Mana> floatingMana = new TreeMapOfLists<Byte, Mana>(listFactory);
/** Constant <code>map</code>. */ /** Constant <code>map</code>. */
private static final Map<String, Integer> MAP = new HashMap<String, Integer>();
private final Player owner; private final Player owner;
/** /**
@@ -63,91 +62,15 @@ public class ManaPool {
*/ */
public ManaPool(final Player player) { public ManaPool(final Player player) {
owner = 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);
} }
/** public final int getAmountOfColor(final byte color) {
* <p> Collection<Mana> ofColor = floatingMana.get(color);
* calculatManaTotals for the Player panel. return ofColor == null ? 0 : ofColor.size();
* </p>
*
*/
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())]++;
}
}
} }
/** private void addMana(final Mana mana) {
* <p> floatingMana.add(mana.getColorCode(), mana);
* getAmountOfColor.
* </p>
*
* @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];
}
/**
* <p>
* isEmpty.
* </p>
*
* @return a boolean.
*/
private boolean isEmpty() {
return this.floatingMana.size() == 0;
}
/**
* <p>
* addManaToPool.
* </p>
*
* @param pool
* a {@link java.util.ArrayList} object.
* @param mana
* a {@link forge.card.mana.Mana} object.
*/
private void addManaToPool(final ArrayList<Mana> 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]++;
}
}
owner.updateObservers(); owner.updateObservers();
} }
@@ -159,9 +82,9 @@ public class ManaPool {
* @param manaList * @param manaList
* a {@link java.util.ArrayList} object. * a {@link java.util.ArrayList} object.
*/ */
public final void addManaToFloating(final ArrayList<Mana> manaList) { public final void add(final Iterable<Mana> manaList) {
for (final Mana m : manaList) { for (final Mana m : manaList) {
this.addManaToPool(this.floatingMana, m); this.addMana(m);
} }
owner.getGame().getAction().checkStateEffects(); owner.getGame().getAction().checkStateEffects();
owner.updateObservers(); owner.updateObservers();
@@ -175,36 +98,24 @@ public class ManaPool {
* </p> * </p>
*/ */
public final int clearPool(boolean isEndOfPhase) { 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)) { if (isEndOfPhase && owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty)) {
return numRemoved; return 0;
} }
if (this.floatingMana.isEmpty()) { int numRemoved = 0;
this.calculateManaTotals(); boolean keepGreenMana = isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.");
//this.owner.updateObservers();
return numRemoved;
}
if (isEndOfPhase && this.owner.hasKeyword("Green mana doesn't empty from your mana pool as steps and phases end.")) { Set<Byte> keys = floatingMana.keySet();
// Omnath in play, clear all non-green mana if ( keepGreenMana )
int i = 0; keys.remove(Byte.valueOf(MagicColor.GREEN));
while (i < this.floatingMana.size()) {
if (this.floatingMana.get(i).isColor(Constant.Color.GREEN)) { for(Byte b : keys) {
i++; numRemoved += floatingMana.get(b).size();
continue; floatingMana.get(b).clear();
}
numRemoved++;
this.floatingMana.remove(i);
}
} else {
numRemoved = this.floatingMana.size();
this.floatingMana.clear();
} }
this.calculateManaTotals();
//this.owner.updateObservers();
return numRemoved; return numRemoved;
} }
@@ -221,166 +132,86 @@ public class ManaPool {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link forge.card.mana.Mana} object. * @return a {@link forge.card.mana.Mana} object.
*/ */
private Mana getMana(final String manaStr, final SpellAbility saBeingPaidFor, String restriction) { private Mana getMana(final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) {
final ArrayList<Mana> pool = this.floatingMana; final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(shard, saBeingPaidFor, restriction);
//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<Pair<Mana, Integer>> weightedOptions = new ArrayList<Pair<Mana, Integer>>();
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));
}
// Exclude border case // Exclude border case
if (weightedOptions.isEmpty()) { if (weightedOptions.isEmpty()) {
return null; // There is no matching mana in the pool return null; // There is no matching mana in the pool
} }
// have at least one option at this moment // select equal weight possibilities
int maxWeight = Integer.MIN_VALUE; List<Mana> manaChoices = new ArrayList<Mana>();
int equalWeights = 0; int bestWeight = Integer.MIN_VALUE;
Mana toPay = null;
for (Pair<Mana, Integer> option : weightedOptions) { for (Pair<Mana, Integer> option : weightedOptions) {
int thisWeight = option.getRight(); int thisWeight = option.getRight();
if (thisWeight > maxWeight) { Mana thisMana = option.getLeft();
maxWeight = thisWeight;
equalWeights = 1; if (thisWeight > bestWeight) {
toPay = option.getLeft(); manaChoices.clear();
} else if (thisWeight == maxWeight) { bestWeight = thisWeight;
equalWeights++; }
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? // got an only one best option?
if (equalWeights == 1) { if (manaChoices.size() == 1) {
return toPay; return manaChoices.get(0);
} }
// select equal weight possibilities // Let them choose then
List<Mana> options = new ArrayList<Mana>(); return owner.getController().chooseManaFromPool(manaChoices);
for (Pair<Mana, Integer> option : weightedOptions) { }
int thisWeight = option.getRight();
if (maxWeight == thisWeight) {
options.add(option.getLeft());
}
}
// if the options are equal, there is no difference on which to spend private List<Pair<Mana, Integer>> selectManaToPayFor(final ManaCostShard shard, final SpellAbility saBeingPaidFor,
toPay = options.get(0); String restriction) {
boolean allAreEqual = true; final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<Pair<Mana, Integer>>();
for (int i = 1; i < options.size(); i++) { for (final Byte manaKey : this.floatingMana.keySet()) {
if (!toPay.equals(options.get(i))) { if(!shard.canBePaidWithManaOfColor(manaKey.byteValue()))
continue;
allAreEqual = false;
break; for(final Mana thisMana : this.floatingMana.get(manaKey)) {
} if (!thisMana.getManaAbility().meetsManaRestrictions(saBeingPaidFor)) {
} continue;
if (allAreEqual) {
return toPay;
}
// Not found a good one - then let them choose
final List<Mana> 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<String> alChoice = new ArrayList<String>();
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;
}
} }
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 weightedOptions;
return payment;
} }
/** /**
@@ -393,87 +224,27 @@ public class ManaPool {
* @param choice * @param choice
* a {@link forge.card.mana.Mana} object. * a {@link forge.card.mana.Mana} object.
*/ */
private void removeManaFrom(final ArrayList<Mana> pool, final Mana choice) { private void removeMana(final Mana mana) {
if (choice != null && pool.contains(choice)) { Collection<Mana> cm = floatingMana.get(mana.getColorCode());
pool.remove(choice); if (cm.remove(mana)) {
if (pool.equals(this.floatingMana)) {
int i = ManaPool.MAP.get(choice.getColor());
if (choice.isSnow()) {
this.floatingSnowTotals[i]--;
}
else {
this.floatingTotals[i]--;
}
}
owner.updateObservers(); owner.updateObservers();
} }
} }
/** public final void payManaFromPool(final SpellAbility saBeingPaidFor, final ManaCostBeingPaid manaCost, final ManaCostShard manaShard) {
* <p> if (manaCost.isPaid()) {
* payManaFromPool.
* </p>
*
* @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<Mana> manaPaid = saBeingPaidFor.getPayingMana();
List<String> 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);
}
}
}
}
}
/**
* <p>
* payManaFromPool.
* </p>
*
* @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()) {
return; return;
} }
// get a mana of this type from floating, bail if none available // 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) { if (mana == null) {
return; // no matching mana in the pool return; // no matching mana in the pool
} }
else if (manaCost.isNeeded(mana)) { else if (manaCost.isNeeded(mana)) {
manaCost.payMana(mana); manaCost.payMana(mana);
saBeingPaidFor.getPayingMana().add(mana); saBeingPaidFor.getPayingMana().add(mana);
this.removeManaFrom(this.floatingMana, mana); this.removeMana( mana);
if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) { if (mana.addsNoCounterMagic() && saBeingPaidFor.getSourceCard() != null) {
saBeingPaidFor.getSourceCard().setCanCounter(false); 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 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)) { if (manaCost.isNeeded(mana)) {
manaCost.payMana(mana); manaCost.payMana(mana);
manaPaid.add(mana); manaPaid.add(mana);
this.removeManaFrom(this.floatingMana, mana); this.removeMana(mana);
if (mana.addsNoCounterMagic() && sa.getSourceCard() != null) { if (mana.addsNoCounterMagic() && sa.getSourceCard() != null) {
sa.getSourceCard().setCanCounter(false); sa.getSourceCard().setCanCounter(false);
} }
@@ -551,33 +322,35 @@ public class ManaPool {
ability.getSourceCard().setCanCounter(true); ability.getSourceCard().setCanCounter(true);
} }
for (final Mana m : manaPaid) { for (final Mana m : manaPaid) {
this.addManaToPool(this.floatingMana, m); this.addMana(m);
} }
} }
manaPaid.clear(); manaPaid.clear();
this.calculateManaTotals();
this.owner.updateObservers(); this.owner.updateObservers();
} }
private boolean accountFor(final SpellAbility sa, final AbilityManaPart ma) { private boolean accountFor(final SpellAbility sa, final AbilityManaPart ma) {
final ArrayList<Mana> manaPaid = sa.getPayingMana(); final List<Mana> manaPaid = sa.getPayingMana();
if ((manaPaid.size() == 0) && (this.floatingMana.size() == 0)) { if (manaPaid.isEmpty() && this.floatingMana.isEmpty()) {
return false; return false;
} }
final ArrayList<Mana> removePaying = new ArrayList<Mana>(); final ArrayList<Mana> removePaying = new ArrayList<Mana>();
final ArrayList<Mana> removeFloating = new ArrayList<Mana>(); final ArrayList<Mana> removeFloating = new ArrayList<Mana>();
boolean manaNotAccountedFor = false; boolean manaNotAccountedFor = false;
// loop over mana produced by mana ability // loop over mana produced by mana ability
for (Mana mana : ma.getLastProduced()) { for (Mana mana : ma.getLastManaProduced()) {
Collection<Mana> poolLane = this.floatingMana.get(mana.getColorCode());
if (manaPaid.contains(mana)) { if (manaPaid.contains(mana)) {
removePaying.add(mana); removePaying.add(mana);
} }
else if (this.floatingMana.contains(mana)) { else if (poolLane != null && poolLane.contains(mana)) {
removeFloating.add(mana); removeFloating.add(mana);
} }
else { else {
@@ -593,10 +366,10 @@ public class ManaPool {
} }
for (int k = 0; k < removePaying.size(); k++) { 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++) { for (int k = 0; k < removeFloating.size(); k++) {
this.removeManaFrom(this.floatingMana, removeFloating.get(k)); this.removeMana(removeFloating.get(k));
} }
return true; return true;
} }

View File

@@ -22,7 +22,10 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import forge.Card; import forge.Card;
import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.Mana; import forge.card.mana.Mana;
import forge.card.mana.ManaPool; import forge.card.mana.ManaPool;
@@ -44,7 +47,7 @@ public class AbilityManaPart implements java.io.Serializable {
private String origProduced; private String origProduced;
private String lastExpressChoice = ""; private String lastExpressChoice = "";
private String manaRestrictions = ""; private String manaRestrictions = "";
private transient ArrayList<Mana> lastProduced = new ArrayList<Mana>(); private transient ArrayList<Mana> lastManaProduced = new ArrayList<Mana>();
/** The canceled. */ /** The canceled. */
private boolean canceled = false; private boolean canceled = false;
@@ -106,22 +109,20 @@ public class AbilityManaPart implements java.io.Serializable {
final ManaPool manaPool = player.getManaPool(); final ManaPool manaPool = player.getManaPool();
//clear lastProduced //clear lastProduced
this.lastProduced.clear(); this.lastManaProduced.clear();
// loop over mana produced string // loop over mana produced string
for (final String c : produced.split(" ")) { for (final String c : produced.split(" ")) {
try { if(StringUtils.isNumeric(c))
int colorlessAmount = Integer.parseInt(c); for(int i = Integer.parseInt(c); i > 0; i--) {
for (int i = 0; i < colorlessAmount; i++) { this.lastManaProduced.add(new Mana((byte)0, source, this));
this.lastProduced.add(new Mana(c, source, this));
} }
} catch (NumberFormatException e) { else
this.lastProduced.add(new Mana(c, source, this)); this.lastManaProduced.add(new Mana(MagicColor.fromName(c), source, this));
}
} }
// add the mana produced to the mana pool // add the mana produced to the mana pool
manaPool.addManaToFloating(this.lastProduced); manaPool.add(this.lastManaProduced);
// Run triggers // Run triggers
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
@@ -228,6 +229,16 @@ public class AbilityManaPart implements java.io.Serializable {
this.lastExpressChoice = s; 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();
}
/** /**
* <p> * <p>
* Getter for the field <code>lastAnyChoice</code>. * Getter for the field <code>lastAnyChoice</code>.
@@ -256,8 +267,8 @@ public class AbilityManaPart implements java.io.Serializable {
* *
* @return a {@link java.lang.String} object. * @return a {@link java.lang.String} object.
*/ */
public ArrayList<Mana> getLastProduced() { public ArrayList<Mana> getLastManaProduced() {
return this.lastProduced; return this.lastManaProduced;
} }
/** /**

View File

@@ -1,18 +1,18 @@
package forge.control.input; package forge.control.input;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import forge.Card; import forge.Card;
import forge.CardUtil; import forge.CardUtil;
import forge.Constant;
import forge.FThreads; import forge.FThreads;
import forge.card.ColorSet;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.ApiType; import forge.card.ability.ApiType;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.mana.ManaCostShard;
import forge.card.spellability.AbilityManaPart; import forge.card.spellability.AbilityManaPart;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
@@ -71,34 +71,26 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
* @return a boolean. * @return a boolean.
*/ */
private static boolean canMake(final SpellAbility am, final String mana) { private static boolean canMake(final SpellAbility am, final byte colorMask) {
if (mana.contains("1")) { if (colorMask == 0) {
return true; return true;
} }
AbilityManaPart m = am.getManaPart(); AbilityManaPart m = am.getManaPart();
if (mana.contains("S") && m.isSnow()) {
return true;
}
if (m.isAnyMana()) { if (m.isAnyMana()) {
return true; return true;
} }
if (am.getApi() == ApiType.ManaReflected) { if (am.getApi() == ApiType.ManaReflected) {
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet<String>(), new ArrayList<Card>()); final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am);
for (final String color : reflectableColors) { for (final String color : reflectableColors) {
if (mana.contains(MagicColor.toShortString(color))) { if (0 != (colorMask & MagicColor.fromName(color))) {
return true; return true;
} }
} }
} else { } else {
String[] colorsProduced; String colorsProduced = m.isComboMana() ? m.getComboColors() : m.getOrigProduced();
if (m.isComboMana()) { for (final String color : colorsProduced.split(" ")) {
colorsProduced = m.getComboColors().split(" "); if (0 != (colorMask & MagicColor.fromName(color))) {
}
else {
colorsProduced = m.getOrigProduced().split(" ");
}
for (final String color : colorsProduced) {
if (mana.contains(color)) {
return true; 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) { useManaFromPool(colorCode, manaCost); }
protected void useManaFromPool(byte colorCode, ManaCostBeingPaid manaCost) { protected void useManaFromPool(byte colorCode, ManaCostBeingPaid manaCost) {
// Convert Color to short String // 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); onManaAbilityPlayed(null);
showMessage(); showMessage();
@@ -144,21 +136,15 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
return; return;
} }
final StringBuilder cneeded = new StringBuilder(); byte colorCanUse = 0;
final StringBuilder colorRequired = new StringBuilder(); byte colorNeeded = 0;
boolean choice = true;
boolean skipExpress = false; for (final byte color : MagicColor.WUBRG) {
if (manaCost.isAnyPartPayableWith(color)) colorCanUse |= color;
for (final String color : Constant.Color.MANA_COLORS) { if (manaCost.needsColor(color)) colorNeeded |= color;
String shortColor = MagicColor.toShortString(color);
if (manaCost.isNeeded(color)) {
cneeded.append(shortColor);
}
if (manaCost.isColor(shortColor)) {
colorRequired.append(shortColor);
}
} }
boolean canUseColorless = manaCost.isAnyPartPayableWith((byte)0);
List<SpellAbility> abilities = new ArrayList<SpellAbility>(); List<SpellAbility> abilities = new ArrayList<SpellAbility>();
// you can't remove unneeded abilities inside a for(am:abilities) loop :( // 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)) if( StringUtils.isNotBlank(typeRes) && !card.isType(typeRes))
return; return;
boolean guessAbilityWithRequiredColors = true;
for (SpellAbility ma : card.getManaAbility()) { for (SpellAbility ma : card.getManaAbility()) {
ma.setActivatingPlayer(player); ma.setActivatingPlayer(player);
AbilityManaPart m = null; AbilityManaPart m = null;
SpellAbility tail = ma; SpellAbility tail = ma;
while (m == null && tail != null) { while (m == null && tail != null) {
m = tail.getManaPart(); m = tail.getManaPart();
tail = tail.getSubAbility(); tail = tail.getSubAbility();
} }
if (m == null) {
continue; if (m == null || !ma.canPlay()) continue;
} else if (!ma.canPlay()) { if (!canUseColorless && !InputPayManaBase.canMake(ma, colorCanUse)) continue;
continue; if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) continue;
} else if (!InputPayManaBase.canMake(ma, cneeded.toString())) { if (!m.meetsManaRestrictions(saPaidFor)) continue;
continue;
} else if (ma.isAbility() && ma.getRestrictions().isInstantSpeed()) {
continue;
} else if (!m.meetsManaRestrictions(saPaidFor)) {
continue;
}
abilities.add(ma); abilities.add(ma);
if (!skipExpress) { // skip express mana if the ability is not undoable or reusable
// skip express mana if the ability is not undoable or reusable if (!ma.isUndoable() || !ma.getPayCosts().isRenewableResource())
if (!ma.isUndoable()) { guessAbilityWithRequiredColors = false;
skipExpress = true;
continue;
} else if (!ma.getPayCosts().isRenewableResource()) {
skipExpress = true;
continue;
}
}
} }
if (abilities.isEmpty()) { if (abilities.isEmpty()) {
return; return;
} }
// Store some information about color costs to help with any mana choices // Store some information about color costs to help with any mana choices
String colorsNeeded = colorRequired.toString(); if (colorNeeded == 0) { // only colorless left
if ("1".equals(colorsNeeded)) { // only colorless left if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
if (saPaidFor.getSourceCard() != null
&& saPaidFor.getSourceCard().hasSVar("ManaNeededToAvoidNegativeEffect")) {
colorsNeeded = "";
String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(","); String[] negEffects = saPaidFor.getSourceCard().getSVar("ManaNeededToAvoidNegativeEffect").split(",");
for (String negColor : negEffects) { for (String negColor : negEffects) {
// convert long color strings to short color strings byte col = MagicColor.fromName(negColor);
if (negColor.length() > 1) { colorCanUse |= col;
negColor = MagicColor.toShortString(negColor);
}
if (!colorsNeeded.contains(negColor)) {
colorsNeeded = colorsNeeded.concat(negColor);
}
} }
} }
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, // If the card has sunburst or any other ability that tracks mana spent,
// skip express Mana choice // skip express Mana choice
if (saPaidFor.getSourceCard() != null if (saPaidFor.getSourceCard() != null && saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) {
&& saPaidFor.getSourceCard().hasKeyword("Sunburst") && saPaidFor.isSpell()) { colorCanUse = MagicColor.ALL_COLORS;
colorsNeeded = "WUBRG"; guessAbilityWithRequiredColors = false;
skipExpress = true;
} }
if (!skipExpress) { boolean choice = true;
if (guessAbilityWithRequiredColors) {
// express Mana Choice // express Mana Choice
final ArrayList<SpellAbility> colorMatches = new ArrayList<SpellAbility>(); final ArrayList<SpellAbility> colorMatches = new ArrayList<SpellAbility>();
for (final SpellAbility am : abilities) { for (SpellAbility sa : abilities) {
AbilityManaPart m = am.getManaPart(); if (abilityProducesManaOfColor(sa, colorNeeded))
if (am.getApi() == ApiType.ManaReflected) { colorMatches.add(sa);
final Iterable<String> reflectableColors = CardUtil.getReflectableManaColors(am, am, new HashSet<String>(), new ArrayList<Card>());
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);
}
}
}
} }
if (colorMatches.isEmpty()) { if (colorMatches.isEmpty()) {
// can only match colorless just grab the first and move on. // 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 // 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"); // System.out.println("Chosen sa=" + chosen + " of " + chosen.getSourceCard() + " to pay mana");
Runnable proc = new Runnable() { Runnable proc = new Runnable() {
@@ -299,6 +239,30 @@ public abstract class InputPayManaBase extends InputSyncronizedBase implements I
FThreads.invokeInNewThread(proc, true); FThreads.invokeInNewThread(proc, true);
// EDT that removes lockUI from input stack will call our showMessage() method // 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<String> 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) { public void onManaAbilityPlayed(final SpellAbility saPaymentSrc) {
if ( saPaymentSrc != null) // null comes when they've paid from pool if ( saPaymentSrc != null) // null comes when they've paid from pool

View File

@@ -22,6 +22,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -1082,17 +1084,12 @@ public final class GameActionUtil {
sb.append(baseMana); sb.append(baseMana);
} }
else { else {
try { if(StringUtils.isNumeric(baseMana)) {
// if baseMana is an integer(colorless), just multiply amount sb.append(amount * Integer.parseInt(baseMana));
// and baseMana } else {
final int base = Integer.parseInt(baseMana); sb.append(baseMana);
sb.append(base * amount); for (int i = 1; i < amount; i++) {
} catch (final NumberFormatException e) { sb.append(" ").append(baseMana);
for (int i = 0; i < amount; i++) {
if (i != 0) {
sb.append(" ");
}
sb.append(baseMana);
} }
} }
} }

View File

@@ -187,7 +187,7 @@ public class ComputerUtil {
if (unless != null && !unless.endsWith(">")) { if (unless != null && !unless.endsWith(">")) {
final int amount = AbilityUtils.calculateAmount(source, unless, sa); 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 the Unless isn't enough, this should be less likely to be used
if (amount > usableManaSources) { if (amount > usableManaSources) {

View File

@@ -29,7 +29,6 @@ import forge.deck.CardPool;
import forge.deck.Deck; import forge.deck.Deck;
import forge.deck.DeckSection; import forge.deck.DeckSection;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.item.CardPrinted; import forge.item.CardPrinted;
import forge.util.Aggregates; import forge.util.Aggregates;
@@ -784,31 +783,6 @@ public class ComputerUtilCard {
return result; return result;
} }
/**
* <p>
* getUsableManaSources.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
* @return a int.
*/
public static int getUsableManaSources(final Player player) {
List<Card> list = CardLists.filter(player.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@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();
}
/** /**
* <p> * <p>

View File

@@ -1,14 +1,16 @@
package forge.game.ai; package forge.game.ai;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; 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 com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardUtil; import forge.CardUtil;
@@ -19,6 +21,7 @@ import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.cost.CostPayment; import forge.card.cost.CostPayment;
import forge.card.mana.ManaAtom;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostBeingPaid; import forge.card.mana.ManaCostBeingPaid;
import forge.card.mana.ManaCostShard; import forge.card.mana.ManaCostShard;
@@ -30,6 +33,10 @@ import forge.card.spellability.SpellAbility;
import forge.game.GameActionUtil; import forge.game.GameActionUtil;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; 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. * TODO: Write javadoc for this type.
@@ -59,206 +66,286 @@ public class ComputerUtilMana {
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana); ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, test, extraMana);
final Card card = sa.getSourceCard(); final Card card = sa.getSourceCard();
// Make mana needed to avoid negative effect a mandatory cost for the AI
adjustManaCostToAvoidNegEffects(cost, card);
final String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(",");
int amountAdded = 0; final ManaPool manapool = ai.getManaPool();
for (int nStr = 0; nStr < negEffects.length; nStr++) { List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
// convert long color strings to short color strings Collections.sort(unpaidShards); // most difficult shards must come first
if (negEffects[nStr].length() > 1) { for(ManaCostShard part : unpaidShards) {
negEffects[nStr] = MagicColor.toShortString(negEffects[nStr]); if( part != ManaCostShard.X)
} manapool.payManaFromPool(sa, cost, part);
// make mana mandatory for AI
if (!cost.isColor(negEffects[nStr]) && cost.getColorlessManaAmount() > amountAdded) {
cost.combineManaCost(negEffects[nStr]);
amountAdded++;
}
} }
// 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()) { if (cost.isPaid()) {
// refund any mana taken from mana pool when test // refund any mana taken from mana pool when test
manapool.clearManaPaid(sa, test); manapool.clearManaPaid(sa, test);
return true; return true;
} }
// get map of mana abilities // arrange all mana abilities by color produced.
final Map<String, List<SpellAbility>> manaAbilityMap = ComputerUtilMana.mapManaSources(ai, checkPlayable); final MapOfLists<Integer, SpellAbility> manaAbilityMap = ComputerUtilMana.groupSourcesByManaColor(ai, checkPlayable);
// initialize ArrayList list for mana needed if ( manaAbilityMap.isEmpty() ) {
final List<List<SpellAbility>> partSources = new ArrayList<List<SpellAbility>>(); manapool.clearManaPaid(sa, test);
final List<Integer> partPriority = new ArrayList<Integer>();
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
return false; return false;
} }
// Create array to keep track of sources used // select which abilities may be used for each shard
final ArrayList<Card> usedSources = new ArrayList<Card>(); Map<ManaCostShard, Collection<SpellAbility>> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
// this is to prevent errors for mana sources that have abilities that
// cost mana.
usedSources.add(sa.getSourceCard());
// Loop over mana needed // Loop over mana needed
int nPriority = 0; ManaCostShard toPay = null;
List<String> negEffectPaid = new ArrayList<String>(); while (!cost.isPaid()) {
while (nPriority < partPriority.size()) { toPay = getNextShardToPay(cost, sourcesForShards);
final int nPart = partPriority.get(nPriority);
final ManaCostBeingPaid costPart = new ManaCostBeingPaid(costParts[nPart]); Collection<SpellAbility> saList = sourcesForShards.get(toPay);
// Loop over mana abilities that can be used to current mana cost part List<SpellAbility> payableSources = new ArrayList<SpellAbility>();
for (final SpellAbility ma : partSources.get(nPart)) { if( saList != null ) {
final Card sourceCard = ma.getSourceCard(); for (final SpellAbility ma : saList) {
if( canPayShardWithSpellAbility(toPay, ai, ma, sa, checkPlayable || !test ) ) {
// Check if source has already been used payableSources.add(ma);
if (usedSources.contains(sourceCard)) { }
continue;
} }
}
// Check if AI can still play this mana ability
ma.setActivatingPlayer(ai); if( payableSources.isEmpty() ) {
// if the AI can't pay the additional costs skip the mana ability if(!toPay.isPhyrexian() || !ai.canPayLife(2))
if (ma.getPayCosts() != null && checkPlayable) { break; // cannot pay
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
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<SpellAbility> 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; 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 { } else {
if (m.isComboMana()) { System.err.println("Ability " + saPayment + " from " + saPayment.getSourceCard() + " had NULL as payCost");
String colorChoice = costParts[nPart]; saPayment.getSourceCard().tap();
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<String>(), new ArrayList<Card>()).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;
}
} }
// add source card to used list AbilityUtils.resolve(saPayment, false);
usedSources.add(sourceCard); // subtract mana from mana pool
negEffectPaid.add(manaProduced); manapool.payManaFromAbility(sa, cost, saPayment);
costPart.payMultipleMana(manaProduced);
// no need remove abilities from resource map, once their costs are paid and consume resources, they can not be used again
if (!test) { }
// Pay additional costs }
if (ma.getPayCosts() != null) {
final CostPayment pay = new CostPayment(ma.getPayCosts(), ma); manapool.clearManaPaid(sa, test);
if (!pay.payComputerCosts(ai, ai.getGame())) { if(!cost.isPaid()) {
continue; if( test )
} return false;
} else { else
sourceCard.tap(); throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard().getName() + ". Didn't find what to pay for " + toPay);
} }
// resolve mana ability
//ma.resolve();
AbilityUtils.resolve(ma, false); // if (sa instanceof Spell_Permanent) // should probably add this
// subtract mana from mana pool sa.getSourceCard().setColorsPaid(cost.getColorsPaid());
manapool.payManaFromAbility(sa, cost, ma); sa.getSourceCard().setSunburstValue(cost.getSunburst());
} else { return true;
cost.payMultipleMana(manaProduced); } // 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<String> 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; break;
} }
} // end of mana ability loop
if (!costPart.isPaid() || cost.isPaid()) {
break;
} else {
nPriority++;
} }
} // end of cost parts loop saPayment.getManaPart().setExpressChoice(MagicColor.toShortString(colorChoice));
}
//check for phyrexian mana }
if (!cost.isPaid() && cost.containsPhyrexianMana() && ai.getLife() > 5 && ai.canPayLife(2)) {
cost.payPhyrexian(); private static boolean canPayShardWithSpellAbility(ManaCostShard toPay, Player ai, SpellAbility ma, SpellAbility sa, boolean checkCosts) {
if (!test) { final Card sourceCard = ma.getSourceCard();
ai.payLife(2, sa.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); if (m.isComboMana()) {
// check if paid for(String s : m.getComboColors().split(" ")) {
if (cost.isPaid()) { if ( "Any".equals(s) || toPay.canBePaidWithManaOfColor(MagicColor.fromName(s)))
// if (sa instanceof Spell_Permanent) // should probably add this return true;
sa.getSourceCard().setColorsPaid(cost.getColorsPaid()); }
sa.getSourceCard().setSunburstValue(cost.getSunburst()); return false;
return true;
} else if (ma.getApi() == ApiType.ManaReflected) {
Set<String> 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;
} }
return true;
if (!test) { }
final StringBuilder sb = new StringBuilder();
sb.append("ComputerUtil : payManaCost() cost was not paid for ");
sb.append(sa.getSourceCard().getName()); private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Map<ManaCostShard, Collection<SpellAbility>> sourcesForShards) {
throw new RuntimeException(sb.toString()); // 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);
}
}
}
/**
* <p>
* getComboManaChoice.
* </p>
*
* @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 // 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) { public static void computerPayX(final SpellAbility sa, Player player, int xCost) {
@@ -297,134 +384,37 @@ public class ComputerUtilMana {
* @param foundAllSources * @param foundAllSources
* @return Were all mana sources found? * @return Were all mana sources found?
*/ */
private static boolean findManaSources(final Player ai, final Map<String, List<SpellAbility>> manaAbilityMap, private static MapOfLists<ManaCostShard, SpellAbility> groupAndOrderToPayShards(final Player ai, final MapOfLists<Integer, SpellAbility> manaAbilityMap, final ManaCostBeingPaid cost) {
final List<List<SpellAbility>> partSources, final List<Integer> partPriority, MapOfLists<ManaCostShard, SpellAbility> res = new EnumMapOfLists<ManaCostShard, SpellAbility>(ManaCostShard.class, CollectionSuppliers.<SpellAbility>hashSets());
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<SpellAbility> srcFound = new ArrayList<SpellAbility>();
// 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 void increaseManaCostByX(SpellAbility sa, ManaCostBeingPaid cost, int xValue) { // loop over cost parts
String manaXColor = sa.hasParam("XColor") ? sa.getParam("XColor") : ""; for (ManaCostShard shard : cost.getDistinctShards() ) {
if (manaXColor.isEmpty()) { if ( shard == ManaCostShard.S ) {
cost.increaseColorlessMana(xValue); res.put(shard, manaAbilityMap.get(ManaAtom.IS_SNOW));
} else { continue;
if (manaXColor.equals("B")) { }
cost.increaseShard(ManaCostShard.BLACK, xValue);
} else if (manaXColor.equals("G")) { if (shard.isOr2Colorless()) {
cost.increaseShard(ManaCostShard.GREEN, xValue); Integer colorKey = Integer.valueOf(shard.getColorMask());
} else if (manaXColor.equals("R")) { if (manaAbilityMap.containsKey(colorKey) )
cost.increaseShard(ManaCostShard.RED, xValue); res.addAll(shard, manaAbilityMap.get(colorKey));
} else if (manaXColor.equals("U")) { if (manaAbilityMap.containsKey(ManaAtom.COLORLESS) )
cost.increaseShard(ManaCostShard.BLUE, xValue); res.addAll(shard, manaAbilityMap.get(ManaAtom.COLORLESS));
} else if (manaXColor.equals("W")) { continue;
cost.increaseShard(ManaCostShard.WHITE, xValue); }
/* Max: Never seen these options in real cards for(Entry<Integer, Collection<SpellAbility>> kv : manaAbilityMap.entrySet()) {
} else if (manaXColor.contains("B") && manaXColor.contains("G")) { if( shard.canBePaidWithManaOfColor(kv.getKey().byteValue()) )
cost.increaseShard(ManaCostShard.BG, xValue); res.addAll(shard, kv.getValue());
} 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);
*/
}
} }
} }
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. * Calculate the ManaCost for the given SpellAbility.
* @param sa * @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) { if (!test) {
card.setXManaCostPaid(manaToAdd / cost.getXcounter()); 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 //This method is currently used by AI to estimate human's available mana
private static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) { public static List<Card> getAvailableMana(final Player ai, final boolean checkPlayable) {
final List<Card> list = ai.getCardsIn(ZoneType.Battlefield); final List<Card> list = ai.getCardsIn(ZoneType.Battlefield);
list.addAll(ai.getCardsIn(ZoneType.Hand)); list.addAll(ai.getCardsIn(ZoneType.Hand));
final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() { final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (checkPlayable) { for (final SpellAbility am : getAIPlayableMana(c)) {
for (final SpellAbility am : getAIPlayableMana(c)) { am.setActivatingPlayer(ai);
am.setActivatingPlayer(ai); if (am.canPlay() || !checkPlayable) {
if (am.canPlay()) { return true;
return true;
}
} }
return false;
} else {
return true;
} }
return false;
} }
}); // CardListFilter }); // CardListFilter
@@ -507,8 +494,7 @@ public class ComputerUtilMana {
// 2. Search for mana sources that have a certain number of abilities // 2. Search for mana sources that have a certain number of abilities
// 3. Use lands that produce any color many // 3. Use lands that produce any color many
// 4. all other sources (creature, costs, drawback, etc.) // 4. all other sources (creature, costs, drawback, etc.)
for (int i = 0; i < manaSources.size(); i++) { for (Card card : manaSources) {
final Card card = manaSources.get(i);
if (card.isCreature() || card.isEnchanted()) { if (card.isCreature() || card.isEnchanted()) {
otherManaSources.add(card); otherManaSources.add(card);
@@ -588,27 +574,12 @@ public class ComputerUtilMana {
//This method is currently used by AI to estimate mana available to human //This method is currently used by AI to estimate mana available to human
private static Map<String, List<SpellAbility>> mapManaSources(final Player ai, boolean checkPlayable) { private static MapOfLists<Integer, SpellAbility> groupSourcesByManaColor(final Player ai, boolean checkPlayable) {
final Map<String, List<SpellAbility>> manaMap = new HashMap<String, List<SpellAbility>>(); final MapOfLists<Integer, SpellAbility> manaMap = new TreeMapOfLists<Integer, SpellAbility>(CollectionSuppliers.<SpellAbility>arrayLists());
final List<SpellAbility> whiteSources = new ArrayList<SpellAbility>(); // Loop over all current available mana sources
final List<SpellAbility> blueSources = new ArrayList<SpellAbility>(); for (final Card sourceCard : getAvailableMana(ai, checkPlayable)) {
final List<SpellAbility> blackSources = new ArrayList<SpellAbility>(); for (final SpellAbility m : getAIPlayableMana(sourceCard)) {
final List<SpellAbility> redSources = new ArrayList<SpellAbility>();
final List<SpellAbility> greenSources = new ArrayList<SpellAbility>();
final List<SpellAbility> colorlessSources = new ArrayList<SpellAbility>();
final List<SpellAbility> snowSources = new ArrayList<SpellAbility>();
// Get list of current available mana sources
final List<Card> 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<SpellAbility> manaAbilities = getAIPlayableMana(sourceCard);
// Loop over all mana abilities for a source
for (final SpellAbility m : manaAbilities) {
m.setActivatingPlayer(ai); m.setActivatingPlayer(ai);
if (!m.canPlay() && checkPlayable) { if (!m.canPlay() && checkPlayable) {
continue; continue;
@@ -621,60 +592,32 @@ public class ComputerUtilMana {
continue; continue;
} }
} }
// add to colorless source list manaMap.add(ManaAtom.COLORLESS, m); // add to colorless source list
colorlessSources.add(m);
Set<String> reflectedColors = CardUtil.getReflectableManaColors(m);
// find possible colors // find possible colors
if (m.getManaPart().canProduce("W") if (m.getManaPart().canProduce("W") || reflectedColors.contains(Constant.Color.WHITE)) {
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.WHITE)) { manaMap.add(ManaAtom.WHITE, m);
whiteSources.add(m);
} }
if (m.getManaPart().canProduce("U") if (m.getManaPart().canProduce("U") || reflectedColors.contains(Constant.Color.BLUE)) {
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.BLUE)) { manaMap.add(ManaAtom.BLUE, m);
blueSources.add(m);
} }
if (m.getManaPart().canProduce("B") if (m.getManaPart().canProduce("B") || reflectedColors.contains(Constant.Color.BLACK)) {
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.BLACK)) { manaMap.add(ManaAtom.BLACK, m);
blackSources.add(m);
} }
if (m.getManaPart().canProduce("R") if (m.getManaPart().canProduce("R") || reflectedColors.contains(Constant.Color.RED)) {
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.RED)) { manaMap.add(ManaAtom.RED, m);
redSources.add(m);
} }
if (m.getManaPart().canProduce("G") if (m.getManaPart().canProduce("G") || reflectedColors.contains(Constant.Color.GREEN)) {
|| CardUtil.getReflectableManaColors(m, m, new HashSet<String>(), new ArrayList<Card>()).contains(Constant.Color.GREEN)) { manaMap.add(ManaAtom.GREEN, m);
greenSources.add(m);
} }
if (m.getManaPart().isSnow()) { if (m.getManaPart().isSnow()) {
snowSources.add(m); manaMap.add(ManaAtom.IS_SNOW, m);
} }
} // end of mana abilities loop } // end of mana abilities loop
} // end of mana sources 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; return manaMap;
} }
@@ -704,83 +647,6 @@ public class ComputerUtilMana {
return xMana; return xMana;
} }
/**
* <p>
* getComboManaChoice.
* </p>
*
* @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 // Returns basic mana abilities plus "reflected mana" abilities
/** /**
* <p> * <p>
@@ -796,13 +662,12 @@ public class ComputerUtilMana {
// if a mana ability has a mana cost the AI will miscalculate // if a mana ability has a mana cost the AI will miscalculate
// if there is a parent ability the AI can't use it // if there is a parent ability the AI can't use it
final Cost cost = a.getPayCosts(); final Cost cost = a.getPayCosts();
if (!cost.hasNoManaCost() if (!cost.hasNoManaCost() || (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) {
|| (a.getApi() != ApiType.Mana && a.getApi() != ApiType.ManaReflected)) {
continue; continue;
} }
AbilityManaPart am = a.getManaPart(); //AbilityManaPart am = a.getManaPart();
if (am.isBasic() && !res.contains(a)) { if (/*am.isBasic() && */!res.contains(a)) {
res.add(a); res.add(a);
} }

View File

@@ -8,6 +8,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.control.input.Input; import forge.control.input.Input;
@@ -126,4 +127,5 @@ public abstract class PlayerController {
public abstract void playMadness(SpellAbility madness); public abstract void playMadness(SpellAbility madness);
public abstract List<Card> chooseCardsToDelve(int colorLessAmount, List<Card> grave); public abstract List<Card> chooseCardsToDelve(int colorLessAmount, List<Card> grave);
public abstract List<Card> chooseCardsToDiscardUnlessType(int min, List<Card> hand, String param, SpellAbility sa); public abstract List<Card> chooseCardsToDiscardUnlessType(int min, List<Card> hand, String param, SpellAbility sa);
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
} }

View File

@@ -14,6 +14,7 @@ import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.CardPredicates; import forge.CardPredicates;
import forge.GameEntity; import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
@@ -318,4 +319,10 @@ public class PlayerControllerAi extends PlayerController {
} }
@Override
public Mana chooseManaFromPool(List<Mana> manaChoices) {
return manaChoices.get(0); // no brains used
}
} }

View File

@@ -14,6 +14,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card; import forge.Card;
import forge.FThreads; import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection; import forge.card.spellability.TargetSelection;
@@ -36,6 +37,7 @@ import forge.gui.GuiDialog;
import forge.gui.GuiUtils; import forge.gui.GuiUtils;
import forge.gui.match.CMatchUI; import forge.gui.match.CMatchUI;
import forge.item.CardPrinted; import forge.item.CardPrinted;
import forge.util.TextUtil;
/** /**
@@ -500,4 +502,19 @@ public class PlayerControllerHuman extends PlayerController {
return target.getSelected(); return target.getSelected();
} }
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseManaFromPool(java.util.List)
*/
@Override
public Mana chooseManaFromPool(List<Mana> manaChoices) {
List<String> options = new ArrayList<String>();
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);
}
} }

View File

@@ -384,7 +384,7 @@ public class VField implements IVDoc<CField> {
public void updateManaPool(final Player p0) { public void updateManaPool(final Player p0) {
ManaPool m = p0.getManaPool(); ManaPool m = p0.getManaPool();
for(Pair<FLabel, Byte> label : manaLabels) for(Pair<FLabel, Byte> label : manaLabels)
label.getKey().setText(Integer.toString(m.getAmountOfColor(MagicColor.toLongString(label.getRight())))); label.getKey().setText(Integer.toString(m.getAmountOfColor(label.getRight())));
} }
//========= Retrieval methods //========= Retrieval methods

View File

@@ -31,6 +31,8 @@ import java.util.Map.Entry;
*/ */
public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> { public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
// Constructors here // Constructors here
/** /**
* *

View File

@@ -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<T extends Enum<T>> extends EnumMap<T, Integer> implements MapToAmount<T> {
private static final long serialVersionUID = -4749796492075359368L;
public EnumMapToAmount(Class<T> keyType) {
super(keyType);
}
public EnumMapToAmount(EnumMap<T, ? extends Integer> m) {
super(m);
}
public EnumMapToAmount(Map<T, ? extends Integer> 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<T> 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<T> item) {
for(T i : item) substract(i);
}
@Override
public int countAll() {
int c = 0;
for(java.util.Map.Entry<T, Integer> 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();
}
}

View File

@@ -0,0 +1,18 @@
package forge.util.maps;
import java.util.Map;
/**
* TODO: Write javadoc for this type.
*
*/
public interface MapToAmount<K> extends Map<K, Integer> {
void add(K item);
void add(K item, int amount);
void addAll(Iterable<K> item);
boolean substract(K item);
boolean substract(K item, int amount);
void substractAll(Iterable<K> item);
int countAll();
int count(K item); // just unboxes and returns zero instead of null
}

View File

@@ -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<T> extends TreeMap<T, Integer> implements MapToAmount<T> {
private static final long serialVersionUID = 5001205073403776022L;
public TreeMapToAmount() {
super();
}
public TreeMapToAmount(Comparator<? super T> comparator) {
super(comparator);
}
public TreeMapToAmount(Map<? extends T, ? extends Integer> m) {
super(m);
}
public TreeMapToAmount(SortedMap<T, ? extends Integer> 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<T> 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<T> item) {
for(T i : item) substract(i);
}
@Override
public int countAll() {
int c = 0;
for(java.util.Map.Entry<T, Integer> 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();
}
}

View File

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