pub fn process_part1(input: &str) -> i32 { input .lines() .map(extract_mul_pairs) .map(|pairs| pairs.iter().map(|(a, b)| a * b).sum::()) .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 don't() and do() // not sure how to invert this and combine fn extract_mul_pairs(line: &str) -> Vec<(i32, i32)> { let mut pairs = Vec::new(); for (idx, _) in line.match_indices("mul(") { 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::().unwrap(); let b = pair.1.parse::().unwrap(); pairs.push((a, b)); } } pairs } 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::().unwrap(); let b = pair.1.parse::().unwrap(); pairs.push((a, b)); } } pairs } pub fn process_part2(input: &str) -> i32 { input .lines() .map(extract_mul_pairs_dont) .map(|pairs| pairs.iter().map(|(a, b)| a * b).sum::()) .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); } }