Powered by AppSignal & Oban Pro

Day 7

2021/notebooks/day-07.livemd

Day 7

Setup

input = Aoc.get_input(7)
textarea = Kino.Input.textarea("Puzzle input", default: input)
test_textarea = Kino.Input.textarea("Test input")
options = [
  puzzle: "Puzzle",
  test: "Test"
]

select = Kino.Input.select("Input source", options)
lines =
  select
  |> Kino.Input.read()
  |> case do
    :puzzle -> input
    :test -> test_textarea |> Kino.Input.read()
  end
  |> String.trim()
  |> String.split(",", trim: true)
  |> Enum.map(&String.to_integer/1)

Part 1

max = lines |> Enum.max() |> IO.inspect(label: "max")
min = lines |> Enum.min() |> IO.inspect(label: "min")

calc = fn lines, n -> {n, lines |> Enum.map(fn x -> abs(n - x) end) |> Enum.sum()} end

min..max
|> Enum.map(fn i -> calc.(lines, i) end)
|> Enum.min_by(&elem(&1, 1))
|> elem(1)

Part 2

max = lines |> Enum.max() |> IO.inspect(label: "max")
min = lines |> Enum.min() |> IO.inspect(label: "min")

costs =
  0..max
  |> Enum.reduce(%{}, fn n, acc ->
    prev = Map.get(acc, n - 1, 0)
    Map.put(acc, n, prev + n)
  end)

calc = fn lines, n ->
  {n, lines |> Enum.map(fn x -> Map.get(costs, abs(n - x)) end) |> Enum.sum()}
end

min..max
|> Enum.map(fn i -> calc.(lines, i) end)
|> Enum.min_by(&elem(&1, 1))
|> elem(1)

Part 2 binary search

max = lines |> Enum.max() |> IO.inspect(label: "max")
min = lines |> Enum.min() |> IO.inspect(label: "min")

costs =
  0..max
  |> Enum.reduce(%{}, fn n, acc ->
    prev = Map.get(acc, n - 1, 0)
    Map.put(acc, n, prev + n)
  end)

calc = fn lines, n ->
  {n, lines |> Enum.map(fn x -> Map.get(costs, abs(n - x)) end) |> Enum.sum()}
end

left = calc.(lines, min)
right = calc.(lines, max)

Stream.iterate(0, &(&1 + 1))
|> Enum.reduce_while([left, right], fn _, [{l, _lc}, {r, _rc}] = acc ->
  min = Enum.min_by(acc, &elem(&1, 1))

  case div(r - l, 2) do
    0 ->
      {:halt, min}

    half ->
      mid_index = l + half
      mid = calc.(lines, mid_index)
      {:cont, [min, mid] |> Enum.sort_by(&elem(&1, 0))}
  end
end)
|> elem(1)