diff --git a/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java b/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java new file mode 100644 index 00000000000..1a0e0b36c94 --- /dev/null +++ b/forge-core/src/main/java/forge/deck/generation/DeckGenerator4Color.java @@ -0,0 +1,134 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.deck.generation; + +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import forge.card.ColorSet; +import forge.card.MagicColor; +import forge.deck.CardPool; +import forge.deck.DeckFormat; +import forge.item.PaperCard; +import forge.util.MyRandom; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.util.List; + +/** + *

+ * Generate3ColorDeck class. + *

+ * + * @author Forge + * @version $Id$ + */ +public class DeckGenerator4Color extends DeckGeneratorBase { + @Override + protected final float getLandPercentage() { + return 0.44f; + } + @Override + protected final float getCreaturePercentage() { + return 0.33f; + } + @Override + protected final float getSpellPercentage() { + return 0.23f; + } + + @SuppressWarnings("unchecked") + final List> cmcLevels = Lists.newArrayList( + ImmutablePair.of(new FilterCMC(0, 2), 12), + ImmutablePair.of(new FilterCMC(3, 5), 9), + ImmutablePair.of(new FilterCMC(6, 20), 3) + ); + + public DeckGenerator4Color(IDeckGenPool pool0, DeckFormat format0, Predicate formatFilter0, final String clr1, final String clr2, final String clr3, final String clr4) { + super(pool0, format0, formatFilter0); + initialize(format0,clr1,clr2,clr3,clr4); + } + + public DeckGenerator4Color(IDeckGenPool pool0, DeckFormat format0, final String clr1, final String clr2, final String clr3, final String clr4) { + super(pool0, format0); + initialize(format0,clr1,clr2,clr3,clr4); + } + + private void initialize(DeckFormat format0, final String clr1, final String clr2, final String clr3, final String clr4){ + format0.adjustCMCLevels(cmcLevels); + + int c1 = MagicColor.fromName(clr1); + int c2 = MagicColor.fromName(clr2); + int c3 = MagicColor.fromName(clr3); + int c4 = MagicColor.fromName(clr4); + + int rc = 0; + int combo = c1 | c2 | c3 | c4; + + ColorSet param = ColorSet.fromMask(combo); + switch(param.countColors()) { + case 3: + colors = param; + return; + + case 0: + int color1 = r.nextInt(5); + int color2 = (color1 + 1 + r.nextInt(4)) % 5; + colors = ColorSet.fromMask(MagicColor.WHITE << color1 | MagicColor.WHITE << color2).inverse(); + return; + + case 1: + do { + rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5); + } while ( rc == combo ); + combo |= rc; + + //$FALL-THROUGH$ + case 2: + do { + rc = MagicColor.WHITE << MyRandom.getRandom().nextInt(5); + } while ( (rc & combo) != 0 ); + combo |= rc; + break; + } + colors = ColorSet.fromMask(combo); + } + + @Override + public final CardPool getDeck(final int size, final boolean forAi) { + addCreaturesAndSpells(size, cmcLevels, forAi); + + // Add lands + int numLands = Math.round(size * getLandPercentage()); + adjustDeckSize(size - numLands); + trace.append("numLands:").append(numLands).append("\n"); + + // Add dual lands + List duals = getDualLandList(); + for (String s : duals) { + this.cardCounts.put(s, 0); + } + + int dblsAdded = addSomeStr((numLands / 4), duals); + numLands -= dblsAdded; + + addBasicLand(numLands); + adjustDeckSize(size); + trace.append("DeckSize:").append(tDeck.countAll()).append("\n"); + return tDeck; + } +} diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java b/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java index 82385e0bdc4..3d40e0efac1 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/DecksComboBox.java @@ -25,8 +25,12 @@ public class DecksComboBox extends FComboBoxWrapper { addActionListener(getDeckTypeComboListener()); } - public void refresh(final DeckType deckType) { - setModel(new DefaultComboBoxModel(DeckType.ConstructedOptions)); + public void refresh(final DeckType deckType, final boolean isForCommander) { + if(isForCommander){ + setModel(new DefaultComboBoxModel(DeckType.CommanderOptions)); + }else { + setModel(new DefaultComboBoxModel(DeckType.ConstructedOptions)); + } setSelectedItem(deckType); } diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java index 42fa4a971c7..e54f444b9bf 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java @@ -43,6 +43,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { private ItemManagerContainer lstDecksContainer; private NetDeckCategory netDeckCategory; private boolean refreshingDeckType; + private boolean isForCommander; private final DeckManager lstDecks; private final FLabel btnViewDeck = new FLabel.ButtonBuilder().text("View Deck").fontSize(14).build(); @@ -56,7 +57,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { //Show dialog to select a deck public static Deck promptForDeck(final CDetailPicture cDetailPicture, final String title, final DeckType defaultDeckType, final boolean forAi) { FThreads.assertExecutedByEdt(true); - final FDeckChooser chooser = new FDeckChooser(cDetailPicture, forAi); + final FDeckChooser chooser = new FDeckChooser(cDetailPicture, forAi, GameType.Constructed, false); chooser.initialize(defaultDeckType); chooser.populate(); final Dimension parentSize = JOptionPane.getRootFrame().getSize(); @@ -78,10 +79,11 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { return null; } - public FDeckChooser(final CDetailPicture cDetailPicture, final boolean forAi) { - lstDecks = new DeckManager(GameType.Constructed, cDetailPicture); + public FDeckChooser(final CDetailPicture cDetailPicture, final boolean forAi, GameType gameType, boolean forCommander) { + lstDecks = new DeckManager(gameType, cDetailPicture); setOpaque(false); isAi = forAi; + isForCommander = forCommander; final UiCommand cmdViewDeck = new UiCommand() { @Override public void run() { if (selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK) { @@ -129,7 +131,14 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { } private void updateCustom() { - updateDecks(DeckProxy.getAllConstructedDecks(), ItemManagerConfig.CONSTRUCTED_DECKS); + DeckFormat deckFormat = lstDecks.getGameType().getDeckFormat(); + if(deckFormat.equals(DeckFormat.Commander)){ + updateDecks(DeckProxy.getAllCommanderDecks(), ItemManagerConfig.COMMANDER_DECKS); + }else if(deckFormat.equals(DeckFormat.TinyLeaders)){ + updateDecks(DeckProxy.getAllTinyLeadersDecks(), ItemManagerConfig.COMMANDER_DECKS); + }else { + updateDecks(DeckProxy.getAllConstructedDecks(), ItemManagerConfig.CONSTRUCTED_DECKS); + } } private void updateColors(Predicate formatFilter) { @@ -168,6 +177,49 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { lstDecks.setSelectedIndices(new Integer[]{0}); } + private void updateRandomCommander() { + if((!lstDecks.getGameType().getDeckFormat().equals(DeckFormat.Commander)&& + !(lstDecks.getGameType().getDeckFormat().equals(DeckFormat.TinyLeaders)))){ + return; + } + lstDecks.setAllowMultipleSelections(false); + + lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, false)); + lstDecks.setup(ItemManagerConfig.STRING_ONLY); + + btnRandom.setText("Random"); + btnRandom.setCommand(new UiCommand() { + @Override + public void run() { + DeckgenUtil.randomSelect(lstDecks); + } + }); + + // default selection = basic two color deck + lstDecks.setSelectedIndices(new Integer[]{0}); + } + + private void updateRandomCardGenCommander() { + if((!lstDecks.getGameType().getDeckFormat().equals(DeckFormat.Commander)&& + !(lstDecks.getGameType().getDeckFormat().equals(DeckFormat.TinyLeaders)))){ + return; + } + lstDecks.setAllowMultipleSelections(false); + lstDecks.setPool(CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, true)); + lstDecks.setup(ItemManagerConfig.STRING_ONLY); + + btnRandom.setText("Random"); + btnRandom.setCommand(new UiCommand() { + @Override + public void run() { + DeckgenUtil.randomSelect(lstDecks); + } + }); + + // default selection = basic two color deck + lstDecks.setSelectedIndices(new Integer[]{0}); + } + private void updateThemes() { updateDecks(DeckProxy.getAllThemeDecks(), ItemManagerConfig.STRING_ONLY); } @@ -192,9 +244,6 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { } public Deck getDeck() { - /*if(selectedDeckType.equals(DeckType.STANDARD_CARDGEN_DECK)){ - return DeckgenUtil.buildCardGenDeck(lstDecks.getSelectedItem().getName(),Predicate formatFilter); - }*/ final DeckProxy proxy = lstDecks.getSelectedItem(); if (proxy == null) { return null; @@ -250,7 +299,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { @Override public void deckTypeSelected(final DecksComboBoxEvent ev) { - if (ev.getDeckType() == DeckType.NET_DECK && !refreshingDeckType) { + if ((ev.getDeckType() == DeckType.NET_DECK || ev.getDeckType() == DeckType.NET_COMMANDER_DECK) && !refreshingDeckType) { FThreads.invokeInBackgroundThread(new Runnable() { //needed for loading net decks @Override public void run() { @@ -268,7 +317,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { } netDeckCategory = category; - refreshDecksList(DeckType.NET_DECK, true, ev); + refreshDecksList(ev.getDeckType(), true, ev); } }); } @@ -285,7 +334,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { if (ev == null) { refreshingDeckType = true; - decksComboBox.refresh(deckType); + decksComboBox.refresh(deckType, isForCommander); refreshingDeckType = false; } lstDecks.setCaption(deckType.toString()); @@ -294,6 +343,9 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { case CUSTOM_DECK: updateCustom(); break; + case COMMANDER_DECK: + updateCustom(); + break; case COLOR_DECK: updateColors(null); break; @@ -304,10 +356,22 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { updateColors(FModel.getFormats().getModern().getFilterPrinted()); break; case STANDARD_CARDGEN_DECK: - updateMatrix(FModel.getFormats().getStandard()); + if(FModel.isdeckGenMatrixLoaded()) { + updateMatrix(FModel.getFormats().getStandard()); + } break; case MODERN_CARDGEN_DECK: - updateMatrix(FModel.getFormats().getModern()); + if(FModel.isdeckGenMatrixLoaded()) { + updateMatrix(FModel.getFormats().getModern()); + } + break; + case RANDOM_COMMANDER_DECK: + updateRandomCommander(); + break; + case RANDOM_CARDGEN_COMMANDER_DECK: + if(FModel.isdeckGenMatrixLoaded()) { + updateRandomCardGenCommander(); + } break; case THEME_DECK: updateThemes(); @@ -324,6 +388,9 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { case NET_DECK: updateNetDecks(); break; + case NET_COMMANDER_DECK: + updateNetDecks(); + break; default: break; //other deck types not currently supported here } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java index 20b8b04390d..d76f52543eb 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java @@ -8,6 +8,7 @@ import javax.swing.SwingUtilities; import com.google.common.collect.Iterables; +import forge.deck.CommanderDeckGenerator; import forge.deck.DeckProxy; import forge.deck.DeckType; import forge.deckchooser.DecksComboBoxEvent; @@ -23,6 +24,7 @@ public class CLobby { private final VLobby view; public CLobby(final VLobby view) { this.view = view; + this.view.setForCommander(true); } private void addDecks(final Iterable commanderDecks, FList deckList, String... initialItems) { @@ -50,14 +52,10 @@ public class CLobby { public void update() { SwingUtilities.invokeLater(new Runnable() { @Override public final void run() { - final Iterable commanderDecks = DeckProxy.getAllCommanderDecks(); - final Iterable tinyLeadersDecks = DeckProxy.getAllTinyLeadersDecks(); final Iterable schemeDecks = DeckProxy.getAllSchemeDecks(); final Iterable planarDecks = DeckProxy.getAllPlanarDecks(); for (int i = 0; i < VLobby.MAX_PLAYERS; i++) { - addDecks(commanderDecks, view.getCommanderDeckLists().get(i)); - addDecks(tinyLeadersDecks, view.getTinyLeadersDeckLists().get(i)); addDecks(schemeDecks, view.getSchemeDeckLists().get(i), "Use deck's scheme section (random if unavailable)"); addDecks(planarDecks, view.getPlanarDeckLists().get(i), @@ -76,7 +74,23 @@ public class CLobby { final FDeckChooser fdc = view.getDeckChooser(iSlot); fdc.initialize(FPref.CONSTRUCTED_DECK_STATES[iSlot], defaultDeckTypeForSlot(iSlot)); fdc.populate(); - fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { + /*fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { + @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) { + view.focusOnAvatar(); + } + });*/ + final FDeckChooser fdccom = view.getCommanderDeckChooser(iSlot); + fdccom.initialize(FPref.COMMANDER_DECK_STATES[iSlot], defaultDeckTypeForCommanderSlot(iSlot)); + fdccom.populate(); + fdccom.getDecksComboBox().addListener(new IDecksComboBoxListener() { + @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) { + view.focusOnAvatar(); + } + }); + final FDeckChooser fdtlcom = view.getTinyLeaderDeckChooser(iSlot); + fdtlcom.initialize(FPref.TINY_LEADER_DECK_STATES[iSlot], defaultDeckTypeForTinyLeaderSlot(iSlot)); + fdtlcom.populate(); + fdtlcom.getDecksComboBox().addListener(new IDecksComboBoxListener() { @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) { view.focusOnAvatar(); } @@ -109,4 +123,12 @@ public class CLobby { private static DeckType defaultDeckTypeForSlot(final int iSlot) { return iSlot == 0 ? DeckType.PRECONSTRUCTED_DECK : DeckType.COLOR_DECK; } + + private static DeckType defaultDeckTypeForCommanderSlot(final int iSlot) { + return iSlot == 0 ? DeckType.COMMANDER_DECK : DeckType.RANDOM_CARDGEN_COMMANDER_DECK; + } + + private static DeckType defaultDeckTypeForTinyLeaderSlot(final int iSlot) { + return iSlot == 0 ? DeckType.TINY_LEADERS_DECKS : DeckType.RANDOM_CARDGEN_COMMANDER_DECK; + } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index a3b3907c63e..9e8fc2f93d9 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -116,6 +116,8 @@ public class VLobby implements ILobbyView { private final List> tinyLeadersDeckLists = new ArrayList>(); private final List tinyLeadersDeckPanels = new ArrayList(MAX_PLAYERS); + private final List commanderDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS); + private final List tinyLeadersDeckChoosers = Lists.newArrayListWithCapacity(MAX_PLAYERS); private final List> schemeDeckLists = new ArrayList>(); private final List schemeDeckPanels = new ArrayList(MAX_PLAYERS); @@ -133,6 +135,16 @@ public class VLobby implements ILobbyView { private final Vector humanListData = new Vector(); private final Vector aiListData = new Vector(); + public boolean isForCommander() { + return isForCommander; + } + + public void setForCommander(boolean forCommander) { + isForCommander = forCommander; + } + + private boolean isForCommander = false; + // CTR public VLobby(final GameLobby lobby) { this.lobby = lobby; @@ -202,6 +214,10 @@ public class VLobby implements ILobbyView { for (int iPlayer = 0; iPlayer < activePlayersNum; iPlayer++) { final FDeckChooser fdc = getDeckChooser(iPlayer); fdc.restoreSavedState(); + final FDeckChooser fdcom = getCommanderDeckChooser(iPlayer); + fdcom.restoreSavedState(); + final FDeckChooser fdtl = getTinyLeaderDeckChooser(iPlayer); + fdtl.restoreSavedState(); } } @@ -226,6 +242,7 @@ public class VLobby implements ILobbyView { // visible panels final LobbySlot slot = lobby.getSlot(i); final FDeckChooser deckChooser = getDeckChooser(i); + final FDeckChooser commanderDeckChooser = getCommanderDeckChooser(i); final PlayerPanel panel; final boolean isNewPanel; if (hasPanel) { @@ -240,6 +257,7 @@ public class VLobby implements ILobbyView { } playersScroll.add(panel, constraints); deckChooser.restoreSavedState(); + commanderDeckChooser.restoreSavedState(); if (i == 0) { changePlayerFocus(0); } @@ -331,7 +349,7 @@ public class VLobby implements ILobbyView { @SuppressWarnings("serial") private void buildDeckPanels(final int playerIndex) { // Main deck - final FDeckChooser mainChooser = new FDeckChooser(null, false); + final FDeckChooser mainChooser = new FDeckChooser(null, false, GameType.Constructed, false); mainChooser.getLstDecks().setSelectCommand(new UiCommand() { @Override public final void run() { selectMainDeck(playerIndex); @@ -348,19 +366,36 @@ public class VLobby implements ILobbyView { }); // Commander deck list - buildDeckPanel("Commander Deck", playerIndex, commanderDeckLists, commanderDeckPanels, new ListSelectionListener() { + /*buildDeckPanel("Commander Deck", playerIndex, commanderDeckLists, commanderDeckPanels, new ListSelectionListener() { @Override public final void valueChanged(final ListSelectionEvent e) { selectCommanderDeck(playerIndex); } + });*/ + final FDeckChooser commanderChooser = new FDeckChooser(null, false, GameType.Commander, true); + commanderChooser.getLstDecks().setSelectCommand(new UiCommand() { + @Override public final void run() { + selectCommanderDeck(playerIndex); + } }); + commanderChooser.initialize(); + commanderDeckChoosers.add(commanderChooser); + + final FDeckChooser tinyLeaderChooser = new FDeckChooser(null, false, GameType.TinyLeaders, true); + tinyLeaderChooser.getLstDecks().setSelectCommand(new UiCommand() { + @Override public final void run() { + selectTinyLeadersDeck(playerIndex); + } + }); + tinyLeaderChooser.initialize(); + tinyLeadersDeckChoosers.add(tinyLeaderChooser); - // Tiny Leaders deck list +/* // Tiny Leaders deck list buildDeckPanel("Tiny Leaders Deck", playerIndex, tinyLeadersDeckLists, tinyLeadersDeckPanels, new ListSelectionListener() { @Override public final void valueChanged(final ListSelectionEvent e) { selectTinyLeadersDeck(playerIndex); } - }); + });*/ // Planar deck list buildDeckPanel("Planar Deck", playerIndex, planarDeckLists, planarDeckPanels, new ListSelectionListener() { @@ -432,6 +467,42 @@ public class VLobby implements ILobbyView { mainChooser.saveState(); } + private void selectCommanderDeck(final int playerIndex) { + if (!hasVariant(GameType.Commander)) { + // Only these game types use this specific deck panel + return; + } + final FDeckChooser mainChooser = getCommanderDeckChooser(playerIndex); + final DeckType type = mainChooser.getSelectedDeckType(); + final Deck deck = mainChooser.getDeck(); + final Collection selectedDecks = mainChooser.getLstDecks().getSelectedItems(); + if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) { + final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME); + playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text); + fireDeckChangeListener(playerIndex, deck); + } + mainChooser.saveState(); + } + + private void selectTinyLeadersDeck(final int playerIndex) { + if (!hasVariant(GameType.TinyLeaders)) { + // Only these game types use this specific deck panel + return; + } + final FDeckChooser mainChooser = getTinyLeaderDeckChooser(playerIndex); + final DeckType type = mainChooser.getSelectedDeckType(); + final Deck deck = mainChooser.getDeck(); + final Collection selectedDecks = mainChooser.getLstDecks().getSelectedItems(); + if (playerIndex < activePlayersNum && lobby.mayEdit(playerIndex)) { + final String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME); + playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(text); + fireDeckChangeListener(playerIndex, deck); + } + mainChooser.saveState(); + } + + + private void selectSchemeDeck(final int playerIndex) { if (playerIndex >= activePlayersNum || !(hasVariant(GameType.Archenemy) || hasVariant(GameType.ArchenemyRumble))) { return; @@ -463,36 +534,6 @@ public class VLobby implements ILobbyView { getDeckChooser(playerIndex).saveState(); } - private void selectCommanderDeck(final int playerIndex) { - selectCommanderOrTinyLeadersDeck(playerIndex, GameType.Commander, getCommanderDeckLists()); - } - - private void selectTinyLeadersDeck(final int playerIndex) { - selectCommanderOrTinyLeadersDeck(playerIndex, GameType.TinyLeaders, getTinyLeadersDeckLists()); - } - - private void selectCommanderOrTinyLeadersDeck(final int playerIndex, final GameType gameType, final List> deckLists) { - if (playerIndex >= activePlayersNum || !hasVariant(gameType)) { - return; - } - - final Object selected = deckLists.get(playerIndex).getSelectedValue(); - Deck deck = null; - if (selected instanceof String) { - if (selected.equals("Random")) { - deck = RandomDeckGenerator.getRandomUserDeck(lobby, playerPanels.get(playerIndex).isAi()); - } - } else if (selected instanceof Deck) { - deck = (Deck) selected; - } - if (deck == null) { //Can be null if player deselects the list selection or chose Generate - deck = DeckgenUtil.generateCommanderDeck(isPlayerAI(playerIndex), gameType); - } - playerPanels.get(playerIndex).setCommanderDeckSelectorButtonText(deck.getName()); - fireDeckChangeListener(playerIndex, deck); - getDeckChooser(playerIndex).saveState(); - } - private void selectPlanarDeck(final int playerIndex) { if (playerIndex >= activePlayersNum || !hasVariant(GameType.Planechase)) { return; @@ -594,10 +635,10 @@ public class VLobby implements ILobbyView { } break; case Commander: - decksFrame.add(commanderDeckPanels.get(playerWithFocus), "grow, push"); + decksFrame.add(commanderDeckChoosers.get(playerWithFocus), "grow, push"); break; case TinyLeaders: - decksFrame.add(tinyLeadersDeckPanels.get(playerWithFocus), "grow, push"); + decksFrame.add(tinyLeadersDeckChoosers.get(playerWithFocus), "grow, push"); break; case Planechase: decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push"); @@ -642,6 +683,14 @@ public class VLobby implements ILobbyView { return deckChoosers.get(playernum); } + public final FDeckChooser getCommanderDeckChooser(final int playernum) { + return commanderDeckChoosers.get(playernum); + } + + public final FDeckChooser getTinyLeaderDeckChooser(final int playernum) { + return tinyLeadersDeckChoosers.get(playernum); + } + GameType getCurrentGameMode() { return lobby.getGameType(); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java index ecad23741e8..a6f715c2ea4 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuild.java @@ -2,6 +2,7 @@ package forge.screens.home.gauntlet; import forge.assets.FSkinProp; import forge.deckchooser.FDeckChooser; +import forge.game.GameType; import forge.gauntlet.GauntletIO; import forge.gui.framework.DragCell; import forge.gui.framework.DragTab; @@ -40,7 +41,7 @@ public enum VSubmenuGauntletBuild implements IVSubmenu { private final JPanel pnlStrut = new JPanel(); private final JPanel pnlDirections = new JPanel(); - private final FDeckChooser lstLeft = new FDeckChooser(null, false); + private final FDeckChooser lstLeft = new FDeckChooser(null, false, GameType.Constructed, false); private final JList lstRight = new FList(); private final FScrollPane scrRight = new FScrollPane(lstRight, true, diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java index 1c280b3e0c5..68f48dc66c6 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java @@ -1,6 +1,7 @@ package forge.screens.home.gauntlet; import forge.deckchooser.FDeckChooser; +import forge.game.GameType; import forge.gui.framework.DragCell; import forge.gui.framework.DragTab; import forge.gui.framework.EDocID; @@ -42,7 +43,7 @@ public enum VSubmenuGauntletContests implements IVSubmenu { private final JCheckBox boxModernColorDecks = new FCheckBox(DeckType.MODERN_COLOR_DECK.toString()); private final JCheckBox boxThemeDecks = new FCheckBox(DeckType.THEME_DECK.toString()); - private final FDeckChooser lstDecks = new FDeckChooser(null, false); + private final FDeckChooser lstDecks = new FDeckChooser(null, false, GameType.Constructed, false); private final FLabel lblOptions = new FLabel.Builder().fontSize(16) .fontStyle(Font.BOLD).text("OPTIONS").fontAlign(SwingConstants.CENTER).build(); @@ -85,7 +86,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu { boxThemeDecks.setSelected(true); boxColorDecks.setSelected(true); boxStandardColorDecks.setSelected(true); - if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) { + if(FModel.isdeckGenMatrixLoaded()) { boxStandardCardgenDecks.setSelected(true); boxModernCardgenDecks.setSelected(true); }else{ @@ -114,7 +115,7 @@ public enum VSubmenuGauntletQuick implements IVSubmenu { pnlOptions.add(boxQuestDecks, "w 96%!, h 30px!, gap 2% 0 0 5px"); pnlOptions.add(boxThemeDecks, "w 96%!, h 30px!, gap 2% 0 0 5px"); pnlOptions.add(boxColorDecks, "w 96%!, h 30px!, gap 2% 0 0 5px"); - if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) { + if(FModel.isdeckGenMatrixLoaded()) { pnlOptions.add(boxStandardCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px"); pnlOptions.add(boxModernCardgenDecks, "w 96%!, h 30px!, gap 2% 0 0 5px"); } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 8e676b99abe..883c5843003 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -96,6 +96,7 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbEnforceDeckLegality(), FPref.ENFORCE_DECK_LEGALITY)); lstControls.add(Pair.of(view.getCbCloneImgSource(), FPref.UI_CLONE_MODE_SOURCE)); lstControls.add(Pair.of(view.getCbRemoveSmall(), FPref.DECKGEN_NOSMALL)); + lstControls.add(Pair.of(view.getCbCardBased(), FPref.DECKGEN_CARDBASED)); lstControls.add(Pair.of(view.getCbRemoveArtifacts(), FPref.DECKGEN_ARTIFACTS)); lstControls.add(Pair.of(view.getCbSingletons(), FPref.DECKGEN_SINGLETONS)); lstControls.add(Pair.of(view.getCbEnableAICheats(), FPref.UI_ENABLE_AI_CHEATS)); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 10014f2bdb3..5c5052c4d7b 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -55,6 +55,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final FLabel btnPlayerName = new FLabel.Builder().opaque(true).hoverable(true).text("").build(); private final JCheckBox cbRemoveSmall = new OptionsCheckBox("Remove Small Creatures"); + private final JCheckBox cbCardBased = new OptionsCheckBox("Include Card-based Deck Generation"); private final JCheckBox cbSingletons = new OptionsCheckBox("Singleton Mode"); private final JCheckBox cbRemoveArtifacts = new OptionsCheckBox("Remove Artifacts"); private final JCheckBox cbAnte = new OptionsCheckBox("Play for Ante"); @@ -211,6 +212,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbRemoveArtifacts, titleConstraints); pnlPrefs.add(new NoteLabel("Disables artifact cards in generated decks."), descriptionConstraints); + pnlPrefs.add(cbCardBased, titleConstraints); + pnlPrefs.add(new NoteLabel("Builds more synergistic random decks (requires restart)."), descriptionConstraints); + // Deck building options pnlPrefs.add(new SectionLabel("Deck Editor Options"), sectionConstraints); @@ -492,6 +496,11 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbRemoveSmall; } + /** @return {@link javax.swing.JCheckBox} */ + public final JCheckBox getCbCardBased() { + return cbCardBased; + } + /** @return {@link javax.swing.JCheckBox} */ public final JCheckBox getCbSingletons() { return cbSingletons; diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index 7192b53b3e9..be176d263d7 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -3,9 +3,12 @@ package forge.view; import java.io.File; import java.io.FilenameFilter; import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import forge.LobbyPlayer; import forge.deck.DeckGroup; +import forge.game.*; import forge.properties.ForgeConstants; import forge.tournament.system.*; import forge.util.TextUtil; @@ -15,12 +18,6 @@ import org.apache.commons.lang3.time.StopWatch; import forge.deck.Deck; import forge.deck.io.DeckSerializer; -import forge.game.Game; -import forge.game.GameLogEntry; -import forge.game.GameRules; -import forge.game.GameType; -import forge.game.GameLogEntryType; -import forge.game.Match; import forge.game.player.RegisteredPlayer; import forge.model.FModel; import forge.player.GamePlayerUtil; @@ -161,14 +158,38 @@ public class SimulateMatch { System.out.println("\tq - Quiet flag. Output just the game result, not the entire game log."); } + + private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) { StopWatch sw = new StopWatch(); sw.start(); Game g1 = mc.createGame(); // will run match in the same thread - mc.startGame(g1); - sw.stop(); + + long startTime = System.currentTimeMillis(); + try { + TimeLimitedCodeBlock.runWithTimeout(new Runnable() { + @Override + public void run() { + mc.startGame(g1); + sw.stop(); + } + }, 120, TimeUnit.SECONDS); + } + catch (TimeoutException e) { + System.out.println("Stopping slow match as draw"); + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + }catch (Exception e){ + e.printStackTrace(); + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + }catch(StackOverflowError e){ + g1.setGameOver(GameEndReason.Draw); + sw.stop(); + } + List log; if (outputGamelog) { diff --git a/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java new file mode 100644 index 00000000000..15a3f5a188a --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/view/TimeLimitedCodeBlock.java @@ -0,0 +1,46 @@ +package forge.view; + +import java.util.concurrent.*; + +/** + * Created by maustin on 08/02/2018. + */ +public class TimeLimitedCodeBlock { + + public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception { + runWithTimeout(new Callable() { + @Override + public Object call() throws Exception { + runnable.run(); + return null; + } + }, timeout, timeUnit); + } + + public static T runWithTimeout(Callable callable, long timeout, TimeUnit timeUnit) throws Exception { + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Future future = executor.submit(callable); + executor.shutdown(); // This does not cancel the already-scheduled task. + try { + return future.get(timeout, timeUnit); + } + catch (TimeoutException e) { + //remove this if you do not want to cancel the job in progress + //or set the argument to 'false' if you do not want to interrupt the thread + future.cancel(true); + throw e; + } + catch (ExecutionException e) { + //unwrap the root cause + Throwable t = e.getCause(); + if (t instanceof Error) { + throw (Error) t; + } else if (t instanceof Exception) { + throw (Exception) t; + } else { + throw new IllegalStateException(t); + } + } + } + +} diff --git a/forge-gui-mobile/src/forge/card/CardZoom.java b/forge-gui-mobile/src/forge/card/CardZoom.java index c2015ac0488..eeac3ec4dea 100644 --- a/forge-gui-mobile/src/forge/card/CardZoom.java +++ b/forge-gui-mobile/src/forge/card/CardZoom.java @@ -10,6 +10,10 @@ import com.badlogic.gdx.math.Rectangle; import forge.Forge; import forge.Graphics; import forge.assets.FSkinImage; +import forge.deck.CardThemedDeckGenerator; +import forge.deck.CommanderDeckGenerator; +import forge.deck.DeckProxy; +import forge.deck.FDeckViewer; import forge.game.GameView; import forge.game.card.CardView; import forge.item.IPaperCard; @@ -123,6 +127,17 @@ public class CardZoom extends FOverlay { if (item instanceof CardView) { return (CardView)item; } + if (item instanceof DeckProxy) { + if (item instanceof CardThemedDeckGenerator){ + return CardView.getCardForUi(((CardThemedDeckGenerator)item).getPaperCard()); + }else if (item instanceof CommanderDeckGenerator){ + return CardView.getCardForUi(((CommanderDeckGenerator)item).getPaperCard()); + }else{ + DeckProxy deck = ((DeckProxy)item); + return new CardView(-1, null, deck.getName(), null, deck.getImageKey(false)); + } + + } if (item instanceof IPaperCard) { return CardView.getCardForUi((IPaperCard)item); } diff --git a/forge-gui-mobile/src/forge/deck/FDeckChooser.java b/forge-gui-mobile/src/forge/deck/FDeckChooser.java index 8842a3bce77..81ab2b910c9 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckChooser.java +++ b/forge-gui-mobile/src/forge/deck/FDeckChooser.java @@ -148,7 +148,8 @@ public class FDeckChooser extends FScreen { public void handleEvent(FEvent e) { if (selectedDeckType != DeckType.STANDARD_COLOR_DECK && selectedDeckType != DeckType.STANDARD_CARDGEN_DECK && selectedDeckType != DeckType.MODERN_CARDGEN_DECK && selectedDeckType != DeckType.MODERN_COLOR_DECK && - selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK) { + selectedDeckType != DeckType.COLOR_DECK && selectedDeckType != DeckType.THEME_DECK + && selectedDeckType != DeckType.RANDOM_COMMANDER_DECK && selectedDeckType != DeckType.RANDOM_CARDGEN_COMMANDER_DECK) { FDeckViewer.show(getDeck()); } } @@ -269,6 +270,8 @@ public class FDeckChooser extends FScreen { case COLOR_DECK: case STANDARD_COLOR_DECK: case STANDARD_CARDGEN_DECK: + case RANDOM_CARDGEN_COMMANDER_DECK: + case RANDOM_COMMANDER_DECK: case MODERN_CARDGEN_DECK: case MODERN_COLOR_DECK: case THEME_DECK: @@ -440,7 +443,7 @@ public class FDeckChooser extends FScreen { cmbDeckTypes.addItem(DeckType.QUEST_OPPONENT_DECK); cmbDeckTypes.addItem(DeckType.COLOR_DECK); cmbDeckTypes.addItem(DeckType.STANDARD_COLOR_DECK); - if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) { + if(FModel.isdeckGenMatrixLoaded()) { cmbDeckTypes.addItem(DeckType.STANDARD_CARDGEN_DECK); cmbDeckTypes.addItem(DeckType.MODERN_CARDGEN_DECK); } @@ -453,6 +456,10 @@ public class FDeckChooser extends FScreen { case TinyLeaders: cmbDeckTypes.addItem(DeckType.CUSTOM_DECK); cmbDeckTypes.addItem(DeckType.RANDOM_DECK); + if(FModel.isdeckGenMatrixLoaded()) { + cmbDeckTypes.addItem(DeckType.RANDOM_CARDGEN_COMMANDER_DECK); + } + cmbDeckTypes.addItem(DeckType.RANDOM_COMMANDER_DECK); cmbDeckTypes.addItem(DeckType.NET_DECK); break; case DeckManager: @@ -575,6 +582,17 @@ public class FDeckChooser extends FScreen { pool = DeckProxy.getAllTinyLeadersDecks(); config = ItemManagerConfig.COMMANDER_DECKS; break; + case RANDOM_COMMANDER_DECK: + pool = CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(),isAi, false); + config = ItemManagerConfig.STRING_ONLY; + break; + case RANDOM_CARDGEN_COMMANDER_DECK: + pool= new ArrayList<>(); + if(FModel.isdeckGenMatrixLoaded()) { + pool = CommanderDeckGenerator.getCommanderDecks(lstDecks.getGameType().getDeckFormat(), isAi, true); + } + config = ItemManagerConfig.STRING_ONLY; + break; case SCHEME_DECKS: pool = DeckProxy.getAllSchemeDecks(); config = ItemManagerConfig.SCHEME_DECKS; @@ -603,12 +621,18 @@ public class FDeckChooser extends FScreen { break; case STANDARD_CARDGEN_DECK: maxSelections = 1; - pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi); + pool= new ArrayList<>(); + if(FModel.isdeckGenMatrixLoaded()) { + pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi); + } config = ItemManagerConfig.STRING_ONLY; break; case MODERN_CARDGEN_DECK: maxSelections = 1; - pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi); + pool= new ArrayList<>(); + if(FModel.isdeckGenMatrixLoaded()) { + pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi); + } config = ItemManagerConfig.STRING_ONLY; break; case MODERN_COLOR_DECK: @@ -940,7 +964,7 @@ public class FDeckChooser extends FScreen { public void run(final Integer numOpponents) { if (numOpponents == null) { return; } List deckTypes=null; - if(!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) { + if(FModel.isdeckGenMatrixLoaded()) { deckTypes=Arrays.asList(new DeckType[] { DeckType.CUSTOM_DECK, DeckType.PRECONSTRUCTED_DECK, diff --git a/forge-gui-mobile/src/forge/deck/FDeckViewer.java b/forge-gui-mobile/src/forge/deck/FDeckViewer.java index 69111ce8704..ea38152df37 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckViewer.java +++ b/forge-gui-mobile/src/forge/deck/FDeckViewer.java @@ -5,9 +5,6 @@ import forge.assets.FImage; import forge.assets.FSkin; import forge.assets.FSkinImage; import forge.assets.FTextureRegionImage; -import forge.deck.CardPool; -import forge.deck.Deck; -import forge.deck.DeckSection; import forge.item.PaperCard; import forge.itemmanager.CardManager; import forge.itemmanager.ItemManagerConfig; diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 7ab24398da7..20ec1ed293a 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -983,6 +983,9 @@ public abstract class ItemManager extends FContainer im if (cbxSortOptions != null) { return cbxSortOptions.getWidth(); } + if(filters.isEmpty()){ + return 0f; + } return filters.get(filters.size() - 1).getWidget().getWidth(); } } diff --git a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java index 13f1b774b6e..999a62b16e0 100644 --- a/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java +++ b/forge-gui-mobile/src/forge/itemmanager/views/ImageView.java @@ -11,7 +11,10 @@ import forge.assets.ImageCache; import forge.card.CardRenderer; import forge.card.CardRenderer.CardStackPosition; import forge.card.CardZoom; +import forge.deck.CardThemedDeckGenerator; +import forge.deck.CommanderDeckGenerator; import forge.deck.DeckProxy; +import forge.deck.FDeckViewer; import forge.item.InventoryItem; import forge.item.PaperCard; import forge.itemmanager.ColumnDef; @@ -861,6 +864,10 @@ public class ImageView extends ItemView { public boolean longPress(float x, float y) { ItemInfo item = getItemAtPoint(x + getLeft(), y + getTop()); if (item != null) { + if(item.getKey() instanceof CardThemedDeckGenerator || item.getKey() instanceof CommanderDeckGenerator){ + FDeckViewer.show(((DeckProxy)item.getKey()).getDeck()); + return true; + } CardZoom.show(orderedItems, orderedItems.indexOf(item), itemManager); return true; } diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java index 2e20886d6c6..f8e05cafa2c 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java +++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java @@ -149,8 +149,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { FThreads.invokeInBackgroundThread(new Runnable() { @Override public void run() { - playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); - playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, DeckType.COLOR_DECK); + playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, FPref.COMMANDER_P1_DECK_STATE, FPref.TINY_LEADER_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); + playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, FPref.COMMANDER_P2_DECK_STATE, FPref.TINY_LEADER_P2_DECK_STATE, DeckType.COLOR_DECK); /*playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK); playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK); playerPanels.get(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK); @@ -475,12 +475,15 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { Deck deck; if (hasVariant(GameType.Commander)) { deck = playerPanel.getCommanderDeck(); + playerPanel.getCommanderDeckChooser().saveState(); } else if (hasVariant(GameType.TinyLeaders)) { deck = playerPanel.getTinyLeadersDeck(); + playerPanel.getTinyLeadersDeckChooser().saveState(); } else { deck = playerPanel.getDeck(); + playerPanel.getDeckChooser().saveState(); } Deck playerDeck = deck; @@ -505,8 +508,6 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { playerDeck.putSection(DeckSection.Avatar, avatarPool); } - playerPanel.getDeckChooser().saveState(); - decks[playerIndex] = playerDeck; if (playerChangeListener != null) { playerChangeListener.update(playerIndex, UpdateLobbyPlayerEvent.deckUpdate(playerDeck)); diff --git a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java index c8947af148f..2cbab44ea67 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java +++ b/forge-gui-mobile/src/forge/screens/constructed/PlayerPanel.java @@ -109,25 +109,43 @@ public class PlayerPanel extends FContainer { lstCommanderDecks = new FDeckChooser(GameType.Commander, isAi, new FEventHandler() { @Override public void handleEvent(FEvent e) { - btnCommanderDeck.setText("Commander Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + if( ((DeckManager)e.getSource()).getSelectedItem() != null) { + btnCommanderDeck.setText("Commander Deck: " + ((DeckManager) e.getSource()).getSelectedItem().getName()); + lstCommanderDecks.saveState(); + }else{ + btnCommanderDeck.setText("Commander Deck"); + } } }); lstTinyLeadersDecks = new FDeckChooser(GameType.TinyLeaders, isAi, new FEventHandler() { @Override public void handleEvent(FEvent e) { - btnTinyLeadersDeck.setText("Tiny Leaders Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + if( ((DeckManager)e.getSource()).getSelectedItem() != null) { + btnTinyLeadersDeck.setText("Tiny Leaders Deck: " + ((DeckManager) e.getSource()).getSelectedItem().getName()); + lstTinyLeadersDecks.saveState(); + }else{ + btnTinyLeadersDeck.setText("Tiny Leaders Deck"); + } } }); lstSchemeDecks = new FDeckChooser(GameType.Archenemy, isAi, new FEventHandler() { @Override public void handleEvent(FEvent e) { - btnSchemeDeck.setText("Scheme Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + if( ((DeckManager)e.getSource()).getSelectedItem() != null){ + btnSchemeDeck.setText("Scheme Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + }else{ + btnSchemeDeck.setText("Scheme Deck"); + } } }); lstPlanarDecks = new FDeckChooser(GameType.Planechase, isAi, new FEventHandler() { @Override public void handleEvent(FEvent e) { - btnPlanarDeck.setText("Planar Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + if( ((DeckManager)e.getSource()).getSelectedItem() != null){ + btnPlanarDeck.setText("Planar Deck: " + ((DeckManager)e.getSource()).getSelectedItem().getName()); + }else{ + btnPlanarDeck.setText("Planar Deck"); + } } }); lstVanguardAvatars = new FVanguardChooser(isAi, new FEventHandler() { @@ -217,10 +235,10 @@ public class PlayerPanel extends FContainer { cbTeam.setEnabled(false); } - public void initialize(FPref savedStateSetting, DeckType defaultDeckType) { + public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander, FPref savedStateSettingTinyLeader, DeckType defaultDeckType) { deckChooser.initialize(savedStateSetting, defaultDeckType); - lstCommanderDecks.initialize(null, DeckType.RANDOM_DECK); - lstTinyLeadersDecks.initialize(null, DeckType.RANDOM_DECK); + lstCommanderDecks.initialize(savedStateSettingCommander, DeckType.COMMANDER_DECK); + lstTinyLeadersDecks.initialize(savedStateSettingTinyLeader, DeckType.TINY_LEADERS_DECKS); lstPlanarDecks.initialize(null, DeckType.RANDOM_DECK); lstSchemeDecks.initialize(null, DeckType.RANDOM_DECK); } @@ -666,6 +684,15 @@ public class PlayerPanel extends FContainer { return deckChooser; } + public FDeckChooser getCommanderDeckChooser() { + return lstCommanderDecks; + } + + public FDeckChooser getTinyLeadersDeckChooser() { + return lstTinyLeadersDecks; + } + + public Deck getDeck() { return deckChooser.getDeck(); } diff --git a/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java b/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java index cabee84c191..c8685a347f2 100644 --- a/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java +++ b/forge-gui-mobile/src/forge/screens/gauntlet/NewGauntletScreen.java @@ -82,7 +82,7 @@ public class NewGauntletScreen extends LaunchScreen { if (numOpponents == null) { return; } ListChooser chooser = new ListChooser( - "Choose allowed deck types for opponents", 0, 7, Arrays.asList(new DeckType[] { + "Choose allowed deck types for opponents", 0, 9, Arrays.asList(new DeckType[] { DeckType.CUSTOM_DECK, DeckType.PRECONSTRUCTED_DECK, DeckType.QUEST_OPPONENT_DECK, diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index e1328ac1893..de4d2714c4f 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -155,6 +155,10 @@ public class SettingsPage extends TabPage { "Remove Small Creatures", "Disables 1/1 and 0/X creatures in generated decks."), 2); + lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_CARDBASED, + "Include Card-based Deck Generation", + "Builds more synergistic random decks"), + 2); lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_SINGLETONS, "Singleton Mode", "Disables non-land duplicates in generated decks."), diff --git a/forge-gui/README.txt b/forge-gui/README.txt index 11b369846f2..708d464c08f 100644 --- a/forge-gui/README.txt +++ b/forge-gui/README.txt @@ -1 +1 @@ -This file is automatically updated by our release bot on Discord, Blacksmith. It is created from the files present in the 'release-files' directory. Please do not hand-edit this file if using the bot to perform a release, as your changes will be overwritten. \ No newline at end of file +This file is automatically updated by our release bot on Discord, Blacksmith. It is created from the files present in the 'release-files' directory. Please do not hand-edit this file if using the bot to perform a release, as your changes will be overwritten. diff --git a/forge-gui/res/deckgendecks/Commander.dat b/forge-gui/res/deckgendecks/Commander.dat new file mode 100644 index 00000000000..32142b05d93 Binary files /dev/null and b/forge-gui/res/deckgendecks/Commander.dat differ diff --git a/forge-gui/res/deckgendecks/Modern.dat b/forge-gui/res/deckgendecks/Modern.dat index bd820f750d4..6f7201b2c5f 100644 Binary files a/forge-gui/res/deckgendecks/Modern.dat and b/forge-gui/res/deckgendecks/Modern.dat differ diff --git a/forge-gui/res/deckgendecks/Standard.dat b/forge-gui/res/deckgendecks/Standard.dat index d97fc0c183b..4deb78fbe16 100644 Binary files a/forge-gui/res/deckgendecks/Standard.dat and b/forge-gui/res/deckgendecks/Standard.dat differ diff --git a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java index fbf0d8dbcfa..a17d1219e0a 100644 --- a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java @@ -1,9 +1,12 @@ package forge.deck; +import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import forge.card.CardRules; import forge.card.CardRulesPredicates; +import forge.deck.generation.DeckGeneratorBase; import forge.deck.io.CardThemedMatrixIO; import forge.deck.io.DeckStorage; import forge.game.GameFormat; @@ -22,27 +25,42 @@ import java.util.*; */ public final class CardRelationMatrixGenerator { - public static HashMap>>> cardPools = new HashMap<>(); + public static HashMap>>> cardPools = new HashMap<>(); + public static boolean initialize(){ + List formatStrings = new ArrayList<>(); + formatStrings.add(FModel.getFormats().getStandard().getName()); + formatStrings.add(FModel.getFormats().getModern().getName()); + formatStrings.add(DeckFormat.Commander.toString()); - public static void initialize(){ - HashMap>> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard()); - HashMap>> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern()); - if(standardMap==null || modernMap==null){ - reInitialize(); - return; + for (String formatString : formatStrings){ + if(!initializeFormat(formatString)){ + return false; + } } - cardPools.put(FModel.getFormats().getStandard(),standardMap); - cardPools.put(FModel.getFormats().getModern(),modernMap); + + return true; } - public static void reInitialize(){ - cardPools.put(FModel.getFormats().getStandard(),initializeFormat(FModel.getFormats().getStandard())); - cardPools.put(FModel.getFormats().getModern(),initializeFormat(FModel.getFormats().getModern())); - for(GameFormat format:cardPools.keySet()){ - HashMap>> map = cardPools.get(format); - CardThemedMatrixIO.saveMatrix(format,map); + /** Try to load matrix .dat files, otherwise check for deck folders and build .dat, otherwise return false **/ + public static boolean initializeFormat(String format){ + HashMap>> formatMap = CardThemedMatrixIO.loadMatrix(format); + if(formatMap==null) { + if (CardThemedMatrixIO.getMatrixFolder(format).exists()) { + if(format.equals(FModel.getFormats().getStandard().getName())){ + formatMap=initializeFormat(FModel.getFormats().getStandard()); + }else if(format.equals(FModel.getFormats().getModern().getName())){ + formatMap=initializeFormat(FModel.getFormats().getModern()); + }else{ + formatMap=initializeCommanderFormat(); + } + CardThemedMatrixIO.saveMatrix(format, formatMap); + } else { + return false; + } } + cardPools.put(format, formatMap); + return true; } public static HashMap>> initializeFormat(GameFormat format){ @@ -58,8 +76,8 @@ public final class CardRelationMatrixGenerator { Map cardIntegerMap = new HashMap<>(); Map integerCardMap = new HashMap<>(); for (int i=0; i>> initializeCommanderFormat(){ + + IStorage decks = new StorageImmediatelySerialized("Generator", + new DeckStorage(new File(ForgeConstants.DECK_GEN_DIR,DeckFormat.Commander.toString()), + ForgeConstants.DECK_GEN_DIR, false), + true); + + //get all cards + final Iterable cards = Iterables.filter(FModel.getMagicDb().getCommonCards().getUniqueCards() + , Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND_NOT_WASTES), PaperCard.FN_GET_RULES)); + List cardList = Lists.newArrayList(cards); + cardList.add(FModel.getMagicDb().getCommonCards().getCard("Wastes")); + Map cardIntegerMap = new HashMap<>(); + Map integerCardMap = new HashMap<>(); + Map legendIntegerMap = new HashMap<>(); + Map integerLegendMap = new HashMap<>(); + //generate lookups for cards to link card names to matrix columns + for (int i=0; i legends = Lists.newArrayList(Iterables.filter(cardList,Predicates.compose( + new Predicate() { + @Override + public boolean apply(CardRules rules) { + return DeckFormat.Commander.isLegalCommander(rules); + } + }, PaperCard.FN_GET_RULES))); + + //generate lookups for legends to link commander names to matrix rows + for (int i=0; i>> cardPools = new HashMap<>(); + for (PaperCard card:legends){ + int col=legendIntegerMap.get(card.getName()); + int[] distances = matrix[col]; + int max = (Integer) Collections.max(Arrays.asList(ArrayUtils.toObject(distances))); + if (max>0) { + List> deckPool=new ArrayList<>(); + for(int k=0;k0){ + deckPool.add(new AbstractMap.SimpleEntry(integerCardMap.get(k),matrix[col][k])); + } + } + cardPools.put(card.getName(), deckPool); + } + } + return cardPools; + } + + //update the matrix by incrementing the connectivity count for each card in the deck + public static void updateLegendMatrix(Deck deck, PaperCard legend, Map cardIntegerMap, + Map legendIntegerMap, int[][] matrix){ + for (PaperCard pairCard:Iterables.filter(deck.getMain().toFlatList(), + Predicates.compose(Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND_NOT_WASTES), PaperCard.FN_GET_RULES))){ + if (!pairCard.getName().equals(legend.getName())){ + try { + int old = matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(pairCard.getName())]; + matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(pairCard.getName())] = old + 1; + }catch (NullPointerException ne){ + //Todo: Not sure what was failing here + ne.printStackTrace(); + } + } + + } + //add partner commanders to matrix + if(deck.getCommanders().size()>1){ + for(PaperCard partner:deck.getCommanders()){ + if(!partner.equals(legend)){ + int old = matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(partner.getName())]; + matrix[legendIntegerMap.get(legend.getName())][cardIntegerMap.get(partner.getName())] = old + 1; + } + } + } + } + public static class ArrayIndexComparator implements Comparator { private final Integer[] array; diff --git a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java index 4d48f3df7d7..aa972a8152a 100644 --- a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java @@ -1,7 +1,11 @@ package forge.deck; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; import forge.card.CardEdition; import forge.game.GameFormat; +import forge.item.PaperCard; +import forge.model.FModel; import java.util.ArrayList; import java.util.List; @@ -12,7 +16,7 @@ import java.util.List; public class CardThemedDeckGenerator extends DeckProxy implements Comparable { public static List getMatrixDecks(GameFormat format, boolean isForAi){ final List decks = new ArrayList(); - for(String card: CardRelationMatrixGenerator.cardPools.get(format).keySet()) { + for(String card: CardRelationMatrixGenerator.cardPools.get(format.getName()).keySet()) { decks.add(new CardThemedDeckGenerator(card, format, isForAi)); } return decks; @@ -61,4 +65,15 @@ public class CardThemedDeckGenerator extends DeckProxy implements Comparable cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(name)); + List cards=FModel.getMagicDb().getCommonCards().getAllCards(cardFilter); + return cards.get(cards.size()-1).getImageKey(altState);*/ + return FModel.getMagicDb().getCommonCards().getUniqueByName(name).getImageKey(altState); + } + + public PaperCard getPaperCard(){ + return FModel.getMagicDb().getCommonCards().getUniqueByName(name); + } } diff --git a/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java new file mode 100644 index 00000000000..52af7d5b7d2 --- /dev/null +++ b/forge-gui/src/main/java/forge/deck/CommanderDeckGenerator.java @@ -0,0 +1,107 @@ +package forge.deck; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import forge.card.CardEdition; +import forge.card.CardRules; +import forge.card.CardRulesPredicates; +import forge.deck.generation.DeckGeneratorBase; +import forge.deck.generation.IDeckGenPool; +import forge.game.GameFormat; +import forge.game.GameType; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.util.ItemPool; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by maustin on 09/05/2017. + */ +public class CommanderDeckGenerator extends DeckProxy implements Comparable { + public static List getCommanderDecks(DeckFormat format, boolean isForAi, boolean isCardGen){ + ItemPool uniqueCards; + if(isCardGen){ + uniqueCards = new ItemPool(PaperCard.class); + Iterable legendNames=CardRelationMatrixGenerator.cardPools.get(DeckFormat.Commander.toString()).keySet(); + for(String legendName:legendNames) { + uniqueCards.add(FModel.getMagicDb().getCommonCards().getUniqueByName(legendName)); + } + }else { + uniqueCards = ItemPool.createFrom(FModel.getMagicDb().getCommonCards().getUniqueCards(), PaperCard.class); + } + Predicate canPlay = isForAi ? DeckGeneratorBase.AI_CAN_PLAY : DeckGeneratorBase.HUMAN_CAN_PLAY; + @SuppressWarnings("unchecked") + Iterable legends = Iterables.filter(uniqueCards.toFlatList(), Predicates.compose(Predicates.and( + new Predicate() { + @Override + public boolean apply(CardRules rules) { + return format.isLegalCommander(rules); + } + }, + canPlay), PaperCard.FN_GET_RULES)); + final List decks = new ArrayList(); + for(PaperCard legend: legends) { + decks.add(new CommanderDeckGenerator(legend, format, isForAi, isCardGen)); + } + return decks; + } + + private final PaperCard legend; + private final int index; + private final DeckFormat format; + private final boolean isForAi; + private final boolean isCardgen; + + + private CommanderDeckGenerator(PaperCard legend0, DeckFormat format0, boolean isForAi0, boolean isCardgen0) { + super(); + legend = legend0; + index = 0; + isForAi=isForAi0; + format=format0; + isCardgen=isCardgen0; + } + + public CardEdition getEdition() { + return CardEdition.UNKNOWN; + } + + + @Override + public String getName() { + return legend.getName(); + } + + @Override + public String toString() { + return legend.getName(); + } + + @Override + public int compareTo(final CommanderDeckGenerator d) { + return this.getName().compareTo(d.getName()); + } + + @Override + public Deck getDeck() { + + return DeckgenUtil.generateRandomCommanderDeck(legend, format,isForAi, isCardgen); + } + + @Override + public boolean isGeneratedDeck() { + return true; + } + + public String getImageKey(boolean altState) { + return legend.getImageKey(altState); + } + + public PaperCard getPaperCard(){ + return legend; + } +} diff --git a/forge-gui/src/main/java/forge/deck/DeckType.java b/forge-gui/src/main/java/forge/deck/DeckType.java index 9b074229372..5eabef8f923 100644 --- a/forge-gui/src/main/java/forge/deck/DeckType.java +++ b/forge-gui/src/main/java/forge/deck/DeckType.java @@ -7,6 +7,8 @@ public enum DeckType { CUSTOM_DECK ("Custom User Decks"), CONSTRUCTED_DECK ("Constructed Decks"), COMMANDER_DECK ("Commander Decks"), + RANDOM_COMMANDER_DECK ("Random Commander Decks"), + RANDOM_CARDGEN_COMMANDER_DECK ("Random Commander Card-based Decks"), TINY_LEADERS_DECKS ("Tiny Leaders Decks"), SCHEME_DECKS ("Scheme Decks"), PLANAR_DECKS ("Planar Decks"), @@ -25,9 +27,10 @@ public enum DeckType { NET_COMMANDER_DECK ("Net Commander Decks"); public static DeckType[] ConstructedOptions; + public static DeckType[] CommanderOptions; static { - if (!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY)) { + if (FModel.isdeckGenMatrixLoaded()) { ConstructedOptions = new DeckType[]{ DeckType.CUSTOM_DECK, DeckType.PRECONSTRUCTED_DECK, @@ -55,6 +58,25 @@ public enum DeckType { }; } } + static { + if (FModel.isdeckGenMatrixLoaded()) { + CommanderOptions = new DeckType[]{ + DeckType.COMMANDER_DECK, + DeckType.RANDOM_COMMANDER_DECK, + DeckType.RANDOM_CARDGEN_COMMANDER_DECK, + DeckType.RANDOM_DECK, + DeckType.NET_COMMANDER_DECK + }; + }else{ + CommanderOptions = new DeckType[]{ + DeckType.COMMANDER_DECK, + DeckType.RANDOM_COMMANDER_DECK, + DeckType.RANDOM_DECK, + DeckType.NET_COMMANDER_DECK + }; + } + + } private String value; private DeckType(final String value) { diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java index 326ee1325b6..1b48fc05bf7 100644 --- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java +++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java @@ -16,6 +16,7 @@ import forge.game.GameFormat; import forge.game.GameType; import forge.item.PaperCard; import forge.itemmanager.IItemManager; +import forge.limited.CardThemedCommanderDeckBuilder; import forge.limited.CardThemedDeckBuilder; import forge.model.FModel; import forge.properties.ForgePreferences.FPref; @@ -44,7 +45,7 @@ public class DeckgenUtil { public static Deck buildCardGenDeck(GameFormat format, boolean isForAI){ Random random = new Random(); try { - List keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format).keySet()); + List keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format.getName()).keySet()); String randomKey = keys.get( random.nextInt(keys.size()) ); Predicate cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(randomKey)); PaperCard keyCard = FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0); @@ -119,7 +120,7 @@ public class DeckgenUtil { */ public static Deck buildCardGenDeck(PaperCard card, GameFormat format, boolean isForAI){ List> potentialCards = new ArrayList<>(); - potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName())); + potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format.getName()).get(card.getName())); Collections.sort(potentialCards,new CardDistanceComparator()); Collections.reverse(potentialCards); //get second keycard @@ -146,7 +147,7 @@ public class DeckgenUtil { randMax=preSelectedCards.size(); } PaperCard secondKeycard = preSelectedCards.get(r.nextInt(randMax)); - List> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format).get(secondKeycard.getName()); + List> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format.getName()).get(secondKeycard.getName()); //combine card distances from second key card and re-sort if(potentialSecondCards !=null && potentialSecondCards.size()>0) { @@ -486,7 +487,7 @@ public class DeckgenUtil { return res; } - /** Generate a 2-color Commander deck. */ + /** Generate a 2-5-color Commander deck. */ public static Deck generateCommanderDeck(boolean forAi, GameType gameType) { final Deck deck; IDeckGenPool cardDb = FModel.getMagicDb().getCommonCards(); @@ -504,7 +505,6 @@ public class DeckgenUtil { return format.isLegalCommander(rules); } }, - CardRulesPredicates.Presets.IS_MULTICOLOR, canPlay), PaperCard.FN_GET_RULES)); do { @@ -534,6 +534,104 @@ public class DeckgenUtil { return deck; } + /** Generate a ramdom Commander deck. */ + public static Deck generateRandomCommanderDeck(PaperCard commander, DeckFormat format, boolean forAi, boolean isCardGen) { + final Deck deck; + IDeckGenPool cardDb; + DeckGeneratorBase gen = null; + PaperCard selectedPartner=null; + if(isCardGen){ + List> potentialCards = new ArrayList<>(); + potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(DeckFormat.Commander.toString()).get(commander.getName())); + Random r = new Random(); + //Collections.shuffle(potentialCards, r); + List preSelectedCards = new ArrayList<>(); + for(Map.Entry pair:potentialCards){ + if(format.isLegalCard(pair.getKey())) { + preSelectedCards.add(pair.getKey()); + } + } + //check for partner commanders + List partners=new ArrayList<>(); + for(PaperCard c:preSelectedCards){ + if(c.getRules().canBePartnerCommander()){ + partners.add(c); + } + } + + if(partners.size()>0&&commander.getRules().canBePartnerCommander()){ + selectedPartner=partners.get(MyRandom.getRandom().nextInt(partners.size())); + preSelectedCards.remove(selectedPartner); + } + //randomly remove cards + int removeCount=0; + int i=0; + List toRemove = new ArrayList<>(); + for(PaperCard c:preSelectedCards){ + if(!format.isLegalCard(c)){ + toRemove.add(c); + removeCount++; + } + if(preSelectedCards.size()<75){ + break; + } + if(r.nextInt(100)>60+(15-(i/preSelectedCards.size())*preSelectedCards.size()) && removeCount<4 //randomly remove some cards - more likely as distance increases + &&!c.getName().contains("Urza")&&!c.getName().contains("Wastes")){ //avoid breaking Tron decks + toRemove.add(c); + removeCount++; + } + ++i; + } + preSelectedCards.removeAll(toRemove); + gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,preSelectedCards,forAi,format); + }else{ + cardDb = FModel.getMagicDb().getCommonCards(); + ColorSet colorID; + colorID = commander.getRules().getColorIdentity(); + List comColors = new ArrayList(2); + if (colorID.hasWhite()) { comColors.add("White"); } + if (colorID.hasBlue()) { comColors.add("Blue"); } + if (colorID.hasBlack()) { comColors.add("Black"); } + if (colorID.hasRed()) { comColors.add("Red"); } + if (colorID.hasGreen()) { comColors.add("Green"); } + + if(comColors.size()==1){ + gen = new DeckGeneratorMonoColor(cardDb, format, comColors.get(0)); + }else if(comColors.size()==2){ + gen = new DeckGenerator2Color(cardDb, format, comColors.get(0), comColors.get(1)); + }else if(comColors.size()==3){ + gen = new DeckGenerator3Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2)); + }else if(comColors.size()==4){ + gen = new DeckGenerator4Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2), comColors.get(3)); + }else if(comColors.size()==5){ + gen = new DeckGenerator5Color(cardDb, format); + } + + } + + + + gen.setSingleton(true); + gen.setUseArtifacts(!FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_ARTIFACTS)); + CardPool cards = gen.getDeck(format.getMainRange().getMaximum(), forAi); + + // After generating card lists, build deck. + if(selectedPartner!=null){ + deck = new Deck("Generated " + format.toString() + " deck (" + commander.getName() + + "--" + selectedPartner.getName() + ")"); + }else{ + deck = new Deck("Generated " + format.toString() + " deck (" + commander.getName() + ")"); + } + deck.setDirectory("generated/commander"); + deck.getMain().addAll(cards); + deck.getOrCreate(DeckSection.Commander).add(commander); + if(selectedPartner!=null){ + deck.getOrCreate(DeckSection.Commander).add(selectedPartner); + } + + return deck; + } + public static Map suggestBasicLandCount(Deck d) { int W=0, U=0, R=0, B=0, G=0, total=0; List cards = d.getOrCreate(DeckSection.Main).toFlatList(); diff --git a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java index 1bca2c69420..29253b5f0f6 100644 --- a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java +++ b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java @@ -22,7 +22,7 @@ public class CardThemedMatrixIO { /** suffix for all gauntlet data files */ public static final String SUFFIX_DATA = ".dat"; - public static void saveMatrix(GameFormat format, HashMap>> map){ + public static void saveMatrix(String format, HashMap>> map){ File file = getMatrixFile(format); ObjectOutputStream s = null; try { @@ -43,7 +43,7 @@ public class CardThemedMatrixIO { } } - public static HashMap>> loadMatrix(GameFormat format){ + public static HashMap>> loadMatrix(String format){ try { FileInputStream fin = new FileInputStream(getMatrixFile(format)); ObjectInputStream s = new ObjectInputStream(fin); @@ -61,6 +61,10 @@ public class CardThemedMatrixIO { return new File(ForgeConstants.DECK_GEN_DIR, name + SUFFIX_DATA); } + public static File getMatrixFolder(final String name) { + return new File(ForgeConstants.DECK_GEN_DIR, name); + } + public static File getMatrixFile(final GameFormat gf) { return getMatrixFile(gf.getName()); } diff --git a/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java new file mode 100644 index 00000000000..ab2887b2e25 --- /dev/null +++ b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java @@ -0,0 +1,781 @@ +package forge.limited; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import forge.card.*; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; +import forge.deck.CardPool; +import forge.deck.Deck; +import forge.deck.DeckFormat; +import forge.deck.DeckSection; +import forge.deck.generation.DeckGenPool; +import forge.deck.generation.DeckGeneratorBase; +import forge.item.IPaperCard; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.util.MyRandom; + +import java.util.*; + +/** + * Limited format deck. + */ +public class CardThemedCommanderDeckBuilder extends DeckGeneratorBase { + @Override + protected final float getLandPercentage() { + return 0.44f; + } + @Override + protected final float getCreaturePercentage() { + return 0.33f; + } + @Override + protected final float getSpellPercentage() { + return 0.23f; + } + + protected int targetSize; + protected int numSpellsNeeded; + protected int numCreaturesToStart; + protected int landsNeeded; + + protected final PaperCard commanderCard; + protected final PaperCard partnerCard; + + protected Predicate hasColor; + protected final List availableList; + protected final List aiPlayables; + protected final List deckList = new ArrayList<>(); + protected final List setsWithBasicLands = new ArrayList<>(); + protected List rankedColorList; + + // Views for aiPlayable + protected Iterable onColorCreatures; + protected Iterable onColorNonCreatures; + + protected static final boolean logToConsole = false; + protected static final boolean logColorsToConsole = false; + + + /** + * + * Constructor. + * + * @param dList + * Cards to build the deck from. + */ + public CardThemedCommanderDeckBuilder(PaperCard commanderCard0,PaperCard partner0, final List dList, boolean isForAI, DeckFormat format) { + super(FModel.getMagicDb().getCommonCards(), format); + this.availableList = dList; + commanderCard = commanderCard0; + partnerCard = partner0; + // remove Unplayables + if(isForAI) { + final Iterable playables = Iterables.filter(availableList, + Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES)); + this.aiPlayables = Lists.newArrayList(playables); + }else{ + this.aiPlayables = Lists.newArrayList(availableList); + } + this.availableList.removeAll(aiPlayables); + targetSize=format.getMainRange().getMinimum(); + colors = commanderCard.getRules().getColorIdentity(); + colors = ColorSet.fromMask(colors.getColor() | commanderCard.getRules().getColorIdentity().getColor()); + if(partnerCard!=null) { + colors = ColorSet.fromMask(colors.getColor() | partnerCard.getRules().getColorIdentity().getColor()); + targetSize--; + } + numSpellsNeeded = ((Double)Math.floor(targetSize*(getCreaturePercentage()+getSpellPercentage()))).intValue(); + numCreaturesToStart = ((Double)Math.ceil(targetSize*(getCreaturePercentage()))).intValue(); + landsNeeded = ((Double)Math.ceil(targetSize*(getLandPercentage()))).intValue();; + if (logColorsToConsole) { + System.out.println(commanderCard.getName()); + System.out.println("Pre Colors: " + colors.toEnumSet().toString()); + } + findBasicLandSets(); + } + + + + @Override + public CardPool getDeck(final int size, final boolean forAi) { + return buildDeck().getMain(); + } + + /** + *

+ * buildDeck. + *

+ * + * @return the new Deck. + */ + @SuppressWarnings("unused") + public Deck buildDeck() { + // 1. Prepare + hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS); + if (logColorsToConsole) { + System.out.println(commanderCard.getName()); + System.out.println("Colors: " + colors.toEnumSet().toString()); + } + Iterable colorList = Iterables.filter(aiPlayables, + Predicates.compose(hasColor, PaperCard.FN_GET_RULES)); + rankedColorList = Lists.newArrayList(colorList); + onColorCreatures = Iterables.filter(rankedColorList, + Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES)); + onColorNonCreatures = Iterables.filter(rankedColorList, + Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES)); + // Guava iterables do not copy the collection contents, instead they act + // as filters and iterate over _source_ collection each time. So even if + // aiPlayable has changed, there is no need to create a new iterable. + + // 2. Add any planeswalkers - removed - treat as non-creature + + // 3. Add creatures, trying to follow mana curve + + addManaCurveCards(onColorCreatures, numCreaturesToStart, "Creatures"); + if (logToConsole) { + System.out.println("Post Creatures : " + deckList.size()); + } + + // 4.Try to fill up to num needed with on-color non-creature cards + addManaCurveCards(onColorNonCreatures, numSpellsNeeded - deckList.size(), "Spells"); + if (logToConsole) { + System.out.println("Post Spells : " + deckList.size()); + } + + // 5.If we couldn't get enough, try to fill up with on-color cards + addCards(rankedColorList, numSpellsNeeded - deckList.size()); + if (logToConsole) { + System.out.println("Post more creatures : " + deckList.size()); + } + + // 6. If there are still on-color cards, and the average cmc is low, add + // extras. + double avCMC=getAverageCMC(deckList); + int maxCMC=getMaxCMC(deckList); + if (deckList.size() == numSpellsNeeded && avCMC < 4) { + addLowCMCCard(); + if(targetSize>60){ + addLowCMCCard(); + } + } + if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) { + addLowCMCCard(); + } + if (deckList.size() >= numSpellsNeeded && avCMC < 2.5 && maxCMC<5) { + addLowCMCCard(); + if(targetSize>60){ + addLowCMCCard(); + } + } + if (logToConsole) { + System.out.println("Post lowcoc : " + deckList.size()); + } + +/* // 7. If not enough cards yet, try to add a third color, + // to try and avoid adding purely random cards. + addThirdColorCards(numSpellsNeeded - deckList.size()); + if (logColorsToConsole) { + System.out.println("Post 3rd colour : " + deckList.size()); + System.out.println("Colors: " + colors.toEnumSet().toString()); + }*/ + + // 8. Check for DeckNeeds cards. + //checkRemRandomDeckCards(); - no need + + // 9. If there are still less than 22 non-land cards add off-color + // cards. This should be avoided. + addRandomCards(numSpellsNeeded - deckList.size()); + if (logToConsole) { + System.out.println("Post Randoms : " + deckList.size()); + } + + List duals = getDualLandList(); + addNonBasicLands(); + if (logToConsole) { + System.out.println("Post Nonbasic lands : " + deckList.size()); + } + + checkEvolvingWilds(); + + // 11. Fill up with basic lands. + final int[] clrCnts = calculateLandNeeds(); + + // Add dual lands + if (clrCnts.length>1) { + + for (String s : duals) { + this.cardCounts.put(s, 0); + } + } + + + if (landsNeeded > 0) { + addLands(clrCnts); + } + if (logToConsole) { + System.out.println("Post Lands : " + deckList.size()); + } + if (commanderCard.getRules().getColorIdentity().isColorless()&&landsNeeded>0){ + // 10. Add non-basic lands that were drafted. + addWastesIfRequired(); + } + fixDeckSize(); + if (logToConsole) { + System.out.println("Post Size fix : " + deckList.size()); + } + + //Create Deck + final Deck result = new Deck(generateName()); + result.getMain().add(deckList); + + //Add remaining non-land colour matching cards to sideboard + final CardPool cp = result.getOrCreate(DeckSection.Sideboard); + Iterable potentialSideboard = Iterables.filter(aiPlayables, + Predicates.and(Predicates.compose(hasColor, PaperCard.FN_GET_RULES), + Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES))); + int i=0; + while(i<15 && potentialSideboard.iterator().hasNext()){ + PaperCard sbCard = potentialSideboard.iterator().next(); + cp.add(sbCard); + aiPlayables.remove(sbCard); + rankedColorList.remove(sbCard); + + + ++i; + } + if (logToConsole) { + debugFinalDeck(); + } + return result; + + } + + /** + * If evolving wilds is in the deck and there are fewer than 4 spaces for basic lands - remove evolving wilds + */ + protected void checkEvolvingWilds(){ + List evolvingWilds = Lists.newArrayList(Iterables.filter(deckList,PaperCard.Predicates.name("Evolving Wilds"))); + if((evolvingWilds.size()>0 && landsNeeded<4 ) || colors.countColors()<2){ + deckList.removeAll(evolvingWilds); + landsNeeded=landsNeeded+evolvingWilds.size(); + aiPlayables.addAll(evolvingWilds); + } + } + + + protected void addLowCMCCard(){ + final Iterable nonLands = Iterables.filter(aiPlayables, + Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); + final PaperCard card = Iterables.getFirst(nonLands, null); + if (card != null) { + deckList.add(card); + aiPlayables.remove(card); + rankedColorList.remove(card); + + landsNeeded--; + if (logToConsole) { + System.out.println("Low CMC: " + card.getName()); + } + } + } + + /** + * Set the basic land pool + * @param edition + * @return + */ + protected boolean setBasicLandPool(String edition){ + Predicate isSetBasicLand; + if (edition !=null){ + isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition), + Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); + }else{ + isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES); + } + + landPool = new DeckGenPool(format.getCardPool(fullCardDB).getAllCards(isSetBasicLand)); + return landPool.contains("Plains"); + } + + /** + * Generate a descriptive name. + * + * @return name + */ + private String generateName() { + return commanderCard.getName() +" based commander deck"; + } + + /** + * Print out listing of all cards for debugging. + */ + private void debugFinalDeck() { + int i = 0; + System.out.println("DECK"); + for (final PaperCard c : deckList) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + i = 0; + System.out.println("NOT PLAYABLE"); + for (final PaperCard c : availableList) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + i = 0; + System.out.println("NOT PICKED"); + for (final PaperCard c : aiPlayables) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + } + + /** + * If the deck does not have 40 cards, fix it. This method should not be + * called if the stuff above it is working correctly. + * + */ + private void fixDeckSize() { + while (deckList.size() > targetSize) { + if (logToConsole) { + System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); + } + final PaperCard c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1)); + deckList.remove(c); + aiPlayables.add(c); + if (logToConsole) { + System.out.println(" - Removed " + c.getName() + " randomly."); + } + } + if(deckList.size()==targetSize){ + return; + } + + Predicate possibleFromFullPool = new Predicate() { + @Override + public boolean apply(PaperCard card) { + return format.isLegalCard(card) + &&!card.getRules().getManaCost().isPureGeneric() + && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) + && !deckList.contains(card) + &&!card.getRules().getAiHints().getRemAIDecks() + &&!card.getRules().getAiHints().getRemRandomDecks() + &&!card.getRules().getMainPart().getType().isLand(); + } + }; + List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); + Collections.shuffle(randomPool,new Random()); + Iterator iRandomPool=randomPool.iterator(); + while (deckList.size() < targetSize) { + if (logToConsole) { + System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); + } + PaperCard randomCard = iRandomPool.next(); + deckList.add(randomCard); + if (logToConsole) { + System.out.println(" - Added " + randomCard.getName() + " randomly."); + } + } + } + + /** + * Find the sets that have basic lands for the available cards. + */ + private void findBasicLandSets() { + final Set sets = new HashSet<>(); + for (final PaperCard cp : aiPlayables) { + final CardEdition ee = FModel.getMagicDb().getEditions().get(cp.getEdition()); + if( !sets.contains(cp.getEdition()) && CardEdition.Predicates.hasBasicLands.apply(ee)) { + sets.add(cp.getEdition()); + } + } + setsWithBasicLands.addAll(sets); + if (setsWithBasicLands.isEmpty()) { + setsWithBasicLands.add("BFZ"); + } + } + + /** + * Add lands to fulfill the given color counts. + * + * @param clrCnts + * counts of lands needed, by color + */ + private void addLands(final int[] clrCnts) { + // basic lands that are available in the deck + final Iterable basicLands = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); + final Set snowLands = new HashSet(); + + // total of all ClrCnts + int totalColor = 0; + int numColors = 0; + for (int i = 0; i < 5; i++) { + totalColor += clrCnts[i]; + if (clrCnts[i] > 0) { + numColors++; + } + } + /*if (totalColor == 0) { + for (int j = 0; j < nLand; j++) { + deckList.add(getBasicLand(i)); + } + }*/ + + // do not update landsNeeded until after the loop, because the + // calculation involves landsNeeded + for (int i = 0; i < 5; i++) { + if (clrCnts[i] > 0) { + // calculate number of lands for each color + float p = (float) clrCnts[i] / (float) totalColor; + if (numColors == 2) { + // In the normal two-color case, constrain to within 40% and 60% so that the AI + // doesn't put too few lands of the lesser color, risking getting screwed on that color. + // Don't do this for the odd case where a third color had to be added to the deck. + p = Math.min(Math.max(p, 0.4f), 0.6f); + } + int nLand = Math.round(landsNeeded * p); // desired truncation to int + if (logToConsole) { + System.out.printf("Basics[%s]: %d/%d = %f%% = %d cards%n", MagicColor.Constant.BASIC_LANDS.get(i), clrCnts[i], totalColor, 100*p, nLand); + } + + // if appropriate snow-covered lands are available, add them + for (final PaperCard cp : basicLands) { + if (cp.getName().equals(MagicColor.Constant.SNOW_LANDS.get(i))) { + snowLands.add(cp); + nLand--; + } + } + + for (int j = 0; j < nLand; j++) { + deckList.add(getBasicLand(i)); + } + } + } + + // A common problem at this point is that p in the above loop was exactly 1/2, + // and nLand rounded up for both colors, so that one too many lands was added. + // So if the deck size is > 60, remove the last land added. + // Otherwise, the fixDeckSize() method would remove random cards. + while (deckList.size() > targetSize) { + deckList.remove(deckList.size() - 1); + } + + deckList.addAll(snowLands); + aiPlayables.removeAll(snowLands); + rankedColorList.remove(snowLands); + } + + /** + * Get basic land. + * + * @param basicLand + * the set to take basic lands from (pass 'null' for random). + * @return card + */ + private PaperCard getBasicLand(final int basicLand) { + String set; + if (setsWithBasicLands.size() > 1) { + set = setsWithBasicLands.get(MyRandom.getRandom().nextInt(setsWithBasicLands.size() - 1)); + } else { + set = setsWithBasicLands.get(0); + } + return FModel.getMagicDb().getCommonCards().getCard(MagicColor.Constant.BASIC_LANDS.get(basicLand), set); + } + + /** + * Only adds wastes if present in the card pool but if present adds them all + */ + private void addWastesIfRequired(){ + Iterable wastes = Iterables.filter(aiPlayables,PaperCard.Predicates.name("Wastes")); + if(wastes.iterator().hasNext()){ + PaperCard waste = wastes.iterator().next(); + while(landsNeeded>0) { + deckList.add(waste); + landsNeeded--; + } + aiPlayables.remove(waste); + rankedColorList.remove(waste); + } + } + + /** + * Attempt to optimize basic land counts according to color representation. + * Only consider colors that are supposed to be in the deck. It's not worth + * putting one land in for that random off-color card we had to stick in at + * the end... + * + * @return CCnt + */ + private int[] calculateLandNeeds() { + final int[] clrCnts = { 0,0,0,0,0 }; + // count each card color using mana costs + for (final PaperCard cp : deckList) { + final ManaCost mc = cp.getRules().getManaCost(); + + // count each mana symbol in the mana cost + for (final ManaCostShard shard : mc) { + for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) { + final byte c = MagicColor.WUBRG[i]; + + if ( shard.canBePaidWithManaOfColor(c) && colors.hasAnyColor(c)) { + clrCnts[i]++; + } + } + } + } + return clrCnts; + } + + /** + * Add non-basic lands to the deck. + */ + private void addNonBasicLands() { + final Iterable lands = Iterables.filter(aiPlayables, + Predicates.compose(CardRulesPredicates.Presets.IS_NONBASIC_LAND, PaperCard.FN_GET_RULES)); + List landsToAdd = new ArrayList<>(); + int minBasics;//Keep a minimum number of basics to ensure playable decks + if(colors.isMonoColor()){ + minBasics=Math.round((r.nextInt(15)+6)*targetSize/60); + }else{ + minBasics=Math.round((r.nextInt(8)+6)*targetSize/60); + } + + + for (final PaperCard card : lands) { + if (landsNeeded > minBasics) { + // Throw out any dual-lands for the wrong colors. Assume + // everything else is either + // (a) dual-land of the correct two colors, or + // (b) a land that generates colorless mana and has some other + // beneficial effect. + if (!card.getRules().getColorIdentity().isColorless() && card.getRules().getColorIdentity().getSharedColors(colors).countColors()==0){ + //skip as does not match colours + if (logToConsole) { + System.out.println("Excluding NonBasicLand: " + card.getName()); + } + continue; + } + if (!inverseDLands.contains(card.getName())&&!dLands.contains(card.getName())&&r.nextInt(100)<90) { + landsToAdd.add(card); + landsNeeded--; + if (logToConsole) { + System.out.println("NonBasicLand[" + landsNeeded + "]:" + card.getName()); + } + } + } + } + deckList.addAll(landsToAdd); + aiPlayables.removeAll(landsToAdd); + rankedColorList.removeAll(landsToAdd); + } + + + /** + * Add random cards to the deck. + * + * @param num + * number to add + */ + private void addRandomCards(int num) { + Predicate possibleFromFullPool = new Predicate() { + @Override + public boolean apply(PaperCard card) { + return format.isLegalCard(card) + &&!card.getRules().getManaCost().isPureGeneric() + && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) + && !deckList.contains(card) + &&!card.getRules().getAiHints().getRemAIDecks() + &&!card.getRules().getAiHints().getRemRandomDecks() + &&!card.getRules().getMainPart().getType().isLand(); + } + }; + List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); + Collections.shuffle(randomPool,new Random()); + Iterator iRandomPool=randomPool.iterator(); + for(int i=0;i cards, int num) { + List cardsToAdd = new ArrayList<>(); + for (final PaperCard card : cards) { + if(card.getRules().getMainPart().getType().isLand()){ + continue; + } + if (num +1 > 0) { + cardsToAdd.add(card); + if (logToConsole) { + System.out.println("Extra needed[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); + } + num--; + } else { + break; + } + } + deckList.addAll(cardsToAdd); + aiPlayables.removeAll(cardsToAdd); + rankedColorList.removeAll(cardsToAdd); + } + + /** + * Add cards to the deck, trying to follow some mana curve. Trying to + * have generous limits at each cost, but perhaps still too strict. But + * we're trying to prevent the AI from adding everything at a single cost. + * + * @param creatures + * cards to choose from + * @param num + * number to add + */ + private void addManaCurveCards(final Iterable creatures, int num, String nameForLog) { +/* // Add the deck card + if(commanderCard.getRules().getMainPart().getType().isCreature()) { + keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(commanderCard.getName())); + final List keyCardList = Lists.newArrayList(keyCards); + deckList.addAll(keyCardList); + aiPlayables.removeAll(keyCardList); + rankedColorList.removeAll(keyCardList); + }*/ + final Map targetCMCs = new HashMap<>(); + targetCMCs.put(1,Math.round((r.nextInt(4)+2)*targetSize/60));//2 + targetCMCs.put(2,Math.round((r.nextInt(5)+5)*targetSize/60));//6 + targetCMCs.put(3,Math.round((r.nextInt(5)+6)*targetSize/60));//7 + targetCMCs.put(4,Math.round((r.nextInt(3)+3)*targetSize/60));//4 + targetCMCs.put(5,Math.round((r.nextInt(3)+3)*targetSize/60));//3 + targetCMCs.put(6,Math.round((r.nextInt(3)+1)*targetSize/60));//2 + + + final Map creatureCosts = new HashMap(); + for (int i = 1; i < 7; i++) { + creatureCosts.put(i, 0); + } + final Predicate filter = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, + PaperCard.FN_GET_RULES); + for (final IPaperCard creature : Iterables.filter(deckList, filter)) { + int cmc = creature.getRules().getManaCost().getCMC(); + if (cmc < 1) { + cmc = 1; + } else if (cmc > 6) { + cmc = 6; + } + creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); + } + + List creaturesToAdd = new ArrayList<>(); + for (final PaperCard card : creatures) { + int cmc = card.getRules().getManaCost().getCMC(); + if (cmc < 1) { + cmc = 1; + } else if (cmc > 6) { + cmc = 6; + } + final Integer currentAtCmc = creatureCosts.get(cmc); + boolean willAddCreature = false; + if (cmc <= 1 && currentAtCmc < targetCMCs.get(1)) { + willAddCreature = true; + } else if (cmc == 2 && currentAtCmc < targetCMCs.get(2)) { + willAddCreature = true; + } else if (cmc == 3 && currentAtCmc < targetCMCs.get(3)) { + willAddCreature = true; + } else if (cmc == 4 && currentAtCmc < targetCMCs.get(4)) { + willAddCreature = true; + } else if (cmc == 5 && currentAtCmc < targetCMCs.get(5)) { + willAddCreature = true; + } else if (cmc >= 6 && currentAtCmc < targetCMCs.get(6)) { + willAddCreature = true; + } + + if (willAddCreature) { + creaturesToAdd.add(card); + num--; + creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); + if (logToConsole) { + System.out.println(nameForLog+"[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); + } + } else { + if (logToConsole) { + System.out.println(card.getName() + " not added because CMC " + card.getRules().getManaCost().getCMC() + + " has " + currentAtCmc + " already."); + } + } + if (num <= 0) { + break; + } + } + deckList.addAll(creaturesToAdd); + aiPlayables.removeAll(creaturesToAdd); + rankedColorList.removeAll(creaturesToAdd); + } + + /** + * Calculate average CMC. + * + * @param cards + * cards to choose from + * @return the average + */ + private static double getAverageCMC(final List cards) { + double sum = 0.0; + for (final IPaperCard cardPrinted : cards) { + sum += cardPrinted.getRules().getManaCost().getCMC(); + } + return sum / cards.size(); + } + + /** + * Calculate max CMC. + * + * @param cards + * cards to choose from + * @return the average + */ + private static int getMaxCMC(final List cards) { + int max = 0; + for (final IPaperCard cardPrinted : cards) { + if(cardPrinted.getRules().getManaCost().getCMC()>max) { + max = cardPrinted.getRules().getManaCost().getCMC(); + } + } + return max; + } + + /** + * @return the colors + */ + public ColorSet getColors() { + return colors; + } + + /** + * @param colors0 + * the colors to set + */ + public void setColors(final ColorSet colors0) { + colors = colors0; + } + + /** + * @return the aiPlayables + */ + public List getAiPlayables() { + return aiPlayables; + } + +} diff --git a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java index ef205c81ee6..4c6ca201730 100644 --- a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java @@ -190,7 +190,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { // extras. double avCMC=getAverageCMC(deckList); int maxCMC=getMaxCMC(deckList); - if (deckList.size() == numSpellsNeeded && avCMC < 4) { + if (deckList.size() >= numSpellsNeeded && avCMC < 4) { addLowCMCCard(); } if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) { diff --git a/forge-gui/src/main/java/forge/model/FModel.java b/forge-gui/src/main/java/forge/model/FModel.java index 4be373b2543..33f6bfc8d89 100644 --- a/forge-gui/src/main/java/forge/model/FModel.java +++ b/forge-gui/src/main/java/forge/model/FModel.java @@ -27,6 +27,8 @@ import forge.ai.AiProfileUtil; import forge.card.CardPreferences; import forge.card.CardType; import forge.deck.CardRelationMatrixGenerator; +import forge.deck.DeckFormat; +import forge.deck.io.CardThemedMatrixIO; import forge.deck.io.DeckPreferences; import forge.game.GameFormat; import forge.game.GameType; @@ -218,11 +220,18 @@ public final class FModel { AiProfileUtil.loadAllProfiles(ForgeConstants.AI_PROFILE_DIR); //generate Deck Gen matrix - if(!FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY)) { - CardRelationMatrixGenerator.initialize(); + if(!FModel.getPreferences().getPrefBoolean(FPref.LOAD_CARD_SCRIPTS_LAZILY) + &&FModel.getPreferences().getPrefBoolean(FPref.DECKGEN_CARDBASED)) { + deckGenMatrixLoaded=CardRelationMatrixGenerator.initialize(); } } + private static boolean deckGenMatrixLoaded=false; + + public static boolean isdeckGenMatrixLoaded(){ + return deckGenMatrixLoaded; + } + public static QuestController getQuest() { return quest; } diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java index f3cce03850b..c5258999e0e 100644 --- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java @@ -34,6 +34,22 @@ public class ForgePreferences extends PreferencesStore { CONSTRUCTED_P6_DECK_STATE(""), CONSTRUCTED_P7_DECK_STATE(""), CONSTRUCTED_P8_DECK_STATE(""), + COMMANDER_P1_DECK_STATE(""), + COMMANDER_P2_DECK_STATE(""), + COMMANDER_P3_DECK_STATE(""), + COMMANDER_P4_DECK_STATE(""), + COMMANDER_P5_DECK_STATE(""), + COMMANDER_P6_DECK_STATE(""), + COMMANDER_P7_DECK_STATE(""), + COMMANDER_P8_DECK_STATE(""), + TINY_LEADER_P1_DECK_STATE(""), + TINY_LEADER_P2_DECK_STATE(""), + TINY_LEADER_P3_DECK_STATE(""), + TINY_LEADER_P4_DECK_STATE(""), + TINY_LEADER_P5_DECK_STATE(""), + TINY_LEADER_P6_DECK_STATE(""), + TINY_LEADER_P7_DECK_STATE(""), + TINY_LEADER_P8_DECK_STATE(""), UI_LANDSCAPE_MODE ("false"), UI_COMPACT_MAIN_MENU ("false"), UI_USE_OLD ("false"), @@ -140,6 +156,7 @@ public class ForgePreferences extends PreferencesStore { DECKGEN_SINGLETONS ("false"), DECKGEN_ARTIFACTS ("false"), DECKGEN_NOSMALL ("false"), + DECKGEN_CARDBASED ("true"), PHASE_AI_UPKEEP ("false"), PHASE_AI_DRAW ("false"), @@ -210,6 +227,19 @@ public class ForgePreferences extends PreferencesStore { CONSTRUCTED_P3_DECK_STATE, CONSTRUCTED_P4_DECK_STATE, CONSTRUCTED_P5_DECK_STATE, CONSTRUCTED_P6_DECK_STATE, CONSTRUCTED_P7_DECK_STATE, CONSTRUCTED_P8_DECK_STATE }; + + public static FPref[] COMMANDER_DECK_STATES = { + COMMANDER_P1_DECK_STATE, COMMANDER_P2_DECK_STATE, + COMMANDER_P3_DECK_STATE, COMMANDER_P4_DECK_STATE, + COMMANDER_P5_DECK_STATE, COMMANDER_P6_DECK_STATE, + COMMANDER_P7_DECK_STATE, COMMANDER_P8_DECK_STATE }; + + public static FPref[] TINY_LEADER_DECK_STATES = { + TINY_LEADER_P1_DECK_STATE, TINY_LEADER_P2_DECK_STATE, + TINY_LEADER_P3_DECK_STATE, TINY_LEADER_P4_DECK_STATE, + TINY_LEADER_P5_DECK_STATE, TINY_LEADER_P6_DECK_STATE, + TINY_LEADER_P7_DECK_STATE, TINY_LEADER_P8_DECK_STATE }; + } /** Instantiates a ForgePreferences object. */