Improvements and extensions to Card based random deck generator. Now randomly picks a second keycard to build the deck around too. Also I have now included all non-AI playable decks in the model (the AI still excludes the non-AI playables from the decks it builds). There are now 10x more decks in the model including the latest decks from pro tour AKH. The format of the .dat files has now changed - so these new dat files need to be redeployed if you are manually installing on Android.

This commit is contained in:
austinio7116
2017-05-17 08:23:47 +00:00
parent 1bc868c0c9
commit dde9f064ab
13 changed files with 183 additions and 57 deletions

View File

@@ -153,7 +153,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener {
private void updateMatrix(GameFormat format) {
lstDecks.setAllowMultipleSelections(false);
lstDecks.setPool(CardThemedDeckGenerator.getMatrixDecks(format));
lstDecks.setPool(CardThemedDeckGenerator.getMatrixDecks(format, isAi));
lstDecks.setup(ItemManagerConfig.STRING_ONLY);
btnRandom.setText("Random");

View File

@@ -118,8 +118,8 @@ public class FDeckViewer extends FDialog {
}
});
final int width = 800;
final int height = 600;
final int width = 1920;
final int height = 1080;
this.setPreferredSize(new Dimension(width, height));
this.setSize(width, height);

View File

@@ -601,12 +601,12 @@ public class FDeckChooser extends FScreen {
break;
case STANDARD_CARDGEN_DECK:
maxSelections = 1;
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard());
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi);
config = ItemManagerConfig.STRING_ONLY;
break;
case MODERN_CARDGEN_DECK:
maxSelections = 1;
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern());
pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi);
config = ItemManagerConfig.STRING_ONLY;
break;
case MODERN_COLOR_DECK:

View File

@@ -22,12 +22,12 @@ import java.util.*;
*/
public final class CardRelationMatrixGenerator {
public static HashMap<GameFormat,HashMap<String,List<PaperCard>>> cardPools = new HashMap<>();
public static HashMap<GameFormat,HashMap<String,List<Map.Entry<PaperCard,Integer>>>> cardPools = new HashMap<>();
public static void initialize(){
HashMap<String,List<PaperCard>> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard());
HashMap<String,List<PaperCard>> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern());
HashMap<String,List<Map.Entry<PaperCard,Integer>>> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard());
HashMap<String,List<Map.Entry<PaperCard,Integer>>> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern());
if(standardMap==null || modernMap==null){
reInitialize();
return;
@@ -40,12 +40,12 @@ public final class CardRelationMatrixGenerator {
cardPools.put(FModel.getFormats().getStandard(),initializeFormat(FModel.getFormats().getStandard()));
cardPools.put(FModel.getFormats().getModern(),initializeFormat(FModel.getFormats().getModern()));
for(GameFormat format:cardPools.keySet()){
HashMap<String,List<PaperCard>> map = cardPools.get(format);
HashMap<String,List<Map.Entry<PaperCard,Integer>>> map = cardPools.get(format);
CardThemedMatrixIO.saveMatrix(format,map);
}
}
public static HashMap<String,List<PaperCard>> initializeFormat(GameFormat format){
public static HashMap<String,List<Map.Entry<PaperCard,Integer>>> initializeFormat(GameFormat format){
IStorage<Deck> decks = new StorageImmediatelySerialized<Deck>("Generator", new DeckStorage(new File(ForgeConstants.DECK_GEN_DIR+ForgeConstants.PATH_SEPARATOR+format.getName()),
ForgeConstants.DECK_GEN_DIR, false),
@@ -83,7 +83,7 @@ public final class CardRelationMatrixGenerator {
}
}
}
HashMap<String,List<PaperCard>> cardPools = new HashMap<>();
HashMap<String,List<Map.Entry<PaperCard,Integer>>> cardPools = new HashMap<>();
for (PaperCard card:cardList){
int col=cardIntegerMap.get(card.getName());
int[] distances = matrix[col];
@@ -92,21 +92,21 @@ public final class CardRelationMatrixGenerator {
ArrayIndexComparator comparator = new ArrayIndexComparator(ArrayUtils.toObject(distances));
Integer[] indices = comparator.createIndexArray();
Arrays.sort(indices, comparator);
List<PaperCard> deckPool=new ArrayList<>();
List<Map.Entry<PaperCard,Integer>> deckPool=new ArrayList<>();
int k=0;
boolean isZeroDistance=false;
boolean excludeThisCard=false;//if there are too few cards with at least one connection
for (int j=0;j<20;++k){
if(distances[indices[cardList.size()-1-k]]==0){
isZeroDistance=true;
excludeThisCard = true;
break;
}
PaperCard cardToAdd=integerCardMap.get(indices[cardList.size()-1-k]);
if(!cardToAdd.getRules().getMainPart().getType().isLand()){//need 15 non-land cards
if(!cardToAdd.getRules().getMainPart().getType().isLand()){//need x non-land cards
++j;
}
deckPool.add(cardToAdd);
deckPool.add(new AbstractMap.SimpleEntry<PaperCard, Integer>(cardToAdd,distances[indices[cardList.size()-1-k]]));
};
if(isZeroDistance){
if(excludeThisCard){
continue;
}
cardPools.put(card.getName(),deckPool);

View File

@@ -10,23 +10,25 @@ import java.util.List;
* Created by maustin on 09/05/2017.
*/
public class CardThemedDeckGenerator extends DeckProxy implements Comparable<CardThemedDeckGenerator> {
public static List<DeckProxy> getMatrixDecks(GameFormat format){
public static List<DeckProxy> getMatrixDecks(GameFormat format, boolean isForAi){
final List<DeckProxy> decks = new ArrayList<DeckProxy>();
for(String card: CardRelationMatrixGenerator.cardPools.get(format).keySet()) {
decks.add(new CardThemedDeckGenerator(card, format));
decks.add(new CardThemedDeckGenerator(card, format, isForAi));
}
return decks;
}
private final String name;
private final int index;
private final GameFormat format;
private final boolean isForAi;
private CardThemedDeckGenerator(String cardName, GameFormat format0) {
private CardThemedDeckGenerator(String cardName, GameFormat format0, boolean isForAi0) {
super();
name = cardName;
index = 0;
format=format0;
isForAi=isForAi0;
}
public CardEdition getEdition() {
@@ -52,7 +54,7 @@ public class CardThemedDeckGenerator extends DeckProxy implements Comparable<Car
@Override
public Deck getDeck() {
return DeckgenUtil.buildCardGenDeck(name,format);
return DeckgenUtil.buildCardGenDeck(name,format,isForAi);
}
@Override

View File

@@ -4,6 +4,7 @@ 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.CardDb;
import forge.card.CardRules;
import forge.card.CardRulesPredicates;
@@ -26,10 +27,7 @@ import forge.util.MyRandom;
import forge.util.gui.SOptionPane;
import forge.util.storage.IStorage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.*;
/**
* Utility collection for various types of decks.
@@ -41,35 +39,125 @@ import java.util.Random;
// TODO This class can be used for home menu constructed deck generation as well.
public class DeckgenUtil {
public static Deck buildCardGenDeck(GameFormat format){
public static Deck buildCardGenDeck(GameFormat format, boolean isForAI){
Random random = new Random();
List<String> keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format).keySet());
String randomKey = keys.get( random.nextInt(keys.size()) );
Predicate<PaperCard> cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(randomKey));
PaperCard keyCard = FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0);
try {
return buildCardGenDeck(keyCard,format);
return buildCardGenDeck(keyCard,format,isForAI);
}catch (Exception e){
e.printStackTrace();
return buildCardGenDeck(format);
return buildCardGenDeck(format,isForAI);
}
}
public static Deck buildCardGenDeck(String cardName, GameFormat format){
public static Deck buildCardGenDeck(String cardName, GameFormat format, boolean isForAI){
try {
Predicate<PaperCard> cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(cardName));
return buildCardGenDeck(FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0),format);
return buildCardGenDeck(FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0),format,isForAI);
}catch (Exception e){
e.printStackTrace();
return buildCardGenDeck(format);
return buildCardGenDeck(format,isForAI);
}
}
public static Deck buildCardGenDeck(PaperCard card, GameFormat format){
List<PaperCard> selectedCards = new ArrayList<>();
selectedCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName()));
List<PaperCard> toRemove = new ArrayList<>();
/**
* Take two lists of cards with counts of each and combine the second into the first by adding a mean normalized fraction
* of the count in the second list to the first list.
* @param cards1
* @param cards2
*/
public static void combineDistances(List<Map.Entry<PaperCard,Integer>> cards1,List<Map.Entry<PaperCard,Integer>> cards2){
Integer maxDistance=0;
for (Map.Entry<PaperCard,Integer> pair1:cards1){
maxDistance=maxDistance+pair1.getValue();
}
maxDistance=maxDistance/cards1.size();
Integer maxDistance2=0;
for (Map.Entry<PaperCard,Integer> pair2:cards2){
maxDistance2=maxDistance2+pair2.getValue();
}
maxDistance2=maxDistance2/cards2.size();
for (Map.Entry<PaperCard,Integer> pair2:cards2){
boolean isCardPresent=false;
for (Map.Entry<PaperCard,Integer> pair1:cards1){
if (pair1.getKey().equals(pair2.getKey())){
pair1.setValue(pair1.getValue()+new Float((pair2.getValue()*0.4*maxDistance/maxDistance2)).intValue());
isCardPresent=true;
break;
}
}
if(!isCardPresent){
Map.Entry<PaperCard,Integer> newEntry=new AbstractMap.SimpleEntry<PaperCard, Integer>(pair2.getKey(),new Float((pair2.getValue()*0.4*maxDistance/maxDistance2)).intValue());
cards1.add(pair2);
}
}
}
public static class CardDistanceComparator implements Comparator<Map.Entry<PaperCard,Integer>>
{
@Override
public int compare(Map.Entry<PaperCard,Integer> index1, Map.Entry<PaperCard,Integer> index2)
{
// Autounbox from Integer to int to use as array indexes
return index1.getValue().compareTo(index2.getValue());
}
}
/**
* Build a deck based on the chosen card.
*
* @param card
* @param format
* @param isForAI
* @return
*/
public static Deck buildCardGenDeck(PaperCard card, GameFormat format, boolean isForAI){
List<Map.Entry<PaperCard,Integer>> potentialCards = new ArrayList<>();
potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName()));
Collections.sort(potentialCards,new CardDistanceComparator());
Collections.reverse(potentialCards);
//get second keycard
Random r = new Random();
List<PaperCard> preSelectedCards = new ArrayList<>();
for(Map.Entry<PaperCard,Integer> pair:potentialCards){
preSelectedCards.add(pair.getKey());
}
//filter out land cards and if for AI non-playable cards as potential second key cards
Iterable<PaperCard> preSelectedNonLandCards;
if(isForAI){
preSelectedNonLandCards=Iterables.filter(preSelectedCards,Predicates.and(
Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES),
Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)));
}else{
preSelectedNonLandCards=Iterables.filter(preSelectedCards,
Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES));
}
preSelectedCards= Lists.newArrayList(preSelectedNonLandCards);
//choose a second card randomly from the top 8 cards if possible
int randMax=8;
if(preSelectedCards.size()<randMax){
randMax=preSelectedCards.size();
}
PaperCard secondKeycard = preSelectedCards.get(r.nextInt(randMax));
List<Map.Entry<PaperCard,Integer>> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format).get(secondKeycard.getName());
//combine card distances from second key card and re-sort
if(potentialSecondCards !=null && potentialSecondCards.size()>0) {
combineDistances(potentialCards, potentialSecondCards);
Collections.sort(potentialCards, new CardDistanceComparator());
Collections.reverse(potentialCards);
}
List<PaperCard> selectedCards = new ArrayList<>();
for(Map.Entry<PaperCard,Integer> pair:potentialCards){
selectedCards.add(pair.getKey());
}
List<PaperCard> toRemove = new ArrayList<>();
//randomly remove cards
int removeCount=0;
int i=0;
@@ -78,9 +166,16 @@ public class DeckgenUtil {
toRemove.add(c);
removeCount++;
}
if(c.getName().equals(card.getName())){//may have been added in secondary list
toRemove.add(c);
}
if(c.getName().equals(secondKeycard.getName())){//remove so we can add correct amount
toRemove.add(c);
}
++i;
}
selectedCards.removeAll(toRemove);
//Add keycard
List<PaperCard> playsetList = new ArrayList<>();
int keyCardCount=4;
if(card.getRules().getMainPart().getManaCost().getCMC()>7){
@@ -91,6 +186,16 @@ public class DeckgenUtil {
for(int j=0;j<keyCardCount;++j) {
playsetList.add(card);
}
//Add 2nd keycard
int keyCard2Count=4;
if(card.getRules().getMainPart().getManaCost().getCMC()>7){
keyCard2Count=1+r.nextInt(4);
}else if(card.getRules().getMainPart().getManaCost().getCMC()>5){
keyCard2Count=2+r.nextInt(3);
}
for(int j=0;j<keyCard2Count;++j) {
playsetList.add(secondKeycard);
}
for (PaperCard c:selectedCards){
for(int j=0;j<4;++j) {
if(r.nextInt(100)<90) {
@@ -98,16 +203,18 @@ public class DeckgenUtil {
}
}
}
CardThemedDeckBuilder dBuilder = new CardThemedDeckBuilder(card, playsetList,format);
//build deck from combined list
CardThemedDeckBuilder dBuilder = new CardThemedDeckBuilder(card,secondKeycard, 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);
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);
deck=buildCardGenDeck(format,isForAI);
}
while(deck.get(DeckSection.Sideboard).countAll()>15){
deck.get(DeckSection.Sideboard).remove(deck.get(DeckSection.Sideboard).get(0));

View File

@@ -99,9 +99,9 @@ public class RandomDeckGenerator extends DeckProxy implements Comparable<RandomD
}
return DeckgenUtil.buildColorDeck(colors, null, isAi);
case STANDARD_CARDGEN_DECK:
return DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard());
return DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard(),isAi);
case MODERN_CARDGEN_DECK:
return DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern());
return DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern(),isAi);
case STANDARD_COLOR_DECK:
colors = new ArrayList<String>();
count = Aggregates.randomInt(1, 3);

View File

@@ -30,7 +30,7 @@ public class CardThemedMatrixIO {
/** suffix for all gauntlet data files */
public static final String SUFFIX_DATA = ".dat";
public static void saveMatrix(GameFormat format, HashMap<String,List<PaperCard>> map){
public static void saveMatrix(GameFormat format, HashMap<String,List<Map.Entry<PaperCard,Integer>>> map){
File file = getMatrixFile(format);
ObjectOutputStream s = null;
try {
@@ -51,11 +51,11 @@ public class CardThemedMatrixIO {
}
}
public static HashMap<String,List<PaperCard>> loadMatrix(GameFormat format){
public static HashMap<String,List<Map.Entry<PaperCard,Integer>>> loadMatrix(GameFormat format){
try {
FileInputStream fin = new FileInputStream(getMatrixFile(format));
ObjectInputStream s = new ObjectInputStream(fin);
HashMap<String, List<PaperCard>> matrix = (HashMap<String, List<PaperCard>>) s.readObject();
HashMap<String, List<Map.Entry<PaperCard,Integer>>> matrix = (HashMap<String, List<Map.Entry<PaperCard,Integer>>>) s.readObject();
s.close();
return matrix;
}catch (Exception e){

View File

@@ -33,10 +33,10 @@ public class GauntletUtil {
deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getStandard().getFilterPrinted(),true);
break;
case STANDARD_CARDGEN_DECK:
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard());
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard(),true);
break;
case MODERN_CARDGEN_DECK:
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern());
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern(),true);
break;
case MODERN_COLOR_DECK:
deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getModern().getFilterPrinted(),true);

View File

@@ -46,6 +46,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
protected int landsNeeded = 25;
protected final PaperCard keyCard;
protected final PaperCard secondKeyCard;
protected DeckColors deckColors;
protected Predicate<CardRules> hasColor;
@@ -71,14 +72,19 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
* @param dList
* Cards to build the deck from.
*/
public CardThemedDeckBuilder(PaperCard keyCard0, final List<PaperCard> dList, GameFormat format) {
public CardThemedDeckBuilder(PaperCard keyCard0,PaperCard secondKeyCard0, final List<PaperCard> dList, GameFormat format, boolean isForAI) {
super(FModel.getMagicDb().getCommonCards(), DeckFormat.Limited, format.getFilterPrinted());
this.availableList = dList;
keyCard=keyCard0;
secondKeyCard=secondKeyCard0;
// remove Unplayables
if(isForAI) {
final Iterable<PaperCard> 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);
deckColors = new DeckColors();
for(PaperCard c:getAiPlayables()){
@@ -94,6 +100,9 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
if(!colors.hasAllColors(keyCard.getRules().getColorIdentity().getColor())){
colors = ColorSet.fromMask(colors.getColor() | keyCard.getRules().getColorIdentity().getColor());
}
if(!colors.hasAllColors(secondKeyCard.getRules().getColorIdentity().getColor())){
colors = ColorSet.fromMask(colors.getColor() | secondKeyCard.getRules().getColorIdentity().getColor());
}
if (logColorsToConsole) {
System.out.println(keyCard.getName());
System.out.println("Pre Colors: " + colors.toEnumSet().toString());
@@ -136,6 +145,14 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
aiPlayables.removeAll(keyCardList);
rankedColorList.removeAll(keyCardList);
}
// Add the deck card
if(!secondKeyCard.getRules().getMainPart().getType().isLand()&&!keyCard.getRules().getMainPart().getType().isCreature()) {
Iterable<PaperCard> secondKeyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(secondKeyCard.getName()));
final List<PaperCard> keyCardList = Lists.newArrayList(secondKeyCards);
deckList.addAll(keyCardList);
aiPlayables.removeAll(keyCardList);
rankedColorList.removeAll(keyCardList);
}
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
@@ -302,7 +319,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
* @return name
*/
private String generateName() {
return keyCard.getName() + " based deck";
return keyCard.getName() + " / " + secondKeyCard.getName() +" based deck";
}
/**
@@ -754,11 +771,11 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase {
rankedColorList.removeAll(keyCardList);
}
final Map<Integer,Integer> targetCMCs = new HashMap<>();
targetCMCs.put(1,r.nextInt(5));//2
targetCMCs.put(2,r.nextInt(5)+4);//6
targetCMCs.put(3,r.nextInt(5)+5);//7
targetCMCs.put(4,r.nextInt(5)+2);//4
targetCMCs.put(5,r.nextInt(5)+2);//3
targetCMCs.put(1,r.nextInt(4)+2);//2
targetCMCs.put(2,r.nextInt(5)+5);//6
targetCMCs.put(3,r.nextInt(5)+6);//7
targetCMCs.put(4,r.nextInt(3)+3);//4
targetCMCs.put(5,r.nextInt(3)+3);//3
targetCMCs.put(6,r.nextInt(3)+1);//2

View File

@@ -33,10 +33,10 @@ public class TournamentUtil {
deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getStandard().getFilterPrinted(),true);
break;
case STANDARD_CARDGEN_DECK:
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard());
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard(),true);
break;
case MODERN_CARDGEN_DECK:
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern());
deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern(),true);
break;
case MODERN_COLOR_DECK:
deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getModern().getFilterPrinted(),true);