Hello, loves!

If we draw suite paths in the right order, it should give us the layout we want with no extra work. If this works, it’s a lovely idea. Herein we eschew pompous prolixity.

The new PassageCollection object, which I am inclined to rename to Passages, is now in place. It ensures that once a passage between two rooms has been formed, that passage is preserved even if we’re nominally computing new passages. We did that because when stepping the build for the developer, we want to see the passages as we go, and the passage-computing code would make new decisions about where things should go.

Arguably, we coulda shoulda changed the passage-making in some fundamental way so as to specify somehow just exactly what it should do. That seemed daunting, and once we created the Passages object (see what a nice name that is, gonna change it right now … done) as I was saying, once we created the Passages object, it was very simple to have it keep track of which room pairs already have passages and just ignore any attempts to add passages to them. We even did it with a callback, so that user code need not waste time preparing a path that is inevitably going to be rejected.

All very nice, and quite simple. The task before us now is that given groups of rooms that we have created in subregions of the map, we want to connect those groups with paths that will control the major flow through the dungeon: first this area, then this next area, and so on.

These “major” paths (just long thin rooms really) need to be different from most rooms. Our general rule is that if two rooms share a border, we’ll build a passage across that border. This rule tends to keep the dungeon connected, and seems to make interesting layouts with loops and different ways to get places. But for these inter-region paths, we want that rule changed. We do not want a surprise entry into the side of one of these paths if it happens to pass by some other room. If we were to allow that, there would be a “leak” in the dungeon, allowing the player to bypass some section without solving the problems in that section.

I believe, though I could be wrong, that this is going to turn out to be quite easy, and rather elegant. We’ll see, probably next time.1

So we’ll need to do something special when we connect up our sections, to ensure that the path we build is not permeable with respect to surprise adjacencies2.

Before we get there, however, we need to get the paths laid in, and I have an idea about that as well. There are two methods that work together to keep the dungeon connected:

class DungeonLayout:
    def finish_map(self):
        self.ensure_connected()
        self.make_passages()

ensure_connected looks at the existing rooms and while they are not all connected, adds path rooms between connected “suites”. It just makes paths that are adjacent to a pair of suites, and then make_passages connects things up. It’s in make_passages that the extra entries into the sides of a path are formed.

So. Imagine that we want to create a dungeon with four sections in the four corners, and a boss room, round with a diamond center, in the middle of the dungeon. (That’s what the current build does, you may recall.)

It seems to me that if we were to build the center bit last, we could build section 1, connect it up, build section 2, connect it up, then connect 1 and 2, then build 3, connect it up then connect (1,2) and 3, and so on, it would just work. Once 1 and 2 are connected, and 3 is internally connected, it looks like two suites to the finish_map and it should just hook them together. So we should be able to get the topology we want by just deciding where to place the sections and placing them in the order we want them connected.

So I propose that we begin by changing the build to do that. Here’s what it looks like now:

def make_build_table(layout, dungeon):
    steps =  [
        make_diamond_in_round_room,
        finish,

        bottom_left,
        make_a_diamond_room,
        make_a_diamond_room,
        make_a_diamond_room,
        finish,

        top_left,
        make_a_round_room,
        make_a_round_room,
        make_a_round_room,
        finish,

        bottom_right,
        make_a_cave_room,
        make_a_cave_room,
        make_a_cave_room,
        finish,

        top_right,
        make_a_cave_room,
        make_a_diamond_room,
        make_a_round_room,
        finish,

        clear_subregions,
        finish,
        populate,
        add_content,
    ]
    return BuildStepper(steps, layout, dungeon)

Let’s reorder things to go bottom left, top left, top right, bottom right, center, and add in some additional connections. (I think we’ll initially get too many, but we know how to fix that if we do.)

New stepper:

def make_build_table(layout, dungeon):
    steps =  [
        bottom_left,
        make_a_diamond_room,
        make_a_diamond_room,
        make_a_diamond_room,
        finish,

        top_left,
        make_a_round_room,
        make_a_round_room,
        make_a_round_room,
        finish,
        clear_subregions,
        finish,

        top_right,
        make_a_cave_room,
        make_a_diamond_room,
        make_a_round_room,
        finish,
        clear_subregions,
        finish,

        bottom_right,
        make_a_cave_room,
        make_a_cave_room,
        make_a_cave_room,
        finish,
        clear_subregions,
        finish,

        clear_subregions,
        make_diamond_in_round_room,
        finish,
        populate,
        add_content,
    ]
    return BuildStepper(steps, layout, dungeon)

Note the clear_subregions finish combinations. Those are supposedly going to directly connect around the square. Let’s see what it really does.

map with double paths between sections, central bit not connected where we would wish

So that’s not bad. We have those double paths, the extra paths I was referring to. I think I almost like those. I was imagining that they’d probably be more separated but since we try to find the closest cells between suites to draw our paths, it makes sense that they’ll tend to be side by side.

The reason they are drawn at all is that we draw paths all the way around the suite set from 1 to 2, 2 to 3, n-1 to n, n back to 1. We can suppress that last drawing if we care to.

The other issue is that we wanted the path to the boss room to come from bottom right and it does not. We can fix that with a better subregion for the last bit.

In principle, what we really want, I suspect, is for subregions to be more than mere rectangles, but let’s defer that concern for now. If we do that final finish in a subregion containing just bottom right and the area for the boss room it should do just what we want.

As drawn, it appears to me that the boss room overlaps the top rectangle. Let’s examine the rectangles as defined and see what we need to do:

main.py
def bottom_left(layout, dungeon):
    layout.subregion_LRBT(0, 17, 0, 23)

def top_left(layout, dungeon):
    layout.subregion_LRBT(0, 17, 33, 55)

def bottom_right(layout, dungeon):
    layout.subregion_LRBT(37, 55, 0, 23)

def top_right(layout, dungeon):
    layout.subregion_LRBT(37, 55, 33, 55)

I’ll adjust those to make a little room, and add one more subregion, to include bottom right plus the central area:

main.py
def bottom_left(layout, dungeon):
    layout.subregion_LRBT(0, 20, 0, 20)

def top_left(layout, dungeon):
    layout.subregion_LRBT(0, 20, 35, 55)

def bottom_right(layout, dungeon):
    layout.subregion_LRBT(35, 55, 0, 20)

def top_right(layout, dungeon):
    layout.subregion_LRBT(35, 55, 35, 55)

def bottom_right_plus_center(layout, dungeon):
    layout.subregion_LRBT(21,55, 0, 34)

Also I made the change that doesn’t make a path that circles back:

    def _make_path_rooms(self):
        suites = self.define_suites()
        # self.check_intersection(suites)  # debug method
        # for s1, s2 in zip(suites, suites[1:] + suites[:1]):
        for s1, s2 in zip(suites, suites[1:]):
            cells = s1.find_path_cells(s2)
            if cells:
                room = Room(cells, 'path')
                self.add_room(room)

It’s that + suites[:1] that makes us circle back to the first suite. Those changes give us this picture:

map with single paths between sections boss room connected at lower right almost as intended

That is almost as intended. Woot! Note the tiny loop where the boss room connects to the lower right. I think that is our adjacent paths inter-penetrating and it’s the thing that I think we can fix elegantly … next time.

I want to make it more interesting. Let’s add some rooms.

map with more rooms still not connected quite as intended

Hm. Now there is a connection lower left to lower right that I do not expect, and no connection from upper right to lower left. I think I know why that’s happening: the path-finding isn’t sensitive to the available regions, and the bottom two are closer together than the right side two.

This is a surprise to me and will have to be dealt with. My brilliant idea seems to be slightly flawed. Let’s hope it’s just slightly. If we can make passage-making sensitive to regions, the idea holds water. If not … I’ll need another idea, but I want this one to work. We’ll see.

So that’s fun. In addition to fixing this up, we still have one thing to do, having to do with the double paths. We may need one or two more sessions to get this done. Interesting …



  1. When I started this morning, I thought we’d get to this. I decided at this point lay in paths on the fly, which is also a rather nice idea if I do say so myself. ↩

  2. I generally prefer direct and simple speech. But sometimes phrases like “permeable with respect to surprise adjacencies” just come out of my fingers, and I do enjoy them. I hope that you see them, as I do, as fun play, not some attempt to seem all academic with pompous prolixity, which as a matter of policy we would generally eschew. ↩