From 4e70925eac32faed16446232015d97a200845dc5 Mon Sep 17 00:00:00 2001 From: Maxmtg Date: Tue, 23 Apr 2013 22:20:55 +0000 Subject: [PATCH] Deck generation based on color profile, strictly follows profile cmc curve uses relative weights --- src/main/java/forge/Constant.java | 2 - .../deck/generate/Generate2ColorDeck.java | 33 ++++---- .../deck/generate/Generate3ColorDeck.java | 28 +++---- .../deck/generate/Generate5ColorDeck.java | 28 +++---- .../generate/GenerateColoredDeckBase.java | 83 ++++++++++++------- .../forge/deck/generate/GenerateDeckUtil.java | 5 +- .../deck/generate/GenerateMonoColorDeck.java | 20 +++-- 7 files changed, 114 insertions(+), 85 deletions(-) diff --git a/src/main/java/forge/Constant.java b/src/main/java/forge/Constant.java index 3c2c7b40238..50bad5332e6 100644 --- a/src/main/java/forge/Constant.java +++ b/src/main/java/forge/Constant.java @@ -26,8 +26,6 @@ import java.util.Map; import com.google.common.collect.ImmutableList; -import forge.card.MagicColor; - /** *

* Constant interface. diff --git a/src/main/java/forge/deck/generate/Generate2ColorDeck.java b/src/main/java/forge/deck/generate/Generate2ColorDeck.java index 1e71bb96eea..758a1be7931 100644 --- a/src/main/java/forge/deck/generate/Generate2ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate2ColorDeck.java @@ -17,9 +17,11 @@ */ package forge.deck.generate; -import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; +import com.google.common.collect.Lists; + import forge.card.ColorSet; import forge.card.MagicColor; import forge.deck.generate.GenerateDeckUtil.FilterCMC; @@ -40,12 +42,13 @@ public class Generate2ColorDeck extends GenerateColoredDeckBase { @Override protected final float getCreatPercentage() { return 0.36f; } @Override protected final float getSpellPercentage() { return 0.25f; } - final List cmcLevels = Arrays.asList( - new GenerateDeckUtil.FilterCMC(0, 2), - new GenerateDeckUtil.FilterCMC(3, 4), - new GenerateDeckUtil.FilterCMC(5, 6), - new GenerateDeckUtil.FilterCMC(7, 20)); - final int[] cmcAmounts = {10, 8, 6, 2}; + @SuppressWarnings("unchecked") + final List> cmcRelativeWeights = Lists.newArrayList( + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(0, 2), 6), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(3, 4), 4), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(5, 6), 2), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(7, 20), 1) + ); // mana curve of the card pool // 20x 0 - 2 @@ -77,15 +80,14 @@ public class Generate2ColorDeck extends GenerateColoredDeckBase { public final ItemPoolView getDeck(final int size, final PlayerType pt) { - addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); + addCreaturesAndSpells(size, cmcRelativeWeights, pt); // Add lands - int numLands = (int) (getLandsPercentage() * size); - - tmpDeck.append("numLands:").append(numLands).append("\n"); + int numLands = Math.round(size * getLandsPercentage()); + adjustDeckSize(size - numLands); + tmpDeck.append(String.format("Adjusted deck size to: %d, should add %d land(s)%n", size - numLands, numLands)); // Add dual lands - List duals = GenerateDeckUtil.getDualLandList(colors); for (String s : duals) { this.cardCounts.put(s, 0); @@ -96,10 +98,9 @@ public class Generate2ColorDeck extends GenerateColoredDeckBase { addBasicLand(numLands); tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - - adjustDeckSize(size); - tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - + + //System.out.println(tmpDeck.toString()); + return tDeck; } } diff --git a/src/main/java/forge/deck/generate/Generate3ColorDeck.java b/src/main/java/forge/deck/generate/Generate3ColorDeck.java index bc1e7c7aac6..9ac2e06c879 100644 --- a/src/main/java/forge/deck/generate/Generate3ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate3ColorDeck.java @@ -17,9 +17,12 @@ */ package forge.deck.generate; -import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import com.google.common.collect.Lists; + import forge.card.ColorSet; import forge.card.MagicColor; import forge.deck.generate.GenerateDeckUtil.FilterCMC; @@ -36,11 +39,12 @@ import forge.item.ItemPoolView; * @version $Id$ */ public class Generate3ColorDeck extends GenerateColoredDeckBase { - final List cmcLevels = Arrays.asList( - new GenerateDeckUtil.FilterCMC(0, 2), - new GenerateDeckUtil.FilterCMC(3, 5), - new GenerateDeckUtil.FilterCMC(6, 20)); - final int[] cmcAmounts = {12, 9, 3}; + @SuppressWarnings("unchecked") + final List> cmcLevels = Lists.newArrayList( + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(0, 2), 12), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(3, 5), 9), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(6, 20), 3) + ); /** *

@@ -76,28 +80,24 @@ public class Generate3ColorDeck extends GenerateColoredDeckBase { * @return a {@link forge.CardList} object. */ public final ItemPoolView getDeck(final int size, final PlayerType pt) { - addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); + addCreaturesAndSpells(size, cmcLevels, pt); // Add lands - int numLands = (int) (getLandsPercentage() * size); - + int numLands = Math.round(size * getLandsPercentage()); + adjustDeckSize(size - numLands); tmpDeck.append("numLands:").append(numLands).append("\n"); // Add dual lands - List duals = GenerateDeckUtil.getDualLandList(colors); for (String s : duals) { this.cardCounts.put(s, 0); } + int dblsAdded = addSomeStr((numLands / 4), duals); numLands -= dblsAdded; addBasicLand(numLands); tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - - adjustDeckSize(size); - tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - return tDeck; } } diff --git a/src/main/java/forge/deck/generate/Generate5ColorDeck.java b/src/main/java/forge/deck/generate/Generate5ColorDeck.java index 10f9475517e..852c966a692 100644 --- a/src/main/java/forge/deck/generate/Generate5ColorDeck.java +++ b/src/main/java/forge/deck/generate/Generate5ColorDeck.java @@ -17,9 +17,12 @@ */ package forge.deck.generate; -import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import com.google.common.collect.Lists; + import forge.card.ColorSet; import forge.deck.generate.GenerateDeckUtil.FilterCMC; import forge.game.player.PlayerType; @@ -35,11 +38,12 @@ import forge.item.ItemPoolView; * @version $Id$ */ public class Generate5ColorDeck extends GenerateColoredDeckBase { - final List cmcLevels = Arrays.asList( - new GenerateDeckUtil.FilterCMC(0, 2), - new GenerateDeckUtil.FilterCMC(3, 5), - new GenerateDeckUtil.FilterCMC(6, 20)); - final int[] cmcAmounts = {15, 10, 5}; + @SuppressWarnings("unchecked") + final List> cmcLevels = Lists.newArrayList( + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(0, 2), 3), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(3, 5), 2), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(6, 20), 1) + ); // resulting mana curve of the card pool // 30x 0 - 2 @@ -66,28 +70,24 @@ public class Generate5ColorDeck extends GenerateColoredDeckBase { * @return a {@link forge.CardList} object. */ public final ItemPoolView getDeck(final int size, final PlayerType pt) { - addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); + addCreaturesAndSpells(size, cmcLevels, pt); // Add lands - int numLands = (int) (getLandsPercentage() * size); - + int numLands = Math.round(size * getLandsPercentage()); + adjustDeckSize(size - numLands); tmpDeck.append("numLands:").append(numLands).append("\n"); // Add dual lands - List duals = GenerateDeckUtil.getDualLandList(colors); for (String s : duals) { this.cardCounts.put(s, 0); } + int dblsAdded = addSomeStr((numLands / 4), duals); numLands -= dblsAdded; addBasicLand(numLands); tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - - adjustDeckSize(size); - tmpDeck.append("DeckSize:").append(tDeck.countAll()).append("\n"); - return tDeck; } } diff --git a/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java b/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java index 138366f0b18..f01c7d3a939 100644 --- a/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java +++ b/src/main/java/forge/deck/generate/GenerateColoredDeckBase.java @@ -17,7 +17,6 @@ */ package forge.deck.generate; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,9 +24,12 @@ import java.util.Map.Entry; import java.util.Random; import java.util.TreeMap; +import org.apache.commons.lang3.tuple.ImmutablePair; + import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import forge.Constant; import forge.Singletons; @@ -73,20 +75,24 @@ public abstract class GenerateColoredDeckBase { tDeck = new ItemPool(CardPrinted.class); } - protected void addCreaturesAndSpells(int size, List cmcLevels, int[] cmcAmounts, PlayerType pt) { + protected void addCreaturesAndSpells(int size, List> cmcLevels, PlayerType pt) { + tmpDeck.append("Building deck of ").append(size).append("cards\n"); + final Iterable cards = selectCardsOfMatchingColorForPlayer(pt); // build subsets based on type final Iterable creatures = Iterables.filter(cards, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, CardPrinted.FN_GET_RULES)); - final int creatCnt = (int) (getCreatPercentage() * size); - tmpDeck.append("Creature Count:").append(creatCnt).append("\n"); - addCmcAdjusted(creatures, creatCnt, cmcLevels, cmcAmounts); + final int creatCnt = (int) Math.ceil(getCreatPercentage() * size); + tmpDeck.append("Creatures to add:").append(creatCnt).append("\n"); + addCmcAdjusted(creatures, creatCnt, cmcLevels); Predicate preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NONCREATURE_SPELL_FOR_GENERATOR, CardPrinted.FN_GET_RULES); final Iterable spells = Iterables.filter(cards, preSpells); - final int spellCnt = (int) (getSpellPercentage() * size); - tmpDeck.append("Spell Count:").append(spellCnt).append("\n"); - addCmcAdjusted(spells, spellCnt, cmcLevels, cmcAmounts); + final int spellCnt = (int) Math.ceil(getSpellPercentage() * size); + tmpDeck.append("Spells to add:").append(spellCnt).append("\n"); + addCmcAdjusted(spells, spellCnt, cmcLevels); + + tmpDeck.append(String.format("Current deck size: %d... should be %f%n", tDeck.countAll(), size * (getCreatPercentage() + getSpellPercentage()))); } public ItemPoolView getDeck(final int size, final PlayerType pt) { @@ -97,19 +103,22 @@ public abstract class GenerateColoredDeckBase { for (int i = 0; i < cnt; i++) { CardPrinted cp; int lc = 0; + int srcLen = source.size(); do { - cp = source.get(this.r.nextInt(source.size())); + cp = source.get(this.r.nextInt(srcLen)); lc++; - } while ((this.cardCounts.get(cp.getName()) > (this.maxDuplicates - 1)) && (lc <= 100)); + } while (this.cardCounts.get(cp.getName()) > this.maxDuplicates - 1 && lc <= 100); if (lc > 100) { throw new RuntimeException("Generate2ColorDeck : get2ColorDeck -- looped too much -- Cr12"); } - tDeck.add(CardDb.instance().getCard(cp.getName(), Aggregates.random(cp.getRules().getSets()))); + tDeck.add(cp); final int n = this.cardCounts.get(cp.getName()); this.cardCounts.put(cp.getName(), n + 1); - tmpDeck.append(cp.getName() + " " + cp.getRules().getManaCost() + "\n"); + if( n + 1 == this.maxDuplicates ) + source.remove(cp); + tmpDeck.append(String.format("(%d) %s [%s]%n", cp.getRules().getManaCost().getCMC(), cp.getName(), cp.getRules().getManaCost())); } } @@ -136,6 +145,8 @@ public abstract class GenerateColoredDeckBase { } protected void addBasicLand(int cnt) { + tmpDeck.append(cnt).append(" basic lands remain").append("\n"); + // attempt to optimize basic land counts according to colors of picked cards final Map clrCnts = countLands(tDeck); // total of all ClrCnts @@ -147,24 +158,23 @@ public abstract class GenerateColoredDeckBase { tmpDeck.append("totalColor:").append(totalColor).append("\n"); + int landsLeft = cnt; for (Entry c : clrCnts.entrySet()) { String color = c.getKey(); // calculate number of lands for each color - float p = (float) c.getValue() / totalColor; - final int nLand = (int) (cnt * p); + final int nLand = Math.min(landsLeft, Math.round(cnt * c.getValue() / totalColor)); tmpDeck.append("nLand-").append(color).append(":").append(nLand).append("\n"); - // just to prevent a null exception by the deck size fixing - // code + // just to prevent a null exception by the deck size fixing code this.cardCounts.put(color, nLand); CardPrinted cp = CardDb.instance().getCard(color); String basicLandSet = Aggregates.random(cp.getRules().getSets()); - for (int j = 0; j <= nLand; j++) { - tDeck.add(CardDb.instance().getCard(cp.getName(), basicLandSet)); - } + + tDeck.add(CardDb.instance().getCard(cp.getName(), basicLandSet), nLand); + landsLeft -= nLand; } } @@ -191,19 +201,34 @@ public abstract class GenerateColoredDeckBase { } } - protected void addCmcAdjusted(Iterable source, int cnt, List cmcLevels, int[] cmcAmounts) { - final List curved = new ArrayList(); - - for (int i = 0; i < cmcAmounts.length; i++) { - Iterable matchingCards = Iterables.filter(source, Predicates.compose(cmcLevels.get(i), CardPrinted.FN_GET_RULES)); - curved.addAll(Aggregates.random(matchingCards, cmcAmounts[i])); + protected void addCmcAdjusted(Iterable source, int cnt, List> cmcLevels) { + int totalWeight = 0; + for (ImmutablePair pair : cmcLevels) { + totalWeight += pair.getRight(); } + + float variability = 0.6f; // if set to 1, you'll get minimum cards to choose from + float desiredWeight = (float)cnt / ( maxDuplicates * variability ); + float desiredOverTotal = desiredWeight / totalWeight; + float requestedOverTotal = (float)cnt / totalWeight; + + for (ImmutablePair pair : cmcLevels) { + Iterable matchingCards = Iterables.filter(source, Predicates.compose(pair.getLeft(), CardPrinted.FN_GET_RULES)); + int cmcCountForPool = (int) Math.ceil(pair.getRight().intValue() * desiredOverTotal); + + int addOfThisCmc = Math.round(pair.getRight().intValue() * requestedOverTotal); + tmpDeck.append(String.format("Adding %d cards for cmc range from a pool with %d cards:%n", addOfThisCmc, cmcCountForPool)); - for (CardPrinted c : curved) { - this.cardCounts.put(c.getName(), 0); + final List curved = Aggregates.random(matchingCards, cmcCountForPool); + final List curvedRandomized = Lists.newArrayList(); + for (CardPrinted c : curved) { + this.cardCounts.put(c.getName(), 0); + CardPrinted cpRandomSet = CardDb.instance().getCard(c.getName(), Aggregates.random(c.getRules().getSets())); + curvedRandomized.add(cpRandomSet); + } + + addSome(addOfThisCmc, curvedRandomized); } - - addSome(cnt, curved); } protected Iterable selectCardsOfMatchingColorForPlayer(PlayerType pt) { diff --git a/src/main/java/forge/deck/generate/GenerateDeckUtil.java b/src/main/java/forge/deck/generate/GenerateDeckUtil.java index 92df4b962dc..5ec67aa3194 100644 --- a/src/main/java/forge/deck/generate/GenerateDeckUtil.java +++ b/src/main/java/forge/deck/generate/GenerateDeckUtil.java @@ -58,7 +58,7 @@ public class GenerateDeckUtil { @Override public boolean apply(CardRules c) { ManaCost mc = c.getManaCost(); - return mc.getColorProfile() == 0 && !mc.isNoCost(); + return c.getColorIdentity().isColorless() && !mc.isNoCost(); } }; @@ -72,7 +72,8 @@ public class GenerateDeckUtil { @Override public boolean apply(CardRules subject) { ManaCost mc = subject.getManaCost(); - return !mc.isPureGeneric() && mc.canBePaidWithAvaliable(allowedColor); + return !mc.isPureGeneric() && allowedColor.containsAllColorsFrom(subject.getColorIdentity().getColor()); + //return mc.canBePaidWithAvaliable(allowedColor); // return allowedColor.containsAllColorsFrom(mc.getColorProfile()); } } diff --git a/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java b/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java index 6de68421a63..b395860357d 100644 --- a/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java +++ b/src/main/java/forge/deck/generate/GenerateMonoColorDeck.java @@ -17,9 +17,12 @@ */ package forge.deck.generate; -import java.util.Arrays; import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import com.google.common.collect.Lists; + import forge.card.ColorSet; import forge.card.MagicColor; import forge.deck.generate.GenerateDeckUtil.FilterCMC; @@ -40,12 +43,13 @@ public class GenerateMonoColorDeck extends GenerateColoredDeckBase { @Override protected final float getCreatPercentage() { return 0.36f; } @Override protected final float getSpellPercentage() { return 0.25f; } - final List cmcLevels = Arrays.asList( - new GenerateDeckUtil.FilterCMC(0, 2), - new GenerateDeckUtil.FilterCMC(3, 4), - new GenerateDeckUtil.FilterCMC(5, 6), - new GenerateDeckUtil.FilterCMC(7, 20)); - final int[] cmcAmounts = {10, 8, 5, 3}; + @SuppressWarnings("unchecked") + final List> cmcLevels = Lists.newArrayList( + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(0, 2), 10), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(3, 4), 8), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(5, 6), 5), + ImmutablePair.of(new GenerateDeckUtil.FilterCMC(7, 20), 3) + ); // mana curve of the card pool // 20x 0 - 2 @@ -76,7 +80,7 @@ public class GenerateMonoColorDeck extends GenerateColoredDeckBase { public final ItemPoolView getDeck(final int size, final PlayerType pt) { - addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt); + addCreaturesAndSpells(size, cmcLevels, pt); // Add lands int numLands = (int) (getLandsPercentage() * size);