-
Notifications
You must be signed in to change notification settings - Fork 0
/
day21.ex
96 lines (77 loc) · 2.75 KB
/
day21.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
defmodule Y2022.Day21 do
use Advent.Day, no: 21
def part1(input) do
monkey_value("root", input, &evaluate/1)
end
# 7010269744524 - too high
def part2(input) do
input =
input
|> Map.update!("root", fn [a, _op, b] -> [a, "=", b] end)
|> Map.put("humn", "???")
run_inversion({0, monkey_value("root", input, & &1)})
end
defp can_evaluate?(list) when is_list(list), do: !Enum.member?(List.flatten(list), "???")
defp can_evaluate?(val), do: val != "???"
defp run_inversion({num, "???"}), do: num
defp run_inversion({current, ["=", left, right]}) do
replace_val = fn _, val -> evaluate(val) end
left_or_right(current, [left, right], replace_val, replace_val)
end
defp run_inversion({current, ["/", left, right]}) do
left_or_right(current, [left, right], &div(evaluate(&2), &1), &(&1 * evaluate(&2)))
end
defp run_inversion({current, ["+", left, right]}) do
subtract_fn = fn current, val -> current - evaluate(val) end
left_or_right(current, [left, right], subtract_fn, subtract_fn)
end
defp run_inversion({current, ["-", left, right]}) do
left_or_right(current, [left, right], &(evaluate(&2) - &1), &(&1 + evaluate(&2)))
end
defp run_inversion({current, ["*", left, right]}) do
division_fn = fn current, val -> div(current, evaluate(val)) end
left_or_right(current, [left, right], division_fn, division_fn)
end
defp monkey_value(monkey, input, runner) do
case Map.get(input, monkey) do
[monkey1, op, monkey2] ->
runner.([op, monkey_value(monkey1, input, runner), monkey_value(monkey2, input, runner)])
nil ->
raise "Can't find value for monkey #{monkey} in #{inspect(input)}"
num ->
num
end
end
defp evaluate(num) when is_integer(num), do: num
defp evaluate(["+", one, two]), do: evaluate(one) + evaluate(two)
defp evaluate(["*", one, two]), do: evaluate(one) * evaluate(two)
defp evaluate(["-", one, two]), do: evaluate(one) - evaluate(two)
defp evaluate(["/", one, two]), do: div(evaluate(one), evaluate(two))
def left_or_right(current, [left, right], left_fn, right_fn) do
if can_evaluate?(left) do
{left_fn.(current, left), right}
else
{right_fn.(current, right), left}
end
|> run_inversion()
end
def parse_input(input) do
input
|> String.split("\n", trim: true)
|> Enum.map(&parse_row/1)
|> Enum.into(%{})
end
defp parse_row(row) do
[name, op] = String.split(row, ": ", parts: 2)
{name, parse_op(op)}
end
defp parse_op(op) do
if String.contains?(op, " ") do
String.split(op, " ")
else
String.to_integer(op)
end
end
def part1_verify, do: input() |> parse_input() |> part1()
def part2_verify, do: input() |> parse_input() |> part2()
end