diff --git a/2021/src/common.rs b/2021/src/common.rs index 751c8e4..162f2c1 100644 --- a/2021/src/common.rs +++ b/2021/src/common.rs @@ -102,25 +102,32 @@ where } } +#[derive(Default)] pub struct BitSet { buffer: Vec, } impl BitSet { pub fn new() -> Self { - Self::with_capacity(0) + Self::default() } pub fn with_capacity(capacity: usize) -> Self { - let buffer = Vec::with_capacity(capacity); + let buffer = Vec::with_capacity(capacity / 32); Self { buffer } } - pub fn insert(&mut self, value: usize) -> bool { + fn convert_value(value: usize) -> (usize, u32) { let chunk = value / 32; let bit = 1 << (31 - (value % 32)); + (chunk, bit) + } + + pub fn insert(&mut self, value: usize) -> bool { + let (chunk, bit) = Self::convert_value(value); + if self.buffer.len() <= chunk + 1 { self.buffer.resize(chunk + 1, 0); } @@ -135,4 +142,13 @@ impl BitSet { pub fn len(&self) -> usize { self.buffer.iter().map(|c| c.count_ones() as usize).sum() } + + pub fn contains(&self, value: usize) -> bool { + let (chunk, bit) = Self::convert_value(value); + + self.buffer + .get(chunk) + .map(|&c| c & bit != 0) + .unwrap_or(false) + } } diff --git a/2021/src/day20.rs b/2021/src/day20.rs index 9f113bf..fa67954 100644 --- a/2021/src/day20.rs +++ b/2021/src/day20.rs @@ -1,10 +1,125 @@ -use std::collections::HashSet; +use std::fmt::Display; use std::io::Read; -use std::mem::swap; +use std::ops::Index; + +use crate::common::BitSet; type Translation = [bool; 512]; -type Point = (i32, i32); -type Field = HashSet; + +struct Field { + width: usize, + height: usize, + infinity: bool, + finite: BitSet, +} + +impl Field { + pub fn from_input<'a>(input: impl Iterator) -> Self { + let mut input = input.peekable(); + + let width = input.peek().unwrap().len(); + + let mut finite = BitSet::new(); + + let len = input + .flatten() + .enumerate() + .map(|(index, &c)| { + if c == b'#' { + finite.insert(index); + } + }) + .count(); + + debug_assert_eq!(len % width, 0); + let height = len / width; + + Self { + width, + height, + finite, + infinity: false, + } + } + + pub fn advance(&mut self, translation: &[bool; 512]) { + const INDEX_MASK: usize = (1 << 9) - 1; + + let new_width = self.width + 2; + let new_height = self.height + 2; + + let mut new_finite = BitSet::with_capacity(new_width * new_height); + + // Now we can just do a normal loop + for y in 0..new_height { + for x in 0..new_width { + let mut mask = if self.infinity { INDEX_MASK } else { 0 }; + + for y in y.saturating_sub(2)..=y { + if x < 2 { + for _ in 0..(2 - x) { + mask = self.infinity as usize | (mask << 1); + } + } + + for x in x.saturating_sub(2)..=x { + mask = (mask << 1) | (self[(x, y)] as usize); + } + } + + if translation[mask & INDEX_MASK] { + let index = x + y * new_width; + new_finite.insert(index); + } + } + } + + self.width += 2; + self.height += 2; + self.finite = new_finite; + self.infinity = translation[if self.infinity { INDEX_MASK } else { 0 }]; + } + + pub fn len(&self) -> usize { + assert!(!self.infinity); + self.finite.len() + } +} + +impl Index<(usize, usize)> for Field { + type Output = bool; + + #[inline] + fn index(&self, (x, y): (usize, usize)) -> &Self::Output { + if x >= self.width || y >= self.height { + return &self.infinity; + } + + let index = x + y * self.width; + + if self.finite.contains(index) { + &true + } else { + &false + } + } +} + +impl Display for Field { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for y in 0..self.height { + for x in 0..self.width { + if self[(x, y)] { + write!(f, "#")? + } else { + write!(f, ".")? + } + } + writeln!(f)?; + } + Ok(()) + } +} fn read_input(input: &mut dyn Read) -> (Translation, Field) { let mut buffer = Vec::new(); @@ -19,67 +134,16 @@ fn read_input(input: &mut dyn Read) -> (Translation, Field) { .zip(it.next().unwrap()) .for_each(|(t, &c)| *t = c == b'#'); - let mut field = Field::default(); - - for (y, line) in it.skip(1).enumerate() { - for (x, _) in line.iter().enumerate().filter(|(_, &c)| c == b'#') { - field.insert((x as i32, y as i32)); - } - } + let field = Field::from_input(it.skip(1)); (translation, field) } -fn find_dimensions(field: &Field) -> ((i32, i32), (i32, i32)) { - field - .iter() - .fold(((0, 0), (0, 0)), |((xmin, xmax), (ymin, ymax)), &(x, y)| { - ((xmin.min(x), xmax.max(x)), (ymin.min(y), ymax.max(y))) - }) -} - -fn advance(translation: &Translation, field: &Field, new_field: &mut Field, infinity: &mut bool) { - const INDEX_MASK: usize = (1 << 9) - 1; - new_field.clear(); - - let ((xmin, xmax), (ymin, ymax)) = find_dimensions(field); - - for x in (xmin - 1)..=(xmax + 1) { - let mut index = if *infinity { INDEX_MASK } else { 0 }; - - for y in (ymin - 1)..=(ymax + 1) { - for dx in -1..=1 { - index <<= 1; - - let nx = x + dx; - let ny = y + 1; - - if nx < xmin || nx > xmax || ny < ymin || ny > ymax { - index |= *infinity as usize; - } else if field.contains(&(nx, ny)) { - index |= 1; - } - } - - index &= INDEX_MASK; - - if translation[index] { - new_field.insert((x, y)); - } - } - } - - *infinity = translation[if *infinity { 511 } else { 0 }] -} - fn parts_common(input: &mut dyn Read, count: usize) -> String { let (translation, mut field) = read_input(input); - let mut new_field = Field::new(); - let mut infinity = false; for _ in 0..count { - advance(&translation, &field, &mut new_field, &mut infinity); - swap(&mut field, &mut new_field); + field.advance(&translation); } field.len().to_string()