import Data.Char (isDigit, digitToInt) import Data.Function (on) import Data.List (tails, findIndex, isPrefixOf, minimumBy) import Data.Maybe (mapMaybe) import Data.Bifunctor (first, bimap) type LineOp = String -> Int wordMap :: [(String, Int)] wordMap = [ ("one", 1) , ("two", 2) , ("three", 3) , ("four", 4) , ("five", 5) , ("six", 6) , ("seven", 7) , ("eight", 8) , ("nine", 9) ] fullWordMap :: [(String, Int)] fullWordMap = wordMap ++ (zip =<< map show) [1..9] opLineBasic :: LineOp opLineBasic l = (combineDigits `on` ($ digitToInt <$> filter isDigit l)) head last firstNumInLine :: String -> [(String, Int)] -> Int firstNumInLine l = fst . minimumBy (compare `on` snd) . mapMaybe numIndex where numIndex (str, num) = (num,) <$> findIndex (isPrefixOf str) (tails l) opLineAdvanced :: LineOp opLineAdvanced l = (combineDigits `on` uncurry firstNumInLine) <*> bimap reverse (map $ first reverse) $ (l, fullWordMap) combineDigits :: Int -> Int -> Int combineDigits = (+) . (10 *) opLines :: [String] -> Int opLines = sum . map opLineAdvanced main :: IO () main = do content <- readFile "data.txt" print . opLines . lines $ content