1
+ # --- Day 9: Stream Processing ---
2
+ #
3
+ # A large stream blocks your path. According to the locals, it's not safe to
4
+ # cross the stream at the moment because it's full of garbage. You look down at
5
+ # the stream; rather than water, you discover that it's a stream of characters.
6
+ #
7
+ # You sit for a while and record part of the stream (your puzzle input).
8
+ # The characters represent groups - sequences that begin with { and end with }.
9
+ # Within a group, there are zero or more other things, separated by commas:
10
+ # either another group or garbage. Since groups can contain other groups,
11
+ # a } only closes the most-recently-opened unclosed group - that is,
12
+ # they are nestable. Your puzzle input represents a single, large group which
13
+ # itself contains many smaller ones.
14
+ #
15
+ # Sometimes, instead of a group, you will find garbage.
16
+ # Garbage begins with < and ends with >.
17
+ # Between those angle brackets, almost any character can appear,
18
+ # including { and }. Within garbage, < has no special meaning.
19
+ #
20
+ # In a futile attempt to clean up the garbage, some program has canceled some
21
+ # of the characters within it using !: inside garbage, any character that
22
+ # comes after ! should be ignored, including <, >, and even another !.
23
+ #
24
+ # You don't see any characters that deviate from these rules.
25
+ # Outside garbage, you only find well-formed groups, and garbage always
26
+ # terminates according to the rules above.
27
+ #
28
+ # Here are some self-contained pieces of garbage:
29
+ #
30
+ # <>, empty garbage.
31
+ # <random characters>, garbage containing random characters.
32
+ # <<<<>, because the extra < are ignored.
33
+ # <{!>}>, because the first > is canceled.
34
+ # <!!>, because the second ! is canceled, allowing the > to terminate the garbage.
35
+ # <!!!>>, because the second ! and the first > are canceled.
36
+ # <{o"i!a,<{i<a>, which ends at the first >.
37
+ # Here are some examples of whole streams and the number of groups they contain:
38
+ #
39
+ # {}, 1 group.
40
+ # {{{}}}, 3 groups.
41
+ # {{},{}}, also 3 groups.
42
+ # {{{},{},{{}}}}, 6 groups.
43
+ # {<{},{},{{}}>}, 1 group (which itself contains garbage).
44
+ # {<a>,<a>,<a>,<a>}, 1 group.
45
+ # {{<a>},{<a>},{<a>},{<a>}}, 5 groups.
46
+ # {{<!>},{<!>},{<!>},{<a>}}, 2 groups (since all but the last > are canceled).
47
+ #
48
+ # Your goal is to find the total score for all groups in your input.
49
+ # Each group is assigned a score which is one more than the score of the group
50
+ # that immediately contains it. (The outermost group gets a score of 1.)
51
+ #
52
+ # {}, score of 1.
53
+ # {{{}}}, score of 1 + 2 + 3 = 6.
54
+ # {{},{}}, score of 1 + 2 + 2 = 5.
55
+ # {{{},{},{{}}}}, score of 1 + 2 + 3 + 3 + 3 + 4 = 16.
56
+ # {<a>,<a>,<a>,<a>}, score of 1.
57
+ # {{<ab>},{<ab>},{<ab>},{<ab>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
58
+ # {{<!!>},{<!!>},{<!!>},{<!!>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
59
+ # {{<a!>},{<a!>},{<a!>},{<ab>}}, score of 1 + 2 = 3.
60
+ #
61
+ # What is the total score for all groups in your input?
62
+ #
63
+ # ------------------------------------------------------------------------------
64
+
65
+ import re
66
+ import sys
67
+ import os
68
+
69
+ sys .setrecursionlimit (17000 )
70
+
71
+ def solve1 (chars , ignore = False , garbage = False , open_groups = 0 ):
72
+ if (len (chars ) == 0 ):
73
+ return 0
74
+ char = chars [0 ]
75
+ if ignore :
76
+ return solve1 (chars [1 :], False , garbage , open_groups )
77
+ if char == '!' :
78
+ return solve1 (chars [1 :], True , garbage , open_groups )
79
+ if garbage :
80
+ if char == '>' :
81
+ return solve1 (chars [1 :], ignore , False , open_groups )
82
+ else :
83
+ return solve1 (chars [1 :], ignore , garbage , open_groups )
84
+ if char == '{' :
85
+ return solve1 (chars [1 :], ignore , garbage , open_groups + 1 )
86
+ elif char == '}' :
87
+ if open_groups > 0 :
88
+ return open_groups + solve1 (chars [1 :], ignore , garbage , open_groups - 1 )
89
+ else :
90
+ return solve1 (chars [1 :], ignore , garbage , open_groups )
91
+ elif char == '<' :
92
+ return solve1 (chars [1 :], ignore , True , open_groups )
93
+ else :
94
+ return solve1 (chars [1 :], ignore , garbage , open_groups )
95
+
96
+ def solve2 (chars , ignore = False , garbage = False , open_groups = 0 ):
97
+ if (len (chars ) == 0 ):
98
+ return 0
99
+ char = chars [0 ]
100
+ if ignore :
101
+ return solve2 (chars [1 :], False , garbage , open_groups )
102
+ if char == '!' :
103
+ return solve2 (chars [1 :], True , garbage , open_groups )
104
+ if garbage :
105
+ if char == '>' :
106
+ return solve2 (chars [1 :], ignore , False , open_groups )
107
+ else :
108
+ return 1 + solve2 (chars [1 :], ignore , garbage , open_groups )
109
+ if char == '{' :
110
+ return solve2 (chars [1 :], ignore , garbage , open_groups + 1 )
111
+ elif char == '}' :
112
+ if open_groups > 0 :
113
+ return solve2 (chars [1 :], ignore , garbage , open_groups - 1 )
114
+ else :
115
+ return solve2 (chars [1 :], ignore , garbage , open_groups )
116
+ elif char == '<' :
117
+ return solve2 (chars [1 :], ignore , True , open_groups )
118
+ else :
119
+ return solve2 (chars [1 :], ignore , garbage , open_groups )
120
+
121
+ if __name__ == '__main__' :
122
+ file_path = os .path .join (os .path .dirname (os .path .abspath (__file__ )), "inputs/day09.txt" )
123
+ if len (sys .argv ) == 2 :
124
+ data = sys .argv [1 ]
125
+ else :
126
+ f = open (file_path )
127
+ data = f .read ()
128
+ print (solve1 (data ))
129
+ print (solve2 (data ))
0 commit comments