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: part 2 of day 11 of AoC 2020
jonas.tngl.sh
1 year ago
8ec557dd
7f9b892e
+80
-20
1 changed file
expand all
collapse all
unified
split
aoc_2020
src
day11.rs
+80
-20
aoc_2020/src/day11.rs
reviewed
···
1
1
-
use std::collections::HashSet;
1
1
+
use std::{array, collections::HashSet, ops::RangeInclusive};
2
2
3
3
use aoc_companion::prelude::*;
4
4
use aoc_utils::{geometry::Point, linalg::Vector};
···
24
24
}
25
25
26
26
fn part1(&self) -> usize {
27
27
-
fixed_point_occupancy(&self.seats).len()
27
27
+
fixed_point_occupancy(&self.seats, &DirectNeighborSeatPolicy).len()
28
28
+
}
29
29
+
30
30
+
fn part2(&self) -> usize {
31
31
+
fixed_point_occupancy(&self.seats, &SightlineSeatPolicy::new(&self.seats)).len()
32
32
+
}
33
33
+
}
34
34
+
35
35
+
trait SeatPolicy {
36
36
+
const THRESHOLD: usize;
37
37
+
fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>>;
38
38
+
}
39
39
+
40
40
+
struct DirectNeighborSeatPolicy;
41
41
+
42
42
+
impl SeatPolicy for DirectNeighborSeatPolicy {
43
43
+
const THRESHOLD: usize = 4;
44
44
+
45
45
+
fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>> {
46
46
+
seat.neighbors()
28
47
}
29
48
}
30
49
31
31
-
fn evolve(
50
50
+
struct SightlineSeatPolicy<'s> {
51
51
+
seats: &'s HashSet<Vector<usize, 2>>,
52
52
+
bounds: [RangeInclusive<usize>; 2],
53
53
+
}
54
54
+
55
55
+
impl<'s> SightlineSeatPolicy<'s> {
56
56
+
fn new(seats: &'s HashSet<Vector<usize, 2>>) -> Self {
57
57
+
Self {
58
58
+
seats,
59
59
+
bounds: array::from_fn(|i| 0..=seats.iter().map(|s| s[i]).max().unwrap_or(0)),
60
60
+
}
61
61
+
}
62
62
+
}
63
63
+
64
64
+
impl SeatPolicy for SightlineSeatPolicy<'_> {
65
65
+
const THRESHOLD: usize = 5;
66
66
+
67
67
+
fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>> {
68
68
+
seat.neighbors().filter_map(move |neighbor| {
69
69
+
let dir = neighbor - seat;
70
70
+
(1..)
71
71
+
.map(|n| seat + dir * n)
72
72
+
.take_while(|s| s.in_bounds(&self.bounds))
73
73
+
.find(|s| self.seats.contains(s))
74
74
+
})
75
75
+
}
76
76
+
}
77
77
+
78
78
+
fn evolve<P: SeatPolicy>(
32
79
occupied: &HashSet<Vector<usize, 2>>,
33
80
seats: &HashSet<Vector<usize, 2>>,
81
81
+
policy: &P,
34
82
) -> HashSet<Vector<usize, 2>> {
35
83
let empty = seats.difference(occupied);
36
84
empty
37
85
.copied()
38
38
-
.filter(|seat| seat.neighbors().all(|n| !occupied.contains(&n)))
39
39
-
.chain(
40
40
-
occupied
41
41
-
.iter()
42
42
-
.copied()
43
43
-
.filter(|seat| seat.neighbors().filter(|n| occupied.contains(n)).count() < 4),
44
44
-
)
86
86
+
.filter(|seat| policy.neighbors(*seat).all(|n| !occupied.contains(&n)))
87
87
+
.chain(occupied.iter().copied().filter(|seat| {
88
88
+
policy
89
89
+
.neighbors(*seat)
90
90
+
.filter(|n| occupied.contains(n))
91
91
+
.count()
92
92
+
< P::THRESHOLD
93
93
+
}))
45
94
.collect()
46
95
}
47
96
48
48
-
fn fixed_point_occupancy(seats: &HashSet<Vector<usize, 2>>) -> HashSet<Vector<usize, 2>> {
49
49
-
itertools::iterate(HashSet::new(), |prev| evolve(prev, seats))
97
97
+
fn fixed_point_occupancy(
98
98
+
seats: &HashSet<Vector<usize, 2>>,
99
99
+
policy: &impl SeatPolicy,
100
100
+
) -> HashSet<Vector<usize, 2>> {
101
101
+
itertools::iterate(HashSet::new(), |prev| evolve(prev, seats, policy))
50
102
.tuple_windows()
51
103
.find(|(lhs, rhs)| lhs == rhs)
52
104
.unwrap()
···
152
204
#[test]
153
205
fn all_seats_occupied_after_first_round() {
154
206
let seats = HashSet::from(EXAMPLE_SEATS);
155
155
-
assert_eq!(evolve(&HashSet::new(), &seats), seats);
207
207
+
assert_eq!(
208
208
+
evolve(&HashSet::new(), &seats, &DirectNeighborSeatPolicy),
209
209
+
seats
210
210
+
);
156
211
}
157
212
158
213
#[test]
159
214
fn number_of_occupied_seats_evolves() {
160
215
let seats = HashSet::from(EXAMPLE_SEATS);
161
216
let mut occupied = HashSet::new();
162
162
-
occupied = evolve(&occupied, &seats);
217
217
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
163
218
assert_eq!(occupied.len(), EXAMPLE_SEATS.len());
164
164
-
occupied = evolve(&occupied, &seats);
219
219
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
165
220
assert_eq!(occupied.len(), 20);
166
166
-
occupied = evolve(&occupied, &seats);
221
221
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
167
222
assert_eq!(occupied.len(), 51);
168
168
-
occupied = evolve(&occupied, &seats);
223
223
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
169
224
assert_eq!(occupied.len(), 30);
170
170
-
occupied = evolve(&occupied, &seats);
225
225
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
171
226
assert_eq!(occupied.len(), 37);
172
172
-
occupied = evolve(&occupied, &seats);
227
227
+
occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy);
173
228
assert_eq!(occupied.len(), 37);
174
229
}
175
230
176
231
#[test]
177
232
fn find_fixed_point_occupancy() {
233
233
+
let seats = HashSet::from(EXAMPLE_SEATS);
178
234
assert_eq!(
179
179
-
fixed_point_occupancy(&HashSet::from(EXAMPLE_SEATS)).len(),
235
235
+
fixed_point_occupancy(&seats, &DirectNeighborSeatPolicy).len(),
180
236
37
237
237
+
);
238
238
+
assert_eq!(
239
239
+
fixed_point_occupancy(&seats, &SightlineSeatPolicy::new(&seats)).len(),
240
240
+
26
181
241
);
182
242
}
183
243
}