1 -- Writeup at https://work.njae.me.uk/2023/12/04/advent-of-code-2023-day-04/
4 import Data.Text (Text)
5 import qualified Data.Text.IO as TIO
6 import Data.Attoparsec.Text hiding (take)
7 -- import Control.Applicative
10 data Card = Card { cardID :: Int
15 data QueuedCard = QueuedCard { numMatches :: Int
16 , queuedQuantity :: Int
18 type Queue = [QueuedCard]
23 do dataFileName <- getDataFileName
24 text <- TIO.readFile dataFileName
25 let cards = successfulParse text
31 part1, part2 :: [Card] -> Int
32 part1 = sum . fmap score
34 part2 = winCards 0 . mkQueue
39 | otherwise = 2 ^ (matches - 1)
40 where matches = length $ intersect winners actuals
42 mkQueue :: [Card] -> Queue
43 mkQueue = fmap enqueue
44 where enqueue Card{..} = QueuedCard (length $ intersect winners actuals) 1
46 duplicateCards :: Int -> Int -> Queue -> Queue
47 duplicateCards n scale queue = duplicatedPrefix ++ (drop n queue)
48 where duplicatedPrefix = fmap go $ take n queue
49 go (QueuedCard w q) = QueuedCard w (q + scale)
51 winCards :: Int -> Queue -> Int
53 winCards n (QueuedCard{..}:queue) = winCards n' queue'
54 where n' = n + queuedQuantity
55 queue' = duplicateCards numMatches queuedQuantity queue
58 -- Parse the input file
60 cardsP :: Parser [Card]
62 numbersP :: Parser [Int]
64 cardsP = cardP `sepBy` endOfLine
65 cardP = Card <$> (("Card" *> skipSpace *> decimal) <* ":" <* skipSpace)
66 <*> (numbersP <* " |" <* skipSpace)
69 numbersP = decimal `sepBy` skipSpace
71 successfulParse :: Text -> [Card]
72 successfulParse input =
73 case parseOnly cardsP input of
74 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
75 Right matches -> matches