It is currently 07 Sep 2025, 10:00
   
Text Size

Semi-Random Theme Deck Generator

Post MTG Forge Related Programming Questions Here

Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins

Re: Semi-Random Theme Deck Generator

Postby Sloth » 24 Sep 2010, 09:58

Those are some nice themes Marek14. I'm sure we will find a way to implement them, however the "Adventure mode" turns out.

I guess the hardest part will be to balance them and/or give them a difficulty level or something.
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Semi-Random Theme Deck Generator

Postby Marek14 » 24 Sep 2010, 11:03

Thanks :)

I'm sure some more themes could be found, but for starters I focused on the idea that these are decks played by creatures (and planeswalkers).
Marek14
Tester
 
Posts: 2773
Joined: 07 Jun 2008, 07:54
Has thanked: 0 time
Been thanked: 303 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 24 Sep 2010, 11:27

I don't think we need to limit this concept to just the creature battles. This can be a fun tool for the regular constructed games, both for the AI and human.

Has anyone figured out why my change to remove the remove list is causing the slow down? The fan in my laptop died, so I haven't had a chance to really look into it.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Sloth » 24 Sep 2010, 13:25

Rob Cashwalker wrote:Has anyone figured out why my change to remove the remove list is causing the slow down? The fan in my laptop died, so I haven't had a chance to really look into it.
I found it: You deleted a "!" before c.getSVar("RemAIDeck").equals("True"), so decks were generated only from cards that have RemAIDeck:True. Since their number is so small and the Generation is trial and error it took so long. Fixed with one character: "!". :D
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 24 Sep 2010, 13:36

woo-hoo! Thanks for the catch. I'm glad it was that sort of simple mistake and not something ancillary to the theme deck stuff.

In other news my boss is bringing in his twin of my laptop so I can swap fans.... (his has a cracked LCD)

Sometime this evening I'll start to rework this generator with some of the concepts above.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 26 Sep 2010, 03:28

OK, based on the discussions so far, I've revised the format.

It now expects the following:
[Group MaxCnt=# Percentage=#]
Cards
Cards
[/Group]

and somewhat optional:
BasicLandPercentage=#
(if not defined then all remaining card slots not filled will be basic lands)

Basic Lands are added in proportion to the colors represented by the cards selected.

If after the deck is essentially assembled, if it is over or under the specified Size, then random cards (no more than 4x) already selected in the deck will be added or removed to make up the difference. (Basic Lands will never be removed to meet the Size)

Yes the debug window listing the deck contents is still here for your testing pleasure. I'm thinking of making it an option of the theme file, add a line "Testing" and the debug window pops up.

Code: Select all
package forge;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import forge.error.ErrorViewer;

public class GenerateThemeDeck
{
   private BufferedReader in = null;
   
   public GenerateThemeDeck()
   {
      
   }
   
   public ArrayList<String> getThemeNames()
   {
      ArrayList<String> ltNames = new ArrayList<String>();
      
      File file = new File("res/quest/themes/");
      
        if(!file.exists())
            throw new RuntimeException("GenerateThemeDeck : getThemeNames error -- file not found -- filename is "
                    + file.getAbsolutePath());
       
        if (!file.isDirectory())
           throw new RuntimeException("GenerateThemeDeck : getThemeNames error -- not a direcotry -- "
                 + file.getAbsolutePath());
       
        String[] fileList = file.list();
        for (int i=0; i<fileList.length; i++)
        {
           if (fileList[i].endsWith(".thm"))
              ltNames.add(fileList[i].substring(0, fileList[i].indexOf(".thm")));
        }
       
      return ltNames;
   }
   
   public CardList getThemeDeck(String ThemeName, int Size)
   {
      CardList tDeck = new CardList();
            
      ArrayList<Grp> Groups = new ArrayList<Grp>();
      
      Map<String,Integer> CardCounts = new HashMap<String,Integer>();
      
      String s = "";
      int BLandPercentage = 0;
      
      // read theme file
      String tFileName = "res/quest/themes/" + ThemeName + ".thm";
      File tFile = new File(tFileName);
      if(!tFile.exists())
            throw new RuntimeException("GenerateThemeDeck : getThemeDeck -- file not found -- filename is " + tFile.getAbsolutePath());
      
      try {
            in = new BufferedReader(new FileReader(tFile));
        } catch(Exception ex) {
            ErrorViewer.showError(ex, "File \"%s\" exception", tFile.getAbsolutePath());
            throw new RuntimeException("GenerateThemeDeck : getThemeDeck -- file exception -- filename is " + tFile.getPath());
        }
       
        s = readLine();
        while (!s.equals("End"))
        {
           if (s.startsWith("[Group"))
           {
              Grp G = new Grp();
              
              String ss[] = s.replaceAll("[\\[\\]]", "").split(" ");
              for (int i=0; i<ss.length; i++)
              {
                 if (ss[i].startsWith("Percentage"))
                 {
                    String p = ss[i].substring("Percentage".length() + 1);
                    G.Percentage = Integer.parseInt(p);
                 }
                 if (ss[i].startsWith("MaxCnt"))
                 {
                    String m = ss[i].substring("MaxCnt".length() + 1);
                    G.MaxCnt = Integer.parseInt(m);
                 }
              }
              
              s = readLine();
              while (!s.equals("[/Group]"))
              {
                 G.Cardnames.add(s);
                 CardCounts.put(s, 0);
                 
                 s = readLine();
              }
              
              Groups.add(G);
           }
           
           if (s.equals("BasicLandPercentage"))
              BLandPercentage = Integer.parseInt(s.substring("BasicLandPercentage".length() + 1));
           
           s = readLine();
        }
       
        try {
         in.close();
      } catch (IOException ex) {
            ErrorViewer.showError(ex, "File \"%s\" exception", tFile.getAbsolutePath());
            throw new RuntimeException("GenerateThemeDeck : getThemeDeck -- file exception -- filename is " + tFile.getPath());
      }
      
      String tmpDeck = "";

      // begin assigning cards to the deck
      Random r = new Random();
      
      for (int i=0; i<Groups.size(); i++)
      {
         Grp G = Groups.get(i);
         float p = (float) ((float)G.Percentage * .01);
         int GrpCnt = (int)(p * (float)Size);
         int cnSize = G.Cardnames.size();
         tmpDeck += "Group" + i + ":" + GrpCnt + "\n";
         
         for (int j=0; j<GrpCnt; j++)
         {
            s = G.Cardnames.get(r.nextInt(cnSize));
            
            int lc = 0;
            while (CardCounts.get(s) >= G.MaxCnt || lc > Size) // don't keep looping forever
            {
               s = G.Cardnames.get(r.nextInt(cnSize));
               lc++;
            }
            if (lc > Size)
               throw new RuntimeException("GenerateThemeDeck : getThemeDeck -- looped too much -- filename is " + tFile.getAbsolutePath());
            
            int n = CardCounts.get(s);
            tDeck.add(AllZone.CardFactory.getCard(s, Constant.Player.Computer));
            CardCounts.put(s, n + 1);
            tmpDeck += s + "\n";

         }
      }
      
      int numBLands = 0;
      if (BLandPercentage > 0)   // if theme explicitly defines this
      {
         float p = (float)((float)BLandPercentage * .01);
         numBLands = (int)(p * (float)Size);
      }
      else    // otherwise, just fill in the rest of the deck with basic lands
         numBLands = Size - tDeck.size();
      
      tmpDeck += "numBLands:" + numBLands + "\n";
      
      if (numBLands > 0)   // attempt to optimize basic land counts according to color representation
      {
         CCnt ClrCnts[] = {new CCnt("Plains", 0),
                       new CCnt("Island", 0),
                       new CCnt("Swamp", 0),
                       new CCnt("Mountain", 0),
                       new CCnt("Forest", 0)};
               
         // count each instance of a color in mana costs
         // TODO: count hybrid mana differently?
         // TODO: count all color letters? ie: 2 W W counts as 2
         for (int i=0;i<tDeck.size(); i++)
         {
            Card c = tDeck.get(i);
            String mc = c.getManaCost();
            
            if (mc.contains("W"))
               ClrCnts[0].Count++;
            
            if (mc.contains("U"))
               ClrCnts[1].Count++;
            
            if (mc.contains("B"))
               ClrCnts[2].Count++;
            
            if (mc.contains("R"))
               ClrCnts[3].Count++;
   
            if (mc.contains("G"))
               ClrCnts[4].Count++;
         }
   
         int totalColor = 0;
         for (int i=0;i<5; i++)
         {
            totalColor += ClrCnts[i].Count;
            tmpDeck += ClrCnts[i].Color + ":" + ClrCnts[i].Count + "\n";
         }
         
         tmpDeck += "totalColor:" + totalColor + "\n";
         
         for (int i=0; i<5; i++)
         {
            if (ClrCnts[i].Count > 0)
            {   // calculate number of lands for each color
               float p = (float)ClrCnts[i].Count / (float)totalColor;
               int nLand = (int)((float)numBLands * p);
               tmpDeck += "numLand-" + ClrCnts[i].Color + ":" + nLand + "\n";
            
               for (int j=0; j<nLand; j++)
                  tDeck.add(AllZone.CardFactory.getCard(ClrCnts[i].Color, Constant.Player.Computer));
            }
         }
      }
      tmpDeck += "DeckSize:" + tDeck.size() + "\n";
      
      if (tDeck.size() < Size)
      {
         int diff = Size - tDeck.size();
         
         for (int i=0; i<diff; i++)
         {
            s = tDeck.get(r.nextInt(tDeck.size())).getName();
            
            while (CardCounts.get(s) >= 4)
               s = tDeck.get(r.nextInt(tDeck.size())).getName();
            
            int n = CardCounts.get(s);
            tDeck.add(AllZone.CardFactory.getCard(s, Constant.Player.Computer));
            CardCounts.put(s, n + 1);
            tmpDeck += "Added:" + s + "\n";
         }
      }
      else if (tDeck.size() > Size)
      {
         int diff = tDeck.size() - Size;
         
         for (int i=0; i<diff; i++)
         {
            Card c = tDeck.get(r.nextInt(tDeck.size()));
            
            while (c.getType().contains("Basic"))
               c = tDeck.get(r.nextInt(tDeck.size()));
            
            tDeck.remove(c);
            tmpDeck += "Removed:" + s + "\n";
         }
      }
      
      tmpDeck += "DeckSize:" + tDeck.size() + "\n";
      ErrorViewer.showError(tmpDeck);

      return tDeck;
   }
   
    private String readLine() {
        //makes the checked exception, into an unchecked runtime exception
        try {
            String s = in.readLine();
            if(s != null) s = s.trim();
            return s;
        } catch(Exception ex) {
            ErrorViewer.showError(ex);
            throw new RuntimeException("GenerateThemeDeck : readLine error");
        }
    }//readLine(Card)

}

class CCnt
{
   public String Color;
   public int Count;
   
   public CCnt(String clr, int cnt)
   {
      Color = clr;
      Count = cnt;
   }
}

class Grp
{
   public ArrayList<String> Cardnames = new ArrayList<String>();
   public int MaxCnt;
   public int Percentage;
}
White.thm:
Code: Select all
BasicLandPercentage=34

[Group MaxCnt=3 Percentage=10]
Blasted Landscape
Darksteel Citadel
Drifting Meadow
[/Group]

[Group MaxCnt=4 Percentage=30]
Glorious Anthem
Isamaru, Hound of Konda
Savannah Lions
Silvercoat Lion
Skyhunter Prowler
Stand Firm
Sunlance
[/Group]

[Group MaxCnt=3 Percentage=25]
Standing Troops
Staunch Defenders
Steadfast Guard
Steadfastness
Steppe Lynx
Swords to Plowshares
Unmake
Valorous Charge
[/Group]

End
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Sloth » 26 Sep 2010, 08:31

Thank you Rob, this should be everything I need to design some fine themes. I think I will toy around with it a bit today.
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 27 Sep 2010, 00:15

I see you added the domain theme. However it isn't correct. The MaxCnt parameter is the max number of any one card. In other words, no more than 4. This is intended to give a card group more diversity. The actual card count of the entire group is purely based on the percentage.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby mtgrares » 28 Sep 2010, 17:19

I really like the idea of a Semi-Random Theme Deck Generator. I'm not much of a deck builder and I enjoy playing against the AI with the Generate Deck option. It is hard to balance "how random should it be" because some randomness is good but too much randomness is chaotic. Balancing the converted mana cost is also good because it will make the decks more playable. (Sometimes the generated decks is too random and unplayable.)
mtgrares
DEVELOPER
 
Posts: 1352
Joined: 08 Sep 2008, 22:10
Has thanked: 3 times
Been thanked: 12 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 28 Sep 2010, 19:13

I'm working on a new true random deck generator, based on some of the ideas I had with the theme generator. One of our feature requests are random decks with user-chosen colors, so that's what I'm doing.
The trick I'm trying to implement is avoiding singleton decks, by pre-selecting a small pool of cards, then selecting the actual deck from it.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 30 Sep 2010, 11:52

I just submitted an update to the theme deck generator. If the theme file contains a line "Testing" then it will display the deck list in an ErrorViewer window, otherwise, it will just play the deck.
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Sloth » 13 Oct 2010, 19:54

Rob Cashwalker wrote:I just submitted an update to the theme deck generator. If the theme file contains a line "Testing" then it will display the deck list in an ErrorViewer window, otherwise, it will just play the deck.
I just added 3 new themes, but sometimes the theme deck generator returns a null exception. (I checked that all cards are present). I used odd numbers for the percentages, though.

Example:
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Semi-Random Theme Deck Generator

Postby Rob Cashwalker » 13 Oct 2010, 20:27

error trace for the nulls?
The Force will be with you, Always.
User avatar
Rob Cashwalker
Programmer
 
Posts: 2167
Joined: 09 Sep 2008, 15:09
Location: New York
Has thanked: 5 times
Been thanked: 40 times

Re: Semi-Random Theme Deck Generator

Postby Sloth » 13 Oct 2010, 20:40

Rob Cashwalker wrote:error trace for the nulls?
Code: Select all
Detailed error trace:
java.lang.NullPointerException
   at forge.GenerateThemeDeck.getThemeDeck(Unknown Source)
   at forge.Gui_NewGame.generateConstructedThemeDeck(Unknown Source)
   at forge.Gui_NewGame.genDecks(Unknown Source)
   at forge.Gui_NewGame.startButton_actionPerformed(Unknown Source)
   at forge.Gui_NewGame$11.actionPerformed(Unknown Source)
   at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
   at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
   at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
   at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
   at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
   at java.awt.Component.processMouseEvent(Unknown Source)
   at javax.swing.JComponent.processMouseEvent(Unknown Source)
   at java.awt.Component.processEvent(Unknown Source)
   at java.awt.Container.processEvent(Unknown Source)
   at java.awt.Component.dispatchEventImpl(Unknown Source)
   at java.awt.Container.dispatchEventImpl(Unknown Source)
   at java.awt.Component.dispatchEvent(Unknown Source)
   at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
   at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
   at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
   at java.awt.Container.dispatchEventImpl(Unknown Source)
   at java.awt.Window.dispatchEventImpl(Unknown Source)
   at java.awt.Component.dispatchEvent(Unknown Source)
   at java.awt.EventQueue.dispatchEvent(Unknown Source)
   at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
   at java.awt.EventDispatchThread.run(Unknown Source)
User avatar
Sloth
Programmer
 
Posts: 3498
Joined: 23 Jun 2009, 19:40
Has thanked: 125 times
Been thanked: 507 times

Re: Semi-Random Theme Deck Generator

Postby friarsol » 13 Oct 2010, 21:10

I think I was getting them in the same place. It looks like the Basic Lands are rounding down when there are more than one to fill in. For the deck I was building it was trying to add 15 basic lands. But after calculation it was only adding 8 forests and 6 plains for a total of 14.

Somewhere there needs to be an extra check to see if the amount of Basics generated was the same as expected and if not do something about it.

Line for me was: at forge.GenerateThemeDeck.getThemeDeck(GenerateThemeDeck.java:238)
friarsol
Global Moderator
 
Posts: 7593
Joined: 15 May 2010, 04:20
Has thanked: 243 times
Been thanked: 965 times

PreviousNext

Return to Developer's Corner

Who is online

Users browsing this forum: TRT and 47 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 48 users online :: 1 registered, 0 hidden and 47 guests (based on users active over the past 10 minutes)
Most users ever online was 7303 on 15 Jul 2025, 20:46

Users browsing this forum: TRT and 47 guests

Login Form