Skip to content

Instantly share code, notes, and snippets.

@SteffenDE
Created December 11, 2021 12:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SteffenDE/342dac043af8130cedc78e23b20a8a55 to your computer and use it in GitHub Desktop.
Save SteffenDE/342dac043af8130cedc78e23b20a8a55 to your computer and use it in GitHub Desktop.
Elixir LiveBook solution for AdventOfCode 2021 Day 11

AoC Day 11 - Dumbo Octopus

Setup

https://adventofcode.com/2021/day/11

Mix.install([:kino, :vega_lite])
input = Kino.Input.textarea("Please input data:")
data =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&String.to_charlist/1)
  |> Enum.map(fn line ->
    Enum.map(line, fn char -> List.to_integer([char]) end)
  end)
coords =
  data
  |> Enum.with_index()
  |> Enum.map(fn {line, row_idx} ->
    Enum.with_index(line)
    |> Enum.map(fn {col, col_idx} ->
      {{col_idx, row_idx}, col}
    end)
  end)
  |> Enum.concat()
  |> Enum.into(%{})
defmodule Octopus do
  defp update({coords, flashes}, {x, y} = key) do
    case coords[key] do
      nil ->
        {coords, flashes}

      height when height >= 9 ->
        {%{coords | key => 0}, flashes + 1}
        # diagonals
        |> update({x - 1, y - 1})
        |> update({x + 1, y + 1})
        |> update({x - 1, y + 1})
        |> update({x + 1, y - 1})
        # vertical
        |> update({x, y - 1})
        |> update({x, y + 1})
        # horizontal
        |> update({x - 1, y})
        |> update({x + 1, y})

      height when height == 0 ->
        {coords, flashes}

      height ->
        {%{coords | key => height + 1}, flashes}
    end
  end

  defp increment_9s({coords, flashes}) do
    max_x = Map.keys(coords) |> Enum.map(&elem(&1, 0)) |> Enum.max()
    max_y = Map.keys(coords) |> Enum.map(&elem(&1, 1)) |> Enum.max()

    for x <- 0..max_x, y <- 0..max_y, coords[{x, y}] > 9, reduce: {coords, flashes} do
      {coords, flashes} ->
        update({coords, flashes}, {x, y})
    end
  end

  def next(coords, flashes \\ 0) do
    {coords, flashes}
    |> then(fn {coords, flashes} ->
      {Map.map(coords, fn {_point, h} -> h + 1 end), flashes}
    end)
    |> increment_9s()
  end

  # part 1
  def flash(coords, steps \\ 100) do
    Enum.reduce(1..steps, {coords, 0}, fn _step, {coords, flashes} ->
      next(coords, flashes)
    end)
  end

  # part 2
  def synchronize(coords) do
    # infinite stream counting from 1
    Stream.unfold(1, &{&1, &1 + 1})
    |> Enum.reduce_while({coords, 0}, fn step, {coords, flashes} ->
      {new_coords, _flashes} = new_state = next(coords, flashes)

      if Enum.all?(new_coords, &match?({_point, 0}, &1)) do
        {:halt, step}
      else
        {:cont, new_state}
      end
    end)
  end
end

Part 1

Octopus.flash(coords, 100)
|> elem(1)

Part 1

Octopus.synchronize(coords)

VegaLite

alias VegaLite, as: Vl
graph =
  Vl.new(height: 100, width: 100)
  |> Vl.mark(:circle, size: 60, 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: ["yellow", "red", "brown"]]
  )
  |> Vl.encode_field(:tooltip, "h", format_type: "number")
  |> Kino.VegaLite.new()
  |> Kino.render()

Kino.VegaLite.periodically(
  graph,
  100,
  {coords, 1},
  fn {coords, t} ->
    {coords, _flashes} = Octopus.next(coords)

    data =
      Enum.map(coords, fn {{x, y}, h} ->
        %{"x" => x, "y" => y, "h" => h}
      end)

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

    if t < 256 do
      {:cont, {coords, t + 1}}
    else
      :halt
    end
  end
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment