/
Day13.hs
103 lines (78 loc) · 2.31 KB
/
Day13.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
{-# LANGUAGE LambdaCase #-}
module Day13 where
import Control.Monad.Except (Except, runExcept, throwError)
import Data.Either
import Text.ParserCombinators.ReadP
import Harness
import ParseHelper
import qualified Data.List as L
main :: IO ()
main =
getInputAndSolve
(parseInputRaw parseFullInput)
correctOrderIxSum
calculateDecoderKey
-- SOLVE
correctOrderIxSum :: [(Packet, Packet)] -> Int
correctOrderIxSum =
sum
. map fst
. filter snd
. zip [1 ..]
. map (fromLeft (error "invalid - got equal ordering") . runExcept . isOrdered)
calculateDecoderKey :: [(Packet, Packet)] -> Int
calculateDecoderKey =
let extras =
[ PList [PList [PInt 2]]
, PList [PList [PInt 6]]
]
in product
. map fst
. filter ((`elem` extras) . snd)
. zip [1 ..]
. L.sortBy compareOrdering
. (extras <>)
. uncurry (<>)
. unzip
-- HELPERS
isOrdered :: (Packet, Packet) -> Except Bool ()
isOrdered = \case
(p1@(PInt _), p2@(PList _)) ->
isOrdered (PList [p1], p2)
(p1@(PList _), p2@(PInt _)) ->
isOrdered (p1, PList [p2])
(PInt p1, PInt p2) -> case compare p1 p2 of
LT -> throwError True
GT -> throwError False
EQ -> return ()
(PList [], PList []) ->
return ()
(PList [], PList _) ->
throwError True
(PList _, PList []) ->
throwError False
(PList [p1], PList [p2]) ->
isOrdered (p1, p2)
(PList (p1 : p1rest), PList (p2 : p2rest)) ->
isOrdered (p1, p2) >> isOrdered (PList p1rest, PList p2rest)
compareOrdering :: Packet -> Packet -> Ordering
compareOrdering p1 p2 = case runExcept $ isOrdered (p1, p2) of
Left True -> LT
Left False -> GT
Right () -> EQ
-- PARSE
parseFullInput :: ReadP [(Packet, Packet)]
parseFullInput = sepBy parsePacketPair (newline <* newline) <* newline <* eof
parsePacketPair :: ReadP (Packet, Packet)
parsePacketPair =
(,) <$> parsePacket <*> (newline *> parsePacket)
data Packet
= PInt Int
| PList [Packet]
deriving (Show, Read, Eq, Ord)
parsePacket :: ReadP Packet
parsePacket =
choice
[ PList <$> between (char '[') (char ']') (sepBy parsePacket $ char ',')
, PInt <$> parseInt
]