advent-of-code/2023/haskell/solutions/Day05.hs

96 lines
2.4 KiB
Haskell
Raw Normal View History

2023-12-05 20:31:36 +01:00
{-# LANGUAGE OverloadedStrings #-}
module Day05 (parse, solveA, solveB) where
import Data.Void (Void)
import Data.Text (Text)
import Data.List (sort)
import Data.Maybe (mapMaybe, listToMaybe, fromMaybe)
import Data.Bifunctor (first)
import Control.Monad ((<=<))
import Text.Megaparsec (Parsec, errorBundlePretty, runParser, many, sepBy, some, eof)
import Text.Megaparsec.Char (string, char, space, alphaNumChar)
import Text.Megaparsec.Char.Lexer (decimal)
type Target = Int
type Source = Int
type Range = Int
type Trans = ((Target, Source), Range)
type Recipe = ((String, String), [Trans])
type Input = ([Int], [Recipe])
type Parser = Parsec Void Text
parse :: Text -> Either String Input
parse = first errorBundlePretty . runParser input ""
where
seeds :: Parser [Int]
seeds = string "seeds: " *> decimal `sepBy` char ' ' <* space
trans :: Parser Trans
trans = do
t <- decimal
space
s <- decimal
space
n <- decimal
space
return ((t, s), n)
recipe :: Parser Recipe
recipe = do
f <- some alphaNumChar
_ <- string "-to-"
t <- some alphaNumChar
_ <- string " map:"
space
ms <- many trans
space
return ((f, t), ms)
input :: Parser Input
input = do
ss <- seeds
rs <- many recipe
eof
return (ss, rs)
transform :: Trans -> Int -> Maybe Int
transform ((t, s), r) n
| n >= s && n < s + r = Just $ n - s + t
| otherwise = Nothing
applyRecipe :: Int -> Recipe -> Int
applyRecipe n = fromMaybe n . listToMaybe . mapMaybe (($ n) . transform) . snd
foldRecipes :: Int -> [Recipe] -> Int
foldRecipes = foldl applyRecipe
solveA :: Input -> Int
solveA (ss, rs) = minimum . map (($ rs) . foldRecipes) $ ss
pairs :: [a] -> [(a, a)]
pairs [] = []
pairs (x:y:xs) = (x, y) : pairs xs
pairs _ = undefined
toRange :: (Int, Int) -> (Int, Int)
toRange (a, b) = (a, a + b -1)
unique :: [(Int, Int)] -> [(Int, Int)]
unique = merge . sort
where
merge :: [(Int, Int)] -> [(Int, Int)]
merge [] = []
merge [x] = [x]
merge ((s1, e1) : (s2, e2) : rs)
| e1 >= s2 = merge $ (s1, max e1 e2) : rs
| otherwise = (s1, e1) : merge ((s2, e2) : rs)
toList :: (Int, Int) -> [Int]
toList (a, b) = [a .. b]
solveB :: Input -> Int
solveB (ss, rs) = solveA (expand ss, rs)
where
expand = toList <=< unique . map toRange . pairs