2020 Day 19 & 20 Complete
This commit is contained in:
@@ -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] + "$"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user