96 lines
2.3 KiB
Elixir
96 lines
2.3 KiB
Elixir
defmodule Day4 do
|
|
def input() do
|
|
File.stream!("input/day4.in")
|
|
|> Stream.map(&String.trim/1)
|
|
|> Enum.sort()
|
|
end
|
|
|
|
def part1() do
|
|
{id, minute, _} =
|
|
input()
|
|
|> Enum.map(&parse_record_pattern/1)
|
|
|> Enum.reduce({%{}, [], []}, &stack_and_map/2)
|
|
|> elem(0)
|
|
|> Enum.max_by(fn {_k, v} ->
|
|
v.asleep
|
|
|> Enum.map(&Enum.sum/1)
|
|
|> Enum.sum()
|
|
end)
|
|
|> sleepiest_minute()
|
|
|
|
id * minute
|
|
end
|
|
|
|
def part2() do
|
|
{id, minute, _count} =
|
|
input()
|
|
|> Enum.map(&parse_record_pattern/1)
|
|
|> Enum.reduce({%{}, [], []}, &stack_and_map/2)
|
|
|> elem(0)
|
|
|> Enum.map(&sleepiest_minute/1)
|
|
|> Enum.max_by(fn {_id, _min, count} -> count end)
|
|
|
|
id * minute
|
|
end
|
|
|
|
defp sleepiest_minute({id, %{asleep: list_of_ranges}}) do
|
|
{minute, list} =
|
|
list_of_ranges
|
|
|> Enum.flat_map(& &1)
|
|
|> Enum.group_by(& &1)
|
|
|> Enum.max_by(fn {_k, v} -> length(v) end, fn -> {0, []} end)
|
|
|
|
{id, minute, length(list)}
|
|
end
|
|
|
|
def parse_record_pattern(string) do
|
|
<<
|
|
"[",
|
|
_date::binary-size(10),
|
|
" ",
|
|
_hour::binary-size(2),
|
|
":",
|
|
minute::binary-size(2),
|
|
"] ",
|
|
action::binary
|
|
>> = string
|
|
|
|
%{
|
|
minute: String.to_integer(minute),
|
|
action: parse_action(action)
|
|
}
|
|
end
|
|
|
|
defp parse_action("Guard #" <> string) do
|
|
[id] = Regex.run(~r/\d+/, string)
|
|
{:guard, String.to_integer(id)}
|
|
end
|
|
|
|
defp parse_action("wakes up"), do: :wake_up
|
|
defp parse_action("falls asleep"), do: :sleep
|
|
|
|
def stack_and_map(%{action: {:guard, id}}, {map, awake_stack, asleep_stack}) do
|
|
{Map.put_new(map, id, %{slept: nil, asleep: []}), [id | awake_stack], asleep_stack}
|
|
end
|
|
|
|
def stack_and_map(
|
|
%{action: :sleep, minute: minute} = _record,
|
|
{map, [current_id | awake_stack], asleep_stack}
|
|
) do
|
|
{put_in(map, [current_id, :slept], minute), awake_stack, [current_id | asleep_stack]}
|
|
end
|
|
|
|
def stack_and_map(
|
|
%{action: :wake_up, minute: minute} = _record,
|
|
{map, awake_stack, [current_id | asleep_stack]}
|
|
) do
|
|
went_to_sleep = get_in(map, [current_id, :slept])
|
|
|
|
{update_in(map, [current_id, :asleep], &[went_to_sleep..(minute - 1) | &1]),
|
|
[current_id | awake_stack], asleep_stack}
|
|
end
|
|
end
|
|
|
|
IO.puts(Day4.part1())
|
|
IO.puts(Day4.part2())
|