use super::util; pub fn solve() { let lines = util::read_file("input/day18.txt"); // lines.iter() // .for_each(|s| println!("{} = {}", s, evaluate(s.as_str()).0)); let part1: u64 = lines.iter().map(|s| evaluate(s.as_str()).0).sum(); println!("Day X Part 1: {}", part1); let part2: u64 = lines.iter() // .inspect(|s| println!("{}: {}", s, build_expression_tree(s.as_str()).0.evaluate())) .map(|s| build_expression_tree(s.as_str()).0.evaluate()) .sum(); println!("Day X Part 2: {}", part2); } fn evaluate(input: &str) -> (u64, usize) { let mut i = 0usize; let mut res = 0; let mut operator = Operator::Add; while i < input.len() { let c = &input[i..(i+1)]; if c == "(" { let sub_evaluation = evaluate(&input[(i + 1)..]); i += sub_evaluation.1; res = operator.apply(res, sub_evaluation.0); } else if c == ")" { return (res, i + 1); } else if c == "+" { operator = Operator::Add; } else if c == "*" { operator = Operator::Multiply; } else if c == " " { // Nothing to do } else { let n = c.parse::().unwrap(); res = operator.apply(res, n); } i += 1; } return (res, i); } fn build_expression_tree(input: &str) -> (ExpressionTreeNode, usize) { // println!("Evaluating '{}':", input); let mut subtrees: Vec<(usize, usize, ExpressionTreeNode)> = Vec::new(); let mut evaluate_until = input.len(); { let mut i = 0; // First build subtrees for brackets: while i < evaluate_until { let par_open = input[i..evaluate_until].find("(").map(|p| i + p); let par_close = input[i..evaluate_until].find(")").map(|p| i + p); if par_open.is_some() && par_close.unwrap() > par_open.unwrap() { let subtree_start = par_open.unwrap(); let subtree = build_expression_tree(&input[(subtree_start + 1)..]); let subtree_end = subtree_start + subtree.1 + 1; subtrees.push((subtree_start, subtree_end, subtree.0)); i = subtree_end + 1; } else { if par_close.is_some() { // If we find a closing parenthesis, we're apparently building a subtree evaluate_until = par_close.unwrap(); break; } else { // No parenthesis, so no need to search further break; } } } } // println!("Subtrees between parentheses in '{}':", &input[0..evaluate_until]); // subtrees.iter().for_each(|s| println!("{:?}", s)); // Now we're gonna match/find all +: build_subtrees_for_operators(input, 0, evaluate_until, "+", &Operator::Add, &mut subtrees); build_subtrees_for_operators(input, 0, evaluate_until, "*", &Operator::Multiply, &mut subtrees); // // By now we should have a single root node left // println!("Subtrees in '{}':", &input[0..evaluate_until]); // subtrees.iter().for_each(|s| println!("{:?}", s)); assert_eq!(subtrees.len(), 1); return (subtrees.remove(0).2, evaluate_until); } fn is_not_whitespace(c: char) -> bool { return !c.is_whitespace(); } fn build_subtrees_for_operators(input: &str, from: usize, until: usize, operator_symbol: &str, operator: &Operator, subtrees: &mut Vec<(usize, usize, ExpressionTreeNode)>) { let mut i = if subtrees.is_empty() || subtrees[0].0 > from { 0 } else { subtrees[0].1 + 1 }; // Subtree index points to the subtree after the current search-range let mut subtree_idx = if i == 0 { 0 } else { 1 }; let mut search_until = if subtrees.is_empty() { until } else if subtree_idx < subtrees.len() { subtrees[subtree_idx].0 } else { until }; while i < until { let operator_idx = input[i..search_until].find(operator_symbol).map(|r| i + r); if operator_idx.is_some() { // Found the operator symbol, let's build a subtree with whatever is left and right of it: let subtree_end = build_subtree_for_operator(operator_idx.unwrap(), &input, operator, subtrees); i = subtree_end + 1; if i > search_until { // Our newly build subtree includes the last search range: search_until = subtrees.iter().map(|s| s.0).find(|s| s > &i).unwrap_or(until); } } else { subtree_idx += 1; // If the subtree_idx points the to subtree "after" the last one, that just means to search until the end of the string // If the subtree_idx points even after that, then we're done. if subtree_idx > subtrees.len() { break; } i = subtrees[subtree_idx - 1].1; search_until = if subtrees.is_empty() { until } else if subtree_idx < subtrees.len() { subtrees[subtree_idx].0 } else { until }; if i == search_until { break; } } } } fn build_subtree_for_operator(idx: usize, input: &str, op: &Operator, subtrees: &mut Vec<(usize, usize, ExpressionTreeNode)>) -> usize { let left_idx = input[0..idx].rfind(is_not_whitespace).unwrap(); let right_idx = idx + 1 + input[(idx + 1)..].find(is_not_whitespace).unwrap(); let left_subtree = subtrees.binary_search_by_key(&left_idx, |s| s.1); let right_subtree = subtrees.binary_search_by_key(&right_idx, |s| s.0); let subtree_start = if left_subtree.is_ok() { subtrees[left_subtree.unwrap()].0 } else {left_idx}; let subtree_end = if right_subtree.is_ok() { subtrees[right_subtree.unwrap()].1 } else {right_idx}; let subtree = ExpressionTreeNode { operator: Option::Some(op.clone()), right: Option::Some(Box::from( if right_subtree.is_ok() { subtrees.remove(right_subtree.unwrap()).2 } else { ExpressionTreeNode::leaf_node(&input[right_idx..(right_idx+ 1)]) })), left: Option::Some( Box::from(if left_subtree.is_ok() { subtrees.remove(left_subtree.unwrap()).2 } else { ExpressionTreeNode::leaf_node(&input[left_idx..(left_idx + 1)]) })), num: Option::None, }; let insertion_index = if subtrees.is_empty() { 0 } else { let res = subtrees.binary_search_by_key(&subtree_start, |s| s.0); if res.is_ok() { res.unwrap() } else { res.unwrap_err() } }; subtrees.insert(insertion_index, (subtree_start, subtree_end, subtree)); return subtree_end; } #[derive(Debug, PartialEq, Clone)] enum Operator { Multiply, Add } impl Operator { fn apply(&self, x: u64, y: u64) -> u64 { return match self { Operator::Add => x + y, Operator::Multiply => x * y } } } #[derive(Debug)] struct ExpressionTreeNode { operator: Option, num: Option, left: Option>, right: Option>, } impl ExpressionTreeNode { fn leaf_node(input: &str) -> ExpressionTreeNode { return ExpressionTreeNode { operator: Option::None, left: Option::None, right: Option::None, num: Option::Some(input.parse::().unwrap()) }; } fn evaluate(&self) -> u64 { return if self.num.is_some() { self.num.unwrap() } else { self.operator.as_ref().unwrap().apply(self.left.as_ref().unwrap().evaluate(), self.right.as_ref().unwrap().evaluate()) } } }