-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobjects.py
More file actions
145 lines (115 loc) · 4.49 KB
/
objects.py
File metadata and controls
145 lines (115 loc) · 4.49 KB
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
import heapq
import math
import pygame
from colour import Color
from constants import *
class Grid:
def __init__(self, width, n):
self.grid = [[0 for _ in range(n)] for _ in range(n)]
self.start = 0, 0
self.end = None
self.width = width
self.n = n
self.cell_size = width // n
self.offset_x = (WIDTH - self.width) // 2
self.offset_y = (HEIGHT - self.width) // 2
self.path = []
def draw(self, win):
n = self.n
c_size = self.cell_size
for i in range(n):
for j in range(n):
color = DARK_WHITE if self.grid[i][j] == 1 else LIGHTER_BLACK
if (i, j) == self.end:
color = PURPLE
elif (i, j) == self.start:
color = RED
s = c_size - 1 if self.grid[i][j] else c_size - 4
pygame.draw.rect(win, color,
[j * c_size + self.offset_x, i * c_size + self.offset_y, s, s])
self.show_path(win)
def handle_left_click(self, x, y):
i = (y - self.offset_y) // self.cell_size
j = (x - self.offset_x) // self.cell_size
if i < 0 or i >= self.n or j < 0 or j >= self.n:
return
self.grid[i][j] = 1
self.solve()
def handle_right_click(self, x, y):
i = (y - self.offset_y) // self.cell_size
j = (x - self.offset_x) // self.cell_size
if i < 0 or i >= self.n or j < 0 or j >= self.n:
return
self.grid[i][j] = 0
def set_end(self, x, y):
i = (y - self.offset_y) // self.cell_size
j = (x - self.offset_x) // self.cell_size
self.end = i, j
def show_path(self, win):
n = len(self.path)
start = Color("red")
colors = list(start.range_to(Color("purple"), n + 1))
c_size = self.cell_size
pi, pj = None, None
for x, (i, j) in enumerate(self.path):
r, g, b = colors[x].rgb[0] * 255, colors[x].rgb[1] * 255, colors[x].rgb[2] * 255
if pi is not None and pj is not None:
pygame.draw.line(win, (r, g, b),
((pj + 0.5) * c_size + self.offset_x, (pi + 0.5) * c_size + self.offset_y),
((j + 0.5) * c_size + self.offset_x, (i + 0.5) * c_size + self.offset_y),
int(30 / math.log2(n) * 1))
pi, pj = i, j
def reset(self):
self.__init__(self.width, self.n)
def solve(self):
if self.start and self.end:
self.path = a_star(self.grid, self.start, self.end)
# define heuristic function to estimate distance to goal
def heuristic(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
def a_star(grid, start, end):
n, m = len(grid), len(grid[0])
# define possible movements (up, down, left, right, diagonal)
neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
# create set of visited nodes and heap queue for exploring nodes
visited = set()
heap = [(0, start)]
came_from = dict()
cost_so_far = dict()
came_from[start] = None
cost_so_far[start] = 0
c = 0
# loop until heap queue is empty or goal is reached
while heap:
c += 1
# get node with the lowest cost from heap queue
current_cost, current_node = heapq.heappop(heap)
# check if goal is reached
if current_node == end:
break
# check neighbors of current node
for i, j in neighbors:
neighbor = current_node[0] + i, current_node[1] + j
ni, nj = neighbor
# skip neighbor if it is outside the grid
if ni < 0 or ni >= n or nj < 0 or nj >= m:
continue
new_cost = cost_so_far[current_node] + grid[ni][nj]
# skip neighbor if it is not traversable or has already been visited
if neighbor in visited or grid[ni][nj] == 1:
continue
# update cost and add neighbor to heap queue
cost_so_far[neighbor] = new_cost
priority = new_cost + heuristic(end, neighbor)
heapq.heappush(heap, (priority, neighbor))
visited.add(neighbor)
came_from[neighbor] = current_node
# build path from start to end using came_from dictionary
path = []
current = end
while current != start:
path.append(current)
current = came_from[current]
path.append(start)
path.reverse()
return path