aoc2022/13-b.hs

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