{ "cells": [ { "cell_type": "markdown", "id": "e063ba00", "metadata": {}, "source": [ "# A* Algorithm" ] }, { "cell_type": "markdown", "id": "868592a7", "metadata": {}, "source": [ "## Install Requirements" ] }, { "cell_type": "markdown", "id": "26dff129", "metadata": {}, "source": [ "create a virtual environment in current directory by \n", "\n", "```\n", "python3 -m venv .env # macos\n", "python -m venv .env # linux\n", "```" ] }, { "cell_type": "code", "execution_count": 12, "id": "55fa613b", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:23.660320Z", "start_time": "2025-05-07T21:40:22.973705Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: pygame in /home/safak/Dokumente/Uni/KI/.env/lib64/python3.13/site-packages (2.6.1)\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%pip install pygame" ] }, { "cell_type": "markdown", "id": "db35c098", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": 13, "id": "e7034eee", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:23.825797Z", "start_time": "2025-05-07T21:40:23.718944Z" } }, "outputs": [], "source": [ "import pygame\n", "import math" ] }, { "cell_type": "markdown", "id": "77d18f1b", "metadata": {}, "source": [ "## Global Variables" ] }, { "cell_type": "code", "execution_count": 14, "id": "60b63344", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:23.877569Z", "start_time": "2025-05-07T21:40:23.873797Z" } }, "outputs": [], "source": [ "BLACK = (0, 0, 0)\n", "WHITE = (255, 255, 255)\n", "BLUE = (0, 0, 255)\n", "GREEN = (0, 255, 0)\n", "RED = (255, 0, 0)\n", "ORANGE = (255, 165, 0)\n", "GREY = (128, 128, 128)\n", "\n", "WIDTH = 25\n", "HEIGHT = 25\n", "MARGIN = 3\n", "GRID_SIZE= 20" ] }, { "cell_type": "markdown", "id": "87a05a3b", "metadata": {}, "source": [ "## Classes \n", "\n", "The Queue Class is the same from Task 1" ] }, { "cell_type": "code", "execution_count": 15, "id": "84ad596c", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.013443Z", "start_time": "2025-05-07T21:40:24.010202Z" } }, "outputs": [], "source": [ "class Queue:\n", " def __init__(self, type, sort_by = ''):\n", " self.type = type\n", " self.items = []\n", " self.sort_by = sort_by\n", "\n", " def clear(self):\n", " self.items.clear()\n", " \n", " def is_empty(self):\n", " if len(self.items) > 0:\n", " return False\n", " else:\n", " return True\n", "\n", " def push(self, element):\n", " self.items.append(element)\n", " '''\n", " queue = [element_0, element_1, ... , element_n] <- element_n+1\n", " '''\n", " if self.type == 'PRIO':\n", " '''\n", " Sorting so lowest cost/ value is at [0]\n", " queue = [element_0 < element_1 < ... < element_n < element_n+1]\n", " '''\n", " if self.sort_by == '':\n", " self.items.sort(key=lambda item: item.value)\n", " elif self.sort_by == 'f':\n", " self.items.sort(key=lambda item: item.f)\n", "\n", " def pop(self):\n", " if not self.is_empty():\n", " if self.type == 'LIFO':\n", " ''' LIFO\n", " queue = [element_0, elemente_1, ... , element_n]\n", " -> pop element_n\n", " '''\n", " return self.items.pop()\n", " else:\n", " ''' FIFO & PRIO\n", " queue = [element_0, element_1, ... , element_n]\n", " -> pop element_0\n", " '''\n", " return self.items.pop(0)\n", " return None" ] }, { "cell_type": "code", "execution_count": 16, "id": "d3b77c0a", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.044268Z", "start_time": "2025-05-07T21:40:24.041113Z" } }, "outputs": [], "source": [ "class Field:\n", " def __init__(self, x, y):\n", " self.x = x\n", " self.y = y\n", " self.state = \"free\" # states: free, obstacale, start, target\n", "\n", " self.g = float('inf')\n", " self.h = 0\n", " self.f = float('inf')\n", " self.parent = None\n", "\n", " def draw(self, screen):\n", " # state based coloring\n", " color = WHITE\n", " if self.state == \"obstacale\":\n", " color = BLACK\n", " elif self.state == \"start\":\n", " color = BLUE\n", " elif self.state == \"target\":\n", " color = GREEN\n", " elif self.state == \"path\":\n", " color = ORANGE\n", " elif self.state == \"current\":\n", " color = RED\n", " elif self.state == \"visited\":\n", " color = GREY\n", "\n", " x_calc = (MARGIN + WIDTH) * self.x + MARGIN\n", " y_calc = (MARGIN + HEIGHT) * (GRID_SIZE - 1 - self.y) + MARGIN # flipping\n", "\n", " pygame.draw.rect(\n", " screen,\n", " color,\n", " [x_calc, y_calc, WIDTH, HEIGHT]\n", " )" ] }, { "cell_type": "code", "execution_count": 17, "id": "b3d9f04f", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.125218Z", "start_time": "2025-05-07T21:40:24.120389Z" } }, "outputs": [], "source": [ "class Grid:\n", " def __init__(self, cols, rows):\n", " self.cols = cols # x\n", " self.rows = rows # y\n", " self.grid = []\n", "\n", " i = 0\n", " while i < cols: # col = x\n", " col = []\n", " j = 0\n", " while j < rows: # row = y\n", " col.append(Field(i, j)) # (x,y)\n", " j += 1\n", " self.grid.append(col)\n", " i += 1\n", "\n", " self.start = None\n", " self.target = None\n", "\n", " def draw(self, screen):\n", " for col in self.grid:\n", " for field in col:\n", " field.draw(screen)\n", "\n", " def heuristic(self, field):\n", " return math.sqrt((field.x - self.target[0]) ** 2 + (field.y - self.target[1]) ** 2)\n", "\n", " def get_state(self, x, y):\n", " return self.grid[x][y].state\n", "\n", " def set_state(self, state, x, y):\n", " if state == \"free\" or state == \"obstacale\" or state == \"start\" or state == \"target\" or state == \"path\" or state == \"current\" or state == \"visited\":\n", " self.grid[x][y].state = state\n", "\n", " def set_free(self, x, y):\n", " self.set_state(\"free\", x, y)\n", "\n", " def set_obstacle(self, x, y):\n", " self.set_state(\"obstacale\", x, y)\n", " \n", " def set_current(self, x, y):\n", " self.set_state(\"current\", x, y)\n", " \n", " def set_visited(self, x,y):\n", " self.set_state(\"visited\", x, y)\n", "\n", " def set_path(self, x, y):\n", " if not (x == self.start[0] and y == self.start[1]) and not (x == self.target[0] and y == self.target[1]):\n", " self.set_state(\"path\", x, y)\n", "\n", " def set_start(self, x, y):\n", " # reset old start if it exits\n", " if self.start:\n", " self.set_free(self.start[0], self.start[1])\n", "\n", " self.set_state(\"start\", x, y)\n", " self.grid[x][y].parent = self.grid[x][y]\n", " self.grid[x][y].g = 0\n", " self.grid[x][y].h = self.heuristic(self.grid[x][y])\n", " self.grid[x][y].f = self.grid[x][y].h + self.grid[x][y].g\n", " self.start = (x, y)\n", "\n", " def set_target(self, x, y):\n", " # reset old target if it exits\n", " if self.target:\n", " self.set_free(self.target[0], self.target[1])\n", "\n", " self.set_state(\"target\", x, y)\n", "\n", " self.target = (x, y)" ] }, { "cell_type": "markdown", "id": "a01bed4a", "metadata": {}, "source": [ "## Defining Grid Parameters" ] }, { "cell_type": "code", "execution_count": 18, "id": "53c20ed6", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.289574Z", "start_time": "2025-05-07T21:40:24.250017Z" } }, "outputs": [], "source": [ "\n", "window_width = GRID_SIZE * (WIDTH + MARGIN) + MARGIN\n", "window_height = GRID_SIZE * (HEIGHT + MARGIN) + MARGIN\n", "size = (window_width, window_height) # made size variable\n", "\n", "start = (0, 0)\n", "target = (19, 19)\n", "grid = Grid(GRID_SIZE, GRID_SIZE)\n", "\n", "# check if start an target are valid\n", "if 0 <= start[0] < grid.cols and 0 <= target[0] < grid.cols and 0 <= start[1] < grid.cols and 0 <= target[\n", " 1] < grid.cols:\n", " grid.set_target(target[0], target[1])\n", " grid.set_start(start[0], start[1])\n" ] }, { "cell_type": "markdown", "id": "e67b33e5", "metadata": {}, "source": [ "### Creating Obsticales" ] }, { "cell_type": "code", "execution_count": 19, "id": "d01d53b0", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.329858Z", "start_time": "2025-05-07T21:40:24.327366Z" } }, "outputs": [], "source": [ "for i in range(0, 10):\n", " grid.set_obstacle(9, i)\n", "\n", "for j in range(4, 10):\n", " grid.set_obstacle(j, 9)\n", "\n", "for i in range(9, 20):\n", " grid.set_obstacle(16, i)" ] }, { "cell_type": "markdown", "id": "334e0e61", "metadata": {}, "source": [ "## Initialize A* Parameters" ] }, { "cell_type": "code", "execution_count": 20, "id": "d480bcf1", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:24.378185Z", "start_time": "2025-05-07T21:40:24.374640Z" } }, "outputs": [], "source": [ "open = Queue('PRIO', 'f')\n", "open.push(grid.grid[0][0])\n", "closed = Queue('PRIO', 'f')\n", "neighbors = []\n", "path = []\n", "done = False" ] }, { "cell_type": "markdown", "id": "6898177d", "metadata": {}, "source": [ "## Initialize PyGame Window" ] }, { "cell_type": "code", "execution_count": 21, "id": "a8c78a46", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:25.086317Z", "start_time": "2025-05-07T21:40:24.429636Z" } }, "outputs": [], "source": [ "pygame.init()\n", "screen = pygame.display.set_mode(size)\n", "pygame.display.set_caption(\"A* Algorithm\")\n", "clock = pygame.time.Clock()\n", "\n", "def update_screen():\n", " screen.fill(BLACK)\n", " grid.draw(screen)\n", " pygame.display.flip()\n", " clock.tick(60)" ] }, { "cell_type": "markdown", "id": "3939b45f", "metadata": {}, "source": [ "## A*-Algorithm" ] }, { "cell_type": "code", "execution_count": 22, "id": "4c138642", "metadata": { "ExecuteTime": { "end_time": "2025-05-07T21:40:49.022562Z", "start_time": "2025-05-07T21:40:25.146989Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gefunden\n" ] } ], "source": [ "while not done:\n", " for event in pygame.event.get():\n", " if event.type == pygame.QUIT:\n", " done = True\n", "\n", " while not open.is_empty():\n", " current_field = open.pop()\n", " grid.set_current(current_field.x, current_field.y)\n", " \n", " update_screen()\n", "\n", " if current_field.x == grid.target[0] and current_field.y == grid.target[1]: \n", " path.append(current_field)\n", " grid.set_path(current_field.x, current_field.y)\n", " \n", " while not (current_field.x == grid.start[0] and current_field.y == grid.start[1]):\n", " current_field = current_field.parent\n", " path.insert(0, current_field)\n", " grid.set_path(current_field.x, current_field.y)\n", "\n", " update_screen()\n", "\n", " open.clear()\n", " # done = True\n", "\n", " closed.push(current_field)\n", " grid.set_visited(current_field.x, current_field.y)\n", "\n", "\n", " for dx, dy in [(0, -1), (-1, 0), (0, 1), (1, 0)]:\n", " nx = current_field.x + dx\n", " ny = current_field.y + dy\n", "\n", " if 0 <= nx < grid.cols and 0 <= ny < grid.rows:\n", " neighbor = grid.grid[nx][ny]\n", "\n", " if neighbor.state == \"obstacale\" or closed.items.__contains__(neighbor):\n", " continue\n", "\n", " tentative_g = current_field.g + 1\n", "\n", " if tentative_g < neighbor.g:\n", " neighbor.parent = current_field\n", " neighbor.g = tentative_g\n", " neighbor.h = grid.heuristic(neighbor)\n", " neighbor.f = neighbor.g + neighbor.h\n", "\n", " if open.items.__contains__(neighbor):\n", " open.items.sort(key=lambda item: item.f)\n", " else:\n", " open.push(neighbor)\n", "\n", " update_screen()\n", " \n", "if len(path) == 0:\n", " print(\"No path between\")\n", "else:\n", " print(\"Gefunden\")\n", "\n", "pygame.quit()" ] } ], "metadata": { "kernelspec": { "display_name": ".env", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.3" } }, "nbformat": 4, "nbformat_minor": 5 }