Thursday, October 20, 2011

[Inform 7] Complex interaction, events, and rulebooks

Suppose that your game involves people that can be sleeping or awake; and a huge gong, that, when struck, wakes up everyone in the room and all adjacent rooms. This is the most natural way to implement something like that in Inform 7:
Carry out hitting the huge gong:
   say "You hit the huge gong!";
   repeat with guy running through near people:
      now guy is awake.
Where I am, of course, assuming that you have already created a definition for "near", and so on.

This is fine if your game is simple enough that (a) waking people is the only effect of hitting the huge gong, and (b) hitting the huge gong is the only cause of waking all near people. But if you are aiming for a complex world model, this will often not be the case; and even more often, you will not be certain whether it is going to be the case.

Why is this a problem? Because you do not want your code to look like this:
Carry out hitting the huge gong:
   say "You hit the huge gong!";
   repeat with guy running through near people:
      now guy is awake;
   other stuff that happens;
   more stuff that happens.

Carry out pushing the alarm button:
   say "the alarm sounds!";
   repeat with guy running through near people:
      now guy is awake;
   other stuff that happens;
   more stuff that happens.

Every turn when having-a-screaming-fit is true:
   say "You continue screaming.";
   repeat with guy running through near people:
      now guy is awake;
   other stuff that happens;
   more stuff that happens.
Evil code duplication! Maintenance nightmare!

This is obviously better:
Carry out hitting the huge gong:
   say "You hit the huge gong!";
   have a loud noise event.

Carry out pushing the alarm button:
   say "the alarm sounds!";
   have a loud noise event.

Every turn when having-a-screaming-fit is true:
   say "You continue screaming.";
   have a loud noise event.

To have a loud noise event:
   repeat with guy running through near people:
      now guy is awake;
   other stuff that happens;
   more stuff that happens.
But that is still not ideal, because all code that relates to loud noises must be in the same place. If I code up a delicately balanced Cavendish experiment that will be unbalanced by loud noise (and believe me, it will), I want to have the code about the unbalancing right there with all the other code having to do with the Cavendish experiment. I want to be able to see what the experiment does and how it can be influenced at a single glance, not by looking through the entire code.

I would like to suggest that the natural way to think about this is the following. Loud noise is a kind of event. If object A can generate that that kind of event, this should be defined in the section of the code pertaining to object A. If object B is influenced by that kind of event, this should be defined in the section of code pertaining to object B. This gives us the cleanest, most readable code, that is easiest to maintain and extend.

How do we do that in Inform 7? By using rulebooks, of course:
The loud noise rules are a rulebook.

Carry out hitting the huge gong:
   say "You hit the huge gong!";
   consider the loud noise rules.

Carry out pushing the alarm button:
   say "the alarm sounds!";
   consider the loud noise rules.

Every turn when having-a-screaming-fit is true:
   say "You continue screaming.";
   consider the loud noise rules.

A loud noise rule (this is the noise wakes people rule):
   repeat with guy running through near people:
      now guy is awake.

A loud noise rule (this is the noise does other stuff rule):
   other stuff that happens.

A loud noise rule (this is the noise upsets the Cavendish experiment rule):
   upset the Cavendish experiment.
And all these snippets of code can be placed wherever they most naturally belong -- in the parts of the code pertaining to gongs, alarm buttons, screaming fits, sleeping, and the Cavendish experiment respectively.

But of course this means that, knowing that you are building a game with a complex world model, you should have coded that very first gong like this:
The loud noise rules are a rulebook.

Carry out hitting the huge gong:
   say "You hit the huge gong!";
   consider the loud noise rules.

A loud noise rule (this is the noise wakes people rule):
   repeat with guy running through near people:
      now guy is awake.
Which may seem exceedingly wordy at the time, but will pay off in the long run.

Today's lessons, then: learn to love rulebooks even more than you already do! Resist the temptation to implement special cases!

1 comment:

  1. Rulebooks are something that I have to start using more of, for exactly the reasons you state here: when just starting off in the game, it works fine, but when I continue adding more things, I run into problems. This article has actually been really helpful clearing some stuff up. Thanks!

    ReplyDelete