Advent of Code 2024-Day 5: Print Queue
from advent import parse, ints, mapt, defaultdict, cmp_to_key
rules, updates = parse(5, lambda s: mapt(ints, str.split(s)), sep='\n\n')
order_dict = defaultdict(set)
for p1, p2 in rules: order_dict[p1].add(p2)
is_ordered = lambda t: list(t) == sort_pages(t)
middle = lambda t: t[len(t) // 2]
sort_pages = lambda t: sorted(t, key=cmp_to_key(lambda a, b: -1 if b in order_dict[a] else 1))
assert sum(middle(t) for t in updates if is_ordered(t)) == 6949 # Part 1
assert sum(middle(sort_pages(t)) for t in updates if not is_ordered(t)) == 4145 # Part 2
Parsing
Today's input comes in two parts. First, a list of rules of the form:
Each rule consists of a pair of numbers (before, after) which
indicate page before must come before page after when printed.
Second, a lilst of updates of the form:
78,18,57,52,59,14,87,53,15,28,94
55,88,31,49,93,59,53,13,46,57,86,43,15,18,78,94,52,27,14
33,19,35,67,62,21,47
49,93,59,53,13,64,46,57,15,94,27,96,69
47,31,57,86,78,14,37
...
Each update is a list of pages to be printed.
Our parse() function accepts a separator, sep, argument. Specifying sep='\n\n' will split the input into
two tuples, one for rules, one for updates. Each of the two sections will be a single string with newline characters.
We want a tuple of integers for each rule and update, so we first split into separate lines, and then map the ints
function over each line to obtain a tuple of integers:
Prerequisites
The key element of today's puzzle is to be able to properly order the pages in an update. To do this, we need a predicate to indicate whether one page is less than another page. To represent the ordering information, we'll create a dictionary where the key is a page number, and the value is a set of page numbers that must come after the key page number.
To simplify the logic of dealing with the first addition to the set vs. subsequent additions, we'll
use a defaultdict(set). This will allow us to add the first page to the set without having to first
create the set. Then we load the dictionary from the rules:
Next we need a function to sort pages according to the ordering information in the dictionary.
Python's sorted() function accepts a key, but we want to use a comparator i.e.
a predicate that accepts two pages and indicates if the first is less than the second.
Fortunately, functools.cmp_to_key() can convert a comparator into a key function:
With that in place, we can create an is_ordered() predicate which simply compares
the original tuple to the sorted tuple:
We'll need another helper to return the middle page of a tuple:
Part 1
With the prerequisites in place, part 1 is simple. Generate & sum() the middle page for each
ordered update:
Part 2
For part 2, we need to fix the unordered updates by sorting them. Part 2 is very similar to part 1, except we negate the filter, and we sum the middle of the sorted pages:
New Python Concepts
- 'defaultdict`
//divfloor operatorfunctools.cmp_to_key()str.split()
Conclusion
Python has been very productive for Advent of Code thus far. I'm looking forward to the remaining puzzles!