Skip to content

Instantly share code, notes, and snippets.

@jemmyw
Created December 9, 2018 07:19
Show Gist options
  • Save jemmyw/1b152096f8478dd31b7cfdc9af960fb9 to your computer and use it in GitHub Desktop.
Save jemmyw/1b152096f8478dd31b7cfdc9af960fb9 to your computer and use it in GitHub Desktop.
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