Dialog

On library’s layers, the point is that the entry points and how to use should be well-described (the difference between ver[ACTION] and [ACTION] is still a FAQ in TADS world…)

I’m a bit perplexed on the narrate predicate, whose can lend to unduly heaviness on library’s footprint, because is implemented on the core action, but where the narrate predicate is more appropriate is in the “actions that print a message”, whose is where the core of the Nelsonian “varnish and veneer” often lies…

last but not least, on documenting, I warmly suggest that also part II have an quick reference appendix, listing implemented actions, predicates, traits, narrates &c. (similiar to appendix 1-5 of Inform 6’s DM4, or, more concisely, tables A-D of the same)

Best regards from Italy,
dott. Piergiorgio.

I maintain the French translation of Inform 7, so yeah, I know it’s not only a matter of translating responses. But with Inform, you can replace only parts of the Inform 6 templates, or parts of the Standard Rules, so you don’t need to keep a copy of the whole Standard Rules up to date (only the parts you replaced).

Anyway, I agree translating a WIP may be a bit premature. For the moment, I just wanted some information and I had it. So thanks! :smiley:

I have to say, this looks great! I’ve been getting back into writing IF after a while, and I’m actually considering Dialog for a longer project (could maybe even be a comp game, if I can muster up some self-discipline and focus). Do you think it’s viable for that right now? By which I guess I mostly mean: since I know this is still early days, do you forsee the language changing significantly between now and, say, September? I know there’s likely to be lots of little changes, but as long as nothing earth-shattering is on the horizon…

I hope that with “da sekrit weapon” (the new AC just over here) I can partecipate for once at the IF comp… (southern Italy summer, not precisely a climate conductive to coding, even with cooled “hack fluid”…) but is sooner to plan for it…

Best regards from Italy,
dott. Piergiorgio.

Thanks for your kind words!

By all means, please give it a go! And be sure to let me know if you run into problems, bugs, or annoying limitations. I’m also open to suggestions for features, although minimalism is still very important to me. So, yeah, there’s always a risk involved when you’re using a system that’s in active development, but the other side of the coin is that you get the opportunity to influence that development.

I’m not planning any changes that would break backwards compatibility, except perhaps a different mechanism for updating the status bar.

That’s good to hear! I’ll give it a shot then. As someone who considers themselves more a writer than a programmer, but still has a bit of programming experience and enjoys coding, I think this could be the ideal IF language for me.

The minimalist aspect is definitely part of what appeals to me, and having looked through the documentation I can’t see that there’s anything I’d want to do in the project I’ve been planning that I couldn’t do with Dialog. If I run up against something though, I’ll definitely let you know!

Is this thread the best place for bug reports and feature requests?

Yes, at least for the time being. You can also get in touch via PM if a public post would reveal too much about your upcoming game.

Great, will do, thanks!

I’m excited to unveil the Dialog Interactive Debugger, part of Dialog 0d/01, library 0.19 (click to download).

This tool elevates your Dialog program into a wide-open sandbox where gameplay, coding and debugging mesh together. Think I.A.G. Alpha, but with a parser and no limits.

You start the debugger from the commandline, providing the source code filenames as arguments. The game starts, and you can interact with it as usual.

In addition, you can type arbitrary queries and now-statements at the game prompt, in order to inspect and manipulate the running program.

But the real magic is that the debugger watches your source code files for changes. New code is merged into the running program automatically, so you can test new functionality without restarting the game.

There’s a new chapter in the manual, with all the juicy details.

2 Likes

It is so nice to have a parser game system under actual development. Thank you for all you do.

v/r
Jeff

Hey, this is great, thank you! No more manually recompiling to check every change for me!

A couple of bugfixes in the debugger: Dialog 0d/02, library 0.19.

A question on philosophy: it’s a very common pattern to have a message fire as part of the first time you look from within a room. But at that point, as I understand it, the visited property is already set. What’s the idiomatic way to do this in Dialog?

Are global flags (or maybe it’s the (now) predicate) designed to work in “open code” (like global variables?

If I put :

(now)(a-global-flag) in “open code”, I get :

“Special syntax cannot be redefined”

It seems I need to put the (now)… inside a predicate ? :

Something along these lines:

(look *)
        (select)
                This is printed the first time.
                (par)
        (or)
        (stopping)
        This is printed every time.

Anything that starts in the very first column of a line is interpreted as a rule definition, so the compiler thinks you’re trying to define a rule for a predicate called ‘(now)’. But that’s special syntax, so its behaviour can’t be modified.

If you would like the global flag to be initially set, define a rule with an empty rule body:

(a-global-flag)

This is analogous to how the initial values of other kinds of dynamic properties are defined. For instance:

(#box is #heldby #player)
(#box is open)
(current player #player)

Dialog 0e/01, library 0.20 is out. There are three small but compatibility-breaking changes to the language. They are subtly related, and I’ll end this post with an example that makes use of all three.

In addition to these changes, the release includes several bugfixes (especially in the debugger) and performance improvements.

1. It is no longer necessary to compute and declare the maximum size of global variables. From now on, any global variable can store any value, including lists. Any per-object variable can also store any value.

As a consequence, the old syntax:

(global variable (variable name $) size)

has been removed from the language.

The way this works is that a new long-term heap is shared by all global and per-object variables. This heap must be large enough to house all the complex values (i.e. lists) that are stored at any given time. By default, the compiler will allocate 500 words of long-term heap space, which should be plenty. This size can be changed with a command-line flag, -L.

(Special note for my library translator(s): Because of this change, runtime error code four has been given a slightly different meaning.)

2. Dictionary words are no longer truncated. They now have a small amount of internal structure instead: They are divided into an essential part at the beginning of the word, and an optional part at the end. The optional part is usually blank.

The location of the split is backend-specific. For the Z-machine, it is determined by the usual rules of truncation, meaning that up to nine characters are considered essential.

When two dictionary words are compared, only the essential part is taken into account. But when a word is printed, both the essential part and the optional part are printed (with no visible seam in between).

This is very useful when accepting arbitrary input as part of a grammatical statement. To invent an example, “WRITE CORNFLAKES ON SHOPPING LIST” won’t automatically lead to “On the shopping list is written: cornflake.” anymore, even if “cornflake” happens to be in the game dictionary.

When the ‘(removable word endings)’ feature is used, and the player types an unknown word that can be reduced to a known word, then the resulting dictionary word will have the known word as its essential part, and the removed ending as its optional part. Thus, in a German game where “klein” appears in the game dictionary, but “kleines” does not, the words @klein and @kleines will be considered equal during unification, but their printed representations are still “klein” and “kleines”, respectively.

In light of this change, user input is now always represented as a flat list of dictionary words and integers. It doesn’t matter if some words do not appear in the underlying Z-machine dictionary; at the Dialog level they are still represented as dictionary words. They can be stored in variables, retrieved, printed back, appear in ‘(dict $)’ rules, and so on.

As a consequence, ‘(get raw input $)’ isn’t needed anymore: To ask the player what their name is, use a regular ‘(get input $)’, which will give you a list of words. Print them back by iterating over the list and printing each word in turn, or use one of the new library predicates ‘(print words $)’ and ‘(Print Words $)’.

I have therefore tentatively removed ‘(get raw input $)’ from the language. Please let me know if you were using it for some other purpose and need it back. Likewise, ‘(print raw input $)’ has been removed from the library.

During tracing, the boundary between the essential and optional parts of a word will be marked with a plus sign (+), unless the optional part is blank. So you might see e.g. @transmogr+ify in the trace logs, which means that the word will print as “transmogrify”, but compare as @transmogr. In the same fashion, words that weren’t found in the game dictionary at all will have a plus at the end, e.g. @foo+ if the player typed the unrecognized word “foo”.

3. Resolving words to objects is considerably more efficient.

This is mostly of concern to library programmers. The old construct ‘(collect words) … (and check $)’ has been removed from the language. It is replaced by:

        (determine object $Obj)
                ... object generator ...
        (from words)
                ... word generator ...
        (matching all of $Input)

For instance:

        (determine object $Obj)
                *($Obj is in scope)
        (from words)
                *(dict $Obj)
        (matching all of $Input)

This expression backtracks over every object $Obj that makes the first inner expression succeed, and for which the second expression (when exhausted) emits at least every word in the $Input list.

Compared to the old construct, this has large performance benefits: The compiler can statically figure out what dictionary words are capable of matching what objects. So in a game with only two hats, the word “HAT” in the input will constrain the search, and ‘(determine object $Obj)’ will only backtrack over those two objects, check that they are in scope, collect their dict words, and match them against the input. On the other hand, in the unlikely case that there are hundreds of hats in the game, the object generator will be invoked first, and the full set of objects in scope (probably including just a few of the hats) will be considered.

Thanks to this optimization, a part of the standard library parser has been refactored, so that directions and objects are now parsed in the same way. Thus, complexity has been moved from the library into the compiler.

Working example: Player-named objects.

(intro)
        %% Feature 2, a name like Mr North-west won't be truncated:
        What's your name? > (get input $A)

        %% Feature 1, a per-object variable can be set to a list:
        (now) (#player has assigned name $A)

        Hello, (name #player)!
        (par)

        What's the name of your dog? > (get input $B)
        (now) (#dog has assigned name $B)

#room
(room *)
(look *)
        Here you are, with (the #dog).

#player
(current player *)
(* is #in #room)
(descr *)
        You are (name *), as good looking as ever.

#dog
(* is #in #room)
(proper *)
(descr *)
        Your best friend.

%% Feature 3, by using a trait to restrict this (name $) rule to a small set of
%% objects, we can still benefit from the optimized word-to-object lookup for
%% all other objects in the game:

(nameable #dog/#player)

(name (nameable $Obj))
        %% Feature 2, note that this code works both when printing the name
        %% of the object and when collecting the dictionary words that
        %% correspond to it. Earlier, when 'raw input' was a thing, the name
        %% would have been a list of single-character words, which wasn't
        %% compatible with the usual (dict $) mechanism.

        ($Obj has assigned name $Name)
        (Print Words $Name)

Note: As mentioned in the manual (Part I, Chapter 8, “Some notes on performance”), per-object variables tend to have a large memory footprint. In a story with hundreds of objects, of which only two are nameable, the above approach would be wasteful, and it would be better to use two global variables.

In conclusion, these changes aren’t backwards-compatible, and adapting story code to them might involve a little bit of work. But I think they are justified since they improve the lucidity, minimalism, and performance of Dialog.

Thank you for these improvements. It is very nice to have a parser language in active development.

v/r
Jeff

I’m trying to use the

 *($Part has parent #thing1) 

“construct” within a program so that an “x thing” command will list all the components / children.

If I put the above line of code in the (descr) body, it only displays the first child / component.

#thing1
(descr)
   This is the thing1 description, here are the parts:
    (line)
   *($Part has parent #thing)
     $Part is part of #thing

I’ve tried to make a rule with that clause, but the same thing, only the first component is displayed :

(showAll)
   *($Part has parent #thing)
     $Part is part of #thing


#thing1
(descr)
   This is the thing1 description, here are the parts:
    (line)
   (showAll)

I’ve never really understood Prolog recursion, and that’s probably what I’m missing here too ?

Mike

Yes, that should probably be elaborated some more in the manual. Thanks for bringing it up!

I’m assuming you mean to use either #thing or #thing1 throughout; I’ll use #thing1. This is what happens: The library makes a normal query to (descr #thing1). In the rule for that, you make a multi-query. Although that multi-query has the potential to return more than once, it never needs to do so, because there is no failure condition later. Backtracking usually only happens on failure. However, we can force Dialog to backtrack over every possible option using the ‘(exhaust)’ keyword:

#thing1
(descr *)
        This is the thing1 description, here are the parts:
        (line)
        (exhaust) {
                *($Part has parent #thing1)
                $Part is part of #thing1
        }

Advanced:

Of course, you can now move the entire exhaust-expression into a ‘(showAll)’ predicate, if you like. But it is also possible (though not necessarily a good idea in this situation) to move the inner part of the exhaust-expression to a separate predicate, and then exhaust every possible option of a multi-query to that predicate:

(showAll)       
        *($Part has parent #thing1)
        $Part is part of #thing1 

#thing1
(descr *)
        This is the thing1 description, here are the parts:
        (line)
        (exhaust) {
                *(showAll)
        }

Hope this helps!