summaryrefslogblamecommitdiffstats
path: root/src/day05.exs
blob: e950b0643afceda14930eeb259151f9d47ed181d (plain) (tree)















































































                                                                                 
defmodule Day05 do
  def parse_integer_list(list, split) do
    list
    |> String.split(split, trim: true)
    |> Enum.map(&String.to_integer/1)
  end

  def parse_data(data) do
    [rules, jobs] = data
    |> String.split("\n\n", trim: true)

    rules = rules
    |> String.split("\n", trim: true)
    |> Enum.map(&parse_integer_list(&1, "|"))
    |> Enum.group_by(&hd/1, &hd(tl(&1)))
    |> Enum.into(%{}, fn {k, v} -> {k, MapSet.new(v)} end)

    jobs = jobs
    |> String.split("\n", trim: true)
    |> Enum.map(&parse_integer_list(&1, ","))

    {rules, jobs}
  end

  def part1({rules, jobs}) do
    jobs
    |> Enum.filter(&job_okay?(&1, rules))
    |> Enum.map(&middle_page/1)
    |> Enum.sum()
  end

  def job_okay?(job, rules) do
    case job do
      [] ->
        true
      [page | rest] ->
        page_okay?(page, rest, rules) and
          job_okay?(rest, rules)
    end
  end

  def page_okay?(page, rest, rules) do
    rest
    |> Enum.all?(&Map.get(rules, &1, %MapSet{}) |> MapSet.member?(page) == false)
  end

  def middle_page(job) do
    job |> Enum.at(job |> length() |> div(2))
  end

  def part2({rules, jobs}) do
    jobs
    |> Enum.reject(&job_okay?(&1, rules))
    |> Enum.map(&fix_job(&1, rules))
    |> Enum.map(&middle_page/1)
    |> Enum.sum()
  end

  def fix_job(job, rules, acc \\ []) do
    case job do
      [] ->
        Enum.reverse(acc)
      job ->
        {l, r} = Enum.split_with(job, &page_okay?(&1, job, rules) == false)
        [next, rest] = [hd(r), l ++ tl(r)]
        fix_job(rest, rules, [next | acc])
    end
  end
end

data = IO.read(:stdio, :eof) |> Day05.parse_data()

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

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