Powered by AppSignal & Oban Pro

d19

d19/d19.livemd

d19

Section

ExUnit.start()

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

  def parse(input) do
    [top, bottom] = String.split(input, "\n\n")
    towels = String.split(top, ", ") |> Enum.map(&String.to_charlist/1)
    designs = String.split(bottom, "\n", trim: true) |> Enum.map(&String.to_charlist/1)
    {towels, designs}
  end

  def arrangements(towels, design) do
    case :ets.lookup(:memo_table, {towels, design}) do
      [] ->
        result = arrangements_calc(towels, design)
        :ets.insert(:memo_table, {{towels, design}, result})
        result

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

  defp arrangements_calc(_, []), do: 1

  defp arrangements_calc(towels, design) do
    Enum.map(towels, fn towel ->
      {left, right} = Enum.split(design, Enum.count(towel))
      if left == towel, do: arrangements(towels, right), else: 0
    end)
    |> Enum.sum()
  end

  def part1({towels, designs}) do
    Enum.count(designs, fn design -> arrangements(towels, design) > 0 end)
  end

  def part2({towels, designs}) do
    Enum.reduce(designs, 0, fn design, acc -> acc + arrangements(towels, design) end)
  end

  use ExUnit.Case

  test "sample" do
    input = "r, wr, b, g, bwu, rb, gb, br

brwrr
bggr
gbbr
rrbgbr
ubwu
bwurrg
brgr
bbrgwb
"

    assert 6 == D19.parse(input) |> D19.part1()
    assert 16 == D19.parse(input) |> D19.part2()
  end
end

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