never executed always true always false
1 {-|
2 Module: Y2021.D10
3 Description: Advent of Code 2021 Day 10 Solutions.
4 License: MIT
5 Maintainer: @tylerjl
6
7 Solutions to the 2021 day 10 set of problems for <adventofcode.com>.
8 -}
9 module Y2021.D10
10 ( part10A
11 , part10B
12 , parse10
13 ) where
14
15 import Control.Monad (foldM)
16 import Data.Either.Extra (lefts, rights)
17 import Data.Foldable (foldl')
18 import Data.List (sort)
19 import Data.Text (Text)
20 import Witch
21
22 import qualified Data.Text as T
23
24 -- |Solve part A
25 part10A :: Text -> Int
26 part10A = sum . map errorTable . lefts . map (parse10 . into @String) . T.lines
27
28 -- |Character lookup table for part A
29 errorTable :: Char -> Int
30 errorTable c | c == '(' || c == ')' = 3
31 errorTable c | c == '[' || c == ']' = 57
32 errorTable c | c == '{' || c == '}' = 1197
33 errorTable c | c == '<' || c == '>' = 25137
34 errorTable _ = 0
35
36 -- |Solve part B
37 part10B :: Text -> Int
38 part10B
39 -- Then convert them to scores, custom score, and get the median
40 = median . sort . map (foldl' score 0 . fmap syntaxPoints)
41 -- Get all valid unterminated syntaxes
42 . rights . map (parse10 . into @String) . T.lines
43
44 -- |Manual parser for a given input string. I tried a `Seq` intially, just a
45 -- stock `List`s as a stack works just as well.
46 parse10 :: [Char] -> Either Char [Char]
47 parse10 = fmap (map pair') . foldM go []
48 where
49 go [] c
50 | opener c = pure [c]
51 | otherwise = Left c
52 go acc c | opener c = pure (c:acc)
53 go ('(':acc) ')' = pure acc
54 go ('{':acc) '}' = pure acc
55 go ('[':acc) ']' = pure acc
56 go ('<':acc) '>' = pure acc
57 go _ c = Left c
58 opener c = c == '(' || c == '[' || c == '{' || c == '<'
59
60 -- |Utility to lookup the pairwise values for a expression opener/closer.
61 pair' :: Char -> Char
62 pair' '(' = ')'
63 pair' ')' = '('
64 pair' '{' = '}'
65 pair' '}' = '{'
66 pair' ']' = '['
67 pair' '[' = ']'
68 pair' '<' = '>'
69 pair' '>' = '<'
70 pair' _ = error "invalid pair"
71
72 -- |Silly little median function
73 median :: [a] -> a
74 median [] = error "empty list"
75 median [x] = x
76 median [x,_] = x
77 median xs = median $ init $ tail xs
78
79 -- |Expresses the scoring algorithm for part B, suitable for use in a fold
80 score :: Num a => a -> a -> a
81 score total n = (total * 5) + n
82
83 -- |Maps the characters for part B into their given values.
84 syntaxPoints :: Num p => Char -> p
85 syntaxPoints ')' = 1
86 syntaxPoints ']' = 2
87 syntaxPoints '}' = 3
88 syntaxPoints '>' = 4
89 syntaxPoints _ = 0