What not to wear

I’ve been playing around with this example from the manual and I’ve come to the conclusion that (while the example works) it sucks.

Aside from only working for the player (which was a relatively easy fix). The bigger problem is that I had it working great with only two people (including the player) in the game, but once I thought I’d create the rest of the cast (before I forget who they are!) then a simple wear shirt ends up taking several seconds to complete.

Has anybody else encountered this and have an elegant work around? I believe the problem comes from the when play begins part where every shirt is defined as covering every torso, etc and where every person is defined as having one torso. The number of relationships that inform has to manage increases exponentially as you add more garments and more people.

I’ve been mulling one approach:

I was thinking I should define a value type, let’s call it body-area and maybe it has values head, upper-body, lower-body and feet. Then each body-part will have a body-area property which defines which area it is part of (so head is head, torso is upper body, legs are lower body, etc). Now every garment will also have a body-area, so a hat has a body-area of head, a shirt is upper-body, pants are lower-body (first problem, how to handle long garments that cover multiple areas, like a dress - if I was writing C I might make body-area a bit field, but I don’t think inform has anything like that). Now, instead of defining every shirt as overlying every torso, etc, only when a attempt is made to wear a garment will we look for the body-part of the person doing the action that the garment will cover and then check to see if any other garment is already worn by the person on that body-part (by matching body-area values). Now to handle layers, I can give every garment a layering depth value (a number) and when wearing, if another garment is worn on a particular body part then I can check to see if it’s layering depth is more or less than the garment I’m trying to wear. If it’s less, we wear it and set the noun as overlaying the previously worn item. If it isn’t then we start remove items until we get to a point where we can wear the new item.

I don’t know. It seems like the complexity has increased rapidly and I’m wondering if it’s worth the effort. Another approach might be to try and radically trim the number of relationships. One idea was to add an owning relationship which basically states that this shirt belongs to bob (regardless of whether he is wearing or holding it) and then when play begins use a loop to run through all garments and set them as only overlying garments that are owned by the same person. Then I’d have to stop the player from wearing other people’s clothes (which, let’s face it, probably don’t fit anyway). But that seems like it would restrict some puzzles I might think about. I guess I could make exceptions for a limited number of garments.

Your instincts are good. An IF author has to distinguish between simulation for its own sake and world-modeling that serves the story.

The hard part of course is figuring out the minimal level of implementation that will provide a satisfying player experience. If only a couple characters have clothes that the player needs to interact with, you might be better off defining each article explicitly rather than saying “every person wears a shirt,” etc.

Another alternative is to create kinds of people:

A snappy dresser is a kind of person. Every snappy dresser wears a tie.

Unfortunately my instincts as a programmer of more conventional object-oriented languages makes me want to abstract to the most generic solution possible and not hard code a bunch of relationships when I can make the computer do it for me. I’m having a bit of an existential crisis over this! But I started playing with Inform exactly because I was intrigued by how different it is.

I fight with that too. One thing I’ve started trying to do is remove unnecessary references to specific things from more general sections of code. More than once I’ve had to progressively trim my huge source file in order to find a bug - that’s a lot easier to do when you don’t have to use search-and-replace to remove specific objects and behaviors.

Writing code that way sometimes actually makes it more general too. Instead of saying “do the frobsholt with the goozbar”, I’ll say “do the frobsholt with every knickish thing,” and it’s really no more work to code. The trick is to come up with narrow categories of things that match the game behavior closely. Then you’re still using abstraction, but only where it’s needed.

Ok, I started playing around with this. I defined a body-area:

Body-area is a kind of value. The body-areas are feet, lower-body, legs, hips, upper-body, full-body, head, none.

And I defined a garment-element which has a body-area and a layer-level:

A garment-element is a kind of thing. A garment-element has a number called the layer-level. A garment-element has a body-area called the area.

And some kinds of garments:

A pair of pants is a kind of garment. The plural of pair of pants is pairs of pants. The area of a pair of pants is lower-body. A pair of pants usually has layer-level 5.

So far so good.

Now we come to before an actor wearing a garment rule.

What I need to do is get a list of all garments being worn by the actor that have a body-area equal to the noun (the thing the actor is attempting to wear) and a layer-level > the layer-level of the noun. If the list isn’t empty, all the things in that list need to be removed first.

Any ideas how to do this? I tried may variations on:

let conflicting be the list of garments worn by the actor

But even this fails, although, strangely, this works:

say “[the list of garments worn by the actor]”

So how come I can say the list, but I can’t create a temporary value to hold the list or otherwise process the list?

The explanation that the problem message in the IDE gives is that

I don’t see how the description “garments worn by the player” would contain a reference to the variable “conflicting” (rather than the other way around); but that might well be due to ignorance on my part. However, “let conflicting be the list of garments” compiles fine. And it’s perfectly possible to remove all garments not worn by the player from “conflicting” defined that way. So, why isn’t “let conflicting be the list of garments worn by the player” allowed?

Is it possible that I7 considers “the actor” to be a temporary variable, and so won’t let you use “things held by the actor” as a value? This got me the error:

Instead of singing: let conflicting be the list of things held by the actor; say "Held: [conflicting]."

but this worked as intended:

Permanent-actor is a person that varies. Instead of singing: now permanent-actor is the actor; let conflicting be the list of things held by permanent-actor; say "Held: [conflicting]."

(This seems like a “poorly worded error message” bug, if my diagnosis is correct.)

Sounds reasonable. You might be on to something. It would explain why it seemed to be okay when I used “the player” but not when I used “the actor”. The player is global and exists outside the scope of the rule whereas the actor does not.

That looks weird. The actor is a temporary variable but I’m surprised it couldn’t be used that way. But you shouldn’t need to define a new global. Try using “the person asked.”

This looks like a good place for some conditional relations:

[code]Clothing conflict relates a garment (called the top layer) to a garment (called the conflicting layer) when the conflicting layer is worn by the person asked and the area of the conflicting layer is the area of the top layer and the layer-level of the conflicting layer is not less than the layer-level of the top layer.

The verb to be unwearable because of implies the clothing conflict relation.

The verb to prevent wearing (he prevents wearing, they prevent wearing, he prevented wearing, he is preventing wearing) implies the reversed clothing conflict relation.[/code]

What’s the difference between a garment and a garment-element?

The new global is a kludge, but “the person asked” won’t work for indirectly triggered actions, will it? “Carry out turning on the searchlight: Try George putting on the sunglasses” and the like. {EDIT: On further testing, this is not correct.]

I didn’t catch on that you needed that. But do you? Is it even possible to have someone other than the player asking someone else to try doing something?

This is an area of Inform I have a lot of trouble with. I wonder if going back to Ron Newcomb’s book would help. In the meantime, have a gander at the error message for this code:

To decide what description of objects is the conflicting items: Decide on the list of garments that are worn by the person asked.

So we have three different types: A list of things of some kind, an instruction to work out a list of things of some kind, and a description of objects. Can anyone explain the difference?

I’d call it a bug in the compiler. It isn’t mysterious that you can “say” the list, because that is an immediate use of the temporary values involved (no attempt to store it). But you should certainly be able to define a temporary variable that makes use of a temporary variable with the same (or, as in this case, broader) scope.

–Erik

It comes from the original what not to wear example, and I’m not sure it’s strictly necessary any more. The way the example has it body-part and garment are both garment-elements, but garments are wearable where body-parts aren’t.

The implementation I’m playing with at the moment doesn’t actually need the body-parts at all (and I haven’t defined any) because I can just work with the body-area value instead. However, that won’t let you, for example, examine somebody’s head.

“Worn garments” and “garments worn by the player” are descriptions. Descriptions are a core I7 feature, but they are not values, and you can’t store them in variables. That’s because a description is a bit of code that can be used in various ways; it may describe different objects every time it is evaluated.

let D be the number of worn garments;
now all worn garments are smelly;
repeat with T running through worn garments: ...

A list is a value; it’s a data structure containing some things. Its contents are set when you build the thing, and then don’t change unless you explicitly change the list. (This is a mutable array in the usual programming language sense.) (The confusion here comes from the fact that lists were added to I7 only recently. Descriptions have been in since the beginning.)

You can build a list from a description:

let L be the list of worn garments;

That evaluates the description and sets L to its contents at that moment.

(You could also make a description based on a list, but it’s awkward and requires some ancilliary relation verbs.)

Further confusion: there’s a standard “say” phrase in the library defined this way:

To say a list of (OS - description of objects): ...

So when you say “I see [a list of worn garments],” you’re not creating a list at all. (Again, this dates from the days before I7 had a list type.) If you define L as above, and then say “I see [L],” you’re actually invoking different code.

…and this is why the code “let L be the list of things worn by the actor” should be allowed by the compiler. There is no need for the compiler to store the actor; all it needs to do is evaluate the description and save the list of things to L.

I succumbed to this confusion in my post above.

–Erik

Right, the “descriptions used as values are not allowed to contain references to temporary variables” problem. I have no good answer for that. In theory, I agree with you. In theory, I also see why it’s hard for the compiler to know which phrases use a description “in place” and which phrases store the description.

There may also be scoping issues. A description based on local variables may not be abstractable as a separate function. (Although “the actor” is not actually a local variable; it’s a rulebook variable, so I6 code can access it…)

Okay, thanks everybody for your help. capmikee’s comment on relationships was a huge help and let me greatly simplify everything. Here’s the code I currently have which seems to work fairly well (so far, I haven’t really tested it with multiple NPC’s yet):

[spoiler][code]
“What not to wear II” by Anonymous

Part 1 - Body areas

Body-area is a kind of value. The body-areas are feet, lower-body, legs, hips, upper-body, full-body, head, none.

[relates with areas include other areas]
Body-includes relates various body-area to various body-areas. The verb to body-include (it body-includes, they body-include, it body-included, it is body-including) implies the body-includes relation. The verb to be body-included by implies the reversed body-includes relation.

full-body body-includes hips, legs and upper-body. lower-body body-includes hips and legs.

[identity relationships]
feet body-includes feet. lower-body body-includes lower-body. legs body-includes legs. hips body-includes hips. upper-body body-includes upper-body. full-body body-includes full-body. head body-includes head.

Part 2 - Garments

Chapter 1 - Defining base kinds

A garment-element is a kind of thing. A garment-element has a number called the layer-level. A garment-element has a body-area called the area.

[not actually needed - but you might add body-parts and then test if they are covered in a similar way to testing if a garment covers another garment]
A body-part is a kind of garment-element. A body-part has layer-level 0.

A garment is a kind of garment-element. A garment is wearable. A garment can be transparent.

A pair of pants is a kind of garment. The plural of pair of pants is pairs of pants. The area of a pair of pants is lower-body. A pair of pants usually has layer-level 5.

A pair of underpants is a kind of garment. The plural of pair of underpants is pairs of underpants. The area of a pair of underpants is hips. A pair of underpants usually has layer-level 2.

A foundation garment is a kind of garment. The area of a foundation garment is upper-body. A foundation garment usually has layer-level 2.

A pair of socks is a kind of garment. The plural of pair of socks is pairs of socks. The area of a pair of socks is feet. A pair of socks usually has layer-level 2.

A pair of shoes is a kind of garment. The plural of pair of shoes is pairs of shoes. The area of a pair of shoes is feet. A pair of shoes usually has layer-level 5.

A jacket is a kind of garment. The area of a jacket is usually upper-body. A jacket usually has layer-level 10.

A hat is a kind of garment. The area of a hat is head. A hat usually has layer-level 5.

A dress is a kind of garment. The area of a dress is full-body. A dress usually has layer-level 5.

A skirt is a kind of garment. The area of a skirt is lower-body. A skirt usually has layer-level 5.

A tie is a kind of garment. The area of a tie is upper-body. A tie usually has layer-level 7. A tie is always transparent.

A shirt is a kind of garment. The area of a shirt is upper-body. A shirt usually has layer-level 5.

Chapter 2 - Relationships

Definition: a garment-element is uppermost if it is not unwearable because of something opaque.

Clothing conflict relates a garment (called the top layer) to a garment (called the conflicting layer) when the conflicting layer is worn by the person asked and the top layer conflicts with the conflicting layer.

The verb to be unwearable because of implies the clothing conflict relation.

The verb to prevent wearing (he prevents wearing, they prevent wearing, he prevented wearing, he is preventing wearing) implies the reversed clothing conflict relation.

To decide if (top layer - a garment) conflicts with (conflicting layer - a garment):
[simplest case - if the two garment are the same, they don’t conflict]
if the top layer is the conflicting layer:
decide no;
[simple case - if the top layer has a higher level, it doesn’t conflict]
if the layer-level of the top layer is greater than the layer-level of the conflicting layer:
decide no;
[some part of the top layer area = some part of the conflicting layer area]
otherwise if a body-area body-included by the area of the conflicting layer is body-included by the area of the top layer:
decide yes;
otherwise:
decide no;

Chapter 3 - Rules for handling garments

Section 1 - removing

Before an actor taking off something which is unwearable because of something which is worn by the actor:
while the noun is unwearable because of something (called the impediment) which is worn by the actor:
say “(first removing [the impediment])[command clarification break]”;
silently try actor taking off the impediment;
if the noun is unwearable because of the impediment, stop the action.

Check an actor taking off a garment:
if the noun is unwearable because of something (called the impediment) which is worn by the actor, say “[The impediment] is in the way.” instead.

Report taking off something:
say “You are now wearing [a list of uppermost things worn by the player].” instead.

Section 2 - wearing

Before an actor wearing something unwearable because of a garment worn by the actor:
while the actor wears a garment (called the impediment) which prevent wearing the noun:
say “(first removing [the impediment])[command clarification break]”;
silently try actor taking off the impediment;
if the actor is wearing the impediment, stop the action.

Instead of taking inventory:
say “You’re carrying [a list of things carried by the player][if the player wears something]. You are wearing [a list of uppermost garments worn by the player][end if].”

Part 3 - The Scenario

The Dressing Room is a room.

The player carries some capris, some jeans, a corset, a plunge bra, a thong, boy-shorts, black satin D’Orsay pumps, brown leather boots, a camisole, a cocktail dress, a bolero, a cashmere shrug, a sheer wrap, and a linen tunic.

The woolly socks are a pair of socks.
The D’Orsay pumps and the brown leather boots are pairs of shoes.
The thong and the boy-shorts are pairs of underpants.
The capris and the jeans are pairs of pants.
The tunic is a shirt.
The camisole, the corset, and the plunge bra are foundation garments.
The cocktail dress is a dress.
The bolero, the cashmere shrug, and the sheer wrap are jackets. The shrug and the wrap are transparent.

Test me with “wear capris / wear jeans / i / wear thong / i / wear dress / wear corset / wear dress / i / wear wrap / i / wear boots / wear pumps / i”.
[/code][/spoiler]

Any comments? Criticisms?

I learnt a lot about inform by doing this, the sad part is that I’m not sure I can really think of too many scenarios where this level of clothing detail might actually be useful, so I might well end up never using it. Still, finding the limitations of the original example compelled me to try and find a more efficient solution.

With a little tweaking, a similar system could be created for a situation where you have to keep track of items overlying (either completely or partially) over items.

So one last thing. I wanted to implement partial covering logic for deciding if a garment is uppermost. Right now we do this:

Definition: a garment-element is uppermost if it is not unwearable because of something opaque. 

What I want to do is replace that with a slightly more advanced logic. If (to use the what not to wear example) you wear the dress and the opaque jacket (the bolero - whatever that is) you should be able to still see the dress even though you can’t remove it because of the jacket (unless I’m completely misunderstand what a bolero is and it’s something more akin to a trench coat). So, what I want to do when deciding if something is uppermost is test whether any part of the body area it covers is not covered by something else.

I’ve been trying something along these lines, but can’t quite get it to work:

To decide if (item - a garment) is uppermost on (actor - a person):
	if the area of the item is not complex:
		if the item is not unwearable because of something opaque:
			decide yes;
		otherwise:
			decide no;
	otherwise:
		repeat with test-area running through body-area body-included by the area of the item:			
[the next condition I can't figure out - what I have here isn't any where close and won't compile anyway]
			if the layer-level of a garment worn by the actor is greater than the layer-level of the item:
				decide yes;
		decide no;

That last condition should say something equivalent to if there are no garments worn by the actor with a layer-level greater than the layer-level of the item which include the body-area I’m testing, then I can decide yes, this item is uppermost (i.e. it should be listed in the inventory).

Any ideas?