diff options
Diffstat (limited to '2024_rust/src/bin/day7.rs')
-rw-r--r-- | 2024_rust/src/bin/day7.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/2024_rust/src/bin/day7.rs b/2024_rust/src/bin/day7.rs new file mode 100644 index 0000000..d81ad1e --- /dev/null +++ b/2024_rust/src/bin/day7.rs @@ -0,0 +1,129 @@ +use itertools::Itertools; +use regex::Regex; +use std::fmt; + +struct Operator { + name: char, + f: Box<dyn Fn(u64, u64) -> u64>, +} + +impl fmt::Display for Operator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +#[allow(dead_code)] +fn op_chain_to_string(chain: &Vec<&Operator>) -> String { + format!("[{}]", chain.iter().map(|o| o.to_string()).join(", ")) +} + +#[derive(Debug)] +struct Equation { + result: u64, + values: Vec<u64>, +} + +impl Equation { + fn parse(s: &str) -> Self { + let re = Regex::new("^([0-9]+): ((?:[0-9]+ ?)+)$").unwrap(); + let caps = re.captures(s).unwrap(); + let (_full, [result, values]) = caps.extract(); + Equation { + result: result.parse().unwrap(), + values: values.split(' ').map(|n| n.parse().unwrap()).collect(), + } + } + + fn is_solution(&self, chain: &Vec<&Operator>) -> bool { + let mut chain_it = chain.iter(); + let mut self_it = self.values.iter(); + let mut acc: u64 = *self_it.next().unwrap(); + for value in self_it { + let Some(op) = chain_it.next() else { + panic!("Impossible") + }; + acc = (op.f)(acc, *value); + if acc > self.result { + return false; + } + } + // // TODO why does this not work? + // self.values.iter() + // .reduce(|v1, v2| { + // let Some(op) = chain_it.next() else { panic!("Impossible") }; + // (op.f)(v1, v2) + // }); + acc == self.result + } + + fn find_solution<'a>(&self, ops: &'a [Operator]) -> Option<Vec<&'a Operator>> { + // println!("Finding solution for {self:?}..."); + let op_chains = itertools::repeat_n(ops, self.values.len() - 1).multi_cartesian_product(); + for chain in op_chains { + // print!(" Trying chain {}... ", op_chain_to_string(&chain)); + if self.is_solution(&chain) { + // println!("YES!"); + return Some(chain.clone()); + } + // println!("nope"); + } + None + } + + fn has_solution(&self, ops: &[Operator]) -> bool { + self.find_solution(ops).is_some() + } +} + +fn sum_solutions(input: &str, ops: &[Operator]) -> u64 { + let equations: Vec<Equation> = input.lines().map(|l| Equation::parse(l)).collect(); + // println!("Equations: {equations:?}"); + equations + .iter() + .filter(|e| e.has_solution(&ops)) + .map(|e| e.result) + .sum() +} + +fn p1(input: &str) -> String { + let ops: [Operator; 2] = [ + Operator { + name: '+', + f: Box::new(|x, y| x + y), + }, + Operator { + name: '*', + f: Box::new(|x, y| x * y), + }, + ]; + + sum_solutions(input, &ops).to_string() +} + +fn p2(input: &str) -> String { + let ops: [Operator; 3] = [ + Operator { + name: '+', + f: Box::new(|x, y| x + y), + }, + Operator { + name: '*', + f: Box::new(|x, y| x * y), + }, + Operator { + name: '|', + f: Box::new(|x, y| { + format!("{}{}", x.to_string(), y.to_string()) + .parse() + .unwrap() + }), + }, + ]; + + sum_solutions(input, &ops).to_string() +} + +fn main() { + aoc2024::run_day("7", p1, p2); +} |