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

Day 11

day11.livemd

Day 11

Setup

Mix.install([
  {:kino, "~> 0.4.1"},
  {:vega_lite, "~> 0.1.1"}
])
Example input:
5483143223
2745854711
5264556173
6141336146
6357385478
4167524645
2176841721
6882881134
4846848554
5283751526
Real input:
6617113584
6544218638
5457331488
1135675587
1221353216
1811124378
1387864368
4427637262
6778645486
3682146745
input = Kino.Input.textarea("Puzzle Input")

Process Input

lines =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)

grid =
  for {line, row} <- Enum.with_index(lines),
      {number, col} <- Enum.with_index(String.split(line, "", trim: true)),
      into: %{} do
    {{row, col}, {String.to_integer(number), false}}
  end

""

Part 1 Module First Try

defmodule Octopuses do
  def run(grid, steps \\ 1) do
    grow(grid, steps, 0)
  end

  defp grow(_grid, 0, count) do
    count
  end

  defp grow(grid, steps, count) do
    {new_grid, new_count} =
      grid
      |> Map.map(fn {_key, {val, flashed}} -> {val + 1, flashed} end)
      |> process()

    grow(new_grid, steps - 1, count + new_count)
  end

  defp process(grid) do
    Enum.filter(grid, fn {_key, {val, flashed}} -> val > 9 and !flashed end)
    |> flash(grid)
  end

  defp flash([], grid) do
    flashcount =
      for {_point, status} <- grid,
          {_val, true} <- [status] do
        1
      end
      |> Enum.sum()

    grid =
      Map.map(grid, fn {_point, {_val, flashed} = status} ->
        if flashed, do: {0, false}, else: status
      end)

    {grid, flashcount}
  end

  defp flash(flashes, grid) do
    grid =
      Enum.reduce(flashes, grid, fn {point, {val, _flashed}}, acc ->
        Map.put(acc, point, {val, true})
      end)

    flashpoints =
      for {{x, y}, _status} <- flashes do
        [
          {x - 1, y - 1},
          {x, y - 1},
          {x + 1, y - 1},
          {x - 1, y},
          {x + 1, y},
          {x - 1, y + 1},
          {x, y + 1},
          {x + 1, y + 1}
        ]
      end
      |> List.flatten()

    Enum.reduce(flashpoints, grid, fn point, acc ->
      Map.get_and_update(acc, point, fn status ->
        if is_nil(status) do
          :pop
        else
          {val, flashed} = status
          {status, {val + 1, flashed}}
        end
      end)
      |> elem(1)
    end)
    |> process()
  end
end

Part 1 exec

grid
|> Octopuses.run(100)

Part 2 Module First Try

defmodule Octopus do
  def run(grid) do
    grow(grid, 1)
  end

  defp grow(grid, step) do
    {new_grid, count} =
      grid
      |> Map.map(fn {_key, {val, flashed}} -> {val + 1, flashed} end)
      |> process()

    if count == Enum.count(grid) do
      step
    else
      grow(new_grid, step + 1)
    end
  end

  defp process(grid) do
    Enum.filter(grid, fn {_key, {val, flashed}} -> val > 9 and !flashed end)
    |> flash(grid)
  end

  defp flash([], grid) do
    flashcount =
      for {_point, status} <- grid,
          {_val, true} <- [status] do
        1
      end
      |> Enum.sum()

    grid =
      Map.map(grid, fn {_point, {_val, flashed} = status} ->
        if flashed, do: {0, false}, else: status
      end)

    {grid, flashcount}
  end

  defp flash(flashes, grid) do
    grid =
      Enum.reduce(flashes, grid, fn {point, {val, _flashed}}, acc ->
        Map.put(acc, point, {val, true})
      end)

    flashpoints =
      for {{x, y}, _status} <- flashes do
        [
          {x - 1, y - 1},
          {x, y - 1},
          {x + 1, y - 1},
          {x - 1, y},
          {x + 1, y},
          {x - 1, y + 1},
          {x, y + 1},
          {x + 1, y + 1}
        ]
      end
      |> List.flatten()

    Enum.reduce(flashpoints, grid, fn point, acc ->
      Map.get_and_update(acc, point, fn status ->
        if is_nil(status) do
          :pop
        else
          {val, flashed} = status
          {status, {val + 1, flashed}}
        end
      end)
      |> elem(1)
    end)
    |> process()
  end
end

Part 2 exec

Octopus.run(grid)

Process Input Jose Way

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}

""

Module Jose Way

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

  def inspect({grid, flashes}) do
    for row <- 0..9 do
      IO.puts(for(col <- 0..9, do: grid[{row, col}] + ?0))
    end

    IO.puts("flashes: #{flashes}")
    grid
  end
end

Part 1 Jose Way

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

Part 2 Jose Way

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

Source: https://gist.github.com/SteffenDE/342dac043af8130cedc78e23b20a8a55

Source: https://github.com/josevalim/aoc/blob/main/2021/day-11.livemd

alias VegaLite, as: Vl

graph =
  Vl.new(height: 200, width: 200)
  |> 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: ["purple", "blue", "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
)