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

Day 11

advent_of_code/2021/day-11.livemd

Day 11

Setup

Mix.install([
  {:kino, "~> 0.5.0"},
  {:vega_lite, "~> 0.1.1"}
])
input = Kino.Input.textarea("Input:")
lines = input |> Kino.Input.read() |> String.split("\n", trim: true)

grid =
  for {line, row} <- Enum.with_index(lines),
      {energy, col} <- Enum.with_index(String.to_charlist(line)),
      into: %{},
      do: {{row, col}, energy - ?0}

defmodule Recursion do
  def step(grid) do
    flash(Map.keys(grid), grid, MapSet.new())
  end

  defp flash([{row, col} = key | keys], grid, flashed) do
    value = grid[key]

    cond do
      is_nil(value) or key in flashed ->
        flash(keys, grid, flashed)

      grid[key] >= 9 ->
        keys = [
          {row - 1, col - 1},
          {row - 1, col},
          {row - 1, col + 1},
          {row, col - 1},
          {row, col + 1},
          {row + 1, col - 1},
          {row + 1, col},
          {row + 1, col + 1}
          | keys
        ]

        flash(keys, Map.put(grid, key, 0), MapSet.put(flashed, key))

      true ->
        flash(keys, Map.put(grid, key, value + 1), flashed)
    end
  end

  defp flash([], grid, flashed) do
    {grid, MapSet.size(flashed)}
  end
end

Part 1

1..100
|> Enum.map_reduce(grid, fn _, grid ->
  {grid, flashes} = Recursion.step(grid)
  {flashes, grid}
end)
|> elem(0)
|> Enum.sum()

Part 2

Stream.iterate(1, &amp;(&amp;1 + 1))
|> Enum.reduce_while(grid, fn i, grid ->
  case Recursion.step(grid) do
    {grid, flashes} when map_size(grid) == flashes -> {:halt, i}
    {grid, _flashes} -> {:cont, grid}
  end
end)

VegaLite

alias VegaLite, as: Vl
graph =
  Vl.new(height: 300, width: 300)
  |> Vl.mark(:circle, opacity: 0.8)
  |> Vl.encode_field(:x, "x", type: :quantitative, axis: false)
  |> Vl.encode_field(:y, "y", type: :quantitative, axis: false)
  |> Vl.encode_field(:color, "h",
    type: :quantitative,
    scale: [domain: [0, 9], range: ["brown", "red", "yellow"]]
  )
  |> Vl.encode_field(:size, "h",
    type: :quantitative,
    scale: [domain: [0, 9], range: [20, 100]]
  )
  |> Kino.VegaLite.new()
  |> Kino.render()

Kino.VegaLite.periodically(
  graph,
  100,
  grid,
  fn grid ->
    {grid, flashes} = Recursion.step(grid)

    data =
      Enum.map(grid, fn {{x, y}, h} ->
        %{"x" => x, "y" => y, "h" => if(h == 0, do: 9, else: h - 1)}
      end)

    Kino.VegaLite.clear(graph)
    Kino.VegaLite.push_many(graph, data)

    if map_size(grid) == flashes do
      :halt
    else
      {:cont, grid}
    end
  end
)