Thursday, June 28, 2007

AI Good Enough?

I’m working on a new version of MTG Forge and I keep thinking about how to make the computer smarter, but I’m not sure if that is really relevant. My only thoughts are, “I wish I had more cards to play with.” I almost never think about what the computer is doing. So what is the computer isn’t that smart all the time, I am only looking for an opponent that is “good enough.” I tend to relate playing the computer like you would play with your kid brother. Your kid brother isn’t all that good at Magic, but he still can surprise you. I only expect the computer to play reasonably and to occasionally surprise me, which it does. J

So upon further reflection the AI will be the same, unless some easy method comes to me. I am all about easy coding, code it right the first time, but if it is really complicated, try to get by with simpler code, and optionally go back later to change it if I need to. In my games I don’t really care that the computer isn’t efficiently playing his cards, I’m only thinking about my strategy. My thinking and playing makes the game fun. The computer just acts like your kid brother except that he doesn’t mind losing, lol.

Although I will be programming the AI the same way, I should be able to make the computer play Instants at end of turn and hopefully program the computer to somehow respond to spells also, like bouncing a creature back to his hand. The computer should draw card at end of turn, and use burn effects at end of turn or during combat. I really would like the computer to play Giant Growth effects better also, and occasionally bluff so you don’t know if the computer really has Giant Growth in his hand or not.

Fun AI

The computer AI for MTG Forge is incredibly dumb. It just tries to play a card at random, pure and simple. But since you don’t know what card the computer is holding, the computer seems intelligent. Yesterday the computer did the absolutely best play; it played Wojek Embermage and then Hunted Troll on the next turn. Then it used Wojek’s ability to kill off the tokens that the Troll made. It was an amazing play from a computer that only plays random cards. If you didn’t know the computer was so dumb, you could mistake it for intelligent.

Granted sometimes it makes the worst play ever, usually involving Wrath of God. For example, the computer sometimes plays a creature and then Wrath, which is utterly stupid of course. Even most beginners don’t make a mistake like that, but the computer doesn’t learn like a beginner either. Most cards only do something bad for your opponent, and the computer seems very intelligent when playing with those kinds of cards, but the computer doesn’t seem so intelligent when playing cards that have a drawback.

2 Types of Programming

I think all computer programming can be divided up into two categories. One, code that is short but very tricky and two, code that is long and monotonous. Tricky code includes such things as sorting algorithms, where any small mistake in logic is a total disaster. SQL statements are notoriously tricky, sometimes you need a semicolon at the end and sometimes you don’t. One time I was helping a fellow programmer debug some networking code. He had mistakenly misread a variable read a lower case variable name “L” as a 1. It wasn’t a big mistake, but it still broke the functionality. Tricky code is harder to test because it was to work 100% correctly all of the time. For simple programs monotonous code tends to either run or not run, and that is all the error testing that it needed.

Monotonous code happens when you want to save a file to the hard drive. You know what you want to do, but there is a lot of error checking to be done. Is the hard drive full? Is the file name valid? How do you validate the file when you read it? Monotonous code wants to be lean and mean, but is bloated because of error checking or other requirements. Even though I say error checking “bloats” the code, error checking is very important. Another type of monotonous code is anything visual, like the user interface. Any decent visual interface (GUI) is at least 10,000 lines of code. I know all of those lines are needed, but the intent is just an interface, the product is tons of code that may or may not be manageable.

Monday, June 25, 2007

MTG Improvements

3 new articles today

I’m all for feedback, and I read all the e-mail I get. What are your thoughts about MTG Forge? Do you like the user interface, deck editor, and the computer AI? Do you play more constructed, sealed, or draft? Just click on the comments link at the bottom of this article to let your voice be heard.

Well I’m going to tell you my opinion. I’m not just the developer, I’m also an end user. I enjoy programs (games) that are easy to use and fun. I enjoy good surprises, like when the computer hits me with Hidetsugu's Second Rite which causes 10 damage only if you are at 10 life, it was a great way to lose. I think those kinds of surprise moments really make MTG Forge infinitely re-playable.

Personally, I used to play just sealed games with the “Generate Deck” option. I loved the surprises like getting 2 copies of the same rare in my deck or playing offbeat color combinations like white red or green blue. Recently I’ve done more drafts, about one a night, and I play all seven of the computer opponents. One time the computer had a pretty good red/green land destruction deck that really crippled me. Usually I don’t even experience a game loss, and very rarely I lose a whole match. This is a tidbit of inside info, when you are drafting instead of clicking the “Choose Button” card, you can just right click. By right clicking you are selecting the card that is currently highlighted.

I also really enjoy the deck editor. It took a lot of time to get the tables to show correctly on the screen and sort, I really wanted a sort feature. In case you don’t know, you can sort the cards by clicking on the column name. The deck editor looks a lot like Magic Online, and this is done on purpose. The deck editor is really easy to use, unlike other games. I think making your deck should be as fun as possible.

The suggestions that I’ve heard the most are making the user interface look more like Magic Online, cards pictures in play, stack lands, etc… and to program a whole set of real cards like 9th Edition or Ravnica. Of those two suggestions, the 2nd one is the most feasible. I really would love making MTG Forge more graphical like a video game, but I don’t have the knowledge to do that. Being able to play with a whole set would be cool because you could accurately draft and play block constructed.

I am currently trying to program the 10th edition core set. I am rebuilding MTG Forge from the ground up to make it better and more flexible, so currently it just cycles through the phases and I can play creatures. You can choose which phases stop, so you can play cards at the computer’s end of turn. Combat does not work, and all the old cards won’t work with the new version. I am going to start programming the white cards and many of white’s prevention effects have been eliminated, which makes it a little easier to program. Story Circle is still around, and I’m not sure how to currently program it, but I should be able to figure it out.

High Ground will be a pain to code, it is an enchantment that lets your creatures block 2 creatures. It does some seem useful for either block constructed or in limited games. Windborn Muse is a 2/3 with an ability like Ghostly Prison, that makes your opponent pay 2 for each attack. I think I can program it, but cards like that are prone to errors. Programming the computer AI for that card will be a pain also. Just the 10th edition white cards has first strike, double strike, and protection (yes, Paladin en-Vec seems to be back.)

Mobilization is back, it is an enchantment that gives all your soldiers vigilance and generates new soldier tokens for 2W. Thankfully, Mobilization won’t be hard to program. I have to program aura’s, and I think I can do it, but it will probably have some weird errors that I have to stamp out. Spirit Link is probably the most complicated aura, since it triggers on damage. Heart of Light is an odd aura that prevents all damage to and from the enchanted creature, so it becomes an all-star blocker? I’m not sure. So it prevents combat and regular damage, which might be tricky to program. OK, enough about all that, tell me your comments. --Forge

MTG Forge Overview

Trying to understand a whole programming project is difficult and almost impossible without copious amounts of documentation. I’m going to try to summarize the major classes MTG Forge uses. The Card class mimics the functions of a real card, and the Card class exists in your hand, in play, in the graveyard, and removed from the game. A Card can hold any number of SpellAbility objects. Each Card has at least one SpellAbility and if the Card has an ability like Elvish Piper, the Card will have two SpellAbilities added. One SpellAbility is the default summon creature spell and the other SpellAbility is the activated ability that lets you put a creature from your hand into play.

Every SpellAbility can have an optional mana cost, the only SpellAbilities that don’t have a mana cost are creatures that have tap abilities. Instants and Sorceries also use the SpellAbility class. SpellAbility has a resolve method that is called after it is popped off the stack. The CardFactory class makes new cards and copies existing ones. CardFactory also adds the SpellAbility objects to the Cards, and implements SpellAbility’s resolve method. The resolve method for Wrath of God would destroy everything.

The file “cards.txt” holds simple cards as well as all the text for Instants and Sorceries. This file saved time because I didn’t have to program simple creatures like Glory Seeker. This file also let me specify mana abilities, so creatures like Bird of Paradise are only implemented in cards.txt, no other code is needed. Flying, haste, fear, and vigilance are also supported by cards.txt Some users have added their own cards, which surprised me.

The class that runs everything is Gui_NewGame. It starts up the user interface and initializes everything. Every screen is a separate class and JFrame. These display classes all start with the abbreviation Gui and are only loosely tied together, they all read from Constant.Runtime in order to read which deck you and the computer are using.

I hate not throwing some code at you. J The code below constructs Serra Avenger. AllZone holds all of the zones, in play, graveyard, etc… and can be globally accessed. Global variables are generally bad, but they really helped get MTG Forge working. The computer can play regular creature cards like Serra Avenger using the code below, since it just pays the mana cost and the resolve method works the same for both the human and computer player.

The computer checks the canPlay and canPlayAI methods in SpellAbility to see if it is allowed to play that card. This specific card does not have a canPlayAI method, a card like Hex does, because it targets 6 creatures. The card below belongs in the human player’s deck, since he both controls and owns the card. If this card was going into the computer’s library, the computer would be listed as the owner and controller.


final Card card = new Card();
card.setName("Serra Avenger");
card.setManaCost("W W");

card.setAttack(3);
card.setDefense(3);

card.addKeyword("Flying");
card.addKeyword("Vigilance");

card.setOwner("Human");
card.setController("Human");

SpellAbility spell = new SpellAbility(SpellAbility.Spell, card)
{
public void resolve()
{
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
play.add(card);
}
public boolean canPlay()
{
//Serra Avenger cannot be played for the first three turns of the game
return 3 < AllZone.Phase.getTurn();
}
};
card.addSpellAbility(spell);

Design: Mouse Click

Without this next design concept I would have never been able to get MTG Forge off the ground. Processing user input, usually though the mouse, is critical but complicated. A mouse click can mean that I want to attack, block, play a card, use an ability, select targets for a card, or choose which land to tap. We all innately understand that a mouse click means different things at different times, but how do you program that?

I used the software pattern called State, it is mentioned in the original pattern book “Design Patterns” by Erich Gamma. I’m going to show you some pseudocode that looks similar to Java and then explain it afterwards.

interface Mouse
public void click(Card c);

class MouseState implements Mouse
private Mouse mouse;
public void setMouse(Mouse m) {mouse = m;}
public void click(Card c) {mouse.click(c);}

class MainMouse implements Mouse
public void click(Card c)
{
//If card in hand, try to pay for card
//If card in play, try to play ability
}

class AttackMouse implements Mouse
public void click(Card c) {//add creature to attackers}

class BlockMouse implements Mouse
public void click(Card c) {//add creature to blockers}

OK, we have an interface (or abstract class) that has one method, click(). MouseState just acts like a shell for the 3 other classes.

After this statement MouseState.setMouse(new MainMouse()), MouseState will act like it is a MainMouse object. If MouseState is passed an AttackMouse object, it will act like that object. Basically MouseState morphs into another object. So externally MouseState can act like multiple classes. The beauty of the State pattern is that MouseState doesn’t require a ton of IF statements. I can change the behavior of MouseState without changing the source code of MouseState, can you see the flexibility? The user interface only holds a reference to MouseState, and other classes change the behavior of MouseState as needed. MTG Forge uses Input instead of Mouse and InputControl instead of MouseState.

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.