Advent of Code 2023
Mix.install([
{:req, "~> 0.3.2"}
])
Day 19
input =
"https://adventofcode.com/2023/day/19/input"
|> Req.get!(headers: [cookie: "session=#{System.get_env("AOC_COOKIE")}"])
|> Map.get(:body)
sample = """
px{a<2006:qkq,m>2090:A,rfg}
pv{a>1716:R,A}
lnx{m>1548:A,A}
rfg{s<537:gd,x>2440:R,A}
qs{s>3448:A,lnx}
qkq{x<1416:A,crn}
crn{x>2662:A,R}
in{s<1351:px,qqz}
qqz{s>2770:qs,m<1801:hdj,R}
gd{a>3333:R,R}
hdj{m>838:A,pv}
{x=787,m=2655,a=1222,s=2876}
{x=1679,m=44,a=2067,s=496}
{x=2036,m=264,a=79,s=2244}
{x=2461,m=1339,a=466,s=291}
{x=2127,m=1623,a=2188,s=1013}
"""
defmodule A do
def parse(input) do
input
|> String.split("\n\n", trim: true)
|> Enum.map(&String.split(&1, "\n", trim: true))
|> then(fn [workflows, parts] ->
workflows =
workflows
|> Enum.map(fn line ->
[_, id, rules] = Regex.run(~r/^(.*){(.*)}$/, line)
rules =
String.split(rules, ",")
|> Enum.map(fn rule ->
cond do
String.contains?(rule, "<") ->
[id, n, action] = Regex.split(~r/[<:]/, rule)
{:lt, id, String.to_integer(n), action}
String.contains?(rule, ">") ->
[id, n, action] = Regex.split(~r/[>:]/, rule)
{:gt, id, String.to_integer(n), action}
true ->
rule
end
end)
{id, rules}
end)
|> Enum.into(%{})
parts =
parts
|> Enum.map(fn line ->
Regex.run(~r/^{(x)=(\d+),(m)=(\d+),(a)=(\d+),(s)=(\d+)}$/, line)
|> tl()
|> Enum.chunk_every(2)
|> Enum.map(fn [k, v] -> {k, String.to_integer(v)} end)
|> Enum.into(Map.new())
end)
{workflows, parts}
end)
end
def sort_part(_p, _workflows, ["A"]), do: :accept
def sort_part(_p, _workflows, ["R"]), do: :reject
def sort_part(p, workflows, [workflow_id]),
do: sort_part(p, workflows, Map.fetch!(workflows, workflow_id))
def sort_part(p, workflows, [rule | rules]) do
case rule do
{:lt, k, n, r} ->
if p[k] < n do
sort_part(p, workflows, [r])
else
sort_part(p, workflows, rules)
end
{:gt, k, n, r} ->
if p[k] > n do
sort_part(p, workflows, [r])
else
sort_part(p, workflows, rules)
end
end
end
defp distinct_ratings(_workflows, rating, ["A"]), do: [rating]
defp distinct_ratings(_workflows, _rating, ["R"]), do: []
defp distinct_ratings(workflows, rating, [id]),
do: distinct_ratings(workflows, rating, workflows[id])
defp distinct_ratings(workflows, rating, [{:lt, k, n, r} | rules]) do
min..max//_ = rating[k]
{lower, upper} =
if min <= n && n <= max do
{min..(n - 1), n..max}
else
raise "boom"
end
distinct_ratings(workflows, Map.put(rating, k, lower), [r]) ++
distinct_ratings(workflows, Map.put(rating, k, upper), rules)
end
defp distinct_ratings(workflows, rating, [{:gt, k, n, r} | rules]) do
min..max//_ = rating[k]
distinct_ratings(workflows, Map.put(rating, k, (n + 1)..max), [r]) ++
distinct_ratings(workflows, Map.put(rating, k, min..n), rules)
end
def part1(input) do
{workflows, parts} =
input
|> parse()
parts
|> Enum.map(fn part ->
{sort_part(part, workflows, Map.fetch!(workflows, "in")), part}
end)
|> Enum.group_by(&elem(&1, 0))
|> Map.get(:accept)
|> Enum.flat_map(&Map.values(elem(&1, 1)))
|> Enum.sum()
end
def part2(input) do
{workflows, _} =
input
|> parse()
rating =
%{
"x" => 1..4000,
"m" => 1..4000,
"a" => 1..4000,
"s" => 1..4000
}
distinct_ratings(workflows, rating, ["in"])
|> Enum.map(fn ratings ->
ratings
|> Map.values()
|> Enum.map(&Range.size/1)
|> Enum.product()
end)
|> Enum.sum()
end
end
Part 1
input
|> A.part1()
Part 2
input
|> A.part2()
graph TD;
in-->in1{s < 1351?}
in1-->|x=1..4000\nm=1..4000\na=1..4000\ns=1..1350| px
in1-->|x=1..4000\nm=1..4000\na=1..4000\ns=1351..4000| qqz
px-->px1{a<2006?}
px1-->|x=1..4000\nm=1..4000\na=1..2005\ns=1..1350| qkq
px1-->|x=1..4000\nm=1..4000\na=2006..4000\ns=1..1350| px2{m<1801?}
px2-->|x=1..4000\nm=1..1800\na=2006..4000\ns=1..1350| hdj
px2-->reject
hdj-->hdj1{m>838?}
hdj1-->|x=1..4000\nm=839..1800\na=2006..4000\ns=1..1350| accept
hdj1-->pv