package day20 import ( "fmt" "log" "os" "strings" ) func Run() int { fmt.Println("hello from dya 20") filename := "day20/example2" 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 := 10000 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 { panic(fmt.Sprintf("signal %+v can't find it's recepient\n", curSignal)) } 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) }