diff --git a/Cargo.lock b/Cargo.lock index bb256b0..0256ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,25 @@ dependencies = [ name = "aoc2024" version = "0.1.0" dependencies = [ + "itertools", "regex", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "memchr" version = "2.7.6" diff --git a/Cargo.toml b/Cargo.toml index 40c2165..9c89a58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +itertools = "0.14.0" regex = "1.12.2" diff --git a/src/day7.rs b/src/day7.rs index b2e8106..dff7126 100644 --- a/src/day7.rs +++ b/src/day7.rs @@ -1,3 +1,5 @@ +use itertools::Itertools; + pub fn day7_part1(input: &str) -> String { let input = parse(input); let sum: u64 = input @@ -18,58 +20,36 @@ pub fn day7_part2(input: &str) -> String { sum.to_string() } -fn possible_with_concatenation(res: u64, operands: &[u64]) -> bool { +fn possible(result: u64, operands: &[u64]) -> bool { let operator_count = operands.len() - 1; - let possibilities = 0..(3u64.pow(operator_count as u32)); - for possibility in possibilities { - if evaluate_with_concatenation(possibility, operands) == res { - return true; - } - } - dbg!(res); - false + (0..operator_count) + .map(|_| [Operator::Plus, Operator::Times]) + .multi_cartesian_product() + .any(|operators| check(result, operands, operators.as_slice())) } -fn evaluate_with_concatenation(mut bitpattern: u64, operands: &[u64]) -> u64 { - // dbg!(bitpattern, operands); - let selected_option = bitpattern % 3; - let mut result = if selected_option == 0 { - operands[0] + operands[1] - } else if selected_option == 1 { - operands[0] * operands[1] - } else { - concatenate(operands[0], operands[1]) - }; - - let mut debug_operators = vec![]; - - for operator in 2..operands.len() { - bitpattern /= 3; - let selected_option = bitpattern % 3; - - result = if selected_option == 0 { - result + operands[operator] - } else if selected_option == 1 { - result * operands[operator] - } else { - concatenate(result, operands[operator]) - }; +fn possible_with_concatenation(result: u64, operands: &[u64]) -> bool { + let operator_count = operands.len() - 1; + (0..operator_count) + .map(|_| [Operator::Plus, Operator::Times, Operator::Concatenate]) + .multi_cartesian_product() + .any(|operators| check(result, operands, operators.as_slice())) +} - debug_operators.push(if selected_option == 0 { - '+' - } else if selected_option == 1 { - '*' - } else { - '|' - }); +fn check(result: u64, operands: &[u64], operators: &[Operator]) -> bool { + let mut accumulator = operands[0]; + for (operator, operand) in operators.iter().zip(&operands[1..]) { + match operator { + Operator::Times => accumulator *= operand, + Operator::Plus => accumulator += operand, + Operator::Concatenate => accumulator = concatenate(accumulator, *operand), + } } - - // dbg!(result, bitpattern, debug_operators); - result + result == accumulator } fn concatenate(left: u64, right: u64) -> u64 { - let decimal_places_shift = (right as f64).log10().ceil() as u32; + let decimal_places_shift = right.ilog10() + 1; left * 10u64.pow(decimal_places_shift) + right } @@ -78,44 +58,11 @@ fn test_concatenate() { assert_eq!(concatenate(15, 6), 156) } -fn possible(res: u64, operands: &[u64]) -> bool { - // dbg!(res, operands); - let operator_count = operands.len() - 1; - let possibilities = 0..(2u64.pow(operator_count as u32)); - for possibility in possibilities { - if evaluate(possibility, operands) == res { - return true; - } - } - - false -} - -fn evaluate(bitpattern: u64, operands: &[u64]) -> u64 { - // dbg!(bitpattern, operands); - let mut result = if bitpattern & 0b1 == 0 { - operands[0] + operands[1] - } else { - operands[0] * operands[1] - }; - - for operator in 2..operands.len() { - let multiply = bitpattern & (0b1 << (operator - 1)) != 0; - if multiply { - result *= operands[operator]; - } else { - result += operands[operator]; - } - } - - // dbg!(result); - result -} - #[derive(Copy, Clone, Eq, PartialEq, Debug)] enum Operator { Times, Plus, + Concatenate, } fn parse(input: &str) -> Vec<(u64, Vec)> { diff --git a/src/lib.rs b/src/lib.rs index 84761ac..d443049 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,17 +356,18 @@ mod tests { assert_eq!(result, "3598800864292"); } - // #[ignore] + #[ignore] #[test] fn day7_part2_test() { - // let simple_result = day7::day7_part2(include_str!("../input/day7_custom.test.txt")); - // assert_eq!(simple_result, (12345 + 12034050 + 15 + 120).to_string()); + let simple_result = day7::day7_part2(include_str!("../input/day7_custom.test.txt")); + assert_eq!(simple_result, (12345 + 12034050 + 15 + 120).to_string()); - // let test_result = day7::day7_part2(include_str!("../input/day7.test.txt")); - // assert_eq!(test_result, "11387"); + let test_result = day7::day7_part2(include_str!("../input/day7.test.txt")); + assert_eq!(test_result, "11387"); let result = day7::day7_part2(include_str!("../input/day7.txt")); let parsed_result: u64 = result.parse::().unwrap(); assert!(parsed_result > 318_910_516_761_637, "{parsed_result} was too low"); + assert_eq!(parsed_result, 340_362_529_351_427); } #[test]