-
Notifications
You must be signed in to change notification settings - Fork 3
/
elixir_day09.ex
122 lines (107 loc) · 3.58 KB
/
elixir_day09.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
defmodule ElixirDay09 do
@moduledoc """
Documentation for ElixirDay09.
"""
alias ElixirDay09.{Computer, ComputerServer}
def parse(filename) do
File.stream!(filename)
|> Stream.map(&String.trim/1)
|> Enum.at(0)
|> String.split(",")
|> Enum.map(&String.to_integer/1)
end
def permutations([]), do: [[]]
def permutations(list),
do: for(elem <- list, rest <- permutations(list -- [elem]), do: [elem | rest])
@doc """
Given a program, try all permutations of [0, 1, 2, 3, 4],
to see which phase sequence generates the highest value
when passed through amplify_once.
"""
def amplify_once_max_seq(program) when is_list(program) do
permutations([0, 1, 2, 3, 4])
|> Enum.map(fn phase_sequence ->
value = amplify_once(program, phase_sequence)
{phase_sequence, value}
end)
|> Enum.max_by(fn {_phase_sequence, value} ->
value
end)
end
@doc """
Given a program and a phase sequence, run the program through 5 chained computers.
The phase sequence is a list of 5 inputs, like, [3, 2, 4, 1, 5]. These are fed
to each computer as the first input.
0 -> A -> B -> C -> D -> E -> Output.
"""
def amplify_once(program, phase_sequence) when is_list(program) and is_list(phase_sequence) do
phase_sequence
|> Enum.map(fn phase ->
{:ok, pid} = ComputerServer.start(program, [phase])
pid
end)
|> Enum.reduce(0, fn pid, acc ->
ComputerServer.add_input(pid, acc)
ComputerServer.pop_output(pid)
end)
end
@doc """
Given a program, try all permutations of [5, 6, 7, 8 9],
to see which phase sequence generates the highest value
when passed through amplify_loop.
"""
def amplify_loop_max_seq(program) when is_list(program) do
permutations([5, 6, 7, 8, 9])
|> Enum.map(fn phase_sequence ->
value = amplify_loop(program, phase_sequence)
{phase_sequence, value}
end)
|> Enum.max_by(fn {_phase_sequence, value} ->
value
end)
end
@doc """
Given a program and a phase sequence, run the program through 5 chained computers,
with feedback.
The phase sequence is a list of 5 inputs, like, [3, 2, 4, 1, 5]. These are fed
to each computer as the first input.
0 -> A -> B -> C -> D -> E -> Output.
^ v
\ <- <- <- /
The feedback stops once all computers have halted.
"""
def amplify_loop(program, phase_sequence) when is_list(program) and is_list(phase_sequence) do
# Start 5 computers
pids =
phase_sequence
|> Enum.map(fn phase ->
{:ok, pid} = ComputerServer.start(program, [phase])
pid
end)
# Loop through the computers, feeding them the last computer's output as their next input.
# Starts with initial input "0", and tracks the number of halted computers,
# stops when all of them have halted. Assumes they will all halt in order, directly
# one after another.
pids
|> Stream.cycle()
|> Enum.reduce_while(%{input: 0, halt_count: 0}, fn pid, acc ->
%{input: input, halt_count: halt_count} = acc
ComputerServer.add_input(pid, input)
output = ComputerServer.pop_output(pid)
halt_count = halt_count + if ComputerServer.halted?(pid), do: 1, else: 0
if halt_count == length(phase_sequence) do
{:halt, output}
else
{:cont, %{input: output, halt_count: halt_count}}
end
end)
end
def main do
parse("../../09/input.txt")
|> Computer.solve([1])
|> IO.inspect(label: "day 9 part 1")
parse("../../09/input.txt")
|> Computer.solve([2])
|> IO.inspect(label: "day 9 part 2")
end
end