use std::collections::HashMap; use std::collections::HashSet; use std::io::Read; use std::mem::swap; use crate::common::Lines; use crate::Solution; type Space = HashSet<(i32, i32, i32)>; type Space4 = HashSet<(i32, i32, i32, i32)>; fn read_input(input: &mut dyn Read) -> Space { let mut space = HashSet::new(); for (y, line) in Lines::new(input).enumerate() { space.extend(line.chars().enumerate().filter_map(|(x, c)| { if c == '#' { Some((x as i32, y as i32, 0)) } else { None } })) } space } fn advance(current: &Space, next: &mut Space) { let mut live_count = HashMap::new(); for &(x, y, z) in current { for nx in (x - 1)..=(x + 1) { for ny in (y - 1)..=(y + 1) { for nz in (z - 1)..=(z + 1) { if x != nx || y != ny || nz != z { *live_count.entry((nx, ny, nz)).or_insert(0) += 1; } } } } } next.clear(); for (pos, neighbours) in live_count { if (neighbours == 2 && current.contains(&pos)) || neighbours == 3 { next.insert(pos); } } } fn read_input2(input: &mut dyn Read) -> Space4 { let mut space = HashSet::new(); for (y, line) in Lines::new(input).enumerate() { space.extend(line.chars().enumerate().filter_map(|(x, c)| { if c == '#' { Some((0, x as i32, y as i32, 0)) } else { None } })) } space } fn advance2(current: &Space4, next: &mut Space4) { let mut live_count = HashMap::new(); for &(w, x, y, z) in current { for nw in (w - 1)..=(w + 1) { for nx in (x - 1)..=(x + 1) { for ny in (y - 1)..=(y + 1) { for nz in (z - 1)..=(z + 1) { if nw != w || x != nx || y != ny || nz != z { *live_count.entry((nw, nx, ny, nz)).or_insert(0) += 1; } } } } } } next.clear(); for (pos, neighbours) in live_count { if (neighbours == 2 && current.contains(&pos)) || neighbours == 3 { next.insert(pos); } } } #[derive(Default)] pub struct Day17; impl Solution for Day17 { fn part1(&mut self, input: &mut dyn Read) -> String { let mut space = read_input(input); let mut scratch_pad = Space::new(); for _ in 0..6 { advance(&space, &mut scratch_pad); swap(&mut space, &mut scratch_pad); } space.len().to_string() } fn part2(&mut self, input: &mut dyn Read) -> String { let mut space = read_input2(input); let mut scratch_pad = Space4::new(); for _ in 0..6 { advance2(&space, &mut scratch_pad); swap(&mut space, &mut scratch_pad); } space.len().to_string() } } #[cfg(test)] mod tests { use crate::test_implementation; use super::*; const SAMPLE: &[u8] = include_bytes!("../samples/17.txt"); #[test] fn sample_part1() { test_implementation(Day17, 1, SAMPLE, 112); } #[test] fn sample_part2() { test_implementation(Day17, 2, SAMPLE, 848); } }