AdventOfCode/2018/day4/day4.exs

98 lines
3.1 KiB
Elixir

defmodule Day4 do
@log_pattern ~r/\[(\d\d\d\d-\d\d-\d\d \d\d:\d\d)\] (.+)/
@guard_pattern ~r/Guard #(\d+) begins shift/
def parse_log_line(str) do
[date_str, entry] = Regex.run(@log_pattern, str, capture: :all_but_first)
{:ok, date} = NaiveDateTime.from_iso8601(date_str <> ":00")
{date, parse_entry(entry)}
end
def parse_entry("wakes up"), do: :wake
def parse_entry("falls asleep"), do: :sleep
def parse_entry(str) do
[id] = Regex.run(@guard_pattern, str, capture: :all_but_first)
{:arrive, String.to_integer(id)}
end
# [
# {~N[1518-02-18 00:08:00], :sleep, 131},
# {~N[1518-02-18 00:29:00], :wake, 131},
# ...
# ]
def parse_log() do
File.stream!("input")
|> Stream.map(&String.trim/1)
|> Stream.map(&parse_log_line/1)
|> Enum.sort(fn {d1, _}, {d2, _} -> NaiveDateTime.compare(d1, d2) === :lt end)
|> Enum.map_reduce(nil, fn
{date, {:arrive, id}}, _id -> {{date, :arrive, id}, id}
{date, entry}, id -> {{date, entry, id}, id}
end)
|> elem(0)
|> Enum.reject(fn {_date, event, _id} -> event === :arrive end)
end
def sleepy do
parse_log()
|> Enum.chunk_every(2)
|> Enum.map(fn [{sleep, _, _}, {wake, _, id}] ->
{id, div(NaiveDateTime.diff(wake, sleep), 60)}
end)
|> Enum.group_by(fn {id, _mins} -> id end, fn {_id, mins} -> mins end)
|> Enum.map(fn {id, mins} -> {id, Enum.sum(mins)} end)
|> Enum.max_by(fn {_id, mins} -> mins end)
|> elem(0)
end
def most_min_slept(guard_id) do
parse_log()
|> Enum.reject(fn {_date, _event, id} -> id !== guard_id end)
|> Enum.chunk_every(2)
|> Enum.map(fn [{sleep, _, _}, {wake, _, _}] -> sleep.minute..(wake.minute - 1) end)
|> Enum.reduce(%{}, fn range, freq_map ->
Enum.reduce(range, freq_map, fn minute, freq_map ->
Map.update(freq_map, minute, 1, &(&1 + 1))
end)
end)
|> Enum.max_by(fn {_minute, freq} -> freq end)
|> elem(0)
end
def part1 do
sleepy = sleepy()
most_min_slept = most_min_slept(sleepy)
IO.puts("Guard most often asleep: #{sleepy}")
IO.puts("Most mintutes that guard slept: #{most_min_slept}")
IO.puts("Part 1 answer: #{sleepy * most_min_slept}")
end
def most_sleepy_guard_in(guard_freq_map) do
Enum.max_by(guard_freq_map, fn {_id, freq} -> freq end)
end
def part2 do
{minute, {id, _freq}} =
parse_log()
|> Enum.chunk_every(2)
|> Enum.map(fn [{sleep, _, _}, {wake, _, id}] -> {id, sleep.minute..(wake.minute - 1)} end)
|> Enum.reduce(%{}, fn {id, range}, minute_map ->
Enum.reduce(range, minute_map, fn minute, minute_map ->
Map.update(minute_map, minute, %{id => 1}, fn guard_map ->
Map.update(guard_map, id, 1, &(&1 + 1))
end)
end)
end)
|> Enum.map(fn {minute, freq_map} -> {minute, most_sleepy_guard_in(freq_map)} end)
|> Enum.max_by(fn {_minute, {_id, freq}} -> freq end)
IO.puts("The guard that was most freequently alseep on the same minute: #{id}")
IO.puts("The minute that guard was most frequently asleep on: #{minute}")
IO.puts("Part 2 answer: #{id * minute}")
end
end
Day4.part1()
Day4.part2()