-
Notifications
You must be signed in to change notification settings - Fork 0
/
iterative_astar.py
137 lines (106 loc) · 4.75 KB
/
iterative_astar.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
#A* but it starts from the end and recalculates all the nodes each cycle.
#Essentially A* with iterative deepening
#Very CPU intensive, but it uses far less memory.
#Incredibly slow on large maps.
class Iterative_astar:
G_INCREMENT = 1 #Controls how wide it searches
def __init__(self,map):
self.map = map
self.name = 'Iterative A*'
def get_path(self,winning_node,nodes):
path = []
current_node = winning_node
for i in range(0,winning_node[1]): #Will only loop for the length of the path
if current_node[4] != None:
path.append(current_node[3])
for node in nodes:
if current_node[4] == node[0]:
current_node = node
break #Slightly speeds it up
return path
def get_heuristic(self,start_cords = None):
#Gets the distance between the start and end, and converts it to an actual distance.
#Could also use the standard distance formula but I already had this code written.
#Uses Manhattan Distance
end_cords = self.map.get_2D(self.map.start_point)
if start_cords is None:
start_cords = self.map.get_2D(self.map.end_point)
else:
start_cords = self.map.get_2D(start_cords)
if end_cords[0] > start_cords[0]:
col_dist = end_cords[0] - start_cords[0]
else:
col_dist = start_cords[0] - end_cords[0]
if end_cords[1] > start_cords[1]:
row_dist = end_cords[1] - start_cords[1]
else:
row_dist = start_cords[1] - end_cords[1]
distance = col_dist+row_dist
return distance
#Will return true or false depending on whether or not the move will violate the rules
def valid_move(self,move,current_node,expanded,threshold):
for node in expanded:
if current_node[0] + move == node[0]:
return False
if current_node[1]+current_node[2] > threshold:
return False
if current_node[0] + move >= 0 and current_node[0] + move < len(self.map.tiles): #Check out of bounds
if self.map.tiles[current_node[0] + move].is_wall == False: #Check wall collision
if move == -1:
if self.map.get_2D(current_node[0])[0] == 0: #Stop it from using the left edge
return False
elif move == 1:
if self.map.get_2D(current_node[0])[0] == self.map.columns -1: #Stop it from using the right edge
return False
#Above the top and below the bottom are both out of bounds and should have already been caught, this is just an added precaution.
elif move == self.map.columns:
if self.map.get_2D(current_node[0])[1] == self.map.rows -1: #Stop it from using the bottom
return False
elif move == -self.map.columns:
if self.map.get_2D(current_node[0])[1] == 0: #Stop it from using the top
return False
return True
return False
def find_path(self):
start_node = self.map.end_point,0,self.get_heuristic(),None,None #Starts at the end and works its way back
current_node = start_node
actions = 0 #How many moves it took to find the end
threshold = 6
winning_node = None
while winning_node == None:
threshold += 1 + self.G_INCREMENT
dead_end = False
nodes = [start_node] #tile index, distance from start, heuristic,parent
expanded = []
actions = actions + 1
while dead_end == False and winning_node == None:
self.map.print_map()
print ('---------------------------')
actions = actions + 1
lowest_cost = threshold
for node in nodes:
if node[1] + node[2] <= lowest_cost and node not in expanded:
lowest_cost = node[1] + node[2]
priority_queue = []
#Adds all nodes with that cost into the queue
dead_end = True
for node in nodes:
if node[1]+node[2] == lowest_cost and node not in expanded:
priority_queue.append(node)
dead_end = False
#Adds all surrounding nodes into the list, and adds the node that was expanded into the list of expanded nodes
for node in priority_queue:
if node[0] == self.map.start_point:
winning_node = node
break
if self.valid_move(-self.map.columns,node,expanded,threshold):
nodes.append([node[0] - self.map.columns,node[1]+self.G_INCREMENT,self.get_heuristic(node[0] - self.map.columns),'down',node[0]])
if self.valid_move(-1, node,expanded,threshold):
nodes.append([node[0] - 1,node[1]+self.G_INCREMENT,self.get_heuristic(node[0] - 1),'right',node[0]])
if self.valid_move(self.map.columns,node,expanded,threshold):
nodes.append([node[0] + self.map.columns,node[1]+self.G_INCREMENT,self.get_heuristic(node[0] + self.map.columns),'up',node[0]])
if self.valid_move(1,node,expanded,threshold):
nodes.append([node[0] + 1,node[1]+self.G_INCREMENT,self.get_heuristic(node[0] + 1),'left',node[0]])
expanded.append(node)
self.map.tiles[node[0]].is_expanded = True #For printing purposes
return self.get_path(winning_node,nodes),len(nodes)