-
Notifications
You must be signed in to change notification settings - Fork 0
/
day20.py
129 lines (102 loc) · 3.82 KB
/
day20.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# vi: set shiftwidth=4 tabstop=4 expandtab:
import datetime
import itertools
VALUES = [".", "#"]
def get_algo_from_str(s):
assert len(s) == 512 == 2 ** 9
assert all(c in VALUES for c in s)
return [c == "#" for c in s]
def get_points_from_lines(lines):
points = set()
for x, line in enumerate(lines):
for y, val in enumerate(line):
assert val in VALUES
if val == "#":
points.add((x, y))
return points
def get_info_from_lines(lines):
assert lines[1] == ""
return get_algo_from_str(lines[0]), get_points_from_lines(lines[2:])
def get_info_from_file(file_path="day20_input.txt"):
with open(file_path) as f:
return get_info_from_lines([l.strip() for l in f])
def show_points(points):
x_vals = [p[0] for p in points]
y_vals = [p[1] for p in points]
x_range = list(range(min(x_vals), 1 + max(x_vals)))
y_range = list(range(min(y_vals), 1 + max(y_vals)))
for x in x_range:
print("".join(VALUES[(x, y) in points] for y in y_range))
print()
def get_square(point):
x, y = point
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
yield x + dx, y + dy
def get_range(vals, width):
return range(min(vals) - width, max(vals) + width + 1)
def get_square_value(point, points):
return int("".join([str(int(p in points)) for p in get_square(point)]), base=2)
def enhance(points, algo):
assert not algo[0]
width = 1 # Width of the border to consider
x_range = get_range([p[0] for p in points], width)
y_range = get_range([p[1] for p in points], width)
return {
point
for point in itertools.product(x_range, y_range)
if algo[get_square_value(point, points)]
}
def enhance_n(points, algo, n):
if not algo[0]:
algo_odd, algo_even = algo, algo
else:
# If algo starts with '#', points in the middle of nowhere gets lit
# Instead of keeping track of lit points, let's keep track of unlit points
# by reversing the algo values
algo_even = [not val for val in algo]
# Then on next step, this needs to be taken into account
algo_odd = list(reversed(algo))
for i in range(n):
points = enhance(points, algo_even if i % 2 == 0 else algo_odd)
return points
def run_tests():
algo_str = "..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#"
algo = get_algo_from_str(algo_str)
grid_str = [
"#..#.",
"#....",
"##..#",
"..#..",
"..###",
]
points = get_points_from_lines(grid_str)
points2 = enhance_n(points, algo, 2)
assert len(points2) == 35
points50 = enhance_n(points, algo, 50)
assert len(points50) == 3351
# Additional test
algo_str = "." + "#" * 511
algo = get_algo_from_str(algo_str)
grid_str = [
"...",
".#.",
"...",
]
points = get_points_from_lines(grid_str)
points1 = enhance_n(points, algo, 1)
assert len(points1) == 9
points2 = enhance_n(points, algo, 2)
assert len(points2) == 25
def get_solutions():
algo, points = get_info_from_file()
points2 = enhance_n(points, algo, 2)
print(len(points2))
points50 = enhance_n(points, algo, 50)
print(len(points50))
if __name__ == "__main__":
begin = datetime.datetime.now()
run_tests()
get_solutions()
end = datetime.datetime.now()
print(end - begin)