summaryrefslogblamecommitdiffstats
path: root/src/day09.exs
blob: b8164e40e2ab1d5e5676ff8a32926f1aaa97e7f6 (plain) (tree)


























































                                                                        




                                                                  



































                                                            
defmodule Day09 do
  def part1(data) do
    values = data
    |> String.split("\n", trim: true)
    |> hd()
    |> String.codepoints()
    |> Enum.map(& String.to_integer/1)

    files = values
    |> Enum.take_every(2)
    |> Enum.with_index()
    |> Enum.flat_map(fn {v, i} -> Stream.cycle([i]) |> Enum.take(v) end)

    revfiles = files
    |> Enum.reverse()

    v2 = values
    |> Enum.with_index()
    |> Enum.flat_map(fn {v, i} -> Stream.cycle([i]) |> Enum.take(v) end)
    |> Enum.with_index()

    c1 = v2
    |> Enum.filter(fn {i, _} -> rem(i, 2) == 0 end)
    |> Enum.zip(files)

    c2 = v2
    |> Enum.filter(fn {i, _} -> rem(i, 2) == 1 end)
    |> Enum.zip(revfiles)

    (c1 ++ c2)
    |> Enum.sort_by(fn {{_, i}, _} -> i end)
    |> Enum.take(Enum.count(files))
    |> Enum.map(fn {{_, i}, v} -> i*v end)
    |> Enum.sum()
  end

  def part2(data) do
    values = data
    |> String.split("\n", trim: true)
    |> hd()
    |> String.codepoints()
    |> Enum.map(& String.to_integer/1)
    |> Enum.scan({0, 0}, fn v, {o, i} -> {v, o+i} end)

    spaces = values
    |> tl()
    |> Enum.take_every(2)

    values
    |> Enum.take_every(2)
    |> Enum.with_index()
    |> Enum.reverse()
    |> locations(spaces)
    |> Enum.map(fn {a, b, i} -> i*Enum.sum(b..b+a-1) end)
    |> Enum.sum()
  end

  defp locations(acc \\ [], revfiles, spaces)

  # a (block size), b (block position), i (block index)
  # c (space size), d (space position)
  # IMPROVE ME: since there are at most 9 sizes, we could keep the
  # locations of the spaces in a tree for each size, and achieve
  # better performance, might do it later..
  defp locations(acc, [{{a, b}, i} | t], spaces) do
    spaces = Enum.filter(spaces, fn {_, d} -> d < b end)

    s = spaces
    |> Enum.filter(fn {c, _} -> c >= a end)
    |> Enum.min_by(fn {_, d} -> d end, &<=/2, fn -> nil end)

    if s == nil do
      locations([{a, b, i} | acc], t, spaces)
    else
      spaces = List.delete(spaces, s)
      {c, d} = s
      spaces =
        if c == a do
          spaces
        else
          [{c-a, d+a} | spaces]
        end
      locations([{a, d, i} | acc], t, spaces)
    end
  end

  defp locations(acc, [], _) do
    acc
  end
end

data = IO.read(:stdio, :eof)

{time1 , ans1} = :timer.tc(fn -> Day09.part1(data) end)
IO.puts("Time  : #{time1 / 1000000}")
IO.puts("Answer: #{ans1}")

{time2 , ans2} = :timer.tc(fn -> Day09.part2(data) end)
IO.puts("Time  : #{time2 / 1000000}")
IO.puts("Answer: #{ans2}")