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

Advent of Code 2024/5

2024/5.livemd

Advent of Code 2024/5

Mix.install([
  {:kino, "~> 0.14.2"}
])

Section

input = Kino.Input.textarea("input")
defmodule AoC2024_5 do
  def convert(lists) do
    Enum.map(lists, fn list -> Enum.map(list, fn el -> String.to_integer(el) end) end)
  end
  
  def parse(input) do
    Kino.Input.read(input)
    |> String.split("\n\n", trim: true)
    |> Enum.map(fn part -> String.split(part, "\n", trim: true) end)
    |> (fn [a, b] -> [
      b |> Enum.map(fn item -> String.split(item, ",", trim: true) end) |> convert,
      a |> Enum.map(fn item -> String.split(item, "|", trim: true) end) |> convert
    ] end).()
  end

  def slice_set(set, 0), do: [[], Enum.slice(set, 1..(length(set)-1))]
  def slice_set(set, index) when length(set) - 1 == index, do: [Enum.slice(set, 0..(length(set)-2)), []]
  def slice_set(set, index) do
    [
      Enum.slice(set, 0..(index - 1)),
      Enum.slice(set, (index + 1)..(length(set) - 1))
    ]
  end

  def valid_page({page, index}, set, rules) do
    [before_set, after_set] = slice_set(set, index)
    after_pages = Enum.filter(rules, fn rule -> Enum.at(rule, 0) == page end) |> Enum.map(fn rule -> Enum.at(rule, 1) end)
    before_pages = Enum.filter(rules, fn rule -> Enum.at(rule, 1) == page end) |> Enum.map(fn rule -> Enum.at(rule, 0) end)
    MapSet.disjoint?(MapSet.new(before_set), MapSet.new(after_pages)) && MapSet.disjoint?(MapSet.new(after_set), MapSet.new(before_pages))
  end

  def part_1(input) do
    [page_sets, rules] = input
    |> parse

    page_sets
    |> Enum.filter(fn set -> !Enum.any?(Enum.with_index(set), fn page -> !valid_page(page, set, rules) end) end)
    |> Enum.map(fn set -> Enum.at(set, div(length(set),2)) end)
    |> Enum.sum
  end

  def before_by_rules(first, second, rules) do
    length(Enum.filter(rules, fn rule -> Enum.at(rule, 0) == first && Enum.at(rule, 1) == second end)) > 0
  end

  def part_2(input) do
    [page_sets, rules] = input
    |> parse

    page_sets
    |> Enum.filter(fn set -> Enum.any?(Enum.with_index(set), fn page -> !valid_page(page, set, rules) end) end)
    |> Enum.map(fn set -> Enum.sort(set, &(before_by_rules(&1, &2, rules))) end)
    |> Enum.map(fn set -> Enum.at(set, div(length(set),2)) end)
    |> Enum.sum
  end
end
AoC2024_5.part_1(input)
AoC2024_5.part_2(input)