Regions containing regions and things, cats and dogs living together

This is driving me batty. This source:

The Lab is a room.
The House is a region.
The Lab is in the house.
The Block is a region.
The House is in the block.
The Neighborhood is a region.
The Block is in the neighborhood.

The player is wearing the sweater.
The pocket is a container.
The pocket is part of the sweater.
The medallion is in the pocket.

when play begins:
  repeat with a-region running through regions begin;
    say "The list of objects that [a-region] relates to by the regional-containment relation: ";
    say "[list of objects that a-region relates to by the regional-containment relation].";
    say "The list of objects that [a-region] relates to by the containment relation: ";
    say "[list of objects that a-region relates to by the containment relation].";
  end repeat;
  if the House contains the sweater, say "The House contains the sweater.";
  if the Block contains the pocket, say "The Block contains the pocket.";
  if the Neighborhood contains the medallion, say "The Neighborhood contains the medallion.";
  repeat with obj running through objects begin;
    if obj is a region, say "[obj] [if obj contains the medallion]contains[else]doesn't contain[end if] the medallion.";
    if obj contains the medallion, say "[obj] contains the medallion.";
  end repeat;
  let r1 be the Neighborhood;
  let m1 be the medallion;
  say "[r1] [if r1 contains the m1]contains[else]doesn't contain[end if] [m1].".

produces this self-contradicting output:

The list of objects that Neighborhood relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that Neighborhood relates to by the containment relation: Block.
The list of objects that Block relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that Block relates to by the containment relation: House.
The list of objects that House relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that House relates to by the containment relation: Lab.
The House contains the sweater.
The Block contains the pocket.
The Neighborhood contains the medallion.
Neighborhood doesn't contain the medallion.
Block doesn't contain the medallion.
House doesn't contain the medallion.
pocket contains the medallion.
Neighborhood contains medallion.

The regional-containment results look fine… the regions have the room (Lab) and its contents. And the House containing the Lab is fine. But we’re seeing the Neighborhood containing the Block and the Block containing the House… that shouldn’t be.

Then when we pose specific questions about Regions containing specific objects, the condition is true!

But then when we loop through all the objects and checking if any regions contain the medallion, we’re told no. When we look for anything that does contain the medallion, we only get the pocket.

Grasping at straws, to see if it has some weird cursed relationship with performing the containment relation check on values from variables, we assign Neighborhood and Medallion to temporary variables… and the condition comes back true again.

Can anyone please shed light on this?

I’m not sure whether I can shed light on the whole issue, but it does have something to do with whether we check this using variables or not.

Compare the handling of “in” as decribed in chapter 6.11 in the docs, 6.11. A word about in.

The test with “let r1 be the Neighborhood;” does not seem to capture the temporary nature adequately.
Because if you add these global variables:

rvar is an object that varies.
mvar is an object that varies.

and then add the following to “when play begins”:

  now rvar is the Neighborhood;
  now mvar is the medallion;
  say "[rvar] [if rvar contains the mvar]contains[else]doesn't contain[end if] [mvar]. Using 'in', [mvar] [if mvar is in rvar]is [else]is not [end if]in [rvar]. Using 'regionally in', [mvar] [if mvar is regionally in rvar]is [else]is not [end if]regionally in [rvar].";

then you will get the following output (because we used variables, similar to the repeat loop):

In general, if you add checks using “in” in your code, like this:

  if the medallion is in the Neighborhood, say "The medallion is in the neighborhood.";
  repeat with obj running through objects begin;
	if obj is a region, say "[obj] [if obj contains the medallion]contains[else]doesn't contain[end if] the medallion. Using 'in', the medallion [if the medallion is in obj]is [else]is not [end if]in [obj]. Using 'regionally in', the medallion [if the medallion is regionally in obj]is [else]is not [end if]in [obj].";
	if obj contains the medallion, say "[obj] contains the medallion.";
  end repeat;

then it will turn out that the results of “contains” match the results of “in”:

(You probably know these posts, but for anyone stumbling over this thread, Dr Peter Bates wrote a very thorough description of the containment (and other) relations in Inform in these posts: Elegant way to test whether something is in a location (even if in a container) - #7 by drpeterbatesuk and Elegant way to test whether something is in a location (even if in a container) - #26 by drpeterbatesuk)

2 Likes

Hrmph. When I make the code:

To decide which object is the real-container of (o - an object):
    (- ContainerOf({o}) -).

To decide whether (o - an object) pseudo-contains (p - an object):
    if p is in o, decide yes;
    decide no.

real-containment relates an object (called X) to an object (called Y) when X is real-container of Y.
pseudo-containment relates an object (called X) to an object (called Y) when X pseudo-contains Y.

The verb to really-contain means the real-containment relation.
The verb to pseudo-contain means the pseudo-containment relation.

when play begins:
  repeat with a-region running through regions begin;
    say "The list of objects that [a-region] relates to by the regional-containment relation: ";
    say "[list of objects that a-region relates to by the regional-containment relation].";
    say "The list of objects that [a-region] relates to by the containment relation: ";
    say "[list of objects that a-region relates to by the containment relation].";
    say "The list of objects that [a-region] relates to by the real-containment relation: ";
    say "[list of objects that a-region relates to by the real-containment relation].";
    say "The list of objects that [a-region] relates to by the pseudo-containment relation: ";
    say "[list of objects that a-region relates to by the pseudo-containment relation].";
end repeat;
  if the House contains the sweater, say "The House contains the sweater.";
  if the House really-contains the sweater, say "The House really-contains the sweater.";
  if the House pseudo-contains the sweater, say "The House pseudo-contains the sweater.";
  if the Neighborhood really-contains the Block, say "The Neighborhood really-contains the Block.";
  if the Neighborhood contains the medallion, say "The Neighborhood contains the medallion.";
  if the Neighborhood really-contains the medallion, say "The Neighborhood really-contains the medallion.";
  if the Neighborhood pseudo-contains the medallion, say "The Neighborhood pseudo-contains the medallion.";
  if the Block really-contains the House, say "The Block really-contains the House.";
  if the Block contains the pocket, say "The Block contains the pocket.";
  if the Block really-contains the pocket, say "The Block really-contains the pocket.";
  if the Block pseudo-contains the pocket, say "The Block pseudo-contains the pocket.";
  repeat with obj running through objects begin;
    if obj is a region, say "[obj] [if obj contains the medallion]contains[else]doesn't contain[end if] the medallion.";
    if obj is a region, say "[obj] [if obj really-contains the medallion]really-contains[else]doesn't really-contain[end if] the medallion.";
    if obj is a region, say "[obj] [if obj pseudo-contains the medallion]pseudo-contains[else]doesn't pseudo-contain[end if] the medallion.";
    if obj contains the medallion, say "[obj] contains the medallion.";
    if obj really-contains the medallion, say "[obj] really-contains the medallion.";
    if obj pseudo-contains the medallion, say "[obj] pseudo-contains the medallion.";
  end repeat;
  let r1 be the Neighborhood;
  let m1 be the medallion;
  say "[r1] [if r1 contains the m1]contains[else]doesn't contain[end if] [m1].";
  say "[r1] [if r1 really-contains the m1]really-contains[else]doesn't really-contain[end if] [m1].";
  say "[r1] [if r1 pseudo-contains the m1]pseudo-contains[else]doesn't pseudo-contain[end if] [m1].".

I get:

The list of objects that Neighborhood relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that Neighborhood relates to by the containment relation: Block.
The list of objects that Neighborhood relates to by the real-containment relation: Block.
The list of objects that Neighborhood relates to by the pseudo-containment relation: Block.
The list of objects that Block relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that Block relates to by the containment relation: House.
The list of objects that Block relates to by the real-containment relation: House.
The list of objects that Block relates to by the pseudo-containment relation: House.
The list of objects that House relates to by the regional-containment relation: yourself, sweater, Lab, pocket and medallion.
The list of objects that House relates to by the containment relation: Lab.
The list of objects that House relates to by the real-containment relation: Lab.
The list of objects that House relates to by the pseudo-containment relation: Lab.
The House contains the sweater.
The Neighborhood really-contains the Block.
The Neighborhood contains the medallion.
The Block really-contains the House.
The Block contains the pocket.
Neighborhood doesn't contain the medallion.
Neighborhood doesn't really-contain the medallion.
Neighborhood doesn't pseudo-contain the medallion.
Block doesn't contain the medallion.
Block doesn't really-contain the medallion.
Block doesn't pseudo-contain the medallion.
House doesn't contain the medallion.
House doesn't really-contain the medallion.
House doesn't pseudo-contain the medallion.
pocket contains the medallion.
pocket really-contains the medallion.
pocket pseudo-contains the medallion.
Neighborhood contains medallion.
Neighborhood doesn't really-contain medallion.
Neighborhood doesn't pseudo-contain medallion.

real-containment gives mostly the expected answers, with the surprise that it still shows regions containing regions, which I didn’t expect from Dr. Bates’ thorough write-ups.

I had expected to evade any of the “in” ambiguity by invoking contains every time.

Containment seems to not actually be a basic relation, but a calculated one like adjacent. It seems to be something like:

does X contain Y?
If we know at compile-time that X is a region, and X regionally-contains Y, then yes.
Otherwise, return the result for whether X really is the container of Y.

[Edited to add: obviously, I’m speaking loosely there – it’s not the case that there’s a dynamic consideration of what was known at compile-time, my speculation was that the compiler generated different code for the two cases]

That did shed light, @StJohnLimbo , thanks.

1 Like

Or it might be that the containment relation is a real relation and regions really can have the containment relation with each other, and it’s just that the contain verb goes off-script from meaning the containment relation, despite what the Standard Rules and Project Index tell us.

Looking at the generated I6, these odd results are a consequence of how the compiler tries to decide between applying the containment and the regional-containment relations to phrases including ‘in’, ‘contains’, ‘is contained by’ ‘relates by the containment relation’ etc.

Essentially, if the compiler can determine that the ‘? container’ is a region, it will apply the regional-containment relation. This is why phrases such as ‘If region A is in region B’ always return false. However, if the compiler cannot know at compile-time whether the ‘? container’ is a region or not, it defaults to applying the containment relation. As noted by St John Limbo, this behaviour is alluded to in chapter 6.11 of WI.

The quirk your code has newly uncovered is that if the containment of a region is tested through this mechanism a region holding another region DOES contain it- it’s just that this is not easily tested for because tests of containment for regions are usually diverted by the compiler into tests for regional-containment.

I’ll have to edit my spatial relations post accordingly…

2 Likes

I’m relieved there really is a new quirk – this experimentation came out of my efforts to try to be sure I understood your post! (And thank you for writing it – it was much needed.)

Another quirk of disconnect of expected results when invoking a verb and relation:

The verb to possess means the possession relation.
real-possession relates a person (called P) to a thing (called X) when P has X.
[...]
say "The list of objects that player relates to by the possession relation: [list of objects that player relates to by the possession relation].";
say "The list of objects that player relates to by the real-possession relation: [list of objects that player relates to by the real-possession relation].";
say "The player [if the player has the sweater]has[else]doesn't have[end if] the sweater.";
say "The player [if the player possesses the sweater]possesses[else]doesn't possess[end if] the sweater.";
The list of objects that player relates to by the possession relation: .
The list of objects that player relates to by the real-possession relation: sweater.
The player has the sweater.
The player possesses the sweater.

And also, while you get a Runtime error if you test whether something is adjacent to a region, you can test adjacency for any other pair of objects. (And again ‘list of objects that X relates to by the adjacency relation’ gets different results from looping through objects and testing ‘X is adjacent to Y’.)

It looks like a thing and a room are adjacent if the room is adjacent to the thing’s location. OK, that’s reasonable. And all doors are mutually adjacent with everything. And absolutely everything else is adjacent to the same one particular room, whichever room was first declared to be a given direction from any other room. If no rooms are directly connected, those objects are adjacent only to any doors. That door over there? The (Inform Parser) is adjacent to it.

All of this is absolutely useless except as trivia about language-abuse. :grinning:

This one appears to be a compiler bug- the relations handler for possession is defective such that the code elements which should generate a list of ‘possessed’ items (in this case, task RELS_LOOKUP_ALL_Y) are blank:

[ Rel_Handler_27 
    rr ! Implied call parameter
    task ! Implied call parameter
    X ! Implied call parameter
    Y ! Implied call parameter
    Z1 ! loop counter
    Z2 ! loop counter
    Z3 ! loop counter
    Z4 ! loop counter
    ;
    switch (task) {
        RELS_TEST: if (X == OwnerOf(Y)) rtrue; rfalse;
        RELS_LOOKUP_ANY: if (Y == RLANY_GET_X or RLANY_CAN_GET_X) {
            if (Y == RLANY_CAN_GET_X or RLANY_CAN_GET_Y) rfalse;
            rfalse;
        } else {
            if (Y == RLANY_CAN_GET_X or RLANY_CAN_GET_Y) rfalse;
            rfalse;
        }
        RELS_LOOKUP_ALL_X, RELS_LOOKUP_ALL_Y: LIST_OF_TY_SetLength(Y, 0);
        if (task == RELS_LOOKUP_ALL_X) {  
        } else {  !#### code to build list of possessed items should appear here####
        }
        return Y;
        RELS_LIST: LIST_OF_TY_SetLength(X, 0);
        if (Y == RLIST_ALL_X) {
        } else if (Y == RLIST_ALL_Y) {
        }
        return X;
        RELS_ASSERT_TRUE: MoveObject(Y,X); rtrue;
        RELS_ASSERT_FALSE: rfalse; rtrue;
    }
    rfalse;
];
1 Like

PS

This is a quirk of the I6 code generated by the phrase ‘list of objects that… relates to by the possession relation’ because ‘list of objects which are possessed by …’ generates an i6 objectloop rather than invoking the (dodgy) relation handler.

Another small patch of seeming perversity:

Lab is a room.
Box is an enterable container in the lab.
The player is in box.
Plinth is an enterable supporter in the lab.

When play begins:
    if box contains the player, say "box contains.";
    if a container contains the player, say "container contains.";
    if a container holds the player, say "container holds.";
    if box holds the player, say "box holds.";
    move the player to the plinth;
    if plinth supports the player, say "plinth supports.";
    if a supporter supports the player, say "supporter supports.";
    if a supporter holds the player, say "supporter holds.";
    if plinth holds the player, say "plinth holds.".

produces

box contains.
container contains.
box holds.

Lab (on Plinth)
You can see Box (empty) here.

plinth supports.
supporter supports.
plinth holds.

i.e., the if a container holds the player and if a supporter holds the player tests failed. Here’s an excerpt from the Inform 6:

   ! [2: if box contains the player]
    if (((I126_box == ContainerOf(player))))
    {! [3: say ~box contains.~]
        say__p=1;! [4: ~box contains.~]
        ParaContent(); print "box contains."; new_line; .L_Say1; .L_SayX1;}
    ! [5: if a container contains the player]
    if (((ContainerOf(player) ofclass K5_container)))
    {! [6: say ~container contains.~]
        say__p=1;! [7: ~container contains.~]
        ParaContent(); print "container contains."; new_line; .L_Say2; .L_SayX2;}
    ! [8: if a container holds the player]
    if (((HolderOf(player) ofclass K5_container) && (HolderOf(player) ofclass K8_person)))
    {! [9: say ~container holds.~]
        say__p=1;! [10: ~container holds.~]
        ParaContent(); print "container holds."; new_line; .L_Say3; .L_SayX3;}
    ! [11: if box holds the player]
    if (((I126_box == HolderOf(player))))
    {! [12: say ~box holds.~]
        say__p=1;! [13: ~box holds.~]
        ParaContent(); print "box holds."; new_line; .L_Say4; .L_SayX4;}

The if the container holds the player test turned into “if the holder of the player is a container and the holder of the player is a person”!

After verifying that this behaved the same with regions and regions, regions and rooms, and containers and things, I finally got it. It’s invoking the same meaning of hold you get in assertions.

I thought “holds” always meant the holding relation in conditionals. But apparently if the left-hand side is a kind, it means the carrying relation. When I added:

The player carries a frog.

then if a person holds a frog and if a thing holds a frog are true.

But if that’s the meaning of hold it’s going for, it still seems weird that if X holds Y turns into “if HolderOf Y is X and HolderOf Y is a person” instead of “If OwnerOf Y is X”… though I know so little of the I6 template layer it’s probably reckless to speculate.