I7 treats these items as identical but they aren't

I’ve been playing around with the I7 source code example Claims Adjustment, getting it to use Jesse McGrew’s Dynamic Objects instead of creating all the photo paper beforehand. There’s one major problem, though (and in fact the original version of the example has the same issue). Although the photographs are distinguishable by their subject, Inform thinks that they’re all identical. This means that X PHOTOGRAPH picks any photo at random, instead of asking which photograph you mean, and that all the photos are automatically grouped together in the inventory instead of being listed separately. How can I get Inform to treat the photos as distinct items?

[code]“Claims Adjustment”

Include Dynamic Objects by Jesse McGrew.

A photograph is a kind of thing. The prototype is a photograph. The printed name of the prototype is “photograph”. Understand “photograph” or “photo” as a photograph.

Appearance relates one thing to various photographs. The verb to be shown by implies the appearance relation.

The description of a photograph is usually “It shows [a random thing which is shown by the item described].”

Understand “of [something related by reversed appearance]” as a photograph.

[This allows the player to refer to any photograph by its subject: useful if we have a large number of them.]

[Now we create an action to let the player use the camera and generate these photograph objects:]

The player carries a cheap instant camera.

Understand “photograph [something] with [camera]” as photographing. Understand “photograph [something] with [something preferably held]” as photographing. Photographing is an action applying to one visible thing and one carried thing, requiring light.

The photographing action has an object called the selected film.

Check photographing:
if the second noun is not the camera, say “You need a camera for that purpose.” instead.

Check photographing:
if the noun is the camera, say “Sadly impossible.” instead.

Carry out photographing:
let the copy be a new object cloned from the prototype;
[if we ran out of memory and couldn’t create a new photo:]
if the copy is nothing:
say “Ack, it looks like you’ve run out of photo paper! I guess you’re not going to be taking any more photos tonight.”;
stop the action;
otherwise:
now the selected film is the copy;
now the noun is shown by the selected film;
move the selected film to the player.

Report photographing:
say “Your camera instantly spits out [a selected film].”

[Now we use two activities from the Activities chapter to describe the photographs to the player more elegantly:]

After printing the name of a photograph (called target):
say " of [a random thing which is shown by the target]".

[After printing the plural name of a photograph (called target):
let N be the holder of the target;
say " of [a list of things which are shown by photographs which are held by N]";
if the number of things which are shown by photographs which are held by N is greater than one, say " (variously)".]

[And finally we provide a brief scenario to give the player something to take pictures of:]

The Treasure Room is a room. “Despite the fancy name, this is no more than a closet – albeit a closet with its own special circuit on the house alarm.”

The Treasure Room contains a small Degas, a Ming vase, and a collection of South African krugerrands.

The description of the forms is “Completely filled out in black ink in block letters: now all you need to do is attach photographic evidence of the objects you wish to insure.”

Test me with “photograph degas / photograph vase / x photograph”.[/code]

Indeed. This is a library bug, although it might turn out to be a difficult bug to fix.

If you use “Understand the foo property as describing a photograph”, I7 correctly uses the foo property to determine indistinguishability. But with the understand line in this example, it doesn’t.

I filed inform7.com/mantis/view.php?id=971 .

I tried this as a workaround (for the original I7 example, without Dynamic Objects).

Add this:

A photograph has indexed text called the virtual printed name. Understand the virtual printed name property as describing a photograph.

and change the carry out photographing rule to this:

Carry out photographing: now the noun is shown by the selected film; move the selected film to the player; now the virtual printed name of the selected film is "a photograph of [a random thing which is shown by the selected film]".

If I’m doing this right, the virtual printed name should track the (printed name + whatever gets printed after it) of a photograph, so the library should be able to distinguish two photographs when (and only when) the game calls them something different. If you try “photograph degas/g/x photograph” it won’t disambiguate, but if you try “photograph degas/photograph vase/x photograph” it will – which I believe is your desired behavior.

The disambiguation behavior is incredibly hinky; when it asks “Which do you mean, the photograph of a Ming vase or the photograph of a small Degas?” the answer has to start with “of” or it won’t be understood. And if you type “photograph of degas” it automatically takes a photograph of the photograph, even if you’re disambiguating “x photograph”! This is because “photograph” can be understood as a verb and “of Degas” can be understood as the photograph of a Degas – which is true even in the original example, without my additions. So I’d use Numbered Disambiguation Choices or something like that, which is probably a good idea anyway if you ever might be in the position where you have to disambiguate between a photograph of a photograph of a Degas and a photograph of a photograph of a photograph of a Degas.

Just for reference, my entire code is below (just the original example with those two added lines):

[spoiler][code]“Claims Adjustment”

A photograph is a kind of thing. 36 photographs are in the film roll. A photograph has indexed text called the virtual printed name. Understand the virtual printed name property as describing a photograph.

Appearance relates one thing to various photographs. The verb to be shown by implies the appearance relation.

The description of a photograph is usually “It shows [a random thing which is shown by the item described].”

Understand “of [something related by reversed appearance]” as a photograph.

[This allows the player to refer to any photograph by its subject: useful if we have a large number of them.]

[Now we create an action to let the player use the camera and generate these photograph objects:]

The player carries a cheap instant camera.

Understand “photograph [something] with [camera]” as photographing. Understand “photograph [something] with [something preferably held]” as photographing. Photographing is an action applying to one visible thing and one carried thing, requiring light.

The photographing action has an object called the selected film.

Setting action variables for photographing:
let N be a random photograph in the film roll;
now the selected film is N.

Check photographing:
if the second noun is not the camera, say “You need a camera for that purpose.” instead.

Check photographing:
if the noun is the camera, say “Sadly impossible.” instead.

Check photographing:
if the selected film is nothing, say “You’re out of film.” instead.

Carry out photographing:
now the noun is shown by the selected film;
move the selected film to the player;
now the virtual printed name of the selected film is “xxxa photograph of [a random thing which is shown by the selected film]”.

Report photographing:
say “Your camera instantly spits out [a selected film].”

[Now we use two activities from the Activities chapter to describe the photographs to the player more elegantly:]

After printing the name of a photograph (called target):
say " of [a random thing which is shown by the target]".

After printing the plural name of a photograph (called target):
let N be the holder of the target;
say " of [a list of things which are shown by photographs which are held by N]";
if the number of things which are shown by photographs which are held by N is greater than one, say " (variously)".

[And finally we provide a brief scenario to give the player something to take pictures of:]

The Treasure Room is a room. “Despite the fancy name, this is no more than a closet – albeit a closet with its own special circuit on the house alarm.”

The Treasure Room contains a small Degas, a Ming vase, and a collection of South African krugerrands. The player is carrying insurance forms, a first-class stamp, and a security envelope.

The description of the forms is “Completely filled out in black ink in block letters: now all you need to do is attach photographic evidence of the objects you wish to insure.”

Test me with “photograph degas / i / photograph degas / i / x photograph of degas / photograph me / x photograph of me / i / photograph vase / photograph camera / photograph collection / g / i / test more”.

Test more with “x photograph of collection / x photograph of krugerrands / x photograph of collection of south african krugerrands / photograph photograph of degas / x photograph of photograph of degas”.

[/code][/spoiler]

That’s because the original code’s Understand line insists on the phrase “of SUBJECT”, as does your virtual name property.

Here’s another way to do this, based on properties instead of relations:

An art-descriptor is a kind of value. The art-descriptors are degas-desc, ming-desc, kruger-desc.

Understand "small", "degas" as degas-desc.
Understand "ming", "vase" as ming-desc.
Understand "collection", "south", "african", "krugerrands" as kruger-desc.

A photograph has an art-descriptor.
Understand "photo", "photo of", "photograph of" as a photograph.
Understand the art-descriptor property as referring to a photograph.

[...needs code to set the art-descriptor property when a photograph is taken.]

This will understand “x ming photo”, “x photo of ming”, and it disambiguates mostly correctly. However, it won’t handle photographs of photographs.

It will also run into trouble as soon as art-descriptor names start to overlap. If you have both a Ming vase and a Korean moon vase, this system falls apart, because kind-of-value parsing can’t disambiguate “vase” properly.

The appearance relation seems like a very roundabout way to describe what a given photograph depicts. In most languages this would just be done with an object-reference field in the photograph, but in I7 I guess an object reference isn’t a valid kind of value. “A photograph has a thing called the subject. Understand the subject property as describing a photograph.” Sadly this doesn’t work. Is there some deep technical reason why it shouldn’t?

Disambiguation is, as a rule, interrupted if the player enters a command verb (LOOK or TAKE ALL or PHOTOGRAPH or whatever). Inform handles exceptions to this rule on the I6 level by the LanguageVerbMayBeName routine, so one can hack the I6 templates to add a verb that may be a name:

[code]Include (-
[ LanguageVerb i;
switch (i) {
‘i//’,‘inv’,‘inventory’:
print “take inventory”;
‘l//’: print “look”;
‘x//’: print “examine”;
‘z//’: print “wait”;
default: rfalse;
}
rtrue;
];

[ LanguageVerbLikesAdverb w;
if (w == ‘look’ or ‘go’ or ‘push’ or ‘walk’)
rtrue;
rfalse;
];

[ LanguageVerbMayBeName w;
if (w == ‘long’ or ‘short’ or ‘normal’ or ‘brief’ or ‘full’ or ‘verbose’ or ‘photograph’) !!!Adding your verbs that may be names here!!!
rtrue;
rfalse;
];
-) instead of “Commands” in “Language.i6t”.[/code]

I believe this does exactly what you want.

[spoiler][code]“Claims Adjustment”

Include Dynamic Objects by Jesse McGrew.

Include (-

Attribute photo;

-) after “Definitions.i6t”.

Include (-

[ Identical o1 o2 p1 p2 n1 n2 d1 d2 i j flag;
if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side
if (o1 == 0 || o2 == 0) rfalse; ! Similarly
if (o1 ofclass K3_direction || o2 ofclass K3_direction) rfalse; ! Saves time

!  What complicates things is that o1 or o2 might have a parsing routine,
!  so the parser can't know from here whether they are or aren't the same.
!  If they have different parsing routines, we simply assume they're
!  different.  If they have the same routine (which they probably got from
!  a class definition) then the decision process is as follows:
!
!     the routine is called (with self being o1, not that it matters)
!       with noun and second being set to o1 and o2, and action being set
!       to the fake action TheSame.  If it returns -1, they are found
!       identical; if -2, different; and if >=0, then the usual method
!       is used instead.

if (o1 has photo && o2 has photo) {
	d1 = noun; d2 = second;
	noun = o1; second = o2;
	ProcessRulebook((+ the photograph comparison rule +));
	noun = d1; second = d2;
	if (RulebookSucceeded()) {
		!print "Identical!^";
		rtrue;
	}
	else if (RulebookFailed()) {
		!print "Different!^";
		rfalse;
	}
}

if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
  if (o1.parse_name ~= o2.parse_name) rfalse;
  parser_action = ##TheSame; parser_one = o1; parser_two = o2;
  j = wn; i = RunRoutines(o1,parse_name); wn = j;
  if (i == -1) rtrue;
  if (i == -2) rfalse;
}

!  This is the default algorithm: do they have the same words in their
!  "name" (i.e. property no. 1) properties.  (Note that the following allows
!  for repeated words and words in different orders.)

p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;

!  for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
!  for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;

for (i=0 : i<n1 : i++) {
	flag = 0;
	for (j=0 : j<n2 : j++)
		if (p1-->i == p2-->j) flag = 1;
	if (flag == 0) rfalse;
}

for (j=0 : j<n2 : j++) {
	flag = 0;
	for (i=0 : i<n1 : i++)
		if (p1-->i == p2-->j) flag = 1;
	if (flag == 0) rfalse;
}

!  print "Which are identical!^";
rtrue;

];

-) instead of “Identical” in “Parser.i6t”.

This is the photograph comparison rule:
let image one be a random thing which is shown by the noun;
let image two be a random thing which is shown by the second noun;
if image one is image two begin;
[say “([image one] == [image two])[line break]”;]
rule succeeds;
otherwise;
[say “([image one] != [image two])[line break]”;]
rule fails;
end if.

Carry out taking inventory (this is the print advanced inventory rule):
issue library message taking inventory action number 2;
say “:[if the player holds less than two photographs][line break][end if]”;
list the contents of the player, with newlines, indented, including contents, giving inventory information, with extra indentation.

The print advanced inventory rule is listed instead of the print standard inventory rule in the carry out taking inventory rulebook.

A photograph is a kind of thing. The description of a photograph is usually “It shows [a random thing which is shown by the item described].”. Understand “photograph”, “photo” and “of” as a photograph. Understand “[something related by reversed appearance]” as a photograph.

Include (- has photo, -) when defining a photograph.

The prototype is a photograph. The printed name of the prototype is “photograph”.

Appearance relates one thing to various photographs. The verb to be shown by implies the appearance relation.

[This allows the player to refer to any photograph by its subject: useful if we have a large number of them.]

[Now we create an action to let the player use the camera and generate these photograph objects:]

The player carries a cheap instant camera.

Photographing is an action applying to one visible thing and one carried thing, requiring light. Understand “Photograph [something] with [camera]” as photographing. Understand “Photograph [something] with [something preferably held]” as photographing.

Does the player mean photographing a photograph: it is very unlikely.

The photographing action has an object called the selected film.

Setting action variables for photographing:
let the copy be a new object cloned from the prototype;
now the selected film is the copy.

Check photographing:
if the second noun is not the camera, say “You need a camera for that purpose.” instead.

Check photographing:
if the noun is the camera, say “Sadly impossible.” instead.

Check photographing:
if the selected film is nothing, say “Ack, it looks like you’ve run out of photo paper! I guess you’re not going to be taking any more photos tonight.” instead.

Carry out photographing:
now the noun is shown by the selected film;
move the selected film to the player.

Report photographing:
say “Your camera instantly spits out [a selected film].”.

Before listing contents: group photographs together.

Rule for grouping together photographs:
say “some photographs of various items”.

After printing the name of a photograph (called target) while not grouping together:
say " of [a random thing which is shown by the target]".

After printing the plural name of a photograph (called target) while not grouping together:
say " of [a random thing which is shown by the target]".

The Treasure Room is A Room. The description of the treasure room is “Despite the fancy name, this is no more than a closet – albeit a closet with its own special circuit on the house alarm.”.

The treasure room contains a small degas, a ming vase, a collection of south african krugerrands and some forms.

The description of the forms is “Completely filled out in black ink in block letters: now all you need to do is attach photographic evidence of the objects you wish to insure.”.

Test me with “x forms / i / photograph degas / x photograph / i / photograph degas / x photograph / i / photograph vase / x photograph / vase / i / photograph krugerrands / x photograph / krugerrands / i”.[/code][/spoiler]

It’s quite a fiddly solution, but it does treat photos of different items as distinct and photos of the same item as identical.

Hope this helps.

Only in the sense that the parser is deep (or complicated, or murky, or baroque…) and every feature makes it more complicated.

This whole notion of “Understand the … property as describing …” is a big special case in the parser. I won’t call it a “hack”, because it’s a pretty clean concept, but it generates some nasty scary code down in the I6 parse_name routine. To understand kinds-of-thing (rather than kinds-of-value) in this way is not conceptually harder, but it would be a different batch of nasty code. It would make a reasonable feature-request.

(In the old days, of course, we had to write our own nasty scary I6 parse_name code…)

Possibly related to what happened over here: https://intfiction.org/t/i7-dynamic-objects-and-programming-error/2095/7

Thanks for the help, everyone!

Well, that explains a lot! Thanks for reporting it, too.

Thank you so much! I had to tweak it a little to make it match the full version of my code, but it’s working great now.