diff --git a/day16/day16.py b/day16/day16.py index cfa1b10..bebb6e0 100644 --- a/day16/day16.py +++ b/day16/day16.py @@ -8,11 +8,12 @@ identify invalid nearby tickets by considering only whether tickets contain valu def main(): - rules, my_ticket, tickets = open("input.txt").read().split("\n\n") + rules, my_ticket, other_tickets = open("input.txt").read().split("\n\n") rules = parse_fields(rules) - tickets = tickets.splitlines()[1:] - print("Ticket scanning error rate ", part1(tickets, rules)) - part2(tickets, rules) + my_ticket = my_ticket.splitlines()[1] + other_tickets = other_tickets.splitlines()[1:] + print("Ticket scanning error rate ", part1(other_tickets, rules)) + part2(my_ticket, other_tickets, rules) def parse_fields(fields): @@ -35,31 +36,68 @@ def validate_ticket(ticket, rules): invalid_fields = [] for value in ticket.split(","): value = int(value) - validations = (any(value in r for r in rule) for rule in rules.values()) - if not any(validations): + if not validate_field(value, *rules.values()): invalid_fields.append(value) return invalid_fields -def part2(tickets, rules): - # filter only valid tickets - valid_tickets = [ticket for ticket in tickets if validate_ticket(ticket, rules) == []] +def validate_field(field, *rules): + validations = (any(field in r for r in rule) for rule in rules) + return any(validations) - # field => [matching_rules] - # eliminate field with only one matching rule - all_matching_rules = {} - for ticket in valid_tickets: - possible_matching_rules = defaultdict(set) - for field_index, field_value in enumerate(ticket.split(",")): - field_value = int(field_value) - for name, rule in rules.items(): - match = {r: field_value in r for r in rule} - if any(match.values()): - possible_matching_rules[name].add(field_index) - all_matching_rules[ticket]= possible_matching_rules - a = 1 + +def part2(my_ticket, other_tickets, rules): + # filter only valid tickets + valid_tickets = [ticket for ticket in other_tickets if validate_ticket(ticket, rules) == []] + valid_tickets.append(my_ticket) # my ticket is valid + + # possible field for each index of a ticket + candidates = defaultdict(set) + for index in range(len(rules)): + def inner(): + for rule_name, constraints in rules.items(): + for ticket in valid_tickets: + field_value = int(ticket.split(",")[index]) + if not validate_field(field_value, constraints): + return + candidates[index].add(rule_name) + inner() - # repeat + sorted_candidates = sort_candidates(candidates) + + fields_indexes = {} + try: + while len(fields_indexes) != len(rules): + index, found = sorted_candidates.popitem() + found = next(iter(found)) + fields_indexes[index] = found + sorted_candidates = remove_item(sorted_candidates, found) + except: + pass + + fields_indexes = {k: v for k,v in fields_indexes.items() if v.startswith('departure')} + + total = 1 + my_ticket = my_ticket.split(',') + for index in fields_indexes: + total *= int(my_ticket[index]) + a = 1 + + +def sort_candidates(c): + return {x: c[x] for x in sorted(c, key=lambda k: len(c[k]), reverse=True)} + +def remove_item(candidates, item): + ret = {} + for key, value in candidates.items(): + try: + value.remove(item) + except ValueError: + pass + ret[key] = value + + #candidates = {k: set(v - item) for k,v in candidates.items()} + return ret