Monday, March 29, 2010

New Version

Every month I get the proud opportunity to announce a new version of our beloved program Forge. The best and greatest program ever written (boy, do I have a big head). Forge now has almost 150 new cards with an astounding total of 2,846 cards.

Everyone loves new cards, me included. Forge now supports X spells so now you burn you opponent with some of fiery red's best cards, Blaze (deals X damage to target creature or player), Earthquake (deals X damage to each creature without flying and each player), and Enrage (target creature gets +X/+0 until end of turn).
There are plenty of good X spells for the other colors to. Green gets Hurricane (which deals X damage to each creature with flying and each player), Windstorm (deals X damage to each creature with flying), Squall Line (deals X damage to each creature with flying and each player) and the classic Stream of Life (target player gains X life).

Black has a powerhouse of X spells such as: Mind Twist and Mind Shatter (target player discards X cards at random), Howl from Beyond (target creature gets +X/+0 until end of turn) and Swallowing Plague (deals X damage to target creature and you gain X life). Not to be outdone, blue gets a bunch of "draw X cards" like Prosperity, Braingeyser, and Stroke of Genius.

Other insane cards include the fan-favorite Isochron Scepter which lets you abuse cheap instants (2, tap: You may copy the exiled card. If you do, you may cast the copy without paying its mana cost.) Gamers who like to "win by other means" will be entranced by Helix Pinnacle which lets you win if it has 100 counters on it. (I suggest using Double Seasoning.)

There are a slew of "prevent combat damage" cards including the original green Fog, along with white's Holy Day, and black's Darkness. And in case you wanted to try out multikicker, Forge has 7 multikicker cards (use the filter option and put "multikicker" in the Card Text textbox): Lightkeeper of Emeria, Quag Vampires, Skitter of Lizards, Wolfbriar Elemental, Enclave Elite, Apex Hawks, and Gnarlid Pack (1G, 2/2 creature, Multikicker 1G, Gnarlid Pack enters the battlefield with a +1/+1 counter on it for each time it was kicked).

3-18-forge.zip (8 MB) - For more details about the new improvements read "changes.txt". 3/18 was the day that it was released on the forums. Uses Java and runs on Windows, Mac, Linux, etc...

card-pictures.zip (73 MB) - Includes most of the card and token pictures, low quality

Get Java - in case you don't have Java installed on your computer.

Get Tutorial - This is a Windows demo/tutorial in case you don't know how to play Magic: The Gathering.

To download the card pictures you have two choices: "Download Card LQ Pictures" and "Download Card HQ Pictures". LQ stands for low-quality, which is the default size for Gatherer (http://gatherer.wizards.com) and the daily Wizard articles. HQ stands for high-quality and the pictures are much bigger and therefore take longer to download.

p.s.
As always a big thank-you goes to Dennis, Rob, Silly Freak, Chris. H., Zerker, slapshot5, BinarySpike, Jim and everyone else that helped. Your time and effort is greatly appreciated.

Wednesday, March 24, 2010

Why I haven't worked on version 2

I don't really know why I haven't worked on Forge in awhile. Maybe I've burned out programming Magic for awhile. I'm "supposed" to be working on Forge version 2.0 but I know how big version 1 is, and yes version 2 might be smaller, it still very big. On version 1, I didn't mind making mistakes because I just wanted to get it out-the-door (i.e. working). I knew that everything could have been done better and I just designed stuff on the fly.

_And_ version 1 is 95% great. Yes the AI could be better and yes it could have more cards but basically version 1 is remarkably good. The quest mode keeps me coming back for more and we, on the forums, are talking about adding a card shop. People can upload and download decks. Drafting is a blast, even if it is just a simulator and sealed deck is challenging because it makes you think in different ways. Right now Forge kicks a lot of ass and maybe version 2 could kick more ass but I don't really know.

If Forge stopped right now and was never developed any further, it would still be an awesome program that people would want to play for years. Similar to Shandalar, Forge is an enduring classic because Magic is an enduring classic.

And I feel like I've let a few people down since I haven't worked on version 2 in awhile. Sometimes it feels like programming just drains your lifeblood and you only have so much to give. Even though I enjoy programming, it is always very hard.

Failure is also hard to deal with. If you think you will fail, it is hard to get started. I just finished reading this great book and I thought the author was very good. I almost choked when I read that this was his third book and the first two books didn’t get published, ouch!

So in conclusion, enjoy Forge as-is because I have no idea when version 2 will get off the ground. This-just-in, according to my sources (sources, source code, I kill me) version 2 will be finished days before the fourth Matrix movie. :=)

Monday, March 22, 2010

583 Hours of Work

Forge is a relatively big program with about 35,000 lines of code and an additional 35,000 lines of comments. The problem isn't bad programming, it is just that it takes a ton of code to do anything. The user interface code takes up about 7,000 lines of code.

Talking about lines of code is a little deceiving because the numbers are hard to understand. Roughly (and I'm mean very roughly) it takes about 1 minute to write 1 line of code. So 35,000 lines equals 35,000 minutes, which is 583 hours or about 15 weeks of work at 40 hours a week. Obviously a great deal of time and effort has gone into this program. Sometimes you are doing good if you can write 1 line of code per minute because you are debugging which entails more thinking than writing.

I put in maybe 100 to 200 hours of work to begin with and all of the other effort has been Dennis, Rob, Chris, Zerker and the other guys on the forum. For the last 6 months or so I've just been sitting on my backside and letting them do all of the hard work. I'm just glad that people enjoy playing and programming Forge. It is also humbling to allow other people to look at your code because there are always sections that could have been done better. Dennis and the guys have been adding new features despite the limitations of my initial, "add a feature if you need it", "hacky" design.

One of the first things that Dennis did was to add protection. I, the creator of Forge, know that protection is not supported and I didn't think that Forge would ever support protection. Dennis redid CardFactory, one of the biggest chunks of code, and added protection to every spell and ability that needed it. And that was only the beginning. He and Rob added cards like Hypnotic Specter which Forge does not support but uses hacks in order to get it working. (A hack is like duct tape. It could be done better in a hypothetical world but it works, so no one really minds.)

I try to highlight some of the people and work that is done behind the scenes that most people don't notice. I stopped working on Forge when it had 1,200 cards and now it has more than 2,800 cards. That is what I call progress.

All statistics exclude blank lines and were generated with cloc, thanks to Frololo for the suggestion.

Thank you Dennis, Rob, Chris, Zerker, Huggybaby (for the forums) and everybody else who has helped.

Monday, March 15, 2010

CardFactory - How to create 2,700 different objects?

The CardFactory class is responsible for only one thing, to create all new Card objects. Every Magic card is represented internally as a Card object. The difficultly is that Forge implements 2,700 cards, so that means that CardFactory has to return 2,700 different objects.

So the question becomes how do you actually create a method that can return (output) 2,700 different things? First, I try to refer to the gang of four "Design Patterns" book as often as possible. It mentions the prototype pattern but is seems very vague as to the actual implementation. From page 121, "The hardest part of the Prototype pattern is implementing the Clone operation correctly." So basically you have four very smart people saying that copying an object is hard, yeah.

My very "dumb" solution was to stick all of the objects together in one huge method. The reason why I did this instead of reading them from XML or a text file is because each object was fundamentally different (they were anonymous classes). I couldn't just read the information from a file because each Card object is fundamentally different, Shock is different than Elvish Piper.

I enjoyed having all of the card code in CardFactory because I could quickly cut-and-paste, which is a horrible practice but it helped things zip along. Now things don't "zip" because the Forge architecture isn't very flexible since everything was designed only for basic, simple Magic cards. (Even basic, simple cards are hard to program.)

The one CardFactory method was around 18,000 lines long, which is insane I know but each card had separate brackets and didn't share any variables. I submitted it to the Guinness Book of World Records as the "world's longest method" but they didn't think it was very important. (They were right but I still wanted my plaque, ha.) Currently CardFactory has been divided up into about 5 different classes, which helps the "long method problem" but it doesn't solve it.

Alternatively, I could have created 2,700 different files to hold each card.

I really want to create cards like this.
Card card = new Card("Wrath of God");

SpellAbility spell = new Spell(card)
spell.addResolve("Destroy all creatures");

card.addSpellAbility(spell);
Hopefully the AI won't need specific instructions on how to handle each card. Right now the computer will play Wrath of God if it has less than 7 life or if it would destroy 2 more of its opponents creatures versus its own creatures. This is just a rough guess when the AI should play Wrath of God and it doesn't work for all situations. This is also a good explanation of why the AI seems so stupid sometimes. (At least AI isn't stupid 100% of the time, ha.)

This is current (ugly) way.
if(cardName.equals("Wrath of God") || cardName.equals("Damnation"))
{
SpellAbility spell = new Spell(card)
{
public void resolve()
{
CardList all = new CardList();
all.addAll(AllZone.Human_Play.getCards());
all.addAll(AllZone.Computer_Play.getCards());

for(int i = 0; i < all.size(); i++)
{
Card c = all.get(i);
if(c.isCreature())
AllZone.GameAction.destroyNoRegeneration(c);
}
}//resolve()

//should the AI play this spell or ability?
public boolean canPlayAI()
{
CardList human = new CardList(AllZone.Human_Play.getCards());
CardList computer = new CardList(AllZone.Computer_Play.getCards());

human = human.getType("Creature");
computer = computer.getType("Creature");

//the computer will at least
//destroy 2 more human creatures
return computer.size() < human.size()-1 ||
(AllZone.Computer_Life.getLife() < 7 &&
!human.isEmpty());

}//canPlayAI()

};//SpellAbility

card.addSpellAbility(spell);
p.s. I don't usually have shoutouts because they are stupid. But here it is anyways, shoutouts to pheadbaq.

Wednesday, March 10, 2010

Math and Magic – Probabilities

Just a short article today and two geeky probability questions.

Probabilities in Magic are very interesting but I haven't been able to use them to my advantage. If I'm playing a two color draft deck, I tend to mulligan if I don't have both of my lands (Island and Plains) but I don't explicitly use probabilities during deck construction or while playing.

The land count is always very important and probabilities might be able to indicate the number of land and land types that a deck needs. Most people develop their intuition by playing a ton of games instead of doing the (boring) calculations.

I presume probabilities would be more useful in limited games because the deck size is smaller.

If I'm splashing 3 blue spells and 5 Islands in a 40 card deck, what is the probability that I'll draw a blue spell and an Island by turn 7? (I used these numbers from Forge's Deck Analysis.)

I think the answer is 29 percent (about one-third of the time) because 0.448 x 0.639 = 0.29 (rounded to 2 decimal places).

Likewise, what is the probability of starting out with an Island and a blue spell?

The answer should be 0.125 x 0.075 = 0.01 (rounded to 2 decimal places). The answer is 1 percent of the time or 1 out of 100 games. (The super-geeky can take into account mulligans which I ignored.)

Thank you for journeying into the world-of-math. Shoutouts to the book: Godel, Escher, Bach: The Eternal Golden Braid. (It is a great book even though it twists your mind a few times. I hated the dialogue between chapters but everything else was great.)

p.s.
I double checked my math but I wouldn't stake my life on it. ;-)

Monday, March 8, 2010

The Fantastic Deck Editor

This article is going to be half advertisement (because the deck editor is soooo good) and half FAQ. I wanted to highlight some of the deck editor's features. I even have a celebrity endorsement. Yoda says, "Love the deck editor, you will."

In the middle of the deck editor you can easily see the buttons for Land, Creature, Sorcery, etc... which easily let you add or remove cards of that type. The "Deck Analysis" button gives you more information about your deck such as breakdown by color, type (Creature, Instant, etc..) mana cost (how many cards cost 1 mana, 2 mana, etc..) and it also has a "Random Starting Hand" generator. For serious players the "Deck Analysis" button also shows you the probably of drawing each card for turn 1, turn 2, etc... Checkout this screenshot to see Deck Analysis in action.

OK, I know what you are thinking, "Enough with the easy stuff and show us something cool." Well the filter option is very cool. It allows you to filter cards by name, card text, color or type. If you wanted to build a blue counterspell deck, a quick way to view all of the counterspell cards is to create a new filter and in the "Card Text" textbox type in "counter target" and then poof, 29 matches. (Searching for the words "counter target" gives you 8 more cards than searching for "counter target spell".)

While the "Card Text" filter is probably the easiest and most useful option in the deck editor it doesn't always do what you want it to do. Let's say that you wanted to build a goblin deck. You could search for "Goblins" using the "Card Text" filter and that would show you 35 cards. To view the rest of the Goblin cards, remove the filter and just click on the "Type" column name (click on the word "type") to sort all of the cards by type. Most of the Goblin cards are under "Creature - Goblin". Remember legendary and tribal goblin cards, so you should scroll down to see "Legendary Creature - Goblin" in order to find Kiki-Jiki, Mirror Breaker (one of my favorites) and "Tribal Instant Goblin" for Tarfire.

I think Forge's deck editor is very good. Hopefully the deck editor is so good that you don't have to think about it.

(I added the deck editor very late into development. I probably had 100 to 200 cards working before I coded the deck editor. The deck editor was so easy to use that I wished that I had added it much sooner because creating decks by hand is just painful, (very tedious).

p.s.
The first trading card game that I actually played was Yu-Gi-Oh on Gameboy Advance. Yes, Yu-Gi-Oh is a kids game but as an introduction to the world of TCGs the game was pretty good. And the deck editor sucked because there wasn't a mouse and you really needed a mouse.

For what it is worth, I was really into Gameboy Advance games back in 2001 and I loved playing them on my PC. I was fascinated by the whole "Gameboy emulator scene" that actually assigned a number to each individual game that was released, see gbatemp.net. To learn more about emulation Google the granddaddy of all emulator programs "MAME" and checkout the huge list of arcade games that it allows you to play. (The emulator program is free but you have to download the slightly illegal roms in order to play the actual games.)

p.p.s. I'm posting another article on Wednesday "Math and Magic: Probabilities".

Monday, March 1, 2010

Combat is Hard

Forge is sometimes really stupid when it comes to combat. Deciding when to attack or block is a very thorny issue. (Well…most things in Magic are thorny aren’t they?) I wrote the AI combat code and although I’ve rewritten it once, it still has some major flaws.

First let me state that the code works well but is restricted by the overall combat architecture, which I did designed. The code answers the question, “Should this one creature attack?” While this question is important, it leads the AI down the wrong path and makes it look stupid. The correct question would be “What is the best way that all of the creatures should attack?”

So now we know that the AI makes attacking and blocking decisions by only looking at each individual creature but that doesn’t approach the intellect of playing against a real human opponent. A better AI would have to do an actual search and try to find good combat scenarios. (Doing an actual search may take 5-30 seconds but this is just a guess.)

Right now the alpha-beta search algorithm could be applied to Forge but it wouldn’t take into account activated or triggered abilities. Each ability is hardcoded and tries to find a good target, the AI doesn’t have a way to make an ability try different targets. The AI code would only see regular creatures with a few simple abilities like flying, vigilance, and trample. Also the code might not work with the rest of the program because of the overall architecture, which I designed on-the-fly.

So butter by backside and call me a biscuit, I think that’s all I have to say. Hopefully the computer occasionally surprises you with a good play.

p.s.
The blocking code is more complicated that the attacking code because you can have multiple blockers. And the attacking code does look to see should it attack with all of its attackers in order to win.

The blocking code tries to see the situation when the AI might attack with everything and then you attack with everything and easily win. Unfortunately I messed up the logic so even that doesn’t work sometimes and it actually makes the AI seem more stupid since it doesn’t race. (Racing is when you attack with everything and then your opponent attacks with everything.)

If the AI could figure out should it race or not, that information would really improve the AI’s performance. (If the computer shouldn’t race, it would attack less and block more, thus making the game go longer because the AI would be trying to topdeck something good.)