-
Notifications
You must be signed in to change notification settings - Fork 4
/
day13_fast.py
119 lines (107 loc) · 3.17 KB
/
day13_fast.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
"""
Day 13: Distress Signal
"""
# pylint: disable=duplicate-code
from itertools import zip_longest
from numbers import Number
_TOKEN_OPEN = object()
_TOKEN_CLOSE = object()
def _parse(line):
index = 0
while index < len(line):
if line[index] == "[":
yield _TOKEN_OPEN
elif line[index] == "]":
yield _TOKEN_CLOSE
elif line[index].isdigit():
start_index, index = index, index + 1
while line[index].isdigit():
index += 1
yield int(line[start_index:index])
continue
index += 1
# pylint: disable=too-many-return-statements,too-many-branches
def _compare(lhs, rhs):
while True:
try:
left = next(lhs)
except StopIteration:
try:
next(rhs)
return -1
except StopIteration:
return 0
try:
right = next(rhs)
except StopIteration:
return 1
if left is _TOKEN_CLOSE and right is _TOKEN_CLOSE:
continue
if left is _TOKEN_CLOSE:
return -1
if right is _TOKEN_CLOSE:
return 1
if isinstance(left, Number) and isinstance(right, Number):
if left < right:
return -1
if left > right:
return 1
elif isinstance(left, Number):
depth = 0
while right is _TOKEN_OPEN:
depth += 1
right = next(rhs)
if isinstance(right, Number):
if left < right:
return -1
if left > right:
return 1
else:
return 1
for _ in range(depth):
if next(rhs) is not _TOKEN_CLOSE:
return -1
elif isinstance(right, Number):
depth = 0
while left is _TOKEN_OPEN:
depth += 1
left = next(lhs)
if isinstance(left, Number):
if right < left:
return 1
if right > left:
return -1
else:
return -1
for _ in range(depth):
if next(lhs) is not _TOKEN_CLOSE:
return 1
elif left != right:
raise AssertionError()
def part1(lines):
"""
>>> from .day13 import SAMPLE_INPUT
>>> part1(SAMPLE_INPUT)
13
"""
total = 0
for i, pair in enumerate(zip_longest(*(iter(lines),) * 3)):
if _compare(_parse(pair[0]), _parse(pair[1])) <= 0:
total += i + 1
return total
def part2(lines):
"""
>>> from .day13 import SAMPLE_INPUT
>>> part2(SAMPLE_INPUT)
140
"""
divider1 = (_TOKEN_OPEN, _TOKEN_OPEN, 2, _TOKEN_CLOSE, _TOKEN_CLOSE)
divider2 = (_TOKEN_OPEN, _TOKEN_OPEN, 6, _TOKEN_CLOSE, _TOKEN_CLOSE)
count1, count2 = 1, 1
for line in filter(str.strip, lines):
if _compare(_parse(line), iter(divider1)) < 0:
count1 += 1
elif _compare(_parse(line), iter(divider2)) < 0:
count2 += 1
return count1 * (count1 + count2)
parts = (part1, part2)