in main.py the output of an a-star results from a Path of Nodes is printed out as expected and gives me the wanted changes in the pygame window:
import pygame
import sys
from models import Node, Path, Map
from pathfinding import a_star
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# Create nodes and path
node1 = Node(100, 100)
node2 = Node(200, 200)
node3 = Node(300, 100)
node4 = Node(400, 300)
node5 = Node(500, 200)
path = Path()
path.add_node(node1)
path.add_node(node2)
path.add_node(node3)
path.add_node(node4)
path.add_node(node5)
# Create mini-map and add path
mini_map = Map(screen, SCREEN_WIDTH, SCREEN_HEIGHT)
# mini_map.generate_random_path()
mini_map.add_path(path)
# Pathfinding
## Path 1
start_node = node1
end_node = node5
found_path = a_star(start_node, end_node)
# PRINTS EXPECTED OUTPUT
print(found_path)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((0, 0, 0))
mini_map.draw_paths()
mini_map.draw_astar_paths()
# Draw found path in a different color
# for i in range(len(found_path) - 1):
# start_pos = (found_path[i].x, found_path[i].y)
# end_pos = (found_path[i + 1].x , found_path[i + 1].y )
# pygame.draw.line(screen, (255, 0, 0), start_pos, end_pos, 2)
# for i in range(len(found_path_g) - 1):
# start_pos_g = (found_path_g[i].x, found_path_g[i].y)
# end_pos_g = (found_path_g[i + 1].x , found_path_g[i + 1].y )
# pygame.draw.line(screen, (255, 0, 0), start_pos_g, end_pos_g, 2)
# if found_path:
# for i in range(len(found_path) - 1):
# start_pos = (found_path[i].x, found_path[i].y)
# end_pos = (found_path[i + 1].x, found_path[i + 1].y)
# pygame.draw.line(screen, (255, 0, 0), start_pos, end_pos, 2)
pygame.display.flip()
clock.tick(30)
Why does it not work in the draw_astar_paths() in models.py. If I create an Array of Nodes the function draws the objects as expected in the GUI. I don’t understand what I am doing wrong, as a_star() returns me an empty list, even when it should be the exact same Node Objects.
import pygame, random
from pathfinding import a_star
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.neighbors = []
self.g = float('inf') # Cost from start node
self.h = 0 # Heuristic cost to end node
self.f = float('inf') # Total cost (g + h)
self.parent = None
def __lt__(self, other):
return self.f < other.f
def __repr__(self):
return f"Node({self.x}, {self.y})"
class Path:
def __init__(self):
self.nodes = []
def add_node(self, node):
self.nodes.append(node)
if len(self.nodes) > 1:
self.nodes[-2].neighbors.append(node)
node.neighbors.append(self.nodes[-2])
def get_nodes(self):
return self.nodes
def get_start_node(self):
return self.nodes[0]
def get_end_node(self):
return self.nodes[len(self.nodes) - 1]
class Map:
def __init__(self, screen, width, height):
self.screen = screen
self.width = width
self.height = height
self.paths = []
def add_path(self, path):
self.paths.append(path)
def generate_random_path(self):
number_of_nodes = random.randint(3, 14)
x_values = random.sample(range(0, self.width), number_of_nodes)
print(x_values)
y_values = random.sample(range(0, self.height), number_of_nodes)
print(y_values)
path = Path()
for a, b in zip(x_values, y_values):
path.add_node(Node(a, b))
self.add_path(path=path)
def get_paths(self):
return self.paths
def draw_paths(self):
for path in self.paths:
nodes = path.get_nodes()
for i in range(len(nodes) - 1):
start_pos = (nodes[i].x, nodes[i].y)
end_pos = (nodes[i + 1].x, nodes[i + 1].y)
pygame.draw.line(self.screen, (255, 255, 255), start_pos, end_pos, 2)
for node in nodes:
pos = (node.x, node.y)
pygame.draw.circle(self.screen, (0, 255, 0), pos, 5)
def draw_astar_paths(self):
for path in self.paths:
for node in path.get_nodes():
# print(f"{node} has {len(node.neighbors)} neighbors.")
pass
start_node = path.get_start_node()
end_node = path.get_end_node()
if not start_node or not end_node:
print("Start node or end node is None")
continue
# found_path = [node1, node2, node3, node4, node5]
# print(f"Start node: {start_node}")
# print(f"End node: {end_node}")
found_path = a_star(start_node, end_node)
if not found_path:
print("No path found by A*")
continue
for node in found_path:
print(f"Node in found path: {node}")
for i in range(len(found_path) - 1):
start_pos = (found_path[i].x, found_path[i].y)
end_pos = (found_path[i + 1].x, found_path[i + 1].y)
pygame.draw.line(self.screen, (255, 0, 0), start_pos, end_pos, 2)
In Below is the code for the pathfinding in pathfinding.py:
import heapq
import math
def heuristic(node, end_node):
return math.sqrt((node.x - end_node.x) ** 2 + (node.y - end_node.y) ** 2)
def a_star(start_node, end_node):
open_list = []
closed_list = set()
start_node.g = 0
start_node.f = heuristic(start_node, end_node)
heapq.heappush(open_list, start_node)
while open_list:
current_node = heapq.heappop(open_list)
if current_node == end_node:
return reconstruct_path(current_node)
closed_list.add(current_node)
for neighbor in current_node.neighbors:
if neighbor in closed_list:
continue
tentative_g_score = current_node.g + heuristic(current_node, neighbor)
if tentative_g_score < neighbor.g:
neighbor.parent = current_node
neighbor.g = tentative_g_score
neighbor.h = heuristic(neighbor, end_node)
neighbor.f = neighbor.g + neighbor.h
if neighbor not in open_list:
heapq.heappush(open_list, neighbor)
return []
def reconstruct_path(current_node):
path = []
while current_node is not None:
path.append(current_node)
current_node = current_node.parent
path.reverse()
return path
I tried writing the code for displaying the a_star path in the main.py which worked, but I want to call the a_star function an function in the Map class as it is cleaner code.
andy-us is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.