Wednesday, November 18, 2009

Difficult Cards to Program

Magic is great game full of wonderful, complicated cards. Today I'm going to talk about some of the hardest, thorniest cards to implement.

MTG Forge has 2,100 cards so there are plenty of complicated cards to choose from. The planeswalkers stand out in my mind. I had to add an extra combat phase which wasn't too hard (although MTG Forge still suffers from the flaw that if your opponent has two planeswalkers, you can only attack one of them). The planeswalker's abilities were the hardest part because I had to write the code from scratch which involves some very heavy hacking.

Many planeswalkers have rather common effects but Elspeth, Knight-Errant ultimate ability says "For the rest of the game, artifacts, creatures, enchantments, and lands you control are indestructible" which isn't something easy like dealing damage, it generates an ongoing effect. Each of the planeswalkers took about 220 lines.

Both Adarkar Valkyrie and Kiki-Jiki Mirror Breaker both have abilities that copy creatures and each take up about 100 lines of code. Adarkar Valkyrie lets you "steal" a creature if it is put into the graveyard and Kiki-Jiki lets you copy one of your creatures for one turn, then it is sacrificed. Both cards are extremely fun to use because of the broad variety of creatures that have effects when they enter or leave the battlefield. Flametongue Kavu is a perfect example. He is a 4/2 and does 4 damage to one creature when it enters the battlefield. Combine him with Kiki-Jiki to really turn up the heat.

Incendiary Command was very hard to program but the card isn't that useful. It weighs in at around 200 lines. I coded it after my (failed) phone interview with Wizards. The head guy who called himself Elf or something, asked me how I would implement the 5 card cycle of Commands. I answered him then furiously coded Incendiary Command that night.

I eagerly e-mailed him the next day and ... well nothing happened. For some reason I chose Incendiary Command which is the weakest card in the cycle. When I add cards I usually like to get more bang for my buck, like a super big creature or a devastating spell. I wouldn't even pay 25 cents for Incendiary Command, it is a crap rare indeed. I would rather use the cheaper Pyroclasm which only costs 1R and deals 2 damage to each creature.

p.s.
Kiki-Jiki is unusual because it creates tokens that have a name, typically tokens are nameless.

7 comments:

Xuelynom said...

What did you answer, on how you would implement it ?

Have read some Pattern design books ? I don't know if there's only one good answer for what he asked, but i'm certain that "each card has his own code" is not a good one.

ajr said...

Tokens generally do have names; Bitterblossom makes tokens named "Faerie Rogue", and Sprouting Thrinax makes tokens named "Saproling". What's really unusual about Kiki-Jiki tokens is that they have a converted mana cost.

Anonymous said...

If anyone deserves to be hired by Wizards, it's you. But maybe you couldn't tell them about Forge due to copyright issues?

wololo said...

I agree with Xuelynom here.
Honestly you shouldn't mention CardFactory.java in recruiting interviews if it's for a Software Developer job :P

I believe MTGForge as a whole is a good thing to mention on your resume, but don't let serious recruiters/devs dig too deep into its code.

200 lines for a single card is, of course, way too much.

CardFactory.java is actually a good example of bad code. It spits on all principles of OO programming, and it's a f#cking blurb of 17'000 lines of code (closer to 40'000 if you add the ones dedicated to creatures, auras, etc...).
It mixes up Data, Control, UI, AI in one file, within a long series of "if's".

But hey... I know too much how it is: after thinking for months about the concept of a game, it's time to act, and you can't get everything right from the start. Then people start helping you, and before you realize it, your code shows all its flaws to the rest of the world. The more people help you, the more you realize this or that is inefficient or fundamentally broken... and the code keeps on growing, less and less controlable...

But one difference between MTGForge and the "ideal" MTG simulation is big: MTG Forge exists and is fun to play :)

Forge said...

I exist because I program, I think ;)

Guga said...

An error has occured. You can copy/paste this message or save it to a file.
Please report this, plus what you tried to do, to:
http://www.slightlymagic.net/forum/viewforum.php?f=26
If you don't want to register an account, you can mail it directly to
mtgrares@yahoo.com

2/Risn't an indexable single character.

Detailed error trace:
java.lang.IllegalArgumentException: 2/Risn't an indexable single character.
at ManaPool.cIndex(ManaPool.java:13)
at ManaPool.subtractOne(ManaPool.java:239)
at ManaPool.subtractMana(ManaPool.java:222)
at Input_PayManaCostUtil.tapCard(Input_PayManaCostUtil.java:10)
at Input_PayManaCost.selectCard(Input_PayManaCost.java:43)
at GuiInput.selectCard(GuiInput.java:35)
at GuiDisplay3$8.mousePressed(GuiDisplay3.java:309)
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)

Xuelynom said...

Well i wanted to say something like wololo, but I don't want you to get angry or repeat that you DID write something that works while i did nothing ; the point is, if you want a big software company to employ you, You'll have to learn advanced programming techniques. Please don't take that as an insult, but as an advice.

I've looked through the other magic engines, the best written seems to be Firemox, but it's hard to enter the code if you're not into some concepts...