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))