-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp - phase 2.py
565 lines (451 loc) · 20.7 KB
/
app - phase 2.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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# David Chen and Brian Yeh - CS440 Spring 2017
from random import choice, randint, random
from win32api import GetSystemMetrics
import pygame
from datetime import datetime
from utils.colors import *
from utils.helper_defs import *
from utils.helper_defs import round_down_to_multiple as RDTM, euclidean_dist as E_dist, manhattan_dist as M_dist, \
change_direction as CD, chebyshev_dist as C_dist, our_inadmissible_dist as In_dist, \
our_admissible_dist as A_dist
from utils.a_star import *
SYSTEM_WIDTH = GetSystemMetrics(0)
SYSTEM_HEIGHT = GetSystemMetrics(1)
# print(SYSTEM_WIDTH, SYSTEM_HEIGHT)
# maps need to be 160 columns by 120 rows so the aspect ratio is 4:3 and thus landscape mode, wide > tall
GRID_BLOCKS_TALL = 120
GRID_BLOCKS_WIDE = 160
# assume screen to be landscape, so map height = map width = screen height, which is the lower pixel count
MAP_HEIGHT = SYSTEM_HEIGHT
SCALE = int(SYSTEM_HEIGHT / GRID_BLOCKS_TALL)
MAP_WIDTH = SCALE * GRID_BLOCKS_WIDE
INTERFACE_HEIGHT = MAP_HEIGHT
INTERFACE_WIDTH = SYSTEM_WIDTH - MAP_WIDTH
INTERFACE_PADDING = SCALE
# print(MAP_WIDTH,MAP_HEIGHT,INTERFACE_HEIGHT,INTERFACE_WIDTH,INTERFACE_PADDING)
NUMBER_OF_HTT_AREAS = 8
NUMBER_OF_RIVERS = 4
NUMBER_OF_BLOCKED_CELLS = int(GRID_BLOCKS_WIDE*GRID_BLOCKS_TALL*.2)
DEFAULT_BORDER_COLOR = LIGHT_GRAY
FONT_SIZE = 15
FONT = None
INTERNAL_MAP = [] # internal map (list of lists)
EXTERNAL_MAP_BASE = None # map visual with no goals
EXTERNAL_MAP_FULL = None # map visual with goals and all
START_NODE = None
GOAL_NODE = None
MAIN_SCREEN = None
GRAPH = None
H = A_dist
WEIGHTS = [1.25, 1.25, 1.25]
W_SELECTION = ['<<', '', '']
W_INDEX = 0
HTT_COORDS = None
H_DEFS = {
0: A_dist,
1: M_dist,
2: E_dist,
3: C_dist,
4: In_dist
}
# cell types
# blocked = 0
# regular unblocked = 1
# hard to traverse = 2
class Cell(object):
def __init__(self, pixel_x, pixel_y, row, col, c_type):
self.pixel_x = int(pixel_x)
self.pixel_y = int(pixel_y)
self.row = int(row)
self.col = int(col)
self.c_type = int(c_type)
self.is_highway = self.is_start = self.is_goal = self.in_fringe = self.expanded_already = False
self.parent = None
self.neighbors = []
self.g_value = self.h_value = None
if self.g_value and self.h_value:
self.f_value = self.g_value + self.h_value
else:
self.f_value = None
def get_color(self):
if self.is_start: # start cell
return GREEN
elif self.is_goal: # goal cell
return RED
elif self.is_highway: # is highway and is...
if self.c_type == 1: # normal cell
return CYAN
elif self.c_type == 2: # HTT cell
return DARK_CYAN
elif self.c_type == 0: # blocked cell
return BLACK
elif self.c_type == 1: # normal cell
return WHITE
elif self.c_type == 2: # HTT cell
return BROWN
else: # unknown cell
return YELLOW
def get_type_string(self):
if self.is_start:
return 'START CELL'
elif self.is_goal:
return 'GOAL CELL'
elif self.is_highway: # is highway and is...
if self.c_type == 1: # normal cell
return 'NORMAL HIGHWAY CELL'
elif self.c_type == 2: # HTT cell
return 'HTT HIGHWAY CELL'
elif self.c_type == 0: # blocked cell
return 'BLOCKED CELL'
elif self.c_type == 1: # normal cell
return 'NORMAL CELL'
elif self.c_type == 2: # HTT cell
return 'HTT CELL'
else: # unknown cell
return 'UNKNOWN CELL'
def is_edge_cell(self):
if self.row == 0 or self.row == GRID_BLOCKS_TALL - 1 or self.col == 0 or self.col == GRID_BLOCKS_WIDE - 1:
return True
else:
return False
def load_user_interface():
if H == C_dist:
h_string = "CHEBYSHEV"
elif H == M_dist:
h_string = "MANHATTAN"
elif H == E_dist:
h_string = "EUCLIDEAN"
elif H == In_dist:
h_string = "INADMISSIBLE"
elif H == A_dist:
h_string = "ADMISSIBLE"
else:
h_string = "ERROR"
instructs = []
i_file = open('instructs.cs440', 'r')
for line in i_file:
instructs.append(line)
i_file.close()
i_w = INTERFACE_WIDTH
i_h = INTERFACE_HEIGHT
i_p = INTERFACE_PADDING
pygame.draw.rect(MAIN_SCREEN, DARK_GRAY, (MAP_WIDTH, 0, i_w, i_h), 0) # BORDER
pygame.draw.rect(MAIN_SCREEN, WHITE, (MAP_WIDTH + i_p, i_p, i_w - 2 * i_p, i_h - 2 * i_p), 0) # INNER
padding = 25
for i in instructs:
current_i = i.strip()
if current_i == '[CURRENT H]':
current_i = 'CURRENT HEURISTIC: {}'.format(h_string)
elif current_i == '[CURRENT A*w]':
current_i = 'WEIGHTED A* WEIGHT: {} {}'.format(WEIGHTS[0], W_SELECTION[0])
elif current_i == '[CURRENT w1]':
current_i = 'SEQ/INT\'D WEIGHT_1: {} {}'.format(WEIGHTS[1], W_SELECTION[1])
elif current_i == '[CURRENT w2]':
current_i = 'SEQ/INT\'D WEIGHT_2: {} {}'.format(WEIGHTS[2], W_SELECTION[2])
text_surface = FONT.render('{}'.format(current_i), True, BLACK)
MAIN_SCREEN.blit(text_surface, (MAP_WIDTH + i_p*2, i_p + padding, i_w - 2 * i_p, i_h - 2 * i_p))
padding += 25
pygame.display.update()
return
def main():
global FONT, MAIN_SCREEN, INTERNAL_MAP, H, WEIGHTS, W_INDEX, W_SELECTION
pygame.init()
MAIN_SCREEN = pygame.display.set_mode((SYSTEM_WIDTH, SYSTEM_HEIGHT), pygame.FULLSCREEN)
icon = pygame.image.load('images/robot_icon.png')
pygame.display.set_icon(icon)
pygame.display.set_caption('CS440 Assignment 1')
FONT = pygame.font.SysFont("monospace", FONT_SIZE)
load_user_interface()
internal_grid, map_visual_base = generate_a_map()
start, goal, map_visual_full = draw_get_start_and_goal(internal_grid, map_visual_base)
save_the_map(internal_grid, start, goal, HTT_COORDS)
pygame.display.update()
previous_highlighted_cell = normal_color = highlighted_cell = None
info_ready = False
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
quit()
if e.type == pygame.KEYDOWN:
solution = []
if e.key == pygame.K_c: # clear labels, keep map and start/goal
MAIN_SCREEN.blit(map_visual_full, (0, 0))
load_user_interface()
pygame.display.update()
if e.key == pygame.K_n: # new map
internal_grid, map_visual_base = generate_a_map()
start, goal, map_visual_full = draw_get_start_and_goal(internal_grid, map_visual_base)
pygame.display.update()
if e.key == pygame.K_g: # new start/goal cells
start, goal, map_visual_full = draw_get_start_and_goal(internal_grid, map_visual_base)
pygame.display.update()
if e.key == pygame.K_a: # run a* normal
solution = a_star(start, goal, internal_grid, H)
path_color = GREEN
if e.key == pygame.K_u: # run UCS
solution = uniform_cost_search(start, goal, internal_grid)
path_color = PINK
if e.key == pygame.K_w: # run weighted A*
solution = weighted_a_star(start, goal, internal_grid, H, WEIGHTS[0])
path_color = PURPLE
if e.key == pygame.K_s: # run sequential A*
solution = sequential_a_star(start, goal, internal_grid, H_DEFS, WEIGHTS[1], WEIGHTS[2])
path_color = YELLOW
if e.key == pygame.K_i: # run integrated A*
solution = integrated_a_star(start, goal, internal_grid, H_DEFS, WEIGHTS[1], WEIGHTS[2])
path_color = RED
if e.key == pygame.K_d: # save the map to file
save_the_map(internal_grid, start, goal, HTT_COORDS)
pygame.display.update()
if e.key == pygame.K_1:
H = C_dist
load_user_interface()
if e.key == pygame.K_2:
H = M_dist
load_user_interface()
if e.key == pygame.K_3:
H = E_dist
load_user_interface()
if e.key == pygame.K_4:
H = In_dist
load_user_interface()
if e.key == pygame.K_5:
H = A_dist
load_user_interface()
if e.key == pygame.K_DOWN:
W_SELECTION[W_INDEX] = ''
if W_INDEX == 2:
W_INDEX = 0
else:
W_INDEX += 1
W_SELECTION[W_INDEX] = '<<'
load_user_interface()
if e.key == pygame.K_UP:
W_SELECTION[W_INDEX] = ''
if W_INDEX == 0:
W_INDEX = 2
else:
W_INDEX -= 1
W_SELECTION[W_INDEX] = '<<'
load_user_interface()
if e.key == pygame.K_LEFT:
WEIGHTS[W_INDEX] -= 0.25
load_user_interface()
if e.key == pygame.K_RIGHT:
WEIGHTS[W_INDEX] += 0.25
load_user_interface()
if e.key == pygame.K_ESCAPE:
pygame.quit()
quit()
if solution:
info_ready = True
for cell in solution:
tmp_sq = pygame.Surface((SCALE - 1, SCALE - 1))
tmp_sq.fill(path_color)
# tmp_sq.set_alpha(100)
MAIN_SCREEN.blit(tmp_sq, (cell.pixel_x + 1, cell.pixel_y + 1))
pygame.display.update()
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1: # if left mouse button clicked
m_pos = pygame.mouse.get_pos()
pix_x = m_pos[0]
pix_y = m_pos[1]
if pix_x < MAP_WIDTH and info_ready: # means clicked within the map and not the interface
c_pix_x = RDTM(pix_x, SCALE)
c_pix_y = RDTM(pix_y, SCALE)
row = int(c_pix_y / SCALE)
col = int(c_pix_x / SCALE)
clicked_cell = internal_grid[row][col]
display_info(clicked_cell)
else:
pass
if e.type == pygame.MOUSEMOTION:
m_pos = pygame.mouse.get_pos()
pix_x = m_pos[0]
pix_y = m_pos[1]
if pix_x < MAP_WIDTH: # means hovering within the map and not the interface
c_pix_x = RDTM(pix_x, SCALE)
c_pix_y = RDTM(pix_y, SCALE)
row = int(c_pix_y / SCALE)
col = int(c_pix_x / SCALE)
if highlighted_cell != internal_grid[row][col]:
highlighted_cell = internal_grid[row][col]
if previous_highlighted_cell:
if previous_highlighted_cell is not highlighted_cell:
draw_cell_rect(MAIN_SCREEN, normal_color, previous_highlighted_cell.pixel_x,
previous_highlighted_cell.pixel_y, DEFAULT_BORDER_COLOR, 1)
pygame.display.update()
normal_color = MAIN_SCREEN.get_at((highlighted_cell.pixel_x + 5, highlighted_cell.pixel_y + 5))
draw_cell_rect(MAIN_SCREEN, YELLOW, highlighted_cell.pixel_x,
highlighted_cell.pixel_y, DEFAULT_BORDER_COLOR, 1)
pygame.display.update()
previous_highlighted_cell = internal_grid[row][col]
else:
pass
return
def generate_a_map():
# generates a new map without start and goal nodes
# returns the internal grid representative of this map and the external visual of this base map
map_surface = pygame.Surface((MAP_WIDTH, MAP_HEIGHT), pygame.SRCALPHA, 32)
map_surface = map_surface.convert_alpha()
grid = draw_init_grid(map_surface)
draw_htt_areas_on_grid(map_surface, grid, NUMBER_OF_HTT_AREAS)
draw_rivers_on_grid(map_surface, grid, NUMBER_OF_RIVERS)
draw_blocked_cells_on_grid(map_surface, grid, NUMBER_OF_BLOCKED_CELLS)
return grid, map_surface.copy()
def draw_init_grid(map_surface):
grid_map = []
for vertical_pixel in range(0, MAP_HEIGHT, SCALE):
temp_row = []
for horizontal_pixel in range(0, MAP_WIDTH, SCALE):
temp_cell = Cell(horizontal_pixel, vertical_pixel, vertical_pixel / SCALE, horizontal_pixel / SCALE, 1)
temp_row.append(temp_cell)
draw_cell_rect(map_surface, temp_cell.get_color(), horizontal_pixel, vertical_pixel, DEFAULT_BORDER_COLOR, 1)
grid_map.append(temp_row)
MAIN_SCREEN.blit(map_surface, (0, 0))
pygame.display.update()
return grid_map
def draw_htt_areas_on_grid(map_surface, grid, num_htt_areas):
global HTT_COORDS
random_htt_area_coords = []
while len(random_htt_area_coords) != num_htt_areas:
htt_area_origin_column = randint(0, GRID_BLOCKS_WIDE - 1)
htt_area_origin_row = randint(0, GRID_BLOCKS_TALL - 1)
htt_area_origin_coords = [htt_area_origin_column, htt_area_origin_row]
if htt_area_origin_coords not in random_htt_area_coords:
random_htt_area_coords.append(htt_area_origin_coords)
for coordinates in random_htt_area_coords:
htt_col = coordinates[0]
htt_row = coordinates[1]
for tmp_col in range(htt_col-15, htt_col+16):
for tmp_row in range(htt_row-15, htt_row+16):
if tmp_col < 0 or tmp_col > GRID_BLOCKS_WIDE - 1 or tmp_row < 0 or tmp_row > GRID_BLOCKS_TALL - 1:
continue
else:
if random() < 0.5:
tmp_cell = grid[tmp_row][tmp_col]
tmp_cell.c_type = 2 # set to HTT
draw_cell_rect(map_surface, tmp_cell.get_color(),
tmp_cell.pixel_x, tmp_cell.pixel_y, DEFAULT_BORDER_COLOR, 1)
MAIN_SCREEN.blit(map_surface, (0, 0))
pygame.display.update()
HTT_COORDS = random_htt_area_coords
return
def draw_rivers_on_grid(map_surface, grid, num_rivers):
for _ in range(num_rivers):
valid_river = False
drawing_q = []
while not valid_river:
# if not valid river...
drawing_q = [] # reset the drawing queue
river_origin_cell = get_riverless_edge_cell(grid) # get a new river origin not on a river
current_direction = river_start_direction(river_origin_cell) # get direction to begin in
drawing_q.append(river_origin_cell)
edge_hit = False
river_hit = False
self_hit = False
begin_direction = current_direction
begin_cell = river_origin_cell
while not edge_hit:
mini_q = twenty_steps_in_direction(grid, begin_cell, begin_direction)
if not mini_q: # in the event the function above encountered another river, break and scrap
river_hit = True
break
else: # below here means the function made 20 steps or encountered an edge
for tmp_cell in mini_q: # check if any of the mini q cells are already existing in q; if so, remake
if tmp_cell in drawing_q:
self_hit = True
break
begin_direction = CD(begin_direction)
begin_cell = mini_q[-1]
drawing_q.extend(mini_q)
if begin_cell.is_edge_cell(): # if the last cell queued is an edge cell, set edge_hit to true
edge_hit = True
if len(drawing_q) >= 100 and not river_hit and not self_hit:
valid_river = True
if valid_river:
for cell in drawing_q:
cell.is_highway = True
draw_cell_rect(map_surface, cell.get_color(), cell.pixel_x, cell.pixel_y, DEFAULT_BORDER_COLOR, 1)
MAIN_SCREEN.blit(map_surface, (0, 0))
pygame.display.update()
return
def draw_blocked_cells_on_grid(map_surface, grid, num_blocked):
blocked_cells_drawn = 0
while blocked_cells_drawn != num_blocked:
random_cell = grid[randint(0, GRID_BLOCKS_TALL-1)][randint(0, GRID_BLOCKS_WIDE-1)]
if not random_cell.is_highway or not random_cell.c_type:
random_cell.c_type = 0 # update to blocked cell
blocked_cells_drawn += 1
draw_cell_rect(map_surface, random_cell.get_color(), random_cell.pixel_x, random_cell.pixel_y, DEFAULT_BORDER_COLOR, 1)
MAIN_SCREEN.blit(map_surface, (0, 0))
pygame.display.update()
return
def draw_get_start_and_goal(grid, map_base):
clear_start_goal_nodes(grid)
tmp_map_surface = map_base.copy()
MAIN_SCREEN.blit(tmp_map_surface, (0,0))
pygame.display.update()
while True: # first determine the start cell
if random() < 0.5:
start_row = choice([randint(0, 19), randint(100, 119)]) # rows: top 20 or bottom 20
start_col = randint(0, GRID_BLOCKS_WIDE-1)
else:
start_row = randint(0, GRID_BLOCKS_TALL-1)
start_col = choice([randint(0, 19), randint(139, 159)]) # cols: left 20 or right 20
start_node = grid[start_row][start_col]
if start_node.c_type != 0: # if this cell is not blocked, break out of the while loop
break
while True: # then determine goal cell and test the distance
if random() < 0.5:
goal_row = choice([randint(0, 19), randint(100, 119)]) # rows: top 20 or bottom 20
goal_col = randint(0, GRID_BLOCKS_WIDE-1)
else:
goal_row = randint(0, GRID_BLOCKS_TALL-1)
goal_col = choice([randint(0, 19), randint(139, 159)]) # cols: left 20 or right 20
goal_node = grid[goal_row][goal_col]
distance = E_dist(start_node, goal_node)
if goal_node.c_type != 0 and distance >= 100: # if this cell is not blocked, test to see if it's euclidean distance >= 100
start_node.is_start = True
goal_node.is_goal = True
draw_cell_rect(tmp_map_surface, start_node.get_color(), start_node.pixel_x, start_node.pixel_y, DEFAULT_BORDER_COLOR, 1)
draw_cell_rect(tmp_map_surface, goal_node.get_color(), goal_node.pixel_x, goal_node.pixel_y, DEFAULT_BORDER_COLOR, 1)
MAIN_SCREEN.blit(tmp_map_surface, (0,0))
pygame.display.update()
break
return start_node, goal_node, tmp_map_surface
def draw_cell_rect(map_surface, inner_color, pix_x, pix_y, border_color = DEFAULT_BORDER_COLOR, border_thickness = 0):
if border_thickness <= 0: # if not border needed
pygame.draw.rect(map_surface, inner_color, (pix_x, pix_y, SCALE, SCALE), 0)
else:
pygame.draw.rect(map_surface, border_color, (pix_x, pix_y, SCALE, SCALE), border_thickness)
pygame.draw.rect(map_surface, inner_color, (pix_x + border_thickness,
pix_y + border_thickness,
SCALE - border_thickness,
SCALE - border_thickness), 0)
return
def display_info(cell):
l_bgc = cell.get_color()
if l_bgc == WHITE or l_bgc == GREEN or l_bgc == RED:
l_ftc = BLACK
else:
l_ftc = WHITE
if l_bgc == WHITE:
l_bgc = LIGHT_GRAY
text = '{} | h={} g={} f={} | ROW{}, COL{}'.format(cell.get_type_string(), round(cell.h_value,5), round(cell.g_value,5),
round(cell.h_value+cell.g_value,5), cell.row+1, cell.col+1)
label_width, label_height = FONT.size(text)
if cell.row < GRID_BLOCKS_TALL/2:
y_offset = SCALE
else:
y_offset = -label_height
if cell.col < GRID_BLOCKS_WIDE/2:
x_offset = SCALE
else:
x_offset = -label_width
label = FONT.render(text, 1, l_ftc, l_bgc)
MAIN_SCREEN.blit(label, (cell.pixel_x+x_offset, cell.pixel_y+y_offset))
pygame.display.update()
return
if __name__ == '__main__':
main()