From 89bb8f2bb8e7959c9545f2742ee427b495285d10 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Wed, 20 Dec 2023 09:10:17 -0600 Subject: [PATCH] 2023 Day 20 Complete! --- 2023/day20/input | 58 ++++++++++ 2023/day20/main.go | 239 +++++++++++++++++++++++++++++++++++++++++ 2023/day20/problem | 241 ++++++++++++++++++++++++++++++++++++++++++ 2023/day20/testinput1 | 5 + 2023/day20/testinput2 | 5 + 5 files changed, 548 insertions(+) create mode 100644 2023/day20/input create mode 100644 2023/day20/main.go create mode 100644 2023/day20/problem create mode 100644 2023/day20/testinput1 create mode 100644 2023/day20/testinput2 diff --git a/2023/day20/input b/2023/day20/input new file mode 100644 index 0000000..410063f --- /dev/null +++ b/2023/day20/input @@ -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 diff --git a/2023/day20/main.go b/2023/day20/main.go new file mode 100644 index 0000000..8dabf5c --- /dev/null +++ b/2023/day20/main.go @@ -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 + } + } +} diff --git a/2023/day20/problem b/2023/day20/problem new file mode 100644 index 0000000..470959e --- /dev/null +++ b/2023/day20/problem @@ -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); diff --git a/2023/day20/testinput1 b/2023/day20/testinput1 new file mode 100644 index 0000000..2dc1bab --- /dev/null +++ b/2023/day20/testinput1 @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a diff --git a/2023/day20/testinput2 b/2023/day20/testinput2 new file mode 100644 index 0000000..2738ceb --- /dev/null +++ b/2023/day20/testinput2 @@ -0,0 +1,5 @@ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output