SHARE
TWEET

A Python Dungeon Generator

Shade_JS Apr 11th, 2016 (edited) 937 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #! /usr/bin/env python
  2. # coding: utf-8
  3.  
  4. # generator-1.py, a simple python dungeon generator by
  5. # James Spencer <jamessp [at] gmail.com>.
  6.  
  7. # To the extent possible under law, the person who associated CC0 with
  8. # pathfinder.py has waived all copyright and related or neighboring rights
  9. # to pathfinder.py.
  10.  
  11. # You should have received a copy of the CC0 legalcode along with this
  12. # work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
  13.  
  14. from __future__ import print_function
  15. import random
  16.  
  17. CHARACTER_TILES = {'stone': ' ',
  18.                    'floor': '.',
  19.                    'wall': '#'}
  20.  
  21.  
  22. class Generator():
  23.     def __init__(self, width=64, height=64, max_rooms=15, min_room_xy=5,
  24.                  max_room_xy=10, rooms_overlap=False, random_connections=1,
  25.                  random_spurs=3, tiles=CHARACTER_TILES):
  26.         self.width = width
  27.         self.height = height
  28.         self.max_rooms = max_rooms
  29.         self.min_room_xy = min_room_xy
  30.         self.max_room_xy = max_room_xy
  31.         self.rooms_overlap = rooms_overlap
  32.         self.random_connections = random_connections
  33.         self.random_spurs = random_spurs
  34.         self.tiles = CHARACTER_TILES
  35.         self.level = []
  36.         self.room_list = []
  37.         self.corridor_list = []
  38.         self.tiles_level = []
  39.  
  40.     def gen_room(self):
  41.         x, y, w, h = 0, 0, 0, 0
  42.  
  43.         w = random.randint(self.min_room_xy, self.max_room_xy)
  44.         h = random.randint(self.min_room_xy, self.max_room_xy)
  45.         x = random.randint(1, (self.width - w - 1))
  46.         y = random.randint(1, (self.height - h - 1))
  47.  
  48.         return [x, y, w, h]
  49.  
  50.     def room_overlapping(self, room, room_list):
  51.         x = room[0]
  52.         y = room[1]
  53.         w = room[2]
  54.         h = room[3]
  55.  
  56.         for current_room in room_list:
  57.  
  58.             # The rectangles don't overlap if
  59.             # one rectangle's minimum in some dimension
  60.             # is greater than the other's maximum in
  61.             # that dimension.
  62.  
  63.             if (x < (current_room[0] + current_room[2]) and
  64.                 current_room[0] < (x + w) and
  65.                 y < (current_room[1] + current_room[3]) and
  66.                 current_room[1] < (y + h)):
  67.  
  68.                 return True
  69.  
  70.         return False
  71.  
  72.  
  73.     def corridor_between_points(self, x1, y1, x2, y2, join_type='either'):
  74.         if x1 == x2 and y1 == y2 or x1 == x2 or y1 == y2:
  75.             return [(x1, y1), (x2, y2)]
  76.         else:
  77.             # 2 Corridors
  78.             # NOTE: Never randomly choose a join that will go out of bounds
  79.             # when the walls are added.
  80.             join = None
  81.             if join_type is 'either' and set([0, 1]).intersection(
  82.                  set([x1, x2, y1, y2])):
  83.  
  84.                 join = 'bottom'
  85.             elif join_type is 'either' and set([self.width - 1,
  86.                  self.width - 2]).intersection(set([x1, x2])) or set(
  87.                  [self.height - 1, self.height - 2]).intersection(
  88.                  set([y1, y2])):
  89.  
  90.                 join = 'top'
  91.             elif join_type is 'either':
  92.                 join = random.choice(['top', 'bottom'])
  93.             else:
  94.                 join = join_type
  95.  
  96.             if join is 'top':
  97.                 return [(x1, y1), (x1, y2), (x2, y2)]
  98.             elif join is 'bottom':
  99.                 return [(x1, y1), (x2, y1), (x2, y2)]
  100.  
  101.     def join_rooms(self, room_1, room_2, join_type='either'):
  102.         # sort by the value of x
  103.         sorted_room = [room_1, room_2]
  104.         sorted_room.sort(key=lambda x_y: x_y[0])
  105.  
  106.         x1 = sorted_room[0][0]
  107.         y1 = sorted_room[0][1]
  108.         w1 = sorted_room[0][2]
  109.         h1 = sorted_room[0][3]
  110.         x1_2 = x1 + w1 - 1
  111.         y1_2 = y1 + h1 - 1
  112.  
  113.         x2 = sorted_room[1][0]
  114.         y2 = sorted_room[1][1]
  115.         w2 = sorted_room[1][2]
  116.         h2 = sorted_room[1][3]
  117.         x2_2 = x2 + w2 - 1
  118.         y2_2 = y2 + h2 - 1
  119.  
  120.         # overlapping on x
  121.         if x1 < (x2 + w2) and x2 < (x1 + w1):
  122.             jx1 = random.randint(x2, x1_2)
  123.             jx2 = jx1
  124.             tmp_y = [y1, y2, y1_2, y2_2]
  125.             tmp_y.sort()
  126.             jy1 = tmp_y[1] + 1
  127.             jy2 = tmp_y[2] - 1
  128.  
  129.             corridors = self.corridor_between_points(jx1, jy1, jx2, jy2)
  130.             self.corridor_list.append(corridors)
  131.  
  132.         # overlapping on y
  133.         elif y1 < (y2 + h2) and y2 < (y1 + h1):
  134.             if y2 > y1:
  135.                 jy1 = random.randint(y2, y1_2)
  136.                 jy2 = jy1
  137.             else:
  138.                 jy1 = random.randint(y1, y2_2)
  139.                 jy2 = jy1
  140.             tmp_x = [x1, x2, x1_2, x2_2]
  141.             tmp_x.sort()
  142.             jx1 = tmp_x[1] + 1
  143.             jx2 = tmp_x[2] - 1
  144.  
  145.             corridors = self.corridor_between_points(jx1, jy1, jx2, jy2)
  146.             self.corridor_list.append(corridors)
  147.  
  148.         # no overlap
  149.         else:
  150.             join = None
  151.             if join_type is 'either':
  152.                 join = random.choice(['top', 'bottom'])
  153.             else:
  154.                 join = join_type
  155.  
  156.             if join is 'top':
  157.                 if y2 > y1:
  158.                     jx1 = x1_2 + 1
  159.                     jy1 = random.randint(y1, y1_2)
  160.                     jx2 = random.randint(x2, x2_2)
  161.                     jy2 = y2 - 1
  162.                     corridors = self.corridor_between_points(
  163.                         jx1, jy1, jx2, jy2, 'bottom')
  164.                     self.corridor_list.append(corridors)
  165.                 else:
  166.                     jx1 = random.randint(x1, x1_2)
  167.                     jy1 = y1 - 1
  168.                     jx2 = x2 - 1
  169.                     jy2 = random.randint(y2, y2_2)
  170.                     corridors = self.corridor_between_points(
  171.                         jx1, jy1, jx2, jy2, 'top')
  172.                     self.corridor_list.append(corridors)
  173.  
  174.             elif join is 'bottom':
  175.                 if y2 > y1:
  176.                     jx1 = random.randint(x1, x1_2)
  177.                     jy1 = y1_2 + 1
  178.                     jx2 = x2 - 1
  179.                     jy2 = random.randint(y2, y2_2)
  180.                     corridors = self.corridor_between_points(
  181.                         jx1, jy1, jx2, jy2, 'top')
  182.                     self.corridor_list.append(corridors)
  183.                 else:
  184.                     jx1 = x1_2 + 1
  185.                     jy1 = random.randint(y1, y1_2)
  186.                     jx2 = random.randint(x2, x2_2)
  187.                     jy2 = y2_2 + 1
  188.                     corridors = self.corridor_between_points(
  189.                         jx1, jy1, jx2, jy2, 'bottom')
  190.                     self.corridor_list.append(corridors)
  191.  
  192.  
  193.     def gen_level(self):
  194.  
  195.         # build an empty dungeon, blank the room and corridor lists
  196.         for i in range(self.height):
  197.             self.level.append(['stone'] * self.width)
  198.         self.room_list = []
  199.         self.corridor_list = []
  200.  
  201.         max_iters = self.max_rooms * 5
  202.  
  203.         for a in range(max_iters):
  204.             tmp_room = self.gen_room()
  205.  
  206.             if self.rooms_overlap or not self.room_list:
  207.                 self.room_list.append(tmp_room)
  208.             else:
  209.                 tmp_room = self.gen_room()
  210.                 tmp_room_list = self.room_list[:]
  211.  
  212.                 if self.room_overlapping(tmp_room, tmp_room_list) is False:
  213.                     self.room_list.append(tmp_room)
  214.  
  215.             if len(self.room_list) >= self.max_rooms:
  216.                 break
  217.  
  218.         # connect the rooms
  219.         for a in range(len(self.room_list) - 1):
  220.             self.join_rooms(self.room_list[a], self.room_list[a + 1])
  221.  
  222.         # do the random joins
  223.         for a in range(self.random_connections):
  224.             room_1 = self.room_list[random.randint(0, len(self.room_list) - 1)]
  225.             room_2 = self.room_list[random.randint(0, len(self.room_list) - 1)]
  226.             self.join_rooms(room_1, room_2)
  227.  
  228.         # do the spurs
  229.         for a in range(self.random_spurs):
  230.             room_1 = [random.randint(2, self.width - 2), random.randint(
  231.                      2, self.height - 2), 1, 1]
  232.             room_2 = self.room_list[random.randint(0, len(self.room_list) - 1)]
  233.             self.join_rooms(room_1, room_2)
  234.  
  235.         # fill the map
  236.         # paint rooms
  237.         for room_num, room in enumerate(self.room_list):
  238.             for b in range(room[2]):
  239.                 for c in range(room[3]):
  240.                     self.level[room[1] + c][room[0] + b] = 'floor'
  241.  
  242.         # paint corridors
  243.         for corridor in self.corridor_list:
  244.             x1, y1 = corridor[0]
  245.             x2, y2 = corridor[1]
  246.             for width in range(abs(x1 - x2) + 1):
  247.                 for height in range(abs(y1 - y2) + 1):
  248.                     self.level[min(y1, y2) + height][
  249.                         min(x1, x2) + width] = 'floor'
  250.  
  251.             if len(corridor) == 3:
  252.                 x3, y3 = corridor[2]
  253.  
  254.                 for width in range(abs(x2 - x3) + 1):
  255.                     for height in range(abs(y2 - y3) + 1):
  256.                         self.level[min(y2, y3) + height][
  257.                             min(x2, x3) + width] = 'floor'
  258.  
  259.         # paint the walls
  260.         for row in range(1, self.height - 1):
  261.             for col in range(1, self.width - 1):
  262.                 if self.level[row][col] == 'floor':
  263.                     if self.level[row - 1][col - 1] == 'stone':
  264.                         self.level[row - 1][col - 1] = 'wall'
  265.  
  266.                     if self.level[row - 1][col] == 'stone':
  267.                         self.level[row - 1][col] = 'wall'
  268.  
  269.                     if self.level[row - 1][col + 1] == 'stone':
  270.                         self.level[row - 1][col + 1] = 'wall'
  271.  
  272.                     if self.level[row][col - 1] == 'stone':
  273.                         self.level[row][col - 1] = 'wall'
  274.  
  275.                     if self.level[row][col + 1] == 'stone':
  276.                         self.level[row][col + 1] = 'wall'
  277.  
  278.                     if self.level[row + 1][col - 1] == 'stone':
  279.                         self.level[row + 1][col - 1] = 'wall'
  280.  
  281.                     if self.level[row + 1][col] == 'stone':
  282.                         self.level[row + 1][col] = 'wall'
  283.  
  284.                     if self.level[row + 1][col + 1] == 'stone':
  285.                         self.level[row + 1][col + 1] = 'wall'
  286.  
  287.     def gen_tiles_level(self):
  288.  
  289.         for row_num, row in enumerate(self.level):
  290.             tmp_tiles = []
  291.  
  292.             for col_num, col in enumerate(row):
  293.                 if col == 'stone':
  294.                     tmp_tiles.append(self.tiles['stone'])
  295.                 if col == 'floor':
  296.                     tmp_tiles.append(self.tiles['floor'])
  297.                 if col == 'wall':
  298.                     tmp_tiles.append(self.tiles['wall'])
  299.  
  300.             self.tiles_level.append(''.join(tmp_tiles))
  301.  
  302.         print('Room List: ', self.room_list)
  303.         print('\nCorridor List: ', self.corridor_list)
  304.  
  305.         [print(row) for row in self.tiles_level]
  306.  
  307.  
  308. if __name__ == '__main__':
  309.     gen = Generator()
  310.     gen.gen_level()
  311.     gen.gen_tiles_level()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top