Grouping together kinds of things

I’ve really given this my best shot… I thought the below might work, but it just seems to group everything into one group. How can I get it so that when things are being listed of the same kind and with the same printed name that they are grouped together with a text that reflects their kind?

To say the kind of (O - an object): (-
	if ({O} provides KD_Count) {
		print (I7_Kind_Name) (KindHierarchy-->(({O}.KD_Count) * 2));
	} else {
		print "object";
	}
-).

The kitchen is a room.

The player is in the kitchen.

Understand the printed name property as describing a thing.

An apple is a kind of thing.

An orange is a kind of thing.

A thing can be groupable. A thing is usually not groupable.

A thing can be already-grouped. A thing is usually not already-grouped.

Before listing contents when not taking inventory:
	repeat with x running through list of things that are marked for listing:
		if x is not already-grouped:
			repeat with y running through list of things that are marked for listing:
				if "[the kind of y]" is "[the kind of x]" and "[the printed name of y]" is "[the printed name of x]":
					now y is already-grouped;
					now y is groupable;
			group groupable things together as "[the kind of x]";
		now every thing is not groupable;
        now every thing is not already-grouped;

Rule for grouping together things while listing contents:
	say "some [the printed name of the item described][s]";

apple1 is an apple in the kitchen. the printed name of apple1 is "red apple".

apple2 is an apple in the kitchen. the printed name of apple2 is "red apple".

orange1 is an orange in the kitchen. the printed name of orange1 is "orange".

orange2 is an orange in the kitchen. the printed name of orange2 is "orange".

The above always returns all objects as “some red apples”, instead of saying “some red apples and some oranges” like I would like it to.

Comparing texts with substitutions for equality doesn’t evaluate them.

In fact the thing that you need there is “matches the text…” rather than “is.” That’s what kept getting me in the last thing we were talking about.

Aside from that, there’s a few issues:
There’s nothing to prevent x and y from being the same thing. So every object is going to get matched as itself and marked as groupable.
Also, when you match x to y, you mark y as groupable but not x. x should be marked as groupable too.
…I also thought that you were grouping every single groupable thing together, but it looks like that might have been because something went funny in the indentation in the code and I restored the logic wrong.

However, once I do something that tries to fix all that stuff, I still can’t get the oranges to group together. Grouping is pretty mysterious to me (and I actually have something in one of my projects where I need to solve a similar problem, or rather right now I don’t solve the problem and I’m hoping no one will notice; search for the word “bugs”). I will think about it.

OK, I’m stumped. Here’s what I have right now (the definition of homography had to be broken down to avoid an internal error):

Lab is a room.

To say the kind of (O - an object): (-
	if ({O} provides KD_Count) {
		print (I7_Kind_Name) (KindHierarchy-->(({O}.KD_Count) * 2));
	} else {
		print "object";
	}
-).

The kitchen is a room.

The player is in the kitchen.

Understand the printed name property as describing a thing.

An apple is a kind of thing.

An orange is a kind of thing.

A thing can be already-grouped. A thing is usually not already-grouped.

To decide whether (X - a thing) has the same kind name as (Y - a thing): if "[the kind of y]" exactly matches the text "[the kind of x]", decide yes.

To decide whether (X - a thing) has the same printed name as (Y -  a thing): if "[the printed name of y]" exactly matches the text "[the printed name of x]", decide yes.

Homography relates a thing (called X) to a thing (called Y) when X has the same kind name as Y and X has the same printed name as Y. The verb to be homographic with means the homography relation.

Before listing contents when not taking inventory:
	repeat with x running through list of things that are marked for listing:
		[say "Testing [printed name of x]; [if x is already-grouped]already-grouped[end if]; [if x is groupable]groupable[end if].";]
		[showme L;]
		if x is not already-grouped:
			if something that is not x is homographic with x:
				group things that are homographic with x together;
				now everything that is homographic with x is already-grouped;
	now every thing is not already-grouped;

Rule for grouping together something (called the item):
	say "some ([listing group size]) [the printed name of the item][s]";

apple1 is an apple in the kitchen. the printed name of apple1 is "red apple". The printed plural name of apple1 is "red apples".

apple2 is an apple in the kitchen. the printed name of apple2 is "red apple".

orange1 is an orange in the kitchen. the printed name of orange1 is "orange".

orange2 is an orange in the kitchen. the printed name of orange2 is "orange".

…and I’ve confirmed that the “group things together” phrase is getting called on two different occasions, to group the two apples and two oranges together. But when it comes time to group them together in the listing, it’s still grouping all four things as red apples. Anyone who understands this stuff know why that is? If you just say “group oranges together” and “group apples together” then the groups print separately as they should. The code for grouping things together is some I6 stuff that I can’t begin to puzzle out.

I don’t think I’ve made any progress past dootdoot’s original obstacle; feels like I’m banging my head against the same problem.

The grouping mechanism in Inform’s lister goes back to I6. It doesn’t behave the way you expect. Actually I think it’s buggy in 6L02 – not completely adapted to the new string system.

I won’t get into the full story, but you want to use the phrase “group (DESC) together as (TEXT)” for this case. This should group items with identical texts. However, it doesn’t seem to work right if the strings have substitutions. (Even if you use “the substituted form of…”)

I should point out that the low-level grouping mechanism already operates on equivalence classes, which is what you want. The way this should work is that you assign the I6 property list_together to be the same as the object’s printed name, for every object, and then you get the right result. But this seems impossible using the current I7 API.

I had wondered if something in the way we were grouping things was making our groups get munged together. Anyway, this allows for a truly ugly kludge, since when we come to the rule for grouping things together we throw out the “group together as” text anyway:

Lab is a room.

To say the kind of (O - an object): (-
	if ({O} provides KD_Count) {
		print (I7_Kind_Name) (KindHierarchy-->(({O}.KD_Count) * 2));
	} else {
		print "object";
	}
-).

The kitchen is a room.

The player is in the kitchen.

Understand the printed name property as describing a thing.

An apple is a kind of thing.

An orange is a kind of thing.

A thing can be already-grouped. A thing is usually not already-grouped.

To decide whether (X - a thing) has the same kind name as (Y - a thing): if "[the kind of y]" exactly matches the text "[the kind of x]", decide yes.

To decide whether (X - a thing) has the same printed name as (Y -  a thing): if "[the printed name of y]" exactly matches the text "[the printed name of x]", decide yes.

Homography relates a thing (called X) to a thing (called Y) when X has the same kind name as Y and X has the same printed name as Y. The verb to be homographic with means the homography relation.

Grouping count is a number that varies.

Before listing contents when not taking inventory:
	now grouping count is 1;
	repeat with x running through list of things that are marked for listing:
		[say "Testing [printed name of x]; [if x is already-grouped]already-grouped[end if]; [if x is groupable]groupable[end if].";]
		[showme L;]
		if x is not already-grouped:
			if something that is not x is homographic with x:
				if grouping count is:
				-- 1: group things that are homographic with x together as "a";
				-- 2: group things that are homographic with x together as "b";
				-- 3: group things that are homographic with x together as "c";
				-- 4: group things that are homographic with x together as "d";
				-- 5: group things that are homographic with x together as "e"; [and if we have more than five groups, we just give up grouping as the best of a bad job]
				now everything that is homographic with x is already-grouped;
				increment grouping count;
	now every thing is not already-grouped;

Rule for grouping together something (called the item):
	say "some ([listing group size]) [the printed name of the item][s]";

apple1 is an apple in the kitchen. the printed name of apple1 is "red apple". The printed plural name of apple1 is "red apples".

apple2 is an apple in the kitchen. the printed name of apple2 is "red apple".

orange1 is an orange in the kitchen. the printed name of orange1 is "orange".

orange2 is an orange in the kitchen. the printed name of orange2 is "orange".

And we can increase or decrease the number of cases if we think we might have more (or fewer) groups in any one description.

BTW, it would probably be better to use a printed plural name property instead of relying on sticking an s on the end of the printed name (depending on your use case, I guess, but you can change the printed plural name dynamically). Also if you want things to show up as “an orange” instead of “orange” you should declare “An orange1 is an orange in the kitchen.” And if you want to actually pick up the oranges or anything you need the disambiguation code we were talking about the other day, except for some reason I used “homographic” in different ways in both codes so that needs to change.

Thanks for the explanations, zarf!

I also take it that grouping doesn’t override whatever it is that causes anonymous kind instances to get lumped together? Because this:

[code]A bread slice is a kind of thing. A bread slice can be toasted or untoasted. Before printing the name of a bread slice: say "[if the item described is toasted]toasted[otherwise]untoasted[end if] ".
The indefinite article of a bread slice is “a[if untoasted]n[end if]”.
Before listing contents while taking inventory:
if the player carries more than one toasted bread slice:
group toasted bread slices together as “toasted bread slices”;
if the player carries more than one untoasted bread slice:
group untoasted bread slices together as “untoasted bread slices”.

Before grouping together bread slices: say "[listing group size in words] ".

Kitchen is a room. The player carries two toasted bread slices and two untoasted bread slices.

Test me with “i”. [/code]

still yields “four bread slices” as the contents of the inventory, even though the toasted and untoasted slices are getting grouped together.

I haven’t looked at that bit.

Could you do something like this?

A thing has some text called the list identifier. The list identifier property translates into I6 as "list_together". The list identifier of a thing is usually "[printed name]".

I don’t know.

Thank you again, matt w. This does work. I don’t know if I can use it though this time, as the performance is very slow. I am not complaining, mind you, just pointing it out. It takes forever to “look” a room with this installed. I’m certainly no expert, but this seems to happen with relations a lot, so I try to steer clear of them as much as possible. I think the homography relation requires a lot of processing. Do you think it is possible to limit the scope in some other way to improve performance?

If not, I can live with not grouping things this way, it’s not that big of a deal, but just a cool idea.

Edit:
This seems to help, but I’d still like to make it even better:

change this:

if a something that is not x is homographic with x:

to this:

if a thing that is marked for listing that is not x is homographic with x:

It doubles the performance, at least in my application, to do this… even still though, there’s still an unacceptable lag. I wonder if anything further can be done.

Well, I basically know nothing about the internal dynamics of Inform or writing efficient Inform code. Some things that might help, maybe:

Caching a list of the contents of whatever you’re listing contents of at the beginning so you don’t have to recalculate them every time.
Maybe rewrite the definition of homographic to only check things that are in the room, or marked for listing? You could possibly hack this by keeping the marked for listing cache in a global variable; since the definition of homographic calls to decide whether phrases anyway, you could just put everything in the to decide whether phrase maybe with an immediate “no” if it’s not marked for listing.
Eliminate or reduce the number of calls that loop over all things. I’m never quite sure when a call will loop over all things, but I suspect that “things that are homographic with x” does. The question may be whether there’s any way to write the “group together” phrase without a call that will loop over everything.

When I was working on large-game optimization (see https://intfiction.org/t/improving-performance-for-large-games/6677/1 ) I found that I7’s default model of “set grouping flags every time you list contents” didn’t scale well. I was able to switch that over to “set grouping flags when play begins, leave them alone thereafter.” Big improvement.

Of course (a) that requires some deep I6 hacking, and (b) the kind of game you’re talking about needs to change the grouping flags sometimes, and © I haven’t ported my sample code to 6L02. But it will eventually be worth going down this path if you intend to make this game.

Well, for what it’s worth, I’m no longer doing the text-based workaround (or trying to work with TADS as an alternative)… I’m really kind of wanting to not be stuck building the code base anymore, and trying to write code that deals with content and game-specific mechanics. Otherwise, I’m just getting too frustrated and will lose interest at this point. So, I have to scale back my expectations for the game, unfortunately. I wanted to do lots of programmatically generated random stuff, but that’s just a pipe dream now.

Anyway, the point is, I am not dealing with things that are made only out of text substitutions anymore, but I do still use instances of kinds, as in “An apple is a kind of thing. There are 10 apples in the prop-room.” I’m no longer lumping all things together as “props”, but I’m still not declaring “apple1, apple2, apple3…”. That would be beyond tedious with the kind of game I am still making. There are lots of kinds, and I don’t care to distinguish many of these, because everything of many of the kinds works the same way at all times.

How this relates to the topic post… I end up with rooms like this (not the real items in my game, but this is analagous):

Kitchen
Kitchen description.
You see an apple, an apple, an apple, an apple, an orange, an orange, an orange, a pear, a pear and a pear here.

This kind of sucks, but it sucks less than having to spend any more time trying to learn any new code languages like I6 or TADS and taking away from my time writing creative/content code or having terrible performance. Meh.

Ok, so I’m not trying to intentionally group everything together by kind anymore, but it looks like this is happening “on accident” anyway, but in ways that are inconsistent with the rest of the game. So, in this case, I guess I am asking how to SUPPRESS grouping in these use cases (unless it can be made possible to group them in a better way without the performance hit, but that was discussed above and it doesn’t seem so).

Anyway, the problem is that this code:

The kitchen is a room.

The player is in the kitchen.

A color is a kind of value. Colors are red and green. 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".
A green-apple-code-name is a kind of thing. The printed name of a green-apple-code-name is usually "green apple".

2 red apple-code-names are in the kitchen.
2 green-apple-code-names are in the kitchen.

Generates this result on the first turn:

You can see a red apple, a red apple and two green-apple-code-names here.

When there is a text substitution in the name, it won’t group. When there isn’t, it groups, but it uses the kind name, not the printed name. I never want the kind name to appear, and the grouping should be consistent. How do I get there?

To prevent the kind name from printing you can use the printed plural name property:

The printed plural name of a green-apple-code-name is usually "green apples".

I’m actually getting the (red) apple-code-names grouped together when I run your code; do you have something else in there that might be messing it up?

Thank you for the plural-printed name fix, matt w.

I did have this in an extension… I didn’t ever imagine it could have an impact on this, but this was what was breaking grouping the ones with the substitutions from grouping:

Understand the printed name property as describing a thing.

With my word-parsing script from the other thread, I guess this line is pointless now, so I can remove it. I have no idea why that would interfere with grouping things though. Honestly, I’m clearly no Inform expert, asking lots of questions, but grouping things is a complete mystery.

As for the plural-printed name part… It’s pretty annoying to have to write that for every one. (You have not annoyed, me, to be clear, you have quite helped me, but just having to deal with the plural printed name of each thing manually is the kind of thing I hate having to do).

Edit: This should help

Rule for printing the plural name of something (called x):
	if the printed plural name of x is "[the kind of x]s":
		instead say "[the printed name of x]s";
	if the printed plural name of x is "[the kind of x]es":
		instead say "[the printed name of x]es";
	otherwise:
		say "[the printed plural name of x]";

This is my guess (and it could be wrong, there are a lot of things in here I don’t remember):

Inform will always group together indistinguishable kind instances, like might be created with “Two rocks are in the Lab” if “rock” is a kind of thing.

But for a lot of purposes (for instance disambiguation, I think), if you’re understanding a property as describing a thing then Inform won’t treat the kind instances as distinguishable any more…

…and as we found out earlier in the thread, it has trouble figuring out whether two instances of " apple" are the same when the color of the apples are the same. Maybe they even don’t count as the same because they’re different text functions which evaluate the colors of different apples.

So it’s not shocking to me that that understand line would mess up grouping when there’s a text substitution in the printed name. It wouldn’t have been shocking to me if it didn’t, either. This is all very mysterious.