It is currently 13 Sep 2025, 22:53
   
Text Size

Splitting CardFactory

Post MTG Forge Related Programming Questions Here

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

Re: Splitting CardFactory

Postby Chris H. » 12 Oct 2009, 16:55

DennisBergkamp wrote:There HAS to be some way of physically splitting this thing. We're about 100 lines away from 40,000 in CardFactory :roll: my Eclipse rolls over and dies whenever I change anything in there. I'll also have another attempt at this.
`
We feel your pain Dennis, I find that using the revert command on CardFactory takes long enough for a coffee break. :shock:
User avatar
Chris H.
Forge Moderator
 
Posts: 6320
Joined: 04 Nov 2008, 12:11
Location: Mac OS X Yosemite
Has thanked: 644 times
Been thanked: 643 times

Re: Splitting CardFactory

Postby silly freak » 12 Oct 2009, 17:02

don't even think of doing the automatic eclipse formatter, save yourself from that pain ;)
___

where's the "trust me, that will work!" switch for the compiler?
Laterna Magica - blog, forum, project, 2010/09/06 release!
silly freak
DEVELOPER
 
Posts: 598
Joined: 26 Mar 2009, 07:18
Location: Vienna, Austria
Has thanked: 93 times
Been thanked: 25 times

Re: Splitting CardFactory

Postby Rob Cashwalker » 12 Oct 2009, 19:16

My boss and I were discussing some possible solutions. His recommendation was to turn each card into its own class file, each being an inherited card object... and groups of the classes can be easily packaged together. Which sounds like something right up silly freak's alley.
Now, his understanding of the architecture is nil. I explained that we're doing something similar. (really just subclassing the spellability objects instead of the whole card) But I'm thinking, we're probably not far off from that sort of implementation, and is something I recall Rares was talking about in v2.
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: Splitting CardFactory

Postby DennisBergkamp » 12 Oct 2009, 19:56

It's interesting, a friend of mine (who is a very good software architect) was thinking the same thing, using reflection and creating a separate class for each card.
Now, I'm not sure if that's the best way... first of all, it would obviously take a tremendous amount of work.
We also talked about the option of splitting up cards by type, but then there's the possibility of permanents having multiple types (Artifact Creature, Artifact Land, Creature Enchantment, etc.). But still I prefer the latter direction.
User avatar
DennisBergkamp
AI Programmer
 
Posts: 2602
Joined: 09 Sep 2008, 15:46
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby nantuko84 » 12 Oct 2009, 20:36

In MagicWars I use reflection to load card implementation from java class.
Actually it is easier for me as at the moment I have only 430 cards implemented that is less than yours, but even 430 is large number and it is not problem for me as I use packages depending on card color:
package mw.server.card.gold.ubr
so ubr package have only 8 cards than more less than 430 ;)

and my next step will be bringing set name to package name, smth like
mw.server.card.zen.green.

first of all, it would obviously take a tremendous amount of work.
your CardFactory has determinate structure: <if><card implementation><else if> etc. so I do believe it's easy to write parser that will cut them into java classes according to template. I'd say it will be entertaining task to do;) there are also generics at the beginning, I guess they also should be moved to separate classes (factories or decorators if you can combine them).
nantuko84
DEVELOPER
 
Posts: 266
Joined: 08 Feb 2009, 21:14
Has thanked: 2 times
Been thanked: 9 times

Re: Splitting CardFactory

Postby zerker2000 » 13 Oct 2009, 04:52

@Dennis: for me, it's more of my computer slowing down to a sleepy snail's pace :P.

@Rob: I would say that an optimal solution for AI methods would be a CardFactoryAI, which stores all (non-equivalent) AIresolve, canPlayAI, and chooseTargetAI methods, and only gets used during the actual game.

@Classes idea: I do think it's better to have a 40k line class than having 2000 20 line classes :?. Also, if AI methods get split off, it would be feasible to make a pseudocode card factory and run it through a generic action parser, and then convert all the "Card tok = new Card()"'s and "AllZone.Play.add(tok)"'s
into something more non-coder friendly:
Code: Select all
Make activated ability named ab1
Set ab1 mana cost to "1G"
Make action named ab1resolve
{
  Make card named tok
  Set tok name to "Insect"
  Set tok mana cost to "G"
  Set tok pt to 1/1
  Set tok image to "insect_1_1.jpg"
  Put tok in battlefield
}
Set ab1 resolve to ab1resolve
Set ab1 description to "1G: Put a 1/1 green Insect creature token onto the battlefield."
Add ab1 to card
Along these lines, you could of course have
Code: Select all
Make input named in
Set in buttons to none
Set in choose card to "target <insert spDestroy card filter here>"
Set card input to in
Or
Code: Select all
...
Make action choose
{
  Get in chosen card named choice
  If choice not in play
    Exit
  Get choice controller named cont
  Get maincard controller named cont2
  If cont not equal to cont2
    Exit
  Set maincard target to choice
  Make input named manain
  Set card mana cost to mcost
  Input mcost
}
Set in choose card to choose
...
This shouldn't be too hard to make either:
<code pending, wait for edit if not obvious, meanwhile comments please :)>
EDIT: Look a couple posts down :)
Last edited by zerker2000 on 14 Oct 2009, 00:53, edited 1 time in total.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby Rob Cashwalker » 13 Oct 2009, 15:31

That sort of syntax is quite tempting... and not far from Rares' own vision.

10,000 classes doesn't seem too bad (in theory) since the code for any one card should be completely contained therein, especially if they're packaged separately by set or something. (not counting the possibility that cards show up in multiple sets...) Also consider that many cards can share a single class file that covers their effect... similar to how we do the keywords now. I'm told that Java can dynamically reference a class by using a string variable. So we could have for example, a single class that deals with "spPumpTgt".
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: Splitting CardFactory

Postby zerker2000 » 13 Oct 2009, 22:29

Rob Cashwalker wrote:I'm told that Java can dynamically reference a class by using a string variable. So we could have for example, a single class that deals with "spPumpTgt".
I think that would be
Code: Select all
java.lang.Class.forName("spPumpTgt").newInstance();
What would spPumpTgt extend though, SpellAbility?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Parsing code, first atempt

Postby zerker2000 » 14 Oct 2009, 00:54

Code: Select all
ArrayList<Object> all;
   HashMap<String, Integer> labels;   
   Integer index;
   public Card getNextCard(BufferedReader reader) throws IOException
   {
     labels.clear();
     final Card res = new Card();
     res.setName(reader.readLine());
     all.add(res);
     index++;
     labels.put("maincard", 0);
     parseCommand(reader, true).execute();
     return res;
   }

   Command parseCommand(BufferedReader reader, boolean linesleft) throws IOException
   {
     final String command = reader.readLine().trim();
     if(command.isEmpty() || command.equals ("}"))
     {
       linesleft = false;
       return Command.Blank;
     }
     final String[] pieces = command.split(" ");
     switch(pieces[0]){
       case "Exit": return null;
       case "{":
         boolean cont = true;
         final ArrayList<Command> todo;
         while(cont) todo.add(parseCommand(reader, cont));
         return new Command(){ public void execute()
         {
           for(Command c : todo)
           {
             if(c != null)
               c.execute();
             else
               break;
           }
         }};
      
      String label = command.split(" named ")[1];
      final int where = index;
       case "Make":
       labels.put(label, index);
       index++;
       return new Command(){ public void execute()
         {
           all.set(where, parseClass.get(command.substring(5).split(" named ")[0]).newInstance());
           //where parseClass is a Map<String, Class> containing values like <"activated ability", Ability.Class>
         }
       };
      
       final Object whose = labelGet(pieces[1]);
       final String how = unParseClass.get(whose.getClass()) + command.split(" to ")[0].substring(command.indexOf(" ", 4));
       //where unParseClass is a Map<Class, String> containing values like <Input.Class, "input">
       case "Set":
       final Object what = labelGet(command.split(" to ")[1]);
       return new Command(){ public void execute(){ parseSet.get(how).invoke(whose, what);}};
       //where parseSet is a Map<String, Method> containing values like <"card name", Card.setName>
      
       case "Get":
       labels.put(label, index);
       index++;
       return new Command(){ public void execute(){ all.set(where, parseGet.get(how).invoke(whose, what));}};
       //where parseGet is a Map<String, Method> containing values like <"spell or ability mana cost", SpellAbility.setManaCost>
      
       case "Input":
       final Object in = labelGet(pieces[1]);
       if(!(in instanceof Input)) throw new IllegalArgumentException("Error : " + command.split(" named ")[1] + " is not an Input");
       return new Command(){ public void execute(){ AllZone.InputControl.setInput((Input)in);}};

       case "Add":
       final SpellAbility sa = (SpellAbility)labelGet(pieces[1]);
       final Card card = (Card)labelGet(command.split(" to ")[1]);
       return new Command(){ public void execute(){ card.addSpellAbility(sa);}};//TODO: Counters?
      
       case "Put":
       final Card card_2 = (Card)labelGet(pieces[1]);
       final PlayerZone zone = parseZone.get(command.split(" in ")[1]);
       //where parseZone is a Map<String, PLayerZone> ... i.e. <"battlefield", AllZone.Play>
       return new Command(){ public void execute(){AllZone.GameAction.moveTo(zone, card_2);}};
      
       case "If":
       final Object arg1 = labelGet(pieces[1]);
       final boolean not = pieces[2].equals("not");
       final Command res = parseCommand(reader, linesleft);
       final Method todo_2 = parseIf.get(unParseClass.get(whose.getClass()) + command.substring(command.indexOf(" ", 3) + (not ? 0 : 4)));
       //where parseIf is a Map... <"input equals", Input.equals>, <"card in play", Card.inPlay>
       Class[] need = todo_2.getParameterTypes();
       final Object[] args = new Object[need.length];
       if(need.length == 1) args[1] = labelGet(pieces[pieces.length-1]);
       return new Command(){ public void execute(){ if(todo_2.invoke(arg1, args).equals(false) ^ not) res.execute();}};
     }
   }
   
   Object labelGet(String label){return all.get(labels.get(label));}
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby zerker2000 » 15 Oct 2009, 00:29

Err, comments please? Also, is there any way to get around switches being unable to use Strings without converting the code to a series of if statements?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby Rob Cashwalker » 15 Oct 2009, 02:55

I was thinking that you had blown my mind... and I was without any comment....
Then you brought it back down to earth, when you realized you can't switch Strings.
To be honest, if-blocks don't make much of a difference in readability. And a switch becomes basically the same as a series of if's when compiled.

Also, I'm thinking that the syntax example you're trying to work with is too free-form.
I'm thinking of something that combines the pseudo functions (from my spDamageTgt post) with a parser that doesn't care which order the pseudo functions are in the keyword string.
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: Splitting CardFactory

Postby zerker2000 » 15 Oct 2009, 04:08

Code: Select all
enum name
{
EXIT, MAKE, SET, GET, INPUT, ADD, PUT, IF, PAROPEN;
public String toString(){return name().charAt(0) + name().toLowerCase().substring(1);}
}
HashMap<String, name> parseCommand = new HashMap<String, name>;
public void init()//call where appropriate
{
  parseCommand.put("{", name.PAROPEN);
  for (name n : name.values())
    if(!n.toString().equals("Paropen"))
      parseCommand.put(n.toString(), n);
}
...
switch (parseCommand.get(pieces[0]))
case EXIT:
...
case IF:
:?:
P.S. If you were blown away by what it would have been if Strings were switchable, would you like to write the parseFoo map initializer :mrgreen: ?
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby Rob Cashwalker » 15 Oct 2009, 11:24

Hmm... you're making this almost too easy.
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: Splitting CardFactory

Postby zerker2000 » 15 Oct 2009, 14:41

The basic parsing part is. However, there's still the matter of going through all of CardFactory, converting it to this format, and mapping all conceivable variable types, get and set combinations, and If conditions: basically, lots of long tedious paperwork :P.
O forest, hold thy wand'ring son
Though fears assail the door.
O foliage, cloak thy ravaged one
In vestments cut for war.


--Eladamri, the Seed of Freyalise
zerker2000
Programmer
 
Posts: 569
Joined: 09 May 2009, 21:40
Location: South Pasadena, CA
Has thanked: 0 time
Been thanked: 0 time

Re: Splitting CardFactory

Postby Rob Cashwalker » 15 Oct 2009, 18:22

No, I see it as something that we should be able to make work concurrently with the existing stuff... just like the keywords are interpreted now. We just use a delimiter other than CRLF..
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

PreviousNext

Return to Developer's Corner

Who is online

Users browsing this forum: No registered users and 38 guests

Main Menu

User Menu

Our Partners


Who is online

In total there are 38 users online :: 0 registered, 0 hidden and 38 guests (based on users active over the past 10 minutes)
Most users ever online was 7967 on 09 Sep 2025, 23:08

Users browsing this forum: No registered users and 38 guests

Login Form