-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtesting.py
292 lines (231 loc) · 9.13 KB
/
testing.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri May 17 05:59:08 2019
@author: kazzastic
"""
from mazelib import *
import numpy as np
import cv2
from random import randrange
from random import choice, shuffle
import cython
if not cython.compiled:
from mazelib.solve.MazeSolveAlgo import MazeSolveAlgo
class Maze(object):
def __init__(self):
self.generator = None
self.grid = arr
self.start = None
self.end = None
self.solver = WallFollower()
self.solutions = None
def generate_entrances(self, start_outer=True, end_outer=True):
""" Generate maze entrances.
Entrances can be on the walls, or inside the maze.
"""
if start_outer and end_outer:
self._generate_outer_entrances()
elif not start_outer and not end_outer:
self._generate_inner_entrances()
elif start_outer:
self.start, self.end = self._generate_opposite_entrances()
else:
self.end, self.start = self._generate_opposite_entrances()
# the start and end shouldn't be right next to each other
if abs(self.start[0] - self.end[0]) + abs(self.start[1] - self.end[1]) < 2:
self.generate_entrances(start_outer, end_outer)
def _generate_outer_entrances(self):
""" Generate maze entrances, along the outer walls. """
H = self.grid.shape[0]
W = self.grid.shape[1]
start_side = randrange(4)
# maze entrances will be on opposite sides of the maze.
if start_side == 0:
self.start = (0, randrange(1, W, 2)) # North
self.end = (H - 1, randrange(1, W, 2))
elif start_side == 1:
self.start = (H - 1, randrange(1, W, 2)) # South
self.end = (0, randrange(1, W, 2))
elif start_side == 2:
self.start = (randrange(1, H, 2), 0) # West
self.end = (randrange(1, H, 2), W - 1)
else:
self.start = (randrange(1, H, 2), W - 1) # East
self.end = (randrange(1, H, 2), 0)
def _generate_inner_entrances(self):
""" Generate maze entrances, randomly within the maze. """
H, W = self.grid.shape
self.start = (randrange(1, H, 2), randrange(1, W, 2))
end = (randrange(1, H, 2), randrange(1, W, 2))
# make certain the start and end points aren't the same
while end == self.start:
end = (randrange(1, H, 2), randrange(1, W, 2))
self.end = end
def solve(self):
""" public method to solve a new maze, if possible """
#if self.generator is None:
#raise UnboundLocalError('No maze-solving algorithm has been set.')
if self.start is None or self.end is None:
raise UnboundLocalError('Start and end times must be set first.')
else:
self.solutions = self.solver.solve(self.grid, self.start, self.end)
def tostring(self, entrances=False, solutions=False):
""" Return a string representation of the maze. """
if self.grid is None:
return ''
# build the walls of the grid
txt = []
for row in self.grid:
txt.append(''.join(['#' if cell else ' ' for cell in row]))
# insert the start and end points
if entrances and self.start and self.end:
r, c = self.start
txt[r] = txt[r][:c] + 'S' + txt[r][c+1:]
r, c = self.end
txt[r] = txt[r][:c] + 'E' + txt[r][c+1:]
# if extant, insert the solution path
if solutions and self.solutions:
for r, c in self.solutions[0]:
txt[r] = txt[r][:c] + '+' + txt[r][c+1:]
return '\n'.join(txt)
def __str__(self):
""" display maze walls, entrances, and solutions, if available """
return self.tostring(True, True)
def __repr__(self):
return self.__str__()
class WallFollower(MazeSolveAlgo):
"""
The Algorithm
Follow the right wall and you will eventually end up at the end.
details:
1. Choose a random starting direction.
2. At each intersection, take the rightmost turn. At dead-ends, turn around.
3. If you have gone more than (H * W) + 2 cells, stop; the maze will not be solved.
4. Terminate when you reach the end cell.
5. Prune the extraneous branches from the solution before returning it.
Optional Parameters
Turn: String ['left', 'right']
Do you want to follow the right wall or the left wall? (default 'right')
"""
def __init__(self, turn='right', prune=True):
# turn can take on values 'left' or 'right'
if turn == 'left':
self.directions = [(-2, 0), (0, -2), (2, 0), (0, 2)]
else: # default to right turns
self.directions = [(-2, 0), (0, 2), (2, 0), (0, -2)]
self.prune = prune
def _solve(self):
solution = []
current = self.start
# a first move has to be made
if self._on_edge(self.start):
current = self._push_edge(self.start)
solution.append(current)
# pick a random direction and move
last = current
current = choice(self._find_neighbors(last, False))
last_diff = (current[0] - last[0], current[1] - last[1])
last_dir = self.directions.index(last_diff)
# add first move to solution
solution.append(self._midpoint(last, current))
solution.append(current)
# now that you have set up the situation, follow the walls
solution = self._follow_walls(last_dir, current, solution)
if self.prune:
solution = self._prune_solution(solution)
solution = self._fix_entrances(solution)
return [solution]
def _follow_walls(self, last_dir, current, solution):
"""Perform the wall following logic.
Loop until you have found the end,
or prove you won't solve the maze.
"""
limit = self.grid.shape[0] * self.grid.shape[1] + 2
while len(solution) < limit:
last_dir, temp = self._follow_one_step(last_dir, current)
# the solution should not include the end point
if temp == self.end:
midpoint = self._midpoint(temp, current)
if midpoint != self.end:
solution.append(midpoint)
break
solution.append(self._midpoint(temp, current))
solution.append(temp)
current = temp
if len(solution) >= limit:
raise RuntimeError('This algorithm was not able to converge on a solution.')
return solution
def _follow_one_step(self, last_dir, current):
"""At each new cell you reach, take the rightmost turn.
Turn around if you reach a dead end.
if right is not available, then straight, if not straight, left, etc...
"""
for d in range(4):
next_dir = (last_dir - 1 + d) % 4
next_cell = self._move(current, self.directions[next_dir])
r, c= self._midpoint(next_cell, current)
if self.grid[r, c] == 0 and (r, c) != self.start:
return (next_dir, next_cell)
elif (r, c) == self.end:
return (next_dir, self.end)
return (last_dir, current)
def _fix_entrances(self, solution):
"""Ensure the start and end are appropriately placed in the solution."""
# prune if start is found in solution
if self.start in solution:
i = solution.index(self.start)
solution = solution[i+1:]
# prune if first position is repeated
if solution[0] in solution[1:]:
i = solution[1:].index(solution[0])
solution = solution[i+1:]
# prune duplicate end points
if len(solution) > 1 and solution[-2] == solution[-1]:
solution = solution[:-1]
return solution
file = 'mazegreen.jpg'
img = cv2.imread(file)
cv2.imshow('The Initial', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray', gray_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
blurred = cv2.GaussianBlur(gray_img, (5,5), 0)
cv2.imshow('Blur', blurred)
cv2.waitKey(0)
cv2.destroyAllWindows()
(t, thresh) = cv2.threshold(blurred, 155, 255, cv2.THRESH_BINARY)
cv2.imshow('Threshold Binary', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
arr = np.array(thresh)
for i in range(0, 293):
for j in range(0, 257):
if(arr[i][j] == 255):
arr[i][j] = 1
canny = cv2.Canny(blurred, 30, 150)
cv2.imshow('Canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
cnts = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
new = img.copy()
cv2.drawContours(new, cnts, -1, (0,255,0),2)
cv2.imshow('Cont', new)
cv2.waitKey(0)
cv2.destroyAllWindows()
for i in range(0, 293):
for j in range(0, 257):
if(arr[i][j] == 1):
arr[i][j] = 255
cv2.imshow('Result', arr)
cv2.waitKey(0)
cv2.destroyAllWindows()
'''
my_maze = Maze()
#my_maze.solver = WallFollower()
my_maze.generate_entrances(start_outer=True, end_outer=True)
my_maze.solve()
'''