Upon Reflection
Hello, loves!
I think PassageCollection should have the protection against duplication built in. We have a lambda, how nice.
As things stand, the user of the PassageCollection is responsible for ensuring that there’s only one passage per room. Since that’s the default behavior we want, let’s build it into the object. Let’s have add_passage return True if the passage was added, and False if it was not.
Creating a passage to add is costly, so let’s see if we can pass in a lambda that is only called if needed. Let’s enhance our test.
def test_create_passage(self):
layout = DungeonLayout(10, 10)
room_1 = Room([Cell(0,0), Cell(0,1), Cell(0,2)])
room_2 = Room([Cell(1,0), Cell(1,1), Cell(1,2)])
layout.add_room(room_1)
layout.add_room(room_2)
passages = PassageCollection()
cells = lambda: (Cell(0,0), Cell(1,0))
done = passages.create_passage(room_1, room_2, cells)
assert done == True
assert len(passages.cell_connections) == 2
assert len(passages.room_connections) == 2
cells = lambda: (Cell(0,22), Cell(1,2))
done = passages.create_passage(room_1, room_2, cells)
assert done == False
assert len(passages.cell_connections) == 2
assert len(passages.room_connections) == 2
And this:
class PassageCollection:
def create_passage(self, r1: Room, r2: Room, func):
if self.rooms_are_connected(r1, r2):
return False
else:
passage = func()
self.add_passage(passage)
return True
I expect that no one will look at the return except for the tests.
Now we can change the production code from this:
class DungeonLayout:
def make_passages(self):
for r1, r2 in itertools.combinations(self.rooms, 2):
if not self.passages.rooms_are_connected(r1, r2):
passage = self.random_passage(r1, r2)
self.add_passage(passage)
To this:
def make_passages(self):
for r1, r2 in itertools.combinations(self.rooms, 2):
self.passages.create_passage(r1, r2, lambda: self.random_passage(r1, r2))
Much nicer. Let’s rename the add method now.
def create_passage(self, r1: Room, r2: Room, func):
if self.rooms_are_connected(r1, r2):
return False
else:
passage = func()
self.add_passage_unconditionally(passage)
return True
def add_passage_unconditionally(self, passage:tuple[Cell, Cell]):
if passage:
c1, c2 = passage
self.cell_connections.add((c1, c2))
self.cell_connections.add((c2, c1))
self.room_connections.add((c1.room, c2.room))
self.room_connections.add((c2.room, c1.room))
That looks good. Commit: PassageCollection create_passage calls creation lambda only if a passage is not already present for the provided rooms.
Summary
I almost invariably find that after a break of a half day or day, some improvement ideas come up for something I’ve just done. I find it both useful and pleasant to pay attention to the ideas and usually find something worth doing.
Here, we have moved a bit of complexity inside the PassageCollection, and our use of it is a bit simpler, although we do now have a lambda to call, but as that is quite common in the dungeon, I think it’s fair to think of it as a well-understood technique.
And there is no one here to say me nay. See you next time!