diff --git a/Cargo.lock b/Cargo.lock index c0b21dc..52fce8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,7 @@ version = "0.1.0" dependencies = [ "anyhow", "aoc", + "itertools", ] [[package]] diff --git a/aoc2020/Cargo.toml b/aoc2020/Cargo.toml index 58ee1cf..a5e88e4 100644 --- a/aoc2020/Cargo.toml +++ b/aoc2020/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] aoc = { path = "../aoc" } anyhow = "1.0" +itertools = "0.9" [lib] path = "src/lib.rs" diff --git a/aoc2020/aoc2020_bench/benches/aoc2020_bench.rs b/aoc2020/aoc2020_bench/benches/aoc2020_bench.rs index d6a8ba8..6c9aff5 100644 --- a/aoc2020/aoc2020_bench/benches/aoc2020_bench.rs +++ b/aoc2020/aoc2020_bench/benches/aoc2020_bench.rs @@ -16,6 +16,7 @@ use aoc2020::day13; use aoc2020::day14; use aoc2020::day15; use aoc2020::day16; +use aoc2020::day17; fn aoc2020_all(c: &mut Criterion) { c.bench_function("day01", |b| b.iter(|| day01::run().unwrap())); @@ -34,6 +35,7 @@ fn aoc2020_all(c: &mut Criterion) { c.bench_function("day14", |b| b.iter(|| day14::run().unwrap())); c.bench_function("day15", |b| b.iter(|| day15::run().unwrap())); c.bench_function("day16", |b| b.iter(|| day16::run().unwrap())); + c.bench_function("day17", |b| b.iter(|| day17::run().unwrap())); } criterion_group! { diff --git a/aoc2020/input/day17.txt b/aoc2020/input/day17.txt new file mode 100644 index 0000000..2d03d2d --- /dev/null +++ b/aoc2020/input/day17.txt @@ -0,0 +1,8 @@ +#####..# +#..###.# +###..... +.#.#.#.. +##.#..#. +######.. +.##..### +###.#### diff --git a/aoc2020/input/day17_provided.txt b/aoc2020/input/day17_provided.txt new file mode 100644 index 0000000..eedd3d2 --- /dev/null +++ b/aoc2020/input/day17_provided.txt @@ -0,0 +1,3 @@ +.#. +..# +### diff --git a/aoc2020/src/day17.rs b/aoc2020/src/day17.rs new file mode 100644 index 0000000..baa726f --- /dev/null +++ b/aoc2020/src/day17.rs @@ -0,0 +1,110 @@ +use std::collections::HashSet; +use std::fmt::Write; + +use anyhow::{anyhow, Result}; + +use itertools::iproduct; + +const INPUT: &str = include_str!("../input/day17.txt"); + +pub fn run() -> Result { + let mut res = String::with_capacity(128); + + writeln!(res, "part 1: {}", part1(INPUT)?)?; + + Ok(res) +} + +fn part1(input: &str) -> Result { + let mut pocket_dim: PocketDimension = input.parse()?; + + for _ in 0..6 { + pocket_dim.update(); + } + + Ok(pocket_dim.points.len()) +} + +type Point = (i64, i64, i64); + +struct PocketDimension { + points: HashSet, +} + +impl PocketDimension { + fn neighbours(point: Point) -> impl Iterator { + iproduct!(-1..=1, -1..=1, -1..=1) + .filter(|(x, y, z)| *x != 0 || *y != 0 || *z != 0) + .map(move |(dx, dy, dz)| (point.0 + dx, point.1 + dy, point.2 + dz)) + } + + fn active_neighbours(&self, point: Point) -> usize { + Self::neighbours(point) + .filter(|p| self.points.contains(p)) + .count() + } + + fn update(&mut self) { + let mut processed: HashSet = HashSet::new(); + let mut new = self.points.clone(); + + for point in &self.points { + for neighbour in Self::neighbours(*point).chain(std::iter::once(*point)) { + if processed.contains(&neighbour) { + continue; + } + + processed.insert(neighbour); + + // if point is active + if self.points.contains(&neighbour) { + if !(2..=3).contains(&self.active_neighbours(neighbour)) { + // now inactive + new.remove(&neighbour); + } + } else if self.active_neighbours(neighbour) == 3 { + new.insert(neighbour); + } + } + } + + self.points = new; + } +} + +impl std::str::FromStr for PocketDimension { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let points = s + .lines() + .enumerate() + .flat_map(|(i, line)| { + line.chars().enumerate().filter_map(move |(j, c)| match c { + '#' => Some(Ok((i as i64, j as i64, 0))), + '.' => None, + _ => Some(Err(anyhow!("unexpected char: `{}`", c))), + }) + }) + .collect::>()?; + + Ok(Self { points }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const PROVIDED: &str = include_str!("../input/day17_provided.txt"); + + #[test] + fn part1_provided() { + assert_eq!(part1(PROVIDED).unwrap(), 112); + } + + #[test] + fn part1_real() { + assert_eq!(part1(INPUT).unwrap(), 336); + } +} diff --git a/aoc2020/src/lib.rs b/aoc2020/src/lib.rs index f281e9a..cf569f2 100644 --- a/aoc2020/src/lib.rs +++ b/aoc2020/src/lib.rs @@ -16,3 +16,4 @@ pub mod day13; pub mod day14; pub mod day15; pub mod day16; +pub mod day17; diff --git a/aoc2020/src/main.rs b/aoc2020/src/main.rs index c73812f..9c35ca5 100644 --- a/aoc2020/src/main.rs +++ b/aoc2020/src/main.rs @@ -18,6 +18,7 @@ use aoc2020::day13; use aoc2020::day14; use aoc2020::day15; use aoc2020::day16; +use aoc2020::day17; fn main() -> Result<()> { let days: &[DayFunc] = &[ @@ -37,6 +38,7 @@ fn main() -> Result<()> { day14::run, day15::run, day16::run, + day17::run, ]; aoc::run(days)