Powered by AppSignal & Oban Pro

AoC 2022 Day 18

2022/day18.livemd

AoC 2022 Day 18

Mix.install([:kino])

defmodule Utils do
  def split(line, sep \\ "") do
    String.split(line, sep, trim: true)
  end

  def split_all_lines(text, sep \\ "") do
    text
    |> String.split("\n", trim: true)
    |> Enum.map(&split(&1, sep))
  end

  def to_numbers(number) when is_binary(number) do
    String.to_integer(number)
  end

  def to_numbers(numbers) when is_list(numbers) do
    Enum.map(numbers, &to_numbers/1)
  end

  def to_matrix(text, sep \\ "") do
    text
    |> split_all_lines(sep)
    |> then(fn data ->
      for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
        {{r, c}, col}
      end
    end)
    |> Map.new()
  end
end

Setup

import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
data = split_all_lines(text, ",") |> to_numbers()

P1

defmodule P1 do
  @offsets [
    [1, 0, 0],
    [-1, 0, 0],
    [0, 1, 0],
    [0, -1, 0],
    [0, 0, 1],
    [0, 0, -1]
  ]

  def solve(data) do
    Enum.reduce(data, {MapSet.new(), 0}, fn cube, {set, count} ->
      neighbours = neighbours(cube)

      dc = Enum.count(neighbours, &(!(&1 in set)))

      {MapSet.put(set, cube), count + dc - (6 - dc)}
    end)
  end

  def neighbours(cube) do
    Enum.map(@offsets, fn offset ->
      Enum.zip_with(cube, offset, &Kernel.+/2)
    end)
  end
end
{lava_set, count} = P1.solve(data)
count

P2

defmodule P2 do
  def solve(lava_set) do
    Stream.resource(
      fn ->
        start = [-1, -1, -1]
        {[start], MapSet.new([start])}
      end,
      fn
        {[], air_set} ->
          {:halt, air_set}

        {[air_cube | air_todo], air_set} ->
          neighbours =
            P1.neighbours(air_cube)
            |> Enum.filter(fn cube ->
              Enum.all?(cube, &(&1 >= -1 and &1 <= 22)) and
                cube not in air_set and
                cube not in lava_set
            end)

          {[air_cube],
           {
             neighbours ++ air_todo,
             Enum.reduce(neighbours, air_set, &MapSet.put(&2, &1))
           }}
      end,
      fn _ -> IO.puts("╰(*°▽°*)╯") end
    )
    |> Enum.reduce(0, fn cube, count ->
      cube
      |> P1.neighbours()
      |> Enum.count(&(&1 in lava_set))
      |> Kernel.+(count)
    end)
  end
end
P2.solve(lava_set)