| 1 | #!/usr/bin/env python |
|---|
| 2 | # |
|---|
| 3 | # maze.py |
|---|
| 4 | # Generating Maze using Python |
|---|
| 5 | # http://mornie.org/blog/2007/08/02/Generating-Maze-using-Python/ |
|---|
| 6 | # |
|---|
| 7 | # Copyright (C) 2007 Daniele Tricoli aka Eriol <eriol@mornie.org> |
|---|
| 8 | # |
|---|
| 9 | # This program is free software; you can redistribute it and/or |
|---|
| 10 | # modify it under the terms of the GNU General Public License |
|---|
| 11 | # as published by the Free Software Foundation; either version 2 |
|---|
| 12 | # of the License, or (at your option) any later version. |
|---|
| 13 | # |
|---|
| 14 | # This program is distributed in the hope that it will be useful, |
|---|
| 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | # GNU General Public License for more details. |
|---|
| 18 | # |
|---|
| 19 | # You should have received a copy of the GNU General Public License |
|---|
| 20 | # along with this program; if not, write to the Free Software |
|---|
| 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|---|
| 22 | |
|---|
| 23 | import random |
|---|
| 24 | |
|---|
| 25 | class Cell(object): |
|---|
| 26 | '''A square cell''' |
|---|
| 27 | |
|---|
| 28 | def __init__(self, x, y): |
|---|
| 29 | self.walls = [1, 1, 1, 1] |
|---|
| 30 | self.x = x |
|---|
| 31 | self.y = y |
|---|
| 32 | |
|---|
| 33 | def has_all_walls_intact(self): |
|---|
| 34 | if 0 in self.walls: |
|---|
| 35 | return False |
|---|
| 36 | else: |
|---|
| 37 | return True |
|---|
| 38 | |
|---|
| 39 | def find_adjacent_wall(self, cell): |
|---|
| 40 | if cell.x == self.x: |
|---|
| 41 | if cell.y < self.y: |
|---|
| 42 | return 0 # left |
|---|
| 43 | else: |
|---|
| 44 | return 2 # right |
|---|
| 45 | else: |
|---|
| 46 | if cell.x < self.x: |
|---|
| 47 | return 1 # above |
|---|
| 48 | else: |
|---|
| 49 | return 3 # below |
|---|
| 50 | |
|---|
| 51 | def destroy_wall(self, cell=None, wall=None): |
|---|
| 52 | if cell is not None: |
|---|
| 53 | wall_to_destroy = self.find_adjacent_wall(cell) |
|---|
| 54 | self.walls[wall_to_destroy] = 0 |
|---|
| 55 | cell.walls[(wall_to_destroy + 2) % 4] = 0 # The opposite wall |
|---|
| 56 | elif wall is not None: |
|---|
| 57 | self.walls[wall] = 0 |
|---|
| 58 | |
|---|
| 59 | def neighbors_coords(self): |
|---|
| 60 | return ((self.x - 1, self.y), # above |
|---|
| 61 | (self.x + 1, self.y), # below |
|---|
| 62 | (self.x, self.y - 1), # left |
|---|
| 63 | (self.x, self.y + 1)) # right |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | class Maze(object): |
|---|
| 67 | |
|---|
| 68 | def __init__(self, width, height): |
|---|
| 69 | self.width = width |
|---|
| 70 | self.height = height |
|---|
| 71 | self.cells = [[None for x in range(self.width)] |
|---|
| 72 | for x in range(self.height)] |
|---|
| 73 | self.total_cells = self.width * self.height |
|---|
| 74 | |
|---|
| 75 | for i in range(self.height): |
|---|
| 76 | for j in range(self.width): |
|---|
| 77 | self.cells[i][j] = Cell(i,j) |
|---|
| 78 | |
|---|
| 79 | self.current_cell = self.cells[0][0] |
|---|
| 80 | self.visited_cells = 1 |
|---|
| 81 | |
|---|
| 82 | def find_neighbors_with_walls(self, cell): |
|---|
| 83 | coords = self.cells[cell.x][cell.y].neighbors_coords() |
|---|
| 84 | all_neighbors = [self.cells[i][j] for i, j in coords |
|---|
| 85 | if self.isinside(i, j)] |
|---|
| 86 | return [neighbors for neighbors in all_neighbors |
|---|
| 87 | if neighbors.has_all_walls_intact()] |
|---|
| 88 | |
|---|
| 89 | def isinside(self, x, y): |
|---|
| 90 | if (0 <= x < self.height) and (0 <= y < self.width): |
|---|
| 91 | return True |
|---|
| 92 | |
|---|
| 93 | def create(self): |
|---|
| 94 | cell_stack = [] |
|---|
| 95 | while self.visited_cells < self.total_cells: |
|---|
| 96 | neighbors = self.find_neighbors_with_walls(self.current_cell) |
|---|
| 97 | if neighbors: |
|---|
| 98 | new_cell = random.choice(neighbors) |
|---|
| 99 | self.current_cell.destroy_wall(cell=new_cell) |
|---|
| 100 | cell_stack.append(self.current_cell) |
|---|
| 101 | self.current_cell = new_cell |
|---|
| 102 | self.visited_cells += 1 |
|---|
| 103 | else: |
|---|
| 104 | self.current_cell = cell_stack.pop() |
|---|
| 105 | |
|---|
| 106 | def draw_ascii(self): |
|---|
| 107 | canvas = [] |
|---|
| 108 | for x in range(self.height): |
|---|
| 109 | line = [] |
|---|
| 110 | line2 = [] |
|---|
| 111 | for y in range(self.width): |
|---|
| 112 | cell = self.cells[x][y] |
|---|
| 113 | line.append(' -'[cell.walls[1]] * 2) |
|---|
| 114 | line2.append(' |'[cell.walls[0]] + ' ') |
|---|
| 115 | canvas.append('+' + '+'.join(line) + '+\n') |
|---|
| 116 | canvas.append(''.join(line2) + '|\n') |
|---|
| 117 | canvas.append('+--' * self.width + '+\n') |
|---|
| 118 | return ''.join(canvas) |
|---|
| 119 | |
|---|
| 120 | if __name__ == '__main__': |
|---|
| 121 | x = Maze(10,6) |
|---|
| 122 | x.create() |
|---|
| 123 | print x.draw_ascii() |
|---|