From a user's perspective, a program's job is to do work, look good etc. If you want to look into a program, and very deep into it, you will notice that all a program does is to move data around. Data and code are two very important things in programming, because the code does the work, and only with data there is work.
But the borders can become unclear when you think of it. Data and code do both lie on your hard disk, or in memory when a program runs, so where's the difference? Well, code is data that represents some kind of work.
Magic is all about doing work. "You lose one life" is a piece of work. After the work is done, I might have lost the game. Still, I wouldn't like to have that effect around in my code - in my Java code, at least.
Hardcoding and softcoding is all about how work is represented.
Hardcoding essentially means "compiled". For example, the five colors and the card types are hardcoded into laterna - to change one, you need the source files and a Java compiler to generate a runnable program from it once again.
Softcoding, on the other hand, means "interpreted". "You lose one life", which might be a spell's effect, could be written in a text file. That file is read when the program is started, and will do its work, although it never was java code.
So softcoding is the better way to do work in all situations? Clearly no. Enabling something to be softcoded needs massive effort. Not only do you need to parse the file and get the important information from it, you also need to translate it into a form the program can handle. For Magic cards, this is very complex, since the work is done much later than the card is read, And a card might do different things everytime you use it. I'll talk about it in more detail the next time.
Monday, April 26, 2010
Monday, April 19, 2010
Spells
I fear that my blog evolves into a redundant commit log of my project, but I really try to write interesting and original.
First, let me say I'm steadily making progress. Now there's a mana pool and you can play mana abilities. Lands do untap and mana is removed properly. I have Llanowar Elves, my first nonland card, which you can play. It's put on the stack and then resolves. You don't pay costs for it yet...
And that's exactly my topic: What does it take to play a spell? There are several reasons why I started with lands, but the most important is that playing a land is a relatively simple thing: "To play a land, a player puts that land onto the battlefield from the zone it was in".
Playing a land is a single line in a Subpoint of the Subpoint 114.2a of "Special Actions" in the comprehensive rules. "Casting Spells" is Section 601, and 601.2 describes the steps necessary to do so, a to h, each one much longer than 114.2a.
The best known steps for playing spells are choosing targets and paying costs. Targets are a somewhat advanced, and I will rest a little before implementing them, but costs are a really essential part. The hard part is not costs per se, but paying them. Just for paying a mana cost, the player must choose how to pay for hybrid or variable symbols and then choose which mana to use.
Now that I know that playing spells works in general, I can try to fill in the details. When I have costs, and can therefore play simple spells rules-compliant, I might take a break to do some cards, improve the GUI, or implement combat... And swoosh! A game isn't that far away any more!
PS: There's one cost working already: The tap cost ;)
First, let me say I'm steadily making progress. Now there's a mana pool and you can play mana abilities. Lands do untap and mana is removed properly. I have Llanowar Elves, my first nonland card, which you can play. It's put on the stack and then resolves. You don't pay costs for it yet...
And that's exactly my topic: What does it take to play a spell? There are several reasons why I started with lands, but the most important is that playing a land is a relatively simple thing: "To play a land, a player puts that land onto the battlefield from the zone it was in".
Playing a land is a single line in a Subpoint of the Subpoint 114.2a of "Special Actions" in the comprehensive rules. "Casting Spells" is Section 601, and 601.2 describes the steps necessary to do so, a to h, each one much longer than 114.2a.
The best known steps for playing spells are choosing targets and paying costs. Targets are a somewhat advanced, and I will rest a little before implementing them, but costs are a really essential part. The hard part is not costs per se, but paying them. Just for paying a mana cost, the player must choose how to pay for hybrid or variable symbols and then choose which mana to use.
Now that I know that playing spells works in general, I can try to fill in the details. When I have costs, and can therefore play simple spells rules-compliant, I might take a break to do some cards, improve the GUI, or implement combat... And swoosh! A game isn't that far away any more!
PS: There's one cost working already: The tap cost ;)
Sunday, April 11, 2010
Finally...
I'm glad to announce the first "playable" version of Laterna Magica. Features:
The class Player in laterna magica is more or less a data structure and can only perform tasks that don't need decisions - like loosing life, drawing a card or discarding a card at random.
Discarding a chosen card is different, because it needs knowledge, which is something the Player class should not have. Different players make different decisions, and in different ways.
The solution is the Actor class. Whenever some decision is needed, like which card to discard, the actor is asked for that decision. The actor may implement an AI algorithm for choosing, it may get the choice over the network, or, most naturally, depend on user input from the GUI.
Although separating actor from player was a clear choice for me, I don't know how that class will look in the end. Magic is a game with tons of choices - there's not only "discard a card" but also "discard two cards", "discard a creature card", "discard a card of each color"... you know what I mean, this can quickly get messy.
For now, there's basically only "You have priority. What do you want to do?", so that problem is still far away.
And now screenies! When you start the program, you get a GUI with an empty board, like this:

But it doesn't stay for long, it will quickly populate your game with cards. You can watch how your library grows and you draw cards!
After passing priority twice, It will be your main, and you can play one of your (many) Plains:
Looks great? Look at that, during your next turn, you can do it again^^
What you have just seen demonstrates some important things:
- A rather simple GUI
- Drawing a card during your draw step
- Playing Lands
- An opponent who does... nothing
The class Player in laterna magica is more or less a data structure and can only perform tasks that don't need decisions - like loosing life, drawing a card or discarding a card at random.
Discarding a chosen card is different, because it needs knowledge, which is something the Player class should not have. Different players make different decisions, and in different ways.
The solution is the Actor class. Whenever some decision is needed, like which card to discard, the actor is asked for that decision. The actor may implement an AI algorithm for choosing, it may get the choice over the network, or, most naturally, depend on user input from the GUI.
Although separating actor from player was a clear choice for me, I don't know how that class will look in the end. Magic is a game with tons of choices - there's not only "discard a card" but also "discard two cards", "discard a creature card", "discard a card of each color"... you know what I mean, this can quickly get messy.
For now, there's basically only "You have priority. What do you want to do?", so that problem is still far away.
And now screenies! When you start the program, you get a GUI with an empty board, like this:

But it doesn't stay for long, it will quickly populate your game with cards. You can watch how your library grows and you draw cards!
After passing priority twice, It will be your main, and you can play one of your (many) Plains:
Looks great? Look at that, during your next turn, you can do it again^^
What you have just seen demonstrates some important things:
- Laterna Magica has many events. Playing a land doesn't explicitly remove it from the panel. Instead, the panel listens for cards entering/exiting the zone (and later also controller changes) to keep track of what cards are to be shown. When the land is played, the rules engine moves it, and the GUI listens to the rules engine. Similar things are true for the zone size labels, the life total labels and the turn progress label.
- And because swing supports it, the game loop (passing priority back and forth, basically) can run in an extra thread. Whenever the player "freak" has priority, the GameLoop queries the Actor for what to do. That actor is a GuiActor and waits for the user to either click "Pass priority" or a Plains card. Therefore, the GUI thread is not blocked when the game loop is working.
- The GUI has a utility for finding all the actions possible for clicking a card. Currently, there's only playing a land, but it will also check for playing spells or activated abilities, unmorphing etc. in the future. That Utility also checks if the action is legal (timing, only one land per turn etc.)
- what aparently doesn't work is discarding excess cards at end of turn. While it's easy, I've chosen to show you what I have as soon as drawing and playing a land works.
- If you would click yourself all the way through until a library is empty, you would notice that the game doesn't end with that. Although the state based action does exist, the loseGame() method of player is a No-Op.
Tuesday, March 30, 2010
Getting a runnable program
It's a nice thing to get everything right the first time. I think I have many things working already, among them are characteristic-setting effects, replacement effects, skipping turns, taking extra turns, and more than two players.
But what I don't have is a game, which is rather disappointing. Sometimes, I think "okay I want to really try it out, a real-world situation. just add in the glue that's missing and make a simple GUI", and then I realize how much of the glue is missing.
Even though I have effects, I have neither spells nor abilities. I can't play cards or abilities. I have (vanilla) creatures, but no combat. I haven't even tested zones, not to mention the Stack.
Okay, now that I finished whining, let me say that I'm still on this. I'm not giving up. The last few days, I focused on the tight core of magic. Turn order is done, all with phases, steps and priority. Next comes the zones, excluding the Stack for now. When I'm done with that, I'll try to program playing a land, which is drastically different from playing spells or abilities.
This should be enough "core engine" to let me finally play a game. Draw a card, play a land or not, pass priority back and forth until it's the opponent's turn (myself again, no AI yet), and so forth.
Of course, there are needs besides the core engine. Most obviously, a GUI. The other two that come to my mind are decks, which i have started already, and a "controller". LaternaMagica has a Player class, which handles life points and so on, but there's no way yet to to make decisions. But that's another story ;)
Quick follow-up: an important thing I've forgotten for my to-do list is state-based actions. it would be too bad if a player doesn't die when he has no life (however that should happen without creatures or spells...)
But what I don't have is a game, which is rather disappointing. Sometimes, I think "okay I want to really try it out, a real-world situation. just add in the glue that's missing and make a simple GUI", and then I realize how much of the glue is missing.
Even though I have effects, I have neither spells nor abilities. I can't play cards or abilities. I have (vanilla) creatures, but no combat. I haven't even tested zones, not to mention the Stack.
Okay, now that I finished whining, let me say that I'm still on this. I'm not giving up. The last few days, I focused on the tight core of magic. Turn order is done, all with phases, steps and priority. Next comes the zones, excluding the Stack for now. When I'm done with that, I'll try to program playing a land, which is drastically different from playing spells or abilities.
This should be enough "core engine" to let me finally play a game. Draw a card, play a land or not, pass priority back and forth until it's the opponent's turn (myself again, no AI yet), and so forth.
Of course, there are needs besides the core engine. Most obviously, a GUI. The other two that come to my mind are decks, which i have started already, and a "controller". LaternaMagica has a Player class, which handles life points and so on, but there's no way yet to to make decisions. But that's another story ;)
Quick follow-up: an important thing I've forgotten for my to-do list is state-based actions. it would be too bad if a player doesn't die when he has no life (however that should happen without creatures or spells...)
Friday, March 26, 2010
Undo ... and replacement effects!
I finally got time to work on Laterna Magica again. It wasn't that exciting, just adding undo support to all the classes that already existed. While I browsed the classes and looked for actions that I hadn't revised yet, I found the LifeTotal class. Its job is easy: store the number of life points for a player, and whenever it changes, inform listeners.
...except that there are replacement effects. Only because I want to change life points, that doesn't mean it happens! what could be done?
Surprisingly, the solution looks very similar to my undo engine: Every modification is encapsulated into an edit and stored in the engine. If you want to undo something, the undo engine knows what happened, and every single edit knows how to revert itself. For the caller, it's only addListener(myListener), but inside there's new AddListenerEdit(myListener).execute().
Similarly, the replacement engine is the authority for performing replaceable actions, like gaining life. Calling gainLife(amount) really means new LifeEvent(amount).execute().
Execute uses the replacement engine to apply all replacement effects. Those may replace the event with another event, until all are through. The resulting event is then really executed (the implementation method is called execute0).
Edit and ReplaceableEvent are not connected. Both have an execute method, but that's more or less coincidence. Logically, there is a connection. Look at execute0 from LifeEvent:
public void execute0() {
((LifeTotalImpl) getPlayer().getLifeTotal()).fireLifeEvent(this);
}
//...and from LifeTotalImpl
void fireLifeEvent(LifeEvent e) {
CompoundEdit ed = new CompoundEdit(getGame(), true, "Adjust " + getPlayer() + "'s life by "
+ (e.isGained()? "+":"-") + e.getAmount());
new AdjustLifeEdit(e.getChange()).execute();
// Guaranteed to return a non-null array
Object[] l = listeners.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
boolean gain = e.isGained();
for(int i = l.length - 2; i >= 0; i -= 2) {
if(l[i] == LifeListener.class) {
if(gain) ((LifeListener) l[i + 1]).lifeGained(e);
else ((LifeListener) l[i + 1]).lifeLost(e);
}
}
ed.end();
}
Two things are of importance here: First, changing the life is done through an edit, so it can be undone. Second, informing the listeners is encapsulated ina compound edit, so that everything related to this event is grouped together.
I'm quite proud about this piece of code; I expected replacement effects to be very hard, but as it turns out, if there are no flaws in this concept, they should be quite easy.
...except that there are replacement effects. Only because I want to change life points, that doesn't mean it happens! what could be done?
Surprisingly, the solution looks very similar to my undo engine: Every modification is encapsulated into an edit and stored in the engine. If you want to undo something, the undo engine knows what happened, and every single edit knows how to revert itself. For the caller, it's only addListener(myListener), but inside there's new AddListenerEdit(myListener).execute().
Similarly, the replacement engine is the authority for performing replaceable actions, like gaining life. Calling gainLife(amount) really means new LifeEvent(amount).execute().
Execute uses the replacement engine to apply all replacement effects. Those may replace the event with another event, until all are through. The resulting event is then really executed (the implementation method is called execute0).
Edit and ReplaceableEvent are not connected. Both have an execute method, but that's more or less coincidence. Logically, there is a connection. Look at execute0 from LifeEvent:
public void execute0() {
((LifeTotalImpl) getPlayer().getLifeTotal()).fireLifeEvent(this);
}
//...and from LifeTotalImpl
void fireLifeEvent(LifeEvent e) {
CompoundEdit ed = new CompoundEdit(getGame(), true, "Adjust " + getPlayer() + "'s life by "
+ (e.isGained()? "+":"-") + e.getAmount());
new AdjustLifeEdit(e.getChange()).execute();
// Guaranteed to return a non-null array
Object[] l = listeners.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
boolean gain = e.isGained();
for(int i = l.length - 2; i >= 0; i -= 2) {
if(l[i] == LifeListener.class) {
if(gain) ((LifeListener) l[i + 1]).lifeGained(e);
else ((LifeListener) l[i + 1]).lifeLost(e);
}
}
ed.end();
}
Two things are of importance here: First, changing the life is done through an edit, so it can be undone. Second, informing the listeners is encapsulated ina compound edit, so that everything related to this event is grouped together.
I'm quite proud about this piece of code; I expected replacement effects to be very hard, but as it turns out, if there are no flaws in this concept, they should be quite easy.
Wednesday, March 24, 2010
Progress: LaternaCommon
The second thing I've done in the last months is LaternaCommon. It is more or less a collection of useful utilities:
You can use this, too, for free. However, I haven't yet uploaded a jar'ed version of it.
Okay, that's for now, and don't worry, in the last few days I resumed development of the core program!
- It provides a single, application-wide TreeProperties object, loaded from a configurable location
- Configures log4j from a properties file, either configured directly, or by naming a property which stores the location
- Provides an error-handling dialog for uncaught exceptions or to invoke manually
- last but not least, it has a tool that can automatically unzip a file inside the jar. this is very useful to init an application on its first launch.
With this utility, you store a regular zip file inside the jar, and configure a destination directory and a check file on the file system. If the check file is not found on launch, the zip file is unpacked to the specified directory. Any number of zip files can be specified this way.
You can use this, too, for free. However, I haven't yet uploaded a jar'ed version of it.
Okay, that's for now, and don't worry, in the last few days I resumed development of the core program!
Sunday, March 21, 2010
Progress: TreeProperties
I just realized that I didn't post anything for about two months. Well, I did do something, and I want to keep you up.
I have to say that code versioning systems like SVN are a great thing. Besides having a backup on the internet, I'm able to trace back my actions in the last two months. You can look here anytime to see what I was coding recently.
My task was to rework the TreeProperties system that i have come up with sometimes. The point behind it is that you can store properties in simple text files and, for example, implement localization this way. Well, java has something like that built in, but my Library allows much more, the biggest pro being that properties can be split over several files and keys are hierarchical. You can read more about - and download - it here.
Then was a long pause, I took a vacation and went skiing. After that, work went on, and I added many features, fixed bugs etc. In a nutshell, what the library is able to do is:
Next time is about the new sibling of the LaternaMagica project, LaternaCommon, which adds even another level of convenience for the developer in the areas of logging, error handling and deployment.
I have to say that code versioning systems like SVN are a great thing. Besides having a backup on the internet, I'm able to trace back my actions in the last two months. You can look here anytime to see what I was coding recently.
My task was to rework the TreeProperties system that i have come up with sometimes. The point behind it is that you can store properties in simple text files and, for example, implement localization this way. Well, java has something like that built in, but my Library allows much more, the biggest pro being that properties can be split over several files and keys are hierarchical. You can read more about - and download - it here.
Then was a long pause, I took a vacation and went skiing. After that, work went on, and I added many features, fixed bugs etc. In a nutshell, what the library is able to do is:
- Store properties in a tree structure of multiple files
A file can reference another file, which is then parsed in the same way. Thus, any structure of files may be used to realize the needed properties. - Use hierarchical property-keys, like "/lang/en/save"
The namespace mechanism is independent of the file structure, i.e. not bound to a directory structure. When a file is referenced, it inherits its referrer's namespace. For example, if you reference a file with the key "/lang/en", its "save" property has the full key "/lang/en/save". However, if you prefer, you can reference the file with the empty key and literally use "/lang/en/save" in the included file for the same result. - Support for many data types
Unlike the java Properties class, which only supports unparsed string values, TreeProperties supports many data types out of the box - no more casting and parsing when accessing properties! Data types include all of the java primitive types, String, BigInteger and BigDecimal, Date and Calendar, File, Path and URL. The mechanism of data types is easily extendable. It requires implementing one interface and registering the type - that's it!
Special attention goes to the Path type - it adds an abstraction to regular java files that allows to use system properties to be substituted in the path. for example, the values "~/file.txt" or "${user.home}/file.txt", interpreted using the path type, both represent the file "file.txt" in the executing user's home directory. Including files (see above) uses the path type to parse the include, therefore the included file may be personalized for each user, allowing him to set preferences without annoying other users. - Simple access to the API
Most needs don't go beyond reading and writing existing properties. Therefore there's a class for exactly that - it has convenience methods that return the standard types already cast, allows default values, and supports localization. The only reason to read into the API is if you need to store new properties. - Easy GUI
TreeProperties is a background utility that makes a developer's life way easier. However, now and then there's a case when it steps into the foreground. If you want to show a GUI to the user to let him edit the values, don't build it yourself. It's already there! Setting up a GUI with labels and input components to allow the user to edit properties requires a single method call. what you get is a fully layouted swing panel, ready to add to your GUI. The input components are adjusted for the data type - (formatted) text fields, checkboxes and dropdown fields - and initialized to the current value. Changes to the inputs write to the properties object and are therefore available immediately.
Next time is about the new sibling of the LaternaMagica project, LaternaCommon, which adds even another level of convenience for the developer in the areas of logging, error handling and deployment.
Subscribe to:
Posts (Atom)