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

Advent of Code - Day 5

2024/05.livemd

Advent of Code - Day 5

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Problem

https://adventofcode.com/2024/day/5

I have yet to complete part 2. I feel it could be done with topological sort of rules, but wanted to find another solution.

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2024", "5", System.fetch_env!("LB_AOC_SESSION"))
defmodule Day5 do  

  def parse_rules(rules) do
    String.split(rules, ["\n", "|"], trim: true) 
      |> Enum.map(&String.to_integer(&1))
      |> Enum.chunk_every(2, 2)
      |> Enum.reduce(%{}, fn [a,b], map ->
        map |> Map.update(a, MapSet.new([b]), fn set -> MapSet.put(set, b) end)
            |> Map.update(b, MapSet.new(), fn set -> set end)
      end)
  end
  
  def parse(str) do
    [rules, updates] = String.split(str, "\n\n")

    updates = String.split(updates, "\n") |> Enum.map(fn x ->
      String.split(x, ",") |> Enum.map(&String.to_integer/1)
    end)
    
    rules = parse_rules(rules)

    {rules, updates}
  end
    
  def correct_order?([], _), do: true

  def correct_order?([page | pages], rules) do
    rule = Map.fetch!(rules, page)
    Enum.all?(pages, &Enum.member?(rule, &1)) and correct_order?(pages, rules)
  end

  def add_middle_pages(updates) do
    Enum.reduce(updates, 0, fn x, acc ->
      acc + Enum.at(x, div(length(x),2))
    end)
  end

  def solve_part1(str) do
    {rules, updates } = parse(str)
    Enum.filter(updates, fn pages ->
      correct_order?(pages, rules)
    end)
    |> add_middle_pages()
  end
end
example_input = "47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47"
Day5.solve_part1(puzzle_input)