-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday15.ex
118 lines (95 loc) · 3.18 KB
/
day15.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
defmodule Y2019.Day15 do
use Advent.Day, no: 15
alias Y2019.Intcode
# Inputs and what those inputs do to x/y co-ordinates
@moves %{1 => {0, 1}, 2 => {0, -1}, 3 => {-1, 0}, 4 => {1, 0}}
def part1(input) do
program =
input
|> Intcode.from_string()
|> Intcode.new()
{graph, map} = do_part1([{{0, 0}, program}], Graph.new(), %{{0, 0} => :start})
{win, _} = Enum.find(map, fn {_, key} -> key == :win end)
# length is total number of positions in the path, movements is gaps between them
length(Graph.dijkstra(graph, {0, 0}, win)) - 1
end
def part2(input) do
program =
input
|> Intcode.from_string()
|> Intcode.new()
{graph, map} = do_part1([{{0, 0}, program}], Graph.new(), %{{0, 0} => :start})
{win, _} = Enum.find(map, fn {_, key} -> key == :win end)
spread_oxygen(graph, [win], 0)
end
defp spread_oxygen(graph, sources, time) do
if Graph.info(graph).num_edges == 0 do
time
else
{graph, sources} =
Enum.reduce(sources, {graph, []}, fn source, {graph, new_sources} ->
new_sources = Graph.neighbors(graph, source) ++ new_sources
graph = Graph.delete_vertex(graph, source)
{graph, new_sources}
end)
spread_oxygen(graph, sources, time + 1)
end
end
defp do_part1([], graph, state), do: {graph, state}
defp do_part1([{position, program} | rest], graph, state) do
{state, graph, rest} =
Enum.reduce(@moves, {state, graph, rest}, fn {input, change}, {state, graph, rest} ->
{[output], new_program} =
program
|> Intcode.add_input(input)
|> Intcode.run()
|> Intcode.pop_outputs()
case output do
0 ->
# Wall.
{Map.put(state, new_position(position, change), :wall), graph, rest}
1 ->
# Space.
new_position = new_position(position, change)
if Map.has_key?(state, new_position) do
{state, graph, rest}
else
{
Map.put(state, new_position, :space),
Graph.add_edge(graph, position, new_position),
[{new_position, new_program} | rest]
}
end
2 ->
# Found it!!
new_position = new_position(position, change)
{
Map.put(state, new_position, :win),
Graph.add_edge(graph, position, new_position),
rest
}
end
end)
do_part1(rest, graph, state)
end
defp new_position({x1, y1}, {x2, y2}), do: {x1 + x2, y1 + y2}
def visualize(state, path \\ []) do
grid_size = 22
IEx.Helpers.clear()
for y <- grid_size..-grid_size do
for x <- -grid_size..grid_size do
to_tile(Map.get(state, {x, y}), Enum.member?(path, {x, y}))
end
|> Enum.join()
end
|> Enum.each(&IO.puts/1)
end
defp to_tile(nil, _), do: " "
defp to_tile(:wall, _), do: "#"
defp to_tile(:space, true), do: "*"
defp to_tile(:space, false), do: "."
defp to_tile(:start, _), do: "X"
defp to_tile(:win, _), do: "!"
def part1_verify, do: input() |> part1()
def part2_verify, do: input() |> part2()
end