import Data.List.Split (splitOn) import Data.Char (isNumber) import Data.List (elemIndices) {- cabal: build-depends: base, split -} 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 main = interact $ show . sum . map (+ 1) . elemIndices LT . map ((\[a, b] -> a `compareNodes` b) . map parseNode . lines) . splitOn "\n\n"