Skip to content

Commit 3c52f4e

Browse files
committed
Add Day 17
1 parent 3cec249 commit 3c52f4e

File tree

4 files changed

+189
-0
lines changed

4 files changed

+189
-0
lines changed

aoc17/Cargo.lock

+30
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aoc17/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "aoc17"
3+
version = "0.1.0"
4+
authors = ["Sergree <[email protected]>"]
5+
license = "MIT OR Apache-2.0"
6+
edition = "2018"
7+
8+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9+
10+
[dependencies]
11+
anyhow = "1.0.40"
12+
itertools = "0.10.0"

aoc17/input/input.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
..#..#.#
2+
##.#..#.
3+
#....#..
4+
.#..####
5+
.....#..
6+
...##...
7+
.#.##..#
8+
.#.#.#.#

aoc17/src/main.rs

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//! Generic over dimension solution
2+
//! Requires stable Rust 1.51+
3+
4+
use anyhow::{bail, Result};
5+
use itertools::Itertools;
6+
use std::collections::HashSet;
7+
use std::io::{self, Read, Write};
8+
use std::str::FromStr;
9+
10+
fn main() -> Result<()> {
11+
let mut input = String::new();
12+
io::stdin().read_to_string(&mut input)?;
13+
14+
part1(&input)?;
15+
part2(&input)?;
16+
Ok(())
17+
}
18+
19+
fn part1(input: &str) -> Result<()> {
20+
let mut world = World::<3>::from_str(input)?;
21+
world.cycles(6);
22+
writeln!(io::stdout(), "{}", world.active_count())?;
23+
Ok(())
24+
}
25+
26+
fn part2(input: &str) -> Result<()> {
27+
let mut world = World::<4>::from_str(input)?;
28+
world.cycles(6);
29+
writeln!(io::stdout(), "{}", world.active_count())?;
30+
Ok(())
31+
}
32+
33+
struct World<const DIM: usize> {
34+
cubes: HashSet<Cube<DIM>>,
35+
offsets: Vec<Vec<i32>>,
36+
}
37+
38+
#[derive(Clone, Eq, PartialEq, Hash)]
39+
struct Cube<const DIM: usize> {
40+
coords: [i32; DIM],
41+
}
42+
43+
impl<const DIM: usize> Cube<DIM> {
44+
fn new(coords: [i32; DIM]) -> Self {
45+
Cube { coords }
46+
}
47+
}
48+
49+
impl<const DIM: usize> FromStr for World<DIM> {
50+
type Err = anyhow::Error;
51+
52+
fn from_str(value: &str) -> Result<Self, Self::Err> {
53+
if DIM < 2 {
54+
bail!("There must be at least 2 dimensions");
55+
}
56+
57+
let mut cubes = HashSet::new();
58+
59+
for (y, row) in value.lines().enumerate() {
60+
for (x, char) in row.chars().enumerate() {
61+
if char == '#' {
62+
let mut values = [0; DIM];
63+
// We may use the index because we checked the length above
64+
values[0] = x as i32;
65+
values[1] = y as i32;
66+
cubes.insert(Cube::new(values));
67+
}
68+
}
69+
}
70+
71+
let offsets = (0..DIM)
72+
.map(|_| -1..=1)
73+
.multi_cartesian_product()
74+
.filter(|v| !v.iter().all(|n| *n == 0))
75+
.collect();
76+
77+
Ok(World { cubes, offsets })
78+
}
79+
}
80+
81+
impl<const DIM: usize> World<DIM> {
82+
fn cycle(&mut self) {
83+
let mut new_cubes = HashSet::new();
84+
85+
let mut lower_bounds = [0; DIM];
86+
let mut upper_bounds = [0; DIM];
87+
88+
if self.cubes.is_empty() {
89+
return;
90+
}
91+
92+
(0..DIM).for_each(|i| {
93+
// We may call .unwrap() here because we checked the length above
94+
lower_bounds[i] = self.cubes.iter().map(|c| c.coords[i]).min().unwrap() - 1;
95+
upper_bounds[i] = self.cubes.iter().map(|c| c.coords[i]).max().unwrap() + 1;
96+
});
97+
98+
(0..DIM)
99+
.map(|i| lower_bounds[i]..=upper_bounds[i])
100+
.multi_cartesian_product()
101+
.for_each(|values_vec| {
102+
let mut values = [0; DIM];
103+
(0..DIM).for_each(|j| values[j] = values_vec[j]);
104+
105+
let cube = Cube::new(values);
106+
107+
let active = self.cubes.contains(&cube);
108+
let active_neighbors = self.active_neighbors(&cube);
109+
110+
match (active, active_neighbors) {
111+
(true, 2..=3) | (false, 3) => {
112+
new_cubes.insert(cube);
113+
}
114+
_ => {}
115+
}
116+
});
117+
118+
self.cubes = new_cubes;
119+
}
120+
121+
fn cycles(&mut self, count: usize) {
122+
(0..count).for_each(|_| self.cycle());
123+
}
124+
125+
fn active_neighbors(&self, cube: &Cube<DIM>) -> usize {
126+
self.offsets
127+
.iter()
128+
.filter(|o| {
129+
let mut values = [0; DIM];
130+
(0..DIM).for_each(|i| values[i] = cube.coords[i] + o[i]);
131+
self.cubes.contains(&Cube::new(values))
132+
})
133+
.count()
134+
}
135+
136+
fn active_count(&self) -> usize {
137+
self.cubes.iter().count()
138+
}
139+
}

0 commit comments

Comments
 (0)