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.
1
40
use crate :: util:: hash:: * ;
2
41
use crate :: util:: parse:: * ;
3
42
@@ -43,11 +82,13 @@ pub struct Input {
43
82
pub fn parse ( input : & str ) -> Input {
44
83
let lines: Vec < _ > = input. lines ( ) . collect ( ) ;
45
84
85
+ // Assign each monkey an index on a first come first served basis.
46
86
let indices: FastMap < _ , _ > =
47
87
lines. iter ( ) . enumerate ( ) . map ( |( index, line) | ( & line[ 0 ..4 ] , index) ) . collect ( ) ;
48
88
49
89
let monkeys: Vec < _ > = lines. iter ( ) . map ( |line| Monkey :: parse ( & line[ 6 ..] , & indices) ) . collect ( ) ;
50
90
91
+ // We only need the specific indices of the root and human.
51
92
let root = indices[ "root" ] ;
52
93
let humn = indices[ "humn" ] ;
53
94
let mut input =
@@ -68,6 +109,7 @@ pub fn part2(input: &Input) -> i64 {
68
109
inverse ( input, * root, -1 )
69
110
}
70
111
112
+ /// Recursively compute the total following the tree structure all the way to the leaves.
71
113
fn compute ( input : & mut Input , index : usize ) -> i64 {
72
114
let result = match input. monkeys [ index] {
73
115
Monkey :: Number ( n) => n,
@@ -78,10 +120,13 @@ fn compute(input: &mut Input, index: usize) -> i64 {
78
120
Operation :: Div => compute ( input, left) / compute ( input, right) ,
79
121
} ,
80
122
} ;
123
+ // Cache the computed value for use in part two.
81
124
input. yell [ index] = result;
82
125
result
83
126
}
84
127
128
+ /// Recursively find the humn node then mark it and all its parents all the way to the
129
+ /// root as "unknown".
85
130
fn find ( input : & mut Input , humn : usize , index : usize ) -> bool {
86
131
let result = match input. monkeys [ index] {
87
132
Monkey :: Number ( _) => humn == index,
@@ -91,18 +136,25 @@ fn find(input: &mut Input, humn: usize, index: usize) -> bool {
91
136
result
92
137
}
93
138
139
+ /// Recursively finds the value of the expression on the "unknown" side so that it equals the
140
+ /// known side.
94
141
fn inverse ( input : & Input , index : usize , value : i64 ) -> i64 {
95
142
let Input { root, yell, unknown, monkeys } = input;
96
143
97
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.
98
147
Monkey :: Number ( _) => value,
148
+ // If we're the root then the left and right side must be equal.
99
149
Monkey :: Result ( left, _, right) if index == * root => {
100
150
if unknown[ left] {
101
151
inverse ( input, left, yell[ right] )
102
152
} else {
103
153
inverse ( input, right, yell[ left] )
104
154
}
105
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.
106
158
Monkey :: Result ( left, operation, right) => {
107
159
if unknown[ left] {
108
160
match operation {
0 commit comments