Advent of Code solutions
1module io at "trilogy:io" use readlines, println
2module str at "trilogy:str" use chomp, split, fmt
3module array at "trilogy:array" use is_empty, push, all, fold
4module num at "trilogy:num" use lcm
5module iter at "trilogy:iter" use range
6module tuple at "trilogy:tuple" use mapsnd
7module record at "trilogy:record" use contains
8
9module broadcaster destinations {
10 proc add_input!(_) {}
11
12 proc init!() {
13 for dest in destinations {
14 yield 'init(dest)
15 }
16 }
17
18 proc signal!(_) {
19 for dest in destinations {
20 yield 'low(dest)
21 }
22 }
23
24 export add_input, init, signal
25}
26
27module flipflop destinations {
28 slot state = {| 'on => false |}
29
30 proc add_input!(_) {}
31
32 proc init!() {
33 for dest in destinations {
34 yield 'init(dest)
35 }
36 }
37
38 proc signal!(signal) {
39 match signal
40 case 'low(_) {
41 if state.'on {
42 state.'on = false
43 for dest in destinations {
44 yield 'low(dest)
45 }
46 } else {
47 state.'on = true
48 for dest in destinations {
49 yield 'high(dest)
50 }
51 }
52 }
53 case 'high {}
54 }
55
56 export add_input, init, signal
57}
58
59module conjunction destinations {
60 slot state = {||}
61
62 proc add_input!(input) {
63 state.input = 'low
64 }
65
66 proc init!() {
67 for dest in destinations {
68 yield 'init(dest)
69 }
70 }
71
72 proc signal!(signal) {
73 match signal
74 case 'low(input) {
75 state.input = 'low
76 }
77 case 'high(input) {
78 state.input = 'high
79 }
80 for _:value in state {
81 if value == 'low {
82 for dest in destinations {
83 yield 'high(dest)
84 }
85 return
86 }
87 }
88 for dest in destinations {
89 yield 'low(dest)
90 }
91 }
92
93 export add_input, init, signal
94}
95
96slot important = ["mp", "qt", "qb", "ng"]
97
98func parse_node srcdest = let [name, dests] = split " -> " srcdest, name : split ", " dests
99
100func parse "%" <> srcdest = parse_node srcdest |> (fn n:x.n:flipflop x)
101func parse "&" <> srcdest = parse_node srcdest |> (fn n:x.n:conjunction x)
102func parse "broadcaster -> " <> dests = "broadcaster" : broadcaster (split ", " dests)
103
104proc main!() {
105 let nodes = {| |}
106 for line in readlines!() {
107 let name:node = parse <| chomp line
108 nodes.name = node
109 }
110
111 for name:node in nodes {
112 with { node::init!() }
113 when 'init(dest) resume (with (nodes.dest)::add_input!(name) else cancel unit)
114 else yield
115 }
116
117 let keys = {| |}
118
119 let mut presses = 0
120 while !(all (fn x. contains x keys) important) {
121 presses += 1
122 let mut queue = ["button":'low("broadcaster")]
123 while !(is_empty queue) {
124 let [src:msg, ..rest] = queue
125 let 'low(dest) or 'high(dest) = msg
126 queue = rest
127 with {
128 match msg
129 case 'low(_) {
130 (nodes.dest)::signal!('low(src))
131 }
132 case 'high(_) {
133 if is ^src in important {
134 keys.src = presses
135 }
136 (nodes.dest)::signal!('high(src))
137 }
138 } when 'low(tgt) invert {
139 push!(queue, dest:'low(tgt))
140 cancel resume unit
141 } when 'high(tgt) invert {
142 push!(queue, dest:'high(tgt))
143 cancel resume unit
144 } when 'MIA cancel unit
145 else yield
146 }
147 }
148
149 println!(fold lcm 1 [val for _:val in keys])
150}