Created
December 9, 2018 07:19
-
-
Save jemmyw/1b152096f8478dd31b7cfdc9af960fb9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Sleeps do | |
def parse([]), do: [] | |
def parse([nil, t]), do: parse(t) | |
def parse([h | t]), do: [parse(h) | parse(t)] | |
def parse(str) do | |
~r/^\[(\d+)-(\d+)-(\d+) (\d+):(\d+)\] (.+)/ | |
|> Regex.run(str) | |
|> List.delete_at(0) | |
|> List.to_tuple() | |
end | |
def sleep_datetime({y, m, d, h, mm, _}) do | |
[y, m, d, h, mm] |> Enum.join() |> String.to_integer() | |
end | |
def sort(sleeps) do | |
sleeps | |
|> Enum.sort_by(fn sleep -> | |
sleep |> sleep_datetime | |
end) | |
end | |
def normalize(sleeps, last_id \\ nil) | |
def normalize([], _), do: [] | |
def normalize([sleep | t], last_id) do | |
{id, state} = normalize_id(sleep, last_id) | |
{d, m, y, _, mm, _} = sleep | |
date = [d, m, y] |> Enum.map(&String.to_integer(&1)) |> Enum.join("-") | |
rec = {id, state, date, mm |> String.to_integer()} | |
[rec | normalize(t, id)] | |
end | |
def normalize_id({_, _, _, _, _, "falls asleep"}, last_id), do: {last_id, :asleep} | |
def normalize_id({_, _, _, _, _, "wakes up"}, last_id), do: {last_id, :awake} | |
def normalize_id({_, _, _, _, _, state}, _) do | |
r = ~r/Guard #(\d+)/ |> Regex.run(state) | |
[_ | [guard | _]] = r | |
{guard |> String.to_integer(), :awake} | |
end | |
def fill_minutes([]), do: [] | |
def fill_minutes([sleep | [next_sleep | t]]) do | |
from = sleep |> elem(3) | |
to = (next_sleep |> elem(3)) - 1 | |
range = | |
cond do | |
from < to -> from..to | |
from == to -> from..to | |
from > to -> from..59 | |
end | |
[sleep |> Tuple.append(range) | fill_minutes([next_sleep | t])] | |
end | |
def fill_minutes([sleep | t]) do | |
from = sleep |> elem(3) | |
[sleep |> Tuple.append(from..59) | t] | |
end | |
def group_by_guard(sleeps) do | |
sleeps |> Enum.group_by(&elem(&1, 0)) | |
end | |
def count_minutes([], _), do: 0 | |
def count_minutes([{_, state, _, _, r} | t], state) do | |
Enum.count(r) + count_minutes(t, state) | |
end | |
def count_minutes([{_, _, _, _, _} | t], state), do: count_minutes(t, state) | |
def count_minutes_by_guard(sleeps, state) do | |
sleeps | |
|> group_by_guard | |
|> Enum.reduce(%{}, fn {guard, sleeps}, acc -> | |
Map.put(acc, guard, count_minutes(sleeps, state)) | |
end) | |
end | |
def highest_by_key(map) do | |
map | |
|> Enum.reduce({nil, 0}, fn | |
{ng, nm}, {_, lm} when nm > lm -> {ng, nm} | |
_, lg -> lg | |
end) | |
end | |
def guard_sleeping_the_most(sleeps) do | |
sleeps | |
|> count_minutes_by_guard(:asleep) | |
|> highest_by_key | |
|> elem(0) | |
end | |
def expand_range(range) do | |
Enum.map(range, fn x -> x end) | |
end | |
def expand_ranges(ranges) do | |
Enum.map(ranges, &expand_range/1) | |
end | |
def count_occurrances(list) do | |
Enum.reduce(list, %{}, fn item, acc -> | |
Map.put(acc, item, Map.get(acc, item, 0) + 1) | |
end) | |
end | |
def most_common_slept_minute(sleeps) do | |
sleeps | |
|> Enum.filter(fn sleep -> elem(sleep, 1) == :asleep end) | |
|> Enum.map(fn sleep -> sleep |> elem(4) end) | |
|> expand_ranges() | |
|> List.flatten() | |
|> count_occurrances | |
|> highest_by_key | |
end | |
def guard_with_most_sleepy_minute(sleeps) do | |
sleeps | |
|> Enum.map(fn {guard, sleeps} -> | |
{guard, most_common_slept_minute(sleeps)} | |
end) | |
|> Enum.reduce({nil, nil, 0}, fn | |
{ng, {nm, nc}}, {_, _, ln} when nc > ln -> {ng, nm, nc} | |
{_, {_, nc}}, {lg, lm, ln} when nc <= ln -> {lg, lm, ln} | |
end) | |
end | |
end | |
sleeps = | |
File.read("4.txt") | |
|> elem(1) | |
|> String.split("\n") | |
|> Sleeps.parse() | |
|> Sleeps.sort() | |
|> Sleeps.normalize() | |
|> Sleeps.fill_minutes() | |
# Part 1 | |
sleepy = sleeps |> Sleeps.guard_sleeping_the_most() | |
IO.inspect(sleepy) | |
common = | |
sleeps | |
|> Sleeps.group_by_guard() | |
|> Map.get(sleepy) | |
|> Sleeps.most_common_slept_minute() | |
|> elem(0) | |
IO.inspect(common) | |
IO.inspect(sleepy * common) | |
# Part 2 | |
best_minute = | |
sleeps | |
|> Sleeps.group_by_guard() | |
|> Sleeps.guard_with_most_sleepy_minute() | |
IO.inspect(best_minute) | |
{guard, minute, _} = best_minute | |
IO.inspect(guard * minute) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment