51 lines
No EOL
1.7 KiB
Python
51 lines
No EOL
1.7 KiB
Python
from typing import List, Tuple
|
|
|
|
class Rule:
|
|
name: str
|
|
ranges: Tuple[int, ...]
|
|
|
|
def __init__(self, instr:str) -> None:
|
|
# arrival location: 26-482 or 504-959
|
|
field, conditions = instr.strip().split(": ")
|
|
self.name = field
|
|
ranges = conditions.split("or")
|
|
self.ranges = tuple([int(x) for x in ranges[0].strip().split("-")] + [int(x) for x in ranges[1].strip().split("-")])
|
|
|
|
class Ticket:
|
|
fields: Tuple[int]
|
|
|
|
def __init__(self, instr:str) -> None:
|
|
self.fields = tuple([int(x) for x in instr.strip().split(",")])
|
|
|
|
def parse(instr: str) -> Tuple[List[Rule], Ticket, List[Ticket]]:
|
|
|
|
raw_rules, raw_my_ticket, raw_other_tickets = instr.strip().split("\n\n")
|
|
|
|
rules = [Rule(x) for x in raw_rules.split("\n")]
|
|
|
|
my_ticket = Ticket(raw_my_ticket.split("\n")[-1])
|
|
|
|
other_tickets = [Ticket(x) for x in raw_other_tickets.strip("nearby tickets:\n").split("\n")]
|
|
|
|
return rules, my_ticket, other_tickets
|
|
|
|
def test_value(value:int, condition:Tuple[int, ...]) -> bool:
|
|
return condition[0] <= value <= condition[1] or condition[2] <= value <= condition[3]
|
|
|
|
def find_invalid(rules:List[Rule], tickets:List[Ticket]) -> Tuple[List[int], List[int]]:
|
|
# returns invalid values and indexes of invalid tickets
|
|
|
|
invalid_values = []
|
|
invalid_indexes = []
|
|
|
|
for i, ticket in enumerate(tickets):
|
|
for field in ticket.fields:
|
|
field_is_valid = False
|
|
for rule in rules:
|
|
if test_value(field, rule.ranges):
|
|
field_is_valid = True
|
|
if not field_is_valid:
|
|
invalid_values.append(field)
|
|
invalid_indexes.append(i)
|
|
|
|
return invalid_values, invalid_indexes |