Monday, July 21, 2008

Hacky Code

The word “hack” has many definitions like a person that breaks into your computer, a “hacker”. But when the word “hack” is applied to source code is has a different meaning. It means something like “This code could be improved but it works so I’ll change it later if I have the time.” A “hack” is similar to duct tape. Duct tape isn’t the best solution but it is a solution.

Many things in MTG Forge are added by “hacks.” Planeswalkers generally work correctly but if two are in play you cannot choose which one to attack. Planeswalkers are a colossal hack. Reach of Branches is another hack. It should return to your hand when you play a Forest but it doesn’t always work. I can’t really fix Reach of Branches because it was hacked already. MTG Forge does well with activated abilities but many other cards effects are “hacked” on.

MTG Forge uses many hacks and this means that it is time for version 2. Version 2 will hopefully implement cards like Planeswalkers and Reach of Branches without having to hack the code. Version 2 will have a dedicated rules engine just like Magic Online.

I’ll close by showing you some Java source code just in case you have never seen any. This is the code for Ancestral Recall (draw three cards). This card was in the first set of Magic cards ever printed and very few people own an original copy.

The computer will play this spell if he has 5 or less cards in his hand, otherwise the computer would play this it on turn one, effectively wasting it. If the computer has 5 cards in his hand, after Ancestral Recall resolves, he will have 7 cards in hand because 5 – 1 + 3 = 7. Minus one for Ancestral Recall itself. I love cards like Ancestral Recall because it is a great card that is simple to program.
//taken from CardFactory
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());
}//resolve()

public boolean canPlayAI()
{
return AllZone.Computer_Hand.getCards().length <= 5;
}//canPlayAI()
};//SpellAbility

spell.setChooseTargetAI(CardFactoryUtil.AI_targetComputer());

spell.setBeforePayMana(CardFactoryUtil.input_targetPlayer(spell));

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

14 comments:

Anonymous said...

So, basically, you had to "hack" your own game to make it work.

Yeah, that makes sense. LOL!

(even after your explanation, it still sounds funny.)

Gando the Wandering Fool said...

I've thought about this a couple times since I started playing with MTG Forge...Ben Bleiweiss, myself and some other Neutral Grounders came up with an "Engine" of sorts for balancing new magic cards (and seeing if they were worth it) we used rules for all the abilities and "hacked" a few rules for those special abilities that didnt fit nicely. I have been thinking that MTG Forge works similarly and what it really needs is a complete rules engine so that the players can add new cards, make thier own (it is a forge right? lol), or fix old cards they think are wrong or that don't work the way they want.

I really want to fix things in MTG Forge 1.0 so that for instance Mishra's Factory reverts to the bomb it is in the real game instead of just being a so-so land/creature. It would be fun to add in Standing Stones under the same principle...but since the only abilities a player can add to a card are limited to a very short list (11 I think?) this is just a pipe dream...But if your rules engine is thorough and made accessible through some .ini or .txt file included with the program or even just allowed for then such a thought becoems a reality.

I look forward to seeing what you do ...I'd imagine the best way to add rules to cards is have them be totally independent of the cards and attach rules based on specifiers (such as in an .ini or .txt file)
for example:

definition of white knight
string Name = "White Knight";
string Cost = "WW"
string Type = "Creature";
string SubType = "Knight"
int basePow = 2;
int basTou = 2;
int array addedAbility = new Array();
addedAbility[addedAbility.length] = 6;
addedAbility[addedAbility.length] = 12;

(6 = First Strike, 12 = Protection from Black)

I know this psuedo code doesn't really match up to Java but something like that would be easier to code than hack I think.

The above section would be modularized by having the code read values in from a file and plug them in the right places.

it might be like this in the file:

"White Knight", "WW", "Creature", "Knight", 2, 2, 6, 12;

The user would need a list of ability codes but that would be a cynch to do.

The mana might be trickier to code for this to include all the (amazing) dual cost cards and modal cards but you could leave spaces blank in the parser for special cases like that.

Activation costs would be trickiest. Particular with dual mana costs...Id say adding sepparate lines for each mana color would be the simplest way to code that.

This could take some of the burden or inputting cards directly into the program off your shoulders and on to ours.

Anyway I hope this gives you some good things to chew on.

Gando the Wandering Fool said...

on reflection the parser code might be something like this:

public BuildCard (string cName, string cCostColored1, string cCostColored2, string cCostColored3, string cCostColored4, string cCostColored5, int cCostUncolored, string cBaseType, string cSubType, array ArAbilities[])
{
...
//variables assigned here
...
//add abilities section loop based on length of ArAbilities with a function such as getAbility(ArAbilities[index]); being called for each element.
}

Anonymous said...

Hey Gando,

You've described the way Incantus does cards. Actually, activated abilities are pretty straightfoward. The difficult part is delayed triggers, replacement effects, and conditional static abilities ;)

Forge said...

MTG Forge 1.0 is almost just a protoype and I hope that 2.0 will be infinitely more flexible. 2.0 should let users easily add cards (if their are any easy cards that I miss). I just spent 30 minutes programming and debuggin Jugan's ability (when this creature is destroyed, but distribute six +1/+1 counters) I cheated and only let you pump up one creature.

So hopefully version 2.0 will be better in every sense.

Anonymous said...

Hey Gando,
The idea of a configuration file to create the cards is nice, but I have personally been thinking (a lot) about this, and there are several drawbacks to a "too" simple system.

I takes time to code even a simple parser, while it probably doesn't take time to add a card with a simple ability such as "protection from black" directly into the code.
Also, if "protection from black" is identified with the id "12", what id will "protection from goblins" become ? 13 ? Then how about "protection from zombies" ? and so on...
Basically, you can have a very simple parser for very basic abilities, (and that's nice, don't make me say what I didn't say), but when it comes to more complex stuff, you have to use some dedicated scripting language. In the case of "protection from", it should look like "protection:type:zombie" or "protection:color:white", and then that would be parsed by the engine.
I believe that's where cool languages like Python and Ruby come in handy, because you can probably use some simple code directly in the conf file and don't need to write a dedicated parser.
Anyways, if you have to use some scripting language to declare the abilities, most people will give up, and the programmer will be the only one using his system in the end...

@Incantus: How is your game going ?

rising fruition said...

If hack means: “This code could be improved but it works so I’ll change it later if I have the time.”, then 99% of all the code I have ever written is a hack!

When I think hack, I think cutting and pasting (without putting similar code into functions), or making some object global in order to add a feature where you want it.
Hacks do not look elegant. In fact, they usually are quite ugly. Ugly code in the midst of beautiful code is probably a hack. Often, a hack is a new feature that doesn't fit in the original design.

Keep on hacking. :^)

Anonymous said...

@Willow in MTG Forge 1.0 "Protection from black" is impossible to do without fairly difficult hacks to it. It sounds like MTG Forge 2.0 will allow for this to be implemented much more easily.

Anonymous said...

Тока один пиздежь о 2.0 и никуя более

Unknown said...

gando, what you've described, I tried to do. I tried to write a parser which would at least pull costs from the left side of a ":" and an effect keyword, target restrictions, and associated variables from the right side. I'm pretty good at this sort of thing... in VB. Java was just too restrictive in string manipulation and comparison for my style. There were just too many variants of costs and effects and interpreting the variables differently for the different effects. It was looking like more of a hack than the current design, with nearly every card explicitly defined in code. Granted, I tried to do it within the confines of the current v1.x architecture - I thought I could just tweak the code a bit and stick in the variables.

I decided to scrap that for the moment, and focus on an alternative way to achieve a similar effect - generic code for standard effects. There are 100 creatures, with what I'll call Simple Self Pumpers. This would be deemed "Firebreath" by MaRo, but while the ability is predominatly Red, in the "R:+1/+0 Until EOT" form, all the other colors have self-pumping effects, like "W:+0/+1 UEOT" or "B:+1/+1 UEOT", or even "1R:+2/-1 UEOT". Also, creatures that give themselves one of Forge's recognized ability words, like "U:Gains Flying UEOT". These can all be handled by about 400 lines of code that can be easily added to as new cards come out that fit the category. There was already about 500 lines of code to implement just a handful of such cards. This code will replace those, and provide a home for a whole lot more.

I plan to do the same for other simple effects. Like one single handler for all growth effects, one single handler for all regenerators, hell, I think one single handler for all Glorious Anthem effects should be doable.

rising fruition - most of the Forge CardFactory code is exactly that - cut and pasted. Much of the code doesn't even attempt to change the variable names between those similar cards. Like Imperious Perfect has the same Glorious Anthem-inspired variable names. Looking at it, I realized I could consolidate much of the code between these copy/paste "templates" into actual self-contained functions.

As Forge often says, he'd rather make fun new cards than make the code more elegant. Given that I'm not about to try to figure out how to make new effects, or re-work the engine, I want to help tighten up the code and make as many generic cards available as possible.

Gando the Wandering Fool said...

Incantus: Cool beans...I will have too google your project and see how it works.

Willow yes you are correct...and I went for a simple card because I was aware of the inherant difficulty of which you speak. and I like the formula:

ability: type : subtype.

But I think it can not be quite so complicated if the approach is correct.


Rob I appreciate what you are saying and your time spent experimenting with this idea was not wasted I think.

Yes it might make for a longer source but I think it is possible to do this without string manipulation at all. (Beyond some display tricks to make the game nicer to look at etc)

The concept I was going for was kind of separation of data from code. I know its a tough thing to do with some magic cards (Imagine wotc's programmers everytime a new set comes out ~ *giggle*) but it can be done Im sure...Sadly while I have the vision of how it can be done Im not enough of a coder to actually go ahead and write the parser myself.

Also I get that MTGForge 1 is just the working prototype so my myriad of bug reports are probably just uselessly clogging your in box but its a good thing to keep track of what to watch out for in 2.

Anonymous said...

@Rob Cashwalker, don't blame Java for poor string manipulation. Java has regular expression support, so this shouldn't have been a problem.

Forge said...

Gando,

I read everyone's bug reports, mostly because I don't want to make the same mistakes in version 2. Version 1 was "the best that I could do right now." And now that I wrote version 1, I can write an even better version 2.

Electro said...

Forge, I hope you read this. I can be reached via
root: reactive
prefix: electro
suffix: de mail dat google haz.
gmail dot com if you've never used it. And there's a connector.

Basically, I wanted to invert your question for a second... not how would you design an AI for a card game... but how would you design a card game for an AI?


But if your rules engine is thorough and made accessible through some .ini or .txt file included with the program or even just allowed for then such a thought becoems a reality.

... open... source... or at least make the rules engine open source. Or even just 'visible source, okay to modify under my terms' would be okay. You could embed it into a dll and link the whole rules engine, or write it in a scripting language so it's clearly visible.

txt files always have limits, as will xml, if the code is acessible... it's open to everyone.

Yeah, I do want to cannibalize it to see what I can learn when creating my own card game... I want to enable the most complex thought processes of the AI, with the simplest requirements of the player to let them complete without the player being destroyed by the AI.

P.S. My game will never compete with MTG... because
A: I like magic, and
B: I'm not stupid. XD

I also heard that magic is coming back again... I hope it's true... good luck.

Part of the problem you're having is rules interpretation too... this is due to the ambiguos nature of english. Quite annoying, good luck.