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.)

5 comments:

Anonymous said...

Just have it concat any numbers it encounters, then cast to int - or whatever your program uses internally.
Numbers normally only appear in this order anyway: (X[0-9]WUBRG)
A better solution that is more future-proof would be for the program to encapsulate each single mana payment in braces (like this: {10}{g}{g}). This allows cards such as the reaper king:
http://magiccards.info/shm/en/260.html

grendel said...

I did not execute the code in a test, but you are probably getting an IndexOutOfBoundsException on "10". You while statement attempts to find the first non-digit. In the case of "10" you do not and you are not testing for the end of the string.

How does this routine handle hybrid symbols? I still think using a pattern matcher is a better mechanism but it depends on your output needs i.e. string vs array of strings.

rising fruition said...

Not counting the IndexOutOfBounds exception that grendel mentions might happen, all the examples will have a trailing space.

This goes to show that the title "A Million Details" is literal, not figurative.

Forge said...

"A Million Details" is literal. I guess any computer program has to do a million things right in order to work. And just like a car engine, a million things can go wrong.

The code doesn't work with "10". I fixed it by adding an "if" somewhere (I forget where). It just goes to show you that parsing (reading from text) is hard.

Rob Cashwalker said...

I would have written the code to remove the symbol characters one at a time (counting each one), leaving the numeric by itself, then it's a simple Val(string) statement.