-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_15.py
110 lines (92 loc) · 3.05 KB
/
day_15.py
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
106
107
108
109
110
"""AOC Day 15"""
import pathlib
import time
import heapq
from collections import defaultdict
from math import inf
TEST_INPUT = """1163751742
1381373672
2136511328
3694931569
7463417111
1319128137
1359912421
3125421639
1293138521
2311944581"""
def read_input(input_path: str) -> str:
"""take input file path and return a str with the file's content"""
with open(input_path, 'r') as input_file:
input_data = input_file.read().strip()
return input_data
def extract(input_data: str) -> list:
"""take input data and return the appropriate data structure"""
grid = list()
for row in input_data.split('\n'):
grid.append(list(map(int, row)))
return grid
def neighbors(row: int, column: int, height: int, width: int) -> tuple:
for inc_row, inc_column in ((1, 0), (-1, 0), (0, 1), (0, -1)):
delta_row, delta_column = (row + inc_row, column + inc_column)
if 0 <= delta_row < height and 0 <= delta_column < width:
yield(delta_row, delta_column)
def dijkstra(grid: list) -> int:
h, w = len(grid), len(grid[0])
source = (0, 0)
destination = (h - 1, w - 1)
queue = [(0, source)]
min_dists = defaultdict(lambda: inf, {source: 0})
visited = set()
while queue:
dist, node = heapq.heappop(queue)
if node == destination:
return dist
if node in visited:
continue
visited.add(node)
r, c = node
for neighbor in neighbors(r, c, h, w):
if neighbor in visited:
continue
nr, nc = neighbor
new_dist = dist + grid[nr][nc]
if new_dist < min_dists[neighbor]:
min_dists[neighbor] = new_dist
heapq.heappush(queue, (new_dist, neighbor))
return inf
def part1(grid: list) -> int:
"""part1 solver take the entries and return the part1 solution"""
return dijkstra(grid)
def part2(grid: list) -> int:
"""part2 solver take the entries and return the part2 solution"""
tile_w = len(grid)
tile_h = len(grid[0])
for _ in range(4):
for row in grid:
tail = row[-tile_w:]
row.extend((x + 1) if x < 9 else 1 for x in tail)
for _ in range(4):
for row in grid[-tile_h:]:
row = [(x + 1) if x < 9 else 1 for x in row]
grid.append(row)
return dijkstra(grid)
def test_input_day_15():
"""pytest testing function"""
entries = extract(TEST_INPUT)
assert part1(entries) == 40
assert part2(entries) == 315
def test_bench_day_15(benchmark):
"""pytest-benchmark function"""
benchmark(main)
def main():
"""main function"""
input_path = str(pathlib.Path(__file__).resolve().parent.parent) + "/inputs/" + str(pathlib.Path(__file__).stem)
start_time = time.time()
input_data = read_input(input_path)
entries = extract(input_data)
print("Part 1: %d" % part1(entries))
print("Part 2: %d" % part2(entries))
end_time = time.time()
print("Execution time: %f" % (end_time-start_time))
if __name__ == "__main__":
main()