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

Advent of Code 2023 - Day 19

2023/19.livemd

Advent of Code 2023 - Day 19

Mix.install([
  {:req, "~> 0.4.0"}
])

Input

opts = [headers: [{"cookie", "session=#{System.fetch_env!("LB_AOC_SESSION")}"}]]
puzzle_input = Req.get!("https://adventofcode.com/2023/day/19/input", opts).body
[workflow_input, parts_input] =
  puzzle_input
  |> String.split("\n\n", trim: true)
parse_rules = fn input ->
  for raw <- input do
    case Regex.scan(~r/(.)(<|>)(.*):(.*)/, raw, capture: :all_but_first) do
      [[r, c, v, n]] -> {String.to_atom(r), c, String.to_integer(v), n}
      [] -> raw
    end
  end
end

workflows =
  workflow_input
  |> String.split("\n", trim: true)
  |> Stream.map(&amp;String.split(&amp;1, ["{", ",", "}"], trim: true))
  |> Stream.map(fn [n | rule_input] -> {n, parse_rules.(rule_input)} end)
  |> Enum.into(%{})
parts =
  parts_input
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    line
    |> String.split(["{", ",", "}", "="], trim: true)
    |> Stream.chunk_every(2)
    |> Stream.map(fn [k, v] -> {String.to_atom(k), String.to_integer(v)} end)
    |> Enum.into(%{})
  end)

Part 1

defmodule Part1 do
  def work("A", _, part), do: Map.values(part)

  def work("R", _, _), do: [0]

  def work(name, flows, part) do
    flows[name]
    |> Enum.find_value(fn
      {r, "<", v, n} -> if part[r] < v, do: n
      {r, ">", v, n} -> if part[r] > v, do: n
      n -> n
    end)
    |> work(flows, part)
  end
end

parts
|> Stream.flat_map(&amp;Part1.work("in", workflows, &amp;1))
|> Enum.sum()

Part 2

defmodule Part2 do
  def work("A", _, parts), do: combinations(parts)
  def work("R", _, _), do: 0
  def work(name, flows, parts), do: do_work(flows[name], flows, parts)

  def do_work([{r, c, v, n} | rest], flows, parts) do
    {p1, p2} = split(parts, parts[r], r, c, v)
    work(n, flows, p1) + do_work(rest, flows, p2)
  end

  def do_work([n], flows, parts), do: work(n, flows, parts)

  defp split(p, f..l, r, "<", v), do: {%{p | r => f..(v - 1)}, %{p | r => v..l}}
  defp split(p, f..l, r, ">", v), do: {%{p | r => (v + 1)..l}, %{p | r => f..v}}

  defp combinations(parts) do
    parts
    |> Map.values()
    |> Stream.map(&amp;Range.size/1)
    |> Enum.product()
  end
end

Part2.work("in", workflows, %{
  x: 1..4000,
  m: 1..4000,
  a: 1..4000,
  s: 1..4000
})

Run in Livebook