advent-of-code/2020/day16_ticket_translation.py

94 lines
2.6 KiB
Python

import re
from functools import lru_cache
from typing import List, Tuple
def main(filename: str, expected_part_1: int = None, expected_part_2: int = None):
print(f"\n+ Running on {filename}")
with open(filename) as f:
blocks = f.read().strip().split("\n\n")
counter_part_1 = solve_part_1(blocks)
print(f"1. Found {counter_part_1}")
if expected_part_1:
assert expected_part_1 == counter_part_1
counter_part_2 = solve_part_2(blocks)
print(f"2. Found {counter_part_2}")
if expected_part_2:
assert expected_part_2 == counter_part_2
Range = Tuple[int, int]
Ranges = List[Range]
def solve_part_1(blocks):
analyser = TicketAnalyserPart1(blocks)
return analyser.get_error_rate()
class TicketAnalyserPart1:
def __init__(self, blocks):
named_ranges = blocks[0].split("\n")
ranges = []
for named_range in named_ranges:
ranges.extend(self.extract_ranges(named_range))
self.ranges = self.merge_ranges(ranges)
self.nearby_tickets = blocks[2].split("\n")[1:]
@staticmethod
def extract_ranges(named_range: str) -> Ranges:
reg = re.compile(r"^.*: (\d+)-(\d+) or (\d+)-(\d+)$")
matches = reg.match(named_range)
groups = [int(group) for group in matches.groups()]
return [(groups[0], groups[1]), (groups[2], groups[3])]
@staticmethod
def merge_ranges(times) -> Ranges:
ranges = []
saved = list(times[0])
for st, en in sorted([sorted(t) for t in times]):
if st <= saved[1]:
saved[1] = max(saved[1], en)
else:
ranges.append(tuple(saved))
saved[0] = st
saved[1] = en
ranges.append(tuple(saved))
return ranges
def get_error_rate(self):
error_rate = 0
for ticket in self.nearby_tickets:
error_rate += sum(self.get_invalid_values(ticket))
return error_rate
def get_invalid_values(self, ticket: str) -> List[int]:
ticket = map(int, ticket.split(","))
invalid_values = []
for value in ticket:
if self.value_is_invalid(value):
invalid_values.append(value)
return invalid_values
@lru_cache(None)
def value_is_invalid(self, value: int) -> bool:
return not self.value_is_valid(value)
def value_is_valid(self, value: int) -> bool:
for rng in self.ranges:
if value in range(rng[0], rng[1] + 1):
return True
return False
def solve_part_2(blocks):
return 0
if __name__ == "__main__":
main("inputs/day16-test1", 71)
main("inputs/day16", 32835)