diff --git a/A-Star-PathFindingAlgorithm-Script/README.md b/A-Star-PathFindingAlgorithm-Script/README.md new file mode 100644 index 0000000000..ddc41bdfe2 --- /dev/null +++ b/A-Star-PathFindingAlgorithm-Script/README.md @@ -0,0 +1,28 @@ +

A Star Path Finding Algorithm (Python Implementaion)

+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) \ No newline at end of file diff --git a/A-Star-PathFindingAlgorithm-Script/images/1.png b/A-Star-PathFindingAlgorithm-Script/images/1.png new file mode 100644 index 0000000000..d731f4f134 Binary files /dev/null and b/A-Star-PathFindingAlgorithm-Script/images/1.png differ diff --git a/A-Star-PathFindingAlgorithm-Script/images/2.png b/A-Star-PathFindingAlgorithm-Script/images/2.png new file mode 100644 index 0000000000..aa4154f8dd Binary files /dev/null and b/A-Star-PathFindingAlgorithm-Script/images/2.png differ diff --git a/A-Star-PathFindingAlgorithm-Script/pathfinding.py b/A-Star-PathFindingAlgorithm-Script/pathfinding.py new file mode 100644 index 0000000000..693bdff2b3 --- /dev/null +++ b/A-Star-PathFindingAlgorithm-Script/pathfinding.py @@ -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) + + +