Cleaning
Hello, loves!
I have a bit of time where I can’t concentrate deeply but can probably do some easy things. A new class reduces DungeonLayout.
Let’s see what we can find that might be useful. A bit of mess reduction.
Here’s the code that turns a picture of a map into cells:
class DungeonLayout:
# probably should be separate object.
def add_rooms_from_map(self, string_map):
d = self.string_map_to_dictionary(string_map)
self.add_from_dictionary(d)
@staticmethod
def string_map_to_dictionary(string_map):
string_map = textwrap.dedent(string_map)
strings = string_map.split('\n')
strings = [string for string in strings if string]
room_cells = dict()
for y, line in enumerate(strings):
for x, char in enumerate(line):
if char != '.':
if not char in room_cells:
room_cells[char] = []
room_cells[char].append((x, y))
return room_cells
def add_from_dictionary(self, name_to_xy):
for name, tuples in name_to_xy.items():
cells = [ Cell(x,y) for x,y in tuples ]
self.add_room(Room(cells, name))
Like the comment says, that should probably be its own object. The static method is a clear signal that it doesn’t need to be in the layout, although as written the add_from does. What is it to be called? It makes rooms from a map and adds them to whoever asks. I think we’ll have it generate the rooms, most likely.
No, it makes rooms from a string map. I think it is a StringMap whose input is a string. We’ll rename it as needed. As written, it doesn’t declare the layout. We use it only in tests, like this:
def test_long_path_to_nearby_cell(self):
layout = DungeonLayout(5, 3)
map = '''
111..
..1..
111..
'''
layout.add_rooms_from_map(map)
...
I think this needs to be a factory object. Let’s just repurpose that test right there.
def test_long_path_to_nearby_cell(self):
map = '''
111..
..1..
111..
'''
layout = TextLayout(map).layout
assert layout.max_x == 5
assert layout.max_y == 3
origin = Cell(0, 2)
target = Cell(0,0)
results = dict()
for cell, distance in (Flooder(layout=layout, origin=origin)
.in_any_room()
.flood()):
results[cell] = distance
assert origin.manhattan_distance(target) == 2
assert results[target] == 6
We’ll add the check that the layout is the right size as well.
Now to do it. It’s mostly just moving the code into a class:
class TextLayout:
def __init__(self, text):
self.max_x = 0
self.max_y = 0
map = self.string_map_to_dictionary(text)
self.layout = DungeonLayout(self.max_x, self.max_y)
self.add_from_dictionary(map)
def string_map_to_dictionary(self, string_map):
string_map = textwrap.dedent(string_map)
strings = string_map.split('\n')
strings = [string for string in strings if string]
self.max_y = len(strings)
self.max_x = len(strings[0])
room_cells = dict()
for y, line in enumerate(strings):
for x, char in enumerate(line):
if char != '.':
if not char in room_cells:
room_cells[char] = []
room_cells[char].append((x, y))
return room_cells
def add_from_dictionary(self, name_to_xy):
for name, tuples in name_to_xy.items():
cells = [ Cell(x,y) for x,y in tuples ]
self.layout.add_room(Room(cells, name))
Now use it for all the callers of the one in DungeonLayout … and done … remove the old code, commit.
Summary
There’s about ten percent of the length of DungeonLayout moved off to the side. Making a dungeon from a text map is now nicely isolated into a class that just does that. Could have done a forwarding method in DungeonLayout but changing the tests was easy enough.
Small changes toward better. This is the way. See you next time!