Advent of Code solutions in Rust

feat: day 17 of AoC 2020

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