1
0
aoc-2020/16/part2.py

84 lines
2.6 KiB
Python

import re
def getInput(filename):
with open(filename, "r") as f:
fields, myTicket, nearbyTickets = f.read().split("\n\n")
myTicket = myTicket.replace("your ticket:\n", "")
nearbyTickets = nearbyTickets.replace("nearby tickets:\n", "")
return (
{m[1]: m[2] for m in re.finditer(r"(.+): ([^\n]+)", fields)},
[int(n) for n in myTicket.split(",")],
[[int(n) for n in ticket.split(",") ] for ticket in nearbyTickets.splitlines()]
)
class FieldRule:
def __init__(self, raw: str):
self.raw = raw
match = re.match(r"(\d+)\-(\d+) or (\d+)\-(\d+)", raw)
assert match
self.range1 = [int(match[1]), int(match[2])]
self.range2 = [int(match[3]), int(match[4])]
def __call__(self, num: int):
return (self.range1[0] <= num <= self.range1[1]) or \
(self.range2[0] <= num <= self.range2[1])
def __repr__(self):
return f'FieldRule("{self.raw}")'
def isFieldValid(tickets, fieldRule, pos):
for ticket in tickets:
if not fieldRule(ticket[pos]):
return False
return True
# A backtracking algorithm to find the field order
def solveFieldOrder(tickets, fields: dict):
fieldOrder = {}
numOfFields = len(fields)
unassignedFields = fields.copy()
while len(fieldOrder) < numOfFields:
possibleFields = {}
for fieldName, fieldRule in unassignedFields.items():
for i in range(numOfFields):
if i not in fieldOrder.values() and isFieldValid(tickets, fieldRule, i):
possibleFields[fieldName] = possibleFields.get(fieldName, [])
possibleFields[fieldName].append(i)
for fieldName, orders in possibleFields.items():
if len(orders) == 1:
fieldOrder[fieldName] = orders[0]
del unassignedFields[fieldName];
return fieldOrder
fields, myTicket, nearbyTickets = getInput("input.txt")
# Preproccess the ticket so that checking is faster
for fieldName, fieldRule in fields.items():
fields[fieldName] = FieldRule(fieldRule)
# Filter out the invalid tickets
validNearbyTickets = []
for ticket in nearbyTickets:
isTicketValid = True
for num in ticket:
if all(not rule(num) for rule in fields.values()): # type: ignore
isTicketValid = False
break
if isTicketValid:
validNearbyTickets.append(ticket)
# Find combination of fields that is valid
fieldOrder = solveFieldOrder(validNearbyTickets, fields)
solution = 1
for fieldName, order in fieldOrder.items():
if fieldName.startswith("departure"):
solution *= myTicket[order]
print(solution)