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 17 of AoC 2020
jonas.tngl.sh
3 months ago
1640e8a7
aac5fd04
1/1
rust.yml
success
3m 38s
+399
-2
3 changed files
expand all
collapse all
unified
split
aoc_2020
src
day17.rs
main.rs
aoc_utils
src
linalg.rs
+243
aoc_2020/src/day17.rs
···
1
1
+
use std::collections::HashSet;
2
2
+
3
3
+
use aoc_companion::prelude::*;
4
4
+
use aoc_utils::{geometry::Point, linalg::Vector};
5
5
+
use itertools::Itertools as _;
6
6
+
7
7
+
pub(crate) struct Door {
8
8
+
active_cubes: HashSet<Vector<i64, 3>>,
9
9
+
}
10
10
+
11
11
+
impl<'input> Solution<'input> for Door {
12
12
+
fn parse(input: &'input str) -> Result<Self> {
13
13
+
let plane = aoc_utils::geometry::parse_ascii_map(input)?;
14
14
+
let offset = Vector([plane.dim().1 as i64 / 2, plane.dim().0 as i64 / 2, 0]);
15
15
+
let active_cubes = plane
16
16
+
.indexed_iter()
17
17
+
.filter_map(|((y, x), &b)| (b == b'#').then_some(Vector([x, y]).try_cast_as()))
18
18
+
.map_ok(|p| p.embed() - offset)
19
19
+
.try_collect()?;
20
20
+
Ok(Self { active_cubes })
21
21
+
}
22
22
+
23
23
+
fn part1(&self) -> usize {
24
24
+
active_after_boot(self.active_cubes.clone())
25
25
+
}
26
26
+
27
27
+
fn part2(&self) -> usize {
28
28
+
active_after_boot(self.active_cubes.iter().map(|v| v.embed::<4>()).collect())
29
29
+
}
30
30
+
}
31
31
+
32
32
+
fn evolve<const N: usize>(active_cubes: &HashSet<Vector<i64, N>>) -> HashSet<Vector<i64, N>>
33
33
+
where
34
34
+
Vector<i64, N>: Point,
35
35
+
{
36
36
+
let mut new_cubes: HashSet<_> = active_cubes
37
37
+
.iter()
38
38
+
.copied()
39
39
+
.filter(|p| (2..=3).contains(&p.neighbors().filter(|n| active_cubes.contains(n)).count()))
40
40
+
.collect();
41
41
+
new_cubes.extend(
42
42
+
active_cubes
43
43
+
.iter()
44
44
+
.flat_map(|p| p.neighbors())
45
45
+
.filter(|p| !active_cubes.contains(p))
46
46
+
.filter(|p| p.neighbors().filter(|n| active_cubes.contains(n)).count() == 3),
47
47
+
);
48
48
+
new_cubes
49
49
+
}
50
50
+
51
51
+
fn active_after_boot<const N: usize>(active_cubes: HashSet<Vector<i64, N>>) -> usize
52
52
+
where
53
53
+
Vector<i64, N>: Point,
54
54
+
{
55
55
+
let after_boot = std::iter::successors(Some(active_cubes), |cubes| Some(evolve(cubes)))
56
56
+
.nth(6)
57
57
+
.unwrap();
58
58
+
after_boot.len()
59
59
+
}
60
60
+
61
61
+
#[cfg(test)]
62
62
+
mod tests {
63
63
+
use super::*;
64
64
+
65
65
+
const EXAMPLE_INPUT: &str = "\
66
66
+
.#.
67
67
+
..#
68
68
+
###";
69
69
+
70
70
+
const EXAMPLE_CUBES: [Vector<i64, 3>; 5] = [
71
71
+
Vector([0, -1, 0]),
72
72
+
Vector([1, 0, 0]),
73
73
+
Vector([-1, 1, 0]),
74
74
+
Vector([0, 1, 0]),
75
75
+
Vector([1, 1, 0]),
76
76
+
];
77
77
+
78
78
+
const EXAMPLE_AFTER_1: &[&str] = &[
79
79
+
"\
80
80
+
...
81
81
+
...
82
82
+
#..
83
83
+
..#
84
84
+
.#.",
85
85
+
"\
86
86
+
...
87
87
+
...
88
88
+
#.#
89
89
+
.##
90
90
+
.#.",
91
91
+
"\
92
92
+
...
93
93
+
...
94
94
+
#..
95
95
+
..#
96
96
+
.#.",
97
97
+
];
98
98
+
99
99
+
const EXAMPLE_AFTER_2: &[&str] = &[
100
100
+
"\
101
101
+
.....
102
102
+
.....
103
103
+
.....
104
104
+
.....
105
105
+
..#..
106
106
+
.....
107
107
+
.....",
108
108
+
"\
109
109
+
.....
110
110
+
.....
111
111
+
..#..
112
112
+
.#..#
113
113
+
....#
114
114
+
.#...
115
115
+
.....",
116
116
+
"\
117
117
+
.....
118
118
+
.....
119
119
+
##...
120
120
+
##...
121
121
+
#....
122
122
+
....#
123
123
+
.###.",
124
124
+
"\
125
125
+
.....
126
126
+
.....
127
127
+
..#..
128
128
+
.#..#
129
129
+
....#
130
130
+
.#...
131
131
+
.....",
132
132
+
"\
133
133
+
.....
134
134
+
.....
135
135
+
.....
136
136
+
.....
137
137
+
..#..
138
138
+
.....
139
139
+
.....",
140
140
+
];
141
141
+
142
142
+
const EXAMPLE_AFTER_3: &[&str] = &[
143
143
+
"\
144
144
+
.......
145
145
+
.......
146
146
+
.......
147
147
+
.......
148
148
+
..##...
149
149
+
..###..
150
150
+
.......
151
151
+
.......
152
152
+
.......",
153
153
+
"\
154
154
+
.......
155
155
+
.......
156
156
+
..#....
157
157
+
...#...
158
158
+
#......
159
159
+
.....##
160
160
+
.#...#.
161
161
+
..#.#..
162
162
+
...#...",
163
163
+
"\
164
164
+
.......
165
165
+
.......
166
166
+
...#...
167
167
+
.......
168
168
+
#......
169
169
+
.......
170
170
+
.....##
171
171
+
.##.#..
172
172
+
...#...",
173
173
+
"\
174
174
+
.......
175
175
+
.......
176
176
+
..#....
177
177
+
...#...
178
178
+
#......
179
179
+
.....##
180
180
+
.#...#.
181
181
+
..#.#..
182
182
+
...#...",
183
183
+
"\
184
184
+
.......
185
185
+
.......
186
186
+
.......
187
187
+
.......
188
188
+
..##...
189
189
+
..###..
190
190
+
.......
191
191
+
.......
192
192
+
.......",
193
193
+
];
194
194
+
195
195
+
#[test]
196
196
+
fn parse_example_input() {
197
197
+
let Door { active_cubes } = Door::parse(EXAMPLE_INPUT).unwrap();
198
198
+
assert_eq!(active_cubes, HashSet::from(EXAMPLE_CUBES));
199
199
+
}
200
200
+
201
201
+
fn from_z_layers(layers: &[&str]) -> HashSet<Vector<i64, 3>> {
202
202
+
layers
203
203
+
.iter()
204
204
+
.enumerate()
205
205
+
.flat_map(|(z, layer)| {
206
206
+
let Door { active_cubes } = Door::parse(layer).unwrap();
207
207
+
active_cubes
208
208
+
.into_iter()
209
209
+
.map(move |p| p + Vector([0, 0, z as i64 - layers.len() as i64 / 2]))
210
210
+
})
211
211
+
.collect()
212
212
+
}
213
213
+
214
214
+
#[test]
215
215
+
fn evolve_example() {
216
216
+
itertools::assert_equal(
217
217
+
std::iter::successors(Some(HashSet::from(EXAMPLE_CUBES)), |cubes| {
218
218
+
Some(evolve(cubes))
219
219
+
})
220
220
+
.take(4),
221
221
+
[
222
222
+
&[EXAMPLE_INPUT],
223
223
+
EXAMPLE_AFTER_1,
224
224
+
EXAMPLE_AFTER_2,
225
225
+
EXAMPLE_AFTER_3,
226
226
+
]
227
227
+
.map(from_z_layers),
228
228
+
);
229
229
+
}
230
230
+
231
231
+
#[test]
232
232
+
fn active_after_boot_in_3d() {
233
233
+
assert_eq!(active_after_boot(HashSet::from(EXAMPLE_CUBES)), 112);
234
234
+
}
235
235
+
236
236
+
#[test]
237
237
+
fn active_after_boot_in_4d() {
238
238
+
assert_eq!(
239
239
+
active_after_boot(HashSet::from(EXAMPLE_CUBES.map(|v| v.embed::<4>()))),
240
240
+
848
241
241
+
);
242
242
+
}
243
243
+
}
+2
-2
aoc_2020/src/main.rs
···
16
16
mod day14;
17
17
mod day15;
18
18
mod day16;
19
19
-
// mod day17;
19
19
+
mod day17;
20
20
// mod day18;
21
21
// mod day19;
22
22
// mod day20;
···
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),
50
50
+
door!(2020-12-17 ~> day17),
51
51
// door!(2020-12-18 ~> day18),
52
52
// door!(2020-12-19 ~> day19),
53
53
// door!(2020-12-20 ~> day20),
+154
aoc_utils/src/linalg.rs
···
44
44
Ok(Vector(results.map(|res| res.unwrap())))
45
45
}
46
46
}
47
47
+
48
48
+
pub fn embed<const M: usize>(self) -> Vector<T, M> {
49
49
+
let mut res = Vector::new();
50
50
+
51
51
+
for (dest, elem) in res.iter_mut().zip(self.into_iter()) {
52
52
+
*dest = elem;
53
53
+
}
54
54
+
55
55
+
res
56
56
+
}
47
57
}
48
58
49
59
impl<T, const N: usize> Default for Vector<T, N>
···
525
535
impl_point_3d!(i128);
526
536
impl_point_3d!(isize);
527
537
538
538
+
macro_rules! impl_point_4d {
539
539
+
($num:ty) => {
540
540
+
impl Point for Vector<$num, 4> {
541
541
+
fn neighbors(self) -> Neighbors<Self> {
542
542
+
const NN: &[Vector<$num, 4>] = &[
543
543
+
Vector([-1, -1, -1, -1]),
544
544
+
Vector([-1, -1, -1, 0]),
545
545
+
Vector([-1, -1, -1, 1]),
546
546
+
Vector([-1, -1, 0, -1]),
547
547
+
Vector([-1, -1, 0, 0]),
548
548
+
Vector([-1, -1, 0, 1]),
549
549
+
Vector([-1, -1, 1, -1]),
550
550
+
Vector([-1, -1, 1, 0]),
551
551
+
Vector([-1, -1, 1, 1]),
552
552
+
Vector([-1, 0, -1, -1]),
553
553
+
Vector([-1, 0, -1, 0]),
554
554
+
Vector([-1, 0, -1, 1]),
555
555
+
Vector([-1, 0, 0, -1]),
556
556
+
Vector([-1, 0, 0, 0]),
557
557
+
Vector([-1, 0, 0, 1]),
558
558
+
Vector([-1, 0, 1, -1]),
559
559
+
Vector([-1, 0, 1, 0]),
560
560
+
Vector([-1, 0, 1, 1]),
561
561
+
Vector([-1, 1, -1, -1]),
562
562
+
Vector([-1, 1, -1, 0]),
563
563
+
Vector([-1, 1, -1, 1]),
564
564
+
Vector([-1, 1, 0, -1]),
565
565
+
Vector([-1, 1, 0, 0]),
566
566
+
Vector([-1, 1, 0, 1]),
567
567
+
Vector([-1, 1, 1, -1]),
568
568
+
Vector([-1, 1, 1, 0]),
569
569
+
Vector([-1, 1, 1, 1]),
570
570
+
Vector([0, -1, -1, -1]),
571
571
+
Vector([0, -1, -1, 0]),
572
572
+
Vector([0, -1, -1, 1]),
573
573
+
Vector([0, -1, 0, -1]),
574
574
+
Vector([0, -1, 0, 0]),
575
575
+
Vector([0, -1, 0, 1]),
576
576
+
Vector([0, -1, 1, -1]),
577
577
+
Vector([0, -1, 1, 0]),
578
578
+
Vector([0, -1, 1, 1]),
579
579
+
Vector([0, 0, -1, -1]),
580
580
+
Vector([0, 0, -1, 0]),
581
581
+
Vector([0, 0, -1, 1]),
582
582
+
Vector([0, 0, 0, -1]),
583
583
+
Vector([0, 0, 0, 1]),
584
584
+
Vector([0, 0, 1, -1]),
585
585
+
Vector([0, 0, 1, 0]),
586
586
+
Vector([0, 0, 1, 1]),
587
587
+
Vector([0, 1, -1, -1]),
588
588
+
Vector([0, 1, -1, 0]),
589
589
+
Vector([0, 1, -1, 1]),
590
590
+
Vector([0, 1, 0, -1]),
591
591
+
Vector([0, 1, 0, 0]),
592
592
+
Vector([0, 1, 0, 1]),
593
593
+
Vector([0, 1, 1, -1]),
594
594
+
Vector([0, 1, 1, 0]),
595
595
+
Vector([0, 1, 1, 1]),
596
596
+
Vector([1, -1, -1, -1]),
597
597
+
Vector([1, -1, -1, 0]),
598
598
+
Vector([1, -1, -1, 1]),
599
599
+
Vector([1, -1, 0, -1]),
600
600
+
Vector([1, -1, 0, 0]),
601
601
+
Vector([1, -1, 0, 1]),
602
602
+
Vector([1, -1, 1, -1]),
603
603
+
Vector([1, -1, 1, 0]),
604
604
+
Vector([1, -1, 1, 1]),
605
605
+
Vector([1, 0, -1, -1]),
606
606
+
Vector([1, 0, -1, 0]),
607
607
+
Vector([1, 0, -1, 1]),
608
608
+
Vector([1, 0, 0, -1]),
609
609
+
Vector([1, 0, 0, 0]),
610
610
+
Vector([1, 0, 0, 1]),
611
611
+
Vector([1, 0, 1, -1]),
612
612
+
Vector([1, 0, 1, 0]),
613
613
+
Vector([1, 0, 1, 1]),
614
614
+
Vector([1, 1, -1, -1]),
615
615
+
Vector([1, 1, -1, 0]),
616
616
+
Vector([1, 1, -1, 1]),
617
617
+
Vector([1, 1, 0, -1]),
618
618
+
Vector([1, 1, 0, 0]),
619
619
+
Vector([1, 1, 0, 1]),
620
620
+
Vector([1, 1, 1, -1]),
621
621
+
Vector([1, 1, 1, 0]),
622
622
+
Vector([1, 1, 1, 1]),
623
623
+
];
624
624
+
Neighbors {
625
625
+
center: self,
626
626
+
rel_iter: NN.iter(),
627
627
+
}
628
628
+
}
629
629
+
630
630
+
fn nearest_neighbors(self) -> Neighbors<Self> {
631
631
+
unimplemented!()
632
632
+
}
633
633
+
634
634
+
fn next_nearest_neighbors(self) -> Neighbors<Self> {
635
635
+
unimplemented!()
636
636
+
}
637
637
+
}
638
638
+
};
639
639
+
}
640
640
+
641
641
+
impl_point_4d!(i8);
642
642
+
impl_point_4d!(i16);
643
643
+
impl_point_4d!(i32);
644
644
+
impl_point_4d!(i64);
645
645
+
impl_point_4d!(i128);
646
646
+
impl_point_4d!(isize);
647
647
+
528
648
unsafe impl<const N: usize> ndarray::NdIndex<ndarray::Dim<[usize; N]>> for Vector<usize, N>
529
649
where
530
650
[usize; N]: ndarray::NdIndex<ndarray::Dim<[usize; N]>>,
···
544
664
545
665
#[cfg(test)]
546
666
mod tests {
667
667
+
use std::collections::HashSet;
668
668
+
547
669
use super::*;
548
670
use assert_matches::assert_matches;
549
671
use itertools::assert_equal;
···
802
924
Vector([3, -5]),
803
925
],
804
926
);
927
927
+
}
928
928
+
929
929
+
#[test]
930
930
+
fn vector_3d_neighbors_covers_cube() {
931
931
+
let center = Vector([5, -6, 3]);
932
932
+
let cube: HashSet<_> = [[-1, 0, 1]; 3]
933
933
+
.iter()
934
934
+
.multi_cartesian_product()
935
935
+
.map(|cs| {
936
936
+
let v = Vector(crate::array::from_iter_exact(cs.into_iter().copied()).unwrap());
937
937
+
center + v
938
938
+
})
939
939
+
.collect();
940
940
+
assert_eq!(cube.len(), 27);
941
941
+
let center_and_neighbors: HashSet<_> = center.neighbors().chain([center]).collect();
942
942
+
assert_eq!(center_and_neighbors, cube);
943
943
+
}
944
944
+
945
945
+
#[test]
946
946
+
fn vector_4d_neighbors_covers_cube() {
947
947
+
let center = Vector([5, -6, 3, -1]);
948
948
+
let cube: HashSet<_> = [[-1, 0, 1]; 4]
949
949
+
.iter()
950
950
+
.multi_cartesian_product()
951
951
+
.map(|cs| {
952
952
+
let v = Vector(crate::array::from_iter_exact(cs.into_iter().copied()).unwrap());
953
953
+
center + v
954
954
+
})
955
955
+
.collect();
956
956
+
assert_eq!(cube.len(), 81);
957
957
+
let center_and_neighbors: HashSet<_> = center.neighbors().chain([center]).collect();
958
958
+
assert_eq!(center_and_neighbors, cube);
805
959
}
806
960
}