147
day10/day10.py
Executable file
147
day10/day10.py
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
def __init__(self, got: str, expected: str):
|
||||
self.got = got
|
||||
self.expected = expected
|
||||
super().__init__(f"expected {repr(expected)} but got {repr(got)} instead")
|
||||
|
||||
|
||||
CLOSING = ")]}>"
|
||||
|
||||
|
||||
class Parser:
|
||||
def __init__(self, text: str):
|
||||
self.text = text
|
||||
self.ptr = 0
|
||||
|
||||
def reset(self):
|
||||
self.ptr = 0
|
||||
|
||||
@property
|
||||
def is_eof(self) -> bool:
|
||||
return self.ptr >= len(self.text)
|
||||
|
||||
@property
|
||||
def c(self) -> str:
|
||||
if self.is_eof:
|
||||
return ""
|
||||
else:
|
||||
return self.text[self.ptr]
|
||||
|
||||
def adv(self):
|
||||
if self.ptr >= len(self.text):
|
||||
self.ptr = len(self.text)
|
||||
else:
|
||||
self.ptr += 1
|
||||
|
||||
def expect(self, c: str):
|
||||
if c == self.c:
|
||||
self.adv()
|
||||
else:
|
||||
raise ParseError(self.c, c)
|
||||
|
||||
def parse(self):
|
||||
if self.c == "(":
|
||||
self.parse_paren()
|
||||
elif self.c == "[":
|
||||
self.parse_bracket()
|
||||
elif self.c == "{":
|
||||
self.parse_brace()
|
||||
elif self.c == "<":
|
||||
self.parse_angle()
|
||||
|
||||
def parse_paren(self):
|
||||
self.expect("(")
|
||||
while self.c not in CLOSING:
|
||||
self.parse()
|
||||
self.expect(")")
|
||||
|
||||
def parse_bracket(self):
|
||||
self.expect("[")
|
||||
while self.c not in CLOSING:
|
||||
self.parse()
|
||||
self.expect("]")
|
||||
|
||||
def parse_brace(self):
|
||||
self.expect("{")
|
||||
while self.c not in CLOSING:
|
||||
self.parse()
|
||||
self.expect("}")
|
||||
|
||||
def parse_angle(self):
|
||||
self.expect("<")
|
||||
while self.c not in CLOSING:
|
||||
self.parse()
|
||||
self.expect(">")
|
||||
|
||||
|
||||
def part1(lines: Sequence[str]):
|
||||
scores = {
|
||||
")": 3,
|
||||
"]": 57,
|
||||
"}": 1197,
|
||||
">": 25137,
|
||||
"": 0,
|
||||
}
|
||||
points = 0
|
||||
for line in lines:
|
||||
try:
|
||||
parser = Parser(line)
|
||||
while not parser.is_eof:
|
||||
parser.parse()
|
||||
except ParseError as e:
|
||||
points += scores[e.got]
|
||||
print(points)
|
||||
|
||||
|
||||
def part2(lines: Sequence[str]):
|
||||
scores = {
|
||||
")": 1,
|
||||
"]": 2,
|
||||
"}": 3,
|
||||
">": 4,
|
||||
}
|
||||
all_points = []
|
||||
for line in lines:
|
||||
completion = ""
|
||||
try:
|
||||
parser = Parser(line)
|
||||
while not parser.is_eof:
|
||||
parser.parse()
|
||||
except ParseError as e:
|
||||
if e.got != "":
|
||||
continue
|
||||
# Try to complete the parse using exceptions :^)
|
||||
parser.text += e.expected
|
||||
completion += e.expected
|
||||
while True:
|
||||
parser.reset()
|
||||
try:
|
||||
while not parser.is_eof:
|
||||
parser.parse()
|
||||
break
|
||||
except ParseError as e:
|
||||
assert e.expected != ""
|
||||
parser.text += e.expected
|
||||
completion += e.expected
|
||||
# Calculate points using the completion
|
||||
points = 0
|
||||
for c in completion:
|
||||
points *= 5
|
||||
points += scores[c]
|
||||
|
||||
all_points += [points]
|
||||
all_points = sorted(all_points)
|
||||
print(all_points[len(all_points) // 2])
|
||||
|
||||
|
||||
lines = list(map(str.strip, sys.stdin))
|
||||
|
||||
print("Part 1")
|
||||
part1(lines)
|
||||
print("Part 2")
|
||||
part2(lines)
|
||||
Reference in New Issue
Block a user