-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay15.hs
93 lines (76 loc) · 3.35 KB
/
Day15.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
{-# LANGUAGE TemplateHaskell #-}
module Day15
( part1
, part2
) where
import Control.Applicative (some, (<|>))
import Data.Bits ((.&.))
import Data.ByteString (ByteString)
import Data.ByteString.Char8 (pack)
import Data.Char (ord)
import Data.IntMap (IntMap, empty, foldrWithKey, insert,
notMember, (!))
import Data.List (foldl')
import Data.Sequence (Seq ((:<|), (:|>)), breakl,
foldrWithIndex, singleton, tails,
(><))
import qualified Data.Sequence as Sq (filter, null)
import FlatParse.Basic (anyAsciiDecimalInt, isLatinLetter,
optional_, satisfy, switch)
import qualified FlatParse.Basic as FB (anyAsciiChar, char, runParser)
import FlatParse.Stateful (get, modify, put)
import qualified FlatParse.Stateful as FS (anyAsciiChar, char, runParser)
import Helpers.Parsers.FlatParse (Parser, ParserS, extract, extractS)
type Label = ByteString
type Hash = Int
type Focal = Int
type Boxes = IntMap Box
type Box = Seq (Label, Focal)
parseValue :: ParserS () Int
parseValue =
($(FS.char ',') >> get >>= \v -> put 0 >> (v +) <$> parseValue)
<|> ($(FS.char '\n') >> get)
<|> (FS.anyAsciiChar >>= \v ->
modify (\x -> ((ord v + x) * 17) .&. 255) >> parseValue)
parseBoxes :: Boxes -> Parser Int
parseBoxes boxes =
(parseLabel >>= \l ->
parseOp l boxes >>= \b -> optional_ $(FB.char ',') >> parseBoxes b)
<|> ($(FB.char '\n') >> (pure . scoreBoxes $ boxes))
parseOp :: (Label, Hash) -> Boxes -> Parser Boxes
parseOp label boxes =
$(switch
[|case _ of
"-" -> pure $ removeFromBox label boxes
"=" ->
anyAsciiDecimalInt >>= \focal ->
pure . insertInBox label focal $ boxes|])
parseLabel :: Parser (Label, Hash)
parseLabel =
some (satisfy isLatinLetter) >>= \l ->
pure (pack l, foldl' (\acc c -> ((ord c + acc) * 17) .&. 255) 0 l)
insertInBox :: (Label, Hash) -> Focal -> Boxes -> Boxes
insertInBox (label, hash) focal boxes
| hash `notMember` boxes = insert hash (singleton (label, focal)) boxes
| Sq.null . Sq.filter ((== label) . fst) $ box =
insert hash (box :|> (label, focal)) boxes
| otherwise = insert hash ((before :|> (label, focal)) >< after) boxes
where
box = boxes ! hash
(before, _ :<| after) = breakl ((== label) . fst) box
removeFromBox :: (Label, Hash) -> Boxes -> Boxes
removeFromBox (label, hash) boxes
| hash `notMember` boxes = boxes
| Sq.null . Sq.filter ((== label) . fst) $ box = boxes
| otherwise = insert hash (before >< after) boxes
where
box = boxes ! hash
(before, _ :<| after) = breakl ((== label) . fst) box
scoreBox :: Box -> Int
scoreBox = foldrWithIndex (\index (_, v) acc -> (index + 1) * v + acc) 0 -- foldr ((+) . sum) 0 . tails . fmap snd
scoreBoxes :: Boxes -> Int
scoreBoxes = foldrWithKey (\k v acc -> (k + 1) * scoreBox v + acc) 0
part1 :: Bool -> ByteString -> String
part1 _ = show . extractS . FS.runParser parseValue () 0
part2 :: Bool -> ByteString -> String
part2 _ = show . extract . FB.runParser (parseBoxes empty)