-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday06.ex
115 lines (98 loc) · 3.14 KB
/
day06.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
defmodule AdventOfCode.Day06 do
def part_1(input) do
{positions, start, width, height} = parse_input(input)
bounds = {width, height}
simulate_movement(positions, bounds, start)
|> Enum.map(&elem(&1, 0))
|> Enum.uniq()
|> Enum.count()
end
def part_2(input) do
{positions, start, width, height} = parse_input(input)
bounds = {width, height}
simulate_movement(positions, bounds, start)
|> Stream.chunk_every(100)
|> Enum.map(fn chunk ->
Task.async(fn ->
Enum.reduce(chunk, MapSet.new(), fn {{x, y} = _position, {dx, dy} = _direction}, acc ->
next_position = {x + dx, y + dy}
positions = Map.put(positions, next_position, "#")
if simulate_movement(positions, bounds, start) == :loop_detected do
MapSet.put(acc, next_position)
else
acc
end
end)
end)
end)
|> Task.await_many()
|> Enum.reduce(MapSet.new(), &MapSet.union(&1, &2))
|> Enum.count()
end
def simulate_movement(
positions,
bounds,
position,
direction \\ {0, -1},
visited \\ MapSet.new()
)
def simulate_movement(_positions, {width, height}, {x, y}, _direction, _visited)
when x < 0 or y < 0 or x >= width or y >= height,
do: []
def simulate_movement(positions, bounds, {x, y} = position, {dx, dy} = direction, visited) do
if MapSet.member?(visited, {position, direction}) do
:loop_detected
else
next_position = {x + dx, y + dy}
if Map.has_key?(positions, next_position) do
visited = MapSet.put(visited, {position, direction})
next_direction = switch_direction(direction)
simulate_movement(positions, bounds, position, next_direction, visited)
else
visited = MapSet.put(visited, {position, direction})
case simulate_movement(positions, bounds, next_position, direction, visited) do
:loop_detected -> :loop_detected
steps -> [{position, direction} | steps]
end
end
end
end
def switch_direction({0, -1}), do: {1, 0}
def switch_direction({1, 0}), do: {0, 1}
def switch_direction({0, 1}), do: {-1, 0}
def switch_direction({-1, 0}), do: {0, -1}
# Functions to parse the input
def parse_input(input) do
lines = String.split(input, "\n", trim: true)
positions = parse_positions(lines)
start = parse_start_position(lines)
width = String.length(Enum.at(lines, 0))
height = Enum.count(lines)
{positions, start, width, height}
end
def parse_positions(lines) do
lines
|> Enum.with_index()
|> Enum.reduce(%{}, fn {line, y}, acc ->
line
|> String.split("", trim: true)
|> Enum.with_index()
|> Enum.filter(fn {value, _} -> value == "#" end)
|> Enum.reduce(acc, fn {value, x}, acc ->
Map.put(acc, {x, y}, value)
end)
end)
end
def parse_start_position(lines) do
lines
|> Enum.with_index()
|> Enum.find_value(fn {line, y} ->
line
|> String.split("", trim: true)
|> Enum.with_index()
|> Enum.find_value(fn {value, x} ->
value == "^" and {x, y}
end)
end)
end
end