AdventOfCode/y2024/src/days/d3.rs
2024-12-03 12:53:58 +01:00

129 lines
3.7 KiB
Rust

use regex::Regex;
pub fn process_part1(input: &str) -> i32 {
input
.lines()
.map(extract_mul_pairs)
.map(|pairs| pairs.iter().map(|(a, b)| a * b).sum::<i32>())
.sum()
}
// Could use this regex but wouldn't know how to solve part 2
// /mul\([0-9]{1,3},[0-9]{1,3}\)/g
// /don't\(\).*do\(\)/g would select anything between first don't() and last do()
// not sure how to fix, invert and combine this
fn extract_mul_pairs(line: &str) -> Vec<(i32, i32)> {
let reg = Regex::new(r"mul\([0-9]{1,3},[0-9]{1,3}\)").unwrap();
reg.find_iter(line)
.map(|m| {
let match_str = m.as_str();
let (a, b) = match_str[4..match_str.len() - 1].split_once(",").unwrap();
let a = a.parse::<i32>().unwrap();
let b = b.parse::<i32>().unwrap();
(a, b)
})
.collect::<Vec<(i32, i32)>>()
}
fn extract_mul_pairs_dont(line: &str) -> Vec<(i32, i32)> {
let mut pairs = Vec::new();
for (idx, _) in line.match_indices("mul(") {
if let Some(dont_func) = line[..idx].rfind("don't()") {
if let Some(do_func) = line[..idx].rfind("do()") {
if do_func < dont_func {
continue;
}
} else {
continue;
}
}
let mut invalid = false;
let mut comma_pos = 0;
let mut pair = ("".to_string(), "".to_string());
// max length of parenthesis == 9
for paren_idx in 0..9 {
let paren_content = match line.as_bytes().get(idx + 4 + paren_idx) {
Some(content) => *content as char,
None => {
invalid = true;
break;
}
};
if paren_content == ')' {
if comma_pos == 0 {
invalid = true;
}
break;
}
if paren_content == ',' {
comma_pos = paren_idx;
continue;
}
if !paren_content.is_ascii_digit() {
invalid = true;
break;
}
if comma_pos == 0 {
pair.0.push(paren_content);
} else {
pair.1.push(paren_content);
}
}
if !invalid {
let a = pair.0.parse::<i32>().unwrap();
let b = pair.1.parse::<i32>().unwrap();
pairs.push((a, b));
}
}
pairs
}
pub fn process_part2(input: &str) -> i32 {
let input = input.lines().collect::<Vec<&str>>().join("");
extract_mul_pairs_dont(&input)
.into_iter()
.map(|(a, b)| a * b)
.sum()
}
#[cfg(test)]
mod tests {
use std::fs;
use super::*;
const INPUT: &str =
"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))mul( 1, 3)";
const INPUT_2: &str =
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))";
#[test]
fn part1() {
let result = process_part1(INPUT);
assert_eq!(result, 161);
}
#[test]
fn res1() {
let root = env!("CARGO_MANIFEST_DIR");
let content = fs::read_to_string(format!("{root}/resources/3_input.txt")).unwrap();
let result = process_part1(&content);
assert_eq!(result, 183788984);
}
#[test]
fn part2() {
let result = process_part2(INPUT_2);
assert_eq!(result, 48);
}
#[test]
fn res2() {
let root = env!("CARGO_MANIFEST_DIR");
let content = fs::read_to_string(format!("{root}/resources/3_input.txt")).unwrap();
let result = process_part2(&content);
assert_eq!(result, 62098619);
}
}