Powered by AppSignal & Oban Pro

d11

d11/d11.livemd

d11

Section

ExUnit.start()

defmodule D11 do
  def parse(input) do
    String.split(input)
    |> Enum.map(&String.to_integer/1)
  end

  def start_link do
    if :ets.whereis(:memo_table) == :undefined do
      :ets.new(:memo_table, [:set, :public, :named_table])
    else
      :ok
    end
  end

  def evolve(stone, n) do
    case :ets.lookup(:memo_table, {stone, n}) do
      [] ->
        result = D11.evolve_calc(stone, n)
        :ets.insert(:memo_table, {{stone, n}, result})
        result

      [{_, result}] ->
        result
    end
  end

  def evolve_calc(_, 0), do: 1

  def evolve_calc(stone, n) do
    cond do
      stone == 0 ->
        D11.evolve(1, n - 1)

      rem(String.length("#{stone}"), 2) == 0 ->
        {x, y} = String.split_at("#{stone}", div(String.length("#{stone}"), 2))

        D11.evolve(String.to_integer(x), n - 1) +
          D11.evolve(String.to_integer(y), n - 1)

      true ->
        D11.evolve(stone * 2024, n - 1)
    end
  end

  def part1(parsed) do
    parsed
    |> Enum.map(&D11.evolve(&1, 25))
    |> Enum.sum()
  end

  def part2(parsed) do
    parsed
    |> Enum.map(&D11.evolve(&1, 75))
    |> Enum.sum()
  end

  use ExUnit.Case

  test "sample" do
    assert 7 ==
             D11.parse("0 1 10 99 999")
             |> Enum.map(&D11.evolve(&1, 1))
             |> Enum.sum()

    assert 55312 == D11.part1([125, 17])
  end
end

D11.start_link()
ExUnit.run()
input = File.read!(__DIR__ <> "/input")
parsed = D11.parse(input)
D11.part1(parsed)
D11.part2(parsed)