1
0
Fork 0

day(18): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2023-01-07 16:19:37 +01:00
parent ca5174b41e
commit 89a9758e64
Signed by: mfocko
GPG key ID: 7C47D46246790496
2 changed files with 199 additions and 0 deletions

13
samples/day18.txt Normal file
View 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
View 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
)
}
}