This commit is contained in:
Shadowfacts 2018-12-04 13:15:58 -05:00
parent ad6c796cd2
commit 8f639ecdcd
Signed by: shadowfacts
GPG Key ID: 83FB3304046BADA4
3 changed files with 1299 additions and 0 deletions

144
lib/day4/day4.ex Normal file
View File

@ -0,0 +1,144 @@
defmodule Day4 do
defmodule Timestamp do
defstruct [:year, :month, :day, :hour, :minute]
def create(y, m, d, h, min) do
%Timestamp{
year: y |> String.to_integer(),
month: m |> String.to_integer(),
day: d |> String.to_integer(),
hour: h |> String.to_integer(),
minute: min |> String.to_integer
}
end
def range(first, second) do
first.minute..second.minute
end
end
def parse_event(line) do
res = Regex.run(~r/\[(\d+)-(\d+)-(\d+) (\d+):(\d+)\] (.+)/, line)
[_line, y, m, d, h, min, event | _tail] = res
{
Timestamp.create(y, m, d, h, min),
event
}
end
def parse_events(lines) do
lines
|> Enum.map(&parse_event/1)
end
def sort_events(events) do
events
|> Enum.sort_by(fn {ts, _} ->
{ts.year, ts.month, ts.day, ts.hour, ts.minute}
end)
end
def parse_guard({_, msg}) do
case Regex.run(~r/Guard #(\d+) begins shift/, msg) do
[_event, id | _tail] -> id
_ -> nil
end
end
def get_sleep_times(events) do
chunk_fun = fn event, acc ->
case parse_guard(event) do
nil -> {:cont, [event | acc]}
_id -> {:cont, Enum.reverse(acc), [event]}
end
end
after_fun = fn
[] -> {:cont, []}
acc -> {:cont, Enum.reverse(acc), acc}
end
events
|> Enum.chunk_while([], chunk_fun, after_fun)
|> Enum.reject(fn
[] -> true
_ -> false
end)
|> Enum.reduce(%{}, fn [begin_shift | events], acc ->
guard_id = parse_guard(begin_shift)
ranges = events
|> Enum.chunk_every(2)
|> Enum.map(fn [{sleep, _}, {wake, _} | []] ->
Timestamp.range(sleep, wake)
end)
Map.update(acc, guard_id, ranges, &(&1 ++ ranges))
end)
|> Enum.reject(fn
{_id, []} -> true
_ -> false
end)
end
def get_max_sleep_time(guards) do
guards
|> Enum.map(fn {id, times} ->
total = times
|> Enum.map(fn a..b -> b - a end)
|> Enum.sum()
{minute, _count} = times
|> Enum.flat_map(&Enum.to_list/1)
|> Enum.reduce(%{}, fn minute, acc ->
Map.update(acc, minute, 1, &(&1 + 1))
end)
|> Enum.max_by(fn {_minute, count} -> count end)
{id, total, minute}
end)
|> Enum.max_by(fn {_id, total, _minute} -> total end)
end
def range_containing(ranges) do
first = Enum.reduce(ranges, Enum.at(ranges, 0).first, fn first.._, acc ->
min(first, acc)
end)
last = Enum.reduce(ranges, Enum.at(ranges, 0).last, fn _..last, acc ->
max(last, acc)
end)
first..last
end
def get_most_frequent_sleep_time(guards) do
guards
|> Enum.map(fn {id, times} ->
{minute, count} = range_containing(times)
|> Enum.map(fn minute ->
{minute, Enum.count(times, fn range -> minute in range end)}
end)
|> Enum.max_by(fn {_minute, count} -> count end)
{id, minute, count}
end)
|> Enum.max_by(fn {_id, _minute, count} -> count end)
end
def parse_input() do
File.read!("lib/day4/input.txt")
|> String.split("\n", trim: true)
end
def part1() do
{id, _total, minute} = parse_input()
|> parse_events()
|> sort_events()
|> get_sleep_times()
|> get_max_sleep_time()
String.to_integer(id) * minute
end
def part2() do
{id, minute, _count} = parse_input()
|> parse_events()
|> sort_events()
|> get_sleep_times()
|> get_most_frequent_sleep_time()
String.to_integer(id) * minute
end
end

1072
lib/day4/input.txt Normal file

File diff suppressed because it is too large Load Diff

83
test/day4_test.exs Normal file
View File

@ -0,0 +1,83 @@
defmodule Day4Test do
use ExUnit.Case
doctest Day4
alias Day4.Timestamp
test "parse event" do
assert Day4.parse_event("[1518-11-01 00:00] Guard #10 begins shift") == {%Timestamp{year: 1518, month: 11, day: 01, hour: 0, minute: 0}, "Guard #10 begins shift"}
end
test "parse guard" do
event = {%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 00}, "Guard #10 begins shift"}
assert Day4.parse_guard(event) == "10"
end
test "sort events" do
events = [
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 05}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 55}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 25}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 00}, "Guard #10 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 30}, "falls asleep"}
]
assert Day4.sort_events(events) == [
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 00}, "Guard #10 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 05}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 25}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 30}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 01, hour: 00, minute: 55}, "wakes up"}
]
end
test "get sleep times" do
events = [
{%Timestamp{year: 1518, month: 11, day: 1, hour: 0, minute: 0}, "Guard #10 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 1, hour: 0, minute: 5}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 1, hour: 0, minute: 25}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 1, hour: 0, minute: 30}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 1, hour: 0, minute: 55}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 1, hour: 23, minute: 58}, "Guard #99 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 2, hour: 0, minute: 40}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 2, hour: 0, minute: 50}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 3, hour: 0, minute: 5}, "Guard #10 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 3, hour: 0, minute: 24}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 3, hour: 0, minute: 29}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 4, hour: 0, minute: 2}, "Guard #99 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 4, hour: 0, minute: 36}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 4, hour: 0, minute: 46}, "wakes up"},
{%Timestamp{year: 1518, month: 11, day: 5, hour: 0, minute: 3}, "Guard #99 begins shift"},
{%Timestamp{year: 1518, month: 11, day: 5, hour: 0, minute: 45}, "falls asleep"},
{%Timestamp{year: 1518, month: 11, day: 5, hour: 0, minute: 55}, "wakes up"}
]
assert Day4.get_sleep_times(events) == [
{"10", [5..25, 30..55, 24..29]},
{"99", [40..50, 36..46, 45..55]}
]
end
test "get max sleep time" do
guards = [
{"10", [5..25, 30..55, 24..29]},
{"99", [40..50, 36..46, 45..55]}
]
assert Day4.get_max_sleep_time(guards) == {"10", 50, 24}
end
test "range containing" do
assert Day4.range_containing([5..25, 30..55, 24..29]) == 5..55
assert Day4.range_containing([40..50, 36..46, 45..55]) == 36..55
end
test "most frequent sleep time" do
guards = [
{"10", [5..25, 30..55, 24..29]},
{"99", [40..50, 36..46, 45..55]}
]
assert Day4.get_most_frequent_sleep_time(guards) == {"99", 45, 3}
end
end