package day20 import ( "cmp" "log" "slices" ) func TransitiveOutputs(from string, allModules map[string]Module, visited map[string]any) map[string]any { // log.Printf("looking for transitive children of %s\n", from) _, alreadyProcessed := visited[from] if alreadyProcessed { return visited } module, found := allModules[from] if !found { return visited } visited[from] = struct{}{} children := module.Outputs() for _, output := range children { TransitiveOutputs(output, allModules, visited) } delete(visited, "th") // for key, _ := range visited { // result = append(result, key) // } return visited } func FindSubGraphLoopLength(subgraph map[string]any, allModules map[string]Module, monitorOutputsOf string) (fromStep, toStep int, monitoredPulses map[int][]PulseType) { step := 1 seenSubgraphStates := make(map[string]int) monitoredPulses = make(map[int][]PulseType) for { monitoredPulsesOfTheStep := PropagateButtonPressWithMonitor(allModules, step, monitorOutputsOf) subgraphModules := make(map[string]Module) for key, _ := range subgraph { subgraphModules[key] = allModules[key] } subgraphState := ModulesState(subgraphModules) // log.Printf("looping %d. state is %s", step, subgraphState) prevSteps, known := seenSubgraphStates[subgraphState] if known { // log.Printf(">>> searching for loop of %+v", subgraph) log.Printf(">>> found loop from %d to %d. of size %d\n", prevSteps, step - 1, step - prevSteps) return prevSteps, step, monitoredPulses } seenSubgraphStates[subgraphState] = step if len(monitoredPulsesOfTheStep) > 0 { monitoredPulses[step] = monitoredPulsesOfTheStep } step++ } panic("") } // i see lot's of 'LowPulse' // while i want to find steps where all inputs are remembered as High. // so i'm interested in steps with "high" pulses and next steps that make it 'low' after func FilterMonitoredPulses(requestedPulses map[int][]PulseType) { afterHigh := false for step, pulses := range requestedPulses { processedPulses := make([]PulseType, 0) for _, pulse := range pulses { if pulse == HighPulse { processedPulses = append(processedPulses, pulse) afterHigh = true continue } if afterHigh { processedPulses = append(processedPulses, pulse) afterHigh = false } } if len(processedPulses) > 0 { requestedPulses[step] = processedPulses } else { delete(requestedPulses, step) } } } // loop math // 2023/12/20 12:35:08 >>> searching for loop of sr // 2023/12/20 12:35:08 >>> found loop from 1 to 4028. of size 4028 // 2023/12/20 12:35:08 the pulses: +map[4026:[high low]] // 2023/12/20 12:35:08 >>> searching for loop of ch // 2023/12/20 12:35:08 >>> found loop from 0 to 3923. of size 3924 // 2023/12/20 12:35:08 the pulses: +map[3817:[high low]] // 2023/12/20 12:35:08 >>> searching for loop of hd // 2023/12/20 12:35:09 >>> found loop from 0 to 3793. of size 3794 // 2023/12/20 12:35:09 the pulses: +map[3427:[high low]] // 2023/12/20 12:35:09 >>> searching for loop of bx // 2023/12/20 12:35:09 >>> found loop from 0 to 3739. of size 3740 // 2023/12/20 12:35:09 the pulses: +map[3211:[high low]] func CalcCommonStep() int { type LoopInfo struct { loopLength, initialDesiredStep int curStep int } loopA := &LoopInfo{4027, 4026, 4026} loopB := &LoopInfo{3923, 3922, 3922} loopC := &LoopInfo{3793, 3792, 3792} loopD := &LoopInfo{3739, 3211, 3738} // nope they can have different amount of own loops. // so it's 4 unknowns, 5 unknowns. // i could store 4 'steps' and on each iteration increase the smallest one // until they are all equal loops := []*LoopInfo{loopA, loopB, loopC, loopD} allSameStep := loopA.curStep == loopB.curStep && loopB.curStep == loopC.curStep && loopC.curStep == loopD.curStep i := 0 for !allSameStep { minLoop := slices.MinFunc(loops, func(a *LoopInfo, b *LoopInfo) int { return cmp.Compare(a.curStep, b.curStep) }) minLoop.curStep += minLoop.loopLength if i % 10000000 == 0 { log.Printf(">> iterations made: %d, min step is %d", i, minLoop.curStep) } i++ } return loopA.curStep }