Wednesday, April 29, 2009

Room for Improvement

Even though I consider MTG Forge to be an outstanding success, there are many areas that could be improved. For example, when an error occurs it is not displayed to the user. This wasn't done on purpose and is more of an accident. The error is displayed on the "command prompt" which is very visible when I am programming but is usually hidden when the user runs the program. Ideally the error message should be written to a file and displayed to the user. If MTG Forge had an Internet connection, the error message could be automatically forwarded to the developer (me).

Furthermore, MTG Forge stores all of the card information in one method. Yes, the guts of all 1,500 cards is stuffed into one very long method, I use nested brackets to give each card its own namespace. This was done as a quick-and-dirty way to create new card objects. A better solution would have been to put each card in a separate file. If these card files were read at runtime, adding new cards would be easy versus compiling the whole project.

Currently MTG Forge stores all of the deck files in one master file. While this scheme simplifies some of the programming, it makes life more difficult for the user. Typically users make new decks and tweak preexisting ones and every time the user upgrades to a newer version, they have to save their "master deck file". I think it would be better if the decks were stored in separate files.

The deck files should also be plaintext in order to allow the user to easily cut-and-paste the deck contents when posting the deck to a forum. While MTG Forge doesn't support separate deck files, individual decks can be imported and exported, a binary file and a text file of the deck's contents is created. (You can view decks that people have posted here.)

The user interface could also be improved and although MTG Forge version 2.0 is still being built, it uses an abstract user interface. This will easily let allow another "front-end" to be written. MTG Forge's "rules engine" handles all of the mouse input and menus, so the user interface is only responsible for displaying life points and cards.

p.s.
Best Island ever, the Japanese get all the good card art, lol.

p.s.s.
The simplest videogame libraries I have found for Java are Fly, Ucigame, and GTGE (the link is currently down but it used to work). LWJGL seems popular but I don't understand it. LWJGL seems to require a working knowledge of OpenGL.

//example of nested brackets from CardFactory
getCard(String cardName)
{

{
//create Wrath of God
}

{
//create Giant Growth
}

{
//create Eager Cadet
}

}//getCard()

Monday, April 27, 2009

New Version

I’m happy to announce another new version of MTG Forge with 100 additional cards. All of the credit goes to DennisBergkamp and to the great guys on the forum.

MTG Forge 04-24: Download

The major updates include shroud and protection. I never thought MTG Forge would support these keywords because large portions of the code had to be replaced and updated. Akroma, Angel of Wrath now is fully functional. At first she just had haste and flying, which still made her very impressive. Later, first strike was added and now she has protection as well. You can now use old favorites like White Knight and Black Knight.

This update also supports hybrid mana which is very cool. The previous version of MTG Forge added wither but now since hybrid mana is supported, many more wither cards have been added. Other interesting cards include Divinity of Pride which has the converted mana cost of 5 and can be paid with either white or black mana. Divinity of Pride has flying and lifelink and supports a hefty 4/4 body which gets a +4/+4 bonus if you have 25 life or more. You can see other new cards by looking at the beginning of the file “cards.txt” which is where new cards are added.

- Added the ability to view removed cards from both the player's and computer's removed from game zone.
- New keywords: Protection from {color}, Shroud and Flanking.
- Hybrid mana is now supported, thank you rares Chris gave me the fixes in cards.txt to currently existing hybrid mana fakers in MTGForge.
- Rhys the Redeemed works now without resorting to hacky code (and he should trigger cards like Quirion Dryad now).
- Updated some cards in cards.txt that had protection / shroud data missing (Simic Sky Swallower, both Akroma Legends, White Shield Crusader, ... , to name a few).
- Added some AI code for Scarblade Elite (hopefully its AI side will actually work now?).
- Faceless Butcher, Oblivion Ring and Oubliette should actually return the removed cards back to play again.
- Fixed Seizan, Perverter of Truth.
- Fixed Flame Spirit.
- Fixed Wild Nacatl.
- Fixed Meng Huo, Barbarian King.
- Fixed creatures with Fear being able to get blocked by face down morphs.
- Fixed Wydwen the Biting Gale (I think?).
- Nicol Bolas AI (first ability) should be fixed.
- Fixed being unable to assign damage to multiple blockers when using the old non-resizable GUI.
- Fixed Kiki-Jiki, Mirror Breaker targeting facedown creatures, and those creatures turning face up.
- Fixed Anaba Shaman text.
- Merged a bunch of Rob's changes which removes warnings for the later java versions.
- Fixed a big bug where during the "Combat damage is on the stack" phase if a creature that has assigned damage to another creature leaves play, the damage would not resolve.
- Fixed Keening Banshee (will give target -2/-2 until EOT instead of dealing 2 damage to it).
- Fixed Man-o'-War where if the only creature on the human's side leaves play in response to Man-o'-War coming into play, it will target itself instead of null.
- Stonecloaker should be fixed.
- Fixed Timberwatch Elf (and other cards that use input_targetCreature_NoCost_TapAbility), they should not be able to target creatures with protection from {color} / shroud.
- Fixed AI devour: AI should devour any 1/1, 1/2 and 0/2 creature it finds (I should probably include a general method somewhere that returns all valuable creatures to exclude... (Prodigal Sorcerer, Rhys the Redeemed, Elvish Piper, etc.))
- Fixed Lifelink not triggering during planeswalker combat for non-first strikers.
- Fixed Wilt-Leaf Cavaliers text.
- Fixed Divinity of Pride.
- Fixed some issues with cards that trigger when damaging a player and trample (Spawnwrithe, Slith Predator).
- AI shouldn't use Rhys' second ability unless there are 2 or more tokens in play.
- AI should be able to use Elvish Farmer / Mycologist sack saproling for life ability now (will do it when his life < 6).
- A 1/5 wall blocking a Deft Duelist should actually destroy the Deft Duelist (this is a more general bug fix of course, before this, attacking with a first striker would not receive combat damage if it was blocked with a non-first striker provided the attacking creature was controlled by the (human) player).
- Fixed Rolling Stones, all walls can attack now, it used to only affect the walls of the controlling player.
- Quest mode will start with 6 extra red commons, and because I'm just so damn generous 1 extra red uncommon Anyway, I hope this makes red more playable in quest mode...

New Cards

A lot, too many to list, but here's a few of the coolest:
Divinity of Pride, Cold-Eyed Selkie, Scion of Oona, Blastoderm / Calciderm, Eladamri, Lord of Leaves, Crystalline Sliver, Sidewinder Sliver, Nimble Mongoose, White Knight, Black Knight, Argothian Enchantress.

Main stuff to test would obviously be the protection from and shroud stuff. There's a lot of code that had to be updated all over the place... so I'm sure there will be a bunch of new bugs.

Wednesday, April 22, 2009

A Million Details - Part 2

This is a continuation of my previous post.

I've changed my mind and I have decided that having spaces in the mana cost is completely ugly. I want "1GG" not "1 G G". (MTG Forge currently requires mana costs to have spaces because it is easier to decode.) I want to keep all of my beautifully written mana code, so last night I wrote the addSpaces() method, which converts "1GG" into "1 G G" so the existing code will still work. The user won't know that internally MTG Forge uses spaces in the mana cost because I will remove the spaces when I show the mana cost to the user.

The addSpaces() method is not complicated and consists of only eight lines but it still very useful. I wasn't sure of the exact logic, so I wrote a method and kept tweaking it until it worked. (This "discovery" process is good for short methods but I wouldn't use it for anything bigger than a class or two. Even I have to pseudocode the hard stuff in order to make sure it works.)

The addSpaces() method is one of the million bits of code in MTG Forge. Another code tidbit checks to see if a creature has enough damage and should be put into the graveyard. While it is only six lines, it is still very important, the same goes for the code that untaps everything. The goal is to write it and then forget about it because the code works so well. When I'm working on another part of MTG Forge I don't want to think about addSpaces() or the untap code, I just want it to work.

As a bonus you can see the Java code that converts a card name into a URL so you can download the picture from wizards.com. (I stole this code from another Magic project, Nate's Deckbuilder.)

//Java code
//converts "1GG" to "1 G G"
String addSpaces(String manaCost)
{
String out = "";

int i = 0;

while(Character.isDigit(manaCost.charAt(i)))
i++;

out = manaCost.substring(0, i) +" ";

for( ; i < manaCost.length(); i++)
out += manaCost.charAt(i) +" ";

return out.trim();
}

//URL to download card pictures from Wizards
String name = "Elvish Piper".replace(", ", "_").replaceAll("[ -]", "_").replaceAll("[':\"!]", "").toLowerCase();

String url = "http://mi.wizards.com/global/images/magic/general/" + name + ".jpg";

p.s.
addSpaces() still isn't quite right, can you spot the error? Which one of these test cases breaks addSpaces, "G", "GG", "10", "10GGG"? For a hint see Stratadon. Even short code is hard to get 100% correct. (Wizards has updated Gatherer for better or worse.)

Monday, April 20, 2009

A Million Details - Part 1

MTG Forge is made up of a million little bits of code. Magic has a huge number of details and you have to tackle each one without being overwhelmed. How will the user interface show damage and assigned combat damage? How will decks be stored, plain text or some other format? How will players assign combat damage to multiple blockers? How do you make a decent deck editor? Which dialog boxes are required during a typical game of Magic? (Dialog boxes are those things that pop up and say, "Are you sure you want to quit?")

Obviously Magic is very big so you have break it down into very small parts in order to translate it into specific steps that you can code. MTG Forge currently requires mana costs to have a space, such as "1 G G" and "W W". I made this decision because it was easier to decode the mana cost since it was already broken into pieces. Having the computer decode "1 G G" is easier than "1GG".

Requiring all mana costs to have spaces isn't a very big decision but it is a decision that affects the whole project. I could have spent a little more time on the code so the computer could decode "1GG" but I made a decision and stuck with it.

p.s.
If you want a small programming challenge write your own method to convert "1GG" into "1 G G". Other good test cases include "G", "GG", "10" and "10GGG". You can compare your code to mine on Wednesday.

Friday, April 17, 2009

Wizards shuts down Magic-League.com

Wizards of the Coast has sent a cease and desist notice to magic-league.com which held online Apprentice and Magic Workstation tournaments. I’m a little disappointed but not surprised. The main issue in the letter is this:

"Magic-League" name and "magic-league.com" domain name further evidences your bad faith intent to capitalize on the good will associated with Wizards’ MAGIC: THE GATHERING® trademark.


Below is taken from this much longer post.

Hello to all of you,

This news message is to inform all of you about impending changes that are forced upon us by the legal representatives of wizards of the coast.
Last friday we received an e-mail from wizards of the coast (wich I will paste after this news message) demanding we stop using wizard's trademarks and copyrights on this site. Also it is demanded from us to stop promoting and linking to apprentice and mws.

To us this means that we cannot continue magic-league in it's current form and name. Therefor, as of friday the judges panel will be deactivated, so it will not be possible to run tournaments of any form for the moment. The rated single matches will stay since you can play that in any form, and we don't have any influence over that.

The clipping of the league will be temporary and as we speak we are discussing different solutions on how to continue our on-line activities in a form that doesnot infringe on any of the copyrights as mentioned in the C&D mail.

Following is the mail as we received it friday (minus the adresses, since hatemail is not appreciated)

Re: Infringement of Wizards of the Coast LLC’s
MAGIC: THE GATHERING® Copyrights and Trademarks

Dear Mr. Moerman:
We are counsel for Wizards of the Coast LLC (“Wizards”), the owner of the copyrights and trademarks for the MAGIC: THE GATHERING® trading card game, including the MAGIC: THE GATHERING ONLINE® version of the game. It recently came to our attention that your group has created a website, www.magic-league.com, that touts itself as the place for “free online Magic: The Gathering” tournament and casual play, using two unauthorized computer programs: Apprenctice and Magic Workstation. These software programs use text and, in some cases, artwork, from Wizards’ MAGIC: THE GATHERING® cards. Your site also makes available free MAGIC: THE GATHERING® cards. Your use of the “Magic-League” name and “magic-league.com” domain name further evidences your bad faith intent to capitalize on the good will associated with Wizards’ MAGIC: THE GATHERING® trademark, and pass your site off as authorized or associated with Wizards’ MAGIC: THE GATHERING® game.

etc...

Wednesday, April 15, 2009

Phases in MTG Forge

Magic’s phases are simple to understand but complicated to program (much like combat and other parts of Magic.) MTG Forge implements the phases as a huge array since the phases always progress in the same order. In order to makes progressing to the next phase as simple as possible, the array also includes each place that a user can "stop and play a card" which is called priority.

Let me show you a part of the array and then explain it in more detail.
String phaseArray[][] = 
{
{"Human", "Human", "Untap"},

{"Human", "Human", "Upkeep"},
{"Human", "Computer", "Upkeep"},

{"Human", "Human", "Draw"},
{"Human", "Computer", "Draw"},

{"Human", "Human", "Main1"},
{"Human", "Computer", "Main1"},
}
This is a two-dimensional array that holds whose turn is it (active player), the player who has priority and the name of the phase. The line
{"Human", "Computer", "Upkeep"}
means that the active player is the human and the computer currently has priority during the Upkeep.

This array is part of the Phase object. The most important methods of Phase is getPhase(), getActivePlayer(), getPriorityPlayer(), and nextPhase(). From the example above, getActivePlayer() would return "Human" and getPriorityPlayer() would return "Computer". nextPhase() is used throughout the program to advance to the next phase. Progressing from one phase to the next is very easy, all you have to do is to increment the array index. If there are no attacking creatures those phases are skipped by calling nextPhase().

The Phase object works well but MTG Forge’s phase problem seems to be in the code that observes Phase. I love the observer pattern and use it throughout MTG Forge but sometimes it causes me trouble if I have too many observers that also change the object that they are observing.

For example objects A, B, C are observing Z and when B is notified of a change from Z it modifies Z. (B only modifies Z once every 24 hours so there isn't an infinite loop.) If Z changes, A, B, C are called twice, once when Z was first changed and then later when B changes Z. And to further complicate the issue, A and C may be called before B changed Z or after. I’ve tried to make this example as simple as possible but it is still hard to understand. Basically the problem is that when Z changes, A,B,C are each called twice, once for the initial change of Z and then again when B changes Z.

The problem is when you have multiple observers and one of the observers changes the object that it is observing, then the other observers don't work correctly. If you know of a solution for this, please let me know.

I’ve come up with a solution that I call an "ordered observer". From the example above when Z changes, the observers would be notified in this order A, B, A, B, C instead of A, B, C, A, B, C thus reducing the number of times that C is called. You can download the full Java code here.

p.s.
You can also read more about programming Magic's phases here. What other topics would you like to read about?

class OrderedObserver
{
private boolean shouldContinue;

void updateObservers()
{
shouldContinue = true;
notifyObservers();
shouldContinue = false;
}

//if updateObservers() is called while looping through all observers
//the loop stops and then starts over
void notifyObservers()
{
OrderedObserver ob;
for(int i = 0; i < list.size(); i++)
{
ob = (OrderedObserver) list.get(i);
if(shouldContinue)
ob.notify(this);
}
}//notifyObservers()

}//end class
Magic’s phases (some of these are technically steps)
Untap
Upkeep
Draw
1st Main
Begin Combat
Declare Attackers
After Declare Attackers
Declare Blockers
After Declare Blockers
Combat Damage
End of Combat
2nd Main
End of Turn
Cleanup

Monday, April 13, 2009

Plaintext Creatures and Cards.txt

If you don’t know what cards.txt is about, let me tell you. Cards.txt has a long listing of creature like this:

Raging Goblin
R
Creature Goblin Berserker
1/1
Haste

Simple creatures can be added to MTG Forge just by adding them to cards.txt which is read when the program starts up (runtime). At first cards.txt was just a simple way to save me some coding, versus coding all these creatures in Java, but cards.txt has evolved to support a number of regular abilities like haste and flying, as well as other abilities like cycling and “comes into play tapped.”

Cards.txt has become one of the most important parts of MTG Forge. Cards.txt has been expanded to include spells like Shock and other spells that damage creatures or players. Cards.txt supports a large number of “regular keywords” like regenerate, flying, defender, reach, first strike, double strike, flash, fear, landwalk, wither and indestructible.

Cards.txt also recognizes mana abilities. This is the text for Forest:

Forest
no cost
Basic Land Forest
tap: add G

Creatures can also have mana abilities like Llanowar Elves.

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

And the mother of all mana creatures, Birds of Paradise, can be added by using cards.txt

Birds of Paradise
G
Creature Bird
0/1
tap: add W
tap: add B
tap: add U
tap: add R
tap: add G
Flying

Plain text is very powerful and should be used as often as possible. (I prefer plain text over XML but your tastes may vary.)

Words that cards.txt recognizes

Comes into play tapped.
Cycling:{mana cost}
Defender
Devour:{mangitude}
Double Strike
Fear
First Strike
Flash
Flying
Forestwalk
Haste
Indestructible
Islandwalk
KPump {mana cost}:{keyword}
Morph:{manaCost}
Mountainwalk
Plainswalk
PTPump {mana cost}:{+-p}/{+-t}
PTKPump {mana cost}:{+-p}/{+-t}/{keyword}
Reach
RegenerateMe:{mana cost}
Remove three spore counters from this card: Put a 1/1 green Saproling creature token into play.
SearchMerc:{manacost}
SearchRebel:{manacost}
Shadow
spDamageCP:{damage}
spDamageP:{damage}
Swampwalk
tap: add 1
tap: add B
tap: add G
tap: add R
tap: add U
tap: add W
TgtKPump {mana/tap cost}:{keyword}
This creature can block as though it had flying.
This creature can block only creatures with flying.
This creature cannot block
Trample
Unblockable
Vigilance
Whenever a creature dealt damage by this card this turn is put into a graveyard, put a +1/+1 counter on this card.
Wither

Thursday, April 9, 2009

What Magic Programs Do You Use?

This will be just a quick post. What Magic programs do you use? I'm sure many people have used Magic Online. How popular is Magic Workstation? And have you ever used Incantus or Firemox?

Incantus looks great and here is a screenshot with many abilities on the stack.

Wednesday, April 8, 2009

Post your questions and topics

I'm not sure what to write about next, so feel free to post your questions and topics. It seems like I've covered everything even though I know that isn't true. I'm slowly working on version 2.0 and how the cards will (hopefully) be just plain text instead of hardcoded into Java.







Royal Assassin

Type: Creature - Human Assassin
Cost: 1BB
Resolve: summon creature, 2/2

Type: Activated Ability
Text: tap: Destroy target tapped creature.
Cost: tap
Target: creature - tapped
Resolve: destroy creature

Monday, April 6, 2009

Documentation

Writing documentation is the unwanted, dark side of programming, lol. Everybody knows documentation is good but no one wants to write it. There are two types of documentation. External documentation is for the user of the program and answers questions about how to use the program. Internal documentation is for another programmer and explains how the program works so it can be modified.

I’ve been working on the external docs for users, you can view it here. The readme contents are listed below. I presume 95% of the people that download MTG Forge already know how to play the game of Magic. For the other 5% I give a link to the demo and the rulebook.

MTG Forge as practically no internally documentation. I have put together all of my blog posting about how to program cards and named them “Readme – compile.html” but it only covers the basics. I wouldn’t mind working on UML charts and the like, trying to explain the internals of MTG Forge but I’ve never seen good internal documentation. Using Javadocs would be a good start since it generates easy to read html pages.

MTG Forge Readme

Quick Answers:
Quest Mode
Constructed and Sealed
Generate Deck
Booster Draft
Deck Editor – Import and Export
New Deck – Sealed
New Deck – Generate Random Constructed CardPool
Smooth AI Land
Resizable Game Area

FAQ:
What is MTG Forge?
Which file do I run?
Help, I don't know how to play Magic: The Gathering!!!
How do I download the card pictures?
How do you target a player?
Where are the other phases?
How do I choose which cards to add?
Why doesn't MTG Forge have my favorite card?
Where is the mana pool?
Why doesn't MTG Forge support a whole set or block?
Why does the user interface look weird?
Can I remove cards that don't work correctly?
What data files does MTG Forge use?
How do I install a newer version?
Why are there so many questData files?
Can I change the computer's quest decks?
How does the AI work?
Can the AI play Instants or Counterspell?
Can you play another person on the Internet?
General Thoughts

Wednesday, April 1, 2009

Questing Fun

I love MTG Forge’s new quest mode. You start out with a very limited card pool and you win more cards. Sometimes your initial card pool is very strong and I have won my next 10 matches without changing my deck. Although other times your original card pool is very weak and you have to cobble together a 3 or 4 color deck and hope to draw the correct mana.

The quest mode is great because it forces you to use cards that you normally wouldn’t use. I won a few games using Guiltfeeder which causes damaging according the number of cards in your opponent’s graveyard. In a way he is an overpriced at 4B for a 0/4 with fear, but against certain decks he is great.

When questing, you also really appreciate a 2 and 3 drop that has an extra ability. I had a great time stomping the computer with Shivan Wurm, a card that I have never used before. (A 7/7 trampler that costs 3RG but you have to return a red or green creature to your hand.)

The quest mode is also great because you have memorable game experiences. Just last night I won a game where the computer was up to 50 life points and I beat him down. Granted the computer should have won, but he didn’t, and I don’t feel bad for him. It was a great thrill to struggle against overwhelming odds against a more powerful deck and still win.

While the quest mode isn’t perfect and there could be more balancing issues, it is still great fun. (Maybe in the future I will add a “card shop” so you can buy cards, but then I also have to add money. And I can’t decide should the card shop sell singles or boosters or what. Do I really want to put a price on each card or should I just price cards by rarity?)

p.s.
The card art is a Japanese promo.