Monday, March 30, 2009

Millions of Details

When writing a Magic implementation there are a million little details to keep in mind. Let’s say the user interface (UI) shows the mana cost of all the cards, but some cards reduce the amount of mana that you have to pay. Does the UI update all of the cards’ mana costs or does it just show the printed mana cost? For convenience the UI probably loads the card’s jpg, so the mana cost is never reduced and the player has to keep that in mind.

Cards and abilities that have “may” in them are also problematic like Dragon's Claw, which is an artifact that costs 2 and says, Whenever a player plays a red spell, you may gain 1 life. It is much easier to always have a card do something, which is what the player would do 99% of the time, but that other 1% causes much difficulty. When I code the AI for a card like Dragon’s Claw, the AI always gains life.

In a real game of Magic the player has to speak up and put any “may” abilities on the stack. But I believe the rules say that the ability always goes on the stack and the player has a choice when the ability resolves. So should a program like Magic Online put the ability on the stack or require the user to “play” that ability, thus making the user less lazy and more alert.

The main problem when programming Magic is the large volume of rules, which players internalize after awhile. The rules make programming hard, but they make the game fun.

Wednesday, March 25, 2009

New Version - Remove Bad Cards

Thanks to DennisBergkamp there is a new version of MTG Forge with 1396 cards. I'm happy to say that morph is now supported. I have also improved the AI's quest decks, so now there is a total of 23 decks with wacky names such as Frodo, Iceman, and Einstein. The deck editor now sorts faster thanks to a 2 line fix.

The other new feature is that you can remove cards from MTG Forge that you don't like. MTG Forge has many cards that don't work 100%, so you can easily remove those cards. Execute "run-remove-cards.exe" and you can choose which cards that you don't want to exist. If you happen to use a deck that has any removed cards, the cards will be named "Removed Card".

MTG Forge is Java based and will run on Windows, Linux, or Mac.

MTG Forge 03-20 Download

Monday, March 23, 2009

Static Abilities as Triggered Abilities

Static abilities are difficult to implement, so today I’m going to talk about how to transform many static abilities into triggered abilities. Static abilities are always “on” and Glorious Anthem is a good example. Triggered abilities occur when the condition is fulfilled such as “When this card comes into play” such as Venerable Monk. Triggered abilities always start with “when”, “whenever” or “as” so they should be easy to identify.

First, let me briefly digress and talk about listeners and events. Usually a Magic implementation like MTG Forge has many events that other parts of the program can observe. One of the most common events is when a card is added or removed from a zone, addCardEvent and removeCardEvent.

Now that we have these two events can you figure out how to implement Glorious Anthem? Hopefully this should be easy, every time that addCardEvent triggers Glorious Anthem checks to see if you control that card and then adds +1/+1 to it.

Fortunately the above logic works most of the time but there are also other considerations to keep in mind. If you “steal” your opponent’s creature with Control Magic, addCardEvent wouldn’t trigger because the “play area” is a shared zone, both you and your opponent share the same play zone.

One solution is to have another event that only triggers when cards are added to your play area. (Your play area is defined as, when a new creature comes into play under your control.) Since many cards like Glorious Anthem care about only creatures that you control, subdividing addCardEvent into more specific events is a good idea.

Currently MTG Forge does implement cards like Glorious Anthem but they are a little slow and inefficient. If you have many Glorious Anthems in play MTG Forge will react a little bit slower because of the extra CPU time that is required. In essence the Glorious Anthem code is executed “too many times” and usually doesn’t do anything. Using triggered events for Glorious Anthem is both efficient and easy to understand.

Friday, March 20, 2009

Free Physics Games

Magic: The Gathering seems to attract lots of smart guys and smart guys tend to like (or be interested) in physics. I know of three pretty good games that use physics.

Fantastic Contraption - Your goal is very simple, build a machine (usually something like a car or tank) to move the pink piece on the left to the pink area on the right. Great fun and very addicting. I would love to code a game like this.


Crayon Physics Demo - Your goal is to move the ball on the left to the star on the right by drawing with your mouse. In the demo you can only draw squares but in the full version, $20, you can draw any shape. Very simple, but very cute also. Fantastic Contraption is probably a little better but you can play this offline.



IncrediBots - You build your robot out of parts and then you use your robot in order to achieve the goal. This is the most complicated but it seems interesting if you can get past the learning curve.

Wednesday, March 18, 2009

Quest Mode - New Version

Well I’m happy to announce that MTG Forge now has a quest mode. The questing is very simple and all menu based but still fun. You start out with a few cards and the more you play, the more cards you get. On easy you get more cards after every game, whether you win or lose, while medium requires that you win or lose 2 games. (I figured that players should always feel like they are progressing whether they are winning or losing.)

The Quest Mode is still very beta and many things need to be ironed out. The computer’s deck names should be like Frodo and Morpheus and the computer should use a wider variety of decks. The Quest Mode isn’t perfect but it is a lot of fun.

Download 03-16 Version

p.s.
If you are interested in improving the quest mode check out the forums, there are quest discussions going on here and here.

Monday, March 16, 2009

Random Magical Thoughts

I usually like to write about one coherent thought for about three paragraphs but I’m stumped. So today I’m going to write about two or three ideas.

First, Magic is a complicated game and trying to construct a basic user interface (UI) for it is hard. I keep thinking that I’ll rewrite the UI but I know it will be hard and frustrating since I’ve never done anything like that before. I really enjoyed the UI for the previous version of Magic Online and the UI for Shandalar was very clear and easy to use.

Basically I’m not a very visual person and I can’t tell what looks good and what doesn’t. My only hope for a better UI is to copy a preexisting one. I understand Shandalar’s UI the best but I will be doing good if I can write something that looks half as good.

Second, Java is my programming language of choice but it still gives me problems. The problem is that there are many different versions of Java which are supposed to work the same way but they don’t. The newest version of Java is 1.6, which is all fine and dandy, but the user interface looks different between 1.4 and 1.6 and I wrote MTG Forge with 1.4. (The user interface is “more white”, making it harder on the eyes.) And other problems abound, such as Java 1.4 code will compile with 1.6 if you turn off all the warnings, which is usually considered a bad, lazy approach to programming.

Third, MTG Forge’s source code is about 1.5 MB uncompressed and 271 KB zipped, which is about the size of Charles Dickens “A Tale of Two Cities”, 294 KB zipped. You might be wondering why am I comparing zipped sizes between Java source code and plain text? The answer is that if two things compress to the same size they have about the same amount of information.

I have no idea which project took longer to write but by compared zipped sizes, we know that they have about the same information. (Although hopefully my program is more interesting than an old book, but who knows?)

p.s.
Information theory is part of computer science which deals with the theory behind computers and how to code better programs.

Wednesday, March 11, 2009

Program Life Cycle

I’m going to be boring for a minute and just talk about plain, old programming. When you write a program from scratch it is brand new and shiny. Everything works completely as it should and it feels like a brand new card.

But any good program is constantly modified in order to add new and better features, so the program grows little hairs and feels “less new.” When a program gets into middle age it is harder to add new features because everything feels fragile. Modifications that were never intended have been grafted in, so the design is a little bit messy. (MTG Forge barely supports planeswalkers.)

After awhile, pick whatever time span that you want, the program feels old. New features are very hard to add and constantly require the programmer to “tip-toe” around the old code. Parts of the program may have completely been rewritten and are poorly documented. And overall the program feels like a 10 year old car, usable but definitely not new.

After a program has gotten “old”, it is time for a bright, shiny new version. MTG Forge has probably become old and needs to be retired. That way the new 2.0 version can have a different, improved design that fixes many of the “hacks” that were needed with the old code.

I’ve started working on version 2.0 and it is a tough beast to handle. At first I was too ambitious, but at the very least it will require a total rewrite from the ground up. Many of the ideas from version 1.0 can be applied to 2.0, but the implementation will be completely different. A plausible goal is to get 2.0 working with a few cards so that you can see progress without being completely overwhelmed.

With all of that said and done, it is still nice to add new stuff to 1.0 because it works and you can immediately see the results.

Monday, March 9, 2009

Overriding Core Rules 2

Cards likes Mana Flare and Doran, the Siege Tower are hard to implement because they change the core rules of Magic. The basic way, which is perfectly fine, is to have various parts of the program look to see which cards are in play. This has the drawback that all of the card code for Doran is not in one place and cannot be easily used by different cards. (Although it doesn’t seem like Wizards will be printing variations on Doran.)

Let me get sidetracked for a minute and then I’ll return to the main topic. I think it is a good idea if all of the rules are encoded in one large class which is named something like RulesEngine. Various phases and actions in Magic are intertwined which makes even getting the card’s color complicated sometimes, Painter’s Servant says something like “All cards in play are white”. Since RulesEngine is handling all aspects of Magic, even the small ones, RulesEngine can just easily check to see if Painter’s Servant is in play and return the appropriate color.

Now back Doran, if RulesEngine has a method like changeRules(String), Doran can simply use changeRules(RulesEngine.ASSIGN_TOUGHNESS). The RulesEngine class could have a bunch of constants that could be passed to changeRules(). Doran doesn’t care how its ability is implemented in RulesEngine, it just knows that it will be handled. Likewise Mana Flare could call changeRules(RulesEngine.DOUBLE_MANA).

There is an old computer joke that says anything can be programmed with enough layers of indirection (methods calling other methods), which is often true. If RulesEngine handles all of the data and actions that happen in a game, implementing “hard” cards like Doran and Mana Flare should be simple.

The reason that MTG Forge currently cannot handle protection is because there isn’t a “global” canTarget() method which is used to check to see if that spell can target that permanent. And when I say “global”, I just mean that it is used everywhere that a target is assigned.

Separating WHAT from the HOW is very important. In HTML the "b" attribute tells the browser to put that text in bold. The b attribute doesn't tell the browser HOW to do it.

p.s.
Painter’s Friend isn’t a great example but I couldn’t think of a better one. I know I’ve read cards that say something like “All cards are white” but I probably have the wrong wording. Just for reference here is Doran and Painter’s Servant.

Friday, March 6, 2009

Download Whole Blog

Just for what it is worth you can download all of my wonderful articles and read that offline if you want to.

download - Word Format
download - Blogger Atom export format

I really don't know what "Blogger Atom export format" is, but it was an option on blogspot.com

Have fun. All typos and mistakes are on purpose, ha.

Wednesday, March 4, 2009

Quest Update - Programming Details

As you may know I’m busily working on a quest mode for MTG Forge where you would start out with a small card pool and you would win more cards. The good news is that I’ve worked on it for about 4 hours today and although I haven’t finished, I’ve made good progress.

The quest mode seems very easy on paper, but when you have to spell out every detail in code, it seems excruciating. Does this method return null or an array of size 0? What if a file cannot be found, what should be done? Is there any way in the universe that this simple method could fail? When you program, you have to answer a million little questions like these.

I programmed a very simple screen that let the user choose from 4 radio buttons to select the difficultly of the quest mode. (The higher the difficulty, the more wins you have to get in order to earn new cards.) Four radio buttons, it seems pretty simple, just see which one is selected and then go on. I forgot to check to see if none of the buttons were selected, so I added a very important “else” clause.

Since I know MTG Forge is a little buggy by design and I never want a player to lose his progress, MTG Forge never erases the old save file. The old save file is renamed something like “questData-10” and a new save file is created. This seems like a pretty good way of providing a safety net.

Tuesday, March 3, 2009

When to Attack?

MTG Forge does its best to figure out which creatures should attack but overall it does a pretty lousy job. The AI doesn’t even play spells or abilities during the combat phase, which should simplify the combat step. And the AI doesn’t to any planning, like it won’t attack into a bigger creature and then Shock it. With all of that said, determining what creatures should attack is still difficult.

In general the AI will attack with a creature if the creature cannot be blocked, like if the creature has flying or fear, or if the creature is the biggest on the board. The AI will also attack with creatures that can be “traded”, it will attack with a 2/2 if you have a 2/2. I’ve tried to randomize the attacking routine a little bit by not always attacking with all of the creatures, but that seems to just weaken the AI and make the games more of a landslide.

I’m playing around with a very simple attacking routine, such as the AI would attack with all of its creatures if it had the same number or more creatures in play. (If you had 2 creatures in play and the AI had 2, it would attack.)

That way the AI would at least attack more often and would be putting on the pressure even if some of the time it is completely stupid. I read an article awhile ago on magicthegathering.com that suggested that players in limited games should attack 95% of the time and I’m trying to apply that strategy to the AI.

The basic idea of attacking is to attack when it is beneficial, but defining “beneficial” is very hard. When I write the AI attack routine, I am defining “beneficial”.