Skip to content

Commit 28ea3f1

Browse files
committed
Day 22 completed, finally.
1 parent c6a6d20 commit 28ea3f1

File tree

3 files changed

+223
-2
lines changed

3 files changed

+223
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ All solutions linked below:
3030
| [19](https://adventofcode.com/2023/day/19) | Aplenty | 67.0µs | 110µs | [day19.rs](./src/day19.rs) | :woozy_face: |
3131
| [20](https://adventofcode.com/2023/day/20) | Pulse Propagation | 5.00ms | 21.0ms | [day20.rs](./src/day20.rs) | :sob: |
3232
| [21](https://adventofcode.com/2023/day/21) | Step Counter | 2.31ms | 120ms | [day21.rs](./src/day21.rs) | :persevere: |
33-
<!--| [22](https://adventofcode.com/2023/day/22) | Sand Slabs | | | [day22.rs](./src/day22.rs) | |-->
33+
| [22](https://adventofcode.com/2023/day/22) | Sand Slabs | 14.4µs | 10.3ms | [day22.rs](./src/day22.rs) | :confounded: |
3434
<!--| [23](https://adventofcode.com/2023/day/23) | A Long Walk | | | [day23.rs](./src/day23.rs) | |-->
3535
<!--| [24](https://adventofcode.com/2023/day/24) | Never Tell Me The Odds | | | [day24.rs](./src/day24.rs) | |-->
3636
<!--| [25](https://adventofcode.com/2023/day/25) | Snowverload | | | [day25.rs](./src/day25.rs) | |-->
@@ -50,4 +50,5 @@ All solutions linked below:
5050
12. For day 16, this was my first time writing a depth-first search, and it ran perfectly on the first attempt :exploding_head:. Because I tend to write these solutions generalised for robustness, it effectively meant part 2 was already solved as well.
5151
13. For day 17, I was originally going for Dijkstra's, but decided to needlessly overcomplicate things by usinga heuristic, and included a visualiser to see the shortest path discovered. This could be a bit faster though.
5252
14. Day 18 basically reused the shoelace formula and Pick's theorem from day 10. It's not clear why part 2 consistnently run twice as fast as part 1 even though they both use the exact same function.
53-
15. Day 21 spent far too long trying to bug hunt, and it turns out it wasn't a math problem, it was a very basic problem involving the features I'm excluding.
53+
15. Day 21 spent far too long trying to bug hunt, and it turns out it wasn't a math problem, it was a very basic problem involving the features I'm excluding.
54+
16. Getting false positives in Day 22 tests was not helping matters much.

src/day22.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use std::collections::{HashSet, VecDeque};
2+
3+
use Colinear::*;
4+
5+
#[derive(Debug, Clone, PartialEq, Eq)]
6+
enum Colinear {
7+
X,
8+
Y,
9+
Z,
10+
}
11+
12+
#[derive(Debug, Clone, PartialEq, Eq)]
13+
struct Point {
14+
x: usize,
15+
y: usize,
16+
z: usize,
17+
}
18+
19+
impl Point {
20+
fn from_str(point_str: &str) -> Self {
21+
let vec: Vec<usize> = point_str
22+
.split(',')
23+
.map(|num| num.parse().unwrap())
24+
.collect();
25+
26+
Point {
27+
x: vec[0],
28+
y: vec[1],
29+
z: vec[2],
30+
}
31+
}
32+
}
33+
34+
#[derive(Debug, Clone, PartialEq, Eq)]
35+
pub struct Brick {
36+
head: Point,
37+
tail: Point,
38+
axis: Colinear,
39+
dominates: Vec<usize>,
40+
dominated: Vec<usize>,
41+
}
42+
43+
impl Brick {
44+
fn extend_dominated(&mut self, doms: &Vec<(usize, Option<usize>)>) {
45+
for (_, id) in doms {
46+
if let Some(brick_id) = id {
47+
self.dominated.push(*brick_id);
48+
}
49+
}
50+
}
51+
52+
fn height(&self) -> usize {
53+
match self.axis {
54+
Z => self.tail.z - self.head.z + 1,
55+
_ => 1,
56+
}
57+
}
58+
59+
fn pearls(&self) -> Vec<(usize, usize)> {
60+
match self.axis {
61+
X => (self.head.x..=self.tail.x).map(|x| (x, self.head.y)).collect(),
62+
Y => (self.head.y..=self.tail.y).map(|y| (self.head.x, y)).collect(),
63+
Z => vec![(self.head.x, self.head.y)],
64+
}
65+
}
66+
}
67+
68+
trait Tetris {
69+
fn disintegrate(&self, id: usize) -> usize;
70+
fn import(&mut self, sups: Vec<Vec<usize>>);
71+
}
72+
73+
impl Tetris for Vec<Brick> {
74+
fn disintegrate(&self, id: usize) -> usize {
75+
let mut count = 0;
76+
let mut queue = VecDeque::from([id]);
77+
let mut disintegrated = HashSet::new();
78+
79+
while let Some(brick_id) = queue.pop_front() {
80+
disintegrated.insert(brick_id);
81+
for &dom_id in &self[brick_id].dominates {
82+
if self[dom_id].dominated.iter().all(|&id| disintegrated.contains(&id)) {
83+
queue.push_back(dom_id);
84+
count += 1;
85+
}
86+
}
87+
}
88+
89+
count
90+
}
91+
92+
fn import(&mut self, sups: Vec<Vec<usize>>) {
93+
for (id, supports) in sups.iter().enumerate() {
94+
self[id].dominates.extend(supports);
95+
}
96+
}
97+
}
98+
99+
trait HeightRecord {
100+
fn drop(&mut self, brick: &Brick, id: usize, doms: &Vec<(usize, Option<usize>)>);
101+
fn find_doms(&self, brick: &Brick) -> Vec<(usize, Option<usize>)>;
102+
}
103+
104+
impl HeightRecord for [(usize, Option<usize>); 100] {
105+
fn drop(&mut self, brick: &Brick, id: usize, doms: &Vec<(usize, Option<usize>)>) {
106+
let points = brick.pearls();
107+
let new_z = doms.first().cloned().unwrap().0 + brick.height();
108+
for (x, y) in points.iter() {
109+
self[10 * y + x] = (new_z, Some(id));
110+
}
111+
}
112+
113+
fn find_doms(&self, brick: &Brick) -> Vec<(usize, Option<usize>)> {
114+
match brick.axis {
115+
Z => {
116+
vec![self[10 * brick.head.y + brick.head.x]]
117+
},
118+
_ => {
119+
let tops = brick.pearls().iter().map(|(x, y)| self[10 * y + x]).collect::<Vec<_>>();
120+
let max = tops.iter().map(|&(top, _)| top).max().unwrap();
121+
let uniques: HashSet<(usize, Option<usize>)> = tops.into_iter().filter(|&(top, _)| top == max).collect();
122+
uniques.iter().map(|(top, opt)| (*top, *opt)).collect()
123+
},
124+
}
125+
}
126+
}
127+
128+
#[aoc_generator(day22)]
129+
pub fn input_generator(input: &str) -> Vec<Brick> {
130+
let mut bricks = input
131+
.lines()
132+
.map(|line| {
133+
let (head, tail) = line
134+
.trim()
135+
.split_once('~')
136+
.map(|(l, r)| (Point::from_str(l), Point::from_str(r)))
137+
.expect("Invalid input format.");
138+
let axis = match (head.x != tail.x, head.y != tail.y) {
139+
(true, _) => X,
140+
(_, true) => Y,
141+
_ => Z,
142+
};
143+
Brick { head, tail, axis, dominates: vec![], dominated: vec![] }
144+
})
145+
.collect::<Vec<Brick>>();
146+
147+
bricks.sort_by_key(|brick| brick.head.z);
148+
149+
// Bricks are all confined to a finite x,y grid of infinite height
150+
// The actions performed is more like Tetris, but the result resembles a Jenga tower
151+
let mut jenga: [(usize, Option<usize>); 100] = [(0, None); 100];
152+
let mut dominates = vec![vec![]; bricks.len()];
153+
154+
for (id, brick) in bricks.iter_mut().enumerate() {
155+
let doms = jenga.find_doms(brick);
156+
brick.extend_dominated(&doms);
157+
doms.iter()
158+
.filter_map(|(_, dom_id)| dom_id.clone())
159+
.for_each(|brick_id| dominates[brick_id].push(id));
160+
jenga.drop(brick, id, &doms);
161+
}
162+
bricks.import(dominates);
163+
164+
bricks
165+
}
166+
167+
#[aoc(day22, part1)]
168+
pub fn solve_part1(input: &Vec<Brick>) -> usize {
169+
input
170+
.iter()
171+
.filter(|brick| {
172+
brick.dominates
173+
.iter()
174+
.all(|&id| input[id].dominated.len() > 1)
175+
})
176+
.count()
177+
}
178+
179+
#[aoc(day22, part2)]
180+
pub fn solve_part2(input: &Vec<Brick>) -> usize {
181+
let antisafe: Vec<usize> = input
182+
.iter()
183+
.enumerate()
184+
.filter_map(|(id, brick)| {
185+
if brick.dominates
186+
.iter()
187+
.all(|&dom_id| input[dom_id].dominated.len() > 1){
188+
None
189+
} else {
190+
Some(id)
191+
}
192+
})
193+
.collect();
194+
195+
antisafe.iter().map(|brick| input.disintegrate(brick.clone())).sum()
196+
}
197+
198+
#[cfg(test)]
199+
mod tests {
200+
use super::*;
201+
202+
const TEST: &str = "1,0,1~1,2,1
203+
0,0,2~2,0,2
204+
0,2,3~2,2,3
205+
0,0,4~0,2,4
206+
2,0,5~2,2,5
207+
0,1,6~2,1,6
208+
1,1,8~1,1,9";
209+
210+
#[test]
211+
fn part1_test() {
212+
assert_eq!(solve_part1(&input_generator(TEST)), 5);
213+
}
214+
215+
#[test]
216+
fn part2_test() {
217+
assert_eq!(solve_part2(&input_generator(TEST)), 7);
218+
}
219+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ pub mod day18;
2424
pub mod day19;
2525
pub mod day20;
2626
pub mod day21;
27+
pub mod day22;
2728

2829
aoc_lib! { year = 2023 }

0 commit comments

Comments
 (0)