Chimerror Productions

Quoll Work Thread, 2025-02-17 - 2025-02-17 18:50

Tags: blog gamedev inform7 interactive fiction programming quoll testing text adventures

Long time no update, I know, but I have been working on this bug when I have found the energy away from day job, and I am glad that I have decided on a workaround. But I wanted to (re-)explain the issue and how I ended up fixing it for posterity, so here we are…

(WARNING: This post contains slight optional puzzle spoilers for the First sublevel of Glitch’s Non-Breaking Space, but not the actual solution.)

So, for this sublevel, I have introduced the first of Ada’s “old” roommates that have been created by Glitch to represent the various people of the past that Ada may end up affecting with her magic as she performs Chronomancy, Taffany. There is an optional puzzle for Taffany where she wants Ada to find one of several just-bought CDs to bring back to her. Of course, one of the major verbs of this game are the conversational verbs of ASK and TELL, so the CDs are themselves something Ada will want to use those verbs for.

In general, Ada is unable to converse about Things that are not known to her, meaning that she is either familiar with it or has seen it. So how could Ada “ASK ABOUT CDS” then if she hasn’t seen them or been told about them in some other fashion? Well, there are a group of Subjects for the CDs, a Subject being a special type of Thing to represent conversation topics and pieces of knowledge:

GNBSP-1-Missing-CDs-Subject is a privately-named unfamiliar subject.
[...]
GNBSP-1-Unfamiliar-CDs-Subject is a privately-named subject.

There are two different Subjects because Ada doesn’t start out knowing that the CDs are Taffany’s problem, merely that she has some problem, and I wanted to do a cool thing in case the Playerr types something like "ASK ABOUT CDS" before Ada is aware that it’s the CDs specifically where Ada acknowledges her lack of knowledge. Mainly out of feeling the need to have the Playerr keep in mind that Ada’s knowledge of the world is not the same as the Playerr’s.

You may also note that GNBSP-1-Missing-CDs-Subject is unfamiliar, while GNBSP-1-Unfamiliar-CDs-Subject is unspecified, meaning that it is familiar because Subjects are familiar by default. I mark them as privately-named because I don’t want the parser to latch onto these names as it does by default, but instead tell the parser itself what to understand as the subjects:

Understand "problem" or "issue" or "[taffany-possessive-token] problem" or "[taffany-possessive-token] issue" as
GNBSP-1-Missing-CDs-Subject.
Understand "missing/desired/unimplemented/-- [gnbsp-1-cd-singular-token]" or
"[taffany-possessive-token] missing/desired/unimplemented/-- [gnbsp-1-cd-singular-token]" or
"missing/desired/unimplemented/-- [gnbsp-1-cd-plural-token]" or
"[taffany-possessive-token] missing/desired/unimplemented/-- [gnbsp-1-cd-plural-token]"
as "[gnbsp-1-missing-cd-token]".
Understand "[gnbsp-1-missing-cd-token]" as GNBSP-1-Missing-CDs-Subject when GNBSP-1-Missing-CDs-Subject is familiar.
Understand "[gnbsp-1-missing-cd-token]" as GNBSP-1-Unfamiliar-CDs-Subject when GNBSP-1-Missing-CDs-Subject is
unfamiliar.

Either way, because Ada is familiar with GNBSP-1-Unfamiliar-CDs-Subject at the start of the game, she can successfully “ASK ABOUT CDS” without having seen the physical CD Things that are defined like so:

The CD-Album-White is a CD-Color-White compact disc album. The description is "TODOTODO: White CD Description.". It is
in the black plastic bag.

The CD-Album-Yellow is a CD-Color-Yellow compact disc album. The description is "TODOTODO: Yellow CD Description.". It
is in the black plastic bag.

The CD-Album-Green is a CD-Color-Green compact disc album. The description is "TODOTODO: Green CD Description.". It is
in the black plastic bag.

The CD-Album-Blue is a CD-Color-Blue compact disc album. The description is "TODOTODO: Blue CD Description.". It is in
the black plastic bag.

The CD-Album-Black is a CD-Color-Black compact disc album. The description is "TODOTODO: Black CD Description.". It is
in the black plastic bag.

So that’s nice, but what happens if she has seen the physical CDs?

Well, my hope was that she would then just ASK ABOUT the physical CDs, which would redirect to the same responses as the subject. I thought I had this working, but it turns out that my testing never tested something like “ASK ABOUT CDS” after Ada had seen the physical CDs. And when I added a test that accidentally hit this scenario, to my horror it fired the Default Response that is printed when I have not prepared a response for a character on that particular topic. Why did that happen?

Well, let me show off a bit from Conversation Framework by Eric Eve, where the relevant implicit-asking and implicit-quizzing Actions for the “ASK ABOUT” command is defined:

Implicit-asking is an action applying to one topic.
Understand "ask about [text]" or "a [text]" as implicit-asking.

[...]

Implicit-quizzing is an action applying to one visible thing.
Understand "ask about [any known thing]" or "a [any known thing]" as implicit-quizzing.

Now you might be surprised to see that two different Actions are mapped to “ASK ABOUT”, but there is a very good reason for this. Basically, there are Things in the model world that the parser knows about and is able to map input to. For example, if the Playerr types “ASK ABOUT HOODIE”, the the parser can map “HOODIE” to the (say) Glitchtech collegiate hoodie that Ada wears in Glitch’s Non-breaking Space.

But if the Playerr tries something that has no parallel in the model world such as “ASK ABOUT JEANS”? By default, this would result in something like “I can’t see any such thing.”, which is especially confusing in the case of trying to converse because you can certainly talk about something you can’t currently see.

This is where the two different Actions come in. In the first case where there is a model-world Thing, the implicit-quizzing Action is used, because that matches the "ask about [any known thing]" Understanding. In the second case, the implicit-asking Action is used based on the "ask about [text]" Understanding, which would treat “JEANS” as just an arbitrary text string.

(By the way, the “implicit” part of these Actions are to discern them from explicitly commanding who to ask such as “ASK TAFFANY ABOUT CDS”.)

What was happening was that “ASK ABOUT CDS” was being directed to the textual implicit-asking Action despite the fact there were multiple known Things Ada could have asked about, the Subjects and the physical CD Things. I’ll get to the “why” in a bit, but let me go over a few things I tried.

When there are multiple possible Actions that could be taken for the same input command, Inform 7 decides between them based on what Things are nearby and carried or touchable. If it can’t decide between the possible Actions, it will usually then ask the Playerr to disambiguate between them. However, there might be cases where the parser really should favor (or disfavor!) one possible Action over others, and this can be accomplished by a special type of Rule:

Does the player mean doing something with a compact disc album: it is very likely.
Does the player mean conversing when the second noun is a compact disc album: it is likely.
Does the player mean implicit-quizzing a compact disc album: it is likely.
Does the player mean implicit-informing a compact disc album: it is likely.
[...]
Does the player mean quizzing someone about GNBSP-1-Missing-CDs-Subject: it is very likely.
Does the player mean implicit-quizzing GNBSP-1-Missing-CDs-Subject: it is very likely.
Does the player mean informing someone about GNBSP-1-Missing-CDs-Subject: it is very likely.
Does the player mean implicit-informing GNBSP-1-Missing-CDs-Subject: it is very likely.

Does the player mean quizzing someone about GNBSP-1-Unfamiliar-CDs-Subject: it is very likely.
Does the player mean implicit-quizzing GNBSP-1-Unfamiliar-CDs-Subject: it is very likely.
Does the player mean informing someone about GNBSP-1-Unfamiliar-CDs-Subject: it is very likely.
Does the player mean implicit-informing GNBSP-1-Unfamiliar-CDs-Subject: it is very likely.

Note that the goal of these Rules is to favor the Subjects when conversing versus the physical compact disc albums when doing anything else. Note that for the compact disc album, all the Rules around conversing say that it is likely versus it is very likely for the conversing Rules around the Subjects.

When I got to fixing this issue I had noticed that I hadn’t written these Rules to favor the Subjects when conversing, so that was my first attempt to fix this. It didn’t work.

I was surprised that this didn’t work, and in a fit, I basically erased the implicit-asking Action that was getting called. This worked out because it revealed that the reason that the Parser was not choosing implicit-quizzing was because implicit-quizzing only works on one thing, not multiple things, by printing out a parser error. If Ada instead was told to ask about a singular CD with “ASK ABOUT CD”, it worked fine.

That convinced me to look around the Inform 7 documentation and I found example #295, The Left Hand of Autumn where the “EXAMINE” command was extended to account for examining multiple things by pointing to a multiply-examining Action. I tried to implement my own version of this with a multiply-implicit-quizzing Action but it didn’t work. One of the fun headaches I had was that I had defined my multiply-implicit-quizzing Action as:

Multiply-implicit-quizzing is an action applying to one thing.
Understand "ask about [known things]" and "a [known things]" as multiply-implicit-quizzing.

But I really should have defined it as:

Multiply-implicit-quizzing is an action applying to one visible thing.
Understand "ask about [any known things]" and "a [any known things]" as multiply-implicit-quizzing.

because by default a thing only refers to Things that Ada can touch, you instead need to refer to visible things to get other Things that may be in scope (and not necessarily visible, either). The same reason is for the [any known things] versus [known things]. But this didn’t work and gave me the same parser error, and in some cases had knock-on effects for say, asking about one of the now multiple skunk characters in the game.

The parser error in question was written out as “You can’t use multiple objects with that verb.”, and is referred to as the can't use multiple objects error in the code. This then gave me the idea to use a newer extension I’ve added, Smarter Parser by Aaron Reed, which introduces a bunch of rules that are meant to run before a parser error is printed to try to salvage the command.

But this wasn’t going to work, either, because while I had disabled the implicit-asking Action for my testing purposes, I would need to re-enable it so it can be there to catch arbitrary text I haven’t accounted for to redirect to the Default Response. Like it works fine for what it’s there for— it just isn’t what I want in this case. Regardless, if I re-enabled the implicit-asking Action, then the parser error would never get printed because the parser would instead try that Action.

In the end, I chose to attack that Action directly and prevent it from its normal behavior:

Before implicit-asking (this is the gnbsp 1 fix trying to implicit-ask about cds rule):
    unless the current sublevel is greater than 0, make no decision;
    if the topic understood matches "[gnbsp-1-missing-cd-token]":
        if GNBSP-1-Missing-CDs-Subject is familiar:
            try implicit-quizzing GNBSP-1-Missing-CDs-Subject instead;
        otherwise:
            try implicit-quizzing GNBSP-1-Unfamiliar-CDs-Subject instead.

And this finally worked! I had put a lot of effort in trying to stop the parser from choosing this Action, and it really was for naught, because it was easier to just make the Action do the right thing instead.

(The part about the current sublevel is there so the rule doesn’t need to run until the first sublevel. Mostly just a pre-optimization for performance. It may have been better to actually put that check in the preamble to the Rule…)

So there you go. I’ll be honest, this bug has been so basically frustrating that it is severely making me reconsider how many more of the games in this series are going to be written in Inform 7. There is so much I love about doing it like this, but this is just one of many issues I’ve had directly related to working around the archaic parser. And I know from watching Playerrs that even with all my work it still makes huge mistakes.

Oh well, I feel stuck into this for at least this zeroth game in the series, but we’ll see what the future holds. For now, I am at least overjoyed to finally be in a position where I can just write all of these responses I have scaffolded. Writer brain has been itching for this for over a year at this point and I’m hoping it will be really fun. Be on the look out for updates! (Though not today, I’m done with this for today.)

Jaycie “chimerror” Mitchell