diff --git a/2024/input/day07.txt b/2024/input/day07.txt new file mode 100644 index 0000000..97c0e20 Binary files /dev/null and b/2024/input/day07.txt differ diff --git a/2024/src/day07.rs b/2024/src/day07.rs new file mode 100644 index 0000000..292087b --- /dev/null +++ b/2024/src/day07.rs @@ -0,0 +1,119 @@ +const INPUT: &str = include_str!("../../2024/input/day07.txt"); + +pub(crate) fn part1() -> usize { + solve_part1(INPUT) +} + +pub(crate) fn part2() -> usize { + solve_part2(INPUT) +} + +fn solve_part1(input: &str) -> usize { + parse(input) + .filter(Equation::has_solution_without_concatenation) + .map(Equation::left_side) + .sum() +} + +fn solve_part2(input: &str) -> usize { + parse(input) + .filter(Equation::has_solution_with_concatenation) + .map(Equation::left_side) + .sum() +} + +#[derive(Debug)] +struct Equation { + left_side: usize, + right_side: Vec, +} +impl Equation { + fn left_side(self) -> usize { + self.left_side + } + fn has_solution_without_concatenation(&self) -> bool { + Equation::has_solution(self.left_side, &self.right_side, false) + } + fn has_solution_with_concatenation(&self) -> bool { + Equation::has_solution(self.left_side, &self.right_side, true) + } + fn has_solution(left_side: usize, right_side: &[usize], allow_concat: bool) -> bool { + let last_idx = right_side.len() - 1; + let last_num = right_side[last_idx]; + if right_side.len() == 1 { + return left_side == last_num; + } + let front_nums = &right_side[..last_idx]; + + let last_operation_is_addition = || { + left_side > last_num + && Equation::has_solution(left_side - last_num, front_nums, allow_concat) + }; + let last_op_is_multiplication = || { + left_side % last_num == 0 + && Equation::has_solution(left_side / last_num, front_nums, allow_concat) + }; + let last_op_is_concatenation = || { + let last_num_string = last_num.to_string(); + let concat_divisor = 10usize.pow(last_num_string.len() as u32); + left_side.to_string().ends_with(&last_num_string) + && Equation::has_solution(left_side / concat_divisor, front_nums, allow_concat) + }; + + last_operation_is_addition() + || last_op_is_multiplication() + || allow_concat && last_op_is_concatenation() + } +} + +fn parse(input: &str) -> impl Iterator + use<'_> { + input.trim().lines().map(|line| { + let (left, right) = line.split_once(": ").unwrap(); + let left = left.parse().unwrap(); + let right = right + .split_whitespace() + .map(|n| n.parse().unwrap()) + .collect(); + Equation { + left_side: left, + right_side: right, + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + const EXAMPLE: &str = "\ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 +"; + + #[test] + fn test_part1_example() { + assert_eq!(190 + 3267 + 292, solve_part1(EXAMPLE)); + } + + #[test] + fn test_part1() { + assert_eq!(42_283_209_483_350, solve_part1(INPUT)); + } + + #[test] + fn test_part2_example() { + assert_eq!(190 + 3267 + 156 + 7290 + 192 + 292, solve_part2(EXAMPLE)); + } + + #[test] + fn test_part2() { + assert_eq!(1_026_766_857_276_279, solve_part2(INPUT)); + } +} diff --git a/2024/src/main.rs b/2024/src/main.rs index 488ae9f..96f4f4e 100644 --- a/2024/src/main.rs +++ b/2024/src/main.rs @@ -6,6 +6,7 @@ mod day03; mod day04; mod day05; mod day06; +mod day07; fn main() { print_result(1, day01::part1(), day01::part2()); @@ -14,6 +15,7 @@ fn main() { print_result(1, day04::part1(), day04::part2()); print_result(1, day05::part1(), day05::part2()); print_result(1, day06::part1(), day06::part2()); + print_result(1, day07::part1(), day07::part2()); } fn print_result(day: i32, part1: impl Display, part2: impl Display) {