never executed always true always false
1 {-|
2 Module: Y2018.D04
3 Description: Advent of Code Day 03 Solutions.
4 License: MIT
5 Maintainer: @tylerjl
6
7 Solutions to the day 04 set of problems for <adventofcode.com>.
8 -}
9 module Y2018.D04
10 ( laziestGuard
11 , laziestMinute
12 )
13 where
14
15 import Y2015.Util (regularParse, intParser)
16
17 import qualified Data.Map.Strict as Map
18
19 import Control.Applicative ((<|>))
20 import Data.List (foldl', maximumBy, sort)
21 import Data.Ord (comparing)
22 import Text.Parsec.String (Parser)
23 import Text.Parsec.Char (endOfLine)
24 import Text.Parsec
25 ( ParseError
26 , many
27 , optional
28 , space
29 , string
30 )
31
32 data Log = Log
33 { timeStamp :: TimeStamp
34 , entry :: Entry
35 } deriving (Eq, Ord, Show)
36
37 data TimeStamp = TimeStamp
38 { _year :: Int
39 , _month :: Int
40 , _day :: Int
41 , _hour :: Int
42 , minute :: Int
43 } deriving (Eq, Ord, Show)
44
45 data Entry = StartShift Guard
46 | Sleep
47 | Wake
48 deriving (Eq, Ord, Show)
49
50 type Guard = Int
51
52 type GuardHistory = (Maybe Guard, Map.Map Guard Shift)
53
54 data Shift = Shift
55 { minutesSlept :: Map.Map Int Int
56 , lastChange :: TimeStamp
57 } deriving (Eq, Ord, Show)
58
59 laziestMinute :: String -> Either ParseError Int
60 laziestMinute input = case parseLog input of
61 Left e -> Left e
62 Right logs -> Right $ logFilter maximum logs
63
64 laziestGuard :: String -> Either ParseError Int
65 laziestGuard input = case parseLog input of
66 Left e -> Left e
67 Right logs -> Right $ logFilter (Map.foldl (+) 0) logs
68
69 logFilter :: Ord a => (Map.Map Int Int -> a) -> [Log] -> Guard
70 logFilter f logs =
71 let sleepiestMinute =
72 fst
73 $ maximumBy (\m c -> compare (snd m) (snd c))
74 $ Map.toList
75 $ minutesSlept
76 $ snd guard
77 guardID = fst guard
78 guard =
79 maximumBy (comparing (f . minutesSlept . snd))
80 $ Map.toList
81 $ snd
82 $ foldl' recordLog (Nothing, Map.empty)
83 $ sort logs
84 in guardID * sleepiestMinute
85
86 recordLog :: GuardHistory -> Log -> GuardHistory
87 recordLog (_current, h) (Log { timeStamp = ts, entry = (StartShift g) }) =
88 (Just g, Map.insertWith shiftChange g toShift h)
89 where toShift =
90 Shift
91 { minutesSlept = Map.fromList $ zip [0 .. 59] $ repeat 0
92 , lastChange = ts
93 }
94 shiftChange _newShift oldShift =
95 oldShift { lastChange = ts }
96 recordLog (Just current, h) (Log { timeStamp = ts@(TimeStamp { minute = m }), entry = (Wake) }) =
97 (Just current, Map.adjust transition current h)
98 where transition oldShift@(Shift { lastChange = (TimeStamp { minute = m' }), minutesSlept = minutes }) =
99 oldShift
100 { lastChange = ts
101 , minutesSlept = Map.unionWith (+) minutes $ Map.fromList $ zip [m' .. (m - 1)] $ repeat 1
102 }
103 recordLog (Just current, h) (Log { timeStamp = ts, entry = (Sleep) }) =
104 (Just current, Map.adjust transition current h)
105 where transition oldShift = oldShift { lastChange = ts }
106 recordLog gh@(Nothing, _) _ = gh
107
108 -- Parsing
109
110 parseLog :: String
111 -> Either ParseError [Log]
112 parseLog = regularParse logParser
113
114 logParser :: Parser [Log]
115 logParser = many (parseRawLog <* optional endOfLine)
116
117 parseRawLog :: Parser Log
118 parseRawLog = Log <$> (parseTimeStamp <* space) <*> parseEntry
119
120 parseTimeStamp :: Parser TimeStamp
121 parseTimeStamp = TimeStamp <$ string "[" <*> intParser <* string "-"
122 <*> intParser <* string "-"
123 <*> intParser <* space
124 <*> intParser <* string ":"
125 <*> intParser <* string "]"
126
127 parseEntry :: Parser Entry
128 parseEntry = StartShift <$ string "Guard #" <*> intParser <* string " begins shift"
129 <|> Sleep <$ string "falls asleep"
130 <|> Wake <$ string "wakes up"