Monday, October 29, 2007

Cut and Paste Cards

The secret to programming is really cut-and-paste. If you have never used cut and paste, you aren’t really using a computer. OK, let’s say I want to program Eternal Witness, what cards have I programmed that are really similar? Well, Gravedigger is 99% the same, so I just copy the code and make a few, small tweaks. Voila, another card is added!! Oblivion Ring and Oubliette are both “brothers from a different color” and I practically used the same code for both cards.

Shock, Char, Psionic Blast, Cackling Flames, Volcanic Hammer, and Firebolt are all essentially the same card, burn is burn. Infest and Pyroclasm both use exactly the same code. Wrath of God and Damnation are perfect twins also. At first glance it doesn’t seem that Terramorphic Expanse and Sakura-Tribe Elder have much in common but these unlikely relatives share the same code. I was curious about cards like Coercion so I also added similar cards like Distress, and Thoughtseize (probably Lorwyn’s most popular chase rare). Mishra's Factory and Blinkmoth Nexus are both lands that turn into creatures, so I copied the code and just changed the specifics like power, toughness, and creature types.

The hardest cards are ones that do something new and weird. Recently I programmed Cranial Extraction, and that code was a little long. It is a really cool card, but it did something brand new, and “brand new” equals “more work.” Each of the planeswalkers are 200 lines because they each had 3 abilities, which is pretty unusual. Incendiary Command was also another tough card, because two of the options let the user choose a target. If Incendiary Command didn’t require any targets, the card would have been a lot easier to program.


a said...

I hope you don't mind my comments, but really, "The secret to programming is really cut-and-paste." is terrible.

From wikipedia: "Copy and paste programming is an informal computer programming style that copies code from one program or procedure to another. It is often criticized as a bad practice or an anti-pattern. The term is in conjecture with a common activity in computing, copy and paste."

I downloaded MTGForge and took a look around the source, I hope you don't mind my advice. Obviously you've worked really hard on this and put a ton of effort into it, but I just see so many places for improvement. I'm not going to list them all, but the one that stood out to me are the really long classes. When you're in any decent programming job, you know you're going to get criticized for ridiculously long classes (CardFactory is what? 14,000 lines long). You might want to take a look at Code Smells ( for other possible improvements.

I'm just trying to offer some advice to improve your code, I hope you don't take it the wrong way. Best of luck :)

Anonymous said...

Infest and Pyroclasm are similar but not identical; damage != -X/-X. For example, a creature with zero toughness can't regenerate.

Forge said...

LOL, great comments. Roy said, "I just see so many places for improvement."

Thats true, there are tons of places for improvement, do I have any takers?

And Infest and Pyroclasm are different but MTG Forge implements them the same way. MTG Forge tries to be as close as possible to Magic's comprehensive rule set, but corners do have to be cut sometimes.

Eternal Witness and Gravedigger are similar but they don't need a whole separate super class, so what is a programmer supposed to do? Many Magic cards are very similar, but do slightly different things, like Eternal Witness and Gravedigger.

At least my blog is inviting comments and that is my goal. --Forge

a said...

"Eternal Witness and Gravedigger are similar but they don't need a whole separate super class, so what is a programmer supposed to do? Many Magic cards are very similar, but do slightly different things, like Eternal Witness and Gravedigger."

Use a parameterized effect class. Define a class like RegrowEffect. It can accept as a parameter the type of cards allowed to be Regrown. When you instantiate a Gravedigger, you also assign it an instance of RegrowEffect with allowed card type = "Creature", etc.

For more complicated effects, use composition. i.e. say you only define 3 classes: BurnEffect, DrawEffect, PumpEffect. Just using these three classes, you can implement many cards already by using combinations of them. More likely than not MtgO does something similar (probably not using only 3 classes though)

Just some suggestions. :)

The AI is good BTW.

Anonymous said...

The biggest difference between Infest and Pyroclasm is that Infest lowers power and Pyroclasm doesn't. If you have a 3/3 and cast Pyroclasm to kill your opponent's pair of 2/2s, that 3/3 creature can attack for 3 this turn. If you play Infest, it only attacks for 1.

Forge said...

I like BurnEffect, DrawEffect, PumpEffect. In the next version of MTG Forge a "master class" called MagicEngine which will do all the effects like drawing a card or causing damage. So Shock would look like this.

resolve(MagicEngine m)
m.damage(getTarget(), 2)

I often cut-and-paste when programming speciality one of a kind cards like Royal Assassin since it can only target a tapped creature. I copy and paste my regular "target creature" code and check to see is the target tapped.

For some reason I also don't want an explosion of classes. I don't want 500 classes and if I can reduce that my copying and pasting some code I will.

Yes I do know that Pyroclasm and Infest work differently.

Thanks for your comments, Forge