Thursday, May 31, 2012

Reminder: SOLID roguelike code is good roguelike code

I have the strong suspicion that roguelike worldgen code tends to be very procedural. Even with object oriented languages, worldgen code has a natural tendency to turn into a large procedural class with procedural methods modifying procedural data in a very procedural way: little if any polymorphism and little if any interaction among objects with single well-defined responsibilities and behavior. But I recently refactored some worldgen code of mine and it's now several small classes that interact to build a world. It's of course much easier and safer to extend too. Here's how you too can turn procedural worldgen code into SOLID worlgen code.

The core idea is taken from an excellent post by the most excellent Mark Seemann where he introduces the concept of a Customization to his project AutoFixture. Here's the interface for a customization to a fixture:

public interface ICustomization
{
  void Customize(IFixture fixture);
}

And here's how it's used:

var fixture = new Fixture()
  .Customize(new DomainCustomization());

Neat. The ICustomization basically represents an arbitrary change to the default behavior of a fixture. This is very much like worldgen code - if you look at it a certain way. Start with a blank map, add a bunch of customizations, and you end up with an interesting world. One customization makes a lake, another adds a river, another adds a mountain, and on and on and on.

I took this idea and created a similar actionscript interface:

public interface Feature
{
  function addTo(grid:WorldGrid):void;
}

The various world features - lakes, rivers, towns, caves, fields, forests, etc - are each represented by a single class that implements Feature. Most are just a few lines but some are more complex and actually encapsulate logic that is only relevant to that feature. Creating a world with these features is also straightforward:

var grid:WorldGrid = new WorldGrid(width, height);
grid.add(new Shore());
grid.add(new Lake());
grid.add(new Lake());
grid.add(new Island());
// etc.

I wasn't sure if restructuring things like this would do much good or not but I'm convinced it has. Being able to add new features without touching existing code is such a great feeling that I'm trying to find more places to try out this ICustomization idea. What if customizations could change the overall world parameters like temperature or creature spawn rate? Could the core gameplay itself be customized and so that new features and subsystems like identification or poison be added just as easily?

None of this is new or difficult or all that remarkable on it's own. "Something that represents arbitrary changes" is a simple idea that won't revolutionize the roguelike world or the programming industry. You don't even have to learn any new acronym or framework or principle or anything else really. It's more like a reminder - which I need from time to time - of what happens when you consistently apply the Extract Class refactoring: good clean code.

Friday, May 25, 2012

First Flash Roguelike step

After all the trouble I had with getting my Java 7DRL applet to run in the browser, I've decided to check out Flash for my next rogulike. I've used flash before so I figured it would be easy enough and after getting the AsciiPanel.as repo on github, I was able to play around a bit. Here's version 0.1 of my next roguelike project which will be the successor to I rule, you rule, we all rule old-school Hyrule.


Some things to note:

  • The overworld is completely connected - you won't start in a place that's blocked since every tile is connected to every other tile.
  • There's underworld dungeons. They're only one room right now but it's a start.
  • I have some ideas on how to improve on the original game while still keeping the spirit the same.
  • The current working title is much shorter.
  • Once I clean up the worldgen code a bit more, I'll put the source on Github and add it to the RogueBasin.

Saturday, May 19, 2012

Revolutions and revelations in how I program

I've been a professional software engineer for about 6 years now but I've been programming on my own for a few years more than that. Although nearly all of my opinions have changed, and often changed back again, there are a few changes that really stand out.

Polymorphism for different choices. One of the earliest things I noticed was that any method I wrote tended to either make a decision or do something. This newly forming idea that each class and method should do only one thing led me to the habit of classifying methods as "doers" and "deciders". I don't remember where I first saw it - probably on the original wiki at c2.com - but seeing switch statements and if statements replaced by polymorphism really blew my mind. For a long time I tried to do everything I could with subclassing and implementing interfaces just to see what was possible. My code slowly began to be broken into objects that actually did things ("doers"), objects that decided what implementations would be used to do things ("deciders"), and objects that controlled an overall algorithm or flow ("managers") with not a single enum or switch statement in sight. Using polymorphism for conditionals makes code much easier to understand and abstracts unnecessary details.

Design patterns for familiarity. At first I resisted design patterns since none of them actually did anything. I had already seen code where only a small portion actually did work and the majority was excess plumbing and was dead-set on not writing anything like that. It took a while before I realized that the design patterns don't help with solving the domain problems; they help with solving the code problems. They help you identify and structure the pieces of your code that need to work together - they weren't useless baggage at all. In fact I had already stumbled upon the Strategy pattern (my "doers"), the Abstract Factory (my "deciders") and the Template pattern (similar to my "managers"). I've never found a need for some of them (Flyweight? Prototype? Memento?) but I began to see that my code was already doing the things some of the patterns were doing. By extracting those parts into their own classes that followed the pattern names and structures, my code was much cleaner and self-evident. This also broke me away from the naive view that a class represents a real-world noun and realize that a class actually represents a well defined and clearly named role or responsibility. Preferably a single responsibility that is familiar. Design patterns make common responsibilities and structures clear and easy to understand.

Immutability and higher order functions for safety and conciseness. Haskell has taught me a lot of really cool things like monads, monoids, functors, the beauty of concise language, and how to get the most out of the type system. Unfortunately the languages I use on a regular basis are no where near as concise or powerful. Fortunately all modern languages support some variation of higher order functions and immutability. Until Haskell I had never seriously thought about mutating state. After Haskell I became obsessed with it and came to the same conclusions as just about everyone else: eliminating mutable state eliminates many bugs and eliminating accessibility of mutable state eliminates many bugs. Concise code, meaning code that does one and only one thing with minimal boilerplate, is also easier to read, understand, and fit in your head - meaning even fewer places for bugs to hide.

Constructor injection for explicit dependancies. I used to have the constructor set up all the dependencies, wrongly thinking that's what everyone means by information hiding and designing for reuse. Just new-up an instance and start using it: you don't even have to know what it's using behind the scenes. How easy to use! How nice and thoughtful of me! At least until you need it to behave slightly different or need to use it in a slightly different context. Then you have problems. The kind of problems that are only solved by rewriting existing code - a bad and dangerous thing. But by making dependencies explicit you know what's going on. By making dependencies part of the constructor, you can change the behavior to suit the context without rewriting. And by thinking about dependencies as its own concept, your objects become much easier to use. Whether you use an IOC container or just use poor-man's dependency injection, identifying dependancies and making them explicit makes for code that has fewer surprising side effects and is easier to extend.

Refactoring for working with legacy code. I was aware of refactoring but wasn't sure how to apply it to most of the code that I had to work on in a day-to-day environment. Beyond the occasional Rename or Extract Method I didn't really do any refactoring; like design patterns it seemed to often be extra work that didn't actually do anything. Reading Working Effectively With Legacy Code changed that. I had an entire new arsenal of techniques to break dependencies, shrink giant classes, increase test coverage, clarify what the system actually does, and make the code just a little more pleasant to work with. Working Effectively With Legacy Code, and refactoring in general, is great enabler that allows you to bridge the gap between what happened to be and what could be.

Events and messages for decoupled systems. Until I really started using domain events, it seemed like creating a decoupled system was just a lofty goal that you could possibly approach but never get close to. I was wrong. Dependency injection had taught me about about dependencies and how to make them clear but I wasn't sure how to really get rid of them. This is how. With an event system you can remove dependancies between collaborating classes and add functionality without modifying existing code. Not only that, but all the public methods and properties that managed collaboration disappear and your classes expose just their own domain behavior. I still run into issues with sequencing or scenarios where I'm not sure how to do something with events from time to time but simple events and simple pub/sub drastically simplifies and decouples the domain objects and overall architecture.


I could write a post, or probably a small series, on each of these (or link to a hundred other sites). There have been other things that have helped me become a better developer (TDD, DRY, SOLID, debugging tricks, SICP, communicating with others, really listening to users, etc) but these are the ones that really shook things up and moved me in a new direction. What has changed how you program? What are you currently looking into? What do think your next big change will be?


Wednesday, May 2, 2012

Review of a review of iryrwarosh

I recently watched the awesome review of my 7DRL iryrwaroshr by the awesome TheUberHunter. Here's my thoughts (or, Review of a Review of a 7DRL).

He started by pronouncing my name right (which is an achievement in and of itself), then wrong (or at least not how I pronounce it).

@1:04 "Oh Wow! That's kind of cool. (hums zelda theme)" Yes! Total victory for me. If someone starts out thinking it's cool and get's what the overall theme is, then I'm off to a good start.

@1:40 "hiding on the text". Yeah, my bad. The text should move so that it doesn't obscure your view. The "Fame" leaderboard does it so the messages should too. Again: my bad.

@1:55 "serious damage.... more than meets the eye" combat messages need to explain the weapons better. And it needs to be more obvious that goblins use weapons.

@2:40 "I have to really point that thing when I use it" Welcome to Old-School Hyrule! That's one of the things from the original Zelda. It kind of sucks.

@4:20 evasion is mostly based on your immediate surroundings: the more places you can move in to the higher your evasion is. When you evade an attack you not only avoid all damage but you actually move into an adjacent space at random. Other creatures have an evasion percent and certain items affect it. Not at all obvious and rarely important - It's confusing and probably should have been dropped.

@4:40 treasure and fame? Fame is important - It's how you win!

@5:14 "twilight zone time" One of my favorite little details. When you start a new game the screen sort of fades in but you are visible the entire time so you can clearly see where you are. I tend to cram a lot onto the screen so this helps focus on your stating location. Games that have a field of view don't have this problem since you are at the center of what's visible on the screen.

@6:12 evasion and auto-attack. Open space is good for some weapons and a closed space is better for others.

@6:30 "I don't know how first aid kits work" You need 5 rupees - that's too much. Or there's not enough rupees in the game.

@7:19 The exclamation point is an evasion potion. It should be more clear what it does.

@7:20 It took far too long to check the help. Maybe explain immediately? Help should also say what each of the items are.

@7:40 Press space to examine. Unfortunately the examine square is in the center of the screen and not where you are. This is because of some architectural constraints so that's pretty lame. Good design helps make a good user experience - it doesn't hinder it.

@8:40 Yes! TheUberHunter lost because someone else got 100% fame before he did. Victory for me! I'm glad the AI was able to win a game.

@10:05 "whatever you say dude". Many of the auto attacks like impaling on spikes aren't very clear. Part of it is because of the message/event architecture I tried using made it difficult to do some things in a specific order. It's also unclear because it affects things nearby and everything is moving so you can die adjacent to a monster that then moves two spaces away (if is has the DOUBLE_MOVE trait).

@10:20 Once again, there's a horrible lack of rupees throughout so the spells are too expensive.

@11:40 poison for 3 turns == totally weak

@12:25 "does the armor do anything? .... no.... Good to know. Good to know.". No active ability, fail on my part since everything should have an active and passive ability.

@12:56 Evasion potions are still unclear....

@13:04 Help doesn't help with items. It really should - especially the evasion potions.

@14:20 "Dork! I will Dork you in the Dork! Dork! Behold Dork!" Lol.

@15:45 The desert does have too many monsters. The reason is that each region starts with the same number of monsters. Since the desert is only 2x2, it ends up very densely populated. An oddand unintended consequence.

@16:28 rupee machine -> "I learned nothing so far!". But it does make rupees over time! You just died so quickly!

@17:05 "I was hoping for auto attack's to be more happenin'... but that doesn't really seem to be the case". Yeah; me too. It turned out to be a bit underwhelming. Maybe 100% chance would have been better.

@18:30 "a pretty good game anyway" Yay! A few things didn't work out very well but I'm glad that overall, it's not a bad experience.

@19:30 "I guess we could play one more" Hahahahahahahaha! I WIN!

@19:54 You only lasted 18 turns. With as much randomness as this game has you can end up in situations where you die quickly. You can always flee but there should be something to make sure you don't start in such a bad spot.

@20:50 "surprising, but doesn't work properly", auto attack and evade are based on chance. It is hard to know when it will happen and that should have been made more apparent.

@20:55 "a lot to learn" Ha! I really went overboard with the items and subtle points of evasion and auto-atacking. Next year will be much clearer - but also much more stuff....

It was really funny and interesting watching someone else play. It was good to see the "wtf?" moments as well as the "aha!" moments. I knew the auto attack and evasion would be unclear and that there are far too few rupees. I also figured that most people would play aggressively the first few times and die quickly. Once you see that you win by fame and not killing goblins and regular monsters, you play much more cautiously. I wonder if he noticed the flowing water, or the lava, or the different colored trees. The graphics are probably the best thing I did but maybe they're one of those things that don't even get noticed when they're done right. I don't think he knew that goblins have a random weapon or that the monsters had random traits that you can see by examining them with the space bar.

Anyway; it's great that TheUberHunter is taking so much time to play these roguelikes and anyone who made a 7DRL or is thinking of making a game should watch him play some.