-
Notifications
You must be signed in to change notification settings - Fork 3
/
day04.ex
98 lines (83 loc) · 2.11 KB
/
day04.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
defmodule Elixir2016.Day04 do
alias Elixir2016.Day04.Rotate
def parse(filename) do
File.stream!(filename)
|> Stream.map(&String.trim/1)
|> Stream.map(&parse_room/1)
end
def part1(filename) do
parse(filename)
|> Stream.filter(&valid_room?/1)
|> Enum.map(fn room -> room.sector_id end)
|> Enum.sum()
end
def part2(filename) do
parse(filename)
|> Stream.filter(&valid_room?/1)
|> Stream.map(&add_real_name/1)
|> Stream.filter(&north_pole?/1)
|> Stream.map(fn room -> room.sector_id end)
|> Enum.at(0)
end
def add_real_name(room) do
real_name =
room.name
|> Rotate.rotate(room.sector_id)
|> String.replace("-", " ")
room |> Map.put(:real_name, real_name)
end
def north_pole?(room) do
room.real_name
|> String.downcase()
|> String.contains?("northpole")
end
def parse_room(string) do
[_, name, sector_id, checksum] = Regex.run(~r/^(\D*?)-(\d+)\[(\w+)\]$/, string)
%{name: name, sector_id: String.to_integer(sector_id), checksum: checksum}
end
def valid_room?(room) when is_map(room) do
compute_checksum(room) == room.checksum
end
def valid_room?(room_str) when is_binary(room_str) do
room_str
|> parse_room()
|> valid_room?
end
def compute_checksum(%{name: name} = _room) do
counts =
name
|> String.replace("-", "")
|> String.graphemes()
|> Enum.reduce(%{}, fn x, acc ->
Map.update(acc, x, 1, fn x -> x + 1 end)
end)
Map.keys(counts)
|> Enum.sort_by(fn letter -> {0 - counts[letter], letter} end)
|> Enum.take(5)
|> Enum.join("")
end
end
defmodule Elixir2016.Day04.Rotate do
def rotate(string, num) do
string
|> String.to_charlist()
|> Enum.map(fn x -> rotate_codepoint(x, num) end)
|> List.to_string()
end
def rotate_codepoint(x, num) do
# 97 - 122: a-z
# 65 - 90: A-Z
cond do
97 <= x and x <= 122 ->
x = x - 97
x = rem(x + num, 26)
x + 97
65 <= x and x <= 90 ->
x = x - 65
x = rem(x + num, 26)
x + 65
true ->
x
end
end
end