From 891bd78ef5c6821c3dde4ec1f6cdbfd5097e126b Mon Sep 17 00:00:00 2001 From: mcrawford620 Date: Wed, 20 Jul 2016 04:27:49 +0000 Subject: [PATCH] Fix bugs in deck building --- .../main/java/forge/limited/CardRanker.java | 34 ++++++---- .../forge/limited/LimitedDeckBuilder.java | 64 ++++++++++++------- .../java/forge/limited/SealedDeckBuilder.java | 9 ++- 3 files changed, 69 insertions(+), 38 deletions(-) diff --git a/forge-gui/src/main/java/forge/limited/CardRanker.java b/forge-gui/src/main/java/forge/limited/CardRanker.java index f01587cbe08..0a18fb1a902 100644 --- a/forge-gui/src/main/java/forge/limited/CardRanker.java +++ b/forge-gui/src/main/java/forge/limited/CardRanker.java @@ -1,6 +1,5 @@ package forge.limited; -import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import forge.card.*; @@ -16,17 +15,19 @@ public class CardRanker { private static final double SCORE_UNPICKABLE = -100.0; private static final Map typeFactors = ImmutableMap.builder() + .put(DeckHints.Type.ABILITY, 5) .put(DeckHints.Type.COLOR, 1) .put(DeckHints.Type.KEYWORD, 5) .put(DeckHints.Type.NAME, 20) .put(DeckHints.Type.TYPE, 5) .build(); + private static boolean logToConsole = false; /** * Rank cards. * * @param cards PaperCards to rank - * @return List of beans with card rankings + * @return List of ranked cards */ public List rankCardsInDeck(final Iterable cards) { List> cardScores = getScores(cards); @@ -34,6 +35,14 @@ public class CardRanker { 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 rankCardsInPack( final Iterable cardsInPack, final List deck, @@ -62,7 +71,7 @@ public class CardRanker { } List otherCards = getCardsExceptOne(cache, i); - score += calculateSynergies(card, otherCards); + score += getScoreForDeckHints(card, otherCards); cardScores.add(Pair.of(score, card)); } @@ -87,7 +96,7 @@ public class CardRanker { score -= 50.0; } - score += calculateSynergies(card, deck); + score += getScoreForDeckHints(card, deck); cardScores.add(Pair.of(score, card)); } @@ -130,14 +139,6 @@ public class CardRanker { return otherCards; } - private double calculateSynergies(PaperCard card, Iterable otherCards) { - double synergyScore = 0.0; - - synergyScore += getScoreForDeckHints(card, otherCards); - - return synergyScore; - } - private double getScoreForDeckHints(PaperCard card, Iterable otherCards) { double score = 0.0; final DeckHints hints = card.getRules().getAiHints().getDeckHints(); @@ -146,6 +147,9 @@ public class CardRanker { for (DeckHints.Type type : cardsByType.keySet()) { Iterable cards = cardsByType.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(); @@ -154,6 +158,9 @@ public class CardRanker { for (DeckHints.Type type : cardsByType.keySet()) { Iterable cards = cardsByType.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; @@ -165,6 +172,9 @@ public class CardRanker { List rankedCards = new ArrayList<>(cardScores.size()); for (Pair pair : cardScores) { rankedCards.add(pair.getValue()); + if (logToConsole) { + System.out.println(pair.getValue().getName() + "[" + pair.getKey().toString() + "]"); + } } return rankedCards; diff --git a/forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java index 5c8edac2714..0d586643f94 100644 --- a/forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java @@ -58,14 +58,15 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { private final List aiPlayables; private final List deckList = new ArrayList(); private final List setsWithBasicLands = new ArrayList(); + private List rankedColorList; - private CardRanker ranker = new CardRanker(); + protected CardRanker ranker = new CardRanker(); // Views for aiPlayable private Iterable onColorCreatures; private Iterable onColorNonCreatures; - private static final boolean logToConsole = false; + protected static final boolean logToConsole = true; /** * @@ -85,7 +86,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { // remove Unplayables final Iterable playables = Iterables.filter(availableList, 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); findBasicLandSets(); @@ -131,10 +132,10 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS); Iterable colorList = Iterables.filter(aiPlayables, Predicates.compose(hasColor, PaperCard.FN_GET_RULES)); - - onColorCreatures = Iterables.filter(colorList, + rankedColorList = ranker.rankCardsInDeck(colorList); + onColorCreatures = Iterables.filter(rankedColorList, 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)); // Guava iterables do not copy the collection contents, instead they act // as filters and iterate over _source_ collection each time. So even if @@ -146,25 +147,25 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { final List walkers = Lists.newArrayList(onColorWalkers); deckList.addAll(walkers); aiPlayables.removeAll(walkers); + rankedColorList.removeAll(walkers); if (walkers.size() > 0 && logToConsole) { System.out.println("Planeswalker: " + walkers.get(0).getName()); } // 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()); - // 5.If we couldn't get up to 22, try to fill up to 22 with on-color - // creature cards + // 5.If we couldn't get enough, try to fill up with on-color creature cards addCreatures(onColorCreatures, numSpellsNeeded - deckList.size()); // 6. If there are still on-color cards, and the average cmc is low, add - // a 23rd card. - if (deckList.size() == numSpellsNeeded && getAverageCMC(deckList) < 3) { - final Iterable nonLands = Iterables.filter(colorList, + // an extra. + if (deckList.size() == numSpellsNeeded && getAverageCMC(deckList) < 4) { + final Iterable nonLands = Iterables.filter(rankedColorList, Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); final PaperCard card = Iterables.getFirst(nonLands, null); if (card != null) { @@ -324,6 +325,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { * Add lands to fulfill the given color counts. * * @param clrCnts + * counts of lands needed, by color * @param landSetCode * 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); aiPlayables.removeAll(snowLands); } @@ -458,8 +468,11 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { if (num > 0) { final Iterable others = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); - List toAdd = new ArrayList<>(num); - for (final PaperCard card : others) { + // We haven't yet ranked the off-color cards. + // Compare them to the cards already in the deckList. + List rankedOthers = ranker.rankCardsInPack(others, deckList, colors, true); + List toAdd = new ArrayList<>(); + for (final PaperCard card : rankedOthers) { // Want a card that has just one "off" color. final ColorSet off = colors.getOffColors(card.getRules().getColor()); if (off.isMonoColor()) { @@ -498,7 +511,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { private void addRandomCards(int num) { final Iterable others = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); - List toAdd = new ArrayList<>(num); + List toAdd = new ArrayList<>(); for (final PaperCard card : others) { if (num > 0) { toAdd.add(card); @@ -513,6 +526,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { } deckList.addAll(toAdd); aiPlayables.removeAll(toAdd); + rankedColorList.removeAll(toAdd); } /** @@ -524,7 +538,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { * number to add */ private void addNonCreatures(final Iterable nonCreatures, int num) { - List toAdd = new ArrayList<>(num); + List toAdd = new ArrayList<>(); for (final PaperCard card : nonCreatures) { if (num > 0) { toAdd.add(card); @@ -539,6 +553,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { } deckList.addAll(toAdd); aiPlayables.removeAll(toAdd); + rankedColorList.removeAll(toAdd); } /** @@ -606,7 +621,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { * number to add */ private void addCreatures(final Iterable creatures, int num) { - List creaturesToAdd = new ArrayList<>(num); + List creaturesToAdd = new ArrayList<>(); for (final PaperCard card : creatures) { if (num > 0) { creaturesToAdd.add(card); @@ -620,6 +635,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { } deckList.addAll(creaturesToAdd); aiPlayables.removeAll(creaturesToAdd); + rankedColorList.removeAll(creaturesToAdd); } /** @@ -649,7 +665,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); } - List creaturesToAdd = new ArrayList<>(num); + List creaturesToAdd = new ArrayList<>(); for (final PaperCard card : creatures) { int cmc = card.getRules().getManaCost().getCMC(); if (cmc < 1) { @@ -661,15 +677,15 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { boolean willAddCreature = false; if (cmc <= 1 && currentAtCmc < 2) { willAddCreature = true; - } else if (cmc == 2 && currentAtCmc < 4) { + } else if (cmc == 2 && currentAtCmc < 6) { willAddCreature = true; - } else if (cmc == 3 && currentAtCmc < 6) { + } else if (cmc == 3 && currentAtCmc < 7) { willAddCreature = true; - } else if (cmc == 4 && currentAtCmc < 7) { + } else if (cmc == 4 && currentAtCmc < 4) { willAddCreature = true; } else if (cmc == 5 && currentAtCmc < 3) { willAddCreature = true; - } else if (cmc >= 6 && currentAtCmc < 3) { + } else if (cmc >= 6 && currentAtCmc < 2) { willAddCreature = true; } @@ -692,12 +708,14 @@ public class LimitedDeckBuilder extends DeckGeneratorBase { } deckList.addAll(creaturesToAdd); aiPlayables.removeAll(creaturesToAdd); + rankedColorList.removeAll(creaturesToAdd); } /** * Calculate average CMC. * * @param cards + * cards to choose from * @return the average */ private static double getAverageCMC(final List cards) { diff --git a/forge-gui/src/main/java/forge/limited/SealedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/SealedDeckBuilder.java index c25a397d87f..0903f1575f4 100644 --- a/forge-gui/src/main/java/forge/limited/SealedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/SealedDeckBuilder.java @@ -33,9 +33,10 @@ public class SealedDeckBuilder extends LimitedDeckBuilder { private ColorSet chooseColors() { // choose colors based on top 33% of cards final List colorChooserList = new ArrayList(); + List initialRanked = ranker.rankCardsInDeck(getAiPlayables()); double limit = getAiPlayables().size() * .33; for (int i = 0; i < limit; i++) { - PaperCard cp = getAiPlayables().get(i); + PaperCard cp = initialRanked.get(i); colorChooserList.add(cp); //System.out.println(cp.getName() + " " + cp.getRules().getManaCost().toString()); } @@ -80,8 +81,10 @@ public class SealedDeckBuilder extends LimitedDeckBuilder { } } - System.out.println("COLOR = " + color1); - System.out.println("COLOR = " + color2); + if (logToConsole) { + System.out.println("COLOR = " + color1); + System.out.println("COLOR = " + color2); + } return ColorSet.fromNames(color1, color2); } }