Support generating sealed deck for mobile game

This commit is contained in:
drdev
2014-06-08 21:56:57 +00:00
parent 2721901ea0
commit 5ec6e504bc
7 changed files with 231 additions and 155 deletions

View File

@@ -2,40 +2,24 @@ package forge.screens.home.sanctioned;
import forge.UiCommand; import forge.UiCommand;
import forge.Singletons; import forge.Singletons;
import forge.card.MagicColor;
import forge.deck.*; import forge.deck.*;
import forge.game.GameType; import forge.game.GameType;
import forge.gui.GuiChoose;
import forge.deck.DeckProxy; import forge.deck.DeckProxy;
import forge.gui.framework.FScreen; import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc; import forge.gui.framework.ICDoc;
import forge.item.InventoryItem; import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.itemmanager.ItemManagerConfig; import forge.itemmanager.ItemManagerConfig;
import forge.limited.DraftRankCache;
import forge.limited.LimitedPoolType;
import forge.limited.SealedCardPoolGenerator; import forge.limited.SealedCardPoolGenerator;
import forge.limited.SealedDeckBuilder;
import forge.model.FModel; import forge.model.FModel;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.screens.deckeditor.CDeckEditorUI; import forge.screens.deckeditor.CDeckEditorUI;
import forge.screens.deckeditor.controllers.ACEditorBase; import forge.screens.deckeditor.controllers.ACEditorBase;
import forge.screens.deckeditor.controllers.CEditorLimited; import forge.screens.deckeditor.controllers.CEditorLimited;
import forge.toolbox.FOptionPane; import forge.toolbox.FOptionPane;
import forge.util.MyRandom;
import forge.util.storage.IStorage;
import org.apache.commons.lang3.StringUtils;
import javax.swing.*; import javax.swing.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/** /**
* Controls the sealed submenu in the home UI. * Controls the sealed submenu in the home UI.
@@ -48,8 +32,6 @@ public enum CSubmenuSealed implements ICDoc {
/** */ /** */
SINGLETON_INSTANCE; SINGLETON_INSTANCE;
private Map<String, Deck> aiDecks;
private final UiCommand cmdDeckSelect = new UiCommand() { private final UiCommand cmdDeckSelect = new UiCommand() {
@Override @Override
public void run() { public void run() {
@@ -93,16 +75,6 @@ public enum CSubmenuSealed implements ICDoc {
*/ */
@Override @Override
public void update() { public void update() {
final List<Deck> humanDecks = new ArrayList<Deck>();
aiDecks = new HashMap<String, Deck>();
// Since AI decks are tied directly to the human choice,
// they're just mapped in a parallel map and grabbed when the game starts.
for (final DeckGroup d : FModel.getDecks().getSealed()) {
aiDecks.put(d.getName(), d.getAiDecks().get(0));
humanDecks.add(d.getHumanDeck());
}
final VSubmenuSealed view = VSubmenuSealed.SINGLETON_INSTANCE; final VSubmenuSealed view = VSubmenuSealed.SINGLETON_INSTANCE;
view.getLstDecks().setPool(DeckProxy.getAllSealedDecks(FModel.getDecks().getSealed())); view.getLstDecks().setPool(DeckProxy.getAllSealedDecks(FModel.getDecks().getSealed()));
view.getLstDecks().setup(ItemManagerConfig.SEALED_DECKS); view.getLstDecks().setup(ItemManagerConfig.SEALED_DECKS);
@@ -141,81 +113,8 @@ public enum CSubmenuSealed implements ICDoc {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T extends DeckBase> void setupSealed() { private <T extends DeckBase> void setupSealed() {
final String prompt = "Choose Sealed Deck Format"; final DeckGroup sealed = SealedCardPoolGenerator.generateSealedDeck();
final LimitedPoolType poolType = GuiChoose.oneOrNone(prompt, LimitedPoolType.values()); if (sealed == null) { return; }
if (poolType == null) { return; }
SealedCardPoolGenerator sd = new SealedCardPoolGenerator(poolType);
if (sd.isEmpty()) { return; }
final CardPool humanPool = sd.getCardPool(true);
if (humanPool == null) { return; }
// System.out.println(humanPool);
// This seems to be limited by the MAX_DRAFT_PLAYERS constant
// in DeckGroupSerializer.java. You could create more AI decks
// but only the first seven would load. --BBU
Integer rounds = GuiChoose.getInteger("How many opponents are you willing to face?", 1, 7);
if (rounds == null) { return; }
final String sDeckName = FOptionPane.showInputDialog(
"Save this card pool as:",
"Save Card Pool",
FOptionPane.QUESTION_ICON);
if (StringUtils.isBlank(sDeckName)) {
return;
}
final Deck deck = new Deck(sDeckName);
deck.getOrCreate(DeckSection.Sideboard).addAll(humanPool);
final int landsCount = 10;
final boolean isZendikarSet = sd.getLandSetCode().equals("ZEN"); // we want to generate one kind of Zendikar lands at a time only
final boolean zendikarSetMode = MyRandom.getRandom().nextBoolean();
for (final String element : MagicColor.Constant.BASIC_LANDS) {
int numArt = FModel.getMagicDb().getCommonCards().getArtCount(element, sd.getLandSetCode());
int minArtIndex = isZendikarSet ? (zendikarSetMode ? 1 : 5) : 1;
int maxArtIndex = isZendikarSet ? minArtIndex + 3 : numArt;
if (FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_ART_IN_POOLS)) {
for (int i = minArtIndex; i <= maxArtIndex; i++) {
deck.get(DeckSection.Sideboard).add(element, sd.getLandSetCode(), i, numArt > 1 ? landsCount : 30);
}
} else {
deck.get(DeckSection.Sideboard).add(element, sd.getLandSetCode(), 30);
}
}
final IStorage<DeckGroup> sealedDecks = FModel.getDecks().getSealed();
if (sealedDecks.contains(sDeckName)) {
if (!FOptionPane.showConfirmDialog(
"'" + sDeckName + "' already exists. Do you want to replace it?",
"Sealed Deck Game Exists")) {
return;
}
sealedDecks.delete(sDeckName);
}
final DeckGroup sealed = new DeckGroup(sDeckName);
sealed.setHumanDeck(deck);
for (int i = 0; i < rounds; i++) {
// Generate other decks for next N opponents
final CardPool aiPool = sd.getCardPool(false);
if (aiPool == null) { return; }
sealed.addAiDeck(new SealedDeckBuilder(aiPool.toFlatList()).buildDeck());
}
// Rank the AI decks
sealed.rankAiDecks(new DeckComparer());
FModel.getDecks().getSealed().add(sealed);
final ACEditorBase<? extends InventoryItem, T> editor = (ACEditorBase<? extends InventoryItem, T>) new CEditorLimited( final ACEditorBase<? extends InventoryItem, T> editor = (ACEditorBase<? extends InventoryItem, T>) new CEditorLimited(
FModel.getDecks().getSealed(), FScreen.DECK_EDITOR_SEALED); FModel.getDecks().getSealed(), FScreen.DECK_EDITOR_SEALED);
@@ -232,47 +131,4 @@ public enum CSubmenuSealed implements ICDoc {
public UiCommand getCommandOnSelect() { public UiCommand getCommandOnSelect() {
return null; return null;
} }
static class DeckComparer implements java.util.Comparator<Deck> {
public double getDraftValue(Deck d) {
double value = 0;
double divider = 0;
if (d.getMain().isEmpty()) {
return 0;
}
double best = 1.0;
for (Entry<PaperCard, Integer> kv : d.getMain()) {
PaperCard evalCard = kv.getKey();
int count = kv.getValue();
if (DraftRankCache.getRanking(evalCard.getName(), evalCard.getEdition()) != null) {
double add = DraftRankCache.getRanking(evalCard.getName(), evalCard.getEdition());
// System.out.println(evalCard.getName() + " is worth " + add);
value += add * count;
divider += count;
if (best > add) {
best = add;
}
}
}
if (divider == 0 || value == 0) {
return 0;
}
value /= divider;
return (20.0 / (best + (2 * value)));
}
@Override
public int compare(Deck o1, Deck o2) {
double delta = getDraftValue(o1) - getDraftValue(o2);
if ( delta > 0 ) return 1;
if ( delta < 0 ) return -1;
return 0;
}
}
} }

View File

@@ -115,6 +115,11 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
} }
tabPage.initialize(); tabPage.initialize();
} }
//if opening brand new sealed deck, show sideboard (card pool) by default
if (editorType == EditorType.Sealed && deck.getMain().isEmpty()) {
setSelectedPage(sideboardPage);
}
} }
public EditorType getEditorType() { public EditorType getEditorType() {

View File

@@ -38,6 +38,12 @@ public class DraftScreen extends LaunchScreen {
lstDecks.setPool(DeckProxy.getDraftDecks(FModel.getDecks().getDraft())); lstDecks.setPool(DeckProxy.getDraftDecks(FModel.getDecks().getDraft()));
lstDecks.setup(ItemManagerConfig.DRAFT_DECKS); lstDecks.setup(ItemManagerConfig.DRAFT_DECKS);
lstDecks.setItemActivateHandler(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
editSelectedDeck();
}
});
btnNewDraft.setFont(FSkinFont.get(16)); btnNewDraft.setFont(FSkinFont.get(16));
btnNewDraft.setCommand(new FEventHandler() { btnNewDraft.setCommand(new FEventHandler() {
@@ -71,14 +77,18 @@ public class DraftScreen extends LaunchScreen {
btnEditDeck.setCommand(new FEventHandler() { btnEditDeck.setCommand(new FEventHandler() {
@Override @Override
public void handleEvent(FEvent e) { public void handleEvent(FEvent e) {
final DeckProxy deck = lstDecks.getSelectedItem(); editSelectedDeck();
if (deck == null) { return; }
Forge.openScreen(new FDeckEditor(EditorType.Draft, deck.getDeck()));
} }
}); });
} }
private void editSelectedDeck() {
final DeckProxy deck = lstDecks.getSelectedItem();
if (deck == null) { return; }
Forge.openScreen(new FDeckEditor(EditorType.Draft, deck.getDeck()));
}
@Override @Override
protected void doLayoutAboveBtnStart(float startY, float width, float height) { protected void doLayoutAboveBtnStart(float startY, float width, float height) {
float x = ItemFilter.PADDING; float x = ItemFilter.PADDING;

View File

@@ -42,7 +42,7 @@ public class HomeScreen extends FScreen {
public void handleEvent(FEvent e) { public void handleEvent(FEvent e) {
Forge.openScreen(new SealedScreen()); Forge.openScreen(new SealedScreen());
} }
}, false); }, true);
addButton("Quest Mode", new FEventHandler() { addButton("Quest Mode", new FEventHandler() {
@Override @Override
public void handleEvent(FEvent e) { public void handleEvent(FEvent e) {

View File

@@ -1,17 +1,91 @@
package forge.screens.sealed; package forge.screens.sealed;
import forge.FThreads;
import forge.Forge;
import forge.assets.FSkinFont;
import forge.deck.DeckGroup;
import forge.deck.DeckProxy;
import forge.deck.FDeckChooser;
import forge.deck.FDeckEditor;
import forge.deck.FDeckEditor.EditorType;
import forge.game.GameType; import forge.game.GameType;
import forge.itemmanager.DeckManager;
import forge.itemmanager.ItemManagerConfig;
import forge.itemmanager.filters.ItemFilter;
import forge.limited.SealedCardPoolGenerator;
import forge.model.FModel;
import forge.screens.LaunchScreen; import forge.screens.LaunchScreen;
import forge.toolbox.FButton;
import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler;
import forge.util.ThreadUtil;
public class SealedScreen extends LaunchScreen { public class SealedScreen extends LaunchScreen {
private final DeckManager lstDecks = add(new DeckManager(GameType.Draft));
private final FButton btnNewDeck = add(new FButton("New Deck"));
private final FButton btnEditDeck = add(new FButton("Edit Deck"));
public SealedScreen() { public SealedScreen() {
super("Sealed Deck"); super("Sealed Deck");
lstDecks.setPool(DeckProxy.getAllSealedDecks(FModel.getDecks().getSealed()));
lstDecks.setup(ItemManagerConfig.SEALED_DECKS);
lstDecks.setItemActivateHandler(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
editSelectedDeck();
}
});
btnNewDeck.setFont(FSkinFont.get(16));
btnNewDeck.setCommand(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
ThreadUtil.invokeInGameThread(new Runnable() { //must run in game thread to prevent blocking UI thread
@Override
public void run() {
final DeckGroup sealed = SealedCardPoolGenerator.generateSealedDeck();
if (sealed == null) { return; }
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
Forge.openScreen(new FDeckEditor(EditorType.Sealed, sealed.getHumanDeck()));
}
});
}
});
}
});
btnEditDeck.setFont(btnNewDeck.getFont());
btnEditDeck.setCommand(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
editSelectedDeck();
}
});
}
private void editSelectedDeck() {
final DeckProxy deck = lstDecks.getSelectedItem();
if (deck == null) { return; }
Forge.openScreen(new FDeckEditor(EditorType.Sealed, deck.getDeck()));
} }
@Override @Override
protected void doLayoutAboveBtnStart(float startY, float width, float height) { protected void doLayoutAboveBtnStart(float startY, float width, float height) {
// TODO Auto-generated method stub float x = ItemFilter.PADDING;
float y = startY;
float w = width - 2 * x;
float buttonWidth = (w - FDeckChooser.PADDING) / 2;
float buttonHeight = btnNewDeck.getAutoSizeBounds().height * 1.2f;
float listHeight = height - buttonHeight - y - FDeckChooser.PADDING;
lstDecks.setBounds(x, y, w, listHeight);
y += listHeight + FDeckChooser.PADDING;
btnNewDeck.setBounds(x, y, buttonWidth, buttonHeight);
btnEditDeck.setBounds(x + buttonWidth + FDeckChooser.PADDING, y, buttonWidth, buttonHeight);
} }
@Override @Override

View File

@@ -110,7 +110,7 @@ public class FOptionPane extends FDialog {
inputField = cbInput; inputField = cbInput;
} }
final FOptionPane optionPane = new FOptionPane(message, title, icon, inputField, new String[] {"OK", "Cancel"}, -1, new Callback<Integer>() { final FOptionPane optionPane = new FOptionPane(message, title, icon, inputField, new String[] {"OK", "Cancel"}, 0, new Callback<Integer>() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void run(Integer result) { public void run(Integer result) {

View File

@@ -17,27 +17,37 @@
*/ */
package forge.limited; package forge.limited;
import forge.assets.FSkinProp;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.card.IUnOpenedProduct; import forge.card.IUnOpenedProduct;
import forge.card.MagicColor;
import forge.card.UnOpenedProduct; import forge.card.UnOpenedProduct;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckGroup;
import forge.deck.DeckSection;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.item.SealedProduct; import forge.item.SealedProduct;
import forge.model.CardBlock; import forge.model.CardBlock;
import forge.model.FModel; import forge.model.FModel;
import forge.model.UnOpenedMeta; import forge.model.UnOpenedMeta;
import forge.properties.ForgeConstants; import forge.properties.ForgeConstants;
import forge.properties.ForgePreferences.FPref;
import forge.util.FileUtil; import forge.util.FileUtil;
import forge.util.MyRandom;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.gui.SGuiChoose; import forge.util.gui.SGuiChoose;
import forge.util.gui.SOptionPane; import forge.util.gui.SOptionPane;
import forge.util.storage.IStorage;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
import java.util.Map.Entry;
/** /**
* <p> * <p>
@@ -56,6 +66,84 @@ public class SealedCardPoolGenerator {
/** The Land set code. */ /** The Land set code. */
private String landSetCode = null; private String landSetCode = null;
public static DeckGroup generateSealedDeck() {
final String prompt = "Choose Sealed Deck Format";
final LimitedPoolType poolType = SGuiChoose.oneOrNone(prompt, LimitedPoolType.values());
if (poolType == null) { return null; }
SealedCardPoolGenerator sd = new SealedCardPoolGenerator(poolType);
if (sd.isEmpty()) { return null; }
final CardPool humanPool = sd.getCardPool(true);
if (humanPool == null) { return null; }
// System.out.println(humanPool);
// This seems to be limited by the MAX_DRAFT_PLAYERS constant
// in DeckGroupSerializer.java. You could create more AI decks
// but only the first seven would load. --BBU
Integer rounds = SGuiChoose.getInteger("How many opponents are you willing to face?", 1, 7);
if (rounds == null) { return null; }
final String sDeckName = SOptionPane.showInputDialog(
"Save this card pool as:",
"Save Card Pool",
FSkinProp.ICO_QUESTION);
if (StringUtils.isBlank(sDeckName)) {
return null;
}
final IStorage<DeckGroup> sealedDecks = FModel.getDecks().getSealed();
if (sealedDecks.contains(sDeckName)) {
if (!SOptionPane.showConfirmDialog(
"'" + sDeckName + "' already exists. Do you want to replace it?",
"Sealed Deck Game Exists")) {
return null;
}
sealedDecks.delete(sDeckName);
}
final Deck deck = new Deck(sDeckName);
deck.getOrCreate(DeckSection.Sideboard).addAll(humanPool);
final int landsCount = 10;
final boolean isZendikarSet = sd.getLandSetCode().equals("ZEN"); // we want to generate one kind of Zendikar lands at a time only
final boolean zendikarSetMode = MyRandom.getRandom().nextBoolean();
for (final String element : MagicColor.Constant.BASIC_LANDS) {
int numArt = FModel.getMagicDb().getCommonCards().getArtCount(element, sd.getLandSetCode());
int minArtIndex = isZendikarSet ? (zendikarSetMode ? 1 : 5) : 1;
int maxArtIndex = isZendikarSet ? minArtIndex + 3 : numArt;
if (FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_ART_IN_POOLS)) {
for (int i = minArtIndex; i <= maxArtIndex; i++) {
deck.get(DeckSection.Sideboard).add(element, sd.getLandSetCode(), i, numArt > 1 ? landsCount : 30);
}
}
else {
deck.get(DeckSection.Sideboard).add(element, sd.getLandSetCode(), 30);
}
}
final DeckGroup sealed = new DeckGroup(sDeckName);
sealed.setHumanDeck(deck);
for (int i = 0; i < rounds; i++) {
// Generate other decks for next N opponents
final CardPool aiPool = sd.getCardPool(false);
if (aiPool == null) { return null; }
sealed.addAiDeck(new SealedDeckBuilder(aiPool.toFlatList()).buildDeck());
}
// Rank the AI decks
sealed.rankAiDecks(new SealedDeckComparer());
FModel.getDecks().getSealed().add(sealed);
return sealed;
}
/** /**
* <p> * <p>
* Constructor for SealedDeck. * Constructor for SealedDeck.
@@ -64,7 +152,7 @@ public class SealedCardPoolGenerator {
* @param poolType * @param poolType
* a {@link java.lang.String} object. * a {@link java.lang.String} object.
*/ */
public SealedCardPoolGenerator(final LimitedPoolType poolType) { private SealedCardPoolGenerator(final LimitedPoolType poolType) {
switch(poolType) { switch(poolType) {
case Full: case Full:
// Choose number of boosters // Choose number of boosters
@@ -368,4 +456,47 @@ public class SealedCardPoolGenerator {
public boolean isEmpty() { public boolean isEmpty() {
return product.isEmpty(); return product.isEmpty();
} }
private static class SealedDeckComparer implements java.util.Comparator<Deck> {
public double getDraftValue(Deck d) {
double value = 0;
double divider = 0;
if (d.getMain().isEmpty()) {
return 0;
}
double best = 1.0;
for (Entry<PaperCard, Integer> kv : d.getMain()) {
PaperCard evalCard = kv.getKey();
int count = kv.getValue();
if (DraftRankCache.getRanking(evalCard.getName(), evalCard.getEdition()) != null) {
double add = DraftRankCache.getRanking(evalCard.getName(), evalCard.getEdition());
// System.out.println(evalCard.getName() + " is worth " + add);
value += add * count;
divider += count;
if (best > add) {
best = add;
}
}
}
if (divider == 0 || value == 0) {
return 0;
}
value /= divider;
return (20.0 / (best + (2 * value)));
}
@Override
public int compare(Deck o1, Deck o2) {
double delta = getDraftValue(o1) - getDraftValue(o2);
if ( delta > 0 ) return 1;
if ( delta < 0 ) return -1;
return 0;
}
}
} }