Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/advent-of-code-2021
Path: blob/main/src/day18.rs
97 views
1
pub fn run() {
2
let snailfish_nums: Vec<_> = include_str!("inputs/day18")
3
.lines()
4
.map(parse_snailfish_number)
5
.collect();
6
7
let mut sum = snailfish_nums[0].clone();
8
for num in &snailfish_nums[1..] {
9
sum = add(&sum, num);
10
}
11
println!("{}", magnitude(&sum));
12
13
let mut max_magnitude = 0;
14
for n1 in snailfish_nums.iter() {
15
for n2 in snailfish_nums.iter() {
16
max_magnitude = max_magnitude.max(magnitude(&add(n1, n2)))
17
}
18
}
19
println!("{}", max_magnitude);
20
}
21
22
// Instead of modelling snailfish numbers as tree-like structures, just
23
// represent them as a list of tokens and then manipulate them symbolically.
24
type SnailfishTokens = Vec<Token>;
25
26
#[derive(Debug, Clone, Copy)]
27
enum Token {
28
LParen,
29
RParen,
30
Number(u32),
31
}
32
33
fn parse_snailfish_number(s: &str) -> SnailfishTokens {
34
s.chars()
35
.filter_map(|ch| match ch {
36
'[' => Some(Token::LParen),
37
']' => Some(Token::RParen),
38
'0'..='9' => Some(Token::Number(ch.to_digit(10).unwrap())),
39
_ => None,
40
})
41
.collect()
42
}
43
44
fn add(lhs: &SnailfishTokens, rhs: &SnailfishTokens) -> SnailfishTokens {
45
let mut result = Vec::with_capacity(lhs.len() + rhs.len() + 2);
46
result.push(Token::LParen);
47
result.extend(lhs);
48
result.extend(rhs);
49
result.push(Token::RParen);
50
51
reduce(&mut result);
52
result
53
}
54
55
fn reduce(num: &mut SnailfishTokens) {
56
loop {
57
if explode(num) {
58
continue;
59
}
60
if split(num) {
61
continue;
62
}
63
break;
64
}
65
}
66
67
fn explode(tokens: &mut SnailfishTokens) -> bool {
68
let mut explode_index = None;
69
let mut depth = 0;
70
for (i, token) in tokens.iter().enumerate() {
71
match token {
72
Token::LParen => {
73
if depth == 4 {
74
explode_index = Some(i);
75
break;
76
}
77
depth += 1;
78
}
79
Token::RParen => depth -= 1,
80
_ => {}
81
}
82
}
83
if let Some(index) = explode_index {
84
match &tokens[index..index + 4] {
85
&[Token::LParen, Token::Number(exploded_l), Token::Number(exploded_r), Token::RParen] => {
86
tokens.splice(index..index + 4, [Token::Number(0)]);
87
for i in (0..index).rev() {
88
if let Token::Number(first_num_left) = tokens[i] {
89
tokens[i] = Token::Number(first_num_left + exploded_l);
90
break;
91
}
92
}
93
for i in index + 1..tokens.len() {
94
if let Token::Number(first_num_right) = tokens[i] {
95
tokens[i] = Token::Number(first_num_right + exploded_r);
96
break;
97
}
98
}
99
}
100
unexpected => panic!("unexpected pattern on explosion {:?}", unexpected),
101
}
102
return true;
103
}
104
false
105
}
106
107
fn split(tokens: &mut SnailfishTokens) -> bool {
108
for (index, token) in tokens.iter().enumerate() {
109
match token {
110
&Token::Number(n) if n > 9 => {
111
tokens.splice(
112
index..=index,
113
[
114
Token::LParen,
115
Token::Number(n / 2),
116
Token::Number((n + 1) / 2),
117
Token::RParen,
118
],
119
);
120
return true;
121
}
122
_ => {}
123
}
124
}
125
false
126
}
127
128
fn magnitude(tokens: &SnailfishTokens) -> u32 {
129
// Keep a stack of magnitudes to the left of the current token. Whenever a
130
// pair closes, pop the last two magnitudes and compute the pair's magnitude.
131
let mut stack = vec![];
132
for token in tokens {
133
match token {
134
Token::Number(n) => stack.push(*n),
135
Token::RParen => {
136
let r = stack.pop().unwrap();
137
let l = stack.pop().unwrap();
138
stack.push(3 * l + 2 * r);
139
}
140
_ => {}
141
}
142
}
143
assert_eq!(stack.len(), 1);
144
stack[0]
145
}
146
147