Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/advent-of-code-2021
Path: blob/main/src/day18_oo.rs
97 views
1
use serde_json::Value;
2
use std::fmt;
3
use std::rc::Rc;
4
5
// This day 18 solution uses a quite OO approach to modeling and delegation.
6
// The main problem of it is that for proper memory management, it's either
7
// necessary use `Box`es everywhere and constantly copy data for each boxed
8
// pointer to own a single object; or use reference counting and avoid
9
// unnecessary copies.
10
pub fn run() {
11
let snailfish_nums: Vec<Rc<SnailfishNumber>> = include_str!("inputs/day18")
12
.lines()
13
.map(|l| Rc::new(SnailfishNumber::from_str(l)))
14
.collect();
15
16
let mut sum = snailfish_nums[0].clone();
17
for num in &snailfish_nums[1..] {
18
sum = add(sum, num.clone());
19
}
20
21
println!("{}", sum.magnitude());
22
23
let mut max_magnitude = 0;
24
for n1 in snailfish_nums.iter() {
25
for n2 in snailfish_nums.iter() {
26
max_magnitude = max_magnitude.max(add(n1.clone(), n2.clone()).magnitude())
27
}
28
}
29
println!("{}", max_magnitude);
30
}
31
32
fn add(lhs: Rc<SnailfishNumber>, rhs: Rc<SnailfishNumber>) -> Rc<SnailfishNumber> {
33
reduce(Rc::new(SnailfishNumber::Pair((lhs, rhs))))
34
}
35
36
fn reduce(num: Rc<SnailfishNumber>) -> Rc<SnailfishNumber> {
37
let mut result = num;
38
loop {
39
if let Some(explode_result) = result.try_explode(0) {
40
result = Rc::new(explode_result.result);
41
continue;
42
}
43
if let Some(split_result) = result.try_split() {
44
result = Rc::new(split_result);
45
continue;
46
}
47
break;
48
}
49
result
50
}
51
52
#[derive(Debug)]
53
enum SnailfishNumber {
54
Number(u32),
55
Pair((Rc<SnailfishNumber>, Rc<SnailfishNumber>)),
56
}
57
58
struct ExplodeResult {
59
result: SnailfishNumber,
60
left: Option<u32>,
61
right: Option<u32>,
62
}
63
64
impl SnailfishNumber {
65
fn from_str(s: &str) -> SnailfishNumber {
66
Self::from_json(&serde_json::from_str(s).unwrap())
67
}
68
69
fn from_json(json: &Value) -> SnailfishNumber {
70
match json {
71
Value::Number(n) => SnailfishNumber::Number(n.as_u64().unwrap() as u32),
72
Value::Array(pair) => {
73
let left = Self::from_json(&pair[0]);
74
let right = Self::from_json(&pair[1]);
75
SnailfishNumber::Pair((Rc::new(left), Rc::new(right)))
76
}
77
_ => unreachable!(),
78
}
79
}
80
81
fn try_explode(&self, depth: usize) -> Option<ExplodeResult> {
82
match self {
83
SnailfishNumber::Number(_) => None,
84
SnailfishNumber::Pair((left, right)) => {
85
if depth == 4 {
86
if let (SnailfishNumber::Number(l), SnailfishNumber::Number(r)) =
87
(left.as_ref(), right.as_ref())
88
{
89
return Some(ExplodeResult {
90
result: SnailfishNumber::Number(0),
91
left: Some(*l),
92
right: Some(*r),
93
});
94
}
95
}
96
97
if let Some(explode_result) = left.try_explode(depth + 1) {
98
let new_right = if let Some(right_overflow) = explode_result.right {
99
right.add_to_left(right_overflow)
100
} else {
101
right.clone()
102
};
103
Some(ExplodeResult {
104
result: SnailfishNumber::Pair((Rc::new(explode_result.result), new_right)),
105
left: explode_result.left,
106
right: None,
107
})
108
} else if let Some(explode_result) = right.try_explode(depth + 1) {
109
let new_left = if let Some(left_overflow) = explode_result.left {
110
left.add_to_right(left_overflow)
111
} else {
112
left.clone()
113
};
114
Some(ExplodeResult {
115
result: SnailfishNumber::Pair((new_left, Rc::new(explode_result.result))),
116
left: None,
117
right: explode_result.right,
118
})
119
} else {
120
None
121
}
122
}
123
}
124
}
125
126
fn add_to_left(&self, n: u32) -> Rc<SnailfishNumber> {
127
Rc::new(match self {
128
SnailfishNumber::Number(n2) => (SnailfishNumber::Number(n + n2)),
129
SnailfishNumber::Pair((left, right)) => {
130
SnailfishNumber::Pair((left.add_to_left(n), right.clone()))
131
}
132
})
133
}
134
135
fn add_to_right(&self, n: u32) -> Rc<SnailfishNumber> {
136
Rc::new(match self {
137
SnailfishNumber::Number(n2) => SnailfishNumber::Number(n + n2),
138
SnailfishNumber::Pair((left, right)) => {
139
SnailfishNumber::Pair((left.clone(), right.add_to_right(n)))
140
}
141
})
142
}
143
144
fn try_split(&self) -> Option<SnailfishNumber> {
145
match self {
146
SnailfishNumber::Number(n) => {
147
if *n > 9 {
148
let left = SnailfishNumber::Number(n / 2);
149
let right = SnailfishNumber::Number((n + 1) / 2);
150
Some(SnailfishNumber::Pair((Rc::new(left), Rc::new(right))))
151
} else {
152
None
153
}
154
}
155
SnailfishNumber::Pair((left, right)) => {
156
if let Some(split_result) = left.try_split() {
157
Some(SnailfishNumber::Pair((
158
Rc::new(split_result),
159
right.clone(),
160
)))
161
} else if let Some(split_result) = right.try_split() {
162
Some(SnailfishNumber::Pair((left.clone(), Rc::new(split_result))))
163
} else {
164
None
165
}
166
}
167
}
168
}
169
170
fn magnitude(&self) -> u32 {
171
match self {
172
SnailfishNumber::Number(n) => *n,
173
SnailfishNumber::Pair((left, right)) => 3 * left.magnitude() + 2 * right.magnitude(),
174
}
175
}
176
}
177
178
impl fmt::Display for SnailfishNumber {
179
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180
match self {
181
SnailfishNumber::Number(n) => write!(f, "{}", n),
182
SnailfishNumber::Pair((l, r)) => write!(f, "[{},{}]", l, r),
183
}
184
}
185
}
186
187