Singular Topic Matching Plural

This is something that keeps annoying me. I have several instances where the player can ask about a broad, plural topic, or a specific instance. For example, they could ask about ‘cats’ or about ‘the missing cat’.

The trouble is, when I type, >ASK ABOUT CAT, I’m getting the response for ‘cats’. But I think a player doing this is likely to expect that the singular ‘cat’ would be a reference to the missing cat, and not to cats in general. Obviously I can get around this by setting a higher matchScore for the AskTellTopic for the missing cat, but I don’t want to do this for every single NPC. I just want TADS to assume that ‘cat’ means ‘cat’ and ‘cats’ means ‘cats’.

Is there something I can do on the missing cat object to make more likely to be matched? Or is there some other way to get some sensible behaviour out of this?

Hi, Pacian:

If I understand your question properly…

I just tested this code, and it works fine:

[code]questionSubjects1: Topic ‘kid’;

++ GoOutside1 : AskTellTopic @questionSubjects1
"Can I go outside to play in the snow with one kid? you ask.
<.p>Yes, but dress warmly. "
;

questionSubjects2: Topic ‘outside snow kids children friends’;

++ GoOutside2 : AskTellTopic @questionSubjects2
"Can I go outside to play in the snow with the other kids? you ask.
<.p>Yes, but dress warmly. "
;
[/code]

Jeff

Now try this one:

[code]
+Someone: Person ‘someone’ ‘someone’
isProperName=true
;

++ GoOutside1 : AskTellTopic @topicElephants
"Can I go outside to play in the snow with the elephants? you ask.
<.p>Okay, but only if the bad elephant isn’t there. ";
;

++ GoOutside2 : AskTellTopic @topicBadElephant
"Can I go outside to play in the snow with the bad elephant? you ask.
<.p>No, she would steal your peanuts. "
;

topicElephants: Topic ‘elephants’;

topicBadElephant: Topic ‘bad elephant’;[/code]
‘Cat’ and ‘kid’ were bad examples because they’re only three letters, but TADS can potentially accept ‘elephant’ for ‘elephants’ (or for that matter ‘elepha’ or anything else more than five letters long).

For me the above code gives:

Now here’s where it gets really interesting - when I just made this example by changing your code, ‘a elephant’ asked about the bad elephant. But by changing the order of the topics, I got it to stop working. And in fact, changing which topic comes first and which topic is GoOutside1 or GoOutside2 (or whether they’re anonymous or not) seems to change which topic is chosen as the response to ‘elephant’.

Now it’s possible I can do this kind of tweaking in my WIP, but can I expect it to behave consistently in other interpreters or on other people’s computers? I know that later AltTopics are given more precendence, is the opposite true of AskTellTopics? Or as I suspect, is TADS just treating elephant and elephants as equivalent words, and it’s a matter of pot-luck which topic gets set up as the favourite?

And back to my original question, what do I do to reliably give a topic precedence for the singular form of its name, without having to set a higher matchScore on each individual AskTellTopic referring to it?

That sounds like a parser bug to me. You’d better report it. (I know there have been other bugs with plurals and truncation.)

Until it’s fixed, you can make TADS pay attention to the whole word by turning off truncation. Just set gameMain.parserTruncLength to nil.

I would expect this to behave consistently across computers and interpreters, but like you, I’d be a bit wary of using source code order to determine the outcome.

What’s happening internally is that TopicDatabase.findTopicResponse() looks through all the available TopicEntries that could match the player input and returns the one with the highest matchScore. While it’s running through the list of relevant topics it notes any TopicEntry that matches that has a matchScore higher than the previous best matchScore found, which means that in the event of a tied matchScore it’ll return the first TopicEntry it finds with that matchScore. In this case which TopicEntry it’ll end up choosing will thus depend on which order the TopicEntries are stored in the relevant list (e.g. of AskTopics and TellTopics), which in turn is probably dependent on source text order. I’d expect this behaviour to be determinate once a game has been compiled (and hence interpreter independent), but not something I’d particularly want to rely on when writing a game.

I’m not sure whether it’s technically a bug or simply a consequence of the way parser truncation works. In this example ‘elephants’ isn’t a plural so far as the parser is concerned (it doesn’t follow an asterisk in the vocabWords property), it’s simpler a longer noun than ‘elephant’, so it may be unrelated to other parser bugs with plurals. That said, this probably is worth reporting as a bug (even if a change might technically be a feature request) since the behaviour is clearly undesirable. What’s less obvious to me is whether there’s a clearly preferable behaviour, e.g. if truncation is allowed and the player can type (the obviously ambiguous):

A ELEPHA

Should that be treated differently from

A ELEPHANT
?

Well, perhaps it should: I suppose the ideal behaviour would be for the parser always to prefer an exact match over a truncated match. Would this be a bug-fix or a feature change?

That would seem to be the best approach (an alternative would be to set gameMain.parserTruncLength to a sufficiently large number to distinguish any objects that need to be distinguished in your game, which allowing the odd very long word to be abbreviated to, say, the first nine characters, but in practice you may as well disable truncation altogether).

– Eric

Well, it might be overkill, but…

See the GameMainDef class

GameMainDef includes a property parserTruncLength

If you want to see the defaults used by TADS 3

and

Jeff

I will do so.

Thank you. I did wonder if that might be possible.

Although the truncation always seemed like a cool idea to me, it’s not something I expect players to really use. And I think it’s kind of polite not to give items/people long names in the first place, just for those people who don’t realise they can do this.

I think this is kind of what I expected TADS to do. But as you say, useability issues frequently fall into a grey area of what’s bug and what’s feature.

It seems pretty clear to me that TADS should prefer an exact match - and in most cases it does, doesn’t it? Suppose your game contains a button and a buttonhole and the user types X BUTTON. That will always select the button, rather than the buttonhole. If the player ASKs JOE ABOUT BUTTON: A) she almost certainly mean the button, not the buttonhole; and B) even if she doesn’t (perhaps she hasn’t noticed the button’s existence) it should behave the same way as other cases.

This is a tricky one. The basic problem is that topic match ranking order is by design done on the Topic (database) side rather than the input side. The parser explicitly treats truncation as irrelevant for topic matching because of this - the input matching doesn’t get to do any ranking of topics, so the parser’s choices for a given word match are “keep” or “discard”, and it always chooses “keep” so that the topic database ranker will have a chance to decide when we get to that point.

The topic database ranking is all oriented around the strength of the contextual match - that’s what the matchScore is meant to represent. It’s difficult to also incorporate the strength of the textual match, since that introduces a second dimension of rankings.

My proposal would be something like this. Currently, in the topic database searcher, the first step is that we look for the active Topics that match the input, and we keep the active Topic with the highest matchScore. In the situation we’re talking about here, there are multiple topics (the “elephant” match and the “elephants” match) with equal scores. When there are ties, the searcher currently just picks the first one it finds, which is why you’ve observed that it’s sensitive to source order. My proposal is to make a list of the Topics that are tied for best score, and then submit these to a game-defined tie-breaker function.

My reason for going this route, rather than adding general-purpose truncation ranking, is that I don’t think truncation is necessarily going to be a sufficiently general selection principle for cases like this. I think it’s more likely that you’ll want to check for the presence or absence of certain words, the order of the words, etc - things that would be really hard to encode into a general framework. I think it’ll be a lot easier as an author if you can just write some one-off custom code to handle cases as they come up.

Assuming this is the right approach, the next question is where to put the tie-breaker function. It could just be a top-level function that takes a list of topics and the input vocabulary, and returns the winner. Or, it could be a method on the individual Topic objects: we’d call it (with the same arguments as for the function) on each Topic in the list; the method could return an object to propose a winner, or return nil to say it doesn’t care. The question is what happens if we get two or more different winners. I suppose if we just pick one arbitrarily we’ll be no worse off than we are now, and in all likelihood this just won’t come up since it’s going to be custom code to tweak a special situation anyway.

Let me know what you think.