Too Many Rooms?
Drawing a room looks good. Let’s generate a whole space of them and see how the map looks. An interesting drawing, and a small curious effect.
I began this little project because GeePaw Hill had started a similar one. His scheme is quite powerful: he works from a set of elementary shapes, polyominos, and attempts to tile the entire rectangular area with them. I think he started there because he had some tiling code left over from another project. So anyway, my scheme is much less intelligent, although arguably simpler. My program starts from some available cell in the space, and grows by appending available adjacent cells, picking cells and directions at random.
My scheme has some flaws, which we’ll discuss in a moment, but the main concern that I have with it is that if I envision the map it draws as an oddly rectangular cavern, it doesn’t look right to me. I’d expect fewer rooms, not all packed side by side, sometimes perhaps adjacent but more often connected by twisty little passages all different. Currently it looks like this:

Even if we were to just draw the borders, those shapes seem to me to be packed too closely together.
Also, there is a small red space near the lower left in the map above. That is actually part of one of the large red areas. Presently, when we’re building a room and can’t find any available space, we just grab available space elsewhere and then continue. I think we should stop.
So … what we have is doing what we asked of it, and we’re learning that we don’t quite like what we asked for. So we’ll ask for some other stuff until we find something good enough.
I think one thing that I’d like to do is to simply draw the borders between the rooms, coloring the floors all the same color. The borders should be on the lines between the rooms, I think. (Sometimes a dungeon will have walls that are one cell thick. Ours will have zero thickness if we stick with this idea.)
How can we do this? Maybe something like this:
for each room
for each cell in the room
for each neighbor of that cell
if the neighbor is NOT in the room
draw a border on that side of the cell
That’s a lot of checking, since our current sample map is 64*56 cells, which is, as all know, 3,584 cells, which will, as we all readily see, cause us to check 14,336 neighbors just to draw a few lines. I say piffle, sir, piffle! If this is too slow we’ll do something about it but I reckon it won’t be.
Let’s find a simple way to go about this. I think it’s a function of the DungeonView, so let’s look in there to see what it does now.
def main_loop(self):
running = True
moving = False
background = "gray33"
color = "darkblue"
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
self.screen.fill(background)
self.draw_grid(color)
self.draw_rooms()
self.clock.tick(60)
pygame.display.flip()
pygame.quit()
def draw_rooms(self):
for room in self.dungeon:
self.draw_room(room)
def draw_room(self, room):
view = RoomView(room)
view.draw(self.screen)
Huh. Why not ask the room to do the job? Makes some sense doesn’t it. Check RoomView.
- Note
- We chose to create a few RoomView each time through the loop. If the dungeon is stable, that’s wasteful, but drawing it 60 times a second is also wasteful. Maybe it won’t always be stable: maybe there will be things moving around in there.
Anyway …
class RoomView:
def __init__(self, room):
self.room = room
def draw(self, screen):
for cell in self.room.cells:
dx = cell[0] * cell_size
dy = cell[1] * cell_size
pygame.draw.rect(screen, self.room.color,
(dx, dy, cell_size, cell_size))
Seems like we could do a new version of draw that draws borders. Let’s Extract a Method, to preserve the existing draw:
def draw(self, screen):
for cell in self.room.cells:
self.draw_colored_cell(cell, screen)
def draw_colored_cell(self, cell, screen):
dx = cell[0] * cell_size
dy = cell[1] * cell_size
pygame.draw.rect(screen, self.room.color,
(dx, dy, cell_size, cell_size))
Ideally, we’d have a cell class to defer to but just now we have no such thing.
Anyway, by Wishful Thinking:
def draw(self, screen):
cells = set(self.room.cells)
for cell in cells:
self.draw_border(cell, cells, screen)
I made the cells temp a set, because we’re going to be asking in quite a lot. Now to code up draw_border according to the outline above.
After just a bit of hackery and of course getting all the combinations wrong, I have this:
def draw_border(self, cell, cells, screen):
x, y = cell
cx0 = cell[0]*cell_size
cy0 = cell[1]*cell_size
cx1 = cx0 + cell_size
cy1 = cy0 + cell_size
for neighbor in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
dx, dy = neighbor
check = (x+dx, y+dy)
if check not in cells:
if neighbor == (-1,0):
#left
self.line(cx0, cy0, cx0, cy1, screen)
pass
elif neighbor == (0,1):
#bottom
self.line(cx0, cy1, cx1, cy1, screen)
pass
elif neighbor == (0,-1):
#top
self.line(cx0, cy0, cx1, cy0, screen)
pass
elif neighbor == (1,0):
#right
self.line(cx1, cy0, cx1, cy1, screen)
pass
else:
raise(ValueError)
That’s all spaced out and commented like that because the picture is just a bit odd.
- Interesting
- This is the interesting drawing advertised in the blurb. I certainly prefer it to the colored one.

- Curious Effect
- And the advertised curious effect is a tiny square that we’ll chase for a bit. There’s an outlined square that somehow caught my eye. Including colors, it looks like this:

There are three greenish areas about 1/3 of the way down. Note that between the last two, there seems to be a single cell with all four sides drawn. Unless that is a single-cell room, I don’t see how that could happen. So I spent some time turning the sides on and off to see what was going on. I still don’t know.
Next step, let’s see if there are any cells that get all four sides. I just count neighbors that don’t match and print if all four are found:
neighbor_count = 0
for neighbor in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
dx, dy = neighbor
check = (x+dx, y+dy)
if check not in cells:
neighbor_count += 1
## drawing the lines skipped for brevity
if neighbor_count == 4:
print(f'all four sides! {cell=} {len(cells)=} {self.room.color}')
all four sides! cell=(49, 18) len(cells)=100 (128, 110, 92, 255)
all four sides! cell=(20, 20) len(cells)=100 (103, 229, 0, 255)
These are two different rooms, but I don’t know which ones. I think I’ll color them black.
That was a good idea:

Now we see a small black area beneath the isolated black cell. That tells me that we were growing in that confined space and ran out of cells and the bug fact that we just jump to a disconnected cell took us to that one. I’m not sure if it was just bad luck that we only found one there but the picture gives me confidence that we have a split room and very likely that little black cell belongs to neither of those green rooms. We plan to change that fact anyway, so I think we’re OK.
It does suggest that we should consider some kind of testing on rooms to make sure that they are never split into separate chunks and perhaps other criteria. Testing like that could get very messy, so I’d rather write code that can be tested to show that it never uses a cell that isn’t adjacent to a given cell. Failing that, the code needs to be obviously not capable of doing that. We can’t go around testing every possible map.
Summary
I’ve been at this for long enough to want a break and we’re at a decent spot. I’m not sure how I feel about the map with just the borders. It’s a lot less troubling in monochrome than with all those colors. I think we still want something more spaced out, but we’ll decide that next time, and then probably decide again a few times until we get a look that we like. This is the nature of a visual effect: we can’t really specify what we want but we know whether we like it.
So be it.
No big discoveries today. It seems likely that a Cell object would be helpful. There’s a lot of conversion from cell indexes to coordinates going on and maybe a Cell and perhaps other helpful objects could help clear that up. But it’s not horrible code as it stands. Not lovely, but mostly short and not too magical.
Might be nice to be more clear about how we get from the neighbor coordinates to the lines we draw, but I don’t see anything better just now. Perhaps some wise person on Mastodon will suggest something.
We have a better picture, in terms of “what we like”. Good progress.
See you next time!