96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
# SPDX-License-Identifier: MIT
|
|
# Copyright (c) 2020 Akumatic
|
|
#
|
|
#https://adventofcode.com/2020/day/16
|
|
|
|
def parseInput(blocks: list) -> dict:
|
|
data = {"rules": dict(), "you": list(), "tickets": list()}
|
|
# 1st block, rules
|
|
for rule in blocks[0]:
|
|
rule = rule.split(": ")
|
|
data["rules"][rule[0]] = []
|
|
vals = [int(n) for r in rule[1].split(" or ") for n in r.split("-")]
|
|
for i in range(0, len(vals), 2):
|
|
data["rules"][rule[0]].append(range(vals[i], vals[i+1] + 1))
|
|
# 2nd block, your ticket
|
|
data["you"] += [int(num) for num in blocks[1][1].split(",")]
|
|
# 3rd block, nearby tickets
|
|
for ticket in blocks[2][1:]:
|
|
data["tickets"].append([int(num) for num in ticket.split(",")])
|
|
return data
|
|
|
|
def readFile() -> dict:
|
|
with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f:
|
|
blocks = [block.split("\n") for block in f.read().strip().split("\n\n")]
|
|
return parseInput(blocks)
|
|
|
|
def check_rule(rule: list, number: int) -> bool:
|
|
return any([number in r for r in rule])
|
|
|
|
def check_rules(rules: dict, number: int) -> bool:
|
|
for rule in rules:
|
|
if check_rule(rules[rule], number):
|
|
return True
|
|
return False
|
|
|
|
def validate_ticket(rules: dict, ticket: list) -> bool:
|
|
for number in ticket:
|
|
if not check_rules(rules, number):
|
|
return False
|
|
return True
|
|
|
|
def get_valid_tickets(data: dict) -> list:
|
|
valid_tickets = [data["you"]]
|
|
for ticket in data["tickets"]:
|
|
if validate_ticket(data["rules"], ticket):
|
|
valid_tickets.append(ticket)
|
|
return valid_tickets
|
|
|
|
def get_possible_positions(rules: dict, valid_tickets: list) -> dict:
|
|
possible_positions = dict()
|
|
size = len(rules)
|
|
for rule in rules:
|
|
pos = []
|
|
for i in range(size):
|
|
if all([check_rule(rules[rule], t[i]) for t in valid_tickets]):
|
|
pos.append(i)
|
|
possible_positions[rule] = pos
|
|
return possible_positions
|
|
|
|
def determine_positions(possible_positions: dict) -> dict:
|
|
positions = dict()
|
|
keys = list(possible_positions.keys())
|
|
while keys:
|
|
for key in keys:
|
|
if len(possible_positions[key]) == 1:
|
|
positions[key] = possible_positions[key][0]
|
|
keys.remove(key)
|
|
for pos in keys:
|
|
if positions[key] in possible_positions[pos]:
|
|
possible_positions[pos].remove(positions[key])
|
|
continue
|
|
return positions
|
|
|
|
def part1(data: dict) -> int:
|
|
error_rate = 0
|
|
for ticket in data["tickets"]:
|
|
for number in ticket:
|
|
if not check_rules(data["rules"], number):
|
|
error_rate += number
|
|
return error_rate
|
|
|
|
def part2(data: dict) -> int:
|
|
valid_tickets = get_valid_tickets(data)
|
|
possible_positions = get_possible_positions(data["rules"], valid_tickets)
|
|
positions = determine_positions(possible_positions)
|
|
|
|
result = 1
|
|
for rule in positions:
|
|
if rule.startswith("departure"):
|
|
result *= data["you"][positions[rule]]
|
|
return result
|
|
|
|
if __name__ == "__main__":
|
|
input = readFile()
|
|
print(f"Part 1: {part1(input)}")
|
|
print(f"Part 2: {part2(input)}") |