-
Notifications
You must be signed in to change notification settings - Fork 1
/
maze.py
118 lines (115 loc) · 4.87 KB
/
maze.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# Copyright 2018 Jason Sewall (jasonsewall@gmail.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from numpy import ndarray
from collections import defaultdict
from random import randint
class rect_maze:
"""Represents a rectilinear maze. We use the labels 'n' 'e' 's' 'w' to identify cardinal directions."""
def __init__(self, n, m):
"""Make a 2d n x m 'maze' with all walls in place"""
self.n = n
self.m = m
self.wall_x = ndarray((self.n+1, self.m), dtype=int)
self.wall_y = ndarray((self.n, self.m+1), dtype=int)
self.wall_x[:,:] = 1
self.wall_y[:,:] = 1
self.start = None
self.ends = []
def dfs_maze(self, start_i, start_j):
"""Starting at start_i, start_j, remove walls to create a maze"""
self.start = [start_i, start_j]
visited = ndarray((self.n, self.m), dtype=int)
visited[:,:] = 0
visited[start_i, start_j] = 1
visited_count = 1
current_i, current_j = start_i, start_j
trail = []
trail.append((current_i, current_j))
while visited_count < self.n*self.m:
cand_neighbors = list(filter(lambda x: x[0] >= 0 and x[1] >= 0 and x[0] < self.n and x[1] < self.m and visited[x[0],x[1]] == 0,
[(current_i+1, current_j), (current_i-1, current_j), (current_i, current_j+1), (current_i, current_j-1)]))
if len(cand_neighbors) > 0:
pick = cand_neighbors[randint(0, len(cand_neighbors)-1)]
diff = (pick[0]-current_i, pick[1]-current_j)
assert abs(diff[0]) + abs(diff[1]) == 1
if diff[1] == 1: # south
self.wall_y[current_i, current_j+1] = 0
elif diff[1] == -1: # north
self.wall_y[current_i, current_j] = 0
elif diff[0] == 1: # east
self.wall_x[current_i+1, current_j] = 0
elif diff[0] == -1: # west
self.wall_x[current_i, current_j] = 0
trail.append(pick)
visited[pick[0],pick[1]] = 1
visited_count += 1
(current_i, current_j) = pick
else:
if len(trail) > 0:
(current_i, current_j) = trail.pop()
def wall_n(self, pos):
"""Returns exisitence of wall north of cell pos"""
return self.wall_y[pos[0],pos[1]]
def wall_s(self, pos):
"""Returns exisitence of wall south of cell pos"""
return self.wall_y[pos[0],pos[1]+1]
def wall_e(self, pos):
"""Returns exisitence of wall east of cell pos"""
return self.wall_x[pos[0]+1,pos[1]]
def wall_w(self, pos):
"""Returns exisitence of wall west of cell pos"""
return self.wall_x[pos[0],pos[1]]
def get_lines(self):
"""Produce vertices & indices to pass to batched rendering for OpenGL"""
verts = []
idx = 0
lines = []
for m in range(self.m+1):
for n in range(self.n+1):
verts += [n-0.5,m-0.5]
if n > 0:
if self.wall_y[n-1,m]:
lines += [idx-1,idx]
if m > 0:
if self.wall_x[n,m-1]:
lines += [idx-self.n-1,idx]
idx += 1
return (verts,lines)
class maze_walker:
"""A basic path-tracking class for traversing a maze"""
def __init__(self, maze):
"""Set up reference to maze to be solved"""
self.maze = maze
self.pos = maze.start
self.path = [self.pos]
def solved(self):
"""Return true if we have reached one of the ends"""
return self.pos in self.maze.ends
def look(self, dr):
"""Check to see if there is a wall in this direction (one of 'n', 'e', 's', 'w')"""
if dr in 'wnse':
return getattr(self.maze, "wall_"+dr)(self.pos)
def move(self, dr):
"""Try to move in specified direction, respecting walls."""
if not self.look(dr):
if dr == 'w':
self.pos = [self.pos[0]-1, self.pos[1]]
if dr == 'e':
self.pos = [self.pos[0]+1, self.pos[1]]
if dr == 'n':
self.pos = [self.pos[0], self.pos[1]-1]
if dr == 's':
self.pos = [self.pos[0], self.pos[1]+1]
self.path.append(self.pos)
return self.pos