aoc/year2022/
day21.rs

1//! # Monkey Math
2//!
3//! The Monkeys form a [binary tree](https://en.wikipedia.org/wiki/Binary_tree). We first
4//! compute the result by recursively following the strucuture all the ways to the leaves.
5//! We also find the `humn` node and all its parents the same way, marking them as "unknown".
6//!
7//! For part two we know that the value on the left and the right of the root must be equal.
8//! Following the tree down the path previously marked "unknown" we recursively solve
9//! equations until we reached the `humn` node.
10//!
11//! For example say the root's children are `a` and `b`
12//!
13//! ```none
14//!     yell[a] = 6
15//!     unknown[a] = false
16//!     yell[b] = 5
17//!     unknown[b] = true
18//! ```
19//!
20//! So this implies `b` is a parent of `humn` and must equal `6` to pass (the current value is
21//! irrelevant). We then recursively look at the children of `b`:
22//!
23//! ```none
24//!     yell[c] = 4
25//!     unknown[a] = true
26//!     operation = "+"
27//!     yell[d] = 4
28//!     unknown[b] = false
29//! ```
30//!
31//! We know that `c + d` must equal 6 so this implies `c = 2`. We then recursively look at the
32//! children of `c`
33//!
34//! ```none
35//!     yell[humn] = 123
36//!     unknown[a] = true
37//! ```
38//!
39//! Once we finally reach the `humn` node the value that we currently have `2` is the answer.
40use crate::util::hash::*;
41use crate::util::parse::*;
42
43#[derive(Clone, Copy)]
44enum Operation {
45    Add,
46    Sub,
47    Mul,
48    Div,
49}
50
51enum Monkey {
52    Number(i64),
53    Result(usize, Operation, usize),
54}
55
56impl Monkey {
57    fn parse(str: &str, indices: &FastMap<&str, usize>) -> Monkey {
58        if str.len() < 11 {
59            Monkey::Number(str.signed())
60        } else {
61            let left = indices[&str[0..4]];
62            let right = indices[&str[7..11]];
63            let operation = match str.as_bytes()[5] {
64                b'+' => Operation::Add,
65                b'-' => Operation::Sub,
66                b'*' => Operation::Mul,
67                b'/' => Operation::Div,
68                _ => unreachable!(),
69            };
70            Monkey::Result(left, operation, right)
71        }
72    }
73}
74
75pub struct Input {
76    root: usize,
77    monkeys: Vec<Monkey>,
78    yell: Vec<i64>,
79    unknown: Vec<bool>,
80}
81
82pub fn parse(input: &str) -> Input {
83    let lines: Vec<_> = input.lines().collect();
84
85    // Assign each monkey an index on a first come first served basis.
86    let indices: FastMap<_, _> =
87        lines.iter().enumerate().map(|(index, line)| (&line[0..4], index)).collect();
88
89    let monkeys: Vec<_> = lines.iter().map(|line| Monkey::parse(&line[6..], &indices)).collect();
90
91    // We only need the specific indices of the root and human.
92    let root = indices["root"];
93    let humn = indices["humn"];
94    let mut input =
95        Input { root, monkeys, yell: vec![0; lines.len()], unknown: vec![false; lines.len()] };
96
97    compute(&mut input, root);
98    find(&mut input, humn, root);
99    input
100}
101
102pub fn part1(input: &Input) -> i64 {
103    let Input { yell, root, .. } = input;
104    yell[*root]
105}
106
107pub fn part2(input: &Input) -> i64 {
108    let Input { root, .. } = input;
109    inverse(input, *root, -1)
110}
111
112/// Recursively compute the total following the tree structure all the way to the leaves.
113fn compute(input: &mut Input, index: usize) -> i64 {
114    let result = match input.monkeys[index] {
115        Monkey::Number(n) => n,
116        Monkey::Result(left, operation, right) => match operation {
117            Operation::Add => compute(input, left) + compute(input, right),
118            Operation::Sub => compute(input, left) - compute(input, right),
119            Operation::Mul => compute(input, left) * compute(input, right),
120            Operation::Div => compute(input, left) / compute(input, right),
121        },
122    };
123    // Cache the computed value for use in part two.
124    input.yell[index] = result;
125    result
126}
127
128/// Recursively find the humn node then mark it and all its parents all the way to the
129/// root as "unknown".
130fn find(input: &mut Input, humn: usize, index: usize) -> bool {
131    let result = match input.monkeys[index] {
132        Monkey::Number(_) => humn == index,
133        Monkey::Result(left, _, right) => find(input, humn, left) || find(input, humn, right),
134    };
135    input.unknown[index] = result;
136    result
137}
138
139/// Recursively finds the value of the expression on the "unknown" side so that it equals the
140/// known side.
141fn inverse(input: &Input, index: usize, value: i64) -> i64 {
142    let Input { root, yell, unknown, monkeys } = input;
143
144    match monkeys[index] {
145        // The only leaf node we'll actually ever reach is the "humn" node so the value at this
146        // point is the answer.
147        Monkey::Number(_) => value,
148        // If we're the root then the left and right side must be equal.
149        Monkey::Result(left, _, right) if index == *root => {
150            if unknown[left] {
151                inverse(input, left, yell[right])
152            } else {
153                inverse(input, right, yell[left])
154            }
155        }
156        // Addition and multiplication are commutative, but subtraction and division are not,
157        // so we have to handle unknowns on the right and left differently.
158        Monkey::Result(left, operation, right) => {
159            if unknown[left] {
160                match operation {
161                    Operation::Add => inverse(input, left, value - yell[right]),
162                    Operation::Sub => inverse(input, left, value + yell[right]),
163                    Operation::Mul => inverse(input, left, value / yell[right]),
164                    Operation::Div => inverse(input, left, value * yell[right]),
165                }
166            } else {
167                match operation {
168                    Operation::Add => inverse(input, right, value - yell[left]),
169                    Operation::Sub => inverse(input, right, yell[left] - value),
170                    Operation::Mul => inverse(input, right, value / yell[left]),
171                    Operation::Div => inverse(input, right, yell[left] / value),
172                }
173            }
174        }
175    }
176}