Rooms Within Rooms
We sometimes get rooms within rooms by accident. Let’s see if we can do it on purpose.
Recall that we get fully connected rooms now by adding new rooms until the map is connected. This usually results in full connection with between a dozen and perhaps 20 rooms. I noticed, however, that sometimes the program would crash while trying to connect. It didn’t take long to notice that it was creating smaller and smaller rooms until it ran out of cells, having filled the entire available space, but it still thought that it wasn’t connected.
My experimental code went through a few quick versions that I don’t recall now, but in the end it turns out that I was throwing away rooms that were too small, and that I was not returning the cells that had been used in that room, so the room in question didn’t contribute to connection but left us with fewer and fewer cells, until we ran out.
Here’s what we have now, including some informational prints, and it seems always to generate nice fully-connected maps:
def main():
space = CellSpace(64, 56)
# random.seed(234)
dungeon = Dungeon()
number_of_rooms = random.randint(10,10)
maker = RoomMaker(space)
for _ in range(number_of_rooms):
origin = space.random_available_cell()
size = random.randint(100, 200)
room = maker.experimental(size, origin)
if len(room.cells) < 100:
print(len(room.cells))
dungeon.add_room(room)
# for room_1, room_2 in zip(dungeon.rooms, dungeon.rooms[1:]):
# dungeon.find_path_between_rooms(space, room_1, room_2)
while not dungeon.is_fully_connected:
origin = space.random_available_cell()
if origin is None:
break
size = random.randint(100, 200)
room = maker.experimental(size, origin)
if len(room.cells) >= 100:
dungeon.add_room(room)
else:
room.return_cells()
print(f'too small {len(room.cells)} cells')
print(f'{dungeon.is_fully_connected=}\n{len(dungeon.rooms)=}')
view = DungeonView(dungeon)
view.main_loop()
We have a new method on room, return_cells:
class Room:
def return_cells(self):
for cell in self.cells:
cell.room = None
So that’s good. Then I was trying to think of something interesting to do, and it occurred to me that we had talked, last Tuesday night, at Friday Geeks Night Out, about finding small holes in the map and making special rooms of them. I didn’t know then, and don’t know now how to find small holes in the map. But, at some point yesterday I thought about the rooms that the code above throws back as too small, and this scheme came to me:
My Cunning Plan
Build small secret rooms like this:
- Build a small room;
- Return its cells but don’t lose the room;
- Build a large room where the small one was;
- Have the small room take back its cell,
- While removing them from the large room.
So we have a plan. We’ll do it as an experiment, and see what it tells us about how the code might really do such a thing.
Let’s build a diamond room inside a round room, in main.
It goes quickly, adding this to main:
def main():
space = CellSpace(64, 56)
# random.seed(234)
dungeon = Dungeon()
number_of_rooms = random.randint(10,10)
maker = RoomMaker(space)
origin = space.at(32, 28)
diamond = maker.diamond(25, origin)
diamond.return_cells()
round = maker.round(8, origin)
diamond.reclaim_cells_from(round)
dungeon.add_room(diamond)
dungeon.add_room(round)
for _ in range(number_of_rooms):
...
And two new little methods in Room
class Room:
def forget(self, cell):
self.cells.remove(cell)
def reclaim_cells_from(self, other):
for cell in self.cells:
other.forget(cell)
cell.room = self
We get just what we expected:

A Fine Result
Isn’t that a nice one? The map is divided into two separate sections, separated by our room within room. Very ominous. Entirely accidental.
So that worked exactly as intended, with little or no difficulty. I can see some things to improve, including but not limited to:
- The
forgetmethod could fail if the cell was already absent from the forgetting room: we should handle that. - There are many possible combinations of rooms within rooms that we might want, such as round within diamond, rectangle within rectangle and so on. We don’t even have rectangular rooms yet.
- There should be some kind of general creation support for rooms within rooms, specialized factories or something.
- Quite possibly a room created this way should “know” that it contains an inner room, since it might be important at the level of the actual game, if we were ever to produce a game using these maps.
Summary
I like to stop on a win. Commit: rooms within rooms.
Looking forward, I have a general feeling that we need some improvement. We have done a lot of experimental work that needs to be reviewed and consolidated. It’s working fine, but the code isn’t what it could be. I’m thinking, for example:
-
Dungeon creation is ad-hoc, inside main. We should allow for that sort of thing, but I have the feeling that it could be made less chaotic.
-
The ‘Experimental’ room factory needs to give up its hold in favor of a general scheme that embodies how that now works.
-
We need at least one, perhaps a few more room types.
-
Room creation parameters are different, but similar enough to be confusing. We might be able to do better.
-
Room combinations, not just rooms within rooms, could probably be devised. Imagine a nice linear series of regular shapes leading to some big boss.
-
Different dungeon styles might be devised, not just the cavern style we have here.
-
It seems likely that we could have better ways of “tuning” rooms than things like that probability for whether to use the preceding cell. (Don’t worry if you don’t remember that: I do.)
-
… I could go on …
And we definitely could use some code cleanup. We’re running pretty fast, sort of leaning forward. Not quite thrashing our arms yet but if we don’t settle down we’re going to fall on our nose with code we can no longer trust nor control. It’s time to settle down from the wild and woolly experimental mode and get back to slow and smooth.
But those dungeons look good to me! I am pleased that such an ad-hoc yet simple approach has made such nice organic-seeming shapes. And I did it with my own little fingers and little brain, no AI-LLM here, no sir, no ma’am, no my friend, no.
See you next time!