277 lines
5.7 KiB
Go
277 lines
5.7 KiB
Go
package day20
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
type PulseType int
|
|
func (pt PulseType)String() string {
|
|
types := []string{"high", "low"}
|
|
return types[pt]
|
|
}
|
|
|
|
const (
|
|
HighPulse PulseType = iota
|
|
LowPulse
|
|
)
|
|
|
|
type Signal struct {
|
|
To, From string
|
|
PulseType PulseType
|
|
}
|
|
|
|
type Module interface {
|
|
Receive(s Signal) []Signal
|
|
Outputs() []string
|
|
StateSnapshot() string
|
|
MermaidFlow() string
|
|
}
|
|
|
|
// Modules
|
|
type FlipFlop struct {
|
|
Name string
|
|
OutputNames []string
|
|
IsOn bool
|
|
}
|
|
|
|
// ignores HighPulse
|
|
// on LowPulse - toggle state and send signal
|
|
func (ff *FlipFlop)Receive(s Signal) []Signal {
|
|
if s.PulseType == HighPulse {
|
|
return []Signal{}
|
|
}
|
|
|
|
ff.IsOn = !ff.IsOn
|
|
outTemplate := Signal{
|
|
From: ff.Name,
|
|
}
|
|
if ff.IsOn {
|
|
outTemplate.PulseType = HighPulse
|
|
} else {
|
|
outTemplate.PulseType = LowPulse
|
|
}
|
|
|
|
result := make([]Signal, len(ff.OutputNames))
|
|
for i, outName := range ff.OutputNames {
|
|
out := outTemplate
|
|
out.To = outName
|
|
result[i] = out
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (ff *FlipFlop)Outputs() []string {
|
|
return ff.OutputNames
|
|
}
|
|
|
|
func (ff *FlipFlop)String() string {
|
|
return fmt.Sprintf("[flip-flop '%s' (on: %t) -> %s]", ff.Name, ff.IsOn, ff.OutputNames)
|
|
}
|
|
|
|
func (ff *FlipFlop)StateSnapshot() string {
|
|
return ff.String()
|
|
}
|
|
|
|
func (ff *FlipFlop)MermaidFlow() string {
|
|
result := "\n"
|
|
for _, toName := range ff.OutputNames {
|
|
result += fmt.Sprintf("%s --> %s\n", ff.Name, toName)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func IsLineFlipFlop(line string) bool {
|
|
return strings.HasPrefix(line, "%")
|
|
}
|
|
|
|
func ParseFlipFlop(line string) (result FlipFlop) {
|
|
re := regexp.MustCompile(`%(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
|
matches := re.FindStringSubmatch(line)
|
|
|
|
// log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
|
result.Name = matches[1]
|
|
result.OutputNames = strings.Split(matches[2], ", ")
|
|
|
|
return
|
|
}
|
|
|
|
type Broadcast struct {
|
|
OutputNames []string
|
|
}
|
|
|
|
// send same pulse to all outputs
|
|
func (b *Broadcast)Receive(s Signal) (result []Signal) {
|
|
signalTemplate := Signal{From: "broadcast", PulseType: s.PulseType}
|
|
for _, out := range b.OutputNames {
|
|
outSignal := signalTemplate
|
|
outSignal.To = out
|
|
result = append(result, outSignal)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (b *Broadcast)Outputs() []string {
|
|
return b.OutputNames
|
|
}
|
|
|
|
func (b *Broadcast)String() string {
|
|
return fmt.Sprintf("[broadcast -> %+v]", b.OutputNames)
|
|
}
|
|
func (b *Broadcast)StateSnapshot() string {
|
|
return b.String()
|
|
}
|
|
|
|
func (b *Broadcast)MermaidFlow() string {
|
|
result := "\n"
|
|
for _, toName := range b.OutputNames {
|
|
result += fmt.Sprintf("%s --> %s\n", "broadcast", toName)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func IsLineBroadcast(line string) bool {
|
|
return strings.HasPrefix(line, "broadcaster")
|
|
}
|
|
|
|
func ParseBroadcast(line string) (result Broadcast) {
|
|
re := regexp.MustCompile(`broadcaster -> (?P<OUTPUTS>.+)`)
|
|
matches := re.FindStringSubmatch(line)
|
|
|
|
result.OutputNames = strings.Split(matches[1], ", ")
|
|
return
|
|
}
|
|
|
|
type Conjunction struct {
|
|
Name string
|
|
OutputNames []string
|
|
MostRecentPulseFromInputIsHigh map[string]bool
|
|
}
|
|
|
|
// remembers last signal type from all inputs (initial default is Low)
|
|
// when receiving pulse, first update memory for that input
|
|
// then if for all inputs remembered is high - send LowPulse
|
|
// otherwise if some remembers are low - send HighPulse
|
|
func (c *Conjunction)Receive(s Signal) (result []Signal) {
|
|
c.MostRecentPulseFromInputIsHigh[s.From] = s.PulseType == HighPulse
|
|
|
|
allHigh := true
|
|
for _, latestImpulseHight := range c.MostRecentPulseFromInputIsHigh {
|
|
if !latestImpulseHight {
|
|
allHigh = false
|
|
break
|
|
}
|
|
}
|
|
|
|
outTemplate := Signal{From: c.Name}
|
|
if allHigh {
|
|
outTemplate.PulseType = LowPulse
|
|
} else {
|
|
outTemplate.PulseType = HighPulse
|
|
}
|
|
|
|
for _, outName := range c.OutputNames {
|
|
outSignal := outTemplate
|
|
outSignal.To = outName
|
|
result = append(result, outSignal)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (c *Conjunction)Outputs() []string {
|
|
return c.OutputNames
|
|
}
|
|
|
|
func (c *Conjunction)RegisterInputs(allModules map[string]Module) {
|
|
for name, module := range allModules {
|
|
if slices.Contains( module.Outputs(), c.Name) {
|
|
c.MostRecentPulseFromInputIsHigh[name] = false
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Conjunction)String() string {
|
|
return fmt.Sprintf("[conjunction '%s' -> %+v]", c.Name, c.OutputNames)
|
|
}
|
|
|
|
func (c *Conjunction)StateSnapshot() string {
|
|
return fmt.Sprintf("[conjunction '%s' -> %+v]", c.Name, c.MostRecentPulseFromInputIsHigh)
|
|
}
|
|
|
|
func (c *Conjunction)MermaidFlow() string {
|
|
result := "\n"
|
|
for _, toName := range c.OutputNames {
|
|
result += fmt.Sprintf("%s --> %s\n", c.Name, toName)
|
|
}
|
|
return result
|
|
|
|
}
|
|
|
|
func IsLineConjunction(line string) bool {
|
|
return strings.HasPrefix(line, "&")
|
|
}
|
|
|
|
func ParseConjunction(line string) (result Conjunction) {
|
|
re := regexp.MustCompile(`&(?P<NAME>\D+) -> (?P<OUTPUTS>.+)`)
|
|
matches := re.FindStringSubmatch(line)
|
|
|
|
// log.Printf("matching %s getting '%s' and '%s'\n", line, matches[1], matches[2])
|
|
result.Name = matches[1]
|
|
result.OutputNames = strings.Split(matches[2], ", ")
|
|
|
|
result.MostRecentPulseFromInputIsHigh = map[string]bool{}
|
|
|
|
return
|
|
}
|
|
|
|
type Button struct {}
|
|
|
|
func (b *Button)Receive(s Signal) []Signal {
|
|
return []Signal{
|
|
{ To: "broadcast", From: "button", PulseType: LowPulse },
|
|
}
|
|
}
|
|
|
|
func (b *Button)Outputs() []string {
|
|
return []string{"broadcast"}
|
|
}
|
|
|
|
func (b *Button)String() string {
|
|
return "[button]"
|
|
}
|
|
|
|
func (b *Button)StateSnapshot() string {
|
|
return b.String()
|
|
}
|
|
|
|
func (b *Button)MermaidFlow() string {
|
|
return "button --> broadcast\n"
|
|
}
|
|
|
|
type Output struct {}
|
|
|
|
func (o *Output)Receive(s Signal) []Signal {
|
|
// log.Print("Outut received signal: ", s)
|
|
return []Signal{}
|
|
}
|
|
|
|
func (o *Output)Outputs() []string {
|
|
return []string{}
|
|
}
|
|
|
|
func (o *Output)String() string {
|
|
return "[output]"
|
|
}
|
|
|
|
func (o *Output)StateSnapshot() string {
|
|
return o.String()
|
|
}
|
|
|
|
func (o *Output)MermaidFlow() string {
|
|
return ""
|
|
}
|