Skip to content

Instantly share code, notes, and snippets.

@Ludisposed
Last active January 14, 2019 13:01
Show Gist options
  • Save Ludisposed/fae6c40d1eea9de6e92c672ac63fda18 to your computer and use it in GitHub Desktop.
Save Ludisposed/fae6c40d1eea9de6e92c672ac63fda18 to your computer and use it in GitHub Desktop.
from queue import Queue
from random import choice, randint, seed
from collections import namedtuple
from enum import Enum
import itertools
Position = namedtuple("Position", ["y", "x"])
class Tile(Enum):
WALL = '#'
EMPTY = ' '
ROCK = '.'
class Split(Enum):
HORIZONTAL = 0
VERTICAL = 1
class Room():
def __init__(self, lu, rd):
self.lu = lu
self.rd = rd
@property
def position_map(self):
return self._get_positions()
@property
def border_map(self):
return self._get_border()
@property
def width(self):
return self.rd.x - self.lu.x
@property
def height(self):
return self.rd.y - self.lu.y
@property
def down(self):
return self.rd.y
@property
def up(self):
return self.lu.y
@property
def left(self):
return self.lu.x
@property
def right(self):
return self.rd.x
def _get_positions(self):
for y, x in itertools.product(
range(self.left + 1, self.right), range(self.down + 1, self.up)
):
yield Position(y, x)
def _get_border(self):
left_wall = (Position(y, self.left) for y in range(self.up, self.down))
right_wall = (Position(y, self.right) for y in range(self.up, self.down))
upper_wall = (Position(self.up, x) for x in range(self.left, self.right))
lower_wall = (Position(self.down, x) for x in range(self.left, self.right + 1))
return itertools.chain(upper_wall, right_wall, lower_wall, left_wall)
class Leaf():
def __init__(self, lu, rd, parent):
self.lu = lu
self.rd = rd
self.parent = parent
self._children = None
self._sibling = None
self._room = None
@property
def children(self):
return self._children
@children.setter
def children(self, value):
self._children = value
@property
def sibling(self):
return self._sibling
@sibling.setter
def sibling(self, value):
self._sibling = value
@property
def room(self):
return self._room or self._generate_room()
@property
def width(self):
return self.rd.x - self.lu.x
@property
def height(self):
return self.rd.y - self.lu.y
@property
def down(self):
return self.rd.y
@property
def up(self):
return self.lu.y
@property
def left(self):
return self.lu.x
@property
def right(self):
return self.rd.x
def __str__(self):
return f"{self.lu} {self.rd}"
def _generate_room(self):
room = Room(self.lu, self.rd)
self._room = room
return room
class NotSplittableException(Exception):
pass
class Map():
def __init__(self, width, height, min_room_space=range(10, 15), split_threshold=1.25):
self.width = width
self.height = height
self.lu = Position(0, 0)
self.rd = Position(height-1, width-1)
self.min_room_space = min_room_space
self.split_threshold = split_threshold
self._leaves = None
self.board = [[Tile.ROCK] * (self.width) for _ in range(self.height)]
@property
def leaves(self):
if self._leaves is None:
self._leaves = tuple(self._generate_leaves())
return self._leaves
def generate(self, random_seed=None):
seed(random_seed)
self.board = [[Tile.ROCK] * (self.width) for _ in range(self.height)]
self._generate_leaves()
for leaf in self.leaves:
for pos in leaf.room.position_map:
self.board[pos.y][pos.x] = Tile.EMPTY
for pos in leaf.room.border_map:
self.board[pos.y][pos.x] = Tile.WALL
#TODO How do I connect the rooms here?
# ie... make sure that all rooms could be accesed through some path?
print()
print(leaf)
print(leaf.sibling)
def _generate_leaves(self):
splitable = Queue()
splitable.put(Leaf(self.lu, self.rd, None))
leaves = Queue()
while splitable.qsize() > 0:
leaf = splitable.get()
try:
leaf_a, leaf_b = self._split(leaf)
leaf.children = (leaf_a, leaf_b)
leaf_a.sibling = leaf_b
leaf_b.sibling = leaf_a
splitable.put(leaf_a)
splitable.put(leaf_b)
except NotSplittableException:
yield leaf
def _split(self, leaf):
if leaf.width / leaf.height >= self.split_threshold:
split_f = self._split_horizontal
elif leaf.height / leaf.width >= self.split_threshold:
split_f = self._split_vertical
else:
split_f = choice((self._split_vertical, self._split_horizontal))
return split_f(leaf)
def _split_vertical(self, leaf):
min_room_space = choice(self.min_room_space)
if leaf.height < min_room_space * 2:
raise NotSplittableException
random_split = randint(leaf.up + min_room_space, leaf.down - min_room_space)
leaf_a = Leaf(
leaf.lu,
Position(random_split, leaf.right),
leaf
)
leaf_b = Leaf(
Position(random_split + 1, leaf.left),
leaf.rd,
leaf
)
return leaf_a, leaf_b
def _split_horizontal(self, leaf):
min_room_space = choice(self.min_room_space)
if leaf.width < min_room_space * 2:
raise NotSplittableException
random_split = randint(leaf.left + min_room_space, leaf.right - min_room_space)
leaf_a = Leaf(
leaf.lu,
Position(leaf.down, random_split),
leaf
)
leaf_b = Leaf(
Position(leaf.up, random_split + 1),
leaf.rd,
leaf
)
return leaf_a, leaf_b
def __str__(self):
return "\n".join("".join(tile.value for tile in b) for b in self.board)
if __name__ == "__main__":
m = Map(50, 50)
m.generate()
print(m)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment