Skip to content

Instantly share code, notes, and snippets.

@whiter4bbit
Last active December 17, 2020 13:16
Show Gist options
  • Save whiter4bbit/a8c37090ec4007fb8ab49fce6b3606d2 to your computer and use it in GitHub Desktop.
Save whiter4bbit/a8c37090ec4007fb8ab49fce6b3606d2 to your computer and use it in GitHub Desktop.
use std::collections::{HashMap, HashSet};
use std::convert::Into;
use std::fs;
use std::io;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Point {
d: [i64; 4],
}
#[derive(Clone, Copy)]
enum Dimensions {
Three,
Four,
}
struct Neighbors {
base: [i64; 4],
submask: u32,
mask: u32,
}
fn has_0b11(submask: u32) -> bool {
for i in 0 as u32..4 {
if (submask >> (2 * i)) & 0b11 == 0b11 {
return true;
}
}
false
}
const SHIFTS: [i64; 3] = [0, -1, 1];
impl Iterator for Neighbors {
type Item = Point;
fn next(&mut self) -> Option<Point> {
while has_0b11(self.submask) {
self.submask = (self.submask - 1) & self.mask;
}
match self.submask {
0 => None,
_ => {
let mut d = self.base.clone();
for i in 0u32..4 {
d[i as usize] += SHIFTS[((self.submask >> (2 * i)) & 0b11) as usize];
}
self.submask = (self.submask - 1) & self.mask;
Some(Point { d: d })
}
}
}
}
impl Point {
fn neighbors(&self, dimensions: Dimensions) -> Neighbors {
let mask: u32 = match dimensions {
Dimensions::Three => 0b00111111,
Dimensions::Four => 0b11111111,
};
Neighbors {
base: self.d.clone(),
submask: mask,
mask: mask,
}
}
}
#[derive(Debug, Clone)]
struct Cubes {
active: HashSet<Point>,
}
impl Into<Cubes> for String {
fn into(self) -> Cubes {
Cubes {
active: self
.lines()
.enumerate()
.flat_map(|(y, line)| {
line.bytes()
.enumerate()
.filter_map(move |(x, cell)| match cell {
b'#' => Some(Point {
d: [x as i64, y as i64, 0, 0],
}),
_ => None,
})
})
.collect(),
}
}
}
fn transition(cubes: &Cubes, dimensions: Dimensions) -> Cubes {
let mut transition_active: HashSet<Point> = cubes
.active
.iter()
.filter(|point| {
let active_neighbors = point
.neighbors(dimensions)
.filter(|adjacent| cubes.active.contains(adjacent))
.take(4)
.count();
(2..=3).contains(&active_neighbors)
})
.map(|point| point.clone())
.collect();
let mut inactive_neighbors: HashMap<Point, usize> = HashMap::new();
cubes
.active
.iter()
.flat_map(|cube| {
cube.neighbors(dimensions)
.filter(|neighbor| !cubes.active.contains(neighbor))
})
.for_each(|cube| *inactive_neighbors.entry(cube).or_insert(0) += 1);
inactive_neighbors
.iter()
.filter(|(_, count)| count == &&3)
.for_each(|(cube, _)| {
transition_active.insert(cube.clone());
});
Cubes {
active: transition_active,
}
}
fn run_transitions<T>(init: T, count: usize, dimensions: Dimensions) -> Cubes
where
T: Into<Cubes>,
{
(0..count)
.into_iter()
.fold(init.into(), |result, _| transition(&result, dimensions))
}
#[test]
fn test_run_transitions() {
assert_eq!(
112,
run_transitions(
"
.#.
..#
###
"
.to_string(),
6,
Dimensions::Three,
)
.active
.len()
);
}
#[allow(dead_code)]
pub fn solve_p1(input: &str) -> io::Result<usize> {
Ok(
run_transitions(fs::read_to_string(input)?.to_string(), 6, Dimensions::Three)
.active
.len(),
)
}
#[allow(dead_code)]
pub fn solve_p2(input: &str) -> io::Result<usize> {
Ok(
run_transitions(fs::read_to_string(input)?.to_string(), 6, Dimensions::Four)
.active
.len(),
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment