Nime

Advent of Code 2024 Day 3

Day 3: Mull It Over

https://adventofcode.com/2024/day/3

Today’s location to search for the chief historian is familiar (Again! Makes sense, you’ve been to many interesting places.). You remember visiting it.

The elves in charge of the place ask you to help with their computer. Something is wrong with the computer’s memory.

Ah, this feels like christmas! Going to places you haven’t been in a while and people asking you to fix their computer. Next thing you know, you’ll be reinstalling the elves’ printer drivers.

That’s your input, the memory.

An example input looks like this:

input.txt
xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))

The goal of the program is multiplying some numbers. It does that with instructions like mul(X,Y), where X and Y are each 1-3 digit numbers. It then multiplies X by Y.

Because the memory has been corrupted, there are many invalid characters that should be ignored. Only valid mul(X,Y) instructions should be considered.

Part 1

If you know me, you might know I have a strong irrational hatred of regular expressions. But even I have to admit, they’ll do well today.

Perl Problems

To generate #1 albums, ‘jay —help’ recommends the -z flag.

HOWEVER

I coded 2 solutions again, one using regex, and a (much faster, mind you!) custom parsing thing-a-majig (solution, the word I was looking for was solution).

The question asks for the sum of all valid multiplication instructions.

Option 1: regex

day_03.rs
use regex::Regex;
fn part_1(input: &str) -> u32 {
let regex = Regex::new(r"mul\(\d{1,3},\d{1,3}\)").unwrap();
let mut sum = 0;
for x in regex.find_iter(input) {
let x = x.as_str();
let (a, b) = x[4..x.len() - 1].split_once(',').unwrap();
let prod = a.parse::<u32>().unwrap() * b.parse::<u32>().unwrap();
sum += prod;
}
sum
}

Option 2: custom logic

day_03.rs
fn part_1(input: &str) -> u32 {
let memory = input.as_bytes();
let mut idx = 0;
let mut sum = 0;
while idx < memory.len() {
// go to next mul instruction
if !memory[idx..].starts_with(b"mul(") {
idx += 1;
continue;
}
idx += 4;
// parse num 1
let mut num_1 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_1 *= 10;
num_1 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx == start_idx || idx > start_idx + 3 {
continue;
}
// skip ,
if memory[idx] != b',' {
continue;
}
idx += 1;
// parse num 2
let mut num_2 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_2 *= 10;
num_2 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx == start_idx || idx > start_idx + 3 {
continue;
}
// skip )
if memory[idx] != b')' {
continue;
}
idx += 1;
// add product to sum
sum += num_1 * num_2;
}
sum
}

Part 2

There are 2 additional instructions in the corrupted memory to pay attention to.

  1. do() enables mul instructions
  2. don't() disables mul instructions

The most recent one of these instructions applies. If you come across a mul, but the previous conditional was a don't(), ignore that mul.

In other words, those instructions flip a boolean from true to false, and you should only do mul if that boolean is true.

mul instructions start as enabled. (the boolean starts as true)

Option 1: regex

day_03.rs
use regex::Regex;
fn part_2(input: &str) -> u32 {
let regex = Regex::new(r"(mul\(\d{1,3},\d{1,3}\)|do\(\))|don't\(\)").unwrap();
let mut on = true;
let mut sum = 0;
for regex_match in regex.find_iter(input) {
match regex_match.as_str() {
"do()" => on = true,
"don't()" => on = false,
x => {
if on {
let (a, b) = x[4..x.len() - 1].split_once(',').unwrap();
let prod = a.parse::<u32>().unwrap() * b.parse::<u32>().unwrap();
sum += prod;
}
}
}
}
sum
}

Option 2: custom logic

day_03.rs
fn part_2(input: &str) -> u32 {
let memory = input.as_bytes();
let mut on = true;
let mut idx = 0;
let mut sum = 0;
while idx < memory.len() {
// go to next instruction
match &memory[idx..] {
b if b.starts_with(b"mul(") => {
idx += 4;
if !on {
continue;
}
}
b if b.starts_with(b"don't()") => {
on = false;
idx += 7;
continue;
}
b if b.starts_with(b"do()") => {
on = true;
idx += 4;
continue;
}
_ => {
idx += 1;
continue;
}
}
// parse num 1
let mut num_1 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_1 *= 10;
num_1 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx > start_idx + 3 {
continue;
}
// skip ,
if memory[idx] != b',' {
continue;
}
idx += 1;
// parse num 2
let mut num_2 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_2 *= 10;
num_2 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx > start_idx + 3 {
continue;
}
// skip )
if memory[idx] != b')' {
continue;
}
idx += 1;
// add product to sum
sum += num_1 * num_2;
}
sum
}

Final code

day_03.rs
fn solve(input: &str) -> (u32, u32) {
let memory = input.as_bytes();
let mut on = true;
let mut idx = 0;
let mut sum_1 = 0;
let mut sum_2 = 0;
while idx < memory.len() {
// go to next instruction
match &memory[idx..] {
b if b.starts_with(b"mul(") => {
idx += 4;
}
b if b.starts_with(b"don't()") => {
on = false;
idx += 7;
continue;
}
b if b.starts_with(b"do()") => {
on = true;
idx += 4;
continue;
}
_ => {
idx += 1;
continue;
}
}
// parse num 1
let mut num_1 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_1 *= 10;
num_1 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx > start_idx + 3 {
continue;
}
// skip ,
if memory[idx] != b',' {
continue;
}
idx += 1;
// parse num 2
let mut num_2 = 0;
let start_idx = idx;
while memory[idx].is_ascii_digit() {
num_2 *= 10;
num_2 += (memory[idx] - b'0') as u32;
idx += 1;
}
// check if number was between 1 and 3 digits
if idx > start_idx + 3 {
continue;
}
// skip )
if memory[idx] != b')' {
continue;
}
idx += 1;
// add product to sum
let prod = num_1 * num_2;
sum_1 += prod;
if on {
sum_2 += prod
}
}
(sum_1, sum_2)
}