mirror of
https://github.com/Crocmagnon/advent-of-code.git
synced 2024-11-05 14:23:58 +01:00
94 lines
2.6 KiB
Python
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)
|