156 lines
3.8 KiB
Go
156 lines
3.8 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)
|
|
|
|
low, high := Count10000ButtonPresses(modules)
|
|
log.Printf("got low %d and high %d\n", low, high)
|
|
|
|
return low * high
|
|
}
|
|
|
|
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++ {
|
|
stepLow, stepHigh := PropagateButtonPress(modules)
|
|
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) (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))
|
|
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
|
|
}
|
|
|
|
// 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(allModules 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 allModules {
|
|
states[name] = module.StateSnapshot()
|
|
}
|
|
|
|
return fmt.Sprint(states)
|
|
}
|