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!

Two notebooks on a table. Text on the top one reads You come first, not second.
Photo by Felicia Buitenwerf on Unsplash

Part of the AoC 2024 series

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!

adventofcode2024/day05 at main · javorszky/adventofcode2024
Contribute to javorszky/adventofcode2024 development by creating an account on GitHub.

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!