Parsing Cards rules

When creating a Magic the Gathering simulation (or any other collectible card game), one of the first decisions one has to make is how to store the Data for the cards. It is a decision that needs to be taken in early stages of the development, yet it has such a huge impact on the future of the game that it shouldn’t be taken lightly.
This information will of course tell the game what the card does, when it can be played, what it’s effects are, etc…, so it is obviously very important.

There are several MTG computer simulations out there, and we all went with different choices.

Firemox

Firemox’s cards are coded in XML. Firemox was designed to be extremely flexible and allow several kinds of games, but as far as I know it never supported anything else than Magic the gathering. Their XML files looks overly complex to me, especially since the “flexibility” is still limited by the possibilities of the engine behind.

<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<card xmlns=”http://sourceforge.net/projects/firemox”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://sourceforge.net/projects/firemox ../../validator.xsd”
name=”Angel of Light”>
<rules-author-comment>riclas</rules-author-comment>
<init>
<registers>
<register index=”white” value=”1″/>
<register index=”colorless” value=”4″/>
<register index=”power” value=”3″/>
<register index=”toughness” value=”3″/>
</registers>
<colors>white</colors>
<idcards>creature</idcards>
<properties>vigilance flying angel</properties>
</init>
<abilities>
<ability ref=”cast-spell”/>
</abilities>
</card>

Incantus

Incantus is coded in Python, and the cards as well. Using a scripting language for the data is a pretty clever way of doing things, especially when it’s the exact same language you’re using for the engine. It’s not tood difficult to read (although not as easy as I would expect), and extremely flexible since it gives you control on the rest of the code.

name = ‘Mogg Fanatic’
cardnum = 219
expansion = ’10E’
type = characteristic(‘Creature’)
supertype = no_characteristic()
subtypes = characteristic(['Goblin'])
cost = ‘R’
color = characteristic(['R'])
text = ['Sacrifice Mogg Fanatic: Mogg Fanatic deals 1 damage to target creature or player.']

play_permanent(card, cost)

in_play_role = Permanent(card, Creature(1, 1))

@activated(card, txt=text[0])
def effects(source):
payment = yield SacrificeCost()
target = yield Target(target_types=isCreatureOrPlayer)
source.dealDamage(target, 1)
yield

MAGic MAchine

Magic Machine apparently uses an encoded string to represent the cards, but my attempts at retrieving any useful information on Magic Machine’s card format didn’t give anything. The game itself only works on windows and requires admin rights to be installed, the databases I could find where password protected, and the help forums are in italian and don’t give much info on their format. It seems quite flexible though, and MagMa is the freeware with the biggest card database around here (but Incantus will soon have more, and Wagic is getting closer). The fact that it requires an editor seems a bit problematic to me, but if it gives enough motivation for players to create cards, it might be good.

Duels of the Planeswalkers (XBOX360)

The recent XBOX360 game “duels of the Planeswalkers” also uses XML to represent their cards. Unlike the xml in firemox, it is very readable:

Of course, this is a commercial game, so it’s not like you could create your own cards, which sort of ruins the point of even talking about their representation…

MTGForge

MTGForge has a mix of hardcoded cards and cards using a small set of keywords (http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=701). Recently the set of keywords recognized by MTGForge has been growing rapidly. Here is what Birds of Paradise look like in MTGForge:

Birds of Paradise
G
Creature Bird
0/1
tap: add W
tap: add B
tap: add U
tap: add R
tap: add G
Flying

Wagic

MTGForge was the initial inspiration for Wagic, so I initially went with hardcoded cards, and it was clear to me that at least the “data” part had to be stored outside of the code, as external data files, one for each expansion. They key in allowing people to add their own cards was that the format has to be easy to read and understand, so I went with simple text files. I think the first release only allowed some basic abilities (such as “vigilance, flying..) and maybe mana sources (“auto={T}:Add …”).
The possibility to add your own cards in Wagic is probably the feature that had the most success so far. Wagic was initially planned to handle around 300 cards and focus on the AI. But in the last months, more work has been made on adding new cards (and allowing people to add more) than on the rest of the game.
Here are the cards previously mentioned for other games, the way we represent them in Wagic:
Birds of Paradise

[card]
text=Flying (This creature can’t be blocked except by creatures with flying or reach.)  {T}: Add one mana of any color to your mana pool.
abilities=flying
auto={T}:Add{G}
auto={T}:Add{R}
auto={T}:Add{U}
auto={T}:Add{B}
auto={T}:Add{W}
id=129906
name=Birds of Paradise
rarity=R
color=Green
type=Creature
mana={G}
power=0
subtype=Bird
toughness=1
[/card]

Angel of Mercy

[card]
text=Flying (This creature can’t be blocked except by creatures with flying or reach.)  When Angel of Mercy comes into play, you gain 3 life.
abilities=flying
auto=life:3
id=129465
name=Angel of Mercy
rarity=U
color=White
type=Creature
mana={4}{W}
power=3
subtype=Angel
toughness=3
[/card]

Mogg Fanatic

[card]
text=Sacrifice Mogg Fanatic: Mogg Fanatic deals 1 damage to target creature or player.
auto={s}:Damage:1 target(creature,player)
id=134748
name=Mogg Fanatic
rarity=U
type=Creature
mana={R}
power=1
subtype=Goblin
toughness=1
[/card]

Angel of light

[card]
text=Vigilance,flying
name=Angel of Light
rarity=R
type=Creature
mana={4}{W}
power=3
subtype=Angel
toughness=3
[/card]

And here is a more complex card, that will work with the upcoming engine:

[card]
id=189909
name=Mold Adder
mana={G}
type=Creature
subtype=Fungus Snake
power=1
toughness=1
text=Whenever an opponent casts a blue or black spell, you may put a +1/+1 counter on Mold Adder.
auto=@movedTo(*[black;blue]|opponentStack):may counter(1/1)
rarity=U
[/card]

Overall I think it looks pretty simple, even for very complex effects.

All these methods have advantages and drawbacks. XML (firemox, duels of the planeswalkers) offers some structure and flexibility, but I think it fails in human readability, and flexibility could be achieved more easily with scripting languages such as python or lua. Magic Machine’s proprietary system looks flexible, but is clearly unreadable if you don’t have access to the card Editor, which to me would represent extra work that I am not ready to do. Plus it is always possible to have an editor yet a readable output. A scripting language (for example Python for Incantus) is probably the most flexible solution, and readability feels quite good to me.
The hybrid solution chosen by MTGForge and Wagic (some hardcoded cards, and a few keywords) is quite easy to code and easy to read in early stages. Newcomers can create basic cards in a few minutes with a text editor, while “experienced” coders can add cards in the code and recompile the game. Overall it works quite well, but after a while it shows its limits when you try to add new cards with more complex mechanisms. What were initially simple keywords become confusing strings that don’t always do what you want…

I’m happy with Wagic’s current system but I am not sure it can be extended infinitely. If I were to start again, I would highly consider a hybrid between a scripting language such as lua and parsed keywords.

sources

http://mtgrares.blogspot.com/2009/04/plaintext-creatures-and-cardstxt.html

http://mtgrares.blogspot.com/2008/01/magma.html

http://www.wizards.com/Magic/Magazine/Article.aspx?x=mtg/daily/feature/43c

http://www.firemox.org/mtg.html

http://www.slightlymagic.net/forum/viewtopic.php?f=27&t=557

  1. Jonathan’s avatar

    ^.^ start again huh? Sounds good to me, no joke~

    Reply

  2. Incantus’s avatar

    Hey Wololo,

    Your version of the Incantus card code is a bit out of date, though. This is how Mogg Fanatic would look now:

    #################################
    name = ‘Mogg Fanatic’
    types = characteristic(‘Creature’)
    supertypes = characteristic()
    subtypes = characteristic(‘Goblin’)
    cost = ‘R’
    color = characteristic(‘R’)
    power = 1
    toughness = 1
    text = ['Sacrifice Mogg Fanatic: Mogg Fanatic deals 1 damage to target creature or player.']

    @activated(txt=text[0])
    def ability():
    def effects(controller, source):
    cost = yield SacrificeCost()
    target = yield Target(isCreatureOrPlayer)
    source.deal_damage(target, 1)
    yield
    return effects
    abilities.add(ability)
    #################################

    A little more readable. I considered doing a dual approach, but then I figured it wasn’t much more difficult to do the above, and practicing on easy cards would make it easier for somebody to try their hand on more difficult cards. In addition, I have a parser that will generate a template card based on the oracle text, so you pretty much just have to fill in the code for each ability. For example, with your approach, it’s probably impossible to do a card where the latter part of the ability refers to the previous part (like Swords to Plowshares)

    But I agree that your hybrid approach is probably the best way when using a language like C++ or Java.

    Reply

Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>