111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package challenge
|
|
|
|
import (
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
charRegex = regexp.MustCompile(`^\"([a-z])\"$`) // group 1 contains interesting information
|
|
orRuleRegex = regexp.MustCompile(`^((\d+ ?)+) \| ((\d+ ?)+)$`) // group 1 and 3 contain interesting info
|
|
singleRuleRegex = regexp.MustCompile(`^((\d+ ?)+)$`) // group 0 and 1 both contain the same interesting information
|
|
)
|
|
|
|
const replaceMarker = "!!!REPLACETHISMARKER!!!"
|
|
|
|
func generateCompoundRuleRegex(ruleset map[int]string, compound string, current int) string {
|
|
|
|
// Used when you have a rule that looks like `125: 116 33 | 131 113`
|
|
// compound should be a sequence like `116 33`
|
|
// current should be the current rule number
|
|
|
|
var o string
|
|
|
|
for _, xs := range strings.Split(compound, " ") {
|
|
x, err := strconv.Atoi(xs)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if x == current {
|
|
if x == 8 {
|
|
o += generateRuleRegex(ruleset, 42)
|
|
o += "+"
|
|
} else {
|
|
o += replaceMarker
|
|
}
|
|
} else {
|
|
o += generateRuleRegex(ruleset, x)
|
|
}
|
|
}
|
|
|
|
return o
|
|
}
|
|
|
|
func generateRuleRegex(ruleset map[int]string, rule int) string {
|
|
|
|
// Rule is the integer number of the rule to generate a regular expression for
|
|
// Said integer should correspond to a key-value pair in the ruleset
|
|
|
|
ruleContent := ruleset[rule]
|
|
|
|
if charRegex.MatchString(ruleContent) {
|
|
// is a single character
|
|
// `116: "a"`
|
|
return charRegex.FindAllStringSubmatch(ruleContent, -1)[0][1]
|
|
}
|
|
|
|
// if we get here, that means there are multiple options for the rule
|
|
// hence we need to open a new set of brackets
|
|
output := "("
|
|
|
|
if orRuleRegex.MatchString(ruleContent) {
|
|
// rule is an OR rule
|
|
// `83: 26 131 | 47 116`
|
|
submatches := orRuleRegex.FindAllStringSubmatch(ruleContent, -1)[0]
|
|
|
|
output += generateCompoundRuleRegex(ruleset, submatches[1], rule)
|
|
output += "|"
|
|
output += generateCompoundRuleRegex(ruleset, submatches[3], rule)
|
|
} else if singleRuleRegex.MatchString(ruleContent) {
|
|
// rule is a simple sequence rule
|
|
// `92: 67 131`
|
|
output += generateCompoundRuleRegex(ruleset, singleRuleRegex.FindAllStringSubmatch(ruleContent, -1)[0][1], rule)
|
|
}
|
|
|
|
// close original set of brackets
|
|
output += ")"
|
|
|
|
return output
|
|
}
|
|
|
|
func makeRulesetRegex(ruleset map[int]string) string {
|
|
return "^" + generateRuleRegex(ruleset, 0) + "$"
|
|
}
|
|
|
|
func run(messages []string, regex *regexp.Regexp) int {
|
|
var validInputs int
|
|
for _, message := range messages {
|
|
if regex.MatchString(message) {
|
|
validInputs += 1
|
|
}
|
|
}
|
|
return validInputs
|
|
}
|
|
|
|
func parse(instr string) (map[int]string, []string) {
|
|
|
|
splitInput := strings.Split(strings.TrimSpace(instr), "\n\n")
|
|
|
|
rules := make(map[int]string)
|
|
for _, x := range strings.Split(splitInput[0], "\n") {
|
|
splitRule := strings.Split(x, ":")
|
|
num, err := strconv.Atoi(strings.TrimSpace(splitRule[0]))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
rules[num] = strings.TrimSpace(splitRule[1])
|
|
}
|
|
|
|
return rules, strings.Split(strings.TrimSpace(splitInput[1]), "\n")
|
|
}
|