day20, part2 done with online LCM calculator
This commit is contained in:
parent
57fdfb01cb
commit
98206fe6d4
142
day20/looping.go
Normal file
142
day20/looping.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
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 {
|
||||||
|
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
|
||||||
|
monitoredPulsesOfTheStep := PropagateButtonPressWithMonitor(allModules, step, monitorOutputsOf)
|
||||||
|
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
|
||||||
|
}
|
@ -2,7 +2,6 @@ package day20
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
@ -93,7 +92,7 @@ func ParseFlipFlop(line string) (result FlipFlop) {
|
|||||||
re := regexp.MustCompile(`%(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
re := regexp.MustCompile(`%(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
||||||
matches := re.FindStringSubmatch(line)
|
matches := re.FindStringSubmatch(line)
|
||||||
|
|
||||||
log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
// log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
||||||
result.Name = matches[1]
|
result.Name = matches[1]
|
||||||
result.OutputNames = strings.Split(matches[2], ", ")
|
result.OutputNames = strings.Split(matches[2], ", ")
|
||||||
|
|
||||||
@ -220,7 +219,7 @@ func ParseConjunction(line string) (result Conjunction) {
|
|||||||
re := regexp.MustCompile(`&(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
re := regexp.MustCompile(`&(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
||||||
matches := re.FindStringSubmatch(line)
|
matches := re.FindStringSubmatch(line)
|
||||||
|
|
||||||
log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
// log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
||||||
result.Name = matches[1]
|
result.Name = matches[1]
|
||||||
result.OutputNames = strings.Split(matches[2], ", ")
|
result.OutputNames = strings.Split(matches[2], ", ")
|
||||||
|
|
||||||
@ -256,7 +255,7 @@ func (b *Button)MermaidFlow() string {
|
|||||||
type Output struct {}
|
type Output struct {}
|
||||||
|
|
||||||
func (o *Output)Receive(s Signal) []Signal {
|
func (o *Output)Receive(s Signal) []Signal {
|
||||||
log.Print("Outut received signal: ", s)
|
// log.Print("Outut received signal: ", s)
|
||||||
return []Signal{}
|
return []Signal{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
105
day20/notes.org
105
day20/notes.org
@ -67,3 +67,108 @@ but first let's check if loops over
|
|||||||
- zl
|
- zl
|
||||||
|
|
||||||
are manageable.
|
are manageable.
|
||||||
|
|
||||||
|
well.
|
||||||
|
i'll need to what?
|
||||||
|
not only track the inputs of the th.
|
||||||
|
but state of the 'subloop'
|
||||||
|
and they are separate
|
||||||
|
|
||||||
|
is there an easy way to collect the names from each subloop?
|
||||||
|
i guess i could write a collect.
|
||||||
|
|
||||||
|
from each of outputs of 'broadcast'
|
||||||
|
|
||||||
|
then have a funciton that checks loop size of each subgraphs
|
||||||
|
|
||||||
|
but i will also need to figure out on which steps output of the loop is remembered as High \ Low
|
||||||
|
|
||||||
|
let's start with loop size? and modify things if need be
|
||||||
|
** starting points of loops:
|
||||||
|
children of the broadcast:
|
||||||
|
broadcaster -> sr, ch, hd, bx
|
||||||
|
|
||||||
|
sr, ch, hd, bx
|
||||||
|
** ok. some data here
|
||||||
|
2023/12/20 12:05:06 >>> searching for loop of sr
|
||||||
|
2023/12/20 12:05:06 >>> found loop from 1 to 4028. of size 4028
|
||||||
|
2023/12/20 12:05:06 >>> searching for loop of ch
|
||||||
|
2023/12/20 12:05:06 >>> found loop from 0 to 3923. of size 3924
|
||||||
|
2023/12/20 12:05:06 >>> searching for loop of hd
|
||||||
|
2023/12/20 12:05:06 >>> found loop from 0 to 3793. of size 3794
|
||||||
|
2023/12/20 12:05:06 >>> searching for loop of bx
|
||||||
|
2023/12/20 12:05:07 >>> found loop from 0 to 3739. of size 3740
|
||||||
|
|
||||||
|
one of these guys starts from 1, not from 0.
|
||||||
|
this is unusual, but OK
|
||||||
|
|
||||||
|
now, i want to figure out what are steps where output for the each cycle is 'considered as saved as 1'
|
||||||
|
|
||||||
|
i guess i could just directly probe the
|
||||||
|
`th`
|
||||||
|
|
||||||
|
on each step up to 4028
|
||||||
|
|
||||||
|
but also, if the signallings from those are rare - would be eaiser to collect steps of each signal.
|
||||||
|
** ok. i collected 'monitored pulses' and i see lots of 'Low'
|
||||||
|
what i want is all "high" and first low after those.
|
||||||
|
** oh wow, this crap
|
||||||
|
2023/12/20 12:30:05 >>> searching for loop of ch
|
||||||
|
2023/12/20 12:30:05 >>> found loop from 1 to 3924. of size 3924
|
||||||
|
2023/12/20 12:30:05 the pulses
|
||||||
|
+map[3922:[high low]]
|
||||||
|
2023/12/20 12:30:05 >>> searching for loop of hd
|
||||||
|
2023/12/20 12:30:05 >>> found loop from 0 to 3793. of size 3794
|
||||||
|
2023/12/20 12:30:05 the pulses
|
||||||
|
+map[3661:[high low]]
|
||||||
|
2023/12/20 12:30:05 >>> searching for loop of bx
|
||||||
|
2023/12/20 12:30:05 >>> found loop from 0 to 3739. of size 3740
|
||||||
|
2023/12/20 12:30:05 the pulses
|
||||||
|
+map[3499:[high low]]
|
||||||
|
2023/12/20 12:30:05 >>> searching for loop of sr
|
||||||
|
2023/12/20 12:30:05 >>> found loop from 0 to 4027. of size 4028
|
||||||
|
2023/12/20 12:30:05 the pulses
|
||||||
|
+map[624:[high low]]
|
||||||
|
*** but at least these 'high low' are all on same step.
|
||||||
|
now with info on loop start, place of pulse in the loop and length of loops,
|
||||||
|
what is the step so that those [high low] occur on same step num?
|
||||||
|
*** math should be:
|
||||||
|
3922 + LOOP_N * (LOOP_LEN)
|
||||||
|
** wait i now get different output?
|
||||||
|
2023/12/20 12:57:50 >>> searching for loop of bx
|
||||||
|
2023/12/20 12:57:50 >>> found loop from 1 to 3739. of size 3739
|
||||||
|
2023/12/20 12:57:50 the pulses: +map[3738:[high low]]
|
||||||
|
2023/12/20 12:57:50 >>> searching for loop of sr
|
||||||
|
2023/12/20 12:57:50 >>> found loop from 0 to 4026. of size 4027
|
||||||
|
2023/12/20 12:57:50 the pulses: +map[286:[high low]]
|
||||||
|
2023/12/20 12:57:50 >>> searching for loop of ch
|
||||||
|
2023/12/20 12:57:50 >>> found loop from 0 to 3922. of size 3923
|
||||||
|
2023/12/20 12:57:50 the pulses: +map[78:[high low]]
|
||||||
|
2023/12/20 12:57:50 >>> searching for loop of hd
|
||||||
|
2023/12/20 12:57:51 >>> found loop from 0 to 3792. of size 3793
|
||||||
|
2023/12/20 12:57:51 the pulses: +map[3481:[high low]]
|
||||||
|
** why is my filtering unstable?
|
||||||
|
** let's check for single loop?
|
||||||
|
** yikes. but maybe
|
||||||
|
2023/12/20 13:08:52 >>> searching for loop of sr
|
||||||
|
2023/12/20 13:08:52 >>> found loop from 2 to 4028. of size 4027
|
||||||
|
2023/12/20 13:08:52 the pulses: +map[4027:[high low]]
|
||||||
|
|
||||||
|
2023/12/20 13:09:23 >>> searching for loop of ch
|
||||||
|
2023/12/20 13:09:23 >>> found loop from 2 to 3924. of size 3923
|
||||||
|
2023/12/20 13:09:23 the pulses: +map[3923:[high low]]
|
||||||
|
|
||||||
|
2023/12/20 13:09:37 >>> searching for loop of hd
|
||||||
|
2023/12/20 13:09:37 >>> found loop from 2 to 3794. of size 3793
|
||||||
|
2023/12/20 13:09:37 the pulses: +map[3793:[high low]]
|
||||||
|
|
||||||
|
2023/12/20 13:09:49 >>> searching for loop of bx
|
||||||
|
2023/12/20 13:09:49 >>> found loop from 2 to 3740. of size 3739
|
||||||
|
2023/12/20 13:09:49 the pulses: +map[3739:[high low]]
|
||||||
|
|
||||||
|
all loops start from same plase.
|
||||||
|
i could just do 1 press. then the loop starts. and all of them have [high low] on last place.
|
||||||
|
so it's going to be 1 + least common ...
|
||||||
|
** aaand, i just did least common multiple of the cycle lenghts.
|
||||||
|
and i didn't even added 1. which is strange. i guess i did have 'off-by-one'
|
||||||
|
crap
|
||||||
|
@ -13,15 +13,39 @@ func Run() int {
|
|||||||
filename := "day20/input"
|
filename := "day20/input"
|
||||||
modules := ReadModules(filename)
|
modules := ReadModules(filename)
|
||||||
InitStuffs(modules)
|
InitStuffs(modules)
|
||||||
log.Print("got modules:\n", modules)
|
// log.Print("got modules:\n", modules)
|
||||||
|
|
||||||
var low, high int
|
// var low, high int
|
||||||
// low, high = Count10000ButtonPresses(modules)
|
// low, high = Count10000ButtonPresses(modules)
|
||||||
log.Printf("got low %d and high %d\n", low, high)
|
// log.Printf("got low %d and high %d\n", low, high)
|
||||||
|
|
||||||
fmt.Println(AllMermaidFlowChard(modules))
|
CheckSubgraphsStuff(modules)
|
||||||
|
|
||||||
return low * high
|
var result int
|
||||||
|
// result = CalcCommonStep()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckSubgraphsStuff(allModules map[string]Module) {
|
||||||
|
// 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 {
|
||||||
|
log.Printf(">>> searching for loop of %s", start)
|
||||||
|
themap := make(map[string]any)
|
||||||
|
loopModules := TransitiveOutputs(start, allModules, themap)
|
||||||
|
_, _, requestedPulses := FindSubGraphLoopLength(loopModules, allModules, end)
|
||||||
|
FilterMonitoredPulses(requestedPulses)
|
||||||
|
log.Printf("the pulses: +%v", requestedPulses)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Count10000ButtonPresses(modules map[string]Module) (lowSignalsCount, highSignalsCount int) {
|
func Count10000ButtonPresses(modules map[string]Module) (lowSignalsCount, highSignalsCount int) {
|
||||||
@ -108,6 +132,41 @@ func PropagateButtonPress(modules map[string]Module, i int) (lowSignalsCount, hi
|
|||||||
return
|
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"
|
// process sends single `low pulse` directly to "broadcast"
|
||||||
|
|
||||||
func ReadModules(filename string) map[string]Module {
|
func ReadModules(filename string) map[string]Module {
|
||||||
@ -132,7 +191,7 @@ func ReadModules(filename string) map[string]Module {
|
|||||||
result[parsed.Name] = &parsed
|
result[parsed.Name] = &parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(line)
|
// log.Println(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonModule := Button{}
|
buttonModule := Button{}
|
||||||
@ -152,11 +211,11 @@ func InitStuffs(allModules map[string]Module) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ModulesState(allModules map[string]Module) string {
|
func ModulesState(modules map[string]Module) string {
|
||||||
// relying on printing of map values to be ordered by key
|
// relying on printing of map values to be ordered by key
|
||||||
// https://stackoverflow.com/a/54524991/2788805
|
// https://stackoverflow.com/a/54524991/2788805
|
||||||
states := make(map[string]string)
|
states := make(map[string]string)
|
||||||
for name, module := range allModules {
|
for name, module := range modules {
|
||||||
states[name] = module.StateSnapshot()
|
states[name] = module.StateSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +223,7 @@ func ModulesState(allModules map[string]Module) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AllMermaidFlowChard(allModules map[string]Module) (result string) {
|
func AllMermaidFlowChard(allModules map[string]Module) (result string) {
|
||||||
result = "flowchart LR\n"
|
result = "flowchart TD\n"
|
||||||
for _, module := range allModules {
|
for _, module := range allModules {
|
||||||
result += module.MermaidFlow()
|
result += module.MermaidFlow()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user