diff --git a/data/hoehleLarger.txt b/data/hoehleLarger.txt new file mode 100644 index 0000000..3124343 --- /dev/null +++ b/data/hoehleLarger.txt @@ -0,0 +1,24 @@ +"Höhleneingang" <> "Ost/West-Passage" +"Höhleneingang" <> "Nord/Süd-Passage" +"Nord/Süd-Passage" <> "Nebelraum" +"Steiniger Pfad" > "Ost/West-Passage" +"Ost/West-Passage" <> "Schwefelgewölbe" +"Schwefelgewölbe" > "Steiniger Pfad" +"Schatzkammer" > "Nebelraum" +"Steiniger Pfad" > "Schatzkammer" +"Kristallhöhle" <> "Schwefelgewölbe" +"Kristallhöhle" <> "Nebelraum" +"Versteckter Gang" > "Kristallhöhle" +"Versteckter Gang" > "Schatzkammer" +"Unterirdischer See" <> "Kristallhöhle" +"Unterirdischer See" <> "Ost/West-Passage" +"Geheimgang" > "Unterirdischer See" +"Geheimgang" > "Versteckter Gang" +"Abgrund" > "Geheimgang" +"Abgrund" > "Nebelraum" +"Verlorene Kammer" > "Abgrund" +"Verlorene Kammer" > "Schwefelgewölbe" +"Geheimgang" > "Schatzkammer" +"Kristallhöhle" > "Höhleneingang" +"Schwefelgewölbe" > "Höhleneingang" +"Abgrund" > "Schatzkammer" diff --git a/schoeffelbe/pr08.py b/schoeffelbe/pr08.py new file mode 100644 index 0000000..c4f65e6 --- /dev/null +++ b/schoeffelbe/pr08.py @@ -0,0 +1,121 @@ +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) + if neighbor not in visited and (edgesGonePassed is None or edge 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) + + 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)