Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions A-Star-PathFindingAlgorithm-Script/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<h1 align="center">A Star Path Finding Algorithm (Python Implementaion)</h1>
A python visualization of the A* path finding algorithm. Allows the user to input the starting node, ending node and barrier nodes to visualize the whole process of finding the path.

---------------------------------------------------------------------
## How it works

- Pygame module draws out the grids and sets up the application window, such that the colour of each node can be varied.

- Methods are created to change the colour and functionalities of nodes,
- For left click:- *The first click and seconod click being the start point and end point respectively*, followed by setting up barriers between the two nodes for every click.
- For right click:- Deleting the setup barrier.

- A-star algorithm is implemented -
- `heuristics` function returns approimate distance between *start* and *end* points.
- `update_neighbours` method is used to find out the neighbour nodes of the current node.
- `open_set` queue keeps a list of tuples containing neighbour nodes information as #(fscore, count, node)
- `g_score` storing G score of all neighbour nodes of the current node
- `f_score` storing G score of all neighbour nodes of the current node

- Algorithm is called upon pressing `ENTER`/`RETURN` key
- All inputs reset upon pressing `SPACE BAR` key

---------------------------------------------------------------------
## Requirements (Py modules used)
- pygame
- queue

[](images/1.png)
Binary file added A-Star-PathFindingAlgorithm-Script/images/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added A-Star-PathFindingAlgorithm-Script/images/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
239 changes: 239 additions & 0 deletions A-Star-PathFindingAlgorithm-Script/pathfinding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import pygame
from queue import PriorityQueue

WIDTH = 700
WIN = pygame.display.set_mode((WIDTH,WIDTH))
pygame.display.set_caption("Path Finding Script")


RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (128, 0, 128)
ORANGE = (255, 165 ,0)
GREY = (128, 128, 128)

class Node:
def __init__(self, row, col, width, total_rows):
self.row = row
self.col = col
self.x = row * width
self.y = col * width
self.color = WHITE
self.neighbours = []
self.width = width
self.total_rows = total_rows

def get_pos(self):
return self.row, self.col

def is_closed(self):
return self.color == RED

def is_open(self):
return self.color == GREEN

def is_barrier(self):
return self.color == BLACK

def is_start(self):
return self.color == ORANGE

def is_end(self):
return self.color == PURPLE

def reset(self):
self.color = WHITE

def make_start(self):
self.color = ORANGE

def make_closed(self):
self.color = RED

def make_open(self):
self.color = YELLOW

def make_barrier(self):
self.color = BLACK

def make_end(self):
self.color = PURPLE

def make_path(self):
self.color = GREEN

def draw(self, win):
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.width))

def update_neighbours(self, grid):
self.neighbours = []
if self.row < self.total_rows - 1 and not grid[self.row + 1][self.col].is_barrier(): # DOWN
self.neighbours.append(grid[self.row + 1][self.col])

if self.row > 0 and not grid[self.row - 1][self.col].is_barrier(): # UP
self.neighbours.append(grid[self.row - 1][self.col])

if self.col < self.total_rows - 1 and not grid[self.row][self.col + 1].is_barrier(): # RIGHT
self.neighbours.append(grid[self.row][self.col + 1])

if self.col > 0 and not grid[self.row][self.col - 1].is_barrier(): # LEFT
self.neighbours.append(grid[self.row][self.col - 1])


def __lt__(self, other):
return False

def heuristics(p1, p2):
#MANHATTAN DISTANCE
x1, y1 = p1
x2, y2 = p2
return abs(x1-x2)+abs(y1-y2)

def reconstruct_path(came_from, current, draw):
while current in came_from:
current = came_from[current]
current.make_path()
draw()

def algorithm(draw, grid, start, end):
count = 0
open_set = PriorityQueue()
open_set.put((0, count, start)) #(fscore, count, node)
came_from = {} #Keep track of previous node
g_score = {node: float("inf") for row in grid for node in row} #SETTING Gscore of all NODES infinity
g_score[start] = 0
f_score = {node: float("inf") for row in grid for node in row} #SETTING Fscore of all NODES infinity
f_score[start] = heuristics(start.get_pos(), end.get_pos()) #ESTIMATED DISTANCE BETWEEN START POINT AND ENDPOINT

open_set_hash = {start} #A copy of `open_set` that Keep track of items in PriorityQueue

while not open_set.empty(): #Until every single possible node is checked/ No item in open set left
for event in pygame.event.get():
#QUIT BUTTON
if event.type == pygame.QUIT:
pygame.quit()

current = open_set.get()[2] #THE 'start' node
open_set_hash.remove(current) #Remove the element that was popped from original `open_set`

if current == end:
reconstruct_path(came_from, end, draw)
end.make_end()
return True

for neighbour in current.neighbours: #Check for all neighbours of the `current` node selected
temp_g_score = g_score[current] + 1

if temp_g_score < g_score[neighbour]: #If the current neighbour has a smaller fscore then the f_score, g_scrore and open_set values are updated
came_from[neighbour] = current
g_score[neighbour] = temp_g_score
f_score[neighbour] = temp_g_score + heuristics(neighbour.get_pos(), end.get_pos())
if neighbour not in open_set_hash:
count += 1
open_set.put((f_score[neighbour], count, neighbour))
open_set_hash.add(neighbour)
neighbour.make_open()

draw()

if current != start:
current.make_closed()

return False

def make_grid(rows, width):
global node
grid = []
gap = width//rows
for i in range(rows):
grid.append([])
for j in range(rows):
node = Node(i, j, gap, rows)
grid[i].append(node)
return grid

def draw_grid_lines(win, rows, width):
gap = width//rows
for i in range(rows):
pygame.draw.line(win, GREY, (0, i*gap), (width, i*gap))
for j in range(rows):
pygame.draw.line(win, GREY, (j*gap, 0), (j*gap, width))

def draw(win, grid, rows, width):
win.fill(WHITE)
for row in grid:
for node in row:
node.draw(win)

draw_grid_lines(win, rows, width)
pygame.display.update()

def get_clicked_pos(pos, rows, width):
gap = width // rows
y, x = pos

row = y//gap
col = x//gap
return row, col

def main(win, width):
global node
ROWS = 30
grid = make_grid(ROWS, width)

start = None
end = None

run = True
while run:
draw(win, grid, ROWS, width)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if pygame.mouse.get_pressed()[0]: #LEFT CLICK
pos = pygame.mouse.get_pos()
row, col = get_clicked_pos(pos, ROWS, width)
node = grid[row][col]
if not start and node != end:
start = node
start.make_start()

elif not end and node != start:
end = node
end.make_end()

elif node!=end and node!=start:
node.make_barrier()

elif pygame.mouse.get_pressed()[2]: #RIGHT CLICK
pos = pygame.mouse.get_pos()
row, col = get_clicked_pos(pos, ROWS, width)
node = grid[row][col]
node.reset()
if node == start:
start = None
if node == end:
end = None

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN and start and end:
for row in grid:
for node in row:
node.update_neighbours(grid)

algorithm(lambda: draw(win, grid, ROWS, width), grid, start, end)

if event.key == pygame.K_SPACE:
start = None
end = None
grid = make_grid(ROWS, width)
pygame.quit()


main(WIN, WIDTH)