forked from hofmannol/AlgoDatSoSe25
61 lines
2.8 KiB
Python
61 lines
2.8 KiB
Python
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
with open('2024/data/input16.data') as f:
|
|
# with open('2024/data/input16.example') as f:
|
|
# Dont need edges, these are irrelevant to us
|
|
content = [list(line.strip()[1:-1]) for line in f.readlines()[1:-1]]
|
|
|
|
directions = {'N': (-1, 0), 'E': (0, 1), 'S': (1, 0), 'W': (0, -1)}
|
|
|
|
start = next((row, col, 'E') for row in range(len(content)) for col in range(len(content[0])) if content[row][col] == 'S')
|
|
end = next((row, col) for row in range(len(content)) for col in range(len(content[0])) if content[row][col] == 'E')
|
|
|
|
# Python it up^^ (outer loop goes over each cell in grid and continues with non-walls,
|
|
# for each valid cell go over all directions and applies costs dependent on turning)
|
|
graph = { (row, col, dirName): {(row+nextdirRow, col+nextdirCol, nextDir): (0 if dirName == nextDir else 1000) + 1
|
|
for nextDir, (nextdirRow, nextdirCol) in directions.items()
|
|
if 0 <= row+nextdirRow < len(content) and 0 <= col+nextdirCol < len(content[0]) and content[row+nextdirRow][col+nextdirCol] != '#'}
|
|
for row in range(len(content)) for col in range(len(content[0])) if content[row][col] != '#' for dirName in directions }
|
|
|
|
# Algo
|
|
def dijsktra(graph, start, end):
|
|
import heapq # Priority Queue, Do not use own for shorter Code^^
|
|
|
|
pq, costs, predecessors = [(0, start)], {node: float('inf') for node in graph}, {node: None for node in graph}
|
|
costs[start] = 0
|
|
while pq:
|
|
cost, node = heapq.heappop(pq)
|
|
logger.debug(f"Procesing node {node} with current cost {cost}")
|
|
if node[:2] == end[:2]: # Stop when reaching the end position, dont care about direction
|
|
path = []
|
|
while node:
|
|
path.append(node)
|
|
node = predecessors[node]
|
|
logger.debug(f"Final cost: {cost}")
|
|
return cost, path[::-1] # Reverse path to get start-to-end order
|
|
if cost > costs[node]: continue
|
|
|
|
for neighbor, edge_cost in graph[node].items():
|
|
if (new_cost := cost + edge_cost) < costs[neighbor]:
|
|
costs[neighbor], predecessors[neighbor] = new_cost, node
|
|
heapq.heappush(pq, (new_cost, neighbor))
|
|
logger.debug(f"Relaxing edge {node} -> {neighbor}, edge cost: {edge_cost}, new cost: {new_cost}")
|
|
return float('inf'), [] # No path found
|
|
|
|
def drawGraph(content, path=None):
|
|
# Create copy of grid to overlay graph -> No inPlace Stuff
|
|
grid = [row[:] for row in content]
|
|
|
|
if path:
|
|
symbols = {'N': '^', 'E': '>', 'S': 'v', 'W': '<'}
|
|
for row, col, direction in path:
|
|
grid[row][col] = symbols[direction]
|
|
return '\n'.join(''.join(row) for row in grid)
|
|
|
|
costShortest, shortestPath = dijsktra(graph, start, end)
|
|
print("Shortest path cost:", costShortest)
|
|
print(drawGraph(content, shortestPath))
|