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

Advent of code day 11

2024/livebooks/day-11.livemd

Advent of code day 11

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

Setup input

example = Kino.Input.textarea("Please paste your input example:")
stones =
  example
  |> Kino.Input.read()
  |> String.split(" ", trim: true)
  |> Enum.map(&String.to_integer/1)
# copied from elixir docs
defmodule SimpleCache do
  @moduledoc """
  A simple ETS based cache for expensive function calls.
  """

  def init_cache() do
    if :ets.whereis(:simple_cache) != :undefined do
      :ets.delete(:simple_cache)
    end

    :ets.new(:simple_cache, [:set, :public, :named_table])
  end

  @doc """
  Retrieve a cached value or apply the given function caching and returning
  the result.
  """
  def get(mod, fun, args, opts \\ []) do
    case lookup(mod, fun, args) do
      nil ->
        ttl = Keyword.get(opts, :ttl, 3600)
        cache_apply(mod, fun, args, ttl)

      result ->
        result
    end
  end

  defp lookup(mod, fun, args) do
    case :ets.lookup(:simple_cache, [mod, fun, args]) do
      [result | _] -> check_freshness(result)
      [] -> nil
    end
  end

  defp check_freshness({_mfa, result, expiration}) do
    cond do
      expiration > :os.system_time(:seconds) -> result
      :else -> nil
    end
  end

  defp cache_apply(mod, fun, args, ttl) do
    result = apply(mod, fun, args)
    expiration = :os.system_time(:seconds) + ttl
    :ets.insert(:simple_cache, {[mod, fun, args], result, expiration})
    result
  end
end
SimpleCache.init_cache()
# changed my mind. lets use memo xD
defmodule Blinker do
  def blink(_stone, 0), do: 1
  def blink(0, steps), do: SimpleCache.get(Blinker, :blink, [1, steps - 1])

  def blink(stone, steps) do
    digits = Integer.digits(stone)

    case rem(Enum.count(digits), 2) do
      0 ->
        {a, b} = Enum.split(digits, div(Enum.count(digits), 2))

        SimpleCache.get(Blinker, :blink, [Integer.undigits(a), steps - 1]) +
          SimpleCache.get(Blinker, :blink, [Integer.undigits(b), steps - 1])

      1 ->
        SimpleCache.get(Blinker, :blink, [stone * 2024, steps - 1])
    end
  end
end

Part 01

Enum.reduce(stones, 0, fn stone, acc ->
  Blinker.blink(stone, 25) + acc
end)

Part 02

Enum.reduce(stones, 0, fn stone, acc ->
  Blinker.blink(stone, 75) + acc
end)