Advent of Code 2024, Day 5
HashMap in Rust to the rescue! Along with lazy iterators, maps of maps of whatevers, and two gold stars!
I was busy today, and I bumped into a bunch of weird (to me) things in Rust, so it’s taken me some more time than I would have liked.
Today’s exercise was brought to you by HashMap
!
Links, and as always, spoilers ahoy!
Spoilers
Part 1
There are two parts to this exercise, the first bit is just a general list of ordering rules that look like this:
12|55
94|74
74|21
Means page 12 comes before 55, 94 comes before 74, 74 comes before 21. It’s a fairly straightfoward format, and I decided the best is to parse it into a HashMap so I can look up any two numbers: “what’s the relation of X to Y?”
The HashMap looks somewhat like this:
[
12:[
55: After
],
55:[
12: Before
],
94:[
74: After
],
74:[
94: Before,
21: After
],
21:[
74: Before
]
]
That way I can check any two numbers, and get either After
, Before
, or Unknown
, which I use as the default. Unknowns do not fail a validity.
Then I parse each individual line and build up an “in this line the numbers are organised such” HashMap, which somewhat looks like this:
// 1,2,3,4
[
1:[
2: After,
3: After,
4: After
],
2:[
1: Before,
3: After,
4: After
],
3:[
1: Before,
2: Before,
4: After
],
4:[
1: Before,
2: Before,
3: Before
]
]
And then for each line I parse the line into this format, and then compare each pair from the actual line to the rules I have, and if the rule exists (as in, not Unknown
for that pair), and the order is different, it’s a fail.
Plus another tiny function to grab the middle number for the sums. Anyhow, tests helped.
A new thing I learned this time though is that any iterator will evaluate lazily. That means I can’t use it in lieu of a for loop:
for line in data.lines() {
// this will always evaluate for each line
}
// whereas
let _ = data.lines().map(|line| -> {
// this will _only_ evaluate, if whatever variable I set it to
// is going to be used someplace else
});
Apparently though if I .collect()
, it will consume the iterator, which means it will be used, so yay? Ideally you won’t run into this. Anyhoo, part 2!
Part 2
This part is the hackiest most “lol it’s working, DO NOT TOUCH IT” code I’ve written in a long while.
But basically I’ve done like 95% of the work for part 1 anyways. The idea is that I still parse the rules into the ungodly map of map of whatever, iterate over all the print lines, but this time I only focus on the broken lines, and when I find one, I fix them!
To fix them I completely discard its current order, treat each number as itself, then create a mini-rule map of map of whatever between every pair of that print, then for each number I gather what rules they have, so I end up with a map of vec of orders like this:
1: {After, After, After},
2: {Before, After, After},
3: {Before, Before, After},
4: {Before, Before, Before}
This one means: compared to 1
, all the other numbers are after it. Compared to 3
, two of the other numbers are before it, one of them is after.
Then I translate this into a number: an After
gets a -1, a Before
gets a +1, and an Unknown
gets nothing, and sum them up for each number in the print.
I use those to order the numbers, rearrange the individual numbers into a new print order, then grab its middle number and construct the sum that way.
I didn’t think it would work, but the ⭐️⭐️ tells me I got it right. Yay!