Advent-of-Code-2023/day20/pulsePropagation.go

241 lines
6.1 KiB
Go

package day20
import (
"fmt"
"log"
"os"
"strings"
)
func Run() int {
// fmt.Println("hello from dya 20")
filename := "day20/input"
// modules := ReadModules(filename)
// InitStuffs(modules)
// log.Print("got modules:\n", modules)
// var low, high int
// low, high = Count10000ButtonPresses(modules)
// log.Printf("got low %d and high %d\n", low, high)
CheckSubgraphsStuff(filename)
var result int
// result = CalcCommonStep()
return result
}
func CheckSubgraphsStuff(filename string) {
// loopStarts := allModules["broadcast"].Outputs()
// loop start and loop sink
loopItems := map[string]string {
"sr": "xn",
"ch": "xf",
"hd": "qn",
"bx": "zl",
}
for start, end := range loopItems {
allModules := ReadModules(filename)
InitStuffs(allModules)
log.Printf(">>> searching for loop of %s", start)
themap := make(map[string]any)
loopModules := TransitiveOutputs(start, allModules, themap)
// i think my bug is not to reset state of `allModules`
_, _, requestedPulses := FindSubGraphLoopLength(loopModules, allModules, end)
FilterMonitoredPulses(requestedPulses)
log.Printf("the pulses: +%v", requestedPulses)
}
// yeah. and now all cycles start from 1 (first button press)
// and then they emit the [high low] on last step of their cycle
// so just LCM of these all
}
func Count10000ButtonPresses(modules map[string]Module) (lowSignalsCount, highSignalsCount int) {
count := 1000
type counts struct {
low, high int
step int
}
countsAfterState := make(map[string]counts)
// after each button press check if reached already known state - cycle is present.
// then calculate amount of signals before the loop - how much was on that previous state.
// then diff - how much added after the loop
// for now let's just print the info on loop
for i := 0; i < count; i++ {
if i % 10000 == 0 {
log.Println("done button presses: ", i)
}
stepLow, stepHigh := PropagateButtonPress(modules, i)
lowSignalsCount += stepLow
highSignalsCount += stepHigh
// log.Printf("after step %d low is %d and high is %d", i, lowSignalsCount, highSignalsCount)
state := ModulesState(modules)
prevCounts, found := countsAfterState[state]
if found {
loopLen := i - prevCounts.step
log.Printf(">>> found loop. from step %d to step %d. of len %d",
prevCounts.step, i, loopLen)
multiplication := count / loopLen
lowCountInCycle := lowSignalsCount - prevCounts.low
highCountInCycle := highSignalsCount - prevCounts.high
lowSignalsCount = lowCountInCycle * multiplication
highSignalsCount = highCountInCycle * multiplication
return
}
countsAfterState[state] = counts{stepLow, stepHigh, i}
}
return
}
func PropagateButtonPress(modules map[string]Module, i int) (lowSignalsCount, highSignalsCount int) {
signals := []Signal{{From: "button", To: "broadcast", PulseType: LowPulse}}
lowSignalsCount += 1
for len(signals) > 0 {
curSignal := signals[0]
signals = signals[1:]
// log.Printf("%s -%s-> %s", curSignal.From, curSignal.PulseType, curSignal.To)
receivingModule, found := modules[curSignal.To]
if !found {
// log.Print(fmt.Sprintf("signal %+v can't find it's recepient\n", curSignal))
if curSignal.To == "rx" && curSignal.PulseType == LowPulse {
panic(fmt.Sprintf("getting low signal to rx, on step %d", i))
}
continue
}
newSignals := receivingModule.Receive(curSignal)
// all newSignals will have same type
newSignalsAmount := len(newSignals)
if newSignalsAmount > 0 {
signals = append(signals, newSignals...)
someNewSignal := newSignals[0]
if someNewSignal.PulseType == HighPulse {
highSignalsCount += newSignalsAmount
} else {
lowSignalsCount += newSignalsAmount
}
}
}
return
}
func PropagateButtonPressWithMonitor(modules map[string]Module, i int, monitorAllOutputsOf string) []PulseType {
result := make([]PulseType, 0)
signals := []Signal{{From: "button", To: "broadcast", PulseType: LowPulse}}
for len(signals) > 0 {
curSignal := signals[0]
signals = signals[1:]
if curSignal.From == monitorAllOutputsOf {
result = append(result, curSignal.PulseType)
}
// log.Printf("%s -%s-> %s", curSignal.From, curSignal.PulseType, curSignal.To)
receivingModule, found := modules[curSignal.To]
if !found {
// log.Print(fmt.Sprintf("signal %+v can't find it's recepient\n", curSignal))
if curSignal.To == "rx" && curSignal.PulseType == LowPulse {
panic(fmt.Sprintf("getting low signal to rx, on step %d", i))
}
continue
}
newSignals := receivingModule.Receive(curSignal)
// all newSignals will have same type
newSignalsAmount := len(newSignals)
if newSignalsAmount > 0 {
signals = append(signals, newSignals...)
}
}
return result
}
// process sends single `low pulse` directly to "broadcast"
func ReadModules(filename string) map[string]Module {
result := make(map[string]Module)
bytes, err := os.ReadFile(filename)
if err != nil {
panic(fmt.Sprint("error reading file: ", filename))
}
text := strings.TrimSpace(string(bytes))
for _, line := range strings.Split(text, "\n") {
switch {
case IsLineBroadcast(line):
parsed := ParseBroadcast(line)
result["broadcast"] = &parsed
case IsLineFlipFlop(line):
parsed := ParseFlipFlop(line)
result[parsed.Name] = &parsed
case IsLineConjunction(line):
parsed := ParseConjunction(line)
result[parsed.Name] = &parsed
}
// log.Println(line)
}
buttonModule := Button{}
result["button"] = &buttonModule
outputModule := Output{}
result["output"] = &outputModule
return result
}
func InitStuffs(allModules map[string]Module) {
for _, module := range allModules {
if conjunction, ok := module.(*Conjunction); ok {
conjunction.RegisterInputs(allModules)
}
}
}
func ModulesState(modules map[string]Module) string {
// relying on printing of map values to be ordered by key
// https://stackoverflow.com/a/54524991/2788805
states := make(map[string]string)
for name, module := range modules {
states[name] = module.StateSnapshot()
}
return fmt.Sprint(states)
}
func AllMermaidFlowChard(allModules map[string]Module) (result string) {
result = "flowchart TD\n"
for _, module := range allModules {
result += module.MermaidFlow()
}
return
}