MTG Forge uses the files common.txt, uncommon.txt, and rare.txt to generate the booster packs that are used for sealed deck and drafting. These files can be edited by the user but since MTG Forge has more than 1,200 cards, the job has become nearly impossible. The good news is that I’m almost done with a program that will let you easily edit these files using a nice user interface.
The set editor is almost done but there have been many obstacles. The set editor reads from the three files previously mentioned as well as from cards.txt. Anytime you read or write files things become very complicated.
There are numerous errors that can occur even when the user is trying to do everything right. For example, maybe there is a card name in uncommon.txt that isn’t in cards.txt. This is a major error because cards.txt holds all of the cards information and every card should be in cards.txt. My program checks all of the card names in common.txt, uncommon.txt, and rare.txt and shows the user an error message that they can understand.
I have to say thanks to Rob Cashwalker for the inspiration, there would be no set editor without him. Hopefully I’ll have the set editor done by Friday.
Wednesday, January 28, 2009
Monday, January 26, 2009
SpellAbility Improvements - Part 3
The most important method in SpellAbility is resolve(), which does the "action" of the card like dealing 2 damage or destroying all creatures but I haven't talked about what really goes into resolve(). The idea behind resolve() is the Command Pattern, "I don't care how you do it, just do it."
So resolve() can have anything in it, but of course the rest of the program has to support it. I have tried programming Time Walk (1U, Take an extra turn after this one), but MTG Forge's phases are messed up and yes they are messed up because me (darn those phases). Odd fact, the pre-alpha Time Walk said "Opponent loses next turn", you can read more here. It was changed because a player thought that his opponent immediately lost game, instead of just losing a turn.
The resolve() of most cards does something like destroying a creature or dealing damage. Many of these actions are done using the GameAction object which handles actions involving a game of Magic like newGame(). (GameAction isn't a great name, but you have to name it something.)
GameAction.destroy(Card c) can destroy any card in play. The Card object itself is dealt damage, Card.addDamage(int n), addDamage() is more correct than "setDamage(int n)" because you are "adding more damage to the card." You must use addDamage() since the card may have other damage.
In retrospect, it probably would have been better to put addDamage() in GameAction in order to let GameAction check for other effects that might trigger when damage is dealt. Some abilities only trigger during combat so GameAction would also need addCombatDamage(int n) as well because combat damage is different than regular damage.
p.s.
"Opening up a working system is more like opening up a human brain and replacing a nerve than opening up a sink and replacing a washer. Would maintenance be easier if it was called Software Brain Surgery?"
--Gerald Weinberg
So resolve() can have anything in it, but of course the rest of the program has to support it. I have tried programming Time Walk (1U, Take an extra turn after this one), but MTG Forge's phases are messed up and yes they are messed up because me (darn those phases). Odd fact, the pre-alpha Time Walk said "Opponent loses next turn", you can read more here. It was changed because a player thought that his opponent immediately lost game, instead of just losing a turn.
The resolve() of most cards does something like destroying a creature or dealing damage. Many of these actions are done using the GameAction object which handles actions involving a game of Magic like newGame(). (GameAction isn't a great name, but you have to name it something.)
GameAction.destroy(Card c) can destroy any card in play. The Card object itself is dealt damage, Card.addDamage(int n), addDamage() is more correct than "setDamage(int n)" because you are "adding more damage to the card." You must use addDamage() since the card may have other damage.
In retrospect, it probably would have been better to put addDamage() in GameAction in order to let GameAction check for other effects that might trigger when damage is dealt. Some abilities only trigger during combat so GameAction would also need addCombatDamage(int n) as well because combat damage is different than regular damage.
p.s.
"Opening up a working system is more like opening up a human brain and replacing a nerve than opening up a sink and replacing a washer. Would maintenance be easier if it was called Software Brain Surgery?"
--Gerald Weinberg
Sunday, January 25, 2009
New Version
Thanks to DennisBergkamp, jpb, Rob Cashwalker, GandoTheBard and all others who contributed. This new version is a result of their hard work.
MTG Forge now has a whopping 1,206 cards and now supports first and double strike. There have been a lot of bugs fixes such as Hex, Crib Swap, Control Magic, Cranial Extraction, Archon of Justice, Defense of the Heart, Howling Mine / Black Vise, Sorceress Queen, and drawing on first turn.
Have fun, I know I will :*)
Download MTG Forge version 1/15
MTG Forge now has a whopping 1,206 cards and now supports first and double strike. There have been a lot of bugs fixes such as Hex, Crib Swap, Control Magic, Cranial Extraction, Archon of Justice, Defense of the Heart, Howling Mine / Black Vise, Sorceress Queen, and drawing on first turn.
Have fun, I know I will :*)
Download MTG Forge version 1/15
Thursday, January 22, 2009
SpellAbility Improvements - Part 2
Ideally SpellAbility could also be used for upkeep triggered abilities like Juzam Djinn (deal 1 damage to you during your upkeep) and damage triggered abilities like Thieving Magpie (draw one card when you deal damage). This would require more methods to be added to SpellAbility.
In order to implement upkeep triggered abilities like Juzam Djinn, SpellAbility could have the method isPhaseTrigger() which returns true or false if the ability should trigger during the current phase. SpellAbility.doPhaseTrigger() would add the SpellAbility object onto the stack and SpellAbility.resolve() could implement the effect of the trigger, such as dealing 1 damage. isPhaseTrigger() could work for any phase but it would be most commonly use during the upkeep phase. (I know up upkeep is technically a step and not a phase but I refer to it as a phase because it is just easier.)
Damage triggered abilities like Thieving Magpie require a little bit more work since damage triggered abilities can trigger when a creature damages another creature or damages a player. damageCard(Card c, int damage) and damagePlayer(String player, int damage) could be added to SpellAbility. And to further complicate things, some cards only trigger on combat damage, so corresponding combat damage methods would also have to be added.
In case you didn't know or forgot, "A triggered ability begins with the word “when,” “whenever,” or “at.”", 404.1 of Magic's comprehensive rules.
p.s.
"The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time." --Tom Cargill
Tuesday, January 20, 2009
SpellAbility Improvements - Part 1
The SpellAbility class represents all spells and abilities in MTG Forge. MTG Forge implements cards as basically just a bunch of SpellAbility objects, the Card class just holds one or more SpellAbility objects.
In order prevent you from playing most abilities while you are holding a card in your hand, SpellAbility has a canPlay() method which returns true if the ability can currently be played. canPlay() answers the question, "Can this SpellAbility be played when this card is in this zone?" SpellAbility also has a mana cost associated with it, since most spells and some abilities require some amount of mana.
There are many areas to improve SpellAbility. First, SpellAbility doesn't handle "tap" and "sacrifice" activated effects very well, they have to be "hacked" by tapping the card using custom code that pays the mana cost. The custom pay mana cost code just taps the card after the mana cost is paid, the same applies to sacrifice effects.
In order to generalize tap and sacrifice, a method like runBeforeStack() needs to be added to SpellAbility. runBeforeStack() would always be executed before the SpellAbility object would be put on the stack. Most of the time runBeforeStack() would be empty, but it would be a way of implements tap and sacrifice effects.
runBeforeStack() could be used to remove counters from a card. canPlay() would check to see if the card had the necessary number of counters in order for the ability to be played in the first place.
This is an example of how these SpellAbility methods work together. First, canPlay() is called, then you pay your mana costs and/or choose your targets, then runBeforeStack() would executed. Then resolve() is called after the spell or ability is resolved off of the stack.
p.s.
Lands are the only cards in MTG Forge that don't have a SpellAbility object. Lands are internally represented as cards with no abilities because all mana abilities are handled using strings instead of SpellAbility objects. In MTG Forge version 2, lands will have a SpellAbility that creates mana which will be either used immediately or added to the mana pool.
p.s.s.
"Programming can be fun, so can cryptography; however they should not be combined". --Kreitzberg and Shneiderman
In order prevent you from playing most abilities while you are holding a card in your hand, SpellAbility has a canPlay() method which returns true if the ability can currently be played. canPlay() answers the question, "Can this SpellAbility be played when this card is in this zone?" SpellAbility also has a mana cost associated with it, since most spells and some abilities require some amount of mana.
There are many areas to improve SpellAbility. First, SpellAbility doesn't handle "tap" and "sacrifice" activated effects very well, they have to be "hacked" by tapping the card using custom code that pays the mana cost. The custom pay mana cost code just taps the card after the mana cost is paid, the same applies to sacrifice effects.
In order to generalize tap and sacrifice, a method like runBeforeStack() needs to be added to SpellAbility. runBeforeStack() would always be executed before the SpellAbility object would be put on the stack. Most of the time runBeforeStack() would be empty, but it would be a way of implements tap and sacrifice effects.
runBeforeStack() could be used to remove counters from a card. canPlay() would check to see if the card had the necessary number of counters in order for the ability to be played in the first place.
This is an example of how these SpellAbility methods work together. First, canPlay() is called, then you pay your mana costs and/or choose your targets, then runBeforeStack() would executed. Then resolve() is called after the spell or ability is resolved off of the stack.
p.s.
Lands are the only cards in MTG Forge that don't have a SpellAbility object. Lands are internally represented as cards with no abilities because all mana abilities are handled using strings instead of SpellAbility objects. In MTG Forge version 2, lands will have a SpellAbility that creates mana which will be either used immediately or added to the mana pool.
p.s.s.
"Programming can be fun, so can cryptography; however they should not be combined". --Kreitzberg and Shneiderman
Thursday, January 15, 2009
Computer vs. Computer
The idea of getting the computer to play itself in order to test answer the question, “Which deck is better?” is very difficult. Even if you could create an AI good enough, what would it matter if one deck always won? If you wanted to play the deck in a tournament you would have to learn how to play the deck yourself, which would be complicated.
I don’t think computer vs. computer games are interesting or even possible. One, you have to program a great AI. I think writing a good AI is possible, but a tournament winner would require the computer to use the sideboard also, which seems impossible to me. Two, you would have to implement all of the cards for a couple of blocks, which is about 1000+ cards and have them all work correctly.
Three, watching the computer play itself is boring. I think everyone would agree that playing Magic is more fun that watching Magic. The idea of the computer playing against itself is theoretically interesting, much like a chess program that plays itself, but in reality it wouldn’t mean much. Tournaments have a decent amount of luck involved even if you are piloting the best deck, since you are paired against other random decks which would increase/decrease your chances of winning that match.
I don’t think computer vs. computer games are interesting or even possible. One, you have to program a great AI. I think writing a good AI is possible, but a tournament winner would require the computer to use the sideboard also, which seems impossible to me. Two, you would have to implement all of the cards for a couple of blocks, which is about 1000+ cards and have them all work correctly.
Three, watching the computer play itself is boring. I think everyone would agree that playing Magic is more fun that watching Magic. The idea of the computer playing against itself is theoretically interesting, much like a chess program that plays itself, but in reality it wouldn’t mean much. Tournaments have a decent amount of luck involved even if you are piloting the best deck, since you are paired against other random decks which would increase/decrease your chances of winning that match.
Tuesday, January 13, 2009
Supertype, Type, and Subtype
Before I started programming I didn't really understand a card's supertype, type, and subtype. Phage the Untouchable is a good example, "Legendary Creature - Zombie Minion". Legendary is a supertype, creature is the type, and zombie minion are both subtypes.
According to Magic's Comprehensive rules 205.4, a card can have multiple supertypes. Basic, snow, world, and legendary are all supertypes. A card's type can be artifact, creature, enchantment, instant, land, planeswalker, sorcery, and tribal, 205.2a. Once again a card may have one or more type, like an "Artifact Creature". Subtypes are easy to spot since they always come after the dash, 205.3.
OK, that previous paragraph is a little boring but what I am trying to say is that you need to keep the supertype, type, and subtype separate in your Magic program. Currently MTG Forge keeps everything grouped together as one big type line, while this makes some operations easy, it is really hard to do other things like adding that stupid little dash.
Most of the time in a game of Magic the type line is transparent and easy to understand. The problem arises when a card like Blood Moon (2R, enchantment, Nonbasic lands are Mountains) changes the type line and then things get confusing. Coat of Arms is another card that looks at the type line and then pumps up your creature if you have any more creatures with the same creature subtype. While Coat of Arms seems like a popular card, it seems a little confusing to program so I haven't tried yet. You can post your hints on how to program this card.
Basically a card's type line is simple until it gets complicated. Part of Magic's appeal has always been creating those weird complications on purpose.
According to Magic's Comprehensive rules 205.4, a card can have multiple supertypes. Basic, snow, world, and legendary are all supertypes. A card's type can be artifact, creature, enchantment, instant, land, planeswalker, sorcery, and tribal, 205.2a. Once again a card may have one or more type, like an "Artifact Creature". Subtypes are easy to spot since they always come after the dash, 205.3.
OK, that previous paragraph is a little boring but what I am trying to say is that you need to keep the supertype, type, and subtype separate in your Magic program. Currently MTG Forge keeps everything grouped together as one big type line, while this makes some operations easy, it is really hard to do other things like adding that stupid little dash.
Most of the time in a game of Magic the type line is transparent and easy to understand. The problem arises when a card like Blood Moon (2R, enchantment, Nonbasic lands are Mountains) changes the type line and then things get confusing. Coat of Arms is another card that looks at the type line and then pumps up your creature if you have any more creatures with the same creature subtype. While Coat of Arms seems like a popular card, it seems a little confusing to program so I haven't tried yet. You can post your hints on how to program this card.
Basically a card's type line is simple until it gets complicated. Part of Magic's appeal has always been creating those weird complications on purpose.
Wednesday, January 7, 2009
Custom Observer Pattern
Sometimes my articles are a balance of both Magic and programming but this one is 99% programming, so you have my blessing to skip this one if your eyes start to glaze over and you think that coding is utterly boring/confusing. (In truth I find that coding is utterly boring/confusing sometimes, lol.)
The observer pattern is very common and it consists of two objects: the observer itself and the object that is being observed. For brevity object A will be the object that we are observing. A brief overview of the observer pattern is that the observer registers itself to A typically through a method like A.addObserver(observer) and when A changes, it notifies all of its observers.
Some of the problems in MTG Forge that occur like the phase upkeep mistakes and drawing too many cards to begin with is because of problems with the observer pattern. Let me give an example to show the difference between the typical observer pattern and my custom observer.
Let's say that objects X,Y,Z are observing A. When A changes X,Y, Z are all notified. The problem is when X, Y, Z is notified and one of those objects change A, so A has to notify X, Y, Z again. For example let us presume that Y changes A. The objects are notified in this order:
X
Y - changes A
Z
X
Y
Z
So you might see my problem, in my way of viewing things X, Y, Z are notified too many times. I only want each object to be notified once when a change occurs:
X
Y - changes A
X
Y
Z
The Java code is almost trivial once I realized what exactly that I wanted the observer pattern to do. This custom observer solves my problem of "observers being called too many times." In essence I am actually trying to remove the recursion that happens when Y changes A.
The observer pattern is very common and it consists of two objects: the observer itself and the object that is being observed. For brevity object A will be the object that we are observing. A brief overview of the observer pattern is that the observer registers itself to A typically through a method like A.addObserver(observer) and when A changes, it notifies all of its observers.
Some of the problems in MTG Forge that occur like the phase upkeep mistakes and drawing too many cards to begin with is because of problems with the observer pattern. Let me give an example to show the difference between the typical observer pattern and my custom observer.
Let's say that objects X,Y,Z are observing A. When A changes X,Y, Z are all notified. The problem is when X, Y, Z is notified and one of those objects change A, so A has to notify X, Y, Z again. For example let us presume that Y changes A. The objects are notified in this order:
X
Y - changes A
Z
X
Y
Z
So you might see my problem, in my way of viewing things X, Y, Z are notified too many times. I only want each object to be notified once when a change occurs:
X
Y - changes A
X
Y
Z
The Java code is almost trivial once I realized what exactly that I wanted the observer pattern to do. This custom observer solves my problem of "observers being called too many times." In essence I am actually trying to remove the recursion that happens when Y changes A.
//shouldContinue is an class (instance) variable
public void updateObservers()
{
shouldContinue = true;
notifyObservers();
shouldContinue = false;
}
//if updateObservers() is called while
//looping through all observers
//the loop stops and then starts over
private void notifyObservers()
{
Observer ob;
for(int i = 0; i < list.size(); i++)
{
ob = (Observer) list.get(i);
if(shouldContinue)
ob.notify(this);
}
}
Monday, January 5, 2009
MTG Forge Architecture
I’m going to discuss the current software architecture that MTG Forge uses. In order to explain some of the quirks I have to remind you MTG Forge was built one piece at a time and I did very little overall architecture planning. I explain various other details in readme-compile.htm that comes in the MTG Forge zip file.
The main classes are GameAction, Card, CardFactory, Input, Phase, and AllZone. GameAction implements such methods as destroy(Card) and newGame(). The Card class has many actions associated with a physical card such as getName() and getAttack(). Since I knew that creating cards was going to be a complicated process, CardFactory’s goal is just to create new Card objects.
Phase’s most important methods are getPhase() and nextPhase(), which advances to the next phase such as Main1 to Declare Attackers. The Input class handles all of the mouse input and uses the State software pattern. AllZone holds all of the zones in Magic such as the player’s hand or the in play zone and is a global variable, public static, because so many different parts of the program need to access the various zones.
Observers also play an important part in MTG Forge. The user interface, gui, observes all of the zones and each zone observes all of the cards in that zone. The Phase object is also observed and whenever Phase.nextPhase() is called, the user interface gets the correct Input class. The Input class for the Main1 phase would let you play any card, while the Input object representing the “Declare Attackers” phase lets you decide which creatures to attack with.
Many of the card drawing problems that MTG Forge has with Black Vise and cards that generate upkeep effects are due to Phase.nextPhase(). To fix these problems in version 2 I have slightly altered the observer code to work in a more predictable way. I’ll try to explain this in more detail next time. :)
The main classes are GameAction, Card, CardFactory, Input, Phase, and AllZone. GameAction implements such methods as destroy(Card) and newGame(). The Card class has many actions associated with a physical card such as getName() and getAttack(). Since I knew that creating cards was going to be a complicated process, CardFactory’s goal is just to create new Card objects.
Phase’s most important methods are getPhase() and nextPhase(), which advances to the next phase such as Main1 to Declare Attackers. The Input class handles all of the mouse input and uses the State software pattern. AllZone holds all of the zones in Magic such as the player’s hand or the in play zone and is a global variable, public static, because so many different parts of the program need to access the various zones.
Observers also play an important part in MTG Forge. The user interface, gui, observes all of the zones and each zone observes all of the cards in that zone. The Phase object is also observed and whenever Phase.nextPhase() is called, the user interface gets the correct Input class. The Input class for the Main1 phase would let you play any card, while the Input object representing the “Declare Attackers” phase lets you decide which creatures to attack with.
Many of the card drawing problems that MTG Forge has with Black Vise and cards that generate upkeep effects are due to Phase.nextPhase(). To fix these problems in version 2 I have slightly altered the observer code to work in a more predictable way. I’ll try to explain this in more detail next time. :)
Sunday, January 4, 2009
I’m Not the Best Programmer
This article has nothing to do with Magic and programming and yet it has everything to do with it. (Warning: this article might change your life, or not, who knows.)
For some reason I have a really big inferiority complex. My mind keeps saying “There are hundreds of better programmers than you that could write Forge a million times better.” And although my mind may be telling the truth, the real truth is that even if there are hundreds (or thousands) of better programmers out there, they haven’t written anything like Forge. (Granted there are a few other Magic projects like Magma and Wagic but I consider Forge to be unique since it also offers draft, sealed, and a gimmicky quest mode.)
The truth is that there are better programmers out there but maybe they are too busy to write something like Forge. Time is always a necessary expense. Maybe there aren’t thousands of better programmers out there that could have written Forge, maybe my mind is making up facts that only sound true but really aren’t. Maybe I’m a really good programmer and don’t know it. There isn’t a universal test that can measure one’s programming talent just like there isn’t a test for business managers or instrumentalists in the symphony.
The truth is that I know Forge isn’t perfect. It even has some huge glaring flaws but that is ok. No computer program is perfect, cough **Word 2003**, **Windows Vista**. The Xbox 360 has a high defect rate and yet the console is very popular. The truth is that something doesn’t have to be perfect in order to be sold or to have fun. I think that Forge is insanely fun.
Every person has to “get over themselves” and say, “I’m good enough to do blank.” Blank might be: write a story, take a stupid test in school, discover uranium, or write an off-the-wall videogame. Forge isn’t perfect and Forge version 2 won’t be perfect either. Nothing in this world is perfect and that is OK, and even normal.
So in closing, all mistakes aren’t mistakes, do something that you think will fail and see if you actually DO or DON’T fail, take a few chances, be yourself, quit trying to guess what other people think of you.
p.s.
On a related note, I loved them movie Kung-Fu Panda. The secret to great kung-fu is nothing but be yourself.
For some reason I have a really big inferiority complex. My mind keeps saying “There are hundreds of better programmers than you that could write Forge a million times better.” And although my mind may be telling the truth, the real truth is that even if there are hundreds (or thousands) of better programmers out there, they haven’t written anything like Forge. (Granted there are a few other Magic projects like Magma and Wagic but I consider Forge to be unique since it also offers draft, sealed, and a gimmicky quest mode.)
The truth is that there are better programmers out there but maybe they are too busy to write something like Forge. Time is always a necessary expense. Maybe there aren’t thousands of better programmers out there that could have written Forge, maybe my mind is making up facts that only sound true but really aren’t. Maybe I’m a really good programmer and don’t know it. There isn’t a universal test that can measure one’s programming talent just like there isn’t a test for business managers or instrumentalists in the symphony.
The truth is that I know Forge isn’t perfect. It even has some huge glaring flaws but that is ok. No computer program is perfect, cough **Word 2003**, **Windows Vista**. The Xbox 360 has a high defect rate and yet the console is very popular. The truth is that something doesn’t have to be perfect in order to be sold or to have fun. I think that Forge is insanely fun.
Every person has to “get over themselves” and say, “I’m good enough to do blank.” Blank might be: write a story, take a stupid test in school, discover uranium, or write an off-the-wall videogame. Forge isn’t perfect and Forge version 2 won’t be perfect either. Nothing in this world is perfect and that is OK, and even normal.
So in closing, all mistakes aren’t mistakes, do something that you think will fail and see if you actually DO or DON’T fail, take a few chances, be yourself, quit trying to guess what other people think of you.
p.s.
On a related note, I loved them movie Kung-Fu Panda. The secret to great kung-fu is nothing but be yourself.
Subscribe to:
Posts (Atom)