Done day 23 part 1
[advent-of-code-23.git] / advent20 / Main.hs
index 5f772783b30bfd8d40624a8685a99aab146e571a..931c8673e922a0983a992ea81d91167d0bb3605b 100644 (file)
@@ -1,4 +1,4 @@
--- Writeup at https://work.njae.me.uk/2022/12/14/advent-of-code-2022-day-11/
+-- Writeup at https://work.njae.me.uk/2023/12/25/advent-of-code-2023-day-20/
 
 import Debug.Trace
 
@@ -12,12 +12,12 @@ import qualified Data.Map.Strict as M
 import Data.Map ((!))
 import qualified Data.Sequence as Q
 import Data.Sequence ((|>), (><), Seq( (:|>), (:<|) ) ) 
--- import Data.Sequence ((|>), (><)) 
 import Control.Lens hiding (Level)
 import Control.Monad.State.Strict
 import Control.Monad.Reader
 import Control.Monad.Writer
 import Control.Monad.RWS.Strict
+import Data.Function (on)
 
 type Name = String
 
@@ -54,46 +54,41 @@ main =
       text <- TIO.readFile dataFileName
       let config = successfulParse text
       let (network, modules) = assembleNetwork config
-      -- print config
-      -- print $ assembleNetwork config
-      -- let pulse0 = Q.singleton $ Pulse "button" Low "broadcaster"
-      -- let (state1, out1) = 
-      --         execRWS handlePulses
-      --           network 
-      --           (NetworkState modules pulse0)
-      -- print (out1, state1)
-      -- let (state2, out2) = 
-      --         execRWS handlePulses
-      --           network 
-      --           (state1 & queue .~ pulse0)
-      -- print (out2, state2)
-      -- let (state3, out3) = 
-      --         execRWS handlePulses
-      --           network 
-      --           (state2 & queue .~ pulse0)
-      -- print (out3, state3)
-      -- let (state4, out4) = 
-      --         execRWS handlePulses
-      --           network 
-      --           (state3 & queue .~ pulse0)
-      -- print (out4, state4)
       print $ part1 network modules
-      putStrLn $ showDot network
+      print $ part2 network modules
 
 
 
 
-part1 :: Network -> Modules -> Int
-part1 network modules = highs * lows
-  where (highs, lows) = manyButtonPress 1000 (0, 0) network state0
+part1, part2 :: Network -> Modules -> Int
+part1 network modules = highs * lows 
+  where (_, (highs, lows)) = 
+         (!! 1000) $ iterate (pressAndEvaluate network part1Extractor) (state0, (0, 0))
         state0 = NetworkState modules Q.empty
-
-manyButtonPress :: Int -> (Int, Int) -> Network -> NetworkState -> (Int, Int)
-manyButtonPress 0 (highs, lows) _ _ = (highs, lows)
-manyButtonPress n (highs, lows) network state = 
-  manyButtonPress (n - 1) (highs + length hs, lows + length ls) network state'
-  where (state', pulses) = buttonPress network state
-        (hs, ls) = partition ((== High) . _level) pulses
+part2 network modules = foldl' lcm 1 cycleLengths
+  where (_, lxPulses) = 
+         (!! 10000) $ iterate (pressAndEvaluate network part2Extractor) (state0, [(0, [])])
+        state0 = NetworkState modules Q.empty
+        lxHighs = filter (not . null . snd) lxPulses
+        cycleLengths = fmap ((fst . head) . sort) $ 
+                          groupBy ((==) `on` (_source . snd)) $ 
+                          sortBy (compare `on` (\(_, p) -> p ^. source)) $ 
+                          fmap (\(n, ps) -> (n, head ps)) lxHighs 
+
+pressAndEvaluate :: Network -> (a -> [Pulse] -> a) -> (NetworkState, a) -> (NetworkState, a)
+pressAndEvaluate network resultExtractor (state0, result0) = (state1, result1)
+  where (state1, pulses) = buttonPress network state0
+        result1 = resultExtractor result0 pulses
+
+part1Extractor :: (Int, Int) -> [Pulse] -> (Int, Int)
+part1Extractor (highs, lows) pulses = (highs + length hs, lows + length ls)
+  where (hs, ls) = partition ((== High) . _level) pulses
+
+part2Extractor :: [(Int, [Pulse])] -> [Pulse] -> [(Int, [Pulse])]
+part2Extractor allRs@((i, _):rs) pulses = (i + 1, lxPulses) : allRs
+  where lxPulses = filter catchLx pulses
+        catchLx (Pulse _ High "lx") = True
+        catchLx _ = False
 
 buttonPress :: Network -> NetworkState -> (NetworkState, [Pulse])
 buttonPress network state = 
@@ -104,19 +99,12 @@ buttonPress network state =
 handlePulses :: NetworkHandler ()
 handlePulses = 
   do  pulses <- gets _queue
-      if Q.null pulses
-        then return ()
-        else 
-          do  let p :<| _ = pulses
-              handlePulse p
-              modify (\s -> s & queue %~ (Q.drop 1))
-              handlePulses
-      -- case Q.viewl pulses of
-      --   Q.EmptyL -> return ()
-      --   p :< _ -> 
-      --     do  modify (\s -> s & queue %~ (Q.drop 1))
-      --         handlePulse p
-      --         handlePulses
+      case pulses of
+        Q.Empty -> return ()
+        (p :<| ps) -> 
+          do modify (\s -> s & queue .~ ps)
+             handlePulse p
+             handlePulses
 
 handlePulse :: Pulse -> NetworkHandler ()
 handlePulse p@(Pulse _ _ destination) = 
@@ -130,8 +118,6 @@ handlePulse p@(Pulse _ _ destination) =
         Just level' -> 
           do let newPulses = fmap (Pulse destination level') outGoings
              modify (\s -> s & queue %~ (>< (Q.fromList newPulses)))
-            --  tell newPulses
-             return ()
 
 processPulse :: Pulse -> Module -> (Module, Maybe Level)
 -- processPulse p m | trace ((show p) ++ " " ++ (show m) ) False = undefined
@@ -145,7 +131,7 @@ processPulse (Pulse s l _) (Conjunction memory) =
         outLevel = if all (== High) $ M.elems memory' then Low else High
 processPulse _ Untyped = (Untyped, Nothing)
 
-
+-- Assemble the network
 
 assembleNetwork :: [((Module, Name), [Name])] -> (Network, Modules)
 assembleNetwork config = (network, modules)
@@ -153,14 +139,14 @@ assembleNetwork config = (network, modules)
         modules1 = M.union (mkModules config) modules0
         modules = addConjunctionMemory network modules1
 
-mkModules :: [((Module, Name), [Name])] -> Modules
-mkModules = M.fromList . fmap (\((m, n), _) -> (n, m))
-
 mkNetwork :: [((Module, Name), [Name])] -> (Network, Modules)
 mkNetwork config = (net, mods) 
   where net = M.fromList $ fmap (\((_, n), ds) -> (n, ds)) config
         mods = M.fromList $ concatMap (\(_, ds) -> fmap (\d -> (d, Untyped)) ds) config
 
+mkModules :: [((Module, Name), [Name])] -> Modules
+mkModules = M.fromList . fmap (\((m, n), _) -> (n, m))
+
 addConjunctionMemory :: Network -> Modules -> Modules
 addConjunctionMemory network modules = 
   M.foldlWithKey addMemory modules network
@@ -172,17 +158,18 @@ addMemory modules source connections =
 addOneMemory :: Name -> Modules -> Name -> Modules
 addOneMemory source modules destination =
   case modules ! destination of
-    Conjunction memory -> M.insert destination (Conjunction $ M.insert source Low memory) modules
+    Conjunction memory -> 
+      M.insert destination 
+               (Conjunction $ M.insert source Low memory) 
+               modules
     _ -> modules
 
-
 showDot :: Network -> String
 showDot network = 
   "digraph {\n" ++ (concatMap showDotLine $ M.toList network) ++ "\n}"
   where showDotLine (source, destinations) = 
           concatMap (\d -> source ++ " -> " ++ d ++ ";\n") destinations
 
-
 -- Parse the input file
 
 configLinesP :: Parser [((Module, Name), [Name])]