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

Day 11

y2021/day-11.livemd

Day 11

Setup

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

Input

input = Kino.Input.textarea("Please paste your input:")
input =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&String.to_charlist/1)
  |> Enum.with_index()
  |> Enum.reduce(%{}, fn {line, r}, acc ->
    line
    |> Enum.with_index()
    |> Enum.reduce(acc, fn {char, c}, acc -> Map.put(acc, {r, c}, char - ?0) end)
  end)

Recursor

defmodule Recursor do
  def step(grid, times) do
    1..times
    |> Enum.reduce(grid, fn _, grid_acc ->
      grid = increase_all(grid_acc)

      Enum.reduce(grid, grid, fn {coords, _}, acc ->
        run(acc, coords)
      end)
    end)
  end

  defp run(grid, {r, c} = coords) do
    case grid[coords] do
      10 ->
        Agent.update(:counter, &(&1 + 1))

        grid
        |> Map.put(coords, 0)
        |> add({r - 1, c - 1})
        |> add({r - 1, c})
        |> add({r - 1, c + 1})
        |> add({r, c - 1})
        |> add({r, c + 1})
        |> add({r + 1, c - 1})
        |> add({r + 1, c})
        |> add({r + 1, c + 1})

      _ ->
        grid
    end
  end

  defp add(grid, coords) when not is_map_key(grid, coords), do: grid
  defp add(grid, coords) when :erlang.map_get(coords, grid) == 10, do: grid
  defp add(grid, coords) when :erlang.map_get(coords, grid) == 0, do: grid

  defp add(grid, coords) do
    grid
    |> Map.update!(coords, &(&1 + 1))
    |> run(coords)
  end

  defp increase_all(grid) do
    grid
    |> Enum.reduce(%{}, fn {coords, energy_level}, acc ->
      Map.put(acc, coords, energy_level + 1)
    end)
  end
end

Part 1

if Process.whereis(:counter), do: Agent.stop(:counter)
{:ok, _pid} = Agent.start_link(fn -> 0 end, name: :counter)

input
|> Recursor.step(100)

Agent.get(:counter, & &1)

Part 2

if Process.whereis(:counter), do: Agent.stop(:counter)
{:ok, _pid} = Agent.start_link(fn -> 0 end, name: :counter)

Stream.iterate(1, &(&1 + 1))
|> Enum.reduce_while(input, fn step, grid ->
  grid = Recursor.step(grid, 1)

  if Enum.all?(grid, fn {_, energy_level} -> energy_level == 0 end),
    do: {:halt, step},
    else: {:cont, grid}
end)