[I7] What effect does "[something preferably held]" actually have?

I had thought that, per WWI 17.4 Standard tokens of grammar, the "[something preferably held]" token was part of the machinery for generating implicit takes. However, that doesn’t seem to be the case.

example scenario
"Preferably Held"

Place is a room.

A locked container called a chest is in Place. A fabulous jewel is in the chest.

A brass key is in Place. It unlocks the chest.

The player carries an iron key.

Whomping it with is an action applying to two things. Understand “whomp [something] with [something preferably held]” as whomping it with. [does not generate implicit take because lacking "carried thing"?]

Smacking it with is an action applying to one thing and one carried thing. Understand “smack [something] with [something]” as smacking it with. [generates implicit take even without preferably held"]

To smack is a verb. To whomp is a verb.

Report smacking something with something:
    say "[We] [smack] [the noun] with [the second noun]."

Report whomping something with something:
    say "[We] [whomp] [the noun] with [the second noun]."

Test me with "whomp chest with key / drop key / whomp chest with key / take brass key / whomp chest with key / drop key / smack chest with key / take brass key / smack chest with key".

The smacking it with action does generate implicit takes, but this seems to be due to the fact that the action definition specifies a carried thing for the second noun. Its grammar lines don’t specify "[something preferably held]".

The whomping it with action does not generate implicit takes, even though its grammar lines do specify “[something preferably held]”. Its action definition does not specify a carried thing for the second noun.

At the I6 level, what’s generated is:

Verb 'whomp'
    * noun 'with' held  -> A77_whomping_it_with
;
Verb 'smack'
    * noun 'with' noun  -> A78_smacking_it_with
;

Although the parser shows a preference for items held by the player, this is true in either case. What effect does using "[something preferably held]" actually have?

It’s equivalent to (generates) the held token described in the I6 manual, chapter 31. [things preferably held] is equivalent to multiheld.

1 Like

OK, but the observed behavior doesn’t seem to match up. This is what DM4 section 31 says about the held token:

[held] Matches any single object which is an immediate possession of the actor. (Thus, if a key is inside a box being carried by the actor, the box might match but the key cannot.) This is convenient for two reasons. Firstly, many actions, such as Eat or Wear, only sensibly apply to things being held. Secondly, suppose we have grammar

Verb ’eat’ * held -> Eat;

and the player types “eat the banana” while the banana is, say, in plain view on a shelf. It would be petty of the game to refuse on the grounds that the banana is not being held. So the parser will generate a Take action for the banana and then, if the Take action succeeds, an Eat action. Notice that the parser does not just pick up the object, but issues an action in the proper way – so if the banana had rules making it too slippery to pick up, it won’t be picked up. This is called “implicit taking”, and happens only for the player, not for other actors.

As you say, and as included above, the held token is generated at the I6 level for the grammar of ##A77_whomping_it_with.

But no implicit take is generated by the whomping it with action:

>I
You are carrying:
  an iron key
  a bucket

>DROP IRON
Dropped.

>WHOMP CHEST WITH KEY
Which do you mean, the iron key or the brass key?

>IRON
You whomp the chest with the iron key.

>L
Place
You can see an iron key, a chest (closed) and a brass key here.

So, yes, the effect is to generate a grammar line using the I6 held token, but what effect does that have at the I7 level?

Again, the ##A78_smacking_it_with action does generate implicit takes, even though its grammar line lacks the I6 held token:

>SMACK CHEST WITH KEY
Which do you mean, the iron key or the brass key?

>BRASS
(first taking the brass key)
You smack the chest with the brass key.

Do you see what I mean? The implicit take behavior seems to be governed by the use of “carried thing” when defining the action, and nothing else.

1 Like

According to the documentation:

17.4. Standard tokens of grammar

Understand “wear [something preferably held]” as wearing.

Here we expect that the named item will be one that is held by the player, and the parser will use this to resolve ambiguities between names of things carried and not carried.

This is apparently a little misleading, as the parser prefers to match held items over others regardless of whether the “preferably held” token is present or not. I can’t find any difference in behaviour between [something] and [something preferably held].

I have found one effect, though; the token [things preferably held] affects the behaviour of “all”. (So “drop all” does not attempt to drop items which are not held.)

3 Likes

Thank you, @jrb. I’m very glad that someone else is seeing the same behavior!

Looking at the generated code a little more, I notice that the ActionData table array includes:

##A77_whomping_it_with  $$00011011  OBJECT_TY OBJECT_TY  0 20077
##A78_smacking_it_with  $$10011011  OBJECT_TY OBJECT_TY  0 20078

Note the difference in the high bit setting. That looks like:

Constant CARRY_SECOND_ABIT = $$10000000;

So in 6M62 the implicit take behavior is driven by ActionData settings and not the grammar token, then? A significant improvement, I would think, but I’m still curious about what "[something preferably held]" is doing (or is supposed to do) at the I7 level.

Most of the time, the standard rules don’t enforce implicit takes at the grammar level at all.

Instead, they have a Check rule that does something like this:

	if the noun is portable and the actor is not carrying the noun:
		carry out the implicitly taking activity with the noun;
		if the actor is not carrying the noun, stop the action.

This allows some reactions to occur even when the item isn’t carried – for example, while normally it makes sense that you should take a glass or bottle before drinking from it, there might also be a water fountain in the story (which obviously you can’t take, but you should still be able to drink from).

The docs say that it affects implicitly taking, but that’s not true because that’s either done as above or is controlled by applying to one carried thing etc. The docs imply that you need to both say carried thing as well as preferably held, but that’s not true – only the first matters.

So theoretically all that’s left is to influence the parser’s ambiguity resolution, but it does appear that regardless it does prefer things held regardless of whether you use that token or not. (And there doesn’t appear to be any kind of something preferably not held token, which might be useful in other cases.)

I wonder if perhaps this has just been the victim of a compiler change and doesn’t serve any real purpose any more.

1 Like

So, I did some research in the 6M62 Standard Rules. The following actions make use of "[something preferably held]" in their grammar lines (in order of appearance):

  • taking off
  • wearing
  • throwing it at
  • giving it to
  • showing it to
  • unlocking it with
  • locking it with
  • eating

The actions that have the kind of check rule that you’re talking about are (in order of appearance):

  • putting it on (can’t put what’s not held rule)
  • inserting it into (can’t insert what’s not held rule)
  • eating (can’t eat portable food without carrying it rule)

The only action on both lists is the eating action, strangely. (I vaguely recall seeing a bug report about not being able to eat scenery things at one point. Perhaps a change in response to that?)

From the ActionData table (alphabetized and aligned, preceded by bit mask constants, asterisks added):

Constant CARRY_NOUN_ABIT   = $$01000000;
Constant CARRY_SECOND_ABIT = $$10000000;

##Disrobe  $$00001001  OBJECT_TY OBJECT_TY  0 20023 *
##Eat      $$00001001  OBJECT_TY OBJECT_TY  0 20006 *
##Give     $$01011011  OBJECT_TY OBJECT_TY  0 20024
##Insert   $$00011011  OBJECT_TY OBJECT_TY  0 20005 *
##Lock     $$10011011  OBJECT_TY OBJECT_TY  0 20016
##PutOn    $$00011011  OBJECT_TY OBJECT_TY  0 20004 *
##Show     $$01011001  OBJECT_TY OBJECT_TY  0 20025
##ThrowAt  $$01011001  OBJECT_TY OBJECT_TY  0 20027
##Unlock   $$10011011  OBJECT_TY OBJECT_TY  0 20017
##Wear     $$01001001  OBJECT_TY OBJECT_TY  0 20022

Those marked by asterisks lack enforcement at the ActionData level, i.e. they are not defined as requiring carried thing for either noun or second noun (alphabetized):

Eating is an action applying to one thing.
Inserting it into is an action applying to two things.
Putting it on is an action applying to two things.
Taking off is an action applying to one thing.

By comparison, the rest (alphabetized):

Giving it to is an action applying to one carried thing and one thing.
Locking it with is an action applying to one thing and one carried thing.
Showing it to is an action applying to one carried thing and one visible thing.
Throwing it at is an action applying to one carried thing and one visible thing.
Unlocking it with is an action applying to one thing and one carried thing.
Wearing is an action applying to one carried thing.

All very interesting, especially the presence of the two distinct styles of control in the Standard Rules… but none of it answers my original question! You may be right – it’s just an area that’s undergoing change.

I’d write this up as a suggestion for improving the documentation, but the Mantis bug tracker has been down for months now.

1 Like

It pretty much causes the parser to favour held things over those that aren’t in the Adjudicate function. The same thing happens with “a carried thing” but to a much smaller degree. You can see this here with this slightly adjusted version of your starting code.

"Preferably Held"

Include (-

[ Adjudicate context i j k good_ones last n ultimate flag offset;
    #Ifdef DEBUG;
    if (parser_trace >= 4) {
        print "   [Adjudicating match list of size ", number_matched,
        	" in context ", context, "^";
        print "   ";
        if (indef_mode) {
            print "indefinite type: ";
            if (indef_type & OTHER_BIT)  print "other ";
            if (indef_type & MY_BIT)     print "my ";
            if (indef_type & THAT_BIT)   print "that ";
            if (indef_type & PLURAL_BIT) print "plural ";
            if (indef_type & LIT_BIT)    print "lit ";
            if (indef_type & UNLIT_BIT)  print "unlit ";
            if (indef_owner ~= 0) print "owner:", (name) indef_owner;
            new_line;
            print "   number wanted: ";
            if (indef_wanted == INDEF_ALL_WANTED) print "all"; else print indef_wanted;
            new_line;
            print "   most likely GNAs of names: ", indef_cases, "^";
        }
        else print "definite object^";
    }
    #Endif; ! DEBUG

    j = number_matched-1; good_ones = 0; last = match_list-->0;
    for (i=0 : i<=j : i++) {
        n = match_list-->i;
        match_scores-->i = good_ones;
        ultimate = ScopeCeiling(n);

        if (context==HELD_TOKEN && parent(n)==actor)
        {   good_ones++; last=n; }
        if (context==MULTI_TOKEN && ultimate==ScopeCeiling(actor)
            && n~=actor && n hasnt concealed && n hasnt scenery) 
        {   good_ones++; last=n; }
        if (context==MULTIHELD_TOKEN && parent(n)==actor)
        {   good_ones++; last=n; }

        if (context==MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN)
        {   if (advance_warning==-1)
            {   if (context==MULTIEXCEPT_TOKEN)
                {   good_ones++; last=n;
                 }
                if (context==MULTIINSIDE_TOKEN)
                {   if (parent(n)~=actor) { good_ones++; last=n; }
                 }
            }
            else
            {   if (context==MULTIEXCEPT_TOKEN && n~=advance_warning)
                {   good_ones++; last=n; }
                if (context==MULTIINSIDE_TOKEN && n in advance_warning)
                {   good_ones++; last=n; }
            }
         }
        if (context==CREATURE_TOKEN && CreatureTest(n)==1)
        {   good_ones++; last=n; }
        
        match_scores-->i = 1000*(good_ones - match_scores-->i);
    }
!    if (good_ones == 1) {
!		if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0 &&
!			context == MULTI_TOKEN or MULTIHELD_TOKEN or
!				MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) {
!	        BeginActivity(DECIDING_WHETHER_ALL_INC_ACT, last);
!            if ((ForActivity(DECIDING_WHETHER_ALL_INC_ACT, last)) &&
!            	(RulebookFailed())) good_ones = 0;
!	        EndActivity(DECIDING_WHETHER_ALL_INC_ACT, last);
!			if (good_ones == 1) return last;
!		} else {
!			return last;
!		}
!    }

    ! If there is ambiguity about what was typed, but it definitely wasn't
    ! animate as required, then return anything; higher up in the parser
    ! a suitable error will be given.  (This prevents a question being asked.)

    if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0;

    if (indef_mode == 0) indef_type=0;

    ScoreMatchL(context);
    if (number_matched == 0) return -1;

    if (indef_mode == 0) {
        !  Is there now a single highest-scoring object?
        i = SingleBestGuess();
        if (i >= 0) {

            #Ifdef DEBUG;
            if (parser_trace >= 4) print "   Single best-scoring object returned.]^";
            #Endif; ! DEBUG
            return i;
        }
    }

    if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) {
        if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN
                     or MULTIINSIDE_TOKEN) {
            etype = MULTI_PE;
            return -1;
        }
        i = 0; offset = multiple_object-->0;
        for (j=BestGuess(): j~=-1 && i<indef_wanted && i+offset<MATCH_LIST_WORDS-1:
        	j=BestGuess()) {
            flag = 0;
            BeginActivity(DECIDING_WHETHER_ALL_INC_ACT, j);
            if ((ForActivity(DECIDING_WHETHER_ALL_INC_ACT, j)) == 0) {
                if (j hasnt concealed && j hasnt worn) flag = 1;
                if (context == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN && parent(j) ~= actor)
                    flag = 0;

                if (action_to_be == ##Take or ##Remove && parent(j) == actor)
                    flag = 0;

                k = ChooseObjects(j, flag);

                if (k == 1)
                    flag = 1;
                else {
                    if (k == 2) flag = 0;
                }
            } else {
                flag = 0; if (RulebookSucceeded()) flag = 1;
            }
            EndActivity(DECIDING_WHETHER_ALL_INC_ACT, j);
            if (flag == 1) {
                i++; multiple_object-->(i+offset) = j;
                #Ifdef DEBUG;
                if (parser_trace >= 4) print "   Accepting it^";
                #Endif; ! DEBUG
            }
            else {
                i = i;
                #Ifdef DEBUG;
                if (parser_trace >= 4) print "   Rejecting it^";
                #Endif; ! DEBUG
            }
        }
        if (i < indef_wanted && indef_wanted < INDEF_ALL_WANTED) {
            etype = TOOFEW_PE; multi_wanted = indef_wanted;
			 if (parser_trace >= 4) print "Too few found^";
            multi_had=i;
            return -1;
        }
        multiple_object-->0 = i+offset;
        multi_context = context;
        #Ifdef DEBUG;
        if (parser_trace >= 4)
            print "   Made multiple object of size ", i, "]^";
        #Endif; ! DEBUG
        return 1;
    }

    for (i=0 : i<number_matched : i++) match_classes-->i = 0;

    n = 1;
    for (i=0 : i<number_matched : i++)
        if (match_classes-->i == 0) {
            match_classes-->i = n++; flag = 0;
            for (j=i+1 : j<number_matched : j++)
                if (match_classes-->j == 0 && Identical(match_list-->i, match_list-->j) == 1) {
                    flag=1;
                    match_classes-->j = match_classes-->i;
                }
            if (flag == 1) match_classes-->i = 1-n;
        }
     n--; number_of_classes = n;

    #Ifdef DEBUG;
    if (parser_trace >= 4) {
        print "   Grouped into ", n, " possibilities by name:^";
        for (i=0 : i<number_matched : i++)
            if (match_classes-->i > 0)
                print "   ", (The) match_list-->i, " (", match_list-->i, ")  ---  group ",
                  match_classes-->i, "^";
    }
    #Endif; ! DEBUG

    if (indef_mode == 0) {
        if (n > 1) {
            k = -1;
            for (i=0 : i<number_matched : i++) {
                if (match_scores-->i > k) {
                    k = match_scores-->i;
                    j = match_classes-->i; j = j*j;
                    flag = 0;
                }
                else
                    if (match_scores-->i == k) {
                        if ((match_classes-->i) * (match_classes-->i) ~= j)
                            flag = 1;
                    }
            }

        if (flag) {
            #Ifdef DEBUG;
            if (parser_trace >= 4) print "   Unable to choose best group, so ask player.]^";
            #Endif; ! DEBUG
            return 0;
        }
        #Ifdef DEBUG;
        if (parser_trace >= 4) print "   Best choices are all from the same group.^";
        #Endif; ! DEBUG
        }
    }

    !  When the player is really vague, or there's a single collection of
    !  indistinguishable objects to choose from, choose the one the player
    !  most recently acquired, or if the player has none of them, then
    !  the one most recently put where it is.

    if (n == 1) dont_infer = true;
    return BestGuess();

]; ! Adjudicate

-) instead of "Adjudicate" in "Parser.i6t".

Place is a room.

A locked container called a chest is in Place. A fabulous jewel is in the chest.

A brass key is in Place.

The player carries an iron key.

To decide whether the current action requires a carried noun: (- NeedToCarryNoun() -).

To decide whether the current action requires a carried second noun: (- NeedToCarrySecondNoun() -).

Whomping it with is an action applying to two things. Understand “whomp [something] with [something preferably held]” as whomping it with. [does not generate implicit take because lacking "carried thing"?]

Smacking it with is an action applying to one thing and one carried thing. Understand “smack [something] with [something]” as smacking it with. [generates implicit take even without preferably held"]

Before whomping something with something: 
if the current action requires a carried noun begin;
say "CARRIED NOUN FOR WHOMPING!";
otherwise;
say "UNCARRIED NOUN FOR WHOMPING!";
end if;
if the current action requires a carried second noun begin;
say "CARRIED SECOND NOUN FOR WHOMPING!";
otherwise;
say "UNCARRIED SECOND NOUN FOR WHOMPING!";
end if.

Before smacking something with something: 
if the current action requires a carried noun begin;
say "CARRIED NOUN FOR SMACKING!";
otherwise;
say "UNCARRIED NOUN FOR SMACKING!";
end if;
if the current action requires a carried second noun begin;
say "CARRIED SECOND NOUN FOR SMACKING!";
otherwise;
say "UNCARRIED SECOND NOUN FOR SMACKING!";
end if.

To smack is a verb. To whomp is a verb.

Report smacking something with something: say "[We] [smack] [the noun] with [the second noun].".

Report whomping something with something: say "[We] [whomp] [the noun] with [the second noun].".

Test me with "trace 4 / whomp chest with key / drop key / whomp chest with key / take brass key / whomp chest with key / drop key / smack chest with key / take brass key / smack chest with key / trace 0".

With “[something preferably held]”, held items get an extra 1000 points while with “a carried thing” it’s only 20 points. It also seems like with “[something preferably held]”, the carrying is optional but with “a carried thing” it’s required.

Dropping, inserting it into, putting it on are the best examples where “[something preferably held]” would be preferable over “a carried thing”, otherwise you could end up taking something just to drop it back on the floor where it was, insert it back into the container it was in or put it back into the supporter it was on.

Hope this helps.

1 Like

Ah, Adjudicate(). I’m a little familiar with that routine in the I6 Standard Library, but this particular wrinkle must be an I7 refinement.

Thank you for pointing this out! I haven’t yet traced through the generated I6 code myself, but it definitely makes sense, and I’ll take your word for it.