Skip to content

Commit f9a4508

Browse files
committed
Day19
1 parent a086dee commit f9a4508

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

19/sample_input

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
px{a<2006:qkq,m>2090:A,rfg}
2+
pv{a>1716:R,A}
3+
lnx{m>1548:A,A}
4+
rfg{s<537:gd,x>2440:R,A}
5+
qs{s>3448:A,lnx}
6+
qkq{x<1416:A,crn}
7+
crn{x>2662:A,R}
8+
in{s<1351:px,qqz}
9+
qqz{s>2770:qs,m<1801:hdj,R}
10+
gd{a>3333:R,R}
11+
hdj{m>838:A,pv}
12+
13+
{x=787,m=2655,a=1222,s=2876}
14+
{x=1679,m=44,a=2067,s=496}
15+
{x=2036,m=264,a=79,s=2244}
16+
{x=2461,m=1339,a=466,s=291}
17+
{x=2127,m=1623,a=2188,s=1013}

19/sol.exs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
defmodule Day19 do
2+
def read_input(file_path) do
3+
[workflows_str, parts_str] =
4+
File.read!(file_path)
5+
|> String.split("\n\n", trim: true)
6+
7+
parts =
8+
parts_str
9+
|> String.split("\n", trim: true)
10+
|> Enum.map(
11+
&(Regex.named_captures(~r/{x=(?<x>\d+),m=(?<m>\d+),a=(?<a>\d+),s=(?<s>\d+)}/, &1)
12+
|> Enum.map(fn {x, y} -> {x, String.to_integer(y)} end)
13+
|> Map.new())
14+
)
15+
16+
workflows =
17+
workflows_str
18+
|> String.split("\n", trim: true)
19+
|> Enum.map(&parse_workflow/1)
20+
|> Map.new()
21+
22+
{workflows, parts}
23+
end
24+
25+
def parse_workflow(line) do
26+
[operation | rules] = String.split(line, ~r/{|}/, trim: true)
27+
rules = String.split(Enum.at(rules, 0), ",")
28+
default_destination = String.trim(Enum.at(rules, -1))
29+
rules = Enum.slice(rules, 0..-2//1)
30+
31+
rules =
32+
Enum.map(rules, fn rule ->
33+
parsed = Regex.named_captures(~r/(?<key>\w+)(?<check>[<>])(?<value>\d+):(?<to>\w+)/, rule)
34+
value = String.to_integer(parsed["value"])
35+
36+
%{
37+
check:
38+
if(parsed["check"] == "<",
39+
do: &Kernel.<(&1[parsed["key"]], value),
40+
else: &Kernel.>(&1[parsed["key"]], value)
41+
),
42+
to: parsed["to"],
43+
key: parsed["key"],
44+
fun:
45+
if(parsed["check"] == "<",
46+
do: :less_than,
47+
else: :greater_than
48+
),
49+
value: value
50+
}
51+
end)
52+
53+
{operation, %{rules: rules, default_destination: default_destination}}
54+
end
55+
56+
def process_workflow(workflows, workflow, part),
57+
do: process_workflow(workflows, workflow[:rules], workflow[:default_destination], part)
58+
59+
def process_workflow(workflows, [], default_destination, part) do
60+
case(default_destination) do
61+
"A" -> true
62+
"R" -> false
63+
trg -> process_workflow(workflows, workflows[trg], part)
64+
end
65+
end
66+
67+
def process_workflow(workflows, [rule | rest], default_destination, part) do
68+
if(rule[:check].(part)) do
69+
case rule[:to] do
70+
"A" -> true
71+
"R" -> false
72+
trg -> process_workflow(workflows, workflows[trg], part)
73+
end
74+
else
75+
process_workflow(workflows, rest, default_destination, part)
76+
end
77+
end
78+
79+
def partA(file_path) do
80+
{workflows, parts} = read_input(file_path)
81+
82+
parts
83+
|> Enum.filter(fn part -> process_workflow(workflows, workflows["in"], part) end)
84+
|> Enum.map(fn part -> part["x"] + part["m"] + part["a"] + part["s"] end)
85+
|> Enum.sum()
86+
end
87+
88+
def partB(file_path) do
89+
{workflows, _} = read_input(file_path)
90+
91+
distinct_combs(
92+
workflows,
93+
[
94+
%{
95+
:loc => "in",
96+
"x" => %{h: 4000, l: 1},
97+
"m" => %{h: 4000, l: 1},
98+
"a" => %{h: 4000, l: 1},
99+
"s" => %{h: 4000, l: 1}
100+
}
101+
],
102+
0
103+
)
104+
end
105+
106+
def distinct_combs(_, [], acc), do: acc
107+
108+
def distinct_combs(
109+
workflows,
110+
[
111+
%{
112+
:loc => loc,
113+
"x" => %{h: xh, l: xl},
114+
"m" => %{h: mh, l: ml},
115+
"a" => %{h: ah, l: al},
116+
"s" => %{h: sh, l: sl}
117+
}
118+
| rest
119+
],
120+
acc
121+
)
122+
when xl > xh or ml > mh or al > ah or sl > sh,
123+
do: distinct_combs(workflows, rest, acc)
124+
125+
def distinct_combs(
126+
workflows,
127+
[
128+
%{
129+
:loc => "A",
130+
"x" => %{h: xh, l: xl},
131+
"m" => %{h: mh, l: ml},
132+
"a" => %{h: ah, l: al},
133+
"s" => %{h: sh, l: sl}
134+
}
135+
| rest
136+
],
137+
acc
138+
),
139+
do:
140+
distinct_combs(
141+
workflows,
142+
rest,
143+
acc + (xh - xl + 1) * (mh - ml + 1) * (ah - al + 1) * (sh - sl + 1)
144+
)
145+
146+
def distinct_combs(workflows, [%{loc: "R"} | rest], acc),
147+
do: distinct_combs(workflows, rest, acc)
148+
149+
def distinct_combs(workflows, [curr_range | rest], acc) do
150+
new_r =
151+
new_ranges(
152+
workflows[curr_range[:loc]][:rules],
153+
workflows[curr_range[:loc]][:default_destination],
154+
curr_range,
155+
[]
156+
)
157+
158+
distinct_combs(workflows, new_r ++ rest, acc)
159+
end
160+
161+
def new_ranges([], default_destination, range, acc),
162+
do: [Map.put(range, :loc, default_destination) | acc]
163+
164+
def new_ranges([rule | rest], default_destination, range, acc) do
165+
key_range = range[rule[:key]]
166+
167+
{true_key_range, false_key_range} =
168+
if rule[:fun] == :greater_than do
169+
{%{h: key_range[:h], l: max(key_range[:l], rule[:value] + 1)},
170+
%{h: min(key_range[:h], rule[:value]), l: key_range[:l]}}
171+
else
172+
{%{h: min(key_range[:h], rule[:value] - 1), l: key_range[:l]},
173+
%{h: key_range[:h], l: max(key_range[:l], rule[:value])}}
174+
end
175+
176+
true_range = Map.put(range, rule[:key], true_key_range) |> Map.put(:loc, rule[:to])
177+
false_range = Map.put(range, rule[:key], false_key_range)
178+
[true_range | new_ranges(rest, default_destination, false_range, acc)]
179+
end
180+
end
181+
182+
IO.puts(Day19.partA("./input"))
183+
IO.puts(Day19.partB("./input"))

0 commit comments

Comments
 (0)