The 2011 7DRL challenge has come and gone and although I finished my entry Twelve Hours, it wasn't as fun or interesting as I expected. I learned some stuff about programming and design as well as game mechanics and gameplay - and I get to write a postmortem!
I knew from the beginning I wanted you to be one of many heroes but I wasn't sure if you'd be on the same team or rivals or what the story would be. It took a few days to come up with the 'defend the village' story and until then there was a lot of changing my mind and trying little changes and general aimlessness. The final game still feels unfocused and I believe much of that is because I was unsure what the story would be for the first %50 of the time. The survival theme also wasn't as fun as I expected. Once while playing Star Craft I remember a heart pounding, grueling, gut wrenching level that ended with me nervously watching the timer count down as the Zerg swarmed my Terran base, demolishing my major buildings and cutting through my inner defense, rushing toward my heroes as my last marines fell to give me the few seconds I needed to win. Playing Twelve hours should have been more like that, or any decent zombie movie right before the good guys bust in and save everyone, but it ended up with me either relaxing in the town center while the villagers fend off ghosts or with me trying to keep up with the fighter chasing down a few stray skeletons. I tried to emphasize the heroes and working together to save the weaker ones by de-emphasizing the stats and inventory options but I ended up with a game where those aspects just feel weak and only partially implemented.
As far as technical learning, this was the first time I used the A* algorithm (I had always used Dijkstra maps) and even though there's a lot of hard-coded junk in there, it went well and I can add that to my toolbox. Creating separate Creature and CreatureController classes was, I suppose, the textbook-correct thing to do, but I found that they are so tightly coupled that they might as well be rolled into one class. I've played around a bit with that and it seems much better. I also got to try deploying my second Java applet (the example for my AsciiPanel project being the first). I prefer C# but chose Java since it works better cross platform and because I found the C# silverlight api to be unbearable. The deployment didn't go perfectly and it seems like it isn't working now so I made it run as an applet or a standalone app - which was neat. The code itself is exactly what you'd expect from a 7 day project; it's thrown together, cut's corners, and has a lot of hard-coded junk, but it get's the job done and hints at how it could be improved.
The 7DRL challenge is a neat opportunity for a small project with a deadline that allows for creativity, comp-sci algorithms, storytelling, and trying new things with code design and game design. It's bigger than a code kata but small enough that it's still good for learning. I'm not sure if the 7DRL challenge is more for the players, the programmers, or the RL community, but I definitely benefit from it as a developer. A lot of neat ideas get explored and it can be great fun for the developer even if the end product is aimless and feels like it hints at more than it delivers.
Sunday, March 20, 2011
Saturday, March 12, 2011
Twelve Hours, my 2011 7DRL
My 7DRL, initially called Late Start but now called Twelve Hours, is done. You and the other heroes must save the villagers from swarms of undead and survive twelve hours (720 turns). There's 3 different weapons with different properties, 5 different enemies with different abilities, and 6 other heroes with different abilities as well.
Although it's technically complete, and can be entertaining to follow the other heros around, it it often possible to survive by hiding indoors and using villagers as meat-shields while the other heroes bust some undead skulls. That wasn't what I was going for, and you miss seeing what the heroes can do, but I guess that's what the other cowardly villagers are trying to do as well. I seemed to get the best score while protecting the priest or hanging with the samurai.
You can move (arrow keys, numpad, or vi keys) and swap your weapon or armor with what's on the ground ([g] or [,]). You should press [?] when you start to get a better idea of what's going on.
[edit]
It looks like sometimes the applet doesn't work. You can download the jar file and run it on your machine as a standalone app. Technically this was added after the 7 day limit, but it doesn't affect the gameplay.
[/edit]
The other heroes are:
The source is on github at https://github.com/trystan/latestart7DRL.
It uses my previous project AsciiPanel for the old-school graphics.
Although it's technically complete, and can be entertaining to follow the other heros around, it it often possible to survive by hiding indoors and using villagers as meat-shields while the other heroes bust some undead skulls. That wasn't what I was going for, and you miss seeing what the heroes can do, but I guess that's what the other cowardly villagers are trying to do as well. I seemed to get the best score while protecting the priest or hanging with the samurai.
You can move (arrow keys, numpad, or vi keys) and swap your weapon or armor with what's on the ground ([g] or [,]). You should press [?] when you start to get a better idea of what's going on.
[edit]
It looks like sometimes the applet doesn't work. You can download the jar file and run it on your machine as a standalone app. Technically this was added after the 7 day limit, but it doesn't affect the gameplay.
[/edit]
The other heroes are:
- The Fighter has a better weapon and can go into a rage.
- The Monk can attack multiple times per turn and heal himself but doesn't use weapons.
- The Priest can turn undead and heal others.
- The Samurai has a katana and can do a circular attack that hits each adjacent opponent. The Samurai will also commit suicide rather than being turned into a zombie.
- The Slayer starts with a wooden stake that instantly kills vampires and she heals twice as fast as other heroes.
- The Wizard can teleport himself and attack others at a distance.
The source is on github at https://github.com/trystan/latestart7DRL.
It uses my previous project AsciiPanel for the old-school graphics.
Monday, March 7, 2011
late start for 2011 7DRL
I can't believe I almost missed this years 7RDL challenge.
(I removed the applet since the final version is now on http://trystans.blogspot.com/2011/03/twelve-hours-my-2011-7drl.html)
Nothing special; start screen, random scenery, walking around.
It uses my previous project AsciiPanel and the source can be found on github at https://github.com/trystan/latestart7drl.
(I removed the applet since the final version is now on http://trystans.blogspot.com/2011/03/twelve-hours-my-2011-7drl.html)
Nothing special; start screen, random scenery, walking around.
It uses my previous project AsciiPanel and the source can be found on github at https://github.com/trystan/latestart7drl.
Tuesday, March 1, 2011
Programming for fun
Hspec is fun; at least it has been for me. People are watching the github repository, adding issues, making suggestions, forking, and fixing it. I've even gotten a few emails asking about hspec! I've also learned more about BDD, git, github, good (and not so good) design, Haskell idioms, and the Haskell community.
Even if hspec doesn't make me a better programmer or improve what I write, it's reminded me of how exciting and fulfilling programming and collaboration can be.
Even if hspec doesn't make me a better programmer or improve what I write, it's reminded me of how exciting and fulfilling programming and collaboration can be.
Friday, February 11, 2011
Announcing hspec
Announcing hspec - BDD for Haskell
I've long been interested in Behavior Driven Design and it's something that is strangely missing from the Haskell community. QuickCheck is an amazing tool that I use whenever I can, but BDD has it's uses too, even in Haskell.
hspec aims to be simple and there's only three functions that most people will need to use;
hspec aims to be extendable so
hspec aims to be useful and if you'd like an easy to read report then there's two extra functions
We see from the '
I've got some more ideas for it but I think it's simple enough, extendable enough, and useful enough to release to the Haskell community now.
hspec can be found on github at https://github.com/trystan/hspec
I've long been interested in Behavior Driven Design and it's something that is strangely missing from the Haskell community. QuickCheck is an amazing tool that I use whenever I can, but BDD has it's uses too, even in Haskell.
hspec aims to be simple and there's only three functions that most people will need to use;
describe, it, and hspec. Requirements are always about something and describe is how you say what they are about. describe takes the name of something and a list of requirements for it. The requirements themselves have a plain english description of desired behavior and some Haskell code that verifies the behavior is implemented correctly. The function it takes the english description and the verifying code. So describe and it are the two functions that package up something's requirements and verifiers into a list of Specs.
specs :: IO [Spec]
specs = describe "quantify" [
it "makes single quantities singular"
(quantify 1 "thing" == "1 thing"),
it "makes larger quantities plural"
(quantify 2 "thing" == "2 things"),
it "makes zero quantities plural"
(quantify 0 "thing" == "0 things"),
it "treats negative quantities just like positive quantities"
(quantify (-1) "thing" = "-1 thing"),
it "handles words that end with an 'x' (eg box -> boxes)"
(pending "no need for this yet")
]
hspec aims to be extendable so
it is part of a type class, making it easy to add your own verifiers such as hUnit. The currently supported verifiers are an expression that reduces to a Bool, or you can use "pending", or you can use a QuickCheck property. The last requirement's verifier is "pending", representing the fact that this requirement isn't expected to be implemented yet so it doesn't have verifier code. That's all you need right there. Since the Specs are just a Haskell data structure you can evaluate them yourself to see what is successful and what is failing, no need for a test runner.hspec aims to be useful and if you'd like an easy to read report then there's two extra functions
pureHspec and hHspec. pureHspec takes a list of Specs and returns a nicely formatted list of strings documenting each spec and it's status. hHspec takes a handle and IO [Specs] and writes to the handle (usually stdout but it could be a file handle)
>> hHspec stdout specs
quantify
- makes single quantities singular
- makes larger quantities plural
- makes zero quantities plural
x treats negative quantities just like positive quantities
- handles words that end with an 'x' (eg box -> boxes)
# no need for this yet
Finished in 0.00021 seconds
5 examples, 1 failure
We see from the '
-' that most requirements for quantify are met, the one with an 'x' is failing, and the one with a '#' and extra details is pending since there's no need for it yet. We also get a summary of how long it took and how many specs failed.I've got some more ideas for it but I think it's simple enough, extendable enough, and useful enough to release to the Haskell community now.
hspec can be found on github at https://github.com/trystan/hspec
Wednesday, February 9, 2011
Understanding vs NUnit
Can you tell what MysteriousMethod does from looking at these C# unit tests?
Maybe it returns true if the input is even, or a single digit, or if it's not prime, or if it's divisible by 4 or 8, or maybe it only returns true for the number 8, I don't know. All I do know is that it returns true for 8 and false for 11.
Can you tell what mysteriousFunction does from looking at these Haskell QuickCheck properties?
It looks like it returns true if the input is divisible by four and false if it isn't. Pretty clear to me.
I'm working on a Haskell project and it's reminding me why I like QuickCheck so much. Not only does it do a much better job at catching unexpected cases but the meaning and intention of each function is so clear, much clearer than when using xUnit style testing. Going back to C# and NUnit feels weird; you're basically just comparing hard coded values. I find it hard to tell why one value is used instead of something else, i.e. what's essential and what's accidental, especially when the method being tested has several inputs or other variables it relies on. This can lead to bad assumptions and misunderstanding what something actually does. With QuickCheck there's less room for such confusion because the focus is on properties of the values and not the specific values themselves. The clarity of BDD tools (rspec, etc) seems to help, but I wonder what more can be done.
[Test]
public void Test1()
{
var obj = new MysteriousObject();
Assert.IsTrue(obj.MysteriousMethod(8));
}
[Test]
public void Test2()
{
var obj = new MysteriousObject();
Assert.IsFalse(obj.MysteriousMethod(11));
}
Maybe it returns true if the input is even, or a single digit, or if it's not prime, or if it's divisible by 4 or 8, or maybe it only returns true for the number 8, I don't know. All I do know is that it returns true for 8 and false for 11.
Can you tell what mysteriousFunction does from looking at these Haskell QuickCheck properties?
prop_test1 :: Int -> Bool
prop_test1 x = x `mod` 4 == 0 ==> mysteriousFunction x
prop_test2 :: Int -> Bool
prop_test2 x = x `mod` 4 /= 0 ==> not $ mysteriousFunction x
It looks like it returns true if the input is divisible by four and false if it isn't. Pretty clear to me.
I'm working on a Haskell project and it's reminding me why I like QuickCheck so much. Not only does it do a much better job at catching unexpected cases but the meaning and intention of each function is so clear, much clearer than when using xUnit style testing. Going back to C# and NUnit feels weird; you're basically just comparing hard coded values. I find it hard to tell why one value is used instead of something else, i.e. what's essential and what's accidental, especially when the method being tested has several inputs or other variables it relies on. This can lead to bad assumptions and misunderstanding what something actually does. With QuickCheck there's less room for such confusion because the focus is on properties of the values and not the specific values themselves. The clarity of BDD tools (rspec, etc) seems to help, but I wonder what more can be done.
Wednesday, February 2, 2011
Interfaces as object contexts
Here's a possibly useful idea I came up with a few weeks ago. I've come across situations where an object needs different behavior depending on the context or how it's used. Usually the state or strategy design pattern (or a mess of
First make an interface for each set of behavior you want.
Then create the different contexts or scenarios using the interface with the behavior you want.
Then create a class that implements the different behavior.
And now you can use interfaces to select which set of behavior you want your object to use.
I think this is neat. It won't replace state and strategy patterns, but there's probably cases where those aren't possible or where this would work better.
if statements) can handle this, but so can explicit interfaces in C#.First make an interface for each set of behavior you want.
interface IContextA
{
string Property { get; }
int Method(int data);
}
interface IContextB
{
string Property { get; }
int Method(int data);
}Then create the different contexts or scenarios using the interface with the behavior you want.
static void DoStuffInContextA(IContextA example)
{
Console.WriteLine(example.Property);
Console.WriteLine(example.Method(8));
}
static void DoStuffInContextB(IContextB example)
{
Console.WriteLine(example.Property);
Console.WriteLine(example.Method(8));
}Then create a class that implements the different behavior.
class MultiContextObject : IContextA, IContextB
{
string IContextA.Property
{
get { return "called in context a"; }
}
int IContextA.Method(int data)
{
return data + 1;
}
string IContextB.Property
{
get { return "called in context b"; }
}
int IContextB.Method(int data)
{
return data - 1;
}
}And now you can use interfaces to select which set of behavior you want your object to use.
static void ExplicitInterfaceAsContextTest()
{
MultiContextObject example = new MultiContextObject();
DoStuffInContextA(example);
DoStuffInContextB(example);
}I think this is neat. It won't replace state and strategy patterns, but there's probably cases where those aren't possible or where this would work better.
Subscribe to:
Posts (Atom)