AoC2020/src/day17.rs

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());
}
}