Understand every word of the printed name?

I built a pretty extensive rule to try to do what the title says, but it is a little hard on performance (not too bad, but a little)… but worse, it get’s really complicated to deal with second (or third) nouns. I thought I posted this somewhere before, but here it is anyway as I can’t find the post I may have shared this in before.

[rant]There’s also some stuff in here to make it work with some other extensions, like Numbered Disambiguation Choices.

[code]
Disambiguate-command is a text that varies.

Disambiguate-flag is a kind of value. Disambiguate-flag are disambiguate-yes and disambiguate-no. A thing has a disambiguate-flag. A thing is usually disambiguate-no. Understand the disambiguate-flag property as describing a thing.

text-disambiguation-applicable is a truth state that varies. text-disambiguation-applicable is true.

Understand “take/look/touch/wear/eat” as “[commands]”.

Understand “take inventory” as “[disambiguate-inapplicable-commands]”.

After reading a command while text-disambiguation-applicable is true:
if the player’s command includes “[commands]” and the player’s command does not include “[disambiguate-inapplicable-commands]”:
if the noun is nothing:
let disambiguate-command be text;
let disambiguateTextOne be “asdfjklimpossiblenever”;
let disambiguateTextTwo be “asdfjklimpossiblenever”;
let disambiguateTextThree be “asdfjklimpossiblenever”;
let command-understood be text;
let command-understood be “[the matched text]”;
now disambiguate-command is “[command-understood]”;
cut the matched text;
let original-command-nouns be text;
let original-command-nouns be the player’s command;
if the player’s command matches the regular expression “\w|\w\p|\p\w”, case insensitively:
let first-match be text;
let first-match be “[the matched text]”;
now disambiguateTextOne is “[first-match]”;
cut the matched text;
if the player’s command matches the regular expression “\w|\w\p|\p\w”:
let second-match be text;
let second-match be “[the matched text]”;
now disambiguateTextTwo is “[second-match]”;
cut the matched text;
if the player’s command matches the regular expression “\w|\w\p|\p\w”:
let third-match be text;
let third-match be “[the matched text]”;
now disambiguateTextThree is “[third-match]”;
cut the matched text;
if disambiguateTextOne is not “asdfjklimpossiblenever”:
let exact-match be a truth state;
now exact-match is false;
repeat with text-disambiguable running through visible things:
if the printed name of text-disambiguable exactly matches the regular expression “(?i)^\b[original-command-nouns]\b?”:
now text-disambiguable is disambiguate-yes;
now exact-match is true;
if text-disambiguable is held by the player and the disambiguate-command is “take”:
now text-disambiguable is disambiguate-no;
now exact-match is false;
if exact-match is false:
repeat with text-disambiguable running through visible things:
if disambiguateTextThree is not “asdfjklimpossiblenever”:
if “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextOne]\b|\b[disambiguateTextOne]\p\b|\b\p[disambiguateTextOne]\b” and “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextTwo]\b|\b[disambiguateTextTwo]\p\b|\b\p[disambiguateTextTwo]\b” and “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextThree]\b|\b[disambiguateTextThree]\p\b|\b\p[disambiguateTextThree]\b”:
now text-disambiguable is disambiguate-yes;
if disambiguateTextTwo is not “asdfjklimpossiblenever” and disambiguateTextThree is “asdfjklimpossiblenever”:
if “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextOne]\b|\b[disambiguateTextOne]\p\b|\b\p[disambiguateTextOne]\b” and “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextTwo]\b|\b[disambiguateTextTwo]\p\b|\b\p[disambiguateTextTwo]\b”:
now text-disambiguable is disambiguate-yes;
if disambiguateTextTwo is “asdfjklimpossiblenever”:
if “[the printed name of text-disambiguable]” matches the regular expression “(?i)\b[disambiguateTextOne]\b|\b[disambiguateTextOne]\p\b|\b\p[disambiguateTextOne]\b”:
now text-disambiguable is disambiguate-yes;
change the text of the player’s command to “[disambiguate-command] disambiguate-yes”;
otherwise:
change the text of the player’s command to “[disambiguate-command] [original-command-nouns]”;
now text-disambiguation-applicable is false;

First every turn:
now every thing is disambiguate-no;
now text-disambiguation-applicable is true;
follow the Numbered Disambiguation Choices reset disambiguables rule.

Rule for printing a parser error:
now every thing is disambiguate-no;
now text-disambiguation-applicable is true;
follow the Numbered Disambiguation Choices reset disambiguables rule;
continue the action;
[/code][/rant]

Anyway, that code works for a single noun, and there aren’t technically any problems with it. However, I really don’t want to deal with building out the logic into that monster for second or third nouns, and I don’t like the performance hit.

What I’m hoping is that there is a much simpler solution I just don’t know how to do myself yet. Something like this psuedo code is what I wish would work:

Understand the printed name property as describing a thing.
Understand any word in the printed name property as describing a thing.

That second bit is what I’m after. So if the printed name of an object was “sphere of unimaginable chaos”… the player should be able to type “look sphere” “look unimaginable” “look chaos” or, even “look of” for all I care, and this should put that object into the parser. Is there any way to do this without writing some monster rule with lots of regular expressions and conditionals?

Hi. I’m as dumb as a post, but what are you trying to achieve over and above:

The big scary book of terror is in the Library. The printed name of the big scary book of terror is "the very big and bulky scary book of terror". Understand "very" or "bulky" as the big scary book of terror.

Sorry if I’m being dense.

If I can speak for dootdoot, the issue is what happens if you change the printed name in the course of play. So the very same I7 thing might at one point have the print name “big scary book of terror” and at another point “sphere of unimaginable chaos” and at another “tome of cute fluffy bunnies,” and we want all the parts of the printed name to be understood no matter what it is.

Anyway, dootdoot, I’m pretty sure the answer is: No. I asked a similar question about dynamically understanding printed names here back in the 6G60 days and was told that it was hard, at least in I7.

In 6L02 things are better at least in that you can actually do “Understand the printed name property as referring to a thing” (you couldn’t do this in 6G60 because of the text/indexed text distinction; the printed name was a text, but only indexed texts could be used in command lines). But AFAICT there is no easy way to winkle out individual words of a property and have them understood by the parser without some monster rule. Every attempt I had to do something like this involved using an After reading a command rule kind of like yours, and I don’t think I ever did any very sophisticated disambiguation.

One possible approach would be to create a bunch of properties “first printed word,” “second printed word,” etc., change the routines that set the printed name to set these properties instead (and setting the printed name to their concatenation), and then writing “Understand the first printed word property as describing a thing” etc. This would require you to restrict printed names to a set word length but that might not be such a bad thing.

I also don’t know how writing a bunch of understand statements referring to text properties would affect performance. My guess is that these understand statements aren’t super-cheap, but as I’ve said before I really don’t understand performance and the internals.

PS you might be amused by this code; now a simple “understand the printed name property as describing a thing” would take care of that, though it was unnecessarily convoluted even so.

I had to be away from my personal computer for a few days, but now that I’m back, I can share what I have on this.

It does look like you have to write a fairly detailed rule to get this to work. My approach was a lot like what you said above, matt w, in that each thing has to have multiple texts to hold each word. I settled for a maximum of 6 here, but it is easy to add more.

Understand the printed name property as describing a thing.

A thing has a text called first-word. A thing has a text called second-word. A thing has a text called third-word. A thing has a text called fourth-word. A thing has a text called fifth-word. A thing has a text called sixth-word.

Understand the first-word property as describing a thing. Understand the second-word property as describing a thing. Understand the third-word property as describing a thing. Understand the fourth-word property as describing a thing. Understand the fifth-word property as describing a thing. Understand the sixth-word property as describing a thing.

A thing can be word-parsed. A thing is usually not word-parsed.

Before reading a command:
	repeat with x running through visible things that are not word-parsed:
		let full-text be the printed name of x;
		repeat with y running from 1 to 6:
			if full-text matches the regular expression "(?i)^\w*\s" or full-text matches the regular expression "(?i)^\w*\p\w*\s" or full-text matches the regular expression "(?i)^\w*$" or full-text matches the regular expression "(?i)^\w*\p\w*$":
				let z be text matching regular expression;
				replace the regular expression "[z]" in full-text with "";
				replace the regular expression "\s" in z with "";
				if y is:
				-- 1: now the first-word of x is z;
				-- 2: now the second-word of x is z;
				-- 3: now the third-word of x is z;
				-- 4: now the fourth-word of x is z;
				-- 5: now the fifth-word of x is z;
				-- 6: now the sixth-word of x is z;
				now x is word-parsed;
			otherwise:
				next;

This is a lot better than my last attempt I think. Although, I really can’t ever tell if in the long run I’ll run out of memory with stuff like this. It works fine in my tests of my “framework”, but I’d hate to get to the point where I’ve built out 90% of a real game based on the assumption that I can deal with understanding using this code, and end up having to scrap it because there isn’t enough room for every thing in the game having 6 texts associated with it. I just don’t know how to predict that without doing real examples in Inform.

Edit: Oh, and yes, in a function that changes the printed name of something, you’d want to add a line that makes the thing also become not word-parsed at that point. matt w is right that that is part of why I want to do this, that things change their printed name. The other part is that I’m “lazy” and want to write programmatic ways for the system to do all that grunt work, not have to write “understand such and such as such and such” for every thing… let the computer figure out what should be understood based on a cascading rule. That’s how I prefer to work, which is why I’m spending over a year just writing a framework that smooths things out (in my opinion) before writing any content.

Be careful. While this method may be easier for you, the resultant game will run a LOT slower (because it will need to do several regular expression comparisons and replacements for every object every turn). It looks like you might be falling prey to a variant of the inner-platform effect.

EDIT: Some of the regular expression replacements there are unnecessary, such as matching against “[z]”. It’s faster to match against z as text than as a regex.

Actually, I’m getting issues with substituted texts with these regex… I am starting to hate substituted texts in Inform. I tried throwing a “substituted form of” in the code somewhere at one point, but this doesn’t seem to help.

Using the above script in addition, this works fine:

The kitchen is a room.

The player is in the kitchen.

An apple-code-name is a kind of thing. The printed name of an apple-code-name is usually  "red apple".

1 apple-code-name is in the kitchen.

But this gives a regex error saying the character range never ends:

The kitchen is a room.

The player is in the kitchen.

Color is a kind of value. Colors are red. A thing has a color.

An apple-code-name is a kind of thing. The printed name of an apple-code-name is usually  " apple".

1 red apple-code-name is in the kitchen.

Edit: @Draconis (posted while I was posting) it will only run every time you come into contact with a kind of thing you haven’t before. Once a thing is “word-parsed,” it won’t trigger this script again until it becomes not word-parsed again (if ever).

Edit Again: Nevermind. I found the problem.
This:

		let full-text be the printed name of x;

Needed to be this:

		let full-text be "[the printed name of x]";

True, but didn’t you say earlier that you planned to “recycle” props that were not visible each turn? It wouldn’t run every turn, but it would run every time you moved (since the props would be brought back and renamed).

Another problem I just noticed: if movement commands and object commands are entered on the same line (“GO NORTH THEN EXAMINE APPLE”), the regexes will run before the movement, so the apple (which is not visible at the start of the turn) will not have its name parsed. It might be better to run this at the end of each turn (in a “last after doing something” rule, perhaps).

The two actions from one command thing could be problematic, but I may disallow this in my game anyway, as there are actually a lot of other checks happening. The sequences are much denser… kind of hard to explain at a high level, but even when the player enters commands successfully, there are a lot of in-game success failure chances for everything you do that “go north and examine apple” may make no sense, as even going north will usually not be something you are guaranteed to be able to accomplish, despite it being a valid direction.

I’m not entirely doing props anymore. I tried and tried, but it looks like doing things different ways just shuffles the burden of performance and memory allocation around, but never solves it. I just resigned myself to doing a smaller game in the final product instead. I am still using assemblies on kinds, and recycling lots of objects to the prop room, but their printed names are fixed unless a specific script were to change them.

I know I’m building an inner-platform. I’ve gone into that intentionally and eyes open. That’s actually my intention… I’m building another “standard rules” on top of or partially in place of the normal standard rules. The kind of game I want to make needs the parser, object model, basic functions and stuff that people might take for granted (there’s a ton of stuff that Inform does that writing from scratch would be insane to do alone)… but the stuff closer to the surface, the stuff that more closely interacts with game mechanic coding, is just not built for the kind of game I want to write. I need to write a sub-system to make it possible.

So far, the performance on this hasn’t been TOO bad, but I am always worried about performance and want things lightning quick… so I am making another attempt at this. This version requires a lot of work on the part of the programmer, but greatly reduces the burden on the application. I have it mostly working, but it seems to be preventing grouping things together from happening again. I still can’t quite get my head around how that is fixed…

Note that if you want to interact with any of these objects in the example code, you will need a strong disambiguation implementation in your build. I can’t share that whole thing here, as it would involve attaching 2 entire extensions and several modifications to said extensions I have made. It does work with those though, in the sense that every word of the printed name is understood. The problem, again, is that the plural printed name never comes into effect because the items never seem to get grouped now.

A thing has a text called first-word. first-word is usually "". A thing has a text called second-word. second-word is usually "". A thing has a text called third-word. third-word is usually "". A thing has a text called fourth-word. fourth-word is usually "". A thing has a text called fifth-word. fifth-word is usually "".  A thing has a text called sixth-word. sixth-word is usually "".

A thing has a text called first-word-plural. The first-word-plural is usually "[first-word]". A thing has a text called second-word-plural. The second-word-plural is usually "[second-word]". A thing has a text called third-word-plural. The third-word-plural is usually "[third-word]". A thing has a text called fourth-word-plural. The fourth-word-plural is usually "[fourth-word]". A thing has a text called fifth-word-plural. The fifth-word-plural is usually "[fifth-word]". A thing has a text called sixth-word-plural. The sixth-word-plural is usually "[sixth-word]".

Understand the first-word property as describing a thing. Understand the second-word property as describing a thing. Understand the third-word property as describing a thing. Understand the fourth-word property as describing a thing. Understand the fifth-word property as describing a thing. Understand the sixth-word property as describing a thing.

Understand the first-word-plural property as describing a thing. Understand the second-word-plural property as describing a thing. Understand the third-word-plural property as describing a thing. Understand the fourth-word-plural property as describing a thing. Understand the fifth-word-plural property as describing a thing. Understand the sixth-word-plural property as describing a thing.

To decide if (x - a text) is not blank-text:
	if x is "":
		no;
	otherwise:
		yes;

The printed name of a thing is usually "[if first-word is not blank-text][first-word][end if][if second-word is not blank-text] [second-word][end if][if third-word is not blank-text] [third-word][end if][if fourth-word is not blank-text] [fourth-word][end if][if fifth-word is not blank-text] [fifth-word][end if][if sixth-word is not blank-text] [sixth-word][end if]".

The printed plural name of a thing is usually "[if first-word-plural is not blank-text][first-word-plural][end if][if second-word-plural is not blank-text] [second-word-plural][end if][if third-word-plural is not blank-text] [third-word-plural][end if][if fourth-word-plural is not blank-text] [fourth-word-plural][end if][if fifth-word-plural is not blank-text] [fifth-word-plural][end if][if sixth-word-plural is not blank-text] [sixth-word-plural][end if]".

The kitchen is a room.

The player is in the kitchen.

A sphere-of-unimaginable-chaos is a kind of thing.

3 sphere-of-unimaginable-chaos are in the kitchen.

The first-word of a sphere-of-unimaginable-chaos is usually "sphere". The second-word of a sphere-of-unimaginable-chaos is usually "of". The third-word of a sphere-of-unimaginable-chaos is usually "unimaginable". The fourth-word of a sphere-of-unimaginable-chaos is usually "chaos".

The first-word-plural of a sphere-of-unimaginable-chaos is usually "spheres".

The description of a sphere-of-unimaginable-chaos is usually "You can't even imagine the chaos this will cause.".

Ok, I made a few other changes, but the core of my question isn’t about those, so I’m going to hide this wall of code under a spoiler.

[spoiler][code]
A thing has a text called first-word. first-word is usually “”. A thing has a text called second-word. second-word is usually “”. A thing has a text called third-word. third-word is usually “”. A thing has a text called fourth-word. fourth-word is usually “”. A thing has a text called fifth-word. fifth-word is usually “”. A thing has a text called sixth-word. sixth-word is usually “”.

A thing has a text called first-word-plural. The first-word-plural is usually “[first-word]”. A thing has a text called second-word-plural. The second-word-plural is usually “[second-word]”. A thing has a text called third-word-plural. The third-word-plural is usually “[third-word]”. A thing has a text called fourth-word-plural. The fourth-word-plural is usually “[fourth-word]”. A thing has a text called fifth-word-plural. The fifth-word-plural is usually “[fifth-word]”. A thing has a text called sixth-word-plural. The sixth-word-plural is usually “[sixth-word]”.

Understand the first-word property as describing a thing. Understand the second-word property as describing a thing. Understand the third-word property as describing a thing. Understand the fourth-word property as describing a thing. Understand the fifth-word property as describing a thing. Understand the sixth-word property as describing a thing.

Understand the first-word-plural property as describing a thing. Understand the second-word-plural property as describing a thing. Understand the third-word-plural property as describing a thing. Understand the fourth-word-plural property as describing a thing. Understand the fifth-word-plural property as describing a thing. Understand the sixth-word-plural property as describing a thing.

To decide if (x - a text) is not blank-text:
if x is “”:
no;
otherwise:
yes;

Printed-name-shunt is a text that varies. Printed-name-shunt is “[if first-word is not blank-text][first-word][end if][if second-word is not blank-text] [second-word][end if][if third-word is not blank-text] [third-word][end if][if fourth-word is not blank-text] [fourth-word][end if][if fifth-word is not blank-text] [fifth-word][end if][if sixth-word is not blank-text] [sixth-word][end if]”.

Printed-plural-name-shunt is a text that varies. Printed-plural-name-shunt is “[if first-word-plural is not blank-text][first-word-plural][end if][if second-word-plural is not blank-text] [second-word-plural][end if][if third-word-plural is not blank-text] [third-word-plural][end if][if fourth-word-plural is not blank-text] [fourth-word-plural][end if][if fifth-word-plural is not blank-text] [fifth-word-plural][end if][if sixth-word-plural is not blank-text] [sixth-word-plural][end if]”.

The printed name of a thing is usually “[printed-name-shunt]”.

The kitchen is a room.

The player is in the kitchen.

A sphere-of-unimaginable-chaos is a kind of thing.

3 sphere-of-unimaginable-chaos are in the kitchen.

The printed plural name of a sphere-of-unimaginable-chaos is usually “[printed-plural-name-shunt]”.

The first-word of a sphere-of-unimaginable-chaos is usually “sphere”. The second-word of a sphere-of-unimaginable-chaos is usually “of”. The third-word of a sphere-of-unimaginable-chaos is usually “unimaginable”. The fourth-word of a sphere-of-unimaginable-chaos is usually “chaos”.

The first-word-plural of a sphere-of-unimaginable-chaos is usually “spheres”.

The description of a sphere-of-unimaginable-chaos is usually “You can’t even imagine the chaos this will cause.”.

Before listing contents:
group sphere-of-unimaginable-chaos together as “[listing group size in words] [printed plural name of a random sphere-of-unimaginable-chaos]”;
[/code][/spoiler]

The important part of my question now is this bit:

Before listing contents:
	group sphere-of-unimaginable-chaos together as "[listing group size in words] [printed plural name of a random sphere-of-unimaginable-chaos]";

This works, for this use case. However, this is kind of ridiculous to have to write for every kind of thing. Is there a way to write it so that before listing contents, every thing that has an identical plural printed name in that list is grouped together as the listing group size of the plural printed names of said things?

I couldn’t figure out how to do this, because I can’t figure out from the documentation how to iterate over the “contents” list. Does it have an accessible name in I7? Is there an entirely better way to do this?