Added advanced deck generator functions and tidied up the layout.

This commit is contained in:
Simown 2016-03-11 11:22:56 +00:00
parent 578cb128dd
commit 9dc71d2996
6 changed files with 665 additions and 155 deletions

View file

@ -155,7 +155,10 @@ public class DeckGenerator {
* @return the final deck to use. * @return the final deck to use.
*/ */
private static Deck generateDeck(int deckSize, List<ColoredManaSymbol> allowedColors, List<String> setsToUse) { private static Deck generateDeck(int deckSize, List<ColoredManaSymbol> allowedColors, List<String> setsToUse) {
genPool = new DeckGeneratorPool(deckSize, allowedColors, genDialog.isSingleton(), genDialog.isColorless());
genPool = new DeckGeneratorPool(deckSize, genDialog.getCreaturePercentage(), genDialog.getNonCreaturePercentage(),
genDialog.getLandPercentage(), allowedColors, genDialog.isSingleton(), genDialog.isColorless(),
genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC());
final String[] sets = setsToUse.toArray(new String[setsToUse.size()]); final String[] sets = setsToUse.toArray(new String[setsToUse.size()]);
@ -210,7 +213,7 @@ public class DeckGenerator {
private static void generateSpells(CardCriteria criteria, int spellCount) { private static void generateSpells(CardCriteria criteria, int spellCount) {
List<CardInfo> cardPool = CardRepository.instance.findCards(criteria); List<CardInfo> cardPool = CardRepository.instance.findCards(criteria);
int retrievedCount = cardPool.size(); int retrievedCount = cardPool.size();
List<DeckGeneratorCMC> deckCMCs = genPool.getCMCsForSpellCount(spellCount); List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(spellCount);
Random random = new Random(); Random random = new Random();
int count = 0; int count = 0;
int reservesAdded = 0; int reservesAdded = 0;
@ -221,7 +224,7 @@ public class DeckGenerator {
Card card = cardPool.get(random.nextInt(retrievedCount)).getMockCard(); Card card = cardPool.get(random.nextInt(retrievedCount)).getMockCard();
if (genPool.isValidSpellCard(card)) { if (genPool.isValidSpellCard(card)) {
int cardCMC = card.getManaCost().convertedManaCost(); int cardCMC = card.getManaCost().convertedManaCost();
for (DeckGeneratorCMC deckCMC : deckCMCs) { for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) {
if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) {
int currentAmount = deckCMC.getAmount(); int currentAmount = deckCMC.getAmount();
if (currentAmount > 0) { if (currentAmount > 0) {
@ -339,8 +342,9 @@ public class DeckGenerator {
* database. * database.
*/ */
private static void addBasicLands(int landsNeeded, Map<String, Double> percentage, Map<String, Integer> count, Map<String, List<CardInfo>> basicLands) { private static void addBasicLands(int landsNeeded, Map<String, Double> percentage, Map<String, Integer> count, Map<String, List<CardInfo>> basicLands) {
int colorTotal = 0; int colorTotal = 0;
ColoredManaSymbol colorToAdd = null; ColoredManaSymbol colorToAdd = ColoredManaSymbol.U;
// Add up the totals for all colors, to keep track of the percentage a color is. // Add up the totals for all colors, to keep track of the percentage a color is.
for (Map.Entry<String, Integer> c : count.entrySet()) { for (Map.Entry<String, Integer> c : count.entrySet()) {
@ -372,14 +376,12 @@ public class DeckGenerator {
minPercentage = (neededPercentage - thisPercentage); minPercentage = (neededPercentage - thisPercentage);
} }
} }
if (colorToAdd != null) {
genPool.addCard(getBasicLand(colorToAdd, basicLands)); genPool.addCard(getBasicLand(colorToAdd, basicLands));
count.put(colorToAdd.toString(), count.get(colorToAdd.toString()) + 1); count.put(colorToAdd.toString(), count.get(colorToAdd.toString()) + 1);
colorTotal++; colorTotal++;
landsNeeded--; landsNeeded--;
} }
} }
}
/** /**
* Return a random basic land of the chosen color. * Return a random basic land of the chosen color.

View file

@ -27,11 +27,65 @@
*/ */
package mage.client.deck.generator; package mage.client.deck.generator;
/** import java.util.ArrayList;
* Stores a range of converted mana costs (CMC) for use in deck generation.
*/ public enum DeckGeneratorCMC {
public class DeckGeneratorCMC
{ Low(
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.60f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 6, 0.10f));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.65f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 5, 0.05f));
}}),
Default(
new ArrayList<CMC>() {{
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));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.30f));
add(new CMC(3, 4, 0.45f));
add(new CMC(5, 6, 0.20f));
add(new CMC(7, 100, 0.05f));
}}),
High(
new ArrayList<CMC>() {{
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));
}},
new ArrayList<CMC>() {{
add(new CMC(0, 2, 0.10f));
add(new CMC(3, 4, 0.30f));
add(new CMC(5, 6, 0.45f));
add(new CMC(7, 100, 0.15f));
}});
private ArrayList<CMC> poolCMCs60, poolCMCs40;
DeckGeneratorCMC(ArrayList<CMC> CMCs60, ArrayList<CMC> CMCs40) {
this.poolCMCs60 = CMCs60;
this.poolCMCs40 = CMCs40;
}
public ArrayList<CMC> get40CardPoolCMC() {
return this.poolCMCs40;
}
public ArrayList<CMC> get60CardPoolCMC() {
return this.poolCMCs60;
}
static class CMC
{
public final int min; public final int min;
public final int max; public final int max;
public final float percentage; public final float percentage;
@ -43,7 +97,7 @@ public class DeckGeneratorCMC
* @param max the maximum CMC a card in this range can be. * @param max the maximum CMC a card in this range can be.
* @param percentage the percentage of cards in the range (min, max) * @param percentage the percentage of cards in the range (min, max)
*/ */
DeckGeneratorCMC(int min, int max, float percentage) CMC(int min, int max, float percentage)
{ {
this.min = min; this.min = min;
this.max = max; this.max = max;
@ -67,4 +121,7 @@ public class DeckGeneratorCMC
{ {
return this.amount; return this.amount;
} }
}
} }

View file

@ -35,9 +35,14 @@ import mage.client.util.gui.ColorsChooser;
import mage.client.util.sets.ConstructedFormats; import mage.client.util.sets.ConstructedFormats;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@ -48,13 +53,14 @@ import java.util.Date;
*/ */
public class DeckGeneratorDialog { public class DeckGeneratorDialog {
private JDialog dlg; private static JDialog dlg;
private String selectedColors; private static String selectedColors;
private JComboBox<String> cbSets; private static JComboBox cbSets, cbDeckSize, cbCMC;
private JComboBox<String> cbDeckSize; private static JButton btnGenerate, btnCancel, btnReset;
private JButton btnGenerate, btnCancel; private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced;
private JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless; private static JLabel averageCMCLabel;
private SimpleDateFormat dateFormat; private static SimpleDateFormat dateFormat;
private static RatioAdjustingSliderPanel adjustingSliderPanel;
public DeckGeneratorDialog() public DeckGeneratorDialog()
{ {
@ -63,59 +69,92 @@ public class DeckGeneratorDialog {
} }
private void initDialog() { private void initDialog() {
JPanel p0 = new JPanel();
p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
JLabel text = new JLabel("Choose color for your deck: "); JPanel mainPanel = new JPanel();
text.setAlignmentX(Component.CENTER_ALIGNMENT);
p0.add(text);
p0.add(Box.createVerticalStrut(5)); mainPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 15, 0, 0);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0.10;
JLabel text = new JLabel("Choose color for your deck:");
mainPanel.add(text, c);
// Color selector dropdown
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.80;
c.ipadx = 30;
c.insets = new Insets(5, 10, 0, 10);
c.gridx = 1;
c.gridy = 0;
String chosen = MageFrame.getPreferences().get("genDeckColor", "u"); String chosen = MageFrame.getPreferences().get("genDeckColor", "u");
final ColorsChooser colorsChooser = new ColorsChooser(chosen); final ColorsChooser colorsChooser = new ColorsChooser(chosen);
p0.add(colorsChooser); mainPanel.add(colorsChooser, c);
p0.add(Box.createVerticalStrut(5)); c.insets = new Insets(0, 15, 0, 0);
JLabel text2 = new JLabel("(X - random color)"); c.fill = GridBagConstraints.HORIZONTAL;
text2.setAlignmentX(Component.CENTER_ALIGNMENT); c.weightx = 0.10;
p0.add(text2); c.gridx = 2;
c.gridy = 0;
c.ipadx = 0;
JLabel text2 = new JLabel("(X = random color)");
mainPanel.add(text2);
p0.add(Box.createVerticalStrut(5)); // Format/set label
JPanel jPanel = new JPanel(); JLabel formatSetText = new JLabel("Choose format/set for your deck:");
JLabel text3 = new JLabel("Choose sets:"); c.fill = GridBagConstraints.HORIZONTAL;
cbSets = new JComboBox<String>(ConstructedFormats.getTypes()); c.gridx = 0;
c.gridy = 1;
c.weightx = 0.10;
mainPanel.add(formatSetText, c);
// Format/set dropdown
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
c.ipadx = 30;
c.insets = new Insets(5, 10, 0, 10);
c.weightx = 0.90;
cbSets = new JComboBox<>(ConstructedFormats.getTypes());
cbSets.setSelectedIndex(0); cbSets.setSelectedIndex(0);
cbSets.setPreferredSize(new Dimension(300, 25)); mainPanel.add(cbSets, c);
cbSets.setMaximumSize(new Dimension(300, 25));
cbSets.setAlignmentX(Component.LEFT_ALIGNMENT);
jPanel.add(text3);
jPanel.add(cbSets);
p0.add(jPanel);
String prefSet = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SET, null); String prefSet = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SET, null);
if (prefSet != null) { if (prefSet != null) {
cbSets.setSelectedItem(prefSet); cbSets.setSelectedItem(prefSet);
} }
p0.add(Box.createVerticalStrut(5)); // Deck size label
JPanel jPanel2 = new JPanel(); c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(0, 15, 0, 0);
c.ipadx = 0;
c.gridx = 0;
c.gridy = 2;
c.weightx = 0.10;
JLabel textDeckSize = new JLabel("Deck size:"); JLabel textDeckSize = new JLabel("Deck size:");
cbDeckSize = new JComboBox<String>(new String[] { "40", "60" }); mainPanel.add(textDeckSize, c);
// Deck size dropdown
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 2;
c.ipadx = 30;
c.insets = new Insets(5, 10, 0, 10);
c.weightx = 0.90;
cbDeckSize = new JComboBox<>(new String[] { "40", "60" });
cbDeckSize.setSelectedIndex(0); cbDeckSize.setSelectedIndex(0);
cbDeckSize.setPreferredSize(new Dimension(300, 25));
cbDeckSize.setMaximumSize(new Dimension(300, 25));
cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT); cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT);
jPanel2.add(textDeckSize); mainPanel.add(cbDeckSize, c);
jPanel2.add(cbDeckSize);
p0.add(jPanel2);
String prefSize = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_DECK_SIZE, "60"); String prefSize = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_DECK_SIZE, "60");
if (prefSet != null) { if (prefSet != null) {
cbDeckSize.setSelectedItem(prefSize); cbDeckSize.setSelectedItem(prefSize);
} }
p0.add(Box.createVerticalStrut(5)); JPanel jCheckBoxes = new JPanel(new FlowLayout(FlowLayout.LEFT));
JPanel jCheckBoxes = new JPanel();
// Singletons // Singletons
cSingleton = new JCheckBox("Singleton", false); cSingleton = new JCheckBox("Singleton", false);
@ -138,16 +177,47 @@ public class DeckGeneratorDialog {
cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled)); cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled));
jCheckBoxes.add(cNonBasicLands); jCheckBoxes.add(cNonBasicLands);
// Non-basic lands // 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.valueOf(colorlessEnabled));
jCheckBoxes.add(cColorless); jCheckBoxes.add(cColorless);
c.ipadx = 0;
c.gridx = 0;
c.gridy = 3;
c.weightx = 1;
c.gridwidth = 3;
mainPanel.add(jCheckBoxes, c);
// Create the advanced configuration panel
JPanel advancedPanel = createAdvancedPanel();
// Advanced checkbox (enable/disable advanced configuration)
cAdvanced = new JCheckBox("Advanced");
cAdvanced.setToolTipText("Enable advanced configuration options");
cAdvanced.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent itemEvent) {
boolean enable = cAdvanced.isSelected();
enableAdvancedPanel(enable);
}
});
// Advanced Checkbox
String advancedSavedValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, "false");
boolean advancedEnabled = Boolean.valueOf(advancedSavedValue);
enableAdvancedPanel(advancedEnabled);
cAdvanced.setSelected(advancedEnabled);
c.gridy = 4;
c.weightx = 0;
c.insets = new Insets(10, 15, 10, 0);
mainPanel.add(cAdvanced, c);
c.gridy = 5;
c.weightx = 1;
c.insets = new Insets(5, 10, 0, 5);
mainPanel.add(advancedPanel, c);
jCheckBoxes.setPreferredSize(new Dimension(450, 25));
jCheckBoxes.setMaximumSize(new Dimension(450, 25));
p0.add(jCheckBoxes);
btnGenerate = new JButton("Ok"); btnGenerate = new JButton("Ok");
btnGenerate.addActionListener(new ActionListener() { btnGenerate.addActionListener(new ActionListener() {
@ -169,12 +239,95 @@ public class DeckGeneratorDialog {
} }
}); });
JButton[] options = {btnGenerate, btnCancel}; JButton[] options = {btnGenerate, btnCancel};
JOptionPane optionPane = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]); JOptionPane optionPane = new JOptionPane(mainPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]);
dlg = optionPane.createDialog("Generating deck"); dlg = optionPane.createDialog("Generating Deck");
dlg.setResizable(false);
dlg.setVisible(true); dlg.setVisible(true);
dlg.dispose(); dlg.dispose();
} }
private void enableAdvancedPanel(boolean enable) {
adjustingSliderPanel.setEnabled(enable);
btnReset.setEnabled(enable);
cbCMC.setEnabled(enable);
averageCMCLabel.setEnabled(enable);
}
private JPanel createAdvancedPanel() {
JPanel advancedPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
// Average CMC Label
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0.10;
averageCMCLabel = new JLabel("Average CMC:");
advancedPanel.add(averageCMCLabel, c);
// CMC selection dropdown
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.90;
c.gridx = 2;
c.gridy = 0;
cbCMC = new JComboBox<>(DeckGeneratorCMC.values());
cbCMC.setSelectedItem(DeckGeneratorCMC.Default);
String cmcSelected = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED_CMC, DeckGeneratorCMC.Default.name());
cbCMC.setSelectedItem(DeckGeneratorCMC.valueOf(cmcSelected));
advancedPanel.add(cbCMC, c);
// Advanced percentage sliders
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 20;
c.ipadx = 40;
c.weightx = 1;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
c.insets = new Insets(10, 0, 0, 0);
adjustingSliderPanel = new RatioAdjustingSliderPanel();
// Restore saved slider values
String creaturePercentage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE,
Integer.toString(DeckGeneratorPool.DEFAULT_CREATURE_PERCENTAGE));
adjustingSliderPanel.setCreaturePercentage(Integer.parseInt(creaturePercentage));
String nonCreaturePercentage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE,
Integer.toString(DeckGeneratorPool.DEFAULT_NON_CREATURE_PERCENTAGE));
adjustingSliderPanel.setNonCreaturePercentage(Integer.parseInt(nonCreaturePercentage));
String landPercentage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_LAND_PERCENTAGE,
Integer.toString(DeckGeneratorPool.DEFAULT_LAND_PERCENTAGE));
adjustingSliderPanel.setLandPercentage(Integer.parseInt(landPercentage));
advancedPanel.add(adjustingSliderPanel, c);
// Reset
c.fill = GridBagConstraints.NONE;
c.ipadx = 0;
c.ipady = 0;
c.weightx = 1.0;
c.anchor = GridBagConstraints.LAST_LINE_END;
c.insets = new Insets(10,10, 0, 0);
c.gridx = 2;
c.gridwidth = 1;
c.gridy = 2;
btnReset = new JButton("Reset");
btnReset.setToolTipText("Reset advanced dialog to default values");
btnReset.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
cbCMC.setSelectedItem(DeckGeneratorCMC.Default);
adjustingSliderPanel.resetValues();
}
});
advancedPanel.add(btnReset, c);
// Add a border around the advanced bits
CompoundBorder border = BorderFactory.createCompoundBorder(new EtchedBorder(), new EmptyBorder(10, 10, 10, 10));
advancedPanel.setBorder(border);
return advancedPanel;
}
public void cleanUp() { public void cleanUp() {
for (ActionListener al: btnGenerate.getActionListeners()) { for (ActionListener al: btnGenerate.getActionListeners()) {
btnGenerate.removeActionListener(al); btnGenerate.removeActionListener(al);
@ -182,6 +335,12 @@ public class DeckGeneratorDialog {
for (ActionListener al: btnCancel.getActionListeners()) { for (ActionListener al: btnCancel.getActionListeners()) {
btnCancel.removeActionListener(al); btnCancel.removeActionListener(al);
} }
for (ActionListener al: btnReset.getActionListeners()) {
btnReset.removeActionListener(al);
}
for(ItemListener il: cAdvanced.getItemListeners()) {
cAdvanced.removeItemListener(il);
}
} }
public String saveDeck(Deck deck) { public String saveDeck(Deck deck) {
@ -189,7 +348,7 @@ public class DeckGeneratorDialog {
// Random directory through the system property to avoid random numeric string attached to temp files. // Random directory through the system property to avoid random numeric string attached to temp files.
String tempDir = System.getProperty("java.io.tmpdir"); String tempDir = System.getProperty("java.io.tmpdir");
// Generated deck has a nice unique name which corresponds to the timestamp at which it was created. // Generated deck has a nice unique name which corresponds to the timestamp at which it was created.
String deckName = "Generated-Deck-" + dateFormat.format( new Date()); String deckName = "Generated-Deck-" + dateFormat.format(new Date());
File tmp = new File(tempDir + File.separator + deckName + ".dck"); File tmp = new File(tempDir + File.separator + deckName + ".dck");
tmp.createNewFile(); tmp.createNewFile();
deck.setName(deckName); deck.setName(deckName);
@ -230,10 +389,42 @@ public class DeckGeneratorDialog {
return selected; return selected;
} }
public boolean isAdvanced() {
boolean selected = cAdvanced.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, Boolean.toString(selected));
return selected;
}
public int getCreaturePercentage() {
int percentage = adjustingSliderPanel.getCreaturePercentage();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE, Integer.toString(percentage));
return percentage;
}
public int getNonCreaturePercentage() {
int percentage = adjustingSliderPanel.getNonCreaturePercentage();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE, Integer.toString(percentage));
return percentage;
}
public int getLandPercentage() {
int percentage = adjustingSliderPanel.getLandPercentage();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_LAND_PERCENTAGE, Integer.toString(percentage));
return percentage;
}
public int getDeckSize() { public int getDeckSize() {
return Integer.parseInt(cbDeckSize.getSelectedItem().toString()); return Integer.parseInt(cbDeckSize.getSelectedItem().toString());
} }
public DeckGeneratorCMC getDeckGeneratorCMC() {
DeckGeneratorCMC selectedCMC = (DeckGeneratorCMC)cbCMC.getSelectedItem();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED_CMC, selectedCMC.name());
return selectedCMC;
}
public String getSelectedColors() { public String getSelectedColors() {
if (selectedColors != null) { if (selectedColors != null) {
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_DECK_SIZE, cbDeckSize.getSelectedItem().toString()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_DECK_SIZE, cbDeckSize.getSelectedItem().toString());

View file

@ -41,18 +41,15 @@ import java.util.*;
*/ */
public class DeckGeneratorPool public class DeckGeneratorPool
{ {
// Constants for a 40 card deck
private static final int CREATURE_COUNT_40 = 15;
private static final int LAND_COUNT_40 = 17; public static int DEFAULT_CREATURE_PERCENTAGE = 38;
private static final int NONCREATURE_COUNT_40 = 8; public static int DEFAULT_NON_CREATURE_PERCENTAGE = 21;
// Constants for a 60 card deck public static int DEFAULT_LAND_PERCENTAGE = 41;
private static final int CREATURE_COUNT_60 = 23;
private static final int LAND_COUNT_60 = 24;
private static final int NONCREATURE_COUNT_60 = 13;
private final List<ColoredManaSymbol> allowedColors; private final List<ColoredManaSymbol> allowedColors;
private boolean colorlessAllowed; private boolean colorlessAllowed;
private final List<DeckGeneratorCMC> 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 landCount; private final int landCount;
@ -69,7 +66,21 @@ public class DeckGeneratorPool
private List<Card> reserveSpells = new ArrayList<>(); private List<Card> reserveSpells = new ArrayList<>();
private Deck deck; private Deck deck;
public DeckGeneratorPool(final int deckSize, final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed) /**
* Creates a card pool with specified criterea used when generating a deck.
*
* @param deckSize the size of the complete 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 landPercentage percentage of lands to use when generating the 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 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 deckGeneratorCMC the CMC curve to use for this deck
*/
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)
{ {
this.deckSize = deckSize; this.deckSize = deckSize;
this.allowedColors = allowedColors; this.allowedColors = allowedColors;
@ -78,28 +89,26 @@ public class DeckGeneratorPool
this.deck = new Deck(); this.deck = new Deck();
if(this.deckSize > 40) { // Advanced (CMC Slider panel and curve drop-down in the dialog)
this.creatureCount = CREATURE_COUNT_60; if(isAdvanced) {
this.nonCreatureCount = NONCREATURE_COUNT_60; this.creatureCount = (int)Math.ceil((deckSize / 100.0) * creaturePercentage);
this.landCount = LAND_COUNT_60; this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0)* nonCreaturePercentage);
poolCMCs = new ArrayList<DeckGeneratorCMC>() {{ this.landCount = (int)Math.ceil((deckSize / 100.0)* landPercentage);
add(new DeckGeneratorCMC(0, 2, 0.20f)); if(this.deckSize == 60) {
add(new DeckGeneratorCMC(3, 5, 0.50f)); this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC();
add(new DeckGeneratorCMC(6, 7, 0.25f)); } else {
add(new DeckGeneratorCMC(8, 100, 0.05f)); this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC();
}}; }
} else {
// Ignore the advanced group, just use defaults
this.creatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_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);
if(this.deckSize == 60) {
this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC();
} else {
this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC();
} }
else {
this.creatureCount = CREATURE_COUNT_40;
this.nonCreatureCount = NONCREATURE_COUNT_40;
this.landCount = LAND_COUNT_40;
poolCMCs = new ArrayList<DeckGeneratorCMC>() {{
add(new DeckGeneratorCMC(0, 2, 0.30f));
add(new DeckGeneratorCMC(3, 4, 0.45f));
add(new DeckGeneratorCMC(5, 6, 0.20f));
add(new DeckGeneratorCMC(7, 100, 0.05f));
}};
} }
if(allowedColors.size() == 1) { if(allowedColors.size() == 1) {
@ -114,16 +123,15 @@ public class DeckGeneratorPool
* @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> getCMCsForSpellCount(int cardsCount) { public List<DeckGeneratorCMC.CMC> getCMCsForSpellCount(int cardsCount) {
List<DeckGeneratorCMC> 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 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;
} }
/** /**
* 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.
@ -391,18 +399,16 @@ public class DeckGeneratorPool
int spellsNeeded = nonLandSize-spellSize; int spellsNeeded = nonLandSize-spellSize;
// If we haven't got enough spells in reserve to fulfil the amount we need, we can't continue. // 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) {
throw new IllegalStateException("Not enough cards found to generate deck. Please try again");
}
List<Card> spellsToAdd = new ArrayList<>(spellsNeeded); List<Card> spellsToAdd = new ArrayList<>(spellsNeeded);
// Initial reservoir // Initial reservoir
for(int i = 0; i < spellsNeeded; i++) for (int i = 0; i < spellsNeeded; i++)
spellsToAdd.add(reserveSpells.get(i)); spellsToAdd.add(reserveSpells.get(i));
for(int i = spellsNeeded+1; i < reserveSpells.size()-1; i++) { for (int i = spellsNeeded + 1; i < reserveSpells.size() - 1; i++) {
int j = random.nextInt(i); int j = random.nextInt(i);
Card randomCard = reserveSpells.get(j); Card randomCard = reserveSpells.get(j);
if (isValidSpellCard(randomCard) && j < spellsToAdd.size()) { if (isValidSpellCard(randomCard) && j < spellsToAdd.size()) {
@ -412,24 +418,35 @@ public class DeckGeneratorPool
// Add randomly selected spells needed // Add randomly selected spells needed
deckCards.addAll(spellsToAdd); deckCards.addAll(spellsToAdd);
} }
}
// 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(random.nextInt(deckCards.size())); deckCards.remove(random.nextInt(deckCards.size()));
} }
} }
// Not strictly necessary as we check when adding cards, but worth double checking anyway. // 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. Please try again"); throw new IllegalStateException("Not enough cards found to generate deck.");
} }
// Return the fixed amount // Return the fixed amount
return deckCards; return deckCards;
} }
/**
* Returns if this land taps for the given color.
* Basic string matching to check the ability adds one of the chosen mana when tapped.
* @param ability MockAbility of the land card
* @param symbol colored mana symbol.
* @return if the ability is tapping to produce the mana the symbol represents.
*/
private boolean landTapsForAllowedColor(String ability, String symbol) {
return ability.matches(".*Add \\{" + symbol + "\\} to your mana pool.");
}
/** /**
* 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.
@ -455,17 +472,6 @@ public class DeckGeneratorPool
return false; return false;
} }
/**
* Returns if this land taps for the given color.
* Basic string matching to check the ability adds one of the chosen mana when tapped.
* @param ability MockAbility of the land card
* @param symbol colored mana symbol.
* @return if the ability is tapping to produce the mana the symbol represents.
*/
private boolean landTapsForAllowedColor(String ability, String symbol) {
return ability.matches(".*Add \\{" + symbol + "\\} to your mana pool.");
}
/** /**
* 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.

View file

@ -0,0 +1,248 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.client.deck.generator;
import mage.client.deck.generator.DeckGeneratorPool;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* @author Simown
*/
public class RatioAdjustingSliderPanel extends JPanel {
private JStorageSlider creatureSlider, nonCreatureSlider, landSlider;
private List<JLabel> textLabels = new ArrayList<>();
private AdjustingSliderGroup sg;
private class JStorageSlider extends JSlider {
// Slider stores its initial value to revert to when reset
private int defaultValue;
private int previousValue;
public JStorageSlider(int min, int max, int value) {
super(min, max, value);
previousValue = value;
defaultValue = value;
setMinorTickSpacing(5);
setMajorTickSpacing(10);
setPaintTicks(true);
setPaintLabels(true);
setLabelTable(createStandardLabels(10));
}
public int getPreviousValue() {
return previousValue;
}
public void setPreviousValue(int value) {
previousValue = value;
}
public void resetDefault() {
this.setValue(defaultValue);
previousValue = defaultValue;
}
}
private class AdjustingSliderGroup
{
private final ArrayList<JStorageSlider> storageSliders;
private int sliderIndex = 0;
AdjustingSliderGroup(JStorageSlider... sliders)
{
storageSliders = new ArrayList<>();
for(JStorageSlider slider: sliders) {
storageSliders.add(slider);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
fireSliderChangedEvent((JStorageSlider) e.getSource());
}
});
}
}
public void fireSliderChangedEvent(JStorageSlider source) {
// We don't want to do anything if the value isn't changing
if(!source.getValueIsAdjusting())
return;
// Update the slider depending on how much it's changed relative to its previous position
int change = (source.getValue() - source.getPreviousValue());
updateSliderPosition(change, source);
}
private void updateSliderPosition(int change, JStorageSlider source) {
int remaining = change;
while (remaining != 0) {
// Get the currently indexed slider
JStorageSlider slider = storageSliders.get(sliderIndex);
// If it's not the slider that fired the event
if (slider != source) {
// Check we don't go over the upper and lower bounds
if (remaining < 0 || (remaining > 0 && slider.getValue() > 0)) {
// Adjust the currently selected slider by +/- 1
int adjustment = Integer.signum(remaining);
slider.setValue(slider.getValue() - adjustment);
remaining -= adjustment;
}
}
// Select the next slider in the list of sliders
sliderIndex = (sliderIndex + 1) % storageSliders.size();
}
for (JStorageSlider slider : storageSliders) {
slider.setPreviousValue(slider.getValue());
}
}
List<JStorageSlider> getSliders() {
return storageSliders;
}
}
public RatioAdjustingSliderPanel() {
initPanel();
}
private void initPanel() {
// Create three sliders with default values
creatureSlider = new JStorageSlider(0, 100, DeckGeneratorPool.DEFAULT_CREATURE_PERCENTAGE);
nonCreatureSlider = new JStorageSlider(0, 100, DeckGeneratorPool.DEFAULT_NON_CREATURE_PERCENTAGE);
landSlider = new JStorageSlider(0, 100, DeckGeneratorPool.DEFAULT_LAND_PERCENTAGE);
sg = new AdjustingSliderGroup(creatureSlider, nonCreatureSlider, landSlider);
this.setLayout(new GridLayout(3, 1));
this.add(createSliderPanel("Creatures ", creatureSlider));
this.add(createSliderPanel("Non-creatures ", nonCreatureSlider));
this.add(createSliderPanel("Lands ", landSlider));
setEnabled(true);
}
private JPanel createSliderPanel(String label, JStorageSlider slider) {
JPanel sliderPanel = new JPanel(new BorderLayout());
// Title
JLabel titleLabel = new JLabel(label);
textLabels.add(titleLabel);
sliderPanel.add(titleLabel, BorderLayout.WEST);
// Slider
slider.setToolTipText("Percentage of " + label.trim().toLowerCase() + " in the generated deck.");
sliderPanel.add(slider, BorderLayout.CENTER);
// Percentage
JLabel percentageLabel = createChangingPercentageLabel(slider);
textLabels.add(percentageLabel);
sliderPanel.add(percentageLabel, BorderLayout.EAST);
return sliderPanel;
}
private static JLabel createChangingPercentageLabel(final JSlider slider) {
final JLabel label = new JLabel(" " + String.valueOf(slider.getValue()) + "%");
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
String value = String.valueOf(slider.getValue());
StringBuilder labelBuilder = new StringBuilder();
// Pad with spaces so all percentage labels are of equal size
for(int i = 0; i < (5-value.length()); i++) {
labelBuilder.append(" ");
}
labelBuilder.append(value);
labelBuilder.append("%");
label.setText(labelBuilder.toString());
}
});
return label;
}
@Override
public void setEnabled(boolean enabled) {
for(JStorageSlider slider: sg.getSliders()) {
slider.setEnabled(enabled);
}
for(JLabel label: textLabels) {
label.setEnabled(enabled);
}
}
public void resetValues() {
for(JStorageSlider slider: sg.getSliders()) {
slider.resetDefault();
}
}
public int getCreaturePercentage() {
return creatureSlider.getValue();
}
public int getNonCreaturePercentage() {
return nonCreatureSlider.getValue();
}
public int getLandPercentage() {
return landSlider.getValue();
}
public void setCreaturePercentage(int percentage) {
creatureSlider.setValue(percentage);
creatureSlider.previousValue = percentage;
}
public void setNonCreaturePercentage(int percentage) {
nonCreatureSlider.setValue(percentage);
nonCreatureSlider.previousValue = percentage;
}
public void setLandPercentage(int percentage) {
landSlider.setValue(percentage);
landSlider.previousValue = percentage;
}
}

View file

@ -236,6 +236,12 @@ public class PreferencesDialog extends javax.swing.JDialog {
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_ADVANCED = "newDeckGeneratorAdvanced";
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_LAND_PERCENTAGE = "newDeckGeneratorLandPercentage";
public static final String KEY_NEW_DECK_GENERATOR_ADVANCED_CMC = "newDeckGeneratorAdvancedCMC";
// used to save and restore the settings for the cardArea (draft, sideboarding, deck builder) // used to save and restore the settings for the cardArea (draft, sideboarding, deck builder)
public static final String KEY_DRAFT_VIEW = "draftView"; public static final String KEY_DRAFT_VIEW = "draftView";