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 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)
|
|
|
|
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]]
|
|
|
|
set k v = ix (hash k) %~ update
|
|
|
|
where
|
|
|
|
update :: [Entry] -> [Entry]
|
|
|
|
update es
|
|
|
|
| any ((k ==) . fst) es = map (\(k', v') -> if k == k' then (k, v) else (k', v')) es
|
|
|
|
| otherwise = es <> [(k, v)]
|
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..]
|