From ebe39683c786ead3e4bb823443cc822d2a2498dc Mon Sep 17 00:00:00 2001 From: Matej Focko Date: Sun, 2 Jul 2023 22:55:51 +0200 Subject: [PATCH] day(21): add solution Signed-off-by: Matej Focko --- samples/day21.txt | 15 +++ src/bin/day21.rs | 260 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 samples/day21.txt create mode 100644 src/bin/day21.rs diff --git a/samples/day21.txt b/samples/day21.txt new file mode 100644 index 0000000..7993b87 --- /dev/null +++ b/samples/day21.txt @@ -0,0 +1,15 @@ +root: pppw + sjmn +dbpl: 5 +cczh: sllz + lgvd +zczc: 2 +ptdq: humn - dvpt +dvpt: 3 +lfqf: 4 +humn: 5 +ljgn: 2 +sjmn: drzm * dbpl +sllz: 4 +pppw: cczh / lfqf +lgvd: ljgn * ptdq +drzm: hmdt - zczc +hmdt: 32 \ No newline at end of file diff --git a/src/bin/day21.rs b/src/bin/day21.rs new file mode 100644 index 0000000..9f0a6ef --- /dev/null +++ b/src/bin/day21.rs @@ -0,0 +1,260 @@ +use std::{collections::HashMap, ops::Neg, str::FromStr}; + +use aoc_2022::*; + +type Input = Riddle; +type Output = i64; + +#[derive(Debug, Clone)] +enum Operation { + Add, + Sub, + Mul, + Div, +} + +impl Operation { + fn apply(&self, l: i64, r: i64) -> i64 { + match self { + Operation::Add => l + r, + Operation::Sub => l - r, + Operation::Mul => l * r, + Operation::Div => l / r, + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +enum Direction { + #[default] + None, + Left, + Right, +} + +impl Neg for Direction { + type Output = Direction; + + fn neg(self) -> Self::Output { + match self { + Direction::None => unreachable!(), + Direction::Left => Direction::Right, + Direction::Right => Direction::Left, + } + } +} + +impl Direction { + fn next(&self) -> Direction { + match self { + Direction::None => Direction::Left, + Direction::Left => Direction::Right, + Direction::Right => unreachable!(), + } + } +} + +#[derive(Debug, Clone)] +struct Expr { + left: String, + op: Operation, + right: String, +} + +impl Expr { + fn get(&self, d: Direction) -> String { + match d { + Direction::Left => self.left.clone(), + Direction::Right => self.right.clone(), + Direction::None => todo!(), + } + } + + fn solve(&self, d: Direction, x: i64, other: i64) -> Output { + match self.op { + Operation::Add => x - other, + Operation::Sub => match d { + Direction::Left => x + other, + Direction::Right => other - x, + _ => unreachable!(), + }, + Operation::Mul => x / other, + Operation::Div => match d { + Direction::Left => x * other, + Direction::Right => other / x, + _ => unreachable!(), + }, + } + } +} + +#[derive(Debug, Clone)] +struct Riddle { + precomputed: HashMap, + expressions: HashMap, +} + +impl FromStr for Riddle { + type Err = Report; + + fn from_str(s: &str) -> std::result::Result { + let mut precomputed = HashMap::new(); + let mut expressions = HashMap::new(); + + for parts in s + .lines() + .map(|line| line.split_ascii_whitespace().collect_vec()) + { + let node = parts[0].strip_suffix(':').unwrap().to_string(); + + match parts.len() { + 2 => { + let c: Output = parts[1].parse().unwrap(); + precomputed.insert(node, c); + } + 4 => { + let left = parts[1].to_string(); + let right = parts[3].to_string(); + + let op = match parts[2] { + "+" => Operation::Add, + "-" => Operation::Sub, + "*" => Operation::Mul, + "/" => Operation::Div, + _ => return Err(eyre!("invalid operation")), + }; + + expressions.insert(node, Expr { left, right, op }); + } + _ => return Err(eyre!("invalid count of parts")), + } + } + + Ok(Riddle { + precomputed, + expressions, + }) + } +} + +#[derive(Default)] +enum Color { + #[default] + Unvisited, + Open, + Closed, +} + +impl Riddle { + fn compute(&mut self, wanted_node: &str) { + let mut stack = vec![wanted_node]; + + while let Some(node) = stack.pop() { + if self.precomputed.get(node).is_some() { + continue; + } + + let expr = self.expressions.get(node).unwrap(); + if self.precomputed.get(&expr.left).is_none() + || self.precomputed.get(&expr.right).is_none() + { + stack.push(node); + + if self.precomputed.get(&expr.left).is_none() { + stack.push(&expr.left); + } + if self.precomputed.get(&expr.right).is_none() { + stack.push(&expr.right); + } + + continue; + } + + let l = *self.precomputed.get(&expr.left).unwrap(); + let r = *self.precomputed.get(&expr.right).unwrap(); + + let res = expr.op.apply(l, r); + self.precomputed.insert(node.to_string(), res); + } + } + + fn dfs(&self, seeked: &str) -> Vec<(String, Direction)> { + let mut state: HashMap = HashMap::new(); + + let mut path = vec![("root".to_string(), Direction::default())]; + while !path.is_empty() && path.last().unwrap().0.as_str() != seeked { + let (node, direction) = path.pop().unwrap(); + + if direction == Direction::None { + state.insert(node.clone(), Color::Open); + } + + match direction { + Direction::Right => { + state.insert(node.clone(), Color::Closed); + } + d => { + let next_d = d.next(); + + path.push((node.clone(), next_d)); + if let Some(expr) = self.expressions.get(node.as_str()) { + path.push((expr.get(next_d), Direction::default())); + } + } + } + } + + path + } + + fn get_other(&self, node: &str, direction: Direction) -> i64 { + *self + .precomputed + .get(&self.expressions.get(node).unwrap().get(-direction)) + .unwrap() + } + + fn reverse(&self, path: &[(String, Direction)]) -> Output { + let mut wanted = self.get_other("root", path.first().unwrap().1); + + for (node, direction) in path.iter().skip(1) { + let expr = self.expressions.get(node).unwrap(); + let other = self.get_other(node, *direction); + + wanted = expr.solve(*direction, wanted, other); + } + + wanted + } +} + +struct Day21; +impl Solution for Day21 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname).parse().unwrap() + } + + fn part_1(input: &Input) -> Output { + let mut riddle = input.clone(); + riddle.compute("root"); + + *riddle.precomputed.get("root").unwrap() + } + + fn part_2(input: &Input) -> Output { + let mut riddle = input.clone(); + riddle.compute("root"); + + let mut path = riddle.dfs("humn"); + path.pop(); + // debug!("Path: {:?}", path); + + riddle.reverse(&path) + } +} + +fn main() -> Result<()> { + Day21::main() +} + +test_sample!(day_21, Day21, 152, 301);