Day3.hs (3446B)
1 module Day3 (solution) where 2 3 import Text.Regex.Base.RegexLike (getAllTextMatches) 4 import Text.Regex.TDFA ((=~)) 5 import Data.List (foldl') 6 import Data.Functor ((<&>)) 7 8 import Utils (SolType(..), replace, safeRead) 9 10 type Enabled = Bool 11 12 input :: IO String 13 input = readFile "inputs/day3" 14 15 exampleInput1 :: String 16 exampleInput1 = "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" 17 18 exampleInput2 :: String 19 exampleInput2 = "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))" 20 21 -- | Extract just the uncorrupted calls from part 1 22 -- Example: 23 -- >>> filter1 exampleInput1 24 -- ["mul(2,4)","mul(5,5)","mul(11,8)","mul(8,5)"] 25 filter1 :: String -> [String] 26 filter1 str = getAllTextMatches $ str =~ "mul\\([0-9]+,[0-9]+\\)" 27 28 -- | Parse the filtered instructions from part 1 into pairs of numbers 29 -- Example: 30 -- >>> parse1 ["mul(2,4)","mul(5,5)","mul(11,8)","mul(8,5)"] 31 -- Just [(2,4),(5,5),(11,8),(8,5)] 32 parse1 :: [String] -> Maybe [(Integer, Integer)] 33 parse1 strs = sequence $ map helper strs 34 where 35 helper :: String -> Maybe (Integer, Integer) 36 helper str = do 37 ns <- sequence $ map safeRead $ words $ replace ',' ' ' 38 $ filter (\x -> (x >= '0' && x <= '9') || x == ',') str 39 case ns of 40 (x1:x2:_) -> Just (x1,x2) 41 _ -> Nothing 42 43 -- | Solution to day 3 part 1 44 -- Example: 45 -- >>> solution1 exampleInput1 46 -- Just 161 47 solution1 :: String -> Maybe Integer 48 solution1 str = do 49 operands <- parse1 $ filter1 str 50 let results = map (\(x,y) -> x * y) operands 51 return $ foldl' (+) 0 results 52 53 -- | Extract just the uncorrupted calls from part 2 54 -- Example: 55 -- >>> filter2 exampleInput2 56 -- ["mul(2,4)","don't()","mul(5,5)","mul(11,8)","do()","mul(8,5)"] 57 filter2 :: String -> [String] 58 filter2 str = getAllTextMatches $ str =~ "mul\\([0-9]+,[0-9]+\\)|do\\(\\)|don't\\(\\)" 59 60 -- | Parse the filtered instructions from part 2 into executable operations 61 -- Example: 62 -- >>> parse2 ["mul(2,4)","don't()","mul(5,5)","mul(11,8)","do()","mul(8,5)"] 63 -- Just [Left (2,4),Right False,Left (5,5),Left (11,8),Right True,Left (8,5)] 64 parse2 :: [String] -> Maybe [Either (Integer, Integer) Enabled] 65 parse2 strs = sequence $ map helper strs 66 where 67 helper :: String -> Maybe (Either (Integer, Integer) Enabled) 68 helper str = do 69 case str of 70 ('d':'o':'n':'\'':'t':_) -> Just (Right False) 71 ('d':'o':_) -> Just (Right True) 72 _ -> 73 case (sequence $ map (safeRead @Integer) $ words $ replace ',' ' ' 74 $ filter (\x -> (x >= '0' && x <= '9') || x == ',') str) of 75 Just (x1:x2:_) -> Just (Left (x1, x2)) 76 _ -> Nothing 77 78 -- | Execute the parsed instructions for part 2, the solution to day 3 part 2 79 -- Example: 80 -- >>> solution2 exampleInput2 81 -- Just 48 82 solution2 :: String -> Maybe Integer 83 solution2 str = do 84 instructions <- parse2 $ filter2 str 85 return $ compute True instructions 86 where 87 compute :: Enabled -> [Either (Integer, Integer) Enabled] -> Integer 88 compute _ (Right b : is) = compute b is 89 compute False (_:is) = compute False is 90 compute True (Left (a, b) : is) = (a * b) + compute True is 91 compute _ [] = 0 92 93 solution :: IO (SolType, SolType) 94 solution = do 95 probStr <- input 96 let sol1 = (solution1 probStr) <&> IntSol 97 let sol2 = (solution2 probStr) <&> IntSol 98 return (MaybeSol sol1, MaybeSol sol2)