Coding Puzzles

Recently on the intfiction forum someone asked me how to code puzzles in I7. I found that a bit of a stumper, but I cobbled something together, and he liked the answer enough that he suggested I post the reply more permanently on my blog. So I’m doing that, with a little bit of editing and fleshing out.

There are some tutorials out there on designing puzzles — like, working out what makes a good or bad one. But tutorials for coding puzzles? Not exactly, because that’s too broad a question: a puzzle could involve anything in the world model. So it’s hard to give advice about “how to code a puzzle” without knowing what the puzzle is that you want to code.

But let me try to break it down a little conceptually.

Typically in IF a puzzle involves something that the player wants to get, reach, or achieve — some action he wants to do — but can’t, until he’s fulfilled some previous requirement. So it might be a door he can’t open, a room he can’t go into, a trophy he can’t take, a woman he can’t dance with. But all these things are tied up with some action that the player might type into the game, for which he should get refusals until he tries under the correct circumstances.

That means that to code the puzzle, we need to keep track of some information:

— has the player fulfilled the requirement for doing the puzzle-solving action? E.g., is he carrying the key that opens the door? Is the tall lady carrying the rose that the player is supposed to give her before dancing with her? That sort of thing.

— has the puzzle already been solved once? (If we’re keeping score, we need to know this for scoring purposes so that we don’t give the player more points each time he opens the same locked door, for instance.) We might keep track of this with a special attribute — like “The tall lady can be ready to dance or finished dancing.” and then set that attribute from “ready to dance” to “finished dancing” when the player manages to dance with her.

Now we need action rules. We need to put a restriction on the action the player is trying to do, until the requirement is fulfilled. In Inform, that might mean writing rules like

Instead of dancing with the tall lady when the tall lady does not carry the rose:
say "The lady doesn't seem at all interested. Perhaps you'll need to get her attention with a token of your admiration first."

And of course we also need a rule to say what happens when the player succeeds:

Instead of dancing with the tall lady when the tall lady carries the rose:
if the tall lady is ready to dance:
say "She is delighted to dance with you!";
award five points.
now the tall lady is finished dancing;
otherwise:
say "She has already danced enough for the evening."

Notice that this rule will only occur when the player has achieved the prerequisite (handing over the rose somehow); then it will check to see whether the puzzle is previously unsolved (if she’s “ready to dance”). If so, it awards the points and describes the success case, and marks the lady as “finished dancing”. The next time the player tries the same thing, we’ll instead get the refusal message (“She has already danced enough…”) and no more points will be awarded in the future.

There are many much more complicated ways to code puzzles depending on the world model features involved, but it usually comes back to the same concepts:

— check whether the player is *allowed* to do the action that completes the puzzle
— check whether this is the first time he’s doing so, if you’re tracking points or have a puzzle that can’t be repeated
— if this is a valid solution attempt, print the message describing success and give whatever rewards the player has earned (such as raised score, new objects to play with, etc)
— if it’s not a valid attempt, print a message that gives the player an indication of why things failed
— (if appropriate) flag that the puzzle is done.

Something the original post doesn’t go into is the difference between solutions that trigger on a player’s action (which is most of them, hence the simplification) and solutions that trigger on world state.

What does that mean? Well, one way to detect the solution to a puzzle is to do what this post describes, and have a rule that happens when the player tries an action that should succeed because he’s done the right prerequisite actions.

But sometimes, especially if you have multiple solutions to a puzzle that’s about manipulating a complicated simulation, you instead want your rule not to be about how the player did whatever he did, but about what state the world is now in. For instance, let’s suppose we’ve got a puzzle where the player needs to balance a balance beam.

Using an action-based approach to recognizing the solution gets us a certain distance, and we can make this pretty general with a rule like this:

After putting something on the right pan or putting something on the left pan:
if the total weight of the right pan is the total weight of the left pan: (stuff that happens when the puzzle is solved)

Buuut what if we have such a sophisticated simulation that the pans can come into balance in other ways? What if we can throw things at the pans? Ask NPCs to put things on them? Put a block of ice on there and wait for it to melt just to the right weight?

Now a better way to recognize the solution is to process actions normally, but then check on the world state every turn and see whether the beams are balanced.

Every turn:
if the total weight of the right pan is the total weight of the left pan: (stuff)

The way Inform works, this will happen after the action’s outcome is described to the player, so you’d get output like

>WAIT
The ice melts a bit more.

The scales come into balance! Slowly, by ancient mechanisms, the golden door creaks open.

This is an advanced technique, relatively speaking — in the vast majority of cases, you can safely tie your puzzle solution to action rules. But it’s worth mentioning because a puzzle based on a good simulation model can be very satisfying to players, because it can often be solved in many different but equally logical ways.

13 thoughts on “Coding Puzzles”

  1. I think your split between “solutions that trigger on a player’s action” and “solutions that trigger on world state” is a little confusing, or at least not well served by the examples.

    Your first example has the player able to dance with the lady as long as she is carrying the rose – no matter how she got it. To me, that’s triggered by the world state. If you leave it lying around and she picks it up, you can dance with her. If you give to your rival and HE gives it to her, you can dance with her. Or if you give it to her yourself, which is probably the expected solution, you can dance with her.

    That might make sense if the idea is that finding a rose makes her happy, and she’ll dance with you as long as she’s in a good mood. (Although an even better way to model this would be to have a mood flag, so that many things could put her in a good mood.) But probably the idea is that the only way to get the flower into her possession is by giving it to her. This is fragile, because now you have to be careful she doesn’t accidentally get the flower some other way. It’s safer to actually set a flag when the player performs the action of giving her the flower, and then check for that flag. (And then you can safely add a rule to make her put the flower in a vase after a while, so that the player always has another chance to give it to her if she picks it up some other way.)

    1. Your first example has the player able to dance with the lady as long as she is carrying the rose – no matter how she got it. To me, that’s triggered by the world state.

      Okay, I see your point, but I was distinguishing between the hooks to attach the last puzzle solution to, not how we assess preconditions for that solution. If the author knows that there’s only one way to get the rose into the lady’s possession, it’s fine to make that the precondition.

      The more simulation is going on in the world model, the more defensively one needs to code, but that’s a more complicated subject than I wanted to get into here; it seemed like the author was really asking something like “where do I put the hook to make something happen?”

  2. A couple of technicalish questions:

    In the dancing example, could we turn the “instead of dancing with the tall lady rule” into a “carry out dancing with the tall lady rule,” and then change “if the tall lady is ready to dance” into “if the player has danced with the tall lady”? IINM, this would mean that the attempt to dance with the tall lady had succeeded, whereas in the code as given it fails — in case that makes a difference somewhere. (I guess you’d have to add “rule fails” to the case where the player has danced with the tall lady.)

    In the original balance example, why is it an “instead of” rule instead of an “after” rule? It seems to me that this code would mean that the puzzle solution triggers only when the player tries to put something on the balance after it’s already balanced.

    1. In the original balance example, why is it an “instead of” rule instead of an “after” rule? It seems to me that this code would mean that the puzzle solution triggers only when the player tries to put something on the balance after it’s already balanced.

      Brain fart on my part. I’ve edited it now.

      In the dancing example, could we turn the “instead of dancing with the tall lady rule” into a “carry out dancing with the tall lady rule,” and then change “if the tall lady is ready to dance” into “if the player has danced with the tall lady”?

      Not quite, because the phrasing should be “if we have danced with the tall lady”. But otherwise, yes, we could do that.

      It just seemed a bit more complex to explain that option to a new user. Using flags is a more general-purpose solution, given that there are lots of cases for which checking history with the “if we have…” syntax won’t work out. (E.g., because we can only check the history of specific objects, a phrase applying to a kind “if we have given the flower to a woman” won’t work at all.)

  3. Am I the only person whose response to reading your example was “The mass of the water will be the same as the mass of ice, so unless some is dripping off, the balance’s weight won’t change.”?

  4. Emily, do you have a favorite article on IF puzzle design? I’ve been reading all of them I can get my hands on, and it occurs to me that I might’ve missed one that you found particularly insightful.

  5. “if the total weight of the right pan is the total weight of the left pan:”

    Such solutions and other major events tied to worldstate are fine candidates for a scene begin/end rule. “Balance puzzle complete is a scene. Balance puzzle complete begins when the total weight of the right pan is the total weight of the left pan.” I wouldn’t really call any of this an advanced technique, though.

    1. Yeah, “relatively speaking”.

      I’m really just trying to set down the most bare-bones description of how to hook in puzzle rewards, assuming a user who isn’t terribly familiar with the scope of possibilities in the language.

  6. (Er, I wasn’t being contrary just for the sake of it. I just don’t want new coders coming to this article to be artificially scared off of using good technique because of the “advanced” label. “Old school” and “modern” labels might be more appropriate.)

  7. This is likely pushing a metaphor too far, but: this reminds me of the two classes of interrupts: edge-trigger and level-trigger. The former corresponding to player action (that is, a change in state) and the latter to the game state itself.

  8. The problem with the last example which is not mentioned is that we have to handle the case when the player is absent from the room when the puzzle gets solved and amend messages in that case.

    On that topic, now I realize that I have never seen a puzzle for which you are granted points only when you have noticed that you have solved it, not when you have solved it. Are there games which do that?

Leave a Reply to rogercarbol Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s