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
420

Castle Anthrax: Dungeon Generation Techniques by James King

PyCon 2014

April 11, 2014
Tweet

More Decks by PyCon 2014

Transcript

  1. [[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]]
  2. [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]
  3. 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
  4. 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
  5. 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)
  6. 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. """
  7. # from a hypothetical InequalityConstraint … def propagate(self, variable): if

    variable.is_unique: for v in self.variables: v.narrow(v.values - variable.values)
  8. 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
  9. Optimize! • Represent sets as bitmasks • Undo-stack • Use

    as few variables as possible stay in the discrete/finite domain as much as possible
  10. • 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