Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 5

livebook/5.livemd

Day 5

Mix.install(
  [
    {:nimble_parsec, "~> 1.4"}
  ]
)

Part 1

# input = File.read!("5/example.txt")
input = File.read!("5/input.txt")
defmodule U do
  defmodule Parser do
    import NimbleParsec

    eol =
      choice([
        string("\r\n"),
        string("\n"),
        eos()
      ])

    rule = integer(min: 1) |> ignore(string("|")) |> integer(min: 1) |> ignore(eol) |> tag(:rule)
    rules = repeat(rule) |> tag(:rules)

    update =
      integer(min: 1)
      |> repeat(ignore(string(",")) |> concat(integer(min: 1)))
      |> ignore(eol)
      |> tag(:update)

    updates = repeat(update) |> tag(:updates)

    defparsec(:input, rules |> concat(ignore(eol)) |> concat(updates))
  end

  def parse(input) do
    {:ok, [rules: rules, updates: updates], "", _, _, _} = Parser.input(input)

    {rules |> Enum.map(fn {:rule, [n1, n2]} -> {n1, n2} end),
     updates |> Enum.map(fn {:update, values} -> values end)}
  end

  def middle(l) do
    Enum.at(l, (Enum.count(l) / 2) |> trunc)
  end
end
{rules, upgrades} = input |> U.parse
defmodule Part1 do
  def check_upgrade(pages, rules) do
    check_upgrade([], pages, rules)
  end

  def check_upgrade(_, [], _), do: true

  def check_upgrade(before, [p | tail], rules) do
    Enum.all?(rules, fn
      {^p, should_after} ->
        !Enum.any?(before, fn
          ^should_after -> true
          _ -> false
        end)
      _ ->
        true
    end) && check_upgrade([p | before], tail, rules)
  end
end
Enum.reduce(upgrades, 0, fn upgrade, sum ->
  if Part1.check_upgrade(upgrade, rules) do
    sum + U.middle(upgrade)
  else
    sum
  end
end)

Part2

defmodule Part2 do
  def reorder(upgrade, rules) do
    reorder([], upgrade, rules, rules)
  end

  def reorder(before, [], [], _) do
    before
  end

  def reorder(before, [p | tail], [], rules) do
    reorder(before ++ [p], tail, rules, rules)
  end

  def reorder(before, [p | afte], [{p, should_after} | tail], rules) do
    if should_after in before do
      reorder([], [p | before] ++ afte, rules, rules)
    else
      reorder(before, [p | afte], tail, rules)
    end
  end

  def reorder(before, afte, [_ | tail], rules) do
    reorder(before, afte, tail, rules)
  end
end
Enum.reduce(upgrades, 0, fn upgrade, sum ->
  if Part1.check_upgrade(upgrade, rules) do
    sum
  else
    reordered = Part2.reorder(upgrade, rules)
    sum + U.middle(reordered)
  end
end)
Part2.reorder([61, 13, 29], rules) |> inspect(charlists: :as_lists)