Pathfinding Visualisierung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Marvin93
User
Beiträge: 38
Registriert: Samstag 4. Mai 2019, 15:16

Hallo alle zusammen,

ich bin noch am programmieren lernen und bin auf Youtube auf ein paar Videos gestoßen. Würde gerne verschiedenen Pathfinding Algorithmen visualisieren.

Ich bin erstmal diesem Video hier gefolgt:
https://www.youtube.com/watch?v=JtiK0DO ... echWithTim

Habe das soweit verstanden und gut was gelernt dabei, aber ja im Grunde alles nur nachgetippert. Ich würde das Programm gerne erweitern und in eine GUI integrieren, über ein Dropdown Menü verschiedene Algorithmen auswählen können und Labyrinthe importieren bzw. exportieren können. Wie stelle ich das am besten an? Die Visualisierung ist im Moment in Pygame programmiert. Ich habe überlegt das irgendwie in eine PyQt5 Gui zu implementieren, aber irgendwie funktioniert das alles nicht so richtig. Habe sowohl mit pygame als auch PyQt5 noch nicht wirklich gearbeitet und auch gelesen, dass die Kombination keinen Sinn macht. Was ist der richtige Ansatz, um so ein Projekt umzusetzen? Kann ich den Code so wie er jetzt ist überhaupt verwenden oder sollte ich nochmal von 0 anfangen? Welche Bibliotheken eignen sich für diesen Fall am besten, um möglichst eine gut aussehende Visualisierung in einer GUI. Oder hat jemand noch eine andere interessante Herangehensweise? Würde das gleiche danach auch noch mit verschiedenen Sortieralgorithmen und was sich sonst noch so anbietet umsetzen wollen.

Code: Alles auswählen

import pygame
import math
from queue import PriorityQueue

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PURPLE = (128, 0, 128)
ORANGE = (255, 165, 0)
GREY = (128, 128, 128)
TURQUOISE = (64, 224, 208)

class Spot:
    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.neighbors = []
        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 == TURQUOISE)

    def reset(self):
        self.color = WHITE
    
    def make_closed(self):
        self.color = RED
    
    def make_open(self):
        self.color = GREEN

    def make_barrier(self):
        self.color = BLACK

    def make_start(self):
        self.color = ORANGE

    def make_end(self):
        self.color = TURQUOISE

    def make_path(self):
        self.color = PURPLE

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

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

        if self.row > 0 and not grid[self.row - 1][self.col].is_barrier(): # UP
            self.neightbors.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.neightbors.append(grid[self.row][self.col + 1])

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

    def __lt__(self, other):
        return (False)

def h(p1, p2):
    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))
    came_from = {}
    g_score = {spot: float ("inf") for row in grid for spot in row}
    g_score[start] = 0
    f_score = {spot: float ("inf") for row in grid for spot in row}
    f_score[start] = h(start.get_pos(), end.get_pos())

    open_set_hash = {start}

    while not open_set.empty():
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()

        current = open_set.get()[2]
        open_set_hash.remove(current)

        if current == end: # draws the path
            reconstruct_path(came_from, end, draw)
            end.make_end()
            return True

        for neighbor in current.neightbors:
            temp_g_score = g_score[current] + 1

            if temp_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = temp_g_score
                f_score[neighbor] = temp_g_score + h(neighbor.get_pos(), end.get_pos())
                if neighbor not in open_set_hash:
                    count += 1
                    open_set.put((f_score[neighbor], count, neighbor))
                    open_set_hash.add(neighbor)
                    neighbor.make_open()

        draw()

        if current != start: 
            current.make_closed()

    return False
                    
def make_grid(rows, width):
    grid = []
    gap = width // rows
    for i in range(rows):
        grid.append([])
        for j in range(rows):
            spot = Spot(i, j, gap, rows)
            grid[i].append(spot)

    return grid

def draw_grid(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 spot in row:
            spot.draw(win)

    draw_grid(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():
    width = 800
    win = pygame.display.set_mode((width, width))
    pygame.display.set_caption("A* Path Finding Algorithm")


    ROWS = 50
    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]:
                pos = pygame.mouse.get_pos()
                row, col = get_clicked_pos(pos, ROWS, width)
                spot = grid [row][col]

                if not start and spot != end:
                    start = spot
                    start.make_start()

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


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

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE and start and end:
                    for row in grid:
                        for spot in row:
                            spot.update_neighbors(grid)
                    
                    algorithm(lambda: draw(win, grid, ROWS, width), grid, start, end)

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

    pygame.quit()

if __name__ == "__main__":
    main()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Code ist halbwegs ok, wenn auch etwas ungewoehnlich. Normalerweise wuerde man die Zustaende eines Systems explizit modellieren, und Farben dann den Zustaenden zuordnen. Du hast das genau umgekehrt gemacht. Das wuerde ich aendern. Und auch nicht lauter getter und setter bauen, sondern einfach ein Attribut mit dem Zustand als Enum. Das kann man dann einfach setzen,

Allerdings halte ich PyQt5 fuer geeigneter fuer so eine Aufgabe. Und dann fliegt natuerlich nochmal eine Menge raus.

Ich wuerde auf QGraphicsView und Co setzten, um deine Graphstruktur zu visualisieren, und mir ein generisches JSON-Format ausdenken, mit der man einen Graph modellieren kann.

Alles in allem eine nette, aber auch sehr aufwaendige Aufgabe. Aber wenn der Weg das Ziel ist, dann ist das ja ok.
Antworten