Wednesday, September 10, 2008

Programming Cards using Plaintext 2

I love the plaintext format that I previously talked about. Instead of programming card directly into Java, I just used a text file. This format is great for regular spells like Shock but cards like Cone of Flame that have multiple targets take a little bit more ingenuity.

Cone of Flame has 3 targets, the first target gets 1 damage, the second target gets 2 damage and the third target gets 3 damage.

Cone of Flame
3 R R
Sorcery

Spell
Text: Blah, Blah.
Target: creature or player
Resolve: damage creature or player, 1
Target: creature or player
Resolve: damage creature or player, 2
Target: creature or player
Resolve: damage creature or player, 3

The above description of Cone of Flame is a little long and messy, but the text on the printed card is long also. The challenge with Cone of Flame will be to make sure every target is different.

Orcish Artillery is another great card.

Orcish Artillery
1 R R
Creature Orc Warrior

Activated Ability
Text: tap: Orcish Artillery deals 2 damage to target creature or player and 3 damage to you.
Target: creature or player
Resolve: damage creature or player, 2
Resolve: damage you, 3

Orcish Artillery does two things when its ability resolve, so there are two “Resolve” clauses. Many cards in Magic do effect A and B, so being able to combine effects is very powerful. Aggressive Urge is another great card that does two different things when it resolves.

Aggressive Urge
1 G
Instant

Spell
Text: Target creature gets +1/+1 until end of turn. Draw a card.
Target: creature
Resolve: target creature gets X/X until EOT, 1, 1
Resolve: controller – draw a card

As you may or may not have noticed all of these cards are from 10th Edition. One of my goals for MTG Forge version 2 is to program all of the cards in 10th Edition. And don’t count on me programming Mind Bend. I know it is a “fun little card,” but it does some insane stuff. Mind Bend says, “Change the text of target permanent by replacing all instances of one color word with another or one basic land type with another.” Mind Bend is very complicated and it impresses me that Magic Online can implement this card.

27 comments:

PsudoBuddha said...

Im not for certain, but i believe MGTO use some form of hooking for it, i would say a var reassignable while the card is on the stack.

Anonymous said...

You might want check the text on cone of flame, even if it is long. There's no restriction on your plain text to keep you from popping a x/6 or a single player for 6 damage, while the card requires three discrete targets.

Gando the Wandering Fool said...

I think that the rules interpretation needs to be separate from the card implimentation, mr mous. The fact that 3 targets are indicated in the text of the card should be parsed elsewhere to indicate 3 discrete targets as opposed to 1 since it is a general rule of magic that the same card can not target the same target two or more times (ie: fireball.) I believe there is something in Oracle about this specifically in fact. (Its been a decade+ since I last had to think about this particular peculiarity of the game)

Jesse said...

Gando, I dont think this is true. The reason you cannot select the same target twice with Cone of Flame is because the card specifically says "to target creature or player" then "to another target creature or player" and then "to a third target creature". So the text of this card makes sure these targets are discrete. If "another" and "to a third" were not used you could target the same creature or player multiple times with Cone of Flame.

409.1c If the spell or ability requires any targets, the player first announces how many targets he or she will choose (if the spell or ability has a variable number of targets), then announces his or her choice of an appropriate player, object, or zone for each of those targets. A player can’t play a spell or ability unless he or she chooses the required number of legal targets. The same target can’t be chosen multiple times for any one instance of the word “target” on the spell or ability. If the spell or ability uses the word “target” in multiple places, the same object, player, or zone can be chosen once for each instance of the word “target” (as long as it fits the targeting criteria).
Example: If an ability reads “Tap two target creatures,” then the same target can’t be chosen twice; the ability requires two different legal targets. An ability that reads “Destroy target artifact and target land,” however, can target the same artifact land twice because it uses the word “target” in multiple places.

Gando the Wandering Fool said...

Hmmm judge?!? Just kidding...Though that is not exactly how I remember it (but that rules section is what I was thinking of.)

In anycase I still think the rules interpreter needs to be separate from the cards text. Even though the card gives 3 targets, the use of the "another" keyword implies that the card is targetting 3 discrete targets.

Gando the Wandering Fool said...

woops premature publication there. I meant to add:

So the rules interpreter just needs to parse the card text being passed to it for such keywords. I realize this is abit more sophisticated than mtgforge is thinking about at first but eventually it should be doable. Adding rules interpretation inside the text leads to mixing data with code again. which imho is the wrong approach.

rising fruition said...

I'm sure there are many ways of solving the problems like Cone of Flame. In addition to the 'creature or player' targeting restriction, you might add 'not equal Target 1'. So:

Text: Blah, Blah.
Target 1: creature or player
Resolve: damage Target 1, 1
Target 2: creature or player, not equal Target 1
Resolve: damage Target 2, 2
Target 3: creature or player, not equal Target 1, not equal Target 2
Resolve: damage Target 3, 3

Regarding Mind Bend, it seems that plain text is the IDEAL medium to enable implementing it. Search for all the color words and basic land types in the text, display that list so the user can select one, then display a list colors or land types for the replacement.
I guess this will work well if you use a text representation all the way through to resolution.
There are still some complexities, like if a card is called Green Slime and its text says "Green Slime does 1 damage to target red creature." You would want Mind Bend to be able to replace 'red' but not 'Green' in this case.

As Gando said, ideally the parser would be able to take the card's text (or the Oracle version for it) and work with that directly. But that sounds very complicated. The new cards are certainly better and more carefully worded than in years gone by. That is for a number of reasons. For one, I bet they are considering how things could be implemented in Magic OnLine in the early phases of card development. If I were programming Magic OnLine, I would INSIST on early evaluation of cards!

Incantus said...

Regarding Cone of Flame, the rules are pretty clear. You must select 3 different targets - however, if a card uses the word target 2, you may pick the same card for each target.

The way Incantus does this is a special Target called MultipleTargets, which has code to make sure the player selects 3 separate targets. So MTGForge can do something like this:

Text: Blah, Blah.
MultipleTargets: 3 creature or player
Resolve: damage MultipleTargets 1, 1
Resolve: damage MultipleTargets 2, 1
Resolve: damage MultipleTargets 3, 1

One issue is that as long as at least 1 target is valid, the effect is not countered.

However, I disagree with Gando that you need to keep functionality separate from the card text. This is a declarative approach, and is fine for most simple abilities (that's how i did my initial implementation of card abilities); however, as soon as you get abilities that can refer to previously defined values (for example, a card that deals damage, and then gives you life equivalent to the damage you dealt), requires a basic scripting language. Basically, instead of thinking of magic cards declaratively (the noun approach), you need to think of them functionally (since they are basically verbs - deal damage, add mana, tap creature, etc). I've found that taking a scripting approach has made easy cards slightly more verbose, but difficult cards possible (whereas before they were impossible).

Gando the Wandering Fool said...

Im not sure we are speaking the same language here sometimes. I say code you say functionality. I say interpretation you say something else. All the way since our discussion is hypothetical we can't really find common ground because our words keep flying over each other's heads or yours fly over mine anyway. I THINK that functionality in cards is when you have keywords that the parser can translate to functions plus plain text such as "Resolve: damage MultipleTargets 1, 1"

where Resolve, Damage, and MultipleTargets are all keywords telling the parser the following:

1)(Resolve) this is a function. (so resolve it)
2)(damage) the functionality is to deal damage
3)(MultipleTargets) there is a condition (multiple targets) so set up the appropriate if statements before execution and be sure to get user input where needed.

Is this correct? if so isnt the interpretation going to the parser side not the card text? Please eludidate if I am incorrect.

Gando the Wandering Fool said...

Seriously wish we could edit these posts...Apparently I am mobbed with Typo fairies this afternoon (morning for me since I just got up.)

All the way = Anyway (somehow my fingers translated that)

Eludidate = Elucidate.

MageKing17 said...

The reason Incantus uses a scripting approach is for a number of reasons. Firstly, with the use of decorators, it can be made to look fairly clean, while still being functional.

Secondly, Corrupt is simply not doable in the old version without creating special effects solely for it.

How would you do Corrupt in a "declarative" format?

Incantus said...

Gando,

By functionality, i mean something more than declarative functionality. For cone of flame it is fine, but what kind of syntax do you propose to capture these cards:

Corrupt {5B} |Sorcery| Corrupt deals damage equal to the number of Swamps you control to target creature or player. You gain life equal to the damage dealt this way.

Spoils of War {XB} |Sorcery| X is the number of artifact and/or creature cards in an opponent's graveyard as you play Spoils of War. / Distribute X +1/+1 counters among any number of target creatures.

I venture to guess there is no declarative way, short of defining your own mini scripting language.

Forge said...

At least my blog invites conversation, that was my goal. As with most thing in life, there is more than one way of doing something.

Gando the Wandering Fool said...

The reason Incantus uses a scripting approach is for a number of reasons. Firstly, with the use of decorators, it can be made to look fairly clean, while still being functional.

Secondly, Corrupt is simply not doable in the old version without creating special effects solely for it.

How would you do Corrupt in a "declarative" format?


um 1) I fully support the idea of scripting out as much of the program as is feasible. I did say this several times already. 2)What decorators? You are hiring decorators? Im pretty good with balloons but I hate hanging them up on the walls. And personally I think the walls should be superstar blue. 3) As Incantus points out below: Corrupt is special.

Corrupt:
cost: (5cmc)3BB
type: sorcery
target: 1 player or creature.
EFFECT(1a): DAMAGE target, COUNT SWAMPS (CONTROLLER).
EFFECT(1b): GAIN LIFE, COUNT SWAMPS (CONTROLLER).

Since 1b effect is tied to 1a effect it fails if 1st effect fails.

Spoils of War = if opponent is using artifacts/creatures, you WIN. :P Just kidding.

Spoils of War.
cost: (1+xcmc) XB
type: sorcery
targets: ANY creatures.
effect: PLACE numcounters (+1/+1) among targets.
numcounters: COUNT TOTAL artifacts, creatures IN OPPONENTGRAVE

I realize this makes it less readable but it is still plaintext. Also I know that this is not simple to implement and so might just be a bunch of hooey but I think it is worth discussing.

Incantus said...

Gando,

I hope you don't feel like i'm picking on you. I think we are agreeing on the same thing.

I wish I had had this discussion earlier, and skipped a few months of trying to implement some cards with the declarative approach.

What MageKing means by decorators is just some python syntactic sugar to make it easier to write abilities without having to know too much of the underlying structure.

The reason I brought up Corrupt is that it is a tricky case. The ability says you gain life equal to the damage you dealt. Which means that if the creature has a prevention effect that prevents 2 damage, then you'll only end up gaining # of swamps - 2 life. The problem is, without the ability to define variables and more complicated control structures in your card code, you'll never be able to capture that fact. For example, if you look at firemox's xml code, you'll see they basically tried to write a mini programming language in XML, with loops and conditions and stuff. Now would you want to program in that, or a true programming language?

Gando the Wandering Fool said...

Well obviously only someone entirely unversed in coding would prefer a psuedo language to an actual language given all other factors being equal, however I can see why someone would make a hybrid syntax using xml. I dont agree with that approach but I can see its validity.

Now about the case of Corrupt...correct me if I am wrong but there is a further limitation in that you only gain life = to the lethal damage dealt with Drain Life (and soul burn too.) So yet another case of something tricky. I think you need to be careful not to modify the plain text to the extent that its neither plain to read nor text. If you adding the capability of functions and variables to the DATA portion I think you will run into problems down the road where someone wants to add a card and cant because like now the part of the card functionality they want is obscure to them.

If instead you have special cases in the code that react to keyword declaratives in the card text such as "Protection" that triggers a function to check when said item is targeted whether such a target is legal (because it is protected from the controller ability of the effect) (corrupt vs white knight) or whether it is dealt any damage at all (pestilence activation vs white knight) you get the best of both worlds...modularity (separation of data and code too) and perfect functionality.

The way Im viewing Declaritive Plain Text coding for cards is modeling them after objects in javascript where whatever method/variable you want it is declared as a member of the object and then is globally available to your application through the object. I think I mentioned this briedly in an earlier post.

So you have card data (maybe formed by the user, maybe not) in plain text...and a parser engine (the card builder lets say) and the rules engine which takes what is made in the card builder and applies that to the game being played.

The process of assigning variables starts in the data obviously...continues in the card builder and then ends in the rules engine which is the final arbiter of what use that information is given.

Im rambling a little because Im tired but I think my idea here is fairly robust if wordy.

And no I dont feel picked upon...Im used to being wrong :P or thought wrong at anyrate. Everyone in my family is knowledgable, has an opinion and knows better than anyone else about everything. :D... I recognize my lack of expertise here but think I have something to contribute nonetheless.

Incantus said...

Well obviously only someone entirely unversed in coding would prefer a psuedo language to an actual language given all other factors being equal,

I disagree. XML code is difficult to read and follow, where as a simple language with loops and variables would be much easier to read and write. For example, here is Corrupt in firemox:

http://pastebin.com/m48feafc7

And here is Corrupt in Incantus:

http://pastebin.com/m680ba67a

I bet you a non programmer would be able to understand (and write) the scripting language version. (Note - the version in firemox is incorrect as well, since it doesn't account for the amount of damage you actually deal)

I think what you propose is exactly what Incantus does, and is only possible in a dynamic language. For example, almost all the actions you would want to take are defined in the Player class (such as player.add_mana, player.choose_from_zone, etc) and the Permanent class (card.move_to, card.add_counters), which can be called from the card code.

I think the way to do this in MTGForge is to take the same approach, figuring out the atomic actions necessary to build any card, and then exporting them to a java based scripting language (such as groovy - http://groovy.codehaus.org/, or even python on java - http://www.jython.org/Project/).

Incantus said...

Sorry, i forgot my links wouldn't be escaped automatically. Here is Corrupt in firemox and Corrupt in Incantus

Anonymous said...

Hey Incantus, how do you handle the costs versus the effects? How are they separated? The spell must go on the stack after the costs have been paid but before the effects are "executed".. so there must be some kind of separation.

I'm guessing it has something to do with those two yields in the method but I'd like some more explanation on this method :) How can you "stop"/"pause" the execution of the "effects" method *after* the costs have been paid but *before* the effects are executed?

Incantus said...

Ah, that's the magic of what are called generators in python (which are similar to coroutines in other languages, although not as powerful). Here's a good intro.

But basically, the yield statement allows you to yield back to the caller of the function (sort of like return), but the next time the function is called it resumes at the most recent point where it yielded. One common use of a generator is simulate an infinite sequence, without actually having to generate it all. For example, you can write an infinite counter:

def count():
i = 0
while True:
yield i
i += 1

As you can see, the loop never terminates, but the program won't run forever (unless the caller of count never stops calling it)

So back to Incantus. When an ability/spell is played, the effect function is called, a cost is yielded back to the rules engine, then the function is called again, passing back the payment, and then the effects function yields the Target object and gets back the actual target selected. Finally, when the ability resolves, the effects function is called, resuming at the second yield call.

One major benefit of this approach is that all important objects (the cost payment, the target) are local to the function and can be referred to later in the effects code.

Fran├žois said...

Yeah it's a pretty clever idea :) So every effect has exactly one "payment" and one "target"?

Gando the Wandering Fool said...

"I bet you a non programmer"

Im not sure Id characterize myself as such...more a demiprogrammer who is very rusty and not necessarily familiar with Java...I did program quite extensively in C/C++ when I was younger and I do write complex serverside aps in ASP/Javascript (crazy as that sounds).

Incantus said...

Not exactly. You can yield back multiple cost objects and target objects. For example here's Grazing Kelpie with a cost and a sacrifice cost. Here is an instance of Arena (Arena|Land|3, T: Tap target creature you control and target creature of an opponent's choice he or she controls. Each of those creatures deals damage equal to its power to the other.)

Incantus said...

Oh, gando, i hope you didn't read that statement as saying I thought you were a non-programmer.

What i was saying is that I bet that a non-programmer would be able to understand the non-XML code better

Gando the Wandering Fool said...

Ah yes I can see my personal parser (read: my brain) was partially offline when I read that.:P)

The thing is ... a psuedolanguage has to deal with severe limitations. Though I actually like using XML implementations for magic projects because that furthers the goals of the OMD project which I was a part of for a while in the early 00s. (Yes I made the lousy website and help pages :P)

The plus of having more people involved is definitely a big one but when it comes to defining rules that work consistantly it might not be possible to keep it in XML. Also our chief programmer (rares) might find hooking into an XMLbased psuedo language ontop of what hes needing to do already a bit of a challenge.

Also we need to keep the DATA part as dumbed down as possible so that people who are complete nonprogrammers and who really just want to play with the cards and not spend hours writing data files can participate and enjoy the game. Well thats my perspective anyway... perhaps a combination of ideas is in order?

Anonymous said...

But there is always 3 yield statements in each effect right (so that your "main" code knows what the method is returning from the next() call)? It's just that those yield can return a collection of objects?

Also, do you have a way to enforce this structure for all effects? If someone forgets to return a payment, will he knows at compile-time? (I guess not since python is interpreted from what I know).

Incantus said...

Since this conversation is going off topic from MTGForge, I'm moving this discussion to a new topic in the Magic Rules Engine Programming forum on slightly magic.`