Monday, July 30, 2007

Version 2 Update

I have some major pieces workings like the user interface (GUI) and the logic to play cards. The GUI looks the same but I had to add a small section for the mana pool, so you can actually get mana burned this time around. The mana pool is for any cards that make mana like Dark Ritual or Seething Song. The Card and SpellAbility classes are the same, except I added a few methods to SpellAbility so it could have more than one target. Most spells/abilities only have one target, but a few, a troublesome few, have more than one, like the new Cone of Flame card in 10th Edition.

I know I want to include cards with protection. The problem is that protection covers multiple areas like regular damage, combat damage, blocking, targeting, and enchanting. I have a few simple enchantments workings like Angelic Blessing. It seems really weird playing with enchantments since MTG Forge never had them before.

I haven’t worked at all on the AI, although it seems like it will work like previous versions. I have gotten the computer to play Remove Soul, which makes the game seem more interactive, since you know the computer might respond to a spell. I would just love it if the computer played Terror during combat. I really want to be able to have the computer use Giant Growth during combat, so that it keeps you guessing some of the time.

My goal is to include a puzzle mode, where the board is already setup and you have one turn to beat the computer. This would help players learn some of the more tricky parts of Magic like combat tricks. Users could also submit situations to me, and I could add them.

A quest mode to become world champion would really help make MTG Forge a full-fledged video game. You would start out with a basic 2 colored deck, and you would have to play other players for money in order to buy booster packs or individual cards. You would participate in constructed, draft, and sealed tournaments. I might also program a DCI rating that went up or down depending on if you won or lost. The rating would not be a “real” rating, but it would still add some interest value.

For what’s its worth, MTG Forge will just be you against the computer. If you want to play against another human player (with the rules) try out sourceforge.net/projects/mtgproject

And for discussion, what feature do you want programmed into MTG Forge?

Friday, July 27, 2007

Good Core Set

10th edition is out and I was thinking about what are the characteristics of a good core set? One way to gauge the popularity of each core set is to look at the market value of each set. If you divide the cost of the whole set by the number of cards in the set, you will get the average cost for a single card.

4th edition is 0.45
5th edition is 0.36
6th edition is 0.60
7th edition is 0.63
8th edition is 0.71
9th edition is 0.72

On average, each card from the 6th to 9th edition has each increased in value over the previous set. The average cost of a card in the 6th edition was 60 cents while for 9th edition it is 72 cents. 5th Edition is the runt of the family. Each card is only worth 36 cents. It weighed in at 434 cards, and apparently none of those cards were very interesting to players. Thankfully, every core set after that had only a lean 350 cards.

Core sets tend to be composed of average cards when compared to an expert set like Time Spiral. For some reason and marketing is the only one that would know, core sets are labeled “advanced” while regular sets are marked “expert.” Core sets have many plain creatures as well the normal abilities of that color i.e. blue draws cards and red burns things.

Core sets have a lasting impact on constructed. The pain lands like Adarkar Wastes from 9th Edition have influenced almost every deck in the constructed format. The pain lands increased the power level of 9th Edition over its predecessor, although you may remember that they were included in the preceding three core sets. Core sets are also designed to be easier for beginners to learn and I agree with that. I started learning Magic during Mirrodin and all of those artifact cards were confusing. I played with the 8th Edition theme decks until I really understood the game.

Card Evaluations

The job of Wizards of the Coast is to make cards fun and hard to evaluate. What I mean by “hard to evaluate” is that if two cards cost the same but one is 2/2 creature and the other one is a 3/3 creature, the 2nd card is always superior. Suspend is a good mechanic because it makes cards hard to evaluate. Should I pay G now or 4G later? Suspend also creates tension, because your opponent sees any suspended cards.

We all know Akroma, Angel of Wrath is a great card, but when Legions first came out paying 5WWW seemed like too much. Tooth and Nail doesn’t work with basic lands, but is a standout with Urza's lands. It has been said that there are no good cards, only good decks, which is true. A card isn’t good in the abstract; it is only good if everything in the whole deck works together. Wrath of God doesn’t fit into every white deck just because it is a good card. Wrath works great in a white control deck, but definitely not in an aggressive beatdown deck.

MTG Forge History


I've posted this before but people seemed to enjoy it. I'll probably only post 1 or 2 things a week, but thanks everyone for reading my stuff :)

Many times I’ve downloaded a cool program and I wanted to learn more about it. Most of the time I can’t find any information describing why a program exists, it just does. This article will describe the history and motivation behind MTG Forge.

It all started when I found the cool 1996 Magic game by MicroProse called Duels of the Planeswalkers, http://www.the-underdogs.info/game.php?id=1550. It let you play against the computer, which lessened the humiliation of defeat. It featured about 640 cards from Alpha, Legends, Antiquities, The Dark, and 4th Edition. It was also way ahead of its time by letting you play against other people over the Internet, much like a free Magic Online. It had all the classic power cards and I was in awe when I first saw them in action. Nevinyrral's Disk, Ancestral Recall, Ali from Cairo, Time Vault, Berserk, and Black Lotus are all very broken. I could tell that these cards were overpowered, but also really fun. I played constructed (all the cards) constantly for about a month or two straight.

I was getting bored, but I found an option to play sealed deck. I randomly clicked on it to see how it was, but I wasn’t expecting anything too great. I loved it! I got to choose my cards from a random card pool and then face off in a 32 player tournament. It was great. Those early card sets were not designed with sealed in mind so I had to use 2 tournaments packs and 4 boosters in order to construct a viable deck, anything less was unplayable.

The MicroProse game was great. The computer acted intelligent enough and would sometimes surprise me by combining the effects of two different cards. I enjoyed playing land destruction and other nasty decks, without actually annoying another person. Sealed deck was really fun, but I wish I could change the card set that it used. Some cards I just got tired of seeing and I wanted to delete them.

I started programming just to see how far I could get. I started by programming code that represented a card and then phase etc… The project just got bigger and bigger and now it is around 700kb of source code. The source code is about the length of a 300 page novel. I programmed in Java because that is my best language. I am sort of surprised the whole project didn’t collapse, and it did a few times. When nothing worked, those were the hardest days. I programmed MTG Forge mainly for myself and I am my own biggest fan. I play it almost every day.

Thursday, July 19, 2007

Feedback 2.0

Hi all, I just wanted to get some feedback about what you find interesting. I don't know if everyone is tired of looking at Java code or what? I don't really know who my target audience is anyways, other than people who are interested in MTG Forge and Magic.

I could may write more on MTG Forge but I'm not sure what else to write. I've had a couple of card submissions, so a few people seem to be programming on their own. I still working on the project and I only have progress reports, like 3 cards done and no AI yet, whoooo!!! Maybe progress reports could be fun to read if I tried, who knows. Just write a comment or two to help me improve things. --Forge

I want my Block

When I started programming MTG Forge, just getting anything to work was a miracle. So I knew from the beginning that I wouldn’t be able to program a whole block. (In case you didn’t know, Time Spiral Block is composed of the sets Time Spiral, Planar Chaos, and Future Sight.) So I decided to program cards that would be fun to play with and very powerful. In essence, I would program my own “super-block” filled with Magic’s all-stars like Flametongue Kavu and Kokusho, the Evening Star.

My super-block idea worked a little, I mean there are plenty of powerful cards to choose from. MTG Forge currently has 401 cards, and all of them are playable. But not having a real block of cards to use did seem hallow for some reason. Blocks have cohesion, while my ragtag grouping of cards was unrelated. It was like a bunch of individual pro players not working as a team, powerful but lacking consistency.

So currently I am working on MTG Forge 2.0 and 10th Edition. The newest core set has many simple cards that are easily programmed as well as some toughies like March of the Machines (which animates all artifacts), Rule of Law (which limits each player to just one spell a turn), and Platinum Angel (You can’t lose the game, and your opponents can’t win the game). It would be great to be able to play draft, sealed, and block constructed with 10th Edition. Just a heads up, it will be many months before I get done. I don’t mind people asking about MTG Forge 2.0, but don’t expect anytime soon.

10th Edition

Hi all, I figured I would intersperse some non-programming Magic articles here and there. Programming is really boring, especially trying to explain it to someone else. If you have never programmed, take by word for it. I do enjoy programming, but it is tedious.

Anywho…10th edition is officially out. It looks like a good many cards, around 50+, have new artwork. I like the new Giant Growth, but not the new Shock. Magic, and maybe all collectable games, inspire people to play with the newest cards available. I swear if Wizards of the Coast could make everyone forget some old set like Invasion, that they would release it to hordes of hungry fans.

Personally the whole black bordered thing doesn’t mean anything to me. (In case you didn’t know, 10th Edition is all black bordered.) Yes you to can own your very own black-bordered Serra Angel without paying those outrageous Alpha prices. I presume it is meant to entice collectors, who knows? I don’t own many cards, less than 100, and I usually only play with digital copies of them. I’m talking about MTG Forge, not Magic Online, paying for cards is an easy way to go broke; I program them instead.

Foil cards, also known as premium cards, won’t have any reminder text which makes them more appealing to everyone but your kid brother. (They will also have different flavor text for those who are into that sort of thing.) This interests me and now I want to download all the foil card pictures for the new core set. For some reason the Pokemon phrase, “Collect them all,” comes to mind, not that Magic players like to be compared to a measly kids game. After all, Magic was invented by a Ph.D in math, so it has to be good for your right? As long as Magic stays away from reverse foil, super rare cards (one in every booster box!!!), I think we will be alright.

Monday, July 16, 2007

Combat 2.0

Combat is really complicated. I realize some people might say, “No, combat is easy. It is just programming.” But programming combat is a tough, thorny problem. Thankfully, once our brains get accustomed to Magic, combat is pretty simple in real life. That creature dies, and that ability triggers, no biggie. But once you test for everything like bouncing creatures after damage is assigned, calculate trample damage, first and double strike, and abilities that trigger when they damage another creature or player, the code starts to look a little messy.

Thankfully I think all of those mentioned features are working. I also figured since combat is essentially difficult I should do a little bit of automatic testing, like around 30 tests, which would test 90% of all situations. I am working on version 2.0, which is still under development and has a long way to go.

Like all great journeys it was a hard one. First, I wrote 200 lines which I thought totally worked and named it “Combat1”. I had an idea that Combat2 and 3 might be needed, and I was right. Combat1 worked in my head but not in reality. I am not sure if anyone else has had this problem. In your head it seems perfect, but then that piece of code doesn’t work correctly. Oh the joys of programming. That would make a good book title.

OK, so I quickly program Combat2 that has similarities to Combat1 but actually works as promised. I’m happy. It passes all of the tests and life is good. Unfortunately the code is rather messy and a little hard to follow. And it is very likely that there are a few really nasty errors hidden in the messy part. So I take out a bunch of variables and put them into a different class called CombatMap. So Combat3 uses CombatMap, and although the combined new code is longer than Combat2, the logic is easier to follow.

Oops, another snafu. Verifying creatures in play is messy and complicated. This is an artificial step that the computer has to do in order to make combat work correctly. This step of verifying creatures that are in play is taken for granted because we have trained our brains to work that way. So give a big welcome to Combat4, which moved all of the “verify creatures in play” code to CombatMap. It takes a couple of tries to make it work with the tests, thankfully they are automated, but it works nicely.

The trick was removing Card objects that were both keys in a Map as well as the data values. The map key was the attackers, and the data was the blockers. Thankfully it seems to be working and hopefully I don’t have to touch it with a ten-foot stick for many months.

Protection should work because Combat4 uses the Magic Engine to do damage. Combat4 deals damage by “magicEngine.damage(Card to, int damage, Card damageFrom)” So the method can check for protection and all that jazz. I spent probably 10-15 hours on Combat 1-4. I pseudocoded parts of it, but the devil is in the details, so I ended up writing the whole thing and then testing it.

AI Testing Framework

My idea is to test different AI routines on a non-trivial, real problem – a game of Magic. Programming the AI should be made as simple as possible so it is easy to get things up and running.

What led me to this idea is that I keep thinking about the AI and how to make it smarter. Then I always come back to the question what is “smarter,” in this case I wish it played more like another human player. The problem with many AI routines and methods is that they are complicated. Even simple depth first or breadth first routines are difficult to integrate into a working program. More sophisticated algorithms like min-max are superior in many ways, but it is hard to test the success of one AI method over another.

It would be great to have a simple Magic game with about 100 cards that you could easily program and test your favorite AI algorithm. Even combat is difficult enough. When should you trade? When should you race? If I have two 2/2 creatures and my opponent has 7/7 creature, should I attack? Yes if he is at 2 life, or if I am holding a card like Lava Axe and I need to knock him down 2 more life.

Testing the algorithms with a non-trivial game like Magic would be great. You could play against the computer and see the mistakes of the AI in action. Maybe at first your combat routines would just check the creatures that are on the field and then later adjust its play according to what cards it is holding. AI versus AI would be cool also. Each AI player could use the same deck, and the better AI would win. I still believe the only way to thoroughly test the strength of an AI method is to play it yourself. A great AI routine should be able to beat you half of the time if you are both playing with the same deck. I know all this is just a dream, but a lovely one.

You could even host a Magic version of the “Turing Test” where people play an unknown player and have to guess if the opponent is another human player or a computer.

In reality unless many people are interested in this, and I do mean at least 30, I probably won’t program this. (Or at least not any time soon.) I have all of the components in MTG Forge, the GUI, the logic, the cards, but I would have to rework some parts of it. If I coded this, and that is a big if, it would be in Java. Also I don’t know that much about AI. Yes I’ve glanced at some AI books but I didn’t see any useful cut-and-paste code. AI books and websites seem to be mostly theory. I’m sure that there is some good AI information somewhere out there, but it is really hard to find. Even if I could just find some AI that worked great it combat, I would be eternally grateful. I’ve probably seen 100 different AI routines for playing Tic-Tac-Toe, but where are the routines that play Tetris?

Early and Late Cards

What do the mechanics cycling, kicker, morph, buyback, and suspend have in common? All of these cards let you pay 2 different costs. Most cards have only 1 cost, and that is all well and good, but it makes them less versatile. Take for example Barkhide Mauler, a 4/4 creature that costs 4G and also has cycling 2. Now most of the time I will pay 4G but occasionally I’ll be short on mana, so I’ll cycle him for 2, just to hopefully draw another land.

Morph on a big creature is great; you get a small creature early on and then a bigger creature latter on. Usually you have to just sit on your 7/7 fattie until late in the game, but if it had morph, it is like you are getting 2 creatures for 1 card, as long as you don’t lose it in combat. Buyback gives you card advantage if you have enough mana, and if you don’t, you can still play the card. Suspend gives you more options, by letting you pay G now or 4G latter. Suspend also gives you good cards that you can play on turns 1 and 2, turns that usually don’t have much influence in the game.

Input Explained

Some people have commented that the Input class is giving them problems and I understand where they are coming from. All of this code was never meant to be understood by anyone else, lol. Anyways, the Input class handles all the user input, which is from a mouse, and is used to set targets for cards. The targets may be either a player or a card. SpellAbility setTargetPlayer(String player) and setTargetCard(Card) actually sets the target that the resolve will use. There is corresponding getTargetPlay and getTargetCard commands. This limits cards to a single target. Hex has multiple targets, but it is an exception. Multiple targets can be programmed but the code is messy.

The method SpellAbility setBeforePayMana(Input) is used to set the Input that will handle getting a card’s target. The method could have been named better.

CardFactoryUtil returns common Inputs that are useful.

To target a player, CardFactoryUtil.input_targetPlayer(SpellAbility spell). Hopefully Ancestral Recall is straightforward and easy to understand. The computer always sets himself as the target with the method setChooseTargetAI and canPlayAI makes sure the computer doesn’t have to discard any cards.


if(cardName.equals("Ancestral Recall"))
{
SpellAbility spell = new Spell(card)
{
public void resolve()
{
AllZone.GameAction.drawCard(getTargetPlayer());
AllZone.GameAction.drawCard(getTargetPlayer());
AllZone.GameAction.drawCard(getTargetPlayer());
}
public boolean canPlayAI()
{
return AllZone.Computer_Hand.getCards().length <= 5;
}
};
spell.setChooseTargetAI(CardFactoryUtil.AI_targetComputer());

spell.setBeforePayMana(CardFactoryUtil.input_targetPlayer(spell));
card.clearSpellAbility();
card.addSpellAbility(spell);


To target a creature, CardFactoryUtil. input_targetCreature(SpellAbility spell) Swords to Plowshares removes a creature from play and gives it’s controller some life.


if(cardName.equals("Swords to Plowshares"))
{
SpellAbility spell = new Spell(card)
{
public void resolve()
{
if(AllZone.GameAction.isCardInPlay(getTargetCard()))
{
//add life
String player = getTargetCard().getController();
PlayerLife life = AllZone.GameAction.getPlayerLife(player);
life.addLife(getTargetCard().getAttack());

//remove card from play
PlayerZone zone = AllZone.getZone(getTargetCard());
zone.remove(getTargetCard());
}
}//resolve()
public boolean canPlayAI()
{
CardList creature = new CardList(AllZone.Human_Play.getCards());
creature = creature.getType("Creature");
return creature.size() != 0 && (AllZone.Phase.getTurn() > 4);
}
public void chooseTargetAI()
{
CardList play = new CardList(AllZone.Human_Play.getCards());
Card target = CardFactoryUtil.AI_getBestCreature(play);
setTargetCard(target);
}
};//SpellAbility
spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));

card.clearSpellAbility();
card.addSpellAbility(spell);
}


To target anything in the “type” line of a card like Creature, Land, Goblin, or Forest. CardFactoryUtil.input_targetType(SpellAbility spell, String cardType)
Type can also be “All” which would let the user target any permanent.


if(cardName.equals("Stone Rain"))
{
final SpellAbility spell = new Spell(card)
{
public void resolve()
{
Card c = getTargetCard();
if(AllZone.GameAction.isCardInPlay(c))
AllZone.GameAction.destroy(c);
}

};//Spell
card.clearSpellAbility();
card.addSpellAbility(spell);

spell.setChooseTargetAI(CardFactoryUtil.AI_targetType("Land", AllZone.Human_Play));

spell.setBeforePayMana(CardFactoryUtil.input_targetType(spell, "Land"));
}


Stone Rain also configures the AI by the method
CardFactoryUtil.AI_targetType(String type, PlayerZone zone)

Type is like Forest, Creature, Land, or All. Common PlayerZones are

AllZone.Human_Play – Shock
AllZone.Computer_Play – Giant Growth
AllZone.Computer_Graveyard – Gravedigger

AllZone is just a global object that holds all of the “zones”, like in play, graveyard, etc…

The human player can only target cards in play, so cards like Gravedigger show a window so a card from the graveyard can be selected. Dark Banishing has a custom Input that targets a non-black creature. After a creature is chosen in the selectCard method, the user has to pay the mana cost which stops the current Input. The SpellAbility of Dark Banishing was omitted for clarity.


Input target = new Input()
{
public void showMessage()
{
AllZone.Display.showMessage("Select target non-black creature for " +spell.getSourceCard());

ButtonUtil.enableOnlyCancel();
}
public void selectButtonCancel() {stop();}

public void selectCard(Card card, PlayerZone zone)
{
if((!CardUtil.getColors(card).contains(Constant.Color.Black))
&& card.isCreature()
&& zone.is(Constant.Zone.Play))
{
spell.setTargetCard(card);
stopSetNext(new Input_PayManaCost(spell));
}//if
}//selectCard()
};//Input

spell.setBeforePayMana(target);

Thursday, July 12, 2007

Programming Giant Growth

People seemed to have really enjoyed my entry on “Programming Shock” so I’ll cover another common card. If you look at the code, you will see that it works for both Giant Growth and its Planar Chaos brother Brute Force. Remember at the top of the CardFactory getCard method the mana cost and name of the card is already set.

Giant Growth and other effects stay around “until end of turn.” End of turn is commonly abbreviated EOT. In order to program EOT effects some bit of code has to be executed later.

MTG Forge source code has an interface called Command, which is a software Design Pattern. The interface for Command is very simple.

public interface Command
{
public void execute();
}

Command is a generic object that is used to execute arbitrary code at a later time. So when Giant Growth resolves it will give the target creature +3/+3 and make a Command object that resets the creatures stats to normal. AllZone.EndOfTurn.addUntil(Command)
executes all until EOT Commands.

There are really two types of EOT effects, until EOT and at EOT. Giant Growth says until EOT and Ball Lightning says at EOT. So Giant Growth would use AllZone.EndOfTurn.addUntil(Command) and Ball Lightning would use AllZone.EndOfTurn.addAt(Command) This division doesn’t matter in the current version of MTG Forge, since you cannot stop at EOT. Technically “At EOT” effects are done first, then you would get priority to play cards/abilities, and then “Until Effects” wear off.

Unfortunately the resolve method for Giant Growth is a little messy because a Command object has to be constructed. Command has to be inside the resolve method in case Giant Growth is played, and then returned to your hand, and then played again.

This is very Java specific and a little complicated. The array “target” is used because the Command object needs a final variable. Target simulates a variable that can be changed, while fulfilling the syntax that Java needs to make the Command object.

The AI just makes sure that the computer is attacking with at least one creature and plays Giant Growth on that creature. Currently the computer cannot play any spells/abilities during combat, so the computer will never attack with a smaller creature and then play Giant Growth. The computer will only play Giant Growth during his main phase.


if(cardName.equals("Giant Growth") cardName.equals("Brute Force"))
{
SpellAbility spell = new Spell(card)
{
public void resolve()
{
final Card[] target = new Card[1];
final Command untilEOT = new Command()
{
public void execute()
{
if(AllZone.GameAction.isCardInPlay(target[0]))
{
target[0].setAttack (target[0].getAttack() - 3);
target[0].setDefense(target[0].getDefense()- 3);
}
}
};

target[0] = getTargetCard();
if(AllZone.GameAction.isCardInPlay(target[0]))
{
target[0].setAttack (target[0].getAttack() + 3);
target[0].setDefense(target[0].getDefense()+ 3);

AllZone.EndOfTurn.addUntil(untilEOT);
}
}//resolve()
};

spell.setBeforePayMana(CardFactoryUtil.input_targetCreature(spell));

//Instants and Sorceries should clear because cards are presumed to be permanents
card.clearSpellAbility();
card.addSpellAbility(spell);
}

//AI removed for clarity, should be put in SpellAbility
public boolean canPlayAI()
{
return getAttacker() != null;
}
public void chooseTargetAI()
{
setTargetCard(getAttacker());
}
public Card getAttacker()
{
//target creature that is going to attack
Combat c = ComputerUtil.getAttackers();
Card[] att = c.getAttackers();
if(att.length != 0)
return att[0];
else
return null;
}

Modes of Magic

Magic like many great games can be played in a variety of ways. Games like Monopoly, Risk, and Scrabble are so popular, that many people play with their own house rules. Magic has an overgrowing number of formats that are all gaining in popularity. Magic Online has 17 formats. Vanguard has spawned its own versions of Standard, Extended, and Classic as well as Momir Basic which is a subset of Vanguard itself. Vanguard lets you start with a special card in play that changes the game like “Draw 1 card and lose 1 life during your upkeep, you start out with 15 life.”

Tribal lets you play with your favorite creatures types. Singleton restricts all cards to just a single copy, no crazy combo decks here. Prismatic lets you assemble an extra-large 250 card deck that requires all 5 colors to be included. Other types of games include Peasant Magic which includes only commons and a few uncommons. Highlander which is basically Singleton decks with a minimum of 100 cards, which makes every game different. Now there is even the officially sanctioned Two Headed Giant format as well as regular multiplayer Magic. Every format has different a feel to it. So if you are tired of just Standard, try something else. And don’t worry if you lose at first, you have to walk before you can run. :)

Saturday, July 7, 2007

MTG New Version

New Version. Hi loyal readers, yes all 10 of you. :) Trample is now working, miscellaneous creatures with trampling were added. Wildsize and Tromp the Domains have trample also. 40+ cards were added to MTG Forge: Akroma, Angel of Wrath (no protection though), Essence Warden, Soul Warden, Nightmare, Magus of the Disk, Magus of the Library, Library of Alexandria, Tendrils of Corruption, Minions' Murmurs, Ichor Slick, Lucent Liminid, Boil, Baru, Fist of Krosa, Korlash, Heir to Blackblade, Tortuga. Ok you got me, Tortuga isn’t a real Wizards of the Coast card, but it is a cool card I made and you just can’t beat Johnny Depp. Maybe it is just me, but I think Johnny Depp deserves his own Magic card. I was told by a nephew of a guy that worked as Johnny Depp’s assistant that he heard that Johnny Depp loves Magic. It has to be true, right? If you download the card pictures check out Tortuga featuring Johnny Depp and a foil Wonder. (If you are using the card pictures from the previous version, you will have to delete the file Wonder.jpg in order to get the new version to download the foil Wonder.)

I added the two Future Sight cards Korlash, Heir to Blackblade and Baru, Fist of Krosa. The grandeur for those two cards and Tarox Bladewing all work, so have fun playing with those nice rares. In MTG Forge I put together a deck with 4x copies of all 3 of the grandeur creatures and it plays pretty good. It has 12 Ravnica duel lands in order to support all of the crazy mana requirements and for convenience it is only 40 cards. The deck is aptly named “grandeur.” “U-permission” is filled with Counterspells and Library of Alexandria. “Green-elvish-piper” now works better with Akroma, Angel of Wrath because it doesn’t tap and it also has haste, a rare combination.

Essence Warden and Soul Warden sounded cool, but gaining 1 life when another creature comes into play is only so so useful, probably for about 3-5 life I would guess. Magus of the Disk is a great 2/4 Wrath of a God. Magus of the Library is almost impossible to use. Compare that to Library of Alexandria which is totally worth the $200 dollars if you had to buy it in real life. I think it is cool when modern Magic players get to play with old school cards and see how they work and why they are so good. (I used two “ands” in one sentence, cool.) I would love to program Time Vault, but I haven’t tried yet.

The Ravnica duel lands now work correctly, you have to pay 2 life for them to come into play untapped. The computer also plays them correctly. The computer sometimes had a glitch when using the mana from Birds of Paradise and Vine Trellis. I think I solved the problem but I couldn’t test it for sure.

For those few people who actually are still reading, I spent about 4 hours programming these cards. Grandeur turned out to be a little harder than I initially thought, but it works great and I think they are fun cards. Soul Warden (and Essence Warden) work correctly most of the time except when another Soul Warden comes into play, you won’t gain life. My first iteration had the player gaining two life when the 2nd Soul Warden was played.

Better AI

I’ve talked a lot about the AI, but I have still a little more on my mind. Hopefully I can get the computer to play Instants and abilities during combat and at end of turn. I say “hopefully” because I am not quite to that point yet and although it sounds simple to theory, in reality it is always more complicated.

Anyways, as you might remember the AI calls SpellAbility canPlayAI method if it should play that card or ability. If that method returns true, then chooseTargetAI is called and it sets any necessary targets. This is a simple way that I have found to give each card some intelligence.

The problem came about when I think Shock should kill a 2/2 flyer, in order to maximize its effect. The computer will first try to kill you with Shock if your life points are that low. The problem is that in constructed (all the available cards) killing a 2/2 flyer is fairly common, but in sealed or draft it is more unusual. I could split the AI put into limited and non-limited games, but generally the same AI does fine for both. In general I want the AI to be more flexible, so that Shock should usually target a 2/2 flyer, but sometimes if the computer is desperate, it will kill a non-flyer.

I could split canPlayAI and chooseTargetAI into two sets of methods. The first set of methods could check if the best targets are available. And the second set of methods could check for just good targets. If the computer is over 10 life only the “best” functions would be called, so a card would always get the maximum effect. Otherwise, the “good” methods would be checked, so a card would have a lesser effect. Using this division would ensure that only good cards are played at the beginning of the game and that lesser cards are used out of necessity in order keep from dying.

The main drawback with this type of AI is that the computer will not double Shock a 4/4 creature, because each individual Shock only looks for a X/2 creature to kill. This worries me when I am programming but I don’t notice when I’m playing a game. I guess it is just a case of “over thinking” the situation.

Thursday, July 5, 2007

Attack!!!

Hi loyal readers. I just wanted to see what feedback you had to offer. Am I posting too much code or does the code need more comments? Or should I change the template of the blog so that the code is easier to read? Or do you want to read some programming and some general Magic articles like below? --Thanks for your feedback, Forge

In Magic, knowing when to attack is a fundamental question. Naturally you attack when it helps you win. In limited games in particular, there are times when you and your opponent just go back and forth attacking each other, this is known as racing. You should race if you will win, which unfortunately requires a little math, but not much.

Your Turn 15 life, 2/2 creature – Opponent 7 life, 3/3 creature – Should you race?
The easiest way to figure out to race is to first calculate how many turns it will take for you to kill your opponent. Now compute how many turns it takes your opponent to kill you. Obviously if you can kill your opponent first, attack. In this example, you will win in 4 turns, and you opponent will win in 5 turns, so you should attack.

Your Turn 2 life, 3/3 creature – Opponent 5 life, 1/1 creature – Should you race? Yes
Your Turn 5 life, 2/2 creature – Opponent 11 life, 1/1 creature – Should you race? No
Your Turn 17 life, 3/3 creature – Opponent 15 life, 2/2 creature – Should you race? Yes
Your Turn 19 life, flying 1/1 creature – Opponent 9 life, 2/2 creature – Should you race? Yes
Your Turn 8 life, flying 2/2 creature – Opponent 7 life, 3/3 creature – Should you race? No

Obviously you don’t know what cards your opponent will draw, but you can race to reduce his chances of winning. Racing increases your chances of winning because it puts pressure on your opponent. If you are loosing the race, you will want to trade creatures in combat, both of them will die, in hopes that you can draw a solution. In most games you will be both the aggressor, who wants to race, and the defender. Aggro decks are built upon the concept of racing, and hope to attack on every turn.

Programming Shock

Many cards and abilities are similar to Shock. In case you can’t remember, Shock deals 2 damage to target creature or player. First, Shock has to be added to the cards.txt file. Cards.txt holds all of the text that the card displays as well as the mana cost.

Shock
R
Instant
Shock deals 2 damage to target creature or player.

The resolve method checks to see if the card is still in play, something that we take for granted in a real-life game. SpellAbility only allows for 1 target to be set, either a card or a player by the setTargetPlayer(String player) and setTargetCard(Card) methods. To get the target you call either getTargetPlayer or getTargetCard. The SpellAbiliy method setBeforePayMana(Input), lets the user actually choose the target card or player. CardFactoryUtil has a bunch of reusable Input classes that gets a user choice. If the spell just targeted a creature you would use CardFactoryUtil.input_targetCreature.

SpellAbility canPlayAI method returns false if you do not want the computer to play this spell. A very simple AI for Shock is below. canPlayAI is called first, and if it returns true, then chooseTargetAI is called. chooseTargetAI sets the target for the spell by either calling setTargetCard(Card) or setTargetPlayer(String player). Some cards like Wrath of God do not have a chooseTargetAI method, because no targets are needed.

This checks to see that the human player (you) have at least 1 creature in play and sets that creature as the target. A more sophisticated AI would try to get the best creature that Shock could kill, like a 2/2 flyer. The AI could also see if you had low life, and could alternately target you instead of a creature. The regular AI for Shock tries to kill a creature with flying or you if your life is low.


//simple AI for Shock
//should be put in a SpellAbility object
public boolean canPlayAI()
{
CardList list = CardFactoryUtil.AI_getHumanCreature();
return 0 < list.size();
}
public void chooseTargetAI()
{
CardList list = CardFactoryUtil.AI_getHumanCreature();
setTargetCard(list.get(0));
}

//Complete card
final Card card = new Card();
card.setName(“Shock”);
card.setManaCost("R");
card.setOwner(Constant.Player.Human);
card.setController(Constant.Player.Human);

final SpellAbility spell = new Spell(card)
{
int damage = 2;

public boolean canPlayAI() {return false;}

public void resolve()
{
if(getTargetCard() != null)
{
if(AllZone.GameAction.isCardInPlay(getTargetCard()))
{
Card c = getTargetCard();
c.addDamage(damage);
}
}
else
AllZone.GameAction.getPlayerLife(getTargetPlayer()).subtractLife(damage);
}//resolve()
};//SpellAbility
card.addSpellAbility(spell);
spell.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(spell));

//Shock as it would be in CardFactory
if(cardName.equals("Shock"))
{
final SpellAbility spell = new Spell(card)
{
//same stuff
};//SpellAbility

//this is cleared because a card is presumed to be a permanent
//all Sorceries and Instants have to call card.clearSpellAbility()
card.clearSpellAbility();
card.addSpellAbility(spell);

spell.setBeforePayMana(CardFactoryUtil.input_targetCreaturePlayer(spell));
}

Programming Life Gain

I wanted to cover some of the basic abilities that cards do like gaining life, losing life, drawing a card, etc… AllZone is a static global variable that can be accessed from anywhere. GameAction does some high level actions like drawing cards and destroying a creature. The code snippets that I will be going over usually will go in the resolve method of SpellAbility objects. These are some common actions that are done throughout the game.

This is how you gain life.

PlayerLife p = AllZone.GameAction.getPlayerLife(Constant.Player.Computer);
p.addLife(2);

Usually I wouldn’t hardcode the player (human or computer) so the code would look like this.

PlayerLife p = AllZone.GameAction.getPlayerLife(card.getController());
p.addLife(2);

Subtracting life is similar.

PlayerLife p = AllZone.GameAction.getPlayerLife(Constant.Player.Computer);
p.subtractLife(4);

Drawing a card looks like this. If you wanted to draw multiple cards, you would use a for loop. AllZone.GameAction.drawCard(Constant.Player.Human);

Whenever a card is put into a graveyard, AllZone.GameAction.destroy(Card) is used.
AllZone.GameAction.destroy(Card);

Cards like Strangling Soot will have a SpellAbility.resolve that looks like this.
AllZone.GameAction.destroy(spell.getTargetCard());

Terror is similar but says that the card cannot be regenerated. AllZone.GameAction.destroyNoRegeneration(Card);

This discards a card at random from a player.
AllZone.GameAction.discardRandom(Constant.Player.Computer);

Regular discard is done this way. This assumes that the chosen card is already picked and this method just moves the card from a player’s hand to the graveyard.
AllZone.GameAction.discard(Card);

Sacrificing a card is similar to discarding, the chosen card is already picked. This would be called from the Input class, since it handles all the user input from the mouse. The Input class lets the user choose a card and then the code below is executed.
AllZone.GameAction.sacrifice(Card);

Shuffling your library is a common occurrence.
AllZone.GameAction.shuffle(card.getController());

Cards like Shock just add damage to a card. GameAction.checkStateEffects moves the card to the graveyard if it is needed.
Card c = spell.getTargetCard();
c.addDamage(2);

Monday, July 2, 2007

Static Abilities

Today, I’m going to talk about the hardest cards to program. Cards like Glorious Anthem caused me a lot of headaches.

This is a definition of static abilities from the rules manual.

You don’t play and resolve static abilities like the other two ability types. When a permanent with a static ability comes into play, the ability’s effect simply “turns on.” It stays on as long as the permanent stays in play. (Static abilities create continuous effects.) Most enchantments have static abilities. For example, Telepathy reads, “Your opponents play with their hands revealed.” Once Telepathy is in play, you don’t have to pay a cost to have your opponent reveal his or her hand. Your opponent’s hand is just kept face up on the table until Telepathy leaves play.

I call static ability, state abilities since they depend on the current “state” or condition of the game. State abilities include Glorious Anthem, Levitation, Soul Warden, and Castle Raptors. Glorious Anthem gives your creatures +1/+1, so it changes the state of the game. Castle Raptors gets a bonus if it is untapped.

In MTG Forge static effects are processed after you play a land, the computer plays a land, or a spell/ability is resolved. The code for Glorious Anthem is a little messy since it keeps track of old creatures that were already in play. Glorious Anthem first takes away the +1/+1 from all old cards and then gives the bonus for each copy of Glorious Anthem in play. The code loops through all copies of Glorious Anthem in case you and your opponent both have it in play. This code also works correctly if you have multiple Anthems in play.

The code below was taken from the class GameActionUtil. It has a method called executeCardStateEffects that executes all of the effects. Doing state effects this way has the drawback of separating code from the Card and SpellAbility classes which I would prefer not to. Platinum Angel has a very cool and hard to program state effect that denies your opponent from winning the game. Check it out in the upcoming 10th edition core set.

GameAction also implements some state effects in the method checkStateEffects. Other MTG Forge state effects include sending a creature to the graveyard if it has sufficient damage. Destroying legendary creatures if two are into play and checking to see if any player’s life is 0. The cards that have a state effect in MTG Forge are Baru, Fist of Krosa, Soul Warden, Essence Warden, Nightmare, Korlash, Heir to Blackblade, Glorious Anthem, Gaea's Anthem, Wonder, Anger, Valor, Veteran Armorer, Radiant, Archangel, and Castle Raptors.


static Command Glorious_Anthem = new Command()
{
CardList gloriousAnthemList = new CardList();
public void execute()
{
CardList list = gloriousAnthemList;
Card c;
//reset all cards in list - aka "old" cards
for(int i = 0; i < list.size(); i++)
{
c = list.get(i);
c.setAttack(c.getAttack() - 1);
c.setDefense(c.getDefense() - 1);
}

//add +1/+1 to cards
list.clear();
PlayerZone[] zone = getZone("Glorious Anthem");

//for each zone found add +1/+1 to each card
for(int outer = 0; outer < zone.length; outer++)
{
CardList creature = new CardList(zone[outer].getCards());
creature = creature.getType("Creature");

for(int i = 0; i < creature.size(); i++)
{
c = creature.get(i);
c.setAttack(c.getAttack() + 1);
c.setDefense(c.getDefense() + 1);

gloriousAnthemList.add(c);
}//for inner
}//for outer
}//execute()
};//Glorious Anthem

//returns all PlayerZones that has at least 1 Glorious Anthem
//if Computer has 2 Glorious Anthems, AllZone.Computer_Play will be returned twice
PlayerZone[] getZone(String cardName)
//method body removed on purpose

Programming Magic Cards 1

Many people are interested in how I program cards in MTG Forge. Obviously some of the cards are simple and some are complicated. Wrath of God is very simple while Kiki-Jiki, Mirror Breaker is very complicated. The article “MTG Forge Overview” showed you Serra Angel which is a typical creature. Today I’m going to show you how I programmed tap abilities with and without mana costs as well as how to draw a card and gain life.

The code showed below is for “Super Card.” It is an artifact that lets you tap to draw a card or tap and pay 1 to gain a life. Super Card has 3 SpellAbilities added to the card: 1. so it comes into play as a permanent, 2. gain 1 life, and 3. draw a card. As you may or not remember, the SpellAbility class is used for all spells and abilities, hence its name. The life gaining effects costs 1 mana of any color while the draw card ability only taps the card. The resolve method of SpellAbility does the main function of any card or ability. The card’s controller is the one who gains life. If your opponent steals it during the game the effect will still work correctly and you will still own the card, but your opponent would control it. If it is put into a graveyard, it will go into its owner’s graveyard.

The setDescription method of SpellAbility sets the text that is show text box of the card on screen. The setStackDescription, also a part of SpellAbility, is the text that the stack displays. These two abilities require a different argument for the setBeforePayMana function. The setBeforePayMana function optionally lets a user do some action before choosing any targets and before paying for mana, it is the first thing that is checked when a card is clicked on. The setBeforePayMana function makes the user pay for the mana cost when gaining life, and just taps the card when drawing another card. You can put anything into the setBeforePayMana method and I added “tap abilities” late in development. setBeforePayMana accepts Input objects. Input objects process all user input through the mouse and allows the user choose targets. This way of doing abilities and tap abilities is not every elegant but it works.

AllZone is a global (static) object that can be accessed from anywhere in the program and GameAction does some logical actions like drawing a card, gaining life, discarding, etc... When this card is clicked on the user is given the choice of playing either of the two abilities.


Card card = new Card();
card.setName("Super Card");
card.setOwner(“Human”);
card.setController(“Human”);
card.setManaCost(“1”);
card.addType(“Artifact”);
card.addSpellAbility(new Spell_Permanent(card));

final SpellAbility a = new Ability_Tap(card, "1")
{
public void resolve()
{
AllZone.GameAction.getPlayerLife(card.getController()).addLife(1);
setStackDescription(card.getController() +" gains 1 life.");
}
};
a.setDescription("1, tap: You gain 1 life.");
a.setBeforePayMana(new Input_PayManaCost(a));
card.addSpellAbility(a);


final SpellAbility b = new Ability_Tap(card, "0")
{
public void resolve()
{
AllZone.GameAction.drawCard(card.getController());
setStackDescription(card.getController() +" draws a card");
}
};
b.setDescription("tap: You draw a card.");
b.setBeforePayMana(new Input_NoCost_TapAbility((Ability_Tap) b));
card.addSpellAbility(b);