package magic.model.choice;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import magic.model.MagicCostManaType;
import magic.model.MagicGame;
import magic.model.MagicManaType;
import magic.model.MagicPermanent;
import magic.model.MagicPlayer;
import magic.model.event.MagicSourceManaActivation;
public class MagicPayManaCostResultBuilder {
private final MagicGame game;
private final MagicBuilderManaCost cost;
private final List<MagicSourceManaActivation> activations;
private MagicCostManaType[] types;
private int[] amounts;
private int activationsSize;
private Map<MagicBuilderPayManaCostResult,MagicBuilderPayManaCostResult> results;
public MagicPayManaCostResultBuilder(final MagicGame aGame,final MagicPlayer aPlayer,final MagicBuilderManaCost aCost) {
game = aGame;
cost = aCost;
activations = aPlayer.getManaActivations(game);
activationsSize = activations.size();
types = cost.getTypes();
amounts = cost.getAmounts();
}
private boolean build(final int index,final boolean single) {
// base case: valid result is reached.
if (index == types.length) {
if (single) {
return true;
}
final MagicBuilderPayManaCostResult result = new MagicBuilderPayManaCostResult(activations);
final MagicBuilderPayManaCostResult currentResult = results.get(result);
// keep result with lowest weight
if (currentResult == null || currentResult.getWeight() > result.getWeight()) {
result.buildResults(activations, cost);
results.put(result, result);
}
return true;
}
assert types[index] != null : "types[index] is null, index=" + index;
// Generate all available activations for mana cost type.
final MagicCostManaType costManaType = types[index];
final MagicSourceManaActivation[] typeActivations = new MagicSourceManaActivation[activationsSize];
final MagicManaType[] producedTypes = new MagicManaType[activationsSize];
int typeActivationSize = 0;
for (final MagicSourceManaActivation activation : activations) {
final MagicManaType manaType = activation.canProduce(costManaType);
if (manaType.isValid()) {
typeActivations[typeActivationSize] = activation;
producedTypes[typeActivationSize] = manaType;
typeActivationSize++;
}
}
final int minAmount = amounts[index];
// Skip when the amount is 0 or no activations to pay
if (minAmount == 0 || (typeActivationSize == 0 && minAmount < 0)) {
return build(index+1, single);
}
// Check if there are enough activations for amount > 0.
if (minAmount > typeActivationSize) {
return false;
}
final boolean hasX = costManaType == MagicCostManaType.Generic && cost.hasX();
// Fast implementation when minimum amount is 1 without X.
if (minAmount == 1 && hasX == false) {
for (int i = 0; i < typeActivationSize; i++) {
final MagicSourceManaActivation typeActivation = typeActivations[i];
typeActivation.available = false;
typeActivation.manaType = producedTypes[i];
if (build(index + 1, single) && single) {
return true;
}
typeActivation.available = true;
}
return false;
}
// Fast implementation when minimum amount is equal to number of left activations.
if (minAmount == typeActivationSize) {
for (int i = 0; i < typeActivationSize; i++) {
final MagicSourceManaActivation typeActivation = typeActivations[i];
typeActivation.available = false;
typeActivation.manaType = producedTypes[i];
}
if (build(index + 1, single) && single) {
return true;
}
for (int i = 0; i < typeActivationSize; i++) {
typeActivations[i].available = true;
}
return false;
}
assert typeActivationSize > 0 : "no activations for " + costManaType + " with min " + minAmount + " and hasX " + hasX;
// Generate all possible combinations with at least the minimum number of sources.
final int[] option = new int[typeActivationSize];
int i = 0;
int count = 0;
option[0] = -1;
while (i >= 0) {
switch (++option[i]) {
case 0:
typeActivations[i].available = false;
typeActivations[i].manaType = producedTypes[i];
count++;
if (count >= minAmount && (hasX == false || cost.validX(count)) && build(index + 1, single) && single) {
return true;
}
if (count < minAmount || (hasX && i + 1 != typeActivationSize)) {
i++;
option[i] = -1;
}
break;
case 1:
typeActivations[i].available = true;
count--;
if (typeActivationSize - i + count > minAmount && i + 1 != typeActivationSize) {
i++;
option[i] = -1;
}
break;
case 2:
i--;
break;
}
}
return false;
}
public boolean hasResults() {
// Check if there are enough mana sources.
if (cost.getMinimumAmount() > activationsSize) {
return false;
}
return build(0,true);
}
/** Finds all possible options to pay the cost for AI. */
Collection<Object> getResults() {
for (final MagicSourceManaActivation activation : activations) {
activation.available = true;
}
results = new HashMap<MagicBuilderPayManaCostResult,MagicBuilderPayManaCostResult>();
build(0, false);
return new TreeSet<Object>(results.values());
}
/** Find all possible mana sources to pay one mana of given type for the player. */
Set<MagicPermanent> getManaSources(final MagicCostManaType type,final boolean all) {
cost.removeType(type,1);
cost.compress();
types = cost.getTypes();
amounts = cost.getAmounts();
final Set<MagicPermanent> manaSources = new HashSet<MagicPermanent>();
final Set<Integer> manaIds = new HashSet<Integer>();
for (final MagicSourceManaActivation currentActivation : activations) {
currentActivation.available = true;
if (currentActivation.canProduce(type).isValid()) {
for (final MagicSourceManaActivation activation : activations) {
activation.available = activation != currentActivation;
}
if (hasResults()) {
final MagicPermanent permanent = currentActivation.permanent;
if (all) {
manaSources.add(permanent);
} else {
final int manaId = permanent.getManaId();
if (manaIds.contains(manaId) == false) {
manaSources.add(permanent);
manaIds.add(manaId);
}
}
}
}
}
return manaSources;
}
void useManaSource(final MagicPermanent permanent,final MagicCostManaType type) {
// Use the mana activation.
final MagicSourceManaActivation sourceActivation = new MagicSourceManaActivation(game,permanent);
sourceActivation.produce(game,type);
// Remove permanent for available sources.
for (final Iterator<MagicSourceManaActivation> iterator = activations.iterator(); iterator.hasNext();) {
final MagicSourceManaActivation activation = iterator.next();
if (activation.permanent == permanent) {
iterator.remove();
break;
}
}
activationsSize = activations.size();
}
/** Works only for all the remaining generic mana. */
boolean useAllManaSources(final MagicCostManaType type) {
if (activationsSize > cost.getMinimumAmount() || type != MagicCostManaType.Generic) {
return false;
}
for (final MagicSourceManaActivation activation : activations) {
final MagicSourceManaActivation sourceActivation = new MagicSourceManaActivation(game, activation.permanent);
if (sourceActivation.available) {
sourceActivation.produce(game, MagicCostManaType.Generic);
}
}
return true;
}
public int getActivationsSize() {
return activationsSize;
}
}