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

74 lines
2 KiB
Haskell
Raw Normal View History

2023-12-15 22:03:03 +01:00
{-# LANGUAGE OverloadedStrings #-}
2023-12-16 11:46:07 +01:00
module Day15 (parse, solveA, solveB) where
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
import Data.Void (Void)
2023-12-15 22:03:03 +01:00
import Data.Text (Text)
import qualified Data.Text as T
import Data.Char (ord, isSpace)
2023-12-16 12:49:02 +01:00
import Data.List (findIndex)
2023-12-16 11:46:07 +01:00
import Data.Bifunctor (bimap)
import Text.Megaparsec (Parsec, errorBundlePretty, runParser, sepBy, optional, eof, many, choice)
import Text.Megaparsec.Char (newline, alphaNumChar, char)
import Text.Megaparsec.Char.Lexer (decimal)
2023-12-16 12:49:02 +01:00
import Lens.Micro (ix, (%~), (.~))
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
data Op
= Set Text Int
| Del Text
deriving Show
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
type Entry = (Text, Int)
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
type Parser = Parsec Void Text
parse :: Text -> Either String [(Text, Op)]
parse text = bimap errorBundlePretty (zip . simple $ text) . runParser complex "" $ text
where
simple :: Text -> [Text]
simple = T.splitOn "," . T.filter (not . isSpace)
single :: Parser Op
single = do
label <- T.pack <$> many alphaNumChar
choice
[ Set label <$> (char '=' *> decimal)
, Del label <$ char '-'
]
complex :: Parser [Op]
complex = single `sepBy` "," <* optional newline <* eof
2023-12-15 22:03:03 +01:00
hash :: Text -> Int
2023-12-16 11:46:07 +01:00
hash = T.foldl step 0
2023-12-15 22:03:03 +01:00
where
step :: Int -> Char -> Int
step curr = (`rem` 256) . (17 *) . (curr +) . ord
2023-12-16 11:46:07 +01:00
solveA :: [(Text, Op)] -> Int
solveA = sum . map (hash . fst)
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
del :: Text -> [[Entry]] -> [[Entry]]
del k = ix (hash k) %~ filter ((k /=) . fst)
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
set :: Text -> Int -> [[Entry]] -> [[Entry]]
2023-12-16 12:49:02 +01:00
set k v = ix (hash k) %~ upsert
2023-12-16 11:46:07 +01:00
where
2023-12-16 12:49:02 +01:00
update :: Int -> [Entry] -> [Entry]
update n = ix n .~ (k, v)
insert :: [Entry] -> [Entry]
insert = (<> [(k, v)])
upsert :: [Entry] -> [Entry]
upsert es = maybe (insert es) (`update` es) . findIndex ((k ==) . fst) $ es
2023-12-15 22:03:03 +01:00
2023-12-16 11:46:07 +01:00
solveB :: [(Text, Op)] -> Int
solveB = sum . zipWith row [1..] . foldl (flip run) (replicate 256 []) . map snd
where
run :: Op -> [[Entry]] -> [[Entry]]
run op = case op of
Set k v -> set k v
Del k -> del k
box :: Int -> Entry -> Int
box i = (i *) . snd
row :: Int -> [Entry] -> Int
row i = (i *) . sum . zipWith box [1..]