Bidirectional Connections - A Mini Extension

Allow me to present Bidirectional Connections, a 25-line long extension containing a full seven lines of actual code (or two if you remove some line breaks).

I was initially going to post this on April 1st, since the extension seems almost too small to release, but I wanted to make sure it was fully tested. Now, instead of specifying (from #field go #north to #barn) and (from #barn go #south to #field), you only need to type (#barn is #north of #field)!

Here’s the extension:

(extension version) Bidirectional Connections version 1 by Melisande.

%% The purpose of this extension is to simplify the declaration of regular, symmetrical room connections.
%% With it, you no longer have to state (from #kitchen go #east to #pantry) (from #pantry go #west to #kitchen). You can now include a single line of code: (#pantry is #east of #kitchen).

%% Both ($Room1 is $Dir of $Room2) and ($Room1 is $Dir from $Room2) are valid syntax. When stating connections between rooms, all three parameters must be fully bound. This syntax should only be used to establish connections, not to query them!

%% If you want to use a query to find connections between rooms, always use the (from $ go $ to $) syntax. This will return all of the appropriate connections. If you try to use the bidirectional connection syntax in a query, it will likely miss some.

%% Warning: if you use this with custom directions (like ship directions), be sure to state opposite pairs (e. g. "(opposite of #port is #starboard)(opposite of #starboard is #port)").


%% For the cardinal and ordinal directions, this is a more idiomatic way of phrasing it.
@($Room1 is $Dir of $Room2)
	($Room1 is $Dir from $Room2)

%% Since the standard library checks to see if there is a matching rule head, (from $ go $ to $) just needs to evaluate true or false correctly.
%% Because the syntax for a bidirectional connection only explicitly mentions a one-way connection, the query also needs to succeed if the reverse of the established connection is true.
%% When checking for the reverse, it first finds all of the connections matching the bound parameters then finds the reverse of each connection, because this method doesn't require $Dir to be bound. If it instead tried to find the reverse of the direction then check for connections, $Dir would need to be bound and pathfinding wouldn't work.
(from $Room1 go $Dir to $Room2)
	*($Room2 is $Dir from $Room1)
	(or)
	*($Room1 is $OppositeDir from $Room2)
	(opposite of $OppositeDir is $Dir)

bidirectional_connections.dg (2.0 KB)

If anyone has questions, issues, or suggestions, please let me know.

EDIT: Version 2 is now available.

bidirectional_connections_v2.dg (2.7 KB)

The source code can be found below.

5 Likes

Very good for preventing mistakes! Have you considered a variation that handles doors as well?

1 Like

That’s a good idea. I’ll update the extension to include doors, and release a new version today or tomorrow.

1 Like

The only other thing I’m curious about is optimization. But until we have a proper profiler, it’s hard to say what impact this will have on it. (I just know (from $ go $ to $) was very carefully named so that it’s always queried with a bound first parameter, because that makes for the cleanest lookups.)

So I might be tempted to try something like…

@($X is $Dir from $Y)
    (from $Y go $Dir to $X)
    (from $X go $Dir to $Y reversed)

(from $X go $Dir to $Y)
    *(from $X go $Opposite to $Y reversed)
    (opposite of $Opposite is $Dir)

In other words, transforming it at compile-time into two predicates that follow the library’s argument structure. But premature optimization is the root of all evil, and I don’t understand the predicate optimizer well enough to say whether this would actually make a difference.

1 Like

Here’s version 2, now with support for doors:

(extension version) Bidirectional Connections version 2 by Melisande.

%% Thanks to Daniel Stelzer for showing me how to optimize this extension by using access predicates.

%% The purpose of this extension is to simplify the declaration of regular, symmetrical room connections.
%% With it, you no longer have to state (from #kitchen go #east to #pantry) (from #pantry go #west to #kitchen). You can now include a single line of code: (#pantry is #east of #kitchen).

%% Both ($Room1 is $Dir of $Room2) and ($Room1 is $Dir from $Room2) are valid syntax. When stating connections between rooms, all three parameters must be fully bound. This syntax should only be used to establish connections, not to query them!

%% If you want to use a query to find connections between rooms, always use the (from $ go $ to $) syntax. This will return all of the appropriate connections. If you try to use the bidirectional connection syntax in a query, it will likely miss some.

%% For two rooms connected by a door, the syntax is similar: ($Room1 is $Dir through $Door of $Room2) or ($Room1 is $Dir through $Door from $Room2).

%% Warning: if you use this with custom directions (like ship directions), be sure to state opposite pairs (e. g. "(opposite of #port is #starboard)(opposite of #starboard is #port)").


%% For the cardinal and ordinal directions, this is a more idiomatic way of phrasing it.
@($Room1 is $Dir of $Room2)
	($Room1 is $Dir from $Room2)

%% Since the standard library checks to see if there is a matching rule head, (from $ go $ to $) just needs to evaluate true or false correctly.
%% Because the syntax for a bidirectional connection only explicitly mentions a one-way connection, the query also needs to succeed if the reverse of the established connection is true (hence the addition of "(from $Room2 go $Dir to $Room1 reversed)").
%% When checking for the reverse, it first finds all of the connections matching the bound parameters then finds the reverse of each connection, because this method doesn't require $Dir to be bound. If it instead tried to find the reverse of the direction then check for connections, $Dir would need to be bound and pathfinding wouldn't work.

@($Room2 is $Dir from $Room1)
	(from $Room1 go $Dir to $Room2)
	(from $Room2 go $Dir to $Room1 reversed)

(from $Room1 go $Dir to $Room2)
	*(from $Room1 go $OppositeDir to $Room2 reversed)
	(opposite of $OppositeDir is $Dir)

@($Room1 is $Dir through $Door of $Room2)
	($Room1 is $Dir through $Door from $Room2)

@($Room2 is $Dir through $Door from $Room1)
	(from $Room1 go $Dir through $Door to $Room2)
	(from $Room2 go $Dir through $Door to $Room1 reversed)

(from $Room1 go $Dir through $Door to $Room2)
	*(from $Room1 go $OppositeDir through $Door to $Room2 reversed)
	(opposite of $OppositeDir is $Dir)

bidirectional_connections_v2.dg (2.7 KB)

Thank you @Draconis for the optimized version of the code. I tend to forget that Dialog is designed to run on retro hardware.

The extension does generate two apparently spurious compiler warnings, but it works correctly.

Warning: bidirectional_connections_v2.dg, line 40: Possible typo: Local variable $Room2 only appears once.
Warning: bidirectional_connections_v2.dg, line 40: Possible typo: Local variable $Dir only appears once.

As long as they match these, don’t worry.

2 Likes

Hmm. Those warnings definitely shouldn’t be happening. Time to track those down!

3 Likes

Not only useful, but also consistent with the “syntactic sugar” philosophy of Dialog !

kudos to Melisande !

Best regards from Italy,
dott. Piergiorgio.

1 Like

“Syntactic sugar gives you cancer of the semicolon,” as the saying goes, but we don’t have semicolons in Dialog, so it’s all good. :slight_smile:

3 Likes