adventOfCode/19-monsterMessages/go/challenge/common.go
2020-12-19 20:29:34 +00:00

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")
}