use std::collections::HashMap; #[path = "../lib.rs"] mod lib; enum Monkey { Number(i64), Add(String, String), Sub(String, String), Mul(String, String), Div(String, String), } enum Calc { Number(i64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Var, } fn get_monkeys() -> HashMap { let mut monkeys = HashMap::new(); for line in lib::iter_input() { match line.split(" ").collect::>()[..] { [name, value] => { monkeys.insert( name.strip_suffix(":").unwrap().to_string(), Monkey::Number(value.parse().unwrap()), ); }, [name, a, "+", b] => { monkeys.insert( name.strip_suffix(":").unwrap().to_string(), Monkey::Add(a.to_string(), b.to_string()), ); }, [name, a, "-", b] => { monkeys.insert( name.strip_suffix(":").unwrap().to_string(), Monkey::Sub(a.to_string(), b.to_string()), ); }, [name, a, "*", b] => { monkeys.insert( name.strip_suffix(":").unwrap().to_string(), Monkey::Mul(a.to_string(), b.to_string()), ); }, [name, a, "/", b] => { monkeys.insert( name.strip_suffix(":").unwrap().to_string(), Monkey::Div(a.to_string(), b.to_string()), ); }, _ => unreachable!(), } } return monkeys; } fn calc(monkeys: &HashMap, name: &String, var: &str) -> Calc { // in my data, each name is only used once, so there is no point in caching if name == var { return Calc::Var; } return match monkeys.get(name).unwrap() { Monkey::Number(value) => Calc::Number(*value), Monkey::Add(a, b) => match (calc(monkeys, a, var), calc(monkeys, b, var)) { (Calc::Number(va), Calc::Number(vb)) => Calc::Number(va + vb), (ca, cb) => Calc::Add(Box::new(ca), Box::new(cb)), }, Monkey::Sub(a, b) => match (calc(monkeys, a, var), calc(monkeys, b, var)) { (Calc::Number(va), Calc::Number(vb)) => Calc::Number(va - vb), (ca, cb) => Calc::Sub(Box::new(ca), Box::new(cb)), }, Monkey::Mul(a, b) => match (calc(monkeys, a, var), calc(monkeys, b, var)) { (Calc::Number(va), Calc::Number(vb)) => Calc::Number(va * vb), (ca, cb) => Calc::Mul(Box::new(ca), Box::new(cb)), }, Monkey::Div(a, b) => match (calc(monkeys, a, var), calc(monkeys, b, var)) { (Calc::Number(va), Calc::Number(vb)) => Calc::Number(va / vb), (ca, cb) => Calc::Div(Box::new(ca), Box::new(cb)), }, }; } fn solve(value: i64, calc: Calc) -> i64 { let mut var = value; let mut rest = calc; loop { match rest { Calc::Add(left, right) => { match (*left, *right) { (Calc::Number(l), r) => { var -= l; rest = r; }, (l, Calc::Number(r)) => { var -= r; rest = l; }, _ => unreachable!(), } }, Calc::Sub(left, right) => { match (*left, *right) { (Calc::Number(l), r) => { var = l - var; rest = r; }, (l, Calc::Number(r)) => { var += r; rest = l; }, _ => unreachable!(), } }, Calc::Mul(left, right) => { match (*left, *right) { (Calc::Number(l), r) => { var /= l; rest = r; }, (l, Calc::Number(r)) => { var /= r; rest = l; }, _ => unreachable!(), } }, Calc::Div(left, right) => { match (*left, *right) { (Calc::Number(l), r) => { var = l / var; rest = r; }, (l, Calc::Number(r)) => { var *= r; rest = l; }, _ => unreachable!(), } }, Calc::Number(_) => unreachable!(), Calc::Var => { return var; }, } } } fn main() { let monkeys = get_monkeys(); match calc(&monkeys, &"root".to_string(), "") { Calc::Number(i) => println!("part1: {}", i), _ => unreachable!(), } let (left, right) = match calc(&monkeys, &"root".to_string(), "humn") { Calc::Add(left, right) => (left, right), Calc::Sub(left, right) => (left, right), Calc::Mul(left, right) => (left, right), Calc::Div(left, right) => (left, right), _ => unreachable!(), }; let (value, calc) = match (*left, *right) { (Calc::Number(value), calc) => (value, calc), (calc, Calc::Number(value)) => (value, calc), _ => unreachable!(), }; println!("part2: {}", solve(value, calc)); }