use std::{ fs::File, io::{BufRead, BufReader}, path::Path, str::FromStr, }; pub fn solve(input: &Path) -> anyhow::Result<()> { println!("part one: {}", part_one(input)?); println!("part two: {}", part_two(input)?); Ok(()) } #[derive(Debug)] struct Equation { answer: u64, inputs: Vec, } impl FromStr for Equation { type Err = Box; fn from_str(s: &str) -> Result { let mut parts = s.split(":"); Ok(Equation { answer: parts.next().unwrap().parse()?, inputs: parts .next() .unwrap() .split_whitespace() .map(|n| -> u64 { n.parse().unwrap() }) .collect(), }) } } fn part_one(input: &Path) -> anyhow::Result { let reader = BufReader::new(File::open(input)?); let mut equations = vec![]; for line in reader.lines() { equations.push(Equation::from_str(&line?).unwrap()); } Ok(equations .iter() .filter(|eq| check_equation_one(eq)) .map(|eq| eq.answer) .collect::>() .iter() .sum()) } fn check_equation_one(eq: &Equation) -> bool { let positions = eq.inputs.len() - 1; if positions == 1 { if eq.inputs[0] + eq.inputs[1] == eq.answer { return true; } if eq.inputs[0] * eq.inputs[1] == eq.answer { return true; } return false; } let operators = ["+", "*"]; let combinations: Vec = (operators.len()..positions).fold( operators .iter() .flat_map(|c| operators.iter().map(move |&d| d.to_owned() + *c)) .collect(), |a, _| { a.into_iter() .flat_map(|c| operators.iter().map(move |&d| d.to_owned() + &*c)) .collect() }, ); for combination in combinations { let combination: Vec = combination.chars().collect(); let mut current: Option = None; for (i, input) in eq.inputs.iter().enumerate() { if current.is_none() { current = Some(*input); continue; } match combination[i - 1] { '+' => current = Some(current.unwrap() + input), '*' => current = Some(current.unwrap() * input), _ => panic!(), } } if current.unwrap() == eq.answer { return true; } } false } fn part_two(input: &Path) -> anyhow::Result { let reader = BufReader::new(File::open(input)?); let mut equations = vec![]; for line in reader.lines() { equations.push(Equation::from_str(&line?).unwrap()); } Ok(equations .iter() .filter(|eq| check_equation_two(eq)) .map(|eq| eq.answer) .collect::>() .iter() .sum()) } fn check_equation_two(eq: &Equation) -> bool { let positions = eq.inputs.len() - 1; if positions == 1 { if eq.inputs[0] + eq.inputs[1] == eq.answer { return true; } if eq.inputs[0] * eq.inputs[1] == eq.answer { return true; } if (eq.inputs[0].to_string() + &eq.inputs[1].to_string()) .parse::() .unwrap() == eq.answer { return true; } return false; } let operators = ["+", "*", "|"]; let combinations: Vec = (operators.len()..positions + 1).fold( operators .iter() .flat_map(|c| operators.iter().map(move |&d| d.to_owned() + *c)) .collect(), |a, _| { a.into_iter() .flat_map(|c| operators.iter().map(move |&d| d.to_owned() + &*c)) .collect() }, ); for combination in combinations { let combination: Vec = combination.chars().collect(); let mut current: Option = None; for (i, input) in eq.inputs.iter().enumerate() { if current.is_none() { current = Some(*input); continue; } match combination[i - 1] { '+' => current = Some(current.unwrap() + input), '*' => current = Some(current.unwrap() * input), '|' => { current = Some( (current.unwrap().to_string() + &input.to_string()) .parse() .unwrap(), ) } _ => panic!(), } } if current.unwrap() == eq.answer { return true; } } false }