Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 18

2022/day18.livemd

Day 18

Mix.install([{:kino, "~>0.8.0"}])

Section

input = Kino.Input.textarea("input file")

Common

defmodule Day18 do
  def count_sides(cube, cubes) do
    [{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}]
    |> Enum.reduce(0, fn delta, acc ->
      case MapSet.member?(cubes, new_position(cube, delta)) do
        true -> acc
        false -> acc + 1
      end
    end)
  end

  def new_position({x, y, z}, {dx, dy, dz}) do
    {x + dx, y + dy, z + dz}
  end
end

Part 1

positions =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(fn coord ->
    coord
    |> String.split(",", trim: true)
    |> Enum.map(&String.to_integer/1)
    |> List.to_tuple()
  end)

cubes = MapSet.new(positions)

cubes
|> Enum.reduce(0, fn cube, acc ->
  acc + Day18.count_sides(cube, cubes)
end)

Part 2

defmodule CubeMath do
  def invert(cubes) do
    {mx, my, mz} = bounds(cubes)

    for x <- -1..(mx + 1), y <- -1..(my + 1), z <- -1..(mz + 1), reduce: MapSet.new() do
      empty ->
        pos = {x, y, z}

        if MapSet.member?(cubes, pos) do
          empty
        else
          empty |> MapSet.put(pos)
        end
    end
  end

  def get_outer_area(cubes) do
    get_connected_cubes({-1, -1, -1}, cubes)
  end

  # Private Methdos
  defp bounds(cubes) do
    for {x, y, z} <- cubes, reduce: {0, 0, 0} do
      {mx, my, mz} -> {max(mx, x), max(my, y), max(mz, z)}
    end
  end

  defp get_connected_cubes(pos, cubes) do
    get_connected_cubes(pos, cubes, MapSet.new())
  end

  defp get_connected_cubes(pos, cubes, visited) do
    for delta <- [{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}],
        reduce: visited do
      visited ->
        neighboor = Day18.new_position(pos, delta)

        if MapSet.member?(cubes, neighboor) &amp;&amp; !MapSet.member?(visited, neighboor) do
          get_connected_cubes(neighboor, cubes, MapSet.put(visited, neighboor))
        else
          visited
        end
    end
  end
end

positions =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(fn coord ->
    coord
    |> String.split(",", trim: true)
    |> Enum.map(&amp;String.to_integer/1)
    |> List.to_tuple()
  end)

cubes = MapSet.new(positions)

total =
  cubes
  |> Enum.reduce(0, fn cube, acc ->
    acc + Day18.count_sides(cube, cubes)
  end)

# Invert cube
inverted = CubeMath.invert(cubes)

# Get outer area
outer_area = CubeMath.get_outer_area(inverted)
# find difference

difference = MapSet.difference(inverted, outer_area)

cubes = MapSet.new(difference)

interior =
  cubes
  |> Enum.reduce(0, fn cube, acc ->
    acc + Day18.count_sides(cube, cubes)
  end)

total - interior