I'm taking a puzzle which is tricky to implement in Inform, and trying to take you through the steps needed to turn it into working code.
Here are the meanings of the return codes of the most commonly-used
routines:
If in doubt, read the Designer's Manual, and look
carefully at the example games to see how they use return codes.
For example (after Adventure), a delicate ming vase that
shattered when the player dropped it could be coded by using the `after'
routine of the vase to look for the `Drop' action. Or (after
Hitchhiker's) a room with holes in the floor through which
any dropped object fell could be coded by using the `after' routine of
the room to look for the `Drop' action.
In order to do this, it helps to be familiar with the operation of the
library. Table 6A
of the Designer's Manual lists all the actions that
the library knows how to deal with, and it's probably worth looking
briefly at the source code (in the Verb
Library) to see exactly what
they all do - in the cases of `Take', `Enter', and `Insert' the algorithm
is very complex. You may also need to look at the Grammar file to see
which actions are generated by which player commands.
Also, when code fails to do what you intended, you'll often find it
worthwhile to look at the library source to see exactly what's going
on.
In any case, the rug needs to respond to the following commands.
After making the above changes, Alice can climb onto the mantelpiece,
and some more checking reveals a few more bugs.
We also have to be careful not just to use the test `noun in
mantelpiece' to see if the noun is at hand, because that would fail if
the object were carried by the player. Instead, we use the function
`Inside' to carry out this test. The function takes two objects arnd
returns 1 if the first object is inside the second, or 0 otherwise.
Starting
Alice: An Inform Tutorial (continued)
4. Common difficulties
The most complicated parts of the Inform language are to do with which
order routines are evaluated during play, and what to return from
routines to affect this order. These parts cause more problems to
beginning programmers than any of the other features of the language.
4.1 Return codes
A routine inside an object returns 0 (false) by default, and a routine
outside of an object returns 1 (true) by default. You can think of this
as meaning that there's an invisible `rfalse' statement just before the
closing square bracket of a routine inside an object, and at the end of
each block of code dealing with an action (and an invisible `rtrue' just
before the closing square bracket of a routine outside an object), so
that
Object thing
with ...
before [;
Action1: ...
Action2: ...
];
really means
Object thing
with ...
before [;
Action1: ... rfalse;
Action2: ... rfalse;
];
and
[ Routine; ... ];
really means
[ Routine; ... rtrue; ];
You can override this default using the `return', `rtrue' and `rfalse'
statements, or by omitting the `print' instruction from a print
statement, because
"Text in double-quotes";
really means
print "Text in double-quotes"; new_line; rtrue;
The use to which the return code of a function is put varies from
function to function, but usually when the library calls a function of
yours, then you return 0 to mean `apply the usual library rules', and
nonzero to mean `do something special' or maybe `don't do anything
at all'.
In an object's `before' routine, it's very common to want to
execute some code, print some text to tell the player what has happened,
and then to return 1 to prevent the library from doing anything else. A
good trick in this situation is to omit the `print' keyword from the
last print statement. Much of the code in the example game developed
here uses this trick.
`life'
False (0) means that the usual library rules should now apply
to the player's input (note that this doesn't necessarily mean that the
routine has done nothing).
False (0) means that the usual library message should now be
printed (again, this doesn't necessarily mean that the routine has done
nothing).
False (0) means that the library should print the usual
description of the object.
4.2 Evaluation order
Here's a summary of what the Inform library does when the player types a
command (DM 6).
It will be seen from this table that `before' routines should be
used to change completely the effect of a particular action, and that
`after' routines should be used to change the message associated with a
particular action, or to cause something else to happen after an action
has been successfully completed.5. Adding objects
Now that I have a room, I can fill it up with objects. I'll start
with the simplest objects, and work up to the most complicated (the
kittens). I'll try to add the objects in such a way that the code can
be tested after each addition (but if two objects refer to each other
this won't be possible).
5.1 The red queen: a simple object
The simplest objects just have a `name' and a `description', and the red
queen is such an object. The following definition appears after the
drawingroom.
Object red_queen "red queen"
with name "red" "queen",
description "She's a fierce little chess piece.";
Since she's absent from the game to start with, I don't specify which
object is her parent (Inform will ensure that her parent is `nothing').
5.2 The chess board
The chess board is not quite so simple. Because it doesn't appear in
the room description, I'll give it an `initial' description. And Alice
might try to `put red queen on chess board', so I'll make it a
`supporter'.
Object chess_board "chess board" Drawing_Room
has supporter
with name "chess" "board" "checker" "chequer" "chessboard",
initial "An abandoned chess board lies on the floor.",
description "It's left here from the game you were playing just
now, but the pieces are all missing - the kittens will insist
on playing with them.";
5.3 The hearth: a scenery object
The fireplace is mentioned in the initial room description, so I
probably ought to provide a `scenery' object to represent it. The
object plays no role in the game; it's just there for decoration.
Object hearth "hearth" Drawing_Room
has scenery
with name "hearth" "fire" "place" "fireplace",
description "Looking at the hearth, you wonder if they have a
hearth in the looking-glass house. You can never tell by
looking, unless your fire smokes, and then smoke comes up in
the looking-glass room too - but that may be only pretence,
just to make it look as if they had a fire.";
5.4 The rug: a complex object
Often, the best way to deal with more complicated objects is to consider
all the commands that the player might type that ought to make the
object behave differently from the default way in which the library
handles that action.
Some other points of interest:
Object rug "rug" Drawing_Room
has concealed static supporter enterable
! general if you've found the red queen under it
with name "hearthrug" "hearth-rug" "rug" "indian" "arabian" "beautiful"
"soft",
description "It's a beautiful rug, made in some far off country,
perhaps India or Araby, wherever those might be.",
before [;
Take: "The rug is much too large and heavy for you to carry.";
Push,Pull: "But a hearth-rug is meant to be next to the hearth!";
LookUnder:
if (player in self)
"You try to lift up a corner of the rug, but fail. After
a while, you realise that this is because you are
standing on it. How curious the world is!";
if (self hasnt general) {
give self general;
move red_queen to player;
"You lift up a corner of the rug and, peering underneath,
discover the red queen from the chess set.";
}
];
5.5 The armchair
My puzzle involves not being able to move the armchair if the kittens
are playing near it. But since I'm saving the kittens for last
(because they're the most complicated objects) I can't do a full
implementation of the armchair. Instead, I'll do the best I can,
leaving a comment in the place where I ought to check for the kittens.
Here the relevant commands to consider are the following.
Object armchair "arm-chair" Drawing_Room
has static concealed supporter enterable
! general if its by the mantelpiece
with name "arm" "chair" "armchair" "arm-chair",
description "It's a huge arm-chair, the perfect place for a kitten
or a little girl to curl up in and doze.",
before [;
Push,Pull:
! code to check for the kittens
if (self has general) {
give self ~general;
"You push the arm-chair away from the hearth.";
}
give self general;
"You push the arm-chair over to the hearth.";
];
5.6 The mantelpiece
For the mantelpiece, I need to consider the following commands.
Object mantelpiece "mantelpiece" Drawing_Room
has concealed supporter enterable
with name "mantel" "mantelpiece",
description "It's higher off the ground than your head, but it
looks wide enough and sturdy enough to support you.",
before [;
Enter,Climb:
if (player notin armchair)
"The mantelpiece is much too high to climb up onto.";
if (armchair hasnt general)
"You can't reach the mantelpiece from here.";
if (children(player) > 0)
"Your hands are too full.";
PutOn,LetGo:
if (player notin self && (player notin armchair ||
armchair hasnt general))
"The mantelpiece is so high that you can't reach.";
];
5.7 The mirror
The mirror presents the following problems.
Object mirror "looking-glass" Drawing_Room
has static concealed
with name "mirror" "looking" "glass" "looking-glass",
description [;
if (player in mantelpiece)
"Strangely, the glass is beginning to melt away, just
like a bright silvery mist.";
if (player in armchair)
"In the looking-glass you can see the drawing-room of the
looking-glass house. What you can see is very much the
same as this drawing-room, only all reversed, left for
right. But you are sure that out of the corners of the
glass, where you can't see, the looking-glass world is
quite different from yours.";
"In the looking-glass you can see the ceiling of the
drawing-room of the looking-glass house. It looks much the
same as the ceiling of your drawing-room.";
],
before [;
if (action ~= ##Examine && player notin mantelpiece)
"You can't reach the looking-glass from where you're
standing.";
Touch,Pull,Push:
"Your hand goes right through the silvery mist!";
Enter:
! Really, move Alice to the looking-glass house.
deadflag = 2;
"Your hand goes right through the silvery mist, and in
another moment the rest of you follows, and you are through
the glass...";
];
5.8 The ball of worsted
(Worsted is a kind of fine wool used for embroidery.) This object will
start out neatly rolled into a ball, but when its given to a kitten, it
will get into a tangled state. I'll use the `general' attribute to
indicate whether its tangled or not. I want to be able to take care of
the player typing `untangle worsted' or `roll up worsted', so I define a
new action, and some new grammar to go with it (after the inclusion of
the grammar library):
Verb "roll" "untangle" "wind"
* noun -> Untangle
* "up" noun -> Untangle
* noun "up" -> Untangle;
and write a basic routine to deal with `untangle chess board', and so on.
[ UntangleSub; "What curious ideas you have!"; ];
Now I can create the ball of worsted; note that like the mirror, its
description can vary.
Object worsted "ball of worsted" Drawing_Room
! general if its in a tangle
with name "ball" "of" "worsted" "fine" "blue" "wool",
initial "A discarded ball of worsted lies on the floor here.",
description [;
if (self has general)
"It's in a terrible tangle. All that time you spent
rolling it up, and now look at it!";
"It's a ball of fine blue wool, all rolled up in preparation
for some embroidery.";
],
before [;
Untangle:
give self ~general;
"You're as quick as can be at rolling up balls of wool,
though you say so yourself! Soon it's neat and tidy again.";
];
6. Testing, part 1
Now I have a program, alice2.inf (browse | download), which,
although incomplete, will compile and run, and allow me to attempt a few
actions. I advise you to compile this fragment of a game, and test it
yourself to see what problems you can discover (there are quite a few!),
before I start to list the ones I found.
1. > throw queen at mirror
You can't reach the looking-glass from where you're standing.
This is easy to correct; just add the following to the mirror's `before'
routine:
ThrownAt: "You don't want seven years' bad luck, do you?";
and change `action ~= ##Examine' to `action ~= ##Examine or ##ThrownAt'.
2. > get on chair
You get onto the arm-chair.
> push chair
You push the arm-chair over to the hearth.
Oops. Alice shouldn't be able to push the chair if she's in it, or on
the mantelpiece, or on the rug! Add the following at the start of the
armchair's `Push,Pull' action.
if (player notin Drawing_Room)
"You'll have to get off ", (the) parent(player),
" first.";
3. > climb mantelpiece
I don't think much is to be achieved by that.
The `Climb' action doesn't do the right thing here (see the Verb Library or DM4 Table 6
for details), so I have to add my own code to the end of the
mantelpiece's `Enter,Climb' action.
move player to mantelpiece;
"You scramble up onto the mantelpiece.";
4. > get on mantelpiece
But you're already on the arm-chair.
The solution to the previous problem also solved this problem.
5. > enter chair
You get onto the arm-chair.
> look under rug
You find nothing of interest.
Alice shouldn't be able to look under the rug when she's in the armchair
or on the mantelpiece. So add the following to the start of the rug's
`LookUnder' action.
if (player in mantelpiece || player in armchair)
"You're unable to reach the rug from here.";
6. > examine red queen
She's a fierce little chess piece.
> drop her
I'm not sure what "her" refers to.
I need to give the queen the `female' attribute.
> enter mantelpiece
You scramble up onto the mantelpiece.
> down
You'll have to get off the mantelpiece first.
> get on chair
But you're already on the mantelpiece.
> get board
Taken.
> get off
You are on your own two feet again.
I can take care of `down' and `get off' (and also `exit' and `out') by
adding a `before' routine to the drawingroom.
before [;
if (player notin Mantelpiece) rfalse;
Exit,Go:
if (noun == d_obj or out_obj)
"That's not the way to get down from a mantelpiece!";
];
And I can introduce code to the armchair's `before' routine allow Alice
to climb down from the mantelpiece to the armchair.
Climb,Enter:
move player to armchair
"You jump into the warm and comfortable arm-chair.";
The final problem (being able to touch objects on the floor while on the
mantelpiece) is very tricky to solve. Inform doesn't support the idea
of `physically reachable from here' [since this tutorial was first
written, it has done just that -- GN], but we can amend the `before'
routine of the drawingroom to look like this:
before [;
if (player notin Mantelpiece) rfalse;
Exit,Go:
if (noun == d_obj or out_obj)
"That's not the way to get down from a mantelpiece!";
Examine,Enter,ThrowAt,ThrownAt: ;
default:
if (inp1 ~= 1 && noun ~= 0 && Inside(noun,mantelpiece) == 0)
"You can't reach ", (the) noun, " from up here.";
if (inp2 ~= 1 && second ~= 0 && Inside(second,mantelpiece) == 0)
"You can't reach ", (the) second, " from up here.";
];
The actions `Examine', `Enter', `ThrowAt' and `ThrownAt' are all actions
that will work regardless of where the object is, but all remaining
actions will only work if the objects are to hand. The code that checks
that they really are to hand has to be careful, because `noun' and
`second' might not always be objects (they might be numbers, for
example). However, the variable `inp1' 1 if `noun' is not an object,
and `inp2' is 1 if `second' is not an object
(DM4 6), so we can check
these first.
[ Inside x y;
do {
x = parent(x);
} until (x == 0 or y);
if (x == 0) rfalse;
];
These problems arise from having several `enterable' objects in the same
room, and trying to restrict what Alice is allowed to do, according to
which of the objects she's on. This has turned out to be a much more
complicated situation to program than it seemed at first!
Last updated 23 June 2004.
This site is no longer supported; information may be out of date.
Maintained as a historical archive by the Interactive Fiction Technology Foundation.
Copyright 1993-2018 IFTF, CC-BY-SA unless otherwise noted.
This page was originally managed by Graham Nelson (graham@gnelson.demon.co.uk) assisted by C Knight.