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

Advent 2021 - Day 11

day11.livemd

Advent 2021 - Day 11

Setup

Mix.install([
  {:kino, github: "livebook-dev/kino"}
])
input = Kino.Input.textarea("Please paste your input file:")
list_of_lists =
  input
  |> Kino.Input.read()
  |> String.trim()
  |> String.split("\n")
  |> Enum.map(fn line ->
    line |> String.graphemes() |> Enum.map(&String.to_integer(&1)) |> Enum.with_index()
  end)
  |> Enum.with_index()

grid =
  Enum.reduce(list_of_lists, %{}, fn {row, y}, acc ->
    row =
      Enum.reduce(row, %{}, fn {value, x}, acc ->
        Map.merge(acc, %{{x, y} => value})
      end)

    Map.merge(row, acc)
  end)

Utils

defmodule Utils do
  def run(grid, condition) do
    step(grid, 0, 0, condition)
  end

  def step(grid, step_count, flash_count, condition) do
    {grid, flashed} =
      Map.map(grid, fn {_, v} -> v + 1 end)
      |> process_flashes()

    grid = Map.map(grid, fn {_, v} -> if v >= 10, do: 0, else: v end)

    step_count = step_count + 1

    if condition.(step_count, flashed) do
      {step_count, flash_count}
    else
      flash_count = Enum.count(flashed) + flash_count

      step(grid, step_count, flash_count, condition)
    end
  end

  def flash(grid, point = {x, y}, flashed) do
    inc = fn grid, x, y ->
      if Map.has_key?(grid, {x, y}) do
        Map.update!(grid, {x, y}, &(&1 + 1))
      else
        grid
      end
    end

    grid =
      grid
      |> inc.(x - 1, y - 1)
      |> inc.(x - 1, y)
      |> inc.(x - 1, y + 1)
      |> inc.(x, y - 1)
      |> inc.(x, y + 1)
      |> inc.(x + 1, y - 1)
      |> inc.(x + 1, y)
      |> inc.(x + 1, y + 1)

    flashed = MapSet.put(flashed, point)

    {grid, flashed}
  end

  def process_flashes(grid, flashed \\ MapSet.new()) do
    flash_points =
      Map.filter(grid, fn {point, value} ->
        already_flashed = point in flashed
        greater_than_cap = value >= 10
        !already_flashed and greater_than_cap
      end)

    {new_grid, flashed} =
      Enum.reduce(flash_points, {grid, flashed}, fn {point, _}, {grid, flashed} ->
        flash(grid, point, flashed)
      end)

    if grid == new_grid do
      {new_grid, flashed}
    else
      process_flashes(new_grid, flashed)
    end
  end
end

Part 1

{_, total_flashes} = Utils.run(grid, fn step_count, _ -> step_count > 100 end)

total_flashes

Part 2

count_of_all_points = Enum.count(Map.keys(grid))

{step_count, _} =
  Utils.run(grid, fn _, flashes ->
    Enum.count(flashes) == count_of_all_points
  end)

step_count