Advent of Code 2024, Day 3
Day 3 complete with two gold stars, lots of one liners, regex use, and some string concatenation.
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:
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 lastdon't
block in the filedon'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