-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay16.hs
146 lines (127 loc) · 3.97 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
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Day16 where
import Control.Monad
import Data.Bits
import Data.Char
import Data.Functor
import Numeric
import Text.ParserCombinators.ReadP
import Harness
import ParseHelper
import qualified Data.List as L
main :: IO ()
main = getInputAndSolve parser sumVersionNumbers evaluate
where
parser :: String -> Packet
parser =
parseInputRaw (parsePacket True) . show . parseInputRaw parseBitstring
-- SOLVE
sumVersionNumbers :: Packet -> Int
sumVersionNumbers =
sum . getVersions
where
getVersions :: Packet -> [Int]
getVersions = \case
Literal v _ _ -> [v]
Operator v _ subPackets -> v : concatMap getVersions subPackets
evaluate :: Packet -> Integer
evaluate = \case
Literal _ _ v ->
toInteger v
Operator _ typeId (map evaluate -> subPackets) ->
case typeId of
0 ->
sum subPackets
1 ->
product subPackets
2 ->
minimum subPackets
3 ->
maximum subPackets
5 ->
let [l, r] = subPackets
in if l > r then 1 else 0
6 ->
let [l, r] = subPackets
in if l < r then 1 else 0
7 ->
let [l, r] = subPackets
in if l == r then 1 else 0
_ ->
error $ "Unexpected typeId: " <> show typeId
-- HELPERS
-- PARSE
newtype Bitstring =
Bitstring [Int]
deriving (Read, Eq, Ord)
instance Show Bitstring where
show (Bitstring is) = concatMap show is
parseBitstring :: ReadP Bitstring
parseBitstring = do
hexChars <- many1 $ satisfy (\c -> isDigit c || c `elem` ['A' .. 'F'])
void newline
let hexInts = map (fst . head . readHex . (: [])) hexChars
return $ Bitstring $ concatMap splitHexInt hexInts
where
splitHexInt :: Int -> [Int]
splitHexInt i =
map (getBit i) [3, 2 .. 0]
getBit :: (Bits a, Num a) => a -> Int -> a
getBit byte ix =
if testBit byte ix then
1
else
0
data Packet
= Literal !Int !Int !Int
| Operator !Int !Int ![Packet]
deriving (Show)
parsePacket :: Bool -> ReadP Packet
parsePacket isOutermostPacket = do
version <- toInt <$> count 3 readBit
pType <- toInt <$> count 3 readBit
packet <- case pType of
4 ->
Literal version pType <$> parseLiteral
_ -> do
lengthType <- readBit
Operator version pType <$> parseOperator lengthType
when isOutermostPacket . void . many $ char '0'
return packet
where
parseLiteral :: ReadP Int
parseLiteral = do
leading <- concat <$> many (char '1' *> count 4 readBit)
final <- char '0' *> count 4 readBit
return $ toInt $ leading <> final
parseOperator :: Int -> ReadP [Packet]
parseOperator = \case
0 -> do
subpacketBitCount <- toInt <$> count 15 readBit
subpacketsBitstring <- count subpacketBitCount (choice [char '0', char '1'])
let subpackets = parseInputRaw (many1 $ parsePacket False) subpacketsBitstring
return subpackets
1 -> do
subpacketCount <- toInt <$> count 11 readBit
count subpacketCount $ parsePacket False
e -> error $ "unexpected length type: " <> show e
readBit :: ReadP Int
readBit =
choice
[ char '1' $> 1
, char '0' $> 0
]
toInt :: [Int] -> Int
toInt =
L.foldl'
(\i b ->
shiftL i 1 .|. b
)
0