I made a 2d mario kart using python, and now I'm putting together an algorithm to finish the track. The idea is very simple, calculate the shortest route to the next checkpoint and walk towards it. So far the code works, but when I add weight to the grass edges (where the kart has greater friction than the track, making the path through the grass 100x more expensive) the code simply takes a long time to solve the shortest path. When the kart is on a straight line from the checkpoint, it calculates it super quickly, but if there is a grass in the middle of the straight line, the code takes a long time to calculate the path (I don't have the exact value, but I think it's around 10,000x slower).
Here's the code snippet for the A* function:
COST_MAPPING = {
Road: 3,
Checkpoint: 3,
Boost: 1,
Grass: 300
}
def a_star(self, string, start, goal, h, neighbors):
"""
A* Search Algorithm
A* is an informed search algorithm that uses a combination of the cost to
reach a node (goal), and an estimated cost (h) from the current node to
the goal, to determine the priority of nodes in the search.
Wikipedia page: https://en.wikipedia.org/wiki/A*_search_algorithm
"""
def reconstruct_path(cameFrom, current):
total_path = [current]
while current in cameFrom:
current = cameFrom[current]
total_path.insert(0, current)
return total_path
openSet = [(0, start)]
cameFrom = {}
gScore = {tuple(start): 0}
fScore = {tuple(start): h(string, start, goal)}
while openSet:
_, current = heappop(openSet)
if tuple(current) == tuple(goal):
return reconstruct_path(cameFrom, tuple(current))
for neighbor in neighbors(string, current):
# lines with problem
neighbor_track_element = self.kart.get_track_element(string, neighbor[0], neighbor[1])[0]
cost = COST_MAPPING.get(neighbor_track_element)
tentative_gScore = gScore[tuple(current)] + cost
# ends here
if tentative_gScore < gScore.get(tuple(neighbor), float('inf')):
cameFrom[tuple(neighbor)] = tuple(current)
gScore[tuple(neighbor)] = tentative_gScore
fScore[tuple(neighbor)] = tentative_gScore + h(string, neighbor, goal)
heappush(openSet, (fScore[tuple(neighbor)], tuple(neighbor)))
return None
def h(self, string, current, goal):
manhattan_distance = abs(int(current[0]) - int(goal[0])) + abs(int(current[1]) - int(goal[1]))
kart_speed = self.kart.get_speed()
speed_x, speed_y = self.kart.get_speed()
kart_speed = math.sqrt(speed_x**2 + speed_y**2)
track_element, _ = self.kart.get_track_element(string, current[0], current[1])
terrain_penalty = 1
if track_element == Grass:
terrain_penalty = 100
combined_heuristic = manhattan_distance + terrain_penalty * kart_speed
return combined_heuristic
def neighbors(self, string, current):
x, y = current
possible_neighbors = [
(x + 1, y),
(x - 1, y),
(x, y + 1),
(x, y - 1),
(x - 1, y - 1),
(x - 1, y + 1),
(x + 1, y - 1),
(x + 1, y + 1)
]
valid_neighbors = [
(nx, ny) for nx, ny in possible_neighbors
if self.kart.get_track_element(string, nx, ny)[0] in (Road, Boost, Checkpoint, Grass)
]
return valid_neighbors
The heuristic is based on the distance between the start and the goal coordinates combined with the terrain_penalty (100x for the grass) times the current kart speed.
combined_heuristic = manhattan_distance + terrain_penalty * kart_speed
The cost when I set it to be always 1 makes the calculations really fast, but the kart goes over grass (obviously).
tentative_gScore = gScore[tuple(current)] + cost
My question is, why is cost affecting code performance? what could be the problem?
I tried to apply a simpler heuristic, using only the Manhattan distance, but it didn't work either.