Files
maze-game/maze.py
2021-09-15 21:24:34 +01:00

98 lines
2.9 KiB
Python

import random
from itertools import product
from typing import Tuple, Set, Union, Any, List, NamedTuple
from pprint import pprint
class Cell(NamedTuple):
x: int
y: int
class Wall(NamedTuple):
first_cell: Cell
second_cell: Cell
class Maze:
def __init__(self, width: int, height: int):
self.distance_map = dict()
self.cells = set(Cell(*c) for c in product(range(width), range(height)))
self.maze = dict()
for cell in self.cells:
self.maze[cell] = self.get_neighbours(cell)
self.walls = self.get_walls()
self.generate_maze()
self.remove_blocked_links()
self.build_map()
self.visited = set()
self.dead_ends = dict()
self.dead_end_limit = 5
def check_dead_end(self, cell: Cell) -> bool:
# return true is new dead end
if cell in self.dead_ends:
return False
if len(self.maze[cell]) == 1:
self.dead_ends[cell] = 0
return True
def build_map(self):
distance = 0
first_cell = max(self.cells)
visited = set()
visited.add(first_cell)
unvisited = self.cells.copy()
unvisited.remove(first_cell)
self.distance_map[first_cell] = 0
current_group = self.maze[first_cell]
while unvisited:
next_group = set()
distance += 1
for node in current_group:
self.distance_map[node] = distance
next_group.update(self.maze[node])
visited.add(node)
unvisited.remove(node)
current_group = next_group.difference(visited)
def get_neighbours(self, cell: Cell) -> List[Cell]:
diffs = ((0, 1), (0, -1), (1, 0), (-1, 0))
return list(Cell(cell.x + x, cell.y + y) for x, y in diffs if Cell(cell.x + x, cell.y + y) in self.cells)
def get_walls(self) -> Set[Wall]:
walls = set()
for cell in self.cells:
walls.update(set(Wall(*sorted([cell, c])) for c in self.maze[cell]))
return walls
def remove_blocked_links(self):
for wall in self.walls:
cell1, cell2 = wall
self.maze[cell1].remove(cell2)
self.maze[cell2].remove(cell1)
def generate_maze(self) -> None:
visited = set()
unvisited = list(self.cells)
node = random.choice(unvisited)
visited.add(node)
unvisited.remove(node)
while unvisited:
random_neighbour = random.choice(list(self.maze[node]))
if random_neighbour not in visited:
self.walls.remove(Wall(*sorted([node, random_neighbour])))
visited.add(random_neighbour)
try:
unvisited.remove(random_neighbour)
except ValueError:
pass
node = random_neighbour
if __name__ == "__main__":
maze = Maze(3, 3)
pprint(maze.walls)