GUI, deck: improved random deck generation:

- added commander decks support (close #5680);
- fixed and improved cards searching due selected colors (now it search by color identity instead mana cost);
This commit is contained in:
Oleg Agafonov 2025-05-24 00:18:24 +04:00
parent ab362abe10
commit 8649869ed4
5 changed files with 319 additions and 166 deletions

View file

@ -5,20 +5,21 @@ import mage.cards.decks.Deck;
import mage.cards.repository.CardCriteria; import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.ExpansionRepository; import mage.cards.repository.ExpansionRepository;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.sets.ConstructedFormats; import mage.client.util.sets.ConstructedFormats;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.ColoredManaSymbol; import mage.constants.ColoredManaSymbol;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.util.RandomUtil; import mage.util.RandomUtil;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/** /**
* Generates random card pool and builds a deck. * Generates random card pool and builds a deck.
* *
* @author nantuko * @author nantuko, Simown, JayDi85
* @author Simown
*/ */
public final class DeckGenerator { public final class DeckGenerator {
@ -124,7 +125,7 @@ public final class DeckGenerator {
genPool = new DeckGeneratorPool(deckSize, genDialog.getCreaturePercentage(), genDialog.getNonCreaturePercentage(), genPool = new DeckGeneratorPool(deckSize, genDialog.getCreaturePercentage(), genDialog.getNonCreaturePercentage(),
genDialog.getLandPercentage(), allowedColors, genDialog.isSingleton(), genDialog.isColorless(), genDialog.getLandPercentage(), allowedColors, genDialog.isSingleton(), genDialog.isColorless(),
genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC()); genDialog.isCommander(), genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC());
final String[] sets = setsToUse.toArray(new String[setsToUse.size()]); final String[] sets = setsToUse.toArray(new String[setsToUse.size()]);
@ -155,8 +156,8 @@ public final class DeckGenerator {
// Generate basic land cards // Generate basic land cards
Map<String, List<CardInfo>> basicLands = DeckGeneratorPool.generateBasicLands(setsToUse); Map<String, List<CardInfo>> basicLands = DeckGeneratorPool.generateBasicLands(setsToUse);
DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount()); DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount(), genPool.getCommandersCount());
DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount()); DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount(), 0);
DeckGeneratorPool.generateLands(genDialog.useNonBasicLand(), nonBasicLandCriteria, basicLands); DeckGeneratorPool.generateLands(genDialog.useNonBasicLand(), nonBasicLandCriteria, basicLands);
// Reconstructs the final deck and adjusts for Math rounding and/or missing cards // Reconstructs the final deck and adjusts for Math rounding and/or missing cards

View file

@ -1,46 +1,76 @@
package mage.client.deck.generator; package mage.client.deck.generator;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
/**
* Mana value distribution between cards in diff deck sizes
*/
public enum DeckGeneratorCMC { public enum DeckGeneratorCMC {
Low(ImmutableList.<CMC>builder() Low(
.add(new CMC(0, 2, 0.60f)) // 100
.add(new CMC(3, 4, 0.30f)) ImmutableList.<CMC>builder()
.add(new CMC(5, 6, 0.10f)).build(), .add(new CMC(0, 2, 0.55f))
.add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 6, 0.15f)).build(),
// 60
ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.60f))
.add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 6, 0.10f)).build(),
// 40
ImmutableList.<CMC>builder() ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.65f)) .add(new CMC(0, 2, 0.65f))
.add(new CMC(3, 4, 0.30f)) .add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 5, 0.05f)).build()), .add(new CMC(5, 5, 0.05f)).build()),
Default(ImmutableList.<CMC>builder() Default(
.add(new CMC(0, 2, 0.20f)) // 100
.add(new CMC(3, 5, 0.50f)) ImmutableList.<CMC>builder()
.add(new CMC(6, 7, 0.25f)) .add(new CMC(0, 2, 0.15f))
.add(new CMC(8, 100, 0.05f)).build(), .add(new CMC(3, 5, 0.50f))
.add(new CMC(6, 7, 0.30f))
.add(new CMC(8, 100, 0.05f)).build(),
// 60
ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.20f))
.add(new CMC(3, 5, 0.50f))
.add(new CMC(6, 7, 0.25f))
.add(new CMC(8, 100, 0.05f)).build(),
// 40
ImmutableList.<CMC>builder() ImmutableList.<CMC>builder()
.add(new CMC(0, 2, 0.30f)) .add(new CMC(0, 2, 0.30f))
.add(new CMC(3, 4, 0.45f)) .add(new CMC(3, 4, 0.45f))
.add(new CMC(5, 6, 0.20f)) .add(new CMC(5, 6, 0.20f))
.add(new CMC(7, 100, 0.05f)).build()), .add(new CMC(7, 100, 0.05f)).build()),
High(ImmutableList.<CMC>builder(). High(
add(new CMC(0, 2, 0.05f)) // 100
.add(new CMC(3, 5, 0.35f)) ImmutableList.<CMC>builder().
.add(new CMC(6, 7, 0.40f)) add(new CMC(0, 2, 0.05f))
.add(new CMC(8, 100, 0.15f)).build(), .add(new CMC(3, 5, 0.40f))
.add(new CMC(6, 7, 0.40f))
.add(new CMC(8, 100, 0.15f)).build(),
// 60
ImmutableList.<CMC>builder().
add(new CMC(0, 2, 0.05f))
.add(new CMC(3, 5, 0.35f))
.add(new CMC(6, 7, 0.40f))
.add(new CMC(8, 100, 0.15f)).build(),
// 40
ImmutableList.<CMC>builder(). ImmutableList.<CMC>builder().
add(new CMC(0, 2, 0.10f)) add(new CMC(0, 2, 0.10f))
.add(new CMC(3, 4, 0.30f)) .add(new CMC(3, 4, 0.30f))
.add(new CMC(5, 6, 0.45f)) .add(new CMC(5, 6, 0.45f))
.add(new CMC(7, 100, 0.15f)).build()); .add(new CMC(7, 100, 0.15f)).build());
private final List<CMC> poolCMCs100;
private final List<CMC> poolCMCs60; private final List<CMC> poolCMCs60;
private final List<CMC> poolCMCs40; private final List<CMC> poolCMCs40;
DeckGeneratorCMC(List<CMC> CMCs60, List<CMC> CMCs40) { DeckGeneratorCMC(List<CMC> CMCs100, List<CMC> CMCs60, List<CMC> CMCs40) {
this.poolCMCs100 = CMCs100;
this.poolCMCs60 = CMCs60; this.poolCMCs60 = CMCs60;
this.poolCMCs40 = CMCs40; this.poolCMCs40 = CMCs40;
} }
@ -53,6 +83,10 @@ public enum DeckGeneratorCMC {
return this.poolCMCs60; return this.poolCMCs60;
} }
public List<CMC> get100CardPoolCMC() {
return this.poolCMCs100;
}
static class CMC { static class CMC {
public final int min; public final int min;
public final int max; public final int max;

View file

@ -32,7 +32,7 @@ public class DeckGeneratorDialog {
private static String selectedColors; private static String selectedColors;
private static JComboBox cbSets, cbDeckSize, cbCMC; private static JComboBox cbSets, cbDeckSize, cbCMC;
private static JButton btnGenerate, btnCancel, btnReset; private static JButton btnGenerate, btnCancel, btnReset;
private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced; private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced, cCommander;
private static JLabel averageCMCLabel; private static JLabel averageCMCLabel;
private static SimpleDateFormat dateFormat; private static SimpleDateFormat dateFormat;
private static RatioAdjustingSliderPanel adjustingSliderPanel; private static RatioAdjustingSliderPanel adjustingSliderPanel;
@ -63,7 +63,7 @@ public class DeckGeneratorDialog {
c.insets = new Insets(5, 10, 0, 10); c.insets = new Insets(5, 10, 0, 10);
c.gridx = 1; c.gridx = 1;
c.gridy = 0; c.gridy = 0;
String chosen = MageFrame.getPreferences().get("genDeckColor", "u"); String chosen = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, "u");
final ColorsChooser colorsChooser = new ColorsChooser(chosen); final ColorsChooser colorsChooser = new ColorsChooser(chosen);
mainPanel.add(colorsChooser, c); mainPanel.add(colorsChooser, c);
@ -135,7 +135,7 @@ public class DeckGeneratorDialog {
c.ipadx = 30; c.ipadx = 30;
c.insets = new Insets(5, 10, 0, 10); c.insets = new Insets(5, 10, 0, 10);
c.weightx = 0.90; c.weightx = 0.90;
cbDeckSize = new JComboBox<>(new String[]{"40", "60"}); cbDeckSize = new JComboBox<>(new String[]{"40", "60", "100"});
cbDeckSize.setSelectedIndex(0); cbDeckSize.setSelectedIndex(0);
cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT); cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT);
mainPanel.add(cbDeckSize, c); mainPanel.add(cbDeckSize, c);
@ -148,31 +148,33 @@ public class DeckGeneratorDialog {
JPanel jCheckBoxes = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel jCheckBoxes = new JPanel(new FlowLayout(FlowLayout.LEFT));
// Singletons // Singletons
boolean commanderEnabled = Boolean.parseBoolean(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, "false"));
cSingleton = new JCheckBox("Singleton", false); cSingleton = new JCheckBox("Singleton", false);
cSingleton.setToolTipText("Allow only a single copy of each non-land card in your deck."); cSingleton.setToolTipText("Allow only a single copy of each non-land card in your deck.");
String singletonEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SINGLETON, "false"); String singletonEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SINGLETON, "false");
cSingleton.setSelected(Boolean.valueOf(singletonEnabled)); cSingleton.setSelected(Boolean.parseBoolean(singletonEnabled));
jCheckBoxes.add(cSingleton); jCheckBoxes.add(cSingleton);
cSingleton.setEnabled(!commanderEnabled);
// Artifacts // Artifacts
cArtifacts = new JCheckBox("Artifacts", false); cArtifacts = new JCheckBox("Artifacts", false);
cArtifacts.setToolTipText("Use artifacts and artifact creatures in your deck."); cArtifacts.setToolTipText("Use artifacts and artifact creatures in your deck.");
String artifactEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ARTIFACTS, "false"); String artifactEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ARTIFACTS, "false");
cArtifacts.setSelected(Boolean.valueOf(artifactEnabled)); cArtifacts.setSelected(Boolean.parseBoolean(artifactEnabled));
jCheckBoxes.add(cArtifacts); jCheckBoxes.add(cArtifacts);
// Non-basic lands // Non-basic lands
cNonBasicLands = new JCheckBox("Non-basic Lands", false); cNonBasicLands = new JCheckBox("Non-basic Lands", false);
cNonBasicLands.setToolTipText("Use non-basic lands in your deck (if applicable)."); cNonBasicLands.setToolTipText("Use non-basic lands in your deck (if applicable).");
String nonBasicEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS, "false"); String nonBasicEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS, "false");
cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled)); cNonBasicLands.setSelected(Boolean.parseBoolean(nonBasicEnabled));
jCheckBoxes.add(cNonBasicLands); jCheckBoxes.add(cNonBasicLands);
// Colorless mana // Colorless mana
cColorless = new JCheckBox("Colorless mana", false); cColorless = new JCheckBox("Colorless mana", false);
cColorless.setToolTipText("Allow cards with colorless mana cost."); cColorless.setToolTipText("Allow cards with colorless mana cost.");
String colorlessEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, "false"); String colorlessEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, "false");
cColorless.setSelected(Boolean.valueOf(colorlessEnabled)); cColorless.setSelected(Boolean.parseBoolean(colorlessEnabled));
jCheckBoxes.add(cColorless); jCheckBoxes.add(cColorless);
c.ipadx = 0; c.ipadx = 0;
c.gridx = 0; c.gridx = 0;
@ -181,12 +183,28 @@ public class DeckGeneratorDialog {
c.gridwidth = 3; c.gridwidth = 3;
mainPanel.add(jCheckBoxes, c); mainPanel.add(jCheckBoxes, c);
// Commander
cCommander = new JCheckBox("Commander", false);
cCommander.setToolTipText("Add legendary creature as commander");
cCommander.setSelected(commanderEnabled);
jCheckBoxes.add(cCommander);
c.ipadx = 0;
c.gridx = 0;
c.gridy = 3;
c.weightx = 1;
c.gridwidth = 3;
mainPanel.add(jCheckBoxes, c);
cCommander.addItemListener(itemEvent -> {
// commander require singletone mode
cSingleton.setEnabled(!cCommander.isSelected());
});
// Create the advanced configuration panel // Create the advanced configuration panel
JPanel advancedPanel = createAdvancedPanel(); JPanel advancedPanel = createAdvancedPanel();
// Advanced checkbox (enable/disable advanced configuration) // Advanced checkbox (enable/disable advanced configuration)
cAdvanced = new JCheckBox("Advanced"); cAdvanced = new JCheckBox("Customize distribution");
cAdvanced.setToolTipText("Enable advanced configuration options"); cAdvanced.setToolTipText("Customize cards distribution due mana values and types");
cAdvanced.addItemListener(itemEvent -> { cAdvanced.addItemListener(itemEvent -> {
boolean enable = cAdvanced.isSelected(); boolean enable = cAdvanced.isSelected();
enableAdvancedPanel(enable); enableAdvancedPanel(enable);
@ -194,7 +212,7 @@ public class DeckGeneratorDialog {
// Advanced Checkbox // Advanced Checkbox
String advancedSavedValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, "false"); String advancedSavedValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, "false");
boolean advancedEnabled = Boolean.valueOf(advancedSavedValue); boolean advancedEnabled = Boolean.parseBoolean(advancedSavedValue);
enableAdvancedPanel(advancedEnabled); enableAdvancedPanel(advancedEnabled);
cAdvanced.setSelected(advancedEnabled); cAdvanced.setSelected(advancedEnabled);
c.gridy = 4; c.gridy = 4;
@ -212,7 +230,7 @@ public class DeckGeneratorDialog {
colorsChooser.setEnabled(false); colorsChooser.setEnabled(false);
selectedColors = (String) colorsChooser.getSelectedItem(); selectedColors = (String) colorsChooser.getSelectedItem();
dlg.setVisible(false); dlg.setVisible(false);
MageFrame.getPreferences().put("genDeckColor", selectedColors); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, selectedColors);
}); });
btnCancel = new JButton("Cancel"); btnCancel = new JButton("Cancel");
btnCancel.addActionListener(e -> { btnCancel.addActionListener(e -> {
@ -297,7 +315,7 @@ public class DeckGeneratorDialog {
c.gridwidth = 1; c.gridwidth = 1;
c.gridy = 2; c.gridy = 2;
btnReset = new JButton("Reset"); btnReset = new JButton("Reset");
btnReset.setToolTipText("Reset advanced dialog to default values"); btnReset.setToolTipText("Reset custom cards distribution to default values");
btnReset.addActionListener(actionEvent -> { btnReset.addActionListener(actionEvent -> {
cbCMC.setSelectedItem(DeckGeneratorCMC.Default); cbCMC.setSelectedItem(DeckGeneratorCMC.Default);
adjustingSliderPanel.resetValues(); adjustingSliderPanel.resetValues();
@ -374,6 +392,12 @@ public class DeckGeneratorDialog {
return selected; return selected;
} }
public boolean isCommander() {
boolean selected = cCommander.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, Boolean.toString(selected));
return selected;
}
public boolean isAdvanced() { public boolean isAdvanced() {
boolean selected = cAdvanced.isSelected(); boolean selected = cAdvanced.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, Boolean.toString(selected)); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, Boolean.toString(selected));

View file

@ -1,6 +1,6 @@
package mage.client.deck.generator; package mage.client.deck.generator;
import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
@ -11,16 +11,17 @@ import mage.constants.ColoredManaSymbol;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.util.RandomUtil; import mage.util.RandomUtil;
import mage.util.TournamentUtil; import mage.util.TournamentUtil;
import org.apache.log4j.Logger;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
/** /**
* * @author Simown, JayDi85
* @author Simown
*/ */
public class DeckGeneratorPool public class DeckGeneratorPool {
{
private static final Logger logger = Logger.getLogger(DeckGeneratorPool.class);
public static final int DEFAULT_CREATURE_PERCENTAGE = 38; public static final int DEFAULT_CREATURE_PERCENTAGE = 38;
public static final int DEFAULT_NON_CREATURE_PERCENTAGE = 21; public static final int DEFAULT_NON_CREATURE_PERCENTAGE = 21;
@ -31,6 +32,7 @@ public class DeckGeneratorPool
private final List<DeckGeneratorCMC.CMC> poolCMCs; private final List<DeckGeneratorCMC.CMC> poolCMCs;
private final int creatureCount; private final int creatureCount;
private final int nonCreatureCount; private final int nonCreatureCount;
private final int commandersCount;
private final int landCount; private final int landCount;
private final boolean isSingleton; private final boolean isSingleton;
private final int deckSize; private final int deckSize;
@ -48,65 +50,83 @@ public class DeckGeneratorPool
/** /**
* Creates a card pool with specified criterea used when generating a deck. * Creates a card pool with specified criterea used when generating a deck.
* *
* @param deckSize the size of the complete deck * @param deckSize the size of the complete deck
* @param creaturePercentage what percentage of creatures to use when generating the deck. * @param creaturePercentage what percentage of creatures to use when generating the deck.
* @param nonCreaturePercentage percentage of non-creatures to use when generating the deck. * @param nonCreaturePercentage percentage of non-creatures to use when generating the deck.
* @param landPercentage percentage of lands to use when generating the deck. * @param landPercentage percentage of lands to use when generating the deck.
* @param allowedColors which card colors are allowed in the generated deck. * @param allowedColors which card colors are allowed in the generated deck.
* @param isSingleton if the deck only has 1 copy of each non-land card. * @param isSingleton if the deck only has 1 copy of each non-land card.
* @param colorlessAllowed if colourless mana symbols are allowed in costs in the deck. * @param colorlessAllowed if colourless mana symbols are allowed in costs in the deck.
* @param isAdvanced if the user has provided advanced options to generate the deck. * @param isAdvanced if the user has provided advanced options to generate the deck.
* @param deckGeneratorCMC the CMC curve to use for this deck * @param isCommander reserve commander card
* @param deckGeneratorCMC the CMC curve to use for this deck
*/ */
public DeckGeneratorPool(final int deckSize, final int creaturePercentage, final int nonCreaturePercentage, final int landPercentage, public DeckGeneratorPool(final int deckSize, final int creaturePercentage, final int nonCreaturePercentage, final int landPercentage,
final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC) final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isCommander,
{ boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC) {
this.deckSize = deckSize; this.deckSize = deckSize;
this.allowedColors = allowedColors; this.allowedColors = allowedColors;
this.isSingleton = isSingleton;
this.colorlessAllowed = colorlessAllowed; this.colorlessAllowed = colorlessAllowed;
this.commandersCount = isCommander ? 1 : 0;
this.isSingleton = isSingleton || isCommander; // commander must use singleton mode only
this.deck = new Deck(); this.deck = new Deck();
// Advanced (CMC Slider panel and curve drop-down in the dialog) // Advanced (CMC Slider panel and curve drop-down in the dialog)
if(isAdvanced) { if (isAdvanced) {
this.creatureCount = (int)Math.ceil((deckSize / 100.0) * creaturePercentage); this.creatureCount = (int) Math.ceil((deckSize / 100.0) * creaturePercentage);
this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0)* nonCreaturePercentage); this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * nonCreaturePercentage);
this.landCount = (int)Math.ceil((deckSize / 100.0)* landPercentage); this.landCount = (int) Math.ceil((deckSize / 100.0) * landPercentage);
if(this.deckSize == 60) { switch (this.deckSize) {
this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC(); case 100:
} else { this.poolCMCs = deckGeneratorCMC.get100CardPoolCMC();
this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC(); break;
case 60:
this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC();
break;
case 40:
this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC();
break;
default:
throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize);
} }
} else { } else {
// Ignore the advanced group, just use defaults // Ignore the advanced group, just use defaults
this.creatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE); this.creatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE);
this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE); this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE);
this.landCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE); this.landCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE);
if(this.deckSize == 60) { switch (this.deckSize) {
this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC(); case 100:
} else { this.poolCMCs = DeckGeneratorCMC.Default.get100CardPoolCMC();
this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC(); break;
case 60:
this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC();
break;
case 40:
this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC();
break;
default:
throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize);
} }
} }
if(allowedColors.size() == 1) { if (allowedColors.size() == 1) {
monoColored = true; monoColored = true;
} }
} }
/** /**
* Adjusts the number of spell cards that should be in a converted mana cost (CMC) range, given the amount of cards total. * Adjusts the number of spell cards that should be in a converted mana cost (CMC) range, given the amount of cards total.
*
* @param cardsCount the number of total cards. * @param cardsCount the number of total cards.
* @return a list of CMC ranges, with the amount of cards for each CMC range * @return a list of CMC ranges, with the amount of cards for each CMC range
*/ */
public List<DeckGeneratorCMC.CMC> getCMCsForSpellCount(int cardsCount) { public List<DeckGeneratorCMC.CMC> getCMCsForSpellCount(int cardsCount) {
List<DeckGeneratorCMC.CMC> adjustedCMCs = new ArrayList<>(this.poolCMCs); List<DeckGeneratorCMC.CMC> adjustedCMCs = new ArrayList<>(this.poolCMCs);
// For each CMC calculate how many spell cards are needed, given the total amount of cards // For each CMC calculate how many spell cards are needed, given the total amount of cards
for(DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) { for (DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) {
deckCMC.setAmount((int)Math.ceil(deckCMC.percentage * cardsCount)); deckCMC.setAmount((int) Math.ceil(deckCMC.percentage * cardsCount));
} }
return adjustedCMCs; return adjustedCMCs;
} }
@ -115,15 +135,15 @@ public class DeckGeneratorPool
* Verifies if the spell card supplied is valid for this pool of cards. * Verifies if the spell card supplied is valid for this pool of cards.
* Checks that there isn't too many copies of this card in the deck. * Checks that there isn't too many copies of this card in the deck.
* Checks that the card fits the chosen colors for this pool. * Checks that the card fits the chosen colors for this pool.
*
* @param card the spell card * @param card the spell card
* @return if the spell card is valid for this pool. * @return if the spell card is valid for this pool.
*/ */
public boolean isValidSpellCard(Card card) public boolean isValidSpellCard(Card card) {
{
int cardCount = getCardCount((card.getName())); int cardCount = getCardCount((card.getName()));
// Check it hasn't already got the maximum number of copies in a deck // Check it hasn't already got the maximum number of copies in a deck
if(cardCount < (isSingleton ? 1 : 4)) { if (cardCount < (isSingleton ? 1 : 4)) {
if(cardFitsChosenColors(card)) { if (cardFitsChosenColors(card)) {
return true; return true;
} }
} }
@ -132,11 +152,11 @@ public class DeckGeneratorPool
/** /**
* Verifies if the non-basic land card supplied is valid for this pool of cards. * Verifies if the non-basic land card supplied is valid for this pool of cards.
*
* @param card the non-basic land card * @param card the non-basic land card
* @return if the land card generates the allowed colors for this pool. * @return if the land card generates the allowed colors for this pool.
*/ */
public boolean isValidLandCard(Card card) public boolean isValidLandCard(Card card) {
{
int cardCount = getCardCount((card.getName())); int cardCount = getCardCount((card.getName()));
// No need to check if the land is valid for the colors chosen // No need to check if the land is valid for the colors chosen
// They are all filtered before searching for lands to include in the deck. // They are all filtered before searching for lands to include in the deck.
@ -146,60 +166,56 @@ public class DeckGeneratorPool
/** /**
* Adds a card to the pool and updates the count of this card. * Adds a card to the pool and updates the count of this card.
*
* @param card the card to add. * @param card the card to add.
*/ */
public void addCard(Card card) public void addCard(Card card) {
{
Object cnt = cardCounts.get((card.getName())); Object cnt = cardCounts.get((card.getName()));
if(cnt == null) if (cnt == null)
cardCounts.put(card.getName(), 0); cardCounts.put(card.getName(), 0);
int existingCount = cardCounts.get((card.getName())); int existingCount = cardCounts.get((card.getName()));
cardCounts.put(card.getName(), existingCount+1); cardCounts.put(card.getName(), existingCount + 1);
deckCards.add(card); deckCards.add(card);
} }
public void clearCards(boolean isClearReserve) {
cardCounts.clear();
deckCards.clear();
if (isClearReserve) {
reserveSpells.clear();
}
}
/** /**
* Adds a card to the reserve pool. * Adds a card to the reserve pool.
* Reserve pool is used when the deck generation fails to build a complete deck, or * Reserve pool is used when the deck generation fails to build a complete deck, or
* a partially complete deck (e.g. if there are no cards found that match a CMC) * a partially complete deck (e.g. if there are no cards found that match a CMC)
* @param card the card to add *
* @param card the card to add
* @param cardCMC the converted mana cost of the card * @param cardCMC the converted mana cost of the card
*/ */
public boolean tryAddReserve(Card card, int cardCMC) { public boolean tryAddReserve(Card card, int cardCMC) {
// Only cards with CMC < 7 and don't already exist in the deck // Only cards with CMC < 7 and don't already exist in the deck
// can be added to our reserve pool as not to overwhelm the curve // can be added to our reserve pool as not to overwhelm the curve
// with high CMC cards and duplicates. // with high CMC cards and duplicates.
if(cardCMC < 7 && getCardCount(card.getName()) == 0) { if (cardCMC < 7 && getCardCount(card.getName()) == 0) {
this.reserveSpells.add(card); this.reserveSpells.add(card);
return true; return true;
} }
return false; return false;
} }
/**
* Checks if the mana symbols in the card all match the allowed colors for this pool.
* @param card the spell card to check.
* @return if all the mana symbols fit the chosen colors.
*/
private boolean cardFitsChosenColors(Card card) { private boolean cardFitsChosenColors(Card card) {
for (String symbol : card.getManaCostSymbols()) { Set<String> needColors = allowedColors.stream().map(ColoredManaSymbol::toString).collect(Collectors.toSet());
boolean found = false; List<ObjectColor> cardColors = card.getColorIdentity().getColors();
symbol = symbol.replace("{", "").replace("}", ""); for (ObjectColor cardColor : cardColors) {
if (isColoredManaSymbol(symbol)) { if (!needColors.contains(cardColor.toString())) {
for (ColoredManaSymbol allowed : allowedColors) {
if (symbol.contains(allowed.toString())) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
if (symbol.equals("C") && !colorlessAllowed) {
return false; return false;
} }
} }
if (cardColors.isEmpty() && !colorlessAllowed) {
return false;
}
return true; return true;
} }
@ -208,6 +224,7 @@ public class DeckGeneratorPool
* Calculates the percentage of colored mana symbols over all spell cards in the deck. * Calculates the percentage of colored mana symbols over all spell cards in the deck.
* Used to balance the generation of basic lands so the amount of lands matches the * Used to balance the generation of basic lands so the amount of lands matches the
* cards mana costs. * cards mana costs.
*
* @return a list of colored mana symbols and the percentage of symbols seen in cards mana costs. * @return a list of colored mana symbols and the percentage of symbols seen in cards mana costs.
*/ */
public Map<String, Double> calculateSpellColorPercentages() { public Map<String, Double> calculateSpellColorPercentages() {
@ -221,14 +238,14 @@ public class DeckGeneratorPool
int totalCount = 0; int totalCount = 0;
List<Card> fixedSpells = getFixedSpells(); List<Card> fixedSpells = getFixedSpells();
for(Card spell: fixedSpells) { for (Card spell : fixedSpells) {
for (String symbol : spell.getManaCostSymbols()) { for (String symbol : spell.getManaCostSymbols()) {
symbol = symbol.replace("{", "").replace("}", ""); symbol = symbol.replace("{", "").replace("}", "");
if (isColoredManaSymbol(symbol)) { if (isColoredManaSymbol(symbol)) {
for (ColoredManaSymbol allowed : allowedColors) { for (ColoredManaSymbol allowed : allowedColors) {
if (symbol.contains(allowed.toString())) { if (symbol.contains(allowed.toString())) {
int cnt = colorCount.get(allowed.toString()); int cnt = colorCount.get(allowed.toString());
colorCount.put(allowed.toString(), cnt+1); colorCount.put(allowed.toString(), cnt + 1);
totalCount++; totalCount++;
} }
} }
@ -236,7 +253,7 @@ public class DeckGeneratorPool
} }
} }
final Map<String, Double> percentages = new HashMap<>(); final Map<String, Double> percentages = new HashMap<>();
for(Map.Entry<String, Integer> singleCount: colorCount.entrySet()) { for (Map.Entry<String, Integer> singleCount : colorCount.entrySet()) {
String color = singleCount.getKey(); String color = singleCount.getKey();
int count = singleCount.getValue(); int count = singleCount.getValue();
// Calculate the percentage this color has out of the total color counts // Calculate the percentage this color has out of the total color counts
@ -248,20 +265,20 @@ public class DeckGeneratorPool
/** /**
* Calculates how many of each mana the non-basic lands produce. * Calculates how many of each mana the non-basic lands produce.
*
* @param deckLands the non-basic lands which will be used in the deck. * @param deckLands the non-basic lands which will be used in the deck.
* @return a mapping of colored mana symbol to the amount that can be produced. * @return a mapping of colored mana symbol to the amount that can be produced.
*/ */
public Map<String,Integer> countManaProduced(List<Card> deckLands) public Map<String, Integer> countManaProduced(List<Card> deckLands) {
{
Map<String, Integer> manaCounts = new HashMap<>(); Map<String, Integer> manaCounts = new HashMap<>();
for (final ColoredManaSymbol color : ColoredManaSymbol.values()) { for (final ColoredManaSymbol color : ColoredManaSymbol.values()) {
manaCounts.put(color.toString(), 0); manaCounts.put(color.toString(), 0);
} }
for(Card land: deckLands) { for (Card land : deckLands) {
for(Ability landAbility: land.getAbilities()) { for (Ability landAbility : land.getAbilities()) {
for (ColoredManaSymbol symbol : allowedColors) { for (ColoredManaSymbol symbol : allowedColors) {
String abilityString = landAbility.getRule(); String abilityString = landAbility.getRule();
if(landTapsForAllowedColor(abilityString, symbol.toString())) { if (landTapsForAllowedColor(abilityString, symbol.toString())) {
Integer count = manaCounts.get(symbol.toString()); Integer count = manaCounts.get(symbol.toString());
manaCounts.put(symbol.toString(), count + 1); manaCounts.put(symbol.toString(), count + 1);
} }
@ -271,15 +288,17 @@ public class DeckGeneratorPool
return manaCounts; return manaCounts;
} }
/** Filter all the non-basic lands retrieved from the database. /**
* Filter all the non-basic lands retrieved from the database.
*
* @param landCardsInfo information about all the cards. * @param landCardsInfo information about all the cards.
* @return a list of cards that produce the allowed colors for this pool. * @return a list of cards that produce the allowed colors for this pool.
*/ */
public List<Card> filterLands(List<CardInfo> landCardsInfo) { public List<Card> filterLands(List<CardInfo> landCardsInfo) {
List<Card> matchingLandList = new ArrayList<>(); List<Card> matchingLandList = new ArrayList<>();
for(CardInfo landCardInfo: landCardsInfo) { for (CardInfo landCardInfo : landCardsInfo) {
Card landCard = landCardInfo.createMockCard(); Card landCard = landCardInfo.createMockCard();
if(landProducesChosenColors(landCard)) { if (landProducesChosenColors(landCard)) {
matchingLandList.add(landCard); matchingLandList.add(landCard);
} }
} }
@ -288,11 +307,12 @@ public class DeckGeneratorPool
/** /**
* Returns the card name that represents the basic land for this color. * Returns the card name that represents the basic land for this color.
*
* @param symbolString the colored mana symbol. * @param symbolString the colored mana symbol.
* @return the name of a basic land card. * @return the name of a basic land card.
*/ */
public static String getBasicLandName(String symbolString) { public static String getBasicLandName(String symbolString) {
switch(symbolString) { switch (symbolString) {
case "B": case "B":
return "Swamp"; return "Swamp";
case "G": case "G":
@ -311,25 +331,50 @@ public class DeckGeneratorPool
/** /**
* Returns a complete deck. * Returns a complete deck.
*
* @return the deck. * @return the deck.
*/ */
public Deck getDeck() { public Deck getDeck() {
Set<Card> actualDeck = deck.getCards(); deck.getCards().clear();
actualDeck.addAll(deckCards); deck.getSideboard().clear();
List<Card> useCards = new ArrayList<>(deckCards);
List<Card> useCommanders = new ArrayList<>();
// take random commanders
if (commandersCount > 0) {
List<Card> possibleCommanders = deckCards.stream()
.filter(this::isValidCommander)
.collect(Collectors.toList());
Card nextCommander = RandomUtil.randomFromCollection(possibleCommanders);
while (nextCommander != null && useCommanders.size() < commandersCount) {
useCards.remove(nextCommander);
useCommanders.add(nextCommander);
}
}
deck.getCards().addAll(useCards);
deck.getSideboard().addAll(useCommanders);
return deck; return deck;
} }
/** /**
* Returns the number of creatures needed in this pool. * Returns the number of creatures needed in this pool.
*
* @return the number of creatures. * @return the number of creatures.
*/ */
public int getCreatureCount() { public int getCreatureCount() {
return creatureCount; return creatureCount;
} }
public int getCommandersCount() {
return commandersCount;
}
/** /**
* Returns the number of non-creatures needed in this pool. * Returns the number of non-creatures needed in this pool.
*
* @return the number of non-creatures. * @return the number of non-creatures.
*/ */
public int getNonCreatureCount() { public int getNonCreatureCount() {
@ -338,6 +383,7 @@ public class DeckGeneratorPool
/** /**
* Returns the number of lands (basic + non-basic) needed in this pool. * Returns the number of lands (basic + non-basic) needed in this pool.
*
* @return the number of lands. * @return the number of lands.
*/ */
public int getLandCount() { public int getLandCount() {
@ -346,6 +392,7 @@ public class DeckGeneratorPool
/** /**
* Returns if this pool only uses one color. * Returns if this pool only uses one color.
*
* @return if this pool is monocolored. * @return if this pool is monocolored.
*/ */
public boolean isMonoColoredDeck() { public boolean isMonoColoredDeck() {
@ -354,6 +401,7 @@ public class DeckGeneratorPool
/** /**
* Returns the size of the deck to generate from this pool. * Returns the size of the deck to generate from this pool.
*
* @return the deck size. * @return the deck size.
*/ */
public int getDeckSize() { public int getDeckSize() {
@ -364,20 +412,20 @@ public class DeckGeneratorPool
* Fixes undersized or oversized decks that have been generated. * Fixes undersized or oversized decks that have been generated.
* Removes random cards from an oversized deck until it is the correct size. * Removes random cards from an oversized deck until it is the correct size.
* Uses the reserve pool to fill up and undersized deck with cards. * Uses the reserve pool to fill up and undersized deck with cards.
*
* @return a fixed list of cards for this deck. * @return a fixed list of cards for this deck.
*/ */
private List<Card> getFixedSpells() private List<Card> getFixedSpells() {
{
int spellSize = deckCards.size(); int spellSize = deckCards.size();
int nonLandSize = (deckSize - landCount); int nonLandSize = (deckSize - landCount);
// Less spells than needed // Less spells than needed
if(spellSize < nonLandSize) { if (spellSize < nonLandSize) {
int spellsNeeded = nonLandSize-spellSize; int spellsNeeded = nonLandSize - spellSize;
// If we haven't got enough spells in reserve to fulfil the amount we need, skip adding any. // If we haven't got enough spells in reserve to fulfil the amount we need, skip adding any.
if(reserveSpells.size() >= spellsNeeded) { if (reserveSpells.size() >= spellsNeeded) {
List<Card> spellsToAdd = new ArrayList<>(spellsNeeded); List<Card> spellsToAdd = new ArrayList<>(spellsNeeded);
@ -398,15 +446,15 @@ public class DeckGeneratorPool
} }
// More spells than needed // More spells than needed
else if(spellSize > (deckSize - landCount)) { else if (spellSize > (deckSize - landCount)) {
int spellsRemoved = (spellSize)-(deckSize-landCount); int spellsRemoved = (spellSize) - (deckSize - landCount);
for(int i = 0; i < spellsRemoved; ++i) { for (int i = 0; i < spellsRemoved; ++i) {
deckCards.remove(RandomUtil.nextInt(deckCards.size())); deckCards.remove(RandomUtil.nextInt(deckCards.size()));
} }
} }
// Check we have exactly the right amount of cards for a deck. // Check we have exactly the right amount of cards for a deck.
if(deckCards.size() != nonLandSize) { if (deckCards.size() != nonLandSize) {
throw new IllegalStateException("Not enough cards found to generate deck."); throw new IllegalStateException("Not enough cards found to generate deck.");
} }
// Return the fixed amount // Return the fixed amount
@ -416,16 +464,18 @@ public class DeckGeneratorPool
/** /**
* Returns if this land taps for the given color. * Returns if this land taps for the given color.
* Basic string matching to check the ability adds one of the chosen mana when tapped. * Basic string matching to check the ability adds one of the chosen mana when tapped.
*
* @param ability MockAbility of the land card * @param ability MockAbility of the land card
* @param symbol colored mana symbol. * @param symbol colored mana symbol.
* @return if the ability is tapping to produce the mana the symbol represents. * @return if the ability is tapping to produce the mana the symbol represents.
*/ */
private boolean landTapsForAllowedColor(String ability, String symbol) { private boolean landTapsForAllowedColor(String ability, String symbol) {
return ability.matches(".*Add \\{" + symbol + "\\}."); return ability.matches(".*Add \\{" + symbol + "\\}.");
} }
/** /**
* Returns if this land will produce the chosen colors for this pool. * Returns if this land will produce the chosen colors for this pool.
*
* @param card a non-basic land card. * @param card a non-basic land card.
* @return if this land card taps to produces the colors chosen. * @return if this land card taps to produces the colors chosen.
*/ */
@ -434,32 +484,49 @@ public class DeckGeneratorPool
// and other Abilities so we have to do some basic string matching on land cards for now. // and other Abilities so we have to do some basic string matching on land cards for now.
List<Ability> landAbilities = card.getAbilities(); List<Ability> landAbilities = card.getAbilities();
int count = 0; int count = 0;
for(Ability ability : landAbilities) { for (Ability ability : landAbilities) {
String abilityString = ability.getRule(); String abilityString = ability.getRule();
// Lands that tap to produce mana of the chosen colors // Lands that tap to produce mana of the chosen colors
for(ColoredManaSymbol symbol : allowedColors) { for (ColoredManaSymbol symbol : allowedColors) {
if(landTapsForAllowedColor(abilityString, symbol.toString())) { if (landTapsForAllowedColor(abilityString, symbol.toString())) {
count++; count++;
} }
} }
if(count > 1) { if (count > 1) {
return true; return true;
} }
} }
return false; return false;
} }
private boolean isValidCommander(Card card) {
// commander must be legendary creature
if (!card.isCreature() || !card.isLegendary()) {
return false;
}
// commander must have all chosen colors
for (ColoredManaSymbol symbol : allowedColors) {
if (!card.getColor().contains(new ObjectColor(symbol.toString()))) {
return false;
}
}
return true;
}
/** /**
* Returns if the symbol is a colored mana symbol. * Returns if the symbol is a colored mana symbol.
*
* @param symbol the symbol to check. * @param symbol the symbol to check.
* @return If it is a basic mana symbol or a hybrid mana symbol. * @return If it is a basic mana symbol or a hybrid mana symbol.
*/ */
private static boolean isColoredManaSymbol(String symbol) { private static boolean isColoredManaSymbol(String symbol) {
// Hybrid mana // Hybrid mana
if(symbol.contains("/")) { if (symbol.contains("/")) {
return true; return true;
} }
for(ColoredManaSymbol c: ColoredManaSymbol.values()) { for (ColoredManaSymbol c : ColoredManaSymbol.values()) {
if (symbol.charAt(0) == (c.toString().charAt(0))) { if (symbol.charAt(0) == (c.toString().charAt(0))) {
return true; return true;
} }
@ -470,14 +537,15 @@ public class DeckGeneratorPool
/** /**
* Returns how many of this card is in the pool. * Returns how many of this card is in the pool.
* If there are none in the pool it will initalise the card count. * If there are none in the pool it will initalise the card count.
*
* @param cardName the name of the card to check. * @param cardName the name of the card to check.
* @return the number of cards in the pool of this name. * @return the number of cards in the pool of this name.
*/ */
private int getCardCount(String cardName) { private int getCardCount(String cardName) {
Object cC = cardCounts.get((cardName)); Object cC = cardCounts.get((cardName));
if(cC == null) if (cC == null)
cardCounts.put(cardName, 0); cardCounts.put(cardName, 0);
return cardCounts.get((cardName)); return cardCounts.get((cardName));
} }
@ -490,8 +558,8 @@ public class DeckGeneratorPool
* color of cards. * color of cards.
* *
* @param useNonBasicLand * @param useNonBasicLand
* @param criteria the criteria of the lands to search for in the database. * @param criteria the criteria of the lands to search for in the database.
* @param basicLands information about the basic lands from the sets used. * @param basicLands information about the basic lands from the sets used.
*/ */
protected static void generateLands(boolean useNonBasicLand, CardCriteria criteria, Map<String, List<CardInfo>> basicLands) { protected static void generateLands(boolean useNonBasicLand, CardCriteria criteria, Map<String, List<CardInfo>> basicLands) {
DeckGeneratorPool genPool = DeckGenerator.genPool; DeckGeneratorPool genPool = DeckGenerator.genPool;
@ -541,45 +609,69 @@ public class DeckGeneratorPool
* non-creatures are retrieved separately to ensure the deck contains a * non-creatures are retrieved separately to ensure the deck contains a
* reasonable mix of both. * reasonable mix of both.
* *
* @param criteria the criteria to search for in the database. * @param criteria the criteria to search for in the database.
* @param spellCount the number of spells that match the criteria needed in * @param needCardsCount the number of spells that match the criteria needed in
* the deck. * the deck.
* @param needCommandersCount make sure it contains commander creature (must be uses on first generateSpells only)
*/ */
protected static void generateSpells(CardCriteria criteria, int spellCount) { protected static void generateSpells(CardCriteria criteria, int needCardsCount, int needCommandersCount) {
DeckGeneratorPool genPool = DeckGenerator.genPool; DeckGeneratorPool genPool = DeckGenerator.genPool;
if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) {
throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first");
}
List<CardInfo> cardPool = CardRepository.instance.findCards(criteria); List<CardInfo> cardPool = CardRepository.instance.findCards(criteria);
int retrievedCount = cardPool.size(); List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(spellCount);
int count = 0; int count = 0;
int validCommanders = 0;
int reservesAdded = 0; int reservesAdded = 0;
boolean added; if (cardPool.size() > 0 && cardPool.size() >= needCardsCount) {
if (retrievedCount > 0 && retrievedCount >= spellCount) {
int tries = 0; int tries = 0;
while (count < spellCount) { while (true) {
Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).createMockCard(); tries++;
if (genPool.isValidSpellCard(card)) {
int cardCMC = card.getManaValue(); // can't finish deck, stop and use reserved cards later
for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { if (tries > DeckGenerator.MAX_TRIES) {
if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors");
int currentAmount = deckCMC.getAmount(); break;
if (currentAmount > 0) { }
deckCMC.setAmount(currentAmount - 1);
genPool.addCard(card.copy()); // can finish deck - but make sure it has commander
count++; if (count >= needCardsCount) {
} if (validCommanders < needCommandersCount) {
} else if (reservesAdded < (genPool.getDeckSize() / 2)) { // reset deck search from scratch (except reserved cards)
added = genPool.tryAddReserve(card, cardCMC); count = 0;
if (added) { validCommanders = 0;
reservesAdded++; deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
genPool.clearCards(false);
continue;
}
break;
}
Card card = cardPool.get(RandomUtil.nextInt(cardPool.size())).createMockCard();
if (!genPool.isValidSpellCard(card)) {
continue;
}
int cardCMC = card.getManaValue();
for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) {
if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) {
int currentAmount = deckCMC.getAmount();
if (currentAmount > 0) {
deckCMC.setAmount(currentAmount - 1);
genPool.addCard(card.copy());
count++;
// make sure it has compatible commanders
if (genPool.isValidCommander(card)) {
validCommanders++;
} }
} }
} else if (reservesAdded < (genPool.getDeckSize() / 2)) {
if (genPool.tryAddReserve(card, cardCMC)) {
reservesAdded++;
}
} }
} }
tries++;
if (tries > DeckGenerator.MAX_TRIES) {
// Break here, we'll fill in random missing ones later
break;
}
} }
} else { } else {
throw new IllegalStateException("Not enough cards to generate deck."); throw new IllegalStateException("Not enough cards to generate deck.");

View file

@ -268,12 +268,14 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel"; public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel";
// pref setting for deck generator // pref setting for deck generator
public static final String KEY_NEW_DECK_GENERATOR_COLORS = "newDeckGeneratorDeckColors";
public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize"; public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize";
public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet"; public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet";
public static final String KEY_NEW_DECK_GENERATOR_SINGLETON = "newDeckGeneratorSingleton"; public static final String KEY_NEW_DECK_GENERATOR_SINGLETON = "newDeckGeneratorSingleton";
public static final String KEY_NEW_DECK_GENERATOR_ARTIFACTS = "newDeckGeneratorArtifacts"; public static final String KEY_NEW_DECK_GENERATOR_ARTIFACTS = "newDeckGeneratorArtifacts";
public static final String KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS = "newDeckGeneratorNonBasicLands"; public static final String KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS = "newDeckGeneratorNonBasicLands";
public static final String KEY_NEW_DECK_GENERATOR_COLORLESS = "newDeckGeneratorColorless"; public static final String KEY_NEW_DECK_GENERATOR_COLORLESS = "newDeckGeneratorColorless";
public static final String KEY_NEW_DECK_GENERATOR_COMMANDER = "newDeckGeneratorCommander";
public static final String KEY_NEW_DECK_GENERATOR_ADVANCED = "newDeckGeneratorAdvanced"; public static final String KEY_NEW_DECK_GENERATOR_ADVANCED = "newDeckGeneratorAdvanced";
public static final String KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE = "newDeckGeneratorCreaturePercentage"; public static final String KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE = "newDeckGeneratorCreaturePercentage";
public static final String KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE = "newDeckGeneratorNonCreaturePercentage"; public static final String KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE = "newDeckGeneratorNonCreaturePercentage";