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.

Friday, April 6, 2012

Interesting Perlin noise caves

I'm not a big fan of using Perlin noise to generate random terrain - mostly because the results are uninteresting and you have so little control over it. However; I came up with a use for using Perlin noise to generate interesting caves that I haven't seen before.

Step 1: Generate a map of solid rock. Use Perlin noise (or whatever) to make it of varying density.
Step 2: Place points of interest in the rock.
Step 3: Use a pathfinding algorithm to connect those points using the rock density as movement cost.

This will create a a map that is guaranteed to connect all the interesting points and the paths between them will wind, curve, connect, and split trying to follow the path of least density between the points. By varying the size of the path finder or removing low-density tiles near the path, the caves can be made to look more interesting.

Here's an example that connects 9 points on a map that is 120 by 40 tiles.




And here is a view that shows the density of the rock, the lighter it is the more dense it is and the less likely a cave will go through it.




There you go, caves that twist and wind and widen and narrow from point A to point B.

Wednesday, March 28, 2012

Common uses for the State pattern

Of all the design patterns, I think the state pattern is one of the simplest and probably the one I use the most. It's easy to understand and easy to use but I still see many places where it could be used but isn't. Here's a few things that indicate when using state objects would be simpler.

If you have a variable that represents what 'mode' or 'state' something is in, or what the current behavior is, then you may want to use the state pattern. A common place for this is if you have a gui and the user can interact with one of many tools. Instead of:

public void onClick(int x, int y){
  if (currentTool == PAINT)
    canvas.paint(x, y);
  else if (currentTool == BLUR)
    canvas.blur(x, y);
  else if (currentTool == ERASE)
    canvas.erase(x, y);
  else if (currentTool == LINE && lineStart == null)
    lineStart = new Point(x, y);
  else if (currentTool == LINE && lineStart != null) {
    canvas.drawLine(lineStart.x, linesStart.y, x, y);
    lineStart = null;
  }
}

you could refactor to the state pattern:

public void onClick(int x, int y){
  currentTool.onClick(x, y);
}

class PaintTool extends GuiTool {
  @Override
  public void onClick(Canvas canvas, int x, int y){
    canvas.paint(x,y);
  }
}

This means that new tools could be added without having to change the gui code. The state pattern would also simplify the gui since it no longer needs to determine what clicking means. This is especially beneficial when multi-click operations are involved since the gui no longer needs extra variables to track that.

class LineTool implements GuiTool {

  private Point startPoint;

  @Override
  public void onClick(Canvas canvas, int x, int y){
    if (startPoint == null)
      startPoint = new Point(x, y);
    else {
      canvas.drawLine(startPoint.x, startPoint.y, x,y);
  }
}

That brings to another common case where the state pattern is useful: If you have variables that are used in some situations and ignored in others, then you may want to use the state pattern instead. For example, if you have a game or simulation where each agent either has a fixed home, another agent it follows, a group it considers "home", then instead of:

public void goHome(){
  if (homeLocation != null)
    goTo(homeLocation.x, homeLocation.y);
  else if (leader != null)
    follow(leader);
  else if (group != null)
    follow(group.getRandomMember());
  else
    wander();
}

you could refactor to the state pattern:

public void goHome(){
  home.goTo();
}

public class StationaryHome implements AgentHome {
  private Agent agent;
  private Point home;

  public StationaryHome(Agent agent, Point home){
    this.agent = agent;
    this.home = home;
  }

  @Override
  public void goTo(){
    agent.goTo(home);
  }
}

Once you have the different states, then the agent no longer needs all of those extra variables and is made much simpler. It also disallows states that don't make sense: what does it mean if an agent has a location it considers home and a group it considers home? If that's not a valid situation then you need to make sure you never end up with both. If it is a valid situation then you can just create a new state object that handles it.

You may have noticed something about these two examples: If you have a bunch of if/if else statements that compare variables to literal values (like enums!), then you may want to use the state pattern instead. Complex objects with complex logic can be greatly simplified by using the state pattern. In fact; it's possible to completely remove all branching and delegate behavior to multiple different state objects instead.

Since these scenarios are all cases where we want different behavior based on some runtime values, they're good candidates for the state pattern. So keep an eye out for chances to use the state pattern.

Monday, March 19, 2012

I rule, you rule, we all rule old-school Hyrule retrospective part 1

So the 2012 7DRLC has come and gone and I've finished my third and best entry. I've looked at some posts and announcements that others have made but have only tried a couple other entries so far. Without getting "official" feedback from this years judges and without comparing my entry to the others, I have to ask myself: what went right? What went wrong? What did I learn? And what can I do differently?

Right. I had a plan and stuck with it when it worked and dropped it when it didn't. I got a zeldalike overworld that was aesthetically pleasing and fairly interesting. I got a bunch of different items to use, most of which have a passive and active use. I also have fairly intelligent rivals that really push you forward and made the game much more fun. The random monsters were sometimes fun and I really liked the camouflage and calling for help. The code - while a bit on the spaghetti side - was mostly decent and did use events. In the end I had a game that was somewhat fun with a clear indication of progress and you could win by cautious exploration, reckless combat, or an opportunistic combination of both. I also had someone playtest and spot so many spelling errors.

Wrong. The overworld sometimes had inaccessible places. It's rare and not game breaking, unless you start on a tiny area and can't escape, but it happens. The evasion thing was pretty uninteresting and some of the items aren't as useful or interesting as others. I tried to make all items equally interesting and useful but that didn't work out as well as I thought. I ditched many of my ideas because they were just not fun. Sometimes the monsters were too overpowering or just uninteresting. The game was often to short to really get into it. I also had a hard time deploying as an applet and eventually just provided a download link. That really sucked since a big part of choosing java was being able to play it in the browser. The biggest problem was that I also spent the middle portion of my week flubbing around and spastically tweaking things left and right without a clear plan. It was chaotic and I moved sideways as much as I moved forward, if that makes any sense.

Learned. Worldgen shouldn't be all or nothing - if I could make one "room" or "screen", check it, and then accept or reject it on its own then I could avoid having inaccessible sections and unintentional dead ends. I don't think I could have done the items much better since I originally had detailed plans that just turned out to be uninteresting. That's not to say they couldn't be done better but I think that's one thing that you just have to get right through iteration and hands on playtesting. My weakness was probably the items and item balancing and I probably should have stuck with the the best parts: worldgen and rivals. Not that it matters that much - the game is so short that I often see only a small portion of it before the game ends. I think that the more randomness a game has and the more content it has to explore, the more chances you need to overcome the randomness and the more time you need to explore. I'll have to make a post on randomness and the number of choices required to rise above being slave to the RNG.

Next time. I tend to go overboard with my ideas; like having a zelda overworld, random monsters, evasion, 2 item max, rivals, and fame percent all in one seven day project. Although it's great having different options to explore, the items were probably my weakness. I should either focus on that and practice making item-based roguelikes or ditch it for the next 7DRL. The events were also a bit of a mixed blessing. When should I use events and when should I code things inline? It was also difficult to control the order of when messages appeared and deal with situations where one action would have similar events published and logged. I'll try dropping the event based thing and rely on explicitly sequenced overrides and callbacks as an alternative. Yeah, it's tightly coupled, but I also have the control I need. I also need to have specific goals for each session: worldgen, rivals, polish, etc and not have generic "content". That's how I end up with a bunch of items most people will never see and even fewer will like. Lastly, java isn't as cross platform and easy to deploy as I thought. I'll have to look more into Flash for my next roguelike.

So, have a plan but ditch it when it doess't work and focus on your strengths since your strong points will make your project a success and your weak points will, at best, be a waste of time, and, at worse, may overshadow your strengths. Also, if you can, have someone else playtest it. Once I get more feedback from the fine people at the roguetemple and other folks who review my game, I'll come up with a retrospective part 2.