PyCon 2014
April 11, 2014
410

# Castle Anthrax: Dungeon Generation Techniques by James King

April 11, 2014

## Transcript

1. James King
@agentultra
[email protected]

2. Dungeon Generation Techniques

3. http://goo.gl/INm3W9

4. [[0 for x in range(5)] for y in range(5)]

5. [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]

6. [0 for _ in range(5 * 5)]

7. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

8. Row Major Order
offset = (row * num_cols) + column

9. g = Grid(5, 5)
g[1, 2] = 3
g[1, 2]
# 3

10. Mazes

11. def generate_maze():
grid = Grid(MAZE_ROWS, MAZE_COLS, value=default_cell)
stack = []
current_cell = random.choice(grid.coordinates)
grid[current_cell]['visited'] = True
stack.append(current_cell)
!
while stack:
ns = unvisited_neighbours(grid, current_cell)
if ns:
n = random.choice(ns)
remove_wall_between(current_cell, n, grid)
stack.append(current_cell)
current_cell = n
grid[current_cell]['visited'] = True
else:
current_cell = stack.pop()
!
return grid

12. def generate_maze():
grid = Grid(MAZE_ROWS, MAZE_COLS, default_cell)
frontier = []
start_cell = random.choice(grid.coordinates)
grid[start_cell]['set'] = INTERIOR
ns = neighbours(grid, start_cell, is_unvisited)
for n in ns:
grid[n]['set'] = FRONTIER
frontier.extend(ns)
!
while frontier:
frontier_cell = frontier.pop(random.randrange(len(frontier)))
interior_cell = random.choice(
neighbours(grid, frontier_cell, is_interior))
remove_wall_between(interior_cell, frontier_cell, grid)
grid[frontier_cell]['set'] = INTERIOR
new_frontier_cells = neighbours(grid,
frontier_cell,
is_unvisited)
for nfc in new_frontier_cells:
grid[nfc]['set'] = FRONTIER
frontier.append(nfc)
!
return grid

13. http://goo.gl/teGL8D

14. http://goo.gl/vZ0EH1

15. • Poisson Disks
• Cellular Automata
• Perlin Noise

16. class Variable(object):
!
def __init__(self, values):
self.values = set(values)
!
...

17. class Variable(object):
!
...
!
def narrow(self, values):
new_values = self.values & values
if not new_values:
return False
!
if self.values != new_values:
if self._last_save_frame != self._current_save_frame:
self._save(new_values)
!
for constraint in self.constraints:
constraint.propagate(self)

18. import abc
!
!
class Constraint(object):
__metaclass__ = abc.ABCMeta
!
@abc.abstractproperty
def variables(self):
""" The variables involved in the constraint."""
!
@abc.abstractmethod
def propagate(self, variable):
"""
Propagate the values in `variable` to each of the
variables involved in
this constraint.
"""

19. # from a hypothetical InequalityConstraint

def propagate(self, variable):
if variable.is_unique:
for v in self.variables:
v.narrow(v.values - variable.values)

20. def solve():
if all(v.is_unique for v in variables):
return (v.values[0] for v in variables)
!
# some undo-stack management code here...
!
variable = random.choice(v for v in variables if v.values > 1)
for value in variable.values:
if not variable.narrow(value)
return
!
if not any(v.is_empty for v in variables):
if not solve():
return
!
# more undo stack management

21. Optimize!
• Undo-stack
• Use as few variables as possible stay in the
discrete/ﬁnite domain as much as possible

22. Thanks!

23. • Rogue Basin http://goo.gl/UTlwFU
• How to Build a Contraint Propagator in a Weekend
http://goo.gl/sdrbkJ
• Poisson Disk Sampling http://goo.gl/71OzY
• Artiﬁcial Intelligence: A Modern Approach
http://goo.gl/CCCel8
• The Art of Computer Programming
http://goo.gl/EUhZ8M

24. • Doryen Library http://goo.gl/71EpZx
• Horton http://goo.gl/xpLTFB
• Pygame http://goo.gl/1hv6DK
• PyAngband http://goo.gl/mr5MMD