aoc2024

My solutions to the 2024 Advent of Code puzzles
git clone git://git.ethandl.dev/aoc2024
Log | Files | Refs | LICENSE

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)