-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday17.rs
156 lines (132 loc) · 3.78 KB
/
day17.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! [Day 17: Conway Cubes](https://adventofcode.com/2020/day/17)
use rustc_hash::FxHashSet;
#[derive(Eq, PartialEq, Hash, Clone)]
struct Cube {
is_hyper: bool,
x: i32,
y: i32,
z: i32,
w: i32,
}
impl Cube {
const fn new_3d(x: i32, y: i32, z: i32) -> Self {
Self {
is_hyper: false,
x,
y,
z,
w: 0,
}
}
const fn make_4(&self) -> Self {
Self {
is_hyper: true,
x: self.x,
y: self.y,
z: self.z,
w: self.w,
}
}
fn neighbors(&self) -> impl Iterator<Item = Self> + '_ {
(-1..=1).flat_map(move |dx| {
(-1..=1).flat_map(move |dy| {
(-1..=1).flat_map(move |dz| {
let fourth_dim = if self.is_hyper { -1..=1 } else { 0..=0 };
fourth_dim.filter_map(move |dw| {
if dx != 0 || dy != 0 || dz != 0 || (self.is_hyper && dw != 0) {
Some(Self {
is_hyper: self.is_hyper,
x: self.x + dx,
y: self.y + dy,
z: self.z + dz,
w: self.w + dw,
})
} else {
None
}
})
})
})
})
}
fn cycle(cubes: &FxHashSet<Self>) -> FxHashSet<Self> {
let mut next_cubes = FxHashSet::default();
let mut tested = FxHashSet::default();
for cube in cubes {
let mut actives = 0;
for c in cube.neighbors() {
if cubes.contains(&c) {
actives += 1;
} else if tested.insert(c.clone()) {
// c is inactive (and never tested)
if c.neighbors().filter(|cc| cubes.contains(cc)).count() == 3 {
// becomes active since it has exactly 3 active neighbors
next_cubes.insert(c);
}
}
}
if actives == 2 || actives == 3 {
// active with exactly 2 or 3 active neighbors, the cube remains active
next_cubes.insert(cube.clone());
}
}
next_cubes
}
}
struct Puzzle {
cubes: FxHashSet<Cube>,
}
impl Puzzle {
fn new(data: &str) -> Self {
let mut cubes = FxHashSet::default();
for (y, line) in (0..).zip(data.lines()) {
for (x, c) in (0..).zip(line.chars()) {
if c == '#' {
cubes.insert(Cube::new_3d(x, y, 0));
}
}
}
Self { cubes }
}
/// Solve part one.
fn part1(&self) -> usize {
let mut cubes = self.cubes.clone();
for _ in 0..6 {
cubes = Cube::cycle(&cubes);
}
cubes.len()
}
/// Solve part two.
fn part2(&self) -> usize {
let mut hypercubes = self.cubes.iter().map(Cube::make_4).collect();
for _ in 0..6 {
hypercubes = Cube::cycle(&hypercubes);
}
hypercubes.len()
}
}
/// # Panics
#[must_use]
pub fn solve(data: &str) -> (usize, usize) {
let puzzle = Puzzle::new(data);
(puzzle.part1(), puzzle.part2())
}
pub fn main() {
let args = aoc::parse_args();
args.run(solve);
}
#[cfg(test)]
mod test {
use super::*;
const TEST_INPUT: &str = include_str!("test.txt");
#[test]
fn test_part1() {
let puzzle = Puzzle::new(TEST_INPUT);
assert_eq!(puzzle.part1(), 112);
}
#[test]
fn test_part2() {
let puzzle = Puzzle::new(TEST_INPUT);
assert_eq!(puzzle.part2(), 848);
}
}