mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Deck generation based on color profile, strictly follows profile
cmc curve uses relative weights
This commit is contained in:
@@ -26,8 +26,6 @@ import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import forge.card.MagicColor;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constant interface.
|
||||
|
||||
@@ -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<FilterCMC> 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<ImmutablePair<FilterCMC, Integer>> 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<CardPrinted> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FilterCMC> 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<ImmutablePair<FilterCMC, Integer>> 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)
|
||||
);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -76,28 +80,24 @@ public class Generate3ColorDeck extends GenerateColoredDeckBase {
|
||||
* @return a {@link forge.CardList} object.
|
||||
*/
|
||||
public final ItemPoolView<CardPrinted> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FilterCMC> 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<ImmutablePair<FilterCMC, Integer>> 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<CardPrinted> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>(CardPrinted.class);
|
||||
}
|
||||
|
||||
protected void addCreaturesAndSpells(int size, List<FilterCMC> cmcLevels, int[] cmcAmounts, PlayerType pt) {
|
||||
protected void addCreaturesAndSpells(int size, List<ImmutablePair<FilterCMC, Integer>> cmcLevels, PlayerType pt) {
|
||||
tmpDeck.append("Building deck of ").append(size).append("cards\n");
|
||||
|
||||
final Iterable<CardPrinted> cards = selectCardsOfMatchingColorForPlayer(pt);
|
||||
// build subsets based on type
|
||||
|
||||
final Iterable<CardPrinted> 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<CardPrinted> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NONCREATURE_SPELL_FOR_GENERATOR, CardPrinted.FN_GET_RULES);
|
||||
final Iterable<CardPrinted> 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<CardPrinted> 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<String, Integer> 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<String, Integer> 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<CardPrinted> source, int cnt, List<FilterCMC> cmcLevels, int[] cmcAmounts) {
|
||||
final List<CardPrinted> curved = new ArrayList<CardPrinted>();
|
||||
|
||||
for (int i = 0; i < cmcAmounts.length; i++) {
|
||||
Iterable<CardPrinted> matchingCards = Iterables.filter(source, Predicates.compose(cmcLevels.get(i), CardPrinted.FN_GET_RULES));
|
||||
curved.addAll(Aggregates.random(matchingCards, cmcAmounts[i]));
|
||||
protected void addCmcAdjusted(Iterable<CardPrinted> source, int cnt, List<ImmutablePair<FilterCMC, Integer>> cmcLevels) {
|
||||
int totalWeight = 0;
|
||||
for (ImmutablePair<FilterCMC, Integer> 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<FilterCMC, Integer> pair : cmcLevels) {
|
||||
Iterable<CardPrinted> 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<CardPrinted> curved = Aggregates.random(matchingCards, cmcCountForPool);
|
||||
final List<CardPrinted> 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<CardPrinted> selectCardsOfMatchingColorForPlayer(PlayerType pt) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FilterCMC> 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<ImmutablePair<FilterCMC, Integer>> 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<CardPrinted> getDeck(final int size, final PlayerType pt) {
|
||||
addCreaturesAndSpells(size, cmcLevels, cmcAmounts, pt);
|
||||
addCreaturesAndSpells(size, cmcLevels, pt);
|
||||
|
||||
// Add lands
|
||||
int numLands = (int) (getLandsPercentage() * size);
|
||||
|
||||
Reference in New Issue
Block a user