-
Notifications
You must be signed in to change notification settings - Fork 0
/
day20.hs
102 lines (85 loc) · 3.01 KB
/
day20.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{-# LANGUAGE LambdaCase #-}
module Main where
import Control.Monad (msum)
import Data.IntMap qualified as M
import Data.List (partition, (\\))
import Data.List.Split (splitOn)
import Data.Map.Strict (Map, insert, (!?))
import Data.Map.Strict qualified as Map
import Data.Maybe (mapMaybe)
data Module
= Broadcaster [String]
| FlipFlop Bool [String]
| Conjunction (Map String Bool) [String]
deriving (Eq, Show)
data Pulse = Pulse Bool String String deriving (Eq)
type Wires = Map String Module
main = interact (unlines . sequence [part1, part2] . parse)
part1 =
("Part 1: " ++)
. show
. (\(a, b) -> length a * length b)
. partition hi
. concatMap (map fst)
. take 1000
. simulate
part2 = ("Part 2: " ++) . maybe "No example" (show . foldl lcm 1 . map fst) . rxDetector
rxDetector wires = findCycles wires <$> watchlist
where
-- Find out wires providing signals to rx
watchlist = msum [rxInputs mod | mod <- Map.elems wires]
rxInputs = \case
(Conjunction mem ["rx"]) -> return (Map.keys mem)
_ -> Nothing
findCycles wires watchlist =
filter (not . null . snd)
. zip [1 ..]
. allDetected watchlist
$ mapMaybe (detectLow . fst) <$> simulate wires
where
allDetected missing (found : xs)
| null $ missing \\ found = [found]
| otherwise = found : allDetected (missing \\ found) xs
detectLow = \case
(Pulse True from _) | from `elem` watchlist -> return from
_ -> Nothing
simulate wires =
let pulses = run [(pressButton, wires)]
in pulses : simulate (snd $ last pulses)
pressButton = Pulse False "button" "roadcaster"
run :: [(Pulse, Wires)] -> [(Pulse, Wires)]
run = \case
[] -> []
(state@(Pulse phi from key, wires) : next) ->
state : case wires !? key of
Just (Broadcaster targets) ->
run' wires (Pulse phi key <$> targets)
Just (FlipFlop fhi targets)
| not phi ->
run'
(insert key (FlipFlop (not fhi) targets) wires)
(Pulse (not fhi) key <$> targets)
Just (Conjunction mem targets) ->
let mem' = insert from phi mem
in run'
(insert key (Conjunction mem' targets) wires)
(Pulse (not $ and mem') key <$> targets)
input -> run next
where
run' wires pulses = run ((,wires) <$> (fst <$> next) <> pulses)
hi :: Pulse -> Bool
hi (Pulse hi _ _) = hi
parse :: String -> Wires
parse = parseModules . map (parts . splitOn " -> ") . lines
where
parts [t : wire, targets] = (wire, (t, splitOn ", " targets))
parseModules input = Map.fromList $ map modu input
where
connect wire = Map.fromList $ (,False) . fst <$> filter (elem wire . snd . snd) input
modu = \case
(wire, ('%', targets)) -> (wire, FlipFlop False targets)
(wire, ('&', targets)) -> (wire, Conjunction (connect wire) targets)
(wire, ('b', targets)) -> (wire, Broadcaster targets)
-- To match format of the examples
instance Show Pulse where
show (Pulse hi from to) = from ++ " -" ++ (if hi then "high" else "low") ++ "-> " ++ to