mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
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:
parent
ab362abe10
commit
8649869ed4
5 changed files with 319 additions and 166 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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.");
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue