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/maps/CollectionSuppliers.java -text
src/main/java/forge/util/maps/EnumMapOfLists.java -text
src/main/java/forge/util/maps/EnumMapToAmount.java -text
src/main/java/forge/util/maps/HashMapOfLists.java -text
src/main/java/forge/util/maps/MapOfLists.java -text
src/main/java/forge/util/maps/MapToAmount.java -text
src/main/java/forge/util/maps/TreeMapOfLists.java -text
src/main/java/forge/util/maps/TreeMapToAmount.java -text
src/main/java/forge/util/maps/package-info.java -text
src/main/java/forge/util/package-info.java -text
src/main/java/forge/util/storage/IStorage.java -text

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,7 +65,7 @@ public class CostDamage extends CostPart {
*/
@Override
public final void payAI(final PaymentDecision decision, final Player ai, SpellAbility ability, Card source) {
ability.getActivatingPlayer().addDamage(decision.c, source);
ai.addDamage(decision.c, source);
}
/*

View File

@@ -22,7 +22,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import forge.Card;
import forge.card.mana.ManaCost;
import forge.card.spellability.SpellAbility;
import forge.game.GameState;
import forge.game.player.Player;
@@ -178,10 +177,6 @@ public class CostPayment {
final Card source = this.ability.getSourceCard();
final List<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>();
// Set all of the decisions before attempting to pay anything

View File

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

View File

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

View File

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

View File

@@ -17,20 +17,13 @@
*/
package forge.card.mana;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.util.BinaryUtil;
/**
* The Class CardManaCostShard.
*/
public enum ManaCostShard {
COLORLESS(ManaAtom.COLORLESS, "1"),
X(ManaAtom.IS_X, "X"),
S(ManaAtom.IS_SNOW, "S"),
public enum ManaCostShard implements Comparable<ManaCostShard> {
// declaration order matters! Place the shards that offer least ways to be paid for first
/* Pure colors */
WHITE(ManaAtom.WHITE, "W"),
@@ -51,6 +44,17 @@ public enum ManaCostShard {
BG(ManaAtom.BLACK | ManaAtom.GREEN, "B/G", "BG"),
RG(ManaAtom.RED | ManaAtom.GREEN, "R/G", "RG"),
/* Or 2 colorless */
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"),
B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"),
R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"),
G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G"),
// Snow and colorless
S(ManaAtom.IS_SNOW, "S"),
COLORLESS(ManaAtom.COLORLESS, "1"),
/* Phyrexian */
PW(ManaAtom.WHITE | ManaAtom.OR_2_LIFE, "W/P", "PW"),
PU(ManaAtom.BLUE | ManaAtom.OR_2_LIFE, "U/P", "PU"),
@@ -58,13 +62,7 @@ public enum ManaCostShard {
PR(ManaAtom.RED | ManaAtom.OR_2_LIFE, "R/P", "PR"),
PG(ManaAtom.GREEN | ManaAtom.OR_2_LIFE, "G/P", "PG"),
/* Or 2 colorless */
W2(ManaAtom.WHITE | ManaAtom.OR_2_COLORLESS, "2/W", "2W"),
U2(ManaAtom.BLUE | ManaAtom.OR_2_COLORLESS, "2/U", "2U"),
B2(ManaAtom.BLACK | ManaAtom.OR_2_COLORLESS, "2/B", "2B"),
R2(ManaAtom.RED | ManaAtom.OR_2_COLORLESS, "2/R", "2R"),
G2(ManaAtom.GREEN | ManaAtom.OR_2_COLORLESS, "2/G", "2G");
X(ManaAtom.IS_X, "X");
private final int shard;
@@ -275,23 +273,6 @@ public enum ManaCostShard {
public boolean isOr2Colorless() {
return (this.shard & ManaAtom.OR_2_COLORLESS) != 0;
}
/**
* TODO: Can pay for this shard with unlimited mana of given color combination?
* @param color
* @return
*/
public boolean canBePaidWithAvaliable(ColorSet color) {
// can pay with life?
if (this.isPhyrexian()) {
return true;
}
// can pay with any color?
if (this.isOr2Colorless()) {
return true;
}
// either colored part is empty, or there are same colors in shard and mana source
return (COLORS_SUPERPOSITION & this.shard) == 0 || (color.getColor() & this.shard) > 0;
}
public boolean canBePaidWithManaOfColor(byte colorCode) {
return this.isOr2Colorless() || (COLORS_SUPERPOSITION & this.shard) == 0 || (colorCode & this.shard) > 0;

View File

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

View File

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

View File

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

View File

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

View File

@@ -187,7 +187,7 @@ public class ComputerUtil {
if (unless != null && !unless.endsWith(">")) {
final int amount = AbilityUtils.calculateAmount(source, unless, sa);
final int usableManaSources = ComputerUtilCard.getUsableManaSources(ai.getOpponent());
final int usableManaSources = ComputerUtilMana.getAvailableMana(ai.getOpponent(), true).size();
// If the Unless isn't enough, this should be less likely to be used
if (amount > usableManaSources) {

View File

@@ -29,7 +29,6 @@ import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.item.CardPrinted;
import forge.util.Aggregates;
@@ -784,31 +783,6 @@ public class ComputerUtilCard {
return result;
}
/**
* <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>

View File

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

View File

@@ -8,6 +8,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import forge.Card;
import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.control.input.Input;
@@ -126,4 +127,5 @@ public abstract class PlayerController {
public abstract void playMadness(SpellAbility madness);
public abstract List<Card> chooseCardsToDelve(int colorLessAmount, List<Card> grave);
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.CardPredicates;
import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
@@ -318,4 +319,10 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public Mana chooseManaFromPool(List<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.FThreads;
import forge.GameEntity;
import forge.card.mana.Mana;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.card.spellability.TargetSelection;
@@ -36,6 +37,7 @@ import forge.gui.GuiDialog;
import forge.gui.GuiUtils;
import forge.gui.match.CMatchUI;
import forge.item.CardPrinted;
import forge.util.TextUtil;
/**
@@ -500,4 +502,19 @@ public class PlayerControllerHuman extends PlayerController {
return target.getSelected();
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseManaFromPool(java.util.List)
*/
@Override
public Mana chooseManaFromPool(List<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) {
ManaPool m = p0.getManaPool();
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

View File

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