Completed day 17
This commit is contained in:
parent
69740a6fbb
commit
0f038ae1dd
21
day17/Cargo.lock
generated
Normal file
21
day17/Cargo.lock
generated
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day17"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
11
day17/Cargo.toml
Normal file
11
day17/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "day17"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Tyler Hallada <tyler@hallada.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
lazy_static = "1.4"
|
8
day17/input/input.txt
Executable file
8
day17/input/input.txt
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
.##..#.#
|
||||||
|
#...##.#
|
||||||
|
##...#.#
|
||||||
|
.##.##..
|
||||||
|
...#.#.#
|
||||||
|
.##.#..#
|
||||||
|
...#..##
|
||||||
|
###..##.
|
3
day17/input/test.txt
Normal file
3
day17/input/test.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.#.
|
||||||
|
..#
|
||||||
|
###
|
361
day17/src/main.rs
Normal file
361
day17/src/main.rs
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Error, Result};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
use std::ops::Add;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
const INPUT: &str = "input/input.txt";
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||||
|
struct Coordinate {
|
||||||
|
x: isize,
|
||||||
|
y: isize,
|
||||||
|
z: isize,
|
||||||
|
w: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Coordinate {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x + other.x,
|
||||||
|
y: self.y + other.y,
|
||||||
|
z: self.z + other.z,
|
||||||
|
w: self.w + other.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjacent_vectors3() -> [Coordinate; 26] {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut vectors: [Coordinate; 26] = [Coordinate {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
w: 0,
|
||||||
|
}; 26];
|
||||||
|
for z in -1..=1 {
|
||||||
|
for y in -1..=1 {
|
||||||
|
for x in -1..=1 {
|
||||||
|
if !(x == 0 && y == 0 && z == 0) {
|
||||||
|
vectors[i].x = x;
|
||||||
|
vectors[i].y = y;
|
||||||
|
vectors[i].z = z;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vectors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjacent_vectors4() -> [Coordinate; 80] {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut vectors: [Coordinate; 80] = [Coordinate {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
w: 0,
|
||||||
|
}; 80];
|
||||||
|
for w in -1..=1 {
|
||||||
|
for z in -1..=1 {
|
||||||
|
for y in -1..=1 {
|
||||||
|
for x in -1..=1 {
|
||||||
|
if !(x == 0 && y == 0 && z == 0 && w == 0) {
|
||||||
|
vectors[i].x = x;
|
||||||
|
vectors[i].y = y;
|
||||||
|
vectors[i].z = z;
|
||||||
|
vectors[i].w = w;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vectors
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ADJACENT_VECTORS3: [Coordinate; 26] = adjacent_vectors3();
|
||||||
|
static ref ADJACENT_VECTORS4: [Coordinate; 80] = adjacent_vectors4();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum Cube {
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<char> for Cube {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(c: char) -> Result<Self> {
|
||||||
|
match c {
|
||||||
|
'#' => Ok(Cube::Active),
|
||||||
|
'.' => Ok(Cube::Inactive),
|
||||||
|
_ => Err(anyhow!("Unrecognized cube character: {}", c)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Cube {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Cube::Active => write!(f, "#"),
|
||||||
|
Cube::Inactive => write!(f, "."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cube {
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Cube::Active => true,
|
||||||
|
Cube::Inactive => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Grid {
|
||||||
|
cubes: HashMap<Coordinate, Cube>,
|
||||||
|
bounds: Bounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bounds {
|
||||||
|
min: Coordinate,
|
||||||
|
max: Coordinate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
fn from_reader<R: BufRead>(reader: R) -> Result<Self> {
|
||||||
|
let mut cubes = HashMap::new();
|
||||||
|
let mut max = Coordinate {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
w: 0,
|
||||||
|
};
|
||||||
|
for (y, line) in reader.lines().enumerate() {
|
||||||
|
for (x, c) in line?.chars().enumerate() {
|
||||||
|
cubes.insert(
|
||||||
|
Coordinate {
|
||||||
|
x: x as isize,
|
||||||
|
y: y as isize,
|
||||||
|
z: 0,
|
||||||
|
w: 0,
|
||||||
|
},
|
||||||
|
Cube::try_from(c)?,
|
||||||
|
);
|
||||||
|
max.x = x as isize;
|
||||||
|
}
|
||||||
|
max.y = y as isize;
|
||||||
|
}
|
||||||
|
let bounds = Bounds {
|
||||||
|
min: Coordinate {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
w: 0,
|
||||||
|
},
|
||||||
|
max,
|
||||||
|
};
|
||||||
|
Ok(Self { cubes, bounds })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase_bounds(&mut self) {
|
||||||
|
self.bounds.min.x -= 1;
|
||||||
|
self.bounds.min.y -= 1;
|
||||||
|
self.bounds.min.z -= 1;
|
||||||
|
self.bounds.max.x += 1;
|
||||||
|
self.bounds.max.y += 1;
|
||||||
|
self.bounds.max.z += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase_hypercube_bounds(&mut self) {
|
||||||
|
self.bounds.min.x -= 1;
|
||||||
|
self.bounds.min.y -= 1;
|
||||||
|
self.bounds.min.z -= 1;
|
||||||
|
self.bounds.min.w -= 1;
|
||||||
|
self.bounds.max.x += 1;
|
||||||
|
self.bounds.max.y += 1;
|
||||||
|
self.bounds.max.z += 1;
|
||||||
|
self.bounds.max.w += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_active_neighbors(&self, coord: &Coordinate) -> usize {
|
||||||
|
ADJACENT_VECTORS3
|
||||||
|
.iter()
|
||||||
|
.filter_map(|vector| {
|
||||||
|
let neighbor_coord = *coord + *vector;
|
||||||
|
match self.cubes.get(&neighbor_coord) {
|
||||||
|
Some(cube) if cube.is_active() => Some(()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_cube_state(cube: Option<&Cube>, active_neighbors: usize) -> Cube {
|
||||||
|
match cube {
|
||||||
|
Some(Cube::Active) => {
|
||||||
|
if active_neighbors == 2 || active_neighbors == 3 {
|
||||||
|
Cube::Active
|
||||||
|
} else {
|
||||||
|
Cube::Inactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if active_neighbors == 3 {
|
||||||
|
Cube::Active
|
||||||
|
} else {
|
||||||
|
Cube::Inactive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_cycle(&mut self) {
|
||||||
|
self.increase_bounds();
|
||||||
|
let mut new_cubes = HashMap::new();
|
||||||
|
for z in self.bounds.min.z..=self.bounds.max.z {
|
||||||
|
for y in self.bounds.min.y..=self.bounds.max.y {
|
||||||
|
for x in self.bounds.min.x..=self.bounds.max.x {
|
||||||
|
let coord = Coordinate { x, y, z, w: 0 };
|
||||||
|
let cube = self.cubes.get(&coord);
|
||||||
|
let active_neighbors = self.get_active_neighbors(&coord);
|
||||||
|
new_cubes.insert(coord, Grid::new_cube_state(cube, active_neighbors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cubes = new_cubes;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_active_hypercube_neighbors(&self, coord: &Coordinate) -> usize {
|
||||||
|
ADJACENT_VECTORS4
|
||||||
|
.iter()
|
||||||
|
.filter_map(|vector| {
|
||||||
|
let neighbor_coord = *coord + *vector;
|
||||||
|
match self.cubes.get(&neighbor_coord) {
|
||||||
|
Some(cube) if cube.is_active() => Some(()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_hypercube_cycle(&mut self) {
|
||||||
|
self.increase_hypercube_bounds();
|
||||||
|
let mut new_cubes = HashMap::new();
|
||||||
|
for w in self.bounds.min.w..=self.bounds.max.w {
|
||||||
|
for z in self.bounds.min.z..=self.bounds.max.z {
|
||||||
|
for y in self.bounds.min.y..=self.bounds.max.y {
|
||||||
|
for x in self.bounds.min.x..=self.bounds.max.x {
|
||||||
|
let coord = Coordinate { x, y, z, w };
|
||||||
|
let active_neighbors = self.get_active_hypercube_neighbors(&coord);
|
||||||
|
let cube = self.cubes.get(&coord);
|
||||||
|
new_cubes.insert(coord, Grid::new_cube_state(cube, active_neighbors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cubes = new_cubes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Grid {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for w in self.bounds.min.w..=self.bounds.max.w {
|
||||||
|
for z in self.bounds.min.z..=self.bounds.max.z {
|
||||||
|
writeln!(f, "z={} w={}", z, w)?;
|
||||||
|
for y in self.bounds.min.y..=self.bounds.max.y {
|
||||||
|
for x in self.bounds.min.x..=self.bounds.max.x {
|
||||||
|
let cube = self
|
||||||
|
.cubes
|
||||||
|
.get(&Coordinate { x, y, z, w })
|
||||||
|
.expect("cube to exist within bounds");
|
||||||
|
write!(f, "{}", &cube.to_string())?
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_part1(input_path: &str) -> Result<usize> {
|
||||||
|
let file = File::open(input_path)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let mut grid = Grid::from_reader(reader)?;
|
||||||
|
|
||||||
|
for _ in 0..6 {
|
||||||
|
grid.run_cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(grid.cubes.values().filter(|cube| cube.is_active()).count())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_part2(input_path: &str) -> Result<usize> {
|
||||||
|
let file = File::open(input_path)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let mut grid = Grid::from_reader(reader)?;
|
||||||
|
|
||||||
|
for _ in 0..6 {
|
||||||
|
grid.run_hypercube_cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(grid.cubes.values().filter(|cube| cube.is_active()).count())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut now = Instant::now();
|
||||||
|
println!("Part 1: {}", solve_part1(INPUT).unwrap());
|
||||||
|
println!("(elapsed: {:?})", now.elapsed());
|
||||||
|
now = Instant::now();
|
||||||
|
println!("");
|
||||||
|
println!("Part 2: {}", solve_part2(INPUT).unwrap());
|
||||||
|
println!("(elapsed: {:?})", now.elapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const TEST_INPUT: &str = "input/test.txt";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_input() {
|
||||||
|
let file = File::open(TEST_INPUT).unwrap();
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let grid = Grid::from_reader(reader).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
grid.to_string(),
|
||||||
|
r#"
|
||||||
|
z=0 w=0
|
||||||
|
.#.
|
||||||
|
..#
|
||||||
|
###
|
||||||
|
|
||||||
|
"#
|
||||||
|
.trim_start()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn solves_part1() {
|
||||||
|
assert_eq!(solve_part1(TEST_INPUT).unwrap(), 112);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn solves_part2() {
|
||||||
|
assert_eq!(solve_part2(TEST_INPUT).unwrap(), 848);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user