Advent of Code solutions in Rust

feat: part 2 of day 11 of AoC 2020

+80 -20
+80 -20
aoc_2020/src/day11.rs
··· 1 - use std::collections::HashSet; 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 - fixed_point_occupancy(&self.seats).len() 27 + fixed_point_occupancy(&self.seats, &DirectNeighborSeatPolicy).len() 28 + } 29 + 30 + fn part2(&self) -> usize { 31 + fixed_point_occupancy(&self.seats, &SightlineSeatPolicy::new(&self.seats)).len() 32 + } 33 + } 34 + 35 + trait SeatPolicy { 36 + const THRESHOLD: usize; 37 + fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>>; 38 + } 39 + 40 + struct DirectNeighborSeatPolicy; 41 + 42 + impl SeatPolicy for DirectNeighborSeatPolicy { 43 + const THRESHOLD: usize = 4; 44 + 45 + fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>> { 46 + seat.neighbors() 28 47 } 29 48 } 30 49 31 - fn evolve( 50 + struct SightlineSeatPolicy<'s> { 51 + seats: &'s HashSet<Vector<usize, 2>>, 52 + bounds: [RangeInclusive<usize>; 2], 53 + } 54 + 55 + impl<'s> SightlineSeatPolicy<'s> { 56 + fn new(seats: &'s HashSet<Vector<usize, 2>>) -> Self { 57 + Self { 58 + seats, 59 + bounds: array::from_fn(|i| 0..=seats.iter().map(|s| s[i]).max().unwrap_or(0)), 60 + } 61 + } 62 + } 63 + 64 + impl SeatPolicy for SightlineSeatPolicy<'_> { 65 + const THRESHOLD: usize = 5; 66 + 67 + fn neighbors(&self, seat: Vector<usize, 2>) -> impl Iterator<Item = Vector<usize, 2>> { 68 + seat.neighbors().filter_map(move |neighbor| { 69 + let dir = neighbor - seat; 70 + (1..) 71 + .map(|n| seat + dir * n) 72 + .take_while(|s| s.in_bounds(&self.bounds)) 73 + .find(|s| self.seats.contains(s)) 74 + }) 75 + } 76 + } 77 + 78 + fn evolve<P: SeatPolicy>( 32 79 occupied: &HashSet<Vector<usize, 2>>, 33 80 seats: &HashSet<Vector<usize, 2>>, 81 + policy: &P, 34 82 ) -> HashSet<Vector<usize, 2>> { 35 83 let empty = seats.difference(occupied); 36 84 empty 37 85 .copied() 38 - .filter(|seat| seat.neighbors().all(|n| !occupied.contains(&n))) 39 - .chain( 40 - occupied 41 - .iter() 42 - .copied() 43 - .filter(|seat| seat.neighbors().filter(|n| occupied.contains(n)).count() < 4), 44 - ) 86 + .filter(|seat| policy.neighbors(*seat).all(|n| !occupied.contains(&n))) 87 + .chain(occupied.iter().copied().filter(|seat| { 88 + policy 89 + .neighbors(*seat) 90 + .filter(|n| occupied.contains(n)) 91 + .count() 92 + < P::THRESHOLD 93 + })) 45 94 .collect() 46 95 } 47 96 48 - fn fixed_point_occupancy(seats: &HashSet<Vector<usize, 2>>) -> HashSet<Vector<usize, 2>> { 49 - itertools::iterate(HashSet::new(), |prev| evolve(prev, seats)) 97 + fn fixed_point_occupancy( 98 + seats: &HashSet<Vector<usize, 2>>, 99 + policy: &impl SeatPolicy, 100 + ) -> HashSet<Vector<usize, 2>> { 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 - assert_eq!(evolve(&HashSet::new(), &seats), seats); 207 + assert_eq!( 208 + evolve(&HashSet::new(), &seats, &DirectNeighborSeatPolicy), 209 + seats 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 - occupied = evolve(&occupied, &seats); 217 + occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy); 163 218 assert_eq!(occupied.len(), EXAMPLE_SEATS.len()); 164 - occupied = evolve(&occupied, &seats); 219 + occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy); 165 220 assert_eq!(occupied.len(), 20); 166 - occupied = evolve(&occupied, &seats); 221 + occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy); 167 222 assert_eq!(occupied.len(), 51); 168 - occupied = evolve(&occupied, &seats); 223 + occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy); 169 224 assert_eq!(occupied.len(), 30); 170 - occupied = evolve(&occupied, &seats); 225 + occupied = evolve(&occupied, &seats, &DirectNeighborSeatPolicy); 171 226 assert_eq!(occupied.len(), 37); 172 - occupied = evolve(&occupied, &seats); 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 + let seats = HashSet::from(EXAMPLE_SEATS); 178 234 assert_eq!( 179 - fixed_point_occupancy(&HashSet::from(EXAMPLE_SEATS)).len(), 235 + fixed_point_occupancy(&seats, &DirectNeighborSeatPolicy).len(), 180 236 37 237 + ); 238 + assert_eq!( 239 + fixed_point_occupancy(&seats, &SightlineSeatPolicy::new(&seats)).len(), 240 + 26 181 241 ); 182 242 } 183 243 }