-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathday18.py
128 lines (104 loc) · 4.31 KB
/
day18.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
import pytest
from typing import List
@pytest.mark.parametrize('expression, expected',
[
('1 + 2 * 3 + 4 * 5 + 6', 71),
('1 + (2 * 3) + (4 * (5 + 6))', 51),
('2 * 3 + (4 * 5)', 26),
('5 + (8 * 3 + 9 + 3 * 4 * 3)', 437),
('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))', 12240),
('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2', 13632),
])
def test_evaluate(expression: str, expected: int):
assert evaluate(expression) == expected
@pytest.mark.parametrize('expression, expected',
[
('1 + 2 * 3 + 4 * 5 + 6', 231),
('1 + (2 * 3) + (4 * (5 + 6))', 51),
('2 * 3 + (4 * 5)', 46),
('5 + (8 * 3 + 9 + 3 * 4 * 3)', 1445),
('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))', 669060),
('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2', 23340),
])
def test_evaluate_advanced(expression: str, expected: int):
assert evaluate_advanced(expression) == expected
def get_inner_parenthesis_content(expression: str) -> str:
"""
returns the innermost parenthesis - e.g. ((5 + 2 + (3 * 2)) + (2 + 3))
would return (3 * 2)
-- note, other lower may exist later in the string but this is the first
-- occurring leaf node
:param expression: string with math equation
:return: contents of lowest level parenthesis
"""
stack = []
for i, c in enumerate(expression):
if c == '(':
stack.append(i)
elif c == ')' and stack:
start = stack.pop()
return expression[start + 1: i]
return ''
def evaluate(expression_str: str) -> int:
# reduce all parenthesis to their values
# starting with the ones that have no nested parenthesis inside
# until we have an expression with only numbers
while '(' in expression_str:
content = get_inner_parenthesis_content(expression_str)
result = evaluate(content)
expression_str = expression_str.replace('(' + content + ')', str(result))
# now we have an expression with no parenthesis
# - so we can cleanly calculate it
expression = expression_str.split(' ')
if expression:
mem = int(expression.pop(0))
while expression:
op = expression.pop(0)
value = int(expression.pop(0))
if op == '+':
mem += value
elif op == '*':
mem *= value
return mem
else:
return 0
def evaluate_advanced(expression_str: str) -> int:
# reduce all parenthesis to their values
# starting with the ones that have no nested parenthesis inside
# until we have an expression with only numbers
while '(' in expression_str:
content = get_inner_parenthesis_content(expression_str)
result = evaluate_advanced(content)
expression_str = expression_str.replace('(' + content + ')', str(result))
expression = expression_str.split(' ')
# reduce the + first
while True:
try:
pos = expression.index('+')
value = int(expression[pos - 1]) + int(expression[pos + 1])
expression = expression[: pos - 1] + [str(value)] + expression[pos + 2:]
except ValueError:
break
# now we have an expression only multiplication
# - so we can cleanly calculate it
if expression:
mem = int(expression.pop(0))
while expression:
_ = expression.pop(0)
value = int(expression.pop(0))
mem *= value
return mem
else:
return 0
def parse_input(filename: str) -> List[str]:
return [line.strip() for line in open(filename).readlines()]
def part1(expressions: List[str]) -> int:
return sum(evaluate(expression) for expression in expressions)
def part2(expressions: List[str]) -> int:
return sum(evaluate_advanced(expression) for expression in expressions)
def main():
expressions = parse_input('input/day18.txt')
print(f'Part 1: {part1(expressions)}')
print(f'Part 2: {part2(expressions)}')
if __name__ == '__main__':
main()