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.

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

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

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.

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)

%% To get rid of some compiler warnings:
(from $ go $ to $ reversed)	(fail)
(from $ go $ through $ to $ reversed)	(fail)

bidirectional_connections_v2.dg (2.9 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.

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

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

kudos to Melisande !

Best regards from Italy,
dott. Piergiorgio.

“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: