2023 Day 20 Complete!

This commit is contained in:
Brian Buller 2023-12-20 09:10:17 -06:00
parent f5c907314d
commit 89bb8f2bb8
5 changed files with 548 additions and 0 deletions

58
2023/day20/input Normal file
View File

@ -0,0 +1,58 @@
%fg -> nt, gt
&zp -> rx
%fh -> nt, xz
%pj -> zj, zq
%jc -> nt, nk
%mr -> vv, pz
%cl -> fp, zq
%xb -> bl, vv
%nc -> zq
%mg -> vn
%zj -> cf
&sb -> zp
%ht -> pp
%gt -> jc
%rq -> ft
&nt -> rq, fg, ft, nd, gt, xz
%ps -> xm
%fs -> ff
%nb -> dv
%qd -> xb
%kg -> mr, vv
%dv -> vv, hr
%rm -> zq, fs
%nk -> rq, nt
%hr -> dm, vv
%xm -> vn, ht
%pp -> mq
%br -> vn, jz
%gr -> ln
%bh -> qd, vv
%cf -> gr, zq
&vv -> dm, bl, sb, nb, qd, bh
%zc -> zv
%zv -> dc, vn
%jz -> qs
&nd -> zp
%rd -> nc, zq
&ds -> zp
%mq -> vn, zc
%bl -> bb
%qn -> nt
%pz -> vv
%qs -> vn, ps
%lm -> nt, qn
%xz -> dr
%bb -> nb, vv
%dm -> kg
%ft -> fh
&vn -> br, jz, ht, ps, zc, pp, ds
%xd -> nt, lm
&hf -> zp
broadcaster -> pj, fg, bh, br
%fp -> rd, zq
&zq -> fs, gr, ff, hf, ln, zj, pj
%ff -> cl
%dr -> xd, nt
%ln -> rm
%dc -> mg, vn

239
2023/day20/main.go Normal file
View File

@ -0,0 +1,239 @@
package main
import (
"fmt"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
part1(inp)
fmt.Println()
part2(inp)
}
func part1(input []string) {
modules := initialize(input)
var low, high int
var i int
for i = 0; i < 1000; i++ {
var pulses []Pulse
pulses = append(pulses, Pulse{
source: "button",
dest: "broadcaster",
value: false,
})
// Process until queue is empty
var pulse Pulse
for len(pulses) > 0 {
pulse, pulses = pulses[0], pulses[1:]
if pulse.value {
high++
} else {
low++
}
pulses = append(pulses, pulse.handle(modules)...)
}
}
fmt.Println("# Part 1")
fmt.Println(low * high)
}
func part2(input []string) {
modules := initialize(input)
// In my input, at least, only one module outputs to rx
var sendsToRx string
for k, v := range modules {
if h.StringSliceContains(v.strConn, "rx") {
sendsToRx = k
break
}
}
// Now find all modules that output to that module
var inputs []string
for k, v := range modules {
if h.StringSliceContains(v.strConn, sendsToRx) {
inputs = append(inputs, k)
}
}
// Now, when does sendsToRx send a low value to rx
// Run until we have a value for each input
vals := make(map[string]int)
for i := 1; len(vals) != len(inputs); i++ {
var pulses []Pulse
pulses = append(pulses, Pulse{
source: "button",
dest: "broadcaster",
value: false,
})
// Process until queue is empty
var pulse Pulse
for len(pulses) > 0 {
pulse, pulses = pulses[0], pulses[1:]
pulses = append(pulses, pulse.handle(modules)...)
// Check each of our inputs
for _, k := range inputs {
if _, ok := vals[k]; !ok && modules[sendsToRx].received[k] {
vals[k] = i
}
}
}
}
var iVals []int
for _, v := range vals {
iVals = append(iVals, v)
}
lcm := h.Lcm(iVals[0], iVals[1], iVals[2:]...)
fmt.Println("# Part 2")
fmt.Println(lcm)
}
func initialize(input []string) map[string]*Module {
modules := make(map[string]*Module)
for i := range input {
m := NewModule(input[i])
modules[m.name] = m
}
modules["button"] = NewModule("button -> broadcaster")
var need []string
for _, v := range modules {
n := v.checkConnections(modules)
for _, nm := range n {
if !h.StringSliceContains(need, nm) {
need = append(need, nm)
}
}
}
for _, nm := range need {
modules[nm] = &Module{
name: nm,
tp: OUTPUT,
conn: make(map[string]*Module),
received: make(map[string]bool),
}
}
for _, v := range modules {
v.initialize(modules)
}
return modules
}
type Pulse struct {
source string
dest string
value bool
}
func (p *Pulse) handle(modules map[string]*Module) []Pulse {
send := false
m := modules[p.dest]
switch m.tp {
case FLIPFLOP:
if p.value {
return nil
} else {
m.val = !m.val
send = m.val
}
case CONJUNCTION:
m.received[p.source] = p.value
send = false
for _, v := range m.received {
if !v {
send = true
break
}
}
case BROADCAST:
send = p.value
}
resp := make([]Pulse, len(m.strConn))
for i, t := range m.strConn {
resp[i] = Pulse{
source: p.dest,
dest: t,
value: send,
}
}
modules[p.dest] = m
return resp
}
var (
BROADCAST = byte(1)
BUTTON = byte(2)
OUTPUT = byte(3)
FLIPFLOP = byte('%')
CONJUNCTION = byte('&')
LOW = false
HIGH = true
// Special modules
NM_BUTTON = "button"
NM_BROADCAST = "broadcaster"
NM_OUTPUT = "output"
)
type Module struct {
name string
tp byte
strConn []string
received map[string]bool
val bool
conn map[string]*Module
shouldSend bool
}
func NewModule(inp string) *Module {
m := Module{
received: make(map[string]bool),
conn: make(map[string]*Module),
}
pts := strings.Fields(inp)
if pts[0] == NM_BUTTON {
m.name = pts[0]
m.tp = BUTTON
m.val = LOW
} else if pts[0] == NM_BROADCAST {
m.name = pts[0]
m.tp = BROADCAST
m.val = LOW
} else if pts[0] == NM_OUTPUT {
m.name = pts[0]
m.tp = OUTPUT
return &m
} else {
m.name = pts[0][1:]
m.tp = pts[0][0]
m.val = LOW
}
for _, c := range pts[2:] {
m.strConn = append(m.strConn, strings.TrimRight(c, ","))
}
return &m
}
func (m *Module) checkConnections(modules map[string]*Module) []string {
var ret []string
for _, nm := range m.strConn {
if _, ok := modules[nm]; !ok {
ret = append(ret, nm)
}
}
return ret
}
func (m *Module) initialize(modules map[string]*Module) {
for _, nm := range m.strConn {
m.conn[nm] = modules[nm]
if m.conn[nm].tp == CONJUNCTION {
m.conn[nm].received[m.name] = false
}
}
}

241
2023/day20/problem Normal file
View File

@ -0,0 +1,241 @@
[1]Advent of Code
• [2][About]
• [3][Events]
• [4][Shop]
• [5][Settings]
• [6][Log Out]
br0xen [7](AoC++) 40*
  {:year [8]2023}
• [9][Calendar]
• [10][AoC++]
• [11][Sponsors]
• [12][Leaderboard]
• [13][Stats]
Our [14]sponsors help make Advent of Code possible:
[15]Boot.dev - Ready to become a backend developer? If you like AoC, you
might be like us. We think smartest way to learn to code is to ensure
youʼre never bored. Try the most captivating, addictive way to learn to
code on Boot.dev.
--- Day 20: Pulse Propagation ---
With your help, the Elves manage to find the right parts and fix all of
the machines. Now, they just need to send the command to boot up the
machines and get the sand flowing again.
The machines are far apart and wired together with long cables. The cables
don't connect to the machines directly, but rather to communication
modules attached to the machines that perform various initialization tasks
and also act as communication relays.
Modules communicate using pulses. Each pulse is either a high pulse or a
low pulse. When a module sends a pulse, it sends that type of pulse to
each module in its list of destination modules.
There are several different types of modules:
Flip-flop modules (prefix %) are either on or off; they are initially off.
If a flip-flop module receives a high pulse, it is ignored and nothing
happens. However, if a flip-flop module receives a low pulse, it flips
between on and off. If it was off, it turns on and sends a high pulse. If
it was on, it turns off and sends a low pulse.
Conjunction modules (prefix &) remember the type of the most recent pulse
received from each of their connected input modules; they initially
default to remembering a low pulse for each input. When a pulse is
received, the conjunction module first updates its memory for that input.
Then, if it remembers high pulses for all inputs, it sends a low pulse;
otherwise, it sends a high pulse.
There is a single broadcast module (named broadcaster). When it receives a
pulse, it sends the same pulse to all of its destination modules.
Here at Desert Machine Headquarters, there is a module with a single
button on it called, aptly, the button module. When you push the button, a
single low pulse is sent directly to the broadcaster module.
After pushing the button, you must wait until all pulses have been
delivered and fully handled before pushing it again. Never push the button
if modules are still processing pulses.
Pulses are always processed in the order they are sent. So, if a pulse is
sent to modules a, b, and c, and then module a processes its pulse and
sends more pulses, the pulses sent to modules b and c would have to be
handled first.
The module configuration (your puzzle input) lists each module. The name
of the module is preceded by a symbol identifying its type, if any. The
name is then followed by an arrow and a list of its destination modules.
For example:
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a
In this module configuration, the broadcaster has three destination
modules named a, b, and c. Each of these modules is a flip-flop module (as
indicated by the % prefix). a outputs to b which outputs to c which
outputs to another module named inv. inv is a conjunction module (as
indicated by the & prefix) which, because it has only one input, acts like
an inverter (it sends the opposite of the pulse type it receives); it
outputs to a.
By pushing the button once, the following pulses are sent:
button -low-> broadcaster
broadcaster -low-> a
broadcaster -low-> b
broadcaster -low-> c
a -high-> b
b -high-> c
c -high-> inv
inv -low-> a
a -low-> b
b -low-> c
c -low-> inv
inv -high-> a
After this sequence, the flip-flop modules all end up off, so pushing the
button again repeats the same sequence.
Here's a more interesting example:
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output
This module configuration includes the broadcaster, two flip-flops (named
a and b), a single-input conjunction module (inv), a multi-input
conjunction module (con), and an untyped module named output (for testing
purposes). The multi-input conjunction module con watches the two
flip-flop modules and, if they're both on, sends a low pulse to the output
module.
Here's what happens if you push the button once:
button -low-> broadcaster
broadcaster -low-> a
a -high-> inv
a -high-> con
inv -low-> b
con -high-> output
b -high-> con
con -low-> output
Both flip-flops turn on and a low pulse is sent to output! However, now
that both flip-flops are on and con remembers a high pulse from each of
its two inputs, pushing the button a second time does something different:
button -low-> broadcaster
broadcaster -low-> a
a -low-> inv
a -low-> con
inv -high-> b
con -high-> output
Flip-flop a turns off! Now, con remembers a low pulse from module a, and
so it sends only a high pulse to output.
Push the button a third time:
button -low-> broadcaster
broadcaster -low-> a
a -high-> inv
a -high-> con
inv -low-> b
con -low-> output
b -low-> con
con -high-> output
This time, flip-flop a turns on, then flip-flop b turns off. However,
before b can turn off, the pulse sent to con is handled first, so it
briefly remembers all high pulses for its inputs and sends a low pulse to
output. After that, flip-flop b turns off, which causes con to update its
state and send a high pulse to output.
Finally, with a on and b off, push the button a fourth time:
button -low-> broadcaster
broadcaster -low-> a
a -low-> inv
a -low-> con
inv -high-> b
con -high-> output
This completes the cycle: a turns off, causing con to remember only low
pulses and restoring all modules to their original states.
To get the cables warmed up, the Elves have pushed the button 1000 times.
How many pulses got sent as a result (including the pulses sent by the
button itself)?
In the first example, the same thing happens every time the button is
pushed: 8 low pulses and 4 high pulses are sent. So, after pushing the
button 1000 times, 8000 low pulses and 4000 high pulses are sent.
Multiplying these together gives 32000000.
In the second example, after pushing the button 1000 times, 4250 low
pulses and 2750 high pulses are sent. Multiplying these together gives
11687500.
Consult your module configuration; determine the number of low pulses and
high pulses that would be sent after pushing the button 1000 times,
waiting for all pulses to be fully handled after each push of the button.
What do you get if you multiply the total number of low pulses sent by the
total number of high pulses sent?
Your puzzle answer was 791120136.
--- Part Two ---
The final machine responsible for moving the sand down to Island Island
has a module attached named rx. The machine turns on when a single low
pulse is sent to rx.
Reset all modules to their default states. Waiting for all pulses to be
fully handled after each button press, what is the fewest number of button
presses required to deliver a single low pulse to the module named rx?
Your puzzle answer was 215252378794009.
Both parts of this puzzle are complete! They provide two gold stars: **
At this point, you should [16]return to your Advent calendar and try
another puzzle.
If you still want to see it, you can [17]get your puzzle input.
You can also [Shareon [18]Twitter [19]Mastodon] this puzzle.
References
Visible links
1. https://adventofcode.com/
2. https://adventofcode.com/2023/about
3. https://adventofcode.com/2023/events
4. https://teespring.com/stores/advent-of-code
5. https://adventofcode.com/2023/settings
6. https://adventofcode.com/2023/auth/logout
7. Advent of Code Supporter
https://adventofcode.com/2023/support
8. https://adventofcode.com/2023
9. https://adventofcode.com/2023
10. https://adventofcode.com/2023/support
11. https://adventofcode.com/2023/sponsors
12. https://adventofcode.com/2023/leaderboard
13. https://adventofcode.com/2023/stats
14. https://adventofcode.com/2023/sponsors
15. https://www.boot.dev/?promo=ADVENTOFCODE
16. https://adventofcode.com/2023
17. https://adventofcode.com/2023/day/20/input
18. https://twitter.com/intent/tweet?text=I%27ve+completed+%22Pulse+Propagation%22+%2D+Day+20+%2D+Advent+of+Code+2023&url=https%3A%2F%2Fadventofcode%2Ecom%2F2023%2Fday%2F20&related=ericwastl&hashtags=AdventOfCode
19. javascript:void(0);

5
2023/day20/testinput1 Normal file
View File

@ -0,0 +1,5 @@
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a

5
2023/day20/testinput2 Normal file
View File

@ -0,0 +1,5 @@
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output