aboutsummaryrefslogtreecommitdiffstats
path: root/day16/main.hs
blob: 1f1f8ca73e553dc9b345fb517e4a3bc03aea60eb (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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
module Main where

import Control.Monad (foldM)
import Data.Bifunctor (bimap, first, second)
import Data.Char (digitToInt)
import Data.Function (on)
import qualified Data.List as L
import Data.Map.Strict (Map, (!))
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import Debug.Trace (traceShow, traceShowId)
import Utils

convertHex '0' = "0000"
convertHex '1' = "0001"
convertHex '2' = "0010"
convertHex '3' = "0011"
convertHex '4' = "0100"
convertHex '5' = "0101"
convertHex '6' = "0110"
convertHex '7' = "0111"
convertHex '8' = "1000"
convertHex '9' = "1001"
convertHex 'A' = "1010"
convertHex 'B' = "1011"
convertHex 'C' = "1100"
convertHex 'D' = "1101"
convertHex 'E' = "1110"
convertHex 'F' = "1111"
convertHex other = error $ "invalid hex: " ++ [other]

hexToBinaryString "" = ""
hexToBinaryString (hex : rest) = convertHex hex ++ hexToBinaryString rest

applyOperator opTypeId subValues = case opTypeId of
  0 -> sum subValues
  1 -> product subValues
  2 -> minimum subValues
  3 -> maximum subValues
  5 ->
    let [a, b] = subValues
     in if a > b then 1 else 0
  6 ->
    let [a, b] = subValues
     in if a < b then 1 else 0
  7 ->
    let [a, b] = subValues
     in if a == b then 1 else 0
  other -> error $ "invalid operator type id " ++ show opTypeId

parseTypeOperator :: Int -> String -> (Int, Int, String)
parseTypeOperator opTypeId binary =
  let (lengthTypeID, binary') = splitAt 1 binary $> first binaryToInt
      (versionSum, subValues, rest) = case lengthTypeID of
        0 -> parseOpLengthId0 binary'
        1 -> parseOpLengthId1 binary'
        other -> error $ "invalid length type id " ++ show other
      computedResult = applyOperator opTypeId subValues
   in (versionSum, computedResult, rest)

parseOpLengthId0 :: String -> (Int, [Int], String)
parseOpLengthId0 binary =
  let (subPacketsLength, binary') = splitAt 15 binary $> first binaryToInt
      (subPackets, binary'') = splitAt subPacketsLength binary'
      iter = iterate (\(_, _, sub) -> parse sub) (minBound, minBound, subPackets) $> tail .> takeUntil (\(_, _, rest) -> (not . null) rest)
      (subVersions, results, rests) = iter $> unzip3
   in (sum subVersions, results, binary'')

parseOpLengthId1 :: String -> (Int, [Int], String)
parseOpLengthId1 binary =
  let (subPacketsCount, binary') = splitAt 11 binary $> first binaryToInt
      iter = iterate (\(_, _, sub) -> parse sub) (minBound, minBound, binary') $> tail .> take subPacketsCount
      (subVersions, results, rests) = iter $> unzip3
   in (sum subVersions, results, last rests)

parseType4 :: [Char] -> (Int, String)
parseType4 binary =
  let (result, rest) = parseType4' binary
   in (binaryToInt result, rest)

parseType4' :: String -> (String, String)
parseType4' ('0' : rest) = splitAt 4 rest
parseType4' ('1' : rest) =
  let (binary, rest') = splitAt 4 rest
      (result, rest'') = parseType4' rest'
   in (binary ++ result, rest'')
parseType4' other = error $ "invalid type 4: " ++ other

parse :: String -> (Int, Int, String)
parse "" = (0, minBound, "")
parse binary =
  let (packetVersion, binary') = splitAt 3 binary $> first binaryToInt
      (packetTypeID, binary'') = splitAt 3 binary' $> first binaryToInt
   in case packetTypeID of
        4 ->
          let (result, rest) = parseType4 binary''
           in (packetVersion, result, rest)
        opTypeId ->
          let (typeOpVersionSum, result, rest) = parseTypeOperator opTypeId binary''
           in (packetVersion + typeOpVersionSum, result, rest)

main :: IO ()
main =
  do
    contents <- getContents

    let binary = contents $> T.pack .> T.strip .> T.unpack .> hexToBinaryString

    parse binary $> print