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(&String.split(&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(&Part1.work("in", workflows, &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(&Range.size/1)
|> Enum.product()
end
end
Part2.work("in", workflows, %{
x: 1..4000,
m: 1..4000,
a: 1..4000,
s: 1..4000
})