From: Jack Kinsey Date: Sat, 7 Dec 2024 21:41:50 +0000 (-0500) Subject: Complete day 7 X-Git-Url: http://git.jkinsey.net/?a=commitdiff_plain;h=336f98c13a1d4cf7e7fd7c95028563aa563387ce;p=adventofcode2024.git Complete day 7 --- diff --git a/src/day07.rs b/src/day07.rs new file mode 100644 index 0000000..63cd0d4 --- /dev/null +++ b/src/day07.rs @@ -0,0 +1,218 @@ +fn input() -> &'static str { + include_str!("../input/day07.txt") +} + +fn parse(input: &str) -> Vec<(u64, Vec)> { + input + .split_terminator("\n") + .map(|l| { + let [lhs, rhs] = l.split(":").collect::>()[..] else { + panic!() + }; + ( + lhs.parse().unwrap(), + rhs.split_whitespace().map(|n| n.parse().unwrap()).collect(), + ) + }) + .collect() +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +enum Op { + Add, + Mul, + Cat, +} + +#[derive(Debug, Eq, PartialEq, Clone)] +struct OpString { + len: usize, + ops: Vec, + item: Vec, +} + +impl OpString { + fn new(len: usize) -> Self { + OpString { + len, + ops: vec![Op::Add, Op::Mul], + item: vec![], + } + } + + fn new_with_cat(len: usize) -> Self { + OpString { + len, + ops: vec![Op::Add, Op::Mul, Op::Cat], + item: vec![], + } + } +} + +impl Iterator for OpString { + type Item = Vec; + + fn next(&mut self) -> Option { + if self.item.is_empty() { + self.item = vec![self.ops[0]; self.len]; + Some(self.item.clone()) + } else if self.item.iter().all(|&o| o == self.ops[self.ops.len() - 1]) { + None + } else { + let mut carry = true; + let mut digit: usize = self.len; + let opn = self.ops.len() - 1; + while carry { + digit -= 1; + for (i, op) in self.ops.iter().enumerate() { + if self.item[digit] == *op { + if i == opn { + self.item[digit] = self.ops[0]; + } else { + self.item[digit] = self.ops[i + 1]; + carry = false; + } + break; + } + } + } + Some(self.item.clone()) + } + } +} + +fn op_eval(num: &[u64], op: &[Op]) -> u64 { + num.iter() + .skip(1) + .zip(op.iter()) + .fold(num[0], |a, (b, x)| match x { + Op::Add => a + b, + Op::Mul => a * b, + Op::Cat => a * 10u64.pow(b.ilog10() + 1) + b, + }) +} + +fn check_ops(&(lhs, rhs): &&(u64, Vec)) -> bool { + OpString::new(rhs.len()).any(|ops| *lhs == op_eval(rhs, &ops)) +} + +fn check_ops_with_cat(&(lhs, rhs): &&(u64, Vec)) -> bool { + OpString::new_with_cat(rhs.len()).any(|ops| *lhs == op_eval(rhs, &ops)) +} + +pub fn part1() { + let n: u64 = parse(input()) + .iter() + .filter(check_ops) + .map(|&(lhs, _)| lhs) + .sum(); + println!("Day 7 Part 1: {}", n); +} + +pub fn part2() { + let n: u64 = parse(input()) + .iter() + .filter(check_ops_with_cat) + .map(|&(lhs, _)| lhs) + .sum(); + println!("Day 7 Part 2: {}", n); +} + +#[cfg(test)] +mod test { + use super::*; + + const INPUT_STR: &str = concat!( + "190: 10 19\n", + "3267: 81 40 27\n", + "83: 17 5\n", + "156: 15 6\n", + "7290: 6 8 6 15\n", + "161011: 16 10 13\n", + "192: 17 8 14\n", + "21037: 9 7 18 13\n", + "292: 11 6 16 20\n", + ); + + #[test] + fn test_parse() { + assert_eq!( + parse(INPUT_STR), + [ + (190, vec![10, 19]), + (3267, vec![81, 40, 27]), + (83, vec![17, 5]), + (156, vec![15, 6]), + (7290, vec![6, 8, 6, 15]), + (161011, vec![16, 10, 13]), + (192, vec![17, 8, 14]), + (21037, vec![9, 7, 18, 13]), + (292, vec![11, 6, 16, 20]) + ] + ) + } + + #[test] + fn test_op_eval() { + assert_eq!(op_eval(&[1, 2, 3], &[Op::Add, Op::Mul]), 9) + } + + #[test] + fn test_opstring() { + use Op::*; + assert_eq!( + OpString::new(2).collect::>(), + [[Add, Add], [Add, Mul], [Mul, Add], [Mul, Mul]] + ) + } + + #[test] + fn test_opstring_with_cat() { + use Op::*; + assert_eq!( + OpString::new_with_cat(2).collect::>(), + [ + [Add, Add], + [Add, Mul], + [Add, Cat], + [Mul, Add], + [Mul, Mul], + [Mul, Cat], + [Cat, Add], + [Cat, Mul], + [Cat, Cat], + ] + ) + } + + #[test] + fn test_check_op_eval() { + assert_eq!(op_eval(&[1, 1], &[Op::Add]), 2); + assert_eq!(op_eval(&[1, 1], &[Op::Mul]), 1); + assert_eq!(op_eval(&[1, 1], &[Op::Cat]), 11); + } + + #[test] + fn test_check_ops() { + assert_eq!( + parse(INPUT_STR) + .iter() + .filter(check_ops) + .map(|&(lhs, _)| lhs) + .collect::>(), + [190, 3267, 292] + ) + } + + #[test] + fn test_check_ops_with_cat() { + assert_eq!( + parse(INPUT_STR) + .iter() + .filter(check_ops_with_cat) + .map(|&(lhs, _)| lhs) + .collect::>(), + [190, 3267, 156, 7290, 192, 292] + ) + } +} diff --git a/src/main.rs b/src/main.rs index b4be417..2b2032d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ pub mod day03; pub mod day04; pub mod day05; pub mod day06; -// pub mod day07; +pub mod day07; // pub mod day08; // pub mod day09; // pub mod day10; @@ -26,14 +26,14 @@ pub mod day06; type Part = fn(); -const DAYS: [(Part, Part); 6] = [ +const DAYS: [(Part, Part); 7] = [ (day01::part1 as fn(), day01::part2 as fn()), (day02::part1 as fn(), day02::part2 as fn()), (day03::part1 as fn(), day03::part2 as fn()), (day04::part1 as fn(), day04::part2 as fn()), (day05::part1 as fn(), day05::part2 as fn()), (day06::part1 as fn(), day06::part2 as fn()), - // (day07::part1 as fn(), day07::part2 as fn()), + (day07::part1 as fn(), day07::part2 as fn()), // (day08::part1 as fn(), day08::part2 as fn()), // (day09::part1 as fn(), day09::part2 as fn()), // (day10::part1 as fn(), day10::part2 as fn()),