Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Castle Anthrax: Dungeon Generation Techniques by James King

PyCon 2014
April 11, 2014
410

Castle Anthrax: Dungeon Generation Techniques by James King

PyCon 2014

April 11, 2014
Tweet

More Decks by PyCon 2014

Transcript

  1. James King
    @agentultra
    [email protected]

    View Slide

  2. Dungeon Generation Techniques

    View Slide

  3. http://goo.gl/INm3W9

    View Slide

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

    View Slide

  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]]

    View Slide

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

    View Slide

  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]

    View Slide

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

    View Slide

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

    View Slide

  10. Mazes

    View Slide

  11. View Slide

  12. 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

    View Slide

  13. View Slide

  14. 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

    View Slide

  15. http://goo.gl/teGL8D

    View Slide

  16. http://goo.gl/vZ0EH1

    View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. • Poisson Disks
    • Cellular Automata
    • Perlin Noise

    View Slide

  25. View Slide

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

    View Slide

  27. 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)

    View Slide

  28. 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.
    """

    View Slide

  29. # from a hypothetical InequalityConstraint

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

    View Slide

  30. 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

    View Slide

  31. Optimize!
    • Represent sets as bitmasks
    • Undo-stack
    • Use as few variables as possible stay in the
    discrete/finite domain as much as possible

    View Slide

  32. Thanks!

    View Slide

  33. • 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
    • Artificial Intelligence: A Modern Approach
    http://goo.gl/CCCel8
    • The Art of Computer Programming
    http://goo.gl/EUhZ8M

    View Slide

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

    View Slide