Dialog

Thank you for asking.

Imagine that I want to give readers the option to choose between italic emphasis, bold emphasis, or no emphasis (as I did in my last work).

Further, imagine a work in which the player character can enter the shadow world of Super Mario Bros. 2 or the Up Side Down of Stranger Things. (Neither of these is exactly what I want to do, but each is a near enough analogue.) The same room or objects would remain visible, but they might somehow not be as they were – they might look different, and they might not respond the same way when the player character tries to interact with them.

In TADS 3 I might declare constants like this:

#define Italics 1 #define Bold 2 #define ShadowWorld 4
And once these values had been ORed together, I would use bitwise AND to determine which options had been selected.

For this kind of world-spanning options, I would recommend using global flags. For instance, you could create a predicate for asking the player what kind of emphasis they prefer:

(ask about emphasis)
        Would you prefer BOLD, ITALIC, or NO emphasis? > (get input $Words)
        (if) ($Words = [bold]) (then)
                (now) (bold emphasis)
        (elseif) ($Words = [italic]) (then)
                (now) (italic emphasis)
        (elseif) ~($Words = [no]) (then)
                Please type one of the words! (line)
                (ask about emphasis)
        (endif)

The above code implies that ‘(bold emphasis)’ and ‘(italic emphasis)’ are global flags, that can be set or cleared using ‘(now)’ syntax. You’d call that routine from the game intro, and then you’d define an ‘(emph)’ predicate for enabling the player-selected style. Here’s what the rest of the program might look like:

(intro) 
        Before we begin, please answer this question:
        (par)
        (ask about emphasis)
        (par)
        An (emph) excellent choice (roman)!

(emph)  
        (if) (bold emphasis) (then)
                (bold)
        (elseif) (italic emphasis) (then)
                (italic)
        (endif)

(descr #me)
        As (emph) good looking (roman) as ever.

(current player #me)
(#me is #in #room)
(room #room)

Now, under the hood, the compiler will map these global flags to individual bits in some register. So while you are using high-level symbolic predicate names, the compiled code will be using bitwise-and and bitwise-or (and the z-machine ‘TEST’ instruction) to check and modify the flags.

If you test several flags in the same condition, e.g.

        (if) (first flag) (second flag) (then) ... (endif)

or

        (if) (first flag) (or) (second flag) (then) ... (endif)

then, currently, the compiler isn’t smart enough to combine it all into a single check. But such an optimization could be added in the future, if it turns out to have a measurable impact on performance.

hmmm… there’s people whose strive for compactness to the extreme, e.g. if something has a narrow range, ends packed together with other short range value variable and/or flags in a bit/word field. and a Z-machine story file is indeed centered around packed (five bits) short-range character strings, but the sheer majority of those strings are static.
my 2 €c, and

Best regards from Italy,
dott. Piergiorgio.

Yes, this is a fascinating part of language design, and there are many ways to think about it.

Personally, I am very fond of the nitty-gritty details of low-level optimization, but I don’t think it belongs at the story level. When in “author mode”, I want to focus on characters, plot, and scenes, as well as robustness, puzzles, geography, hints, synonyms, and responses. Getting bogged down in bitfields would be a distraction.

Ideally I want a high-level language that can be converted into efficient low-level code, using all kinds of clever optimization trickery in the compiler. That doesn’t work unless the language is designed with such optimizations in mind, but that is the case with Dialog. In my example above, ‘(bold emphasis)’ compiles down to a single bit of memory. I don’t see how the people who “strive for compactness to the extreme” could do any better than that.

Meanwhile, there’s another danger when you begin to expose too much detail at a high level: It becomes more difficult to optimize the code automatically. If, for instance, you allow the story author to specify numeric constants for the compass directions, so they can be stored in four bits, then you might inadvertently prevent the compiler from picking a two-bit encoding for stories that only use north, south, up, and out.

Now, as it happens, the current Dialog compiler doesn’t perform that particular optimization. And, in fairness, there are aspects of the language where I’ve valued versatility over compactness. But as a general rule of thumb, high-level code tends to be more amenable to automated analysis and optimization.

Dialog predicates turn into executable code, z-machine object flags, global registers, bits, linked lists, arrays, word tables stored inside object properties, etc. etc. depending on how they are accessed from the rest of the program. The author shouldn’t have to bother with it, and shouldn’t be allowed to meddle with it, getting in the way of the optimizer.

Bitfield packing is no panacea. You bring up z-machine text encoding as an example. That happens to be a very inefficient solution; a Huffman code would fare way better, and would certainly fit inside the memory of an 8-bit system, while improving loading times due to the increased compression ratio. But, despite its flaws, I think z-machine text encoding is a good example of a nicely designed optimization, precisely because it stays out of the way of the author, who is free to focus on more important things.

1 Like

lft,

Thank you for your very thorough response!

The only benefit of symbolic constants is that they increase the clarity of the code, but the code you have written is quite clear. I also like that Dialog allows a variable representing the control code to be inserted into the text printed to the screen; I was afraid I would have to give that up if I moved away from TADS 3.

I definitely need to examine the manual more thoroughly.

Agree on all this, albeit personally I think that some corner can be cut, for example, as you pointed:

it’s a case for “semi-auto optimisation”, (conditional compiling, macros, defining variable types) as I call it, but this require a pre-processing of the code, and I guess that you don’t want trading elegancy and simplicity for flexibility.

OTOH, IF libraries needs means for customisation, at least of the stock responses (going up while angel, one of my favorite jokes on sins and mimesis… but let’s not open again THAT debate :laughing: )

Best regards from Italy,
dott. Piergiorgio.

I haven’t had time to really look at Dialog for the moment, but I’m always interested in new authoring systems!

I was wondering if writing stories in languages other than English is on your roadmap. I guess it’s not your priority right now, but since I almost exclusively write in French, I’d like to know.

Thanks, and keep up the good work!

Yes, I want to support several languages. The Dialog programming language isn’t restricted to English, apart from keywords and the names of built-in predicates. User-defined names can contain UTF-8 characters.

In order to write a non-English story, one would first have to translate the standard library. I know that somebody is looking into making a German translation, and I’ve added a feature to the language (removable word endings) to facilitate that. I’m willing to add other features that are necessary for other languages, as long as it fits reasonably well with the rest of the system design.

I notice that the error message for trying taking an incorporated plural-named thing is “That’s part of (the $Parent).” Possible bug.

Is it possible in Dialog to let the player type his name and then store the input in a $Variable to print it out later?

Edited 190204: As of version 0e/01, this response is no longer true. You should use ‘(get input $)’ and ‘(print words $)’ instead.

Yes, this can be done with (get raw input $). This gets you a list of single-character words, but at the moment there is no library predicate for printing it back. I should add that. Meanwhile, you can use this:

(print chars [])

(print chars [$First | $Rest])
        $First
        (exhaust) {
                *($Char is one of $Rest)
                (no space)
                $Char
        }

Here’s how you could use it from within a predicate:

(intro) 
        What's your name? > (get raw input $Name)
        Hello, (uppercase) (print chars $Name)!

But you presumably want to print it from a different predicate. Then you also need to store it in a global variable, which is slightly cumbersome. You need to specify a maximum length, which is 63 characters in the example below (+1 for the list itself).

(current player #me)
(room #room)
(#me is #in #room)

(global variable (current name $) 64)
(player's name)
        (current name $Name)
        (uppercase)
        (print chars $Name)

(intro) 
        What's your name? > (get raw input $Name)
        (now) (current name $Name)
        Hello, (player's name)!

(descr #me)
        You are (player's name), as good looking as ever.

Currently, ‘(uppercase)’ only affects a single letter, i.e. the first letter of the name. It would be possible to modify the ‘(print chars $)’ routine to capitalize each word, by detecting space characters.

Oops, it turns out that in version 0c/04, ‘(uppercase)’ doesn’t work properly with dictionary words. That’s coming soon, though.

Thanks for the quick response. In the meantime I came up with the same solution as proposed, but then got stuck at trying to print it out with first letter in capital.
BTW: it’s me who’s looking into a german translation of Dialog and I would estimate it as 90% done. There weren’t any serious obstacles in the way, as far as I can tell. One additional feature might be a built in predicate to replace certain letters in input before parsing. Speaking for german this would be e.g. replacing all ‘ß’ with ‘ss’ or replacing ‘ä’ with ‘ae’.
This could be handsome for some lazy typing germans who don’t know the difference between ‘ß’ and ‘ss’ or for some german dialects like swiss-german who have other conventions concerning ‘ß’ or for some german speaking guys in foreign countries who don’t have a german keyboard handy and therefore have troubles in typing ‘ä’.

:slight_smile:

Version 0c/05 library 0.17 is out now (download link).

This is a minor update based on feedback. Thanks for voicing your opinions!

  • Support for .z5 output format.
  • Bugfix: ‘(uppercase)’ now works properly with dictionary words.
  • Library: Better default messages when trying to look in a direction with no exit.
  • Library: Added a plural variant of the “That’s part of …” message.
  • Library: Minor performance tweak.
  • Library: Default ‘(intro)’ also tries to look around in the initial room.
  • Docs: Some small corrections, and information about using the .z5 format.

And here’s an extra update for the library: Version 0.18.

I’ve added a predicate for printing back raw input, like we discussed a few posts back. It’s called ‘(print raw input $)’ now.

Thanks, it works fine! :slight_smile:

In case you wanna eat an object which is edible but not an item you get no answer.
I’ll suggest to make all edibles also items (item $Obj) *(edible $Obj).

Edit: you do get an answer it was my fault, but making all edibles also items is nonetheless an option.

Regarding the translation of Dialog. One would have to copy the entire standard library? And if the library is updated, one would have to copy back the modifications in the translation? Or is there a system like Inform 7’s responses?

Thanks!

Yes, one would have to maintain a translated version of the library, and keep it up to date.

Inserting an extra layer of indirection into the design would make the library code less readable for authors. Consider the following pair of rules:

(understand [wave | $Words] as [wave $Obj])
        *(understand $Words as object $Obj preferably held)
 
(perform [wave $Obj])
        You wave (the $Obj) in the air, with no apparent consequences.

The word “wave” appears as user input, as the internal name of an action, and inside the response text.

In order to separate the logic from the response message, one could move the message into a secondary rule. It could be called (default message for waving $Obj), or (narrate waving $Obj). But this attempt to disentangle the library code from the English language fails, rather spectacularly, because now we’ve added the English word “waving” to the mix.

The narration predicates for the 18 core actions do represent such a layer of indirection. Here it makes sense because the logic of those actions is sufficiently complex. But in a translated library, the internal action names would probably also be expressed in the target language, and so would the core actions, and the names of their narration predicates.

Translation is an inherently complex task. Translating a work-in-development is even more heroic. I strongly recommend working with diff-tools such as meld, which would take care of the non-texty bits of code automatically. Even so, there will be lots of manual maintenance work involved, and there will always be subtle differences between the various translations and the original. As in any translated work.

Speaking for german I can say that a german translation is far more complex than just to translate the standard answers to actions. I had to adjust parsing and dictionary rules as well and this can only be done be replacing queries. So it could be a point worth discussing whether the english standard answers could be moved to another file maybe called “responses.dg”, but this would not prevent the work in the main file, which has to be updated when the english pendant is updated too. But this seems to contradict the concept of simplicity which makes “dialog” really interesting.

For now, I used Visual Studio Code, I compared the old english lib with the new one every time it was updated, so I could relatively easy find out the changes and implement them into my translation.