package main import ( "fmt" "os" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { fmt.Println("# Day 19") fmt.Println() inp := h.StdinToStringSlice() part := h.OptArgNumber(1, "2") solve(inp, h.Atoi(part)) } var loopingTokens []int var allTokens map[int]*Token var totalRules int /* 8: 42 | 42 8 11: 42 31 | 42 11 31 Figure out the values of 31 & 42, then see how they affect things as they repeat 31: 95 7 | 104 130 42: 29 104 | 115 95 */ func solve(inp []string, part int) { allTokens = make(map[int]*Token) var section int var rules []string var messages []string for _, v := range inp { if v == "" { // End of the rules list section++ } switch section { case 0: rules = append(rules, v) case 1: messages = append(messages, v) } } totalRules = len(rules) last := -1 for RulesSolved() < totalRules && last != RulesSolved() { last = RulesSolved() for _, v := range rules { fmt.Println("Restarting Rules Parsing") pts := strings.Split(v, ": ") num := h.Atoi(pts[0]) if part == 2 { if num == 8 { pts[1] = "42 | 42 8" } else if num == 11 { pts[1] = "42 31 | 42 11 31" } } if _, ok := allTokens[num]; !ok { GenerateToken(num, pts[1]) } } } if RulesSolved() < totalRules { fmt.Println("Unable to solve all rules") os.Exit(1) } fmt.Println("Calculating Answer") var matches int for _, v := range messages { if allTokens[0].Matches(v) { matches++ } } fmt.Printf("## Part 1\nAnswer: %d\n", matches) } // RulesSolved TODO func RulesSolved() int { var ret int for k := range allTokens { if k >= 0 { ret++ } } return ret } // Token Kinds const ( LEAF = iota STICK BRANCH ) // Token TODO type Token struct { kind int num int value string raw string PartNums []int Parts []*Token CachedValues []string Loops bool } // GenerateToken TODO func GenerateToken(num int, raw string) *Token { if v, ok := allTokens[num]; ok { return v } t := Token{num: num, raw: raw} if raw[0] == '"' { t.value = strings.ReplaceAll(raw, "\"", "") t.kind = LEAF } else { if !strings.Contains(raw, "|") { t.kind = STICK var partTokens []*Token pts := strings.Split(raw, " ") for k := range pts { if pts[k] == "|" { continue } t.PartNums = append(t.PartNums, h.Atoi(pts[k])) } solved := true for k := range t.PartNums { if v, ok := allTokens[t.PartNums[k]]; ok || h.IntSliceContains(loopingTokens, t.PartNums[k]) { partTokens = append(partTokens, v) } else { solved = false break } } if solved { t.Parts = partTokens } } else { t.kind = BRANCH pts := strings.Split(raw, " ") for k := range pts { if pts[k] == "|" { continue } n := h.Atoi(pts[k]) if n == num { if !h.IntSliceContains(loopingTokens, t.num) { fmt.Println(num, "IS A LOOPING TOKEN") loopingTokens = append(loopingTokens, t.num) t.Loops = true } } } branches := strings.Split(raw, " | ") var partTokens []*Token for k, branch := range branches { branchID := -(num*1000 + k) t.PartNums = append(t.PartNums, branchID) p := GenerateToken(branchID, branch) if p.Solved() { allTokens[branchID] = p partTokens = append(partTokens, p) } else { continue } } solved := true for _, bid := range t.PartNums { if _, ok := allTokens[bid]; !ok { solved = false break } } if solved { t.Parts = partTokens } } } if t.Solved() { allTokens[num] = &t } else { fmt.Println("> Couldn't Solve", num) } return &t } // Solved TODO func (t *Token) Solved() bool { return len(t.Values()) > 0 } // Values TODO func (t *Token) Values() []string { switch t.kind { case LEAF: return t.LeafValue() case STICK: return t.StickValue() case BRANCH: return t.BranchValue() default: return []string{} } } // LeafValue TODO func (t *Token) LeafValue() []string { return []string{t.value} } // StickValue TODO func (t *Token) StickValue() []string { if len(t.Parts) < len(t.PartNums) { return []string{} } if len(t.CachedValues) > 0 { return t.CachedValues } var ret []string for k := range t.Parts { if len(ret) == 0 { ret = t.Parts[k].Values() } else { var newRet []string for rk := range ret { newRet = append(newRet, h.AppendStrings(ret[rk], t.Parts[k].Values())...) } ret = newRet } } t.CachedValues = ret return ret } // BranchValue TODO func (t *Token) BranchValue() []string { if len(t.Parts) < len(t.PartNums) { return []string{} } if len(t.CachedValues) > 0 { return t.CachedValues } var ret []string for k := range t.Parts { ret = append(ret, t.Parts[k].Values()...) } t.CachedValues = ret return ret } // Matches actually tests a message against this token func (t *Token) Matches(msg string) bool { for _, v := range t.Values() { if msg == v { return true } } return false } func (t Token) String() string { return fmt.Sprintf("{num:%d, kind:%s, partNums:%v, value:%v}", t.num, KindToString(t.kind), t.PartNums, t.Values()) } // KindToString TODO func KindToString(kind int) string { switch kind { case LEAF: return "LEAF" case STICK: return "STICK" case BRANCH: return "BRANCH" default: return "UNKNOWN" } }