2023 Day 20 Complete!
This commit is contained in:
239
2023/day20/main.go
Normal file
239
2023/day20/main.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user