aboutsummaryrefslogtreecommitdiff
path: root/2021/day16/16.hs
blob: f38240ad507bc0530964ef8c68863e4042d4e608 (plain)
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
module Main where

import Numeric (readHex)
import Text.Printf (printf)
import Data.Char (digitToInt)
import Data.List (foldl')


readInt :: Int -> [Bool] -> (Integer, [Bool])
readInt n bs = (foldl' (\acc x -> acc * 2 + boolToDigit x) 0 (take n bs), drop n bs)
  where boolToDigit True = 1
        boolToDigit False = 0

hexToBits :: String -> [Bool]
hexToBits "" = []
hexToBits (c:cs) = case readHex [c] of
                (x, _):_ -> (map binToBool $ printf "%04b" (x :: Int)) ++ hexToBits cs
                _ -> []
  where binToBool '1' = True
        binToBool _ = False

data Packet = Literal Integer Integer Integer |
              Operator Integer Integer [Packet] |
              Padding
            deriving (Show, Eq, Ord)


parseHexPacket :: String -> (Packet, [Bool])
parseHexPacket = parsePacket . hexToBits

parseHeader :: [Bool] -> (Integer, Integer, [Bool])
parseHeader bs = (version, typ, body)
  where (version, afterVersion) = readInt 3 bs
        (typ, body) = readInt 3 afterVersion

parsePacket :: [Bool] -> (Packet, [Bool])
parsePacket bs = case parseHeader bs of
                   (ver, 4, body)  -> let (val, rest) = readLiteralInner body
                                          in (Literal ver 4 val, rest)
                   (ver, op, body) -> let (val, rest) = readOpInner body
                                          in (Operator ver op val, rest)

readLiteralInner :: [Bool] -> (Integer, [Bool])
readLiteralInner bs = readLiteralBlock 0 bs
  where readLiteralBlock acc (cont:bs) | cont      = readLiteralBlock acc' rest
                                       | otherwise = (acc', rest)
                                       where (val, rest) = readInt 4 bs
                                             acc' = (16 * acc) + val

readOpInner :: [Bool] -> ([Packet], [Bool])
readOpInner (False:header) = (parseAllPackets (take (fromIntegral len) body), drop (fromIntegral len) body)
  where (len, body) = readInt 15 header
        parseAllPackets [] = []
        parseAllPackets bs = let (parsed, remaining) = parsePacket bs
                                 in parsed : parseAllPackets remaining
readOpInner (True:header) = parseNPackets n body
  where (n, body) = readInt 11 header
        parseNPackets 0 b = ([], b)
        parseNPackets n bs = let (parsed, remaining) = parsePacket bs
                                 (restParsed, endRemaining) = parseNPackets (n - 1) remaining
                                 in (parsed : restParsed, endRemaining)

sumVersion :: Packet -> Integer
sumVersion (Literal v _ _) = v
sumVersion (Operator v _ ps) = v + (foldr (+) 0 (map sumVersion ps))
sumVersion Padding = 0

eval :: Packet -> Integer
eval (Literal _ _ v) = v
eval (Operator _ 0 ps) = sum (map eval ps)
eval (Operator _ 1 ps) = product (map eval ps)
eval (Operator _ 2 ps) = minimum (map eval ps)
eval (Operator _ 3 ps) = maximum (map eval ps)
eval (Operator _ 5 ps) = if a > b then 1 else 0
  where [a, b] = map eval ps
eval (Operator _ 6 ps) = if a < b then 1 else 0
  where [a, b] = map eval ps
eval (Operator _ 7 ps) = if a == b then 1 else 0
  where [a, b] = map eval ps


main :: IO ()
main = do
  input <- readFile "./input"
  let (parsed, _) = parseHexPacket input
  print $ sumVersion parsed
  print $ eval parsed