# Advent of Code 2022 - Day 18

Today was fun and I got it right the first time MapSets saves the day again.

1 Like

A slightly different take on the same general idea:

``````defmodule Aoe.Y22.Day18 do
alias Aoe.Input, warn: false

Input.stream_file_lines(file, trim: true)
end

def parse_input!(input, _part) do
input |> Enum.map(&parse_line/1)
end

defp parse_line(line) do
[x, y, z] = line |> String.split(",") |> Enum.map(&String.to_integer/1)
{x, y, z}
end

def part_one(cube_list) do
lookup_map = Map.new(cube_list, &{&1, true})

surf_map =
Enum.reduce(cube_list, %{}, fn cube, surf_map ->
Map.put(surf_map, cube, n_uncovered(cube, lookup_map))
end)

surf_map |> Map.values() |> Enum.sum()
end

def part_two(cube_list) do
lookup_map = Map.new(cube_list, &{&1, true})

xl = Enum.reduce(cube_list, &min_x/2) - 1
yl = Enum.reduce(cube_list, &min_y/2) - 1
zl = Enum.reduce(cube_list, &min_z/2) - 1

xh = Enum.reduce(cube_list, &max_x/2) + 1
yh = Enum.reduce(cube_list, &max_y/2) + 1
zh = Enum.reduce(cube_list, &max_z/2) + 1

path_start = {xl, yl, zl}
max_range = {xh, yh, zh}

explored = explore([path_start], {path_start, max_range}, lookup_map, %{})

explored |> Map.values() |> Enum.sum()
end

defp explore([cur | open], minmax, lookup_map, explored) do
{lava_neighs, free_neighs} = split_neighs(cur, lookup_map, minmax)
faces = length(lava_neighs)
explored = Map.put(explored, cur, faces)
free_neighs = Enum.filter(free_neighs, &(&1 not in open and not Map.has_key?(explored, &1)))
explore(free_neighs ++ open, minmax, lookup_map, explored)
end

defp explore([], _, _, explored) do
explored
end

defp split_neighs(cube, lookup_map, minmax) do
neighs = cardinal_neighbours(cube)
{lava_neighs, free_neighs} = Enum.split_with(neighs, &Map.has_key?(lookup_map, &1))
free_neighs = clamp_neighs(free_neighs, minmax)
{lava_neighs, free_neighs}
end

defp clamp_neighs(cubes, minmax) do
Enum.filter(cubes, &in_boundary(&1, minmax))
end

defp in_boundary({x, y, z}, {{xl, yl, zl}, {xh, yh, zh}}) do
x >= xl and x <= xh and
y >= yl and y <= yh and
z >= zl and z <= zh
end

defp n_uncovered(cube, lookup_map) do
cube |> cardinal_neighbours() |> Enum.reject(&Map.has_key?(lookup_map, &1)) |> Enum.count()
end

defp cardinal_neighbours({x, y, z}) do
[
{x - 1, y, z},
{x + 1, y, z},
{x, y - 1, z},
{x, y + 1, z},
{x, y, z - 1},
{x, y, z + 1}
]
end

defp max_x({a, _, _}, {b, _, _}), do: max(a, b)
defp max_x({a, _, _}, b) when is_integer(b), do: max(a, b)
defp min_x({a, _, _}, {b, _, _}), do: min(a, b)
defp min_x({a, _, _}, b) when is_integer(b), do: min(a, b)
defp max_y({_, a, _}, {_, b, _}), do: max(a, b)
defp max_y({_, a, _}, b) when is_integer(b), do: max(a, b)
defp min_y({_, a, _}, {_, b, _}), do: min(a, b)
defp min_y({_, a, _}, b) when is_integer(b), do: min(a, b)
defp max_z({_, _, a}, {_, _, b}), do: max(a, b)
defp max_z({_, _, a}, b) when is_integer(b), do: max(a, b)
defp min_z({_, _, a}, {_, _, b}), do: min(a, b)
defp min_z({_, _, a}, b) when is_integer(b), do: min(a, b)
end

``````

Quick and fun today Still going to try and work through as many days as I can. Very frustrated that I got stuck on a stupid input parsing error for so long on day 17 as this day was actually pretty straight forward. Going to look through other solutions to see how part two was handled as the method I took is pretty inefficient.

@kwando looking at your solution is really nice but I don’t understand how you account for air that is within the overall structure of the droplet but has a connection to outside the droplet. I had initially tried a similar approach but got the wrong answer b/c some air pockets were only partially enclosed. How does `collect_connected/3` avoid that?