289 lines
5.2 KiB
Go
289 lines
5.2 KiB
Go
|
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"
|
||
|
}
|
||
|
}
|