Multiple instances and disambiguation issues?

Three scenarios: one form works, one fails at compilation, and one fails silently at run time (these differ only in the final line in each example).

This one works:

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".
Home is a room. There is a desk in Home.
There is a terminal in Home. Now the terminal is on the desk.

This one fails at compilation:

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".
Home is a room. There is a desk in Home.
A terminal is on the desk.

This one fails silently (no terminal in the world):

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".
Home is a room. There is a desk in Home.
There is a terminal on the desk.

The reason I’m defining a kind rather than simply instantiating the objects directly is because there are many instances of rooms with desks, terminals, etc. (some have complicated parts and behaviors). If I must resort to brute force to define each one separately with unique identifiers, then I shall, but not without some gnashing of teeth. That the first example works is a bit baffling; the run time difference between the second and third is mildly mystifying, but what find perversely puzzling is when adding another room:

Away is south of Home.
There is a desk in Away. There is a terminal in Away.  [Now the terminal is on the desk.]

This gets past the compiler and runs. When I uncomment the final bit, it fails. I suppose it’s a disambiguation problem for I7 and rooms are special in some way.

The larger question has to do with a scalable way to re-use objects (i.e. populating the world with a collection of these n times), which I have not been able to find in the documentation.

I don’t think the first example really works the way you want. Using the “showme” debug command to see what’s really around:

Home
You can see a desky thingie and a stage 4 gallows humor here.

>showme desk
desky thingie - desk
location: in Home
unlit, inedible, fixed in place; transparent; singular-named, improper-named
description: "looks pedestrian"
initial appearance: none
carrying capacity: 100
printed name: "desky thingie"
printed plural name: "desks"
indefinite article: none
list grouping key: none

>showme now the terminal
Now the terminal - thing
location: on the desky thingie out of play
unlit, inedible, portable; singular-named, proper-named
description: none
initial appearance: none
printed name: "Now the terminal"
printed plural name: none
indefinite article: none
list grouping key: none

>

Note that there is a desk in Home, but there is a thing called “Now the terminal” that is out of play, and it’s not a terminal.

What you want is something like this:

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".
Home is a room. 
Small desk is a desk. It is in Home.
VT-100 is a terminal. It is on small desk.

You can avoid weird problems by always explicitly naming instances of a kind of thing. Now, if you want them all to appear the same to the player, use the printed name property, like you have.

Edit: “Now” should only be used in things like rules and actions, not in top-level world-building code.

4 Likes

Never write “the desk” or “the terminal”, if those are kinds of things. The compiler will not understand which instance you mean.

5 Likes

The basic issue you’re running into is that it’s a little tricky to work with instances of a kind that don’t have a unique identifier, because without an identifier how are you going to tell Inform what to do?

Before getting to the nub of the issue, one quick thing to highlight:

It doesn’t actually, because the terminal is just in Home, not on the desk – this is because “now” is only useful inside of a rule (like, “instead of doing X” or “when play begins”), since it changes the world state during the game.

Right – “a terminal is on the desk” doesn’t give Inform enough information to do what you’re asking (which terminal? Who knows!) This is what the “there is” is for, since it tells Inform to create something new.

This one’s fun – if you use the TREE testing command, you can see that actually there is a terminal on a desk, it’s just that it’s a second desk that’s out of play (again, “there is” tells Inform to create something, and since “the desk” doesn’t have a prior definite meaning, boom, new desk).

Fails compilation, you mean? I’m getting it to compile just fine, and it works like the first example (there’s a desk and a terminal in Away, but the terminal isn’t on the desk, because “now” doesn’t do anything outside of a rule).

OK, here’s the key question! I can think of three basic ways to do this. One is, as you’ve intuited, to give unique identifiers to each instance of the kind. Note that I’m adding an understand statement for each of your kinds so that the player doesn’t need to know the private identifier to interact with the interchangeable objects:

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".  Understand "desk/desky thingie/--" as a desk.
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".  Understand "terminal/stage 4/-- gallows/-- humor/--" as a terminal.
Home is a room. home-desk is a desk in home.  home-terminal is a terminal on home-desk.

Away is south of Home.
Away-desk is a desk in Away. away-terminal is a terminal on away-desk.

Second is to avoid giving them a descriptor, but resign yourself to coding being a bit annoying.


A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian".  There are two desks.
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included".  There are two terminals

Office is a region.

Home is a room in Office.

Away is south of Home.  Away is in Office.

When play begins:
	Repeat with foo running through rooms in Office:
		Move a random off-stage desk to foo;
		Move a random off-stage terminal to a random desk in foo.	

(Since there should only be one desk in each room, picking a random one will mean picking the only one, so this winds up faking a unique identifier).

Now there’s the third way, which is probably the simplest – don’t mess around with kinds at all and use backdrops! Backdrops are scenery that move around when the player does, so they in effect can be in multiple rooms at once. The one caveat is that they can’t be supporters, but we can use incorporation to get around that:

Office is a region.

Office-backdrop is a backdrop in office.  It is privately-named.  It is not scenery.  The initial appearance is "As with all rooms here, there's a deskie thingie with a stage 4 gallows humor on it."

Desk is a supporter.  It is part of office-backdrop.  The printed name is "desky thingie". The description is "looks pedestrian".
Terminal is on Desk.  The printed name is "stage 4 gallows humor". The description is "battery not included".

Home is a room in Office.

Away is south of Home.  Away is in Office.

(The initial appearance here is hacky and hardcoded, so if you want the player to be able to move the terminal around or put other things on the desks, you’ll need to do a bit more work, but this note is already too long!)

3 Likes

If you want a number of identical rooms, with different names, you could do something like this instead.

An office is a kind of room

A desk is a kind of supporter. The printed name is "desky thingie". The description is "looks pedestrian". Understand “desky/thingie” as a desk.
A terminal is a kind of thing. The printed name is "stage 4 gallows humor". The description is "battery not included". Understand “stage/4/four/gallows/humor/humour” as a terminal.
There is a desk in every office.
There is a terminal on every desk.

Home is an office.

Away is an office. Away is south from Home.
4 Likes

I did this once - had “bathroom” as a kind of enterable lit container and placed one in each of several hotel rooms so the player could “enter the bathroom” and close themselves inside it. In this case it worked since only one was ever in scope at any given moment.

But it does work better to make sure each instance has its own name.

Bob's Desk is a desk in Office.

But if you want to do it systematically you kind of have to use the magic word “random” even if it’s not a random choice.

3 Likes

Here’s a different approach (see Documentation §4.15. Assemblies and body parts and §4.16. Names made in assembly)

An office is a kind of room

A desk is a kind of supporter with printed name "desk" and description "looks pedestrian". Every office contains a desk (called its desk). A desk is always improper-named.
A terminal is a kind of thing. The printed name is "terminal". The description is "battery not included". Every desk supports a terminal (called its terminal).   A terminal is always improper-named.
A wastepaper basket is a kind of container with printed name "wastepaper basket" and description "woven from willow". A wastepaper basket (called its wastepaper basket) is in every office. A wastepaper basket is always improper-named.

Home is an office. South from Home is an office called Away.

The best way to see at a glance what you’ve created, what it’s called, where it is and what properties it has is to explore the Index → World → Map tab.

This shows that the above creates a desk in each office called e.g. Home's desk, a wastepaper basket in each office called e.g. Home's wastepaper basket and a terminal on each desk called e.g. Home's desk's terminal.

Note that §4.16. Names made in assembly is not fully accurate- something created in this way is automatically given a name in the form X's Y when a Y is a part of every X, but not when a Y is contained by or supported by every X- for that you have to used the (called its …) syntax. Otherwise, (as noted above) every object created is just given the name of its kind- e.g. a desk called desk.

X's Y will by default be created as a proper-named object, so we have to state otherwise if we are to override that internal name with an improper printed name, e.g. with A desk is always improper-named.

4 Likes

That looks powerful, thanks. The remaining thing I’m scratching my head over is how one manipulates these objects without upsetting the compiler sometimes. The following causes trouble (“too vague to describe a specific action”):

LogOn is an action applying to nothing.
Understand "log on" and "log in" and "log" and "logon" and "login" as LogOn.
Check LogOn when loggedOn is true: instead say "You are already logged on".
Carry out LogOn:
	try using terminal.

It will work with an actual instance in place of the terminal kind. But referencing a kind in the following works:

Carry out using something:
	if the location of the noun is the location of the player:
		if the terminal is switched on:
			if the noun is a small red box, now loggedOn is TRUE;
			else say "Thumbprint required";
		else:
			say "The is screen dark.";
	else:
		say "It isn't here." instead.

I presume this works because I7 hopes to see a noun at run time because of something and satisfies itself with using an object of that kind if one happens to be in the neighborhood (scope); however, the first example doesn’t provoke I7 to do this kindness. Is my only recourse to jettison this approach and rewrite it with After reading a command... hackery? There must be a better way…

1 Like

I’m not sure what exactly you’re trying to do (and I would really counsel against including a USE action - it encourages lawnmowering behavior in players and makes coding and debugging much harder since you’ll have a giant tangle of rules and conditions under that one action, some of which will probably be invisibly redirecting to others) but would something like this be helpful?

carry out logging on when a terminal (called the foo) is in the location:
	if the foo is switched on:
		say "You log onto the terminal.";
	otherwise:
		say "The terminal is off!"

The “(called the …)” thing allows you to once again specify exactly what object you’re talking about.

2 Likes

Are you trying to log in to any terminal in the immediate vicinity? That can certainly be done. It’s just unclear exactly what you’re trying to do. Maybe a mock transcript would help.

2 Likes

For this, you can use the “random” method which Mike and Hanon mentioned above:

Carry out LogOn:
	try using a random terminal which is in the location.

It works in the sense that it will compile, but it usually isn’t what you’re looking for. The “the” in “if the terminal is switched on” does not pick out the particular terminal which is in scope; the condition will be fulfilled if any terminal at all is switched on (in other words, writing “if the terminal is switched on” is in this case the same as writing “if a terminal is switched on” - one of the pitfalls of I7’s natural language programming).

This can be verified by having the terminal in the player’s location be switched off, and a terminal in another room switched on. The latter will still make the condition true.

(That’s what zarf meant above, when he said “Never write “the desk” or “the terminal”, if those are kinds of things.”)

The solution is to do what Mike said (the “(called the ...)” syntax), and/or use “the noun”.

2 Likes

Here’s the actual transcript when I use an explicit reference to the home-terminal:

>logon
The screen is dark.

>switch the terminal on
You switch the terminal on.

>logon
Retinal scan required
>scan eye

I’ve been trying to get Mike’s code snipped integrated into mine, but something is amiss. It compiles but it’s not finding a ‘terminal’ in the room at run time. Looking at the map (good tip from Peter, thanks!), I saw that there is a home-terminal (of the terminal kind) instantiated. I believe the relevant RULES output is this:

[Rule "check stage rule" applies.]
[Rule "carry out stage rule" applies.]
[Rule "after stage rule" applies.]

Changing back to code to use the home-terminal yields this:

Rule "check stage rule" applies.]
[Rule "carry out stage rule" applies.]
[Rule "Carry out Logging" applies.]
The screen is dark.
[Rule "after stage rule" applies.]

I’m don’t yet know what is wrong with the code, but the is in the location never seems to find a terminal (the kind only shows up under Nowhere). Neither when a home terminal (called the foo) is in the location: nor putting the test into a separate check rule will work. I tried a number of variations of this, but the only thing that works uses an explicit reference.

In this case, the player should be able to log on from a few terminals that he has somehow hacked. He needs to be at arm’s length, of course.

In the spirit of Mike’s caution to avoid ‘lawnmowering’ and attendant debug headaches, I’m trying not to go back to the USE action.
I did try out fankensteining your use of random in a Check Logging statement and the results were comical: Sometimes it would hit the jackpot and land on a terminal the player had hacked for use. The problem seems to still lie with the location of an object kind that is definitively coincident with the location of the player.

I feel like I’m close to seeing some light in the tunnel. Just not sure yet if it is cast from the sun or an oncoming freight train.

Try “enclosed by the location”, as in:

a random terminal enclosed by the location

If something is on a supporter such as a desk, it’s not directly in the location (even though its location is the location).

2 Likes

Ah, good catch – I just threw a terminal in the room when I was testing the snippet and didn’t bother putting it on the desk. Apologies!

2 Likes

Jubilation! Everything works now. :grinning:

Question about forum protocol: Is there way to mark the two solutions to the two main issues in my post? Both are important and may be helpful to other beginners, so I’m reluctant to indicate only one as a/the solution. I probably should have created a separate post for them at the start, but I didn’t know enough about the topic to realize that.

PS The advice about avoiding definite articles with kinds has already paid dividends. I went through the code and purged the offenders to save me from the debugging that would have ensued.
PPS The lawnmowering pitfall was one I hadn’t known about and seems good cautionary advice.
PPPS Folks, thanks for guiding me to the tunnel that didn’t enclose a freight train!
PPPPS No more postscript.

2 Likes