2023-05-14 18:24:15 +02:00
|
|
|
import heapq
|
2023-04-22 21:31:19 +02:00
|
|
|
from settings import block_size, screen_width, directions
|
2023-04-22 21:51:55 +02:00
|
|
|
import copy
|
2023-05-14 18:24:15 +02:00
|
|
|
from src.map import get_cost_by_type, get_type_by_position, return_fields_list
|
2023-04-22 21:31:19 +02:00
|
|
|
|
2023-05-14 18:24:15 +02:00
|
|
|
fields = return_fields_list()
|
2023-04-22 21:31:19 +02:00
|
|
|
|
|
|
|
class Node:
|
2023-05-14 18:24:15 +02:00
|
|
|
def __init__(self, state, parent=None, action=None, g=0, h=0):
|
2023-04-22 21:31:19 +02:00
|
|
|
self.state = state
|
|
|
|
self.parent = parent
|
|
|
|
self.action = action
|
2023-05-14 18:24:15 +02:00
|
|
|
self.g = g
|
|
|
|
self.h = h
|
|
|
|
|
|
|
|
def f(self):
|
|
|
|
return self.g + self.h
|
|
|
|
|
|
|
|
def __lt__(self, other):
|
|
|
|
return self.f() < other.f()
|
2023-04-22 21:31:19 +02:00
|
|
|
|
2023-05-14 18:24:15 +02:00
|
|
|
class Astar:
|
2023-04-22 21:31:19 +02:00
|
|
|
def __init__(self):
|
|
|
|
self.fringe = []
|
|
|
|
self.explored = []
|
|
|
|
|
|
|
|
def successor(self, state):
|
2023-04-22 21:43:28 +02:00
|
|
|
pos_x, pos_y, rotation = state
|
|
|
|
options = []
|
2023-05-14 18:24:15 +02:00
|
|
|
cost = get_cost_by_type(get_type_by_position(fields, pos_x, pos_y))
|
2023-04-22 21:43:28 +02:00
|
|
|
|
|
|
|
if rotation == directions[0]:
|
|
|
|
states = [(pos_x, pos_y - block_size, directions[0]), (pos_x, pos_y, directions[270]), (pos_x, pos_y, directions[90])]
|
|
|
|
actions = ['F', 'L', 'R']
|
|
|
|
elif rotation == directions[90]:
|
|
|
|
states = [(pos_x + block_size, pos_y, directions[90]), (pos_x, pos_y, directions[0]), (pos_x, pos_y, directions[180])]
|
|
|
|
actions = ['F', 'L', 'R']
|
|
|
|
elif rotation == directions[180]:
|
|
|
|
states = [(pos_x, pos_y + block_size, directions[180]), (pos_x, pos_y, directions[90]), (pos_x, pos_y, directions[270])]
|
|
|
|
actions = ['F', 'L', 'R']
|
|
|
|
elif rotation == directions[270]:
|
|
|
|
states = [(pos_x - block_size, pos_y, directions[270]), (pos_x, pos_y, directions[0]), (pos_x, pos_y, directions[180])]
|
|
|
|
actions = ['F', 'L', 'R']
|
|
|
|
|
2023-04-22 21:47:00 +02:00
|
|
|
for s, a in zip(states, actions):
|
|
|
|
if self.valid_state(s):
|
2023-05-14 18:24:15 +02:00
|
|
|
options.append((a, s, cost))
|
2023-04-22 21:47:00 +02:00
|
|
|
|
|
|
|
return options
|
|
|
|
|
|
|
|
def valid_state(self, state):
|
|
|
|
pos_x, pos_y, rotation = state
|
|
|
|
if pos_x < 0 or pos_x >= screen_width or pos_y < 0 or pos_y >= screen_width:
|
|
|
|
return False
|
|
|
|
return True
|
2023-05-14 18:24:15 +02:00
|
|
|
|
|
|
|
def heuristic(self, state, goal):
|
|
|
|
return abs(state[0] - goal[0]) + abs(state[1] - goal[1])
|
2023-04-22 21:31:19 +02:00
|
|
|
|
2023-04-22 21:54:08 +02:00
|
|
|
def goal_path(self, elem):
|
2023-04-22 21:51:55 +02:00
|
|
|
path = []
|
|
|
|
|
|
|
|
while elem.parent:
|
|
|
|
path.append([elem.action, elem.state[0], elem.state[1]])
|
|
|
|
elem = elem.parent
|
|
|
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
def search(self, istate, goaltest):
|
2023-04-22 21:54:08 +02:00
|
|
|
x, y, rotation = istate
|
2023-05-14 18:24:15 +02:00
|
|
|
start_node = Node((x, y, rotation), None, None, 0, self.heuristic(istate, goaltest))
|
2023-04-22 21:54:08 +02:00
|
|
|
|
2023-05-14 18:24:15 +02:00
|
|
|
heapq.heappush(self.fringe, (start_node.f(), start_node))
|
2023-04-22 21:54:08 +02:00
|
|
|
|
2023-04-22 21:51:55 +02:00
|
|
|
while True:
|
2023-04-22 21:54:08 +02:00
|
|
|
if len(self.fringe) == 0:
|
2023-04-22 21:51:55 +02:00
|
|
|
return False
|
|
|
|
|
2023-05-14 18:24:15 +02:00
|
|
|
_, elem = heapq.heappop(self.fringe)
|
2023-04-22 21:54:08 +02:00
|
|
|
|
2023-04-22 21:51:55 +02:00
|
|
|
if elem.state[0] == goaltest[0] and elem.state[1] == goaltest[1]:
|
2023-04-22 21:54:08 +02:00
|
|
|
return self.goal_path(elem)
|
2023-04-22 21:51:55 +02:00
|
|
|
|
2023-04-22 21:54:08 +02:00
|
|
|
self.explored.append(elem.state)
|
2023-04-22 21:51:55 +02:00
|
|
|
|
2023-05-14 18:24:15 +02:00
|
|
|
for (action, state, cost) in self.successor(elem.state):
|
2023-04-22 21:54:08 +02:00
|
|
|
if state not in self.explored:
|
2023-05-14 18:24:15 +02:00
|
|
|
g = elem.g + cost # cost to move from parent node to current node is based on the field type.
|
|
|
|
h = self.heuristic(state, goaltest) # manhattan distance cost
|
|
|
|
x = Node(state, elem, action, g, h) #creating the node and pushing it into fringe
|
|
|
|
heapq.heappush(self.fringe, (x.f(), x))
|
|
|
|
|
|
|
|
|