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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
|
# -*- coding: utf-8 -*-
maze = [[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,],
[-1, 0, 0, 0,-1, 0, 0, 0,-1,-1, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0,-1,],
[ 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,],
[-1, 0,-1, 0, 0, 0, 0,-1,-1, 0, 0,-1,-1,-1,-1, 0,-1,-1,-1, 0,-1,-1,],
[-1, 0,-1, 0, 0, 0,-1, 0,-1, 0,-1,-1, 0, 0, 0, 0,-1,-1,-1, 0, 0,-1,],
[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0,-1,-1, 0, 0,-1, 0,-1,],
[-1,-1,-1,-1,-1, 0,-1, 0,-1, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0,-1, 0,-1,],
[-1,-1,-1,-1,-1, 0,-1,-1,-1, 0, 0,-1, 0,-1, 0, 0,-1,-1, 0, 0, 0,-1,],
[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0,-1, 0, 9,-1, 0,-1, 0, 0,-1,],
[-1,-1,-1,-1,-1, 0,-1, 0, 0,-1, 0,-1, 0,-1, 0, 0, 0, 0, 0,-1, 0,-1,],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,]]
"""The above maze is simple. We have obstacles = -1, 0 is an unexplored path, and 9 is the starting point. If we hit the path we want to take, we can give it a value of 3.
We will start at *9* and move until we are outside of the bounds of the maze. Getting the bounds should be easy enough, just use the shape function built into numpy arrays. So if our position $x == 0$ or $y==0$, or $x = x_{bound}$ or $y = y_{bound}$. Lots of options.
"""
import numpy as np
import matplotlib.pyplot as plt
import time
from IPython.display import display, clear_output
mmaze = np.array(maze)
y_b, x_b = mmaze.shape
print(mmaze)
plt.pcolor(mmaze[-1::-1,:])
plt.axis('equal')
plt.axis('off')
plt.show()
y, x = np.where(mmaze == 9)
#Get the starting position
fig = plt.figure()
def move(maze, y, x):
global fig
# run into an obstacle, return false
if maze[y, x] == -1 :
return False
# run into a square explored, on the path, or dead end
if maze[y, x] == 1 or maze[y, x] == 2 or maze[y, x] == 3:
return False
# Success, an outside edge not occupied by an obstacle
if x==0 or y == 0 or x >= maze.shape[1] or y >= maze.shape[0]:
try:
maze[y,x] = 3
except:
print("YOU'RE OUTSIDE!")
return True
maze[y,x] = 1
# Otherwise, use logical short circuiting to try each
# direction in turn (if needed)
found = move(maze, y, x+1) or \
move(maze, y-1, x) or \
move(maze, y, x-1) or \
move(maze, y+1, x)
plt.pcolor(maze[-1::-1,:])
plt.axis('equal')
plt.axis('off')
display(fig)
clear_output(wait = True)
plt.pause(1e-1)
if found:
maze[y,x] = 3 # This is the path we wanna follow
else:
maze[y,x] = 2 #dead end
return found
"""
Lets run our code on the maze code above and see what happens!
"""
mmaze = np.array(maze)
y, x = np.where(mmaze == 9)
move(mmaze,y,x)
"""The cells in the map above have *Moore* connectivity. This means from your postion you can travel *north, south, east, or west*.
A *Neumann* neighborhood adds in the four additional directions *north-east, south-east, south-west, and north-west*. How can you modify the above function to incorporate the *Neumann* neighborhood?
**Task 1 (30 points)**
"""
def move_neu(maze,y,x):
global fig
# run into an obstacle, return false
if maze[y, x] == -1 :
return False
# run into a square explored, on the path, or dead end
if maze[y, x] == 1 or maze[y, x] == 2 or maze[y, x] == 3:
return False
# Success, an outside edge not occupied by an obstacle
if x==0 or y == 0 or x >= maze.shape[1] or y >= maze.shape[0]:
try:
maze[y,x] = 3
except:
print("YOU'RE OUTSIDE!")
return True
maze[y,x] = 1
# Otherwise, use logical short circuiting to try each
# direction in turn (if needed)
found = move_neu(maze, y+1, x+1) or \
move_neu(maze, y, x+1) or \
move_neu(maze, y-1, x+1) or \
move_neu(maze, y-1, x) or \
move_neu(maze, y-1, x-1) or \
move_neu(maze, y, x-1) or \
move_neu(maze, y+1, x-1) or\
move_neu(maze, y+1, x)
plt.pcolor(maze[-1::-1,:])
plt.axis('equal')
plt.axis('off')
display(fig)
clear_output(wait = True)
plt.pause(1e-1)
if found:
maze[y,x] = 3 # This is the path we wanna follow
else:
maze[y,x] = 2 #dead end
return found
mmaze = np.array((maze))
y, x = np.where(mmaze == 9)
print(f"Starting move_neu with x={x} and y={y}")
move_neu(mmaze,y,x)
"""** Task 2 (20 Points) **
Make another maze. Programmatically or by modifying the above maze.
Alter the order of moves in the move function and measure the time it takes to solve the maze. Is there an optimal order of operations?
***Hint: use %timeit and kill the plotting ***
"""
# nue maze made by me
maze = [[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,],
[-1, 0, 0, 0,-1, 0, 0, 0,-1,-1, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0,-1,],
[ -1, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1,-1,-1,-1,-1,-1,-1,0,-1,-1,],
[-1, 0,-1, 0, 0, 0, 0,-1,-1, 0, 0,-1,-1,-1,-1, 0,-1,-1,-1, 0,-1,-1,],
[-1, 0,-1, 0, 0, 0,-1, 0,-1, 0,-1,-1, 0, 0, 0, 0,-1,-1,-1, 0, 0,-1,],
[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0,-1,-1, 0, 0,-1, 0,-1,],
[0,0,-1,-1,-1, 0,-1, 0,-1, 0, 0, 0, 0, 0, 0,-1,-1, 0, 0,-1, 0,-1,],
[-1,-1,-1,-1,-1, 0,-1,-1,-1, 0, 0,-1, 0,-1, 0, 0,-1,-1, 0, 0, 0,-1,],
[-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0,-1, 0, 9,-1, 0,-1, 0, 0,-1,],
[-1,-1,-1,-1,-1, 0,-1, 0, 0,-1, 0,-1, 0,-1, 0, 0, 0, 0, 0,-1, 0,-1,],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,]]
from time import time
def move_rdlu(maze, x, y, order):
# run into an obstacle, return false
if maze[y, x] == -1 :
return False
# run into a square explored, on the path, or dead end
if maze[y, x] == 1 or maze[y, x] == 2 or maze[y, x] == 3:
return False
# Success, an outside edge not occupied by an obstacle
if x==0 or y == 0 or x >= maze.shape[1] or y >= maze.shape[0]:
try:
maze[y,x] = 3
except:
print("YOU'RE OUTSIDE!")
return True
maze[y,x] = 1
# Otherwise, use logical short circuiting to try each
# direction in turn (if needed)
found = move_rdlu(maze, x + order[0][0], y + order[0][1], order) or \
move_rdlu(maze, x + order[1][0], y + order[1][1], order) or \
move_rdlu(maze, x + order[2][0], y + order[2][1], order) or \
move_rdlu(maze, x + order[3][0], y + order[3][1], order)
if found:
maze[y,x] = 3 # This is the path we wanna follow
else:
maze[y,x] = 2 #dead end
return found
# This is all 24 possible combinations (taken from google)
moves = ["rlud", "rldu", "rdlu", "drlu", "ruld", "rudl", "rdul", "drul",
"urld", "urdl", "udrl", "durl", "lrud", "lrdu", "ldru", "dlru",
"lurd", "ludr", "ldur", "dlur", "ulrd", "uldr", "udlr", "dulr"]
# These are the numbers that have to be added based on their direction
directions = {
"r": (1, 0),
"l": (-1, 0),
"u": (0, 1),
"d": (0, -1)
}
# using a dictionary for timing so I can easily do multiple tests
times = dict((move,0) for move in moves)
# for each move calculate a list of addition tuples in order and then run
n = 30
for i in range(n):
for move in moves:
mmaze = np.array((maze))
y, x = np.where(mmaze == 9)
order = []
for l in move:
order.append(directions[l])
t0 = time()
move_rdlu(mmaze,x,y, order)
t1 = time()-t0
times[move] += t1
for k in times:
times[k] /= n
times = list(times.values())
print(times)
fig = plt.figure()
plt.bar(moves, times, figure=fig) # purdy graf
plt.show()
"""** Task 3 (50 Points) **
Plagarized from a class on Haskel at Stanford [http://nifty.stanford.edu/2018/stephenson-mondrian-art/Handout_Fall.pdf] We are not doing the same problem, but something similar.
Piet Mondrian (March 7, 1872 – February 1, 1944) was a Dutch painter who created numerous famous
paintings in the early half of the previous century that consisted of a white background, prominent black
horizontal and vertical lines, and regions colored with red, yellow and blue. Three examples are shown
Below is included some boiler plate code to setup a plot employing patches of rectangles onto a 1000 x 800 pixel image.
You need to write a code which will recursively divide the *Canvas* and *paint* each portion of the *canvas*.
It is your choice when to stop dividing the domain and how to paint each of the sections.
You do not need to limit your color pallette or how far down you recurse (though sub pixel division will not produce a change).
In order to get full marks you need to define a series of functions which will recursively produce some **art**.
Thorough comments throughout your code will have a large effect on your final grade. Clearly mark out your base case for recursion. Define any design choices explicitly (i.e. if a sub-divided region is to narrow it merges with a neighbor). If you use a **magic-number**, state why. If you are implementing a random number, why? If using RNG, can you seed it and consistently recreate your art?
"""
import random
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Circle
from IPython.display import display, clear_output
plt.rcParams["figure.figsize"] = (10,10)
fig = plt.figure()
domain = (1000,800)
plt.xlim(0,domain[0])
plt.ylim(0,domain[1])
plt.axis('off')
ax = fig.gca()
rects = [Rectangle((50, 100), 400, 320, linewidth=1, edgecolor='r', facecolor='r'),
Rectangle((125, 500), 100, 200, linewidth=1, edgecolor='k', facecolor='g'),
Rectangle((625, 10), 290, 50, linewidth=11, edgecolor='k', facecolor='b'),
Rectangle((725, 600), 75, 150, linewidth=5, edgecolor='g', facecolor='m')]
[ax.add_patch(rect) for rect in rects]
plt.show()
def rectangles(x0, x1, y0, y1):
""" This Generates nices squares with circles that make what lookes like
snowy clouds. I like it"""
# base case because pixels get too tiny
if (x1-x0)*(y1-y0) < 16*16:
return False
else:
# 2 is a nice number and doesn't leave blanks
xmid = (x1+x0) // 2
ymid = (y1+y0) // 2
width = (x1-x0) // 2
height = (y1-y0) // 2
rects = [Rectangle((x0, y0), width, height, linewidth=1, edgecolor='k', facecolor='r'),
Rectangle((xmid, ymid), width, height, linewidth=1, edgecolor='k', facecolor='g'),
Rectangle((x0, ymid), width, height, linewidth=1, edgecolor='k', facecolor='b'),
Rectangle((xmid, y0), width, height, linewidth=1, edgecolor='k', facecolor='m'),
Circle((xmid, y1), width//2, linewidth=1, edgecolor='k', facecolor="w") # circle needs to be smaller so you can see squares
]
[ax.add_patch(rect) for rect in rects]
show = False # Set this equal to True if you want to see it generate step by step
if show:
display(fig)
clear_output(wait = True)
plt.pause(1e-15)
rectangles(x0, xmid, y0, ymid)
rectangles(xmid, x1, ymid, y1)
rectangles(x0, xmid, ymid, y1)
rectangles(xmid, x1, y0, ymid)
plt.rcParams["figure.figsize"] = (10,10)
fig = plt.figure()
domain = (1024, 1024)
plt.xlim(0,domain[0])
plt.ylim(0,domain[1])
plt.axis('off')
ax = fig.gca()
print("STARTING")
rectangles(0, domain[0], 0, domain[1])
plt.savefig('SnowyCloudsInAPlaidSky.png')
plt.show()
|