140 lines
2.5 KiB
Go
140 lines
2.5 KiB
Go
package nineteen
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type rule struct {
|
|
matches string
|
|
composedOf [][]int
|
|
}
|
|
|
|
type satellite struct {
|
|
rules map[int]rule
|
|
messages []string
|
|
}
|
|
|
|
func (s *satellite) load(filename string) error {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
s.rules = map[int]rule{}
|
|
scanner := bufio.NewScanner(file)
|
|
messages := false
|
|
for scanner.Scan() {
|
|
if !messages {
|
|
if scanner.Text() == "" {
|
|
messages = true
|
|
continue
|
|
}
|
|
|
|
ruleSplit := strings.Split(scanner.Text(), ": ")
|
|
ruleKey, err := strconv.Atoi(ruleSplit[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r := rule{}
|
|
|
|
if strings.Contains(ruleSplit[1], "\"a\"") {
|
|
r.matches = "a"
|
|
} else if strings.Contains(ruleSplit[1], "\"b\"") {
|
|
r.matches = "b"
|
|
} else {
|
|
options := strings.Split(ruleSplit[1], " | ")
|
|
for _, o := range options {
|
|
m := []int{}
|
|
rules := strings.Split(o, " ")
|
|
for _, r := range rules {
|
|
t, err := strconv.Atoi(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m = append(m, t)
|
|
}
|
|
r.composedOf = append(r.composedOf, m)
|
|
}
|
|
}
|
|
|
|
s.rules[ruleKey] = r
|
|
continue
|
|
}
|
|
|
|
s.messages = append(s.messages, scanner.Text())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *satellite) match(r int, input string) (bool, int) {
|
|
if len(input) == 0 {
|
|
return false, 0
|
|
}
|
|
|
|
rule := s.rules[r]
|
|
|
|
if rule.matches == string(input[0]) {
|
|
return true, 1
|
|
}
|
|
|
|
matchedChars := 0
|
|
for i := 0; i < len(rule.composedOf); i++ {
|
|
matchedChars = 0
|
|
valid := true
|
|
if len(rule.composedOf[i]) > len(input) {
|
|
continue
|
|
}
|
|
for _, sr := range rule.composedOf[i] {
|
|
match, count := s.match(sr, input[matchedChars:])
|
|
matchedChars += count
|
|
if !match {
|
|
valid = false
|
|
break
|
|
}
|
|
}
|
|
if valid {
|
|
return true, matchedChars
|
|
}
|
|
}
|
|
|
|
return false, matchedChars
|
|
}
|
|
|
|
func (s *satellite) messageCount(r int) (count int) {
|
|
for _, m := range s.messages {
|
|
match, length := s.match(r, m)
|
|
if match && length == len(m) {
|
|
count++
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
// PartOne How many messages match message 0
|
|
func PartOne() string {
|
|
s := satellite{}
|
|
if err := s.load("nineteen/input.txt"); err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return fmt.Sprintf("There are %d messages that match rule 0", s.messageCount(0))
|
|
}
|
|
|
|
// PartTwo How many messages match message 0 with alternate rules
|
|
func PartTwo() string {
|
|
s := satellite{}
|
|
if err := s.load("nineteen/input2.txt"); err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return fmt.Sprintf("There are %d messages that match rule 0", s.messageCount(0))
|
|
}
|