The recent thread on flipping the map in Inform 7 got me thinking that this is a perfect use case for the way Dialog lets you assign conditions to any rule. So here’s my take on doing the same in Dialog; I’d be curious to know if anyone else has an alternate way of doing it.
(current player #player)
(#player is #in #lab)
#lab
(room *)
(name *) Lab
(look *) An untidy laboratory. The workshop is to
the south and a small closet is to the (west flippable).
(from * go west flippable to #closet)
(from * go #south to #workshop)
#closet
(room *)
(name *) Closet
(look *) A cramped storage closet. The exit is to
the (east flippable).
(from * go east flippable to #lab)
#workshop
(room *)
(name *) Workshop
(look *) A dusty workshop. The lab lies north and a
small doorway opens to the (east flippable).
(from * go #north to #lab)
(from * go east flippable to #kitchenette)
#kitchenette
(room *)
(name *) Kitchenette
(look *) A small kitchenette. The only exit is
(west flippable).
(from * go west flippable to #workshop)
~(world is flipped)
(west flippable)
(if) (world is flipped) (then) east
(else) west (endif)
(east flippable)
(if) (world is flipped) (then) west
(else) east (endif)
(from $A go #west to $B)
(from $A go west flippable to $B)
~(world is flipped)
(from $A go #west to $B)
(from $A go east flippable to $B)
(world is flipped)
(from $A go #east to $B)
(from $A go east flippable to $B)
~(world is flipped)
(from $A go #east to $B)
(from $A go west flippable to $B)
(world is flipped)
(perform [jump])
(if) ~(world is flipped) (then)
(now) (world is flipped)
(else)
(now) ~(world is flipped)
(endif)
Everything looks different!
(try [look])
Woah,I didn’t know you could do this in Dialog. It’s a super interesting lamguage - I always thought of it as a branch of Inform 7, but this feels very different!
Very nice! It definitely shows off one of Dialog’s strengths: it lets many things be computed that Inform requires to be assigned.
This is also one of its weaknesses: unlike Inform, it’s very hard to rewrite arbitrary exits on the fly in Dialog (e.g. if you can tunnel through walls). But overall I think it’s a good tradeoff.
It’s good! Nevertheless I will suggest some minor tweaks…
(current player #player)
(#player is #in #lab)
#lab
(room *)
(name *) Lab
(look *) An untidy laboratory. The workshop is to
the south and a small closet is to the (dir from * to #closet).
(from * go #west flippable to #closet)
(from * go #south to #workshop)
#closet
(room *)
(name *) Closet
(look *) A cramped storage closet. The exit is to
the (dir from * to #lab).
(from * go #east flippable to #lab)
#workshop
(room *)
(name *) Workshop
(look *) A dusty workshop. The lab lies north and a
small doorway opens to the (dir from * to #lab).
(from * go #north to #lab)
(from * go #east flippable to #kitchenette)
#kitchenette
(room *)
(name *) Kitchenette
(look *) A small kitchenette. The only exit is
(dir from * to #workshop).
(from * go #west flippable to #workshop)
~(world is flipped)
(dir from $A to $B)
(from $A go $Dir to $B)
(name $Dir)
(from $A go $Dir to $B)
~(world is flipped)
(from $A go $Dir flippable to $B)
(from $A go #east to $B)
(world is flipped)
(from $A go #west flippable to $B)
(from $A go #west to $B)
(world is flipped)
(from $A go #east flippable to $B)
(perform [jump])
(if) ~(world is flipped) (then)
(now) (world is flipped)
(else)
(now) ~(world is flipped)
(endif)
Everything looks different!
(try [look])
(Most of what I did there is replace some hardcoded predicates, like (east flippable), with slightly more dynamic ones – in that case, querying the direction between two rooms and printing it. I don’t think it makes a ton of difference in your example, but would help if eg. you had passages north-east that ought to become north-west when the world is flipped. As always up to personal taste of course!)
Yeah, I wondered if I should do something more generic than having fixed predicates for east and west. I hadn’t thought about dynamically querying the map connections to figure out what direction to display, which is a neat trick - but you might still want to be able to use other directions in the text as well?
When I coded something like this, it was not the map but the objects on it, including the protagonist, that could be flipped, so I used a per-object flag ($ is flipped). From the player’s perspective it makes little difference whether a map flip hinges on a per-object flag; after all, from the perspective of a flipped protagonist, the world would appear to be flipped. But if the author decides part of the way into a project, “Actually, it would be more interesting if it was the objects that could be flipped,” they will be glad to have used a per-object flag from the start.
Yet another option would be to have a predicate (#east flips to #west), etc., which encodes the raw information, and have everything delegate to that. That’s probably the most Prolog-ish version, since logic programming loves to make little databases of facts and infer things from those facts. Probably overkill for this little example though!
This got me thinking about dynamic maps in general, so I gave “dig” a try. It did indeed turn out to be a pain. I ran into the two variable limit on dynamic predicates, so I made the three-variable from rule depend on two variable dynamic predicates. I also ran into the n-squared problem with two variables that couldn’t be compiled to zblorb, so I made a separate rule for each direction in the two-way tunnel relationship.
(excavated $Room)
(from $Room go $ to $)
(fully excavated $Room)
(starting dig node $Room)
(from $Room go $ to $B)
(from $B go $ to $Room)
(fully excavated $Room)
(collect $Dir)
*(from $Room go $Dir to $)
(into $List)
($List = [$ | $Rest])
~($Rest = [])
(from $A go #east to $B)
(tunnel $A east to $B)
(from $A go #west to $B)
(tunnel $A west to $B)
(from $A go #north to $B)
(tunnel $A north to $B)
(from $A go #south to $B)
(tunnel $A south to $B)
(from $A go #down to $B)
(tunnel $A down to $B)
(from $A go #up to $B)
(tunnel $A up to $B)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(generate 6 (dig node $))
(room *(dig node $))
(name $Room)
(dig node $Room)
tunnel
(unused dig nodes $List)
(collect $Node)
*(dig node $Node) ~(starting dig node $Node) ~(excavated $Node)
(into $List)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
(perform [dig $Dir])
(current room $Room)
(unused dig nodes $Unused)
($Unused = [$NextRoom | $])
(if) ($Dir = #north) (then)
(now) (tunnel $Room north to $NextRoom)
(now) (tunnel $NextRoom south to $Room)
(elseif) ($Dir = #south) (then)
(now) (tunnel $Room south to $NextRoom)
(now) (tunnel $NextRoom north to $Room)
(elseif) ($Dir = #east) (then)
(now) (tunnel $Room east to $NextRoom)
(now) (tunnel $NextRoom west to $Room)
(elseif) ($Dir = #west) (then)
(now) (tunnel $Room west to $NextRoom)
(now) (tunnel $NextRoom east to $Room)
(elseif) ($Dir = #up) (then)
(now) (tunnel $Room up to $NextRoom)
(now) (tunnel $NextRoom down to $Room)
(elseif) ($Dir = #down) (then)
(now) (tunnel $Room down to $NextRoom)
(now) (tunnel $NextRoom up to $Room)
(else)
(fail)
(endif)
Determined to escape, you begin digging with the spoon, and slowly extend
the tunnel (name $Dir).
Yeah, I think the way I’d do it would be to have a list stored in a global variable, [source dir dest source dir dest...], and iterate through that every time an exit needs checking. That takes O(n) time but also only O(n) space, and space is precious on Z-machine.