Implement cost reduction to apply for most cases of reducing hybrid/split/phyrexian cost.

Extend unit test with those cases.
master
Bilbo 2018-11-04 00:10:31 +01:00 committed by Melvin Zhang
parent aaf678b517
commit 28732e250a
3 changed files with 84 additions and 5 deletions

View File

@ -41,6 +41,7 @@ public enum MagicCostManaType {
public static final int NR_OF_TYPES=values().length;
public static final EnumSet<MagicCostManaType> MONO = EnumSet.range(White, Green);
public static final EnumSet<MagicCostManaType> HYBRID = EnumSet.range(WhiteBlue, GreenBlue);
public static final EnumSet<MagicCostManaType> CHOICE = EnumSet.range(WhiteBlue, HybridGreen);
public static final EnumSet<MagicCostManaType> NON_MONO = EnumSet.complementOf(MONO);
private final String name;

View File

@ -145,6 +145,10 @@ public class MagicManaCost implements MagicCopyable {
return XCount > 0;
}
/**
* @param x value for {X} in the mana cost (if it is present)
* @return list of mana costs, with each element representing exactly 1 mana of some kind
*/
public List<MagicCostManaType> getCostManaTypes(final int x) {
final List<MagicCostManaType> types = new ArrayList<>();
int generic=x * XCount;
@ -409,6 +413,7 @@ public class MagicManaCost implements MagicCopyable {
final int idx = type.ordinal();
reducedAmounts[idx] += amt;
if (XCount > 0 && type == MagicCostManaType.Generic && reducedAmounts[idx] < 0) {
// If cost contains {X}, we store even negative value for colorless as possible "discount"
return new MagicManaCost(reducedAmounts, XCount);
} else if (amounts[idx] == 0 && reducedAmounts[idx] < 0) {
return this;
@ -429,12 +434,58 @@ public class MagicManaCost implements MagicCopyable {
/**
* Return cost decreased by some other cost. Only identical mana is removed from the cost.
* For example, cost {R}{B} reduced by {B}{B}{1} will become {R}
*
* When removing colored mana ({R}, {G}, {B}, {U}, {W}), primarily remove the specified mana.
* If that is not present, look for mana with choice (hybrid, phyrexian, or colorless-hybrid in that order)
* and see if the reduction can be deducted from some of that cost.
*
* First possible encountered cost is used for reduction.
* That may not necessarily be optimal choice if more hybrid costs are present.
*
* (i.e. removing {R}{G} from {R/G}{R/U} may remove first {R/G} via {R} even if the result would not be optimal)
*/
public MagicManaCost reducedBy(MagicManaCost extraCost) {
MagicManaCost res = this;
for (final MagicCostManaType cmt : extraCost.getCostManaTypes(0)) {
res = res.reduce(cmt, 1);
final int[] reducedAmounts = Arrays.copyOf(amounts, amounts.length);
boolean changed = false;
List<MagicCostManaType> remaining = new ArrayList<>();
for (final MagicCostManaType type : extraCost.getCostManaTypes(0)) {
final int idx = type.ordinal();
if (reducedAmounts[idx] >= 1) {
reducedAmounts[idx] -= 1;
changed = true;
} else {
if (XCount > 0 && type == MagicCostManaType.Generic) {
// For Generic: if X present, store even negative
reducedAmounts[idx] -= 1;
changed = true;
} else {
// Try in second round
if (MagicCostManaType.MONO.contains(type)) {
remaining.add(type);
}
}
}
}
return res;
// Try to remove monocolored costs from hybrid costs
for (final MagicCostManaType type : remaining) {
MagicManaType thisColor = type.getTypes().get(0);
for (MagicCostManaType candidate : MagicCostManaType.CHOICE) {
final int idx = candidate.ordinal();
if (reducedAmounts[idx] >= 1) {
if (candidate.getTypes().contains(thisColor)) {
reducedAmounts[idx] -= 1;
changed = true;
break;
}
}
}
}
if (changed) {
return new MagicManaCost(reducedAmounts, XCount);
}
return this;
}
}

View File

@ -23,9 +23,36 @@ public class MagicManaCostTest {
assertEquals("{X}{2}{R}", rx.increasedBy(MagicManaCost.create("{2}")).toString());
assertEquals("{X}{X}{2}{U}", uxx.increasedBy(MagicManaCost.create("{2}")).toString());
// Following two are not 100% correct, but as close to the wanted result as possible
// (as the X should be replaced by something like "X-2", or rather the discount should be fully resolved at cast time)
// Note that -2 is stored internally in the amount, but not used for discount when casting.
assertEquals("{X}{R}", rx.reducedBy(MagicManaCost.create("{2}")).toString());
assertEquals("{X}{X}{U}", uxx.reducedBy(MagicManaCost.create("{2}")).toString());
assertEquals("{X}{R}", rx.increasedBy(MagicManaCost.create("{2}")).reducedBy(MagicManaCost.create("{2}")).toString());
assertEquals("{X}{R}", rx.increasedBy(MagicManaCost.create("{2}")).reducedBy(
MagicManaCost.create("{2}")).toString());
}
@Test
public void testSplitCostModification() {
MagicManaCost rw3 = MagicManaCost.create("{R/W}{R/W}{R/W}");
assertEquals("{R/W}{R/W}", rw3.reducedBy(MagicManaCost.create("{R}{G}")).toString());
assertEquals("{R/W}", rw3.reducedBy(MagicManaCost.create("{R}{G}{R/W}")).toString());
MagicManaCost rw2gw = MagicManaCost.create("{R/W}{R/W}{G/W}");
assertEquals("{0}", rw2gw.reducedBy(MagicManaCost.create("{R}{G}{R/W}")).toString());
assertEquals("{0}", rw2gw.reducedBy(MagicManaCost.create("{R}{G}{W}")).toString());
MagicManaCost hybrid = MagicManaCost.create("{2/R}{2/G}{2/W}");
assertEquals("{2/W}", hybrid.reducedBy(MagicManaCost.create("{R}{G}{U}")).toString());
MagicManaCost phyR = MagicManaCost.create("{R}{R/P}{G/P}{W/P}");
assertEquals("{W/P}", phyR.reducedBy(MagicManaCost.create("{R}{R}{G}{U}")).toString());
MagicManaCost phy = MagicManaCost.create("{R/P}{G/P}{W/P}");
assertEquals("{W/P}", phy.reducedBy(MagicManaCost.create("{R}{G}{U}")).toString());
}
}