#!/usr/bin/env nix-shell #!nix-shell --pure -i "runghc -- -i../" -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])" import Aoc (readAndParseStdin) import Data.Map (Map, (!)) import qualified Data.Map as Map import Text.Parsec (char, choice, many, many1, noneOf, sepBy, string) import Text.Parsec.String (Parser) main = do input <- readAndParseStdin parser print $ part1 input print $ part2 input part1 :: Map String Module -> Int part1 input = let (hi, lo) = run input in hi * lo where run :: Map String Module -> (Int, Int) run input = go 1000 (mapConfigToState input) where go 0 _ = (0, 0) go i state = let (nextState, hi, lo, _) = handlePulse input Nothing state "broadcaster" False (nextHi, nextLo) = go (i - 1) nextState in (hi + nextHi, lo + nextLo) part2 :: Map String Module -> Int part2 input = let rxInput = head $ findKeysForDst "rx" pInputs = findKeysForDst rxInput in foldl1 lcm $ findMinPressesForKey pInputs where findKeysForDst rx = Map.keys $ Map.filter (elem rx . dst) input findMinPressesForKey [x] = [scan x (mapConfigToState input) 2] findMinPressesForKey (x : xs) = scan x (mapConfigToState input) 1 : findMinPressesForKey xs scan x state i = let (nextState, _, _, r) = handlePulse input (Just x) state "broadcaster" False in if r then i else scan x nextState (i + 1) mapConfigToState :: Map String Module -> Map String State mapConfigToState input = Map.mapWithKey (\k v -> doMap k (kind v)) input where doMap _ KindBroadcaster = Broadcaster doMap _ KindFlipFlop = FlipFlop False doMap k KindConjunction = Conjunction $ Map.fromList $ map (,False) $ Map.keys $ Map.filter (\v -> k `elem` dst v) input handlePulse :: Map String Module -> Maybe String -> Map String State -> String -> Bool -> (Map String State, Int, Int, Bool) handlePulse config exitOnLow = go "" 0 1 False where go src hiCnt loCnt hitCond state component pulse = let c = config ! component nHitCond = hitCond || (Just component == exitOnLow && not pulse) publishF pulse (state, hiCnt, loCnt, hitCond) output = let nHiCnt = if pulse then hiCnt + 1 else hiCnt nLoCnt = if pulse then loCnt else loCnt + 1 in go component nHiCnt nLoCnt hitCond state output pulse publish s pulse = foldl (publishF pulse) (s, hiCnt, loCnt, nHitCond) (dst c) in case Map.lookup component state of Just Broadcaster -> publish state pulse Just (FlipFlop cs) -> if pulse then (state, hiCnt, loCnt, nHitCond) else publish (Map.insert component (FlipFlop (not cs)) state) (not cs) Just (Conjunction cs) -> let newCs = Map.insert src pulse cs newState = Map.insert component (Conjunction newCs) state in publish newState (not $ and $ Map.elems newCs) Nothing -> (state, hiCnt, loCnt, nHitCond) data State = Broadcaster | FlipFlop Bool | Conjunction (Map String Bool) deriving (Show) parser :: Parser (Map String Module) parser = Map.fromList <$> parseLine `sepBy` char '\n' where parseLine = do kind <- parseKind name <- many (noneOf " ") _ <- string " -> " dst <- many1 (noneOf ",\n") `sepBy` string ", " return (if kind == KindBroadcaster then "broadcaster" else name, Module {kind, dst}) parseKind = choice [ KindBroadcaster <$ string "broadcaster", KindFlipFlop <$ char '%', KindConjunction <$ char '&' ] data Kind = KindBroadcaster | KindFlipFlop | KindConjunction deriving (Enum, Show, Eq) data Module = Module { kind :: Kind, dst :: [String] } deriving (Show)