aboutsummaryrefslogtreecommitdiffstats
path: root/day10/main.hs
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2021-12-10 11:06:44 +0200
committerJan Tuomi <jans.tuomi@gmail.com>2021-12-10 13:40:34 +0200
commita5b217240c57f89e3d483a940041efda9a8ee97c (patch)
tree949cc8775ad4db96222de04db253b9a21baa0bea /day10/main.hs
parentec95a2d6d52f6766589d553dc9a0a7689eab481f (diff)
Day 10
Diffstat (limited to 'day10/main.hs')
-rw-r--r--day10/main.hs127
1 files changed, 127 insertions, 0 deletions
diff --git a/day10/main.hs b/day10/main.hs
new file mode 100644
index 0000000..a368cdd
--- /dev/null
+++ b/day10/main.hs
@@ -0,0 +1,127 @@
+module Main where
+
+import Data.Either (fromLeft, fromRight)
+import Data.List (foldl', sort)
+import Data.Map (Map, (!))
+import qualified Data.Map as Map
+import Utils
+
+data Expr
+ = Paren [Expr]
+ | Bracket [Expr]
+ | Curly [Expr]
+ | Angle [Expr]
+ deriving (Show)
+
+data Failure
+ = Incomplete
+ | Corrupted Char
+ deriving (Show)
+
+type Computation a = Either Failure a
+
+consume expected (c : cs) =
+ if c /= expected
+ then Left (Corrupted c)
+ else Right cs
+consume expected [] = Left Incomplete
+
+pairs =
+ Map.fromList
+ [ ('(', ')'),
+ ('[', ']'),
+ ('{', '}'),
+ ('<', '>')
+ ]
+
+opening = Map.keys pairs
+
+closing = Map.elems pairs
+
+expr :: Char -> [Expr] -> Expr
+expr '(' children = Paren children
+expr '[' children = Bracket children
+expr '{' children = Curly children
+expr '<' children = Angle children
+expr c _ = error $ "invalid c: " ++ [c]
+
+parse :: String -> Computation ([Expr], String)
+parse s
+ | null s = Left Incomplete
+ | head s `elem` opening =
+ let (c : cs) = s
+ closing = pairs ! c
+ in do
+ (children, rest) <- parse cs
+ rest' <- consume closing rest
+ (nextExprs, rest'') <-
+ if not (null rest')
+ then parse rest'
+ else Right ([], rest')
+ Right (expr c children : nextExprs, rest'')
+ | head s `elem` closing = Right ([], s)
+ | otherwise = error "unreachable"
+
+isCorrupted expr = case expr of
+ Left (Corrupted c) -> True
+ _ -> False
+
+fromCorrupted expr = case expr of
+ Left (Corrupted c) -> c
+ a -> error $ "not corrupted: " ++ show a
+
+score1 ')' = 3
+score1 ']' = 57
+score1 '}' = 1197
+score1 '>' = 25137
+score1 a = error $ "not valid for scoring: " ++ [a]
+
+e1 rows =
+ let parsed = map parse rows
+ corrupted = parsed $> filter isCorrupted .> map fromCorrupted
+ scores = corrupted $> map score1
+ in sum scores
+
+repair :: [Char] -> [Char] -> Either Failure [Char]
+repair expectStack s
+ | null s = Right expectStack
+ | head s `elem` opening =
+ let (c : cs) = s
+ correspondingClosing = pairs ! c
+ expectStack' = correspondingClosing : expectStack
+ in repair expectStack' cs
+ | head s `elem` closing =
+ do
+ let (c : cs) = s
+ let (ec : ecs) = expectStack
+ if c /= ec
+ then Left (Corrupted c)
+ else repair ecs cs
+ | otherwise = error "unreachable"
+
+score2 ')' = 1
+score2 ']' = 2
+score2 '}' = 3
+score2 '>' = 4
+score2 a = error $ "not valid for scoring: " ++ [a]
+
+fixToScore = foldl' (\acc c -> 5 * acc + score2 c) 0
+
+e2 rows =
+ let parsed = map (repair []) rows
+ fixesM = parsed $> filter (not . isCorrupted)
+ fixes = fixesM $> map (\(Right fix) -> fix)
+ scores = fixes $> map fixToScore
+ scoresSorted = sort scores
+ scoresN = length scoresSorted
+ in scoresSorted !! (scoresN `div` 2)
+
+main :: IO ()
+main =
+ do
+ contents <- getContents
+
+ let input = contents $> lines
+
+ e1 input $> print
+ e2 input $> print