Docendo discimus

$self->explain

  • Calendar

    April 2010
    M T W T F S S
    « Dec   May »
     1234
    567891011
    12131415161718
    19202122232425
    2627282930  
  • Archives

  • Recent Posts

  • Bling

Vedalken: the Magic Librarian

Posted by brunorc on April 5, 2010

Dawn's Reflection

Dawn's Reflection - a simple, one color spell, but it makes your life really colorful!

Finally I decided to get back to my Magic DB and describe the process of creating it here, on my blog. So here we go:

mkdir vedalken
cd vedalken
catalyst.pl Vedalken

Marisi's Twinclaws

Marisi's Twinclaws - Multicolor with a choice; and a double-strike killer

Now I have the skeleton for the Catalyst app, so it can be stored in the git repository. Why? Simply because it’s a good idea to store the code in the repository. I have to get some sleep after being completely killed after the Tangerine Dream concert in Zootermeer, so there’s no time to elaborate on that now. So, while I’m still in ‘vedalken’ – the main directory of my project – I type:

git init

Now the empty Git repository has been created in the current directory. Since it’s empty, I want to add my Catalyst skeleton to it:

git add -A

The -A flag stands for “all”, as we can be told by git help add (every command of git is nicely explained in git help command). All files are now indexed by Git, so it will pay attention to all the changes, introduced to them. Adding a file is also a change; and changes have to be committed – a commit actually stores changes in the Git repository.

Blaze

Blaze - A spell with variable cost

git commit -a -m "Initial commit; created the Catalyst skeleton"

This time is the -a flag which means “all”; the -m flag specifies the commit message. If it was ommitted, the default editor would be launched, since a commit without message is barely useless…

Reaper King

Reaper King - How much for the color?

Now I can use all the power of Git to maintain the project and keep it growing. My plan is to have a list of cards I own, with a list of decks. And since Magic is a collectible card game, a wishlist may be a nice addition…

cd Vedalken
script/vedalken_create.pl controller Card
script/vedalken_create.pl controller Deck

A bunch of views may be useful:

script/vedalken_create.pl view ViewTT TT
script/vedalken_create.pl view ViewJSON JSON

I don’t have a model yet, since I was thinking for quite a long time about the representation of mana cost – without finding any good solution. Mana cost is the amount of mana (magical energy) you have to spend to cast the particular spell. If you take a look at Dawn’s Reflection, you will see that you have to spend one green mana and three mana of any color (you can spend four green mana – it’s also OK) to cast it.

Budoka Gardener

Budoka Gardener - or Dokai, Weaver of Life

That is easy. But then we have something more complicated – Marisi’s Twinclaws.

To summon those kitties you have to pay one green mana, one red or white mana, and two mana of any color. This may be quite important, because the colors of mana define the color of the card. If the spell may “destroy target white creature”, it will smash your Twinclaws, even if you didn’t use white mana to cast it.

Boom or Bust

Boom or Bust - you choose

Another important aspect of mana is the CMC, which means “converted mana cost”. Both Dawn’s Reflection and Marisi’s Twinclaws have CMC equal to 4. But then comes the Blaze.

Surprisingly, the CMC of Blaze is 1, since you *can* actually cast it for one red mana; it just won’t harm anyone, because of dealing 0 damage (there may be a situation, when such move makes sense). But the X cost is important, since it has to be – for instance – graphically rendered on the page.

This way we come to the pinacle of mana representation problems: the Reaper King.

To have Reaper King on your side, you have to spend one mana of each of the five Magic colors. No blue mana? No problem, just pay two mana of any color. That way this card has CMC equal to 10.

I wanted to have a system of storing mana cost in such a way, that would allow me to:

  • represent the “mixed” mana,
  • represent the “one-or-many” mana,
  • filter cards by color,
  • automatically calculate the mana cost.

I’m going to use Moose to achieve it, since Moose classes and attributes allow to hide all the complexity (or gory details) under a nice interface. I hope it will also make it possible to represent the flip cards from Kamigawa block and multi-part cards:

package Vedalken::Mana;
use Moose;

has [ qw/white green red blue black colorless variable/ ] => (
    is => 'rw',
    isa => 'Int',
    default => 0,
);

It may be a little bit overkill (if any visitor has any advice, I’d like to improve this design), but that way Moose will take care of checking all the keys and values on runtime. Also, it makes it easier to specify the list of attributes that share the same attributes. Below I include a commented (using the multiline “=begin/=end” comment) example of another way, based on TypeConstraints.

package Vedalken::Spell;
use Moose;
use Moose::Util::TypeConstraints;

enum 'SpellType' = qw/
    Artifact
    Basic
    Creature
    Enchantment
    Instant
    Land
    Legendary
    Planeswalker
    Plane
    Snow
    Sorcery
    Tribal
    World
/;

enum 'Rarity' = ( qw/
    Rare
    Uncommon
    Common
    Land
    Special
    Promo
/, "Mythic Rare" );

=begin Alternative

my %colors = map { $_ => 1 } qw/
    White
    Red
    Green
    Blue
    Black
    Colorless
    Variable
/;

subtype 'Mana'
    => as 'Hash',
    => where {
        not grep { not exists $colors{$_} } keys %$_
        and
        not grep { $_ !~ /^[0-9]+$/ } values %$_
    };

=end Alternative

has name => (
    is => 'rw',
    isa => 'Str',
);

has mana_cost => (
    is => 'rw',
    isa => 'Array[Vedalken::Mana]', # or 'Array[Mana]'
);

has cmc => (
    is => 'rw',
    isa => 'Int',
);

has types => (
    is => 'rw',
    isa => 'Array[SpellType]',
);

has subtypes => (
    is => 'rw',
    isa => 'Array[SpellSubtype]',
);

has [ qw/text flavor/ ] => (
    is => 'rw',
    isa => 'Str',
);

has [ qw/power toughness/ ] => (
    is => 'rw',
    isa => 'Int',
);

has rarity => (
    is => 'rw',
    isa => 'Rarity',
);

package Vedalken::Card;
use Moose;

has name => (
    is => 'rw',
    isa => 'Str',
);

has spells => (
    is => 'rw',
    isa => 'HashRef[Vedalken::Spell]',
);

has quantity => (
    is => 'rw',
    isa => 'Int',
);

That way one card can have many spells (we shouldn’t need more than two, but it’s Magic, so you never know for sure). Later we can add a method which would tell us if the card includes two completely independent spells, or it’s a flip card (Kamigawa specific).

package Vedalken::Collection;
use Moose;

has items => (
    is => 'rw',
    isa => 'HashRef[CollectionItem]',
);

has quantity => (
    is => 'rw',
    isa => 'Int',
);

The generic Collection can serve as a basis for the Deck object, as well as for the wishlist and the whole collection.

The advantage of using Moose is – again – that you declare in your package all the information about types (which get checked on runtime) and coertion from other types (so you can expect an Object of type Vedalken::Whatever, but still be able to get an ordinary hash and use it, as long as it meets all conditions). Yeah, in Java or C++ you take it for granted. But on the other hand, it’s still Perl, so you can use all its magic and power plus the joys of strong typing. You declare more, but you write less code. Code, which is usually just the reinvented wheel for checking, if the user input is an integer.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: