Advent of Code 2024, Day 3

Day 3 complete with two gold stars, lots of one liners, regex use, and some string concatenation.

Two light switches on textured wooden wall. One is a double switch.
Photo by Karim MANJRA on Unsplash

Part of the AoC 2024 series

I made something beautiful today! Iā€™m getting more comfy in Rust. Also spoilers ahead, so once I find a different image for a spoiler header, Iā€™ll dive right in! Also the code if you want to look at it:

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

Spoilers

Part 1

This one liner is absolutely hilarious:

impl Day03 {
    pub fn new() -> Day03 {
        Day03{
            muls: Regex::new(REGEX_STRING).unwrap()
                .find_iter(include_str!("../input.txt"))
                .map(|m| m.as_str())
                .collect::<Vec<&str>>()
                .iter()
                .map(|m|  parse_mul(m))
                .collect::<Vec<Mul>>()
        }
    }
}

I love me a language where I can just keep chaining things.

Yes, I know itā€™s riddled with .unwrap(), which will absolutely panic at the first sign of something going wrong, but given advent of code is not a production code project, I want to solve the issue the dumbest way possible that completes in a reasonable time. I also donā€™t need to deal with humans, so them supplying wrong input is not something Iā€™ll run into.

Clippy also enters the chat many times, mostly around ā€œhey, donā€™t forget to specify the type here, here, and here!ā€

And I learned about turbofish! ::<>, which I needed to use in the parse_mul function that turns a mul(333,444) into a struct with two numbers that has a method .product() which gives you their product. It looks like this:

fn parse_mul(input: &str) -> Mul {
    let numbers = input
        .strip_prefix("mul(").unwrap()
        .strip_suffix(")").unwrap()
        .split(",").map(|m|
        m.parse::<i32>().unwrap())
        .collect::<Vec<i32>>();

    Mul::new(numbers[0], numbers[1])
}

Another oneliner! Chefā€™s kiss!

Part 2

Today I learned about regexes in Rust, and string concatenations. I initially had this idea that Iā€™m going to find all of the dos and donā€™t and match them up because I have the starting byte offsets for all of them, but then thought thatā€™s a lot of work dealing with two lists, changing which one is the active one that I should find the next one after in the other list. For example if I am in a do block, then I need to find the next don't tag, and if I happen to be in a don't block, I need to find the next do, and so on.

In the end I brute forced it. Again.

The beginning is always active, so we need from input start until the first don't, which is regex r"^(?s).*?don't\(\)". Parts:

  • r" ... " means that this is a regex string
  • (?s) is a flag, so that the . character also matches \n, given we have a few of those in the file
  • .*? match anything greedily. Without the ? it would try to match as much as possible, so it would go from the beginning to the last don't block in the file
  • don't\(\) thatā€™s the literal thing I want the match to end. Need to escape the ()s

The middle is similarly simple: r"(?s)do\(\).*?don't\(\)". Means ā€œmatch anything between a do() and don't() tag, and stop at the first don't() you find after a do(). This will immediately cover all cases where a do() tag is followed by more do() tags before encountering a closing one.

The end was trickier, but Rustā€™s regex doesnā€™t have negative lookaheads ā€“ i.e. ā€œfind me a do() tag until the end of the input such that there isnā€™t a do() following ahead of youā€ ā€“ so this is where knowing where all the tags are: if the last do() tag has a later offset than the last don't() tag, we know the last tag is a do tag, so we can subslice the remaining file from the offset of the do tag.

And then concatenate them, and run the Mul parser, and do as part 1.

The trick here was that we had to cut out the parts in the input that we donā€™t care about.

And learning about concatenation and why I canā€™t return a &str from any function unless I either passed it in, or itā€™s static, so thereā€™s some &str -> String -> &str juggling going on.

Anyways, got the ā­ļøā­ļø for the day, so Iā€™m happy.

Photo by Cloud Prod on Unsplash