1+ {-# LANGUAGE BlockArguments #-}
2+ {-# LANGUAGE OverloadedStrings #-}
3+ {-# LANGUAGE StrictData #-}
4+ {-# LANGUAGE NoImplicitPrelude #-}
5+
16module AdventOfCode.Year2020.Day15
27 ( main ,
38 getInput ,
@@ -6,47 +11,71 @@ module AdventOfCode.Year2020.Day15
611 )
712where
813
9- import Control.Monad.State (State , evalState , execState , get , put )
10- import Data.IntMap (IntMap )
11- import Data.IntMap qualified as IM
14+ import AdventOfCode.Input (parseInputAoC )
15+ import AdventOfCode.Puzzle
16+ import Control.Lens (makeLenses , use , uses , (%=) , (+=) , (.=) , (<~) )
17+ import Control.Monad.Logger (logDebug )
18+ import Data.IntMap qualified as IntMap
19+ import Relude
20+ import Text.Printf (printf )
21+ import Text.Trifecta (commaSep , natural )
22+
23+ data PuzzleState
24+ = PuzzleState
25+ { _numbersToSay :: NonEmpty Int ,
26+ _turn :: Int ,
27+ _spokenNumbers :: IntMap Int
28+ }
29+ deriving (Eq , Generic , Show )
30+
31+ makeLenses ''PuzzleState
32+
33+ mkState :: NonEmpty Int -> PuzzleState
34+ mkState input = PuzzleState input 0 IntMap. empty
1235
1336main :: IO ()
1437main =
15- do
16- input <- getInput
17- putStr " Part One: "
18- print (partOne input)
19- putStr " Part Two: "
20- print (partTwo input)
38+ getInput <&> ( id &&& mkState) >>= \ (input, initialState) ->
39+ evalPuzzle input initialState do
40+ putStr " Part One: "
41+ print =<< partOne
42+ putStr " Part Two: "
43+ print =<< partTwo
2144
22- getInput :: IO [Int ]
23- getInput = pure [0 , 14 , 1 , 3 , 7 , 9 ]
45+ getInput :: IO (NonEmpty Int )
46+ getInput = fromList <$> parseInputAoC 2020 15 (commaSep posInt)
47+ where
48+ posInt = fromInteger <$> natural
2449
25- partOne :: [ Int ] -> Int
50+ partOne :: Puzzle ( NonEmpty Int ) PuzzleState Int
2651partOne = memoryGame 2020
2752
28- partTwo :: [Int ] -> Int
29- partTwo = memoryGame 30000000
30-
31- memoryGame :: Int -> [Int ] -> Int
32- memoryGame n input =
33- evalState memoryRound $
34- (!! pred n) $
35- iterate (execState memoryRound) (input, 1 , IM. empty)
36-
37- memoryRound :: State ([Int ], Int , IntMap Int ) Int
38- memoryRound =
39- do
40- (input, now, seen) <- get
41- case input of
42- [] -> put ([0 ], now, seen) *> memoryRound
43- current : rest ->
44- case IM. lookup current seen of
45- Nothing ->
46- do
47- put (rest, now + 1 , IM. insert current now seen)
48- pure current
49- Just before ->
50- do
51- put (now - before : rest, now + 1 , IM. insert current now seen)
52- pure current
53+ partTwo :: Puzzle (NonEmpty Int ) PuzzleState Int
54+ partTwo = memoryGame 30_000_000
55+
56+ memoryGame :: Int -> Puzzle (NonEmpty Int ) PuzzleState Int
57+ memoryGame n = loop <* (numbersToSay <~ ask)
58+ where
59+ loop = do
60+ turn += 1
61+ number <- withPuzzle (const (max 1 (n `div` 100 ))) memoryRound
62+ uses turn (== n) >>= bool loop (pure number)
63+
64+ memoryRound :: Puzzle Int PuzzleState Int
65+ memoryRound = do
66+ number :| numbers <- use numbersToSay
67+ uses spokenNumbers (IntMap. lookup number) >>= \ case
68+ Just before ->
69+ numbersToSay <~ uses turn (subtract before >>> (:| numbers))
70+ Nothing ->
71+ numbersToSay .= fromMaybe (0 :| [] ) (nonEmpty numbers)
72+ say number
73+
74+ say :: Int -> Puzzle Int PuzzleState Int
75+ say number =
76+ use turn >>= \ i -> do
77+ spokenNumbers %= IntMap. insert number i
78+ m <- ask
79+ when (i `mod` m == 0 || i `div` m >= 99 )
80+ $ $ (logDebug) (fromString (printf " %d: %d" i number))
81+ pure number
0 commit comments