102 lines
3 KiB
Python
102 lines
3 KiB
Python
|
def read_file():
|
||
|
with open("input.txt","r") as f:
|
||
|
groups = f.read().split('\n\n')
|
||
|
rules = groups[0].split('\n')
|
||
|
your_ticket = groups[1].split('\n')[1:]
|
||
|
tickets = groups[2].split('\n')[1:]
|
||
|
return rules, your_ticket, tickets
|
||
|
|
||
|
def find_valid_set(rule):
|
||
|
_, ranges = rule.split(': ')
|
||
|
range1, range2 = ranges.split(' or ')
|
||
|
a, b = map(int,range1.split('-'))
|
||
|
c, d = map(int,range2.split('-'))
|
||
|
set1 = set(range(a,b+1))
|
||
|
set2 = set(range(c,d+1))
|
||
|
return set1 | set2
|
||
|
|
||
|
def find_valid_superset(rules):
|
||
|
valid_superset = set()
|
||
|
for rule in rules:
|
||
|
valid_set = find_valid_set(rule)
|
||
|
valid_superset = valid_superset | valid_set
|
||
|
return valid_superset
|
||
|
|
||
|
def part1(rules, tickets):
|
||
|
"""Add up all values not valid for any range."""
|
||
|
valid_set = find_valid_superset(rules)
|
||
|
error_rate = 0
|
||
|
for ticket in tickets:
|
||
|
numbers = map(int,ticket.split(','))
|
||
|
for number in numbers:
|
||
|
if number not in valid_set:
|
||
|
error_rate += number
|
||
|
return error_rate
|
||
|
|
||
|
def find_valid_tickets(tickets, rules):
|
||
|
valid_tickets = []
|
||
|
valid_set = find_valid_superset(rules)
|
||
|
for ticket in tickets:
|
||
|
numbers = map(int,ticket.split(','))
|
||
|
if all([number in valid_set for number in numbers]):
|
||
|
valid_tickets.append(ticket)
|
||
|
return valid_tickets
|
||
|
|
||
|
def parse_tickets(tickets):
|
||
|
parsed_tickets = []
|
||
|
for ticket in tickets:
|
||
|
numbers = map(int,ticket.split(','))
|
||
|
parsed_ticket = [int(number) for number in numbers]
|
||
|
parsed_tickets.append(parsed_ticket)
|
||
|
return parsed_tickets
|
||
|
|
||
|
def part2(rules, your_ticket, tickets):
|
||
|
"""Find product of 6 "departure" fields."""
|
||
|
valid_tickets = find_valid_tickets(tickets, rules)
|
||
|
valid_tickets += your_ticket
|
||
|
parsed_tickets = parse_tickets(valid_tickets)
|
||
|
your_ticket = parse_tickets(your_ticket)[0]
|
||
|
ticket_length = len(parsed_tickets[0])
|
||
|
ticket_count = len(parsed_tickets)
|
||
|
# convert rules into valid sets
|
||
|
valid_sets = []
|
||
|
for i in range(ticket_length):
|
||
|
valid_set = find_valid_set(rules[i])
|
||
|
valid_sets.append(valid_set)
|
||
|
# correlate rulesets with values
|
||
|
possibilities = []
|
||
|
for i in range(ticket_length):
|
||
|
## get all values at position
|
||
|
values = set()
|
||
|
for ticket in parsed_tickets:
|
||
|
values.add(ticket[i])
|
||
|
## find which rulesets match
|
||
|
possibility = []
|
||
|
for j, valid_set in enumerate(valid_sets):
|
||
|
if all([value in valid_set for value in values]):
|
||
|
possibility.append(1)
|
||
|
else:
|
||
|
possibility.append(0)
|
||
|
possibilities.append(possibility)
|
||
|
# process of elimination to correlate ruleset to index
|
||
|
mapping = {}
|
||
|
for i in range(ticket_length):
|
||
|
sums = list(map(sum, possibilities))
|
||
|
i = sums.index(1) ## find row with only one 1
|
||
|
row = possibilities[i]
|
||
|
j = row.index(1) ## find 1 in that row
|
||
|
mapping[j] = i
|
||
|
for i in range(ticket_length):
|
||
|
possibilities[i][j] = 0
|
||
|
# this bit only works on actual input bc there's no reason to make it robust
|
||
|
prod = 1
|
||
|
for j in range(6):
|
||
|
prod *= your_ticket[mapping[j]]
|
||
|
return prod
|
||
|
|
||
|
def main():
|
||
|
rules, your_ticket, tickets = read_file()
|
||
|
print(part1(rules, tickets), part2(rules, your_ticket, tickets))
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|