advent-of-code/2022/src/day_18.rs
2022-12-28 23:25:32 -05:00

202 lines
6.3 KiB
Rust

use std::collections::HashSet;
use Side::*;
type Point = (i32, i32, i32);
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
enum Side {
/// positive z side
Top,
/// negative z side
Bottom,
/// negative x side
Left,
/// positive x side
Right,
/// positive y side
Front,
/// negative y side
Back,
}
impl Side {
fn inverse(&self) -> Self {
match *self {
Top => Bottom,
Bottom => Top,
Left => Right,
Right => Left,
Front => Back,
Back => Front,
}
}
}
type Data = HashSet<Point>;
type Output = i32;
fn parse(input: &str) -> Data {
input
.lines()
.map(|l| l.split(',').map(str::parse).map(Result::unwrap))
.map(|mut iter| {
(
iter.next().unwrap(),
iter.next().unwrap(),
iter.next().unwrap(),
)
})
.collect()
}
fn part_one(data: Data) -> Output {
data.iter()
.map(|(x, y, z)| {
let offsets = [
(0, 0, 1),
(0, 1, 0),
(1, 0, 0),
(0, 0, -1),
(0, -1, 0),
(-1, 0, 0),
];
offsets
.iter()
.filter(|(x_off, y_off, z_off)| !data.contains(&(x + x_off, y + y_off, z + z_off)))
.count() as Output
})
.sum()
}
fn part_two(data: Data) -> Output {
let mut queue = vec![(
*data
.iter()
.max_by(|(x1, _, _), (x2, _, _)| x1.cmp(x2))
.unwrap(),
Top,
)];
let mut visited: HashSet<(Point, Side)> = queue.clone().into_iter().collect();
for i in 0.. {
if i >= queue.len() {
return visited.len() as Output;
}
let ((curr_x, curr_y, curr_z), curr_side) = queue[i];
let offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)];
let positions = offsets.iter().map(|(a, b)| match curr_side {
Top | Bottom => (curr_x + a, curr_y + b, curr_z),
Left | Right => (curr_x, curr_y + a, curr_z + b),
Front | Back => (curr_x + a, curr_y, curr_z + b),
});
for (new_x, new_y, new_z) in positions {
let res = match curr_side {
Top | Bottom => {
let side = if curr_x > new_x {
Right
} else if curr_x < new_x {
Left
} else if curr_y > new_y {
Front
} else {
Back
};
if curr_side == Top {
if data.contains(&(new_x, new_y, new_z + 1)) {
((new_x, new_y, new_z + 1), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Top)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
} else {
if data.contains(&(new_x, new_y, new_z - 1)) {
((new_x, new_y, new_z - 1), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Bottom)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
}
}
Left | Right => {
let side = if curr_z > new_z {
Top
} else if curr_z < new_z {
Bottom
} else if curr_y > new_y {
Front
} else {
Back
};
if curr_side == Left {
if data.contains(&(new_x - 1, new_y, new_z)) {
((new_x - 1, new_y, new_z), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Left)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
} else {
if data.contains(&(new_x + 1, new_y, new_z)) {
((new_x + 1, new_y, new_z), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Right)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
}
}
Front | Back => {
let side = if curr_z > new_z {
Top
} else if curr_z < new_z {
Bottom
} else if curr_x > new_x {
Right
} else {
Left
};
if curr_side == Front {
if data.contains(&(new_x, new_y + 1, new_z)) {
((new_x, new_y + 1, new_z), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Front)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
} else {
if data.contains(&(new_x + 1, new_y, new_z)) {
((new_x + 1, new_y, new_z), side)
} else if data.contains(&(new_x, new_y, new_z)) {
((new_x, new_y, new_z), Back)
} else {
((curr_x, curr_y, curr_z), side.inverse())
}
}
}
};
if !visited.contains(&res) {
visited.insert(res);
println!("{:?}", res);
queue.push(res);
}
}
}
unreachable!()
}
advent_of_code_macro::generate_tests!(
day 18,
parse,
part_one,
part_two,
sample tests [64, 58],
star tests [4300, 0]
);