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;
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.
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
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.