From ef4ae63d333e85890db068fce0f5b996472b2751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Thu, 17 Dec 2020 22:25:33 +0100 Subject: [PATCH] day17 --- data/day17/input | 8 ++ src/bin/day17.rs | 222 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 data/day17/input create mode 100644 src/bin/day17.rs diff --git a/data/day17/input b/data/day17/input new file mode 100644 index 0000000..58c2126 --- /dev/null +++ b/data/day17/input @@ -0,0 +1,8 @@ +...#.#.# +..#..#.. +#.#.##.# +###.##.. +#####.## +#....... +#..#..## +...##.## diff --git a/src/bin/day17.rs b/src/bin/day17.rs new file mode 100644 index 0000000..b1056de --- /dev/null +++ b/src/bin/day17.rs @@ -0,0 +1,222 @@ +use std::ops::Range; + +const INPUT: &str = include_str!("../../data/day17/input"); + +#[derive(Clone, Copy, Default)] +struct Dimension { + offset: u32, + size: u32, +} + +impl Dimension { + fn map(&self, pos: i32) -> Option { + let p = (self.offset as i32) + pos; + if p >= 0 && p < (self.size as i32) { + Some(p as usize) + } else { + None + } + } + + fn include(&self, pos: i32) -> Self { + let left = std::cmp::min(pos, -(self.offset as i32)); + let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32); + Self { + offset: (-left) as u32, + size: (right - left + 1) as u32, + } + } + + fn extend(&self) -> Self { + Self { + offset: self.offset + 1, + size: self.size + 2, + } + } +} + +impl IntoIterator for Dimension { + type IntoIter = Range; + type Item = i32; + + fn into_iter(self) -> Self::IntoIter { + -(self.offset as i32)..((self.size - self.offset) as i32) + } +} + +#[derive(Clone)] +struct Field { + x_dim: Dimension, + y_dim: Dimension, + z_dim: Dimension, + w_dim: Dimension, + cells: Vec, +} + +impl Default for Field { + fn default() -> Self { + Self { + x_dim: Dimension { offset: 0, size: 1}, + y_dim: Dimension { offset: 0, size: 1}, + z_dim: Dimension { offset: 0, size: 1}, + w_dim: Dimension { offset: 0, size: 1}, + cells: vec![false], + } + } +} + +impl Field { + fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self { + let mut cells = Vec::new(); + let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize; + cells.resize(size, false); + Self { x_dim, y_dim, z_dim, w_dim, cells } + } + + fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option { + let x = self.x_dim.map(x)?; + let y = self.y_dim.map(y)?; + let z = self.z_dim.map(z)?; + let w = self.w_dim.map(w)?; + let addr = ((w * (self.z_dim.size as usize) + z) * (self.y_dim.size as usize) + y) * (self.x_dim.size as usize) + x; + Some(addr) + } + + fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option { + let addr = self._addr(x, y, z, w)?; + Some(self.cells[addr]) + } + + fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool { + self._get(x, y, z, w).unwrap_or(false) + } + + fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) { + if let Some(addr) = self._addr(x, y, z, w) { + self.cells[addr] = value; + } else if value { + // don't resize to set "false" + let x_dim = self.x_dim.include(x); + let y_dim = self.y_dim.include(y); + let z_dim = self.z_dim.include(z); + let w_dim = self.w_dim.include(w); + let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim); + for w in self.w_dim { + for z in self.z_dim { + for y in self.y_dim { + for x in self.x_dim { + let addr = new_field._addr(x, y, z, w).unwrap(); + new_field.cells[addr] = self.get(x, y, z, w); + } + } + } + } + let addr = new_field._addr(x, y, z, w).unwrap(); + new_field.cells[addr] = value; + *self = new_field; + } + } + + fn parse(data: &str) -> Self { + let mut this = Self::default(); + for (y, line) in data.lines().enumerate() { + for (x, chr) in line.chars().enumerate() { + this.set(x as i32, y as i32, 0, 0, chr == '#'); + } + } + this + } + + fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 { + let mut count = 0; + for dw in -1..=1 { + for dz in -1..=1 { + for dy in -1..=1 { + for dx in -1..=1 { + if dw == 0 && dz == 0 && dy == 0 && dx == 0 { + continue; + } + if self.get(x + dx, y + dy, z + dz, w + dw) { + count += 1; + if count > 3 { return count; } // more than 3 is always bad + } + } + } + } + } + count + } + + fn cycle_3dim(&self) -> Self { + let mut result = self.clone(); + let w = 0; + for z in self.z_dim.extend() { + for y in self.y_dim.extend() { + for x in self.x_dim.extend() { + let count = self.count_active_neighbors(x, y, z, w); + let active = count == 3 || (count == 2 && self.get(x, y, z, w)); + result.set(x, y, z, w, active); + } + } + } + result + } + + fn cycle_4dim(&self) -> Self { + let mut result = self.clone(); + for w in self.w_dim.extend() { + for z in self.z_dim.extend() { + for y in self.y_dim.extend() { + for x in self.x_dim.extend() { + let count = self.count_active_neighbors(x, y, z, w); + let active = count == 3 || (count == 2 && self.get(x, y, z, w)); + result.set(x, y, z, w, active); + } + } + } + } + result + } +} + +impl std::fmt::Debug for Field { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use std::fmt::Write; + for z in self.z_dim { + for w in self.w_dim { + writeln!(f, "z = {}, w = {}", z, w)?; + for y in self.y_dim { + for x in self.x_dim { + if x == 0 && y == 0 { + f.write_char( + if self.get(x, y, z, w) { 'X' } else { '+' } + )?; + } else { + f.write_char( + if self.get(x, y, z, w) { '#' } else { '.' } + )?; + } + } + f.write_char('\n')?; + } + f.write_char('\n')?; + } + } + Ok(()) + } +} + +fn main() { + let input = Field::parse(INPUT); + let mut field = input.clone(); + for _ in 0..6 { + field = field.cycle_3dim(); + } + println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); + let mut field = input.clone(); + for _ in 0..6 { + field = field.cycle_4dim(); + } + println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); + println!("{:?}", input); +}