-
Notifications
You must be signed in to change notification settings - Fork 2
/
__init__.py
104 lines (78 loc) · 2.74 KB
/
__init__.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
from typing import *
from aocpy import BaseChallenge, foldl
Coordinate = Tuple[int, int, int]
CubeMap = Dict[Coordinate, bool]
ADJACENT_LOCATIONS = (
(1, 0, 0),
(-1, 0, 0),
(0, 1, 0),
(0, -1, 0),
(0, 0, 1),
(0, 0, -1),
)
def sum_coordinates(ca: Coordinate, cb: Coordinate) -> Coordinate:
return tuple(a + b for (a, b) in zip(ca, cb))
def parse(instr: str) -> CubeMap:
res: CubeMap = {}
for line in instr.strip().splitlines():
res[tuple(map(int, line.split(",")))] = False
for coord in res:
is_completely_surrounded = True
for modifier in ADJACENT_LOCATIONS:
combined = sum_coordinates(coord, modifier)
is_completely_surrounded = is_completely_surrounded and combined in res
res[coord] = is_completely_surrounded
return res
def count_exposed_faces(targets: Iterable[Coordinate], all_cubes: CubeMap) -> int:
n = 0
for coord in targets:
for modifier in ADJACENT_LOCATIONS:
if sum_coordinates(coord, modifier) not in all_cubes:
n += 1
return n
def min_max(x: Iterable[int]) -> Tuple[int, int]:
mini, maxi = None, 0
for item in x:
if item > maxi:
maxi = item
if mini is None or item < mini:
mini = item
if mini is None:
raise ValueError("empty set")
return mini, maxi
def is_coord_in_range(c: Coordinate, min_c: Coordinate, max_c: Coordinate) -> bool:
return foldl(
lambda x, y: x and y,
((a <= b <= c) for (a, b, c) in zip(min_c, c, max_c)),
True,
)
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
inp = parse(instr)
return count_exposed_faces([p for p in inp if not inp[p]], inp)
@staticmethod
def two(instr: str) -> int:
inp = parse(instr)
min_x, max_x = min_max(c[0] for c in inp)
min_y, max_y = min_max(c[1] for c in inp)
min_z, max_z = min_max(c[2] for c in inp)
min_coord = (min_x - 1, min_y - 1, min_z - 1)
max_coord = (max_x + 1, max_y + 1, max_z + 1)
visited: Dict[Coordinate, None] = {}
touchable_faces = 0
scan_points = [min_coord]
while len(scan_points) != 0:
current = scan_points.pop()
if current in visited:
continue
visited[current] = None
for modifier in ADJACENT_LOCATIONS:
combined = sum_coordinates(current, modifier)
if not is_coord_in_range(combined, min_coord, max_coord):
continue
if combined in inp:
touchable_faces += 1
else:
scan_points.append(combined)
return touchable_faces