-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLazor_project.py
365 lines (311 loc) · 11.7 KB
/
Lazor_project.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
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
from itertools import combinations
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
class Reader:
# This class is used to read the .bff profile. It extracts useful
# information such as the position of o, x, B, the number of flexible
# A, B, C blocks, lazer position & directions and intersections.
# I designed a dictionay to store all information neceesary for
# algorithm.
# **Parameters**
# self : *str*
# filename
# **return**
# a dictionary {} to store useful information
def __init__(self, filename):
self.filename = filename
def reader(self):
# This function opens the bff file, read it and store the informatin.
# Use list to store all the positions
self.filename
grid_start = False
y = 0
stack = {"num_A_block": 0, "num_B_block": 0, "num_C_block": 0}
o_list = []
x_list = []
A_list = []
B_list = []
C_list = []
Lazor = []
P_list = []
with open(self.filename, 'r') as file:
# Read the information line by line, delete the space
# between the words
# The length of line is coordination x and the width
# of number of lines from GRID START to GRID STOP
for line in file.readlines():
line = line.strip()
cells = line.split()
if line == "GRID START":
grid_start = True
elif line == "GRID STOP":
grid_start = False
# Bellow GRID START append each information in list. Here, using
# enumerate to label the index. That is, our x
# Reference: https://www.geeksforgeeks.org/enumerate-in-python/
elif grid_start:
# put index for each element
for x, cell in enumerate(cells):
if cell == 'o':
o_list.append([x, y])
if cell == 'x':
x_list.append([x, y])
if cell == 'A':
A_list.append([x, y])
if cell == 'B':
B_list.append([x, y])
if cell == 'C':
C_list.append([x, y])
y += 1
# We have transfer our coordination number because our coordination
# system for plotting starts from left bottom.
else:
if line != "":
info = line.split()
if info[0] == "A":
stack.update({"num_A_block": int(info[1])})
if info[0] == "B":
stack.update({"num_B_block": int(info[1])})
if info[0] == "C":
stack.update({"num_C_block": int(info[1])})
if info[0] == "L":
Lazor.append(
[int(info[1]) / 2, (int(y) * 2 -
int(info[2])) / 2, int(info[3]) / 2, -
int(info[4]) / 2])
if info[0] == "P":
P_list.append(
[int(info[1]) / 2, (int(y) * 2 -
int(info[2])) / 2])
# Transfer coordination number our plotting coordination system
for i in o_list:
i[1] = y - i[1] - 1
for i in A_list:
i[1] = y - i[1] - 1
for i in B_list:
i[1] = y - i[1] - 1
for i in C_list:
i[1] = y - i[1] - 1
for i in x_list:
i[1] = y - i[1] - 1
# Store data in the dictionary and pass them to other function
# Reference: https://www.programiz.com/python-programming/
# methods/dictionary/update
stack.update({
"Grid_size": [x + 1, y],
"o_list": o_list,
"x_list": x_list,
"A_list": A_list,
"B_list": B_list,
"C_list": C_list,
"Lazor": Lazor,
"P_list": P_list
})
return stack
# Show the possible arrangement
def place_test(o_list, num_A_block, num_B_block, num_C_block, grid_size,
l_list, p_list):
# This function calculates all the possible arrangements. Listed
# all the possibilites and use test_block function to find the
# answer. If test_function is TRUE, it will return the answer
# **Parameters**
# o_list : *list*
# filename
# num_A_block : *int*
# number of A block
# num_B_block : *int*
# number of B block
# num_C_block : *int*
# number of C block
# **return**
# block_info *list of list*
# This shows the positions of A, B, C blocks' placement.
# e.g. [[A, 3, 4][B, 2, 5][C, 2, 3]...]
def combined_block_info(block_position, block_type):
for i in block_position:
block_info.extend([[block_type, i[0], i[1]]])
return block_info
# This function is used to modify the data pass to test_block.
# (Formatting the result after calculation)
# **Parameters**
# block_position: *list of list*
# A, B, C coodination numbers
# block_type: *int*
# A, B, C
# **return**
# block_info *list of list*
# e.g. [[A, 3, 4][B, 2, 5][C, 2, 3]...]
# '''
a_block_combination = list(combinations(o_list, num_A_block))
# Use combinations method in itertools. This can list all the
# possiblity picking number of A blocks in o_list without
# considering the sequence. Repeat this loop for A, B, C blocks.
# Important:
# (1) Remove the elements in each list after each possibilty!
# Do not want collections
# (2) Add back the remove compenets to o_list for next loop
# Reference: https://www.geeksforgeeks.org/itertools-combinations
# -module-python-print-possible-combinations/
for i in a_block_combination:
A_list = []
A = list(i)
A_list.extend(A)
for j in A:
o_list.remove(j)
b_block_combination = list(combinations(o_list, num_B_block))
for i in b_block_combination:
B_list = []
B = list(i)
B_list.extend(B)
for j in B:
o_list.remove(j)
c_block_combination = list(combinations(o_list, num_C_block))
for i in c_block_combination:
C_list = []
C = list(i)
C_list.extend(C)
block_info = []
combined_block_info(A_list, 'A')
combined_block_info(B_list, 'B')
combined_block_info(C_list, 'C')
if test_block(grid_size, block_info, l_list, p_list):
return block_info
# Here, Delete all the number of elements added to A, B, C list
# Clean the lsit. Meanwhile add the deleted elements black to
# o_list
if num_C_block > 0:
del C_list[-num_C_block:]
elif num_C_block == 0:
pass
if num_B_block > 0:
del B_list[-num_B_block:]
elif num_B_block == 0:
pass
o_list.extend(B)
if num_A_block > 0:
del A_list[-num_A_block:]
elif num_A_block == 0:
pass
o_list.extend(A)
# check if the lazor hit the block
def hit_block(l_info):
x, y, vx, vy = l_info
if (x * 2) % 2 == 0:
block_y = y - 0.5
block_x = x
if vx < 0:
block_x -= 1
else:
block_x = x - 0.5
block_y = y
if vy < 0:
block_y -= 1
return (block_x, block_y)
# check the direction of the lazor
# whether it touch the sides of the block or the top-bottoms
def reflect_info(info):
x, y, vx, vy = info
if (x * 2) % 2 == 0: # x is int
vx *= -1
else:
vy *= -1
return (x + vx, y + vy, vx, vy)
# test if the arrangement of the block can be satisfied hte requirement
def test_block(grid_size, block_info, l_list, p_list):
target_set = set([(x[0], x[1]) for x in p_list])
path_check = set()
block_dict = {}
for b in block_info:
block_dict[(b[1], b[2])] = b[0]
def hit(l_info):
x, y, vx, vy = l_info
if l_info in path_check:
return
# check no repeat path
path_check.add(l_info)
target_set.discard((x, y))
b_pos = hit_block(l_info)
# check if the lazor is in the grid
if not (0 <= b_pos[0] < grid_size[0] and 0 <= b_pos[1] < grid_size[1]):
return
# if lazor does not hit any block, keep moving in the same direction
if b_pos not in block_dict:
hit((x + vx, y + vy, vx, vy))
# if lazor hit the block, do the action accordingly
else:
block_type = block_dict[b_pos]
if block_type == 'A':
hit(reflect_info(l_info))
elif block_type == 'B':
return
elif block_type == 'C':
hit(reflect_info(l_info))
hit((x + vx, y + vy, vx, vy))
# do all the lazor
for l in l_list:
hit((l[0], l[1], l[2], l[3]))
# if the targets are all passed
return len(target_set) == 0
def output_image(filename, grid_size, x_list, B_list, block_info):
# adustment of the style of the output images
plt.xlim(0, grid_size[0])
plt.ylim(0, grid_size[1])
plt.axes().set_aspect('equal')
plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))
plt.gca().yaxis.set_major_locator(plt.MultipleLocator(1))
plt.grid(True)
# draw different type of block with different color
for i in block_info:
block_type = i[0]
x = i[1]
y = i[2]
if block_type == "A":
rect = plt.Rectangle((x, y), 1, 1, fc='lightgray', ec="black")
plt.gca().add_patch(rect)
elif block_type == "B":
rect = plt.Rectangle((x, y), 1, 1, fc='dimgray', ec="black")
plt.gca().add_patch(rect)
elif block_type == "C":
rect = plt.Rectangle((x, y), 1, 1, fc='lightblue', ec="black")
plt.gca().add_patch(rect)
# draw the poisiton which cannot be placed with black
for i in x_list:
x = i[0]
y = i[1]
rect = plt.Rectangle((x, y), 1, 1, fc='black', ec="black")
plt.gca().add_patch(rect)
# draw the fixed opague block
for i in B_list:
x = i[0]
y = i[1]
rect = plt.Rectangle((x, y), 1, 1, fc='dimgray', ec="black")
plt.gca().add_patch(rect)
# make the legend
reflect_block = mpatches.Patch(color='lightgray', label='Reflect block')
opague_block = mpatches.Patch(color='dimgray', label='Opague block')
refractive_block = mpatches.Patch(
color='lightblue', label='Refractive block')
X = mpatches.Patch(color='black', label='Cannot be placed')
plt.legend(handles=[reflect_block, opague_block, refractive_block, X],
bbox_to_anchor=(0, 1.02, 1, 0.2), loc="lower left",
mode="expand", borderaxespad=0, ncol=2)
fptr = '.'.join(filename.split(".")[0:-1])
plt.savefig(fptr + '.png')
plt.show()
if __name__ == "__main__":
filename = "yarn_5.bff"
read = Reader(filename)
graph_info = read.reader()
o_list = graph_info['o_list']
num_A_block = graph_info['num_A_block']
num_B_block = graph_info['num_B_block']
num_C_block = graph_info['num_C_block']
B_list = graph_info['B_list']
grid_size = graph_info['Grid_size']
l_list = graph_info['Lazor']
p_list = graph_info['P_list']
x_list = graph_info['x_list']
block_info = place_test(
o_list, num_A_block, num_B_block, num_C_block, grid_size,
l_list, p_list)
output_image(filename, grid_size, x_list, B_list, block_info)