forked from hofmannol/AlgoDatSoSe25
125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
import logging
|
|
from graphviz import Digraph
|
|
from collections import deque
|
|
|
|
logger = logging.getLogger(__name__)
|
|
# logging.basicConfig(level=logging.DEBUG)
|
|
|
|
import time
|
|
|
|
def timeMS(func, *args, **kwargs):
|
|
startTime = time.perf_counter()
|
|
result = func(*args, **kwargs)
|
|
endTime = time.perf_counter()
|
|
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
|
|
print(f"{func.__name__} took {elapsedMS:.2f} ms")
|
|
return result
|
|
|
|
from utils.memory_array import MemoryArray
|
|
from utils.memory_cell import MemoryCell
|
|
from utils.literal import Literal
|
|
from utils.memory_range import mrange
|
|
from utils.memory_manager import MemoryManager
|
|
|
|
class Graph:
|
|
def __init__(self):
|
|
self.adjacencyList = {}
|
|
|
|
def addEdge(self, node1, node2, bidirectional=True):
|
|
if node1 not in self.adjacencyList:
|
|
self.adjacencyList[node1] = []
|
|
if node2 not in self.adjacencyList:
|
|
self.adjacencyList[node2] = []
|
|
self.adjacencyList[node1].append(node2)
|
|
if bidirectional:
|
|
self.adjacencyList[node2].append(node1)
|
|
|
|
def serialize(self):
|
|
return self.adjacencyList
|
|
|
|
def breadthFirstSearch(self, start, goal, edgesGonePassed = None):
|
|
if start not in self.adjacencyList or goal not in self.adjacencyList:
|
|
return None, None
|
|
|
|
# Dont want to have a class for this, set suffices
|
|
visited = set()
|
|
queue = deque([(start, [start], set())]) # (current_node, path, edgesGoneToGetHere)
|
|
|
|
while queue:
|
|
currentNode, path, edgesGone = queue.popleft()
|
|
|
|
if currentNode == goal:
|
|
return path, edgesGone
|
|
|
|
if currentNode not in visited:
|
|
logger.info(f"visiting {currentNode}")
|
|
visited.add(currentNode)
|
|
for neighbor in self.adjacencyList[currentNode]:
|
|
edge = (currentNode, neighbor)
|
|
# We already went this Edge. Read Part3 as not allowing this to happen
|
|
edgeReverse = (neighbor, currentNode)
|
|
if neighbor not in visited and (edgesGonePassed is None or edge not in edgesGonePassed) and (edgesGonePassed is None or edgeReverse not in edgesGonePassed):
|
|
# Pythonic way of saying neighbour, path no next clear neighbour
|
|
# and union of edgesGone with current edge
|
|
queue.append((neighbor, path + [neighbor], edgesGone | {edge}))
|
|
|
|
return None, None
|
|
|
|
|
|
def graphvizify(filePath: str, outputFile: str = 'build/hoehleGraph'):
|
|
graph = Digraph()
|
|
graph.attr(rankdir='TB')
|
|
graph.attr('node', shape='circle', style='filled', fillcolor='lightgray')
|
|
graph.attr('edge', arrowsize='0.7')
|
|
|
|
# Reuse the function to also create our Graph... Waste not want not^^
|
|
caveGraph = Graph();
|
|
|
|
with open(filePath, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
delimiter = '<>' if '<>' in line else '>' if '>' in line else None
|
|
if delimiter:
|
|
node1, node2 = map(str.strip, line.split(delimiter))
|
|
node1, node2 = map(lambda x: x.strip('"'), map(str.strip, line.split(delimiter)))
|
|
graph.edge(f"\"{node1}\"", f"\"{node2}\"", dir='none' if delimiter == '<>' else None)
|
|
caveGraph.addEdge(node1, node2, (delimiter == '<>'));
|
|
|
|
try:
|
|
graph.render(outputFile, view=True)
|
|
except Exception as e:
|
|
print(f"Could not display graph: {e}\n Trying to save without viewing!")
|
|
try:
|
|
graph.render(outputFile, view=False)
|
|
print(f"Your built map should be available here: {outputFile}.pdf")
|
|
except Exception as e:
|
|
print(f"Could not save graph file: {e}")
|
|
|
|
return caveGraph
|
|
|
|
if __name__ == "__main__":
|
|
for filename in [ "data/hoehle.txt", "data/hoehleLarger.txt"]:
|
|
caveGraph = graphvizify(filename, 'build/hoehleGraph')
|
|
|
|
start = "Höhleneingang"
|
|
goal = "Schatzkammer"
|
|
shortestPath, edgesGoneInitial = caveGraph.breadthFirstSearch(start, goal)
|
|
logger.debug(caveGraph.adjacencyList)
|
|
logger.debug(edgesGoneInitial)
|
|
|
|
if shortestPath:
|
|
print(f"Shortest path from {start} to {goal} is:")
|
|
print(" -> ".join(shortestPath))
|
|
else:
|
|
print(f"No path found from {start} to {goal}.")
|
|
|
|
returnpath, _ = caveGraph.breadthFirstSearch(goal, start, edgesGoneInitial)
|
|
|
|
if returnpath:
|
|
print(f"Shortest path from {goal} to {start} is:")
|
|
print(" -> ".join(returnpath))
|
|
else:
|
|
print("No path back Home found. Good Luck")
|
|
|
|
exit(0)
|