mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
Changed card-based deck generation to archetype based making better use of the new LDA models. Decks can now be selected by archetype with names generated from the source decklists. Archetypes are ordered by popularity.
(cherry picked from commit e993b00)
This commit is contained in:
@@ -164,7 +164,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
|
||||
private void updateMatrix(GameFormat format) {
|
||||
lstDecks.setAllowMultipleSelections(false);
|
||||
|
||||
lstDecks.setPool(CardThemedDeckGenerator.getMatrixDecks(format, isAi));
|
||||
lstDecks.setPool(ArchetypeDeckGenerator.getMatrixDecks(format, isAi));
|
||||
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
|
||||
|
||||
btnRandom.setText("Random");
|
||||
|
||||
@@ -654,7 +654,7 @@ public class FDeckChooser extends FScreen {
|
||||
maxSelections = 1;
|
||||
pool= new ArrayList<>();
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi);
|
||||
pool = ArchetypeDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi);
|
||||
}
|
||||
config = ItemManagerConfig.STRING_ONLY;
|
||||
break;
|
||||
@@ -662,7 +662,7 @@ public class FDeckChooser extends FScreen {
|
||||
maxSelections = 1;
|
||||
pool= new ArrayList<>();
|
||||
if(FModel.isdeckGenMatrixLoaded()) {
|
||||
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi);
|
||||
pool = ArchetypeDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi);
|
||||
}
|
||||
config = ItemManagerConfig.STRING_ONLY;
|
||||
break;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
forge-gui/res/deckgendecks/Modern.raw.dat
Normal file
BIN
forge-gui/res/deckgendecks/Modern.raw.dat
Normal file
Binary file not shown.
Binary file not shown.
BIN
forge-gui/res/deckgendecks/Standard.raw.dat
Normal file
BIN
forge-gui/res/deckgendecks/Standard.raw.dat
Normal file
Binary file not shown.
@@ -0,0 +1,92 @@
|
||||
package forge.deck;
|
||||
|
||||
import forge.card.CardEdition;
|
||||
import forge.deck.io.Archetype;
|
||||
import forge.game.GameFormat;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by maustin on 09/05/2017.
|
||||
*/
|
||||
public class ArchetypeDeckGenerator extends DeckProxy implements Comparable<ArchetypeDeckGenerator> {
|
||||
public static List<DeckProxy> getMatrixDecks(GameFormat format, boolean isForAi){
|
||||
final List<DeckProxy> decks = new ArrayList<DeckProxy>();
|
||||
for(Archetype archetype: CardArchetypeLDAGenerator.ldaArchetypes.get(format.getName())) {
|
||||
decks.add(new ArchetypeDeckGenerator(archetype, format, isForAi));
|
||||
}
|
||||
|
||||
return decks;
|
||||
}
|
||||
private final Archetype archetype;
|
||||
private final int index;
|
||||
private final GameFormat format;
|
||||
private final boolean isForAi;
|
||||
private PaperCard card;
|
||||
|
||||
|
||||
private ArchetypeDeckGenerator(Archetype archetype0, GameFormat format0, boolean isForAi0) {
|
||||
super();
|
||||
archetype = archetype0;
|
||||
index = 0;
|
||||
format=format0;
|
||||
isForAi=isForAi0;
|
||||
for(Pair<String, Double> cardPair : archetype.getCardProbabilities()){
|
||||
PaperCard candidate = FModel.getMagicDb().getCommonCards().getUniqueByName(cardPair.getLeft());
|
||||
if(!candidate.getRules().getType().isLand()){
|
||||
card = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CardEdition getEdition() {
|
||||
return CardEdition.UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return archetype.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return archetype.getName();
|
||||
}
|
||||
|
||||
public Archetype getArchetype() {
|
||||
return archetype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ArchetypeDeckGenerator d) {
|
||||
return d.getArchetype().getDeckCount().compareTo(archetype.getDeckCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deck getDeck() {
|
||||
|
||||
return DeckgenUtil.buildLDACArchetypeDeck(archetype,format,isForAi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeneratedDeck() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getImageKey(boolean altState) {
|
||||
/* Predicate<PaperCard> cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(name));
|
||||
List<PaperCard> cards=FModel.getMagicDb().getCommonCards().getAllCards(cardFilter);
|
||||
return cards.get(cards.size()-1).getImageKey(altState);*/
|
||||
return card.getImageKey(altState);
|
||||
}
|
||||
|
||||
public PaperCard getPaperCard(){
|
||||
return card;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.deck;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.deck.io.Archetype;
|
||||
import forge.deck.io.CardThemedLDAIO;
|
||||
import forge.game.GameFormat;
|
||||
import forge.model.FModel;
|
||||
@@ -15,6 +16,7 @@ import java.util.*;
|
||||
public final class CardArchetypeLDAGenerator {
|
||||
|
||||
public static Map<String, Map<String,List<List<Pair<String, Double>>>>> ldaPools = new HashMap();
|
||||
public static Map<String, List<Archetype>> ldaArchetypes = new HashMap<>();
|
||||
|
||||
|
||||
public static boolean initialize(){
|
||||
@@ -33,10 +35,10 @@ public final class CardArchetypeLDAGenerator {
|
||||
|
||||
/** Try to load matrix .dat files, otherwise check for deck folders and build .dat, otherwise return false **/
|
||||
public static boolean initializeFormat(String format){
|
||||
List<Archetype> lda = CardThemedLDAIO.loadRawLDA(format);
|
||||
Map<String,List<List<Pair<String, Double>>>> formatMap = CardThemedLDAIO.loadLDA(format);
|
||||
if(formatMap==null) {
|
||||
try {
|
||||
List<List<Pair<String, Double>>> lda = CardThemedLDAIO.loadRawLDA(format);
|
||||
formatMap = loadFormat(lda);
|
||||
CardThemedLDAIO.saveLDA(format, formatMap);
|
||||
}catch (Exception e){
|
||||
@@ -45,17 +47,18 @@ public final class CardArchetypeLDAGenerator {
|
||||
}
|
||||
}
|
||||
ldaPools.put(format, formatMap);
|
||||
ldaArchetypes.put(format, lda);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Map<String,List<List<Pair<String, Double>>>> loadFormat(List<List<Pair<String, Double>>> lda) throws Exception{
|
||||
public static Map<String,List<List<Pair<String, Double>>>> loadFormat(List<Archetype> lda) throws Exception{
|
||||
|
||||
List<List<Pair<String, Double>>> topics = new ArrayList<>();
|
||||
Set<String> cards = new HashSet<String>();
|
||||
for (int t = 0; t < lda.size(); ++t) {
|
||||
List<Pair<String, Double>> topic = new ArrayList<>();
|
||||
Set<String> topicCards = new HashSet<>();
|
||||
List<Pair<String, Double>> highRankVocabs = lda.get(t);
|
||||
List<Pair<String, Double>> highRankVocabs = lda.get(t).getCardProbabilities();
|
||||
if (highRankVocabs.get(0).getRight()<=0.01d){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ public enum DeckType {
|
||||
PRECONSTRUCTED_DECK("Preconstructed Decks"),
|
||||
QUEST_OPPONENT_DECK ("Quest Opponent Decks"),
|
||||
COLOR_DECK ("Random Color Decks"),
|
||||
STANDARD_CARDGEN_DECK ("Random Standard Card-themed Decks"),
|
||||
MODERN_CARDGEN_DECK ("Random Modern Card-themed Decks"),
|
||||
STANDARD_CARDGEN_DECK ("Random Standard Archetype Decks"),
|
||||
MODERN_CARDGEN_DECK ("Random Modern Archetype Decks"),
|
||||
STANDARD_COLOR_DECK ("Random Standard Color Decks"),
|
||||
MODERN_COLOR_DECK ("Random Modern Color Decks"),
|
||||
THEME_DECK ("Random Theme Decks"),
|
||||
@@ -36,9 +36,9 @@ public enum DeckType {
|
||||
DeckType.PRECONSTRUCTED_DECK,
|
||||
DeckType.QUEST_OPPONENT_DECK,
|
||||
DeckType.COLOR_DECK,
|
||||
DeckType.STANDARD_COLOR_DECK,
|
||||
DeckType.STANDARD_CARDGEN_DECK,
|
||||
DeckType.MODERN_CARDGEN_DECK,
|
||||
DeckType.STANDARD_COLOR_DECK,
|
||||
DeckType.MODERN_COLOR_DECK,
|
||||
DeckType.THEME_DECK,
|
||||
DeckType.RANDOM_DECK,
|
||||
|
||||
@@ -13,10 +13,12 @@ import forge.card.ColorSet;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.deck.generation.*;
|
||||
import forge.deck.io.Archetype;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.GameType;
|
||||
import forge.item.PaperCard;
|
||||
import forge.itemmanager.IItemManager;
|
||||
import forge.limited.ArchetypeDeckBuilder;
|
||||
import forge.limited.CardThemedCommanderDeckBuilder;
|
||||
import forge.limited.CardThemedConquestDeckBuilder;
|
||||
import forge.limited.CardThemedDeckBuilder;
|
||||
@@ -251,6 +253,84 @@ public class DeckgenUtil {
|
||||
return deck;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Build a deck based on the chosen card.
|
||||
*
|
||||
* @param archetype
|
||||
* @param format
|
||||
* @param isForAI
|
||||
* @return
|
||||
*/
|
||||
public static Deck buildLDACArchetypeDeck(Archetype archetype, GameFormat format, boolean isForAI){
|
||||
List<Pair<String, Double>> preSelectedCardNames = archetype.getCardProbabilities();
|
||||
PaperCard card = StaticData.instance().getCommonCards().getUniqueByName(preSelectedCardNames.get(0).getLeft());
|
||||
List<PaperCard> selectedCards = new ArrayList<>();
|
||||
for(Pair<String, Double> pair:preSelectedCardNames){
|
||||
String name = pair.getLeft();
|
||||
PaperCard cardToAdd = StaticData.instance().getCommonCards().getUniqueByName(name);
|
||||
//for(int i=0; i<1;++i) {
|
||||
if(!cardToAdd.getName().equals(card.getName())) {
|
||||
selectedCards.add(cardToAdd);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
List<PaperCard> toRemove = new ArrayList<>();
|
||||
|
||||
//randomly remove cards
|
||||
int removeCount=0;
|
||||
int i=0;
|
||||
for(PaperCard c:selectedCards){
|
||||
if(MyRandom.getRandom().nextInt(100)>70+(15-(i/selectedCards.size())*selectedCards.size()) && removeCount<4 //randomly remove some cards - more likely as distance increases
|
||||
&&!c.getName().contains("Urza")){ //avoid breaking Tron decks
|
||||
toRemove.add(c);
|
||||
removeCount++;
|
||||
}
|
||||
if(c.getName().equals(card.getName())){//may have been added in secondary list
|
||||
toRemove.add(c);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
selectedCards.removeAll(toRemove);
|
||||
//Add keycard
|
||||
List<PaperCard> playsetList = new ArrayList<>();
|
||||
int keyCardCount=4;
|
||||
if(card.getRules().getMainPart().getManaCost().getCMC()>7){
|
||||
keyCardCount=1+MyRandom.getRandom().nextInt(4);
|
||||
}else if(card.getRules().getMainPart().getManaCost().getCMC()>5){
|
||||
keyCardCount=2+MyRandom.getRandom().nextInt(3);
|
||||
}
|
||||
for(int j=0;j<keyCardCount;++j) {
|
||||
playsetList.add(card);
|
||||
}
|
||||
for (PaperCard c:selectedCards){
|
||||
for(int j=0;j<4;++j) {
|
||||
if(MyRandom.getRandom().nextInt(100)<90) {
|
||||
playsetList.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//build deck from combined list
|
||||
ArchetypeDeckBuilder dBuilder = new ArchetypeDeckBuilder(archetype, card, playsetList,format,isForAI);
|
||||
Deck deck = dBuilder.buildDeck();
|
||||
if(deck.getMain().countAll()!=60){
|
||||
System.out.println(deck.getMain().countAll());
|
||||
System.out.println("Wrong card count "+deck.getMain().countAll());
|
||||
deck=buildCardGenDeck(format,isForAI);
|
||||
}
|
||||
if(deck.getMain().countAll(Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES))>27){
|
||||
System.out.println("Too many lands "+deck.getMain().countAll(Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES)));
|
||||
deck=buildCardGenDeck(format,isForAI);
|
||||
}
|
||||
while(deck.get(DeckSection.Sideboard).countAll()>15){
|
||||
deck.get(DeckSection.Sideboard).remove(deck.get(DeckSection.Sideboard).get(0));
|
||||
}
|
||||
return deck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selection {@link java.lang.String} array
|
||||
* @return {@link forge.deck.Deck}
|
||||
|
||||
43
forge-gui/src/main/java/forge/deck/io/Archetype.java
Normal file
43
forge-gui/src/main/java/forge/deck/io/Archetype.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package forge.deck.io;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class Archetype implements Serializable {
|
||||
|
||||
private List<Pair<String, Double>> cardProbabilities;
|
||||
private String name;
|
||||
private Integer deckCount;
|
||||
|
||||
public Archetype(List<Pair<String, Double>> cardProbabilities, String name, Integer deckCount){
|
||||
this.cardProbabilities = cardProbabilities;
|
||||
this.name = name;
|
||||
this.deckCount = deckCount;
|
||||
}
|
||||
|
||||
public List<Pair<String, Double>> getCardProbabilities() {
|
||||
return cardProbabilities;
|
||||
}
|
||||
|
||||
public void setCardProbabilities(List<Pair<String, Double>> cardProbabilities) {
|
||||
this.cardProbabilities = cardProbabilities;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getDeckCount() {
|
||||
return deckCount;
|
||||
}
|
||||
|
||||
public void setDeckCount(Integer deckCount) {
|
||||
this.deckCount = deckCount;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class CardThemedLDAIO {
|
||||
public static final String SUFFIX_DATA = ".lda.dat";
|
||||
public static final String RAW_SUFFIX_DATA = ".raw.dat";
|
||||
|
||||
public static void saveRawLDA(String format, List<List<Pair<String, Double>>> lda){
|
||||
public static void saveRawLDA(String format, List<Archetype> lda){
|
||||
File file = getRAWLDAFile(format);
|
||||
ObjectOutputStream s = null;
|
||||
try {
|
||||
@@ -40,11 +40,11 @@ public class CardThemedLDAIO {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<List<Pair<String, Double>>> loadRawLDA(String format){
|
||||
public static List<Archetype> loadRawLDA(String format){
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(getRAWLDAFile(format));
|
||||
ObjectInputStream s = new ObjectInputStream(fin);
|
||||
List<List<Pair<String, Double>>> matrix = (List<List<Pair<String, Double>>>) s.readObject();
|
||||
List<Archetype> matrix = (List<Archetype>) s.readObject();
|
||||
s.close();
|
||||
return matrix;
|
||||
}catch (Exception e){
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package forge.limited;
|
||||
|
||||
import forge.deck.DeckFormat;
|
||||
import forge.deck.io.Archetype;
|
||||
import forge.game.GameFormat;
|
||||
import forge.item.PaperCard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ArchetypeDeckBuilder extends CardThemedDeckBuilder{
|
||||
|
||||
private Archetype archetype;
|
||||
|
||||
public ArchetypeDeckBuilder(Archetype archetype0, PaperCard keyCard0, final List<PaperCard> dList, GameFormat format, boolean isForAI){
|
||||
super(keyCard0,null, dList, format, isForAI, DeckFormat.Constructed);
|
||||
archetype = archetype0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a descriptive name.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
protected String generateName() {
|
||||
return archetype.getName() + " generated deck";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user