Collecting free text

i think i have a challenge.

i need to give the player the ability to write on an object. they should be able to write whatever they want and it should be bound to the object so it can be examined, compared, read-back later (if they write something specific then something else happens).

i have mucked around with this for a while but it is beyond my dialog parser capabilities.

i’ve played with this predicate along with a custom grammar token.

(interface (asking for free_text in $<Action))
(asking for free_text in $Action)
	(now) (implicit action is $Action)
	(now) (implicit action wants free_text)

have also thrown in variants of:

(understand [write | $Words] as [write $Words on $Object])
    *(split $Words by [on] into $Left and $Right)
    *(understand $Left as free_text $Free)
    *(understand $Right as non-all object $Object)

but no luck. the dialog parser eludes me in many ways (so, so many ways…) so i’m cutting my losses and asking for help.

1 Like

Try something like this?

(understand [write | $Words] as [write $Left on $Object])
    *(split $Words by [on] into $Left and $Right)
    *(understand $Right as non-all object $Object)

Now you’ll end up with an action like [write [a b c] on #cube]. Lists of dictionary words generally don’t end up in actions, currently, so you may need to tweak the (describe action $) predicate to make sure they’re handled properly, but it shouldn’t be hard to then do something like:

(perform [write $Words on #cube])
    You write (print words $Words) on the featureless cube. It is now the "(print words $Words)" cube.
    (now) (cube label $Words)

(dict #cube)
    (cube label $Words)
    (print words $Words)
2 Likes

yeah, this is one of the things i tried. but i can’t get the output right.

if you type, for instance “write aaa bbb ccc on the cube”

the output is:

The:
You write on the cube.

The:
You write on the cube.

The:
You write on the cube.

it seems like it’s treating the contents of the $Words list as separate objects instead of something i can print?

Ahh, okay, I think I see what’s going on here. (try-complex $) has special handling if it finds a list inside an action, and that’s going haywire here. Hmm. I’ll have to experiment with that.

OK, at the time i was able to get around this narratively. but now i’m back and again need to let the player write free text onto an object and i’m no closer to figuring out how to get dialog to stop treating [write [a b c] on $Paper] as a complex action broken into separate actions:

The:
writing a on paper

The:
writing b on paper

The:
writing c on paper

how do i get an action to accept a list of objects as inputs without having it be passed along to (try-complex $)?

Hmm…the first thought that comes to mind is to put some special tag like #words at the start of your list of words, and change (try-complex $) to not break up lists that start with that tag.

Specifically, modify this part:

(regroup stripped action [$HeadIn | $TailIn] of $Action into [$HeadOut | $TailOut] $Multi)
	(if) (nonempty $HeadIn) (then)
		(regroup objects $HeadIn of $Action into $GroupList)
		(if) ($GroupList = [$Single]) (then)
			%% All of the objects in this slot ended up in the
			%% same group, like [#e #d] in the example.
			($HeadOut = $Single)
			*(regroup stripped action $TailIn of $Action into $TailOut $Multi)
		(else)
			%% Backtrack over the groups.
			*($HeadOut is one of $GroupList)
			*(regroup stripped action $TailIn of $Action into $TailOut $MultiTail)
			($Multi = [$HeadOut | $MultiTail])
		(endif)
	(else)
		%% This was a regular object or word, like @frob or #c.
		($HeadOut = $HeadIn)
		*(regroup stripped action $TailIn of $Action into $TailOut $Multi)
	(endif)

Add ~($HeadIn = [#words | $]) to the condition of the if-statement, and now a list starting with #words will be handled like an object or word, not like a list.

For good measure, there’s a line in the definition of (try-complex $) that goes like this:

(if) *($List is one of $MultiAction) (nonempty $List) (then)

Add the same check to this too, ensuring that the first element of $List isn’t #words.

since there’s only one possible writing surface #paper i simplified things a bit and instead of testing for [#words | ] starting the list, i tested instead with:

~($Action = [write $ on #paper])

in the (regroup stripped action … ) predicate

and

~($ComplexAction = [write $ on #paper])

inside (try-complex …)

i added them to the if-conditions you mentioned and have confirmed that these following blocks are no longer being called (inexpertly scattering HERE(line) all about, maybe there’s a better way).

but, nevertheless, my (perform [write $ on $]) is still being called once for each word in the list.

EDIT:
i added the test case to (try regrouped …) also, since that seems to be called as well.

adding
~($Regrouped = [write $ on #paper]) to
(if) *($Elem is one of $Regrouped)

seems to have short-circuited the multiple calls.
so i may be onto something.

EDIT 2:

yeah, that fixed it and now i can happily write on my piece of paper and read it back.

thanks for getting me nearly there. i never would have known where to start looking.

EDIT 3:

one nit that i don’t particularly need to pick but might be meaningful for someone else under certain situations - case is not preserved. that is:

tattoo  ABBA is my FAVORITE band on chest

reads back as ‘abba is my favorite band’ since everything is converted to lowercase by the parser. i’m guessing this would require major parser-diving to fix.

1 Like

The Z-machine itself (the interpreter) converts player input to lowercase. There is simply no way for a game to know the case was.

2 Likes

Same for the Å-machine, yeah. It’s not just a Dialog limitation but a limitation of the underlying technology. So I’m afraid that’s one that won’t be easily circumvented.

But I’m glad this is working now! I missed the (try regrouped…) bit.

1 Like