Friday, June 22, 2007

Design: AI – Part 2

There are two methods in the class SpellAbility that the computer AI uses. Those methods are canPlayAI() and chooseTargetAI() The canPlayAI() returns a boolean value and dictates whether the computer can play this card are not. Some cards like Remove Soul the computer cannot play; I could never get the logic to work correctly.

And currently the computer only plays cards during his turn. Serpent Warrior’s canPlayAI() method checks to see if the computer’s life is over 3, so the computer doesn’t kill himself. (Serpent Warrior causes its controller 3 damage when it comes into play.) Hex’s canPlayAI() checks to see if there are 6 enemy creatures in play. Remember that SpellAbility is used for Spells as wells as for Abilities, so Royal Assassin’s ability checks to see if there are any tapped enemy creatures.

The method chooseTargetAI() does what it says, it sets the target for the computer if the spell or ability has a target. Cards like Giant Growth and Shock need a target, while Counsel of the Soratami (2U, draw 2 cards) and Wrath of God do not have any targets.

Design: Magic Engine

The design for the next version of MTG Forge uses a concept that I call “Magic Engine.” A Magic Engine tightly implements all the functionality of a Magic game, like returning a creature from play to hand or drawing a card. Previously every card would handle these actions individually. If a card was bounced to your hand, the card would have to check if it was a token, now there will be a method in the Magic Engine called movePlayToHand(Card) that does all of the checks.

The idea is that if I want to program some really new card that the old engine cannot handle, I can just program a new engine with the new functionality. I should be able to construct cards separately from the engine, so that different engines could use the previous existing cards. CardFactory, the class that actually makes cards, basically just adds SpellAbilities to the cards. For more info on CardFactory and SpellAbility see the column Design: Card

Tuesday, June 19, 2007

History of Magic - Part 1


2 new posts today, make sure to read the comments in Design: Card for another post that contains alot of code.

This is Richard Garfield explaining how he created Magic: The Gathering, taken from the book the Game Design Workshop. I haven’t seen this anywhere else and I really enjoyed reading it.

The Creation of Magic: The Gathering
By Richard Garfield written in 1993

The ancestry of Magic
Games evolve. New ones take the most loved features of earlier games and add original characteristics. The creation of Magic: The Gathering is a case in point.

Though there are about a dozen games that have directly influenced Magic in one way or another, the game’s most influential ancestor is a game for which I have no end of respect: Cosmic Encounter, originally published by Eon Products and re-released by Mayfair Games. In this game, participants play alien races striving to conquer a piece of the universe. Players can attempt their conquest alone, or forge alliances with other aliens. There are nearly 50 alien races which can be played, each of which has a unique ability: the Amoeba, for example, has the power to Ooze, giving it unlimited token movement; the Sniveler has the power to Whine, allowing it to automatically catch up when behind. The best thing about Cosmic Encounter is precisely this limitless variety. I have played hundreds of times and still can be surprised at the interactions different combinations of aliens produce. Cosmic Encounter remains enjoyable because it is constantly new.

Cosmic Encounter proved to be an interesting complement to my own design ideas. I had been mulling over a longtime idea of mine: a game which used a deck of cards whose composition changed between rounds. During the course of the game, the players would add cards to and remove cards from the deck, so that when you played a new game it would have an entirely different card mix. I remembered playing marbles in elementary school, where each player had his own collection from which he would trade and compete. I was also curious about Strat-o-matic Baseball, in which participants draft, field, and compete their own teams of baseball players, whose abilities are based on real players’ previous year statistics. Intrigued by the structure of the game, I was irritated that the subject was one for which I had no patience.

These thoughts were the essence of what eventually became Magic. My experiences with Cosmic Encounter and other games inspired me to create a card game in 1982 call Five Magics. Five Magics was an attempt to distill the modularity of Cosmic Encounter down to just a card game. The nature of Cosmic Encounter seemed entirely appropriate for a magical card game – wild and not entirely predictable, but not completely unknown, like a set of forces you almost, but don’t quite, understand. Over the next few years, Five Magics went on to inspire entirely new magical card games among my friends.

Ten years later, I was still designing games, and Mike Davis and I had come up with a boardgame called RoboRally. Mike was acting as our agent, and among the companies he approached was a brand-new gaming company called Wizards of the Coast. Things seemed to be going well, so that August, Mike, and I made our way to Portland, Oregon to meet over pizza with Peter Adkison and James Hays of Wizards of the Coast.

Both Peter and James were very receptive to RoboRally, but informed me that they weren’t really in a position to come out with a boardgame right away. This wasn’t what I come out to hear, of course, but I didn’t want the trip to be a total waste. I asked Peter what he would be interested in. Peter replied that he really saw a need for a game that could be played quickly with minimal equipment, a game that would go over well at conventions. Could I do it?
Within a few days, the initial concept for a trading card game was born, based on another card game I had developed in 1985 called Safecracker. I hadn’t been one of my best games. But then I remembered Five Magics.

The first designs
I went back to graduates school at the University of Pennsylvania, and worked on the card game in whatever spare time I had. It wasn’t easy; there were three months of false starts on the project, there are so many aspects of card game design that have to be reconsidered when designing trading card games. First of all, you can’t have any bad cards – people wouldn’t play with them. In fact, you want to prevent too much range in the utility of cards because players will only play with the best – why make cards people won’t play with? Besides, homogeneity of card power is the only way to combat the “rich kid syndrome” that threatened the game concept from the starts. What was to keep someone from going out and getting ten decks and becoming uneatable?

It was a major design concern. I had numerous theories on how to prevent purchasing power from unbalancing the game, none of which were entirely valid but all of which had a grain of truth. The most compelling counter to this “buy-out-the-store” strategy was the ante. If we were playing for ante, the argument ran, and your deck was the distilled fruit of ten decks, when I did win, I would win a more valuable card. Also, if the game had enough skill, then the player purchasing their power would surely be easy prey for the players dueling and trading their way to a good deck. And of course there was the sentiment that buying a lot of poker chips doesn’t make you a winner. In the end, however, the “rich kid syndrome” became less of a concern. Magic is a fun game, and it doesn’t really matter how you get your deck. Playtesting showed that a deck that is too powerful defeats itself. On the one hand, people stopped playing against it for ante unless a handicap was invoked; on the other, it inspired them to assemble more effective decks in response.

The first Magic release was affectionately named Alpha. It consisted of 120 cards split randomly between two players. The two players would ante a card, fight a duel over the ante, and repeat until they got bored. They often took a long time to get bored; even then, Magic was a surprisingly addictive game. About ten o’clock one evening, Barry “Bit” Reich and I started a game in the University of Pennsylvania Astronomy lounge, a windowless, air-conditioned room. We played continuously until about 3:00 a.m.; – at least that’s what we thought, until we left the building and found the sun had risen.

I knew then that I had a game structure that could support the concept of individually owned and tailored decks. The game was quick, and while it had bluffing and strategy, it didn’t seem to get bogged down with too much calculation. The various combinations that came up were enjoyable and often surprising. At the same time, the variety of card combinations didn’t unbalance the game: when a person started to win, it didn’t turn into a landslide.

From alpha to gamma
Except for the card mix, little has changed about Magic since alpha. In alpha, walls could attack, and losing all your lands of a particular color destroyed the associated spells in play, but otherwise, the rules are much the same now as they were in the early stages of playtesting.
Moving from alpha to the beta version was like releasing a wild animal. The enjoyable game that was alpha now burst the confines of the duel to invade the lives of the participants. Players were free to trade cards between game and hunt down weaker players to challenge them to duels, while gamely facing or cravenly avoiding those who were more powerful. Reputations were forged – reputations built on anything from consistently strong play to a few lucky wins to good bluffing. The players didn’t know the card mix, so they learned to stay on their toes during duels. Even the most alert players would occasionally meet with nasty surprises. This constants discovery of unknown realms in an uncharted world gave the game a feeling of infinite size and possibility.

For the gamma version, new cards were added and many of the creature costs were increased. We also doubled the pool of playtesters, adding in a group with Strat-o-matic Baseball experience. We were particularly anxious to find out if Magic could be adapted for league play. Gamma was also the first version which was fully illustrated. Skaff Elias was my art director: he and others spends days poring over old graphic magazines, comic books, and game books searching for art for the cards.

These playtest decks were pretty attractive for crummy black-and-white cardstock photocopies. For the most part, the cards were illustrated with serious pictures, but there were a lot of humorous ones as well. Heal was illustrated by Skaff’s foot. Power Sink showed Calvin (of “Calvin and Hobbes”) in a toilet; after all, what is a toilet but a power sink? Berserk was John Travolta dancing in Saturday Night Fever. Righteousness pictured Captain Kirk, and Blessing showed Spock doing his “live long and prosper” gesture. An old comic book provided a Charles Atlas picture for Holy Strength, and a 98-poind weakling getting sand kicked in his face for Weakness. Instill Energy was Richard Simmons. The infamous Glasses of Urza were some X-ray glasses we found in a catalog. Ruthy Kantororvitz constructed a darling flame-belching baby for Firebreathing. I myself had the honor of being the Goblins. The pictures and additional players greatly added to the game atmosphere. It became clear that while the duels were for two players, the more players playing, the better the game was. In some sense, the individual duels were a part of a single, larger game.

Design: Card

This will be the first of a set of articles that I will write talking about how I designed MTG Forge. (Well 2nd article, the blogger really messes up Java code.) Magic focuses on cards, so I have a Card object. (I capitalize object names, it’s a Java thing.) Card objects are used everywhere that you would have a regular, cardboard card, in hand, in play, in your graveyard. Card objects hold values like card type (Creature, Sorcery), attack, defense, owner, and controller. Card objects don’t really do anything in and of themselves; they just hold a bunch of related values.

Another important class SpellAbility, implements the functionality of all spells and abilities. It has an abstract resolve() method that is overridden, so Wrath of God’s resolve() would destroy everything, got it?

In MTG Forge, Card objects have one or more SpellAbility objects. A generic creature like Eager Cadet, God bless his frail body, has only one SpellAbility. All creatures are spells, so all Card objects hold at least one SpellAbility. The class CardFactory constructs Cards by adding SpellAbilities, and also implementing the core functionality of the card by the resolve() method. (Resolve() is a method so it gets parenthesis. It is just hard to talk about code, without showing code.)

Ok review, Card objects exist in hand, play, grave, and removed from game. Each Card object has at least one SpellAbility. If a creature has an activated ability like Elvish Piper, the Card will have two SpellAbilities, one SpellAbility for the creature spell, and one for the ability. SpellAbility has an abstract resolve() method that implements the card function, it does the work of the card like destroying things or doing damage, etc..


//*************** START *********** START **************************
//note the mana cost was already set, so I can use the same resolve() for both cards
if(cardName.equals("Wrath of God") || cardName.equals("Damnation"))
{
final 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()
public boolean canPlayAI()
{
return 0 < CardFactoryUtil.AI_getHumanCreature().size();
}
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
}//*************** END ************ END **************************


//*************** START *********** START **************************
//note, the card object already has a standard summon creature SpellAbility added to it
if(cardName.equals("Thought Courier"))
{
//Ability_Tap is a subclass of SpellAbility
final Ability_Tap ability = new Ability_Tap(card)
{
public boolean canPlayAI() {return false;}
public void resolve()
{
AllZone.GameAction.drawCard(card.getController());
AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
}
};//SpellAbility
card.addSpellAbility(ability);
ability.setDescription("tap: Draw a card, then discard a card.");
ability.setStackDescription("Thought Courier - draw a card, then discard a card.");
ability.setBeforePayMana(new Input_NoCost_TapAbility(ability));
}//*************** END ************ END **************************


Friday, June 15, 2007

Design: AI

I have gotten questions about how the computer AI is programmed. I presume that the AI seems believable and even smart sometimes. The AI is divided up into 2 parts: cards and combat. A different component handles each one.

The AI that plays cards is essentially very simple, it just randomly plays a card in his hand. Most cards have some AI code hardcoded into them. The computer will only play Wrath of God if you have a creature in play. Elvish Piper’s ability will choose the biggest creature in hand to put into play. The code in Giant Growth will only target a creature that will attack. The AI for each card is usually pretty simple, but combined together it makes the computer seem alive.

Some cards and abilities the computer cannot play because I wasn’t sure how to evaluate them. The computer cannot play Thought Courier’s draw a card, discard a card ability or Remove Soul, I couldn’t get the code to work correctly. Thankfully some cards like Pestilence the computer uses very efficiently.

Combat is divided up into attacking and blocking. Basically the computer trades creatures when attacking or blocking. (Trading means that both creatures die.) The computer will not block a non-flyer with a flyer, which is usually the correct thing to do. The AI combat routines sound simple but they were really hard to program. My mind felt twisted after I got done with them. It is really hard trying to handle all attacking and blocking situations. The object ComputerUtil_Attack2 is 125 lines and ComputerUtil_Block2 is 210 lines.

The code blocks creatures in this order.
-safe block: attacker dies, blocker lives
-shield block: attacker lives, blocker lives
-trade block: attacker dies, blocker dies
-chump block: attacker lives, blocker dies
-multiple blockers: attacker dies, some or all blockers die

Thursday, June 14, 2007

Programming Magic Cards is Hard

If you want to play Magic on the computer, you have to program cards. The central problem is how to encode Magic cards into a programming language. I have had e-mail asking, “How do you program Magic cards” and this article aims to answer that question. I wish I could say that my way is the easiest, best way to program Magic cards, but it is just a way.

The Magic Project lets you play against other human players over the Internet and enforces the rules unlike Apprentice. The Magic Project, sourceforge.net/projects/magic-project, uses XML for each card.

!-- {2}{b}{b} search your library for a card and put that card into your hand. then shuffle your library. -->
init>
registers>
register index="colorless" value="2"/>
register index="black" value="2"/>
/registers>
colors>black
idcards>sorcery
/init>
abilities>
activated-ability playable="this" name="" zone="hand">
cost>
pay-mana value="manacost"/>
/cost>
effects>
action ref="search-lib"/>
action ref="return-to-hand"/>
action ref="finish-spell"/>
/effects>
/activated-ability>
/abilities>
/card>

(Sorry the XML is butchered so badly.)

The Magic Project has 2,000+ cards programmed, which is a Herculean task in and of itself. The XML schema, mpvalidator.xsd, is 240kb, which is pretty long, but it has to allow for so many card variations and restrictions, like Terror that can only target non-black creatures.

My project MTG Forge, sourceforge.net/projects/mtgforge, just codes the cards into Java. MTG Forge currently has 365 cards. Let us look at Thought Courier, since he has a simple ability. The computer cannot play Thought Courier’s ability because it cannot evaluate cards, that is the meaning of “public boolean canPlayAI() {return false;}”

//*************** START *********** START **************************
if(cardName.equals("Thought Courier"))
{
final Ability_Tap ability = new Ability_Tap(card)
{
public boolean canPlayAI() {return false;}
public void resolve()
{
AllZone.GameAction.drawCard(card.getController());
AllZone.InputControl.setInput(CardFactoryUtil.input_discard());
}
};//SpellAbility
card.addSpellAbility(ability);
ability.setDescription("tap: Draw a card, then discard a card.");
ability.setStackDescription("Thought Courier - draw a card, then discard a card.");
ability.setBeforePayMana(new Input_NoCost_TapAbility(ability));
}//*************** END ************ END **************************

Demonic Tutor is a little bit longer, but is very straight forward. In case you don’t know, it lets you search your library for a card and then put it into your hand.

//*************** START *********** START **************************
if(cardName.equals("Demonic Tutor"))
{
final SpellAbility spell = new Spell(card)
{
public void resolve()
{
String player = card.getController();
if(player.equals(Constant.Player.Human))
humanResolve();
else
computerResolve();
}
public void humanResolve()
{
Object check = AllZone.Display.getChoiceOptional("Select card", AllZone.Human_Library.getCards());
if(check != null)
{
PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, card.getController());
AllZone.GameAction.moveTo(hand, (Card)check);
}
AllZone.GameAction.shuffle(Constant.Player.Human);
}
public void computerResolve()
{
CardList list = new CardList();
Card[] library = AllZone.Computer_Library.getCards();

//gets cards that Computer has mana to play
for(int i = 0; i < library.length; i++)
if(ComputerUtil.canPayCost(library[i].getSpellAbility()[0])) list.add(library[i]);
//pick best creature
Card c = CardFactoryUtil.AI_getBestCreature(list);
if(c == null) c = library[0]; //System.out.println("comptuer picked - " +c); AllZone.Computer_Library.remove(c);
AllZone.Computer_Hand.add(c);
}

public boolean canPlay()
{ PlayerZone library = AllZone.getZone(Constant.Zone.Library, card.getController());
return library.getCards().length != 0; }

public boolean canPlayAI() {
CardList creature = new CardList();
creature.addAll(AllZone.Computer_Library.getCards());
creature = creature.getType("Creature");
return creature.size() != 0; }

MTG Forge there is a Card class that represents cards both in your hand and in play. Every Card object has one or more spells or abilities that are added to it. The class SpellAbility implements all spells and abilities. Every spell or ability has to implement a resolve() method in the SpellAbility class, resolve() does the main functionality of the card. Thought Courier’s ability is implemented in the resolve() method. The same goes for Demonic Tutor, the resolve() method lets you actually look through your library and choose a card. Since the computer can play cards, you will see that Demonic Tutor’s resolve() method does something different depending on if you or the computer played it. MTG Forge also reads cards from a file called “cards.txt” Cards with simple abilities like haste, fear, flying, vigilance, and mana abilities don’t have to be programmed, they are just added to the text file. No extra programming has to be done for cards like Llanowar Elves and Lightning Angel.

Llanowar Elves
G
Creature Elf Druid
no text
1/1
tap: add G

Lightning Angel
1 R W U
Creature Angel
no text
3/4
Flying
Vigilance
Haste

Friday, June 8, 2007

Split

Hi all, thanks for visiting this site. I've only been able to get around 10 hits a day and I want to improve that. I think the problem is the focus of this blog, people either care about Magic or Programming, not both. I plan to split this blog into two separate blogs that are more focused on that specific subject area. I welcome any comments on this subject, --Forge