package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Rule ...
type Rule struct {
R rune
M [][]int
}
var ruleCache = make(map[int][]string)
func resetCache() {
ruleCache = make(map[int][]string)
}
func generateValidMessages(rules map[int]Rule, index int, debug bool) []string {
if debug {
fmt.Printf("----\n")
}
if c, ok := ruleCache[index]; ok {
return c
}
result := make([]string, 0, 10)
for _, m := range rules[index].M {
current := make([]string, 0, 10)
for _, i := range m {
if rules[i].R > 0 {
alt := string(rules[i].R)
if debug {
fmt.Printf("adding %s to %+v\n", alt, current)
}
if len(current) == 0 {
current = append(current, alt)
} else {
for i := 0; i < len(current); i++ {
current[i] += alt
}
}
if debug {
fmt.Printf("now %+v\n", current)
}
} else {
tmp := make([]string, 0, 10)
parts := generateValidMessages(rules, i, debug)
if debug {
fmt.Printf("current %+v parts %+v\n", current, parts)
}
for _, p := range parts {
if len(current) == 0 {
tmp = append(tmp, p)
} else {
for _, c := range current {
tmp = append(tmp, c+p)
}
}
}
current = tmp
if debug {
fmt.Printf("new current %+v\n", current)
}
}
}
for _, c := range current {
result = append(result, c)
}
ruleCache[index] = result
}
ruleCache[index] = result
return result
}
// Task1 ...
func Task1(rules map[int]Rule, messages []string, debug bool) int {
valid := generateValidMessages(rules, 0, debug)
fmt.Printf("Valid rules: %d\n", len(valid))
var result int
for _, m := range messages {
var found bool
for _, v := range valid {
if m == v {
found = true
fmt.Printf("%s found!\n", m)
result++
break
}
}
if !found {
fmt.Printf("%s missing...\n", m)
}
}
return result
}
// Task2 ...
func Task2(rules map[int]Rule, messages []string, debug bool) int {
rules[8] = Rule{
M: [][]int{
{42},
{42, 8},
},
}
rules[11] = Rule{
M: [][]int{
{42, 31},
{42, 11, 31},
},
}
valid := generateValidMessages(rules, 0, debug)
fmt.Printf("Valid rules: %d\n", len(valid))
var result int
for _, m := range messages {
var found bool
for _, v := range valid {
if m == v {
found = true
fmt.Printf("%s found!\n", m)
result++
break
}
}
if !found {
fmt.Printf("%s missing...\n", m)
}
}
return result
}
func main() {
file, _ := os.Open("input.txt")
scanner := bufio.NewScanner(file)
rules := make(map[int]Rule)
messages := make([]string, 0, 100)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
break
}
parts := strings.Split(line, ": ")
i, _ := strconv.Atoi(parts[0])
matchParts := strings.Split(parts[1], " | ")
rule := Rule{
M: make([][]int, 0, 10),
}
if matchParts[0][0] == '"' {
rule.R = rune(matchParts[0][1])
}
for _, m := range matchParts {
match := make([]int, 0, 2)
for _, str := range strings.Split(m, " ") {
v, _ := strconv.Atoi(str)
match = append(match, v)
}
rule.M = append(rule.M, match)
}
rules[i] = rule
}
for scanner.Scan() {
messages = append(messages, scanner.Text())
}
file.Close()
resetCache()
result := Task1(rules, messages, false)
fmt.Printf("Task 1: %d\n", result)
resetCache()
result = Task2(rules, messages, false)
fmt.Printf("Task 2: %d\n", result)
}