mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-26 05:10:32 +01:00
177 lines
4.0 KiB
Rust
177 lines
4.0 KiB
Rust
use std::fmt::Display;
|
|
use std::io::Read;
|
|
use std::ops::Index;
|
|
|
|
use crate::common::BitSet;
|
|
|
|
type Translation = [bool; 512];
|
|
|
|
struct Field {
|
|
width: usize,
|
|
height: usize,
|
|
infinity: bool,
|
|
finite: BitSet,
|
|
}
|
|
|
|
impl Field {
|
|
pub fn from_input<'a>(input: impl Iterator<Item = &'a [u8]>) -> 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);
|
|
|
|
for y in 0..new_height {
|
|
let mut mask = if self.infinity { INDEX_MASK } else { 0 };
|
|
|
|
for x in 0..new_width {
|
|
const COLUMN_MASK: usize = 0b001001001;
|
|
let mut submask = if self.infinity { COLUMN_MASK } else { 0 };
|
|
|
|
for y in y.saturating_sub(2)..=y {
|
|
submask = (submask << 3) | (self[(x, y)] as usize);
|
|
}
|
|
|
|
mask <<= 1;
|
|
mask &= !COLUMN_MASK;
|
|
mask |= submask;
|
|
mask &= INDEX_MASK;
|
|
|
|
if translation[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();
|
|
input.read_to_end(&mut buffer).unwrap();
|
|
|
|
let mut translation = [false; 512];
|
|
|
|
let mut it = buffer.split(|&b| b == b'\n');
|
|
|
|
translation
|
|
.iter_mut()
|
|
.zip(it.next().unwrap())
|
|
.for_each(|(t, &c)| *t = c == b'#');
|
|
|
|
let field = Field::from_input(it.skip(1));
|
|
|
|
(translation, field)
|
|
}
|
|
|
|
fn parts_common(input: &mut dyn Read, count: usize) -> String {
|
|
let (translation, mut field) = read_input(input);
|
|
|
|
for _ in 0..count {
|
|
field.advance(&translation);
|
|
}
|
|
|
|
field.len().to_string()
|
|
}
|
|
|
|
pub fn part1(input: &mut dyn Read) -> String {
|
|
parts_common(input, 2)
|
|
}
|
|
|
|
pub fn part2(input: &mut dyn Read) -> String {
|
|
parts_common(input, 50)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::test_implementation;
|
|
|
|
use super::*;
|
|
|
|
const SAMPLE: &[u8] = include_bytes!("samples/20.txt");
|
|
|
|
#[test]
|
|
fn sample_part1() {
|
|
test_implementation(part1, SAMPLE, 35);
|
|
}
|
|
|
|
#[test]
|
|
fn sample_part2() {
|
|
test_implementation(part2, SAMPLE, 3351);
|
|
}
|
|
}
|