How to make Inform not treat numbers as referring to multiple objects?

I have several objects with numbers as their primary identifier (a 10-cent coin, for instance).

But any action that uses the [things] or [other things] grammar tokens will understand a number such as ‘10’ to refer to how many things to attempt the current action on:

"Sandbox" by Brian Rushton

Testing is a room.

The cat, dog, goat, and cow are in testing.

The box is an open container in testing. The box is fixed in place.

Understand "4" as the cow.

Test me with "take all/put 4 in box/x 4"

Here, ‘put 4 in box’ will put the cat, dog, goat, and cow into the box.

I can fix this with code like this (I disconnect all meaning from commands that are synonyms of ‘insert it into’ then redefine them without the [things] token):

Understand the command "put" as something new.
Understand the command "insert" as something new.
Understand the command "drop" as something new.
Understand the command "discard" as something new.

Understand "put on [something preferably held]" as wearing.
Understand "put [something preferably held] on" as wearing.
Understand "put down [things preferably held]" as dropping.
Understand "put [things preferably held] down" as dropping.
Understand "put [something] in/inside/into [something]" as inserting it into.
Understand "put [other things] on/onto [something]" as putting it on.
Understand "insert [something] in/into [something]" as inserting it into.
Understand "drop [things preferably held]" as dropping.

(this is just a partial list I’ll finish if there’s no other way).

This causes problems because at other points in the story, the player needs to dump everything into a locker, so we need ‘inserting it into’ to accept the ‘things’ token. My current plan is to understand “insert [other things] into [something]” and its synonyms as inserting it into only when the player is not in the room with the coin-operated machine.

This seems like a very messy way to solve the problem and prone to bugs. Is there any way to just disable the feature that makes inform take a number like ‘4’ and pick 4 random objects in the inventory? RULES and ACTIONS are no help here.

3 Likes

Hmm, I fixed it with this:

Understand the command "put" as something new.
Understand the command "insert" as something new.
Understand the command "drop" as something new.

Understand "put on [something preferably held]" as wearing.
Understand "put [something preferably held] on" as wearing.
Understand "put down [things preferably held]" as dropping.
Understand "put [things preferably held] down" as dropping.
Understand "put [something] in/inside/into [something]" as inserting it into.
Understand "put [other things] on/onto [something]" as putting it on.
Understand "insert [something] in/into [something]" as inserting it into.
Understand "drop [things preferably held]" as dropping.
Understand "drop [something preferably held] at/against [something]" as throwing it at.
Understand "drop [something] in/into/down [something]" as inserting it into.
Understand "drop [other things] on/onto [something]" as putting it on.

Definition: a thing is nonslot:
	if it is the coin-slot, decide no;
	if it is the crown-slot, decide no;
	decide yes;

Understand "put [other things] in/inside/into [a nonslot thing]" as inserting it into.
Understand "insert [other things] in/into [a nonslot thing]" as inserting it into.
Understand "drop [other things] in/into/down [a nonslot thing]" as inserting it into.

This keeps the functionality of ‘put all in _____’ for things but prevents the player from trying to put 20 things into coin slots specifically.

4 Likes

I tried this. It seems to pretty much work: I only saw screwy behavior where the default behavior is also screwy, i.e., this seemed to work ok for the handling the ten-cent coin case and to fail no badly than Inform would otherwise in other cases.

lab is room.

The ten-cent coin is in the lab.
understand "ten", "cent", "cents" as the ten-cent coin.

rock is a kind of thing.
there are 20 rocks in lab.

box is container in lab.

player holds toy, spam, bill, jack.

The command-text is a text that varies.
To set the/-- command text: now command-text is the substituted form of "[the player's command]".

To set/-- the command text to (t - text):
  now command-text is the substituted form of t;
  change the text of the player's command to command-text.
To update the/-- command text: set the command text to command-text.

First after reading a command: set the command text.
after reading a command:
replace the regular expression "(^| )10( |$)" in command-text with " ten ";
update command text.

for deciding whether all includes something (called the item) when command-text matches the regular expression "(^| )ten( |$)" and the ten-cent coin is in sight of the player: 
  if the item is the ten-cent coin, it does;
  else it does not;

To decide whether (X - thing) is in/within sight of (Y - thing):
  if the common ancestor of X with Y is nothing, decide no;
  if Y is enclosed by a closed opaque container that does not enclose X, decide no;
  if X is enclosed by a closed opaque container that does not enclose Y, decide no;
  decide yes.
2 Likes

Ooh, this looks really nice! Does using written out words not have the same bad behavior as numerals?

By default, written-out words and numbers work the same. The key is the “deciding whether all includes” rule, which ensures that “ten” will pick up only the ten-cent coin and nothing else when trying to grab ten arbitrary things.

1 Like

Yup.

I instinctively reached for normalizing the input of interest to a single canonical version, thus needing to consider only that one canonical form thereafter, eliminating the possibility of forgetting the need to act on all the possible values of interest in all the other places I might act on it, 'cause there aren’t any more than the one.

Given that I then only act on “ten” in one place, it ends up making this code in particular a little more opaque, but it’s sound practice in the general case…

2 Likes

Not being one to leave something potentially useful alone when I could do something gratuitous and silly with it…

You can’t say drop 1 thing 'cause thing doesn’t match a thing. You can’t say drop 1 things 'cause the parser knows “things” should be plural and treats that like drop all things.

The hackery below lets both of those work.

solitude is initially false.

after reading a command:
if the command-text matches the regular expression "(^| )((1|one) (\S+))" begin;
  let q be the text matching subexpression 2;
  unless q matches the regular expression "s$" begin;
    replace the text q in command-text with "[q]s";
    update the command text;
  end unless;
  now solitude is true;
end if;

multiple action processing when solitude is true:
  let multi be the multiple object list;
  truncate multi to one entry;
  say "([multi])";
  alter the multiple object list to multi;
now solitude is false;
```
1 Like

I’m all for mad science as you know, but doesn’t

Understand "thing/things" as a thing.

do what you want?

3 Likes

Yeah, it does, for the drop 1 thing case. Oops.

drop 1 things still does a drop all in that case. But probably better to singularize that case than pluralize the other if one wants to handle it…

1 Like

Ah, it’s a little more subtle than I thought.

drop 1 thing == drop thing
drop 1 things == drop things

if the player is carrying at least one thing which is a ‘thing proper’ i.e. not a subkind of things, and therefore is assigned the things//p name in I6, i.e. ‘things’ marked as a plural name, then drop 1 things == drop things == drop all things.

if the player is carrying no ‘things proper’ i.e. is carrying only things which are subkinds of thing, and therefore none have the things//p name in I6, drop 1 thing==drop things~=drop all things, i.e. having in this instance not identified ‘things’ as a plural name the parser will want to drop just one item with the ‘singular’ i.e. ‘non-plural’ name ‘things’ (in the same way it would want to drop one stone after drop stone)- and since every thing carried will have the name things as one of its ‘singular’ i.e. ‘non-plural’ names, if the player carries more than one this will provoke a disambiguation- which do you mean...

The corollary of this is that without such an ‘Understand…’ statement, if the player carries only subkinds of thing drop 1 things simply leads to ‘you can’t see any such thing’ or suchlike (since none of the items carried will bear either thing or things as names)

1 Like

It’s even more subtle- the player doesn’t even have to be carrying a ‘thing proper’ for ‘things’ to become universally interpreted as a plural name- a ‘thing proper’ anywhere in the universe is sufficient:

"Dropping_One_Thing" by PB

Lab is a room. A hammer is a thing.

A specimen is a kind of thing.

The player carries a rock and a pebble and a stone.  The rock, the pebble and the stone are specimens.

Understand "thing/things" as a thing.

leads to

Lab

>drop 1 things
rock: Dropped.
pebble: Dropped.
stone: Dropped.

even though the hammer (being the only ‘thing proper’ in the universe) is clearly not only not carried but also out of scope.

This happens because of how the name properties of ‘things improper’ are compiled. ‘Understand “thing/things” as a thing’ compiles an I6 parse_name routine that will match to either the word ‘thing’ or ‘things’. This parse_name routine is inherited by every object that is a thing or a subkind of a thing- meaning that they all will match ‘thing’ and/or ‘things’ as one of their names.

Crucially, after matching ‘thing’ or ‘things’ as a name, the parse_name routine looks up that word in the parser’s dictionary to see if it is a plural, and if so flags to the parser that it has found a plural name. If, but only if, there is at least one ‘thing proper’ in the universe, that ‘thing proper’- in this case the hammer- will have been automatically compiled ‘things//p’ as a plural name and at the same time ‘things’ will have been flagged in the dictionary as being a plural word, and so through this mechanism plurality of ‘things’ as a name propagates from a ‘thing proper’ to all ‘things improper’.

The pieces of code that interact with this stuff are spread across many parts of the parser. It doesn’t sound like you want to disable understanding numbers as numeric descriptors indicating how many of an indefinite object clause are being requested – it sounds like you want to improve the recognition of numbers as applying to certain objects.

There is a routine that has the responsibility to decide whether a number in the command input is better handled as a descriptor or a name word. However, it requires a match of at least one word other than the number in order to make that decision. In the case of >PUT 4 IN BOX, there are no other words to match the noun.

Unfortunately, descriptors are identified before any matching of words to nouns occurs, so in a case like >PUT 4 IN BOX any deployed Understand ... as describing a ... or Understand ... as referring to ... statements can’t take priority as they normally would.

I think it is possible to get the parser to behave like you want with relatively minor surgery at the I6 level, but I’m not sure whether you’re up for that. Are you happy with the solution that you have?

1 Like

My current code patches the specific problem that testers were complaining about, but it can’t hurt to post code for others.

I’ve sworn to never use I6 in my game, since Graham Nelson’s been moving away from it and I like to see what Inform 7 alone can do as an extra challenge. But, like I said, it might be useful to other people!