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