Finalized Grid visiualization
Implemented A* Algorithm separatly to reduce clutering in traverse function
This commit is contained in:
88
P1/graph.py
Normal file
88
P1/graph.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
from prettytable import PrettyTable
|
||||||
|
from pygame.event import set_keyboard_grab
|
||||||
|
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
|
||||||
|
def __init__(self, name, x=None, y=None, state="free"):
|
||||||
|
self.parent = None
|
||||||
|
self.name = name
|
||||||
|
self.edges = []
|
||||||
|
self.value = float('inf') # cost reaching this node
|
||||||
|
|
||||||
|
|
||||||
|
class Edge:
|
||||||
|
|
||||||
|
def __init__(self, edge):
|
||||||
|
self.start = edge[0]
|
||||||
|
self.end = edge[1]
|
||||||
|
self.value = edge[2]
|
||||||
|
|
||||||
|
|
||||||
|
class Graph:
|
||||||
|
|
||||||
|
def __init__(self, node_list, edges):
|
||||||
|
self.nodes = []
|
||||||
|
for name in node_list:
|
||||||
|
self.nodes.append(Node(name))
|
||||||
|
|
||||||
|
for e in edges:
|
||||||
|
e = (getNode(e[0], self.nodes), getNode(e[1], self.nodes), e[2])
|
||||||
|
|
||||||
|
self.nodes[next((i for i, v in enumerate(self.nodes) if v.name == e[0].name), -1)].edges.append(Edge(e))
|
||||||
|
self.nodes[next((i for i, v in enumerate(self.nodes) if v.name == e[1].name), -1)].edges.append(
|
||||||
|
Edge((e[1], e[0], e[2])))
|
||||||
|
|
||||||
|
def print(self):
|
||||||
|
node_list = self.nodes
|
||||||
|
|
||||||
|
t = PrettyTable([' '] + [i.name for i in node_list])
|
||||||
|
for node in node_list:
|
||||||
|
edge_values = ['X'] * len(node_list)
|
||||||
|
for edge in node.edges:
|
||||||
|
edge_values[next((i for i, e in enumerate(node_list) if e.name == edge.end.name), -1)] = edge.g
|
||||||
|
t.add_row([node.name] + edge_values)
|
||||||
|
print(t)
|
||||||
|
|
||||||
|
|
||||||
|
class Queue:
|
||||||
|
def __init__(self, type, sort_by = ''):
|
||||||
|
self.type = type
|
||||||
|
self.items = []
|
||||||
|
self.sort_by = sort_by
|
||||||
|
|
||||||
|
def empty(self):
|
||||||
|
return len(self.items) == 0
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
if not self.empty():
|
||||||
|
if self.type == 'LIFO':
|
||||||
|
''' LIFO
|
||||||
|
queue = [node_0, node_1, ... , node_n]
|
||||||
|
-> pop node_n
|
||||||
|
'''
|
||||||
|
return self.items.pop()
|
||||||
|
else:
|
||||||
|
''' FIFO & PRIO
|
||||||
|
queue = [node_0, node_1, ... , node_n]
|
||||||
|
-> pop node_0
|
||||||
|
'''
|
||||||
|
return self.items.pop(0)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def push(self, node):
|
||||||
|
self.items.append(node)
|
||||||
|
'''
|
||||||
|
queue = [node_0, node_1, ... , node_n] <- node_n+1
|
||||||
|
'''
|
||||||
|
if self.type == 'PRIO':
|
||||||
|
'''
|
||||||
|
Sorting so lowest cost/ value is at [0]
|
||||||
|
queue = [node_0 < node_1 < ... < node_n < node_n+1]
|
||||||
|
'''
|
||||||
|
if self.sort_by == '':
|
||||||
|
self.items.sort(key=lambda item: item.value)
|
||||||
|
elif self.sort_by == 'f':
|
||||||
|
self.items.sort(key=lambda item: item.f)
|
||||||
258
P1/grid.py
Normal file
258
P1/grid.py
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
import pygame
|
||||||
|
import math
|
||||||
|
from graph import Queue
|
||||||
|
|
||||||
|
# Define some colors
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
GREEN = (0, 255, 0)
|
||||||
|
RED = (255, 0, 0)
|
||||||
|
ORANGE = (255, 165, 0)
|
||||||
|
GREY = (128, 128, 128)
|
||||||
|
|
||||||
|
WIDTH = 25
|
||||||
|
HEIGHT = 25
|
||||||
|
MARGIN = 3
|
||||||
|
|
||||||
|
grid_size = 20
|
||||||
|
|
||||||
|
|
||||||
|
class Field:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.state = "free" # states: free, obstacale, start, target
|
||||||
|
|
||||||
|
self.g = float('inf')
|
||||||
|
self.h = 0
|
||||||
|
self.f = float('inf')
|
||||||
|
self.parent = None
|
||||||
|
|
||||||
|
def draw(self, screen):
|
||||||
|
# state based coloring
|
||||||
|
color = WHITE
|
||||||
|
if self.state == "obstacale":
|
||||||
|
color = BLACK
|
||||||
|
elif self.state == "start":
|
||||||
|
color = BLUE
|
||||||
|
elif self.state == "target":
|
||||||
|
color = GREEN
|
||||||
|
elif self.state == "path":
|
||||||
|
color = ORANGE
|
||||||
|
elif self.state == "visited":
|
||||||
|
color = WHITE
|
||||||
|
|
||||||
|
x_calc = (MARGIN + WIDTH) * self.x + MARGIN
|
||||||
|
y_calc = (MARGIN + HEIGHT) * (grid_size - 1 - self.y) + MARGIN # flipping
|
||||||
|
|
||||||
|
pygame.draw.rect(
|
||||||
|
screen,
|
||||||
|
color,
|
||||||
|
[x_calc, y_calc, WIDTH, HEIGHT]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Render the heuristic value as text
|
||||||
|
if self.state != "obstacale": # Don't display on obstacles
|
||||||
|
# Create a font object
|
||||||
|
font = pygame.font.Font(None, 16) # None means default font, 14 is the size
|
||||||
|
|
||||||
|
# Round the heuristic value to 1 decimal place for better display
|
||||||
|
f_text = f"{self.f:.1f}"
|
||||||
|
|
||||||
|
# Render the text
|
||||||
|
text = font.render(f_text, True, BLACK) # True for anti-aliasing, BLACK for text color
|
||||||
|
|
||||||
|
# Calculate text position (centered in the rectangle)
|
||||||
|
text_rect = text.get_rect(center=(x_calc + WIDTH / 2, y_calc + HEIGHT / 2))
|
||||||
|
|
||||||
|
# Draw the text on the screen
|
||||||
|
screen.blit(text, text_rect)
|
||||||
|
|
||||||
|
|
||||||
|
class Grid:
|
||||||
|
def __init__(self, cols, rows):
|
||||||
|
self.cols = cols # x
|
||||||
|
self.rows = rows # y
|
||||||
|
self.grid = []
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < cols: # col = x
|
||||||
|
col = []
|
||||||
|
j = 0
|
||||||
|
while j < rows: # row = y
|
||||||
|
col.append(Field(i, j)) # (x,y)
|
||||||
|
j += 1
|
||||||
|
self.grid.append(col)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self.start = None
|
||||||
|
self.target = None
|
||||||
|
|
||||||
|
def draw(self, screen):
|
||||||
|
for col in self.grid:
|
||||||
|
for field in col:
|
||||||
|
field.draw(screen)
|
||||||
|
|
||||||
|
def heuristic(self, field):
|
||||||
|
return math.sqrt((field.x - self.target[0]) ** 2 + (field.y - self.target[1]) ** 2)
|
||||||
|
|
||||||
|
def get_state(self, x, y):
|
||||||
|
return self.grid[x][y].state
|
||||||
|
|
||||||
|
def set_state(self, state, x, y):
|
||||||
|
if state == "free" or state == "obstacale" or state == "start" or state == "target" or state == "path" or state == "visited":
|
||||||
|
self.grid[x][y].state = state
|
||||||
|
|
||||||
|
def set_free(self, x, y):
|
||||||
|
self.set_state("free", x, y)
|
||||||
|
|
||||||
|
def set_obstacle(self, x, y):
|
||||||
|
self.set_state("obstacale", x, y)
|
||||||
|
|
||||||
|
def set_path(self, x, y):
|
||||||
|
if not (x == self.start[0] and y == self.start[1]) and not (x == self.target[0] and y == self.target[1]):
|
||||||
|
self.set_state("path", x, y)
|
||||||
|
|
||||||
|
def set_visited(self, x, y):
|
||||||
|
if not (x == self.start[0] and y == self.start[1]) and not (x == self.target[0] and y == self.target[1]):
|
||||||
|
self.set_state("visited", x, y)
|
||||||
|
|
||||||
|
def set_start(self, x, y):
|
||||||
|
# reset old start if it exits
|
||||||
|
if self.start:
|
||||||
|
self.set_free(self.start[0], self.start[1])
|
||||||
|
|
||||||
|
self.set_state("start", x, y)
|
||||||
|
self.grid[x][y].parent = self.grid[x][y]
|
||||||
|
self.grid[x][y].g = 0
|
||||||
|
self.grid[x][y].h = self.heuristic(self.grid[x][y])
|
||||||
|
|
||||||
|
self.start = (x, y)
|
||||||
|
|
||||||
|
def set_target(self, x, y):
|
||||||
|
# reset old target if it exits
|
||||||
|
if self.target:
|
||||||
|
self.set_free(self.target[0], self.target[1])
|
||||||
|
|
||||||
|
self.set_state("target", x, y)
|
||||||
|
|
||||||
|
self.target = (x, y)
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Initializing the Grid
|
||||||
|
'''
|
||||||
|
start = (0, 0)
|
||||||
|
target = (19, 19)
|
||||||
|
grid = Grid(grid_size, grid_size)
|
||||||
|
|
||||||
|
# check if start an target are valid
|
||||||
|
if 0 <= start[0] < grid.cols and 0 <= target[0] < grid.cols and 0 <= start[1] < grid.cols and 0 <= target[
|
||||||
|
1] < grid.cols:
|
||||||
|
grid.set_target(target[0], target[1])
|
||||||
|
grid.set_start(start[0], start[1])
|
||||||
|
|
||||||
|
for i in range(0, 10):
|
||||||
|
grid.set_obstacle(9, i)
|
||||||
|
|
||||||
|
for j in range(4, 10):
|
||||||
|
grid.set_obstacle(j, 9)
|
||||||
|
|
||||||
|
for i in range(9, 20):
|
||||||
|
grid.set_obstacle(16, i)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Initializing A* Comps
|
||||||
|
'''
|
||||||
|
|
||||||
|
open = Queue('PRIO', 'f')
|
||||||
|
open.push(grid.grid[0][0])
|
||||||
|
closed = Queue('PRIO', 'f')
|
||||||
|
neighbors = []
|
||||||
|
path = []
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
# window
|
||||||
|
window_width = grid_size * (WIDTH + MARGIN) + MARGIN
|
||||||
|
window_height = grid_size * (HEIGHT + MARGIN) + MARGIN
|
||||||
|
size = (window_width, window_height) # made size variable
|
||||||
|
screen = pygame.display.set_mode(size)
|
||||||
|
|
||||||
|
pygame.display.set_caption("A* Algorithm")
|
||||||
|
|
||||||
|
done = False
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
|
||||||
|
def a_star():
|
||||||
|
neighbor = None
|
||||||
|
while not open.empty():
|
||||||
|
current_field = open.pop()
|
||||||
|
|
||||||
|
if current_field.x == grid.target[0] and current_field.y == grid.target[1]:
|
||||||
|
path.append(current_field)
|
||||||
|
grid.set_path(current_field.x, current_field.y)
|
||||||
|
while not (current_field.x == grid.start[0] and current_field.y == grid.start[1]):
|
||||||
|
current_field = current_field.parent
|
||||||
|
path.insert(0, current_field)
|
||||||
|
grid.set_path(current_field.x, current_field.y)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
closed.push(current_field)
|
||||||
|
grid.set_visited(current_field.x, current_field.y)
|
||||||
|
|
||||||
|
# Nachbarn finden
|
||||||
|
for dx, dy in [(0, -1), (-1, 0), (0, 1), (1, 0)]:
|
||||||
|
nx = current_field.x + dx
|
||||||
|
ny = current_field.y + dy
|
||||||
|
|
||||||
|
# Prüfen, ob der Nachbar gültig ist
|
||||||
|
if 0 <= nx < grid.cols and 0 <= ny < grid.rows:
|
||||||
|
neighbor = grid.grid[nx][ny]
|
||||||
|
|
||||||
|
# Hindernis oder bereits in geschlossener Liste -> überspringen
|
||||||
|
if neighbor.state == "obstacale" or neighbor in [item for item in closed.items]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Neuen g-Wert berechnen
|
||||||
|
tentative_g = current_field.g + 1 # Kosten für einen Schritt = 1
|
||||||
|
|
||||||
|
# Wenn Nachbar nicht in offener Liste oder neuer Pfad besser
|
||||||
|
if neighbor not in [item for item in open.items] or tentative_g < neighbor.g:
|
||||||
|
neighbor.parent = current_field
|
||||||
|
neighbor.g = tentative_g
|
||||||
|
neighbor.h = grid.heuristic(neighbor)
|
||||||
|
neighbor.f = neighbor.g + neighbor.h
|
||||||
|
|
||||||
|
# Knoten zur offenen Liste hinzufügen oder aktualisieren
|
||||||
|
if neighbor not in [item for item in open.items]:
|
||||||
|
open.push(neighbor)
|
||||||
|
else:
|
||||||
|
# Queue aktualisieren
|
||||||
|
open.items.sort(key=lambda item: item.f)
|
||||||
|
|
||||||
|
|
||||||
|
def a_star_main():
|
||||||
|
global done
|
||||||
|
while not done:
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
done = True
|
||||||
|
|
||||||
|
a_star()
|
||||||
|
screen.fill(BLACK)
|
||||||
|
grid.draw(screen)
|
||||||
|
|
||||||
|
# refresh display
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
# refreshrate
|
||||||
|
clock.tick(120)
|
||||||
|
|
||||||
|
|
||||||
|
a_star_main()
|
||||||
|
|
||||||
|
pygame.quit()
|
||||||
41
P1/main.py
Normal file
41
P1/main.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from grid import a_star_main
|
||||||
|
from search import ucs, bfs, dfs
|
||||||
|
from graph import Graph
|
||||||
|
|
||||||
|
# directed and weighted digraph
|
||||||
|
romania = Graph(['Or', 'Ne', 'Ze', 'Ia', 'Ar', 'Si', 'Fa',
|
||||||
|
'Va', 'Ri', 'Ti', 'Lu', 'Pi', 'Ur', 'Hi',
|
||||||
|
'Me', 'Bu', 'Dr', 'Ef', 'Cr', 'Gi'],
|
||||||
|
[
|
||||||
|
('Or', 'Ze', 71), ('Or', 'Si', 151),
|
||||||
|
('Ne', 'Ia', 87), ('Ze', 'Ar', 75),
|
||||||
|
('Ia', 'Va', 92), ('Ar', 'Si', 140),
|
||||||
|
('Ar', 'Ti', 118), ('Si', 'Fa', 99),
|
||||||
|
('Si', 'Ri', 80), ('Fa', 'Bu', 211),
|
||||||
|
('Va', 'Ur', 142), ('Ri', 'Pi', 97),
|
||||||
|
('Ri', 'Cr', 146), ('Ti', 'Lu', 111),
|
||||||
|
('Lu', 'Me', 70), ('Me', 'Dr', 75),
|
||||||
|
('Dr', 'Cr', 120), ('Cr', 'Pi', 138),
|
||||||
|
('Pi', 'Bu', 101), ('Bu', 'Gi', 90),
|
||||||
|
('Bu', 'Ur', 85), ('Ur', 'Hi', 98),
|
||||||
|
('Hi', 'Ef', 86)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Task 1
|
||||||
|
graph = romania
|
||||||
|
ucs(graph, 'Si', 'Bu')
|
||||||
|
|
||||||
|
graph = romania
|
||||||
|
bfs(graph, 'Si', 'Bu')
|
||||||
|
|
||||||
|
graph = romania
|
||||||
|
dfs(graph, 'Si', 'Bu')
|
||||||
|
|
||||||
|
# Task 3 A*
|
||||||
|
a_star_main()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
67
P1/search.py
Normal file
67
P1/search.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from graph import *
|
||||||
|
from utils import getNode
|
||||||
|
|
||||||
|
|
||||||
|
def traverse(graph, frontier, start_node_name, target_node_name):
|
||||||
|
explored = []
|
||||||
|
path = []
|
||||||
|
|
||||||
|
# node
|
||||||
|
start_node = getNode(start_node_name, graph.nodes)
|
||||||
|
start_node.value = 0
|
||||||
|
|
||||||
|
target_node = getNode(target_node_name, graph.nodes)
|
||||||
|
|
||||||
|
frontier.push(start_node)
|
||||||
|
|
||||||
|
while not frontier.empty():
|
||||||
|
current_node = frontier.pop()
|
||||||
|
|
||||||
|
if not current_node == target_node:
|
||||||
|
explored.append(current_node.name)
|
||||||
|
|
||||||
|
for edge in current_node.edges:
|
||||||
|
child = edge.end
|
||||||
|
new_cost = current_node.value + edge.value
|
||||||
|
|
||||||
|
if not explored.__contains__(child.name):
|
||||||
|
child.parent = current_node
|
||||||
|
child.value = new_cost
|
||||||
|
frontier.push(child)
|
||||||
|
|
||||||
|
# UCS-only, updating the value and parent of node in the queue
|
||||||
|
elif frontier.type == 'PRIO' and new_cost < child.value:
|
||||||
|
for node in frontier.items:
|
||||||
|
if node.name == child.name:
|
||||||
|
node.value = new_cost
|
||||||
|
node.parent = current_node
|
||||||
|
frontier.items.sort(key=lambda item: item.value)
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
path.append(current_node.name)
|
||||||
|
while not current_node == start_node:
|
||||||
|
current_node = current_node.parent
|
||||||
|
path.insert(0, current_node.name)
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(path) == 0:
|
||||||
|
print('zwischen ' + start_node_name + ' und ' + target_node_name + ' konnte kein Pfad gefunden werden')
|
||||||
|
else:
|
||||||
|
print('From ' + start_node_name + ' to ' + target_node_name + ': ')
|
||||||
|
print('Path: ' + path.__str__().format())
|
||||||
|
print('Cost: ' + target_node.value.__str__())
|
||||||
|
|
||||||
|
|
||||||
|
def bfs(graph, start_node_name, target_node_name):
|
||||||
|
traverse(graph, Queue('FIFO'), start_node_name, target_node_name)
|
||||||
|
|
||||||
|
|
||||||
|
def dfs(graph, start_node_name, target_node_name):
|
||||||
|
traverse(graph, Queue('LIFO'), start_node_name, target_node_name)
|
||||||
|
|
||||||
|
|
||||||
|
def ucs(graph, start_node_name, target_node_name):
|
||||||
|
traverse(graph, Queue('PRIO'), start_node_name, target_node_name)
|
||||||
|
|
||||||
|
|
||||||
47
P1/utils.py
Normal file
47
P1/utils.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
def getNode(name, l):
|
||||||
|
return next((i for i in l if i.name == name), -1)
|
||||||
|
|
||||||
|
|
||||||
|
def print_grid_direct(self, path=None):
|
||||||
|
"""
|
||||||
|
Gibt das Grid in der Konsole aus, direkt wie es im Array gespeichert ist.
|
||||||
|
Die y-Achse nimmt nach unten zu (0 ist oben), entsprechend der Array-Struktur.
|
||||||
|
Optionaler Parameter 'path' ist eine Liste von (x,y)-Koordinaten, die den Pfad darstellen.
|
||||||
|
"""
|
||||||
|
# Symbole für verschiedene Zustände
|
||||||
|
symbols = {
|
||||||
|
"free": ".",
|
||||||
|
"obstacale": "#", # Tippfehler aus Original-Code beibehalten
|
||||||
|
"start": "S",
|
||||||
|
"target": "G",
|
||||||
|
"path": "o"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ausgabe des Grids mit Koordinatenachsen
|
||||||
|
print("\n ", end="")
|
||||||
|
# Obere x-Achsen-Beschriftung
|
||||||
|
for x in range(self.cols):
|
||||||
|
print(f"{x:2d}", end=" ")
|
||||||
|
print("\n " + "-" * (self.cols * 3))
|
||||||
|
|
||||||
|
# Grid mit y-Achsen-Beschriftung
|
||||||
|
for y in range(self.rows):
|
||||||
|
print(f"{y:2d} |", end=" ")
|
||||||
|
for x in range(self.cols):
|
||||||
|
field = self.grid[x][y]
|
||||||
|
if path and (x, y) in path and field.state != "start" and field.state != "target":
|
||||||
|
print(f"{symbols['path']:2s}", end=" ")
|
||||||
|
else:
|
||||||
|
print(f"{symbols[field.state]:2s}", end=" ")
|
||||||
|
print() # Zeilenumbruch
|
||||||
|
|
||||||
|
print("\nLegende:")
|
||||||
|
print(" . = frei")
|
||||||
|
print(" # = Hindernis")
|
||||||
|
print(" S = Start")
|
||||||
|
print(" G = Ziel")
|
||||||
|
print(" o = Pfad")
|
||||||
|
|
||||||
|
# Zusätzliche Statistiken, falls ein Pfad vorhanden ist
|
||||||
|
if path:
|
||||||
|
print(f"Pfadlänge: {len(path)} Felder")
|
||||||
65
graph.py
65
graph.py
@@ -1,65 +0,0 @@
|
|||||||
from prettytable import PrettyTable
|
|
||||||
from utils import *
|
|
||||||
|
|
||||||
class Node:
|
|
||||||
|
|
||||||
def __init__(self, name):
|
|
||||||
self.parent = None
|
|
||||||
self.name = name
|
|
||||||
self.edges = []
|
|
||||||
self.value = 0 # cost reaching this node
|
|
||||||
|
|
||||||
class Edge:
|
|
||||||
|
|
||||||
def __init__(self, edge):
|
|
||||||
self.start = edge[0]
|
|
||||||
self.end = edge[1]
|
|
||||||
self.value = edge[2]
|
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
|
||||||
|
|
||||||
def __init__(self, node_list, edges):
|
|
||||||
self.nodes = []
|
|
||||||
for name in node_list:
|
|
||||||
self.nodes.append(Node(name))
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
e = (getNode(e[0],self.nodes), getNode(e[1], self.nodes), e[2])
|
|
||||||
|
|
||||||
self.nodes[next((i for i,v in enumerate(self.nodes) if v.name == e[0].name), -1)].edges.append(Edge(e))
|
|
||||||
self.nodes[next((i for i,v in enumerate(self.nodes) if v.name == e[1].name), -1)].edges.append(Edge((e[1], e[0], e[2])))
|
|
||||||
|
|
||||||
|
|
||||||
def print(self):
|
|
||||||
node_list = self.nodes
|
|
||||||
|
|
||||||
t = PrettyTable([' '] +[i.name for i in node_list])
|
|
||||||
for node in node_list:
|
|
||||||
edge_values = ['X'] * len(node_list)
|
|
||||||
for edge in node.edges:
|
|
||||||
edge_values[ next((i for i,e in enumerate(node_list) if e.name == edge.end.name) , -1)] = edge.value
|
|
||||||
t.add_row([node.name] + edge_values)
|
|
||||||
print(t)
|
|
||||||
|
|
||||||
class Queue:
|
|
||||||
def __init__(self, type):
|
|
||||||
self.type = type
|
|
||||||
self.items = []
|
|
||||||
|
|
||||||
def empty(self):
|
|
||||||
return len(self.items) == 0
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
if not self.empty():
|
|
||||||
if self.type=='FIFO':
|
|
||||||
return self.items.pop(0)
|
|
||||||
else:
|
|
||||||
return self.items.pop()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def push(self, node):
|
|
||||||
self.items.append(node)
|
|
||||||
if self.type=='PRIO':
|
|
||||||
# Sorting reverse, because nodes with lowest cost/ value should be prioritized
|
|
||||||
self.items.sort(key = lambda item: item.value, reverse=True)
|
|
||||||
83
main.py
83
main.py
@@ -1,83 +0,0 @@
|
|||||||
from graph import Graph, Node, Queue
|
|
||||||
from utils import getNode
|
|
||||||
|
|
||||||
# directed and weighted digraph
|
|
||||||
romania = Graph( ['Or', 'Ne', 'Ze', 'Ia', 'Ar', 'Si', 'Fa',
|
|
||||||
'Va', 'Ri', 'Ti', 'Lu', 'Pi', 'Ur', 'Hi',
|
|
||||||
'Me', 'Bu', 'Dr', 'Ef', 'Cr', 'Gi'],
|
|
||||||
[
|
|
||||||
('Or', 'Ze', 71), ('Or', 'Si', 151),
|
|
||||||
('Ne', 'Ia', 87), ('Ze', 'Ar', 75),
|
|
||||||
('Ia', 'Va', 92), ('Ar', 'Si', 140),
|
|
||||||
('Ar', 'Ti', 118), ('Si', 'Fa', 99),
|
|
||||||
('Si', 'Ri', 80), ('Fa', 'Bu', 211),
|
|
||||||
('Va', 'Ur', 142), ('Ri', 'Pi', 97),
|
|
||||||
('Ri', 'Cr', 146), ('Ti', 'Lu', 111),
|
|
||||||
('Lu', 'Me', 70), ('Me', 'Dr', 75),
|
|
||||||
('Dr', 'Cr', 120), ('Cr', 'Pi', 138),
|
|
||||||
('Pi', 'Bu', 101), ('Bu', 'Gi', 90),
|
|
||||||
('Bu', 'Ur', 85), ('Ur', 'Hi', 98),
|
|
||||||
('Hi', 'Ef', 86)
|
|
||||||
] )
|
|
||||||
|
|
||||||
def search(graph, queue, start_node_name, target_node_name):
|
|
||||||
visited_nodes = [] # Nodes which have been visited
|
|
||||||
path = []
|
|
||||||
|
|
||||||
start_node = getNode(start_node_name, graph.nodes)
|
|
||||||
target_node = getNode(target_node_name, graph.nodes)
|
|
||||||
|
|
||||||
start_node.value = 0
|
|
||||||
|
|
||||||
queue.push(start_node)
|
|
||||||
|
|
||||||
while not queue.empty():
|
|
||||||
current_node = queue.pop()
|
|
||||||
visited_nodes.append(current_node.name)
|
|
||||||
|
|
||||||
if not current_node == target_node:
|
|
||||||
for edge in current_node.edges:
|
|
||||||
neighbor = edge.end
|
|
||||||
condition = not visited_nodes.__contains__(neighbor.name)
|
|
||||||
new_cost = current_node.value + edge.value
|
|
||||||
|
|
||||||
# UCS
|
|
||||||
if queue.type == 'PRIO':
|
|
||||||
condition = not visited_nodes.__contains__(neighbor.name) and new_cost < neighbor.value
|
|
||||||
|
|
||||||
# works with digraph, because current_node is marked at this point, a cycle is not possible
|
|
||||||
if condition:
|
|
||||||
neighbor.parent = current_node
|
|
||||||
neighbor.value = new_cost
|
|
||||||
queue.push(neighbor)
|
|
||||||
else:
|
|
||||||
path.append(current_node.name)
|
|
||||||
while not current_node == start_node:
|
|
||||||
current_node = current_node.parent
|
|
||||||
path.insert(0, current_node.name)
|
|
||||||
|
|
||||||
|
|
||||||
if path.__len__() == 0:
|
|
||||||
print('zwischen ' + start_node_name + ' und ' + target_node_name + ' konnte kein Pfad gefunden werden')
|
|
||||||
else:
|
|
||||||
print('From ' + start_node_name + ' to ' + target_node_name + ': ')
|
|
||||||
print('Path: ' + path.__str__().format())
|
|
||||||
print('Cost: ' + target_node.value.__str__())
|
|
||||||
|
|
||||||
def bfs(graph, start_node_name, target_node_name):
|
|
||||||
search(graph,Queue('FIFO'),start_node_name, target_node_name)
|
|
||||||
|
|
||||||
def dfs(graph, start_node_name, target_node_name):
|
|
||||||
search(graph,Queue('LIFO'),start_node_name, target_node_name)
|
|
||||||
|
|
||||||
def utc(graph, start_node_name, target_node_name):
|
|
||||||
search(graph,Queue('PRIO'),start_node_name, target_node_name)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
bfs(romania, 'Ti', 'Bu')
|
|
||||||
dfs(romania, 'Ti', 'Bu')
|
|
||||||
utc(romania, 'Or', 'Si')
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in New Issue
Block a user