Advent of Code 2018 Day 4 presents a few interesting challenges.
First of all, this is the first puzzle (of 2018) with input whose records/lines aren't fully self-contained. For instance, the following three lines all pertained to a single 'shift' by Guard #419:
[1518-02-09 23:59] Guard #419 begins shift
[1518-02-10 00:16] falls asleep
[1518-02-10 00:47] wakes up
The following seven lines all pertain to a shift for Guard #61:
[1518-02-12 00:00] Guard #61 begins shift
[1518-02-12 00:32] falls asleep
[1518-02-12 00:39] wakes up
[1518-02-12 00:52] falls asleep
[1518-02-12 00:53] wakes up
[1518-02-12 00:57] falls asleep
[1518-02-12 00:58] wakes up
I won't bore you with the loop/recur
required to parse those records here (source). But the goal of parsing these records was a data structure showing when each guard was asleep. When parsed, the sample data provided with the puzzle description looks like this:
[{10 (range 5, 25)}
{10 (range 30 55)}
{99 (range 40 50)}
{10 (range 24 29)}
{99 (range 36 46)}
{99 (range 45 55)}]
Each map has a single key (guard-id) and a sequence of minutes when the corresponding guard was asleep. The next task is to merge that data structure into a single map, with all minutes spent sleeping keyed by guard-id:
{10 (concat (range 5, 25)
(range 30 55)
(range 24 29))
99 (concat (range 40 50)
(range 36 46)
(range 45 55))}
As is typical of solving problems in Clojure, there's a function for doing just that:
(defn merge-naps [parsed]
(apply (partial merge-with concat) parsed))
In a previous post I described my usage of merge-with
to achieve what the frequencies
functional already does. Now I'm using merge-with
in conjunction with concat
to build the map needed for this solution. Having a merged map for each guard allows for analysis of their sleep patterns (source) into a more helpful data structure:
; Guard 10
{:checksum (* 10 24)
:total-minutes-slept 50
:naps-on-sleepiest-minute 2}
; Guard 99
{:checksum (* 99 45)
:total-minutes-slept 30
:naps-on-sleepiest-minute 3}
From there, we're just a sort-function away, a phrase which here refers to the fact that map keys are also map functions:
(defn sleepiest-guard [sort-fn input]
(->> (parse-naps input)
merge-naps
(map analyze-sleep)
(sort-by sort-fn)
last))
(defn part1 [input]
(:checksum (sleepiest-guard :total-minutes-slept input)))
(defn part2 [input]
(:checksum (sleepiest-guard :naps-on-sleepiest-minute input)))