-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay16.hs
167 lines (144 loc) · 4.48 KB
/
Day16.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
module Day16
( part1
, part2
) where
import Control.Monad.State (State, evalState, get, put, runState)
import Data.Char (digitToInt, intToDigit)
import Data.Sequence as Sq (Seq ((:<|), (:|>)), drop,
dropWhileL, empty, fromList, length,
null, singleton, splitAt, take,
(><))
data Packet =
Packet
{ version :: Int
, packetID :: Int
, value :: Value
}
deriving (Show)
data Value
= Literal Int
| Operator (Seq Packet)
deriving (Show)
type DecodeVal = State (Seq Char, Int) (Seq Packet)
type DecodePacket = State (Seq Char) Packet
type DecodeLit = State (Seq Char) (Seq Char)
concatOp :: Packet -> Value -> Value
concatOp p (Operator o) = Operator (p :<| o)
instance Semigroup Packet where
(<>) p o = o {value = concatOp p . value $ o}
isLiteral :: Value -> Bool
isLiteral (Literal _) = True
isLiteral _ = False
fromLiteral :: Value -> Int
fromLiteral (Literal k) = k
fromOperator :: Value -> Seq Packet
fromOperator (Operator seq) = seq
decode :: DecodePacket
decode = do
s <- get
let (a :<| b :<| c :<| d :<| e :<| f :<| p@(lengthID :<| sp)) = s
(result, remainder)
| [d, e, f] == "100" =
( Packet
(fromBin (a :<| b :<| singleton c))
4
(Literal . fromBin $ packLit)
, restLit)
| lengthID == '0' =
( Packet
(fromBin (a :<| b :<| singleton c))
(fromBin (d :<| e :<| singleton f))
(Operator packByLength)
, restLength)
| lengthID == '1' =
( Packet
(fromBin (a :<| b :<| singleton c))
(fromBin (d :<| e :<| singleton f))
(Operator packByNumber)
, restNumber)
where
(packLit, restLit) = runState parseLit p
packLength = fromBin . Sq.take 15 $ sp
restLength = Sq.drop (15 + packLength) sp
packByLength =
evalState parseByLength (Sq.take packLength . Sq.drop 15 $ sp, 0)
(packNumber, toParseNumber) = Sq.splitAt 11 sp
(packByNumber, (restNumber, _)) =
runState parseByNumber (toParseNumber, fromBin packNumber)
put remainder
return result
parseByLength :: DecodeVal
parseByLength = do
(s, _) <- get
let (parsed, rest) = runState decode s
(postParsed, (final, _)) = runState parseByLength (rest, 0)
result
| Sq.null s = empty
| Sq.null rest = singleton parsed
| otherwise = parsed :<| postParsed
toParse
| Sq.null s || Sq.null rest = empty
| otherwise = final
put (toParse, 0)
return result
parseByNumber :: DecodeVal
parseByNumber = do
(s, n) <- get
let (packet, unparsed) = runState decode s
(result, remainder)
| n == 0 = (empty, (s, 0))
| otherwise = (packet :<| parsed, rest)
where
(parsed, rest) = runState parseByNumber (unparsed, n - 1)
put remainder
return result
parseLit :: DecodeLit
parseLit = do
s <- get
let (a :<| as) = s
(result, remainder)
| a == '1' = (Sq.take 4 as >< parsed, rest)
| a == '0' = (Sq.take 4 as, Sq.drop 4 as)
where
(parsed, rest) = runState parseLit (Sq.drop 4 as)
put remainder
return result
fromBin :: Seq Char -> Int
fromBin = foldl (\a b -> digitToInt b + 2 * a) 0
toBin :: Int -> Seq Char
toBin 0 = singleton '0'
toBin 1 = singleton '1'
toBin x = toBin (div x 2) :|> intToDigit (mod x 2)
pad :: Seq Char -> Seq Char
pad s
| Sq.length s == 4 = s
| otherwise = pad ('0' :<| s)
sumVersions :: Packet -> Int
sumVersions p
| isLiteral . value $ p = version p
| otherwise =
(+ version p) . sum . fmap sumVersions . fromOperator . value $ p
valPacket :: Packet -> Int
valPacket p
| i == 0 = sum recVal
| i == 1 = product recVal
| i == 2 = minimum recVal
| i == 3 = maximum recVal
| i == 4 = fromLiteral . value $ p
| i == 5 && fp > sp = 1
| i == 5 = 0
| i == 6 && fp < sp = 1
| i == 6 = 0
| i == 7 && fp == sp = 1
| i == 7 = 0
where
i = packetID p
recVal = fmap valPacket . fromOperator . value $ p
(fp :<| sp :<| empty) = recVal
packets :: String -> Packet
packets =
evalState decode . foldl (><) empty . map (pad . toBin . digitToInt) . init
part1 :: Bool -> String -> String
part1 _ = show . sumVersions . packets
part2 :: Bool -> String -> String
part2 _ = show . valPacket . packets