mirror of
https://github.com/cupcakearmy/advent-of-code.git
synced 2024-12-30 11:56:25 +00:00
day 10
This commit is contained in:
parent
a4f671a6cc
commit
1a427e643d
14
2021/10/README.md
Normal file
14
2021/10/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# 10
|
||||
|
||||
A parser! what a joy :)
|
||||
|
||||
Basically a stack machine wrapped in some boilerplate.
|
||||
A neat trick for part 2 is to simply reverse the order of the stack and count that instead of actually adding the rest.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>392097</li>
|
||||
<li>4263222782</li>
|
||||
</ol>
|
||||
</details>
|
162
2021/10/python/main.py
Normal file
162
2021/10/python/main.py
Normal file
@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from os.path import dirname, join
|
||||
|
||||
# Day 10
|
||||
|
||||
# Common
|
||||
|
||||
|
||||
def log(str):
|
||||
if False:
|
||||
print(str)
|
||||
|
||||
|
||||
def read_input(filename):
|
||||
data = join(dirname(__file__), '..', filename)
|
||||
with open(data) as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
test = read_input('test.txt')
|
||||
data = read_input('input.txt')
|
||||
|
||||
# 1
|
||||
print('1.')
|
||||
|
||||
|
||||
class ResultType(Enum):
|
||||
Valid = 1
|
||||
Incomplete = 2
|
||||
Corrupted = 3
|
||||
|
||||
|
||||
def empty_list() -> list[str]:
|
||||
return []
|
||||
|
||||
|
||||
@dataclass
|
||||
class ParseResult:
|
||||
type: ResultType = ResultType.Valid
|
||||
stack: list[str] = field(default_factory=empty_list)
|
||||
expected: str = ''
|
||||
found: str = ''
|
||||
|
||||
|
||||
class NavigationSubsystemLineParser():
|
||||
pairs = {
|
||||
'(': ')',
|
||||
'[': ']',
|
||||
'{': '}',
|
||||
'<': '>'
|
||||
}
|
||||
|
||||
def __init__(self, line: str):
|
||||
self.line = line
|
||||
self.stack: list[str] = []
|
||||
self.position = 0
|
||||
|
||||
def _allowed_chars(self):
|
||||
allowed = list(self.pairs.keys())
|
||||
if len(self.stack):
|
||||
value = self.stack[-1]
|
||||
if value in self.pairs:
|
||||
allowed.append(self.pairs[value])
|
||||
return allowed
|
||||
|
||||
def validate(self) -> ParseResult:
|
||||
"""
|
||||
Returns -1 for incomplete, 0 for valid, or the position of the first invalid character
|
||||
"""
|
||||
log(f'Validating {self.line}')
|
||||
while self.position < len(self.line):
|
||||
expected = self._allowed_chars()
|
||||
char = self.line[self.position]
|
||||
is_opening = char in self.pairs
|
||||
if is_opening:
|
||||
self.stack.append(char)
|
||||
else:
|
||||
expected = self.pairs[self.stack[-1]]
|
||||
if char != expected:
|
||||
log(f'Invalid char {char} at position {self.position}. Expected {expected}')
|
||||
return ParseResult(ResultType.Corrupted, expected=expected, found=char)
|
||||
else:
|
||||
self.stack.pop()
|
||||
|
||||
self.position += 1
|
||||
incomplete = len(self.stack) > 0
|
||||
return ParseResult(type=ResultType.Incomplete if incomplete else ResultType.Valid, stack=self.stack)
|
||||
|
||||
|
||||
class NavigationSubsystem():
|
||||
|
||||
def __init__(self, data: list[str]):
|
||||
self.data = data
|
||||
|
||||
def validate(self) -> list[ParseResult]:
|
||||
return [NavigationSubsystemLineParser(line).validate() for line in self.data]
|
||||
|
||||
def count_corrupted(self) -> int:
|
||||
results = self.validate()
|
||||
for result in results:
|
||||
log(result)
|
||||
points_map = {
|
||||
')': 3,
|
||||
']': 57,
|
||||
'}': 1197,
|
||||
'>': 25137
|
||||
}
|
||||
points = [points_map[result.found] for result in results if result.type == ResultType.Corrupted]
|
||||
return sum(points)
|
||||
|
||||
def _calculate_autocomplete_score(self, stack: list[str]) -> int:
|
||||
points_map = {
|
||||
'(': 1,
|
||||
'[': 2,
|
||||
'{': 3,
|
||||
'<': 4
|
||||
}
|
||||
stack.reverse()
|
||||
total = 0
|
||||
for char in stack:
|
||||
total *= 5
|
||||
total += points_map[char]
|
||||
return total
|
||||
|
||||
def autocomplete(self) -> int:
|
||||
results = [NavigationSubsystemLineParser(line).validate() for line in self.data]
|
||||
points = [
|
||||
self._calculate_autocomplete_score(result.stack)
|
||||
for result
|
||||
in results
|
||||
if result.type == ResultType.Incomplete
|
||||
]
|
||||
points.sort()
|
||||
log(points)
|
||||
return points[len(points)//2]
|
||||
|
||||
@staticmethod
|
||||
def parse(data: str):
|
||||
return NavigationSubsystem(data.splitlines())
|
||||
|
||||
|
||||
parser = NavigationSubsystem.parse(test)
|
||||
points = parser.count_corrupted()
|
||||
print(points)
|
||||
|
||||
parser = NavigationSubsystem.parse(data)
|
||||
points = parser.count_corrupted()
|
||||
print(points)
|
||||
|
||||
# 2
|
||||
print('\n2.')
|
||||
|
||||
parser = NavigationSubsystem.parse(test)
|
||||
points = parser.autocomplete()
|
||||
print(points)
|
||||
|
||||
parser = NavigationSubsystem.parse(data)
|
||||
points = parser.autocomplete()
|
||||
print(points)
|
2
2021/10/python/tempCodeRunnerFile.py
Normal file
2
2021/10/python/tempCodeRunnerFile.py
Normal file
@ -0,0 +1,2 @@
|
||||
# while self.position < len(self.line):
|
||||
# pass
|
Loading…
Reference in New Issue
Block a user