2020 Day 19 & 20 Complete

This commit is contained in:
2020-12-20 15:57:10 -06:00
parent 069d03004d
commit e9e89e0a42
8 changed files with 2661 additions and 244 deletions

View File

@@ -2,7 +2,7 @@ package main
import (
"fmt"
"os"
"regexp"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
@@ -16,10 +16,6 @@ func main() {
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
@@ -32,7 +28,6 @@ affect things as they repeat
*/
func solve(inp []string, part int) {
allTokens = make(map[int]*Token)
var section int
var rules []string
var messages []string
@@ -48,241 +43,130 @@ func solve(inp []string, part int) {
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
if part == 1 {
solveOne(rules, messages)
} 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]))
solveTwo(rules, messages)
}
}
func solveOne(rules, messages []string) {
reg := regexp.MustCompile(rulesToRegex(rules))
var answer int
for _, msg := range messages {
if reg.MatchString(msg) {
answer++
}
}
fmt.Printf("## Part 1\nAnswer: %d\n", answer)
}
func solveTwo(rules, messages []string) {
// Figure out how many levels deep we have to go to stabilize
var answer int
var last []int
for i := 0; i < 1000; i++ {
answer = 0
rules = unwrapLoops(rules, i)
reg := regexp.MustCompile(rulesToRegex(rules))
for _, msg := range messages {
if reg.MatchString(msg) {
answer++
}
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 isStable(last, 10) {
break
}
last = append(last, answer)
}
fmt.Printf("## Part 2\nAnswer: %d\n", answer)
}
func isStable(history []int, confidence int) bool {
if len(history) < confidence {
return false
}
last := h.MAX_INT
for k := len(history) - 1; k > len(history)-confidence; k-- {
if last == h.MAX_INT {
last = history[k]
continue
}
if history[k] != last {
return false
}
}
return true
}
func unwrapLoops(rules []string, depth int) []string {
for i := range rules {
wrk := rules[i]
if strings.HasPrefix(wrk, "8:") {
wrk = "8: 42 +"
} else if strings.HasPrefix(wrk, "11:") {
rule := "42 31"
for k := 2; k < depth; k++ {
rule += " | "
for j := 0; j < k; j++ {
rule += " 42 "
}
for j := 0; j < k; j++ {
rule += " 31 "
}
}
if solved {
t.Parts = partTokens
wrk = "11: " + rule
}
rules[i] = wrk
}
return rules
}
func rulesToRegex(rules []string) string {
cache := make(map[int]string)
for {
if _, ok := cache[0]; ok {
// We've cached 0, so we're all done
break
}
loop:
for _, rule := range rules {
tokens := strings.Fields(rule)
var num int
fmt.Sscanf(tokens[0], "%d:", &num)
if _, ok := cache[num]; ok {
continue
}
} 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
tokens = tokens[1:]
// A 'leaf'
if len(tokens) == 1 && tokens[0][0] == '"' {
cache[num] = string(tokens[0][1])
continue
}
// All other rules
ruleReg := "(?:"
for _, tkn := range tokens {
c := tkn[0]
switch {
case '0' <= c && c <= '9':
tknN := h.Atoi(tkn)
subRuleReg, ok := cache[tknN]
if !ok {
continue loop
}
ruleReg += subRuleReg
case c == '|':
ruleReg += "|"
case c == '+':
ruleReg += "+"
default:
panic("Error building RegEx:" + tkn)
}
}
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
}
ruleReg += ")"
cache[num] = ruleReg
}
}
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"
}
// We should be done now, we just want an exact match on rule 0
return "^" + cache[0] + "$"
}