-
Notifications
You must be signed in to change notification settings - Fork 0
/
20.py
90 lines (78 loc) · 2.68 KB
/
20.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
# pylint: skip-file
# mypy: ignore-errors
# flake8: noqa
import sys
from collections import defaultdict, deque
sys.setrecursionlimit(10000)
input_value = open("20.txt", "r").read()
lines = input_value.split("\n")
rows = len(lines)
columns = len(lines[0])
start, end = None, None
grid = defaultdict(lambda: None)
for r in range(rows):
for c in range(columns):
grid[r, c] = lines[r][c]
if grid[r, c] == "S":
start = (r, c)
elif grid[r, c] == "E":
end = (r, c)
def explore_to_end(
steps_from_start, cheat_savings, cheat_savings_minimum, position, current_steps
):
(r, c) = position
if grid[r, c] in "S.E":
steps_from_start[r, c] = current_steps
if grid[r, c] == "E":
return
# Force `steps_from_start` to populate first via DFS.
for neighbor in {(r - 1, c), (r, c - 1), (r + 1, c), (r, c + 1)}:
if grid[neighbor] is None:
continue
if grid[neighbor] in ".E" and neighbor not in steps_from_start:
explore_to_end(
steps_from_start,
cheat_savings,
cheat_savings_minimum,
neighbor,
current_steps + 1,
)
for neighbor in {(r - 1, c), (r, c - 1), (r + 1, c), (r, c + 1)}:
if grid[neighbor] is None:
continue
if grid[neighbor] == "#":
explore_to_end(
steps_from_start,
cheat_savings,
cheat_savings_minimum,
neighbor,
current_steps + 1,
)
elif grid[r, c] == "#":
for neighbor in {(r - 1, c), (r, c - 1), (r + 1, c), (r, c + 1)}:
if grid[neighbor] is None:
continue
if (
grid[neighbor] in ".E"
and neighbor in steps_from_start
and steps_from_start[neighbor] - current_steps > cheat_savings_minimum
):
cheat_savings.append(steps_from_start[neighbor] - current_steps)
# Part 1:
steps_from_start = {}
cheat_savings = []
explore_to_end(steps_from_start, cheat_savings, 100, start, 0)
print(len(cheat_savings))
def manhattan(a, b):
(ra, ca) = a
(rb, cb) = b
return abs(rb - ra) + abs(cb - ca)
good_cheats = 0
for cheat_start in steps_from_start:
for cheat_end in steps_from_start:
steps_delta = steps_from_start[cheat_end] - steps_from_start[cheat_start]
distance_cost = manhattan(cheat_start, cheat_end)
if steps_delta - distance_cost >= 100 and distance_cost <= 20:
good_cheats += 1
# Part 2:
print(good_cheats)