advent-of-code-2023/haskell/src/Day1.hs

70 lines
3.4 KiB
Haskell

module Day1 where
import Data.Char
import Data.Foldable (find)
run :: IO ()
run = do
calibrationLines <- parseInput
part1 calibrationLines
part2 calibrationLines
part1 :: [String] -> IO ()
part1 calibrationLines =
printAnswer 1 (answer id id calibrationLines)
part2 :: [String] -> IO ()
part2 calibrationLines =
printAnswer 2 (answer detectDigits detectReverseDigits calibrationLines)
printAnswer :: Int -> Either String Int -> IO ()
printAnswer part (Left err) = putStrLn $ "Part " <> show part <> " Error: " <> err
printAnswer part (Right ans) = putStrLn $ "Part " <> show part <> " Answer: " <> show ans
answer :: (String -> String) -> (String -> String) -> [String] -> Either String Int
answer preprocessorFirst preprocessorLast calibrationLines =
sum <$> mapM (calibrationValue preprocessorFirst preprocessorLast) calibrationLines
calibrationValue :: (String -> String) -> (String -> String) -> String -> Either String Int
calibrationValue preprocessorFirst preprocessorLast line =
let mFirstDigit = find isDigit $ preprocessorFirst line
mLastDigit = find isDigit $ preprocessorLast $ reverse line
in maybe (Left $ "No numbers in this line: " <> line) Right $
twoDigitNumber <$> mFirstDigit <*> mLastDigit
{- ORMOLU_DISABLE -}
detectDigits :: String -> String
detectDigits [] = []
detectDigits ('z' : 'e' : 'r' : 'o' : rest) = '0' : detectDigits rest
detectDigits ('o' : 'n' : 'e' : rest) = '1' : detectDigits rest
detectDigits ('t' : 'w' : 'o' : rest) = '2' : detectDigits rest
detectDigits ('t' : 'h' : 'r' : 'e' : 'e' : rest) = '3' : detectDigits rest
detectDigits ('f' : 'o' : 'u' : 'r' : rest) = '4' : detectDigits rest
detectDigits ('f' : 'i' : 'v' : 'e' : rest) = '5' : detectDigits rest
detectDigits ('s' : 'i' : 'x' : rest) = '6' : detectDigits rest
detectDigits ('s' : 'e' : 'v' : 'e' : 'n' : rest) = '7' : detectDigits rest
detectDigits ('e' : 'i' : 'g' : 'h' : 't' : rest) = '8' : detectDigits rest
detectDigits ('n' : 'i' : 'n' : 'e' : rest) = '9' : detectDigits rest
detectDigits (x : rest) = x : detectDigits rest
detectReverseDigits :: String -> String
detectReverseDigits [] = []
detectReverseDigits ('0' : 'r' : 'e' : 'z' : rest) = '0' : detectReverseDigits rest
detectReverseDigits ('e' : 'n' : 'o' : rest) = '1' : detectReverseDigits rest
detectReverseDigits ('o' : 'w' : 't' : rest) = '2' : detectReverseDigits rest
detectReverseDigits ('e' : 'e' : 'r' : 'h' : 't' : rest) = '3' : detectReverseDigits rest
detectReverseDigits ('r' : 'u' : 'o' : 'f' : rest) = '4' : detectReverseDigits rest
detectReverseDigits ('e' : 'v' : 'i' : 'f' : rest) = '5' : detectReverseDigits rest
detectReverseDigits ('x' : 'i' : 's' : rest) = '6' : detectReverseDigits rest
detectReverseDigits ('n' : 'e' : 'v' : 'e' : 's' : rest) = '7' : detectReverseDigits rest
detectReverseDigits ('t' : 'h' : 'g' : 'i' : 'e' : rest) = '8' : detectReverseDigits rest
detectReverseDigits ('e' : 'n' : 'i' : 'n' : rest) = '9' : detectReverseDigits rest
detectReverseDigits (x : rest) = x : detectReverseDigits rest
{- ORMOLU_ENABLE -}
twoDigitNumber :: Char -> Char -> Int
twoDigitNumber firstDigit lastDigit = 10 * digitToInt firstDigit + digitToInt lastDigit
parseInput :: IO [String]
parseInput = lines <$> getContents