Last active
January 14, 2019 13:01
-
-
Save Ludisposed/fae6c40d1eea9de6e92c672ac63fda18 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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