New Shapes
Now that we can have as many room shapes as we can imagine, let’s imagine some. The results may surprise you. They surprised me, in a good way.
At Friday Geeks Night Out last Tuesday, it was generally agreed that my original room shape, which grows randomly by accretion, would be better if it had more extrusions and incursions, making it a bit less smooth i appearance. I’ve been thinking about different shapes and have some ideas, still created randomly, including:
-
Adjusting the weights of the four directions we might build in should let us create rooms that tend to be longer in one direction than in the other. Perhaps even rooms that look kind of diagonal would appear.
-
Making the cell most recently used have a higher chance of being selected again might make for interesting extrusions.
-
We can clearly build rectangular rooms quite readily, and should probably provide the capability for our right [angle] minded associates.
-
A room built of horizontal or vertical stripes, or both, might be interesting. What if the lengths varied substantially but started short, got long, then short again?
I expect that looking at these will give us ideas about other possibilities.
Mostly Experimental
The whole point of these notions is to build rooms that look good. As such, there are probably no substantive tests to be written for them, although we may encounter surprises that call for tests, and there may be associated tests as we work out ways to create the random values that will behave as we like. Mostly, though, we’ll be coding and looking at the resulting map.
Research
A single search for “python weighted random choice” tells me about random.choices, which takes a list of items to be chosen randomly, and a list of weights. Let’s gin up an experimental builder to see what we get. The tricky bit will be to come up with a basic scheme that lets us influence the choices. Let’s try a similar scheme to the basic cave builder but with a high probability of building off the cell most recently found.
I’ll just snag the basic cave code and put it into a new class, then hammer it until it submits.
Results
I’ll shows you the code in a moment but did you ever have something work so well that you felt you could throw away all your alternative ideas? I just did.
The basic idea behind the pictures that follow is that the standard random cave-making code, which selects a random cell from which to grow, has a high probability of using the most recently used cell, basically idea #2 above.
Here are the results:
50% chance of using previous growth cell

70% chance of using previous growth cell

90% chance of using previous growth cell

Use previous growth cell if possible

Frankly, I find those rooms to be simply marvelous. If you’re in the market for random cave-style rooms, it seems to me that you can get just what you want by tuning the chances of using the preceding cell for the next extension.
Sometimes you do bite the bear. Here’s the code, just a few lines long. This is just a patch: we’ll make the code more nearly right later:
class ExperimentalCellCollector:
def __init__(self):
self.cells:list[Cell] = []
self.growth_candidates:list[Cell] = []
def build(self, number_of_cells: int, origin: Cell, name =''):
self._build_cave(number_of_cells, origin)
return self.cells
def _build_cave(self, number_of_cells, origin):
new_cell: Cell = origin
for _ in range(number_of_cells):
self.take(new_cell)
self.growth_candidates.append(new_cell)
new_cell = self.find_adjacent_cell(new_cell)
if new_cell is None:
return
def take(self, new_cell: Cell):
self.cells.append(new_cell)
new_cell.room = self
def find_adjacent_cell(self, previous):
# changes start here
if random.random() < 0.5: # tune this value
available = previous.available_neighbors
if available:
return choice(available)
# changes end here
for cell in sample(self.growth_candidates,
len(self.growth_candidates)):
available = cell.available_neighbors
if available:
return choice(available)
else:
self.growth_candidates.remove(cell)
return None
That’s all there was to it. Roll the dice, and use the previous cell if it has any usable neighbors.
My tentative but likely plan is to provide an additional (optional?) parameter to the CaveCellCollector’s build. We might also explore using keyword parameters for the build, which should allow us a chance to consolidate some code in RoomMaker, if we think it makes sense.
Summary
These results are so fine that I’m going to stop. Got a late start, wrote half a dozen lines of code, spent most of a pleasant hour trying random values and enjoying maps.
Certainly the new code should just be integrated into the existing cave code, and I do think that we should look into keyword parameters for the various caves we can build. We’ll surely add rectangles, but I’m not sure I want to do much more than that: what we have is fine. Might be time to figure out the next steps for this little exercise.
See you next time!