-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday12.py
More file actions
executable file
·105 lines (78 loc) · 2.95 KB
/
day12.py
File metadata and controls
executable file
·105 lines (78 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#! /usr/bin/env python3
### stdlib imports
import math
import typing
### local imports
import utils
CoordGrid = list[list[str]]
Coord = tuple[int, int]
CoordPath = list[tuple[int, int]]
def getGridSize(grid: CoordGrid) -> tuple[int, int]:
return (len(grid[0]), len(grid))
def findPoint(grid: CoordGrid, char: str) -> Coord:
for y, row in enumerate(grid):
for x, rowChar in enumerate(row):
if rowChar == char:
return (x, y)
utils.printError(f"Char '{char}' not found in grid")
exit(1)
def getNeighbors(
grid: CoordGrid, point: Coord
) -> typing.Generator[Coord, None, None]:
pointX, pointY = point
pointLevel = ord(grid[pointY][pointX])
limitX, limitY = getGridSize(grid)
for modX, modY in [(0, -1), (1, 0), (0, 1), (-1, 0)]:
x = pointX + modX
y = pointY + modY
if (
-1 < x < limitX
and -1 < y < limitY
and ord(grid[y][x]) >= (pointLevel - 1)
):
yield (x, y)
def createTraveralDistanceMap(
grid: CoordGrid, point: Coord
) -> dict[Coord, int]:
"""Using the BFS grid traversal technique, create a mapping of start points to their traversal distance."""
queue: CoordPath = [point]
distanceMap: dict[Coord, int] = {}
distanceMap[point] = 0
while queue:
current = queue.pop(0)
for neighbor in getNeighbors(grid, current):
if neighbor not in distanceMap:
distanceMap[neighbor] = distanceMap[current] + 1
queue.append(neighbor)
return distanceMap
@utils.part1
def part1(puzzleInput: str):
# Split the puzzle input into an addressable grid
grid: CoordGrid = [list(row) for row in puzzleInput.strip().split("\n")]
# Find the origin coordinates
origin = findPoint(grid, "S")
destination = findPoint(grid, "E")
# Modify the origin and destination points with the current character representing their heights
originX, originY = origin
destX, destY = destination
grid[originY][originX] = "a"
grid[destY][destX] = "z"
# Starting from the end point, create a mapping of traversal distances
traversalMap = createTraveralDistanceMap(grid, destination)
# The answer is the traversal distance to the origin point
utils.printAnswer(traversalMap[origin])
# Return the grid and traversal map for part 2 to work with
return (grid, traversalMap)
@utils.part2
def part2(_, part1Data: tuple[CoordGrid, dict[Coord, int]]):
grid, traversalMap = part1Data
# Find all points on the map with the same elevation as the origin
candidates: list[Coord] = []
for y, row in enumerate(grid):
for x, char in enumerate(row):
if char == "a":
candidates.append((x, y))
# The answer is the candidate with the shortest path to the destination point
utils.printAnswer(min([traversalMap.get(c, math.inf) for c in candidates]))
if __name__ == "__main__":
utils.start()