day(18): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
ca5174b41e
commit
89a9758e64
2 changed files with 199 additions and 0 deletions
13
samples/day18.txt
Normal file
13
samples/day18.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
2,2,2
|
||||
1,2,2
|
||||
3,2,2
|
||||
2,1,2
|
||||
2,3,2
|
||||
2,2,1
|
||||
2,2,3
|
||||
2,2,4
|
||||
2,2,6
|
||||
1,2,5
|
||||
3,2,5
|
||||
2,1,5
|
||||
2,3,5
|
186
src/bin/day18.rs
Normal file
186
src/bin/day18.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
use std::cmp::{max, min};
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
|
||||
use aoc_2022::*;
|
||||
|
||||
type Coord = Vector3D<i32>;
|
||||
|
||||
type Input = Vec<Coord>;
|
||||
type Output = usize;
|
||||
|
||||
lazy_static! {
|
||||
static ref TOUCHING: Vec<Coord> = vec![-1, 1]
|
||||
.iter()
|
||||
.flat_map(|&d| vec![
|
||||
Coord::new(d, 0, 0),
|
||||
Coord::new(0, d, 0),
|
||||
Coord::new(0, 0, d)
|
||||
])
|
||||
.collect_vec();
|
||||
}
|
||||
|
||||
fn get_bounding_box(droplets: &[Coord]) -> (Coord, Coord) {
|
||||
let min_coord =
|
||||
droplets
|
||||
.iter()
|
||||
.fold(Coord::new(i32::MAX, i32::MAX, i32::MAX), |acc, &droplet| {
|
||||
Coord::new(
|
||||
min(*acc.x(), *droplet.x()),
|
||||
min(*acc.y(), *droplet.y()),
|
||||
min(*acc.z(), *droplet.z()),
|
||||
)
|
||||
});
|
||||
let max_coord =
|
||||
droplets
|
||||
.iter()
|
||||
.fold(Coord::new(i32::MIN, i32::MIN, i32::MIN), |acc, &droplet| {
|
||||
Coord::new(
|
||||
max(*acc.x(), *droplet.x()),
|
||||
max(*acc.y(), *droplet.y()),
|
||||
max(*acc.z(), *droplet.z()),
|
||||
)
|
||||
});
|
||||
|
||||
(min_coord, max_coord)
|
||||
}
|
||||
|
||||
fn _check_bounding_box(droplets: &[Coord]) {
|
||||
let (min_coord, max_coord) = get_bounding_box(droplets);
|
||||
|
||||
debug!("Bounding box: {:?} <-> {:?}", min_coord, max_coord);
|
||||
debug!(
|
||||
"Cubes in a bounding box: {:?}",
|
||||
(*max_coord.x() - *min_coord.x() + 1)
|
||||
* (*max_coord.y() - *min_coord.y() + 1)
|
||||
* (*max_coord.z() - *min_coord.z() + 1)
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Cube {
|
||||
Air,
|
||||
Lava,
|
||||
Obsidian,
|
||||
}
|
||||
|
||||
fn aerate(space: &mut BTreeMap<Coord, Cube>, start: Coord) {
|
||||
let mut queue = VecDeque::<Coord>::new();
|
||||
queue.push_back(start);
|
||||
space.insert(start, Cube::Air);
|
||||
|
||||
while let Some(pos) = queue.pop_front() {
|
||||
for neighbour in TOUCHING
|
||||
.iter()
|
||||
.map(|&d| d + pos)
|
||||
.filter(|pos| *space.get(pos).unwrap_or(&Cube::Air) == Cube::Lava)
|
||||
.collect_vec()
|
||||
{
|
||||
space.insert(neighbour, Cube::Air);
|
||||
queue.push_back(neighbour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Day18;
|
||||
impl Solution<Input, Output> for Day18 {
|
||||
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||
file_to_lines(pathname)
|
||||
.iter()
|
||||
.map(|l| {
|
||||
let coords = l.split(',').map(|c| c.parse().unwrap()).collect_vec();
|
||||
|
||||
Coord::new(coords[0], coords[1], coords[2])
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
fn part_1(input: &Input) -> Output {
|
||||
let mut surface = 0;
|
||||
|
||||
let mut droplets = BTreeSet::<Coord>::new();
|
||||
for &droplet in input {
|
||||
surface += 6;
|
||||
|
||||
for _touching in TOUCHING
|
||||
.iter()
|
||||
.map(|&d| d + droplet)
|
||||
.filter(|d| droplets.contains(d))
|
||||
{
|
||||
surface -= 2;
|
||||
}
|
||||
|
||||
droplets.insert(droplet);
|
||||
}
|
||||
|
||||
surface
|
||||
}
|
||||
|
||||
fn part_2(input: &Input) -> Output {
|
||||
let mut space = BTreeMap::<Coord, Cube>::new();
|
||||
|
||||
_check_bounding_box(input); // for debugging purposes
|
||||
let (min_coords, max_coords) = get_bounding_box(input);
|
||||
|
||||
// lava everywhere
|
||||
for cube in [
|
||||
*min_coords.x()..=*max_coords.x(),
|
||||
*min_coords.y()..=*max_coords.y(),
|
||||
*min_coords.z()..=*max_coords.z(),
|
||||
]
|
||||
.into_iter()
|
||||
.multi_cartesian_product()
|
||||
.map(|coords| {
|
||||
let (x, y, z) = coords
|
||||
.into_iter()
|
||||
.collect_tuple::<(i32, i32, i32)>()
|
||||
.unwrap();
|
||||
|
||||
Coord::new(x, y, z)
|
||||
}) {
|
||||
space.insert(cube, Cube::Lava);
|
||||
}
|
||||
|
||||
// override with obsidian
|
||||
for &droplet in input {
|
||||
space.insert(droplet, Cube::Obsidian);
|
||||
}
|
||||
|
||||
// update the outside with the air using a BFS
|
||||
aerate(
|
||||
&mut space,
|
||||
Coord::new(*min_coords.x(), *min_coords.y(), *min_coords.z()),
|
||||
);
|
||||
|
||||
// count the faces touching the air
|
||||
let mut surface = 0;
|
||||
for &droplet in input {
|
||||
surface += TOUCHING
|
||||
.iter()
|
||||
.map(|&d| space.get(&(droplet + d)).unwrap_or(&Cube::Air))
|
||||
.filter(|&cube| *cube == Cube::Air)
|
||||
.count();
|
||||
}
|
||||
|
||||
surface
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Day18::run("sample")
|
||||
Day18::main()
|
||||
}
|
||||
|
||||
test_sample!(day_18, Day18, 64, 58);
|
||||
|
||||
#[cfg(test)]
|
||||
mod day_18_additional_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_small_example() {
|
||||
assert_eq!(
|
||||
Day18::part_1(&vec![Vector3D::new(1, 1, 1), Vector3D::new(2, 1, 1)]),
|
||||
10
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue