121 lines
3.8 KiB
Rust
121 lines
3.8 KiB
Rust
use std::io::BufRead;
|
|
use std::collections::{HashSet, HashMap};
|
|
use itertools::Itertools;
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct PocketDimension {
|
|
active_cubes: HashSet<Vec<i32>>,
|
|
dimensions: usize,
|
|
}
|
|
|
|
impl PocketDimension {
|
|
fn with_dimension_count(dimensions: usize) -> Self {
|
|
PocketDimension {
|
|
active_cubes: HashSet::new(),
|
|
dimensions,
|
|
}
|
|
}
|
|
|
|
fn init_from_str(&mut self, s: &str) {
|
|
self.active_cubes = s.lines()
|
|
.enumerate()
|
|
.map(|(line_idx, line)|
|
|
line.chars()
|
|
.enumerate()
|
|
.filter(|(_idx, ch)| *ch == '#')
|
|
.map(|(idx, _ch)| {
|
|
let mut cube = vec![0; self.dimensions];
|
|
cube[0] = idx as i32;
|
|
cube[1] = line_idx as i32;
|
|
cube
|
|
}).collect::<HashSet<_>>()
|
|
).fold(HashSet::new(), |acc, coords| {
|
|
acc.union(&coords).cloned().collect()
|
|
});
|
|
}
|
|
|
|
fn tick(&mut self) {
|
|
let mut memory = HashMap::<Vec<i32>, (usize, bool)>::new();
|
|
for cube in self.active_cubes.drain() {
|
|
let neighbours = cube.iter().map(|c| (c - 1)..=(c + 1))
|
|
.multi_cartesian_product()
|
|
.filter(|neighbour| neighbour != &cube);
|
|
for neighbour in neighbours {
|
|
let entry = memory.entry(neighbour).or_insert((0, false));
|
|
entry.0 += 1;
|
|
}
|
|
memory.entry(cube).and_modify(|(_, active)| *active = true).or_insert((0, true));
|
|
}
|
|
self.active_cubes = memory.drain().filter_map(|(cube, (neighbours_count, is_active))| {
|
|
if is_active && (neighbours_count == 2 || neighbours_count == 3) {
|
|
Some(cube)
|
|
} else if !is_active && neighbours_count == 3 {
|
|
Some(cube)
|
|
} else {
|
|
None
|
|
}
|
|
}).collect();
|
|
}
|
|
|
|
fn simulate(&mut self, cycle_count: usize) {
|
|
(0..cycle_count).for_each(|_| self.tick());
|
|
}
|
|
}
|
|
|
|
pub fn part1<F: BufRead> (mut input: F) {
|
|
let mut buffer = String::new();
|
|
input.read_to_string(&mut buffer).unwrap();
|
|
let mut pocket = PocketDimension::with_dimension_count(3);
|
|
pocket.init_from_str(&buffer);
|
|
pocket.simulate(6);
|
|
println!("{}", pocket.active_cubes.len());
|
|
}
|
|
|
|
pub fn part2<F: BufRead> (mut input: F) {
|
|
let mut buffer = String::new();
|
|
input.read_to_string(&mut buffer).unwrap();
|
|
let mut pocket = PocketDimension::with_dimension_count(4);
|
|
pocket.init_from_str(&buffer);
|
|
pocket.simulate(6);
|
|
println!("{}", pocket.active_cubes.len());
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
pub fn test_parse() {
|
|
let input = ".#.\n..#\n###";
|
|
let expected = PocketDimension {
|
|
active_cubes: [vec![1, 0, 0], vec![2, 1, 0], vec![0, 2, 0], vec![1, 2, 0], vec![2, 2, 0]].iter().cloned().collect(),
|
|
dimensions: 3,
|
|
};
|
|
|
|
let mut pocket = PocketDimension::with_dimension_count(3);
|
|
pocket.init_from_str(input);
|
|
assert_eq!(expected, pocket);
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_simulate3d() {
|
|
let mut pocket = PocketDimension {
|
|
active_cubes: [vec![1, 0, 0], vec![2, 1, 0], vec![0, 2, 0], vec![1, 2, 0], vec![2, 2, 0]].iter().cloned().collect(),
|
|
dimensions: 3,
|
|
};
|
|
pocket.simulate(6);
|
|
assert_eq!(112, pocket.active_cubes.len());
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_simulate4d() {
|
|
let mut pocket = PocketDimension {
|
|
active_cubes: [vec![1, 0, 0, 0], vec![2, 1, 0, 0], vec![0, 2, 0, 0], vec![1, 2, 0, 0], vec![2, 2, 0, 0]].iter().cloned().collect(),
|
|
dimensions: 3,
|
|
};
|
|
pocket.simulate(6);
|
|
assert_eq!(848, pocket.active_cubes.len());
|
|
}
|
|
}
|