tangled
alpha
login
or
join now
jonas.tngl.sh
/
advent-of-code
0
fork
atom
Advent of Code solutions in Rust
0
fork
atom
overview
issues
pulls
pipelines
feat: day 14 of AoC 2020
jonas.tngl.sh
3 months ago
0792beff
b86fc2a8
1/1
rust.yml
success
3m 26s
+348
-2
2 changed files
expand all
collapse all
unified
split
aoc_2020
src
day14.rs
main.rs
+346
aoc_2020/src/day14.rs
···
1
1
+
use std::{collections::HashMap, fmt::Write};
2
2
+
3
3
+
use anyhow::Context as _;
4
4
+
use aoc_companion::prelude::*;
5
5
+
use itertools::Itertools as _;
6
6
+
7
7
+
pub(crate) struct Door {
8
8
+
program: Vec<Instruction>,
9
9
+
}
10
10
+
11
11
+
impl<'input> Solution<'input> for Door {
12
12
+
fn parse(input: &'input str) -> Result<Door> {
13
13
+
Ok(Door {
14
14
+
program: input.lines().map(|instr| instr.parse()).try_collect()?,
15
15
+
})
16
16
+
}
17
17
+
18
18
+
fn part1(&self) -> u64 {
19
19
+
total_memory(generate_writes::<DecoderV1>(self.program.iter().copied()))
20
20
+
}
21
21
+
22
22
+
fn part2(&self) -> u64 {
23
23
+
total_memory(generate_writes::<DecoderV2>(self.program.iter().copied()))
24
24
+
}
25
25
+
}
26
26
+
27
27
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28
28
+
enum Instruction {
29
29
+
SetMask(Mask),
30
30
+
Write(MemWrite),
31
31
+
}
32
32
+
33
33
+
impl std::str::FromStr for Instruction {
34
34
+
type Err = anyhow::Error;
35
35
+
36
36
+
fn from_str(s: &str) -> Result<Self> {
37
37
+
let Some((lhs, rhs)) = s.split_once('=') else {
38
38
+
anyhow::bail!("instruction does not contain assignment operator '='");
39
39
+
};
40
40
+
let lhs = lhs.trim_end();
41
41
+
let rhs = rhs.trim_start();
42
42
+
let (introducer, rest) = lhs.split_at_checked(4).unwrap_or((lhs, ""));
43
43
+
44
44
+
Ok(match introducer {
45
45
+
"mask" => Instruction::SetMask(rhs.parse()?),
46
46
+
"mem[" => Instruction::Write(MemWrite {
47
47
+
addr: rest
48
48
+
.strip_suffix(']')
49
49
+
.with_context(|| "missing closing bracket")?
50
50
+
.parse()
51
51
+
.with_context(|| "invalid memory address")?,
52
52
+
value: rhs.parse().with_context(|| "invalid value to write")?,
53
53
+
}),
54
54
+
_ => anyhow::bail!("illegal instruction, introduced by {introducer:?}"),
55
55
+
})
56
56
+
}
57
57
+
}
58
58
+
59
59
+
#[derive(Clone, Copy, PartialEq, Eq)]
60
60
+
struct Mask([Option<bool>; 36]);
61
61
+
62
62
+
impl std::str::FromStr for Mask {
63
63
+
type Err = anyhow::Error;
64
64
+
65
65
+
fn from_str(s: &str) -> Result<Self> {
66
66
+
// FIXME: This should use `try_from_iter_exact()`
67
67
+
aoc_utils::array::try_from_iter(s.chars().rev().map(|c| match c {
68
68
+
'X' => Ok(None),
69
69
+
'0' => Ok(Some(false)),
70
70
+
'1' => Ok(Some(true)),
71
71
+
_ => Err(anyhow::anyhow!("invalid mask character: {c:?}")),
72
72
+
}))?
73
73
+
.map_err(|v| anyhow::anyhow!("expected mask of length 36, got length {}", v.len()))
74
74
+
.map(Mask)
75
75
+
}
76
76
+
}
77
77
+
78
78
+
impl Default for Mask {
79
79
+
fn default() -> Self {
80
80
+
Self([None; 36])
81
81
+
}
82
82
+
}
83
83
+
84
84
+
impl std::fmt::Debug for Mask {
85
85
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86
86
+
struct MaskInner<'a>(&'a [Option<bool>]);
87
87
+
88
88
+
impl std::fmt::Debug for MaskInner<'_> {
89
89
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90
90
+
for b in self.0.iter().rev() {
91
91
+
f.write_char(match b {
92
92
+
None => 'X',
93
93
+
Some(false) => '0',
94
94
+
Some(true) => '1',
95
95
+
})?;
96
96
+
}
97
97
+
Ok(())
98
98
+
}
99
99
+
}
100
100
+
101
101
+
f.debug_tuple("Mask").field(&MaskInner(&self.0)).finish()
102
102
+
}
103
103
+
}
104
104
+
105
105
+
impl Mask {
106
106
+
fn bits(&self) -> impl Iterator<Item = (Option<bool>, u64)> {
107
107
+
self.0.iter().copied().enumerate().map(|(i, b)| (b, 1 << i))
108
108
+
}
109
109
+
}
110
110
+
111
111
+
trait ApplyMask {
112
112
+
fn apply(mask: Mask, mem_write: MemWrite) -> impl Iterator<Item = MemWrite>;
113
113
+
}
114
114
+
115
115
+
struct DecoderV1;
116
116
+
117
117
+
impl ApplyMask for DecoderV1 {
118
118
+
fn apply(mask: Mask, MemWrite { addr, value }: MemWrite) -> impl Iterator<Item = MemWrite> {
119
119
+
std::iter::once_with(move || MemWrite {
120
120
+
addr,
121
121
+
value: mask
122
122
+
.bits()
123
123
+
.filter_map(|(b, i)| Some((b?, i)))
124
124
+
.fold(value, |v, (b, i)| v ^ (v & i) ^ ((b as u64) * i)),
125
125
+
})
126
126
+
}
127
127
+
}
128
128
+
129
129
+
struct DecoderV2;
130
130
+
131
131
+
impl ApplyMask for DecoderV2 {
132
132
+
fn apply(mask: Mask, MemWrite { addr, value }: MemWrite) -> impl Iterator<Item = MemWrite> {
133
133
+
let addr = mask
134
134
+
.bits()
135
135
+
.filter_map(|(b, i)| (b == Some(true)).then_some(i))
136
136
+
.fold(addr, std::ops::BitOr::bitor);
137
137
+
138
138
+
let floating_bits = mask.bits().filter_map(|(b, i)| b.is_none().then_some(i));
139
139
+
140
140
+
floating_bits
141
141
+
.into_iter()
142
142
+
.map(|i| [0, i])
143
143
+
.multi_cartesian_product()
144
144
+
.map(move |bits| bits.iter().fold(addr, |a, b| a ^ b))
145
145
+
.map(move |addr| MemWrite { addr, value })
146
146
+
}
147
147
+
}
148
148
+
149
149
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
150
150
+
struct MemWrite {
151
151
+
addr: u64,
152
152
+
value: u64,
153
153
+
}
154
154
+
155
155
+
fn generate_writes<D: ApplyMask>(
156
156
+
program: impl IntoIterator<Item = Instruction>,
157
157
+
) -> impl Iterator<Item = MemWrite> {
158
158
+
program
159
159
+
.into_iter()
160
160
+
.scan(Mask::default(), move |mask, instr| match instr {
161
161
+
Instruction::SetMask(new_mask) => {
162
162
+
*mask = new_mask;
163
163
+
Some(None)
164
164
+
}
165
165
+
Instruction::Write(w) => Some(Some(D::apply(*mask, w))),
166
166
+
})
167
167
+
.flatten()
168
168
+
.flatten()
169
169
+
}
170
170
+
171
171
+
fn total_memory(mem_writes: impl IntoIterator<Item = MemWrite>) -> u64 {
172
172
+
let memory: HashMap<u64, u64> = mem_writes
173
173
+
.into_iter()
174
174
+
.map(|MemWrite { addr, value }| (addr, value))
175
175
+
.collect();
176
176
+
memory.into_values().sum()
177
177
+
}
178
178
+
179
179
+
#[cfg(test)]
180
180
+
mod tests {
181
181
+
use std::collections::HashSet;
182
182
+
183
183
+
use super::*;
184
184
+
185
185
+
const EXAMPLE_INPUT: &str = "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
186
186
+
mem[8] = 11
187
187
+
mem[7] = 101
188
188
+
mem[8] = 0";
189
189
+
190
190
+
const EXAMPLE_MASK: Mask = Mask([
191
191
+
None,
192
192
+
Some(false),
193
193
+
None,
194
194
+
None,
195
195
+
None,
196
196
+
None,
197
197
+
Some(true),
198
198
+
None,
199
199
+
None,
200
200
+
None,
201
201
+
None,
202
202
+
None,
203
203
+
None,
204
204
+
None,
205
205
+
None,
206
206
+
None,
207
207
+
None,
208
208
+
None,
209
209
+
None,
210
210
+
None,
211
211
+
None,
212
212
+
None,
213
213
+
None,
214
214
+
None,
215
215
+
None,
216
216
+
None,
217
217
+
None,
218
218
+
None,
219
219
+
None,
220
220
+
None,
221
221
+
None,
222
222
+
None,
223
223
+
None,
224
224
+
None,
225
225
+
None,
226
226
+
None,
227
227
+
]);
228
228
+
229
229
+
const EXAMPLE_PROGRAM: &[Instruction] = &[
230
230
+
Instruction::SetMask(EXAMPLE_MASK),
231
231
+
Instruction::Write(MemWrite { addr: 8, value: 11 }),
232
232
+
Instruction::Write(MemWrite {
233
233
+
addr: 7,
234
234
+
value: 101,
235
235
+
}),
236
236
+
Instruction::Write(MemWrite { addr: 8, value: 0 }),
237
237
+
];
238
238
+
239
239
+
const EXAMPLE_MEM_WRITES_V1: &[MemWrite] = &[
240
240
+
MemWrite { addr: 8, value: 73 },
241
241
+
MemWrite {
242
242
+
addr: 7,
243
243
+
value: 101,
244
244
+
},
245
245
+
MemWrite { addr: 8, value: 64 },
246
246
+
];
247
247
+
248
248
+
#[test]
249
249
+
fn mask_is_parsed_and_printed_consistently() {
250
250
+
const EXAMPLE_MASK_STR: &str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X";
251
251
+
assert_eq!(
252
252
+
format!("{:?}", EXAMPLE_MASK_STR.parse::<Mask>().unwrap()),
253
253
+
format!("Mask({EXAMPLE_MASK_STR})")
254
254
+
);
255
255
+
}
256
256
+
257
257
+
#[test]
258
258
+
fn parse_example_program() {
259
259
+
itertools::assert_equal(
260
260
+
Door::parse(EXAMPLE_INPUT).unwrap().program.iter(),
261
261
+
EXAMPLE_PROGRAM,
262
262
+
);
263
263
+
}
264
264
+
265
265
+
#[test]
266
266
+
fn apply_mask_v1() {
267
267
+
itertools::assert_equal(
268
268
+
DecoderV1::apply(EXAMPLE_MASK, MemWrite { addr: 8, value: 11 }),
269
269
+
[MemWrite { addr: 8, value: 73 }],
270
270
+
);
271
271
+
itertools::assert_equal(
272
272
+
DecoderV1::apply(
273
273
+
EXAMPLE_MASK,
274
274
+
MemWrite {
275
275
+
addr: 7,
276
276
+
value: 101,
277
277
+
},
278
278
+
),
279
279
+
[MemWrite {
280
280
+
addr: 7,
281
281
+
value: 101,
282
282
+
}],
283
283
+
);
284
284
+
itertools::assert_equal(
285
285
+
DecoderV1::apply(EXAMPLE_MASK, MemWrite { addr: 8, value: 0 }),
286
286
+
[MemWrite { addr: 8, value: 64 }],
287
287
+
);
288
288
+
}
289
289
+
290
290
+
#[test]
291
291
+
fn generate_example_writes_v1() {
292
292
+
itertools::equal(
293
293
+
generate_writes::<DecoderV1>(EXAMPLE_PROGRAM.iter().copied()),
294
294
+
EXAMPLE_MEM_WRITES_V1.iter().copied(),
295
295
+
);
296
296
+
}
297
297
+
298
298
+
#[test]
299
299
+
fn find_total_memory_for_example_v1() {
300
300
+
assert_eq!(total_memory(EXAMPLE_MEM_WRITES_V1.iter().copied()), 165);
301
301
+
}
302
302
+
303
303
+
#[test]
304
304
+
fn apply_mask_v2() {
305
305
+
let first_mask: Mask = "000000000000000000000000000000X1001X".parse().unwrap();
306
306
+
assert_eq!(
307
307
+
HashSet::from_iter(DecoderV2::apply(
308
308
+
first_mask,
309
309
+
MemWrite {
310
310
+
addr: 42,
311
311
+
value: 100,
312
312
+
},
313
313
+
)),
314
314
+
HashSet::from([
315
315
+
MemWrite {
316
316
+
addr: 26,
317
317
+
value: 100,
318
318
+
},
319
319
+
MemWrite {
320
320
+
addr: 27,
321
321
+
value: 100,
322
322
+
},
323
323
+
MemWrite {
324
324
+
addr: 58,
325
325
+
value: 100,
326
326
+
},
327
327
+
MemWrite {
328
328
+
addr: 59,
329
329
+
value: 100,
330
330
+
},
331
331
+
]),
332
332
+
);
333
333
+
}
334
334
+
335
335
+
#[test]
336
336
+
fn find_total_memory_for_example_v2() {
337
337
+
let Door { program } = Door::parse(
338
338
+
"mask = 000000000000000000000000000000X1001X
339
339
+
mem[42] = 100
340
340
+
mask = 00000000000000000000000000000000X0XX
341
341
+
mem[26] = 1",
342
342
+
)
343
343
+
.unwrap();
344
344
+
assert_eq!(total_memory(generate_writes::<DecoderV2>(program)), 208);
345
345
+
}
346
346
+
}
+2
-2
aoc_2020/src/main.rs
···
13
13
mod day11;
14
14
mod day12;
15
15
mod day13;
16
16
-
// mod day14;
16
16
+
mod day14;
17
17
// mod day15;
18
18
// mod day16;
19
19
// mod day17;
···
44
44
door!(2020-12-11 ~> day11),
45
45
door!(2020-12-12 ~> day12),
46
46
door!(2020-12-13 ~> day13),
47
47
-
// door!(2020-12-14 ~> day14),
47
47
+
door!(2020-12-14 ~> day14),
48
48
// door!(2020-12-15 ~> day15),
49
49
// door!(2020-12-16 ~> day16),
50
50
// door!(2020-12-17 ~> day17),