Java tutorial
/* * Forge: Play Magic: the Gathering. * Copyright (C) 2011 Forge Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package forge.game.mana; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.IParserManaCost; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import java.util.*; import java.util.Map.Entry; import org.apache.commons.lang3.StringUtils; /** * <p> * ManaCostBeingPaid class. * </p> * * @author Forge * @version $Id: ManaCostBeingPaid.java 30124 2015-09-26 15:01:45Z drdev $ */ public class ManaCostBeingPaid { private class ManaCostBeingPaidIterator implements IParserManaCost { private Iterator<ManaCostShard> mch; private ManaCostShard nextShard = null; private int remainingShards = 0; private boolean hasSentX = false; public ManaCostBeingPaidIterator() { mch = unpaidShards.keySet().iterator(); } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public ManaCostShard next() { if (remainingShards == 0) { throw new UnsupportedOperationException("All shards were depleted, call hasNext()"); } remainingShards--; return nextShard; } @Override public boolean hasNext() { if (remainingShards > 0) { return true; } if (!hasSentX) { if (nextShard != ManaCostShard.X && cntX > 0) { nextShard = ManaCostShard.X; remainingShards = cntX; return true; } else { hasSentX = true; } } if (!mch.hasNext()) { return false; } nextShard = mch.next(); if (nextShard == ManaCostShard.COLORLESS) { return this.hasNext(); // skip colorless } remainingShards = unpaidShards.get(nextShard).totalCount; return true; } @Override public int getTotalColorlessCost() { ShardCount c = unpaidShards.get(ManaCostShard.COLORLESS); return c == null ? 0 : c.totalCount; } } private class ShardCount { private int xCount; private int totalCount; private ShardCount() { } private ShardCount(ShardCount copy) { xCount = copy.xCount; totalCount = copy.totalCount; } } // holds Mana_Part objects // ManaPartColor is stored before ManaPartColorless private final Map<ManaCostShard, ShardCount> unpaidShards = new HashMap<ManaCostShard, ShardCount>(); private Map<String, Integer> xManaCostPaidByColor; private final String sourceRestriction; private byte sunburstMap = 0; private int cntX = 0; /** * Copy constructor * @param manaCostBeingPaid */ public ManaCostBeingPaid(ManaCostBeingPaid manaCostBeingPaid) { for (Entry<ManaCostShard, ShardCount> m : manaCostBeingPaid.unpaidShards.entrySet()) { unpaidShards.put(m.getKey(), new ShardCount(m.getValue())); } if (manaCostBeingPaid.xManaCostPaidByColor != null) { xManaCostPaidByColor = new HashMap<String, Integer>(manaCostBeingPaid.xManaCostPaidByColor); } sourceRestriction = manaCostBeingPaid.sourceRestriction; sunburstMap = manaCostBeingPaid.sunburstMap; cntX = manaCostBeingPaid.cntX; } public ManaCostBeingPaid(ManaCost manaCost) { this(manaCost, null); } public ManaCostBeingPaid(ManaCost manaCost, String srcRestriction) { sourceRestriction = srcRestriction; if (manaCost == null) { return; } for (ManaCostShard shard : manaCost) { if (shard == ManaCostShard.X) { cntX++; } else { increaseShard(shard, 1, false); } } increaseColorlessMana(manaCost.getGenericCost()); } public Map<String, Integer> getXManaCostPaidByColor() { return xManaCostPaidByColor; } public final int getSunburst() { return ColorSet.fromMask(sunburstMap).countColors(); } public final byte getColorsPaid() { return sunburstMap; } public final boolean containsPhyrexianMana() { for (ManaCostShard shard : unpaidShards.keySet()) { if (shard.isPhyrexian()) { return true; } } return false; } public final boolean payPhyrexian() { ManaCostShard phy = null; for (ManaCostShard mcs : unpaidShards.keySet()) { if (mcs.isPhyrexian()) { phy = mcs; break; } } 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 public final boolean needsColor(final byte colorMask, final ManaPool pool) { for (ManaCostShard shard : unpaidShards.keySet()) { if (shard == ManaCostShard.COLORLESS) { continue; } if (shard.isOr2Colorless()) { if ((shard.getColorMask() & colorMask) != 0) { return true; } } else if (pool.canPayForShardWithColor(shard, colorMask)) { return true; } } return false; } // isNeeded(String) still used by the Computer, might have problems activating Snow abilities public final boolean isAnyPartPayableWith(byte colorMask, final ManaPool pool) { for (ManaCostShard shard : unpaidShards.keySet()) { if (pool.canPayForShardWithColor(shard, colorMask)) { return true; } } return false; } public final boolean isNeeded(final Mana paid, final ManaPool pool) { for (ManaCostShard shard : unpaidShards.keySet()) { if (canBePaidWith(shard, paid, pool)) { return true; } } return false; } public final boolean isPaid() { return unpaidShards.isEmpty(); } public final void setXManaCostPaid(final int xPaid, final String xColor) { int xCost = xPaid * cntX; cntX = 0; ManaCostShard shard; if (StringUtils.isEmpty(xColor)) { shard = ManaCostShard.COLORLESS; } else { shard = ManaCostShard.valueOf(MagicColor.fromName(xColor)); } increaseShard(shard, xCost, true); } public final void increaseColorlessMana(final int toAdd) { increaseShard(ManaCostShard.COLORLESS, toAdd, false); } public final void increaseShard(final ManaCostShard shard, final int toAdd) { increaseShard(shard, toAdd, false); } private final void increaseShard(final ManaCostShard shard, final int toAdd, final boolean forX) { if (toAdd <= 0) { return; } ShardCount sc = unpaidShards.get(shard); if (sc == null) { sc = new ShardCount(); unpaidShards.put(shard, sc); } if (forX) { sc.xCount += toAdd; } sc.totalCount += toAdd; } public final void decreaseColorlessMana(final int manaToSubtract) { decreaseShard(ManaCostShard.COLORLESS, manaToSubtract); } public final void decreaseShard(final ManaCostShard shard, final int manaToSubtract) { if (manaToSubtract <= 0) { return; } ShardCount sc = unpaidShards.get(shard); if (sc == null) { System.out.println("Tried to substract a " + shard.toString() + " shard that is not present in this ManaCostBeingPaid"); return; } if (manaToSubtract >= sc.totalCount) { sc.xCount = 0; sc.totalCount = 0; unpaidShards.remove(shard); return; } sc.totalCount -= manaToSubtract; if (sc.xCount > sc.totalCount) { sc.xCount = sc.totalCount; //only decrease xCount if it would otherwise be greater than totalCount } } public final int getColorlessManaAmount() { ShardCount sc = unpaidShards.get(ManaCostShard.COLORLESS); if (sc != null) { return sc.totalCount; } return 0; } /** * <p> * addMana. * </p> * * @param mana * a {@link java.lang.String} object. * @return a boolean. */ public final boolean ai_payMana(final String mana, final ManaPool pool) { final byte colorMask = MagicColor.fromName(mana); if (!this.isAnyPartPayableWith(colorMask, pool)) { //System.out.println("ManaCost : addMana() error, mana not needed - " + mana); return false; //throw new RuntimeException("ManaCost : addMana() error, mana not needed - " + mana); } Predicate<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() { @Override public boolean apply(ManaCostShard ms) { return pool.canPayForShardWithColor(ms, colorMask); } }; return tryPayMana(colorMask, Iterables.filter(unpaidShards.keySet(), predCanBePaid), pool.getPossibleColorUses(colorMask)) != null; } /** * <p> * addMana. * </p> * * @param mana * a {@link forge.game.mana.Mana} object. * @return a boolean. */ public final boolean payMana(final Mana mana, final ManaPool pool) { if (!this.isNeeded(mana, pool)) { 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, pool); } }; byte inColor = mana.getColor(); byte outColor = pool.getPossibleColorUses(inColor); return tryPayMana(inColor, Iterables.filter(unpaidShards.keySet(), predCanBePaid), outColor) != null; } public final ManaCostShard payManaViaConvoke(final byte color) { Predicate<ManaCostShard> predCanBePaid = new Predicate<ManaCostShard>() { @Override public boolean apply(ManaCostShard ms) { return ms.canBePaidWithManaOfColor(color); } }; return tryPayMana(color, Iterables.filter(unpaidShards.keySet(), predCanBePaid), (byte) 0xFF); } public ManaCostShard getShardToPayByPriority(Iterable<ManaCostShard> payableShards, byte possibleUses) { Set<ManaCostShard> choice = EnumSet.noneOf(ManaCostShard.class); int priority = Integer.MIN_VALUE; for (ManaCostShard toPay : payableShards) { // if m is a better to pay than choice int toPayPriority = getPayPriority(toPay, possibleUses); if (toPayPriority > priority) { priority = toPayPriority; choice.clear(); } if (toPayPriority == priority) { choice.add(toPay); } } if (choice.isEmpty()) { return null; } return Iterables.getFirst(choice, null); } private ManaCostShard tryPayMana(final byte colorMask, Iterable<ManaCostShard> payableShards, byte possibleUses) { ManaCostShard chosenShard = getShardToPayByPriority(payableShards, possibleUses); if (chosenShard == null) { return null; } ShardCount sc = unpaidShards.get(chosenShard); if (sc != null && sc.xCount > 0) { //if there's any X part of the cost for the chosen shard, pay it off first and track what color was spent to pay X sc.xCount--; String color = MagicColor.toShortString(colorMask); if (xManaCostPaidByColor == null) { xManaCostPaidByColor = new HashMap<String, Integer>(); } Integer xColor = xManaCostPaidByColor.get(color); if (xColor == null) { xColor = 0; } xManaCostPaidByColor.put(color, xColor + 1); } decreaseShard(chosenShard, 1); if (chosenShard.isOr2Colorless() && (0 == (chosenShard.getColorMask() & possibleUses))) { this.increaseColorlessMana(1); } this.sunburstMap |= colorMask; return chosenShard; } private static int getPayPriority(final ManaCostShard bill, final byte paymentColor) { if (bill == ManaCostShard.COLORLESS) { return 0; } if (bill.isMonoColor()) { if (bill.isOr2Colorless()) { return !ColorSet.fromMask(bill.getColorMask() & paymentColor).isColorless() ? 9 : 4; } if (!bill.isPhyrexian()) { return 10; } return 8; } return 5; } private static boolean canBePaidWith(final ManaCostShard shard, final Mana mana, final ManaPool pool) { if (shard.isSnow() && !mana.isSnow()) { return false; } byte color = mana.getColor(); return pool.canPayForShardWithColor(shard, color); } public final void addManaCost(final ManaCost extra) { for (ManaCostShard shard : extra) { if (shard == ManaCostShard.X) { cntX++; } else { increaseShard(shard, 1, false); } } increaseColorlessMana(extra.getGenericCost()); } public final void subtractManaCost(final ManaCost subThisManaCost) { for (ManaCostShard shard : subThisManaCost) { if (shard == ManaCostShard.X) { cntX--; } else if (unpaidShards.containsKey(shard)) { decreaseShard(shard, 1); } else { decreaseColorlessMana(1); } } decreaseColorlessMana(subThisManaCost.getGenericCost()); } /** * To string. * * @param addX * the add x * @return the string */ public final String toString(final boolean addX) { // Boolean addX used to add Xs into the returned value final StringBuilder sb = new StringBuilder(); if (addX) { for (int i = 0; i < this.getXcounter(); i++) { sb.append("{X}"); } } int nGeneric = getColorlessManaAmount(); if (nGeneric > 0) { if (nGeneric <= 20) { sb.append("{" + nGeneric + "}"); } else { //if no mana symbol exists for colorless amount, use combination of symbols for each digit String genericStr = String.valueOf(nGeneric); for (int i = 0; i < genericStr.length(); i++) { sb.append("{" + genericStr.charAt(i) + "}"); } } } // Sort the keys to get a deterministic ordering. List<ManaCostShard> shards = new ArrayList<ManaCostShard>(unpaidShards.keySet()); Collections.sort(shards); for (ManaCostShard shard : shards) { if (shard == ManaCostShard.COLORLESS) { continue; } final String str = shard.toString(); final int count = unpaidShards.get(shard).totalCount; for (int i = 0; i < count; i++) { sb.append(str); } } return sb.length() == 0 ? "0" : sb.toString(); } /** {@inheritDoc} */ @Override public final String toString() { return this.toString(true); } /** * <p> * getConvertedManaCost. * </p> * * @return a int. */ public final int getConvertedManaCost() { int cmc = 0; for (final Entry<ManaCostShard, ShardCount> s : this.unpaidShards.entrySet()) { cmc += s.getKey().getCmc() * s.getValue().totalCount; } return cmc; } public ManaCost toManaCost() { return new ManaCost(new ManaCostBeingPaidIterator()); } public final int getXcounter() { return cntX; } public final List<ManaCostShard> getUnpaidShards() { List<ManaCostShard> result = new ArrayList<ManaCostShard>(); for (Entry<ManaCostShard, ShardCount> kv : unpaidShards.entrySet()) { for (int i = kv.getValue().totalCount; i > 0; i--) { result.add(kv.getKey()); } } for (int i = cntX; i > 0; i--) { result.add(ManaCostShard.X); } return result; } /** * <p> * removeColorlessMana. * </p> * * @since 1.0.15 */ public final void removeColorlessMana() { unpaidShards.remove(ManaCostShard.COLORLESS); } public String getSourceRestriction() { return sourceRestriction; } public Iterable<ManaCostShard> getDistinctShards() { return unpaidShards.keySet(); } public int getUnpaidShards(ManaCostShard key) { ShardCount sc = unpaidShards.get(key); if (sc != null) { return sc.totalCount; } return 0; } public final byte getUnpaidColors() { byte result = 0; for (ManaCostShard s : unpaidShards.keySet()) { result |= s.getColorMask(); } return result; } }