Fix bugs in deck building

This commit is contained in:
mcrawford620
2016-07-20 04:27:49 +00:00
parent 394bb5b634
commit 891bd78ef5
3 changed files with 69 additions and 38 deletions

View File

@@ -1,6 +1,5 @@
package forge.limited; package forge.limited;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.card.*; import forge.card.*;
@@ -16,17 +15,19 @@ public class CardRanker {
private static final double SCORE_UNPICKABLE = -100.0; private static final double SCORE_UNPICKABLE = -100.0;
private static final Map<DeckHints.Type, Integer> typeFactors = ImmutableMap.<DeckHints.Type, Integer>builder() private static final Map<DeckHints.Type, Integer> typeFactors = ImmutableMap.<DeckHints.Type, Integer>builder()
.put(DeckHints.Type.ABILITY, 5)
.put(DeckHints.Type.COLOR, 1) .put(DeckHints.Type.COLOR, 1)
.put(DeckHints.Type.KEYWORD, 5) .put(DeckHints.Type.KEYWORD, 5)
.put(DeckHints.Type.NAME, 20) .put(DeckHints.Type.NAME, 20)
.put(DeckHints.Type.TYPE, 5) .put(DeckHints.Type.TYPE, 5)
.build(); .build();
private static boolean logToConsole = false;
/** /**
* Rank cards. * Rank cards.
* *
* @param cards PaperCards to rank * @param cards PaperCards to rank
* @return List of beans with card rankings * @return List of ranked cards
*/ */
public List<PaperCard> rankCardsInDeck(final Iterable<PaperCard> cards) { public List<PaperCard> rankCardsInDeck(final Iterable<PaperCard> cards) {
List<Pair<Double, PaperCard>> cardScores = getScores(cards); List<Pair<Double, PaperCard>> cardScores = getScores(cards);
@@ -34,6 +35,14 @@ public class CardRanker {
return sortAndCreateList(cardScores); return sortAndCreateList(cardScores);
} }
/**
* Rank cards in pack comparing to existing cards in deck.
* @param cardsInPack PaperCards to rank
* @param deck existing deck
* @param chosenColors colors of deck
* @param canAddMoreColors can deck add more colors
* @return sorted List of ranked cards
*/
public List<PaperCard> rankCardsInPack( public List<PaperCard> rankCardsInPack(
final Iterable<PaperCard> cardsInPack, final Iterable<PaperCard> cardsInPack,
final List<PaperCard> deck, final List<PaperCard> deck,
@@ -62,7 +71,7 @@ public class CardRanker {
} }
List<PaperCard> otherCards = getCardsExceptOne(cache, i); List<PaperCard> otherCards = getCardsExceptOne(cache, i);
score += calculateSynergies(card, otherCards); score += getScoreForDeckHints(card, otherCards);
cardScores.add(Pair.of(score, card)); cardScores.add(Pair.of(score, card));
} }
@@ -87,7 +96,7 @@ public class CardRanker {
score -= 50.0; score -= 50.0;
} }
score += calculateSynergies(card, deck); score += getScoreForDeckHints(card, deck);
cardScores.add(Pair.of(score, card)); cardScores.add(Pair.of(score, card));
} }
@@ -130,14 +139,6 @@ public class CardRanker {
return otherCards; return otherCards;
} }
private double calculateSynergies(PaperCard card, Iterable<PaperCard> otherCards) {
double synergyScore = 0.0;
synergyScore += getScoreForDeckHints(card, otherCards);
return synergyScore;
}
private double getScoreForDeckHints(PaperCard card, Iterable<PaperCard> otherCards) { private double getScoreForDeckHints(PaperCard card, Iterable<PaperCard> otherCards) {
double score = 0.0; double score = 0.0;
final DeckHints hints = card.getRules().getAiHints().getDeckHints(); final DeckHints hints = card.getRules().getAiHints().getDeckHints();
@@ -146,6 +147,9 @@ public class CardRanker {
for (DeckHints.Type type : cardsByType.keySet()) { for (DeckHints.Type type : cardsByType.keySet()) {
Iterable<PaperCard> cards = cardsByType.get(type); Iterable<PaperCard> cards = cardsByType.get(type);
score += Iterables.size(cards) * typeFactors.get(type); score += Iterables.size(cards) * typeFactors.get(type);
if (logToConsole && Iterables.size(cards) > 0) {
System.out.println(" -- Found " + Iterables.size(cards) + " cards for " + type);
}
} }
} }
final DeckHints needs = card.getRules().getAiHints().getDeckNeeds(); final DeckHints needs = card.getRules().getAiHints().getDeckNeeds();
@@ -154,6 +158,9 @@ public class CardRanker {
for (DeckHints.Type type : cardsByType.keySet()) { for (DeckHints.Type type : cardsByType.keySet()) {
Iterable<PaperCard> cards = cardsByType.get(type); Iterable<PaperCard> cards = cardsByType.get(type);
score += Iterables.size(cards) * typeFactors.get(type); score += Iterables.size(cards) * typeFactors.get(type);
if (logToConsole && Iterables.size(cards) > 0) {
System.out.println(" -- Found " + Iterables.size(cards) + " cards for " + type);
}
} }
} }
return score; return score;
@@ -165,6 +172,9 @@ public class CardRanker {
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size()); List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
for (Pair<Double, PaperCard> pair : cardScores) { for (Pair<Double, PaperCard> pair : cardScores) {
rankedCards.add(pair.getValue()); rankedCards.add(pair.getValue());
if (logToConsole) {
System.out.println(pair.getValue().getName() + "[" + pair.getKey().toString() + "]");
}
} }
return rankedCards; return rankedCards;

View File

@@ -58,14 +58,15 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
private final List<PaperCard> aiPlayables; private final List<PaperCard> aiPlayables;
private final List<PaperCard> deckList = new ArrayList<PaperCard>(); private final List<PaperCard> deckList = new ArrayList<PaperCard>();
private final List<String> setsWithBasicLands = new ArrayList<String>(); private final List<String> setsWithBasicLands = new ArrayList<String>();
private List<PaperCard> rankedColorList;
private CardRanker ranker = new CardRanker(); protected CardRanker ranker = new CardRanker();
// Views for aiPlayable // Views for aiPlayable
private Iterable<PaperCard> onColorCreatures; private Iterable<PaperCard> onColorCreatures;
private Iterable<PaperCard> onColorNonCreatures; private Iterable<PaperCard> onColorNonCreatures;
private static final boolean logToConsole = false; protected static final boolean logToConsole = true;
/** /**
* *
@@ -85,7 +86,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
// remove Unplayables // remove Unplayables
final Iterable<PaperCard> playables = Iterables.filter(availableList, final Iterable<PaperCard> playables = Iterables.filter(availableList,
Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES));
this.aiPlayables = this.ranker.rankCardsInDeck(playables); this.aiPlayables = Lists.newArrayList(playables);
this.availableList.removeAll(aiPlayables); this.availableList.removeAll(aiPlayables);
findBasicLandSets(); findBasicLandSets();
@@ -131,10 +132,10 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS); hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS);
Iterable<PaperCard> colorList = Iterables.filter(aiPlayables, Iterable<PaperCard> colorList = Iterables.filter(aiPlayables,
Predicates.compose(hasColor, PaperCard.FN_GET_RULES)); Predicates.compose(hasColor, PaperCard.FN_GET_RULES));
rankedColorList = ranker.rankCardsInDeck(colorList);
onColorCreatures = Iterables.filter(colorList, onColorCreatures = Iterables.filter(rankedColorList,
Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES));
onColorNonCreatures = Iterables.filter(colorList, onColorNonCreatures = Iterables.filter(rankedColorList,
Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES));
// Guava iterables do not copy the collection contents, instead they act // Guava iterables do not copy the collection contents, instead they act
// as filters and iterate over _source_ collection each time. So even if // as filters and iterate over _source_ collection each time. So even if
@@ -146,25 +147,25 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
final List<PaperCard> walkers = Lists.newArrayList(onColorWalkers); final List<PaperCard> walkers = Lists.newArrayList(onColorWalkers);
deckList.addAll(walkers); deckList.addAll(walkers);
aiPlayables.removeAll(walkers); aiPlayables.removeAll(walkers);
rankedColorList.removeAll(walkers);
if (walkers.size() > 0 && logToConsole) { if (walkers.size() > 0 && logToConsole) {
System.out.println("Planeswalker: " + walkers.get(0).getName()); System.out.println("Planeswalker: " + walkers.get(0).getName());
} }
// 3. Add creatures, trying to follow mana curve // 3. Add creatures, trying to follow mana curve
addManaCurveCreatures(onColorCreatures, 15); addManaCurveCreatures(onColorCreatures, 16);
// 4.Try to fill up to 22 with on-color non-creature cards // 4.Try to fill up to num needed with on-color non-creature cards
addNonCreatures(onColorNonCreatures, numSpellsNeeded - deckList.size()); addNonCreatures(onColorNonCreatures, numSpellsNeeded - deckList.size());
// 5.If we couldn't get up to 22, try to fill up to 22 with on-color // 5.If we couldn't get enough, try to fill up with on-color creature cards
// creature cards
addCreatures(onColorCreatures, numSpellsNeeded - deckList.size()); addCreatures(onColorCreatures, numSpellsNeeded - deckList.size());
// 6. If there are still on-color cards, and the average cmc is low, add // 6. If there are still on-color cards, and the average cmc is low, add
// a 23rd card. // an extra.
if (deckList.size() == numSpellsNeeded && getAverageCMC(deckList) < 3) { if (deckList.size() == numSpellsNeeded && getAverageCMC(deckList) < 4) {
final Iterable<PaperCard> nonLands = Iterables.filter(colorList, final Iterable<PaperCard> nonLands = Iterables.filter(rankedColorList,
Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES));
final PaperCard card = Iterables.getFirst(nonLands, null); final PaperCard card = Iterables.getFirst(nonLands, null);
if (card != null) { if (card != null) {
@@ -324,6 +325,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
* Add lands to fulfill the given color counts. * Add lands to fulfill the given color counts.
* *
* @param clrCnts * @param clrCnts
* counts of lands needed, by color
* @param landSetCode * @param landSetCode
* the set to take basic lands from (pass 'null' for random). * the set to take basic lands from (pass 'null' for random).
*/ */
@@ -366,6 +368,14 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
} }
} }
// A common problem at this point is that nLand in the above loop was exactly 1/2,
// and it rounds up for both colors, and too many lands were added.
// If the deck size is > 40, remove the last land added.
// Otherwise, the fixDeckSize() method would remove random cards.
while (deckList.size() > 40) {
deckList.remove(deckList.size() - 1);
}
deckList.addAll(snowLands); deckList.addAll(snowLands);
aiPlayables.removeAll(snowLands); aiPlayables.removeAll(snowLands);
} }
@@ -458,8 +468,11 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
if (num > 0) { if (num > 0) {
final Iterable<PaperCard> others = Iterables.filter(aiPlayables, final Iterable<PaperCard> others = Iterables.filter(aiPlayables,
Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES));
List<PaperCard> toAdd = new ArrayList<>(num); // We haven't yet ranked the off-color cards.
for (final PaperCard card : others) { // Compare them to the cards already in the deckList.
List<PaperCard> rankedOthers = ranker.rankCardsInPack(others, deckList, colors, true);
List<PaperCard> toAdd = new ArrayList<>();
for (final PaperCard card : rankedOthers) {
// Want a card that has just one "off" color. // Want a card that has just one "off" color.
final ColorSet off = colors.getOffColors(card.getRules().getColor()); final ColorSet off = colors.getOffColors(card.getRules().getColor());
if (off.isMonoColor()) { if (off.isMonoColor()) {
@@ -498,7 +511,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
private void addRandomCards(int num) { private void addRandomCards(int num) {
final Iterable<PaperCard> others = Iterables.filter(aiPlayables, final Iterable<PaperCard> others = Iterables.filter(aiPlayables,
Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES));
List <PaperCard> toAdd = new ArrayList<>(num); List <PaperCard> toAdd = new ArrayList<>();
for (final PaperCard card : others) { for (final PaperCard card : others) {
if (num > 0) { if (num > 0) {
toAdd.add(card); toAdd.add(card);
@@ -513,6 +526,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
} }
deckList.addAll(toAdd); deckList.addAll(toAdd);
aiPlayables.removeAll(toAdd); aiPlayables.removeAll(toAdd);
rankedColorList.removeAll(toAdd);
} }
/** /**
@@ -524,7 +538,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
* number to add * number to add
*/ */
private void addNonCreatures(final Iterable<PaperCard> nonCreatures, int num) { private void addNonCreatures(final Iterable<PaperCard> nonCreatures, int num) {
List<PaperCard> toAdd = new ArrayList<>(num); List<PaperCard> toAdd = new ArrayList<>();
for (final PaperCard card : nonCreatures) { for (final PaperCard card : nonCreatures) {
if (num > 0) { if (num > 0) {
toAdd.add(card); toAdd.add(card);
@@ -539,6 +553,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
} }
deckList.addAll(toAdd); deckList.addAll(toAdd);
aiPlayables.removeAll(toAdd); aiPlayables.removeAll(toAdd);
rankedColorList.removeAll(toAdd);
} }
/** /**
@@ -606,7 +621,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
* number to add * number to add
*/ */
private void addCreatures(final Iterable<PaperCard> creatures, int num) { private void addCreatures(final Iterable<PaperCard> creatures, int num) {
List<PaperCard> creaturesToAdd = new ArrayList<>(num); List<PaperCard> creaturesToAdd = new ArrayList<>();
for (final PaperCard card : creatures) { for (final PaperCard card : creatures) {
if (num > 0) { if (num > 0) {
creaturesToAdd.add(card); creaturesToAdd.add(card);
@@ -620,6 +635,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
} }
deckList.addAll(creaturesToAdd); deckList.addAll(creaturesToAdd);
aiPlayables.removeAll(creaturesToAdd); aiPlayables.removeAll(creaturesToAdd);
rankedColorList.removeAll(creaturesToAdd);
} }
/** /**
@@ -649,7 +665,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); creatureCosts.put(cmc, creatureCosts.get(cmc) + 1);
} }
List<PaperCard> creaturesToAdd = new ArrayList<>(num); List<PaperCard> creaturesToAdd = new ArrayList<>();
for (final PaperCard card : creatures) { for (final PaperCard card : creatures) {
int cmc = card.getRules().getManaCost().getCMC(); int cmc = card.getRules().getManaCost().getCMC();
if (cmc < 1) { if (cmc < 1) {
@@ -661,15 +677,15 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
boolean willAddCreature = false; boolean willAddCreature = false;
if (cmc <= 1 && currentAtCmc < 2) { if (cmc <= 1 && currentAtCmc < 2) {
willAddCreature = true; willAddCreature = true;
} else if (cmc == 2 && currentAtCmc < 4) { } else if (cmc == 2 && currentAtCmc < 6) {
willAddCreature = true; willAddCreature = true;
} else if (cmc == 3 && currentAtCmc < 6) { } else if (cmc == 3 && currentAtCmc < 7) {
willAddCreature = true; willAddCreature = true;
} else if (cmc == 4 && currentAtCmc < 7) { } else if (cmc == 4 && currentAtCmc < 4) {
willAddCreature = true; willAddCreature = true;
} else if (cmc == 5 && currentAtCmc < 3) { } else if (cmc == 5 && currentAtCmc < 3) {
willAddCreature = true; willAddCreature = true;
} else if (cmc >= 6 && currentAtCmc < 3) { } else if (cmc >= 6 && currentAtCmc < 2) {
willAddCreature = true; willAddCreature = true;
} }
@@ -692,12 +708,14 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
} }
deckList.addAll(creaturesToAdd); deckList.addAll(creaturesToAdd);
aiPlayables.removeAll(creaturesToAdd); aiPlayables.removeAll(creaturesToAdd);
rankedColorList.removeAll(creaturesToAdd);
} }
/** /**
* Calculate average CMC. * Calculate average CMC.
* *
* @param cards * @param cards
* cards to choose from
* @return the average * @return the average
*/ */
private static double getAverageCMC(final List<PaperCard> cards) { private static double getAverageCMC(final List<PaperCard> cards) {

View File

@@ -33,9 +33,10 @@ public class SealedDeckBuilder extends LimitedDeckBuilder {
private ColorSet chooseColors() { private ColorSet chooseColors() {
// choose colors based on top 33% of cards // choose colors based on top 33% of cards
final List<PaperCard> colorChooserList = new ArrayList<PaperCard>(); final List<PaperCard> colorChooserList = new ArrayList<PaperCard>();
List<PaperCard> initialRanked = ranker.rankCardsInDeck(getAiPlayables());
double limit = getAiPlayables().size() * .33; double limit = getAiPlayables().size() * .33;
for (int i = 0; i < limit; i++) { for (int i = 0; i < limit; i++) {
PaperCard cp = getAiPlayables().get(i); PaperCard cp = initialRanked.get(i);
colorChooserList.add(cp); colorChooserList.add(cp);
//System.out.println(cp.getName() + " " + cp.getRules().getManaCost().toString()); //System.out.println(cp.getName() + " " + cp.getRules().getManaCost().toString());
} }
@@ -80,8 +81,10 @@ public class SealedDeckBuilder extends LimitedDeckBuilder {
} }
} }
if (logToConsole) {
System.out.println("COLOR = " + color1); System.out.println("COLOR = " + color1);
System.out.println("COLOR = " + color2); System.out.println("COLOR = " + color2);
}
return ColorSet.fromNames(color1, color2); return ColorSet.fromNames(color1, color2);
} }
} }