51 lines
1.5 KiB
Haskell
51 lines
1.5 KiB
Haskell
import Data.Char (isNumber)
|
|
import Data.List (sortBy, findIndices)
|
|
|
|
data Node = List [Node] | Num Int
|
|
deriving (Eq, Show)
|
|
|
|
compareNodes :: Node -> Node -> Ordering
|
|
-- two ints
|
|
compareNodes (Num x) (Num y) = compare x y
|
|
-- two lists
|
|
compareNodes (List []) (List []) = EQ
|
|
compareNodes (List _) (List []) = GT
|
|
compareNodes (List []) (List _) = LT
|
|
compareNodes (List (x:xs)) (List (y:ys))
|
|
| r /= EQ = r
|
|
| otherwise = compareNodes (List xs) (List ys)
|
|
where r = compareNodes x y
|
|
-- mixed
|
|
compareNodes (Num x) (List y) = compareNodes (List [Num x]) (List y)
|
|
compareNodes (List x) (Num y) = compareNodes (List x) (List [Num y])
|
|
|
|
-- "1,[2,3],4,[5,[6,7]]" -> ["1","[2,3]","4","[5,[6,7]]"]
|
|
parseList :: String -> [String]
|
|
parseList = go 0 ""
|
|
where
|
|
go :: Int -> String -> String -> [String]
|
|
go 0 "" [] = []
|
|
go 0 accum [] = [accum]
|
|
go _ _ [] = error "unclosed parens"
|
|
go depth accum (c:xs)
|
|
| c == '[' = go (depth + 1) (accum ++ [c]) xs
|
|
| c == ']' = go (depth - 1) (accum ++ [c]) xs
|
|
| c == ',' && depth == 0 = accum : go depth "" xs
|
|
| otherwise = go depth (accum ++ [c]) xs
|
|
|
|
parseNode :: String -> Node
|
|
parseNode s
|
|
| head s == '[' && last s == ']' = List $ map parseNode $ parseList (reverse $ drop 1 $ reverse $ drop 1 s)
|
|
| all isNumber s = Num $ read s
|
|
| otherwise = error s
|
|
|
|
dividerPackets = [List [List [Num 2]], List [List [Num 6]]]
|
|
|
|
main = interact $
|
|
show
|
|
. product
|
|
. map (+ 1) . findIndices (`elem` dividerPackets)
|
|
. sortBy compareNodes
|
|
. (++ dividerPackets)
|
|
. map parseNode
|
|
. filter (not . null) . lines |