Solution to problem 16 part 2 in Python
This commit is contained in:
133
src/P16.py
133
src/P16.py
@@ -92,7 +92,7 @@ def part_1() -> None:
|
|||||||
rules.append([name, valid_numbers])
|
rules.append([name, valid_numbers])
|
||||||
|
|
||||||
# Create a single set with all rules
|
# Create a single set with all rules
|
||||||
all_rules: Set[int] = set()
|
all_rules = set()
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
all_rules |= rule[1]
|
all_rules |= rule[1]
|
||||||
|
|
||||||
@@ -110,5 +110,136 @@ def part_1() -> None:
|
|||||||
print(f"The ticket scanning error rate is {ticket_scanning_error_rate}")
|
print(f"The ticket scanning error rate is {ticket_scanning_error_rate}")
|
||||||
|
|
||||||
|
|
||||||
|
# --- Part Two ---
|
||||||
|
|
||||||
|
# Now that you've identified which tickets contain invalid values, discard
|
||||||
|
# those tickets entirely. Use the remaining valid tickets to determine which
|
||||||
|
# field is which.
|
||||||
|
|
||||||
|
# Using the valid ranges for each field, determine what order the fields appear
|
||||||
|
# on the tickets. The order is consistent between all tickets: if seat is the
|
||||||
|
# third field, it is the third field on every ticket, including your ticket.
|
||||||
|
|
||||||
|
# For example, suppose you have the following notes:
|
||||||
|
|
||||||
|
# class: 0-1 or 4-19
|
||||||
|
# row: 0-5 or 8-19
|
||||||
|
# seat: 0-13 or 16-19
|
||||||
|
|
||||||
|
# your ticket:
|
||||||
|
# 11,12,13
|
||||||
|
|
||||||
|
# nearby tickets:
|
||||||
|
# 3,9,18
|
||||||
|
# 15,1,5
|
||||||
|
# 5,14,9
|
||||||
|
|
||||||
|
# Based on the nearby tickets in the above example, the first position must be
|
||||||
|
# row, the second position must be class, and the third position must be seat;
|
||||||
|
# you can conclude that in your ticket, class is 12, row is 11, and seat is 13.
|
||||||
|
|
||||||
|
# Once you work out which field is which, look for the six fields on your
|
||||||
|
# ticket that start with the word departure. What do you get if you multiply
|
||||||
|
# those six values together?
|
||||||
|
|
||||||
|
|
||||||
|
def from_part_1():
|
||||||
|
# from part_1
|
||||||
|
rules = []
|
||||||
|
for rule in rules_raw:
|
||||||
|
name = rule.split(": ")[0]
|
||||||
|
valid_numbers = set()
|
||||||
|
for range_numbers in rule.split(": ")[1].split(" or "):
|
||||||
|
numbers = [int(number) for number in range_numbers.split("-")]
|
||||||
|
first, last = range(*numbers).start, range(*numbers).stop
|
||||||
|
list_numbers = list(range(first, last + 1))
|
||||||
|
for number in list_numbers:
|
||||||
|
valid_numbers.add(number)
|
||||||
|
rules.append([name, valid_numbers])
|
||||||
|
|
||||||
|
all_rules = set()
|
||||||
|
for rule in rules:
|
||||||
|
all_rules |= rule[1]
|
||||||
|
|
||||||
|
return rules, all_rules
|
||||||
|
|
||||||
|
|
||||||
|
def remove_invalid(all_rules):
|
||||||
|
valid_tickets = []
|
||||||
|
for line in nearby_tickets[1:]:
|
||||||
|
valid = True
|
||||||
|
for ticket in [int(t) for t in line.split(",")]:
|
||||||
|
if ticket not in all_rules:
|
||||||
|
valid = False
|
||||||
|
break
|
||||||
|
if valid:
|
||||||
|
valid_tickets.append(line)
|
||||||
|
return valid_tickets
|
||||||
|
|
||||||
|
|
||||||
|
def check_validity(rules, ticket_values):
|
||||||
|
# check validity of sets
|
||||||
|
possibles = []
|
||||||
|
for c in rules:
|
||||||
|
possibles.append([c[0], []])
|
||||||
|
for idx, ticket in enumerate(ticket_values):
|
||||||
|
valid = True
|
||||||
|
for value in ticket:
|
||||||
|
if value not in c[1]:
|
||||||
|
valid = False
|
||||||
|
break
|
||||||
|
if valid:
|
||||||
|
possibles[-1][1].append(idx)
|
||||||
|
return possibles
|
||||||
|
|
||||||
|
|
||||||
|
def get_ordered_fields(sorted_possibles):
|
||||||
|
# get each field and its respective order
|
||||||
|
ordered_fields = []
|
||||||
|
for idx, (field, values) in enumerate(sorted_possibles):
|
||||||
|
# first field
|
||||||
|
if len(values) == 1:
|
||||||
|
ordered_fields.append([field, values[0]])
|
||||||
|
else:
|
||||||
|
value = [
|
||||||
|
v for v in values if v not in sorted_possibles[idx - 1][1]
|
||||||
|
][0]
|
||||||
|
ordered_fields.append([field, value])
|
||||||
|
return ordered_fields
|
||||||
|
|
||||||
|
|
||||||
|
def part_2() -> None:
|
||||||
|
rules, all_rules = from_part_1()
|
||||||
|
|
||||||
|
valid_tickets = remove_invalid(all_rules)
|
||||||
|
|
||||||
|
# collect 20 sets of all respective values
|
||||||
|
ticket_values = [set() for x in range(len(rules))]
|
||||||
|
for line in valid_tickets:
|
||||||
|
index = 0
|
||||||
|
for element in line.split(","):
|
||||||
|
ticket_values[index].add(int(element))
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
possibles = check_validity(rules, ticket_values)
|
||||||
|
|
||||||
|
# sort possibles by the length of values each set holds
|
||||||
|
sorted_possibles = sorted(possibles, key=lambda l: (len(l[1]), l))
|
||||||
|
|
||||||
|
sorted_fields = get_ordered_fields(sorted_possibles)
|
||||||
|
|
||||||
|
# parse "my ticket"
|
||||||
|
myticket = [
|
||||||
|
int(v) for elements in my_ticket[1:] for v in elements.split(",")
|
||||||
|
]
|
||||||
|
|
||||||
|
res = 1
|
||||||
|
for (field, idx) in sorted_fields:
|
||||||
|
if field.startswith("departure"):
|
||||||
|
res *= myticket[idx]
|
||||||
|
print(f"The final result is {res}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
part_1()
|
part_1()
|
||||||
|
part_2()
|
||||||
|
|||||||
Reference in New Issue
Block a user