forked from hofmannol/AlgoDatSoSe25
rendeer maze
This commit is contained in:
parent
4d581a87de
commit
6868e089a4
15
data/aoc2416test.txt
Normal file
15
data/aoc2416test.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
###############
|
||||||
|
#.......#....E#
|
||||||
|
#.#.###.#.###.#
|
||||||
|
#.....#.#...#.#
|
||||||
|
#.###.#####.#.#
|
||||||
|
#.#.#.......#.#
|
||||||
|
#.#.#####.###.#
|
||||||
|
#...........#.#
|
||||||
|
###.#.#####.#.#
|
||||||
|
#...#.....#.#.#
|
||||||
|
#.#.#.###.#.#.#
|
||||||
|
#.....#...#.#.#
|
||||||
|
#.###.#.#.#.#.#
|
||||||
|
#S..#.....#...#
|
||||||
|
###############
|
59
praktika/09_aoc/rendeer.py
Normal file
59
praktika/09_aoc/rendeer.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from utils.project_dir import get_path
|
||||||
|
from vorlesung.L08_graphen.graph import AdjacencyListGraph
|
||||||
|
|
||||||
|
DIRECTIONS = [(1,0), (0, 1), (-1, 0), (0, -1)]
|
||||||
|
|
||||||
|
class D16Solution:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
with open(get_path("data/aoc2416.txt"), "r") as file:
|
||||||
|
self.puzzle = [line.strip() for line in file]
|
||||||
|
self.cells = set()
|
||||||
|
self.start = None
|
||||||
|
self.end = None
|
||||||
|
|
||||||
|
def get_cells_start_end(self):
|
||||||
|
for row in range(len(self.puzzle)):
|
||||||
|
for col in range(len(self.puzzle[row])):
|
||||||
|
if self.puzzle[row][col] != '#':
|
||||||
|
self.cells.add((col, row))
|
||||||
|
if self.puzzle[row][col] == 'S':
|
||||||
|
self.start = (col, row)
|
||||||
|
if self.puzzle[row][col] == 'E':
|
||||||
|
self.end = (col, row)
|
||||||
|
|
||||||
|
def get_label(self, cell, direction):
|
||||||
|
"""Generate a label for a cell based on its coordinates and direction."""
|
||||||
|
return f"{cell[0]},{cell[1]}_{direction}"
|
||||||
|
|
||||||
|
def create_graph(self):
|
||||||
|
graph = AdjacencyListGraph()
|
||||||
|
for cell in self.cells:
|
||||||
|
for direction in DIRECTIONS:
|
||||||
|
label = self.get_label(cell, direction)
|
||||||
|
graph.insert_vertex(label)
|
||||||
|
for cell in self.cells:
|
||||||
|
for d, direction in enumerate(DIRECTIONS):
|
||||||
|
label = self.get_label(cell, direction)
|
||||||
|
dx, dy = direction
|
||||||
|
neighbor = (cell[0] + dx, cell[1] + dy)
|
||||||
|
if neighbor in self.cells:
|
||||||
|
graph.connect(label, self.get_label(neighbor, direction))
|
||||||
|
direction_left = DIRECTIONS[(d - 1) % 4]
|
||||||
|
direction_right = DIRECTIONS[(d + 1) % 4]
|
||||||
|
graph.connect(label, self.get_label(cell, direction_left), 1000)
|
||||||
|
graph.connect(label, self.get_label(cell, direction_right), 1000)
|
||||||
|
return graph
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
solution = D16Solution()
|
||||||
|
solution.get_cells_start_end()
|
||||||
|
graph = solution.create_graph()
|
||||||
|
start = solution.get_label(solution.start, DIRECTIONS[0])
|
||||||
|
print(f"Start: {start}")
|
||||||
|
distance_map, predecessor_map = graph.dijkstra(start)
|
||||||
|
end_labels = [solution.get_label(solution.end, direction) for direction in DIRECTIONS]
|
||||||
|
end_vertices = [graph.get_vertex(label) for label in end_labels]
|
||||||
|
min_weight = min([distance_map[vertex] for vertex in end_vertices])
|
||||||
|
print(f"Minimum distance to End {solution.end}: {min_weight}")
|
||||||
|
|
40
utils/priority_queue.py
Normal file
40
utils/priority_queue.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import heapq
|
||||||
|
|
||||||
|
class PriorityQueue:
|
||||||
|
def __init__(self):
|
||||||
|
self.heap = []
|
||||||
|
self.entry_finder = {} # map: item -> [priority, item]
|
||||||
|
self.REMOVED = '<removed>'
|
||||||
|
self.counter = 0 # unique sequence count to break ties
|
||||||
|
|
||||||
|
def add_or_update(self, item, priority):
|
||||||
|
if item in self.entry_finder:
|
||||||
|
self.remove(item)
|
||||||
|
count = self.counter
|
||||||
|
entry = [priority, count, item]
|
||||||
|
self.entry_finder[item] = entry
|
||||||
|
heapq.heappush(self.heap, entry)
|
||||||
|
self.counter += 1
|
||||||
|
|
||||||
|
def remove(self, item):
|
||||||
|
entry = self.entry_finder.pop(item)
|
||||||
|
entry[-1] = self.REMOVED # mark as removed
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
while self.heap:
|
||||||
|
priority, count, item = heapq.heappop(self.heap)
|
||||||
|
if item != self.REMOVED:
|
||||||
|
del self.entry_finder[item]
|
||||||
|
return item, priority
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pq = PriorityQueue()
|
||||||
|
pq.add_or_update('task1', 1)
|
||||||
|
pq.add_or_update('task2', float('inf'))
|
||||||
|
pq.add_or_update('task3', float('inf'))
|
||||||
|
|
||||||
|
print(pq.pop()) # Should print ('task1', 1)
|
||||||
|
pq.add_or_update('task2', 0) # Update priority of 'task1'
|
||||||
|
print(pq.pop()) # Should print ('task2', 0)
|
||||||
|
print(pq.pop()) # Should print ('task3', 3)
|
45
vorlesung/L08_graphen/aoc2212.py
Normal file
45
vorlesung/L08_graphen/aoc2212.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from vorlesung.L08_graphen.graph import Graph, AdjacencyMatrixGraph
|
||||||
|
from utils.project_dir import get_path
|
||||||
|
|
||||||
|
graph = AdjacencyMatrixGraph()
|
||||||
|
start = ""
|
||||||
|
end = ""
|
||||||
|
|
||||||
|
def read_file(filename: str = "data/aoc2212.txt"):
|
||||||
|
"""Read a file and return the content as a string."""
|
||||||
|
|
||||||
|
def adjust_char(char):
|
||||||
|
"""Adjust character for comparison."""
|
||||||
|
if char == 'S':
|
||||||
|
return 'a'
|
||||||
|
elif char == 'E':
|
||||||
|
return 'z'
|
||||||
|
return char
|
||||||
|
|
||||||
|
global start, end
|
||||||
|
with open(get_path(filename), "r") as file:
|
||||||
|
quest = file.read().strip().splitlines()
|
||||||
|
for row, line in enumerate(quest):
|
||||||
|
for col, char in enumerate(line):
|
||||||
|
label = f"{row},{col}"
|
||||||
|
graph.insert_vertex(label)
|
||||||
|
if char == "S":
|
||||||
|
start = label
|
||||||
|
if char == "E":
|
||||||
|
end = label
|
||||||
|
for row, line in enumerate(quest):
|
||||||
|
for col, char in enumerate(line):
|
||||||
|
for neighbor in [(row - 1, col), (row, col - 1), (row + 1, col), (row, col + 1)]:
|
||||||
|
if 0 <= neighbor[0] < len(quest) and 0 <= neighbor[1] < len(line):
|
||||||
|
if ord(adjust_char(quest[neighbor[0]][neighbor[1]])) <= ord(adjust_char(char)) + 1:
|
||||||
|
label1 = f"{row},{col}"
|
||||||
|
label2 = f"{neighbor[0]},{neighbor[1]}"
|
||||||
|
graph.connect(label1, label2)
|
||||||
|
|
||||||
|
|
||||||
|
# Lösung des Adventskalenders 2022, Tag 12
|
||||||
|
read_file("data/aoc2212test.txt")
|
||||||
|
graph.graph()
|
||||||
|
distance_map, predecessor_map = graph.bfs(start)
|
||||||
|
print(distance_map[graph.get_vertex(end)])
|
||||||
|
print(graph.path(end, predecessor_map))
|
@ -4,6 +4,7 @@ from enum import Enum
|
|||||||
import graphviz
|
import graphviz
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from utils.project_dir import get_path
|
from utils.project_dir import get_path
|
||||||
|
from utils.priority_queue import PriorityQueue
|
||||||
|
|
||||||
|
|
||||||
class NodeColor(Enum):
|
class NodeColor(Enum):
|
||||||
@ -18,6 +19,13 @@ class Vertex:
|
|||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Vertex({self.value})"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
"""A graph."""
|
"""A graph."""
|
||||||
@ -147,6 +155,56 @@ class Graph:
|
|||||||
filename = get_path(filename)
|
filename = get_path(filename)
|
||||||
dot.render(filename)
|
dot.render(filename)
|
||||||
|
|
||||||
|
def dijkstra(self, start_name: str) -> tuple[dict[Vertex, float], dict[Vertex, Vertex | None]]:
|
||||||
|
"""
|
||||||
|
Führt den Dijkstra-Algorithmus für kürzeste Pfade durch, implementiert mit Knotenfarben.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start_name: Name des Startknotens
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Ein Tupel aus zwei Dictionaries:
|
||||||
|
- distance_map: Abbildung von Knoten auf ihre kürzeste Distanz vom Startknoten
|
||||||
|
- predecessor_map: Abbildung von Knoten auf ihre Vorgänger im kürzesten Pfad
|
||||||
|
"""
|
||||||
|
|
||||||
|
def relax(vertex, dest, weight):
|
||||||
|
"""
|
||||||
|
Entspannt die Kante zwischen vertex und dest.
|
||||||
|
Aktualisiert die Distanz und den Vorgänger, wenn ein kürzerer Pfad gefunden wird.
|
||||||
|
"""
|
||||||
|
if distance_map[vertex] + weight < distance_map[dest]:
|
||||||
|
distance_map[dest] = distance_map[vertex] + weight
|
||||||
|
predecessor_map[dest] = vertex
|
||||||
|
queue.add_or_update(dest, distance_map[dest])
|
||||||
|
|
||||||
|
# Initialisierung der Maps
|
||||||
|
distance_map = {} # Speichert kürzeste Distanzen
|
||||||
|
predecessor_map = {} # Speichert Vorgänger
|
||||||
|
|
||||||
|
# Initialisiere alle Knoten
|
||||||
|
queue = PriorityQueue()
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
distance_map[vertex] = float('inf') # Initiale Distanz unendlich
|
||||||
|
predecessor_map[vertex] = None # Initialer Vorgänger None
|
||||||
|
queue.add_or_update(vertex, distance_map[vertex]) # Füge Knoten zur Prioritätswarteschlange hinzu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setze Startknoten
|
||||||
|
start_node = self.get_vertex(start_name)
|
||||||
|
distance_map[start_node] = 0
|
||||||
|
queue.add_or_update(start_node, distance_map[start_node])
|
||||||
|
|
||||||
|
while True:
|
||||||
|
entry = queue.pop()
|
||||||
|
if entry is None:
|
||||||
|
break
|
||||||
|
vertex = entry[0]
|
||||||
|
for dest, weight in self.get_adjacent_vertices_with_weight(vertex.value):
|
||||||
|
relax(vertex, dest, weight)
|
||||||
|
return distance_map, predecessor_map
|
||||||
|
|
||||||
|
|
||||||
class AdjacencyListGraph(Graph):
|
class AdjacencyListGraph(Graph):
|
||||||
"""A graph implemented as an adjacency list."""
|
"""A graph implemented as an adjacency list."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user